Coverage Report

Created: 2026-11-12 07:07

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
7.61M
#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
4.36M
{
73
4.36M
  float tx0 = ctm.a * x0 + ctm.c * y0 + ctm.e;
74
4.36M
  float ty0 = ctm.b * x0 + ctm.d * y0 + ctm.f;
75
4.36M
  float tx1 = ctm.a * x1 + ctm.c * y1 + ctm.e;
76
4.36M
  float ty1 = ctm.b * x1 + ctm.d * y1 + ctm.f;
77
4.36M
  fz_insert_rasterizer(ctx, rast, tx0, ty0, tx1, ty1, 0);
78
4.36M
}
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
7.86M
{
87
7.86M
  float dmax;
88
7.86M
  float xab, yab;
89
7.86M
  float xbc, ybc;
90
7.86M
  float xcd, ycd;
91
7.86M
  float xabc, yabc;
92
7.86M
  float xbcd, ybcd;
93
7.86M
  float xabcd, yabcd;
94
95
  /* termination check */
96
7.86M
  dmax = fz_abs(xa - xb);
97
7.86M
  dmax = fz_max(dmax, fz_abs(ya - yb));
98
7.86M
  dmax = fz_max(dmax, fz_abs(xd - xc));
99
7.86M
  dmax = fz_max(dmax, fz_abs(yd - yc));
100
7.86M
  if (dmax < flatness || depth >= MAX_DEPTH)
101
4.19M
  {
102
4.19M
    line(ctx, rast, ctm, xa, ya, xd, yd);
103
4.19M
    return;
104
4.19M
  }
105
106
3.67M
  xab = xa + xb;
107
3.67M
  yab = ya + yb;
108
3.67M
  xbc = xb + xc;
109
3.67M
  ybc = yb + yc;
110
3.67M
  xcd = xc + xd;
111
3.67M
  ycd = yc + yd;
112
113
3.67M
  xabc = xab + xbc;
114
3.67M
  yabc = yab + ybc;
115
3.67M
  xbcd = xbc + xcd;
116
3.67M
  ybcd = ybc + ycd;
117
118
3.67M
  xabcd = xabc + xbcd;
119
3.67M
  yabcd = yabc + ybcd;
120
121
3.67M
  xab *= 0.5f; yab *= 0.5f;
122
  /* xbc *= 0.5f; ybc *= 0.5f; */
123
3.67M
  xcd *= 0.5f; ycd *= 0.5f;
124
125
3.67M
  xabc *= 0.25f; yabc *= 0.25f;
126
3.67M
  xbcd *= 0.25f; ybcd *= 0.25f;
127
128
3.67M
  xabcd *= 0.125f; yabcd *= 0.125f;
129
130
3.67M
  bezier(ctx, rast, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
131
3.67M
  bezier(ctx, rast, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
132
3.67M
}
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
144k
{
187
144k
  flatten_arg *arg = (flatten_arg *)arg_;
188
189
  /* implicit closepath before moveto */
190
144k
  if (arg->c.x != arg->b.x || arg->c.y != arg->b.y)
191
1.56k
    line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y);
192
144k
  arg->c.x = arg->b.x = x;
193
144k
  arg->c.y = arg->b.y = y;
194
195
144k
  fz_gap_rasterizer(ctx, arg->rast);
196
144k
}
197
198
static void
199
flatten_lineto(fz_context *ctx, void *arg_, float x, float y)
200
133k
{
201
133k
  flatten_arg *arg = (flatten_arg *)arg_;
202
203
133k
  line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, x, y);
204
133k
  arg->c.x = x;
205
133k
  arg->c.y = y;
206
133k
}
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
515k
{
211
515k
  flatten_arg *arg = (flatten_arg *)arg_;
212
213
515k
  bezier(ctx, arg->rast, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, x3, y3, 0);
214
515k
  arg->c.x = x3;
215
515k
  arg->c.y = y3;
216
515k
}
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
35.2k
{
231
35.2k
  flatten_arg *arg = (flatten_arg *)arg_;
232
233
35.2k
  line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y);
234
35.2k
  arg->c.x = arg->b.x;
235
35.2k
  arg->c.y = arg->b.y;
