Coverage Report

Created: 2026-06-08 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/draw-path.c
Line
Count
Source
1
// Copyright (C) 2004-2026 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
#include "draw-imp.h"
25
26
#include <math.h>
27
#include <float.h>
28
#include <assert.h>
29
30
5.25M
#define MAX_DEPTH 8
31
32
/*
33
  When stroking/filling, we now label the edges as we emit them.
34
35
  For filling, we walk the outline of the shape in order, so everything
36
  is labelled as '0'.
37
38
  For stroking, we walk up both sides of the stroke at once; the forward
39
  side (0), and the reverse side (1). When we get to the top, either
40
  both sides join back to where they started, or we cap them.
41
42
  The start cap is labelled 2, the end cap is labelled 0.
43
44
  These labels are ignored for edge based rasterization, but are required
45
  for edgebuffer based rasterization.
46
47
  Consider the following simplified ascii art diagram of a stroke from
48
  left to right with 3 sections.
49
50
  |            0           0           0
51
  |      +----->-----+----->-----+----->-----+
52
  |      |                                   |
53
  |      ^ 2   A           B           C     v 0
54
  |      |                                   |
55
  |      +-----<-----+-----<-----+-----<-----+
56
  |            1           1           1
57
58
  Edge 0 is sent in order (the top edge of A then B then C, left to right
59
  in the above diagram). Edge 1 is sent in reverse order (the bottom edge
60
  of A then B then C, still left to right in the above diagram, even though
61
  the sense of the line is right to left).
62
63
  Finally any caps required are sent, 0 and 2.
64
65
  It would be nicer if we could roll edge 2 into edge 1, but to do that
66
  we'd need to know in advance if a stroke was closed or not, so we have
67
  special case code in the edgebuffer based rasterizer to cope with this.
68
*/
69
70
static void
71
line(fz_context *ctx, fz_rasterizer *rast, fz_matrix ctm, float x0, float y0, float x1, float y1)
72
2.99M
{
73
2.99M
  float tx0 = ctm.a * x0 + ctm.c * y0 + ctm.e;
74
2.99M
  float ty0 = ctm.b * x0 + ctm.d * y0 + ctm.f;
75
2.99M
  float tx1 = ctm.a * x1 + ctm.c * y1 + ctm.e;
76
2.99M
  float ty1 = ctm.b * x1 + ctm.d * y1 + ctm.f;
77
2.99M
  fz_insert_rasterizer(ctx, rast, tx0, ty0, tx1, ty1, 0);
78
2.99M
}
79
80
static void
81
bezier(fz_context *ctx, fz_rasterizer *rast, fz_matrix ctm, float flatness,
82
  float xa, float ya,
83
  float xb, float yb,
84
  float xc, float yc,
85
  float xd, float yd, int depth)
86
5.29M
{
87
5.29M
  float dmax;
88
5.29M
  float xab, yab;
89
5.29M
  float xbc, ybc;
90
5.29M
  float xcd, ycd;
91
5.29M
  float xabc, yabc;
92
5.29M
  float xbcd, ybcd;
93
5.29M
  float xabcd, yabcd;
94
95
  /* termination check */
96
5.29M
  dmax = fz_abs(xa - xb);
97
5.29M
  dmax = fz_max(dmax, fz_abs(ya - yb));
98
5.29M
  dmax = fz_max(dmax, fz_abs(xd - xc));
99
5.29M
  dmax = fz_max(dmax, fz_abs(yd - yc));
100
5.29M
  if (dmax < flatness || depth >= MAX_DEPTH)
101
2.87M
  {
102
2.87M
    line(ctx, rast, ctm, xa, ya, xd, yd);
103
2.87M
    return;
104
2.87M
  }
105
106
2.42M
  xab = xa + xb;
107
2.42M
  yab = ya + yb;
108
2.42M
  xbc = xb + xc;
109
2.42M
  ybc = yb + yc;
110
2.42M
  xcd = xc + xd;
111
2.42M
  ycd = yc + yd;
112
113
2.42M
  xabc = xab + xbc;
114
2.42M
  yabc = yab + ybc;
115
2.42M
  xbcd = xbc + xcd;
116
2.42M
  ybcd = ybc + ycd;
117
118
2.42M
  xabcd = xabc + xbcd;
119
2.42M
  yabcd = yabc + ybcd;
120
121
2.42M
  xab *= 0.5f; yab *= 0.5f;
122
  /* xbc *= 0.5f; ybc *= 0.5f; */
123
2.42M
  xcd *= 0.5f; ycd *= 0.5f;
124
125
2.42M
  xabc *= 0.25f; yabc *= 0.25f;
126
2.42M
  xbcd *= 0.25f; ybcd *= 0.25f;
127
128
2.42M
  xabcd *= 0.125f; yabcd *= 0.125f;
129
130
2.42M
  bezier(ctx, rast, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
131
2.42M
  bezier(ctx, rast, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
132
2.42M
}
133
134
/* AIX uses 'quad' as a definition, so we can't use that here. */
135
static void
136
quadratic(fz_context *ctx, fz_rasterizer *rast, fz_matrix ctm, float flatness,
137
  float xa, float ya,
138
  float xb, float yb,
139
  float xc, float yc, int depth)
140
0
{
141
0
  float dmax;
142
0
  float xab, yab;
143
0
  float xbc, ybc;
144
0
  float xabc, yabc;
145
146
  /* termination check */
147
0
  dmax = fz_abs(xa - xb);
148
0
  dmax = fz_max(dmax, fz_abs(ya - yb));
149
0
  dmax = fz_max(dmax, fz_abs(xc - xb));
150
0
  dmax = fz_max(dmax, fz_abs(yc - yb));
151
0
  if (dmax < flatness || depth >= MAX_DEPTH)
152
0
  {
153
0
    line(ctx, rast, ctm, xa, ya, xc, yc);
154
0
    return;
155
0
  }
156
157
0
  xab = xa + xb;
158
0
  yab = ya + yb;
159
0
  xbc = xb + xc;
160
0
  ybc = yb + yc;
161
162
0
  xabc = xab + xbc;
163
0
  yabc = yab + ybc;
164
165
0
  xab *= 0.5f; yab *= 0.5f;
166
0
  xbc *= 0.5f; ybc *= 0.5f;
167
168
0
  xabc *= 0.25f; yabc *= 0.25f;
169
170
0
  quadratic(ctx, rast, ctm, flatness, xa, ya, xab, yab, xabc, yabc, depth + 1);
171
0
  quadratic(ctx, rast, ctm, flatness, xabc, yabc, xbc, ybc, xc, yc, depth + 1);
172
0
}
173
174
typedef struct
175
{
176
  fz_rasterizer *rast;
177
  fz_matrix ctm;
178
  float flatness;
179
  fz_point b;
180
  fz_point c;
181
}
182
flatten_arg;
183
184
static void
185
flatten_moveto(fz_context *ctx, void *arg_, float x, float y)
186
125k
{
187
125k
  flatten_arg *arg = (flatten_arg *)arg_;
188
189
  /* implicit closepath before moveto */
190
125k
  if (arg->c.x != arg->b.x || arg->c.y != arg->b.y)
191
484
    line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y);
192
125k
  arg->c.x = arg->b.x = x;
193
125k
  arg->c.y = arg->b.y = y;
194
195
125k
  fz_gap_rasterizer(ctx, arg->rast);
196
125k
}
197
198
static void
199
flatten_lineto(fz_context *ctx, void *arg_, float x, float y)
200
96.6k
{
201
96.6k
  flatten_arg *arg = (flatten_arg *)arg_;
202
203
96.6k
  line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, x, y);
204
96.6k
  arg->c.x = x;
205
96.6k
  arg->c.y = y;
206
96.6k
}
207
208
static void
209
flatten_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3)
210
453k
{
211
453k
  flatten_arg *arg = (flatten_arg *)arg_;
212
213
453k
  bezier(ctx, arg->rast, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, x3, y3, 0);
214
453k
  arg->c.x = x3;
215
453k
  arg->c.y = y3;
216
453k
}
217
218
static void
219
flatten_quadto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2)
220
0
{
221
0
  flatten_arg *arg = (flatten_arg *)arg_;
222
223
0
  quadratic(ctx, arg->rast, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, 0);
224
0
  arg->c.x = x2;
225
0
  arg->c.y = y2;
226
0
}
227
228
static void
229
flatten_close(fz_context *ctx, void *arg_)
230
26.1k
{
231
26.1k
  flatten_arg *arg = (flatten_arg *)arg_;
232
233
26.1k
  line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y);
