Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/base/gxstroke.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2022 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.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, for further information.
14
*/
15
16
17
/* Path stroking procedures for Ghostscript library */
18
#include "math_.h"
19
#include <stdlib.h> /* abs() */
20
#include "gx.h"
21
#include "gpcheck.h"
22
#include "gserrors.h"
23
#include "gsdcolor.h"
24
#include "gsptype1.h"
25
#include "gsptype2.h"
26
#include "gxfixed.h"
27
#include "gxfarith.h"
28
#include "gxmatrix.h"
29
#include "gscoord.h"
30
#include "gsdevice.h"
31
#include "gxdevice.h"
32
#include "gxhttile.h"
33
#include "gxgstate.h"
34
#include "gzline.h"
35
#include "gzpath.h"
36
#include "gzcpath.h"
37
#include "gxpaint.h"
38
#include "gsstate.h"            /* for gs_currentcpsimode */
39
#include "gzacpath.h"
40
41
/* RJW: There appears to be a difference in the xps and postscript models
42
 * (at least in as far as Microsofts implementation of xps and Acrobats of
43
 * postscript). Acrobat (and ghostscript) are happy to join a line segment
44
 * around a corner, even when the next segment is a dash gap. Microsofts
45
 * implementation of XPS does not.
46
 *
47
 * A test file that shows this up is tests_private/comparefiles/298-05.ps
48
 *
49
 * Enabling the following define would emulate xps behaviour here.
50
 */
51
#undef AVOID_JOINING_TO_DASH_GAPS
52
53
/*
54
 * We don't really know whether it's a good idea to take fill adjustment
55
 * into account for stroking.  Disregarding it means that strokes
56
 * come out thinner than fills; observing it produces heavy-looking
57
 * strokes at low resolutions.  But in any case, we must disregard it
58
 * when stroking zero-width lines.
59
 */
60
#define USE_FILL_ADJUSTMENT
61
62
#ifdef USE_FILL_ADJUSTMENT
63
#  define STROKE_ADJUSTMENT(thin, pgs, xy)\
64
38.6M
     (thin ? fixed_0 : (pgs)->fill_adjust.xy)
65
#else
66
#  define STROKE_ADJUSTMENT(thin, pgs, xy) fixed_0
67
#endif
68
69
/*
70
 * For some reason, we commented out the optimization for portrait,
71
 * landscape, and uniform (non-scaled) transformations.  We have no record
72
 * of why we did this, and we don't know what bugs re-enabling it may
73
 * introduce.
74
 */
75
#define OPTIMIZE_ORIENTATION
76
77
/*
78
 * Compute the amount by which to expand a stroked bounding box to account
79
 * for line width, caps and joins.  Return 0 if the result is exact, 1 if
80
 * it may be conservative, or gs_error_limitcheck if the result is too
81
 * large to fit in a gs_fixed_point.
82
 *
83
 * Because of square caps and miter and triangular joins, the maximum
84
 * expansion on each side (in user space) is
85
 *      K * line_width/2
86
 * where K is determined as follows:
87
 *      For round or butt caps, E = 1
88
 *      For square caps, E = sqrt(2)
89
 *        If the path is only a single line segment, K = E;
90
 *          if triangular joins, K = 2;
91
 *          if miter joins, K = max(miter_limit, E);
92
 *      otherwise, K = E.
93
 *
94
 * If the following conditions apply, K = E yields an exact result:
95
 *      - The CTM is of the form [X 0 0 Y] or [0 X Y 0].
96
 *      - Square or round caps are used, or all subpaths are closed.
97
 *      - All segments (including the implicit segment created by
98
 *        closepath) are vertical or horizontal lines.
99
 *
100
 * Note that these conditions are sufficient, but not necessary, to get an
101
 * exact result.  We choose this set of conditions because it is easy to
102
 * check and covers many common cases.  Clients that care always have the
103
 * option of using strokepath to get an exact result.
104
 */
105
static float join_expansion_factor(const gs_gstate *, gs_line_join);
106
int
107
gx_stroke_path_expansion(const gs_gstate * pgs, const gx_path * ppath,
108
                         gs_fixed_point * ppt)
109
1.46M
{
110
1.46M
    const subpath *psub;
111
1.46M
    const segment *pseg;
112
1.46M
    double cx = fabs(pgs->ctm.xx) + fabs(pgs->ctm.yx);
113
1.46M
    double cy = fabs(pgs->ctm.xy) + fabs(pgs->ctm.yy);
114
1.46M
    double expand = pgs->line_params.half_width;
115
1.46M
    int result = 1;
116
117
1.46M
    if (ppath == NULL) {
118
0
        ppt->x = ppt->y = 0;
119
0
        return 0;   /* no expansion */
120
0
    }
121
1.46M
    psub = ppath->first_subpath;
122
    /* Adjust the expansion (E) for square caps, if needed */
123
1.46M
    if (pgs->line_params.start_cap == gs_cap_square ||
124
1.46M
        pgs->line_params.end_cap == gs_cap_square)
125
96.8k
            expand *= 1.414213562;
126
127
    /* Check for whether an exact result can be computed easily. */
128
1.46M
    if (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
129
1.46M
        is_fzero2(pgs->ctm.xx, pgs->ctm.yy)
130
1.46M
        ) {
131
1.34M
        bool must_be_closed =
132
1.34M
            !(pgs->line_params.start_cap == gs_cap_square ||
133
1.34M
              pgs->line_params.start_cap == gs_cap_round  ||
134
1.34M
              pgs->line_params.end_cap   == gs_cap_square ||
135
1.34M
              pgs->line_params.end_cap   == gs_cap_round  ||
136
1.34M
              pgs->line_params.dash_cap  == gs_cap_square ||
137
1.34M
              pgs->line_params.dash_cap  == gs_cap_round);
138
1.34M
        gs_fixed_point prev;
139
140
1.34M
        prev.x = prev.y = 0; /* Quiet gcc warning. */
141
4.48M
        for (pseg = (const segment *)psub; pseg;
142
3.13M
             prev = pseg->pt, pseg = pseg->next
143
1.34M
             )
144
3.72M
            switch (pseg->type) {
145
1.28M
            case s_start:
146
1.28M
                if (((const subpath *)pseg)->curve_count ||
147
1.28M
                    (must_be_closed && !((const subpath *)pseg)->is_closed)
148
1.28M
                    )
149
251k
                    goto not_exact;
150
1.03M
                break;
151
1.97M
            case s_line:
152
1.97M
            case s_dash:
153
2.43M
            case s_line_close:
154
2.43M
                if (!(pseg->pt.x == prev.x || pseg->pt.y == prev.y))
155
336k
                    goto not_exact;
156
2.10M
                break;
157
2.10M
            case s_gap:
158
0
            default:            /* other/unknown segment type */
159
0
                goto not_exact;
160
3.72M
            }
161
756k
        result = 0;             /* exact result */
162
756k
    }
163
1.46M
not_exact:
164
1.46M
    if (result) {
165
713k
        if (!gx_path_has_curves(ppath) && gx_path_subpath_count(ppath) <= 1 &&
166
713k
            (psub == 0 || (pseg = psub->next) == 0 ||
167
533k
             (pseg = pseg->next) == 0 || pseg->type == s_line_close))
168
713k
            DO_NOTHING;
169
216k
        else {
170
216k
            float factor = join_expansion_factor(pgs, pgs->line_params.join);
171
172
216k
            if (pgs->line_params.curve_join >= 0)
173
0
                factor = max(factor, join_expansion_factor(pgs,
174
216k
                                (gs_line_join)pgs->line_params.curve_join));
175
216k
            expand *= factor;
176
216k
        }
177
713k
    }
178
179
    /* Short-cut gs_bbox_transform. */
180
1.46M
    {
181
1.46M
        float exx = expand * cx;
182
1.46M
        float exy = expand * cy;
183
1.46M
        int code = set_float2fixed_vars(ppt->x, exx);
184
185
1.46M
        if (code < 0)
186
100k
            return code;
187
1.36M
        code = set_float2fixed_vars(ppt->y, exy);
188
1.36M
        if (code < 0)
189
1.73k
            return code;
190
1.36M
    }
191
192
1.36M
    return result;
193
1.36M
}
194
static float
195
join_expansion_factor(const gs_gstate *pgs, gs_line_join join)
196
216k
{
197
216k
    switch (join) {
198
146k
    case gs_join_miter: return pgs->line_params.miter_limit;
199
0
    case gs_join_triangle: return 2.0;
200
69.4k
    default: return 1.0;
201
216k
    }
202
216k
}
203
204
/*
205
 * Structure for a partial line (passed to the drawing routine).
206
 * Two of these are required to do joins right.
207
 * Each endpoint includes the two ends of the cap as well,
208
 * and the deltas for square, round, and triangular cap computation.
209
 *
210
 * The two base values for computing the caps of a partial line are the
211
 * width and the end cap delta.  The width value is one-half the line
212
 * width (suitably transformed) at 90 degrees counter-clockwise
213
 * (in device space, but with "90 degrees" interpreted in *user*
214
 * coordinates) at the end (as opposed to the origin) of the line.
215
 * The cdelta value is one-half the transformed line width in the same
216
 * direction as the line.  From these, we compute two other values at each
217
 * end of the line: co and ce, which are the ends of the cap.
218
 * Note that the cdelta values at o are the negatives of the values at e,
219
 * as are the offsets from p to co and ce.
220
 *
221
 * Initially, only o.p, e.p, e.cdelta, width, and thin are set.
222
 * compute_caps fills in the rest.
223
 */
224
typedef gs_fixed_point *p_ptr;
225
typedef struct endpoint_s {
226
    gs_fixed_point p;           /* the end of the line */
227
    gs_fixed_point co, ce;      /* ends of the cap, p +/- width */
228
    gs_fixed_point cdelta;      /* +/- cap length */
229
} endpoint;
230
typedef endpoint *ep_ptr;
231
typedef const endpoint *const_ep_ptr;
232
typedef struct partial_line_s {
233
    endpoint o;                 /* starting coordinate */
234
    endpoint e;                 /* ending coordinate */
235
    gs_fixed_point width;       /* one-half line width, see above */
236
    gs_fixed_point vector;      /* The line segment direction */
237
    bool thin;                  /* true if minimum-width line */
238
} partial_line;
239
typedef partial_line *pl_ptr;
240
241
/* As we stroke a path, we run through the line segments that make it up.
242
 * We gather each line segment together with any degenerate line segments
243
 * that follow it (call this set "prev"), and then 'join them' to the next
244
 * line segment (and any degenerate line segments that follow it) (if there
245
 * is one) (call this "current").
246
 *
247
 * In order to get the joins right we need to keep flags about both
248
 * prev and current, and whether they originally came from arcs.
249
 */
250
typedef enum note_flags {
251
252
    /* If set, all the line segments that make up current come from arcs. */
253
    nf_all_from_arc       = 1,
254
255
    /* If set, at least one of the line segments that make up current, come
256
     * from arcs. */
257
    nf_some_from_arc      = 2,
258
259
    /* If set then this segment should have a dash cap on the start rather
260
     * than a start cap. */
261
    nf_dash_head          = 4,
262
263
    /* If set then this segment should have a dash cap on the end rather
264
     * than an end cap. */
265
    nf_dash_tail          = 8,
266
267
    /* If set, all the line segments that make up prev come from arcs. */
268
    nf_prev_all_from_arc  = 16,
269
270
    /* If set, at least one of the line segment that make up prev, come from
271
     * arcs. */
272
    nf_prev_some_from_arc = 32,
273
274
    /* If set then prev should have a dash cap on the start rather
275
     * than a start cap. */
276
    nf_prev_dash_head     = 64,
277
278
    /* If set then prev should have a dash cap on the end rather
279
     * than an end cap. */
280
    nf_prev_dash_tail     = 128
281
282
} note_flags;
283
284
/* Macro to combine the prev and current arc_flags. After applying this
285
 * macro, the bits in the result have the following meanings:
286
 *  nf_all_from_arc    set if all the components of current and prev
287
 *                     come from an Arc.
288
 *  nf_some_from_arc   set if any of the components of current and
289
 *                     prev come from an Arc.
290
 *  nf_dash_head       set if prev should have a dash cap rather than
291
 *                     a start cap.
292
 *  nf_dash_tail       set if prev should have a dash cap rather than
293
 *                     an end cap.
294
 */
295
#define COMBINE_FLAGS(F) \
296
57.9M
    (((F>>4) | ((F) & nf_some_from_arc)) & \
297
57.9M
     (((F) & nf_all_from_arc) ? ~0 : ~nf_all_from_arc))
298
299
/* Assign a point.  Some compilers would do this with very slow code */
300
/* if we simply implemented it as an assignment. */
301
#define ASSIGN_POINT(pp, p)\
302
29.7M
  ((pp)->x = (p).x, (pp)->y = (p).y)
303
304
/* Other forward declarations */
305
static bool width_is_thin(pl_ptr);
306
static void adjust_stroke(gx_device *, pl_ptr, const gs_gstate *, bool, bool, note_flags);
307
static int line_join_points(const gx_line_params * pgs_lp,
308
                             pl_ptr plp, pl_ptr nplp,
309
                             gs_fixed_point * join_points,
310
                             const gs_matrix * pmat, gs_line_join join,
311
                             bool reflected);
312
static int line_join_points_fast_cw(const gx_line_params * pgs_lp,
313
                                    pl_ptr plp, pl_ptr nplp,
314
                                    gs_fixed_point * rjoin_points,
315
                                    const gs_matrix * pmat,
316
                                    gs_line_join join);
317
static int line_join_points_fast_ccw(const gx_line_params * pgs_lp,
318
                                     pl_ptr plp, pl_ptr nplp,
319
                                     gs_fixed_point * join_points,
320
                                     const gs_matrix * pmat,
321
                                     gs_line_join join);
322
static void compute_caps(pl_ptr);
323
static int add_points(gx_path *, const gs_fixed_point *,
324
                       int, bool);
325
static int add_pie_join(gx_path *, pl_ptr, pl_ptr, bool, bool);
326
static int add_pie_join_fast_cw(gx_path *, pl_ptr, pl_ptr, bool);
327
static int add_pie_join_fast_ccw(gx_path *, pl_ptr, pl_ptr, bool);
328
static int add_round_cap(gx_path *, const_ep_ptr);
329
static int add_pie_cap(gx_path *, const_ep_ptr);
330
static int cap_points(gs_line_cap, const_ep_ptr,
331
                       gs_fixed_point * /*[3] */ );
332
static int join_under_pie(gx_path *, pl_ptr, pl_ptr, bool);
333
334
int
335
gx_default_stroke_path_shading_or_pattern(gx_device        * pdev,
336
                                    const gs_gstate        * pgs_orig,
337
                                          gx_path          * ppath,
338
                                    const gx_stroke_params * params,
339
                                    const gx_drawing_color * pdevc,
340
                                    const gx_clip_path     * pcpath)
341
47.3k
{
342
47.3k
    gs_gstate *pgs = (gs_gstate *)pgs_orig; /* Nasty cast away const! */
343
47.3k
    gs_logical_operation_t save_lop = gs_current_logical_op_inline(pgs);
344
47.3k
    gx_device_cpath_accum adev;
345
47.3k
    gx_device_color devc;
346
47.3k
    gx_clip_path stroke_as_clip_path;
347
47.3k
    int code;
348
47.3k
    gs_fixed_rect dev_clip_rect = { {min_fixed, min_fixed}, {max_fixed, max_fixed}};
349
350
    /* We want to make a image of the stroke as a clip path, so
351
     * create an empty structure on the stack. */
352
47.3k
    code = gx_cpath_init_local_shared_nested(&stroke_as_clip_path, NULL, pdev->memory, 1);
353
47.3k
    if (code < 0)
354
0
        return code;
355
    /* Now we make an accumulator device that will fill that out. */
356
47.3k
    gx_cpath_accum_begin(&adev, stroke_as_clip_path.path.memory, false);
357
47.3k
    if (pdev != 0)
358
47.3k
        (*dev_proc(pdev, get_clipping_box))(pdev, &dev_clip_rect);
359
47.3k
    gx_cpath_accum_set_cbox(&adev, &dev_clip_rect);
360
47.3k
    set_nonclient_dev_color(&devc, 0); /* arbitrary, but not transparent */
361
47.3k
    gs_set_logical_op_inline(pgs, lop_default);
362
    /* Stroke the path to the accumulator. */
363
47.3k
    code = gx_stroke_path_only(ppath, NULL, (gx_device *)&adev, pgs, params,
364
47.3k
                               &devc, pcpath);
365
    /* Now extract the accumulated path into stroke_as_clip_path. */
366
47.3k
    if (code < 0 || (code = gx_cpath_accum_end(&adev, &stroke_as_clip_path)) < 0)
367
0
        gx_cpath_accum_discard(&adev);
368
47.3k
    gs_set_logical_op_inline(pgs, save_lop);
369
47.3k
    if (code >= 0)
370
47.3k
    {
371
        /* Now, fill a rectangle with the original color through that
372
         * clip path. */
373
47.3k
        gs_fixed_rect clip_box, shading_box;
374
47.3k
        gs_int_rect cb;
375
47.3k
        gx_device_clip cdev;
376
377
47.3k
        gx_cpath_outer_box(&stroke_as_clip_path, &clip_box);
378
        /* This is horrid. If the pdevc is a shading color, then the
379
         * fill_rectangle routine requires us to have intersected it
380
         * with the shading rectangle first. If we don't do this,
381
         * ps3fts/470-01.ps goes wrong. */
382
47.3k
        if (gx_dc_is_pattern2_color(pdevc) &&
383
47.3k
            gx_dc_pattern2_get_bbox(pdevc, &shading_box) > 0)
384
0
        {
385
0
            rect_intersect(clip_box, shading_box);
386
0
        }
387
47.3k
        cb.p.x = fixed2int_pixround(clip_box.p.x);
388
47.3k
        cb.p.y = fixed2int_pixround(clip_box.p.y);
389
47.3k
        cb.q.x = fixed2int_pixround(clip_box.q.x);
390
47.3k
        cb.q.y = fixed2int_pixround(clip_box.q.y);
391
47.3k
        gx_make_clip_device_on_stack(&cdev, &stroke_as_clip_path, pdev);
392
47.3k
        code = pdevc->type->fill_rectangle(pdevc,
393
47.3k
                        cb.p.x, cb.p.y, cb.q.x - cb.p.x, cb.q.y - cb.p.y,
394
47.3k
                        (gx_device *)&cdev, pgs->log_op, NULL);
395
47.3k
    }
396
47.3k
    gx_cpath_free(&stroke_as_clip_path, "gx_default_stroke_path_shading_or_pattern");
397
398
47.3k
    return code;
399
47.3k
}
400
401
/* Define the default implementation of the device stroke_path procedure. */
402
int
403
gx_default_stroke_path(gx_device * dev, const gs_gstate * pgs,
404
                       gx_path * ppath, const gx_stroke_params * params,
405
                       const gx_drawing_color * pdevc,
406
                       const gx_clip_path * pcpath)
