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