Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gspath.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
17
/* Basic path routines for Ghostscript library */
18
#include "gx.h"
19
#include "math_.h"
20
#include "gserrors.h"
21
#include "gxfixed.h"
22
#include "gxmatrix.h"
23
#include "gscoord.h"    /* requires gsmatrix.h */
24
#include "gspath.h"   /* for checking prototypes */
25
#include "gzstate.h"
26
#include "gzpath.h"
27
#include "gxdevice.h"   /* for gxcpath.h */
28
#include "gxdevmem.h"   /* for gs_device_is_memory */
29
#include "gzcpath.h"
30
#include "gxpaint.h"
31
32
/* ------ Miscellaneous ------ */
33
34
int
35
gs_newpath(gs_gstate * pgs)
36
47.6M
{
37
47.6M
    pgs->current_point_valid = false;
38
47.6M
    return gx_path_new(pgs->path);
39
47.6M
}
40
41
int
42
gs_closepath(gs_gstate * pgs)
43
6.53M
{
44
6.53M
    gx_path *ppath = pgs->path;
45
6.53M
    int code = gx_path_close_subpath(ppath);
46
47
6.53M
    if (code < 0)
48
0
        return code;
49
6.53M
    pgs->current_point = pgs->subpath_start;
50
6.53M
    return code;
51
6.53M
}
52
53
int
54
gs_upmergepath(gs_gstate * pgs)
55
0
{
56
    /*
57
     * We really should be able to implement this as simply
58
     *   return gx_path_add_path(pgs->saved->path, pgs->path);
59
     * But because of the current_point members in the gs_gstate,
60
     * we can't.
61
     */
62
0
    gs_gstate *saved = pgs->saved;
63
0
    int code;
64
65
0
    code = gx_path_add_path(saved->path, pgs->path);
66
0
    if (code < 0)
67
0
        return code;
68
0
    if (pgs->current_point_valid) {
69
0
        saved->current_point = pgs->current_point;
70
0
        saved->subpath_start = pgs->subpath_start;
71
0
        saved->current_point_valid = true;
72
0
    }
73
0
    return code;
74
0
}
75
76
/* Get the current path (for internal use only). */
77
gx_path *
78
gx_current_path(const gs_gstate * pgs)
79
4.43M
{
80
4.43M
    return pgs->path;
81
4.43M
}
82
83
/* ------ Points and lines ------ */
84
85
static inline void
86
clamp_point(gs_fixed_point * ppt, double x, double y)
87
5.87M
{
88
5.87M
    ppt->x = clamp_coord(x);
89
5.87M
    ppt->y = clamp_coord(y);
90
5.87M
}
91
92
int
93
gs_currentpoint(gs_gstate * pgs, gs_point * ppt)
94
26.3M
{
95
26.3M
    if (!pgs->current_point_valid)
96
205k
        return_error(gs_error_nocurrentpoint);
97
26.1M
    return gs_itransform(pgs, pgs->current_point.x,
98
26.1M
                              pgs->current_point.y, ppt);
99
26.3M
}
100
101
static inline int
102
gs_point_transform_compat(double x, double y, const gs_matrix_fixed *m, gs_point *pt)
103
86.9M
{
104
#if !PRECISE_CURRENTPOINT
105
    gs_fixed_point p;
106
    int code = gs_point_transform2fixed(m, x, y, &p);
107
108
    if (code < 0)
109
        return code;
110
    pt->x = fixed2float(p.x);
111
    pt->y = fixed2float(p.y);
112
    return 0;
113
#else
114
86.9M
    return gs_point_transform(x, y, (const gs_matrix *)m, pt);
115
86.9M
#endif
116
86.9M
}
117
118
static inline int
119
gs_distance_transform_compat(double x, double y, const gs_matrix_fixed *m, gs_point *pt)
120
24.7M
{
121
#if !PRECISE_CURRENTPOINT
122
    gs_fixed_point p;
123
    int code = gs_distance_transform2fixed(m, x, y, &p);
124
125
    if (code < 0)
126
        return code;
127
    pt->x = fixed2float(p.x);
128
    pt->y = fixed2float(p.y);
129
    return 0;
130
#else
131
24.7M
    return gs_distance_transform(x, y, (const gs_matrix *)m, pt);
132
24.7M
#endif
133
24.7M
}
134
135
static inline int
136
clamp_point_aux(bool clamp_coordinates, gs_fixed_point *ppt, double x, double y)
137
178M
{
138
178M
    if (!f_fits_in_bits(x, fixed_int_bits) || !f_fits_in_bits(y, fixed_int_bits)) {
139
5.87M
        if (!clamp_coordinates)
140
0
            return_error(gs_error_limitcheck);
141
5.87M
        clamp_point(ppt, x, y);
142
172M
    } else {
143
        /* 181-01.ps" fails with no rounding in
144
           "Verify as last element of a userpath and effect on setbbox." */
145
172M
        ppt->x = float2fixed_rounded(x);
146
172M
        ppt->y = float2fixed_rounded(y);
147
172M
    }
148
178M
    return 0;
149
178M
}
150
151
int
152
gs_moveto_aux(gs_gstate *pgs, gx_path *ppath, double x, double y)
153
113M
{
154
113M
    gs_fixed_point pt;
155
113M
    int code;
156
157
113M
    code = clamp_point_aux(pgs->clamp_coordinates, &pt, x, y);
158
113M
    if (code < 0)
159
0
        return code;
160
113M
    if (pgs->hpgl_path_mode && path_subpath_open(ppath))
161
0
    {
162
0
        code = gx_path_add_gap_notes(ppath, pt.x, pt.y, 0);
163
0
        if (code < 0)
164
0
            return code;
165
0
        gx_setcurrentpoint(pgs, x, y);
166
0
    }
167
113M
    else
168
113M
    {
169
113M
        code = gx_path_add_point(ppath, pt.x, pt.y);
170
113M
        if (code < 0)
171
0
            return code;
172
113M
        ppath->start_flags = ppath->state_flags;
173
113M
        gx_setcurrentpoint(pgs, x, y);
174
113M
        pgs->subpath_start = pgs->current_point;
175
113M
    }
176
113M
    pgs->current_point_valid = true;
177
113M
    return 0;
178
113M
}
179
180
int
181
gs_moveto(gs_gstate * pgs, double x, double y)
182
32.3M
{
183
32.3M
    gs_point pt;
184
32.3M
    int code = gs_point_transform_compat(x, y, &pgs->ctm, &pt);
185
186
32.3M
    if (code < 0)
187
0
        return code;
188
32.3M
    return gs_moveto_aux(pgs, pgs->path, pt.x, pt.y);
189
32.3M
}
190
191
int
192
gs_rmoveto(gs_gstate * pgs, double x, double y)
193
14.8M
{
194
14.8M
    gs_point dd;
195
14.8M
    int code;
196
197
14.8M
    if (!pgs->current_point_valid)
198
15.2k
        return_error(gs_error_nocurrentpoint);
199
14.8M
    code = gs_distance_transform_compat(x, y, &pgs->ctm, &dd);
200
14.8M
    if (code < 0)
201
0
        return code;
202
    /* fixme : check in range. */
203
14.8M
    return gs_moveto_aux(pgs, pgs->path,
204
14.8M
                dd.x + pgs->current_point.x, dd.y + pgs->current_point.y);
205
14.8M
}
206
207
static inline int
208
gs_lineto_aux(gs_gstate * pgs, double x, double y)
209
28.1M
{
210
28.1M
    gx_path *ppath = pgs->path;
211
28.1M
    gs_fixed_point pt;
212
28.1M
    int code;
213
214
28.1M
    code = clamp_point_aux(pgs->clamp_coordinates, &pt, x, y);
215
28.1M
    if (code < 0)
216
0
        return code;
217
28.1M
    code = gx_path_add_line(ppath, pt.x, pt.y);
218
28.1M
    if (code < 0)
219
192k
        return code;
220
27.9M
    gx_setcurrentpoint(pgs, x, y);
221
27.9M
    return 0;
222
28.1M
}
223
224
int
225
gs_lineto(gs_gstate * pgs, double x, double y)
226
18.2M
{
227
18.2M
    gs_point pt;
228
18.2M
    int code = gs_point_transform_compat(x, y, &pgs->ctm, &pt);
229
230
18.2M
    if (code < 0)
231
0
        return code;
232
18.2M
    return gs_lineto_aux(pgs, pt.x, pt.y);
233
18.2M
}
234
235
int
236
gs_rlineto(gs_gstate * pgs, double x, double y)
237
9.94M
{
238
9.94M
    gs_point dd;
239
9.94M
    int code;
240
241
9.94M
    if (!pgs->current_point_valid)
242
15
        return_error(gs_error_nocurrentpoint);
243
9.94M
    code = gs_distance_transform_compat(x, y, &pgs->ctm, &dd);
244
9.94M
    if (code < 0)
245
0
        return code;
246
    /* fixme : check in range. */
247
9.94M
    return gs_lineto_aux(pgs, dd.x + pgs->current_point.x,
248
9.94M
                              dd.y + pgs->current_point.y);
249
9.94M
}
250
251
/* ------ Curves ------ */
252
253
static inline int
254
gs_curveto_aux(gs_gstate * pgs,
255
           double x1, double y1, double x2, double y2, double x3, double y3)