407
859k
{
408
859k
    if (gx_dc_is_pattern2_color(pdevc) ||
409
859k
        pdevc->type == &gx_dc_type_data_ht_colored ||
410
859k
        (gx_dc_is_pattern1_color(pdevc) &&
411
811k
         gx_pattern_tile_is_clist(pdevc->colors.pattern.p_tile)))
412
47.3k
        return gx_default_stroke_path_shading_or_pattern(dev, pgs, ppath, params,
413
47.3k
                                                         pdevc, pcpath);
414
811k
    else
415
811k
        return gx_stroke_path_only(ppath, (gx_path *) 0, dev, pgs, params,
416
811k
                                   pdevc, pcpath);
417
859k
}
418
419
/* Fill a partial stroked path.  Free variables: */
420
/* to_path, stroke_path_body, fill_params, always_thin, pgs, dev, pdevc, */
421
/* code, ppath, exit(label). */
422
#define FILL_STROKE_PATH(dev, thin, pcpath, final)\
423
39.6M
  if(to_path==&stroke_path_body && !gx_path_is_void(&stroke_path_body) &&\
424
39.6M
     (final || lop_is_idempotent(pgs->log_op))) {\
425
18.9M
    fill_params.adjust.x = STROKE_ADJUSTMENT(thin, pgs, x);\
426
18.9M
    fill_params.adjust.y = STROKE_ADJUSTMENT(thin, pgs, y);\
427
18.9M
    if (to_path_reverse != NULL) {\
428
0
        code = gx_join_path_and_reverse(to_path, to_path_reverse);\
429
0
        if(code < 0) goto exit;\
430
0
    }\
431
18.9M
    code = gx_fill_path_only(to_path, dev, pgs, &fill_params, pdevc, pcpath);\
432
18.9M
    gx_path_free(&stroke_path_body, "fill_stroke_path");\
433
18.9M
    if ( code < 0 ) goto exit;\
434
18.9M
    gx_path_init_local(&stroke_path_body, ppath->memory);\
435
18.9M
  }
436
437
/*
438
 * Define the internal procedures that stroke a partial_line
439
 * (the first pl_ptr argument).  If both partial_lines are non-null,
440
 * the procedure creates an appropriate join; otherwise, the procedure
441
 * creates an end cap.  If the first int is 0, the procedure also starts
442
 * with an appropriate cap.
443
 */
444
#define stroke_line_proc(proc)\
445
  int proc(gx_path *, gx_path *, bool ensure_closed, int, pl_ptr, pl_ptr,\
446
           const gx_device_color *, gx_device *, const gs_gstate *,\
447
           const gx_stroke_params *, const gs_fixed_rect *, int,\
448
           gs_line_join, bool, note_flags)
449
typedef stroke_line_proc((*stroke_line_proc_t));
450
451
static stroke_line_proc(stroke_add);
452
static stroke_line_proc(stroke_add_compat);
453
static stroke_line_proc(stroke_add_fast);
454
static stroke_line_proc(stroke_fill);
455
static int stroke_add_initial_cap_compat(gx_path * ppath, pl_ptr plp, bool adlust_longitude,
456
           const gx_device_color * pdevc, gx_device * dev,
457
           const gs_gstate * pgs);
458
459
/* Define the orientations we handle specially. */
460
typedef enum {
461
    orient_other = 0,
462
    orient_portrait,            /* [xx 0 0 yy tx ty] */
463
    orient_landscape            /* [0 xy yx 0 tx ty] */
464
} orientation;
465
466
/*
467
 * Internal function used to merge the 2 sides of a stroked path.
468
 * path contains the 'forward' side, rpath contains the 'reversed' side.
469
 * Reverse rpath, then append it to path.
470
 *
471
 * If path is closed, then rpath should be too. If path is open, then the
472
 * starting and ending points of both paths should be the same, so as to
473
 * guarantee a closed path.
474
 */
475
static int
476
gx_join_path_and_reverse(gx_path * path, gx_path * rpath)
477
0
{
478
0
    int code;
479
480
0
    if (gx_path_is_void(rpath))
481
0
        return 0;
482
0
     code = gx_path_append_reversed(rpath, path);
483
0
    if (code < 0)
484
0
        return code;
485
486
0
    gx_path_free(rpath, "gx_join_path_and_reverse");
487
0
    gx_path_init_local(rpath, path->memory);
488
489
0
    return gx_path_close_subpath(path);
490
0
}
491
492
/*
493
 * Stroke a path.  If to_path != 0, append the stroke outline to it;
494
 * if to_path == 0, draw the strokes on pdev.
495
 *
496
 * Note that gx_stroke_path_only with to_path != NULL may clip the path to
497
 * the clipping path, as for to_path == NULL.  This is almost never
498
 * what is wanted.
499
 */
500
static int
501
gx_stroke_path_only_aux(gx_path          *ppath, /* lgtm[cpp/use-of-goto] */
502
                        gx_path          *to_path,
503
                        gx_device        *pdev,
504
                  const gs_gstate        *pgs,
505
                  const gx_stroke_params *params,
506
                  const gx_device_color  *pdevc,
507
                  const gx_clip_path     *pcpath)
508
859k
{
509
859k
    bool CPSI_mode = gs_currentcpsimode(pgs->memory);
510
859k
    bool traditional = CPSI_mode | params->traditional;
511
859k
    stroke_line_proc_t line_proc =
512
859k
               ((to_path == 0 && !gx_dc_is_pattern1_color_clist_based(pdevc))
513
859k
                      ? (lop_is_idempotent(pgs->log_op) ? stroke_fill : stroke_add) :
514
859k
                        (traditional ? stroke_add_compat : stroke_add_fast));
515
859k
    gs_fixed_rect ibox, cbox;
516
859k
    gx_device_clip cdev;
517
859k
    gx_device *dev = pdev;
518
859k
    int code = 0;
519
859k
    gx_fill_params fill_params;
520
859k
    const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs);
521
859k
    int dash_count = pgs_lp->dash.pattern_size;
522
859k
    gx_path fpath, dpath;
523
859k
    gx_path stroke_path_body;
524
859k
    gx_path stroke_path_reverse;
525
859k
    gx_path *to_path_reverse = NULL;
526
859k
    const gx_path *spath;
527
859k
    float xx = pgs->ctm.xx, xy = pgs->ctm.xy;
528
859k
    float yx = pgs->ctm.yx, yy = pgs->ctm.yy;
529
    /*
530
     * We are dealing with a reflected coordinate system
531
     * if transform(1,0) is counter-clockwise from transform(0,1).
532
     * See the note in stroke_add for the algorithm.
533
     */
534
859k
    int uniform;
535
859k
    bool reflected;
536
859k
    orientation orient =
537
859k
        (
538
859k
#ifdef OPTIMIZE_ORIENTATION
539
859k
         is_fzero2(xy, yx) ?
540
737k
         (uniform = (xx == yy ? 1 : xx == -yy ? -1 : 0),
541
737k
          reflected = (uniform ? uniform < 0 : (xx < 0) != (yy < 0)),
542
737k
          orient_portrait) :
543
859k
         is_fzero2(xx, yy) ?
544
4.95k
         (uniform = (xy == yx ? -1 : xy == -yx ? 1 : 0),
545
4.95k
          reflected = (uniform ? uniform < 0 : (xy < 0) == (yx < 0)),
546
4.95k
          orient_landscape) :
547
    /* We should optimize uniform rotated coordinate systems */
548
    /* here as well, but we don't. */
549
122k
#endif
550
122k
         (uniform = 0,
551
117k
          reflected = xy * yx > xx * yy,
552
117k
          orient_other));
553
859k
    const segment_notes not_first = sn_not_first;
554
859k
    gs_line_join curve_join =
555
859k
        (pgs_lp->curve_join >= 0 ? (gs_line_join)pgs_lp->curve_join :
556
859k
         pgs_lp->join == gs_join_none || pgs_lp->join == gs_join_round ?
557
558k
            gs_join_bevel : pgs_lp->join);
558
859k
    float line_width = pgs_lp->half_width;      /* (*half* the line width) */
559
859k
    bool always_thin;
560
859k
    double line_width_and_scale;
561
859k
    double device_line_width_scale = 0; /* Quiet compiler. */
562
859k
    double device_dot_length = pgs_lp->dot_length * fixed_1;
563
859k
    const subpath *psub;
564
859k
    gs_matrix initial_matrix;
565
859k
    bool initial_matrix_reflected, flattened_path = false;
566
859k
    note_flags flags;
567
568
859k
    (*dev_proc(pdev, get_initial_matrix)) (pdev, &initial_matrix);
569
859k
    initial_matrix_reflected = initial_matrix.xy * initial_matrix.yx >
570
859k
                               initial_matrix.xx * initial_matrix.yy;
571
572
#ifdef DEBUG
573
    if (gs_debug_c('o')) {
574
        int i;
575
576
        dmlprintf4(ppath->memory, "[o]half_width=%f, start_cap=%d, end_cap=%d, dash_cap=%d,\n",
577
                   pgs_lp->half_width, (int)pgs_lp->start_cap,
578
                   (int)pgs_lp->end_cap, (int)pgs_lp->dash_cap);
579
        dmlprintf3(ppath->memory, "   join=%d, miter_limit=%f, miter_check=%f,\n",
580
                   (int)pgs_lp->join, pgs_lp->miter_limit,
581
                   pgs_lp->miter_check);
582
        dmlprintf1(ppath->memory, "   dash pattern=%d", dash_count);
583
        for (i = 0; i < dash_count; i++)
584
            dmprintf1(ppath->memory, ",%f", pgs_lp->dash.pattern[i]);
585
        dmputs(ppath->memory, ",\n");
586
        dmlprintf4(ppath->memory, "\toffset=%f, init(ink_on=%d, index=%d, dist_left=%f)\n",
587
                   pgs_lp->dash.offset, pgs_lp->dash.init_ink_on,
588
                   pgs_lp->dash.init_index, pgs_lp->dash.init_dist_left);
589
    }
590
#endif
591
592
859k
    gx_path_bbox(ppath, &ibox);
593
    /* Expand the path bounding box by the scaled line width. */
594
859k
    {
595
859k
        gs_fixed_point expansion;
596
597
859k
        if (gx_stroke_path_expansion(pgs, ppath, &expansion) < 0) {
598
            /* The expansion is so large it caused a limitcheck. */
599
100k
            ibox.p.x = ibox.p.y = min_fixed;
600
100k
            ibox.q.x = ibox.q.y = max_fixed;
601
758k
        } else {
602
758k
            expansion.x += pgs->fill_adjust.x;
603
758k
            expansion.y += pgs->fill_adjust.y;
604
            /*
605
             * It's theoretically possible for the following computations to
606
             * overflow, so we need to check for this.
607
             */
608
758k
            ibox.p.x = (ibox.p.x < min_fixed + expansion.x ? min_fixed :
609
758k
                        ibox.p.x - expansion.x);
610
758k
            ibox.p.y = (ibox.p.y < min_fixed + expansion.y ? min_fixed :
611
758k
                        ibox.p.y - expansion.y);
612
758k
            ibox.q.x = (ibox.q.x > max_fixed - expansion.x ? max_fixed :
613
758k
                        ibox.q.x + expansion.x);
614
758k
            ibox.q.y = (ibox.q.y > max_fixed - expansion.y ? max_fixed :
615
758k
                        ibox.q.y + expansion.y);
616
758k
        }
617
859k
    }
618
    /* Check the expanded bounding box against the clipping regions. */
619
859k
    if (pcpath)
620
409k
        gx_cpath_inner_box(pcpath, &cbox);
621
449k
    else if (pdevc)
622
449k
        (*dev_proc(pdev, get_clipping_box)) (pdev, &cbox);
623
161
    else {
624
        /* This is strokepath, not stroke.  Don't clip. */
625
161
        cbox = ibox;
626
161
    }
627
859k
    if (!rect_within(ibox, cbox)) {
628
        /* Intersect the path box and the clip bounding box. */
629
        /* If the intersection is empty, this call is a no-op. */
630
577k
        gs_fixed_rect bbox;
631
632
577k
        if (pcpath) {
633
361k
            gx_cpath_outer_box(pcpath, &bbox);
634
361k
            if_debug4m('f', ppath->memory, "   outer_box=(%g,%g),(%g,%g)\n",
635
361k
                       fixed2float(bbox.p.x), fixed2float(bbox.p.y),
636
361k
                       fixed2float(bbox.q.x), fixed2float(bbox.q.y));
637
361k
            rect_intersect(ibox, bbox);
638
361k
        } else
639
577k
            rect_intersect(ibox, cbox);
640
577k
        if (ibox.p.x >= ibox.q.x || ibox.p.y >= ibox.q.y) {
641
            /* Intersection of boxes is empty! */
642
199k
            return 0;
643
199k
        }
644
        /*
645
         * The path is neither entirely inside the inner clip box
646
         * nor entirely outside the outer clip box.
647
         * If we had to flatten the path, this is where we would
648
         * recompute its bbox and make the tests again,
649
         * but we don't bother right now.
650
         */
651
        /*
652
         * If there is a clipping path, set up a clipping device.
653
         * for stroke_fill because, because the latter uses low level methods
654
         * which don't accept a clipping path.
655
         * Note that in some cases stroke_fill appends the path to stroke_path_body
656
         * instead a real painting, and it is painted with FILL_STROKE_PATH.
657
         *
658
         * Contrary to that, FILL_STROKE_PATH paints a path with
659
         * the fill_path method, which handles a clipping path,
660
         * so we don't pass the clipper device to FILL_STROKE_PATH
661
         * to prevent an appearence of superposing clippers.
662
         */
663
377k
        if (pcpath && line_proc == stroke_fill) {
664
167k
            gx_make_clip_device_on_stack(&cdev, pcpath, pdev);
665
167k
            cdev.max_fill_band = pdev->max_fill_band;
666
167k
            dev = (gx_device *)&cdev;
667
167k
        }
668
377k
    }
669
659k
    fill_params.rule = gx_rule_winding_number;
670
659k
    fill_params.flatness = pgs->flatness;
671
659k
    if (line_width < 0)
672
0
        line_width = -line_width;
673
659k
    line_width_and_scale = line_width * (double)int2fixed(1);
674
659k
    if (is_fzero(line_width))
675
5.16k
        always_thin = true;
676
654k
    else {
677
654k
        float xa, ya;
678
679
654k
        switch (orient) {
680
638k
            case orient_portrait:
681
638k
                xa = xx, ya = yy;
682
638k
                goto sat;
683
3.41k
            case orient_landscape:
684
3.41k
                xa = xy, ya = yx;
685
641k
              sat:
686
641k
                if (xa < 0)
687
4.38k
                    xa = -xa;
688
641k
                if (ya < 0)
689
464k
                    ya = -ya;
690
641k
                always_thin = (max(xa, ya) * line_width < 0.5);
691
641k
                if (!always_thin && uniform) {  /* Precompute a value we'll need later. */
692
375k
                    device_line_width_scale = line_width_and_scale * xa;
693
375k
                }
694
641k
                break;
695
13.0k
            default:
696
13.0k
                {
697
                    /* The check is more complicated, but it's worth it. */
698
                    /* Compute radii of the transformed round brush. */
699
                    /* Let x = [a, sqrt(1-a^2)]'
700
                       radius^2 is an extremum of :
701
                       rr(a)=(CTM*x)^2 = (a*xx + sqrt(1 - a^2)*xy)^2 + (a*yx + sqrt(1 - a^2)*yy)^2
702
                       With solving D(rr(a),a)==0, got :
703
                       max_rr = (xx^2 + xy^2 + yx^2 + yy^2 + sqrt(((xy + yx)^2 + (xx - yy)^2)*((xy - yx)^2 + (xx + yy)^2)))/2.
704
                       r = sqrt(max_rr);
705
                       Well we could use eigenvalues of the quadratic form,
706
                       but it gives same result with a bigger calculus.
707
                     */
708
13.0k
                    double max_rr = ((double)(xx*xx + xy*xy + yx*yx + yy*yy) +
709
13.0k
                                     sqrt((double)((xy + yx)*(xy + yx) + (xx - yy)*(xx - yy)) *
710
13.0k
                                                  ((xy - yx)*(xy - yx) + (xx + yy)*(xx + yy))
711
13.0k
                                          )
712
13.0k
                                     )/2;
713
714
13.0k
                    always_thin = max_rr * line_width * line_width < 0.25;
715
13.0k
                }
716
654k
        }
717
654k
    }
718
659k
    if_debug7m('o', ppath->memory, "[o]ctm=(%g,%g,%g,%g,%g,%g) thin=%d\n",
719
659k
              xx, xy, yx, yy, pgs->ctm.tx, pgs->ctm.ty, always_thin);
720
659k
    if (device_dot_length != 0) {
721
        /*
722
         * Compute the dot length in device space.  We can't do this
723
         * quite right for non-uniform coordinate systems; too bad.
724
         */
725
0
        gs_matrix mat;
726
0
        const gs_matrix *pmat;
727
728
0
        if (pgs_lp->dot_length_absolute) {
729
0
            gs_deviceinitialmatrix(pdev, &mat);
730
0
            pmat = &mat;
731
0
        } else
732
0
            pmat = (const gs_matrix *)&pgs->ctm;
733
0
        device_dot_length *= fabs(pmat->xy) + fabs(pmat->yy);
734
0
    }
735
    /* Start by flattening the path.  We should do this on-the-fly.... */
736
659k
    if (!gx_path_has_curves(ppath) && !gx_path_has_long_segments(ppath)) {
737
        /* don't need to flatten */
738
546k
        if (!ppath->first_subpath)
739
30.8k
            return 0;
740
515k
        spath = ppath;
741
515k
    } else {
742
113k
        gx_path_init_local(&fpath, ppath->memory);
743
113k
        if ((code = gx_path_add_flattened_for_stroke(ppath, &fpath,
744
113k
                                                params->flatness, pgs)) < 0
745
113k
            )
746
0
            return code;
747
113k
        spath = &fpath;
748
113k
        flattened_path = true;
749
113k
    }