234
26.1k
  arg->c.x = arg->b.x;
235
26.1k
  arg->c.y = arg->b.y;
236
26.1k
}
237
238
static void
239
flatten_rectto(fz_context *ctx, void *arg_, float x0, float y0, float x1, float y1)
240
36.3k
{
241
36.3k
  flatten_arg *arg = (flatten_arg *)arg_;
242
36.3k
  fz_matrix ctm = arg->ctm;
243
244
36.3k
  flatten_moveto(ctx, arg_, x0, y0);
245
246
36.3k
  if (fz_antidropout_rasterizer(ctx, arg->rast))
247
36.3k
  {
248
    /* In the case where we have an axis aligned rectangle, do some
249
     * horrid antidropout stuff. */
250
36.3k
    if (ctm.b == 0 && ctm.c == 0)
251
32.2k
    {
252
32.2k
      float tx0 = ctm.a * x0 + ctm.e;
253
32.2k
      float ty0 = ctm.d * y0 + ctm.f;
254
32.2k
      float tx1 = ctm.a * x1 + ctm.e;
255
32.2k
      float ty1 = ctm.d * y1 + ctm.f;
256
32.2k
      fz_insert_rasterizer_rect(ctx, arg->rast, tx0, ty0, tx1, ty1);
257
32.2k
      return;
258
32.2k
    }
259
4.02k
    else if (ctm.a == 0 && ctm.d == 0)
260
0
    {
261
0
      float tx0 = ctm.c * y0 + ctm.e;
262
0
      float ty0 = ctm.b * x0 + ctm.f;
263
0
      float tx1 = ctm.c * y1 + ctm.e;
264
0
      float ty1 = ctm.b * x1 + ctm.f;
265
0
      fz_insert_rasterizer_rect(ctx, arg->rast, tx0, ty1, tx1, ty0);
266
0
      return;
267
0
    }
268
36.3k
  }
269
270
4.02k
  flatten_lineto(ctx, arg_, x1, y0);
271
4.02k
  flatten_lineto(ctx, arg_, x1, y1);
272
4.02k
  flatten_lineto(ctx, arg_, x0, y1);
273
4.02k
  flatten_close(ctx, arg_);
274
4.02k
}
275
276
static const fz_path_walker flatten_proc =
277
{
278
  flatten_moveto,
279
  flatten_lineto,
280
  flatten_curveto,
281
  flatten_close,
282
  flatten_quadto,
283
  NULL,
284
  NULL,
285
  flatten_rectto
286
};
287
288
static int
289
do_flatten_fill(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, fz_matrix ctm, float flatness)
290
95.8k
{
291
95.8k
  flatten_arg arg;
292
293
95.8k
  arg.rast = rast;
294
95.8k
  arg.ctm = ctm;
295
95.8k
  arg.flatness = flatness;
296
95.8k
  arg.b.x = arg.b.y = arg.c.x = arg.c.y = 0;
297
298
95.8k
  fz_walk_path(ctx, path, &flatten_proc, &arg);
299
95.8k
  if (arg.c.x != arg.b.x || arg.c.y != arg.b.y)
300
573
    line(ctx, rast, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y);
301
302
95.8k
  fz_gap_rasterizer(ctx, rast);
303
304
95.8k
  return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
305
95.8k
}
306
307
int
308
fz_flatten_fill_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, fz_matrix ctm, float flatness, fz_irect scissor, fz_irect *bbox)
309
95.8k
{
310
95.8k
  int empty;
311
95.8k
  fz_irect local_bbox;
312
95.8k
  if (!bbox)
313
287
    bbox = &local_bbox;
314
315
  /* If we're given an empty scissor, sanitize it. This makes life easier
316
   * down the line. */
317
95.8k
  if (fz_is_empty_irect(scissor))
318
20.4k
    scissor.x1 = scissor.x0, scissor.y1 = scissor.y0;
319
320
95.8k
  if (fz_reset_rasterizer(ctx, rast, scissor))
321
0
  {
322
0
    empty = do_flatten_fill(ctx, rast, path, ctm, flatness);
323
0
    if (empty)
324
0
      return *bbox = fz_empty_irect, 1;
325
0
    fz_postindex_rasterizer(ctx, rast);
326
0
  }
327
328
95.8k
  empty = do_flatten_fill(ctx, rast, path, ctm, flatness);
329
95.8k
  if (empty)
330
20.9k
    return *bbox = fz_empty_irect, 1;
331
332
74.8k
  *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
333
74.8k
  return fz_is_empty_irect(*bbox);
334
95.8k
}
335
336
typedef struct sctx
337
{
338
  fz_rasterizer *rast;
339
  fz_matrix ctm;
340
  float flatness;
341
  const fz_stroke_state *stroke;
342
343
  int linejoin;
344
  float linewidth;
345
  float miterlimit;
346
  fz_point beg[2];
347
  fz_point seg[2];
348
  int sn;
349
  int not_just_moves;
350
  int from_bezier;
351
  fz_point cur;
352
353
  fz_rect rect;
354
  const float *dash_list;
355
  float dash_phase;
356
  int dash_len;
357
  float dash_total;
358
  int toggle, cap;
359
  int offset;
360
  float phase;
361
  fz_point dash_cur;
362
  fz_point dash_beg;
363
364
  float dirn_x;
365
  float dirn_y;
366
} sctx;
367
368
static void
369
fz_add_line(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1, int rev)
370
2.02M
{
371
2.02M
  float tx0 = s->ctm.a * x0 + s->ctm.c * y0 + s->ctm.e;
372
2.02M
  float ty0 = s->ctm.b * x0 + s->ctm.d * y0 + s->ctm.f;
373
2.02M
  float tx1 = s->ctm.a * x1 + s->ctm.c * y1 + s->ctm.e;
374
2.02M
  float ty1 = s->ctm.b * x1 + s->ctm.d * y1 + s->ctm.f;
375
376
2.02M
  fz_insert_rasterizer(ctx, s->rast, tx0, ty0, tx1, ty1, rev);
377
2.02M
}
378
379
static void
380
fz_add_horiz_rect(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1)
381
50.5k
{
382
50.5k
  if (fz_antidropout_rasterizer(ctx, s->rast)) {
383
50.5k
    if (s->ctm.b == 0 && s->ctm.c == 0)
384
50.5k
    {
385
50.5k
      float tx0 = s->ctm.a * x0 + s->ctm.e;
386
50.5k
      float ty0 = s->ctm.d * y0 + s->ctm.f;
387
50.5k
      float tx1 = s->ctm.a * x1 + s->ctm.e;
388
50.5k
      float ty1 = s->ctm.d * y1 + s->ctm.f;
389
50.5k
      fz_insert_rasterizer_rect(ctx, s->rast, tx1, ty1, tx0, ty0);
390
50.5k
      return;
391
50.5k
    }
392
1
    else if (s->ctm.a == 0 && s->ctm.d == 0)
393
0
    {
394
0
      float tx0 = s->ctm.c * y0 + s->ctm.e;
395
0
      float ty0 = s->ctm.b * x0 + s->ctm.f;
396
0
      float tx1 = s->ctm.c * y1 + s->ctm.e;
397
0
      float ty1 = s->ctm.b * x1 + s->ctm.f;
398
0
      fz_insert_rasterizer_rect(ctx, s->rast, tx1, ty0, tx0, ty1);
399
0
      return;
400
0
    }
401
50.5k
  }
402
403
1
  fz_add_line(ctx, s, x0, y0, x1, y0, 0);
404
1
  fz_add_line(ctx, s, x1, y1, x0, y1, 1);
405
1
}
406
407
static void
408
fz_add_vert_rect(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1)
409
0
{
410
0
  if (fz_antidropout_rasterizer(ctx, s->rast))
411
0
  {
412
0
    if (s->ctm.b == 0 && s->ctm.c == 0)
413
0
    {
414
0
      float tx0 = s->ctm.a * x0 + s->ctm.e;
415
0
      float ty0 = s->ctm.d * y0 + s->ctm.f;
416
0
      float tx1 = s->ctm.a * x1 + s->ctm.e;
417
0
      float ty1 = s->ctm.d * y1 + s->ctm.f;
418
0
      fz_insert_rasterizer_rect(ctx, s->rast, tx0, ty1, tx1, ty0);
419
0
      return;
420
0
    }
421
0
    else if (s->ctm.a == 0 && s->ctm.d == 0)
422
0
    {
423
0
      float tx0 = s->ctm.c * y0 + s->ctm.e;
424
0
      float ty0 = s->ctm.b * x0 + s->ctm.f;
425
0
      float tx1 = s->ctm.c * y1 + s->ctm.e;
426
0
      float ty1 = s->ctm.b * x1 + s->ctm.f;
427
0
      fz_insert_rasterizer_rect(ctx, s->rast, tx0, ty0, tx1, ty1);
428
0
      return;
429
0
    }
430
0
  }
431
0
432
0
  fz_add_line(ctx, s, x1, y0, x0, y0, 0);
433
0
  fz_add_line(ctx, s, x0, y1, x1, y1, 1);
434
0
}
435
436
static void
437
fz_add_arc(fz_context *ctx, sctx *s,
438
  float xc, float yc,
439
  float x0, float y0,
440
  float x1, float y1,
441
  int rev)
