xref: /plan9/sys/src/cmd/gs/src/gdevbbox.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1996, 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: gdevbbox.c,v 1.23 2005/03/14 18:08:36 dan Exp $ */
18 /* Device for tracking bounding box */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsparam.h"
24 #include "gxdevice.h"
25 #include "gsdevice.h"		/* requires gsmatrix.h */
26 #include "gdevbbox.h"
27 #include "gxdcolor.h"		/* for gx_device_black/white */
28 #include "gxiparam.h"		/* for image source size */
29 #include "gxistate.h"
30 #include "gxpaint.h"
31 #include "gxpath.h"
32 #include "gxcpath.h"
33 
34 /* GC descriptor */
35 public_st_device_bbox();
36 
37 /* Device procedures */
38 private dev_proc_open_device(bbox_open_device);
39 private dev_proc_close_device(bbox_close_device);
40 private dev_proc_output_page(bbox_output_page);
41 private dev_proc_fill_rectangle(bbox_fill_rectangle);
42 private dev_proc_copy_mono(bbox_copy_mono);
43 private dev_proc_copy_color(bbox_copy_color);
44 private dev_proc_get_params(bbox_get_params);
45 private dev_proc_put_params(bbox_put_params);
46 private dev_proc_copy_alpha(bbox_copy_alpha);
47 private dev_proc_fill_path(bbox_fill_path);
48 private dev_proc_stroke_path(bbox_stroke_path);
49 private dev_proc_fill_mask(bbox_fill_mask);
50 private dev_proc_fill_trapezoid(bbox_fill_trapezoid);
51 private dev_proc_fill_parallelogram(bbox_fill_parallelogram);
52 private dev_proc_fill_triangle(bbox_fill_triangle);
53 private dev_proc_draw_thin_line(bbox_draw_thin_line);
54 private dev_proc_strip_tile_rectangle(bbox_strip_tile_rectangle);
55 private dev_proc_strip_copy_rop(bbox_strip_copy_rop);
56 private dev_proc_begin_typed_image(bbox_begin_typed_image);
57 private dev_proc_create_compositor(bbox_create_compositor);
58 private dev_proc_text_begin(bbox_text_begin);
59 
60 /* The device prototype */
61 /*
62  * Normally this would be private, but if the device is going to be used
63  * stand-alone, it has to be public.
64  */
65 /*private */ const
66 /*
67  * The bbox device sets the resolution to some value R (currently 4000), and
68  * the page size in device pixels to slightly smaller than the largest
69  * representable values (around 500K), leaving a little room for stroke
70  * widths, rounding, etc.  If an input file (or the command line) resets the
71  * resolution to a value R' > R, the page size in pixels will get multiplied
72  * by R'/R, and will thereby exceed the representable range, causing a
73  * limitcheck.  That is why the bbox device must set the resolution to a
74  * value larger than that of any real device.  A consequence of this is that
75  * the page size in inches is limited to the maximum representable pixel
76  * size divided by R, which gives a limit of about 120" in each dimension.
77  */
78 #define MAX_COORD (max_int_in_fixed - 1000)
79 #define MAX_RESOLUTION 4000
80 gx_device_bbox gs_bbox_device =
81 {
82     /*
83      * Define the device as 8-bit gray scale to avoid computing halftones.
84      */
85     std_device_dci_body(gx_device_bbox, 0, "bbox",
86 			MAX_COORD, MAX_COORD,
87 			MAX_RESOLUTION, MAX_RESOLUTION,
88 			1, 8, 255, 0, 256, 1),
89     {bbox_open_device,
90      gx_upright_get_initial_matrix,
91      NULL,			/* sync_output */
92      bbox_output_page,
93      bbox_close_device,
94      gx_default_gray_map_rgb_color,
95      gx_default_gray_map_color_rgb,
96      bbox_fill_rectangle,
97      NULL,			/* tile_rectangle */
98      bbox_copy_mono,
99      bbox_copy_color,
100      NULL,			/* draw_line */
101      NULL,			/* get_bits */
102      bbox_get_params,
103      bbox_put_params,
104      gx_default_map_cmyk_color,
105      NULL,			/* get_xfont_procs */
106      NULL,			/* get_xfont_device */
107      gx_default_map_rgb_alpha_color,
108      gx_page_device_get_page_device,
109      NULL,			/* get_alpha_bits */
110      bbox_copy_alpha,
111      NULL,			/* get_band */
112      NULL,			/* copy_rop */
113      bbox_fill_path,
114      bbox_stroke_path,
115      bbox_fill_mask,
116      bbox_fill_trapezoid,
117      bbox_fill_parallelogram,
118      bbox_fill_triangle,
119      bbox_draw_thin_line,
120      gx_default_begin_image,
121      NULL,			/* image_data */
122      NULL,			/* end_image */
123      bbox_strip_tile_rectangle,
124      bbox_strip_copy_rop,
125      NULL,			/* get_clipping_box */
126      bbox_begin_typed_image,
127      NULL,			/* get_bits_rectangle */
128      gx_default_map_color_rgb_alpha,
129      bbox_create_compositor,
130      NULL,			/* get_hardware_params */
131      bbox_text_begin,
132      NULL,			/* finish_copydevice */
133      NULL,			/* begin_transparency_group */
134      NULL,			/* end_transparency_group */
135      NULL,			/* begin_transparency_mask */
136      NULL,			/* end_transparency_mask */
137      NULL,			/* discard_transparency_layer */
138      NULL,			/* get_color_mapping_procs */
139      NULL,			/* get_color_comp_index */
140      NULL,			/* encode_color */
141      NULL			/* decode_color */
142     },
143     0,				/* target */
144     1,				/*true *//* free_standing */
145     1				/*true *//* forward_open_close */
146 };
147 
148 #undef MAX_COORD
149 #undef MAX_RESOLUTION
150 
151 /* Default box procedures */
152 
153 bool
bbox_default_init_box(void * pdata)154 bbox_default_init_box(void *pdata)
155 {
156     gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
157     gs_fixed_rect *const pr = &bdev->bbox;
158 
159     pr->p.x = pr->p.y = max_fixed;
160     pr->q.x = pr->q.y = min_fixed;
161     return bdev->white != bdev->transparent;
162 }
163 #define BBOX_INIT_BOX(bdev)\
164   bdev->box_procs.init_box(bdev->box_proc_data)
165 
166 void
bbox_default_get_box(const void * pdata,gs_fixed_rect * pbox)167 bbox_default_get_box(const void *pdata, gs_fixed_rect *pbox)
168 {
169     const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
170 
171     *pbox = bdev->bbox;
172 }
173 #define BBOX_GET_BOX(bdev, pbox)\
174     bdev->box_procs.get_box(bdev->box_proc_data, pbox);
175 
176 void
bbox_default_add_rect(void * pdata,fixed x0,fixed y0,fixed x1,fixed y1)177 bbox_default_add_rect(void *pdata, fixed x0, fixed y0, fixed x1, fixed y1)
178 {
179     gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
180     gs_fixed_rect *const pr = &bdev->bbox;
181 
182     if (x0 < pr->p.x)
183 	pr->p.x = x0;
184     if (y0 < pr->p.y)
185 	pr->p.y = y0;
186     if (x1 > pr->q.x)
187 	pr->q.x = x1;
188     if (y1 > pr->q.y)
189 	pr->q.y = y1;
190 }
191 #define BBOX_ADD_RECT(bdev, x0, y0, x1, y1)\
192     bdev->box_procs.add_rect(bdev->box_proc_data, x0, y0, x1, y1)
193 #define BBOX_ADD_INT_RECT(bdev, x0, y0, x1, y1)\
194     BBOX_ADD_RECT(bdev, int2fixed(x0), int2fixed(y0), int2fixed(x1),\
195 		  int2fixed(y1))
196 
197 bool
bbox_default_in_rect(const void * pdata,const gs_fixed_rect * pbox)198 bbox_default_in_rect(const void *pdata, const gs_fixed_rect *pbox)
199 {
200     const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
201 
202     return rect_within(*pbox, bdev->bbox);
203 }
204 #define BBOX_IN_RECT(bdev, pbox)\
205     bdev->box_procs.in_rect(bdev->box_proc_data, pbox)
206 
207 private const gx_device_bbox_procs_t box_procs_default = {
208     bbox_default_init_box, bbox_default_get_box, bbox_default_add_rect,
209     bbox_default_in_rect
210 };
211 
212 #define RECT_IS_PAGE(dev, x, y, w, h)\
213   (x <= 0 && y <= 0 && x + w >= dev->width && y + h >= dev->height)
214 
215      /* ---------------- Open/close/page ---------------- */
216 
217 /* Copy device parameters back from the target. */
218 private void
bbox_copy_params(gx_device_bbox * bdev,bool remap_colors)219 bbox_copy_params(gx_device_bbox * bdev, bool remap_colors)
220 {
221     gx_device *tdev = bdev->target;
222 
223     if (tdev != 0)
224 	gx_device_copy_params((gx_device *)bdev, tdev);
225     if (remap_colors) {
226 	bdev->black = gx_device_black((gx_device *)bdev);
227 	bdev->white = gx_device_white((gx_device *)bdev);
228 	bdev->transparent =
229 	    (bdev->white_is_opaque ? gx_no_color_index : bdev->white);
230     }
231 }
232 
233 #define GX_DC_IS_TRANSPARENT(pdevc, bdev)\
234   (gx_dc_pure_color(pdevc) == (bdev)->transparent && gx_dc_is_pure(pdevc))
235 
236 private int
bbox_close_device(gx_device * dev)237 bbox_close_device(gx_device * dev)
238 {
239     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
240     gx_device *tdev = bdev->target;
241 
242     if (bdev->box_procs.init_box != box_procs_default.init_box) {
243 	/*
244 	 * This device was created as a wrapper for a compositor.
245 	 * Just free the devices.
246 	 */
247 	int code = (bdev->forward_open_close ? gs_closedevice(tdev) : 0);
248 
249 	gs_free_object(dev->memory, dev, "bbox_close_device(composite)");
250 	return code;
251     } else {
252 	return (tdev && bdev->forward_open_close ? gs_closedevice(tdev) : 0);
253     }
254 }
255 
256 /* Initialize a bounding box device. */
257 void
gx_device_bbox_init(gx_device_bbox * dev,gx_device * target,gs_memory_t * mem)258 gx_device_bbox_init(gx_device_bbox * dev, gx_device * target, gs_memory_t *mem)
259 {
260     gx_device_init((gx_device *) dev, (const gx_device *)&gs_bbox_device,
261 		   (target ? target->memory : mem), true);
262     if (target) {
263         gx_device_forward_fill_in_procs((gx_device_forward *) dev);
264 	set_dev_proc(dev, get_initial_matrix, gx_forward_get_initial_matrix);
265 	set_dev_proc(dev, map_rgb_color, gx_forward_map_rgb_color);
266 	set_dev_proc(dev, map_color_rgb, gx_forward_map_color_rgb);
267 	set_dev_proc(dev, map_cmyk_color, gx_forward_map_cmyk_color);
268 	set_dev_proc(dev, map_rgb_alpha_color, gx_forward_map_rgb_alpha_color);
269 	set_dev_proc(dev, get_color_mapping_procs, gx_forward_get_color_mapping_procs);
270 	set_dev_proc(dev, get_color_comp_index, gx_forward_get_color_comp_index);
271 	set_dev_proc(dev, encode_color, gx_forward_encode_color);
272 	set_dev_proc(dev, decode_color, gx_forward_decode_color);
273 	set_dev_proc(dev, pattern_manage, gx_forward_pattern_manage);
274 	set_dev_proc(dev, fill_rectangle_hl_color, gx_forward_fill_rectangle_hl_color);
275 	set_dev_proc(dev, include_color_space, gx_forward_include_color_space);
276 	set_dev_proc(dev, update_spot_equivalent_colors,
277 				gx_forward_update_spot_equivalent_colors);
278 	set_dev_proc(dev, get_page_device, gx_forward_get_page_device);
279 	gx_device_set_target((gx_device_forward *)dev, target);
280     } else {
281 	gx_device_fill_in_procs((gx_device *)dev);
282         gx_device_forward_fill_in_procs((gx_device_forward *) dev);
283     }
284     dev->box_procs = box_procs_default;
285     dev->box_proc_data = dev;
286     bbox_copy_params(dev, false);
287     dev->free_standing = false;	/* being used as a component */
288 }
289 
290 /* Set whether a bounding box device propagates open/close to its target. */
291 void
gx_device_bbox_fwd_open_close(gx_device_bbox * dev,bool forward_open_close)292 gx_device_bbox_fwd_open_close(gx_device_bbox * dev, bool forward_open_close)
293 {
294     dev->forward_open_close = forward_open_close;
295 }
296 
297 /* Set whether a bounding box device considers white to be opaque. */
298 void
gx_device_bbox_set_white_opaque(gx_device_bbox * bdev,bool white_is_opaque)299 gx_device_bbox_set_white_opaque(gx_device_bbox *bdev, bool white_is_opaque)
300 {
301     bdev->white_is_opaque = white_is_opaque;
302     bdev->transparent =
303 	(bdev->white_is_opaque ? gx_no_color_index : bdev->white);
304 }
305 
306 /* Release a bounding box device. */
307 void
gx_device_bbox_release(gx_device_bbox * dev)308 gx_device_bbox_release(gx_device_bbox *dev)
309 {
310     /* Just release the reference to the target. */
311     gx_device_set_target((gx_device_forward *)dev, NULL);
312 }
313 
314 /* Read back the bounding box in 1/72" units. */
315 void
gx_device_bbox_bbox(gx_device_bbox * dev,gs_rect * pbbox)316 gx_device_bbox_bbox(gx_device_bbox * dev, gs_rect * pbbox)
317 {
318     gs_fixed_rect bbox;
319 
320     BBOX_GET_BOX(dev, &bbox);
321     if (bbox.p.x > bbox.q.x || bbox.p.y > bbox.q.y) {
322 	/* Nothing has been written on this page. */
323 	pbbox->p.x = pbbox->p.y = pbbox->q.x = pbbox->q.y = 0;
324     } else {
325 	gs_rect dbox;
326 	gs_matrix mat;
327 
328 	dbox.p.x = fixed2float(bbox.p.x);
329 	dbox.p.y = fixed2float(bbox.p.y);
330 	dbox.q.x = fixed2float(bbox.q.x);
331 	dbox.q.y = fixed2float(bbox.q.y);
332 	gs_deviceinitialmatrix((gx_device *)dev, &mat);
333 	gs_bbox_transform_inverse(&dbox, &mat, pbbox);
334     }
335 }
336 
337 private int
bbox_open_device(gx_device * dev)338 bbox_open_device(gx_device * dev)
339 {
340     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
341 
342     if (bdev->free_standing) {
343 	gx_device_forward_fill_in_procs((gx_device_forward *) dev);
344 	bdev->box_procs = box_procs_default;
345 	bdev->box_proc_data = bdev;
346     }
347     if (bdev->box_procs.init_box == box_procs_default.init_box)
348 	BBOX_INIT_BOX(bdev);
349     /* gx_forward_open_device doesn't exist */
350     {
351 	gx_device *tdev = bdev->target;
352 	int code =
353 	    (tdev && bdev->forward_open_close ? gs_opendevice(tdev) : 0);
354 
355 	bbox_copy_params(bdev, true);
356 	return code;
357     }
358 }
359 
360 private int
bbox_output_page(gx_device * dev,int num_copies,int flush)361 bbox_output_page(gx_device * dev, int num_copies, int flush)
362 {
363     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
364 
365     if (bdev->free_standing) {
366 	/*
367 	 * This is a free-standing device.  Print the page bounding box.
368 	 */
369 	gs_rect bbox;
370 
371 	gx_device_bbox_bbox(bdev, &bbox);
372 	dlprintf4("%%%%BoundingBox: %d %d %d %d\n",
373 		  (int)floor(bbox.p.x), (int)floor(bbox.p.y),
374 		  (int)ceil(bbox.q.x), (int)ceil(bbox.q.y));
375 	dlprintf4("%%%%HiResBoundingBox: %f %f %f %f\n",
376 		  bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
377     }
378     return gx_forward_output_page(dev, num_copies, flush);
379 }
380 
381 /* ---------------- Low-level drawing ---------------- */
382 
383 private int
bbox_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)384 bbox_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
385 		    gx_color_index color)
386 {
387     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
388     gx_device *tdev = bdev->target;
389     /* gx_forward_fill_rectangle doesn't exist */
390     int code =
391 	(tdev == 0 ? 0 :
392 	 dev_proc(tdev, fill_rectangle)(tdev, x, y, w, h, color));
393 
394     /* Check for erasing the entire page. */
395     if (RECT_IS_PAGE(dev, x, y, w, h)) {
396 	if (!BBOX_INIT_BOX(bdev))
397 	    return code;
398     }
399     if (color != bdev->transparent)
400 	BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
401     return code;
402 }
403 
404 private int
bbox_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)405 bbox_copy_mono(gx_device * dev, const byte * data,
406 	    int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h,
407 	       gx_color_index zero, gx_color_index one)
408 {
409     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
410     /* gx_forward_copy_mono doesn't exist */
411     gx_device *tdev = bdev->target;
412     int code =
413 	(tdev == 0 ? 0 :
414 	 dev_proc(tdev, copy_mono)
415 	 (tdev, data, dx, raster, id, x, y, w, h, zero, one));
416 
417     if ((one != gx_no_color_index && one != bdev->transparent) ||
418 	(zero != gx_no_color_index && zero != bdev->transparent)
419 	)
420 	BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
421     return code;
422 }
423 
424 private int
bbox_copy_color(gx_device * dev,const byte * data,int dx,int raster,gx_bitmap_id id,int x,int y,int w,int h)425 bbox_copy_color(gx_device * dev, const byte * data,
426 	    int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h)
427 {
428     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
429     /* gx_forward_copy_color doesn't exist */
430     gx_device *tdev = bdev->target;
431     int code =
432 	(tdev == 0 ? 0 :
433 	 dev_proc(tdev, copy_color)
434 	 (tdev, data, dx, raster, id, x, y, w, h));
435 
436     BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
437     return code;
438 }
439 
440 private int
bbox_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)441 bbox_copy_alpha(gx_device * dev, const byte * data, int data_x,
442 		int raster, gx_bitmap_id id, int x, int y, int w, int h,
443 		gx_color_index color, int depth)
444 {
445     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
446     /* gx_forward_copy_alpha doesn't exist */
447     gx_device *tdev = bdev->target;
448     int code =
449 	(tdev == 0 ? 0 :
450 	 dev_proc(tdev, copy_alpha)
451 	 (tdev, data, data_x, raster, id, x, y, w, h, color, depth));
452 
453     BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
454     return code;
455 }
456 
457 private int
bbox_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)458 bbox_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
459    int x, int y, int w, int h, gx_color_index color0, gx_color_index color1,
460 			  int px, int py)
461 {
462     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
463     /* Skip the call if there is no target. */
464     gx_device *tdev = bdev->target;
465     int code =
466 	(tdev == 0 ? 0 :
467 	 dev_proc(tdev, strip_tile_rectangle)
468 	 (tdev, tiles, x, y, w, h, color0, color1, px, py));
469 
470     if (RECT_IS_PAGE(dev, x, y, w, h)) {
471 	if (!BBOX_INIT_BOX(bdev))
472 	    return code;
473     }
474     BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
475     return code;
476 }
477 
478 private int
bbox_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 phase_x,int phase_y,gs_logical_operation_t lop)479 bbox_strip_copy_rop(gx_device * dev,
480 		    const byte * sdata, int sourcex, uint sraster,
481 		    gx_bitmap_id id,
482 		    const gx_color_index * scolors,
483 		    const gx_strip_bitmap * textures,
484 		    const gx_color_index * tcolors,
485 		    int x, int y, int w, int h,
486 		    int phase_x, int phase_y, gs_logical_operation_t lop)
487 {
488     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
489     /* gx_forward_strip_copy_rop doesn't exist */
490     gx_device *tdev = bdev->target;
491     int code =
492 	(tdev == 0 ? 0 :
493 	 dev_proc(tdev, strip_copy_rop)
494 	 (tdev, sdata, sourcex, sraster, id, scolors,
495 	  textures, tcolors, x, y, w, h, phase_x, phase_y, lop));
496 
497     BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
498     return code;
499 }
500 
501 /* ---------------- Parameters ---------------- */
502 
503 /* We implement get_params to provide a way to read out the bounding box. */
504 private int
bbox_get_params(gx_device * dev,gs_param_list * plist)505 bbox_get_params(gx_device * dev, gs_param_list * plist)
506 {
507     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
508     gs_fixed_rect fbox;
509     int code = gx_forward_get_params(dev, plist);
510     gs_param_float_array bba;
511     float bbox[4];
512 
513     if (code < 0)
514 	return code;
515     /*
516      * We might be calling get_params before the device has been
517      * initialized: in this case, box_proc_data = 0.
518      */
519     if (bdev->box_proc_data == 0)
520 	fbox = bdev->bbox;
521     else
522 	BBOX_GET_BOX(bdev, &fbox);
523     bbox[0] = fixed2float(fbox.p.x);
524     bbox[1] = fixed2float(fbox.p.y);
525     bbox[2] = fixed2float(fbox.q.x);
526     bbox[3] = fixed2float(fbox.q.y);
527     bba.data = bbox, bba.size = 4, bba.persistent = false;
528     code = param_write_float_array(plist, "PageBoundingBox", &bba);
529     if (code < 0)
530         return code;
531     code = param_write_bool(plist, "WhiteIsOpaque", &bdev->white_is_opaque);
532     return code;
533 }
534 
535 /* We implement put_params to ensure that we keep the important */
536 /* device parameters up to date, and to prevent an /undefined error */
537 /* from PageBoundingBox. */
538 private int
bbox_put_params(gx_device * dev,gs_param_list * plist)539 bbox_put_params(gx_device * dev, gs_param_list * plist)
540 {
541     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
542     int code;
543     int ecode = 0;
544     bool white_is_opaque = bdev->white_is_opaque;
545     gs_param_name param_name;
546     gs_param_float_array bba;
547 
548     code = param_read_float_array(plist, (param_name = "PageBoundingBox"),
549 				  &bba);
550     switch (code) {
551 	case 0:
552 	    if (bba.size != 4) {
553 		ecode = gs_note_error(gs_error_rangecheck);
554 		goto e;
555 	    }
556 	    break;
557 	default:
558 	    ecode = code;
559 	    e:param_signal_error(plist, param_name, ecode);
560 	case 1:
561 	    bba.data = 0;
562     }
563 
564     switch (code = param_read_bool(plist, (param_name = "WhiteIsOpaque"), &white_is_opaque)) {
565 	default:
566 	    ecode = code;
567 	    param_signal_error(plist, param_name, ecode);
568 	case 0:
569         case 1:
570 	    break;
571     }
572 
573     code = gx_forward_put_params(dev, plist);
574     if (ecode < 0)
575 	code = ecode;
576     if (code >= 0) {
577         if( bba.data != 0) {
578 	    BBOX_INIT_BOX(bdev);
579 	    BBOX_ADD_RECT(bdev, float2fixed(bba.data[0]), float2fixed(bba.data[1]),
580 	    	          float2fixed(bba.data[2]), float2fixed(bba.data[3]));
581         }
582         bdev->white_is_opaque = white_is_opaque;
583     }
584     bbox_copy_params(bdev, bdev->is_open);
585     return code;
586 }
587 
588 /* ---------------- Polygon drawing ---------------- */
589 
590 private fixed
edge_x_at_y(const gs_fixed_edge * edge,fixed y)591 edge_x_at_y(const gs_fixed_edge * edge, fixed y)
592 {
593     return fixed_mult_quo(edge->end.x - edge->start.x,
594 			  y - edge->start.y,
595 			  edge->end.y - edge->start.y) + edge->start.x;
596 }
597 private int
bbox_fill_trapezoid(gx_device * dev,const gs_fixed_edge * left,const gs_fixed_edge * right,fixed ybot,fixed ytop,bool swap_axes,const gx_device_color * pdevc,gs_logical_operation_t lop)598 bbox_fill_trapezoid(gx_device * dev,
599 		    const gs_fixed_edge * left, const gs_fixed_edge * right,
600 		    fixed ybot, fixed ytop, bool swap_axes,
601 		    const gx_device_color * pdevc, gs_logical_operation_t lop)
602 {
603     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
604     /* Skip the call if there is no target. */
605     gx_device *tdev = bdev->target;
606     int code =
607 	(tdev == 0 ? 0 :
608 	 dev_proc(tdev, fill_trapezoid)
609 	 (tdev, left, right, ybot, ytop, swap_axes, pdevc, lop));
610 
611     if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
612 	fixed x0l =
613 	    (left->start.y == ybot ? left->start.x :
614 	     edge_x_at_y(left, ybot));
615 	fixed x1l =
616 	    (left->end.y == ytop ? left->end.x :
617 	     edge_x_at_y(left, ytop));
618 	fixed x0r =
619 	    (right->start.y == ybot ? right->start.x :
620 	     edge_x_at_y(right, ybot));
621 	fixed x1r =
622 	    (right->end.y == ytop ? right->end.x :
623 	     edge_x_at_y(right, ytop));
624 	fixed xminl = min(x0l, x1l), xmaxl = max(x0l, x1l);
625 	fixed xminr = min(x0r, x1r), xmaxr = max(x0r, x1r);
626 	fixed x0 = min(xminl, xminr), x1 = max(xmaxl, xmaxr);
627 
628 	if (swap_axes)
629 	    BBOX_ADD_RECT(bdev, ybot, x0, ytop, x1);
630 	else
631 	    BBOX_ADD_RECT(bdev, x0, ybot, x1, ytop);
632     }
633     return code;
634 }
635 
636 private int
bbox_fill_parallelogram(gx_device * dev,fixed px,fixed py,fixed ax,fixed ay,fixed bx,fixed by,const gx_device_color * pdevc,gs_logical_operation_t lop)637 bbox_fill_parallelogram(gx_device * dev,
638 			fixed px, fixed py, fixed ax, fixed ay,
639 			fixed bx, fixed by, const gx_device_color * pdevc,
640 			gs_logical_operation_t lop)
641 {
642     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
643     /* Skip the call if there is no target. */
644     gx_device *tdev = bdev->target;
645     int code =
646 	(tdev == 0 ? 0 :
647 	 dev_proc(tdev, fill_parallelogram)
648 	 (tdev, px, py, ax, ay, bx, by, pdevc, lop));
649 
650     if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
651 	fixed xmin, ymin, xmax, ymax;
652 
653 	/* bbox_add_rect requires points in correct order. */
654 #define SET_MIN_MAX(vmin, vmax, av, bv)\
655   BEGIN\
656     if (av <= 0) {\
657 	if (bv <= 0)\
658 	    vmin = av + bv, vmax = 0;\
659 	else\
660 	    vmin = av, vmax = bv;\
661     } else if (bv <= 0)\
662 	vmin = bv, vmax = av;\
663     else\
664 	vmin = 0, vmax = av + bv;\
665   END
666 	SET_MIN_MAX(xmin, xmax, ax, bx);
667 	SET_MIN_MAX(ymin, ymax, ay, by);
668 #undef SET_MIN_MAX
669 	BBOX_ADD_RECT(bdev, px + xmin, py + ymin, px + xmax, py + ymax);
670     }
671     return code;
672 }
673 
674 private int
bbox_fill_triangle(gx_device * dev,fixed px,fixed py,fixed ax,fixed ay,fixed bx,fixed by,const gx_device_color * pdevc,gs_logical_operation_t lop)675 bbox_fill_triangle(gx_device * dev,
676 		   fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
677 		   const gx_device_color * pdevc, gs_logical_operation_t lop)
678 {
679     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
680     /* Skip the call if there is no target. */
681     gx_device *tdev = bdev->target;
682     int code =
683 	(tdev == 0 ? 0 :
684 	 dev_proc(tdev, fill_triangle)
685 	 (tdev, px, py, ax, ay, bx, by, pdevc, lop));
686 
687     if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
688 	fixed xmin, ymin, xmax, ymax;
689 
690 	/* bbox_add_rect requires points in correct order. */
691 #define SET_MIN_MAX(vmin, vmax, av, bv)\
692   BEGIN\
693     if (av <= 0) {\
694 	if (bv <= 0)\
695 	    vmin = min(av, bv), vmax = 0;\
696 	else\
697 	    vmin = av, vmax = bv;\
698     } else if (bv <= 0)\
699 	vmin = bv, vmax = av;\
700     else\
701 	vmin = 0, vmax = max(av, bv);\
702   END
703 	SET_MIN_MAX(xmin, xmax, ax, bx);
704 	SET_MIN_MAX(ymin, ymax, ay, by);
705 #undef SET_MIN_MAX
706 	BBOX_ADD_RECT(bdev, px + xmin, py + ymin, px + xmax, py + ymax);
707     }
708     return code;
709 }
710 
711 private int
bbox_draw_thin_line(gx_device * dev,fixed fx0,fixed fy0,fixed fx1,fixed fy1,const gx_device_color * pdevc,gs_logical_operation_t lop)712 bbox_draw_thin_line(gx_device * dev,
713 		    fixed fx0, fixed fy0, fixed fx1, fixed fy1,
714 		    const gx_device_color * pdevc, gs_logical_operation_t lop)
715 {
716     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
717     /* Skip the call if there is no target. */
718     gx_device *tdev = bdev->target;
719     int code =
720 	(tdev == 0 ? 0 :
721 	 dev_proc(tdev, draw_thin_line)
722 	 (tdev, fx0, fy0, fx1, fy0, pdevc, lop));
723 
724     if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
725 	fixed xmin, ymin, xmax, ymax;
726 
727 	/* bbox_add_rect requires points in correct order. */
728 #define SET_MIN_MAX(vmin, vmax, av, bv)\
729   BEGIN\
730     if (av < bv)\
731 	vmin = av, vmax = bv;\
732     else\
733 	vmin = bv, vmax = av;\
734   END
735 	SET_MIN_MAX(xmin, xmax, fx0, fx1);
736 	SET_MIN_MAX(ymin, ymax, fy0, fy1);
737 #undef SET_MIN_MAX
738 	BBOX_ADD_RECT(bdev, xmin, ymin, xmax, ymax);
739     }
740     return code;
741 }
742 
743 /* ---------------- High-level drawing ---------------- */
744 
745 #define adjust_box(pbox, adj)\
746 ((pbox)->p.x -= (adj).x, (pbox)->p.y -= (adj).y,\
747  (pbox)->q.x += (adj).x, (pbox)->q.y += (adj).y)
748 
749 private int
bbox_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_device_color * pdevc,const gx_clip_path * pcpath)750 bbox_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
751 	       const gx_fill_params * params, const gx_device_color * pdevc,
752 	       const gx_clip_path * pcpath)
753 {
754     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
755     gx_device *tdev = bdev->target;
756     dev_proc_fill_path((*fill_path)) =
757 	(tdev == 0 ? dev_proc(&gs_null_device, fill_path) :
758 	 dev_proc(tdev, fill_path));
759     int code;
760 
761     if (!GX_DC_IS_TRANSPARENT(pdevc, bdev) && !gx_path_is_void(ppath)) {
762 	gs_fixed_rect ibox;
763 	gs_fixed_point adjust;
764 
765 	if (gx_path_bbox(ppath, &ibox) < 0)
766 	    return 0;
767 	adjust = params->adjust;
768 	if (params->fill_zero_width)
769 	    gx_adjust_if_empty(&ibox, &adjust);
770 	adjust_box(&ibox, adjust);
771 	/*
772 	 * If the path lies within the already accumulated box, just draw
773 	 * on the target.
774 	 */
775 	if (BBOX_IN_RECT(bdev, &ibox))
776 	    return fill_path(tdev, pis, ppath, params, pdevc, pcpath);
777 	/*
778 	 * If the target uses the default algorithm, just draw on the
779 	 * bbox device.
780 	 */
781 	if (tdev != 0 && fill_path == gx_default_fill_path)
782 	    return fill_path(dev, pis, ppath, params, pdevc, pcpath);
783 	/* Draw on the target now. */
784 	code = fill_path(tdev, pis, ppath, params, pdevc, pcpath);
785 	if (code < 0)
786 	    return code;
787 	if (pcpath != NULL &&
788 	    !gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
789 					 ibox.q.x, ibox.q.y)
790 	    ) {
791 	    /*
792 	     * Let the target do the drawing, but break down the
793 	     * fill path into pieces for computing the bounding box.
794 	     */
795 	    gx_drawing_color devc;
796 
797 	    set_nonclient_dev_color(&devc, bdev->black);  /* any non-white color will do */
798 	    bdev->target = NULL;
799 	    code = gx_default_fill_path(dev, pis, ppath, params, &devc, pcpath);
800 	    bdev->target = tdev;
801 	} else {		/* Just use the path bounding box. */
802 	    BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
803 	}
804 	return code;
805     } else
806 	return fill_path(tdev, pis, ppath, params, pdevc, pcpath);
807 }
808 
809 private int
bbox_stroke_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_stroke_params * params,const gx_drawing_color * pdevc,const gx_clip_path * pcpath)810 bbox_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
811 		 const gx_stroke_params * params,
812 		 const gx_drawing_color * pdevc, const gx_clip_path * pcpath)
813 {
814     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
815     gx_device *tdev = bdev->target;
816     /* Skip the call if there is no target. */
817     int code =
818 	(tdev == 0 ? 0 :
819 	 dev_proc(tdev, stroke_path)(tdev, pis, ppath, params, pdevc, pcpath));
820 
821     if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
822 	gs_fixed_rect ibox;
823 	gs_fixed_point expand;
824 
825 	if (gx_stroke_path_expansion(pis, ppath, &expand) == 0 &&
826 	    gx_path_bbox(ppath, &ibox) >= 0
827 	    ) {
828 	    /* The fast result is exact. */
829 	    adjust_box(&ibox, expand);
830 	} else {
831 	    /*
832 	     * The result is not exact.  Compute an exact result using
833 	     * strokepath.
834 	     */
835 	    gx_path *spath = gx_path_alloc(pis->memory, "bbox_stroke_path");
836 	    int code = 0;
837 
838 	    if (spath)
839 		code = gx_imager_stroke_add(ppath, spath, dev, pis);
840 	    else
841 		code = -1;
842 	    if (code >= 0)
843 		code = gx_path_bbox(spath, &ibox);
844 	    if (code < 0) {
845 		ibox.p.x = ibox.p.y = min_fixed;
846 		ibox.q.x = ibox.q.y = max_fixed;
847 	    }
848 	    if (spath)
849 		gx_path_free(spath, "bbox_stroke_path");
850 	}
851 	if (pcpath != NULL &&
852 	    !gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
853 					 ibox.q.x, ibox.q.y)
854 	    ) {
855 	    /* Let the target do the drawing, but break down the */
856 	    /* fill path into pieces for computing the bounding box. */
857 	    gx_drawing_color devc;
858 
859 	    set_nonclient_dev_color(&devc, bdev->black);  /* any non-white color will do */
860 	    bdev->target = NULL;
861 	    gx_default_stroke_path(dev, pis, ppath, params, &devc, pcpath);
862 	    bdev->target = tdev;
863 	} else {
864 	    /* Just use the path bounding box. */
865 	    BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
866 	}
867     }
868     return code;
869 }
870 
871 private int
bbox_fill_mask(gx_device * dev,const byte * data,int dx,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)872 bbox_fill_mask(gx_device * dev,
873 	       const byte * data, int dx, int raster, gx_bitmap_id id,
874 	       int x, int y, int w, int h,
875 	       const gx_drawing_color * pdcolor, int depth,
876 	       gs_logical_operation_t lop, const gx_clip_path * pcpath)
877 {
878     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
879     gx_device *tdev = bdev->target;
880     /* Skip the call if there is no target. */
881     int code =
882 	(tdev == 0 ? 0 :
883 	 dev_proc(tdev, fill_mask)
884 	 (tdev, data, dx, raster, id, x, y, w, h,
885 	  pdcolor, depth, lop, pcpath));
886 
887     if (pcpath != NULL &&
888 	!gx_cpath_includes_rectangle(pcpath, int2fixed(x), int2fixed(y),
889 				     int2fixed(x + w),
890 				     int2fixed(y + h))
891 	) {
892 	/* Let the target do the drawing, but break down the */
893 	/* image into pieces for computing the bounding box. */
894 	bdev->target = NULL;
895 	gx_default_fill_mask(dev, data, dx, raster, id, x, y, w, h,
896 			     pdcolor, depth, lop, pcpath);
897 	bdev->target = tdev;
898     } else {
899 	/* Just use the mask bounding box. */
900 	BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
901     }
902     return code;
903 }
904 
905 /* ------ Bitmap imaging ------ */
906 
907 typedef struct bbox_image_enum_s {
908     gx_image_enum_common;
909     gs_memory_t *memory;
910     gs_matrix matrix;		/* map from image space to device space */
911     const gx_clip_path *pcpath;
912     gx_image_enum_common_t *target_info;
913     bool params_are_const;
914     int x0, x1;
915     int y, height;
916 } bbox_image_enum;
917 
918 gs_private_st_suffix_add2(st_bbox_image_enum, bbox_image_enum,
919   "bbox_image_enum", bbox_image_enum_enum_ptrs, bbox_image_enum_reloc_ptrs,
920   st_gx_image_enum_common, pcpath, target_info);
921 
922 private image_enum_proc_plane_data(bbox_image_plane_data);
923 private image_enum_proc_end_image(bbox_image_end_image);
924 private image_enum_proc_flush(bbox_image_flush);
925 private image_enum_proc_planes_wanted(bbox_image_planes_wanted);
926 private const gx_image_enum_procs_t bbox_image_enum_procs = {
927     bbox_image_plane_data, bbox_image_end_image,
928     bbox_image_flush, bbox_image_planes_wanted
929 };
930 
931 private int
bbox_image_begin(const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_clip_path * pcpath,gs_memory_t * memory,bbox_image_enum ** ppbe)932 bbox_image_begin(const gs_imager_state * pis, const gs_matrix * pmat,
933 		 const gs_image_common_t * pic, const gs_int_rect * prect,
934 		 const gx_clip_path * pcpath, gs_memory_t * memory,
935 		 bbox_image_enum ** ppbe)
936 {
937     int code;
938     gs_matrix mat;
939     bbox_image_enum *pbe;
940 
941     if (pmat == 0)
942 	pmat = &ctm_only(pis);
943     if ((code = gs_matrix_invert(&pic->ImageMatrix, &mat)) < 0 ||
944 	(code = gs_matrix_multiply(&mat, pmat, &mat)) < 0
945 	)
946 	return code;
947     pbe = gs_alloc_struct(memory, bbox_image_enum, &st_bbox_image_enum,
948 			  "bbox_image_begin");
949     if (pbe == 0)
950 	return_error(gs_error_VMerror);
951     pbe->memory = memory;
952     pbe->matrix = mat;
953     pbe->pcpath = pcpath;
954     pbe->target_info = 0;	/* in case no target */
955     pbe->params_are_const = false;	/* check the first time */
956     if (prect) {
957 	pbe->x0 = prect->p.x, pbe->x1 = prect->q.x;
958 	pbe->y = prect->p.y, pbe->height = prect->q.y - prect->p.y;
959     } else {
960 	gs_int_point size;
961 	int code = (*pic->type->source_size) (pis, pic, &size);
962 
963 	if (code < 0) {
964 	    gs_free_object(memory, pbe, "bbox_image_begin");
965 	    return code;
966 	}
967 	pbe->x0 = 0, pbe->x1 = size.x;
968 	pbe->y = 0, pbe->height = size.y;
969     }
970     *ppbe = pbe;
971     return 0;
972 }
973 
974 private void
bbox_image_copy_target_info(bbox_image_enum * pbe)975 bbox_image_copy_target_info(bbox_image_enum * pbe)
976 {
977     const gx_image_enum_common_t *target_info = pbe->target_info;
978 
979     pbe->num_planes = target_info->num_planes;
980     memcpy(pbe->plane_depths, target_info->plane_depths,
981 	   pbe->num_planes * sizeof(pbe->plane_depths[0]));
982     memcpy(pbe->plane_widths, target_info->plane_widths,
983 	   pbe->num_planes * sizeof(pbe->plane_widths[0]));
984 }
985 
986 private int
bbox_begin_typed_image(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * memory,gx_image_enum_common_t ** pinfo)987 bbox_begin_typed_image(gx_device * dev,
988 		       const gs_imager_state * pis, const gs_matrix * pmat,
989 		   const gs_image_common_t * pic, const gs_int_rect * prect,
990 		       const gx_drawing_color * pdcolor,
991 		       const gx_clip_path * pcpath,
992 		       gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
993 {
994     bbox_image_enum *pbe;
995     int code =
996 	bbox_image_begin(pis, pmat, pic, prect, pcpath, memory, &pbe);
997 
998     if (code < 0)
999 	return code;
1000     /*
1001      * If there is no target, we still have to call default_begin_image
1002      * to get the correct num_planes and plane_depths.
1003      */
1004     {
1005 	gx_device_bbox *const bdev = (gx_device_bbox *) dev;
1006 	gx_device *tdev = bdev->target;
1007 	dev_proc_begin_typed_image((*begin_typed_image));
1008 	byte wanted[GS_IMAGE_MAX_PLANES];
1009 
1010 	if (tdev == 0) {
1011 	    tdev = dev;
1012 	    begin_typed_image = gx_default_begin_typed_image;
1013 	} else {
1014 	    begin_typed_image = dev_proc(tdev, begin_typed_image);
1015 	}
1016 	code = (*begin_typed_image)
1017 	    (tdev, pis, pmat, pic, prect, pdcolor, pcpath, memory,
1018 	     &pbe->target_info);
1019 	if (code) {
1020 	    bbox_image_end_image((gx_image_enum_common_t *)pbe, false);
1021 	    return code;
1022 	}
1023 	/*
1024 	 * We fill in num_planes and plane_depths later.  format is
1025 	 * irrelevant.  NOTE: we assume that if begin_typed_image returned
1026 	 * 0, the image is a data image.
1027 	 */
1028 	code = gx_image_enum_common_init((gx_image_enum_common_t *) pbe,
1029 					 (const gs_data_image_t *)pic,
1030 					 &bbox_image_enum_procs, dev,
1031 					 0, gs_image_format_chunky);
1032 	if (code < 0)
1033 	    return code;
1034 	bbox_image_copy_target_info(pbe);
1035 	pbe->params_are_const =
1036 	    gx_image_planes_wanted(pbe->target_info, wanted);
1037     }
1038     *pinfo = (gx_image_enum_common_t *) pbe;
1039     return 0;
1040 }
1041 
1042 private int
bbox_image_plane_data(gx_image_enum_common_t * info,const gx_image_plane_t * planes,int height,int * rows_used)1043 bbox_image_plane_data(gx_image_enum_common_t * info,
1044 		      const gx_image_plane_t * planes, int height,
1045 		      int *rows_used)
1046 {
1047     gx_device *dev = info->dev;
1048     gx_device_bbox *const bdev = (gx_device_bbox *)dev;
1049     gx_device *tdev = bdev->target;
1050     bbox_image_enum *pbe = (bbox_image_enum *) info;
1051     const gx_clip_path *pcpath = pbe->pcpath;
1052     gs_rect sbox, dbox;
1053     gs_point corners[4];
1054     gs_fixed_rect ibox;
1055     int code;
1056 
1057     code = gx_image_plane_data_rows(pbe->target_info, planes, height,
1058 				    rows_used);
1059     if (code != 1 && !pbe->params_are_const)
1060 	bbox_image_copy_target_info(pbe);
1061     sbox.p.x = pbe->x0;
1062     sbox.p.y = pbe->y;
1063     sbox.q.x = pbe->x1;
1064     sbox.q.y = pbe->y = min(pbe->y + height, pbe->height);
1065     gs_bbox_transform_only(&sbox, &pbe->matrix, corners);
1066     gs_points_bbox(corners, &dbox);
1067     ibox.p.x = float2fixed(dbox.p.x);
1068     ibox.p.y = float2fixed(dbox.p.y);
1069     ibox.q.x = float2fixed(dbox.q.x);
1070     ibox.q.y = float2fixed(dbox.q.y);
1071     if (pcpath != NULL &&
1072 	!gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
1073 				     ibox.q.x, ibox.q.y)
1074 	) {
1075 	/* Let the target do the drawing, but drive two triangles */
1076 	/* through the clipping path to get an accurate bounding box. */
1077 	gx_device_clip cdev;
1078 	gx_drawing_color devc;
1079 	fixed x0 = float2fixed(corners[0].x), y0 = float2fixed(corners[0].y);
1080 	fixed bx2 = float2fixed(corners[2].x) - x0, by2 = float2fixed(corners[2].y) - y0;
1081 
1082 	gx_make_clip_path_device(&cdev, pcpath);
1083 	cdev.target = dev;
1084 	(*dev_proc(&cdev, open_device)) ((gx_device *) & cdev);
1085 	set_nonclient_dev_color(&devc, bdev->black);  /* any non-white color will do */
1086 	bdev->target = NULL;
1087 	gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
1088 				 float2fixed(corners[1].x) - x0,
1089 				 float2fixed(corners[1].y) - y0,
1090 				 bx2, by2, &devc, lop_default);
1091 	gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
1092 				 float2fixed(corners[3].x) - x0,
1093 				 float2fixed(corners[3].y) - y0,
1094 				 bx2, by2, &devc, lop_default);
1095 	bdev->target = tdev;
1096     } else {
1097 	/* Just use the bounding box. */
1098 	BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
1099     }
1100     return code;
1101 }
1102 
1103 private int
bbox_image_end_image(gx_image_enum_common_t * info,bool draw_last)1104 bbox_image_end_image(gx_image_enum_common_t * info, bool draw_last)
1105 {
1106     bbox_image_enum *pbe = (bbox_image_enum *) info;
1107     int code = gx_image_end(pbe->target_info, draw_last);
1108 
1109     gs_free_object(pbe->memory, pbe, "bbox_end_image");
1110     return code;
1111 }
1112 
1113 private int
bbox_image_flush(gx_image_enum_common_t * info)1114 bbox_image_flush(gx_image_enum_common_t * info)
1115 {
1116     bbox_image_enum *pbe = (bbox_image_enum *) info;
1117     gx_image_enum_common_t *target_info = pbe->target_info;
1118 
1119     return (target_info ? gx_image_flush(target_info) : 0);
1120 }
1121 
1122 private bool
bbox_image_planes_wanted(const gx_image_enum_common_t * info,byte * wanted)1123 bbox_image_planes_wanted(const gx_image_enum_common_t * info, byte *wanted)
1124 {
1125     /* This is only used if target_info != 0. */
1126     const bbox_image_enum *pbe = (const bbox_image_enum *)info;
1127 
1128     return gx_image_planes_wanted(pbe->target_info, wanted);
1129 }
1130 
1131 /* Compositing */
1132 
1133 private bool
bbox_forward_init_box(void * pdata)1134 bbox_forward_init_box(void *pdata)
1135 {
1136     gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
1137 
1138     return BBOX_INIT_BOX(bdev);
1139 }
1140 private void
bbox_forward_get_box(const void * pdata,gs_fixed_rect * pbox)1141 bbox_forward_get_box(const void *pdata, gs_fixed_rect *pbox)
1142 {
1143     const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
1144 
1145     BBOX_GET_BOX(bdev, pbox);
1146 }
1147 private void
bbox_forward_add_rect(void * pdata,fixed x0,fixed y0,fixed x1,fixed y1)1148 bbox_forward_add_rect(void *pdata, fixed x0, fixed y0, fixed x1, fixed y1)
1149 {
1150     gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
1151 
1152     BBOX_ADD_RECT(bdev, x0, y0, x1, y1);
1153 }
1154 private bool
bbox_forward_in_rect(const void * pdata,const gs_fixed_rect * pbox)1155 bbox_forward_in_rect(const void *pdata, const gs_fixed_rect *pbox)
1156 {
1157     const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
1158 
1159     return BBOX_IN_RECT(bdev, pbox);
1160 }
1161 private const gx_device_bbox_procs_t box_procs_forward = {
1162     bbox_forward_init_box, bbox_forward_get_box, bbox_forward_add_rect,
1163     bbox_forward_in_rect
1164 };
1165 
1166 private int
bbox_create_compositor(gx_device * dev,gx_device ** pcdev,const gs_composite_t * pcte,gs_imager_state * pis,gs_memory_t * memory)1167 bbox_create_compositor(gx_device * dev,
1168 		       gx_device ** pcdev, const gs_composite_t * pcte,
1169 		       gs_imager_state * pis, gs_memory_t * memory)
1170 {
1171     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
1172     gx_device *target = bdev->target;
1173 
1174     /*
1175      * If there isn't a target, all we care about is the bounding box,
1176      * so don't bother with actually compositing.
1177      */
1178     if (target == 0) {
1179 	*pcdev = dev;
1180 	return 0;
1181     }
1182     /*
1183      * Create a compositor for the target, and then wrap another
1184      * bbox device around it, but still accumulating the bounding
1185      * box in the same place.
1186      */
1187     {
1188 	gx_device *cdev;
1189 	gx_device_bbox *bbcdev;
1190 	int code = (*dev_proc(target, create_compositor))
1191 	    (target, &cdev, pcte, pis, memory);
1192 
1193 	/* If the target did not create a new compositor then we are done. */
1194 	if (code < 0 || target == cdev) {
1195 	    *pcdev = dev;
1196 	    return code;
1197 	}
1198 	bbcdev = gs_alloc_struct_immovable(memory, gx_device_bbox,
1199 					   &st_device_bbox,
1200 					   "bbox_create_compositor");
1201 	if (bbcdev == 0) {
1202 	    (*dev_proc(cdev, close_device)) (cdev);
1203 	    return_error(gs_error_VMerror);
1204 	}
1205 	gx_device_bbox_init(bbcdev, target, memory);
1206 	gx_device_set_target((gx_device_forward *)bbcdev, cdev);
1207 	bbcdev->box_procs = box_procs_forward;
1208 	bbcdev->box_proc_data = bdev;
1209 	*pcdev = (gx_device *) bbcdev;
1210 	return 0;
1211     }
1212 }
1213 
1214 /* ------ Text imaging ------ */
1215 
1216 private int
bbox_text_begin(gx_device * dev,gs_imager_state * pis,const gs_text_params_t * text,gs_font * font,gx_path * path,const gx_device_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * memory,gs_text_enum_t ** ppenum)1217 bbox_text_begin(gx_device * dev, gs_imager_state * pis,
1218 		const gs_text_params_t * text, gs_font * font,
1219 		gx_path * path, const gx_device_color * pdcolor,
1220 		const gx_clip_path * pcpath,
1221 		gs_memory_t * memory, gs_text_enum_t ** ppenum)
1222 {
1223     gx_device_bbox *const bdev = (gx_device_bbox *) dev;
1224     int code = gx_default_text_begin(dev, pis, text, font, path, pdcolor,
1225 				     pcpath, memory, ppenum);
1226 
1227     if (bdev->target != NULL) {
1228         /* See note on imaging_dev in gxtext.h */
1229         rc_assign((*ppenum)->imaging_dev, dev, "bbox_text_begin");
1230     }
1231 
1232     return code;
1233 }
1234 
1235