Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1995, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/* $Id: gdevmrun.c,v 1.5 2003/08/21 14:55:14 igor Exp $ */
18
/* Run-length encoded memory device */
19
#include "memory_.h"
20
#include "gx.h"
21
#include "gserrors.h"
22
#include "gxdevice.h"
23
#include "gdevmrun.h"
24
 
25
/*
26
 * NOTE: THIS CODE HAS NOT BEEN TESTED.  IF YOU WANT TO USE IT, PLEASE
27
 * TEST IT CAREFULLY AND REPORT ANY PROBLEMS.
28
 */
29
 
30
/*
31
 * Define the representation of each run.  We store runs in a doubly-linked
32
 * list.  Run 0 is a dummy end-of-line run; run 1 is a dummy start-of-line
33
 * run.  The dummy runs have length MAX_RUN_LENGTH to prevent merging.
34
 *
35
 * We limit the number of runs per line for two reasons: if there are many
36
 * runs, the run-length representation probably isn't buying us much; and
37
 * we need to allocate temporary space on the stack for the runs when we
38
 * expand a line to uncompressed form.
39
 */
40
typedef gx_color_index run_value;
41
typedef uint run_index;
42
#define RUN_INDEX_BITS 10	/* see above */
43
#define MAX_RUNS (1 << RUN_INDEX_BITS)
44
#define MAX_RUN_INDEX (MAX_RUNS - 1)
45
typedef uint run_length;
46
#define RUN_LENGTH_BITS (32 - 2 * RUN_INDEX_BITS)
47
#define MAX_RUN_LENGTH ((1 << RUN_LENGTH_BITS) - 1)
48
typedef struct run_s {
49
    run_value value;
50
    run_length length : RUN_LENGTH_BITS;
51
    run_index next : RUN_INDEX_BITS;
52
    run_index prev : RUN_INDEX_BITS; /* 0 iff free run */
53
} run;
54
 
55
/*
56
 * Define a pointer into a run list.
57
 * For speed, we keep both the index of and the pointer to the current run.
58
 */
59
typedef struct run_ptr_s {
60
    run *ptr;
61
    run_index index;		/* index of current run */
62
} run_ptr;
63
typedef struct const_run_ptr_s {
64
    const run *ptr;
65
    run_index index;		/* index of current run */
66
} const_run_ptr;
67
 
68
/* Accessors */
69
#define RP_LENGTH(rp) ((rp).ptr->length)
70
#define RP_VALUE(rp) ((rp).ptr->value)
71
#define RP_NEXT(rp) ((rp).ptr->next)
72
#define RP_PREV(rp) ((rp).ptr->prev)
73
#define RL_DATA(line) ((run *)((line) + 1))
74
#define CONST_RL_DATA(line) ((const run *)((line) + 1))
75
#define RDEV_LINE(rdev, y) ((run_line *)scan_line_base(&(rdev)->md, y))
76
/* Traversers */
77
#define RP_AT_START(rp) ((rp).index == 1)
78
#define RP_AT_END(rp) ((rp).index == 0)
79
#define RP_TO_START(rp, data)\
80
  ((rp).index = (data)[1].next,\
81
   (rp).ptr = (data) + (rp).index)
82
/* Note that RP_TO_NEXT and RP_TO_PREV allow rpn == rpc. */
83
#define RP_TO_NEXT(rpc, data, rpn)\
84
  ((rpn).ptr = (data) + ((rpn).index = RP_NEXT(rpc)))
85
#define RP_TO_PREV(rpc, data, rpp)\
86
  ((rpp).ptr = (data) + ((rpp).index = RP_PREV(rpc)))
87
 
88
/*
89
 * Define the state of a single scan line.
90
 *
91
 * We maintain the following invariant: if two adjacent runs have the
92
 * same value, the sum of their lengths is greater than MAX_RUN_LENGTH.
93
 * This may miss optimality by nearly a factor of 2, but it's far easier
94
 * to maintain than a true optimal representation.
95
 *
96
 * For speed in the common case where nothing other than white is ever stored,
97
 * we initially don't bother to construct the runs (or the free run list)
98
 * for a line at all.
99
 */
