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