Coverage Report

Created: 2026-08-14 07:34

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.24M
#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
3.05M
{
73
3.05M
  float tx0 = ctm.a * x0 + ctm.c * y0 + ctm.e;
74
3.05M
  float ty0 = ctm.b * x0 + ctm.d * y0 + ctm.f;
75
3.05M
  float tx1 = ctm.a * x1 + ctm.c * y1 + ctm.e;
76
3.05M
  float ty1 = ctm.b * x1 + ctm.d * y1 + ctm.f;
77
3.05M
  fz_insert_rasterizer(ctx, rast, tx0, ty0, tx1, ty1, 0);
78
3.05M
}
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.35M
{
87
5.35M
  float dmax;
88
5.35M
  float xab, yab;
89
5.35M
  float xbc, ybc;
90
5.35M
  float xcd, ycd;
91
5.35M
  float xabc, yabc;
92
5.35M
  float xbcd, ybcd;
93
5.35M
  float xabcd, yabcd;
94
95
  /* termination check */
96
5.35M
  dmax = fz_abs(xa - xb);
97
5.35M
  dmax = fz_max(dmax, fz_abs(ya - yb));
98
5.35M
  dmax = fz_max(dmax, fz_abs(xd - xc));
99
5.35M
  dmax = fz_max(dmax, fz_abs(yd - yc));
100
5.35M
  if (dmax < flatness || depth >= MAX_DEPTH)
101
2.93M
  {
102
2.93M
    line(ctx, rast, ctm, xa, ya, xd, yd);
103
2.93M
    return;
104
2.93M
  }
105
106
2.41M
  xab = xa + xb;
107
2.41M
  yab = ya + yb;
108
2.41M
  xbc = xb + xc;
109
2.41M
  ybc = yb + yc;
110
2.41M
  xcd = xc + xd;
111
2.41M
  ycd = yc + yd;
112
113
2.41M
  xabc = xab + xbc;
114
2.41M
  yabc = yab + ybc;
115
2.41M
  xbcd = xbc + xcd;
116
2.41M
  ybcd = ybc + ycd;
117
118
2.41M
  xabcd = xabc + xbcd;
119
2.41M
  yabcd = yabc + ybcd;
120
121
2.41M
  xab *= 0.5f; yab *= 0.5f;
122
  /* xbc *= 0.5f; ybc *= 0.5f; */
123
2.41M
  xcd *= 0.5f; ycd *= 0.5f;
124
125
2.41M
  xabc *= 0.25f; yabc *= 0.25f;
126
2.41M
  xbcd *= 0.25f; ybcd *= 0.25f;
127
128
2.41M
  xabcd *= 0.125f; yabcd *= 0.125f;
129
130
2.41M
  bezier(ctx, rast, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
131
2.41M
  bezier(ctx, rast, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
132
2.41M
}
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
135k
{
187
135k
  flatten_arg *arg = (flatten_arg *)arg_;
188
189
  /* implicit closepath before moveto */
190
135k
  if (arg->c.x != arg->b.x || arg->c.y != arg->b.y)
191
472
    line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y);
192
135k
  arg->c.x = arg->b.x = x;
193
135k
  arg->c.y = arg->b.y = y;
194
195
135k
  fz_gap_rasterizer(ctx, arg->rast);
196
135k
}
197
198
static void
199
flatten_lineto(fz_context *ctx, void *arg_, float x, float y)
200
102k
{
201
102k
  flatten_arg *arg = (flatten_arg *)arg_;
202
203
102k
  line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, x, y);
204
102k
  arg->c.x = x;
205
102k
  arg->c.y = y;
206
102k
}
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
513k
{
211
513k
  flatten_arg *arg = (flatten_arg *)arg_;
212
213
513k
  bezier(ctx, arg->rast, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, x3, y3, 0);
214
513k
  arg->c.x = x3;
215
513k
  arg->c.y = y3;
216
513k
}
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
22.9k
{
231
22.9k
  flatten_arg *arg = (flatten_arg *)arg_;
232
233
22.9k
  line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y);
234
22.9k
  arg->c.x = arg->b.x;
235
22.9k
  arg->c.y = arg->b.y;