100
typedef struct run_line_s {
101
    gx_color_index zero;	/* device white if line not initialized, */
102
				/* gx_no_color_index if initialized */
103
    uint xcur;			/* x value at cursor position */
104
    run_ptr rpcur;		/* cursor */
105
    run_index free;		/* head of free list */
106
} run_line;
107
 
108
/* Insert/delete */
109
private void
110
rp_delete_next(run_ptr *prpc, run *data, run_line *line)
111
{
112
    run_ptr rpn, rpn2;
113
 
114
    RP_TO_NEXT(*prpc, data, rpn);
115
    RP_TO_NEXT(rpn, data, rpn2);
116
    RP_NEXT(*prpc) = rpn2.index;
117
    RP_PREV(rpn2) = prpc->index;
118
    RP_NEXT(rpn) = line->free;
119
    RP_PREV(rpn) = 0;
120
    line->free = rpn.index;
121
}
122
private int
123
rp_insert_next(run_ptr *prpc, run *data, run_line *line, run_ptr *prpn)
124
{
125
    run_index new = line->free;
126
    run *prnew = data + new;
127
 
128
    if (new == 0)
129
	return -1;
130
    RP_TO_NEXT(*prpc, data, *prpn);
131
    RP_NEXT(*prpc) = new;
132
    RP_PREV(*prpn) = new;
133
    line->free = prnew->next;
134
    prnew->prev = prpc->index;
135
    prnew->next = prpn->index;
136
    prpn->index = new;
137
    prpn->ptr = prnew;
138
    return 0;
139
}
140
private int
141
rp_insert_prev(run_ptr *prpc, run *data, run_line *line, run_ptr *prpp)
142
{
143
    run_index new = line->free;
144
    run *prnew = data + new;
145
 
146
    if (new == 0)
147
	return -1;
148
    RP_TO_PREV(*prpc, data, *prpp);
149
    RP_NEXT(*prpp) = new;
150
    RP_PREV(*prpc) = new;
151
    line->free = prnew->next;
152
    prnew->prev = prpp->index;
153
    prnew->next = prpc->index;
154
    prpp->index = new;
155
    prpp->ptr = prnew;
156
    return 0;
157
}
158
 
159
/* Define the run-oriented device procedures. */
160
private dev_proc_copy_mono(run_copy_mono);
161
private dev_proc_copy_color(run_copy_color);
162
private dev_proc_fill_rectangle(run_fill_rectangle);
163
private dev_proc_copy_alpha(run_copy_alpha);
164
private dev_proc_strip_tile_rectangle(run_strip_tile_rectangle);
165
private dev_proc_strip_copy_rop(run_strip_copy_rop);
166
private dev_proc_get_bits_rectangle(run_get_bits_rectangle);
167
 
168
/*
169
 * Convert a memory device to run-length form.  The mdev argument should be
170
 * const, but it isn't because we need to call gx_device_white.
171
 */
172
int
173
gdev_run_from_mem(gx_device_run *rdev, gx_device_memory *mdev)
174
{
175
    int runs_per_line =
176
	(bitmap_raster(mdev->width * mdev->color_info.depth) -
177
	 sizeof(run_line)) / sizeof(run);
178
    /*
179
     * We use the scan lines of the memory device for storing runs.  We need
180
     * ceil(width / MAX_RUN_LENGTH) runs to represent a line where all
181
     * elements have the same value, +2 for the start and end runs.
182
     */
183
    int min_runs = (mdev->width + (MAX_RUN_LENGTH - 1)) / MAX_RUN_LENGTH + 2;
184
    int i;
185
    gx_color_index white = gx_device_white((gx_device *)mdev);
186
 
187
    rdev->md = *mdev;
188
    if (runs_per_line > MAX_RUNS)
189
	runs_per_line = MAX_RUNS;
190
    if (runs_per_line < min_runs)
191
	return 0;		/* just use the memory device as-is */
192
    for (i = 0; i < mdev->height; ++i) {
193
	run_line *line = RDEV_LINE(rdev, i);
194
 
195
	line->zero = white;
196
    }
197
    rdev->runs_per_line = runs_per_line;
198
    rdev->umin = 0;
199
    rdev->umax1 = mdev->height;
200
    rdev->smin = mdev->height;
201
    rdev->smax1 = 0;
202
    /* Save and replace the representation-aware rendering procedures. */
203
#define REPLACE(proc, rproc)\
204
  (rdev->save_procs.proc = dev_proc(&rdev->md, proc),\
205
   set_dev_proc(&rdev->md, proc, rproc))
206
    REPLACE(copy_mono, run_copy_mono);
207
    REPLACE(copy_color, run_copy_color);
208
    REPLACE(fill_rectangle, run_fill_rectangle);
209
    REPLACE(copy_alpha, run_copy_alpha);
210
    REPLACE(strip_tile_rectangle, run_strip_tile_rectangle);
211
    REPLACE(strip_copy_rop, run_strip_copy_rop);
212
    REPLACE(get_bits_rectangle, run_get_bits_rectangle);
213
#undef REPLACE
214
    return 0;
215
}
216
 