236
35.2k
}
237
238
static void
239
flatten_rectto(fz_context *ctx, void *arg_, float x0, float y0, float x1, float y1)
240
36.1k
{
241
36.1k
  flatten_arg *arg = (flatten_arg *)arg_;
242
36.1k
  fz_matrix ctm = arg->ctm;
243
244
36.1k
  flatten_moveto(ctx, arg_, x0, y0);
245
246
36.1k
  if (fz_antidropout_rasterizer(ctx, arg->rast))
247
36.1k
  {
248
    /* In the case where we have an axis aligned rectangle, do some
249
     * horrid antidropout stuff. */
250
36.1k
    if (ctm.b == 0 && ctm.c == 0)
251
33.7k
    {
252
33.7k
      float tx0 = ctm.a * x0 + ctm.e;
253
33.7k
      float ty0 = ctm.d * y0 + ctm.f;
254
33.7k
      float tx1 = ctm.a * x1 + ctm.e;
255
33.7k
      float ty1 = ctm.d * y1 + ctm.f;
256
33.7k
      fz_insert_rasterizer_rect(ctx, arg->rast, tx0, ty0, tx1, ty1);
257
33.7k
      return;
258
33.7k
    }
259
2.44k
    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.1k
  }
269
270
2.44k
  flatten_lineto(ctx, arg_, x1, y0);
271
2.44k
  flatten_lineto(ctx, arg_, x1, y1);
272
2.44k
  flatten_lineto(ctx, arg_, x0, y1);
273
2.44k
  flatten_close(ctx, arg_);
274
2.44k
}
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
108k
{
291
108k
  flatten_arg arg;
292
293
108k
  arg.rast = rast;
294
108k
  arg.ctm = ctm;
295
108k
  arg.flatness = flatness;
296
108k
  arg.b.x = arg.b.y = arg.c.x = arg.c.y = 0;
297
298
108k
  fz_walk_path(ctx, path, &flatten_proc, &arg);
299
108k
  if (arg.c.x != arg.b.x || arg.c.y != arg.b.y)
300
698
    line(ctx, rast, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y);
301
302
108k
  fz_gap_rasterizer(ctx, rast);
303
304
108k
  return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
305
108k
}
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
108k
{
310
108k
  int empty;
311
108k
  fz_irect local_bbox;
312
108k
  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
108k
  if (fz_is_empty_irect(scissor))
318
16.0k
    scissor.x1 = scissor.x0, scissor.y1 = scissor.y0;
319
320
108k
  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
108k
  empty = do_flatten_fill(ctx, rast, path, ctm, flatness);
329
108k
  if (empty)
330
17.2k
    return *bbox = fz_empty_irect, 1;
331
332
91.1k
  *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
333
91.1k
  return fz_is_empty_irect(*bbox);
334
108k
}
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.13M
{
371
2.13M
  float tx0 = s->ctm.a * x0 + s->ctm.c * y0 + s->ctm.e;
372
2.13M
  float ty0 = s->ctm.b * x0 + s->ctm.d * y0 + s->ctm.f;
373
2.13M
  float tx1 = s->ctm.a * x1 + s->ctm.c * y1 + s->ctm.e;
374
2.13M
  float ty1 = s->ctm.b * x1 + s->ctm.d * y1 + s->ctm.f;
375
376
2.13M
  fz_insert_rasterizer(ctx, s->rast, tx0, ty0, tx1, ty1, rev);
377
2.13M
}
378
379
static void
380
fz_add_horiz_rect(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1)
381
68.0k
{
382
68.0k
  if (fz_antidropout_rasterizer(ctx, s->rast)) {
383
68.0k
    if (s->ctm.b == 0 && s->ctm.c == 0)
384
68.0k
    {
385
68.0k
      float tx0 = s->ctm.a * x0 + s->ctm.e;
386
68.0k
      float ty0 = s->ctm.d * y0 + s->ctm.f;
387
68.0k
      float tx1 = s->ctm.a * x1 + s->ctm.e;
388
68.0k
      float ty1 = s->ctm.d * y1 + s->ctm.f;
389
68.0k
      fz_insert_rasterizer_rect(ctx, s->rast, tx1, ty1, tx0, ty0);
390
68.0k
      return;
391
68.0k
    }
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
68.0k
  }
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
67.6k
{
443
67.6k
  float th0, th1, r;
444
67.6k
  float theta;
445
67.6k
  float ox, oy, nx, ny;
446
67.6k
  int n, i;
447
448
67.6k
  r = fabsf(s->linewidth);
449
67.6k
  theta = 2 * FZ_SQRT2 * sqrtf(s->flatness / r);
450
67.6k
  th0 = atan2f(y0, x0);
451
67.6k
  th1 = atan2f(y1, x1);
452
453
67.6k
  if (r > 0)
454
67.6k
  {
455
67.6k
    if (th0 < th1)
456
4.92k
      th0 += FZ_PI * 2;
457
67.6k
    n = ceilf((th0 - th1) / theta);
458
67.6k
  }
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
67.6k
  if (rev)
467
54.2k
  {
468
54.2k
    ox = x1;
469
54.2k
    oy = y1;
470
54.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
54.2k
    fz_add_line(ctx, s, xc + x0, yc + y0, xc + ox, yc + oy, rev);
481
54.2k
  }
482
13.3k
  else
483
13.3k
  {
484
13.3k
    ox = x0;
485
13.3k
    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.3k
    fz_add_line(ctx, s, xc + ox, yc + oy, xc + x1, yc + y1, rev);
497
13.3k
  }
498
67.6k
}
499
500
/* FLT_TINY * FLT_TINY is approximately FLT_EPSILON */
501
512k
#define FLT_TINY 3.4e-4F
502
static int find_normal_vectors(float dx, float dy, float linewidth, float *dlx, float *dly)
503
1.45M
{
504
1.45M
  if (dx == 0)
505
30.0k
  {
506
30.0k
    if (dy < FLT_TINY && dy > - FLT_TINY)
507
416
      goto tiny;
508
29.6k
    else if (dy > 0)
509
15.0k
      *dlx = linewidth;
510
14.6k
    else
511
14.6k
      *dlx = -linewidth;
512
29.6k
    *dly = 0;
513
29.6k
  }
514
1.42M
  else if (dy == 0)
515
164k
  {
516
164k
    if (dx < FLT_TINY && dx > - FLT_TINY)
517
0
      goto tiny;
518
164k
    else if (dx > 0)
519
56.7k
      *dly = -linewidth;
520
107k
    else
521
107k
      *dly = linewidth;
522
164k
    *dlx = 0;
523
164k
  }
524
1.25M
  else
525
1.25M
  {
526
1.25M
    float sq = dx * dx + dy * dy;
527
1.25M
    float scale;
528
529
1.25M
    if (sq < FLT_EPSILON)
530
0
      goto tiny;
531
1.25M
    scale = linewidth / sqrtf(sq);
532
1.25M
    *dlx = dy * scale;
533
1.25M
    *dly = -dx * scale;
534
1.25M
  }
535
1.45M
  return 0;
536
416
tiny:
537
416
  *dlx = 0;
538
416
  *dly = 0;
539
416
  return 1;
540
1.45M
}
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
475k
{
545
475k
  float miterlimit = s->miterlimit;
546
475k
  float linewidth = s->linewidth;
547
475k
  fz_linejoin linejoin = s->linejoin;
548
475k
  float dx0, dy0;
549
475k
  float dx1, dy1;
550
475k
  float dlx0, dly0;
551
475k
  float dlx1, dly1;
552
475k
  float dmx, dmy;
553
475k
  float dmr2;
554
475k
  float scale;
555
475k
  float cross;
556
475k
  int rev = 0;
557
558
475k
  dx0 = bx - ax;
559
475k
  dy0 = by - ay;
560
561
475k
  dx1 = cx - bx;
562
475k
  dy1 = cy - by;
563
564
475k
  cross = dx1 * dy0 - dx0 * dy1;
565
  /* Ensure that cross >= 0 */
566
475k
  if (cross < 0)
567
196k
  {
568
196k
    float tmp;
569
196k
    tmp = dx1; dx1 = -dx0; dx0 = -tmp;
570
196k
    tmp = dy1; dy1 = -dy0; dy0 = -tmp;
571
196k
    cross = -cross;
572
196k
    rev = !rev;
573
196k
  }
574
575
475k
  if (find_normal_vectors(dx0, dy0, linewidth, &dlx0, &dly0))
576
0
    linejoin = FZ_LINEJOIN_BEVEL;
577
578
475k
  if (find_normal_vectors(dx1, dy1, linewidth, &dlx1, &dly1))
579
0
    linejoin = FZ_LINEJOIN_BEVEL;
580
581
475k
  dmx = (dlx0 + dlx1) * 0.5f;
582
475k
  dmy = (dly0 + dly1) * 0.5f;
583
475k
  dmr2 = dmx * dmx + dmy * dmy;
584
585
475k
  if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0)
586
176k
    linejoin = FZ_LINEJOIN_BEVEL;
587
588
  /* XPS miter joins are clipped at miterlength, rather than simply
589
   * being converted to bevelled joins. */
590
475k
  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
475k
  else if (linejoin == FZ_LINEJOIN_MITER)
598
231k
    if (dmr2 * miterlimit * miterlimit < linewidth * linewidth)
599
2.35k
      linejoin = FZ_LINEJOIN_BEVEL;
600
601
475k
  if (join_under)
602
450k
  {
603
450k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx + dlx0, by + dly0, !rev);
604
450k
  }