256
12.1M
{
257
12.1M
    gs_fixed_point p1, p2, p3;
258
12.1M
    int code;
259
12.1M
    gx_path *ppath = pgs->path;
260
261
12.1M
    code = clamp_point_aux(pgs->clamp_coordinates, &p1, x1, y1);
262
12.1M
    if (code < 0)
263
0
        return code;
264
12.1M
    code = clamp_point_aux(pgs->clamp_coordinates, &p2, x2, y2);
265
12.1M
    if (code < 0)
266
0
        return code;
267
12.1M
    code = clamp_point_aux(pgs->clamp_coordinates, &p3, x3, y3);
268
12.1M
    if (code < 0)
269
0
        return code;
270
12.1M
    code = gx_path_add_curve(ppath, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
271
12.1M
    if (code < 0)
272
14.7k
        return code;
273
12.1M
    gx_setcurrentpoint(pgs, x3, y3);
274
12.1M
    return 0;
275
12.1M
}
276
277
int
278
gs_curveto(gs_gstate * pgs,
279
           double x1, double y1, double x2, double y2, double x3, double y3)
280
12.1M
{
281
12.1M
    gs_point pt1, pt2, pt3;
282
12.1M
    int code;
283
284
12.1M
    code = gs_point_transform_compat(x1, y1, &pgs->ctm, &pt1);
285
12.1M
    if (code < 0)
286
0
        return code;
287
12.1M
    code = gs_point_transform_compat(x2, y2, &pgs->ctm, &pt2);
288
12.1M
    if (code < 0)
289
0
        return code;
290
12.1M
    code = gs_point_transform_compat(x3, y3, &pgs->ctm, &pt3);
291
12.1M
    if (code < 0)
292
0
        return code;
293
12.1M
    return gs_curveto_aux(pgs,   pt1.x, pt1.y,   pt2.x, pt2.y,   pt3.x, pt3.y);
294
12.1M
}
295
296
int
297
gs_rcurveto(gs_gstate * pgs,
298
     double dx1, double dy1, double dx2, double dy2, double dx3, double dy3)
299
13
{
300
13
    gs_point dd1, dd2, dd3;
301
13
    int code;
302
303
13
    if (!pgs->current_point_valid)
304
11
        return_error(gs_error_nocurrentpoint);
305
2
    code = gs_distance_transform_compat(dx1, dy1, &pgs->ctm, &dd1);
306
2
    if (code < 0)
307
0
        return code;
308
2
    code = gs_distance_transform_compat(dx2, dy2, &pgs->ctm, &dd2);
309
2
    if (code < 0)
310
0
        return code;
311
2
    code = gs_distance_transform_compat(dx3, dy3, &pgs->ctm, &dd3);
312
2
    if (code < 0)
313
0
        return code;
314
    /* fixme : check in range. */
315
2
    return gs_curveto_aux(pgs, dd1.x + pgs->current_point.x, dd1.y + pgs->current_point.y,
316
2
                               dd2.x + pgs->current_point.x, dd2.y + pgs->current_point.y,
317
2
                               dd3.x + pgs->current_point.x, dd3.y + pgs->current_point.y);
318
2
}
319
320
/* ------ Clipping ------ */
321
322
/* Forward references */
323
static int common_clip(gs_gstate *, int);
324
325
/* Figure out the bbox for a path and a clip path with adjustment if we are
326
   also doing a stroke.  This is used by the xps interpeter to deteremine
327
   how big of a transparency group or softmask should be pushed.  Often in
328
   xps we fill a path with a particular softmask and some other graphic object.
329
   The transparency group will be the intersection of the path and clipping
330
   path */
331
int
332
gx_curr_fixed_bbox(gs_gstate * pgs, gs_fixed_rect *bbox, gs_bbox_comp_t comp_type)
333
90.8k
{
334
90.8k
    int code;
335
90.8k
    gx_clip_path *clip_path;
336
90.8k
    gs_fixed_rect path_bbox;
337
90.8k
    int expansion_code;
338
90.8k
    bool include_path = true;
339
90.8k
    gs_fixed_point expansion;
340
341
90.8k
    code = gx_effective_clip_path(pgs, &clip_path);
342
90.8k
    if (code < 0 || clip_path == NULL) {
343
0
        bbox->p.x = bbox->p.y = bbox->q.x = bbox->q.y = 0;
344
0
        return (code < 0) ? code : gs_error_unknownerror;
345
90.8k
    } else {
346
90.8k
        *bbox = clip_path->outer_box;
347
90.8k
    }
348
90.8k
    if (comp_type == NO_PATH) {
349
90.8k
       return 0;
350
90.8k
    }
351
0
    code = gx_path_bbox(pgs->path, &path_bbox);
352
0
    if (code < 0)
353
0
        return code;
354
0
    if (comp_type == PATH_STROKE) {
355
        /* Handle any stroke expansion of our bounding box */
356
0
        expansion_code = gx_stroke_path_expansion(pgs, pgs->path, &expansion);
357
0
        if (expansion_code >= 0) {
358
0
            path_bbox.p.x -= expansion.x;
359
0
            path_bbox.p.y -= expansion.y;
360
0
            path_bbox.q.x += expansion.x;
361
0
            path_bbox.q.y += expansion.y;
362
0
        } else {
363
            /* Stroke is super wide or we could not figure out the stroke bbox
364
               due to wacky joints etc. Just use the clip path */
365
0
            include_path = false;
366
0
        }
367
0
    }
368
0
    if (include_path) {
369
0
        rect_intersect(*bbox, path_bbox);
370
0
    }
371
0
    return 0;
372
0
}
373
374
/* A variation of the above that returns a gs_rect (double) bbox */
375
int
376
gx_curr_bbox(gs_gstate * pgs, gs_rect *bbox, gs_bbox_comp_t comp_type)
377
0
{
378
0
    gs_fixed_rect curr_fixed_bbox;
379
380
0
    gx_curr_fixed_bbox(pgs, &curr_fixed_bbox, comp_type);
381
0
    bbox->p.x = fixed2float(curr_fixed_bbox.p.x);
382
0
    bbox->p.y = fixed2float(curr_fixed_bbox.p.y);
383
0
    bbox->q.x = fixed2float(curr_fixed_bbox.q.x);
384
0
    bbox->q.y = fixed2float(curr_fixed_bbox.q.y);
385
0
    return 0;
386
0
}
387
388
/*
389
 * Return the effective clipping path of a graphics state.  Sometimes this
390
 * is the intersection of the clip path and the view clip path; sometimes it
391
 * is just the clip path.  We aren't sure what the correct algorithm is for
392
 * this: for now, we use view clipping unless the current device is a memory
393
 * device.  This takes care of the most important case, where the current
394
 * device is a cache device.
395
 */
396
int
397
gx_effective_clip_path(gs_gstate * pgs, gx_clip_path ** ppcpath)
398
101M
{
399
101M
    gs_id view_clip_id =
400
101M
        (pgs->view_clip == 0 || pgs->view_clip->rule == 0 ? gs_no_id :
401
101M
         pgs->view_clip->id);
402
403
101M
    if (pgs->device == NULL || gs_device_is_memory(pgs->device) || pgs->clip_path == NULL) {
404
15.0k
        *ppcpath = pgs->clip_path;
405
15.0k
        return 0;
406
15.0k
    }
407
101M
    if (pgs->effective_clip_id == pgs->clip_path->id &&
408
101M
        pgs->effective_view_clip_id == view_clip_id
409
101M
        ) {
410
93.9M
        *ppcpath = pgs->effective_clip_path;
411
93.9M
        return 0;
412
93.9M
    }
413
    /* Update the cache. */
414
7.99M
    if (view_clip_id == gs_no_id) {
415
7.99M
        if (!pgs->effective_clip_shared)
416
27.4k
            gx_cpath_free(pgs->effective_clip_path, "gx_effective_clip_path");
417
7.99M
        pgs->effective_clip_path = pgs->clip_path;
418
7.99M
        pgs->effective_clip_shared = true;
419
7.99M
    } else {
420
0
        gs_fixed_rect cbox, vcbox;
421
422
0
        gx_cpath_inner_box(pgs->clip_path, &cbox);
423
0
        gx_cpath_outer_box(pgs->view_clip, &vcbox);
424
0
        if (rect_within(vcbox, cbox)) {
425
0
            if (!pgs->effective_clip_shared)
426
0
                gx_cpath_free(pgs->effective_clip_path,
427
0
                              "gx_effective_clip_path");
428
0
            pgs->effective_clip_path = pgs->view_clip;
429
0
            pgs->effective_clip_shared = true;
430
0
        } else {
431
            /* Construct the intersection of the two clip paths. */
432
0
            int code;
433
0
            gx_clip_path ipath;
434
0
            gx_path vpath;
435
0
            gx_clip_path *npath = pgs->effective_clip_path;
436
437
0
            if (pgs->effective_clip_shared) {
438
0
                npath = gx_cpath_alloc(pgs->memory, "gx_effective_clip_path");
439
0
                if (npath == 0)
440
0
                    return_error(gs_error_VMerror);
441
0
            }
442
0
            gx_cpath_init_local(&ipath, pgs->memory);
443
0
            code = gx_cpath_assign_preserve(&ipath, pgs->clip_path);
444
0
            if (code < 0)
445
0
                return code;
446
0
            gx_path_init_local(&vpath, pgs->memory);
447
0
            code = gx_cpath_to_path(pgs->view_clip, &vpath);
448
0
            if (code < 0 ||
449
0
                (code = gx_cpath_clip(pgs, &ipath, &vpath,
450
0
                                      gx_rule_winding_number)) < 0 ||
451
0
                (code = gx_cpath_assign_free(npath, &ipath)) < 0
452
0
                )
453
0
                DO_NOTHING;
454
0
            gx_path_free(&vpath, "gx_effective_clip_path");
455
0
            gx_cpath_free(&ipath, "gx_effective_clip_path");
456
0
            if (code < 0)
457
0
                return code;
458
0
            pgs->effective_clip_path = npath;
459
0
            pgs->effective_clip_shared = false;
460
0
        }
461
0
    }
462
7.99M
    pgs->effective_clip_id = pgs->effective_clip_path->id;
463
7.99M
    pgs->effective_view_clip_id = view_clip_id;
464
7.99M
    *ppcpath = pgs->effective_clip_path;
465
7.99M
    return 0;
466
7.99M
}
467
468
#ifdef DEBUG
469
/* Note that we just set the clipping path (internal). */
470
static void
471
note_set_clip_path(const gs_gstate * pgs)
472
{
473
    if (gs_debug_c('P')) {
474
        dmlprintf(pgs->memory, "[P]Clipping path:\n");
475
        gx_cpath_print(pgs->memory, pgs->clip_path);
476
    }
477
}
478
#else
479
10.4M
#  define note_set_clip_path(pgs) DO_NOTHING
480
#endif
481
482
int
483
gs_clippath(gs_gstate * pgs)
484
28.0k
{
485
28.0k
    gx_path cpath;
486
28.0k
    int code;
487
488
28.0k
    gx_path_init_local(&cpath, pgs->path->memory);
489
28.0k
    code = gx_cpath_to_path(pgs->clip_path, &cpath);
490
28.0k
    if (code >= 0) {
491
28.0k
        code = gx_path_assign_free(pgs->path, &cpath);
492
28.0k
        pgs->current_point.x = fixed2float(pgs->path->position.x);
493
28.0k
        pgs->current_point.y = fixed2float(pgs->path->position.y);
494
28.0k
        pgs->current_point_valid = true;
495
28.0k
    }
496
28.0k
    if (code < 0)
497
0
        gx_path_free(&cpath, "gs_clippath");
498
28.0k
    return code;
499
28.0k
}
500
501
int
502
gs_initclip(gs_gstate * pgs)
503
3.01M
{
504
3.01M
    gs_fixed_rect box;
505
3.01M
    int code = gx_default_clip_box(pgs, &box);
506
507
3.01M
    if (code < 0)
508
0
        return code;
509
3.01M
    return gx_clip_to_rectangle(pgs, &box);
510
3.01M
}
511
512
int
513
gs_clip(gs_gstate * pgs)
514
1.07M
{
515
1.07M
    return common_clip(pgs, gx_rule_winding_number);
516
1.07M
}
517
518
int
519
gs_eoclip(gs_gstate * pgs)
520
58.0k
{
521
58.0k
    return common_clip(pgs, gx_rule_even_odd);
522
58.0k
}
523
524
static int
525
common_clip(gs_gstate * pgs, int rule)
526
1.13M
{
527
1.13M
    int code = gx_cpath_clip(pgs, pgs->clip_path, pgs->path, rule);
528
1.13M
    if (code < 0)
529
0
        return code;
530
1.13M
    pgs->clip_path->rule = rule;
531
1.13M
    note_set_clip_path(pgs);
532
1.13M
    return 0;
533
1.13M
}
534
535
/* Establish a rectangle as the clipping path. */
536
/* Used by initclip and by the character and Pattern cache logic. */
537
int
538
gx_clip_to_rectangle(gs_gstate * pgs, gs_fixed_rect * pbox)
539
9.28M
{
540
9.28M
    int code = gx_cpath_from_rectangle(pgs->clip_path, pbox);
541
542
9.28M
    if (code < 0)
543
0
        return code;
544
9.28M
    pgs->clip_path->rule = gx_rule_winding_number;
545
    /* We are explicitly setting the clip to a specific rectangle, the path list
546
     * must therefore be reset (it bears no relation to the actual clip now).
547
     */
548
9.28M
    rc_decrement(pgs->clip_path->path_list, "gx_clip_to_rectangle");
549
9.28M
    pgs->clip_path->path_list = 0;
550
9.28M
    note_set_clip_path(pgs);
551
9.28M
    return 0;
552
9.28M
}
553
554
/* Set the clipping path to the current path, without intersecting. */
555
/* This is very inefficient right now. */
556
int
557
gx_clip_to_path(gs_gstate * pgs)
558
0
{
559
0
    gs_fixed_rect bbox;
560
0
    int code;
561
562
0
    if ((code = gx_path_bbox(pgs->path, &bbox)) < 0 ||
563
0
        (code = gx_clip_to_rectangle(pgs, &bbox)) < 0 ||
564
0
        (code = gs_clip(pgs)) < 0
565
0
        )
566
0
        return code;
567
0
    note_set_clip_path(pgs);
568
0
    return 0;
569
0
}
570
571
/* Get the default clipping box. */
572
int
573
gx_default_clip_box(const gs_gstate * pgs, gs_fixed_rect * pbox)
574
3.01M
{
575
3.01M
    register gx_device *dev = gs_currentdevice(pgs);
576
3.01M
    gs_rect bbox;
577
3.01M
    gs_matrix imat;
578
3.01M
    int code;
579
580
3.01M
    if (dev->ImagingBBox_set) { /* Use the ImagingBBox, relative to default user space. */
581
0
        gs_defaultmatrix(pgs, &imat);
582
0
        bbox.p.x = dev->ImagingBBox[0];
583
0
        bbox.p.y = dev->ImagingBBox[1];
584
0
        bbox.q.x = dev->ImagingBBox[2];
585
0
        bbox.q.y = dev->ImagingBBox[3];
586
3.01M
    } else {     /* Use the MediaSize indented by the HWMargins, */
587
        /* relative to unrotated user space adjusted by */
588
        /* the Margins.  (We suspect this isn't quite right, */
589
        /* but the whole issue of "margins" is such a mess that */
590
        /* we don't think we can do any better.) */
591
3.01M
        (*dev_proc(dev, get_initial_matrix)) (dev, &imat);
592
        /* Adjust for the Margins. */
593
3.01M
        imat.tx += dev->Margins[0];
594
3.01M
        imat.ty += dev->Margins[1];
595
3.01M
        bbox.p.x = dev->HWMargins[0];
596
3.01M
        bbox.p.y = dev->HWMargins[1];
597
3.01M
        bbox.q.x = dev->MediaSize[0] - dev->HWMargins[2];
598
3.01M
        bbox.q.y = dev->MediaSize[1] - dev->HWMargins[3];
599
3.01M
    }
600
3.01M
    code = gs_bbox_transform(&bbox, &imat, &bbox);
601
3.01M
    if (code < 0)
602
0
        return code;
603
    /* Round the clipping box so that it doesn't get ceilinged. */
604
3.01M
    pbox->p.x = fixed_rounded(float2fixed(bbox.p.x));
605
3.01M
    pbox->p.y = fixed_rounded(float2fixed(bbox.p.y));
606
3.01M
    pbox->q.x = fixed_rounded(float2fixed(bbox.q.x));
607
3.01M
    pbox->q.y = fixed_rounded(float2fixed(bbox.q.y));
608
3.01M
    return 0;
609
3.01M
}