442
70.0k
{
443
70.0k
  float th0, th1, r;
444
70.0k
  float theta;
445
70.0k
  float ox, oy, nx, ny;
446
70.0k
  int n, i;
447
448
70.0k
  r = fabsf(s->linewidth);
449
70.0k
  theta = 2 * FZ_SQRT2 * sqrtf(s->flatness / r);
450
70.0k
  th0 = atan2f(y0, x0);
451
70.0k
  th1 = atan2f(y1, x1);
452
453
70.0k
  if (r > 0)
454
70.0k
  {
455
70.0k
    if (th0 < th1)
456
5.12k
      th0 += FZ_PI * 2;
457
70.0k
    n = ceilf((th0 - th1) / theta);
458
70.0k
  }
459
0
  else
460
0
  {
461
0
    if (th1 < th0)
462
0
      th1 += FZ_PI * 2;
463
0
    n = ceilf((th1 - th0) / theta);
464
0
  }
465
466
70.0k
  if (rev)
467
56.2k
  {
468
56.2k
    ox = x1;
469
56.2k
    oy = y1;
470
56.2k
    for (i = n-1; i > 0; i--)
471
5
    {
472
5
      theta = th0 + (th1 - th0) * i / n;
473
5
      nx = cosf(theta) * r;
474
5
      ny = sinf(theta) * r;
475
5
      fz_add_line(ctx, s, xc + nx, yc + ny, xc + ox, yc + oy, rev);
476
5
      ox = nx;
477
5
      oy = ny;
478
5
    }
479
480
56.2k
    fz_add_line(ctx, s, xc + x0, yc + y0, xc + ox, yc + oy, rev);
481
56.2k
  }
482
13.7k
  else
483
13.7k
  {
484
13.7k
    ox = x0;
485
13.7k
    oy = y0;
486
13.8k
    for (i = 1; i < n; i++)
487
47
    {
488
47
      theta = th0 + (th1 - th0) * i / n;
489
47
      nx = cosf(theta) * r;
490
47
      ny = sinf(theta) * r;
491
47
      fz_add_line(ctx, s, xc + ox, yc + oy, xc + nx, yc + ny, rev);
492
47
      ox = nx;
493
47
      oy = ny;
494
47
    }
495
496
13.7k
    fz_add_line(ctx, s, xc + ox, yc + oy, xc + x1, yc + y1, rev);
497
13.7k
  }
498
70.0k
}
499
500
/* FLT_TINY * FLT_TINY is approximately FLT_EPSILON */
501
471k
#define FLT_TINY 3.4e-4F
502
static int find_normal_vectors(float dx, float dy, float linewidth, float *dlx, float *dly)
503
1.37M
{
504
1.37M
  if (dx == 0)
505
31.9k
  {
506
31.9k
    if (dy < FLT_TINY && dy > - FLT_TINY)
507
483
      goto tiny;
508
31.4k
    else if (dy > 0)
509
15.8k
      *dlx = linewidth;
510
15.5k
    else
511
15.5k
      *dlx = -linewidth;
512
31.4k
    *dly = 0;
513
31.4k
  }
514
1.34M
  else if (dy == 0)
515
144k
  {
516
144k
    if (dx < FLT_TINY && dx > - FLT_TINY)
517
0
      goto tiny;
518
144k
    else if (dx > 0)
519
41.1k
      *dly = -linewidth;
520
103k
    else
521
103k
      *dly = linewidth;
522
144k
    *dlx = 0;
523
144k
  }
524
1.20M
  else
525
1.20M
  {
526
1.20M
    float sq = dx * dx + dy * dy;
527
1.20M
    float scale;
528
529
1.20M
    if (sq < FLT_EPSILON)
530
0
      goto tiny;
531
1.20M
    scale = linewidth / sqrtf(sq);
532
1.20M
    *dlx = dy * scale;
533
1.20M
    *dly = -dx * scale;
534
1.20M
  }
535
1.37M
  return 0;
536
483
tiny:
537
483
  *dlx = 0;
538
483
  *dly = 0;
539
483
  return 1;
540
1.37M
}
541
542
static void
543
fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, float cx, float cy, int join_under)
544
456k
{
545
456k
  float miterlimit = s->miterlimit;
546
456k
  float linewidth = s->linewidth;
547
456k
  fz_linejoin linejoin = s->linejoin;
548
456k
  float dx0, dy0;
549
456k
  float dx1, dy1;
550
456k
  float dlx0, dly0;
551
456k
  float dlx1, dly1;
552
456k
  float dmx, dmy;
553
456k
  float dmr2;
554
456k
  float scale;
555
456k
  float cross;
556
456k
  int rev = 0;
557
558
456k
  dx0 = bx - ax;
559
456k
  dy0 = by - ay;
560
561
456k
  dx1 = cx - bx;
562
456k
  dy1 = cy - by;
563
564
456k
  cross = dx1 * dy0 - dx0 * dy1;
565
  /* Ensure that cross >= 0 */
566
456k
  if (cross < 0)
567
195k
  {
568
195k
    float tmp;
569
195k
    tmp = dx1; dx1 = -dx0; dx0 = -tmp;
570
195k
    tmp = dy1; dy1 = -dy0; dy0 = -tmp;
571
195k
    cross = -cross;
572
195k
    rev = !rev;
573
195k
  }
574
575
456k
  if (find_normal_vectors(dx0, dy0, linewidth, &dlx0, &dly0))
576
0
    linejoin = FZ_LINEJOIN_BEVEL;
577
578
456k
  if (find_normal_vectors(dx1, dy1, linewidth, &dlx1, &dly1))
579
0
    linejoin = FZ_LINEJOIN_BEVEL;
580
581
456k
  dmx = (dlx0 + dlx1) * 0.5f;
582
456k
  dmy = (dly0 + dly1) * 0.5f;
583
456k
  dmr2 = dmx * dmx + dmy * dmy;
584
585
456k
  if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0)
586
153k
    linejoin = FZ_LINEJOIN_BEVEL;
587
588
  /* XPS miter joins are clipped at miterlength, rather than simply
589
   * being converted to bevelled joins. */
590
456k
  if (linejoin == FZ_LINEJOIN_MITER_XPS)
591
0
  {
592
0
    if (cross == 0)
593
0
      linejoin = FZ_LINEJOIN_BEVEL;
594
0
    else if (dmr2 * miterlimit * miterlimit >= linewidth * linewidth)
595
0
      linejoin = FZ_LINEJOIN_MITER;
596
0
  }
597
456k
  else if (linejoin == FZ_LINEJOIN_MITER)
598
232k
    if (dmr2 * miterlimit * miterlimit < linewidth * linewidth)
599
2.14k
      linejoin = FZ_LINEJOIN_BEVEL;
600
601
456k
  if (join_under)
602
427k
  {
603
427k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx + dlx0, by + dly0, !rev);
604
427k
  }
605
28.2k
  else if (rev)
606
18.9k
  {
607
18.9k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 0);
608
18.9k
    fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 0);
609
18.9k
  }
610
9.23k
  else
611
9.23k
  {
612
9.23k
    fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 1);
613
9.23k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 1);
614
9.23k
  }
615
616
456k
  switch (linejoin)
