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 | |
48 | static void |
49 | line(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 | |
58 | static void |
59 | bezier(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 | |
112 | static void |
113 | quad(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 | |
151 | typedef struct |
152 | { |
153 | fz_rasterizer *rast; |
154 | fz_matrix ctm; |
155 | float flatness; |
156 | fz_point b; |
157 | fz_point c; |
158 | } |
159 | flatten_arg; |
160 | |
161 | static void |
162 | flatten_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 | |
175 | static void |
176 | flatten_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 | |
185 | static void |
186 | flatten_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 | |
195 | static void |
196 | flatten_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 | |
205 | static void |
206 | flatten_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 | |
215 | static void |
216 | flatten_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 | |
253 | static 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 | |
265 | int |
266 | fz_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 | |
304 | enum { |
305 | ONLY_MOVES = 0, |
306 | NON_NULL_LINE = 1, |
307 | NULL_LINE |
308 | }; |
309 | |
310 | typedef 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 | |
339 | static void |
340 | fz_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 | |
350 | static void |
351 | fz_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 | |
378 | static void |
379 | fz_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 | |
407 | static void |
408 | fz_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 |
473 | static 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; |
507 | tiny: |
508 | *dlx = 0; |
509 | *dly = 0; |
510 | return 1; |
511 | } |
512 | |
513 | static void |
514 | fz_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 | |
646 | static void |
647 | fz_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 | |
709 | static void |
710 | fz_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 | |
736 | static void |
737 | fz_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 | |
749 | static void |
750 | fz_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 | |
761 | static void |
762 | fz_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 | |
813 | static void |
814 | fz_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 | |
840 | static void |
841 | fz_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 | |
894 | static void |
895 | fz_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 | |
933 | static void |
934 | stroke_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 | |
944 | static void |
945 | stroke_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 | |
954 | static void |
955 | stroke_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 | |
964 | static void |
965 | stroke_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 | |
974 | static void |
975 | stroke_close(fz_context *ctx, void *s_) |
976 | { |
977 | sctx *s = (sctx *)s_; |
978 | |
979 | fz_stroke_closepath(ctx, s); |
980 | } |
981 | |
982 | static const fz_path_walker stroke_proc = |
983 | { |
984 | stroke_moveto, |
985 | stroke_lineto, |
986 | stroke_curveto, |
987 | stroke_close, |
988 | stroke_quadto |
989 | }; |
990 | |
991 | static void |
992 | fz_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 | |
1018 | static void |
1019 | fz_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 */ |
1063 | a_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 */ |
1095 | a_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 */ |
1156 | b_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 */ |
1177 | b_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 | { |
1225 | adjust_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 | |
1266 | static void |
1267 | fz_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 | |
1320 | static void |
1321 | fz_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 | |
1359 | static void |
1360 | dash_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 | |
1369 | static void |
1370 | dash_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 | |
1379 | static void |
1380 | dash_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 | |
1389 | static void |
1390 | dash_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 | |
1399 | static void |
1400 | dash_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 | |
1409 | static const fz_path_walker dash_proc = |
1410 | { |
1411 | dash_moveto, |
1412 | dash_lineto, |
1413 | dash_curveto, |
1414 | dash_close, |
1415 | dash_quadto |
1416 | }; |
1417 | |
1418 | static int |
1419 | 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, 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 | |
1483 | int |
1484 | 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, 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 | |