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 | |
19 | typedef 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 | |
42 | struct 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 | |
54 | typedef 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 | |
62 | enum |
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 | |
71 | fz_path * |
72 | fz_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 | */ |
95 | fz_path * |
96 | fz_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 | |
107 | void |
108 | fz_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 | */ |
128 | int 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 | */ |
189 | int |
190 | fz_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 | |
255 | static void |
256 | push_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 | |
271 | static void |
272 | push_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 | |
288 | static void |
289 | push_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 | */ |
312 | fz_point |
313 | fz_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 | */ |
328 | void |
329 | fz_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 | */ |
360 | void |
361 | fz_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 | */ |
423 | void |
424 | fz_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 | */ |
493 | void |
494 | fz_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 | */ |
541 | void |
542 | fz_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 | */ |
593 | void |
594 | fz_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 | */ |
633 | void |
634 | fz_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 | */ |
719 | void |
720 | fz_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 | |
739 | static 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 | */ |
765 | void 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 | |
1012 | typedef 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 | |
1021 | static void |
1022 | bound_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 | |
1029 | static void |
1030 | bound_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 | |
1049 | static void |
1050 | bound_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 | |
1071 | static 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 | */ |
1095 | fz_rect |
1096 | fz_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 | |
1115 | fz_rect |
1116 | fz_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 | */ |
1148 | void |
1149 | fz_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 = 0; |
1344 | int = 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 | */ |
1520 | void 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 | |
1536 | const 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 | */ |
1552 | fz_stroke_state * |
1553 | fz_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 | */ |
1572 | void |
1573 | fz_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 | */ |
1590 | fz_stroke_state * |
1591 | fz_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 | */ |
1620 | fz_stroke_state * |
1621 | fz_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 | */ |
1635 | fz_stroke_state * |
1636 | fz_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 = 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 | */ |
1661 | fz_stroke_state * |
1662 | fz_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 | */ |
1706 | fz_stroke_state * |
1707 | fz_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 | |
1712 | static void * |
1713 | clone_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 | */ |
1737 | fz_path * |
1738 | fz_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 | |