617
456k
  {
618
0
  case FZ_LINEJOIN_MITER_XPS:
619
0
  {
620
0
    float k, t0x, t0y, t1x, t1y;
621
622
0
    scale = linewidth * linewidth / dmr2;
623
0
    dmx *= scale;
624
0
    dmy *= scale;
625
0
    k = (scale - linewidth * miterlimit / sqrtf(dmr2)) / (scale - 1);
626
0
    t0x = bx - dmx + k * (dmx - dlx0);
627
0
    t0y = by - dmy + k * (dmy - dly0);
628
0
    t1x = bx - dmx + k * (dmx - dlx1);
629
0
    t1y = by - dmy + k * (dmy - dly1);
630
631
0
    if (rev)
632
0
    {
633
0
      fz_add_line(ctx, s, t1x, t1y, bx - dlx1, by - dly1, 1);
634
0
      fz_add_line(ctx, s, t0x, t0y, t1x, t1y, 1);
635
0
      fz_add_line(ctx, s, bx - dlx0, by - dly0, t0x, t0y, 1);
636
0
    }
637
0
    else
638
0
    {
639
0
      fz_add_line(ctx, s, bx - dlx0, by - dly0, t0x, t0y, 0);
640
0
      fz_add_line(ctx, s, t0x, t0y, t1x, t1y, 0);
641
0
      fz_add_line(ctx, s, t1x, t1y, bx - dlx1, by - dly1, 0);
642
0
    }
643
0
    break;
644
0
  }
645
230k
  case FZ_LINEJOIN_MITER:
646
230k
    scale = linewidth * linewidth / dmr2;
647
230k
    dmx *= scale;
648
230k
    dmy *= scale;
649
650
230k
    if (rev)
651
85.4k
    {
652
85.4k
      fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 1);
653
85.4k
      fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 1);
654
85.4k
    }
655
144k
    else
656
144k
    {
657
144k
      fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 0);
658
144k
      fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 0);
659
144k
    }
660
230k
    break;
661
662
155k
  case FZ_LINEJOIN_BEVEL:
663
155k
    fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dlx1, by - dly1, rev);
664
155k
    break;
665
666
70.0k
  case FZ_LINEJOIN_ROUND:
667
70.0k
    fz_add_arc(ctx, s, bx, by, -dlx0, -dly0, -dlx1, -dly1, rev);
668
70.0k
    break;
669
670
0
  default:
671
0
    assert("Invalid line join" == NULL);
672
456k
  }
673
456k
}
674
675
static void
676
do_linecap(fz_context *ctx, sctx *s, float bx, float by, fz_linecap linecap, int rev, float dlx, float dly)
677
20.8k
{
678
20.8k
  float flatness = s->flatness;
679
20.8k
  float linewidth = s->linewidth;
680
681
20.8k
  switch (linecap)
682
20.8k
  {
683
11.6k
  case FZ_LINECAP_BUTT:
684
11.6k
    fz_add_line(ctx, s, bx - dlx, by - dly, bx + dlx, by + dly, rev);
685
11.6k
    break;
686
687
9.17k
  case FZ_LINECAP_ROUND:
688
9.17k
  {
689
9.17k
    int i;
690
9.17k
    int n = ceilf(FZ_PI / (2.0f * FZ_SQRT2 * sqrtf(flatness / linewidth)));
691
9.17k
    float ox = bx - dlx;
692
9.17k
    float oy = by - dly;
693
13.8k
    for (i = 1; i < n; i++)
694
4.72k
    {
695
4.72k
      float theta = FZ_PI * i / n;
696
4.72k
      float cth = cosf(theta);
697
4.72k
      float sth = sinf(theta);
698
4.72k
      float nx = bx - dlx * cth - dly * sth;
699
4.72k
      float ny = by - dly * cth + dlx * sth;
700
4.72k
      fz_add_line(ctx, s, ox, oy, nx, ny, rev);
701
4.72k
      ox = nx;
702
4.72k
      oy = ny;
703
4.72k
    }
704
9.17k
    fz_add_line(ctx, s, ox, oy, bx + dlx, by + dly, rev);
705
9.17k
    break;
706
0
  }
707
708
72
  case FZ_LINECAP_SQUARE:
709
72
    fz_add_line(ctx, s, bx - dlx, by - dly,
710
72
      bx - dlx - dly, by - dly + dlx, rev);
711
72
    fz_add_line(ctx, s, bx - dlx - dly, by - dly + dlx,
712
72
      bx + dlx - dly, by + dly + dlx, rev);
713
72
    fz_add_line(ctx, s, bx + dlx - dly, by + dly + dlx,
714
72
      bx + dlx, by + dly, rev);
715
72
    break;
716
717
0
  case FZ_LINECAP_TRIANGLE:
718
0
  {
719
0
    float mx = -dly;
720
0
    float my = dlx;
721
0
    fz_add_line(ctx, s, bx - dlx, by - dly, bx + mx, by + my, rev);
722
0
    fz_add_line(ctx, s, bx + mx, by + my, bx + dlx, by + dly, rev);
723
0
    break;
724
0
  }
725
726
0
  default:
727
0
    assert("Invalid line cap" == NULL);
728
20.8k
  }
729
20.8k
}
730
731
static void
732
fz_add_line_cap(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, fz_linecap linecap, int rev)
733
20.8k
{
734
20.8k
  float linewidth = s->linewidth;
735
20.8k
  float dx = bx - ax;
736
20.8k
  float dy = by - ay;
737
738
20.8k
  float scale = linewidth / sqrtf(dx * dx + dy * dy);
739
20.8k
  float dlx = dy * scale;
740
20.8k
  float dly = -dx * scale;
741
742
20.8k
  do_linecap(ctx, s, bx, by, linecap, rev, dlx, dly);
743
20.8k
}
744
745
static void
746
fz_add_zero_len_cap(fz_context *ctx, sctx *s, float ax, float ay, fz_linecap linecap, int rev)
747
12
{
748
12
  float linewidth = s->linewidth;
749
12
  float dx = rev ? -s->dirn_x : s->dirn_x;
750
12
  float dy = rev ? -s->dirn_y : s->dirn_y;
751
12
  float scale, dlx, dly;
752
753
12
  if (dx == 0 && dy == 0)
754
12
    return;
755
756
0
  scale = linewidth / sqrtf(dx * dx + dy * dy);
757
0
  dlx = dy * scale;
758
0
  dly = -dx * scale;
759
760
0
  do_linecap(ctx, s, ax, ay, linecap, rev, dlx, dly);
761
0
}
762
763
static void
764
fz_add_line_dot(fz_context *ctx, sctx *s, float ax, float ay)
765
0
{
766
0
  float flatness = s->flatness;
767
0
  float linewidth = s->linewidth;
768
0
  int n = ceilf(FZ_PI / (FZ_SQRT2 * sqrtf(flatness / linewidth)));
769
0
  float ox = ax - linewidth;
770
0
  float oy = ay;
771
0
  int i;
772
773
0
  if (n < 3)
774
0
    n = 3;
775
0
  for (i = 1; i < n; i++)
776
0
  {
777
0
    float theta = FZ_PI * 2 * i / n;
778
0
    float cth = cosf(theta);
779
0
    float sth = sinf(theta);
780
0
    float nx = ax - cth * linewidth;
781
0
    float ny = ay + sth * linewidth;
782
0
    fz_add_line(ctx, s, ox, oy, nx, ny, 0);
783
0
    ox = nx;
784
0
    oy = ny;
785
0
  }
786
787
0
  fz_add_line(ctx, s, ox, oy, ax - linewidth, ay, 0);
788
0
}
789
790
static void
791
fz_stroke_flush(fz_context *ctx, sctx *s, fz_linecap start_cap, fz_linecap end_cap)
792
29.3k
{
793
29.3k
  if (s->sn == 1)
794
10.4k
  {
795
10.4k
    fz_add_line_cap(ctx, s, s->beg[1].x, s->beg[1].y, s->beg[0].x, s->beg[0].y, start_cap, 2);
796
10.4k
    fz_add_line_cap(ctx, s, s->seg[0].x, s->seg[0].y, s->seg[1].x, s->seg[1].y, end_cap, 0);
797
10.4k
  }
798
18.8k
  else if (s->not_just_moves)
799
6
  {
800
6
    if (s->cap == FZ_LINECAP_ROUND)
801
0
    {
802
0
      fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y);
803
0
    }
804
6
    else
805
6
    {
806
6
      fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 2);
807
6
      fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 0);
808
6
    }
809
6
  }
810
811
29.3k
  fz_gap_rasterizer(ctx, s->rast);
