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