605
25.4k
  else if (rev)
606
17.5k
  {
607
17.5k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 0);
608
17.5k
    fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 0);
609
17.5k
  }
610
7.83k
  else
611
7.83k
  {
612
7.83k
    fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 1);
613
7.83k
    fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 1);
614
7.83k
  }
615
616
475k
  switch (linejoin)
617
475k
  {
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
229k
  case FZ_LINEJOIN_MITER:
646
229k
    scale = linewidth * linewidth / dmr2;
647
229k
    dmx *= scale;
648
229k
    dmy *= scale;
649
650
229k
    if (rev)
651
83.4k
    {
652
83.4k
      fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 1);
653
83.4k
      fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 1);
654
83.4k
    }
655
146k
    else
656
146k
    {
657
146k
      fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 0);
658
146k
      fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 0);
659
146k
    }
660
229k
    break;
661
662
178k
  case FZ_LINEJOIN_BEVEL:
663
178k
    fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dlx1, by - dly1, rev);
664
178k
    break;
665
666
67.6k
  case FZ_LINEJOIN_ROUND:
667
67.6k
    fz_add_arc(ctx, s, bx, by, -dlx0, -dly0, -dlx1, -dly1, rev);
668
67.6k
    break;
669
670
0
  default:
671
0
    assert("Invalid line join" == NULL);
672
475k
  }