750
628k
    if (dash_count) {
751
10.7k
        float max_dash_len = 0;
752
10.7k
        float expand_squared;
753
10.7k
        int i;
754
10.7k
        float adjust = (float)pgs->fill_adjust.x;
755
10.7k
        if (adjust > (float)pgs->fill_adjust.y)
756
0
            adjust = (float)pgs->fill_adjust.y;
757
26.3k
        for (i = 0; i < dash_count; i++) {
758
15.5k
            if (max_dash_len < pgs_lp->dash.pattern[i])
759
11.8k
                max_dash_len = pgs_lp->dash.pattern[i];
760
15.5k
        }
761
10.7k
        expand_squared = pgs->ctm.xx * pgs->ctm.yy - pgs->ctm.xy * pgs->ctm.yx;
762
10.7k
        if (expand_squared < 0)
763
7.12k
            expand_squared = -expand_squared;
764
10.7k
        expand_squared *= max_dash_len * max_dash_len;
765
        /* Wide lines in curves can show dashes up, so fudge to allow for
766
         * this. */
767
10.7k
        if (pgs->line_params.half_width > 1)
768
210
            adjust /= pgs->line_params.half_width;
769
10.7k
        if (expand_squared*65536.0f >= (float)(adjust*adjust)) {
770
10.7k
            gx_path_init_local(&dpath, ppath->memory);
771
10.7k
            code = gx_path_add_dash_expansion(spath, &dpath, pgs);
772
10.7k
            if (code < 0)
773
0
                goto exf;
774
10.7k
            spath = &dpath;
775
10.7k
        } else {
776
0
            dash_count = 0;
777
0
        }
778
10.7k
    }
779
628k
    if (to_path == 0) {
780
        /* We might try to defer this if it's expensive.... */
781
628k
        to_path = &stroke_path_body;
782
628k
        gx_path_init_local(&stroke_path_body, ppath->memory);
783
628k
    }
784
628k
    if (line_proc == stroke_add_fast) {
785
0
        to_path_reverse = &stroke_path_reverse;
786
0
        gx_path_init_local(&stroke_path_reverse, ppath->memory);
787
0
    }
788
2.83M
    for (psub = spath->first_subpath; psub != 0;) {
789
2.20M
        int index = 0;
790
2.20M
        const segment *pseg = (const segment *)psub;
791
2.20M
        fixed x = pseg->pt.x;
792
2.20M
        fixed y = pseg->pt.y;
793
2.20M
        bool is_closed = ((const subpath *)pseg)->is_closed;
794
2.20M
        partial_line pl, pl_prev, pl_first;
795
2.20M
        bool zero_length = true;
796
2.20M
        int pseg_notes = pseg->notes;
797
798
2.20M
        flags = nf_all_from_arc;
799
800
        /* Run through each segment in the current path, drawing each segment
801
         * delayed by 1 - that is, when we're looking at segment n, we draw
802
         * (or not) segment n-1. This delay allows us to always know whether
803
         * to join or cap the line. */
804
41.2M
        while ((pseg = pseg->next) != 0 &&
805
41.2M
               pseg->type != s_start
806
39.0M
            ) {
807
            /* Compute the width parameters in device space. */
808
            /* We work with unscaled values, for speed. */
809
39.0M
            fixed sx, udx, sy, udy;
810
39.0M
            bool is_dash_segment;
811
812
39.0M
            pseg_notes = pseg->notes;
813
814
39.3M
         d2:is_dash_segment = false;
815
39.3M
         d1:if (pseg->type == s_dash) {
816
202k
                dash_segment *pd = (dash_segment *)pseg;
817
818
202k
                sx = pd->pt.x;
819
202k
                sy = pd->pt.y;
820
202k
                udx = pd->tangent.x;
821
202k
                udy = pd->tangent.y;
822
202k
                is_dash_segment = true;
823
39.1M
            } else if (pseg->type == s_gap) {
824
0
                sx = pseg->pt.x;
825
0
                sy = pseg->pt.y;
826
0
                udx = sx - x;
827
0
                udy = sy - y;
828
0
                is_dash_segment = true;
829
39.1M
            } else {
830
39.1M
                sx = pseg->pt.x;
831
39.1M
                sy = pseg->pt.y;
832
39.1M
                udx = sx - x;
833
39.1M
                udy = sy - y;
834
39.1M
            }
835
39.3M
            zero_length &= ((udx | udy) == 0);
836
39.3M
            pl.o.p.x = x, pl.o.p.y = y;
837
39.3M
          d:flags = (((pseg_notes & sn_not_first) ?
838
33.2M
                      ((flags & nf_all_from_arc) | nf_some_from_arc) : 0) |
839
39.3M
                     ((pseg_notes & sn_dash_head) ? nf_dash_head : 0)    |
840
39.3M
                     ((pseg_notes & sn_dash_tail) ? nf_dash_tail : 0)    |
841
39.3M
                     (flags & ~nf_all_from_arc));
842
39.3M
            pl.e.p.x = sx, pl.e.p.y = sy;
843
39.3M
            if (!(udx | udy) || pseg->type == s_dash || pseg->type == s_gap) { /* degenerate or short */
844
                /*
845
                 * If this is the first segment of the subpath,
846
                 * check the entire subpath for degeneracy.
847
                 * Otherwise, ignore the degenerate segment.
848
                 */
849
582k
                if (index != 0 && pseg->type != s_dash && pseg->type != s_gap)
850
365k
                {
851
365k
                    if (pseg->next == NULL || pseg->next->type == s_start)
852
91.2k
                        continue;
853
274k
                    pseg = pseg->next;
854
                    /* We're skipping a degenerate path segment; if it was
855
                     * labelled as being the first from a curve, then make
856
                     * sure the one we're skipping to is also labelled as
857
                     * being the first from a curve, otherwise we can get
858
                     * improper joins being used. See Bug 696466. */
859
274k
                    pseg_notes = (((pseg_notes & sn_not_first) == 0) ?
860
190k
                                  (pseg->notes & ~sn_not_first) :
861
274k
                                  pseg->notes);
862
274k
                    goto d2;
863
365k
                }
864
                /* Check for a degenerate subpath. */
865
217k
                while ((pseg = pseg->next) != 0 &&
866
217k
                       pseg->type != s_start
867
217k
                    ) {
868
15.5k
                    if (is_dash_segment)
869
3.13k
                        break;
870
12.4k
                    if (pseg->type == s_dash || pseg->type == s_gap)
871
0
                        goto d1;
872
12.4k
                    sx = pseg->pt.x, udx = sx - x;
873
12.4k
                    sy = pseg->pt.y, udy = sy - y;
874
12.4k
                    if (udx | udy) {
875
11.8k
                        zero_length = false;
876
11.8k
                        goto d;
877
11.8k
                    }
878
12.4k
                }
879
205k
                if (pgs_lp->dot_length == 0 &&
880
205k
                    pgs_lp->start_cap != gs_cap_round &&
881
205k
                    pgs_lp->end_cap != gs_cap_round &&
882
205k
                    !is_dash_segment) {
883
                    /* From PLRM, stroke operator :
884
                       If a subpath is degenerate (consists of a single-point closed path
885
                       or of two or more points at the same coordinates),
886
                       stroke paints it only if round line caps have been specified */
887
815
                    break;
888
815
                }
889
                /*
890
                 * If the subpath is a dash, take the orientation from the dash segment.
891
                 * Otherwise orient the dot according to the previous segment if
892
                 * any, or else the next segment if any, or else
893
                 * according to the specified dot orientation.
894
                 */
895
204k
                {
896
                    /* When passing here, either pseg == NULL or it points to the
897
                       start of the next subpaph. So we can't use pseg
898
                       for determining the segment direction.
899
                       In same time, psub->last may help, so use it. */
900
204k
                    const segment *end = psub->last;
901
902
204k
                    if (is_dash_segment) {
903
                        /* Nothing. */
904
202k
                    } else if (end != 0 && (end->pt.x != x || end->pt.y != y))
905
0
                        sx = end->pt.x, sy = end->pt.y, udx = sx - x, udy = sy - y;
906
204k
                }
907
                /*
908
                 * Compute the properly oriented dot length, and then
909
                 * draw the dot like a very short line.
910
                 */
911
204k
                if ((udx | udy) == 0) {
912
1.89k
                    if (is_fzero(pgs_lp->dot_orientation.xy)) {
913
                        /* Portrait orientation, dot length = X */
914
1.89k
                        udx = fixed_1;
915
1.89k
                    } else {
916
                        /* Landscape orientation, dot length = Y */
917
0
                        udy = fixed_1;
918
0
                    }
919
1.89k
                }
920
204k
                if (sx == x && sy == y && (pseg == NULL || pseg->type == s_start)) {
921
194k
                    double scale = device_dot_length /
922
194k
                                hypot((double)udx, (double)udy);
923
194k
                    fixed udx1, udy1;
924
                    /*
925
                     * If we're using butt caps, make sure the "line" is
926
                     * long enough to show up.
927
                     * Don't apply this with always_thin, becase
928
                     * draw thin line always rounds the length up.
929
                     */
930
194k
                    if (!always_thin && (pgs_lp->start_cap == gs_cap_butt ||
931
193k
                                         pgs_lp->end_cap   == gs_cap_butt ||
932
193k
                                         pgs_lp->dash_cap  == gs_cap_butt)) {
933
0
                        fixed dmax = max(any_abs(udx), any_abs(udy));
934
935
0
                        if (dmax * scale < fixed_1)
936
0
                            scale = (float)fixed_1 / dmax;
937
0
                    }
938
194k
                    udx1 = (fixed) (udx * scale);
939
194k
                    udy1 = (fixed) (udy * scale);
940
194k
                    sx = x + udx1;
941
194k
                    sy = y + udy1;
942
194k
                }
943
                /*
944
                 * Back up 1 segment to keep the bookkeeping straight.
945
                 */
946
204k
                pseg = (pseg != 0 ? pseg->prev : psub->last);
947
204k
                if (!is_dash_segment)
948
1.89k
                    goto d;
949
202k
                pl.e.p.x = sx;
950
202k
                pl.e.p.y = sy;
951
202k
            }
952
38.9M
            pl.vector.x = udx;
953
38.9M
            pl.vector.y = udy;
954
38.9M
            if (always_thin) {
955
19.7M
                pl.e.cdelta.x = pl.e.cdelta.y = 0;
956
19.7M
                pl.width.x = pl.width.y = 0;
957
19.7M
                pl.thin = true;
958
19.7M
            } else {
959
19.2M
                if (uniform != 0) {
960
                    /* We can save a lot of work in this case. */
961
                    /* We know orient != orient_other. */
962
6.31M
                    double dpx = udx, dpy = udy;
963
6.31M
                    double wl = device_line_width_scale /
964
6.31M
                    hypot(dpx, dpy);
965
966
6.31M
                    pl.e.cdelta.x = (fixed) (dpx * wl);
967
6.31M
                    pl.e.cdelta.y = (fixed) (dpy * wl);
968
                    /* The width is the cap delta rotated by */
969
                    /* 90 degrees. */
970
6.31M
                    if (initial_matrix_reflected)
971
6.25M
                        pl.width.x = pl.e.cdelta.y, pl.width.y = -pl.e.cdelta.x;
972
53.4k
                    else
973
53.4k
                        pl.width.x = -pl.e.cdelta.y, pl.width.y = pl.e.cdelta.x;
974
6.31M
                    pl.thin = false;    /* if not always_thin, */
975
                    /* then never thin. */
976
977
12.8M
                } else {
978
12.8M
                    gs_point dpt;       /* unscaled */
979
12.8M
                    float wl;
980
981
12.8M
                    code = gs_gstate_idtransform(pgs,
982
12.8M
                                                 (float)udx, (float)udy,
983
12.8M
                                                 &dpt);
984
12.8M
                    if (code < 0) {
985
93
                        dpt.x = 0; dpt.y = 0;
986
                        /* Swallow the error */
987
93
                        code = 0;
988
12.8M
                    } else {
989
12.8M
                        wl = line_width_and_scale /
990
12.8M
                            hypot(dpt.x, dpt.y);
991
                        /* Construct the width vector in */
992
                        /* user space, still unscaled. */
993
12.8M
                        dpt.x *= wl;
994
12.8M
                        dpt.y *= wl;
995
12.8M
                    }
996
997
                    /*
998
                     * We now compute both perpendicular
999
                     * and (optionally) parallel half-widths,
1000
                     * as deltas in device space.  We use
1001
                     * a fixed-point, unscaled version of
1002
                     * gs_dtransform.  The second computation
1003
                     * folds in a 90-degree rotation (in user
1004
                     * space, before transforming) in the
1005
                     * direction that corresponds to counter-
1006
                     * clockwise in device space.
1007
                     */
1008
12.8M
                    pl.e.cdelta.x = (fixed) (dpt.x * xx);
1009
12.8M
                    pl.e.cdelta.y = (fixed) (dpt.y * yy);
1010
12.8M
                    if (orient != orient_portrait)
1011
5.42M
                        pl.e.cdelta.x += (fixed) (dpt.y * yx),
1012
5.42M
                            pl.e.cdelta.y += (fixed) (dpt.x * xy);
1013
12.8M
                    if (!reflected ^ initial_matrix_reflected)
1014
12.8M
                        dpt.x = -dpt.x, dpt.y = -dpt.y;
1015
12.8M
                    pl.width.x = (fixed) (dpt.y * xx),
1016
12.8M
                        pl.width.y = -(fixed) (dpt.x * yy);
1017
12.8M
                    if (orient != orient_portrait)
1018
5.42M
                        pl.width.x -= (fixed) (dpt.x * yx),
1019
5.42M
                            pl.width.y += (fixed) (dpt.y * xy);
1020
12.8M
                    pl.thin = width_is_thin(&pl);
1021
12.8M
                }
1022
19.2M
                if (!pl.thin) {
1023
18.9M
                    if (index)
1024
16.9M
                        dev->sgr.stroke_stored = false;
1025
18.9M
                    adjust_stroke(dev, &pl, pgs, false,
1026
18.9M
                            (pseg->prev == 0 || pseg->prev->type == s_start) &&
1027
18.9M
                            (pseg->next == 0 || pseg->next->type == s_start) &&
1028
18.9M
                            (zero_length || !is_closed),
1029
18.9M
                            COMBINE_FLAGS(flags));
1030
18.9M
                    compute_caps(&pl);
1031
18.9M
                }
1032
19.2M
            }
1033
38.9M
            if (index++) {
1034
36.7M
                gs_line_join join =
1035
36.7M
                    (pseg_notes & not_first ? curve_join : pgs_lp->join);
1036
36.7M
                int first;
1037
36.7M
                pl_ptr lptr;
1038
36.7M
                bool ensure_closed;
1039
1040
36.7M
                if (join == gs_join_none) {
1041
                    /* Fake the end of a subpath so we get */
1042
                    /* caps instead of joins. */
1043
39
                    first = 0;
1044
39
                    lptr = 0;
1045
39
                    index = 1;
1046
36.7M
                } else {
1047
36.7M
                    first = (is_closed ? 1 : index - 2);
1048
36.7M
                    lptr = &pl;
1049
36.7M
                }
1050
#ifdef AVOID_JOINING_TO_DASH_GAPS
1051
                if (is_dash_segment) /* Never join to a dash segment */
1052
                    lptr = NULL;
1053
#endif
1054
36.7M
                if (pseg->type == s_gap)
1055
0
                {
1056
0
                    lptr = NULL;
1057
                    /* We are always drawing one line segment behind, so make
1058
                     * sure we don't draw the next one. */
1059
0
                    index = 0;
1060
0
                }
1061
1062
36.7M
                ensure_closed = ((to_path == &stroke_path_body &&
1063
36.7M
                                  lop_is_idempotent(pgs->log_op)) ||
1064
36.7M
                                 (lptr == NULL ? true : lptr->thin));
1065
                /* Draw the PREVIOUS line segment, joining it to lptr (or
1066
                 * capping if lptr == NULL. */
1067
36.7M
                code = (*line_proc) (to_path, to_path_reverse, ensure_closed,
1068
36.7M
                                     first, &pl_prev, lptr,
1069
36.7M
                                     pdevc, dev, pgs, params, &cbox,
1070
36.7M
                                     uniform, join, initial_matrix_reflected,
1071
36.7M
                                     COMBINE_FLAGS(flags));
1072
36.7M
                if (code < 0)
1073
0
                    goto exit;
1074
36.7M
                FILL_STROKE_PATH(pdev, always_thin, pcpath, false);
1075
36.7M
            } else if (pseg->type == s_gap) {
1076
                /* If this segment is a gap, then we don't want to draw it
1077
                 * next time! */
1078
0
                index = 0;
1079
0
            } else
1080
2.20M
                pl_first = pl;
1081
38.9M
            pl_prev = pl;
1082
38.9M
            x = sx, y = sy;
1083
38.9M
            flags = (flags<<4) | nf_all_from_arc;
1084
38.9M
        }
1085
2.20M
        if (index) {
1086
            /* If closed, join back to start, else cap. */
1087
2.20M
            segment_notes notes = (pseg == 0 ?
1088
628k
                                   (const segment *)spath->first_subpath :
1089
2.20M
                                   pseg)->notes;
1090
2.20M
            gs_line_join join = (notes & not_first ? curve_join :
1091
2.20M
                                 pgs_lp->join);
1092
2.20M
            gs_line_cap cap;
1093
            /* For some reason, the Borland compiler requires the cast */
1094
            /* in the following statement. */
1095
2.20M
            pl_ptr lptr =
1096
2.20M
                (!is_closed || join == gs_join_none || zero_length ?
1097
1.89M
                 (pl_ptr) 0 : (pl_ptr) & pl_first);
1098
1099
#ifdef AVOID_JOINING_TO_DASH_GAPS
1100
            if (lptr && psub->type == s_dash)
1101
                lptr = NULL;
1102
#endif
1103
            /* If the subpath starts with a gap, then cap, don't join! */
1104
2.20M
            if (lptr && psub->type == s_start && psub->next && psub->next->type == s_gap)
1105
0
                lptr = NULL;
1106
1107
2.20M
            flags = (((notes & sn_not_first) ?
1108
2.20M
                      ((flags & nf_all_from_arc) | nf_some_from_arc) : 0) |
1109
2.20M
                     ((notes & sn_dash_head) ? nf_dash_head : 0) |
1110
2.20M
                     ((notes & sn_dash_tail) ? nf_dash_tail : 0) |
1111
2.20M
                     (flags & ~nf_all_from_arc));
1112
2.20M
            code = (*line_proc) (to_path, to_path_reverse, true,
1113
2.20M
                                 index - 1, &pl_prev, lptr, pdevc,
1114
2.20M
                                 dev, pgs, params, &cbox, uniform, join,
1115
2.20M
                                 initial_matrix_reflected,
1116
2.20M
                                 COMBINE_FLAGS(flags));
1117
2.20M
            if (code < 0)
1118
0
                goto exit;
1119
2.20M
            FILL_STROKE_PATH(pdev, always_thin, pcpath, false);
1120
2.20M
            cap = ((flags & nf_prev_dash_head) ?
1121
1.32M
                   pgs_lp->start_cap : pgs_lp->dash_cap);
1122
2.20M
            if (traditional && lptr == 0 && cap != gs_cap_butt) {
1123
                /* Create the initial cap at last. */
1124
399
                code = stroke_add_initial_cap_compat(to_path, &pl_first, index == 1, pdevc, dev, pgs);
1125
399
                if (code < 0)
1126
0
                    goto exit;
1127
399
                FILL_STROKE_PATH(pdev, always_thin, pcpath, false);
1128
399
            }
1129
2.20M
        }
1130
2.20M
        psub = (const subpath *)pseg;
1131
2.20M
    }
