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