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