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