673
475k
}
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
52.4k
{
678
52.4k
  float flatness = s->flatness;
679
52.4k
  float linewidth = s->linewidth;
680
681
52.4k
  switch (linecap)
682
52.4k
  {
683
43.6k
  case FZ_LINECAP_BUTT:
684
43.6k
    fz_add_line(ctx, s, bx - dlx, by - dly, bx + dlx, by + dly, rev);
685
43.6k
    break;
686
687
8.79k
  case FZ_LINECAP_ROUND:
688
8.79k
  {
689
8.79k
    int i;
690
8.79k
    int n = ceilf(FZ_PI / (2.0f * FZ_SQRT2 * sqrtf(flatness / linewidth)));
691
8.79k
    float ox = bx - dlx;
692
8.79k
    float oy = by - dly;
693
13.4k
    for (i = 1; i < n; i++)
694
4.69k
    {
695
4.69k
      float theta = FZ_PI * i / n;
696
4.69k
      float cth = cosf(theta);
697
4.69k
      float sth = sinf(theta);
698
4.69k
      float nx = bx - dlx * cth - dly * sth;
699
4.69k
      float ny = by - dly * cth + dlx * sth;
700
4.69k
      fz_add_line(ctx, s, ox, oy, nx, ny, rev);
701
4.69k
      ox = nx;
702
4.69k
      oy = ny;
703
4.69k
    }
704
8.79k
    fz_add_line(ctx, s, ox, oy, bx + dlx, by + dly, rev);
705
8.79k
    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
52.4k
  }
729
52.4k
}
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
52.4k
{
734
52.4k
  float linewidth = s->linewidth;
735
52.4k
  float dx = bx - ax;
736
52.4k
  float dy = by - ay;
737
738
52.4k
  float scale = linewidth / sqrtf(dx * dx + dy * dy);
739
52.4k
  float dlx = dy * scale;
740
52.4k
  float dly = -dx * scale;
741
742
52.4k
  do_linecap(ctx, s, bx, by, linecap, rev, dlx, dly);
743
52.4k
}
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
158
{
748
158
  float linewidth = s->linewidth;
749
158
  float dx = rev ? -s->dirn_x : s->dirn_x;
750
158
  float dy = rev ? -s->dirn_y : s->dirn_y;
751
158
  float scale, dlx, dly;
752
753
158
  if (dx == 0 && dy == 0)
754
158
    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
40.9k
{
793
40.9k
  if (s->sn == 1)
794
26.2k
  {
795
26.2k
    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
26.2k
    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
26.2k
  }
798
14.7k
  else if (s->not_just_moves)
799
79
  {
800
79
    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
79
    else
805
79
    {
806
79
      fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 2);
807
79
      fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 0);
808
79
    }
809
79
  }
810
811
40.9k
  fz_gap_rasterizer(ctx, s->rast);
812
40.9k
}
813
814
static void
815
fz_stroke_moveto(fz_context *ctx, void *s_, float x, float y)
816
33.2k
{
817
33.2k
  struct sctx *s = (struct sctx *)s_;
818
819
33.2k
  s->seg[0].x = s->beg[0].x = x;
820
33.2k
  s->seg[0].y = s->beg[0].y = y;
821
33.2k
  s->sn = 0;
822
33.2k
  s->not_just_moves = 0;
823
33.2k
  s->from_bezier = 0;
824
33.2k
  s->dirn_x = 0;
825
33.2k
  s->dirn_y = 0;
826
33.2k
}
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
502k
{
831
502k
  float ox = s->seg[s->sn].x;
832
502k
  float oy = s->seg[s->sn].y;
833
502k
  float dx = x - ox;
834
502k
  float dy = y - oy;
835
502k
  float dlx, dly;
836
837
502k
  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
502k
  s->dirn_x = dirn_x;
842
502k
  s->dirn_y = dirn_y;
843
844
  /* We calculate the normal vectors from the delta that we have just moved. */
845
502k
  if (find_normal_vectors(dx, dy, s->linewidth, &dlx, &dly))
846
416
  {
847
416
    return;
848
416
  }
849
850
501k
  if (s->sn == 1)
851
469k
    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
501k
#if 1
854
501k
  if (0 && dx == 0)
855
0
  {
856
0
    fz_add_vert_rect(ctx, s, ox - dlx, oy, x + dlx, y);
857
0
  }
858
501k
  else if (dy == 0)
859
68.0k
  {
860
68.0k
    fz_add_horiz_rect(ctx, s, ox, oy - dly, x, y + dly);
861
68.0k
  }
862
433k
  else
863
433k
#endif
864
433k
  {
865
866
433k
    fz_add_line(ctx, s, ox - dlx, oy - dly, x - dlx, y - dly, 0);
867
433k
    fz_add_line(ctx, s, x + dlx, y + dly, ox + dlx, oy + dly, 1);
868
433k
  }
869
870
501k
  if (s->sn)
871
469k
  {
872
469k
    s->seg[0] = s->seg[1];
873
469k
    s->seg[1].x = x;
874
469k
    s->seg[1].y = y;
875
469k
  }
876
32.1k
  else
877
32.1k
  {
878
32.1k
    s->seg[1].x = s->beg[1].x = x;
879
32.1k
    s->seg[1].y = s->beg[1].y = y;
880
32.1k
    s->sn = 1;
881
32.1k
  }
882
501k
  s->from_bezier = from_bezier;
883
501k
}
884
885
static void
886
fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier)
887
496k
{
888
496k
  float ox = s->seg[s->sn].x;
889
496k
  float oy = s->seg[s->sn].y;
890
496k
  float dx = x - ox;
891
496k
  float dy = y - oy;
892
496k
  fz_stroke_lineto_aux(ctx, s, x, y, from_bezier, dx, dy);
893
496k
}
894
895
static void
896
fz_stroke_closepath(fz_context *ctx, sctx *s)
897
6.00k
{
898
6.00k
  if (s->sn == 1)
899
5.95k
  {
900
5.95k
    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.95k
    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.95k
  }
911
48
  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.00k
  s->seg[0] = s->beg[0];
917
6.00k
  s->sn = 0;
918
6.00k
  s->not_just_moves = 0;
919
6.00k
  s->from_bezier = 0;
920
6.00k
  s->dirn_x = 0;
921
6.00k
  s->dirn_y = 0;
922
923
6.00k
  fz_gap_rasterizer(ctx, s->rast);
924
6.00k
}
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
891k
{
933
891k
  float dmax;
934
891k
  float xab, yab;
935
891k
  float xbc, ybc;
936
891k
  float xcd, ycd;
937
891k
  float xabc, yabc;
938
891k
  float xbcd, ybcd;
939
891k
  float xabcd, yabcd;
940
941
  /* termination check */
942
891k
  dmax = fz_abs(xa - xb);
943
891k
  dmax = fz_max(dmax, fz_abs(ya - yb));
944
891k
  dmax = fz_max(dmax, fz_abs(xd - xc));
945
891k
  dmax = fz_max(dmax, fz_abs(yd - yc));
946
891k
  if (dmax < s->flatness || depth >= MAX_DEPTH)
947
451k
  {
948
451k
    fz_stroke_lineto(ctx, s, xd, yd, 1);
949
451k
    return;
950
451k
  }
951
952
439k
  xab = xa + xb;
953
439k
  yab = ya + yb;
954
439k
  xbc = xb + xc;
955
439k
  ybc = yb + yc;
956
439k
  xcd = xc + xd;
957
439k
  ycd = yc + yd;
958
959
439k
  xabc = xab + xbc;
960
439k
  yabc = yab + ybc;
961
439k
  xbcd = xbc + xcd;
962
439k
  ybcd = ybc + ycd;
963
964
439k
  xabcd = xabc + xbcd;
965
439k
  yabcd = yabc + ybcd;
966
967
439k
  xab *= 0.5f; yab *= 0.5f;
968
  /* xbc *= 0.5f; ybc *= 0.5f; */
969
439k
  xcd *= 0.5f; ycd *= 0.5f;
970
971
439k
  xabc *= 0.25f; yabc *= 0.25f;
972
439k
  xbcd *= 0.25f; ybcd *= 0.25f;
973
974
439k
  xabcd *= 0.125f; yabcd *= 0.125f;
975
976
439k
  fz_stroke_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
977
439k
  fz_stroke_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
978
439k
}
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
29.7k
{
1022
29.7k
  sctx *s = (sctx *)s_;
1023
1024
29.7k
  fz_stroke_flush(ctx, s, s->stroke->start_cap, s->stroke->end_cap);
1025
29.7k
  fz_stroke_moveto(ctx, s, x, y);
1026
29.7k
  s->cur.x = x;
1027
29.7k
  s->cur.y = y;
1028
29.7k
}
1029
1030
static void
1031
stroke_lineto(fz_context *ctx, void *s_, float x, float y)
1032
38.6k
{
1033
38.6k
  sctx *s = (sctx *)s_;
1034
1035
38.6k
  fz_stroke_lineto(ctx, s, x, y, 0);
1036
38.6k
  s->cur.x = x;
1037
38.6k
  s->cur.y = y;
1038
38.6k
}
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
12.1k
{
1043
12.1k
  sctx *s = (sctx *)s_;
1044
1045
12.1k
  fz_stroke_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
1046
12.1k
  s->cur.x = x3;
1047
12.1k
  s->cur.y = y3;
1048
12.1k
}
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.00k
{
1063
6.00k
  sctx *s = (sctx *)s_;
1064
1065
6.00k
  fz_stroke_closepath(ctx, s);
1066
6.00k
}
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
240
{
1080
240
  s->toggle = 1;
1081
240
  s->offset = 0;
1082
240
  s->phase = s->dash_phase;
1083
1084
240
  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
240
  s->dash_cur.x = x;
1094
240
  s->dash_cur.y = y;
1095
1096
240
  if (s->toggle)
1097
240
  {
1098
240
    fz_stroke_flush(ctx, s, s->cap, s->stroke->end_cap);
1099
240
    s->cap = s->stroke->start_cap;
1100
240
    fz_stroke_moveto(ctx, s, x, y);
1101
240
  }
1102
240
}
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.24k
{
1130
5.24k
  float dx, dy, d;
1131
5.24k
  float total, used, ratio, tail;
1132
5.24k
  float ax, ay;
1133
5.24k
  float mx, my;
1134
5.24k
  float old_bx = bx, old_by = by;
1135
5.24k
  float n;
1136
5.24k
  int dash_cap = s->stroke->dash_cap;
1137
1138
5.24k
  ax = s->dash_cur.x;
1139
5.24k
  ay = s->dash_cur.y;
1140
5.24k
  dx = bx - ax;
1141
5.24k
  dy = by - ay;
1142
5.24k
  used = 0;
1143
5.24k
  tail = 0;
1144
5.24k
  total = sqrtf(dx * dx + dy * dy);
1145
1146
  /* If a is off screen, bring it onto the screen. First
1147
   * horizontally... */
1148
5.24k
  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
      goto adjust_for_tail;
1155
0
    }
