1 /* Copyright (C) 1998, 2000 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gxclip.c,v 1.4.2.1.2.1 2003/01/17 00:49:03 giles Exp $ */
20 /* Implementation of (path-based) clipping */
21 #include "gx.h"
22 #include "gxdevice.h"
23 #include "gxclip.h"
24 #include "gxpath.h"
25 #include "gxcpath.h"
26 
27 /* Define whether to look for vertical clipping regions. */
28 #define CHECK_VERTICAL_CLIPPING
29 
30 /* ------ Rectangle list clipper ------ */
31 
32 /* Device for clipping with a region. */
33 /* We forward non-drawing operations, but we must be sure to intercept */
34 /* all drawing operations. */
35 private dev_proc_open_device(clip_open);
36 private dev_proc_fill_rectangle(clip_fill_rectangle);
37 private dev_proc_copy_mono(clip_copy_mono);
38 private dev_proc_copy_color(clip_copy_color);
39 private dev_proc_copy_alpha(clip_copy_alpha);
40 private dev_proc_fill_mask(clip_fill_mask);
41 private dev_proc_strip_tile_rectangle(clip_strip_tile_rectangle);
42 private dev_proc_strip_copy_rop(clip_strip_copy_rop);
43 private dev_proc_get_clipping_box(clip_get_clipping_box);
44 private dev_proc_get_bits_rectangle(clip_get_bits_rectangle);
45 
46 /* The device descriptor. */
47 private const gx_device_clip gs_clip_device =
48 {std_device_std_body(gx_device_clip, 0, "clipper",
49 		     0, 0, 1, 1),
50  {clip_open,
51   gx_forward_get_initial_matrix,
52   gx_default_sync_output,
53   gx_default_output_page,
54   gx_default_close_device,
55   gx_forward_map_rgb_color,
56   gx_forward_map_color_rgb,
57   clip_fill_rectangle,
58   gx_default_tile_rectangle,
59   clip_copy_mono,
60   clip_copy_color,
61   gx_default_draw_line,
62   gx_default_get_bits,
63   gx_forward_get_params,
64   gx_forward_put_params,
65   gx_forward_map_cmyk_color,
66   gx_forward_get_xfont_procs,
67   gx_forward_get_xfont_device,
68   gx_forward_map_rgb_alpha_color,
69   gx_forward_get_page_device,
70   gx_forward_get_alpha_bits,
71   clip_copy_alpha,
72   gx_forward_get_band,
73   gx_default_copy_rop,
74   gx_default_fill_path,
75   gx_default_stroke_path,
76   clip_fill_mask,
77   gx_default_fill_trapezoid,
78   gx_default_fill_parallelogram,
79   gx_default_fill_triangle,
80   gx_default_draw_thin_line,
81   gx_default_begin_image,
82   gx_default_image_data,
83   gx_default_end_image,
84   clip_strip_tile_rectangle,
85   clip_strip_copy_rop,
86   clip_get_clipping_box,
87   gx_default_begin_typed_image,
88   clip_get_bits_rectangle,
89   gx_forward_map_color_rgb_alpha,
90   gx_no_create_compositor,
91   gx_forward_get_hardware_params,
92   gx_default_text_begin,
93   gx_default_finish_copydevice
94  }
95 };
96 
97 /* Make a clipping device. */
98 void
gx_make_clip_translate_device(gx_device_clip * dev,const gx_clip_list * list,int tx,int ty,gs_memory_t * mem)99 gx_make_clip_translate_device(gx_device_clip * dev, const gx_clip_list * list,
100 			      int tx, int ty, gs_memory_t *mem)
101 {
102     gx_device_init((gx_device *)dev, (const gx_device *)&gs_clip_device,
103 		   mem, true);
104     dev->list = *list;
105     dev->translation.x = tx;
106     dev->translation.y = ty;
107 }
108 void
gx_make_clip_path_device(gx_device_clip * dev,const gx_clip_path * pcpath)109 gx_make_clip_path_device(gx_device_clip * dev, const gx_clip_path * pcpath)
110 {
111     gx_make_clip_device(dev, gx_cpath_list(pcpath));
112 }
113 
114 /* Define debugging statistics for the clipping loops. */
115 #ifdef DEBUG
116 struct stats_clip_s {
117     long
118          loops, out, in_y, in, in1, down, up, x, no_x;
119 } stats_clip;
120 
121 private const uint clip_interval = 10000;
122 
123 # define INCR(v) (++(stats_clip.v))
124 # define INCR_THEN(v, e) (INCR(v), (e))
125 #else
126 # define INCR(v) DO_NOTHING
127 # define INCR_THEN(v, e) (e)
128 #endif
129 
130 /*
131  * Enumerate the rectangles of the x,w,y,h argument that fall within
132  * the clipping region.
133  */
134 private int
clip_enumerate_rest(gx_device_clip * rdev,int x,int y,int xe,int ye,int (* process)(P5 (clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)),clip_callback_data_t * pccd)135 clip_enumerate_rest(gx_device_clip * rdev,
136 		    int x, int y, int xe, int ye,
137 		    int (*process)(P5(clip_callback_data_t * pccd,
138 				      int xc, int yc, int xec, int yec)),
139 		    clip_callback_data_t * pccd)
140 {
141     gx_clip_rect *rptr = rdev->current;		/* const within algorithm */
142     int yc;
143     int code;
144 
145 #ifdef DEBUG
146     if (INCR(loops) % clip_interval == 0 && gs_debug_c('q')) {
147 	dprintf5("[q]loops=%ld out=%ld in_y=%ld in=%ld in1=%ld\n",
148 		 stats_clip.loops, stats_clip.out, stats_clip.in,
149 		 stats_clip.in_y, stats_clip.in1);
150 	dprintf4("[q]   down=%ld up=%ld x=%ld no_x=%ld\n",
151 		 stats_clip.down, stats_clip.up, stats_clip.x,
152 		 stats_clip.no_x);
153     }
154 #endif
155     pccd->x = x, pccd->y = y;
156     pccd->w = xe - x, pccd->h = ye - y;
157     /*
158      * Warp the cursor forward or backward to the first rectangle row
159      * that could include a given y value.  Assumes rptr is set, and
160      * updates it.  Specifically, after this loop, either rptr == 0 (if
161      * the y value is greater than all y values in the list), or y <
162      * rptr->ymax and either rptr->prev == 0 or y >= rptr->prev->ymax.
163      * Note that y <= rptr->ymin is possible.
164      *
165      * In the first case below, the while loop is safe because if there
166      * is more than one rectangle, there is a 'stopper' at the end of
167      * the list.
168      */
169     if (y >= rptr->ymax) {
170 	if ((rptr = rptr->next) != 0)
171 	    while (INCR_THEN(up, y >= rptr->ymax))
172 		rptr = rptr->next;
173     } else
174 	while (rptr->prev != 0 && y < rptr->prev->ymax)
175 	    INCR_THEN(down, rptr = rptr->prev);
176     if (rptr == 0 || (yc = rptr->ymin) >= ye) {
177 	INCR(out);
178 	if (rdev->list.count > 1)
179 	    rdev->current =
180 		(rptr != 0 ? rptr :
181 		 y >= rdev->current->ymax ? rdev->list.tail :
182 		 rdev->list.head);
183 	return 0;
184     }
185     rdev->current = rptr;
186     if (yc < y)
187 	yc = y;
188 
189     do {
190 	const int ymax = rptr->ymax;
191 	int yec = min(ymax, ye);
192 
193 	if_debug2('Q', "[Q]yc=%d yec=%d\n", yc, yec);
194 	do {
195 	    int xc = rptr->xmin;
196 	    int xec = rptr->xmax;
197 
198 	    if (xc < x)
199 		xc = x;
200 	    if (xec > xe)
201 		xec = xe;
202 	    if (xec > xc) {
203 		clip_rect_print('Q', "match", rptr);
204 		if_debug2('Q', "[Q]xc=%d xec=%d\n", xc, xec);
205 		INCR(x);
206 /*
207  * Conditionally look ahead to detect unclipped vertical strips.  This is
208  * really only valuable for 90 degree rotated images or (nearly-)vertical
209  * lines with convex clipping regions; if we ever change images to use
210  * source buffering and destination-oriented enumeration, we could probably
211  * take out the code here with no adverse effects.
212  */
213 #ifdef CHECK_VERTICAL_CLIPPING
214 		if (xec - xc == pccd->w) {	/* full width */
215 		    /* Look ahead for a vertical swath. */
216 		    while ((rptr = rptr->next) != 0 &&
217 			   rptr->ymin == yec &&
218 			   rptr->ymax <= ye &&
219 			   rptr->xmin <= x &&
220 			   rptr->xmax >= xe
221 			   )
222 			yec = rptr->ymax;
223 		} else
224 		    rptr = rptr->next;
225 #else
226 		rptr = rptr->next;
227 #endif
228 		code = process(pccd, xc, yc, xec, yec);
229 		if (code < 0)
230 		    return code;
231 	    } else {
232 		INCR_THEN(no_x, rptr = rptr->next);
233 	    }
234 	    if (rptr == 0)
235 		return 0;
236 	}
237 	while (rptr->ymax == ymax);
238     } while ((yc = rptr->ymin) < ye);
239     return 0;
240 }
241 
242 private int
clip_enumerate(gx_device_clip * rdev,int x,int y,int w,int h,int (* process)(P5 (clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)),clip_callback_data_t * pccd)243 clip_enumerate(gx_device_clip * rdev, int x, int y, int w, int h,
244 	       int (*process)(P5(clip_callback_data_t * pccd,
245 				 int xc, int yc, int xec, int yec)),
246 	       clip_callback_data_t * pccd)
247 {
248     int xe, ye;
249     const gx_clip_rect *rptr = rdev->current;
250 
251     if (w <= 0 || h <= 0)
252 	return 0;
253     pccd->tdev = rdev->target;
254     x += rdev->translation.x;
255     xe = x + w;
256     y += rdev->translation.y;
257     ye = y + h;
258     /* Check for the region being entirely within the current rectangle. */
259     if (y >= rptr->ymin && ye <= rptr->ymax &&
260 	x >= rptr->xmin && xe <= rptr->xmax
261 	) {
262 	pccd->x = x, pccd->y = y, pccd->w = w, pccd->h = h;
263 	return INCR_THEN(in, process(pccd, x, y, xe, ye));
264     }
265     return clip_enumerate_rest(rdev, x, y, xe, ye, process, pccd);
266 }
267 
268 /* Open a clipping device */
269 private int
clip_open(gx_device * dev)270 clip_open(gx_device * dev)
271 {
272     gx_device_clip *const rdev = (gx_device_clip *) dev;
273     gx_device *tdev = rdev->target;
274 
275     /* Initialize the cursor. */
276     rdev->current =
277 	(rdev->list.head == 0 ? &rdev->list.single : rdev->list.head);
278     rdev->color_info = tdev->color_info;
279     rdev->cached_colors = tdev->cached_colors;
280     rdev->width = tdev->width;
281     rdev->height = tdev->height;
282     gx_device_copy_color_procs(dev, tdev);
283     rdev->clipping_box_set = false;
284     return 0;
285 }
286 
287 /* Fill a rectangle */
288 int
clip_call_fill_rectangle(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)289 clip_call_fill_rectangle(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
290 {
291     return (*dev_proc(pccd->tdev, fill_rectangle))
292 	(pccd->tdev, xc, yc, xec - xc, yec - yc, pccd->color[0]);
293 }
294 private int
clip_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)295 clip_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
296 		    gx_color_index color)
297 {
298     gx_device_clip *rdev = (gx_device_clip *) dev;
299     clip_callback_data_t ccdata;
300     /* We handle the fastest cases in-line here. */
301     gx_device *tdev = rdev->target;
302     /*const*/ gx_clip_rect *rptr = rdev->current;
303     int xe, ye;
304 
305     if (w <= 0 || h <= 0)
306 	return 0;
307     x += rdev->translation.x;
308     xe = x + w;
309     y += rdev->translation.y;
310     ye = y + h;
311     /* We open-code the most common cases here. */
312     if ((y >= rptr->ymin && ye <= rptr->ymax) ||
313 	((rptr = rptr->next) != 0 &&
314 	 y >= rptr->ymin && ye <= rptr->ymax)
315 	) {
316 	rdev->current = rptr;	/* may be redundant, but awkward to avoid */
317 	INCR(in_y);
318 	if (x >= rptr->xmin && xe <= rptr->xmax) {
319 	    INCR(in);
320 	    return dev_proc(tdev, fill_rectangle)(tdev, x, y, w, h, color);
321 	}
322 	else if ((rptr->prev == 0 || rptr->prev->ymax != rptr->ymax) &&
323 		 (rptr->next == 0 || rptr->next->ymax != rptr->ymax)
324 		 ) {
325 	    INCR(in1);
326 	    if (x < rptr->xmin)
327 		x = rptr->xmin;
328 	    if (xe > rptr->xmax)
329 		xe = rptr->xmax;
330 	    return
331 		(x >= xe ? 0 :
332 		 dev_proc(tdev, fill_rectangle)(tdev, x, y, xe - x, h, color));
333 	}
334     }
335     ccdata.tdev = tdev;
336     ccdata.color[0] = color;
337     return clip_enumerate_rest(rdev, x, y, xe, ye,
338 			       clip_call_fill_rectangle, &ccdata);
339 }
340 
341 /* Copy a monochrome rectangle */
342 int
clip_call_copy_mono(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)343 clip_call_copy_mono(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
344 {
345     return (*dev_proc(pccd->tdev, copy_mono))
346 	(pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
347 	 pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
348 	 xc, yc, xec - xc, yec - yc, pccd->color[0], pccd->color[1]);
349 }
350 private int
clip_copy_mono(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color0,gx_color_index color1)351 clip_copy_mono(gx_device * dev,
352 	       const byte * data, int sourcex, int raster, gx_bitmap_id id,
353 	       int x, int y, int w, int h,
354 	       gx_color_index color0, gx_color_index color1)
355 {
356     gx_device_clip *rdev = (gx_device_clip *) dev;
357     clip_callback_data_t ccdata;
358     /* We handle the fastest case in-line here. */
359     gx_device *tdev = rdev->target;
360     const gx_clip_rect *rptr = rdev->current;
361     int xe, ye;
362 
363     if (w <= 0 || h <= 0)
364 	return 0;
365     x += rdev->translation.x;
366     xe = x + w;
367     y += rdev->translation.y;
368     ye = y + h;
369     if (y >= rptr->ymin && ye <= rptr->ymax) {
370 	INCR(in_y);
371 	if (x >= rptr->xmin && xe <= rptr->xmax) {
372 	    INCR(in);
373 	    return dev_proc(tdev, copy_mono)
374 		(tdev, data, sourcex, raster, id, x, y, w, h, color0, color1);
375 	}
376     }
377     ccdata.tdev = tdev;
378     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
379     ccdata.color[0] = color0, ccdata.color[1] = color1;
380     return clip_enumerate_rest(rdev, x, y, xe, ye,
381 			       clip_call_copy_mono, &ccdata);
382 }
383 
384 /* Copy a color rectangle */
385 int
clip_call_copy_color(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)386 clip_call_copy_color(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
387 {
388     return (*dev_proc(pccd->tdev, copy_color))
389 	(pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
390 	 pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
391 	 xc, yc, xec - xc, yec - yc);
392 }
393 private int
clip_copy_color(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)394 clip_copy_color(gx_device * dev,
395 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
396 		int x, int y, int w, int h)
397 {
398     gx_device_clip *rdev = (gx_device_clip *) dev;
399     clip_callback_data_t ccdata;
400 
401     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
402     return clip_enumerate(rdev, x, y, w, h, clip_call_copy_color, &ccdata);
403 }
404 
405 /* Copy a rectangle with alpha */
406 int
clip_call_copy_alpha(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)407 clip_call_copy_alpha(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
408 {
409     return (*dev_proc(pccd->tdev, copy_alpha))
410 	(pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
411 	 pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
412 	 xc, yc, xec - xc, yec - yc, pccd->color[0], pccd->depth);
413 }
414 private int
clip_copy_alpha(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color,int depth)415 clip_copy_alpha(gx_device * dev,
416 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
417 		int x, int y, int w, int h,
418 		gx_color_index color, int depth)
419 {
420     gx_device_clip *rdev = (gx_device_clip *) dev;
421     clip_callback_data_t ccdata;
422 
423     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
424     ccdata.color[0] = color, ccdata.depth = depth;
425     return clip_enumerate(rdev, x, y, w, h, clip_call_copy_alpha, &ccdata);
426 }
427 
428 /* Fill a region defined by a mask. */
429 int
clip_call_fill_mask(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)430 clip_call_fill_mask(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
431 {
432     return (*dev_proc(pccd->tdev, fill_mask))
433 	(pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
434 	 pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
435 	 xc, yc, xec - xc, yec - yc, pccd->pdcolor, pccd->depth,
436 	 pccd->lop, NULL);
437 }
438 private int
clip_fill_mask(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,const gx_drawing_color * pdcolor,int depth,gs_logical_operation_t lop,const gx_clip_path * pcpath)439 clip_fill_mask(gx_device * dev,
440 	       const byte * data, int sourcex, int raster, gx_bitmap_id id,
441 	       int x, int y, int w, int h,
442 	       const gx_drawing_color * pdcolor, int depth,
443 	       gs_logical_operation_t lop, const gx_clip_path * pcpath)
444 {
445     gx_device_clip *rdev = (gx_device_clip *) dev;
446     clip_callback_data_t ccdata;
447 
448     if (pcpath != 0)
449 	return gx_default_fill_mask(dev, data, sourcex, raster, id,
450 				    x, y, w, h, pdcolor, depth, lop,
451 				    pcpath);
452     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
453     ccdata.pdcolor = pdcolor, ccdata.depth = depth, ccdata.lop = lop;
454     return clip_enumerate(rdev, x, y, w, h, clip_call_fill_mask, &ccdata);
455 }
456 
457 /* Strip-tile a rectangle. */
458 int
clip_call_strip_tile_rectangle(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)459 clip_call_strip_tile_rectangle(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
460 {
461     return (*dev_proc(pccd->tdev, strip_tile_rectangle))
462 	(pccd->tdev, pccd->tiles, xc, yc, xec - xc, yec - yc,
463 	 pccd->color[0], pccd->color[1], pccd->phase.x, pccd->phase.y);
464 }
465 private int
clip_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 phase_x,int phase_y)466 clip_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
467 			  int x, int y, int w, int h,
468      gx_color_index color0, gx_color_index color1, int phase_x, int phase_y)
469 {
470     gx_device_clip *rdev = (gx_device_clip *) dev;
471     clip_callback_data_t ccdata;
472 
473     ccdata.tiles = tiles;
474     ccdata.color[0] = color0, ccdata.color[1] = color1;
475     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y;
476     return clip_enumerate(rdev, x, y, w, h, clip_call_strip_tile_rectangle, &ccdata);
477 }
478 
479 /* Copy a rectangle with RasterOp and strip texture. */
480 int
clip_call_strip_copy_rop(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec)481 clip_call_strip_copy_rop(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
482 {
483     return (*dev_proc(pccd->tdev, strip_copy_rop))
484 	(pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
485 	 pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
486 	 pccd->scolors, pccd->textures, pccd->tcolors,
487 	 xc, yc, xec - xc, yec - yc, pccd->phase.x, pccd->phase.y,
488 	 pccd->lop);
489 }
490 private int
clip_strip_copy_rop(gx_device * dev,const byte * sdata,int sourcex,uint raster,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 phase_x,int phase_y,gs_logical_operation_t lop)491 clip_strip_copy_rop(gx_device * dev,
492 	      const byte * sdata, int sourcex, uint raster, gx_bitmap_id id,
493 		    const gx_color_index * scolors,
494 	   const gx_strip_bitmap * textures, const gx_color_index * tcolors,
495 		    int x, int y, int w, int h,
496 		    int phase_x, int phase_y, gs_logical_operation_t lop)
497 {
498     gx_device_clip *rdev = (gx_device_clip *) dev;
499     clip_callback_data_t ccdata;
500 
501     ccdata.data = sdata, ccdata.sourcex = sourcex, ccdata.raster = raster;
502     ccdata.scolors = scolors, ccdata.textures = textures,
503 	ccdata.tcolors = tcolors;
504     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y, ccdata.lop = lop;
505     return clip_enumerate(rdev, x, y, w, h, clip_call_strip_copy_rop, &ccdata);
506 }
507 
508 /* Get the (outer) clipping box, in client coordinates. */
509 private void
clip_get_clipping_box(gx_device * dev,gs_fixed_rect * pbox)510 clip_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
511 {
512     gx_device_clip *const rdev = (gx_device_clip *) dev;
513 
514     if (!rdev->clipping_box_set) {
515 	gx_device *tdev = rdev->target;
516 	gs_fixed_rect tbox;
517 
518 	(*dev_proc(tdev, get_clipping_box)) (tdev, &tbox);
519 	if (rdev->list.count != 0) {
520 	    gs_fixed_rect cbox;
521 
522 	    if (rdev->list.count == 1) {
523 		cbox.p.x = int2fixed(rdev->list.single.xmin);
524 		cbox.p.y = int2fixed(rdev->list.single.ymin);
525 		cbox.q.x = int2fixed(rdev->list.single.xmax);
526 		cbox.q.y = int2fixed(rdev->list.single.ymax);
527 	    } else {
528 		/* The head and tail elements are dummies.... */
529 		cbox.p.x = int2fixed(rdev->list.xmin);
530 		cbox.p.y = int2fixed(rdev->list.head->next->ymin);
531 		cbox.q.x = int2fixed(rdev->list.xmax);
532 		cbox.q.y = int2fixed(rdev->list.tail->prev->ymax);
533 	    }
534 	    rect_intersect(tbox, cbox);
535 	}
536 	if (rdev->translation.x | rdev->translation.y) {
537 	    fixed tx = int2fixed(rdev->translation.x),
538 		ty = int2fixed(rdev->translation.y);
539 
540 	    if (tbox.p.x != min_fixed)
541 		tbox.p.x -= tx;
542 	    if (tbox.p.y != min_fixed)
543 		tbox.p.y -= ty;
544 	    if (tbox.q.x != max_fixed)
545 		tbox.q.x -= tx;
546 	    if (tbox.q.y != max_fixed)
547 		tbox.q.y -= ty;
548 	}
549 	rdev->clipping_box = tbox;
550 	rdev->clipping_box_set = true;
551     }
552     *pbox = rdev->clipping_box;
553 }
554 
555 /* Get bits back from the device. */
556 private int
clip_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)557 clip_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
558 			gs_get_bits_params_t * params, gs_int_rect ** unread)
559 {
560     gx_device_clip *rdev = (gx_device_clip *) dev;
561     gx_device *tdev = rdev->target;
562     int tx = rdev->translation.x, ty = rdev->translation.y;
563     gs_int_rect rect;
564     int code;
565 
566     rect.p.x = prect->p.x - tx, rect.p.y = prect->p.y - ty;
567     rect.q.x = prect->q.x - tx, rect.q.y = prect->q.y - ty;
568     code = (*dev_proc(tdev, get_bits_rectangle))
569 	(tdev, &rect, params, unread);
570     if (code > 0) {
571 	/* Adjust unread rectangle coordinates */
572 	gs_int_rect *list = *unread;
573 	int i;
574 
575 	for (i = 0; i < code; ++list, ++i) {
576 	    list->p.x += tx, list->p.y += ty;
577 	    list->q.x += tx, list->q.y += ty;
578 	}
579     }
580     return code;
581 }
582