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