1156
0
    ax = s->rect.x0;  /* d > 0, dx > 0 */
1157
0
    goto a_moved_horizontally;
1158
0
  }
1159
5.24k
  else if (d < 0 && (d = (s->rect.x1 - ax)) < 0)
1160
0
  {
1161
0
    if (bx > s->rect.x1)
1162
0
    {
1163
      /* Entirely off screen */
1164
0
      tail = total;
1165
0
      goto adjust_for_tail;
1166
0
    }
1167
0
    ax = s->rect.x1;  /* d < 0, dx < 0 */
1168
0
a_moved_horizontally: /* d and dx have the same sign */
1169
0
    assert((d > 0 && dx > 0) || (d < 0 && dx < 0));
1170
0
    assert(dx != 0);
1171
0
    ay = advance(ay, by, d, dx);
1172
0
    used = total * d/dx;
1173
0
    total -= used;
1174
0
    dx = bx - ax;
1175
0
    dy = by - ay;
1176
0
  }
1177
  /* Then vertically... */
1178
5.24k
  if ((d = s->rect.y0 - ay) > 0)
1179
14
  {
1180
14
    if (by < s->rect.y0)
1181
14
    {
1182
      /* Entirely off screen */
1183
14
      tail = total + used; /* Add back in the 'used' section that we have already removed from total. */
1184
14
      goto adjust_for_tail;
1185
14
    }
1186
0
    ay = s->rect.y0;  /* d > 0, dy > 0 */
1187
0
    goto a_moved_vertically;
1188
14
  }
1189
5.22k
  else if (d < 0 && (d = (s->rect.y1 - ay)) < 0)
1190
0
  {
1191
0
    if (by > s->rect.y1)
1192
0
    {
1193
      /* Entirely off screen */
1194
0
      tail = total + used; /* Add back in the 'used' section that we have already removed from total. */
1195
0
      goto adjust_for_tail;
1196
0
    }
1197
0
    ay = s->rect.y1;  /* d < 0, dy < 0 */
1198
0
a_moved_vertically: /* d and dy have the same sign */
1199
0
    assert((d > 0 && dy > 0) || (d < 0 && dy < 0));
1200
0
    assert(dy != 0);
1201
0
    ax = advance(ax, bx, d, dy);
1202
0
    d = total * d/dy;
1203
0
    total -= d;
1204
0
    used += d;
1205
0
    dx = bx - ax;
1206
0
    dy = by - ay;
1207
0
  }