1132
628k
    if (to_path_reverse != NULL)
1133
0
        code = gx_join_path_and_reverse(to_path, to_path_reverse);
1134
628k
    FILL_STROKE_PATH(pdev, always_thin, pcpath, true);
1135
628k
  exit:
1136
628k
    if (dev == (gx_device *)&cdev)
1137
163k
        cdev.target->sgr = cdev.sgr;
1138
628k
    if (to_path == &stroke_path_body)
1139
628k
        gx_path_free(&stroke_path_body, "gx_stroke_path_only error");   /* (only needed if error) */
1140
628k
    if (to_path_reverse == &stroke_path_reverse)
1141
0
        gx_path_free(&stroke_path_reverse, "gx_stroke_path_only error");
1142
628k
  exf:
1143
628k
    if (dash_count)
1144
10.7k
        gx_path_free(&dpath, "gx_stroke_path exit(dash path)");
1145
    /* If we flattened the path then we set spath to &fpath. If we flattned the path then now we need to free fpath */
1146
628k
    if(flattened_path)
1147
113k
        gx_path_free(&fpath, "gx_stroke_path exit(flattened path)");
1148
628k
    return code;
1149
628k
}
1150
1151
int
1152
gx_stroke_path_only(gx_path * ppath, gx_path * to_path, gx_device * pdev,
1153
               const gs_gstate * pgs, const gx_stroke_params * params,
1154
                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
1155
859k
{
1156
859k
    return gx_stroke_path_only_aux(ppath, to_path, pdev, pgs, params, pdevc, pcpath);
1157
859k
}
1158
1159
/* ------ Internal routines ------ */
1160
1161
/*
1162
 * Test whether a line is thin, i.e., whether the half-width, measured
1163
 * perpendicular to the line in device space, is less than 0.5 pixel.
1164
 * Unfortunately, the width values we computed are perpendicular to the
1165
 * line in *user* space, so we may have to do some extra work.
1166
 */
1167
static bool
1168
width_is_thin(pl_ptr plp)
1169
12.8M
{
1170
12.8M
    fixed dx, dy, wx = plp->width.x, wy = plp->width.y;
1171
1172
    /* If the line is horizontal or vertical, things are easy. */
1173
12.8M
    if ((dy = plp->vector.y) == 0)
1174
667k
        return any_abs(wy) < fixed_half;
1175
12.2M
    if ((dx = plp->vector.x) == 0)
1176
753k
        return any_abs(wx) < fixed_half;
1177
1178
    /* For the longest time, we used to have a test here that
1179
     * attempted to trivially accept diagonal lines as being
1180
     * thin based on the components of the perpendicular
1181
     * width vector in device space as both being less than 0.5.
1182
     * Bug 702196 showed some examples where this was clearly
1183
     * wrong.
1184
     *
1185
     * The cause for this bug was that the 0.5 figure was wrong.
1186
     * For the point to be less than 1/2 a pixel perpendicular
1187
     * distant from the line, we'd need x^2 + y^2 < .5^2.
1188
     * For a 45 degree line, that'd be 2(x^2) < 1/4 = x^2 < 1/8
1189
     * or x < sqr(1/8). 45 degree line is the "worst case", so
1190
     * if both horizontal and vertical widths are less than
1191
     * sqr(1/8), the line is thin. sqr(1/8) = 0.35355339059.
1192
     * So, we should be using sqr(1/8) rather than 0.5.
1193
     *
1194
     * Fixing this did indeed produce many many progressions,
1195
     * but left just the odd file still showing problems.
1196
     *
1197
     * Further investigations show that those cases were due to
1198
     * the use of "non-uniform" scaling matrices, for example
1199
     * (83 0 0 51 0 0). With such matrices, it's possible for
1200
     * nearly horizontal lines to be thin, but nearly vertical
1201
     * ones to be thick (or vice versa). Having the style of
1202
     * line "pop" between thick and thin in a single stroke
1203
     * looks very noticeable.
1204
     *
1205
     * We could change the trivial optimisation below to only
1206
     * apply in the 'uniform' case, but that would never actually
1207
     * trigger (as tested on the cluster), because all such
1208
     * cases are caught by the "always_thin" condition in the
1209
     * caller.
1210
     *
1211
     * Just removing the trivial test and leaving the 'complicated'
1212
     * test below us would leave us vulnerable to "popping",
1213
     * so we disable both. In practice this makes no difference
1214
     * to the number of tests showing diffs in the cluster.
1215
     */
1216
#if 0 /* DISABLED TEST, see above */
1217
    {
1218
        /* thin_threshold = fixed sqr(1/8) - see above. */
1219
        const fixed thin_threshold = float2fixed(0.35355339059f);
1220
        if (any_abs(wx) < thin_threshold && any_abs(wy) < thin_threshold)
1221
            return true;
1222
    }
1223
1224
    /*
1225
     * We have to do this the hard way, by actually computing the
1226
     * perpendicular distance.  The distance from the point (U,V)
1227
     * from a line from (0,0) to (C,D) is
1228
     *      abs(C*V - D*U) / sqrt(C^2 + D^2)
1229
     * In this case, (U,V) is plp->width, and (C,D) is (dx,dy).
1230
     */
1231
    {
1232
        double C = dx, D = dy;
1233
        double num = C * wy - D * wx;
1234
        double denom = hypot(C, D);
1235
1236
        /* both num and denom are scaled by fixed_scale^2, */
1237
        /* so we don't need to do any de-scaling for the test. */
1238
        return fabs(num) < denom * 0.5;
1239
    }
1240
#else
1241
11.4M
    return false;
1242
12.2M
#endif
1243
12.2M
}
1244
1245
/* Adjust the endpoints and width of a stroke segment along a specified axis */
1246
static void
1247
adjust_stroke_transversal(pl_ptr plp, const gs_gstate * pgs, bool thin, bool horiz)
1248
553k
{
1249
553k
    fixed *pw;
1250
553k
    fixed *pov;
1251
553k
    fixed *pev;
1252
553k
    fixed w, w2;
1253
553k
    fixed adj2;
1254
1255
553k
    if (horiz) {
1256
        /* More horizontal stroke */
1257
388k
        pw = &plp->width.y, pov = &plp->o.p.y, pev = &plp->e.p.y;
1258
388k
        adj2 = STROKE_ADJUSTMENT(thin, pgs, y) << 1;
1259
388k
    } else {
1260
        /* More vertical stroke */
1261
165k
        pw = &plp->width.x, pov = &plp->o.p.x, pev = &plp->e.p.x;
1262
165k
        adj2 = STROKE_ADJUSTMENT(thin, pgs, x) << 1;
1263
165k
    }
1264
    /* Round the larger component of the width up or down, */
1265
    /* whichever way produces a result closer to the correct width. */
1266
    /* Note that just rounding the larger component */
1267
    /* may not produce the correct result. */
1268
553k
    w = *pw;
1269
553k
    if (w > 0)
1270
148k
        w2 = fixed_rounded(w << 1);     /* full line width */
1271
405k
    else
1272
405k
        w2 = -fixed_rounded(-w << 1);   /* full line width */
1273
553k
    if (w2 == 0 && *pw != 0) {
1274
        /* Make sure thin lines don't disappear. */
1275
0
        w2 = (*pw < 0 ? -fixed_1 + adj2 : fixed_1 - adj2);
1276
0
        *pw = arith_rshift_1(w2);
1277
0
    }
1278
    /* Only adjust the endpoints if the line is horizontal or vertical. */
1279
553k
    if (*pov == *pev) {
1280
        /* We're going to round the endpoint coordinates, so */
1281
        /* take the fill adjustment into account now. */
1282
407k
        if (w >= 0)
1283
81.3k
            w2 += adj2;
1284
325k
        else
1285
325k
            w2 = adj2 - w2;
1286
407k
        if (w2 & fixed_1)       /* odd width, move to half-pixel */
1287
94.0k
            *pov = *pev = fixed_floor(*pov) + fixed_half;
1288
313k
        else                    /* even width, move to pixel */
1289
313k
            *pov = *pev = fixed_rounded(*pov);
1290
1291
407k
    }
1292
553k
}
1293
1294
static void
1295
adjust_stroke_longitude(pl_ptr plp, const gs_gstate * pgs,
1296
                        bool thin, bool horiz,
1297
                        gs_line_cap start_cap, gs_line_cap end_cap)
1298
205k
{
1299
1300
205k
    fixed *pow = (horiz ? &plp->o.p.y : &plp->o.p.x);
1301
205k
    fixed *pew = (horiz ? &plp->e.p.y : &plp->e.p.x);
1302
1303
    /* Only adjust the endpoints if the line is horizontal or vertical.
1304
       Debugged with pdfwrite->ppmraw 72dpi file2.pdf */
1305
205k
    if (*pow == *pew) {
1306
205k
        fixed *pov = (horiz ? &plp->o.p.x : &plp->o.p.y);
1307
205k
        fixed *pev = (horiz ? &plp->e.p.x : &plp->e.p.y);
1308
205k
        fixed length = any_abs(*pov - *pev);
1309
205k
        fixed length_r, length_r_2;
1310
205k
        fixed mv = (*pov + *pev) / 2, mv_r;
1311
205k
        fixed adj2 = (horiz ? STROKE_ADJUSTMENT(thin, pgs, x)
1312
205k
                            : STROKE_ADJUSTMENT(thin, pgs, y)) << 1;
1313
1314
        /* fixme :
1315
           The best value for adjust_longitude is whether
1316
           the dash is isolated and doesn't cover entire segment.
1317
           The current data structure can't pass this info.
1318
           Therefore we restrict adjust_stroke_longitude with 1 pixel length.
1319
        */
1320
205k
        if (length > fixed_1) /* comparefiles/file2.pdf */
1321
13.4k
            return;
1322
192k
        if (start_cap == gs_cap_butt || end_cap == gs_cap_butt) {
1323
0
            length_r = fixed_rounded(length);
1324
0
            if (length_r < fixed_1)
1325
0
                length_r = fixed_1;
1326
0
            length_r_2 = length_r / 2;
1327
192k
        } else {
1328
            /* Account width for proper placing cap centers. */
1329
192k
            fixed width = any_abs(horiz ? plp->width.y : plp->width.x);
1330
1331
192k
            length_r = fixed_rounded(length + width * 2 + adj2);
1332
192k
            length_r_2 = fixed_rounded(length) / 2;
1333
192k
        }
1334
192k
        if (length_r & fixed_1)
1335
0
            mv_r = fixed_floor(mv) + fixed_half;
1336
192k
        else
1337
192k
            mv_r = fixed_floor(mv);
1338
192k
        if (*pov < *pev) {
1339
0
            *pov = mv_r - length_r_2;
1340
0
            *pev = mv_r + length_r_2;
1341
192k
        } else {
1342
192k
            *pov = mv_r + length_r_2;
1343
192k
            *pev = mv_r - length_r_2;
1344
192k
        }
1345
192k
    }
1346
205k
}
1347
1348
/* Adjust the endpoints and width of a stroke segment */
1349
/* to achieve more uniform rendering. */
1350
/* Only o.p, e.p, e.cdelta, and width have been set. */
1351
static void
1352
adjust_stroke(gx_device *dev, pl_ptr plp, const gs_gstate * pgs,
1353
              bool thin, bool adjust_longitude, note_flags flags)
1354
18.9M
{
1355
18.9M
    bool horiz, adjust = true;
1356
18.9M
    gs_line_cap start_cap = (flags & nf_dash_head ?
1357
100k
                             pgs->line_params.dash_cap :
1358
18.9M
                             pgs->line_params.start_cap);
1359
18.9M
    gs_line_cap end_cap   = (flags & nf_dash_tail ?
1360
105k
                             pgs->line_params.dash_cap :
1361
18.9M
                             pgs->line_params.end_cap);
1362
1363
    /* If stroke_adjustment is disabled, or this isn't a horizontal or
1364
     * vertical line, then bale. */
1365
18.9M
    if (!pgs->stroke_adjust || (plp->width.x != 0 && plp->width.y != 0)) {
1366
18.4M
        dev->sgr.stroke_stored = false;
1367
18.4M
        return;                 /* don't adjust */
1368
18.4M
    }
1369
    /* Recognizing gradients, which some obsolete software
1370
       represent as a set of parallel strokes.
1371
       Such strokes must not be adjusted - bug 687974. */
1372
553k
    if (dev->sgr.stroke_stored &&
1373
553k
        (start_cap == gs_cap_butt || end_cap == gs_cap_butt) &&
1374
553k
        dev->sgr.orig[3].x == plp->vector.x && dev->sgr.orig[3].y == plp->vector.y) {
1375
        /* Parallel. */
1376
190
        if ((int64_t)(plp->o.p.x - dev->sgr.orig[0].x) * plp->vector.x ==
1377
190
            (int64_t)(plp->o.p.y - dev->sgr.orig[0].y) * plp->vector.y &&
1378
190
            (int64_t)(plp->e.p.x - dev->sgr.orig[1].x) * plp->vector.x ==
1379
0
            (int64_t)(plp->e.p.y - dev->sgr.orig[1].y) * plp->vector.y) {
1380
            /* Transversal shift. */
1381
0
            if (any_abs(plp->o.p.x - dev->sgr.orig[0].x) <= any_abs(plp->width.x + dev->sgr.orig[2].x) &&
1382
0
                any_abs(plp->o.p.y - dev->sgr.orig[0].y) <= any_abs(plp->width.y + dev->sgr.orig[2].y) &&
1383
0
                any_abs(plp->e.p.x - dev->sgr.orig[1].x) <= any_abs(plp->width.x + dev->sgr.orig[2].x) &&
1384
0
                any_abs(plp->e.p.y - dev->sgr.orig[1].y) <= any_abs(plp->width.y + dev->sgr.orig[2].y)) {
1385
                /* The strokes were contacting or overlapping. */
1386
0
                if (any_abs(plp->o.p.x - dev->sgr.orig[0].x) >= any_abs(plp->width.x + dev->sgr.orig[2].x) / 2 &&
1387
0
                    any_abs(plp->o.p.y - dev->sgr.orig[0].y) >= any_abs(plp->width.y + dev->sgr.orig[2].y) / 2 &&
1388
0
                    any_abs(plp->e.p.x - dev->sgr.orig[1].x) >= any_abs(plp->width.x + dev->sgr.orig[2].x) / 2 &&
1389
0
                    any_abs(plp->e.p.y - dev->sgr.orig[1].y) >= any_abs(plp->width.y + dev->sgr.orig[2].y) / 2) {
1390
                    /* The strokes were not much overlapping. */
1391
0
                    if (!(any_abs(plp->o.p.x - dev->sgr.adjusted[0].x) <= any_abs(plp->width.x + dev->sgr.adjusted[2].x) &&
1392
0
                          any_abs(plp->o.p.y - dev->sgr.adjusted[0].y) <= any_abs(plp->width.y + dev->sgr.adjusted[2].y) &&
1393
0
                          any_abs(plp->e.p.x - dev->sgr.adjusted[1].x) <= any_abs(plp->width.x + dev->sgr.adjusted[2].x) &&
1394
0
                          any_abs(plp->e.p.y - dev->sgr.adjusted[1].y) <= any_abs(plp->width.y + dev->sgr.adjusted[2].y))) {
1395
                        /* they became not contacting.
1396
                           We should not have adjusted the last stroke. Since if we did,
1397
                           lets change the current one to restore the contact,
1398
                           so that we don't leave gaps when rasterising. See bug 687974.
1399
                         */
1400
0
                        fixed delta_w_x = (dev->sgr.adjusted[2].x - dev->sgr.orig[2].x);
1401
0
                        fixed delta_w_y = (dev->sgr.adjusted[2].y - dev->sgr.orig[2].y);
1402
0
                        fixed shift_o_x = (dev->sgr.adjusted[0].x - dev->sgr.orig[0].x);
1403
0
                        fixed shift_o_y = (dev->sgr.adjusted[0].y - dev->sgr.orig[0].y);
1404
0
                        fixed shift_e_x = (dev->sgr.adjusted[1].x - dev->sgr.orig[1].x); /* Must be same, but we prefer clarity. */
1405
0
                        fixed shift_e_y = (dev->sgr.adjusted[1].y - dev->sgr.orig[1].y);
1406
1407
0
                        if (plp->o.p.x < dev->sgr.orig[0].x ||
1408
0
                            (plp->o.p.x == dev->sgr.orig[0].x && plp->o.p.y < dev->sgr.orig[0].y)) {
1409
                            /* Left contact, adjust to keep the contact. */
1410
0
                            if_debug4m('O', dev->memory, "[O]don't adjust {{%f,%f},{%f,%f}}\n",
1411
0
                                       fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
1412
0
                                       fixed2float(plp->e.p.x), fixed2float(plp->e.p.y));
1413
0
                            plp->width.x += (shift_o_x - delta_w_x) / 2;
1414
0
                            plp->width.y += (shift_o_y - delta_w_y) / 2;
1415
0
                            plp->o.p.x += (shift_o_x - delta_w_x) / 2;
1416
0
                            plp->o.p.y += (shift_o_y - delta_w_y) / 2;
1417
0
                            plp->e.p.x += (shift_e_x - delta_w_x) / 2;
1418
0
                            plp->e.p.y += (shift_e_y - delta_w_y) / 2;
1419
0
                            adjust = false;
1420
0
                        } else {
1421
                            /* Right contact, adjust to keep the contact. */
1422
0
                            if_debug4m('O', dev->memory, "[O]don't adjust {{%f,%f},{%f,%f}}\n",
1423
0
                                       fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
1424
0
                                       fixed2float(plp->e.p.x), fixed2float(plp->e.p.y));
1425
0
                            plp->width.x -= (shift_o_x + delta_w_x) / 2;
1426
0
                            plp->width.y -= (shift_o_y + delta_w_y) / 2;
1427
0
                            plp->o.p.x += (shift_o_x + delta_w_x) / 2;
1428
0
                            plp->o.p.y += (shift_o_y + delta_w_y) / 2;
1429
0
                            plp->e.p.x += (shift_e_x + delta_w_x) / 2;
1430
0
                            plp->e.p.y += (shift_e_y + delta_w_y) / 2;
1431
0
                            adjust = false;
1432
0
                        }
1433
0
                    }
1434
0
                }
1435
0
            }
1436
0
        }
1437
190
    }
1438
553k
    if ((start_cap == gs_cap_butt) || (end_cap == gs_cap_butt)) {
1439
216k
        dev->sgr.stroke_stored = true;
1440
216k
        dev->sgr.orig[0] = plp->o.p;
1441
216k
        dev->sgr.orig[1] = plp->e.p;
1442
216k
        dev->sgr.orig[2] = plp->width;
1443
216k
        dev->sgr.orig[3] = plp->vector;
1444
216k
    } else
1445
336k
        dev->sgr.stroke_stored = false;
1446
553k
    if (adjust) {
1447
553k
        horiz = (any_abs(plp->width.x) <= any_abs(plp->width.y));
1448
553k
        adjust_stroke_transversal(plp, pgs, thin, horiz);
1449
553k
        if (adjust_longitude)
1450
205k
            adjust_stroke_longitude(plp, pgs, thin, horiz, start_cap, end_cap);
1451
553k
    }
1452
553k
    if ((start_cap == gs_cap_butt) || (end_cap == gs_cap_butt)) {
1453
216k
        dev->sgr.adjusted[0] = plp->o.p;
1454
216k
        dev->sgr.adjusted[1] = plp->e.p;
1455
216k
        dev->sgr.adjusted[2] = plp->width;
1456
216k
        dev->sgr.adjusted[3] = plp->vector;
1457
216k
    }
1458
553k
}
1459
1460
/* Compute the intersection of two lines.  This is a messy algorithm */
1461
/* that somehow ought to be useful in more places than just here.... */
1462
/* If the lines are (nearly) parallel, return -1 without setting *pi; */
1463
/* otherwise, return 0 if the intersection is beyond *pp1 and *pp2 in */
1464
/* the direction determined by *pd1 and *pd2, and 1 otherwise. */
1465
static int
1466
line_intersect(
1467
                  p_ptr pp1,    /* point on 1st line */
1468
                  p_ptr pd1,    /* slope of 1st line (dx,dy) */
1469
                  p_ptr pp2,    /* point on 2nd line */
1470
                  p_ptr pd2,    /* slope of 2nd line */
1471
                  p_ptr pi)
1472
20.5M
{                               /* return intersection here */
1473
    /* We don't have to do any scaling, the factors all work out right. */
1474
20.5M
    double u1 = pd1->x, v1 = pd1->y;
1475
20.5M
    double u2 = pd2->x, v2 = pd2->y;
1476
20.5M
    double denom = u1 * v2 - u2 * v1;
1477
20.5M
    double xdiff = pp2->x - pp1->x;
1478
20.5M
    double ydiff = pp2->y - pp1->y;
1479
20.5M
    double f1;
1480
20.5M
    double max_result = any_abs(denom) * (double)max_fixed;
1481
1482
#ifdef DEBUG
1483
    if (gs_debug_c('O')) {
1484
        dlprintf4("[o]Intersect %f,%f(%f/%f)",
1485
                  fixed2float(pp1->x), fixed2float(pp1->y),
1486
                  fixed2float(pd1->x), fixed2float(pd1->y));
1487
        dlprintf4(" & %f,%f(%f/%f),\n",
1488
                  fixed2float(pp2->x), fixed2float(pp2->y),
1489
                  fixed2float(pd2->x), fixed2float(pd2->y));
1490
        dlprintf3("\txdiff=%f ydiff=%f denom=%f ->\n",
1491
                  xdiff, ydiff, denom);
1492
    }
1493
#endif
1494
    /* Check for degenerate result. */
1495
20.5M
    if (any_abs(xdiff) >= max_result || any_abs(ydiff) >= max_result) {
1496
        /* The lines are nearly parallel, */
1497
        /* or one of them has zero length.  Punt. */
1498
59.6k
        if_debug0('O', "\tdegenerate!\n");
1499
59.6k
        return -1;
1500
59.6k
    }
1501
20.4M
    f1 = (v2 * xdiff - u2 * ydiff) / denom;
1502
20.4M
    pi->x = pp1->x + (fixed) (f1 * u1);
1503
20.4M
    pi->y = pp1->y + (fixed) (f1 * v1);
1504
20.4M
    if_debug2('O', "\t%f,%f\n",
1505
20.4M
              fixed2float(pi->x), fixed2float(pi->y));
1506
20.4M
    return (f1 >= 0 && (v1 * xdiff >= u1 * ydiff ? denom >= 0 : denom < 0) ? 0 : 1);
1507
20.5M
}
1508
1509
/* Set up the width and delta parameters for a thin line. */
1510
/* We only approximate the width and height. */
1511
static void
1512
set_thin_widths(register pl_ptr plp)
1513
4.68k
{
1514
4.68k
    fixed dx = plp->e.p.x - plp->o.p.x, dy = plp->e.p.y - plp->o.p.y;
1515
1516
4.68k
#define TRSIGN(v, c) ((v) >= 0 ? (c) : -(c))
1517
4.68k
    if (any_abs(dx) > any_abs(dy)) {
1518
2.15k
        plp->width.x = plp->e.cdelta.y = 0;
1519
2.15k
        plp->width.y = plp->e.cdelta.x = TRSIGN(dx, fixed_half);
1520
2.52k
    } else {
1521
2.52k
        plp->width.y = plp->e.cdelta.x = 0;
1522
2.52k
        plp->width.x = -(plp->e.cdelta.y = TRSIGN(dy, fixed_half));
1523
2.52k
    }
1524
4.68k
#undef TRSIGN
1525
4.68k
}
1526
1527
/* Draw a line on the device. */
1528
/* Treat no join the same as a bevel join. */
1529
/* rpath should always be NULL, hence ensure_closed can be ignored */
1530
static int
1531
stroke_fill(gx_path * ppath, gx_path * rpath, bool ensure_closed, int first,
1532
            register pl_ptr plp, pl_ptr nplp, const gx_device_color * pdevc,
1533
            gx_device * dev, const gs_gstate * pgs,
1534
            const gx_stroke_params * params, const gs_fixed_rect * pbbox,
1535
            int uniform, gs_line_join join, bool reflected,
1536
            note_flags flags)
1537
38.9M
{
1538
38.9M
    const fixed lix = plp->o.p.x;
1539
38.9M
    const fixed liy = plp->o.p.y;
1540
38.9M
    const fixed litox = plp->e.p.x;
1541
38.9M
    const fixed litoy = plp->e.p.y;
1542
1543
    /* assert(lop_is_idempotent(pgs->log_op)); */
1544
38.9M
    if (plp->thin) {
1545
        /* Minimum-width line, don't have to be careful with caps/joins. */
1546
20.0M
        return (*dev_proc(dev, draw_thin_line))(dev, lix, liy, litox, litoy,
1547
20.0M
                                                pdevc, pgs->log_op,
1548
20.0M
                                                pgs->fill_adjust.x,
1549
20.0M
                                                pgs->fill_adjust.y);
1550
20.0M
    }
1551
    /* Check for being able to fill directly. */
1552
18.9M
    {
1553
18.9M
        const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs);
1554
18.9M
        gs_line_cap start_cap = (flags & nf_dash_head ?
1555
17.4M
                                 pgs_lp->dash_cap : pgs_lp->start_cap);
1556
18.9M
        gs_line_cap end_cap   = (flags & nf_dash_tail ?
1557
17.4M
                                 pgs_lp->dash_cap : pgs_lp->end_cap);
1558
1559
18.9M
        if (first != 0)
1560
17.1M
            start_cap = gs_cap_butt;
1561
18.9M
        if (nplp != 0)
1562
17.1M
            end_cap = gs_cap_butt;
1563
18.9M
        if (!plp->thin && (nplp == 0 || !nplp->thin)
1564
18.9M
            && (start_cap == gs_cap_butt || start_cap == gs_cap_square)
1565
18.9M
            && (end_cap   == gs_cap_butt || end_cap   == gs_cap_square)
1566
18.9M
            && (join == gs_join_bevel || join == gs_join_miter ||
1567
18.2M
                join == gs_join_none)
1568
18.9M
            && (pgs->fill_adjust.x | pgs->fill_adjust.y) == 0
1569
18.9M
            ) {
1570
0
            gs_fixed_point points[6];
1571
0
            int npoints, code;
1572
0
            fixed ax, ay, bx, by;
1573
1574
0
            npoints = cap_points(start_cap, &plp->o, points);
1575
0
            if (nplp == 0)
1576
0
                code = cap_points(end_cap, &plp->e, points + npoints);
1577
0
            else
1578
0
                code = line_join_points(pgs_lp, plp, nplp, points + npoints,
1579
0
                                        (uniform ? (gs_matrix *) 0 :
1580
0
                                         &ctm_only(pgs)), join, reflected);
1581
0
            if (code < 0)
1582
0
                goto general;
1583
            /* Make sure the parallelogram fill won't overflow. */
1584
0
#define SUB_OVERFLOWS(r, u, v)\
1585
0
  (((r = u - v) ^ u) < 0 && (u ^ v) < 0)
1586
0
            if (SUB_OVERFLOWS(ax, points[0].x, points[1].x) ||
1587
0
                SUB_OVERFLOWS(ay, points[0].y, points[1].y) ||
1588
0
                SUB_OVERFLOWS(bx, points[2].x, points[1].x) ||
1589
0
                SUB_OVERFLOWS(by, points[2].y, points[1].y)
1590
0
                )
1591
0
                goto general;
1592
0
#undef SUB_OVERFLOWS
1593
0
            if (nplp != 0) {
1594
0
                if (join == gs_join_miter) {
1595
                    /* Make sure we have a bevel and not a miter. */
1596
0
                    if (!(points[2].x == plp->e.co.x &&
1597
0
                          points[2].y == plp->e.co.y &&
1598
0
                          points[5].x == plp->e.ce.x &&
1599
0
                          points[5].y == plp->e.ce.y)
1600
0
                        )
1601
0
                        goto fill;
1602
0
                } {
1603
0
                    const gs_fixed_point *bevel = points + 2;
1604
1605
                    /* Identify which 3 points define the bevel triangle. */
1606
0
                    if (points[3].x == nplp->o.p.x &&
1607
0
                        points[3].y == nplp->o.p.y
1608
0
                        )
1609
0
                        ++bevel;
1610
                    /* Fill the bevel. */
1611
0
                    code = (*dev_proc(dev, fill_triangle)) (dev,
1612
0
                                                         bevel->x, bevel->y,
1613
0
                               bevel[1].x - bevel->x, bevel[1].y - bevel->y,
1614
0
                               bevel[2].x - bevel->x, bevel[2].y - bevel->y,
1615
0
                                                        pdevc, pgs->log_op);
1616
0
                    if (code < 0)
1617
0
                        return code;
1618
0
                }
1619
0
            }
1620
            /* Fill the body of the stroke. */
1621
0
            return (*dev_proc(dev, fill_parallelogram)) (dev,
1622
0
                                                   points[1].x, points[1].y,
1623
0
                                                         ax, ay, bx, by,
1624
0
                                                         pdevc, pgs->log_op);
1625
0
          fill:
1626
0
            code = add_points(ppath, points, npoints + code, true);
1627
0
            if (code < 0)
1628
0
                return code;
1629
0
            return gx_path_close_subpath(ppath);
1630
0
        }
1631
18.9M
    }
1632
    /* General case: construct a path for the fill algorithm. */
1633
18.9M
 general:
1634
18.9M
    return stroke_add(ppath, rpath, ensure_closed, first, plp, nplp, pdevc,
1635
18.9M
                      dev, pgs, params, pbbox, uniform, join, reflected,
1636
18.9M
                      flags);
1637
18.9M
}
1638
1639
/* Add a segment to the path.  This handles all the complex cases. */
1640
static int
1641
stroke_add(gx_path * ppath, gx_path * rpath, bool ensure_closed, int first,
1642
           pl_ptr plp, pl_ptr nplp, const gx_device_color * pdevc,
1643
           gx_device * dev, const gs_gstate * pgs,
1644
           const gx_stroke_params * params,
1645
           const gs_fixed_rect * ignore_pbbox, int uniform,
1646
           gs_line_join join, bool reflected, note_flags flags)
