Coverage Report

Created: 2025-06-24 07:01

/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
}