1 #include "mupdf/fitz.h"
2
3 #include <string.h>
4 #include <assert.h>
5
6 // Thoughts for further optimisations:
7 // All paths start with MoveTo. We could probably avoid most cases where
8 // we store that. The next thing after a close must be a move.
9 // Commands are MOVE, LINE, HORIZ, VERT, DEGEN, CURVE, CURVEV, CURVEY, QUAD, RECT.
10 // We'd need to drop 2 to get us down to 3 bits.
11 // Commands can be followed by CLOSE. Use 1 bit for close.
12 // PDF 'RECT' implies close according to the spec, but I suspect
13 // we can ignore this as filling closes implicitly.
14 // We use a single bit in the path header to tell us whether we have
15 // a trailing move. Trailing moves can always be stripped when path
16 // construction completes.
17
18 typedef enum
19 {
20 FZ_MOVETO = 'M',
21 FZ_LINETO = 'L',
22 FZ_DEGENLINETO = 'D',
23 FZ_CURVETO = 'C',
24 FZ_CURVETOV = 'V',
25 FZ_CURVETOY = 'Y',
26 FZ_HORIZTO = 'H',
27 FZ_VERTTO = 'I',
28 FZ_QUADTO = 'Q',
29 FZ_RECTTO = 'R',
30 FZ_MOVETOCLOSE = 'm',
31 FZ_LINETOCLOSE = 'l',
32 FZ_DEGENLINETOCLOSE = 'd',
33 FZ_CURVETOCLOSE = 'c',
34 FZ_CURVETOVCLOSE = 'v',
35 FZ_CURVETOYCLOSE = 'y',
36 FZ_HORIZTOCLOSE = 'h',
37 FZ_VERTTOCLOSE = 'i',
38 FZ_QUADTOCLOSE = 'q',
39 } fz_path_item_kind;
40
41 struct fz_path
42 {
43 int8_t refs;
44 uint8_t packed;
45 int cmd_len, cmd_cap;
46 unsigned char *cmds;
47 int coord_len, coord_cap;
48 float *coords;
49 fz_point current;
50 fz_point begin;
51 };
52
53 typedef struct
54 {
55 int8_t refs;
56 uint8_t packed;
57 uint8_t coord_len;
58 uint8_t cmd_len;
59 } fz_packed_path;
60
61 /*
62 Paths are created UNPACKED. That means we have a fz_path
63 structure with coords and cmds pointing to malloced blocks.
64
65 After they have been completely constructed, callers may choose
66 to 'pack' them into some target block of memory. If if coord_len
67 and cmd_len are both < 256, then they are PACKED_FLAT into an
68 fz_packed_path with the coords and cmds in the bytes afterwards,
69 all inside the target block. If they cannot be accomodated in
70 that way, then they are PACKED_OPEN, where an fz_path is put
71 into the target block, and cmds and coords remain pointers to
72 allocated blocks.
73 */
74 enum
75 {
76 FZ_PATH_UNPACKED = 0,
77 FZ_PATH_PACKED_FLAT = 1,
78 FZ_PATH_PACKED_OPEN = 2
79 };
80
81 #define LAST_CMD(path) ((path)->cmd_len > 0 ? (path)->cmds[(path)->cmd_len-1] : 0)
82
83 fz_path *
fz_new_path(fz_context * ctx)84 fz_new_path(fz_context *ctx)
85 {
86 fz_path *path;
87
88 path = fz_malloc_struct(ctx, fz_path);
89 path->refs = 1;
90 path->packed = FZ_PATH_UNPACKED;
91 path->current.x = 0;
92 path->current.y = 0;
93 path->begin.x = 0;
94 path->begin.y = 0;
95
96 return path;
97 }
98
99 /*
100 Take an additional reference to
101 a path.
102
103 No modifications should be carried out on a path
104 to which more than one reference is held, as
105 this can cause race conditions.
106 */
107 fz_path *
fz_keep_path(fz_context * ctx,const fz_path * pathc)108 fz_keep_path(fz_context *ctx, const fz_path *pathc)
109 {
110 fz_path *path = (fz_path *)pathc; /* Explicit cast away of const */
111
112 if (path == NULL)
113 return NULL;
114 if (path->refs == 1 && path->packed == FZ_PATH_UNPACKED)
115 fz_trim_path(ctx, path);
116 return fz_keep_imp8(ctx, path, &path->refs);
117 }
118
119 void
fz_drop_path(fz_context * ctx,const fz_path * pathc)120 fz_drop_path(fz_context *ctx, const fz_path *pathc)
121 {
122 fz_path *path = (fz_path *)pathc; /* Explicit cast away of const */
123
124 if (fz_drop_imp8(ctx, path, &path->refs))
125 {
126 if (path->packed != FZ_PATH_PACKED_FLAT)
127 {
128 fz_free(ctx, path->cmds);
129 fz_free(ctx, path->coords);
130 }
131 if (path->packed == FZ_PATH_UNPACKED)
132 fz_free(ctx, path);
133 }
134 }
135
fz_packed_path_size(const fz_path * path)136 int fz_packed_path_size(const fz_path *path)
137 {
138 switch (path->packed)
139 {
140 case FZ_PATH_UNPACKED:
141 if (path->cmd_len > 255 || path->coord_len > 255)
142 return sizeof(fz_path);
143 return sizeof(fz_packed_path) + sizeof(float) * path->coord_len + sizeof(uint8_t) * path->cmd_len;
144 case FZ_PATH_PACKED_OPEN:
145 return sizeof(fz_path);
146 case FZ_PATH_PACKED_FLAT:
147 {
148 fz_packed_path *pack = (fz_packed_path *)path;
149 return sizeof(fz_packed_path) + sizeof(float) * pack->coord_len + sizeof(uint8_t) * pack->cmd_len;
150 }
151 default:
152 assert("This never happens" == NULL);
153 return 0;
154 }
155 }
156
157 size_t
fz_pack_path(fz_context * ctx,uint8_t * pack_,size_t max,const fz_path * path)158 fz_pack_path(fz_context *ctx, uint8_t *pack_, size_t max, const fz_path *path)
159 {
160 uint8_t *ptr;
161 size_t size;
162
163 if (path->packed == FZ_PATH_PACKED_FLAT)
164 {
165 fz_packed_path *pack = (fz_packed_path *)path;
166 fz_packed_path *out = (fz_packed_path *)pack_;
167 size = sizeof(fz_packed_path) + sizeof(float) * pack->coord_len + sizeof(uint8_t) * pack->cmd_len;
168
169 if (size > max)
170 fz_throw(ctx, FZ_ERROR_GENERIC, "Can't pack a path that small!");
171
172 if (out)
173 {
174 out->refs = 1;
175 out->packed = FZ_PATH_PACKED_FLAT;
176 out->coord_len = pack->coord_len;
177 out->cmd_len = pack->cmd_len;
178 memcpy(&out[1], &pack[1], size - sizeof(*out));
179 }
180 return size;
181 }
182
183 size = sizeof(fz_packed_path) + sizeof(float) * path->coord_len + sizeof(uint8_t) * path->cmd_len;
184
185 /* If the path can't be packed flat, then pack it open */
186 if (path->cmd_len > 255 || path->coord_len > 255 || size > max)
187 {
188 fz_path *pack = (fz_path *)pack_;
189
190 if (sizeof(fz_path) > max)
191 fz_throw(ctx, FZ_ERROR_GENERIC, "Can't pack a path that small!");
192
193 if (pack != NULL)
194 {
195 pack->refs = 1;
196 pack->packed = FZ_PATH_PACKED_OPEN;
197 pack->current.x = 0;
198 pack->current.y = 0;
199 pack->begin.x = 0;
200 pack->begin.y = 0;
201 pack->coord_cap = path->coord_len;
202 pack->coord_len = path->coord_len;
203 pack->cmd_cap = path->cmd_len;
204 pack->cmd_len = path->cmd_len;
205 pack->coords = Memento_label(fz_malloc_array(ctx, path->coord_len, float), "path_packed_coords");
206 fz_try(ctx)
207 {
208 pack->cmds = Memento_label(fz_malloc_array(ctx, path->cmd_len, uint8_t), "path_packed_cmds");
209 }
210 fz_catch(ctx)
211 {
212 fz_free(ctx, pack->coords);
213 fz_rethrow(ctx);
214 }
215 memcpy(pack->coords, path->coords, sizeof(float) * path->coord_len);
216 memcpy(pack->cmds, path->cmds, sizeof(uint8_t) * path->cmd_len);
217 }
218 return sizeof(fz_path);
219 }
220 else
221 {
222 fz_packed_path *pack = (fz_packed_path *)pack_;
223
224 if (pack != NULL)
225 {
226 pack->refs = 1;
227 pack->packed = FZ_PATH_PACKED_FLAT;
228 pack->cmd_len = path->cmd_len;
229 pack->coord_len = path->coord_len;
230 ptr = (uint8_t *)&pack[1];
231 memcpy(ptr, path->coords, sizeof(float) * path->coord_len);
232 ptr += sizeof(float) * path->coord_len;
233 memcpy(ptr, path->cmds, sizeof(uint8_t) * path->cmd_len);
234 }
235
236 return size;
237 }
238 }
239
240 static void
push_cmd(fz_context * ctx,fz_path * path,int cmd)241 push_cmd(fz_context *ctx, fz_path *path, int cmd)
242 {
243 if (path->refs != 1)
244 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot modify shared paths");
245
246 if (path->cmd_len + 1 >= path->cmd_cap)
247 {
248 int new_cmd_cap = fz_maxi(16, path->cmd_cap * 2);
249 path->cmds = fz_realloc_array(ctx, path->cmds, new_cmd_cap, unsigned char);
250 path->cmd_cap = new_cmd_cap;
251 }
252
253 path->cmds[path->cmd_len++] = cmd;
254 }
255
256 static void
push_coord(fz_context * ctx,fz_path * path,float x,float y)257 push_coord(fz_context *ctx, fz_path *path, float x, float y)
258 {
259 if (path->coord_len + 2 >= path->coord_cap)
260 {
261 int new_coord_cap = fz_maxi(32, path->coord_cap * 2);
262 path->coords = fz_realloc_array(ctx, path->coords, new_coord_cap, float);
263 path->coord_cap = new_coord_cap;
264 }
265
266 path->coords[path->coord_len++] = x;
267 path->coords[path->coord_len++] = y;
268
269 path->current.x = x;
270 path->current.y = y;
271 }
272
273 static void
push_ord(fz_context * ctx,fz_path * path,float xy,int isx)274 push_ord(fz_context *ctx, fz_path *path, float xy, int isx)
275 {
276 if (path->coord_len + 1 >= path->coord_cap)
277 {
278 int new_coord_cap = fz_maxi(32, path->coord_cap * 2);
279 path->coords = fz_realloc_array(ctx, path->coords, new_coord_cap, float);
280 path->coord_cap = new_coord_cap;
281 }
282
283 path->coords[path->coord_len++] = xy;
284
285 if (isx)
286 path->current.x = xy;
287 else
288 path->current.y = xy;
289 }
290
291 fz_point
fz_currentpoint(fz_context * ctx,fz_path * path)292 fz_currentpoint(fz_context *ctx, fz_path *path)
293 {
294 return path->current;
295 }
296
297 void
fz_moveto(fz_context * ctx,fz_path * path,float x,float y)298 fz_moveto(fz_context *ctx, fz_path *path, float x, float y)
299 {
300 if (path->packed)
301 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
302
303 if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO)
304 {
305 /* Collapse moveto followed by moveto. */
306 path->coords[path->coord_len-2] = x;
307 path->coords[path->coord_len-1] = y;
308 path->current.x = x;
309 path->current.y = y;
310 path->begin = path->current;
311 return;
312 }
313
314 push_cmd(ctx, path, FZ_MOVETO);
315 push_coord(ctx, path, x, y);
316
317 path->begin = path->current;
318 }
319
320 void
fz_lineto(fz_context * ctx,fz_path * path,float x,float y)321 fz_lineto(fz_context *ctx, fz_path *path, float x, float y)
322 {
323 float x0, y0;
324
325 if (path->packed)
326 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
327
328 x0 = path->current.x;
329 y0 = path->current.y;
330
331 if (path->cmd_len == 0)
332 {
333 fz_warn(ctx, "lineto with no current point");
334 return;
335 }
336
337 /* (Anything other than MoveTo) followed by (LineTo the same place) is a nop */
338 if (LAST_CMD(path) != FZ_MOVETO && x0 == x && y0 == y)
339 return;
340
341 if (x0 == x)
342 {
343 if (y0 == y)
344 {
345 if (LAST_CMD(path) != FZ_MOVETO)
346 return;
347 push_cmd(ctx, path, FZ_DEGENLINETO);
348 }
349 else
350 {
351 push_cmd(ctx, path, FZ_VERTTO);
352 push_ord(ctx, path, y, 0);
353 }
354 }
355 else if (y0 == y)
356 {
357 push_cmd(ctx, path, FZ_HORIZTO);
358 push_ord(ctx, path, x, 1);
359 }
360 else
361 {
362 push_cmd(ctx, path, FZ_LINETO);
363 push_coord(ctx, path, x, y);
364 }
365 }
366
367 void
fz_curveto(fz_context * ctx,fz_path * path,float x1,float y1,float x2,float y2,float x3,float y3)368 fz_curveto(fz_context *ctx, fz_path *path,
369 float x1, float y1,
370 float x2, float y2,
371 float x3, float y3)
372 {
373 float x0, y0;
374
375 if (path->packed)
376 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
377
378 x0 = path->current.x;
379 y0 = path->current.y;
380
381 if (path->cmd_len == 0)
382 {
383 fz_warn(ctx, "curveto with no current point");
384 return;
385 }
386
387 /* Check for degenerate cases: */
388 if (x0 == x1 && y0 == y1)
389 {
390 if (x2 == x3 && y2 == y3)
391 {
392 /* If (x1,y1)==(x2,y2) and prev wasn't a moveto, then skip */
393 if (x1 == x2 && y1 == y2 && LAST_CMD(path) != FZ_MOVETO)
394 return;
395 /* Otherwise a line will suffice */
396 fz_lineto(ctx, path, x3, y3);
397 }
398 else if (x1 == x2 && y1 == y2)
399 {
400 /* A line will suffice */
401 fz_lineto(ctx, path, x3, y3);
402 }
403 else
404 fz_curvetov(ctx, path, x2, y2, x3, y3);
405 return;
406 }
407 else if (x2 == x3 && y2 == y3)
408 {
409 if (x1 == x2 && y1 == y2)
410 {
411 /* A line will suffice */
412 fz_lineto(ctx, path, x3, y3);
413 }
414 else
415 fz_curvetoy(ctx, path, x1, y1, x3, y3);
416 return;
417 }
418
419 push_cmd(ctx, path, FZ_CURVETO);
420 push_coord(ctx, path, x1, y1);
421 push_coord(ctx, path, x2, y2);
422 push_coord(ctx, path, x3, y3);
423 }
424
425 void
fz_quadto(fz_context * ctx,fz_path * path,float x1,float y1,float x2,float y2)426 fz_quadto(fz_context *ctx, fz_path *path,
427 float x1, float y1,
428 float x2, float y2)
429 {
430 float x0, y0;
431
432 if (path->packed)
433 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
434
435 x0 = path->current.x;
436 y0 = path->current.y;
437
438 if (path->cmd_len == 0)
439 {
440 fz_warn(ctx, "quadto with no current point");
441 return;
442 }
443
444 /* Check for degenerate cases: */
445 if ((x0 == x1 && y0 == y1) || (x1 == x2 && y1 == y2))
446 {
447 if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO)
448 return;
449 /* A line will suffice */
450 fz_lineto(ctx, path, x2, y2);
451 return;
452 }
453
454 push_cmd(ctx, path, FZ_QUADTO);
455 push_coord(ctx, path, x1, y1);
456 push_coord(ctx, path, x2, y2);
457 }
458
459 void
fz_curvetov(fz_context * ctx,fz_path * path,float x2,float y2,float x3,float y3)460 fz_curvetov(fz_context *ctx, fz_path *path, float x2, float y2, float x3, float y3)
461 {
462 float x0, y0;
463
464 if (path->packed)
465 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
466
467 x0 = path->current.x;
468 y0 = path->current.y;
469
470 if (path->cmd_len == 0)
471 {
472 fz_warn(ctx, "curveto with no current point");
473 return;
474 }
475
476 /* Check for degenerate cases: */
477 if (x2 == x3 && y2 == y3)
478 {
479 /* If (x0,y0)==(x2,y2) and prev wasn't a moveto, then skip */
480 if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO)
481 return;
482 /* Otherwise a line will suffice */
483 fz_lineto(ctx, path, x3, y3);
484 }
485 else if (x0 == x2 && y0 == y2)
486 {
487 /* A line will suffice */
488 fz_lineto(ctx, path, x3, y3);
489 }
490
491 push_cmd(ctx, path, FZ_CURVETOV);
492 push_coord(ctx, path, x2, y2);
493 push_coord(ctx, path, x3, y3);
494 }
495
496 void
fz_curvetoy(fz_context * ctx,fz_path * path,float x1,float y1,float x3,float y3)497 fz_curvetoy(fz_context *ctx, fz_path *path, float x1, float y1, float x3, float y3)
498 {
499 float x0, y0;
500
501 if (path->packed)
502 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
503
504 x0 = path->current.x;
505 y0 = path->current.y;
506
507 if (path->cmd_len == 0)
508 {
509 fz_warn(ctx, "curveto with no current point");
510 return;
511 }
512
513 /* Check for degenerate cases: */
514 if (x1 == x3 && y1 == y3)
515 {
516 /* If (x0,y0)==(x1,y1) and prev wasn't a moveto, then skip */
517 if (x0 == x1 && y0 == y1 && LAST_CMD(path) != FZ_MOVETO)
518 return;
519 /* Otherwise a line will suffice */
520 fz_lineto(ctx, path, x3, y3);
521 }
522
523 push_cmd(ctx, path, FZ_CURVETOY);
524 push_coord(ctx, path, x1, y1);
525 push_coord(ctx, path, x3, y3);
526 }
527
528 void
fz_closepath(fz_context * ctx,fz_path * path)529 fz_closepath(fz_context *ctx, fz_path *path)
530 {
531 uint8_t rep;
532
533 if (path->packed)
534 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
535
536 if (path->cmd_len == 0)
537 {
538 fz_warn(ctx, "closepath with no current point");
539 return;
540 }
541
542 switch(LAST_CMD(path))
543 {
544 case FZ_MOVETO:
545 rep = FZ_MOVETOCLOSE;
546 break;
547 case FZ_LINETO:
548 rep = FZ_LINETOCLOSE;
549 break;
550 case FZ_DEGENLINETO:
551 rep = FZ_DEGENLINETOCLOSE;
552 break;
553 case FZ_CURVETO:
554 rep = FZ_CURVETOCLOSE;
555 break;
556 case FZ_CURVETOV:
557 rep = FZ_CURVETOVCLOSE;
558 break;
559 case FZ_CURVETOY:
560 rep = FZ_CURVETOYCLOSE;
561 break;
562 case FZ_HORIZTO:
563 rep = FZ_HORIZTOCLOSE;
564 break;
565 case FZ_VERTTO:
566 rep = FZ_VERTTOCLOSE;
567 break;
568 case FZ_QUADTO:
569 rep = FZ_QUADTOCLOSE;
570 break;
571 case FZ_RECTTO:
572 /* RectTo implies close */
573 return;
574 case FZ_MOVETOCLOSE:
575 case FZ_LINETOCLOSE:
576 case FZ_DEGENLINETOCLOSE:
577 case FZ_CURVETOCLOSE:
578 case FZ_CURVETOVCLOSE:
579 case FZ_CURVETOYCLOSE:
580 case FZ_HORIZTOCLOSE:
581 case FZ_VERTTOCLOSE:
582 case FZ_QUADTOCLOSE:
583 /* CLOSE following a CLOSE is a NOP */
584 return;
585 default: /* default never happens */
586 case 0:
587 /* Closing an empty path is a NOP */
588 return;
589 }
590
591 path->cmds[path->cmd_len-1] = rep;
592
593 path->current = path->begin;
594 }
595
596 void
fz_rectto(fz_context * ctx,fz_path * path,float x1,float y1,float x2,float y2)597 fz_rectto(fz_context *ctx, fz_path *path, float x1, float y1, float x2, float y2)
598 {
599 if (path->packed)
600 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
601
602 if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO)
603 {
604 /* Collapse moveto followed by rectto. */
605 path->coord_len -= 2;
606 path->cmd_len--;
607 }
608
609 push_cmd(ctx, path, FZ_RECTTO);
610 push_coord(ctx, path, x1, y1);
611 push_coord(ctx, path, x2, y2);
612
613 path->current = path->begin;
614 }
615
bound_expand(fz_rect * r,fz_point p)616 static inline void bound_expand(fz_rect *r, fz_point p)
617 {
618 if (p.x < r->x0) r->x0 = p.x;
619 if (p.y < r->y0) r->y0 = p.y;
620 if (p.x > r->x1) r->x1 = p.x;
621 if (p.y > r->y1) r->y1 = p.y;
622 }
623
fz_walk_path(fz_context * ctx,const fz_path * path,const fz_path_walker * proc,void * arg)624 void fz_walk_path(fz_context *ctx, const fz_path *path, const fz_path_walker *proc, void *arg)
625 {
626 int i, k, cmd_len;
627 float x, y, sx, sy;
628 uint8_t *cmds;
629 float *coords;
630
631 switch (path->packed)
632 {
633 case FZ_PATH_UNPACKED:
634 case FZ_PATH_PACKED_OPEN:
635 cmd_len = path->cmd_len;
636 coords = path->coords;
637 cmds = path->cmds;
638 break;
639 case FZ_PATH_PACKED_FLAT:
640 cmd_len = ((fz_packed_path *)path)->cmd_len;
641 coords = (float *)&((fz_packed_path *)path)[1];
642 cmds = (uint8_t *)&coords[((fz_packed_path *)path)->coord_len];
643 break;
644 default:
645 assert("This never happens" == NULL);
646 return;
647 }
648
649 if (cmd_len == 0)
650 return;
651
652 for (k=0, i = 0; i < cmd_len; i++)
653 {
654 uint8_t cmd = cmds[i];
655
656 switch (cmd)
657 {
658 case FZ_CURVETO:
659 case FZ_CURVETOCLOSE:
660 proc->curveto(ctx, arg,
661 coords[k],
662 coords[k+1],
663 coords[k+2],
664 coords[k+3],
665 x = coords[k+4],
666 y = coords[k+5]);
667 k += 6;
668 if (cmd == FZ_CURVETOCLOSE)
669 {
670 if (proc->closepath)
671 proc->closepath(ctx, arg);
672 x = sx;
673 y = sy;
674 }
675 break;
676 case FZ_CURVETOV:
677 case FZ_CURVETOVCLOSE:
678 if (proc->curvetov)
679 proc->curvetov(ctx, arg,
680 coords[k],
681 coords[k+1],
682 x = coords[k+2],
683 y = coords[k+3]);
684 else
685 {
686 proc->curveto(ctx, arg,
687 x,
688 y,
689 coords[k],
690 coords[k+1],
691 coords[k+2],
692 coords[k+3]);
693 x = coords[k+2];
694 y = coords[k+3];
695 }
696 k += 4;
697 if (cmd == FZ_CURVETOVCLOSE)
698 {
699 if (proc->closepath)
700 proc->closepath(ctx, arg);
701 x = sx;
702 y = sy;
703 }
704 break;
705 case FZ_CURVETOY:
706 case FZ_CURVETOYCLOSE:
707 if (proc->curvetoy)
708 proc->curvetoy(ctx, arg,
709 coords[k],
710 coords[k+1],
711 x = coords[k+2],
712 y = coords[k+3]);
713 else
714 proc->curveto(ctx, arg,
715 coords[k],
716 coords[k+1],
717 coords[k+2],
718 coords[k+3],
719 x = coords[k+2],
720 y = coords[k+3]);
721 k += 4;
722 if (cmd == FZ_CURVETOYCLOSE)
723 {
724 if (proc->closepath)
725 proc->closepath(ctx, arg);
726 x = sx;
727 y = sy;
728 }
729 break;
730 case FZ_QUADTO:
731 case FZ_QUADTOCLOSE:
732 if (proc->quadto)
733 proc->quadto(ctx, arg,
734 coords[k],
735 coords[k+1],
736 x = coords[k+2],
737 y = coords[k+3]);
738 else
739 {
740 float c2x = coords[k] * 2;
741 float c2y = coords[k+1] * 2;
742 float c1x = (x + c2x) / 3;
743 float c1y = (y + c2y) / 3;
744 x = coords[k+2];
745 y = coords[k+3];
746 c2x = (c2x + x) / 3;
747 c2y = (c2y + y) / 3;
748
749 proc->curveto(ctx, arg,
750 c1x,
751 c1y,
752 c2x,
753 c2y,
754 x,
755 y);
756 }
757 k += 4;
758 if (cmd == FZ_QUADTOCLOSE)
759 {
760 if (proc->closepath)
761 proc->closepath(ctx, arg);
762 x = sx;
763 y = sy;
764 }
765 break;
766 case FZ_MOVETO:
767 case FZ_MOVETOCLOSE:
768 proc->moveto(ctx, arg,
769 x = coords[k],
770 y = coords[k+1]);
771 k += 2;
772 sx = x;
773 sy = y;
774 if (cmd == FZ_MOVETOCLOSE)
775 {
776 if (proc->closepath)
777 proc->closepath(ctx, arg);
778 x = sx;
779 y = sy;
780 }
781 break;
782 case FZ_LINETO:
783 case FZ_LINETOCLOSE:
784 proc->lineto(ctx, arg,
785 x = coords[k],
786 y = coords[k+1]);
787 k += 2;
788 if (cmd == FZ_LINETOCLOSE)
789 {
790 if (proc->closepath)
791 proc->closepath(ctx, arg);
792 x = sx;
793 y = sy;
794 }
795 break;
796 case FZ_HORIZTO:
797 case FZ_HORIZTOCLOSE:
798 proc->lineto(ctx, arg,
799 x = coords[k],
800 y);
801 k += 1;
802 if (cmd == FZ_HORIZTOCLOSE)
803 {
804 if (proc->closepath)
805 proc->closepath(ctx, arg);
806 x = sx;
807 y = sy;
808 }
809 break;
810 case FZ_VERTTO:
811 case FZ_VERTTOCLOSE:
812 proc->lineto(ctx, arg,
813 x,
814 y = coords[k]);
815 k += 1;
816 if (cmd == FZ_VERTTOCLOSE)
817 {
818 if (proc->closepath)
819 proc->closepath(ctx, arg);
820 x = sx;
821 y = sy;
822 }
823 break;
824 case FZ_DEGENLINETO:
825 case FZ_DEGENLINETOCLOSE:
826 proc->lineto(ctx, arg,
827 x,
828 y);
829 if (cmd == FZ_DEGENLINETOCLOSE)
830 {
831 if (proc->closepath)
832 proc->closepath(ctx, arg);
833 x = sx;
834 y = sy;
835 }
836 break;
837 case FZ_RECTTO:
838 if (proc->rectto)
839 {
840 proc->rectto(ctx, arg,
841 x = coords[k],
842 y = coords[k+1],
843 coords[k+2],
844 coords[k+3]);
845 }
846 else
847 {
848 proc->moveto(ctx, arg,
849 x = coords[k],
850 y = coords[k+1]);
851 proc->lineto(ctx, arg,
852 coords[k+2],
853 coords[k+1]);
854 proc->lineto(ctx, arg,
855 coords[k+2],
856 coords[k+3]);
857 proc->lineto(ctx, arg,
858 coords[k],
859 coords[k+3]);
860 if (proc->closepath)
861 proc->closepath(ctx, arg);
862 }
863 sx = x;
864 sy = y;
865 k += 4;
866 break;
867 }
868 }
869 }
870
871 typedef struct
872 {
873 fz_matrix ctm;
874 fz_rect rect;
875 fz_point move;
876 int trailing_move;
877 int first;
878 } bound_path_arg;
879
880 static void
bound_moveto(fz_context * ctx,void * arg_,float x,float y)881 bound_moveto(fz_context *ctx, void *arg_, float x, float y)
882 {
883 bound_path_arg *arg = (bound_path_arg *)arg_;
884 arg->move = fz_transform_point_xy(x, y, arg->ctm);
885 arg->trailing_move = 1;
886 }
887
888 static void
bound_lineto(fz_context * ctx,void * arg_,float x,float y)889 bound_lineto(fz_context *ctx, void *arg_, float x, float y)
890 {
891 bound_path_arg *arg = (bound_path_arg *)arg_;
892 fz_point p = fz_transform_point_xy(x, y, arg->ctm);
893 if (arg->first)
894 {
895 arg->rect.x0 = arg->rect.x1 = p.x;
896 arg->rect.y0 = arg->rect.y1 = p.y;
897 arg->first = 0;
898 }
899 else
900 bound_expand(&arg->rect, p);
901 if (arg->trailing_move)
902 {
903 arg->trailing_move = 0;
904 bound_expand(&arg->rect, arg->move);
905 }
906 }
907
908 static void
bound_curveto(fz_context * ctx,void * arg_,float x1,float y1,float x2,float y2,float x3,float y3)909 bound_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3)
910 {
911 bound_path_arg *arg = (bound_path_arg *)arg_;
912 fz_point p = fz_transform_point_xy(x1, y1, arg->ctm);
913 if (arg->first)
914 {
915 arg->rect.x0 = arg->rect.x1 = p.x;
916 arg->rect.y0 = arg->rect.y1 = p.y;
917 arg->first = 0;
918 }
919 else
920 bound_expand(&arg->rect, p);
921 bound_expand(&arg->rect, fz_transform_point_xy(x2, y2, arg->ctm));
922 bound_expand(&arg->rect, fz_transform_point_xy(x3, y3, arg->ctm));
923 if (arg->trailing_move)
924 {
925 arg->trailing_move = 0;
926 bound_expand(&arg->rect, arg->move);
927 }
928 }
929
930 static const fz_path_walker bound_path_walker =
931 {
932 bound_moveto,
933 bound_lineto,
934 bound_curveto,
935 NULL
936 };
937
938 fz_rect
fz_bound_path(fz_context * ctx,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm)939 fz_bound_path(fz_context *ctx, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm)
940 {
941 bound_path_arg arg;
942
943 arg.ctm = ctm;
944 arg.rect = fz_empty_rect;
945 arg.trailing_move = 0;
946 arg.first = 1;
947
948 fz_walk_path(ctx, path, &bound_path_walker, &arg);
949
950 if (!arg.first && stroke)
951 {
952 arg.rect = fz_adjust_rect_for_stroke(ctx, arg.rect, stroke, ctm);
953 }
954
955 return arg.rect;
956 }
957
958 fz_rect
fz_adjust_rect_for_stroke(fz_context * ctx,fz_rect r,const fz_stroke_state * stroke,fz_matrix ctm)959 fz_adjust_rect_for_stroke(fz_context *ctx, fz_rect r, const fz_stroke_state *stroke, fz_matrix ctm)
960 {
961 float expand;
962
963 if (!stroke)
964 return r;
965
966 expand = stroke->linewidth;
967 if (expand == 0)
968 expand = 1.0f;
969 expand *= fz_matrix_max_expansion(ctm);
970 if ((stroke->linejoin == FZ_LINEJOIN_MITER || stroke->linejoin == FZ_LINEJOIN_MITER_XPS) && stroke->miterlimit > 1)
971 expand *= stroke->miterlimit;
972
973 r.x0 -= expand;
974 r.y0 -= expand;
975 r.x1 += expand;
976 r.y1 += expand;
977 return r;
978 }
979
980 void
fz_transform_path(fz_context * ctx,fz_path * path,fz_matrix ctm)981 fz_transform_path(fz_context *ctx, fz_path *path, fz_matrix ctm)
982 {
983 int i, k, n;
984 fz_point p, p1, p2, p3, q, s;
985
986 if (path->packed)
987 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot transform a packed path");
988
989 if (ctm.b == 0 && ctm.c == 0)
990 {
991 /* Simple, in place transform */
992 i = 0;
993 k = 0;
994 while (i < path->cmd_len)
995 {
996 uint8_t cmd = path->cmds[i];
997
998 switch (cmd)
999 {
1000 case FZ_MOVETO:
1001 case FZ_LINETO:
1002 case FZ_MOVETOCLOSE:
1003 case FZ_LINETOCLOSE:
1004 n = 1;
1005 break;
1006 case FZ_DEGENLINETO:
1007 case FZ_DEGENLINETOCLOSE:
1008 n = 0;
1009 break;
1010 case FZ_CURVETO:
1011 case FZ_CURVETOCLOSE:
1012 n = 3;
1013 break;
1014 case FZ_RECTTO:
1015 s.x = path->coords[k];
1016 s.y = path->coords[k+1];
1017 n = 2;
1018 break;
1019 case FZ_CURVETOV:
1020 case FZ_CURVETOY:
1021 case FZ_QUADTO:
1022 case FZ_CURVETOVCLOSE:
1023 case FZ_CURVETOYCLOSE:
1024 case FZ_QUADTOCLOSE:
1025 n = 2;
1026 break;
1027 case FZ_HORIZTO:
1028 case FZ_HORIZTOCLOSE:
1029 q.x = path->coords[k];
1030 p = fz_transform_point(q, ctm);
1031 path->coords[k++] = p.x;
1032 n = 0;
1033 break;
1034 case FZ_VERTTO:
1035 case FZ_VERTTOCLOSE:
1036 q.y = path->coords[k];
1037 p = fz_transform_point(q, ctm);
1038 path->coords[k++] = p.y;
1039 n = 0;
1040 break;
1041 default:
1042 assert("Unknown path cmd" == NULL);
1043 }
1044 while (n > 0)
1045 {
1046 q.x = path->coords[k];
1047 q.y = path->coords[k+1];
1048 p = fz_transform_point(q, ctm);
1049 path->coords[k++] = p.x;
1050 path->coords[k++] = p.y;
1051 n--;
1052 }
1053 switch (cmd)
1054 {
1055 case FZ_MOVETO:
1056 case FZ_MOVETOCLOSE:
1057 s = q;
1058 break;
1059 case FZ_LINETOCLOSE:
1060 case FZ_DEGENLINETOCLOSE:
1061 case FZ_CURVETOCLOSE:
1062 case FZ_CURVETOVCLOSE:
1063 case FZ_CURVETOYCLOSE:
1064 case FZ_QUADTOCLOSE:
1065 case FZ_HORIZTOCLOSE:
1066 case FZ_VERTTOCLOSE:
1067 case FZ_RECTTO:
1068 q = s;
1069 break;
1070 }
1071 i++;
1072 }
1073 }
1074 else if (ctm.a == 0 && ctm.d == 0)
1075 {
1076 /* In place transform with command rewriting */
1077 i = 0;
1078 k = 0;
1079 while (i < path->cmd_len)
1080 {
1081 uint8_t cmd = path->cmds[i];
1082
1083 switch (cmd)
1084 {
1085 case FZ_MOVETO:
1086 case FZ_LINETO:
1087 case FZ_MOVETOCLOSE:
1088 case FZ_LINETOCLOSE:
1089 n = 1;
1090 break;
1091 case FZ_DEGENLINETO:
1092 case FZ_DEGENLINETOCLOSE:
1093 n = 0;
1094 break;
1095 case FZ_CURVETO:
1096 case FZ_CURVETOCLOSE:
1097 n = 3;
1098 break;
1099 case FZ_RECTTO:
1100 s.x = path->coords[k];
1101 s.y = path->coords[k+1];
1102 n = 2;
1103 break;
1104 case FZ_CURVETOV:
1105 case FZ_CURVETOY:
1106 case FZ_QUADTO:
1107 case FZ_CURVETOVCLOSE:
1108 case FZ_CURVETOYCLOSE:
1109 case FZ_QUADTOCLOSE:
1110 n = 2;
1111 break;
1112 case FZ_HORIZTO:
1113 q.x = path->coords[k];
1114 p = fz_transform_point(q, ctm);
1115 path->coords[k++] = p.y;
1116 path->cmds[i] = FZ_VERTTO;
1117 n = 0;
1118 break;
1119 case FZ_HORIZTOCLOSE:
1120 q.x = path->coords[k];
1121 p = fz_transform_point(q, ctm);
1122 path->coords[k++] = p.y;
1123 path->cmds[i] = FZ_VERTTOCLOSE;
1124 n = 0;
1125 break;
1126 case FZ_VERTTO:
1127 q.y = path->coords[k];
1128 p = fz_transform_point(q, ctm);
1129 path->coords[k++] = p.x;
1130 path->cmds[i] = FZ_HORIZTO;
1131 n = 0;
1132 break;
1133 case FZ_VERTTOCLOSE:
1134 q.y = path->coords[k];
1135 p = fz_transform_point(q, ctm);
1136 path->coords[k++] = p.x;
1137 path->cmds[i] = FZ_HORIZTOCLOSE;
1138 n = 0;
1139 break;
1140 default:
1141 assert("Unknown path cmd" == NULL);
1142 }
1143 while (n > 0)
1144 {
1145 q.x = path->coords[k];
1146 q.y = path->coords[k+1];
1147 p = fz_transform_point(q, ctm);
1148 path->coords[k++] = p.x;
1149 path->coords[k++] = p.y;
1150 n--;
1151 }
1152 switch (cmd)
1153 {
1154 case FZ_MOVETO:
1155 case FZ_MOVETOCLOSE:
1156 s = q;
1157 break;
1158 case FZ_LINETOCLOSE:
1159 case FZ_DEGENLINETOCLOSE:
1160 case FZ_CURVETOCLOSE:
1161 case FZ_CURVETOVCLOSE:
1162 case FZ_CURVETOYCLOSE:
1163 case FZ_QUADTOCLOSE:
1164 case FZ_HORIZTOCLOSE:
1165 case FZ_VERTTOCLOSE:
1166 case FZ_RECTTO:
1167 q = s;
1168 break;
1169 }
1170 i++;
1171 }
1172 }
1173 else
1174 {
1175 int extra_coord = 0;
1176 int extra_cmd = 0;
1177 int coord_read, coord_write, cmd_read, cmd_write;
1178
1179 /* General case. Have to allow for rects/horiz/verts
1180 * becoming non-rects/horiz/verts. */
1181 for (i = 0; i < path->cmd_len; i++)
1182 {
1183 uint8_t cmd = path->cmds[i];
1184 switch (cmd)
1185 {
1186 case FZ_HORIZTO:
1187 case FZ_VERTTO:
1188 case FZ_HORIZTOCLOSE:
1189 case FZ_VERTTOCLOSE:
1190 extra_coord += 1;
1191 break;
1192 case FZ_RECTTO:
1193 extra_coord += 2;
1194 extra_cmd += 3;
1195 break;
1196 default:
1197 /* Do nothing */
1198 break;
1199 }
1200 }
1201 if (path->cmd_len + extra_cmd < path->cmd_cap)
1202 {
1203 path->cmds = fz_realloc_array(ctx, path->cmds, path->cmd_len + extra_cmd, unsigned char);
1204 path->cmd_cap = path->cmd_len + extra_cmd;
1205 }
1206 if (path->coord_len + extra_coord < path->coord_cap)
1207 {
1208 path->coords = fz_realloc_array(ctx, path->coords, path->coord_len + extra_coord, float);
1209 path->coord_cap = path->coord_len + extra_coord;
1210 }
1211 memmove(path->cmds + extra_cmd, path->cmds, path->cmd_len * sizeof(unsigned char));
1212 path->cmd_len += extra_cmd;
1213 memmove(path->coords + extra_coord, path->coords, path->coord_len * sizeof(float));
1214 path->coord_len += extra_coord;
1215
1216 for (cmd_write = 0, cmd_read = extra_cmd, coord_write = 0, coord_read = extra_coord; cmd_read < path->cmd_len; i += 2)
1217 {
1218 uint8_t cmd = path->cmds[cmd_write++] = path->cmds[cmd_read++];
1219
1220 switch (cmd)
1221 {
1222 case FZ_MOVETO:
1223 case FZ_LINETO:
1224 case FZ_MOVETOCLOSE:
1225 case FZ_LINETOCLOSE:
1226 n = 1;
1227 break;
1228 case FZ_DEGENLINETO:
1229 case FZ_DEGENLINETOCLOSE:
1230 n = 0;
1231 break;
1232 case FZ_CURVETO:
1233 case FZ_CURVETOCLOSE:
1234 n = 3;
1235 break;
1236 case FZ_CURVETOV:
1237 case FZ_CURVETOY:
1238 case FZ_QUADTO:
1239 case FZ_CURVETOVCLOSE:
1240 case FZ_CURVETOYCLOSE:
1241 case FZ_QUADTOCLOSE:
1242 n = 2;
1243 break;
1244 case FZ_RECTTO:
1245 p.x = path->coords[coord_read++];
1246 p.y = path->coords[coord_read++];
1247 p2.x = path->coords[coord_read++];
1248 p2.y = path->coords[coord_read++];
1249 p1.x = p2.x;
1250 p1.y = p.y;
1251 p3.x = p.x;
1252 p3.y = p2.y;
1253 s = p;
1254 p = fz_transform_point(p, ctm);
1255 p1 = fz_transform_point(p1, ctm);
1256 p2 = fz_transform_point(p2, ctm);
1257 p3 = fz_transform_point(p3, ctm);
1258 path->coords[coord_write++] = p.x;
1259 path->coords[coord_write++] = p.y;
1260 path->coords[coord_write++] = p1.x;
1261 path->coords[coord_write++] = p1.y;
1262 path->coords[coord_write++] = p2.x;
1263 path->coords[coord_write++] = p2.y;
1264 path->coords[coord_write++] = p3.x;
1265 path->coords[coord_write++] = p3.y;
1266 path->cmds[cmd_write-1] = FZ_MOVETO;
1267 path->cmds[cmd_write++] = FZ_LINETO;
1268 path->cmds[cmd_write++] = FZ_LINETO;
1269 path->cmds[cmd_write++] = FZ_LINETOCLOSE;
1270 n = 0;
1271 break;
1272 case FZ_HORIZTO:
1273 q.x = path->coords[coord_read++];
1274 p = fz_transform_point(q, ctm);
1275 path->coords[coord_write++] = p.x;
1276 path->coords[coord_write++] = p.y;
1277 path->cmds[cmd_write-1] = FZ_LINETO;
1278 n = 0;
1279 break;
1280 case FZ_HORIZTOCLOSE:
1281 p.x = path->coords[coord_read++];
1282 p.y = q.y;
1283 p = fz_transform_point(p, ctm);
1284 path->coords[coord_write++] = p.x;
1285 path->coords[coord_write++] = p.y;
1286 path->cmds[cmd_write-1] = FZ_LINETOCLOSE;
1287 q = s;
1288 n = 0;
1289 break;
1290 case FZ_VERTTO:
1291 q.y = path->coords[coord_read++];
1292 p = fz_transform_point(q, ctm);
1293 path->coords[coord_write++] = p.x;
1294 path->coords[coord_write++] = p.y;
1295 path->cmds[cmd_write-1] = FZ_LINETO;
1296 n = 0;
1297 break;
1298 case FZ_VERTTOCLOSE:
1299 p.x = q.x;
1300 p.y = path->coords[coord_read++];
1301 p = fz_transform_point(p, ctm);
1302 path->coords[coord_write++] = p.x;
1303 path->coords[coord_write++] = p.y;
1304 path->cmds[cmd_write-1] = FZ_LINETOCLOSE;
1305 q = s;
1306 n = 0;
1307 break;
1308 default:
1309 assert("Unknown path cmd" == NULL);
1310 }
1311 while (n > 0)
1312 {
1313 q.x = path->coords[coord_read++];
1314 q.y = path->coords[coord_read++];
1315 p = fz_transform_point(q, ctm);
1316 path->coords[coord_write++] = p.x;
1317 path->coords[coord_write++] = p.y;
1318 n--;
1319 }
1320 switch (cmd)
1321 {
1322 case FZ_MOVETO:
1323 case FZ_MOVETOCLOSE:
1324 s = q;
1325 break;
1326 case FZ_LINETOCLOSE:
1327 case FZ_DEGENLINETOCLOSE:
1328 case FZ_CURVETOCLOSE:
1329 case FZ_CURVETOYCLOSE:
1330 case FZ_CURVETOVCLOSE:
1331 case FZ_QUADTOCLOSE:
1332 case FZ_HORIZTOCLOSE:
1333 case FZ_VERTTOCLOSE:
1334 case FZ_RECTTO:
1335 q = s;
1336 break;
1337 }
1338 }
1339 }
1340 }
1341
fz_trim_path(fz_context * ctx,fz_path * path)1342 void fz_trim_path(fz_context *ctx, fz_path *path)
1343 {
1344 if (path->packed)
1345 fz_throw(ctx, FZ_ERROR_GENERIC, "Can't trim a packed path");
1346 if (path->cmd_cap > path->cmd_len)
1347 {
1348 path->cmds = fz_realloc_array(ctx, path->cmds, path->cmd_len, unsigned char);
1349 path->cmd_cap = path->cmd_len;
1350 }
1351 if (path->coord_cap > path->coord_len)
1352 {
1353 path->coords = fz_realloc_array(ctx, path->coords, path->coord_len, float);
1354 path->coord_cap = path->coord_len;
1355 }
1356 }
1357
1358 const fz_stroke_state fz_default_stroke_state = {
1359 -2, /* -2 is the magic number we use when we have stroke states stored on the stack */
1360 FZ_LINECAP_BUTT, FZ_LINECAP_BUTT, FZ_LINECAP_BUTT,
1361 FZ_LINEJOIN_MITER,
1362 1, 10,
1363 0, 0, { 0 }
1364 };
1365
1366 fz_stroke_state *
fz_keep_stroke_state(fz_context * ctx,const fz_stroke_state * strokec)1367 fz_keep_stroke_state(fz_context *ctx, const fz_stroke_state *strokec)
1368 {
1369 fz_stroke_state *stroke = (fz_stroke_state *)strokec; /* Explicit cast away of const */
1370
1371 if (!stroke)
1372 return NULL;
1373
1374 /* -2 is the magic number we use when we have stroke states stored on the stack */
1375 if (stroke->refs == -2)
1376 return fz_clone_stroke_state(ctx, stroke);
1377
1378 return fz_keep_imp(ctx, stroke, &stroke->refs);
1379 }
1380
1381 void
fz_drop_stroke_state(fz_context * ctx,const fz_stroke_state * strokec)1382 fz_drop_stroke_state(fz_context *ctx, const fz_stroke_state *strokec)
1383 {
1384 fz_stroke_state *stroke = (fz_stroke_state *)strokec; /* Explicit cast away of const */
1385
1386 if (fz_drop_imp(ctx, stroke, &stroke->refs))
1387 fz_free(ctx, stroke);
1388 }
1389
1390 fz_stroke_state *
fz_new_stroke_state_with_dash_len(fz_context * ctx,int len)1391 fz_new_stroke_state_with_dash_len(fz_context *ctx, int len)
1392 {
1393 fz_stroke_state *state;
1394
1395 len -= nelem(state->dash_list);
1396 if (len < 0)
1397 len = 0;
1398
1399 state = Memento_label(fz_malloc(ctx, sizeof(*state) + sizeof(state->dash_list[0]) * len), "fz_stroke_state");
1400 state->refs = 1;
1401 state->start_cap = FZ_LINECAP_BUTT;
1402 state->dash_cap = FZ_LINECAP_BUTT;
1403 state->end_cap = FZ_LINECAP_BUTT;
1404 state->linejoin = FZ_LINEJOIN_MITER;
1405 state->linewidth = 1;
1406 state->miterlimit = 10;
1407 state->dash_phase = 0;
1408 state->dash_len = 0;
1409 memset(state->dash_list, 0, sizeof(state->dash_list[0]) * (len + nelem(state->dash_list)));
1410
1411 return state;
1412 }
1413
1414 fz_stroke_state *
fz_new_stroke_state(fz_context * ctx)1415 fz_new_stroke_state(fz_context *ctx)
1416 {
1417 return fz_new_stroke_state_with_dash_len(ctx, 0);
1418 }
1419
1420 fz_stroke_state *
fz_clone_stroke_state(fz_context * ctx,fz_stroke_state * stroke)1421 fz_clone_stroke_state(fz_context *ctx, fz_stroke_state *stroke)
1422 {
1423 fz_stroke_state *clone = fz_new_stroke_state_with_dash_len(ctx, stroke->dash_len);
1424 int extra = stroke->dash_len - nelem(stroke->dash_list);
1425 int size = sizeof(*stroke) + sizeof(stroke->dash_list[0]) * extra;
1426 memcpy(clone, stroke, size);
1427 clone->refs = 1;
1428 return clone;
1429 }
1430
1431 fz_stroke_state *
fz_unshare_stroke_state_with_dash_len(fz_context * ctx,fz_stroke_state * shared,int len)1432 fz_unshare_stroke_state_with_dash_len(fz_context *ctx, fz_stroke_state *shared, int len)
1433 {
1434 int single, unsize, shsize, shlen;
1435 fz_stroke_state *unshared;
1436
1437 fz_lock(ctx, FZ_LOCK_ALLOC);
1438 single = (shared->refs == 1);
1439 fz_unlock(ctx, FZ_LOCK_ALLOC);
1440
1441 shlen = shared->dash_len - nelem(shared->dash_list);
1442 if (shlen < 0)
1443 shlen = 0;
1444 shsize = sizeof(*shared) + sizeof(shared->dash_list[0]) * shlen;
1445 len -= nelem(shared->dash_list);
1446 if (len < 0)
1447 len = 0;
1448 if (single && shlen >= len)
1449 return shared;
1450
1451 unsize = sizeof(*unshared) + sizeof(unshared->dash_list[0]) * len;
1452 unshared = Memento_label(fz_malloc(ctx, unsize), "fz_stroke_state");
1453 memcpy(unshared, shared, (shsize > unsize ? unsize : shsize));
1454 unshared->refs = 1;
1455
1456 if (fz_drop_imp(ctx, shared, &shared->refs))
1457 fz_free(ctx, shared);
1458 return unshared;
1459 }
1460
1461 fz_stroke_state *
fz_unshare_stroke_state(fz_context * ctx,fz_stroke_state * shared)1462 fz_unshare_stroke_state(fz_context *ctx, fz_stroke_state *shared)
1463 {
1464 return fz_unshare_stroke_state_with_dash_len(ctx, shared, shared->dash_len);
1465 }
1466
1467 static void *
clone_block(fz_context * ctx,void * block,size_t len)1468 clone_block(fz_context *ctx, void *block, size_t len)
1469 {
1470 void *target;
1471
1472 if (len == 0 || block == NULL)
1473 return NULL;
1474
1475 target = fz_malloc(ctx, len);
1476 memcpy(target, block, len);
1477 return target;
1478 }
1479
1480 fz_path *
fz_clone_path(fz_context * ctx,fz_path * path)1481 fz_clone_path(fz_context *ctx, fz_path *path)
1482 {
1483 fz_path *new_path;
1484
1485 assert(ctx != NULL);
1486
1487 if (path == NULL)
1488 return NULL;
1489
1490 new_path = fz_malloc_struct(ctx, fz_path);
1491 new_path->refs = 1;
1492 new_path->packed = FZ_PATH_UNPACKED;
1493 fz_try(ctx)
1494 {
1495 switch(path->packed)
1496 {
1497 case FZ_PATH_UNPACKED:
1498 case FZ_PATH_PACKED_OPEN:
1499 new_path->cmd_len = path->cmd_len;
1500 new_path->cmd_cap = path->cmd_cap;
1501 new_path->cmds = Memento_label(clone_block(ctx, path->cmds, path->cmd_cap), "path_cmds");
1502 new_path->coord_len = path->coord_len;
1503 new_path->coord_cap = path->coord_cap;
1504 new_path->coords = Memento_label(clone_block(ctx, path->coords, sizeof(float)*path->coord_cap), "path_coords");
1505 new_path->current = path->current;
1506 new_path->begin = path->begin;
1507 break;
1508 case FZ_PATH_PACKED_FLAT:
1509 {
1510 uint8_t *data;
1511 float *xy;
1512 int i;
1513 fz_packed_path *ppath = (fz_packed_path *)path;
1514
1515 new_path->cmd_len = ppath->cmd_len;
1516 new_path->cmd_cap = ppath->cmd_len;
1517 new_path->coord_len = ppath->coord_len;
1518 new_path->coord_cap = ppath->coord_len;
1519 data = (uint8_t *)&ppath[1];
1520 new_path->coords = Memento_label(clone_block(ctx, data, sizeof(float)*path->coord_cap), "path_coords");
1521 data += sizeof(float) * path->coord_cap;
1522 new_path->cmds = Memento_label(clone_block(ctx, data, path->cmd_cap), "path_cmds");
1523 xy = new_path->coords;
1524 for (i = 0; i < new_path->cmd_len; i++)
1525 {
1526 switch (new_path->cmds[i])
1527 {
1528 case FZ_MOVETOCLOSE:
1529 case FZ_MOVETO:
1530 new_path->current.x = *xy++;
1531 new_path->current.y = *xy++;
1532 new_path->begin.x = new_path->current.x;
1533 new_path->begin.y = new_path->current.y;
1534 break;
1535 case FZ_CURVETO:
1536 xy += 2;
1537 /* fallthrough */
1538 case FZ_CURVETOV:
1539 case FZ_CURVETOY:
1540 case FZ_QUADTO:
1541 /* fallthrough */
1542 xy += 2;
1543 case FZ_LINETO:
1544 new_path->current.x = *xy++;
1545 new_path->current.y = *xy++;
1546 break;
1547 case FZ_DEGENLINETO:
1548 break;
1549 case FZ_HORIZTO:
1550 new_path->current.x = *xy++;
1551 break;
1552 case FZ_VERTTO:
1553 new_path->current.y = *xy++;
1554 break;
1555 case FZ_RECTTO:
1556 xy += 2;
1557 break;
1558 case FZ_CURVETOCLOSE:
1559 xy += 2;
1560 /* fallthrough */
1561 case FZ_CURVETOVCLOSE:
1562 case FZ_CURVETOYCLOSE:
1563 case FZ_QUADTOCLOSE:
1564 case FZ_LINETOCLOSE:
1565 xy++;
1566 /* fallthrough */
1567 case FZ_HORIZTOCLOSE:
1568 case FZ_VERTTOCLOSE:
1569 xy++;
1570 /* fallthrough */
1571 case FZ_DEGENLINETOCLOSE:
1572 new_path->current.x = new_path->begin.x;
1573 new_path->current.y = new_path->begin.y;
1574 break;
1575 }
1576 }
1577 }
1578 default:
1579 fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown packing method found in path");
1580 }
1581 }
1582 fz_catch(ctx)
1583 {
1584 fz_free(ctx, new_path->coords);
1585 fz_free(ctx, new_path->cmds);
1586 fz_free(ctx, new_path);
1587 fz_rethrow(ctx);
1588 }
1589 return new_path;
1590 }
1591