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