1647
18.9M
{
1648
18.9M
    const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs);
1649
18.9M
    gs_fixed_point points[8];
1650
18.9M
    int npoints;
1651
18.9M
    int code;
1652
18.9M
    bool moveto_first = true;
1653
18.9M
    gs_line_cap start_cap = (flags & nf_dash_head ?
1654
17.5M
                             pgs_lp->dash_cap : pgs_lp->start_cap);
1655
18.9M
    gs_line_cap end_cap   = (flags & nf_dash_tail ?
1656
17.5M
                             pgs_lp->dash_cap : pgs_lp->end_cap);
1657
1658
18.9M
    if (plp->thin) {
1659
        /* We didn't set up the endpoint parameters before, */
1660
        /* because the line was thin.  Do it now. */
1661
4.68k
        set_thin_widths(plp);
1662
4.68k
        adjust_stroke(dev, plp, pgs, true, first == 0 && nplp == 0, flags);
1663
4.68k
        compute_caps(plp);
1664
4.68k
    }
1665
    /* Create an initial cap if desired. */
1666
18.9M
    if (first == 0 && start_cap == gs_cap_round) {
1667
499k
        if ((code = gx_path_add_point(ppath, plp->o.co.x, plp->o.co.y)) < 0 ||
1668
499k
            (code = add_pie_cap(ppath, &plp->o)) < 0)
1669
0
            return code;
1670
499k
        npoints = 0;
1671
499k
        moveto_first = false;
1672
18.4M
    } else {
1673
18.4M
        if ((npoints = cap_points((first == 0 ? start_cap : gs_cap_butt),
1674
18.4M
                                  &plp->o, points)) < 0)
1675
0
            return npoints;
1676
18.4M
    }
1677
18.9M
    if (nplp == 0) {
1678
        /* Add a final cap. */
1679
1.74M
        if (end_cap == gs_cap_round) {
1680
499k
            ASSIGN_POINT(&points[npoints], plp->e.co);
1681
499k
            ++npoints;
1682
499k
            if ((code = add_points(ppath, points, npoints, moveto_first)) < 0)
1683
0
                return code;
1684
499k
            code = add_pie_cap(ppath, &plp->e);
1685
499k
            goto done;
1686
499k
        }
1687
1.24M
        code = cap_points(end_cap, &plp->e, points + npoints);
1688
17.2M
    } else if (nplp->thin) /* no join */
1689
7.07k
        code = cap_points(gs_cap_butt, &plp->e, points + npoints);
1690
17.2M
    else if (join == gs_join_round) {
1691
757k
        ASSIGN_POINT(&points[npoints], plp->e.co);
1692
757k
        ++npoints;
1693
757k
        if ((code = add_points(ppath, points, npoints, moveto_first)) < 0)
1694
0
            return code;
1695
757k
        code = add_pie_join(ppath, plp, nplp, reflected, true);
1696
757k
        goto done;
1697
16.4M
    } else if (flags & nf_all_from_arc) {
1698
        /* If all the segments in 'prev' and 'current' are from a curve
1699
         * then the join should actually be a round one, because it would
1700
         * have been round if we had flattened it enough. */
1701
13.1M
        ASSIGN_POINT(&points[npoints], plp->e.co);
1702
13.1M
        ++npoints;
1703
13.1M
        if ((code = add_points(ppath, points, npoints, moveto_first)) < 0)
1704
0
            return code;
1705
13.1M
        code = add_pie_join(ppath, plp, nplp, reflected, false);
1706
13.1M
        goto done;
1707
13.1M
    } else                      /* non-round join */
1708
3.35M
       code = line_join_points(pgs_lp, plp, nplp, points + npoints,
1709
3.35M
                                (uniform ? (gs_matrix *) 0 : &ctm_only(pgs)),
1710
3.35M
                                join, reflected);
1711
4.60M
    if (code < 0)
1712
0
        return code;
1713
4.60M
    code = add_points(ppath, points, npoints + code, moveto_first);
1714
18.9M
  done:
1715
18.9M
    if (code < 0)
1716
0
        return code;
1717
18.9M
    if ((flags & nf_some_from_arc) && (!plp->thin) &&
1718
18.9M
        (nplp != NULL) && (!nplp->thin))
1719
14.5M
        code = join_under_pie(ppath, plp, nplp, reflected);
1720
18.9M
    return gx_path_close_subpath(ppath);
1721
18.9M
}
1722
1723
/* When painting the 'underjoin' (the 'inside' of a join), we
1724
 * need to take special care if the curve is particularly wide as
1725
 * the leading edge of the underside of the first stroked segment
1726
 * may be beyond the leading edge of the underside of the second
1727
 * stroked segment. Similarly, the trailing edge of the second
1728
 * stroked segment may be behing the trailing edge of the first
1729
 * stroked segment. We detect those cases here.
1730
 *
1731
 * We detect the first case by projecting plp.width onto nplp.vector.
1732
 * If the projected vector is longer then nplp.vector, we have a
1733
 * problem.
1734
 *
1735
 * len_vector_squared = nplp.vector.x * nplp.vector.x + nplp.vector.y * nplp.nvector.y
1736
 * len_vector = sqr(len_vector_squared)
1737
 * len_projection_unnormalised = plp.width.x * nplp.vector.x + plp.width.y * nplp.vector.y
1738
 * len_projection = len_projection_unnormalised / len_vector
1739
 *
1740
 * len_projection > len_vector === len_projection_unnormalised > len_vector * len_vector
1741
 * === len_projection_unnormalised > len_vector_squared
1742
 */
1743
1744
#ifdef SLOWER_BUT_MORE_ACCURATE_STROKING
1745
static bool
1746
wide_underjoin(pl_ptr plp, pl_ptr nplp)
1747
{
1748
    double h_squared = (double)nplp->vector.x * nplp->vector.x + (double)nplp->vector.y * nplp->vector.y;
1749
    double dot = (double)plp->width.x * nplp->vector.x + (double)plp->width.y * nplp->vector.y;
1750
1751
    if (dot < 0)
1752
        dot = -dot;
1753
    if (dot > h_squared)
1754
        return 1;
1755
1756
    h_squared = (double)plp->vector.x * plp->vector.x + (double)plp->vector.y * plp->vector.y;
1757
    dot = (double)nplp->width.x * plp->vector.x + (double)nplp->width.y * plp->vector.y;
1758
    if (dot < 0)
1759
        dot = -dot;
1760
    if (dot > h_squared)
1761
        return 1;
1762
1763
    return 0;
1764
}
1765
#endif
1766
1767
static int
1768
check_miter(const gx_line_params * pgs_lp, pl_ptr plp, pl_ptr nplp,
1769
            const gs_matrix * pmat, p_ptr outp, p_ptr np, p_ptr mpt,
1770
            bool ccw0)