217
/* Convert a scan line to expanded form in place. */
218
private int
219
run_expand(gx_device_run *rdev, int y)
220
{
221
    const run_line *line = RDEV_LINE(rdev, y);
222
    const run *const data = CONST_RL_DATA(line);
223
    const_run_ptr rp;
224
    int n, x, w;
225
#if RUN_LENGTH_BITS <= 8
226
    byte length[MAX_RUNS];
227
#else
228
# if RUN_LENGTH_BITS <= 16
229
    ushort length[MAX_RUNS];
230
# else
231
    uint length[MAX_RUNS];
232
# endif
233
#endif
234
    gx_color_index value[MAX_RUNS];
235
 
236
    if (line->zero != gx_no_color_index) {
237
	rdev->save_procs.fill_rectangle((gx_device *)&rdev->md,
238
					0, y, rdev->md.width, 1, line->zero);
239
	return 0;
240
    }
241
    /* Copy the runs into local storage to avoid stepping on our own toes. */
242
    for (n = 0, RP_TO_START(rp, data); !RP_AT_END(rp);
243
	 ++n, RP_TO_NEXT(rp, data, rp)
244
	) {
245
	length[n] = RP_LENGTH(rp);
246
	value[n] = RP_VALUE(rp);
247
    }
248
    for (x = 0, n = 0; x < rdev->md.width; x += w, ++n) {
249
	w = length[n];
250
	rdev->save_procs.fill_rectangle((gx_device *)&rdev->md,
251
					x, y, w, 1, value[n]);
252
    }
253
    return 0;
254
}
255
 
256
/*
257
 * Convert a range of scan lines to standard form.
258
 */
259
private int
260
run_standardize(gx_device_run *rdev, int y, int h)
261
{
262
    int ye, iy;
263
 
264
    fit_fill_y(&rdev->md, y, h);
265
    fit_fill_h(&rdev->md, y, h);
266
    ye = y + h;
267
    if (y < rdev->smin) {
268
	if (ye > rdev->smax1)
269
	    run_standardize(rdev, rdev->smax1, ye - rdev->smax1);
270
	if (ye < rdev->smin)
271
	    ye = rdev->smin;
272
	rdev->smin = y;
273
    } else if (ye > rdev->smax1) {
274
	if (y > rdev->smax1)
275
	    y = rdev->smax1;
276
	rdev->smax1 = ye;
277
    } else
278
	return 0;
279
    for (iy = y; iy < ye; ++iy)
280
	run_expand(rdev, iy);
281
    return 0;
282
}
283
 
284
/* Trampoline rendering procedures */
285
private int
286
run_copy_mono(gx_device * dev, const byte * data, int dx, int raster,
287
	      gx_bitmap_id id, int x, int y, int w, int h,
288
	      gx_color_index zero, gx_color_index one)
289
{
290
    gx_device_run *const rdev = (gx_device_run *)dev;
291
 
292
    run_standardize(rdev, y, h);
293
    return rdev->save_procs.copy_mono((gx_device *)&rdev->md,
294
				      data, dx, raster, id,
295
				      x, y, w, h, zero, one);
296
}
297
private int
298
run_copy_color(gx_device * dev, const byte * data,
299
	       int data_x, int raster, gx_bitmap_id id,
300
	       int x, int y, int w, int h)