236
22.9k
}
237
238
static void
239
flatten_rectto(fz_context *ctx, void *arg_, float x0, float y0, float x1, float y1)
240
37.6k
{
241
37.6k
  flatten_arg *arg = (flatten_arg *)arg_;
242
37.6k
  fz_matrix ctm = arg->ctm;
243
244
37.6k
  flatten_moveto(ctx, arg_, x0, y0);
245
246
37.6k
  if (fz_antidropout_rasterizer(ctx, arg->rast))
247
37.6k
  {
248
    /* In the case where we have an axis aligned rectangle, do some
249
     * horrid antidropout stuff. */
250
37.6k
    if (ctm.b == 0 && ctm.c == 0)
251
35.7k
    {
252
35.7k
      float tx0 = ctm.a * x0 + ctm.e;
253
35.7k
      float ty0 = ctm.d * y0 + ctm.f;
254
35.7k
      float tx1 = ctm.a * x1 + ctm.e;
255
35.7k
      float ty1 = ctm.d * y1 + ctm.f;
256
35.7k
      fz_insert_rasterizer_rect(ctx, arg->rast, tx0, ty0, tx1, ty1);
257
35.7k
      return;
258
35.7k
    }
259
1.96k
    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
37.6k
  }
269
270
1.96k
  flatten_lineto(ctx, arg_, x1, y0);
271
1.96k
  flatten_lineto(ctx, arg_, x1, y1);
272
1.96k
  flatten_lineto(ctx, arg_, x0, y1);
273
1.96k
  flatten_close(ctx, arg_);
274
1.96k
}
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
100k
{
291
100k
  flatten_arg arg;
292
293
100k
  arg.rast = rast;
294
100k
  arg.ctm = ctm;
295
100k
  arg.flatness = flatness;
296
100k
  arg.b.x = arg.b.y = arg.c.x = arg.c.y = 0;
297
298
100k
  fz_walk_path(ctx, path, &flatten_proc, &arg);
299
100k
  if (arg.c.x != arg.b.x || arg.c.y != arg.b.y)
300
576
    line(ctx, rast, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y);
301
302
100k
  fz_gap_rasterizer(ctx, rast);
303
304
100k
  return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
305
100k
}
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
100k
{
310
100k
  int empty;
311
100k
  fz_irect local_bbox;
312
100k
  if (!bbox)
313
286
    bbox = &local_bbox;
314
315
  /* If we're given an empty scissor, sanitize it. This makes life easier
316
   * down the line. */
317
100k
  if (fz_is_empty_irect(scissor))
318
15.5k
    scissor.x1 = scissor.x0, scissor.y1 = scissor.y0;
319
320
100k
  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
100k
  empty = do_flatten_fill(ctx, rast, path, ctm, flatness);
329
100k
  if (empty)
330
16.1k
    return *bbox = fz_empty_irect, 1;
331
332
84.3k
  *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
333
84.3k
  return fz_is_empty_irect(*bbox);
334
100k
}
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
1.98M
{
371
1.98M
  float tx0 = s->ctm.a * x0 + s->ctm.c * y0 + s->ctm.e;
372
1.98M
  float ty0 = s->ctm.b * x0 + s->ctm.d * y0 + s->ctm.f;
373
1.98M
  float tx1 = s->ctm.a * x1 + s->ctm.c * y1 + s->ctm.e;
374
1.98M
  float ty1 = s->ctm.b * x1 + s->ctm.d * y1 + s->ctm.f;
375
376
1.98M
  fz_insert_rasterizer(ctx, s->rast, tx0, ty0, tx1, ty1, rev);
377
1.98M
}
378
379
static void
380
fz_add_horiz_rect(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1)
381
49.4k
{
382
49.4k
  if (fz_antidropout_rasterizer(ctx, s->rast)) {
383
49.4k
    if (s->ctm.b == 0 && s->ctm.c == 0)
384
49.4k
    {
385
49.4k
      float tx0 = s->ctm.a * x0 + s->ctm.e;
386
49.4k
      float ty0 = s->ctm.d * y0 + s->ctm.f;
387
49.4k
      float tx1 = s->ctm.a * x1 + s->ctm.e;
388
49.4k
      float ty1 = s->ctm.d * y1 + s->ctm.f;
389
49.4k
      fz_insert_rasterizer_rect(ctx, s->rast, tx1, ty1, tx0, ty0);
390
49.4k
      return;
391
49.4k
    }
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
49.4k
  }
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
66.7k
{
443
66.7k
  float th0, th1, r;
444
66.7k
  float theta;
445
66.7k
  float ox, oy, nx, ny;
446
66.7k
  int n, i;
447
448
66.7k
  r = fabsf(s->linewidth);
449
66.7k
  theta = 2 * FZ_SQRT2 * sqrtf(s->flatness / r);
450
66.7k
  th0 = atan2f(y0, x0);
451
66.7k
  th1 = atan2f(y1, x1);
452
453
66.7k
  if (r > 0)
454
66.7k
  {
455
66.7k
    if (th0 < th1)
456
4.89k
      th0 += FZ_PI * 2;
457
66.7k
    n = ceilf((th0 - th1) / theta);
458
66.7k
  }
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
66.7k
  if (rev)
467
53.4k
  {
468
53.4k
    ox = x1;
469
53.4k
    oy = y1;
470
53.4k
    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
53.4k
    fz_add_line(ctx, s, xc + x0, yc + y0, xc + ox, yc + oy, rev);
481
53.4k
  }
482
13.2k
  else
483
13.2k
  {
484
13.2k
    ox = x0;
485
13.2k
    oy = y0;
486
13.3k
    for (i = 1; i < n; i++)
487
43
    {
488
43
      theta = th0 + (th1 - th0) * i / n;
489
43
      nx = cosf(theta) * r;
490
43
      ny = sinf(theta) * r;
491
43
      fz_add_line(ctx, s, xc + ox, yc + oy, xc + nx, yc + ny, rev);
492
43
      ox = nx;
493
43
      oy = ny;
494
43
    }
495
496
13.2k
    fz_add_line(ctx, s, xc + ox, yc + oy, xc + x1, yc + y1, rev);
497
13.2k
  }
498
66.7k
}
499
500
/* FLT_TINY * FLT_TINY is approximately FLT_EPSILON */
501
458k
#define FLT_TINY 3.4e-4F
502
static int find_normal_vectors(float dx, float dy, float linewidth, float *dlx, float *dly)
503
1.35M
{
504
1.35M
  if (dx == 0)
505
29.9k
  {
506
29.9k
    if (dy < FLT_TINY && dy > - FLT_TINY)
507
346
      goto tiny;
508
29.5k
    else if (dy > 0)
509
15.0k
      *dlx = linewidth;
510
14.5k
    else
511
14.5k
      *dlx = -linewidth;
512
29.5k
    *dly = 0;
513
29.5k
  }
514
1.32M
  else if (dy == 0)
515
141k
  {
516
141k
    if (dx < FLT_TINY && dx > - FLT_TINY)
517
0
      goto tiny;
518
141k
    else if (dx > 0)
519
40.1k
      *dly = -linewidth;
520
101k
    else
521
101k
      *dly = linewidth;
522
141k
    *dlx = 0;
523
141k
  }
524
1.18M
  else
525
1.18M
  {
526
1.18M
    float sq = dx * dx + dy * dy;
527
1.18M
    float scale;
528
529
1.18M
    if (sq < FLT_EPSILON)
530
0
      goto tiny;
531
1.18M
    scale = linewidth / sqrtf(sq);
532
1.18M
    *dlx = dy * scale;
533
1.18M
    *dly = -dx * scale;
534
1.18M
  }
535
1.35M
  return 0;
536
346
tiny:
537
346
  *dlx = 0;
538
346
  *dly = 0;
539
346
  return 1;
540
1.35M
}
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
447k
{
545
447k
  float miterlimit = s->miterlimit;
546
447k
  float linewidth = s->linewidth;
547
447k
  fz_linejoin linejoin = s->linejoin;
548
447k
  float dx0, dy0;
549
447k
  float dx1, dy1;
550
447k
  float dlx0, dly0;
551
447k
  float dlx1, dly1;
552
447k
  float dmx, dmy;
553
447k
  float dmr2;
554
447k
  float scale;
555
447k
  float cross;
556
447k
  int rev = 0;
557
558
447k
  dx0 = bx - ax;
559
447k
  dy0 = by - ay;
560
561
447k
  dx1 = cx - bx;
562
447k
  dy1 = cy - by;
563
564
447k
  cross = dx1 * dy0 - dx0 * dy1;
565
  /* Ensure that cross >= 0 */
566
447k
  if (cross < 0)
567
189k
  {
568
189k
    float tmp;
569
189k
    tmp = dx1; dx1 = -dx0; dx0 = -tmp;
570
189k
    tmp = dy1; dy1 = -dy0; dy0 = -tmp;
571
189k
    cross = -cross;
572
189k
    rev = !rev;
573
189k
  }
574
575
447k
  if (find_normal_vectors(dx0, dy0, linewidth, &dlx0, &dly0))
576
0
    linejoin = FZ_LINEJOIN_BEVEL;
577
578
447k
  if (find_normal_vectors(dx1, dy1, linewidth, &dlx1, &dly1))
579
0
    linejoin = FZ_LINEJOIN_BEVEL;
580
581
447k
  dmx = (dlx0 + dlx1) * 0.5f;
582
447k
  dmy = (dly0 + dly1) * 0.5f;
583
447k
  dmr2 = dmx * dmx + dmy * dmy;
584
585
447k
  if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0)
586
152k
    linejoin = FZ_LINEJOIN_BEVEL;
587
588
  /* XPS miter joins are clipped at miterlength, rather than simply
589
   * being converted to bevelled joins. */
590
447k
  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
447k
  else if (linejoin == FZ_LINEJOIN_MITER)
598
228k
    if (dmr2 * miterlimit * miterlimit < linewidth * linewidth)
599
2.12k
      linejoin = FZ_LINEJOIN_BEVEL;
600
601
447k
  if (join_under)
602
422k
  {
603
422k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx + dlx0, by + dly0, !rev);
604
422k
  }
