Coverage Report

Created: 2025-01-28 06:17

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