301
{
302
    gx_device_run *const rdev = (gx_device_run *)dev;
303
 
304
    run_standardize(rdev, y, h);
305
    return rdev->save_procs.copy_color((gx_device *)&rdev->md,
306
				       data, data_x, raster, id,
307
				       x, y, w, h);
308
}
309
private int
310
run_copy_alpha(gx_device * dev, const byte * data, int data_x, int raster,
311
	       gx_bitmap_id id, int x, int y, int w, int h,
312
	       gx_color_index color, int depth)
313
{
314
    gx_device_run *const rdev = (gx_device_run *)dev;
315
 
316
    run_standardize(rdev, y, h);
317
    return rdev->save_procs.copy_alpha((gx_device *)&rdev->md,
318
				       data, data_x, raster, id,
319
				       x, y, w, h, color, depth);
320
}
321
private int
322
run_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
323
   int x, int y, int w, int h, gx_color_index color0, gx_color_index color1,
324
				int px, int py)
325
{
326
    gx_device_run *const rdev = (gx_device_run *)dev;
327
 
328
    run_standardize(rdev, y, h);
329
    return rdev->save_procs.strip_tile_rectangle((gx_device *)&rdev->md,
330
						 tiles, x, y, w, h,
331
						 color0, color1, px, py);
332
}
333
private int
334
run_strip_copy_rop(gx_device * dev, const byte * sdata, int sourcex,
335
		   uint sraster, gx_bitmap_id id,
336
		   const gx_color_index * scolors,
337
		   const gx_strip_bitmap * textures,
338
		   const gx_color_index * tcolors,
339
		   int x, int y, int w, int h, int px, int py,
340
		   gs_logical_operation_t lop)
341
{
342
    gx_device_run *const rdev = (gx_device_run *)dev;
343
 
344
    run_standardize(rdev, y, h);
345
    return rdev->save_procs.strip_copy_rop((gx_device *)&rdev->md,
346
					   sdata, sourcex, sraster,
347
					   id, scolors, textures, tcolors,
348
					   x, y, w, h, px, py, lop);
349
}
350
private int
351
run_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
352
		       gs_get_bits_params_t * params, gs_int_rect **unread)
353
{
354
    gx_device_run *const rdev = (gx_device_run *)dev;
355
 
356
    run_standardize(rdev, prect->p.y, prect->q.y - prect->p.y);
357
    return rdev->save_procs.get_bits_rectangle((gx_device *)&rdev->md,
358
					       prect, params, unread);
359
}
360
 
361
/* Finish initializing a line.  This is a separate procedure only */
362
/* for readability. */
363
private void
364
run_line_initialize(gx_device_run *rdev, int y)
365
{
366
    run_line *line = RDEV_LINE(rdev, y);
367
    run *data = RL_DATA(line);
368
    int left = rdev->md.width;
369
    run_index index = 2;
370
    run *rcur;
371
 
372
    line->zero = gx_no_color_index;
373
    data[0].length = MAX_RUN_LENGTH;	/* see above */
374
    data[0].value = gx_no_color_index;	/* shouldn't matter */
375
    data[1].length = MAX_RUN_LENGTH;
376
    data[1].value = gx_no_color_index;
377
    data[1].next = 2;
378
    rcur = data + index;
379
    for (; left > 0; index++, rcur++, left -= MAX_RUN_LENGTH) {
380
	rcur->length = min(left, MAX_RUN_LENGTH);
381
	rcur->value = 0;
382
	rcur->prev = index - 1;
383
	rcur->next = index + 1;
384
    }
385
    rcur->next = 0;
386
    data[0].prev = index - 1;
387
    line->xcur = 0;
388
    line->rpcur.ptr = data + 2;
389
    line->rpcur.index = 2;
390
    line->free = index;
391
    for (; index < rdev->runs_per_line; ++index)
392
	data[index].next = index + 1;
393
    data[index - 1].next = 0;
394
    if (y >= rdev->umin && y < rdev->umax1) {
395
	if (y > (rdev->umin + rdev->umax1) >> 1)
396
	    rdev->umax1 = y;
397
	else
398
	    rdev->umin = y + 1;
399
    }
400
}
401
 
402
/*
403
 * Replace an interval of a line with a new value.  This is the procedure
404
 * that does all the interesting work.  We assume the line has been
405
 * initialized, and that 0 <= xo < xe <= dev->width.
406
 */
