Coverage Report

Created: 2026-07-24 07:44

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