605
24.9k
  else if (rev)
606
17.3k
  {
607
17.3k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 0);
608
17.3k
    fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 0);
609
17.3k
  }
610
7.58k
  else
611
7.58k
  {
612
7.58k
    fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 1);
613
7.58k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 1);
614
7.58k
  }
615
616
447k
  switch (linejoin)
617
447k
  {
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
226k
  case FZ_LINEJOIN_MITER:
646
226k
    scale = linewidth * linewidth / dmr2;
647
226k
    dmx *= scale;
648
226k
    dmy *= scale;
649
650
226k
    if (rev)
651
82.9k
    {
652
82.9k
      fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 1);
653
82.9k
      fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 1);
654
82.9k
    }
655
143k
    else
656
143k
    {
657
143k
      fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 0);
658
143k
      fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 0);
659
143k
    }
660
226k
    break;
661
662
154k
  case FZ_LINEJOIN_BEVEL:
663
154k
    fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dlx1, by - dly1, rev);
664
154k
    break;
665
666
66.7k
  case FZ_LINEJOIN_ROUND:
667
66.7k
    fz_add_arc(ctx, s, bx, by, -dlx0, -dly0, -dlx1, -dly1, rev);
668
66.7k
    break;
669
670
0
  default:
671
0
    assert("Invalid line join" == NULL);