407
private int
408
run_fill_interval(run_line *line, int xo, int xe, run_value new)
409
{
410
    run *data = RL_DATA(line);
411
    int xc = line->xcur;
412
    run_ptr rpc;
413
    int x0, x1;
414
    run_ptr rp0;
415
    int code;
416
 
417
    rpc = line->rpcur;
418
 
419
    /* Find the run that contains xo. */
420
 
421
    if (xo < xc) {
422
	while (xo < xc)
423
	    RP_TO_PREV(rpc, data, rpc), xc -= RP_LENGTH(rpc);
424
    } else {
425
	while (xo >= xc + RP_LENGTH(rpc))
426
	    xc += RP_LENGTH(rpc), RP_TO_NEXT(rpc, data, rpc);
427
    }
428
 
429
    /*
430
     * Skip runs above xo that already contain the new value.
431
     * If the entire interval already has the correct value, exit.
432
     * If we skip any such runs, set xo to just above them.
433
     */
434
 
435
    for (; !RP_AT_END(rpc) && RP_VALUE(rpc) == new;
436
	 RP_TO_NEXT(rpc, data, rpc)
437
	)
438
	if ((xo = xc += RP_LENGTH(rpc)) >= xe)
439
	    return 0;
440
    x0 = xc, rp0 = rpc;
441
 
442
    /* Find the run that contains xe-1. */
443
 
444
    while (xe > xc + RP_LENGTH(rpc))
445
	xc += RP_LENGTH(rpc), RP_TO_NEXT(rpc, data, rpc);
446
 
447
    /*
448
     * Skip runs below xe that already contain the new value.
449
     * (We know that some run between xo and xe doesn't.)
450
     * If we skip any such runs, set xe to just below them.
451
     */
452
 
453
    while (RP_TO_PREV(rpc, data, rpc), RP_VALUE(rpc) == new)
454
	xe = xc -= RP_LENGTH(rpc);
455
    RP_TO_NEXT(rpc, data, rpc);
456
 
457
    /*
458
     * At this point, we know the following:
459
     *      x0 <= xo < x0 + RP_LENGTH(rp0).
460
     *      RP_VALUE(rp0) != new.
461
     *      xc <= xe-1 < xc + RP_LENGTH(rpc).
462
     *      RP_VALUE(rpc) != new.
463
     * Note that rp0 and rpc may point to the same run.
464
     */
465
 
466
    /* Split off any unaffected prefix of the run at rp0. */
467
 
468
    if (x0 < xo) {
469
	uint diff = xo - x0;
470
	run_value v0 = RP_VALUE(rp0);
471
	run_ptr rpp;
472
 
473
	RP_TO_PREV(rp0, data, rpp);
474
	if (RP_VALUE(rpp) == v0 && RP_LENGTH(rpp) + diff <= MAX_RUN_LENGTH)
475
	    RP_LENGTH(rpp) += diff;
476
	else {
477
	    code = rp_insert_prev(&rp0, data, line, &rpp);
478
	    if (code < 0)
479
		return code;
480
	    RP_LENGTH(rpp) = diff;
481
	    RP_VALUE(rpp) = v0;
482
	}
483
	RP_LENGTH(rp0) -= diff;
484
    }
485
 
486
    /* Split off any unaffected suffix of the run at rpc. */
487
 
488
    x1 = xc + RP_LENGTH(rpc);
489
    if (x1 > xe) {
490
	uint diff = x1 - xe;
491
	run_value vc = RP_VALUE(rpc);
492
	run_ptr rpn;
493
 
494
	RP_TO_NEXT(rpc, data, rpn);
495
	if (RP_VALUE(rpn) == vc && RP_LENGTH(rpn) + diff <= MAX_RUN_LENGTH)
496
	    RP_LENGTH(rpn) += diff;
497
	else {
498
	    code = rp_insert_next(&rpc, data, line, &rpn);
499
	    if (code < 0)
500
		return code;
501
	    RP_LENGTH(rpn) = diff;
502
	    RP_VALUE(rpn) = vc;
503
	}
504
	RP_LENGTH(rpc) -= diff;
505
    }
506
 
507
    /* Delete all runs from rp0 through rpc. */
508
 
509
    RP_TO_PREV(rp0, data, rp0);
510
    while (RP_NEXT(rp0) != RP_NEXT(rpc))
511
	rp_delete_next(&rp0, data, line);
512
 
513
    /*
514
     * Finally, insert new runs with the new value.
515
     * We need to check for one boundary case, namely,
516
     * xo == x0 and the next lower run has the new value.
517
     * (There's probably a way to structure the code just slightly
518
     * differently to avoid this test.)
519
     */
520
 
521
    {
522
	uint left = xe - xo;
523
 
524
	if (xo == x0 && RP_VALUE(rp0) == new &&
525
	    RP_LENGTH(rp0) + left <= MAX_RUN_LENGTH
526
	    )
527
	    RP_LENGTH(rp0) += left;
528
	else {
529
	    /*
530
	     * If we need more than one run, we divide up the length to
531
	     * create more runs with length less than MAX_RUN_LENGTH in
532
	     * order to improve the chances of a later merge.  However,
533
	     * we still guarantee that we won't create more runs than
534
	     * the minimum number required to represent the length.
535
	     */
536
	    run_length len;
537
 
538
	    if (left <= MAX_RUN_LENGTH)
539
		len = left;
540
	    else {
541
		/*len = ceil(left / ceil(left / MAX_RUN_LENGTH))*/
542
		int pieces = left + (MAX_RUN_LENGTH - 1) / MAX_RUN_LENGTH;
543
 
544
		len = (left + pieces - 1) / pieces;
545
	    }
546
	    do {
547
		run_ptr rpn;
548
 
549
		/*
550
		 * The allocation in rp_insert_next can't fail, because
551
		 * we just deleted at least as many runs as we're going
552
		 * to insert.
553
		 */
554
		rp_insert_next(&rp0, data, line, &rpn);
555
		RP_LENGTH(rpn) = min(left, len);
556
		RP_VALUE(rpn) = new;
557
	    }
558
	    while ((left -= len) > 0);
559
	}
560
    }
561
 
562
    return 0;
563
}
564
 
