1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
7
This software is distributed under license and may not be copied, modified
8
or distributed except as expressly authorized under the terms of that
9
license. Refer to licensing information at http://www.artifex.com/
10
or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11
San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
13
/*$Id: gdevbbox.c 8803 2008-06-24 14:16:29Z leonardo $ */
14
/* Device for tracking bounding box */
21
#include "gsdevice.h" /* requires gsmatrix.h */
23
#include "gxdcolor.h" /* for gx_device_black/white */
24
#include "gxiparam.h" /* for image source size */
31
public_st_device_bbox();
33
/* Device procedures */
34
static dev_proc_open_device(bbox_open_device);
35
static dev_proc_close_device(bbox_close_device);
36
static dev_proc_output_page(bbox_output_page);
37
static dev_proc_fill_rectangle(bbox_fill_rectangle);
38
static dev_proc_copy_mono(bbox_copy_mono);
39
static dev_proc_copy_color(bbox_copy_color);
40
static dev_proc_get_params(bbox_get_params);
41
static dev_proc_put_params(bbox_put_params);
42
static dev_proc_copy_alpha(bbox_copy_alpha);
43
static dev_proc_fill_path(bbox_fill_path);
44
static dev_proc_stroke_path(bbox_stroke_path);
45
static dev_proc_fill_mask(bbox_fill_mask);
46
static dev_proc_fill_trapezoid(bbox_fill_trapezoid);
47
static dev_proc_fill_parallelogram(bbox_fill_parallelogram);
48
static dev_proc_fill_triangle(bbox_fill_triangle);
49
static dev_proc_draw_thin_line(bbox_draw_thin_line);
50
static dev_proc_strip_tile_rectangle(bbox_strip_tile_rectangle);
51
static dev_proc_strip_copy_rop(bbox_strip_copy_rop);
52
static dev_proc_begin_typed_image(bbox_begin_typed_image);
53
static dev_proc_create_compositor(bbox_create_compositor);
54
static dev_proc_text_begin(bbox_text_begin);
56
/* The device prototype */
58
* Normally this would be static, but if the device is going to be used
59
* stand-alone, it has to be public.
63
* The bbox device sets the resolution to some value R (currently 4000), and
64
* the page size in device pixels to slightly smaller than the largest
65
* representable values (around 500K), leaving a little room for stroke
66
* widths, rounding, etc. If an input file (or the command line) resets the
67
* resolution to a value R' > R, the page size in pixels will get multiplied
68
* by R'/R, and will thereby exceed the representable range, causing a
69
* limitcheck. That is why the bbox device must set the resolution to a
70
* value larger than that of any real device. A consequence of this is that
71
* the page size in inches is limited to the maximum representable pixel
72
* size divided by R, which gives a limit of about 120" in each dimension.
74
#define MAX_COORD (max_int_in_fixed - 1000)
75
#define MAX_RESOLUTION 4000
76
gx_device_bbox gs_bbox_device =
79
* Define the device as 8-bit gray scale to avoid computing halftones.
81
std_device_dci_body(gx_device_bbox, 0, "bbox",
83
MAX_RESOLUTION, MAX_RESOLUTION,
84
1, 8, 255, 0, 256, 1),
86
gx_upright_get_initial_matrix,
87
NULL, /* sync_output */
90
gx_default_gray_map_rgb_color,
91
gx_default_gray_map_color_rgb,
93
NULL, /* tile_rectangle */
100
gx_default_map_cmyk_color,
101
NULL, /* get_xfont_procs */
102
NULL, /* get_xfont_device */
103
gx_default_map_rgb_alpha_color,
104
gx_page_device_get_page_device,
105
NULL, /* get_alpha_bits */
113
bbox_fill_parallelogram,
116
gx_default_begin_image,
117
NULL, /* image_data */
118
NULL, /* end_image */
119
bbox_strip_tile_rectangle,
121
NULL, /* get_clipping_box */
122
bbox_begin_typed_image,
123
NULL, /* get_bits_rectangle */
124
gx_default_map_color_rgb_alpha,
125
bbox_create_compositor,
126
NULL, /* get_hardware_params */
128
NULL, /* finish_copydevice */
129
NULL, /* begin_transparency_group */
130
NULL, /* end_transparency_group */
131
NULL, /* begin_transparency_mask */
132
NULL, /* end_transparency_mask */
133
NULL, /* discard_transparency_layer */
134
NULL, /* get_color_mapping_procs */
135
NULL, /* get_color_comp_index */
136
NULL, /* encode_color */
137
NULL /* decode_color */
140
1, /*true *//* free_standing */
141
1 /*true *//* forward_open_close */
145
#undef MAX_RESOLUTION
147
/* Default box procedures */
150
bbox_default_init_box(void *pdata)
152
gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
153
gs_fixed_rect *const pr = &bdev->bbox;
155
pr->p.x = pr->p.y = max_fixed;
156
pr->q.x = pr->q.y = min_fixed;
157
return bdev->white != bdev->transparent;
159
#define BBOX_INIT_BOX(bdev)\
160
bdev->box_procs.init_box(bdev->box_proc_data)
163
bbox_default_get_box(const void *pdata, gs_fixed_rect *pbox)
165
const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
169
#define BBOX_GET_BOX(bdev, pbox)\
170
bdev->box_procs.get_box(bdev->box_proc_data, pbox);
173
bbox_default_add_rect(void *pdata, fixed x0, fixed y0, fixed x1, fixed y1)
175
gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
176
gs_fixed_rect *const pr = &bdev->bbox;
187
#define BBOX_ADD_RECT(bdev, x0, y0, x1, y1)\
188
bdev->box_procs.add_rect(bdev->box_proc_data, x0, y0, x1, y1)
189
#define BBOX_ADD_INT_RECT(bdev, x0, y0, x1, y1)\
190
BBOX_ADD_RECT(bdev, int2fixed(x0), int2fixed(y0), int2fixed(x1),\
194
bbox_default_in_rect(const void *pdata, const gs_fixed_rect *pbox)
196
const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
198
return rect_within(*pbox, bdev->bbox);
200
#define BBOX_IN_RECT(bdev, pbox)\
201
bdev->box_procs.in_rect(bdev->box_proc_data, pbox)
203
static const gx_device_bbox_procs_t box_procs_default = {
204
bbox_default_init_box, bbox_default_get_box, bbox_default_add_rect,
208
#define RECT_IS_PAGE(dev, x, y, w, h)\
209
(x <= 0 && y <= 0 && x + w >= dev->width && y + h >= dev->height)
211
/* ---------------- Open/close/page ---------------- */
213
/* Copy device parameters back from the target. */
215
bbox_copy_params(gx_device_bbox * bdev, bool remap_colors)
217
gx_device *tdev = bdev->target;
220
gx_device_copy_params((gx_device *)bdev, tdev);
222
bdev->black = gx_device_black((gx_device *)bdev);
223
bdev->white = gx_device_white((gx_device *)bdev);
225
(bdev->white_is_opaque ? gx_no_color_index : bdev->white);
229
#define GX_DC_IS_TRANSPARENT(pdevc, bdev)\
230
(gx_dc_pure_color(pdevc) == (bdev)->transparent && gx_dc_is_pure(pdevc))
233
bbox_close_device(gx_device * dev)
235
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
236
gx_device *tdev = bdev->target;
238
if (bdev->box_procs.init_box != box_procs_default.init_box) {
240
* This device was created as a wrapper for a compositor.
241
* Just free the devices.
243
int code = (bdev->forward_open_close ? gs_closedevice(tdev) : 0);
245
gs_free_object(dev->memory, dev, "bbox_close_device(composite)");
248
return (tdev && bdev->forward_open_close ? gs_closedevice(tdev) : 0);
252
/* Initialize a bounding box device. */
254
gx_device_bbox_init(gx_device_bbox * dev, gx_device * target, gs_memory_t *mem)
256
gx_device_init((gx_device *) dev, (const gx_device *)&gs_bbox_device,
257
(target ? target->memory : mem), true);
259
gx_device_forward_fill_in_procs((gx_device_forward *) dev);
260
set_dev_proc(dev, get_initial_matrix, gx_forward_get_initial_matrix);
261
set_dev_proc(dev, map_rgb_color, gx_forward_map_rgb_color);
262
set_dev_proc(dev, map_color_rgb, gx_forward_map_color_rgb);
263
set_dev_proc(dev, map_cmyk_color, gx_forward_map_cmyk_color);
264
set_dev_proc(dev, map_rgb_alpha_color, gx_forward_map_rgb_alpha_color);
265
set_dev_proc(dev, get_color_mapping_procs, gx_forward_get_color_mapping_procs);
266
set_dev_proc(dev, get_color_comp_index, gx_forward_get_color_comp_index);
267
set_dev_proc(dev, encode_color, gx_forward_encode_color);
268
set_dev_proc(dev, decode_color, gx_forward_decode_color);
269
set_dev_proc(dev, pattern_manage, gx_forward_pattern_manage);
270
set_dev_proc(dev, fill_rectangle_hl_color, gx_forward_fill_rectangle_hl_color);
271
set_dev_proc(dev, include_color_space, gx_forward_include_color_space);
272
set_dev_proc(dev, update_spot_equivalent_colors,
273
gx_forward_update_spot_equivalent_colors);
274
set_dev_proc(dev, get_page_device, gx_forward_get_page_device);
275
set_dev_proc(dev, ret_devn_params, gx_forward_ret_devn_params);
276
gx_device_set_target((gx_device_forward *)dev, target);
278
gx_device_fill_in_procs((gx_device *)dev);
279
gx_device_forward_fill_in_procs((gx_device_forward *) dev);
281
dev->box_procs = box_procs_default;
282
dev->box_proc_data = dev;
283
bbox_copy_params(dev, false);
284
dev->free_standing = false; /* being used as a component */
287
/* Set whether a bounding box device propagates open/close to its target. */
289
gx_device_bbox_fwd_open_close(gx_device_bbox * dev, bool forward_open_close)
291
dev->forward_open_close = forward_open_close;
294
/* Set whether a bounding box device considers white to be opaque. */
296
gx_device_bbox_set_white_opaque(gx_device_bbox *bdev, bool white_is_opaque)
298
bdev->white_is_opaque = white_is_opaque;
300
(bdev->white_is_opaque ? gx_no_color_index : bdev->white);
303
/* Release a bounding box device. */
305
gx_device_bbox_release(gx_device_bbox *dev)
307
/* Just release the reference to the target. */
308
gx_device_set_target((gx_device_forward *)dev, NULL);
311
/* Read back the bounding box in 1/72" units. */
313
gx_device_bbox_bbox(gx_device_bbox * dev, gs_rect * pbbox)
317
BBOX_GET_BOX(dev, &bbox);
318
if (bbox.p.x > bbox.q.x || bbox.p.y > bbox.q.y) {
319
/* Nothing has been written on this page. */
320
pbbox->p.x = pbbox->p.y = pbbox->q.x = pbbox->q.y = 0;
325
dbox.p.x = fixed2float(bbox.p.x);
326
dbox.p.y = fixed2float(bbox.p.y);
327
dbox.q.x = fixed2float(bbox.q.x);
328
dbox.q.y = fixed2float(bbox.q.y);
329
gs_deviceinitialmatrix((gx_device *)dev, &mat);
330
gs_bbox_transform_inverse(&dbox, &mat, pbbox);
335
bbox_open_device(gx_device * dev)
337
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
339
if (bdev->free_standing) {
340
gx_device_forward_fill_in_procs((gx_device_forward *) dev);
341
bdev->box_procs = box_procs_default;
342
bdev->box_proc_data = bdev;
344
if (bdev->box_procs.init_box == box_procs_default.init_box)
346
/* gx_forward_open_device doesn't exist */
348
gx_device *tdev = bdev->target;
350
(tdev && bdev->forward_open_close ? gs_opendevice(tdev) : 0);
352
bbox_copy_params(bdev, true);
358
bbox_output_page(gx_device * dev, int num_copies, int flush)
360
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
362
if (bdev->free_standing) {
364
* This is a free-standing device. Print the page bounding box.
368
gx_device_bbox_bbox(bdev, &bbox);
369
dlprintf4("%%%%BoundingBox: %d %d %d %d\n",
370
(int)floor(bbox.p.x), (int)floor(bbox.p.y),
371
(int)ceil(bbox.q.x), (int)ceil(bbox.q.y));
372
dlprintf4("%%%%HiResBoundingBox: %f %f %f %f\n",
373
bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
375
return gx_forward_output_page(dev, num_copies, flush);
378
/* ---------------- Low-level drawing ---------------- */
381
bbox_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
382
gx_color_index color)
384
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
385
gx_device *tdev = bdev->target;
386
/* gx_forward_fill_rectangle doesn't exist */
389
dev_proc(tdev, fill_rectangle)(tdev, x, y, w, h, color));
391
/* Check for erasing the entire page. */
392
if (RECT_IS_PAGE(dev, x, y, w, h)) {
393
if (!BBOX_INIT_BOX(bdev))
396
if (color != bdev->transparent)
397
BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
402
bbox_copy_mono(gx_device * dev, const byte * data,
403
int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h,
404
gx_color_index zero, gx_color_index one)
406
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
407
/* gx_forward_copy_mono doesn't exist */
408
gx_device *tdev = bdev->target;
411
dev_proc(tdev, copy_mono)
412
(tdev, data, dx, raster, id, x, y, w, h, zero, one));
414
if ((one != gx_no_color_index && one != bdev->transparent) ||
415
(zero != gx_no_color_index && zero != bdev->transparent)
417
BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
422
bbox_copy_color(gx_device * dev, const byte * data,
423
int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h)
425
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
426
/* gx_forward_copy_color doesn't exist */
427
gx_device *tdev = bdev->target;
430
dev_proc(tdev, copy_color)
431
(tdev, data, dx, raster, id, x, y, w, h));
433
BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
438
bbox_copy_alpha(gx_device * dev, const byte * data, int data_x,
439
int raster, gx_bitmap_id id, int x, int y, int w, int h,
440
gx_color_index color, int depth)
442
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
443
/* gx_forward_copy_alpha doesn't exist */
444
gx_device *tdev = bdev->target;
447
dev_proc(tdev, copy_alpha)
448
(tdev, data, data_x, raster, id, x, y, w, h, color, depth));
450
BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
455
bbox_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
456
int x, int y, int w, int h, gx_color_index color0, gx_color_index color1,
459
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
460
/* Skip the call if there is no target. */
461
gx_device *tdev = bdev->target;
464
dev_proc(tdev, strip_tile_rectangle)
465
(tdev, tiles, x, y, w, h, color0, color1, px, py));
467
if (RECT_IS_PAGE(dev, x, y, w, h)) {
468
if (!BBOX_INIT_BOX(bdev))
471
BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
476
bbox_strip_copy_rop(gx_device * dev,
477
const byte * sdata, int sourcex, uint sraster,
479
const gx_color_index * scolors,
480
const gx_strip_bitmap * textures,
481
const gx_color_index * tcolors,
482
int x, int y, int w, int h,
483
int phase_x, int phase_y, gs_logical_operation_t lop)
485
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
486
/* gx_forward_strip_copy_rop doesn't exist */
487
gx_device *tdev = bdev->target;
490
dev_proc(tdev, strip_copy_rop)
491
(tdev, sdata, sourcex, sraster, id, scolors,
492
textures, tcolors, x, y, w, h, phase_x, phase_y, lop));
494
BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
498
/* ---------------- Parameters ---------------- */
500
/* We implement get_params to provide a way to read out the bounding box. */
502
bbox_get_params(gx_device * dev, gs_param_list * plist)
504
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
506
int code = gx_forward_get_params(dev, plist);
507
gs_param_float_array bba;
513
* We might be calling get_params before the device has been
514
* initialized: in this case, box_proc_data = 0.
516
if (bdev->box_proc_data == 0)
519
BBOX_GET_BOX(bdev, &fbox);
520
bbox[0] = fixed2float(fbox.p.x);
521
bbox[1] = fixed2float(fbox.p.y);
522
bbox[2] = fixed2float(fbox.q.x);
523
bbox[3] = fixed2float(fbox.q.y);
524
bba.data = bbox, bba.size = 4, bba.persistent = false;
525
code = param_write_float_array(plist, "PageBoundingBox", &bba);
528
code = param_write_bool(plist, "WhiteIsOpaque", &bdev->white_is_opaque);
532
/* We implement put_params to ensure that we keep the important */
533
/* device parameters up to date, and to prevent an /undefined error */
534
/* from PageBoundingBox. */
536
bbox_put_params(gx_device * dev, gs_param_list * plist)
538
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
541
bool white_is_opaque = bdev->white_is_opaque;
542
gs_param_name param_name;
543
gs_param_float_array bba;
545
code = param_read_float_array(plist, (param_name = "PageBoundingBox"),
550
ecode = gs_note_error(gs_error_rangecheck);
556
e:param_signal_error(plist, param_name, ecode);
561
switch (code = param_read_bool(plist, (param_name = "WhiteIsOpaque"), &white_is_opaque)) {
564
param_signal_error(plist, param_name, ecode);
570
code = gx_forward_put_params(dev, plist);
576
BBOX_ADD_RECT(bdev, float2fixed(bba.data[0]), float2fixed(bba.data[1]),
577
float2fixed(bba.data[2]), float2fixed(bba.data[3]));
579
bdev->white_is_opaque = white_is_opaque;
581
bbox_copy_params(bdev, bdev->is_open);
585
/* ---------------- Polygon drawing ---------------- */
588
edge_x_at_y(const gs_fixed_edge * edge, fixed y)
590
return fixed_mult_quo(edge->end.x - edge->start.x,
592
edge->end.y - edge->start.y) + edge->start.x;
595
bbox_fill_trapezoid(gx_device * dev,
596
const gs_fixed_edge * left, const gs_fixed_edge * right,
597
fixed ybot, fixed ytop, bool swap_axes,
598
const gx_device_color * pdevc, gs_logical_operation_t lop)
600
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
601
/* Skip the call if there is no target. */
602
gx_device *tdev = bdev->target;
605
dev_proc(tdev, fill_trapezoid)
606
(tdev, left, right, ybot, ytop, swap_axes, pdevc, lop));
608
if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
610
(left->start.y == ybot ? left->start.x :
611
edge_x_at_y(left, ybot));
613
(left->end.y == ytop ? left->end.x :
614
edge_x_at_y(left, ytop));
616
(right->start.y == ybot ? right->start.x :
617
edge_x_at_y(right, ybot));
619
(right->end.y == ytop ? right->end.x :
620
edge_x_at_y(right, ytop));
621
fixed xminl = min(x0l, x1l), xmaxl = max(x0l, x1l);
622
fixed xminr = min(x0r, x1r), xmaxr = max(x0r, x1r);
623
fixed x0 = min(xminl, xminr), x1 = max(xmaxl, xmaxr);
626
BBOX_ADD_RECT(bdev, ybot, x0, ytop, x1);
628
BBOX_ADD_RECT(bdev, x0, ybot, x1, ytop);
634
bbox_fill_parallelogram(gx_device * dev,
635
fixed px, fixed py, fixed ax, fixed ay,
636
fixed bx, fixed by, const gx_device_color * pdevc,
637
gs_logical_operation_t lop)
639
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
640
/* Skip the call if there is no target. */
641
gx_device *tdev = bdev->target;
644
dev_proc(tdev, fill_parallelogram)
645
(tdev, px, py, ax, ay, bx, by, pdevc, lop));
647
if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
648
fixed xmin, ymin, xmax, ymax;
650
/* bbox_add_rect requires points in correct order. */
651
#define SET_MIN_MAX(vmin, vmax, av, bv)\
655
vmin = av + bv, vmax = 0;\
657
vmin = av, vmax = bv;\
659
vmin = bv, vmax = av;\
661
vmin = 0, vmax = av + bv;\
663
SET_MIN_MAX(xmin, xmax, ax, bx);
664
SET_MIN_MAX(ymin, ymax, ay, by);
666
BBOX_ADD_RECT(bdev, px + xmin, py + ymin, px + xmax, py + ymax);
672
bbox_fill_triangle(gx_device * dev,
673
fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
674
const gx_device_color * pdevc, gs_logical_operation_t lop)
676
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
677
/* Skip the call if there is no target. */
678
gx_device *tdev = bdev->target;
681
dev_proc(tdev, fill_triangle)
682
(tdev, px, py, ax, ay, bx, by, pdevc, lop));
684
if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
685
fixed xmin, ymin, xmax, ymax;
687
/* bbox_add_rect requires points in correct order. */
688
#define SET_MIN_MAX(vmin, vmax, av, bv)\
692
vmin = min(av, bv), vmax = 0;\
694
vmin = av, vmax = bv;\
696
vmin = bv, vmax = av;\
698
vmin = 0, vmax = max(av, bv);\
700
SET_MIN_MAX(xmin, xmax, ax, bx);
701
SET_MIN_MAX(ymin, ymax, ay, by);
703
BBOX_ADD_RECT(bdev, px + xmin, py + ymin, px + xmax, py + ymax);
709
bbox_draw_thin_line(gx_device * dev,
710
fixed fx0, fixed fy0, fixed fx1, fixed fy1,
711
const gx_device_color * pdevc, gs_logical_operation_t lop)
713
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
714
/* Skip the call if there is no target. */
715
gx_device *tdev = bdev->target;
718
dev_proc(tdev, draw_thin_line)
719
(tdev, fx0, fy0, fx1, fy0, pdevc, lop));
721
if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
722
fixed xmin, ymin, xmax, ymax;
724
/* bbox_add_rect requires points in correct order. */
725
#define SET_MIN_MAX(vmin, vmax, av, bv)\
728
vmin = av, vmax = bv;\
730
vmin = bv, vmax = av;\
732
SET_MIN_MAX(xmin, xmax, fx0, fx1);
733
SET_MIN_MAX(ymin, ymax, fy0, fy1);
735
BBOX_ADD_RECT(bdev, xmin, ymin, xmax, ymax);
740
/* ---------------- High-level drawing ---------------- */
742
#define adjust_box(pbox, adj)\
743
((pbox)->p.x -= (adj).x, (pbox)->p.y -= (adj).y,\
744
(pbox)->q.x += (adj).x, (pbox)->q.y += (adj).y)
747
bbox_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
748
const gx_fill_params * params, const gx_device_color * pdevc,
749
const gx_clip_path * pcpath)
751
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
752
gx_device *tdev = bdev->target;
753
dev_proc_fill_path((*fill_path)) =
754
(tdev == 0 ? dev_proc(&gs_null_device, fill_path) :
755
dev_proc(tdev, fill_path));
759
/* A special handling of shfill with no path. */
761
gs_fixed_point adjust;
765
gx_cpath_inner_box(pcpath, &ibox);
766
adjust = params->adjust;
767
if (params->fill_zero_width)
768
gx_adjust_if_empty(&ibox, &adjust);
769
adjust_box(&ibox, adjust);
770
BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
772
} else if (!GX_DC_IS_TRANSPARENT(pdevc, bdev) && !gx_path_is_void(ppath)) {
774
gs_fixed_point adjust;
776
if (gx_path_bbox(ppath, &ibox) < 0)
778
adjust = params->adjust;
779
if (params->fill_zero_width)
780
gx_adjust_if_empty(&ibox, &adjust);
781
adjust_box(&ibox, adjust);
783
* If the path lies within the already accumulated box, just draw
786
if (BBOX_IN_RECT(bdev, &ibox))
787
return fill_path(tdev, pis, ppath, params, pdevc, pcpath);
789
* If the target uses the default algorithm, just draw on the
792
if (tdev != 0 && fill_path == gx_default_fill_path)
793
return fill_path(dev, pis, ppath, params, pdevc, pcpath);
794
/* Draw on the target now. */
795
code = fill_path(tdev, pis, ppath, params, pdevc, pcpath);
798
if (pcpath != NULL &&
799
!gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
803
* Let the target do the drawing, but break down the
804
* fill path into pieces for computing the bounding box.
806
gx_drawing_color devc;
808
set_nonclient_dev_color(&devc, bdev->black); /* any non-white color will do */
810
code = gx_default_fill_path(dev, pis, ppath, params, &devc, pcpath);
812
} else { /* Just use the path bounding box. */
813
BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
817
return fill_path(tdev, pis, ppath, params, pdevc, pcpath);
821
bbox_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
822
const gx_stroke_params * params,
823
const gx_drawing_color * pdevc, const gx_clip_path * pcpath)
825
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
826
gx_device *tdev = bdev->target;
827
/* Skip the call if there is no target. */
830
dev_proc(tdev, stroke_path)(tdev, pis, ppath, params, pdevc, pcpath));
832
if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) {
834
gs_fixed_point expand;
836
if (gx_stroke_path_expansion(pis, ppath, &expand) == 0 &&
837
gx_path_bbox(ppath, &ibox) >= 0
839
/* The fast result is exact. */
840
adjust_box(&ibox, expand);
843
* The result is not exact. Compute an exact result using
846
gx_path *spath = gx_path_alloc(pis->memory, "bbox_stroke_path");
850
code = gx_imager_stroke_add(ppath, spath, dev, pis);
854
code = gx_path_bbox(spath, &ibox);
856
ibox.p.x = ibox.p.y = min_fixed;
857
ibox.q.x = ibox.q.y = max_fixed;
860
gx_path_free(spath, "bbox_stroke_path");
862
if (pcpath != NULL &&
863
!gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
866
/* Let the target do the drawing, but break down the */
867
/* fill path into pieces for computing the bounding box. */
868
gx_drawing_color devc;
870
set_nonclient_dev_color(&devc, bdev->black); /* any non-white color will do */
872
gx_default_stroke_path(dev, pis, ppath, params, &devc, pcpath);
875
/* Just use the path bounding box. */
876
BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
883
bbox_fill_mask(gx_device * dev,
884
const byte * data, int dx, int raster, gx_bitmap_id id,
885
int x, int y, int w, int h,
886
const gx_drawing_color * pdcolor, int depth,
887
gs_logical_operation_t lop, const gx_clip_path * pcpath)
889
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
890
gx_device *tdev = bdev->target;
891
/* Skip the call if there is no target. */
894
dev_proc(tdev, fill_mask)
895
(tdev, data, dx, raster, id, x, y, w, h,
896
pdcolor, depth, lop, pcpath));
898
if (pcpath != NULL &&
899
!gx_cpath_includes_rectangle(pcpath, int2fixed(x), int2fixed(y),
903
/* Let the target do the drawing, but break down the */
904
/* image into pieces for computing the bounding box. */
906
gx_default_fill_mask(dev, data, dx, raster, id, x, y, w, h,
907
pdcolor, depth, lop, pcpath);
910
/* Just use the mask bounding box. */
911
BBOX_ADD_INT_RECT(bdev, x, y, x + w, y + h);
916
/* ------ Bitmap imaging ------ */
918
typedef struct bbox_image_enum_s {
919
gx_image_enum_common;
920
gs_matrix matrix; /* map from image space to device space */
921
const gx_clip_path *pcpath;
922
gx_image_enum_common_t *target_info;
923
bool params_are_const;
928
gs_private_st_suffix_add2(st_bbox_image_enum, bbox_image_enum,
929
"bbox_image_enum", bbox_image_enum_enum_ptrs, bbox_image_enum_reloc_ptrs,
930
st_gx_image_enum_common, pcpath, target_info);
932
static image_enum_proc_plane_data(bbox_image_plane_data);
933
static image_enum_proc_end_image(bbox_image_end_image);
934
static image_enum_proc_flush(bbox_image_flush);
935
static image_enum_proc_planes_wanted(bbox_image_planes_wanted);
936
static const gx_image_enum_procs_t bbox_image_enum_procs = {
937
bbox_image_plane_data, bbox_image_end_image,
938
bbox_image_flush, bbox_image_planes_wanted
942
bbox_image_begin(const gs_imager_state * pis, const gs_matrix * pmat,
943
const gs_image_common_t * pic, const gs_int_rect * prect,
944
const gx_clip_path * pcpath, gs_memory_t * memory,
945
bbox_image_enum ** ppbe)
949
bbox_image_enum *pbe;
952
pmat = &ctm_only(pis);
953
if ((code = gs_matrix_invert(&pic->ImageMatrix, &mat)) < 0 ||
954
(code = gs_matrix_multiply(&mat, pmat, &mat)) < 0
957
pbe = gs_alloc_struct(memory, bbox_image_enum, &st_bbox_image_enum,
960
return_error(gs_error_VMerror);
961
pbe->memory = memory;
963
pbe->pcpath = pcpath;
964
pbe->target_info = 0; /* in case no target */
965
pbe->params_are_const = false; /* check the first time */
967
pbe->x0 = prect->p.x, pbe->x1 = prect->q.x;
968
pbe->y = prect->p.y, pbe->height = prect->q.y - prect->p.y;
971
int code = (*pic->type->source_size) (pis, pic, &size);
974
gs_free_object(memory, pbe, "bbox_image_begin");
977
pbe->x0 = 0, pbe->x1 = size.x;
978
pbe->y = 0, pbe->height = size.y;
985
bbox_image_copy_target_info(bbox_image_enum * pbe)
987
const gx_image_enum_common_t *target_info = pbe->target_info;
989
pbe->num_planes = target_info->num_planes;
990
memcpy(pbe->plane_depths, target_info->plane_depths,
991
pbe->num_planes * sizeof(pbe->plane_depths[0]));
992
memcpy(pbe->plane_widths, target_info->plane_widths,
993
pbe->num_planes * sizeof(pbe->plane_widths[0]));
997
bbox_begin_typed_image(gx_device * dev,
998
const gs_imager_state * pis, const gs_matrix * pmat,
999
const gs_image_common_t * pic, const gs_int_rect * prect,
1000
const gx_drawing_color * pdcolor,
1001
const gx_clip_path * pcpath,
1002
gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
1004
bbox_image_enum *pbe;
1006
bbox_image_begin(pis, pmat, pic, prect, pcpath, memory, &pbe);
1011
* If there is no target, we still have to call default_begin_image
1012
* to get the correct num_planes and plane_depths.
1015
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
1016
gx_device *tdev = bdev->target;
1017
dev_proc_begin_typed_image((*begin_typed_image));
1018
byte wanted[GS_IMAGE_MAX_COMPONENTS];
1022
begin_typed_image = gx_default_begin_typed_image;
1024
begin_typed_image = dev_proc(tdev, begin_typed_image);
1026
code = (*begin_typed_image)
1027
(tdev, pis, pmat, pic, prect, pdcolor, pcpath, memory,
1030
bbox_image_end_image((gx_image_enum_common_t *)pbe, false);
1034
* We fill in num_planes and plane_depths later. format is
1035
* irrelevant. NOTE: we assume that if begin_typed_image returned
1036
* 0, the image is a data image.
1038
code = gx_image_enum_common_init((gx_image_enum_common_t *) pbe,
1039
(const gs_data_image_t *)pic,
1040
&bbox_image_enum_procs, dev,
1041
0, gs_image_format_chunky);
1044
bbox_image_copy_target_info(pbe);
1045
pbe->params_are_const =
1046
gx_image_planes_wanted(pbe->target_info, wanted);
1048
*pinfo = (gx_image_enum_common_t *) pbe;
1053
bbox_image_plane_data(gx_image_enum_common_t * info,
1054
const gx_image_plane_t * planes, int height,
1057
gx_device *dev = info->dev;
1058
gx_device_bbox *const bdev = (gx_device_bbox *)dev;
1059
gx_device *tdev = bdev->target;
1060
bbox_image_enum *pbe = (bbox_image_enum *) info;
1061
const gx_clip_path *pcpath = pbe->pcpath;
1063
gs_point corners[4];
1067
code = gx_image_plane_data_rows(pbe->target_info, planes, height,
1069
if (code != 1 && !pbe->params_are_const)
1070
bbox_image_copy_target_info(pbe);
1074
sbox.q.y = pbe->y = min(pbe->y + height, pbe->height);
1075
gs_bbox_transform_only(&sbox, &pbe->matrix, corners);
1076
gs_points_bbox(corners, &dbox);
1077
ibox.p.x = float2fixed(dbox.p.x);
1078
ibox.p.y = float2fixed(dbox.p.y);
1079
ibox.q.x = float2fixed(dbox.q.x);
1080
ibox.q.y = float2fixed(dbox.q.y);
1081
if (pcpath != NULL &&
1082
!gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
1085
/* Let the target do the drawing, but drive two triangles */
1086
/* through the clipping path to get an accurate bounding box. */
1087
gx_device_clip cdev;
1088
gx_drawing_color devc;
1089
fixed x0 = float2fixed(corners[0].x), y0 = float2fixed(corners[0].y);
1090
fixed bx2 = float2fixed(corners[2].x) - x0, by2 = float2fixed(corners[2].y) - y0;
1092
gx_make_clip_device_on_stack(&cdev, pcpath, dev);
1093
set_nonclient_dev_color(&devc, bdev->black); /* any non-white color will do */
1094
bdev->target = NULL;
1095
gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
1096
float2fixed(corners[1].x) - x0,
1097
float2fixed(corners[1].y) - y0,
1098
bx2, by2, &devc, lop_default);
1099
gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
1100
float2fixed(corners[3].x) - x0,
1101
float2fixed(corners[3].y) - y0,
1102
bx2, by2, &devc, lop_default);
1103
bdev->target = tdev;
1105
/* Just use the bounding box. */
1106
BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
1112
bbox_image_end_image(gx_image_enum_common_t * info, bool draw_last)
1114
bbox_image_enum *pbe = (bbox_image_enum *) info;
1115
int code = gx_image_end(pbe->target_info, draw_last);
1117
gx_image_free_enum(&info);
1122
bbox_image_flush(gx_image_enum_common_t * info)
1124
bbox_image_enum *pbe = (bbox_image_enum *) info;
1125
gx_image_enum_common_t *target_info = pbe->target_info;
1127
return (target_info ? gx_image_flush(target_info) : 0);
1131
bbox_image_planes_wanted(const gx_image_enum_common_t * info, byte *wanted)
1133
/* This is only used if target_info != 0. */
1134
const bbox_image_enum *pbe = (const bbox_image_enum *)info;
1136
return gx_image_planes_wanted(pbe->target_info, wanted);
1142
bbox_forward_init_box(void *pdata)
1144
gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
1146
return BBOX_INIT_BOX(bdev);
1149
bbox_forward_get_box(const void *pdata, gs_fixed_rect *pbox)
1151
const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
1153
BBOX_GET_BOX(bdev, pbox);
1156
bbox_forward_add_rect(void *pdata, fixed x0, fixed y0, fixed x1, fixed y1)
1158
gx_device_bbox *const bdev = (gx_device_bbox *)pdata;
1160
BBOX_ADD_RECT(bdev, x0, y0, x1, y1);
1163
bbox_forward_in_rect(const void *pdata, const gs_fixed_rect *pbox)
1165
const gx_device_bbox *const bdev = (const gx_device_bbox *)pdata;
1167
return BBOX_IN_RECT(bdev, pbox);
1169
static const gx_device_bbox_procs_t box_procs_forward = {
1170
bbox_forward_init_box, bbox_forward_get_box, bbox_forward_add_rect,
1171
bbox_forward_in_rect
1175
bbox_create_compositor(gx_device * dev,
1176
gx_device ** pcdev, const gs_composite_t * pcte,
1177
gs_imager_state * pis, gs_memory_t * memory)
1179
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
1180
gx_device *target = bdev->target;
1183
* If there isn't a target, all we care about is the bounding box,
1184
* so don't bother with actually compositing.
1191
* Create a compositor for the target, and then wrap another
1192
* bbox device around it, but still accumulating the bounding
1193
* box in the same place.
1197
gx_device_bbox *bbcdev;
1198
int code = (*dev_proc(target, create_compositor))
1199
(target, &cdev, pcte, pis, memory);
1201
/* If the target did not create a new compositor then we are done. */
1202
if (code < 0 || target == cdev) {
1206
bbcdev = gs_alloc_struct_immovable(memory, gx_device_bbox,
1208
"bbox_create_compositor");
1210
(*dev_proc(cdev, close_device)) (cdev);
1211
return_error(gs_error_VMerror);
1213
gx_device_bbox_init(bbcdev, target, memory);
1214
gx_device_set_target((gx_device_forward *)bbcdev, cdev);
1215
bbcdev->box_procs = box_procs_forward;
1216
bbcdev->box_proc_data = bdev;
1217
*pcdev = (gx_device *) bbcdev;
1222
/* ------ Text imaging ------ */
1225
bbox_text_begin(gx_device * dev, gs_imager_state * pis,
1226
const gs_text_params_t * text, gs_font * font,
1227
gx_path * path, const gx_device_color * pdcolor,
1228
const gx_clip_path * pcpath,
1229
gs_memory_t * memory, gs_text_enum_t ** ppenum)
1231
gx_device_bbox *const bdev = (gx_device_bbox *) dev;
1232
int code = gx_default_text_begin(dev, pis, text, font, path, pdcolor,
1233
pcpath, memory, ppenum);
1235
if (bdev->target != NULL) {
1236
/* See note on imaging_dev in gxtext.h */
1237
rc_assign((*ppenum)->imaging_dev, dev, "bbox_text_begin");