1771
3.00M
{
1772
    /*
1773
     * Check whether a miter join is appropriate.
1774
     * Let a, b be the angles of the two lines.
1775
     * We check tan(a-b) against the miter_check
1776
     * by using the following formula:
1777
     *      If tan(a)=u1/v1 and tan(b)=u2/v2, then
1778
     *      tan(a-b) = (u1*v2 - u2*v1) / (u1*u2 + v1*v2).
1779
     *
1780
     * We can do all the computations unscaled,
1781
     * because we're only concerned with ratios.
1782
     * However, if we have a non-uniform coordinate
1783
     * system (indicated by pmat != 0), we must do the
1784
     * computations in user space.
1785
     */
1786
3.00M
    float check;
1787
3.00M
    double u1, v1, u2, v2;
1788
3.00M
    double num, denom;
1789
3.00M
    int code;
1790
1791
    /*
1792
     * Don't bother with the miter check if the two
1793
     * points to be joined are very close together,
1794
     * namely, in the same square half-pixel.
1795
     */
1796
3.00M
    if (fixed2long(outp->x << 1) == fixed2long(np->x << 1) &&
1797
3.00M
        fixed2long(outp->y << 1) == fixed2long(np->y << 1))
1798
978k
        return 1;
1799
1800
2.02M
    check = pgs_lp->miter_check;
1801
2.02M
    u1 = plp->vector.y, v1 = plp->vector.x;
1802
2.02M
    u2 = -nplp->vector.y, v2 = -nplp->vector.x;
1803
1804
2.02M
    if (pmat) {
1805
824k
        gs_point pt;
1806
1807
824k
        code = gs_distance_transform_inverse(v1, u1, pmat, &pt);
1808
824k
        if (code < 0)
1809
0
        return code;
1810
824k
        v1 = pt.x, u1 = pt.y;
1811
824k
        code = gs_distance_transform_inverse(v2, u2, pmat, &pt);
1812
824k
        if (code < 0)
1813
0
            return code;
1814
824k
        v2 = pt.x, u2 = pt.y;
1815
        /*
1816
         * We need to recompute ccw according to the
1817
         * relative positions of the lines in user space.
1818
         * We repeat the computation described above,
1819
         * using the cdelta values instead of the widths.
1820
         * Because the definition of ccw above is inverted
1821
         * from the intuitive one (for historical reasons),
1822
         * we actually have to do the test backwards.
1823
         */
1824
824k
        ccw0 = v1 * u2 < v2 * u1;
1825
#ifdef DEBUG
1826
        {
1827
            double a1 = atan2(u1, v1), a2 = atan2(u2, v2), dif = a1 - a2;
1828
1829
            if (dif < 0)
1830
                dif += 2 * M_PI;
1831
            else if (dif >= 2 * M_PI)
1832
                dif -= 2 * M_PI;
1833
            if (dif != 0 && (dif < M_PI) != ccw0)
1834
                lprintf8("ccw wrong: tan(a1=%g)=%g/%g, tan(a2=%g)=%g,%g, dif=%g, ccw0=%d\n",
1835
                         a1, u1, v1, a2, u2, v2, dif, ccw0);
1836
        }
1837
#endif
1838
824k
    }
1839
2.02M
    num = u1 * v2 - u2 * v1;
1840
2.02M
    denom = u1 * u2 + v1 * v2;
1841
    /*
1842
     * We will want either tan(a-b) or tan(b-a)
1843
     * depending on the orientations of the lines.
1844
     * Fortunately we know the relative orientations already.
1845
     */
1846
2.02M
    if (!ccw0)          /* have plp - nplp, want vice versa */
1847
640k
        num = -num;
1848
#ifdef DEBUG
1849
    if (gs_debug_c('O')) {
1850
        dlprintf4("[o]Miter check: u1/v1=%f/%f, u2/v2=%f/%f,\n",
1851
                  u1, v1, u2, v2);
1852
        dlprintf3("        num=%f, denom=%f, check=%f\n",
1853
                  num, denom, check);
1854
    }
1855
#endif
1856
    /*
1857
     * If we define T = num / denom, then we want to use
1858
     * a miter join iff arctan(T) >= arctan(check).
1859
     * We know that both of these angles are in the 1st
1860
     * or 2nd quadrant, and since arctan is monotonic
1861
     * within each quadrant, we can do the comparisons
1862
     * on T and check directly, taking signs into account
1863
     * as follows:
1864
     *              sign(T) sign(check)     atan(T) >= atan(check)
1865
     *              ------- -----------     ----------------------
1866
     *              +       +               T >= check
1867
     *              -       +               true
1868
     *              +       -               false
1869
     *              -       -               T >= check
1870
     */
1871
2.02M
    if (num == 0 && denom == 0)
1872
0
        return_error(gs_error_unregistered); /* Must not happen. */
1873
2.02M
    if (denom < 0)
1874
954k
        num = -num, denom = -denom;
1875
    /* Now denom >= 0, so sign(num) = sign(T). */
1876
2.02M
    if (check > 0 ?
1877
2.02M
        (num < 0 || num >= denom * check) :
1878
2.02M
        (num < 0 && num >= denom * check)
1879
2.02M
        ) {
1880
        /* OK to use a miter join. */
1881
2.01M
        gs_fixed_point dirn1, dirn2;
1882
1883
2.01M
        dirn1.x = plp->e.cdelta.x;
1884
2.01M
        dirn1.y = plp->e.cdelta.y;
1885
        /* If this direction is small enough that we might have
1886
         * underflowed and the vector record is suitable for us
1887
         * to use to calculate a better one, then do so. */
1888
2.01M
        if ((abs(dirn1.x) + abs(dirn1.y) < 16) &&
1889
2.01M
            ((plp->vector.x != 0) || (plp->vector.y != 0)))
1890
2
        {
1891
2
            float scale = 65536.0;
1892
2
            if (abs(plp->vector.x) > abs(plp->vector.y))
1893
2
                scale /= abs(plp->vector.x);
1894
0
            else
1895
0
                scale /= abs(plp->vector.y);
1896
2
            dirn1.x = (fixed)(plp->vector.x*scale);
1897
2
            dirn1.y = (fixed)(plp->vector.y*scale);
1898
2
        }
1899
2.01M
        dirn2.x = nplp->o.cdelta.x;
1900
2.01M
        dirn2.y = nplp->o.cdelta.y;
1901
        /* If this direction is small enough that we might have
1902
         * underflowed and the vector record is suitable for us
1903
         * to use to calculate a better one, then do so. */
1904
2.01M
        if ((abs(dirn2.x) + abs(dirn2.y) < 16) &&
1905
2.01M
            ((nplp->vector.x != 0) || (nplp->vector.y != 0)))
1906
2
        {
1907
2
            float scale = 65536.0;
1908
2
            if (abs(nplp->vector.x) > abs(nplp->vector.y))
1909
2
                scale /= abs(nplp->vector.x);
1910
0
            else
1911
0
                scale /= abs(nplp->vector.y);
1912
2
            dirn2.x = (fixed)(-nplp->vector.x*scale);
1913
2
            dirn2.y = (fixed)(-nplp->vector.y*scale);
1914
2
        }
1915
2.01M
        if_debug0('O', "        ... passes.\n");
1916
        /* Compute the intersection of the extended edge lines. */
1917
2.01M
        if (line_intersect(outp, &dirn1, np, &dirn2, mpt) == 0)
1918
1.97M
            return 0;
1919
2.01M
    }
1920
50.1k
    return 1;
1921
2.02M
}
1922
1923
/* Add a segment to the path.
1924
 * This works by crafting 2 paths, one for each edge, that will later be
1925
 * merged together. */
1926
static int
1927
stroke_add_fast(gx_path * ppath, gx_path * rpath, bool ensure_closed, int first,
1928
                pl_ptr plp, pl_ptr nplp, const gx_device_color * pdevc,
1929
                gx_device * dev, const gs_gstate * pgs,
1930
                const gx_stroke_params * params,
1931
                const gs_fixed_rect * ignore_pbbox, int uniform,
1932
                gs_line_join join, bool reflected, note_flags flags)
1933
0
{
1934
0
    const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs);
1935
0
    gs_fixed_point points[8];
1936
0
    gs_fixed_point rpoints[8];
1937
0
    int npoints  = 0;
1938
0
    int nrpoints = 0;
1939
0
    int code;
1940
0
    bool moveto_first  = false;
1941
0
    bool rmoveto_first = false;
1942
0
    gs_line_cap start_cap, end_cap;
1943
0
    const gs_matrix *pmat = (uniform ? (const gs_matrix *)NULL : &ctm_only(pgs));
1944
0
    enum {
1945
0
        joinsense_cap = 0,
1946
0
        joinsense_cw = 1,
1947
0
        joinsense_ccw = 2,
1948
0
        joinsense_over = 4,
1949
0
        joinsense_under = 8,
1950
0
    } joinsense = joinsense_cap;
1951
1952
0
    if (plp->thin) {
1953
        /* We didn't set up the endpoint parameters before, */
1954
        /* because the line was thin.  Do it now. */
1955
0
        set_thin_widths(plp);
1956
0
        adjust_stroke(dev, plp, pgs, true, first == 0 && nplp == 0, flags);
1957
0
        compute_caps(plp);
1958
0
    }
1959
0
    start_cap = (flags & nf_dash_head ?
1960
0
                 pgs_lp->dash_cap : pgs_lp->start_cap);
1961
0
    end_cap   = (flags & nf_dash_tail ?
1962
0
                 pgs_lp->dash_cap : pgs_lp->end_cap);
1963
    /* If we're starting a new rpath here, we need to fake a new cap.
1964
     * Don't interfere if we would have been doing a cap anyway. */
1965
0
    if (gx_path_is_void(rpath) && (first != 0)) {
1966
0
        first = 0;
1967
0
        start_cap = gs_cap_butt;
1968
0
        end_cap   = gs_cap_butt;
1969
0
        moveto_first  = true;
1970
0
        rmoveto_first = true;
1971
0
    }
1972
0
    if (first == 0) {
1973
        /* Create an initial cap. */
1974
0
        if (start_cap == gs_cap_round) {
1975
0
            if ((code = gx_path_add_point(ppath, plp->o.co.x, plp->o.co.y)) < 0 ||
1976
0
                (code = add_pie_cap(ppath, &plp->o)) < 0)
1977
0
                return code;
1978
0
            moveto_first = false;
1979
0
        } else {
1980
0
            if ((npoints = cap_points(start_cap, &plp->o, points)) < 0)
1981
0
                return npoints;
1982
0
            moveto_first = true;
1983
0
        }
1984
0
        rmoveto_first = true;
1985
0
        ASSIGN_POINT(&rpoints[0], plp->o.co);
1986
0
        nrpoints = 1;
1987
0
    }
1988
    /* Add points to move us along the edges of this stroke */
1989
0
    ASSIGN_POINT(&points [npoints ], plp->e.co);
1990
0
    ASSIGN_POINT(&rpoints[nrpoints], plp->e.ce);
1991
0
    npoints++;
1992
0
    nrpoints++;
1993
1994
0
    if (nplp != NULL && !nplp->thin) {
1995
        /* We need to do a join. What sense is it it? */
1996
0
        double l, r;
1997
1998
0
        l = (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */;
1999
0
        r = (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */;
2000
2001
0
        if ((l == r) && (join == gs_join_round))
2002
0
             joinsense = joinsense_cap;
2003
0
        else if ((l > r) ^ reflected)
2004
0
             joinsense = joinsense_ccw | joinsense_over | joinsense_under;
2005
0
        else
2006
0
             joinsense = joinsense_cw | joinsense_over | joinsense_under;
2007
2008
0
        if (joinsense != joinsense_cap && join == gs_join_miter) {
2009
            /* We need to do a miter line join. Miters are 'special'
2010
             * in that we'd like to do them by adjusting the existing
2011
             * points, rather than adding new ones. */
2012
0
            gs_fixed_point mpt;
2013
0
            if (joinsense & joinsense_ccw) {
2014
                /* Underjoin (in reverse path):
2015
                 * A = plp->o.co, B = plp->e.ce, C = nplp->o.co, D = nplp->e.ce */
2016
0
                double xa =  plp->o.co.x, ya =  plp->o.co.y;
2017
0
                double xb =  plp->e.ce.x, yb =  plp->e.ce.y;
2018
0
                double xc = nplp->o.co.x, yc = nplp->o.co.y;
2019
0
                double xd = nplp->e.ce.x, yd = nplp->e.ce.y;
2020
0
                double xab = xa-xb, xac = xa-xc, xcd = xc-xd;
2021
0
                double yab = ya-yb, yac = ya-yc, ycd = yc-yd;
2022
0
                double t_num = xac * ycd - yac * xcd;
2023
0
                double t_den = xab * ycd - yab * xcd;
2024
0
                code = check_miter(pgs_lp, plp, nplp, pmat, &plp->e.co,
2025
0
                                   &nplp->o.ce, &mpt, true);
2026
0
                if (code < 0)
2027
0
                    return code;
2028
0
                if (code == 0) {
2029
0
                    points[npoints-1].x = mpt.x;
2030
0
                    points[npoints-1].y = mpt.y;
2031
0
                    if (ensure_closed) {
2032
0
                        points[npoints].x = nplp->o.ce.x;
2033
0
                        points[npoints].y = nplp->o.ce.y;
2034
0
                        npoints++;
2035
0
                    }
2036
0
                    joinsense &= ~joinsense_over;
2037
0
                } else
2038
0
                    join = gs_join_bevel;
2039
0
                if (t_den != 0 &&
2040
0
                    ((t_num >= 0 && t_num <= t_den) ||
2041
0
                     (t_num <= 0 && t_num >= t_den))) {
2042
0
                    double x = xa - xab * t_num / t_den;
2043
0
                    double y = ya - yab * t_num / t_den;
2044
0
                    rpoints[nrpoints-1].x = (fixed)x;
2045
0
                    rpoints[nrpoints-1].y = (fixed)y;
2046
0
                    joinsense &= ~joinsense_under;
2047
0
                }
2048
0
            } else {
2049
                /* Underjoin (in fwd path):
2050
                 * A = plp->o.ce, B = plp->e.co, C = nplp->o.ce, D = nplp->e.co */
2051
0
                double xa =  plp->o.ce.x, ya =  plp->o.ce.y;
2052
0
                double xb =  plp->e.co.x, yb =  plp->e.co.y;
2053
0
                double xc = nplp->o.ce.x, yc = nplp->o.ce.y;
2054
0
                double xd = nplp->e.co.x, yd = nplp->e.co.y;
2055
0
                double xab = xa-xb, xac = xa-xc, xcd = xc-xd;
2056
0
                double yab = ya-yb, yac = ya-yc, ycd = yc-yd;
2057
0
                double t_num = xac * ycd - yac * xcd;
2058
0
                double t_den = xab * ycd - yab * xcd;
2059
0
                code = check_miter(pgs_lp, plp, nplp, pmat, &plp->e.ce,
2060
0
                                   &nplp->o.co, &mpt, false);
2061
0
                if (code < 0)
2062
0
                    return code;
2063
0
                if (code == 0) {
2064
0
                    rpoints[nrpoints-1].x = mpt.x;
2065
0
                    rpoints[nrpoints-1].y = mpt.y;
2066
0
                    if (ensure_closed) {
2067
0
                        rpoints[nrpoints].x = nplp->o.co.x;
2068
0
                        rpoints[nrpoints].y = nplp->o.co.y;
2069
0
                        nrpoints++;
2070
0
                    }
2071
0
                    joinsense &= ~joinsense_over;
2072
0
                } else
2073
0
                    join = gs_join_bevel;
2074
0
                if (t_den != 0 &&
2075
0
                    ((t_num >= 0 && t_num <= t_den) ||
2076
0
                     (t_num <= 0 && t_num >= t_den)))   {
2077
0
                    double x = xa - xab * t_num / t_den;
2078
0
                    double y = ya - yab * t_num / t_den;
2079
0
                    points[npoints-1].x = (fixed)x;
2080
0
                    points[npoints-1].y = (fixed)y;
2081
0
                    joinsense &= ~joinsense_under;
2082
0
                }
2083
0
            }
2084
0
        }
2085
0
    }
2086
2087
0
    if ((code = add_points(ppath, points, npoints, moveto_first)) < 0)
2088
0
        return code;
2089
0
    if ((code = add_points(rpath, rpoints, nrpoints, rmoveto_first)) < 0)
2090
0
        return code;
2091
0
    npoints  = 0;
2092
0
    nrpoints = 0;
2093
2094
0
    if (nplp == 0) { /* Add a final cap. */
2095
0
        if (end_cap == gs_cap_round) {
2096
0
            code = add_pie_cap(ppath, &plp->e);
2097
0
        } else {
2098
0
            code = cap_points(end_cap, &plp->e, points);
2099
0
            npoints = code;
2100
0
        }
2101
0
    } else if (nplp->thin) { /* no join */
2102
0
        code = cap_points(gs_cap_butt, &plp->e, points);
2103
0
        npoints = code;
2104
0
    } else if (joinsense == joinsense_cap) {
2105
        /* Do a cap */
2106
0
        code = add_pie_cap(ppath, &plp->e);
2107
0
        if (code >= 0) {
2108
            /* If the next line is in the opposite direction as the current one
2109
             * we want to leave the point on the same side as it was
2110
             * originally. This is required for paths that come to a stop
2111
             * and then reverse themselves, but may produce more complexity
2112
             * than we'd really like at the ends of smooth beziers. */
2113
0
            if ((double)(plp->width.x) * nplp->width.x + (double)plp->width.y * nplp->width.y >= 0)
2114
0
                code = gx_path_add_line(ppath, plp->e.co.x, plp->e.co.y);
2115
0
        }
2116
0
    } else if (joinsense & joinsense_ccw) {
2117
        /* CCW rotation. Join in the forward path. "Underjoin" in the
2118
         * reverse path. */
2119
0
        if (joinsense & joinsense_over) {
2120
            /* RJW: Ideally we should include the "|| flags" clause in
2121
             * the following condition. This forces all joins between
2122
             * line segments generated from arcs to be round. This would
2123
             * solve some flatness issues, but makes some pathological
2124
             * cases incredibly slow. */
2125
0
            if (join == gs_join_round /* || (flags & nf_all_from_arc) */) {
2126
0
                code = add_pie_join_fast_ccw(ppath, plp, nplp, reflected);
2127
0
            } else { /* non-round join */
2128
0
                code = line_join_points_fast_ccw(pgs_lp, plp, nplp,
2129
0
                                                 points, pmat, join);
2130
0
                npoints = code;
2131
0
            }
2132
0
            if (code < 0)
2133
0
                return code;
2134
0
        }
2135
0
        if (joinsense & joinsense_under) {
2136
            /* The underjoin */
2137
0
#ifndef SLOWER_BUT_MORE_ACCURATE_STROKING
2138
0
            if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) == 0) {
2139
                /* RJW: This is an approximation. We ought to draw a line
2140
                 * back to nplp->o.p, and then independently fill any exposed
2141
                 * region under the curve with a round join. Sadly, that's
2142
                 * a) really hard to do, and b) makes certain pathological
2143
                 * filling cases MUCH slower due to the greater number of
2144
                 * "cross-segment" line segments this produces. Instead,
2145
                 * we just skip the line to the middle, and join across the
2146
                 * bottom instead. This is akin to what other graphics libs
2147
                 * do (such as fitz, libart, etc). It's not perfect but in
2148
                 * most cases it's close, and results in faster to fill
2149
                 * paths.
2150
                 */
2151
                /* RJW: This goes wrong for some paths, as the 'underjoin' wind
2152
                 * will be the wrong way. See bug 694971 */
2153
0
                code = gx_path_add_line(rpath, nplp->o.p.x, nplp->o.p.y);
2154
0
                if (code < 0)
2155
0
                    return code;
2156
0
            }
2157
#else
2158
            if (wide_underjoin(plp, nplp))
2159
            {
2160
                code = gx_path_add_line(rpath, nplp->o.p.x, nplp->o.p.y);
2161
                if (code < 0)
2162
                    return code;
2163
                if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) != 0) {
2164
                    code = gx_path_add_line(rpath, nplp->o.co.x, nplp->o.co.y);
2165
                    if (code < 0)
2166
                        return code;
2167
                    code = gx_path_add_line(rpath, plp->e.ce.x, plp->e.ce.y);
2168
                    if (code < 0)
2169
                        return code;
2170
                    code = gx_path_add_line(rpath, nplp->o.p.x, nplp->o.p.y);
2171
                    if (code < 0)
2172
                        return code;
2173
                }
2174
            }