565
/* Replace a rectangle with a new value. */
566
private int
567
run_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
568
		   gx_color_index color)
569
{
570
    gx_device_run *const rdev = (gx_device_run *)dev;
571
    int xe, ye;
572
    int iy;
573
 
574
    fit_fill(dev, x, y, w, h);
575
    ye = y + h;
576
    /*
577
     * If the new value is white and the rectangle falls entirely within
578
     * the uninitialized region that we're keeping track of,
579
     * we can skip the entire operation.
580
     */
581
    if (y >= rdev->umin && ye <= rdev->umax1 &&
582
	color == RDEV_LINE(rdev, y)->zero
583
	)
584
	return 0;
585
 
586
    /*
587
     * Hand off any parts of the operation that fall within the area
588
     * already in standard form.
589
     */
590
    if (y < rdev->smax1 && ye > rdev->smin) {
591
	/* Some part of the operation must be handed off. */
592
	if (y < rdev->smin) {
593
	    run_fill_rectangle(dev, x, y, w, rdev->smin - y, color);
594
	    y = rdev->smin;
595
	}
596
	/* Now rdev->smin <= y < ye. */
597
	rdev->save_procs.fill_rectangle((gx_device *)&rdev->md,
598
					x, y, w, min(ye, rdev->smax1) - y,
599
					color);
600
	if (ye <= rdev->smax1)
601
	    return 0;
602
	y = rdev->smax1;
603
    }
604
    xe = x + w;
605
    for (iy = y; iy < ye; ++iy) {
606
	run_line *line = RDEV_LINE(rdev, iy);
607
 
608
	if (color != line->zero) {
609
	    if (line->zero != gx_no_color_index)
610
		run_line_initialize(rdev, iy);
611
	    if (run_fill_interval(line, x, xe, color) < 0) {
612
		/* We ran out of runs.  Convert to expanded form. */
613
		run_standardize(rdev, iy, 1);
614
		rdev->save_procs.fill_rectangle((gx_device *)&rdev->md,
615
						x, iy, w, 1, color);
616
	    }
617
	}
618
    }
619
    return 0;
620
}
621