812
29.3k
}
813
814
static void
815
fz_stroke_moveto(fz_context *ctx, void *s_, float x, float y)
816
17.6k
{
817
17.6k
  struct sctx *s = (struct sctx *)s_;
818
819
17.6k
  s->seg[0].x = s->beg[0].x = x;
820
17.6k
  s->seg[0].y = s->beg[0].y = y;
821
17.6k
  s->sn = 0;
822
17.6k
  s->not_just_moves = 0;
823
17.6k
  s->from_bezier = 0;
824
17.6k
  s->dirn_x = 0;
825
17.6k
  s->dirn_y = 0;
826
17.6k
}
827
828
static void
829
fz_stroke_lineto_aux(fz_context *ctx, sctx *s, float x, float y, int from_bezier, float dirn_x, float dirn_y)
830
467k
{
831
467k
  float ox = s->seg[s->sn].x;
832
467k
  float oy = s->seg[s->sn].y;
833
467k
  float dx = x - ox;
834
467k
  float dy = y - oy;
835
467k
  float dlx, dly;
836
837
467k
  s->not_just_moves = 1;
838
839
  /* We store the direction (as used for the alignment of caps etc) based on the
840
   * direction we are passed in. */
841
467k
  s->dirn_x = dirn_x;
842
467k
  s->dirn_y = dirn_y;
843
844
  /* We calculate the normal vectors from the delta that we have just moved. */
845
467k
  if (find_normal_vectors(dx, dy, s->linewidth, &dlx, &dly))
846
483
  {
847
483
    return;
848
483
  }
849
850
466k
  if (s->sn == 1)
851
449k
    fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, ox, oy, x, y, s->from_bezier & from_bezier);
852
853
466k
#if 1
854
466k
  if (0 && dx == 0)
855
0
  {
856
0
    fz_add_vert_rect(ctx, s, ox - dlx, oy, x + dlx, y);
857
0
  }
858
466k
  else if (dy == 0)
859
50.5k
  {
860
50.5k
    fz_add_horiz_rect(ctx, s, ox, oy - dly, x, y + dly);
861
50.5k
  }
862
416k
  else
863
416k
#endif
864
416k
  {
865
866
416k
    fz_add_line(ctx, s, ox - dlx, oy - dly, x - dlx, y - dly, 0);
867
416k
    fz_add_line(ctx, s, x + dlx, y + dly, ox + dlx, oy + dly, 1);
868
416k
  }
869
870
466k
  if (s->sn)
871
449k
  {
872
449k
    s->seg[0] = s->seg[1];
873
449k
    s->seg[1].x = x;
874
449k
    s->seg[1].y = y;
875
449k
  }
876
16.6k
  else
877
16.6k
  {
878
16.6k
    s->seg[1].x = s->beg[1].x = x;
879
16.6k
    s->seg[1].y = s->beg[1].y = y;
880
16.6k
    s->sn = 1;
881
16.6k
  }
882
466k
  s->from_bezier = from_bezier;
883
466k
}
884
885
static void
886
fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier)
887
460k
{
888
460k
  float ox = s->seg[s->sn].x;
889
460k
  float oy = s->seg[s->sn].y;
890
460k
  float dx = x - ox;
891
460k
  float dy = y - oy;
892
460k
  fz_stroke_lineto_aux(ctx, s, x, y, from_bezier, dx, dy);
893
460k
}
894
895
static void
896
fz_stroke_closepath(fz_context *ctx, sctx *s)
897
6.20k
{
898
6.20k
  if (s->sn == 1)
899
6.15k
  {
900
6.15k
    fz_stroke_lineto(ctx, s, s->beg[0].x, s->beg[0].y, 0);
901
    /* fz_stroke_lineto will *normally* end up with s->seg[1] being the x,y coords passed in.
902
     * As such, the following line should draw a linejoin between the closing segment of this
903
     * subpath (seg[0]->seg[1]) == (seg[0]->beg[0]) and the first segment of this subpath
904
     * (beg[0]->beg[1]).
905
     * In cases where the line was already at an x,y infinitesimally close to s->beg[0],
906
     * fz_stroke_lineto may exit without doing any processing. This leaves seg[0]->seg[1]
907
     * pointing at the penultimate line segment. Thus this draws a linejoin between that
908
     * penultimate segment and the end segment. This is what we want. */
909
6.15k
    fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, s->beg[0].x, s->beg[0].y, s->beg[1].x, s->beg[1].y, 0);
910
6.15k
  }
911
51
  else if (s->cap == FZ_LINECAP_ROUND)
912
0
  {
913
0
    fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y);
914
0
  }
915
916
6.20k
  s->seg[0] = s->beg[0];
917
6.20k
  s->sn = 0;
918
6.20k
  s->not_just_moves = 0;
919
6.20k
  s->from_bezier = 0;
920
6.20k
  s->dirn_x = 0;
921
6.20k
  s->dirn_y = 0;
922
923
6.20k
  fz_gap_rasterizer(ctx, s->rast);
924
6.20k
}
925
926
static void
927
fz_stroke_bezier(fz_context *ctx, struct sctx *s,
928
  float xa, float ya,
929
  float xb, float yb,
930
  float xc, float yc,
931
  float xd, float yd, int depth)
932
846k
{
933
846k
  float dmax;
934
846k
  float xab, yab;
935
846k
  float xbc, ybc;
936
846k
  float xcd, ycd;
937
846k
  float xabc, yabc;
938
846k
  float xbcd, ybcd;
939
846k
  float xabcd, yabcd;
940
941
  /* termination check */
942
846k
  dmax = fz_abs(xa - xb);
943
846k
  dmax = fz_max(dmax, fz_abs(ya - yb));
944
846k
  dmax = fz_max(dmax, fz_abs(xd - xc));
945
846k
  dmax = fz_max(dmax, fz_abs(yd - yc));
946
846k
  if (dmax < s->flatness || depth >= MAX_DEPTH)
947
429k
  {
948
429k
    fz_stroke_lineto(ctx, s, xd, yd, 1);
949
429k
    return;
950
429k
  }
951
952
417k
  xab = xa + xb;
953
417k
  yab = ya + yb;
954
417k
  xbc = xb + xc;
955
417k
  ybc = yb + yc;
956
417k
  xcd = xc + xd;
957
417k
  ycd = yc + yd;
958
959
417k
  xabc = xab + xbc;
960
417k
  yabc = yab + ybc;
961
417k
  xbcd = xbc + xcd;
962
417k
  ybcd = ybc + ycd;
963
964
417k
  xabcd = xabc + xbcd;
965
417k
  yabcd = yabc + ybcd;
966
967
417k
  xab *= 0.5f; yab *= 0.5f;
968
  /* xbc *= 0.5f; ybc *= 0.5f; */
969
417k
  xcd *= 0.5f; ycd *= 0.5f;
970
971
417k
  xabc *= 0.25f; yabc *= 0.25f;
972
417k
  xbcd *= 0.25f; ybcd *= 0.25f;
973
974
417k
  xabcd *= 0.125f; yabcd *= 0.125f;
975
976
417k
  fz_stroke_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
977
417k
  fz_stroke_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
978
417k
}
979
980
static void
981
fz_stroke_quad(fz_context *ctx, struct sctx *s,
982
  float xa, float ya,
983
  float xb, float yb,
984
  float xc, float yc, int depth)
