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