672
447k
  }
673
447k
}
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
19.8k
{
678
19.8k
  float flatness = s->flatness;
679
19.8k
  float linewidth = s->linewidth;
680
681
19.8k
  switch (linecap)
682
19.8k
  {
683
11.1k
  case FZ_LINECAP_BUTT:
684
11.1k
    fz_add_line(ctx, s, bx - dlx, by - dly, bx + dlx, by + dly, rev);
685
11.1k
    break;
686
687
8.70k
  case FZ_LINECAP_ROUND:
688
8.70k
  {
689
8.70k
    int i;
690
8.70k
    int n = ceilf(FZ_PI / (2.0f * FZ_SQRT2 * sqrtf(flatness / linewidth)));
691
8.70k
    float ox = bx - dlx;
692
8.70k
    float oy = by - dly;
693
13.2k
    for (i = 1; i < n; i++)
694
4.59k
    {
695
4.59k
      float theta = FZ_PI * i / n;
696
4.59k
      float cth = cosf(theta);
697
4.59k
      float sth = sinf(theta);
698
4.59k
      float nx = bx - dlx * cth - dly * sth;
699
4.59k
      float ny = by - dly * cth + dlx * sth;
700
4.59k
      fz_add_line(ctx, s, ox, oy, nx, ny, rev);
701
4.59k
      ox = nx;
702
4.59k
      oy = ny;
703
4.59k
    }
704
8.70k
    fz_add_line(ctx, s, ox, oy, bx + dlx, by + dly, rev);
705
8.70k
    break;
706
0
  }
707
708
12
  case FZ_LINECAP_SQUARE:
709
12
    fz_add_line(ctx, s, bx - dlx, by - dly,
710
12
      bx - dlx - dly, by - dly + dlx, rev);
711
12
    fz_add_line(ctx, s, bx - dlx - dly, by - dly + dlx,
712
12
      bx + dlx - dly, by + dly + dlx, rev);
713
12
    fz_add_line(ctx, s, bx + dlx - dly, by + dly + dlx,
714
12
      bx + dlx, by + dly, rev);
715
12
    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
19.8k
  }
729
19.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
19.8k
{
734
19.8k
  float linewidth = s->linewidth;
735
19.8k
  float dx = bx - ax;
736
19.8k
  float dy = by - ay;
737
738
19.8k
  float scale = linewidth / sqrtf(dx * dx + dy * dy);
739
19.8k
  float dlx = dy * scale;
740
19.8k
  float dly = -dx * scale;
741
742
19.8k
  do_linecap(ctx, s, bx, by, linecap, rev, dlx, dly);
743
19.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
10
{
748
10
  float linewidth = s->linewidth;
749
10
  float dx = rev ? -s->dirn_x : s->dirn_x;
750
10
  float dy = rev ? -s->dirn_y : s->dirn_y;
751
10
  float scale, dlx, dly;
752
753
10
  if (dx == 0 && dy == 0)
754
10
    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
24.1k
{
793
24.1k
  if (s->sn == 1)
794
9.91k
  {
795
9.91k
    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
9.91k
    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
9.91k
  }
798
14.1k
  else if (s->not_just_moves)
799
5
  {
800
5
    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
5
    else
805
5
    {
806
5
      fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 2);
807
5
      fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 0);
808
5
    }
809
5
  }
810
811
24.1k
  fz_gap_rasterizer(ctx, s->rast);
812
24.1k
}
813
814
static void
815
fz_stroke_moveto(fz_context *ctx, void *s_, float x, float y)
816
16.6k
{
817
16.6k
  struct sctx *s = (struct sctx *)s_;
818
819
16.6k
  s->seg[0].x = s->beg[0].x = x;
820
16.6k
  s->seg[0].y = s->beg[0].y = y;
821
16.6k
  s->sn = 0;
822
16.6k
  s->not_just_moves = 0;
823
16.6k
  s->from_bezier = 0;
824
16.6k
  s->dirn_x = 0;
825
16.6k
  s->dirn_y = 0;
826
16.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
457k
{
831
457k
  float ox = s->seg[s->sn].x;
832
457k
  float oy = s->seg[s->sn].y;
833
457k
  float dx = x - ox;
834
457k
  float dy = y - oy;
835
457k
  float dlx, dly;
836
837
457k
  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
457k
  s->dirn_x = dirn_x;
842
457k
  s->dirn_y = dirn_y;
843
844
  /* We calculate the normal vectors from the delta that we have just moved. */
845
457k
  if (find_normal_vectors(dx, dy, s->linewidth, &dlx, &dly))
846
346
  {
847
346
    return;
848
346
  }
849
850
457k
  if (s->sn == 1)
851
441k
    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
457k
#if 1
854
457k
  if (0 && dx == 0)
855
0
  {
856
0
    fz_add_vert_rect(ctx, s, ox - dlx, oy, x + dlx, y);
857
0
  }
858
457k
  else if (dy == 0)
859
49.4k
  {
860
49.4k
    fz_add_horiz_rect(ctx, s, ox, oy - dly, x, y + dly);
861
49.4k
  }
862
408k
  else
863
408k
#endif
864
408k
  {
865
866
408k
    fz_add_line(ctx, s, ox - dlx, oy - dly, x - dlx, y - dly, 0);
867
408k
    fz_add_line(ctx, s, x + dlx, y + dly, ox + dlx, oy + dly, 1);
868
408k
  }
869
870
457k
  if (s->sn)
871
441k
  {
872
441k
    s->seg[0] = s->seg[1];
873
441k
    s->seg[1].x = x;
874
441k
    s->seg[1].y = y;
875
441k
  }
876
15.7k
  else
877
15.7k
  {
878
15.7k
    s->seg[1].x = s->beg[1].x = x;
879
15.7k
    s->seg[1].y = s->beg[1].y = y;
880
15.7k
    s->sn = 1;
881
15.7k
  }
882
457k
  s->from_bezier = from_bezier;
883
457k
}
884
885
static void
886
fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier)
887
451k
{
888
451k
  float ox = s->seg[s->sn].x;
889
451k
  float oy = s->seg[s->sn].y;
890
451k
  float dx = x - ox;
891
451k
  float dy = y - oy;
892
451k
  fz_stroke_lineto_aux(ctx, s, x, y, from_bezier, dx, dy);
893
451k
}
894
895
static void
896
fz_stroke_closepath(fz_context *ctx, sctx *s)
897
5.84k
{
898
5.84k
  if (s->sn == 1)
899
5.79k
  {
900
5.79k
    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
5.79k
    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
5.79k
  }
911
47
  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
5.84k
  s->seg[0] = s->beg[0];
917
5.84k
  s->sn = 0;
918
5.84k
  s->not_just_moves = 0;
919
5.84k
  s->from_bezier = 0;
920
5.84k
  s->dirn_x = 0;
921
5.84k
  s->dirn_y = 0;
922
923
5.84k
  fz_gap_rasterizer(ctx, s->rast);
924
5.84k
}
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
836k
{
933
836k
  float dmax;
934
836k
  float xab, yab;
935
836k
  float xbc, ybc;
936
836k
  float xcd, ycd;
937
836k
  float xabc, yabc;
938
836k
  float xbcd, ybcd;
939
836k
  float xabcd, yabcd;
940
941
  /* termination check */
942
836k
  dmax = fz_abs(xa - xb);
943
836k
  dmax = fz_max(dmax, fz_abs(ya - yb));
944
836k
  dmax = fz_max(dmax, fz_abs(xd - xc));
945
836k
  dmax = fz_max(dmax, fz_abs(yd - yc));
946
836k
  if (dmax < s->flatness || depth >= MAX_DEPTH)
947
423k
  {
948
423k
    fz_stroke_lineto(ctx, s, xd, yd, 1);
949
423k
    return;
950
423k
  }
951
952
412k
  xab = xa + xb;
953
412k
  yab = ya + yb;
954
412k
  xbc = xb + xc;
955
412k
  ybc = yb + yc;
956
412k
  xcd = xc + xd;
957
412k
  ycd = yc + yd;
958
959
412k
  xabc = xab + xbc;
960
412k
  yabc = yab + ybc;
961
412k
  xbcd = xbc + xcd;
962
412k
  ybcd = ybc + ycd;
963
964
412k
  xabcd = xabc + xbcd;
965
412k
  yabcd = yabc + ybcd;
966
967
412k
  xab *= 0.5f; yab *= 0.5f;
968
  /* xbc *= 0.5f; ybc *= 0.5f; */
969
412k
  xcd *= 0.5f; ycd *= 0.5f;
970
971
412k
  xabc *= 0.25f; yabc *= 0.25f;
972
412k
  xbcd *= 0.25f; ybcd *= 0.25f;
973
974
412k
  xabcd *= 0.125f; yabcd *= 0.125f;
975
976
412k
  fz_stroke_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
977
412k
  fz_stroke_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
978
412k
}
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.2k
{
1022
13.2k
  sctx *s = (sctx *)s_;
1023
1024
13.2k
  fz_stroke_flush(ctx, s, s->stroke->start_cap, s->stroke->end_cap);
1025
13.2k
  fz_stroke_moveto(ctx, s, x, y);
1026
13.2k
  s->cur.x = x;
1027
13.2k
  s->cur.y = y;
1028
13.2k
}
1029
1030
static void
1031
stroke_lineto(fz_context *ctx, void *s_, float x, float y)
1032
22.2k
{
1033
22.2k
  sctx *s = (sctx *)s_;
1034
1035
22.2k
  fz_stroke_lineto(ctx, s, x, y, 0);
1036
22.2k
  s->cur.x = x;
1037
22.2k
  s->cur.y = y;
1038
22.2k
}
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.4k
{
1043
11.4k
  sctx *s = (sctx *)s_;
1044
1045
11.4k
  fz_stroke_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
1046
11.4k
  s->cur.x = x3;
1047
11.4k
  s->cur.y = y3;
1048
11.4k
}
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
5.84k
{
1063
5.84k
  sctx *s = (sctx *)s_;
1064
1065
5.84k
  fz_stroke_closepath(ctx, s);
1066
5.84k
}
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
236
{
1080
236
  s->toggle = 1;
1081
236
  s->offset = 0;
1082
236
  s->phase = s->dash_phase;
1083
1084
236
  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
236
  s->dash_cur.x = x;
1094
236
  s->dash_cur.y = y;
1095
1096
236
  if (s->toggle)
1097
236
  {
1098
236
    fz_stroke_flush(ctx, s, s->cap, s->stroke->end_cap);
1099
236
    s->cap = s->stroke->start_cap;
1100
236
    fz_stroke_moveto(ctx, s, x, y);
1101
236
  }
1102
236
}
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.20k
{
1130
5.20k
  float dx, dy, d;
1131
5.20k
  float total, used, ratio, tail;
1132
5.20k
  float ax, ay;
1133
5.20k
  float mx, my;
1134
5.20k
  float old_bx = 0, old_by = 0;
1135
5.20k
  float n;
1136
5.20k
  int dash_cap = s->stroke->dash_cap;
1137
1138
5.20k
  ax = s->dash_cur.x;
1139
5.20k
  ay = s->dash_cur.y;
1140
5.20k
  dx = bx - ax;
1141
5.20k
  dy = by - ay;
1142
5.20k
  used = 0;
1143
5.20k
  tail = 0;
1144
5.20k
  total = sqrtf(dx * dx + dy * dy);
1145
1146
  /* If a is off screen, bring it onto the screen. First
1147
   * horizontally... */
1148
5.20k
  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.20k
  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.20k
  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.19k
  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.19k
  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.19k
  if (dx == 0)
1258
44
  {
1259
    /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
1260
44
  }
1261
5.15k
  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.15k
  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.19k
  if (dy == 0)
1284
207
  {
1285
    /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
1286
207
  }
1287
4.98k
  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
4.98k
  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
11.7k
  while (total - used > s->dash_list[s->offset] - s->phase)
1312
6.55k
  {
1313
6.55k
    used += s->dash_list[s->offset] - s->phase;
1314
6.55k
    ratio = used / total;
1315
6.55k
    mx = ax + ratio * dx;
1316
6.55k
    my = ay + ratio * dy;
1317
1318
6.55k
    if (s->toggle)
1319
3.34k
    {
1320
3.34k
      fz_stroke_lineto_aux(ctx, s, mx, my, from_bezier, dx, dy);
1321
3.34k
    }
1322
3.21k
    else
1323
3.21k
    {
1324
3.21k
      fz_stroke_flush(ctx, s, s->cap, dash_cap);
1325
3.21k
      s->cap = dash_cap;
1326
3.21k
      fz_stroke_moveto(ctx, s, mx, my);
1327
3.21k
    }
1328
1329
6.55k
    s->toggle = !s->toggle;
1330
6.55k
    s->phase = 0;
1331
6.55k
    s->offset ++;
1332
6.55k
    if (s->offset == s->dash_len)
1333
3.21k
      s->offset = 0;
1334
6.55k
  }
1335
1336
5.19k
  s->phase += total - used;
1337
1338
5.19k
  if (tail == 0.0f)
1339
5.19k
  {
1340
5.19k
    s->dash_cur.x = bx;
1341
5.19k
    s->dash_cur.y = by;
1342
1343
5.19k
    if (s->toggle)
1344
2.51k
    {
1345
2.51k
      fz_stroke_lineto_aux(ctx, s, bx, by, from_bezier, dx, dy);
1346
2.51k
    }
1347
5.19k
  }
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.19k
}
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.05k
{
1398
9.05k
  float dmax;
1399
9.05k
  float xab, yab;
1400
9.05k
  float xbc, ybc;
1401
9.05k
  float xcd, ycd;
1402
9.05k
  float xabc, yabc;
1403
9.05k
  float xbcd, ybcd;
1404
9.05k
  float xabcd, yabcd;
1405
1406
  /* termination check */
1407
9.05k
  dmax = fz_abs(xa - xb);
1408
9.05k
  dmax = fz_max(dmax, fz_abs(ya - yb));
1409
9.05k
  dmax = fz_max(dmax, fz_abs(xd - xc));
1410
9.05k
  dmax = fz_max(dmax, fz_abs(yd - yc));
1411
9.05k
  if (dmax < s->flatness || depth >= MAX_DEPTH)
1412
4.73k
  {
1413
4.73k
    fz_dash_lineto(ctx, s, xd, yd, 1);
1414
4.73k
    return;
1415
4.73k
  }
1416
1417
4.32k
  xab = xa + xb;
1418
4.32k
  yab = ya + yb;
1419
4.32k
  xbc = xb + xc;
1420
4.32k
  ybc = yb + yc;
1421
4.32k
  xcd = xc + xd;
1422
4.32k
  ycd = yc + yd;
1423
1424
4.32k
  xabc = xab + xbc;
1425
4.32k
  yabc = yab + ybc;
1426
4.32k
  xbcd = xbc + xcd;
1427
4.32k
  ybcd = ybc + ycd;
1428
1429
4.32k
  xabcd = xabc + xbcd;
1430
4.32k
  yabcd = yabc + ybcd;
1431
1432
4.32k
  xab *= 0.5f; yab *= 0.5f;
1433
  /* xbc *= 0.5f; ybc *= 0.5f; */
1434
4.32k
  xcd *= 0.5f; ycd *= 0.5f;
1435
1436
4.32k
  xabc *= 0.25f; yabc *= 0.25f;
1437
4.32k
  xbcd *= 0.25f; ybcd *= 0.25f;
1438
1439
4.32k
  xabcd *= 0.125f; yabcd *= 0.125f;
1440
1441
4.32k
  fz_dash_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
1442
4.32k
  fz_dash_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
1443
4.32k
}
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
236
{
1487
236
  sctx *s = (sctx *)s_;
1488
1489
236
  fz_dash_moveto(ctx, s, x, y);
1490
236
  s->dash_beg.x = s->cur.x = x;
1491
236
  s->dash_beg.y = s->cur.y = y;
1492
236
}
1493
1494
static void
1495
dash_lineto(fz_context *ctx, void *s_, float x, float y)
1496
473
{
1497
473
  sctx *s = (sctx *)s_;
1498
1499
473
  fz_dash_lineto(ctx, s, x, y, 0);
1500
473
  s->cur.x = x;
1501
473
  s->cur.y = y;
1502
473
}
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
415
{
1507
415
  sctx *s = (sctx *)s_;
1508
1509
415
  fz_dash_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
1510
415
  s->cur.x = x3;
1511
415
  s->cur.y = y3;
1512
415
}
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
7.40k
{
1546
7.40k
  struct sctx s;
1547
7.40k
  const fz_path_walker *proc = &stroke_proc;
1548
1549
7.40k
  s.stroke = stroke;
1550
7.40k
  s.rast = rast;
1551
7.40k
  s.ctm = ctm;
1552
7.40k
  s.flatness = flatness;
1553
7.40k
  s.linejoin = stroke->linejoin;
1554
7.40k
  s.linewidth = linewidth * 0.5f; /* hairlines use a different value from the path value */
1555
7.40k
  s.miterlimit = stroke->miterlimit;
1556
7.40k
  s.sn = 0;
1557
7.40k
  s.not_just_moves = 0;
1558
7.40k
  s.toggle = 0;
1559
7.40k
  s.offset = 0;
1560
7.40k
  s.phase = 0;
1561
7.40k
  s.dirn_x = 0;
1562
7.40k
  s.dirn_y = 0;
1563
1564
7.40k
  s.cap = stroke->start_cap;
1565
1566
7.40k
  s.dash_list = NULL;
1567
7.40k
  s.dash_len = stroke->dash_len;
1568
7.40k
  if (s.dash_len > 0)
1569
235
  {
1570
235
    int i;
1571
235
    fz_matrix inv;
1572
235
    float max_expand;
1573
235
    const float *list = stroke->dash_list;
1574
1575
235
    s.dash_total = 0;
1576
705
    for (i = 0; i < s.dash_len; i++)
1577
470
      s.dash_total += list[i];
1578
235
    if (s.dash_total == 0)
1579
0
      return 1;
1580
1581
235
    s.rect = fz_scissor_rasterizer(ctx, rast);
1582
235
    if (fz_try_invert_matrix(&inv, ctm))
1583
0
      return 1;
1584
235
    s.rect = fz_transform_rect(s.rect, inv);
1585
235
    s.rect.x0 -= linewidth;
1586
235
    s.rect.x1 += linewidth;
1587
235
    s.rect.y0 -= linewidth;
1588
235
    s.rect.y1 += linewidth;
1589
1590
235
    max_expand = fz_matrix_max_expansion(ctm);
1591
235
    if (s.dash_total >= 0.01f && s.dash_total * max_expand >= 0.5f)
1592
235
    {
1593
235
      proc = &dash_proc;
1594
235
      s.dash_phase = fmodf(stroke->dash_phase, s.dash_total);
1595
235
      s.dash_list = list;
1596
235
    }
1597
235
  }
1598
1599
7.40k
  s.cur.x = s.cur.y = 0;
1600
7.40k
  fz_walk_path(ctx, path, proc, &s);
1601
7.40k
  fz_stroke_flush(ctx, &s, s.cap, stroke->end_cap);
1602
1603
7.40k
  return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
1604
7.40k
}
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
7.40k
{
1609
7.40k
  int empty;
1610
7.40k
  fz_irect local_bbox;
1611
7.40k
  if (!bbox)
1612
6
    bbox = &local_bbox;
1613
1614
7.40k
  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
7.40k
  empty = do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth);
1623
7.40k
  if (empty)
1624
89
    return *bbox = fz_empty_irect, 1;
1625
1626
7.31k
  *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
1627
7.31k
  return fz_is_empty_irect(*bbox);
1628
7.40k
}