1208
5.22k
  if (used != 0.0f)
1209
0
  {
1210
    /* Update the position in the dash array */
1211
0
    if (s->toggle)
1212
0
    {
1213
0
      fz_stroke_lineto(ctx, s, ax, ay, from_bezier);
1214
0
    }
1215
0
    else
1216
0
    {
1217
0
      fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap);
1218
0
      s->cap = s->stroke->dash_cap;
1219
0
      fz_stroke_moveto(ctx, s, ax, ay);
1220
0
    }
1221
0
    used += s->phase;
1222
0
    n = trunc(used/s->dash_total);
1223
0
    used -= n*s->dash_total;
1224
0
    if (is_odd(n) & s->dash_len & 1)
1225
0
      s->toggle = !s->toggle;
1226
0
    while (used >= s->dash_list[s->offset])
1227
0
    {
1228
0
      used -= s->dash_list[s->offset];
1229
0
      s->offset++;
1230
0
      if (s->offset == s->dash_len)
1231
0
        s->offset = 0;
1232
0
      s->toggle = !s->toggle;
1233
0
    }
1234
0
    if (s->toggle)
1235
0
    {
1236
0
      fz_stroke_lineto(ctx, s, ax, ay, from_bezier);
1237
0
    }
1238
0
    else
1239
0
    {
1240
0
      fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap);
1241
0
      s->cap = s->stroke->dash_cap;
1242
0
      fz_stroke_moveto(ctx, s, ax, ay);
1243
0
    }
1244
0
    s->phase = used;
1245
0
    used = 0;
1246
0
  }
1247
1248
  /* Now if bx is off screen, bring it back */
1249
5.22k
  if (dx == 0)
1250
45
  {
1251
    /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
1252
45
  }
1253
5.18k
  else if ((d = bx - s->rect.x0) < 0)
1254
0
  {
1255
0
    bx = s->rect.x0;  /* d < 0, dx < 0 */
1256
0
    goto b_moved_horizontally;
1257
0
  }
1258
5.18k
  else if (d > 0 && (d = (bx - s->rect.x1)) > 0)
1259
0
  {
1260
0
    bx = s->rect.x1;  /* d > 0, dx > 0 */
1261
0
b_moved_horizontally: /* d and dx have the same sign */
1262
0
    assert((d > 0 && dx > 0) || (d < 0 && dx < 0));
1263
0
    assert(dx != 0);
1264
0
    by = advance(by, ay, d, dx);
1265
0
    tail = total * d/dx;
1266
0
    total -= tail;
1267
0
    dx = bx - ax;
1268
0
    dy = by - ay;
1269
0
  }
1270
  /* Then vertically... */
1271
5.22k
  if (dy == 0)
1272
209
  {
1273
    /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
1274
209
  }
1275
5.01k
  else if ((d = by - s->rect.y0) < 0)
1276
0
  {
1277
0
    by = s->rect.y0;  /* d < 0, dy < 0 */
1278
0
    goto b_moved_vertically;
1279
0
  }
1280
5.01k
  else if (d > 0 && (d = (by - s->rect.y1)) > 0)
1281
0
  {
1282
0
    float t;
1283
0
    by = s->rect.y1;  /* d > 0, dy > 0 */
1284
0
b_moved_vertically: /* d and dy have the same sign */
1285
0
    assert((d > 0 && dy > 0) || (d < 0 && dy < 0));
1286
0
    assert(dy != 0);
1287
0
    bx = advance(bx, ax, d, dy);
1288
0
    t = total * d/dy;
1289
0
    tail += t;
1290
0
    total -= t;
1291
0
    dx = bx - ax;
1292
0
    dy = by - ay;
1293
0
  }
1294
1295
11.8k
  while (total - used > s->dash_list[s->offset] - s->phase)
1296
6.61k
  {
1297
6.61k
    used += s->dash_list[s->offset] - s->phase;
1298
6.61k
    ratio = used / total;
1299
6.61k
    mx = ax + ratio * dx;
1300
6.61k
    my = ay + ratio * dy;
1301
1302
6.61k
    if (s->toggle)
1303
3.37k
    {
1304
3.37k
      fz_stroke_lineto_aux(ctx, s, mx, my, from_bezier, dx, dy);
1305
3.37k
    }
1306
3.24k
    else
1307
3.24k
    {
1308
3.24k
      fz_stroke_flush(ctx, s, s->cap, dash_cap);
1309
3.24k
      s->cap = dash_cap;
1310
3.24k
      fz_stroke_moveto(ctx, s, mx, my);
1311
3.24k
    }
1312
1313
6.61k
    s->toggle = !s->toggle;
1314
6.61k
    s->phase = 0;
1315
6.61k
    s->offset ++;
1316
6.61k
    if (s->offset == s->dash_len)
1317
3.24k
      s->offset = 0;
1318
6.61k
  }
1319
1320
5.22k
  s->phase += total - used;
1321
1322
5.22k
  if (tail == 0.0f)
1323
5.22k
  {
1324
5.22k
    s->dash_cur.x = bx;
1325
5.22k
    s->dash_cur.y = by;
1326
1327
5.22k
    if (s->toggle)
1328
2.53k
    {
1329
2.53k
      fz_stroke_lineto_aux(ctx, s, bx, by, from_bezier, dx, dy);
1330
2.53k
    }
1331
5.22k
  }
1332
0
  else
