Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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