xref: /plan9/sys/src/cmd/gs/src/gdevmrun.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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