1333
0
  {
1334
14
adjust_for_tail:
1335
14
    s->dash_cur.x = old_bx;
1336
14
    s->dash_cur.y = old_by;
1337
    /* Update the position in the dash array */
1338
14
    if (s->toggle)
1339
8
    {
1340
8
      fz_stroke_lineto_aux(ctx, s, old_bx, old_by, from_bezier, dx, dy);
1341
8
    }
1342
6
    else
1343
6
    {
1344
6
      fz_stroke_flush(ctx, s, s->cap, dash_cap);
1345
6
      s->cap = dash_cap;
1346
6
      fz_stroke_moveto(ctx, s, old_bx, old_by);
1347
6
    }
1348
14
    tail += s->phase;
1349
14
    n = trunc(tail/s->dash_total);
1350
14
    tail -= n*s->dash_total;
1351
14
    if (is_odd(n) & s->dash_len & 1)
1352
0
      s->toggle = !s->toggle;
1353
20
    while (tail > s->dash_list[s->offset])
1354
6
    {
1355
6
      tail -= s->dash_list[s->offset];
1356
6
      s->offset++;
1357
6
      if (s->offset == s->dash_len)
1358
2
        s->offset = 0;
1359
6
      s->toggle = !s->toggle;
1360
6
    }
1361
14
    if (s->toggle)
1362
6
    {
1363
6
      fz_stroke_lineto_aux(ctx, s, old_bx, old_by, from_bezier, dx, dy);
1364
6
    }
1365
8
    else
1366
8
    {
1367
8
      fz_stroke_flush(ctx, s, s->cap, dash_cap);
1368
8
      s->cap = dash_cap;
1369
8
      fz_stroke_moveto(ctx, s, old_bx, old_by);
1370
8
    }
1371
14
    s->phase = tail;
1372
14
  }
1373
5.22k
}
1374
1375
static void
1376
fz_dash_bezier(fz_context *ctx, struct sctx *s,
1377
  float xa, float ya,
1378
  float xb, float yb,
1379
  float xc, float yc,
1380
  float xd, float yd, int depth)
1381
9.09k
{
1382
9.09k
  float dmax;
1383
9.09k
  float xab, yab;
1384
9.09k
  float xbc, ybc;
1385
9.09k
  float xcd, ycd;
1386
9.09k
  float xabc, yabc;
1387
9.09k
  float xbcd, ybcd;
1388
9.09k
  float xabcd, yabcd;
1389
1390
  /* termination check */
1391
9.09k
  dmax = fz_abs(xa - xb);
1392
9.09k
  dmax = fz_max(dmax, fz_abs(ya - yb));
1393
9.09k
  dmax = fz_max(dmax, fz_abs(xd - xc));
1394
9.09k
  dmax = fz_max(dmax, fz_abs(yd - yc));
1395
9.09k
  if (dmax < s->flatness || depth >= MAX_DEPTH)
1396
4.75k
  {
1397
4.75k
    fz_dash_lineto(ctx, s, xd, yd, 1);
1398
4.75k
    return;
1399
4.75k
  }
1400
1401
4.33k
  xab = xa + xb;
1402
4.33k
  yab = ya + yb;
1403
4.33k
  xbc = xb + xc;
1404
4.33k
  ybc = yb + yc;
1405
4.33k
  xcd = xc + xd;
1406
4.33k
  ycd = yc + yd;
1407
1408
4.33k
  xabc = xab + xbc;
1409
4.33k
  yabc = yab + ybc;
1410
4.33k
  xbcd = xbc + xcd;
1411
4.33k
  ybcd = ybc + ycd;
1412
1413
4.33k
  xabcd = xabc + xbcd;
1414
4.33k
  yabcd = yabc + ybcd;
1415
1416
4.33k
  xab *= 0.5f; yab *= 0.5f;
1417
  /* xbc *= 0.5f; ybc *= 0.5f; */
1418
4.33k
  xcd *= 0.5f; ycd *= 0.5f;
1419
1420
4.33k
  xabc *= 0.25f; yabc *= 0.25f;
1421
4.33k
  xbcd *= 0.25f; ybcd *= 0.25f;
1422
1423
4.33k
  xabcd *= 0.125f; yabcd *= 0.125f;
1424
1425
4.33k
  fz_dash_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
1426
4.33k
  fz_dash_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
1427
4.33k
}
1428
1429
static void
1430
fz_dash_quad(fz_context *ctx, struct sctx *s,
1431
  float xa, float ya,
1432
  float xb, float yb,
1433
  float xc, float yc, int depth)