985
0
{
986
0
  float dmax;
987
0
  float xab, yab;
988
0
  float xbc, ybc;
989
0
  float xabc, yabc;
990
991
  /* termination check */
992
0
  dmax = fz_abs(xa - xb);
993
0
  dmax = fz_max(dmax, fz_abs(ya - yb));
994
0
  dmax = fz_max(dmax, fz_abs(xc - xb));
995
0
  dmax = fz_max(dmax, fz_abs(yc - yb));
996
0
  if (dmax < s->flatness || depth >= MAX_DEPTH)
997
0
  {
998
0
    fz_stroke_lineto(ctx, s, xc, yc, 1);
999
0
    return;
1000
0
  }
1001
1002
0
  xab = xa + xb;
1003
0
  yab = ya + yb;
1004
0
  xbc = xb + xc;
1005
0
  ybc = yb + yc;
1006
1007
0
  xabc = xab + xbc;
1008
0
  yabc = yab + ybc;
1009
1010
0
  xab *= 0.5f; yab *= 0.5f;
1011
0
  xbc *= 0.5f; ybc *= 0.5f;
1012
1013
0
  xabc *= 0.25f; yabc *= 0.25f;
1014
1015
0
  fz_stroke_quad(ctx, s, xa, ya, xab, yab, xabc, yabc, depth + 1);
1016
0
  fz_stroke_quad(ctx, s, xabc, yabc, xbc, ybc, xc, yc, depth + 1);
1017
0
}
1018
1019
static void
1020
stroke_moveto(fz_context *ctx, void *s_, float x, float y)
1021
13.9k
{
1022
13.9k
  sctx *s = (sctx *)s_;
1023
1024
13.9k
  fz_stroke_flush(ctx, s, s->stroke->start_cap, s->stroke->end_cap);
1025
13.9k
  fz_stroke_moveto(ctx, s, x, y);
1026
13.9k
  s->cur.x = x;
1027
13.9k
  s->cur.y = y;
1028
13.9k
}
1029
1030
static void
1031
stroke_lineto(fz_context *ctx, void *s_, float x, float y)
1032
25.5k
{
1033
25.5k
  sctx *s = (sctx *)s_;
1034
1035
25.5k
  fz_stroke_lineto(ctx, s, x, y, 0);
1036
25.5k
  s->cur.x = x;
1037
25.5k
  s->cur.y = y;
1038
25.5k
}
1039
1040
static void
1041
stroke_curveto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2, float x3, float y3)
1042
11.6k
{
1043
11.6k
  sctx *s = (sctx *)s_;
1044
1045
11.6k
  fz_stroke_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
1046
11.6k
  s->cur.x = x3;
1047
11.6k
  s->cur.y = y3;
1048
11.6k
}
1049
1050
static void
1051
stroke_quadto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2)
1052
0
{
1053
0
  sctx *s = (sctx *)s_;
1054
1055
0
  fz_stroke_quad(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, 0);
1056
0
  s->cur.x = x2;
1057
0
  s->cur.y = y2;
1058
0
}
1059
1060
static void
1061
stroke_close(fz_context *ctx, void *s_)
1062
6.20k
{
1063
6.20k
  sctx *s = (sctx *)s_;
1064
1065
6.20k
  fz_stroke_closepath(ctx, s);
1066
6.20k
}
1067
1068
static const fz_path_walker stroke_proc =
1069
{
1070
  stroke_moveto,
1071
  stroke_lineto,
1072
  stroke_curveto,
1073
  stroke_close,
1074
  stroke_quadto
1075
};
1076
1077
static void
1078
fz_dash_moveto(fz_context *ctx, struct sctx *s, float x, float y)
1079
248
{
1080
248
  s->toggle = 1;
1081
248
  s->offset = 0;
1082
248
  s->phase = s->dash_phase;
1083
1084
248
  while (s->phase > 0 && s->phase >= s->dash_list[s->offset])
1085
0
  {
1086
0
    s->toggle = !s->toggle;
1087
0
    s->phase -= s->dash_list[s->offset];
1088
0
    s->offset ++;
1089
0
    if (s->offset == s->dash_len)
1090
0
      s->offset = 0;
1091
0
  }
1092
1093
248
  s->dash_cur.x = x;
1094
248
  s->dash_cur.y = y;
1095
1096
248
  if (s->toggle)
1097
248
  {
1098
248
    fz_stroke_flush(ctx, s, s->cap, s->stroke->end_cap);
1099
248
    s->cap = s->stroke->start_cap;
1100
248
    fz_stroke_moveto(ctx, s, x, y);
1101
248
  }
1102
248
}
1103
1104
/*
1105
  Performs: a += (b-a) * i/n
1106
  allowing for FP inaccuracies that can cause a to "overrun" b.
1107
*/
1108
static float advance(float a, float b, float i, float n)
1109
0
{
1110
0
  float d = b - a;
1111
0
  float target = a + d * i/n;
1112
1113
0
  if (d < 0 && target < b)
1114
0
    target = b;
1115
0
  else if (d > 0 && target > b)
1116
0
    target = b;
1117
1118
0
  return target;
1119
0
}
1120
1121
static int is_odd(float f)
1122
14
{
1123
14
  float intpart = truncf(f);
1124
14
  return isfinite(f) ? (intpart - 2.0f * truncf(intpart / 2.0f) != 0.0f) : 0;
1125
14
}
1126
1127
static void
1128
fz_dash_lineto(fz_context *ctx, struct sctx *s, float bx, float by, int from_bezier)
1129
5.54k
{
1130
5.54k
  float dx, dy, d;
1131
5.54k
  float total, used, ratio, tail;
1132
5.54k
  float ax, ay;
1133
5.54k
  float mx, my;
1134
5.54k
  float old_bx = 0, old_by = 0;
1135
5.54k
  float n;
1136
5.54k
  int dash_cap = s->stroke->dash_cap;
1137
1138
5.54k
  ax = s->dash_cur.x;
1139
5.54k
  ay = s->dash_cur.y;
1140
5.54k
  dx = bx - ax;
1141
5.54k
  dy = by - ay;
1142
5.54k
  used = 0;
1143
5.54k
  tail = 0;
1144
5.54k
  total = sqrtf(dx * dx + dy * dy);
1145
1146
  /* If a is off screen, bring it onto the screen. First
1147
   * horizontally... */
1148
5.54k
  if ((d = s->rect.x0 - ax) > 0)
1149
0
  {
1150
0
    if (bx < s->rect.x0)
1151
0
    {
1152
      /* Entirely off screen */
1153
0
      tail = total;
1154
0
      old_bx = bx;
1155
0
      old_by = by;
1156
0
      goto adjust_for_tail;
1157
0
    }
1158
0
    ax = s->rect.x0;  /* d > 0, dx > 0 */
1159
0
    goto a_moved_horizontally;
1160
0
  }
1161
5.54k
  else if (d < 0 && (d = (s->rect.x1 - ax)) < 0)
1162
0
  {
1163
0
    if (bx > s->rect.x1)
1164
0
    {
1165
      /* Entirely off screen */
1166
0
      tail = total;
1167
0
      old_bx = bx;
1168
0
      old_by = by;
1169
0
      goto adjust_for_tail;
1170
0
    }
1171
0
    ax = s->rect.x1;  /* d < 0, dx < 0 */
1172
0
a_moved_horizontally: /* d and dx have the same sign */
1173
0
    assert((d > 0 && dx > 0) || (d < 0 && dx < 0));
1174
0
    assert(dx != 0);
1175
0
    ay = advance(ay, by, d, dx);
1176
0
    used = total * d/dx;
1177
0
    total -= used;
1178
0
    dx = bx - ax;
1179
0
    dy = by - ay;
1180
0
  }
1181
  /* Then vertically... */
1182
5.54k
  if ((d = s->rect.y0 - ay) > 0)
1183
14
  {
1184
14
    if (by < s->rect.y0)
1185
14
    {
1186
      /* Entirely off screen */
1187
14
      tail = total;
1188
14
      old_bx = bx;
1189
14
      old_by = by;
1190
14
      goto adjust_for_tail;
1191
14
    }
1192
0
    ay = s->rect.y0;  /* d > 0, dy > 0 */
1193
0
    goto a_moved_vertically;
1194
14
  }
1195
5.53k
  else if (d < 0 && (d = (s->rect.y1 - ay)) < 0)
1196
0
  {
1197
0
    if (by > s->rect.y1)
1198
0
    {
1199
      /* Entirely off screen */
1200
0
      tail = total;
1201
0
      old_bx = bx;
1202
0
      old_by = by;
1203
0
      goto adjust_for_tail;
1204
0
    }
1205
0
    ay = s->rect.y1;  /* d < 0, dy < 0 */
1206
0
a_moved_vertically: /* d and dy have the same sign */
1207
0
    assert((d > 0 && dy > 0) || (d < 0 && dy < 0));
1208
0
    assert(dy != 0);
1209
0
    ax = advance(ax, bx, d, dy);
1210
0
    d = total * d/dy;
1211
0
    total -= d;
1212
0
    used += d;
1213
0
    dx = bx - ax;
1214
0
    dy = by - ay;
1215
0
  }
1216
5.53k
  if (used != 0.0f)
1217
0
  {
1218
    /* Update the position in the dash array */
1219
0
    if (s->toggle)
1220
0
    {
1221
0
      fz_stroke_lineto(ctx, s, ax, ay, from_bezier);
1222
0
    }
1223
0
    else
1224
0
    {
1225
0
      fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap);
1226
0
      s->cap = s->stroke->dash_cap;
1227
0
      fz_stroke_moveto(ctx, s, ax, ay);
1228
0
    }
1229
0
    used += s->phase;
1230
0
    n = trunc(used/s->dash_total);
1231
0
    used -= n*s->dash_total;
1232
0
    if (is_odd(n) & s->dash_len & 1)
1233
0
      s->toggle = !s->toggle;
1234
0
    while (used >= s->dash_list[s->offset])
1235
0
    {
1236
0
      used -= s->dash_list[s->offset];
1237
0
      s->offset++;
1238
0
      if (s->offset == s->dash_len)
1239
0
        s->offset = 0;
1240
0
      s->toggle = !s->toggle;
1241
0
    }
1242
0
    if (s->toggle)
1243
0
    {
1244
0
      fz_stroke_lineto(ctx, s, ax, ay, from_bezier);
1245
0
    }
1246
0
    else
1247
0
    {
1248
0
      fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap);
1249
0
      s->cap = s->stroke->dash_cap;
1250
0
      fz_stroke_moveto(ctx, s, ax, ay);
1251
0
    }
