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