comparison mupdf-source/source/fitz/draw-path.c @ 2:b50eed0cc0ef upstream

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