1252
0
    s->phase = used;
1253
0
    used = 0;
1254
0
  }
1255
1256
  /* Now if bx is off screen, bring it back */
1257
5.53k
  if (dx == 0)
1258
49
  {
1259
    /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
1260
49
  }
1261
5.48k
  else if ((d = bx - s->rect.x0) < 0)
1262
0
  {
1263
0
    old_bx = bx;
1264
0
    old_by = by;
1265
0
    bx = s->rect.x0;  /* d < 0, dx < 0 */
1266
0
    goto b_moved_horizontally;
1267
0
  }
1268
5.48k
  else if (d > 0 && (d = (bx - s->rect.x1)) > 0)
1269
0
  {
1270
0
    old_bx = bx;
1271
0
    old_by = by;
1272
0
    bx = s->rect.x1;  /* d > 0, dx > 0 */
1273
0
b_moved_horizontally: /* d and dx have the same sign */
1274
0
    assert((d > 0 && dx > 0) || (d < 0 && dx < 0));
1275
0
    assert(dx != 0);
1276
0
    by = advance(by, ay, d, dx);
1277
0
    tail = total * d/dx;
1278
0
    total -= tail;
1279
0
    dx = bx - ax;
1280
0
    dy = by - ay;
1281
0
  }
1282
  /* Then vertically... */
1283
5.53k
  if (dy == 0)
1284
218
  {
1285
    /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
1286
218
  }
1287
5.31k
  else if ((d = by - s->rect.y0) < 0)
1288
0
  {
1289
0
    old_bx = bx;
1290
0
    old_by = by;
1291
0
    by = s->rect.y0;  /* d < 0, dy < 0 */
1292
0
    goto b_moved_vertically;
1293
0
  }
1294
5.31k
  else if (d > 0 && (d = (by - s->rect.y1)) > 0)
1295
0
  {
1296
0
    float t;
1297
0
    old_bx = bx;
1298
0
    old_by = by;
1299
0
    by = s->rect.y1;  /* d > 0, dy > 0 */
1300
0
b_moved_vertically: /* d and dy have the same sign */
1301
0
    assert((d > 0 && dy > 0) || (d < 0 && dy < 0));
1302
0
    assert(dy != 0);
1303
0
    bx = advance(bx, ax, d, dy);
1304
0
    t = total * d/dy;
1305
0
    tail += t;
1306
0
    total -= t;
1307
0
    dx = bx - ax;
1308
0
    dy = by - ay;
1309
0
  }
1310
1311
12.4k
  while (total - used > s->dash_list[s->offset] - s->phase)
1312
6.94k
  {
1313
6.94k
    used += s->dash_list[s->offset] - s->phase;
1314
6.94k
    ratio = used / total;
1315
6.94k
    mx = ax + ratio * dx;
1316
6.94k
    my = ay + ratio * dy;
1317
1318
6.94k
    if (s->toggle)
1319
3.54k
    {
1320
3.54k
      fz_stroke_lineto_aux(ctx, s, mx, my, from_bezier, dx, dy);
1321
3.54k
    }
1322
3.40k
    else
1323
3.40k
    {
1324
3.40k
      fz_stroke_flush(ctx, s, s->cap, dash_cap);
1325
3.40k
      s->cap = dash_cap;
1326
3.40k
      fz_stroke_moveto(ctx, s, mx, my);
1327
3.40k
    }
1328
1329
6.94k
    s->toggle = !s->toggle;
1330
6.94k
    s->phase = 0;
1331
6.94k
    s->offset ++;
1332
6.94k
    if (s->offset == s->dash_len)
1333
3.40k
      s->offset = 0;
1334
6.94k
  }
1335
1336
5.53k
  s->phase += total - used;
1337
1338
5.53k
  if (tail == 0.0f)
1339
5.53k
  {
1340
5.53k
    s->dash_cur.x = bx;
1341
5.53k
    s->dash_cur.y = by;
1342
1343
5.53k
    if (s->toggle)
1344
2.68k
    {
1345
2.68k
      fz_stroke_lineto_aux(ctx, s, bx, by, from_bezier, dx, dy);
1346
2.68k
    }
1347
5.53k
  }
1348
0
  else
1349
0
  {
1350
14
adjust_for_tail:
1351
14
    s->dash_cur.x = old_bx;
1352
14
    s->dash_cur.y = old_by;
1353
    /* Update the position in the dash array */
1354
14
    if (s->toggle)
1355
8
    {
1356
8
      fz_stroke_lineto_aux(ctx, s, old_bx, old_by, from_bezier, dx, dy);
1357
8
    }
1358
6
    else
1359
6
    {
1360
6
      fz_stroke_flush(ctx, s, s->cap, dash_cap);
1361
6
      s->cap = dash_cap;
1362
6
      fz_stroke_moveto(ctx, s, old_bx, old_by);
1363
6
    }
1364
14
    tail += s->phase;
1365
14
    n = trunc(tail/s->dash_total);
1366
14
    tail -= n*s->dash_total;
1367
14
    if (is_odd(n) & s->dash_len & 1)
1368
0
      s->toggle = !s->toggle;
1369
20
    while (tail > s->dash_list[s->offset])
1370
6
    {
1371
6
      tail -= s->dash_list[s->offset];
1372
6
      s->offset++;
1373
6
      if (s->offset == s->dash_len)
1374
2
        s->offset = 0;
1375
6
      s->toggle = !s->toggle;
1376
6
    }
1377
14
    if (s->toggle)
1378
6
    {
1379
6
      fz_stroke_lineto_aux(ctx, s, old_bx, old_by, from_bezier, dx, dy);
1380
6
    }
1381
8
    else
1382
8
    {
1383
8
      fz_stroke_flush(ctx, s, s->cap, dash_cap);
1384
8
      s->cap = dash_cap;
1385
8
      fz_stroke_moveto(ctx, s, old_bx, old_by);
1386
8
    }
1387
14
    s->phase = tail;
1388
14
  }
1389
5.53k
}
1390
1391
static void
1392
fz_dash_bezier(fz_context *ctx, struct sctx *s,
1393
  float xa, float ya,
1394
  float xb, float yb,
1395
  float xc, float yc,
1396
  float xd, float yd, int depth)