2175
#endif
2176
0
            code = gx_path_add_line(rpath, nplp->o.co.x, nplp->o.co.y);
2177
0
        }
2178
0
    } else if (joinsense & joinsense) {
2179
        /* CW rotation. Join in the reverse path. "Underjoin" in the
2180
         * forward path. */
2181
0
        if (joinsense & joinsense_over) {
2182
            /* RJW: Ideally we should include the "|| flags" clause in
2183
             * the following condition. This forces all joins between
2184
             * line segments generated from arcs to be round. This would
2185
             * solve some flatness issues, but makes some pathological
2186
             * cases incredibly slow. */
2187
0
            if (join == gs_join_round /* || (flags & nf_all_from_arc) */) {
2188
0
                code = add_pie_join_fast_cw(rpath, plp, nplp, reflected);
2189
0
            } else { /* non-round join */
2190
0
                code = line_join_points_fast_cw(pgs_lp, plp, nplp,
2191
0
                                                rpoints, pmat, join);
2192
0
                nrpoints = code;
2193
0
            }
2194
0
            if (code < 0)
2195
0
                return code;
2196
0
        }
2197
0
        if (joinsense & joinsense_under) {
2198
            /* The underjoin */
2199
0
#ifndef SLOWER_BUT_MORE_ACCURATE_STROKING
2200
0
            if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) == 0 &&
2201
0
                join != gs_join_miter) {
2202
                /* RJW: This is an approximation. We ought to draw a line
2203
                 * back to nplp->o.p, and then independently fill any exposed
2204
                 * region under the curve with a round join. Sadly, that's
2205
                 * a) really hard to do, and b) makes certain pathological
2206
                 * filling cases MUCH slower due to the greater number of
2207
                 * "cross-segment" line segments this produces. Instead,
2208
                 * we just skip the line to the middle, and join across the
2209
                 * bottom instead. This is akin to what other graphics libs
2210
                 * do (such as fitz, libart, etc). It's not perfect but in
2211
                 * most cases it's close, and results in faster to fill
2212
                 * paths.
2213
                 */
2214
                /* RJW: This goes wrong for some paths, as the 'underjoin' wind
2215
                 * will be the wrong way. See bug 694971 */
2216
0
                code = gx_path_add_line(ppath, nplp->o.p.x, nplp->o.p.y);
2217
0
                if (code < 0)
2218
0
                    return code;
2219
0
            }
2220
#else
2221
            if (wide_underjoin(plp, nplp))
2222
            {
2223
                code = gx_path_add_line(ppath, nplp->o.p.x, nplp->o.p.y);
2224
                if (code < 0)
2225
                    return code;
2226
                if ((flags & (nf_some_from_arc | nf_prev_some_from_arc)) != 0) {
2227
                    code = gx_path_add_line(ppath, nplp->o.ce.x, nplp->o.ce.y);
2228
                    if (code < 0)
2229
                        return code;
2230
                    code = gx_path_add_line(ppath, plp->e.co.x, plp->e.co.y);
2231
                    if (code < 0)
2232
                        return code;
2233
                    code = gx_path_add_line(ppath, nplp->o.p.x, nplp->o.p.y);
2234
                    if (code < 0)
2235
                        return code;
2236
                }
2237
            }
2238
#endif
2239
0
            code = gx_path_add_line(ppath, nplp->o.ce.x, nplp->o.ce.y);
2240
0
        }
2241
0
    }
2242
0
    if (code < 0)
2243
0
        return code;
2244
0
    if (npoints > 0) {
2245
0
        code = add_points(ppath, points, npoints, false);
2246
0
        if (code < 0)
2247
0
            return code;
2248
0
    }
2249
0
    if (nrpoints > 0) {
2250
0
        code = add_points(rpath, rpoints, nrpoints, false);
2251
0
        if (code < 0)
2252
0
            return code;
2253
0
    }
2254
0
    if (ensure_closed)
2255
0
        return gx_join_path_and_reverse(ppath, rpath);
2256
0
    return 0;
2257
0
}
2258
2259
/* Add a CPSI-compatible segment to the path.  This handles all the complex
2260
 * cases.
2261
 *
2262
 * This method doesn't support start/end/dash caps, but it's only used from
2263
 * postscript, so it doesn't need to.
2264
 */
2265
static int
2266
stroke_add_compat(gx_path * ppath, gx_path *rpath, bool ensure_closed,
2267
                  int first, pl_ptr plp, pl_ptr nplp,
2268
                  const gx_device_color * pdevc, gx_device * dev,
2269
                  const gs_gstate * pgs,
2270
                  const gx_stroke_params * params,
2271
                  const gs_fixed_rect * ignore_pbbox, int uniform,
2272
                  gs_line_join join, bool reflected, note_flags flags)
2273
483
{
2274
    /* Actually it adds 2 contours : one for the segment itself,
2275
       and another one for line join or for the ending cap.
2276
       Note CPSI creates negative contours. */
2277
483
    const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs);
2278
483
    gs_fixed_point points[5];
2279
483
    int npoints;
2280
483
    bool const moveto_first = true; /* Keeping this code closer to "stroke_add". */
2281
483
    int code;
2282
2283
483
    if (plp->thin) {
2284
        /* We didn't set up the endpoint parameters before, */
2285
        /* because the line was thin.  Do it now. */
2286
0
        set_thin_widths(plp);
2287
0
        adjust_stroke(dev, plp, pgs, true, first == 0 && nplp == 0, flags);
2288
0
        compute_caps(plp);
2289
0
    }
2290
    /* The segment itself : */
2291
483
    ASSIGN_POINT(&points[0], plp->o.ce);
2292
483
    ASSIGN_POINT(&points[1], plp->e.co);
2293
483
    ASSIGN_POINT(&points[2], plp->e.ce);
2294
483
    ASSIGN_POINT(&points[3], plp->o.co);
2295
483
    code = add_points(ppath, points, 4, moveto_first);
2296
483
    if (code < 0)
2297
0
        return code;
2298
483
    code = gx_path_close_subpath(ppath);
2299
483
    if (code < 0)
2300
0
        return code;
2301
483
    npoints = 0;
