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