1397
9.67k
{
1398
9.67k
  float dmax;
1399
9.67k
  float xab, yab;
1400
9.67k
  float xbc, ybc;
1401
9.67k
  float xcd, ycd;
1402
9.67k
  float xabc, yabc;
1403
9.67k
  float xbcd, ybcd;
1404
9.67k
  float xabcd, yabcd;
1405
1406
  /* termination check */
1407
9.67k
  dmax = fz_abs(xa - xb);
1408
9.67k
  dmax = fz_max(dmax, fz_abs(ya - yb));
1409
9.67k
  dmax = fz_max(dmax, fz_abs(xd - xc));
1410
9.67k
  dmax = fz_max(dmax, fz_abs(yd - yc));
1411
9.67k
  if (dmax < s->flatness || depth >= MAX_DEPTH)
1412
5.05k
  {
1413
5.05k
    fz_dash_lineto(ctx, s, xd, yd, 1);
1414
5.05k
    return;
1415
5.05k
  }
1416
1417
4.61k
  xab = xa + xb;
1418
4.61k
  yab = ya + yb;
1419
4.61k
  xbc = xb + xc;
1420
4.61k
  ybc = yb + yc;
1421
4.61k
  xcd = xc + xd;
1422
4.61k
  ycd = yc + yd;
1423
1424
4.61k
  xabc = xab + xbc;
1425
4.61k
  yabc = yab + ybc;
1426
4.61k
  xbcd = xbc + xcd;
1427
4.61k
  ybcd = ybc + ycd;
1428
1429
4.61k
  xabcd = xabc + xbcd;
1430
4.61k
  yabcd = yabc + ybcd;
1431
1432
4.61k
  xab *= 0.5f; yab *= 0.5f;
1433
  /* xbc *= 0.5f; ybc *= 0.5f; */
1434
4.61k
  xcd *= 0.5f; ycd *= 0.5f;
1435
1436
4.61k
  xabc *= 0.25f; yabc *= 0.25f;
1437
4.61k
  xbcd *= 0.25f; ybcd *= 0.25f;
1438
1439
4.61k
  xabcd *= 0.125f; yabcd *= 0.125f;
1440
1441
4.61k
  fz_dash_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
1442
4.61k
  fz_dash_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
1443
4.61k
}
1444
1445
static void
1446
fz_dash_quad(fz_context *ctx, struct sctx *s,
1447
  float xa, float ya,
1448
  float xb, float yb,
1449
  float xc, float yc, int depth)
1450
0
{
1451
0
  float dmax;
1452
0
  float xab, yab;
1453
0
  float xbc, ybc;
1454
0
  float xabc, yabc;
1455
1456
  /* termination check */
1457
0
  dmax = fz_abs(xa - xb);
1458
0
  dmax = fz_max(dmax, fz_abs(ya - yb));
1459
0
  dmax = fz_max(dmax, fz_abs(xc - xb));
1460
0
  dmax = fz_max(dmax, fz_abs(yc - yb));
1461
0
  if (dmax < s->flatness || depth >= MAX_DEPTH)
1462
0
  {
1463
0
    fz_dash_lineto(ctx, s, xc, yc, 1);
1464
0
    return;
1465
0
  }
1466
1467
0
  xab = xa + xb;
1468
0
  yab = ya + yb;
1469
0
  xbc = xb + xc;
1470
0
  ybc = yb + yc;
1471
1472
0
  xabc = xab + xbc;
1473
0
  yabc = yab + ybc;
1474
1475
0
  xab *= 0.5f; yab *= 0.5f;
1476
0
  xbc *= 0.5f; ybc *= 0.5f;
1477
1478
0
  xabc *= 0.25f; yabc *= 0.25f;
1479
1480
0
  fz_dash_quad(ctx, s, xa, ya, xab, yab, xabc, yabc, depth + 1);
1481
0
  fz_dash_quad(ctx, s, xabc, yabc, xbc, ybc, xc, yc, depth + 1);
1482
0
}
1483
1484
static void
1485
dash_moveto(fz_context *ctx, void *s_, float x, float y)
1486
248
{
1487
248
  sctx *s = (sctx *)s_;
1488
1489
248
  fz_dash_moveto(ctx, s, x, y);
1490
248
  s->dash_beg.x = s->cur.x = x;
1491
248
  s->dash_beg.y = s->cur.y = y;
1492
248
}
1493
1494
static void
1495
dash_lineto(fz_context *ctx, void *s_, float x, float y)
1496
493
{
1497
493
  sctx *s = (sctx *)s_;
1498
1499
493
  fz_dash_lineto(ctx, s, x, y, 0);
1500
493
  s->cur.x = x;
1501
493
  s->cur.y = y;
1502
493
}
1503
1504
static void
1505
dash_curveto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2, float x3, float y3)
1506
438
{
1507
438
  sctx *s = (sctx *)s_;
1508
1509
438
  fz_dash_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
1510
438
  s->cur.x = x3;
1511
438
  s->cur.y = y3;
1512
438
}
1513
1514
static void
1515
dash_quadto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2)
1516
0
{
1517
0
  sctx *s = (sctx *)s_;
1518
1519
0
  fz_dash_quad(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, 0);
1520
0
  s->cur.x = x2;
1521
0
  s->cur.y = y2;
1522
0
}
1523
1524
static void
1525
dash_close(fz_context *ctx, void *s_)
1526
1
{
1527
1
  sctx *s = (sctx *)s_;
1528
1529
1
  fz_dash_lineto(ctx, s, s->dash_beg.x, s->dash_beg.y, 0);
1530
1
  s->cur.x = s->dash_beg.x;
1531
1
  s->cur.y = s->dash_beg.y;
1532
1
}
1533
1534
static const fz_path_walker dash_proc =
1535
{
1536
  dash_moveto,
1537
  dash_lineto,
1538
  dash_curveto,
1539
  dash_close,
1540
  dash_quadto
1541
};
1542
1543
static int
1544
do_flatten_stroke(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth)
1545
11.6k
{
1546
11.6k
  struct sctx s;
1547
11.6k
  const fz_path_walker *proc = &stroke_proc;
1548
1549
11.6k
  s.stroke = stroke;
1550
11.6k
  s.rast = rast;
1551
11.6k
  s.ctm = ctm;
1552
11.6k
  s.flatness = flatness;
1553
11.6k
  s.linejoin = stroke->linejoin;
1554
11.6k
  s.linewidth = linewidth * 0.5f; /* hairlines use a different value from the path value */
1555
11.6k
  s.miterlimit = stroke->miterlimit;
1556
11.6k
  s.sn = 0;
1557
11.6k
  s.not_just_moves = 0;
1558
11.6k
  s.toggle = 0;
1559
11.6k
  s.offset = 0;
1560
11.6k
  s.phase = 0;
1561
11.6k
  s.dirn_x = 0;
1562
11.6k
  s.dirn_y = 0;
1563
1564
11.6k
  s.cap = stroke->start_cap;
1565
1566
11.6k
  s.dash_list = NULL;
1567
11.6k
  s.dash_len = stroke->dash_len;
1568
11.6k
  if (s.dash_len > 0)
1569
247
  {
1570
247
    int i;
1571
247
    fz_matrix inv;
1572
247
    float max_expand;
1573
247
    const float *list = stroke->dash_list;
1574
1575
247
    s.dash_total = 0;
1576
741
    for (i = 0; i < s.dash_len; i++)
1577
494
      s.dash_total += list[i];
1578
247
    if (s.dash_total == 0)
1579
0
      return 1;
1580
1581
247
    s.rect = fz_scissor_rasterizer(ctx, rast);
1582
247
    if (fz_try_invert_matrix(&inv, ctm))
1583
0
      return 1;
1584
247
    s.rect = fz_transform_rect(s.rect, inv);
1585
247
    s.rect.x0 -= linewidth;
1586
247
    s.rect.x1 += linewidth;
1587
247
    s.rect.y0 -= linewidth;
1588
247
    s.rect.y1 += linewidth;
1589
1590
247
    max_expand = fz_matrix_max_expansion(ctm);
1591
247
    if (s.dash_total >= 0.01f && s.dash_total * max_expand >= 0.5f)
1592
247
    {
1593
247
      proc = &dash_proc;
1594
247
      s.dash_phase = fmodf(stroke->dash_phase, s.dash_total);
1595
247
      s.dash_list = list;
1596
247
    }
1597
247
  }
1598
1599
11.6k
  s.cur.x = s.cur.y = 0;
1600
11.6k
  fz_walk_path(ctx, path, proc, &s);
1601
11.6k
  fz_stroke_flush(ctx, &s, s.cap, stroke->end_cap);
1602
1603
11.6k
  return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
1604
11.6k
}
1605
1606
int
1607
fz_flatten_stroke_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth, fz_irect scissor, fz_irect *bbox)
1608
11.6k
{
1609
11.6k
  int empty;
1610
11.6k
  fz_irect local_bbox;
1611
11.6k
  if (!bbox)
1612
6
    bbox = &local_bbox;
1613
1614
11.6k
  if (fz_reset_rasterizer(ctx, rast, scissor))
1615
0
  {
1616
0
    empty = do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth);
1617
0
    if (empty)
1618
0
      return *bbox = fz_empty_irect, 1;
1619
0
    fz_postindex_rasterizer(ctx, rast);
1620
0
  }
1621
1622
11.6k
  empty = do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth);
1623
11.6k
  if (empty)
1624
4.02k
    return *bbox = fz_empty_irect, 1;
1625
1626
7.65k
  *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
1627
7.65k
  return fz_is_empty_irect(*bbox);
1628
11.6k
}