2302
483
    if (nplp == 0) {
2303
        /* Add a final cap. */
2304
399
        if (pgs_lp->start_cap == gs_cap_butt)
2305
0
            return 0;
2306
399
        if (pgs_lp->start_cap == gs_cap_round) {
2307
399
            ASSIGN_POINT(&points[npoints], plp->e.co);
2308
399
            ++npoints;
2309
399
            if ((code = add_points(ppath, points, npoints, moveto_first)) < 0)
2310
0
                return code;
2311
399
            return add_round_cap(ppath, &plp->e);
2312
399
        }
2313
0
        ASSIGN_POINT(&points[0], plp->e.ce);
2314
0
        ++npoints;
2315
0
        ASSIGN_POINT(&points[npoints], plp->e.co);
2316
0
        ++npoints;
2317
0
        code = cap_points(pgs_lp->start_cap, &plp->e, points + npoints);
2318
0
        if (code < 0)
2319
0
            return code;
2320
0
        npoints += code;
2321
84
    } else if (join == gs_join_round) {
2322
0
        ASSIGN_POINT(&points[npoints], plp->e.co);
2323
0
        ++npoints;
2324
0
        if ((code = add_points(ppath, points, npoints, moveto_first)) < 0)
2325
0
            return code;
2326
0
        return add_round_cap(ppath, &plp->e);
2327
84
    } else if (nplp->thin) {    /* no join */
2328
0
        npoints = 0;
2329
84
    } else {                    /* non-round join */
2330
84
        bool ccw =
2331
84
            (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */ >
2332
84
            (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */;
2333
2334
84
        if (ccw ^ reflected) {
2335
84
            ASSIGN_POINT(&points[0], plp->e.co);
2336
84
            ++npoints;
2337
84
            code = line_join_points(pgs_lp, plp, nplp, points + npoints,
2338
84
                                    (uniform ? (gs_matrix *) 0 : &ctm_only(pgs)),
2339
84
                                    join, reflected);
2340
84
            if (code < 0)
2341
0
                return code;
2342
84
            code--; /* Drop the last point of the non-compatible mode. */
2343
84
            npoints += code;
2344
84
        } else {
2345
0
            code = line_join_points(pgs_lp, plp, nplp, points,
2346
0
                                    (uniform ? (gs_matrix *) 0 : &ctm_only(pgs)),
2347
0
                                    join, reflected);
2348
0
            if (code < 0)
2349
0
                return code;
2350
0
            ASSIGN_POINT(&points[0], plp->e.ce); /* Replace the starting point of the non-compatible mode. */
2351
0
            npoints = code;
2352
0
        }
2353
84
    }
2354
84
    code = add_points(ppath, points, npoints, moveto_first);
2355
84
    if (code < 0)
2356
0
        return code;
2357
84
    code = gx_path_close_subpath(ppath);
2358
84
    return code;
2359
84
}
2360
2361
/* Add a CPSI-compatible segment to the path.  This handles all the complex
2362
 * cases.
2363
 *
2364
 * This method doesn't support start/end/dash caps, but it's only used from
2365
 * postscript, so it doesn't need to.
2366
 */
2367
static int
2368
stroke_add_initial_cap_compat(gx_path * ppath, pl_ptr plp, bool adlust_longitude,
2369
           const gx_device_color * pdevc, gx_device * dev,
2370
           const gs_gstate * pgs)
2371
399
{
2372
399
    const gx_line_params *pgs_lp = gs_currentlineparams_inline(pgs);
2373
399
    gs_fixed_point points[5];
2374
399
    int npoints = 0;
2375
399
    int code;
2376
2377
399
    if (pgs_lp->start_cap == gs_cap_butt)
2378
0
        return 0;
2379
399
    if (plp->thin) {
2380
        /* We didn't set up the endpoint parameters before, */
2381
        /* because the line was thin.  Do it now. */
2382
0
        set_thin_widths(plp);
2383
0
        adjust_stroke(dev, plp, pgs, true, adlust_longitude, 0);
2384
0
        compute_caps(plp);
2385
0
    }
2386
    /* Create an initial cap if desired. */
2387
399
    if (pgs_lp->start_cap == gs_cap_round) {
2388
399
        if ((code = gx_path_add_point(ppath, plp->o.co.x, plp->o.co.y)) < 0 ||
2389
399
            (code = add_round_cap(ppath, &plp->o)) < 0
2390
399
            )
2391
0
            return code;
2392
399
        return 0;
2393
399
    } else {
2394
0
        ASSIGN_POINT(&points[0], plp->o.co);
2395
0
        ++npoints;
2396
0
        if ((code = cap_points(pgs_lp->start_cap, &plp->o, points + npoints)) < 0)
2397
0
            return npoints;
2398
0
        npoints += code;
2399
0
        ASSIGN_POINT(&points[npoints], plp->o.ce);
2400
0
        ++npoints;
2401
0
        code = add_points(ppath, points, npoints, true);
2402
0
        if (code < 0)
2403
0
            return code;
2404
0
        return gx_path_close_subpath(ppath);
2405
0
    }
2406
399
}
2407
2408
/* Add lines with a possible initial moveto. */
2409
static int
2410
add_points(gx_path * ppath, const gs_fixed_point * points, int npoints,
2411
           bool moveto_first)
2412
18.9M
{
2413
18.9M
    int code;
2414
2415
18.9M
    if (moveto_first) {
2416
18.4M
        code = gx_path_add_point(ppath, points[0].x, points[0].y);
2417
18.4M
        if (code < 0)
2418
0
            return code;
2419
18.4M
        return gx_path_add_lines(ppath, points + 1, npoints - 1);
2420
18.4M
    } else {
2421
499k
        return gx_path_add_lines(ppath, points, npoints);
2422
499k
    }
2423
18.9M
}
2424
2425
/* ---------------- Join computation ---------------- */
2426
2427
/* Compute the points for a bevel, miter, or triangle join. */
2428
/* Treat no join the same as a bevel join. */
2429
/* If pmat != 0, we must inverse-transform the distances for */
2430
/* the miter check. */
2431
static int
2432
line_join_points(const gx_line_params * pgs_lp, pl_ptr plp, pl_ptr nplp,
2433
                 gs_fixed_point * join_points, const gs_matrix * pmat,
2434
                 gs_line_join join, bool reflected)
2435
3.35M
{
2436
3.35M
#define jp1 join_points[0]
2437
3.35M
#define np1 join_points[1]
2438
3.35M
#define np2 join_points[2]
2439
3.35M
#define jp2 join_points[3]
2440
3.35M
#define jpx join_points[4]
2441
    /*
2442
     * Set np to whichever of nplp->o.co or .ce is outside
2443
     * the current line.  We observe that the point (x2,y2)
2444
     * is counter-clockwise from (x1,y1), relative to the origin,
2445
     * iff
2446
     *  (arctan(y2/x2) - arctan(y1/x1)) mod 2*pi < pi,
2447
     * taking the signs of xi and yi into account to determine
2448
     * the quadrants of the results.  It turns out that
2449
     * even though arctan is monotonic only in the 4th/1st
2450
     * quadrants and the 2nd/3rd quadrants, case analysis on
2451
     * the signs of xi and yi demonstrates that this test
2452
     * is equivalent to the much less expensive test
2453
     *  x1 * y2 > x2 * y1
2454
     * in all cases.
2455
     *
2456
     * In the present instance, x1,y1 are plp->width,
2457
     * x2,y2 are nplp->width, and the origin is
2458
     * their common point (plp->e.p, nplp->o.p).
2459
     * ccw will be true iff nplp.o.co (nplp.o.p + width) is
2460
     * counter-clockwise from plp.e.ce (plp.e.p + width),
2461
     * in which case we want tan(a-b) rather than tan(b-a).
2462
     *
2463
     * We make the test using double arithmetic only because
2464
     * the !@#&^*% C language doesn't give us access to
2465
     * the double-width-result multiplication operation
2466
     * that almost all CPUs provide!
2467
     */
2468
3.35M
    bool ccw =
2469
3.35M
        (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */ >
2470
3.35M
        (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */;
2471
3.35M
    bool ccw0 = ccw;
2472
3.35M
    p_ptr outp, np;
2473
3.35M
    int   code;
2474
3.35M
    gs_fixed_point mpt;
2475
2476
3.35M
    ccw ^= reflected;
2477
2478
    /* Initialize for a bevel join. */
2479
3.35M
    ASSIGN_POINT(&jp1, plp->e.co);
2480
3.35M
    ASSIGN_POINT(&jp2, plp->e.ce);
2481
2482
    /*
2483
     * Because of stroke adjustment, it is possible that
2484
     * plp->e.p != nplp->o.p.  For that reason, we must use
2485
     * nplp->o.p as np1 or np2.
2486
     */
2487
3.35M
    if (!ccw) {
2488
1.73M
        outp = &jp2;
2489
1.73M
        ASSIGN_POINT(&np2, nplp->o.co);
2490
1.73M
        ASSIGN_POINT(&np1, nplp->o.p);
2491
1.73M
        np = &np2;
2492
1.73M
    } else {
2493
1.61M
        outp = &jp1;
2494
1.61M
        ASSIGN_POINT(&np1, nplp->o.ce);
2495
1.61M
        ASSIGN_POINT(&np2, nplp->o.p);
2496
1.61M
        np = &np1;
2497
1.61M
    }
2498
3.35M
    if_debug1('O', "[O]use %s\n", (ccw ? "co (ccw)" : "ce (cw)"));
2499
2500
    /* Handle triangular joins now. */
2501
3.35M
    if (join == gs_join_triangle) {
2502
8
        fixed tpx = outp->x - nplp->o.p.x + np->x;
2503
8
        fixed tpy = outp->y - nplp->o.p.y + np->y;
2504
2505
8
        ASSIGN_POINT(&jpx, jp2);
2506
8
        if (!ccw) {
2507
            /* Insert tp between np2 and jp2. */
2508
8
            jp2.x = tpx, jp2.y = tpy;
2509
8
        } else {
2510
            /* Insert tp between jp1 and np1. */
2511
0
            ASSIGN_POINT(&jp2, np2);
2512
0
            ASSIGN_POINT(&np2, np1);
2513
0
            np1.x = tpx, np1.y = tpy;
2514
0
        }
2515
8
        return 5;
2516
8
    }
2517
3.35M
    if (join == gs_join_miter &&
2518
3.35M
        (code = check_miter(pgs_lp, plp, nplp, pmat, outp, np, &mpt, ccw0)) <= 0) {
2519
1.97M
        if (code < 0)
2520
0
            return code;
2521
1.97M
        ASSIGN_POINT(outp, mpt);
2522
1.97M
    }
2523
3.35M
    return 4;
2524
3.35M
}
2525
2526
static int
2527
line_join_points_fast_cw(const gx_line_params * pgs_lp,
2528
                         pl_ptr plp, pl_ptr nplp,
2529
                         gs_fixed_point * rjoin_points,
2530
                         const gs_matrix * pmat,
2531
                         gs_line_join join)
2532
0
{
2533
    /* rjoin_points will be added to a path that is currently at plp->e.ce.
2534
     */
2535
2536
    /* Join will be between plp->e.ce and nplp->o.co */
2537
0
    if (join == gs_join_triangle)
2538
0
    {
2539
0
        gs_fixed_point tp;
2540
2541
0
        tp.x = plp->e.ce.x - nplp->o.p.x + nplp->o.co.x;
2542
0
        tp.y = plp->e.ce.y - nplp->o.p.y + nplp->o.co.y;
2543
0
        ASSIGN_POINT(&rjoin_points[0], tp);
2544
0
        ASSIGN_POINT(&rjoin_points[1], nplp->o.co);
2545
0
        return 2;
2546
0
    }
2547
2548
    /* Set up for a Bevel join */
2549
0
    ASSIGN_POINT(&rjoin_points[0], nplp->o.co);
2550
2551
0
    return 1;
2552
0
}
2553
2554
static int
2555
line_join_points_fast_ccw(const gx_line_params * pgs_lp,
2556
                          pl_ptr plp, pl_ptr nplp,
2557
                          gs_fixed_point * join_points,
2558
                          const gs_matrix * pmat,
2559
                          gs_line_join join)
2560
0
{
2561
    /* join_points will be added to a path that is currently at plp->e.co.
2562
     */
2563
    /* Join will be between plp->e.co and nplp->o.ce */
2564
0
    if (join == gs_join_triangle)
2565
0
    {
2566
0
        gs_fixed_point tp;
2567
2568
0
        tp.x = plp->e.co.x - nplp->o.p.x + nplp->o.ce.x;
2569
0
        tp.y = plp->e.co.y - nplp->o.p.y + nplp->o.ce.y;
2570
0
        ASSIGN_POINT(&join_points[0], tp);
2571
0
        ASSIGN_POINT(&join_points[1], nplp->o.ce);
2572
0
        return 2;
2573
0
    }
2574
2575
    /* Set up for a Bevel join */
2576
0
    ASSIGN_POINT(&join_points[0], nplp->o.ce);
2577
2578
0
    return 1;
2579
0
}
2580
/* ---------------- Cap computations ---------------- */
2581
2582
/* Compute the endpoints of the two caps of a segment. */
2583
/* Only o.p, e.p, width, and cdelta have been set. */
2584
static void
2585
compute_caps(pl_ptr plp)
2586
18.9M
{
2587
18.9M
    fixed wx2 = plp->width.x;
2588
18.9M
    fixed wy2 = plp->width.y;
2589
2590
18.9M
    plp->o.co.x = plp->o.p.x + wx2, plp->o.co.y = plp->o.p.y + wy2;
2591
18.9M
    plp->o.cdelta.x = -plp->e.cdelta.x,
2592
18.9M
        plp->o.cdelta.y = -plp->e.cdelta.y;
2593
18.9M
    plp->o.ce.x = plp->o.p.x - wx2, plp->o.ce.y = plp->o.p.y - wy2;
2594
18.9M
    plp->e.co.x = plp->e.p.x - wx2, plp->e.co.y = plp->e.p.y - wy2;
2595
18.9M
    plp->e.ce.x = plp->e.p.x + wx2, plp->e.ce.y = plp->e.p.y + wy2;
2596
#ifdef DEBUG
2597
    if (gs_debug_c('O')) {
2598
        dlprintf4("[o]Stroke o=(%f,%f) e=(%f,%f)\n",
2599
                  fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
2600
                  fixed2float(plp->e.p.x), fixed2float(plp->e.p.y));
2601
        dlprintf4("\twxy=(%f,%f) lxy=(%f,%f)\n",
2602
                  fixed2float(wx2), fixed2float(wy2),
2603
                  fixed2float(plp->e.cdelta.x),
2604
                  fixed2float(plp->e.cdelta.y));
2605
    }
2606
#endif
2607
18.9M
}
2608
2609
#define px endp->p.x
2610
#define py endp->p.y
2611
#define xo endp->co.x
2612
#define yo endp->co.y
2613
#define xe endp->ce.x
2614
#define ye endp->ce.y
2615
#define cdx endp->cdelta.x
2616
#define cdy endp->cdelta.y
2617
2618
/* Add a round cap to a path. */
2619
/* Assume the current point is the cap origin (endp->co). */
2620
static int
2621
add_round_cap(gx_path * ppath, const_ep_ptr endp)
2622
798
{
2623
798
    int code;
2624
2625
    /*
2626
     * Per the Red Book, we draw a full circle, even though a semicircle
2627
     * is sufficient for the join.
2628
     */
2629
798
    if ((code = gx_path_add_partial_arc(ppath, px + cdx, py + cdy,
2630
798
                                        xo + cdx, yo + cdy,
2631
798
                                        quarter_arc_fraction)) < 0 ||
2632
798
        (code = gx_path_add_partial_arc(ppath, xe, ye, xe + cdx, ye + cdy,
2633
798
                                        quarter_arc_fraction)) < 0 ||
2634
798
        (code = gx_path_add_partial_arc(ppath, px - cdx, py - cdy,
2635
798
                                        xe - cdx, ye - cdy,
2636
798
                                        quarter_arc_fraction)) < 0 ||
2637
798
        (code = gx_path_add_partial_arc(ppath, xo, yo, xo - cdx, yo - cdy,
2638
798
                                        quarter_arc_fraction)) < 0 ||
2639
        /* The final point must be (xe,ye). */
2640
798
        (code = gx_path_add_line(ppath, xe, ye)) < 0
2641
798
        )
2642
0
        return code;
2643
798
    return 0;
2644
798
}
2645
2646
/* Add a semicircular cap to a path. */
2647
/* Assume the current point is the cap origin (endp->co). */
2648
static int
2649
add_pie_cap(gx_path * ppath, const_ep_ptr endp)
2650
1.01M
{
2651
1.01M
    int code;
2652
2653
1.01M
    if ((code = gx_path_add_partial_arc(ppath, px + cdx, py + cdy,
2654
1.01M
                                        xo + cdx, yo + cdy,
2655
1.01M
                                        quarter_arc_fraction)) < 0 ||
2656
1.01M
        (code = gx_path_add_partial_arc(ppath, xe, ye, xe + cdx, ye + cdy,
2657
1.01M
                                        quarter_arc_fraction)) < 0 ||
2658
1.01M
        (code = gx_path_add_line(ppath, xe, ye)) < 0)
2659
0
        return code;
2660
1.01M
    return 0;
2661
1.01M
}
2662
2663
static int
2664
do_pie_join(gx_path * ppath, gs_fixed_point *centre,
2665
            gs_fixed_point *current_orig, gs_fixed_point *current_tangent,
2666
            gs_fixed_point *final, gs_fixed_point *final_tangent, bool ccw,
2667
            gs_fixed_point *width)
2668
9.21M
{
2669
9.21M
    int code;
2670
9.21M
    double rad_squared, dist_squared, F;
2671
9.21M
    gs_fixed_point current, tangent, tangmeet;
2672
2673
9.21M
    tangent.x = current_tangent->x;
2674
9.21M
    tangent.y = current_tangent->y;
2675
9.21M
    current.x = current_orig->x;
2676
9.21M
    current.y = current_orig->y;
2677
2678
    /* Is the join more than 90 degrees? */
2679
9.21M
    if ((double)tangent.x * (double)final_tangent->x +
2680
9.21M
        (double)tangent.y * (double)final_tangent->y > 0) {
2681
        /* Yes, so do a quarter turn. */
2682
177k
        code = gx_path_add_partial_arc(ppath,
2683
177k
                                       centre->x + tangent.x,
2684
177k
                                       centre->y + tangent.y,
2685
                                       /* Point where tangents meet */
2686
177k
                                       current.x + tangent.x,
2687
177k
                                       current.y + tangent.y,
2688
177k
                                       quarter_arc_fraction);
2689
177k
        if (code < 0)
2690
0
            return code;
2691
177k
        current.x = centre->x + tangent.x;
2692
177k
        current.y = centre->y + tangent.y;
2693
177k
        if (ccw) {
2694
9.80k
            int tmp = tangent.x;
2695
9.80k
            tangent.x = -tangent.y;
2696
9.80k
            tangent.y = tmp;
2697
167k
        } else {
2698
167k
            int tmp = tangent.x;
2699
167k
            tangent.x = tangent.y;
2700
167k
            tangent.y = -tmp;
2701
167k
        }
2702
177k
    }
2703
2704
    /* Now we are guaranteed that the remaining arc is 90 degrees or
2705
     * less. Find where the tangents meet for this final section. */
2706
9.21M
    if (line_intersect(&current, &tangent,
2707
9.21M
                       final, final_tangent, &tangmeet) != 0) {
2708
3.48M
        return gx_path_add_line(ppath, final->x, final->y);
2709
3.48M
    }
2710
5.73M
    current.x -= tangmeet.x;
2711
5.73M
    current.y -= tangmeet.y;
2712
5.73M
    dist_squared = ((double)current.x) * current.x +
2713
5.73M
                   ((double)current.y) * current.y;
2714
5.73M
    rad_squared  = ((double)width->x) * width->x +
2715
5.73M
                   ((double)width->y) * width->y;
2716
5.73M
    dist_squared /= rad_squared;
2717
5.73M
    F = (4.0/3.0)*(1/(1+sqrt(1+dist_squared)));
2718
5.73M
    return gx_path_add_partial_arc(ppath, final->x, final->y,
2719
9.21M
                                   tangmeet.x, tangmeet.y, F);
2720
9.21M
}
2721
2722
/* Add a pie shaped join to a path. */
2723
/* Assume the current point is the cap origin (endp->co). */
2724
static int
2725
add_pie_join(gx_path * ppath, pl_ptr plp, pl_ptr nplp, bool reflected,
2726
             bool cap)
2727
13.8M
{
2728
13.8M
    int code;
2729
13.8M
    gs_fixed_point *current, *final, *tangent, *final_tangent;
2730
13.8M
    double l, r;
2731
13.8M
    bool ccw;
2732
2733
13.8M
    l = (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */;
2734
13.8M
    r = (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */;
2735
2736
13.8M
    if (l == r) {
2737
        /* Colinear. Suppress drawing a cap unless the path reverses direction. */
2738
5.06M
        if (cap &&
2739
5.06M
            ((double)(plp->width.x) * (nplp->width.x) + (double)(nplp->width.y) * (plp->width.y)) < 0)
2740
13.5k
            return add_pie_cap(ppath, &plp->e);
2741
5.04M
        else
2742
5.04M
            return gx_path_add_line(ppath, plp->e.ce.x, plp->e.ce.y);
2743
5.06M
    }
2744
2745
8.80M
    ccw = (l > r);
2746
2747
8.80M
    ccw ^= reflected;
2748
2749
    /* At this point, the current point is plp->e.co */
2750
8.80M
    if (ccw) {
2751
4.54M
        current       = & plp->e.co;
2752
4.54M
        final         = &nplp->o.ce;
2753
4.54M
        tangent       = & plp->e.cdelta;
2754
4.54M
        final_tangent = &nplp->o.cdelta;
2755
        /* Check for no join required */
2756
4.54M
        if (current->x == final->x && current->y == final->y) {
2757
0
            return gx_path_add_line(ppath, plp->e.ce.x, plp->e.ce.y);
2758
0
        }
2759
4.54M
    } else {
2760
4.25M
        current       = &nplp->o.co;
2761
4.25M
        final         = & plp->e.ce;
2762
4.25M
        tangent       = &nplp->o.cdelta;
2763
4.25M
        final_tangent = & plp->e.cdelta;
2764
4.25M
        code = gx_path_add_line(ppath, plp->e.p.x, plp->e.p.y);
2765
4.25M
        if (code < 0)
2766
0
            return code;
2767
4.25M
        code = gx_path_add_line(ppath, current->x, current->y);
2768
4.25M
        if (code < 0)
2769
0
            return code;
2770
4.25M
        if (current->x == final->x && current->y == final->y)
2771
0
            return 0;
2772
4.25M
    }
2773
2774
8.80M
    if ((code = do_pie_join(ppath, &plp->e.p, current, tangent,
2775
8.80M
                            final, final_tangent, !reflected, &plp->width)) < 0)
2776
0
        return code;
2777
8.80M
    if (ccw &&
2778
8.80M
        ((code = gx_path_add_line(ppath, plp->e.p.x, plp->e.p.y)) < 0 ||
2779
4.54M
         (code = gx_path_add_line(ppath, plp->e.ce.x, plp->e.ce.y)) < 0))
2780
0
        return code;
2781
2782
8.80M
    return 0;
2783
8.80M
}
2784
2785
/* Add a pie shaped join to a path. */
2786
static int
2787
add_pie_join_fast_cw(gx_path * rpath, pl_ptr plp, pl_ptr nplp, bool reflected)
2788
0
{
2789
    /* At this point, the current point is plp->e.ce */
2790
0
    if (plp->e.ce.x == nplp->o.co.x && plp->e.ce.y == nplp->o.co.y)
2791
0
        return 0;
2792
2793
0
    return do_pie_join(rpath, &plp->e.p, &plp->e.ce, &plp->e.cdelta,
2794
0
                       &nplp->o.co, &nplp->o.cdelta, reflected, &plp->width);
2795
0
}
2796
2797
static int
2798
add_pie_join_fast_ccw(gx_path * ppath, pl_ptr plp, pl_ptr nplp, bool reflected)
2799
0
{
2800
    /* At this point, the current point is plp->e.co */
2801
    /* Check for no join required */
2802
0
    if (plp->e.co.x == nplp->o.ce.x && plp->e.co.y == nplp->o.ce.y)
2803
0
        return 0;
2804
2805
0
    return do_pie_join(ppath, &plp->e.p, &plp->e.co, &plp->e.cdelta,
2806
0
                       &nplp->o.ce, &nplp->o.cdelta, !reflected, &plp->width);
2807
0
}
2808
2809
static int
2810
join_under_pie(gx_path * ppath, pl_ptr plp, pl_ptr nplp, bool reflected)
2811
14.5M
{
2812
14.5M
    int code;
2813
14.5M
    gs_fixed_point dirn1, dirn2, tangmeet;
2814
14.5M
    double l, r;
2815
14.5M
    bool ccw;
2816
2817
14.5M
    l = (double)(plp->width.x) /* x1 */ * (nplp->width.y) /* y2 */;
2818
14.5M
    r = (double)(nplp->width.x) /* x2 */ * (plp->width.y) /* y1 */;
2819
2820
14.5M
    if (l == r)
2821
5.21M
        return 0;
2822
2823
9.29M
    ccw = (l > r);
2824
2825
9.29M
    ccw ^= reflected;
2826
2827
9.29M
    if (ccw) {
2828
4.79M
        dirn1.x = - plp->width.x;
2829
4.79M
        dirn1.y = - plp->width.y;
2830
4.79M
        dirn2.x = -nplp->width.x;
2831
4.79M
        dirn2.y = -nplp->width.y;
2832
4.79M
        if (line_intersect(& plp->o.co, &dirn1,
2833
4.79M
                           &nplp->e.ce, &dirn2, &tangmeet) != 0)
2834
4.57M
            return 0;
2835
221k
        if ((code = gx_path_close_subpath(ppath)) < 0 ||
2836
221k
            (code = gx_path_add_point(ppath, tangmeet.x, tangmeet.y)) < 0  ||
2837
221k
            (code = gx_path_add_line(ppath,plp->o.co.x,plp->o.co.y)) < 0 ||
2838
221k
            (code = do_pie_join(ppath, &plp->e.p, &plp->o.co, &plp->o.cdelta,
2839
221k
                                &nplp->e.ce, &nplp->e.cdelta, !reflected,
2840
221k
                                &plp->width)))
2841
0
            return code;
2842
4.49M
    } else {
2843
4.49M
        if (line_intersect(& plp->o.ce, & plp->width,
2844
4.49M
                           &nplp->e.co, &nplp->width, &tangmeet) != 0)
2845
4.30M
            return 0;
2846
195k
        if ((code = gx_path_close_subpath(ppath)) < 0 ||
2847
195k
            (code = gx_path_add_point(ppath, tangmeet.x, tangmeet.y)) < 0  ||
2848
195k
            (code = gx_path_add_line(ppath,nplp->e.co.x,nplp->e.co.y)) < 0 ||
2849
195k
            (code = do_pie_join(ppath, &plp->e.p,&nplp->e.co,&nplp->e.cdelta,
2850
195k
                                &plp->o.ce, &plp->o.cdelta, !reflected,
2851
195k
                                &plp->width)))
2852
0
            return code;
2853
195k
    }
2854
416k
    return 0;
2855
9.29M
}
2856
2857
/* Compute the points for a non-round cap. */
2858
/* Return the number of points. */
2859
static int
2860
cap_points(gs_line_cap type, const_ep_ptr endp, gs_fixed_point *pts /*[3]*/)
2861
19.7M
{
2862
19.7M
#define PUT_POINT(i, px, py)\
2863
39.4M
  pts[i].x = (px), pts[i].y = (py)
2864
19.7M
    switch (type) {
2865
19.6M
        case gs_cap_butt:
2866
19.6M
            PUT_POINT(0, xo, yo);
2867
19.6M
            PUT_POINT(1, xe, ye);
2868
19.6M
            return 2;
2869
61.3k
        case gs_cap_square:
2870
61.3k
            PUT_POINT(0, xo + cdx, yo + cdy);
2871
61.3k
            PUT_POINT(1, xe + cdx, ye + cdy);
2872
61.3k
            return 2;
2873
48
        case gs_cap_triangle:   /* (not supported by PostScript) */
2874
48
            PUT_POINT(0, xo, yo);
2875
48
            PUT_POINT(1, px + cdx, py + cdy);
2876
48
            PUT_POINT(2, xe, ye);
2877
48
            return 3;
2878
0
        default:                /* can't happen */
2879
0
            return_error(gs_error_unregistered);
2880
19.7M
    }
2881
19.7M
#undef PUT_POINT
2882
19.7M
}