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
rp_delete_next(run_ptr * prpc,run * data,run_line * line)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
rp_insert_next(run_ptr * prpc,run * data,run_line * line,run_ptr * prpn)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
rp_insert_prev(run_ptr * prpc,run * data,run_line * line,run_ptr * prpp)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
gdev_run_from_mem(gx_device_run * rdev,gx_device_memory * mdev)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
run_expand(gx_device_run * rdev,int y)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
run_standardize(gx_device_run * rdev,int y,int h)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
run_copy_mono(gx_device * dev,const byte * data,int dx,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index zero,gx_color_index one)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
run_copy_color(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int w,int h)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
run_copy_alpha(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color,int depth)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
run_strip_tile_rectangle(gx_device * dev,const gx_strip_bitmap * tiles,int x,int y,int w,int h,gx_color_index color0,gx_color_index color1,int px,int py)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
run_strip_copy_rop(gx_device * dev,const byte * sdata,int sourcex,uint sraster,gx_bitmap_id id,const gx_color_index * scolors,const gx_strip_bitmap * textures,const gx_color_index * tcolors,int x,int y,int w,int h,int px,int py,gs_logical_operation_t lop)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
run_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)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
run_line_initialize(gx_device_run * rdev,int y)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
run_fill_interval(run_line * line,int xo,int xe,run_value new)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
run_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)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
622