1434
0
{
1435
0
  float dmax;
1436
0
  float xab, yab;
1437
0
  float xbc, ybc;
1438
0
  float xabc, yabc;
1439
1440
  /* termination check */
1441
0
  dmax = fz_abs(xa - xb);
1442
0
  dmax = fz_max(dmax, fz_abs(ya - yb));
1443
0
  dmax = fz_max(dmax, fz_abs(xc - xb));
1444
0
  dmax = fz_max(dmax, fz_abs(yc - yb));
1445
0
  if (dmax < s->flatness || depth >= MAX_DEPTH)
1446
0
  {
1447
0
    fz_dash_lineto(ctx, s, xc, yc, 1);
1448
0
    return;
1449
0
  }
1450
1451
0
  xab = xa + xb;
1452
0
  yab = ya + yb;
1453
0
  xbc = xb + xc;
1454
0
  ybc = yb + yc;
1455
1456
0
  xabc = xab + xbc;
1457
0
  yabc = yab + ybc;
1458
1459
0
  xab *= 0.5f; yab *= 0.5f;
1460
0
  xbc *= 0.5f; ybc *= 0.5f;
1461
1462
0
  xabc *= 0.25f; yabc *= 0.25f;
1463
1464
0
  fz_dash_quad(ctx, s, xa, ya, xab, yab, xabc, yabc, depth + 1);
1465
0
  fz_dash_quad(ctx, s, xabc, yabc, xbc, ybc, xc, yc, depth + 1);
1466
0
}
1467
1468
static void
1469
dash_moveto(fz_context *ctx, void *s_, float x, float y)
1470
240
{
1471
240
  sctx *s = (sctx *)s_;
1472
1473
240
  fz_dash_moveto(ctx, s, x, y);
1474
240
  s->dash_beg.x = s->cur.x = x;
1475
240
  s->dash_beg.y = s->cur.y = y;
1476
240
}
1477
1478
static void
1479
dash_lineto(fz_context *ctx, void *s_, float x, float y)
1480
481
{
1481
481
  sctx *s = (sctx *)s_;
1482
1483
481
  fz_dash_lineto(ctx, s, x, y, 0);
1484
481
  s->cur.x = x;
1485
481
  s->cur.y = y;
1486
481
}
1487
1488
static void
1489
dash_curveto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2, float x3, float y3)
1490
419
{
1491
419
  sctx *s = (sctx *)s_;
1492
1493
419
  fz_dash_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
1494
419
  s->cur.x = x3;
1495
419
  s->cur.y = y3;
1496
419
}
1497
1498
static void
1499
dash_quadto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2)
1500
0
{
1501
0
  sctx *s = (sctx *)s_;
1502
1503
0
  fz_dash_quad(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, 0);
1504
0
  s->cur.x = x2;
1505
0
  s->cur.y = y2;
1506
0
}
1507
1508
static void
1509
dash_close(fz_context *ctx, void *s_)
1510
1
{
1511
1
  sctx *s = (sctx *)s_;
1512
1513
1
  fz_dash_lineto(ctx, s, s->dash_beg.x, s->dash_beg.y, 0);
1514
1
  s->cur.x = s->dash_beg.x;
1515
1
  s->cur.y = s->dash_beg.y;
1516
1
}
1517
1518
static const fz_path_walker dash_proc =
1519
{
1520
  dash_moveto,
1521
  dash_lineto,
1522
  dash_curveto,
1523
  dash_close,
1524
  dash_quadto
1525
};
1526
1527
static int
1528
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)
1529
7.72k
{
1530
7.72k
  struct sctx s;
1531
7.72k
  const fz_path_walker *proc = &stroke_proc;
1532
1533
7.72k
  s.stroke = stroke;
1534
7.72k
  s.rast = rast;
1535
7.72k
  s.ctm = ctm;
1536
7.72k
  s.flatness = flatness;
1537
7.72k
  s.linejoin = stroke->linejoin;
1538
7.72k
  s.linewidth = linewidth * 0.5f; /* hairlines use a different value from the path value */
1539
7.72k
  s.miterlimit = stroke->miterlimit;
1540
7.72k
  s.sn = 0;
1541
7.72k
  s.not_just_moves = 0;
1542
7.72k
  s.toggle = 0;
1543
7.72k
  s.offset = 0;
1544
7.72k
  s.phase = 0;
1545
7.72k
  s.dirn_x = 0;
1546
7.72k
  s.dirn_y = 0;
1547
1548
7.72k
  s.cap = stroke->start_cap;
1549
1550
7.72k
  s.dash_list = NULL;
1551
7.72k
  s.dash_len = stroke->dash_len;
1552
7.72k
  if (s.dash_len > 0)
1553
239
  {
1554
239
    int i;
1555
239
    fz_matrix inv;
1556
239
    float max_expand;
1557
239
    const float *list = stroke->dash_list;
1558
1559
239
    s.dash_total = 0;
1560
717
    for (i = 0; i < s.dash_len; i++)
1561
478
      s.dash_total += list[i];
1562
239
    if (s.dash_total == 0)
1563
0
      return 1;
1564
1565
239
    s.rect = fz_scissor_rasterizer(ctx, rast);
1566
239
    if (fz_try_invert_matrix(&inv, ctm))
1567
0
      return 1;
1568
239
    s.rect = fz_transform_rect(s.rect, inv);
1569
239
    s.rect.x0 -= linewidth;
1570
239
    s.rect.x1 += linewidth;
1571
239
    s.rect.y0 -= linewidth;
1572
239
    s.rect.y1 += linewidth;
1573
1574
239
    max_expand = fz_matrix_max_expansion(ctm);
1575
239
    if (s.dash_total >= 0.01f && s.dash_total * max_expand >= 0.5f)
1576
239
    {
1577
239
      proc = &dash_proc;
1578
239
      s.dash_phase = fmodf(stroke->dash_phase, s.dash_total);
1579
239
      s.dash_list = list;
1580
239
    }
1581
239
  }
1582
1583
7.72k
  s.cur.x = s.cur.y = 0;
1584
7.72k
  fz_walk_path(ctx, path, proc, &s);
1585
7.72k
  fz_stroke_flush(ctx, &s, s.cap, stroke->end_cap);
1586
1587
7.72k
  return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
1588
7.72k
}
1589
1590
int
1591
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)
1592
7.72k
{
1593
7.72k
  int empty;
1594
7.72k
  fz_irect local_bbox;
1595
7.72k
  if (!bbox)
1596
6
    bbox = &local_bbox;
1597
1598
7.72k
  if (fz_reset_rasterizer(ctx, rast, scissor))
1599
0
  {
1600
0
    empty = do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth);
1601
0
    if (empty)
1602
0
      return *bbox = fz_empty_irect, 1;
1603
0
    fz_postindex_rasterizer(ctx, rast);
1604
0
  }
1605
1606
7.72k
  empty = do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth);
1607
7.72k
  if (empty)
1608
85
    return *bbox = fz_empty_irect, 1;
1609
1610
7.63k
  *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
1611
7.63k
  return fz_is_empty_irect(*bbox);
1612
7.72k
}