1 /* Copyright (C) 1990, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved. 2 3 This software is provided AS-IS with no warranty, either express or 4 implied. 5 6 This software is distributed under license and may not be copied, 7 modified or distributed except as expressly authorized under the terms 8 of the license contained in the file LICENSE in this distribution. 9 10 For more information about licensing, please refer to 11 http://www.ghostscript.com/licensing/. For information on 12 commercial licensing, go to http://www.artifex.com/licensing/ or 13 contact Artifex Software, Inc., 101 Lucas Valley Road #110, 14 San Rafael, CA 94903, U.S.A., +1(415)492-9861. 15 */ 16 17 /* $Id: zupath.c,v 1.10 2004/08/04 19:36:13 stefan Exp $ */ 18 /* Operators related to user paths */ 19 #include "ghost.h" 20 #include "oper.h" 21 #include "oparc.h" 22 #include "idict.h" 23 #include "dstack.h" 24 #include "igstate.h" 25 #include "iname.h" 26 #include "iutil.h" 27 #include "store.h" 28 #include "stream.h" 29 #include "ibnum.h" 30 #include "gsmatrix.h" 31 #include "gsstate.h" 32 #include "gscoord.h" 33 #include "gspaint.h" 34 #include "gxfixed.h" 35 #include "gxdevice.h" 36 #include "gspath.h" 37 #include "gzpath.h" /* for saving path */ 38 #include "gzstate.h" /* for accessing path */ 39 40 /* Imported data */ 41 extern const gx_device gs_hit_device; 42 extern const int gs_hit_detected; 43 44 /* Forward references */ 45 private int upath_append(os_ptr, i_ctx_t *); 46 private int upath_stroke(i_ctx_t *, gs_matrix *); 47 48 /* ---------------- Insideness testing ---------------- */ 49 50 /* Forward references */ 51 private int in_test(i_ctx_t *, int (*)(gs_state *)); 52 private int in_path(os_ptr, i_ctx_t *, gx_device *); 53 private int in_path_result(i_ctx_t *, int, int); 54 private int in_utest(i_ctx_t *, int (*)(gs_state *)); 55 private int in_upath(i_ctx_t *, gx_device *); 56 private int in_upath_result(i_ctx_t *, int, int); 57 58 /* <x> <y> ineofill <bool> */ 59 /* <userpath> ineofill <bool> */ 60 private int 61 zineofill(i_ctx_t *i_ctx_p) 62 { 63 return in_test(i_ctx_p, gs_eofill); 64 } 65 66 /* <x> <y> infill <bool> */ 67 /* <userpath> infill <bool> */ 68 private int 69 zinfill(i_ctx_t *i_ctx_p) 70 { 71 return in_test(i_ctx_p, gs_fill); 72 } 73 74 /* <x> <y> instroke <bool> */ 75 /* <userpath> instroke <bool> */ 76 private int 77 zinstroke(i_ctx_t *i_ctx_p) 78 { 79 return in_test(i_ctx_p, gs_stroke); 80 } 81 82 /* <x> <y> <userpath> inueofill <bool> */ 83 /* <userpath1> <userpath2> inueofill <bool> */ 84 private int 85 zinueofill(i_ctx_t *i_ctx_p) 86 { 87 return in_utest(i_ctx_p, gs_eofill); 88 } 89 90 /* <x> <y> <userpath> inufill <bool> */ 91 /* <userpath1> <userpath2> inufill <bool> */ 92 private int 93 zinufill(i_ctx_t *i_ctx_p) 94 { 95 return in_utest(i_ctx_p, gs_fill); 96 } 97 98 /* <x> <y> <userpath> inustroke <bool> */ 99 /* <x> <y> <userpath> <matrix> inustroke <bool> */ 100 /* <userpath1> <userpath2> inustroke <bool> */ 101 /* <userpath1> <userpath2> <matrix> inustroke <bool> */ 102 private int 103 zinustroke(i_ctx_t *i_ctx_p) 104 { /* This is different because of the optional matrix operand. */ 105 os_ptr op = osp; 106 int code = gs_gsave(igs); 107 int spop, npop; 108 gs_matrix mat; 109 gx_device hdev; 110 111 if (code < 0) 112 return code; 113 if ((spop = upath_stroke(i_ctx_p, &mat)) < 0) { 114 gs_grestore(igs); 115 return spop; 116 } 117 if ((npop = in_path(op - spop, i_ctx_p, &hdev)) < 0) { 118 gs_grestore(igs); 119 return npop; 120 } 121 if (npop > 1) /* matrix was supplied */ 122 code = gs_concat(igs, &mat); 123 if (code >= 0) 124 code = gs_stroke(igs); 125 return in_upath_result(i_ctx_p, npop + spop, code); 126 } 127 128 /* ------ Internal routines ------ */ 129 130 /* Do the work of the non-user-path insideness operators. */ 131 private int 132 in_test(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *)) 133 { 134 os_ptr op = osp; 135 gx_device hdev; 136 int npop = in_path(op, i_ctx_p, &hdev); 137 int code; 138 139 if (npop < 0) 140 return npop; 141 code = (*paintproc)(igs); 142 return in_path_result(i_ctx_p, npop, code); 143 } 144 145 /* Set up a clipping path and device for insideness testing. */ 146 private int 147 in_path(os_ptr oppath, i_ctx_t *i_ctx_p, gx_device * phdev) 148 { 149 int code = gs_gsave(igs); 150 int npop; 151 double uxy[2]; 152 153 if (code < 0) 154 return code; 155 code = num_params(oppath, 2, uxy); 156 if (code >= 0) { /* Aperture is a single pixel. */ 157 gs_point dxy; 158 gs_fixed_rect fr; 159 160 gs_transform(igs, uxy[0], uxy[1], &dxy); 161 fr.p.x = fixed_floor(float2fixed(dxy.x)); 162 fr.p.y = fixed_floor(float2fixed(dxy.y)); 163 fr.q.x = fr.p.x + fixed_1; 164 fr.q.y = fr.p.y + fixed_1; 165 code = gx_clip_to_rectangle(igs, &fr); 166 npop = 2; 167 } else { /* Aperture is a user path. */ 168 /* We have to set the clipping path without disturbing */ 169 /* the current path. */ 170 gx_path *ipath = igs->path; 171 gx_path save; 172 173 gx_path_init_local(&save, imemory); 174 gx_path_assign_preserve(&save, ipath); 175 gs_newpath(igs); 176 code = upath_append(oppath, i_ctx_p); 177 if (code >= 0) 178 code = gx_clip_to_path(igs); 179 gx_path_assign_free(igs->path, &save); 180 npop = 1; 181 } 182 if (code < 0) { 183 gs_grestore(igs); 184 return code; 185 } 186 /* Install the hit detection device. */ 187 gx_set_device_color_1(igs); 188 gx_device_init((gx_device *) phdev, (const gx_device *)&gs_hit_device, 189 NULL, true); 190 phdev->width = phdev->height = max_int; 191 gx_device_fill_in_procs(phdev); 192 gx_set_device_only(igs, phdev); 193 return npop; 194 } 195 196 /* Finish an insideness test. */ 197 private int 198 in_path_result(i_ctx_t *i_ctx_p, int npop, int code) 199 { 200 os_ptr op = osp; 201 bool result; 202 203 gs_grestore(igs); /* matches gsave in in_path */ 204 if (code == gs_hit_detected) 205 result = true; 206 else if (code == 0) /* completed painting without a hit */ 207 result = false; 208 else /* error */ 209 return code; 210 npop--; 211 pop(npop); 212 op -= npop; 213 make_bool(op, result); 214 return 0; 215 216 } 217 218 /* Do the work of the user-path insideness operators. */ 219 private int 220 in_utest(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *)) 221 { 222 gx_device hdev; 223 int npop = in_upath(i_ctx_p, &hdev); 224 int code; 225 226 if (npop < 0) 227 return npop; 228 code = (*paintproc)(igs); 229 return in_upath_result(i_ctx_p, npop, code); 230 } 231 232 /* Set up a clipping path and device for insideness testing */ 233 /* with a user path. */ 234 private int 235 in_upath(i_ctx_t *i_ctx_p, gx_device * phdev) 236 { 237 os_ptr op = osp; 238 int code = gs_gsave(igs); 239 int npop; 240 241 if (code < 0) 242 return code; 243 if ((code = upath_append(op, i_ctx_p)) < 0 || 244 (npop = in_path(op - 1, i_ctx_p, phdev)) < 0 245 ) { 246 gs_grestore(igs); 247 return code; 248 } 249 return npop + 1; 250 } 251 252 /* Finish an insideness test with a user path. */ 253 private int 254 in_upath_result(i_ctx_t *i_ctx_p, int npop, int code) 255 { 256 gs_grestore(igs); /* matches gsave in in_upath */ 257 return in_path_result(i_ctx_p, npop, code); 258 } 259 260 /* ---------------- User paths ---------------- */ 261 262 /* User path operator codes */ 263 typedef enum { 264 upath_op_setbbox = 0, 265 upath_op_moveto = 1, 266 upath_op_rmoveto = 2, 267 upath_op_lineto = 3, 268 upath_op_rlineto = 4, 269 upath_op_curveto = 5, 270 upath_op_rcurveto = 6, 271 upath_op_arc = 7, 272 upath_op_arcn = 8, 273 upath_op_arct = 9, 274 upath_op_closepath = 10, 275 upath_op_ucache = 11 276 } upath_op; 277 278 #define UPATH_MAX_OP 11 279 #define UPATH_REPEAT 32 280 static const byte up_nargs[UPATH_MAX_OP + 1] = { 281 4, 2, 2, 2, 2, 6, 6, 5, 5, 5, 0, 0 282 }; 283 284 /* Declare operator procedures not declared in opextern.h. */ 285 int zsetbbox(i_ctx_t *); 286 private int zucache(i_ctx_t *); 287 288 #undef zp 289 static const op_proc_t up_ops[UPATH_MAX_OP + 1] = { 290 zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto, 291 zcurveto, zrcurveto, zarc, zarcn, zarct, 292 zclosepath, zucache 293 }; 294 295 /* - ucache - */ 296 private int 297 zucache(i_ctx_t *i_ctx_p) 298 { 299 /* A no-op for now. */ 300 return 0; 301 } 302 303 /* <userpath> uappend - */ 304 private int 305 zuappend(i_ctx_t *i_ctx_p) 306 { 307 os_ptr op = osp; 308 int code = gs_gsave(igs); 309 310 if (code < 0) 311 return code; 312 if ((code = upath_append(op, i_ctx_p)) >= 0) 313 code = gs_upmergepath(igs); 314 gs_grestore(igs); 315 if (code < 0) 316 return code; 317 pop(1); 318 return 0; 319 } 320 321 /* <userpath> ueofill - */ 322 private int 323 zueofill(i_ctx_t *i_ctx_p) 324 { 325 os_ptr op = osp; 326 int code = gs_gsave(igs); 327 328 if (code < 0) 329 return code; 330 if ((code = upath_append(op, i_ctx_p)) >= 0) 331 code = gs_eofill(igs); 332 gs_grestore(igs); 333 if (code < 0) 334 return code; 335 pop(1); 336 return 0; 337 } 338 339 /* <userpath> ufill - */ 340 private int 341 zufill(i_ctx_t *i_ctx_p) 342 { 343 os_ptr op = osp; 344 int code = gs_gsave(igs); 345 346 if (code < 0) 347 return code; 348 if ((code = upath_append(op, i_ctx_p)) >= 0) 349 code = gs_fill(igs); 350 gs_grestore(igs); 351 if (code < 0) 352 return code; 353 pop(1); 354 return 0; 355 } 356 357 /* <userpath> ustroke - */ 358 /* <userpath> <matrix> ustroke - */ 359 private int 360 zustroke(i_ctx_t *i_ctx_p) 361 { 362 int code = gs_gsave(igs); 363 int npop; 364 365 if (code < 0) 366 return code; 367 if ((code = npop = upath_stroke(i_ctx_p, NULL)) >= 0) 368 code = gs_stroke(igs); 369 gs_grestore(igs); 370 if (code < 0) 371 return code; 372 pop(npop); 373 return 0; 374 } 375 376 /* <userpath> ustrokepath - */ 377 /* <userpath> <matrix> ustrokepath - */ 378 private int 379 zustrokepath(i_ctx_t *i_ctx_p) 380 { 381 gx_path save; 382 int code, npop; 383 384 /* Save and reset the path. */ 385 gx_path_init_local(&save, imemory); 386 gx_path_assign_preserve(&save, igs->path); 387 if ((code = npop = upath_stroke(i_ctx_p, NULL)) < 0 || 388 (code = gs_strokepath(igs)) < 0 389 ) { 390 gx_path_assign_free(igs->path, &save); 391 return code; 392 } 393 gx_path_free(&save, "ustrokepath"); 394 pop(npop); 395 return 0; 396 } 397 398 /* <with_ucache> upath <userpath> */ 399 /* We do all the work in a procedure that is also used to construct */ 400 /* the UnpaintedPath user path for ImageType 2 images. */ 401 int make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath, 402 bool with_ucache); 403 private int 404 zupath(i_ctx_t *i_ctx_p) 405 { 406 os_ptr op = osp; 407 408 check_type(*op, t_boolean); 409 return make_upath(i_ctx_p, op, igs, igs->path, op->value.boolval); 410 } 411 int 412 make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath, 413 bool with_ucache) 414 { 415 int size = (with_ucache ? 6 : 5); 416 gs_path_enum penum; 417 int op; 418 ref *next; 419 int code; 420 421 /* Compute the size of the user path array. */ 422 { 423 gs_fixed_point pts[3]; 424 425 gx_path_enum_init(&penum, ppath); 426 while ((op = gx_path_enum_next(&penum, pts)) != 0) { 427 switch (op) { 428 case gs_pe_moveto: 429 case gs_pe_lineto: 430 size += 3; 431 continue; 432 case gs_pe_curveto: 433 size += 7; 434 continue; 435 case gs_pe_closepath: 436 size += 1; 437 continue; 438 default: 439 return_error(e_unregistered); 440 } 441 } 442 } 443 code = ialloc_ref_array(rupath, a_all | a_executable, size, 444 "make_upath"); 445 if (code < 0) 446 return code; 447 /* Construct the path. */ 448 next = rupath->value.refs; 449 if (with_ucache) { 450 if ((code = name_enter_string(pgs->memory, "ucache", next)) < 0) 451 return code; 452 r_set_attrs(next, a_executable | l_new); 453 ++next; 454 } { 455 gs_rect bbox; 456 457 if ((code = gs_upathbbox(pgs, &bbox, true)) < 0) { 458 /* 459 * Note: Adobe throws 'nocurrentpoint' error, but the PLRM 460 * not list this as a possible error from 'upath', so we 461 * set a reasonable default bbox instead. 462 */ 463 if (code != e_nocurrentpoint) 464 return code; 465 bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0; 466 } 467 make_real_new(next, bbox.p.x); 468 make_real_new(next + 1, bbox.p.y); 469 make_real_new(next + 2, bbox.q.x); 470 make_real_new(next + 3, bbox.q.y); 471 next += 4; 472 if ((code = name_enter_string(pgs->memory, "setbbox", next)) < 0) 473 return code; 474 r_set_attrs(next, a_executable | l_new); 475 ++next; 476 } 477 { 478 gs_point pts[3]; 479 480 /* Patch the path in the gstate to set up the enumerator. */ 481 gx_path *save_path = pgs->path; 482 483 pgs->path = ppath; 484 gs_path_enum_copy_init(&penum, pgs, false); 485 pgs->path = save_path; 486 while ((op = gs_path_enum_next(&penum, pts)) != 0) { 487 const char *opstr; 488 489 switch (op) { 490 case gs_pe_moveto: 491 opstr = "moveto"; 492 goto ml; 493 case gs_pe_lineto: 494 opstr = "lineto"; 495 ml:make_real_new(next, pts[0].x); 496 make_real_new(next + 1, pts[0].y); 497 next += 2; 498 break; 499 case gs_pe_curveto: 500 opstr = "curveto"; 501 make_real_new(next, pts[0].x); 502 make_real_new(next + 1, pts[0].y); 503 make_real_new(next + 2, pts[1].x); 504 make_real_new(next + 3, pts[1].y); 505 make_real_new(next + 4, pts[2].x); 506 make_real_new(next + 5, pts[2].y); 507 next += 6; 508 break; 509 case gs_pe_closepath: 510 opstr = "closepath"; 511 break; 512 default: 513 return_error(e_unregistered); 514 } 515 if ((code = name_enter_string(pgs->memory, opstr, next)) < 0) 516 return code; 517 r_set_attrs(next, a_executable); 518 ++next; 519 } 520 } 521 return 0; 522 } 523 524 /* ------ Internal routines ------ */ 525 526 /* Append a user path to the current path. */ 527 private inline int 528 upath_append_aux(os_ptr oppath, i_ctx_t *i_ctx_p) 529 { 530 ref opcodes; 531 check_read(*oppath); 532 gs_newpath(igs); 533 /****** ROUND tx AND ty ******/ 534 if (!r_is_array(oppath)) 535 return_error(e_typecheck); 536 537 if ( r_size(oppath) == 2 && 538 array_get(imemory, oppath, 1, &opcodes) >= 0 && 539 r_has_type(&opcodes, t_string) 540 ) { /* 1st element is operands, 2nd is operators */ 541 ref operands; 542 int code, format; 543 int repcount = 1; 544 const byte *opp; 545 uint ocount, i = 0; 546 547 array_get(imemory, oppath, 0, &operands); 548 code = num_array_format(&operands); 549 if (code < 0) 550 return code; 551 format = code; 552 opp = opcodes.value.bytes; 553 ocount = r_size(&opcodes); 554 while (ocount--) { 555 byte opx = *opp++; 556 557 if (opx > UPATH_REPEAT) 558 repcount = opx - UPATH_REPEAT; 559 else if (opx > UPATH_MAX_OP) 560 return_error(e_rangecheck); 561 else { /* operator */ 562 do { 563 os_ptr op = osp; 564 byte opargs = up_nargs[opx]; 565 566 while (opargs--) { 567 push(1); 568 code = num_array_get(imemory, &operands, format, i++, op); 569 switch (code) { 570 case t_integer: 571 r_set_type_attrs(op, t_integer, 0); 572 break; 573 case t_real: 574 r_set_type_attrs(op, t_real, 0); 575 break; 576 default: 577 return_error(e_typecheck); 578 } 579 } 580 code = (*up_ops[opx])(i_ctx_p); 581 if (code < 0) 582 return code; 583 } 584 while (--repcount); 585 repcount = 1; 586 } 587 } 588 } else { /* Ordinary executable array. */ 589 const ref *arp = oppath; 590 uint ocount = r_size(oppath); 591 long index = 0; 592 int argcount = 0; 593 op_proc_t oproc; 594 int opx, code; 595 596 for (; index < ocount; index++) { 597 ref rup; 598 ref *defp; 599 os_ptr op = osp; 600 601 array_get(imemory, arp, index, &rup); 602 switch (r_type(&rup)) { 603 case t_integer: 604 case t_real: 605 argcount++; 606 push(1); 607 *op = rup; 608 break; 609 case t_name: 610 if (!r_has_attr(&rup, a_executable)) 611 return_error(e_typecheck); 612 if (dict_find(systemdict, &rup, &defp) <= 0) 613 return_error(e_undefined); 614 if (r_btype(defp) != t_operator) 615 return_error(e_typecheck); 616 goto xop; 617 case t_operator: 618 defp = &rup; 619 xop:if (!r_has_attr(defp, a_executable)) 620 return_error(e_typecheck); 621 oproc = real_opproc(defp); 622 for (opx = 0; opx <= UPATH_MAX_OP; opx++) 623 if (oproc == up_ops[opx]) 624 break; 625 if (opx > UPATH_MAX_OP || argcount != up_nargs[opx]) 626 return_error(e_typecheck); 627 code = (*oproc)(i_ctx_p); 628 if (code < 0) 629 return code; 630 argcount = 0; 631 break; 632 default: 633 return_error(e_typecheck); 634 } 635 } 636 if (argcount) 637 return_error(e_typecheck); /* leftover args */ 638 } 639 return 0; 640 } 641 private int 642 upath_append(os_ptr oppath, i_ctx_t *i_ctx_p) 643 { 644 int code = upath_append_aux(oppath, i_ctx_p); 645 646 if (code < 0) 647 return code; 648 igs->current_point.x = fixed2float(igs->path->position.x); 649 igs->current_point.y = fixed2float(igs->path->position.y); 650 return 0; 651 } 652 653 /* Append a user path to the current path, and then apply or return */ 654 /* a transformation if one is supplied. */ 655 private int 656 upath_stroke(i_ctx_t *i_ctx_p, gs_matrix *pmat) 657 { 658 os_ptr op = osp; 659 int code, npop; 660 gs_matrix mat; 661 662 if ((code = read_matrix(imemory, op, &mat)) >= 0) { 663 if ((code = upath_append(op - 1, i_ctx_p)) >= 0) { 664 if (pmat) 665 *pmat = mat; 666 else 667 code = gs_concat(igs, &mat); 668 } 669 npop = 2; 670 } else { 671 if ((code = upath_append(op, i_ctx_p)) >= 0) 672 if (pmat) 673 gs_make_identity(pmat); 674 npop = 1; 675 } 676 return (code < 0 ? code : npop); 677 } 678 679 /* ---------------- Initialization procedure ---------------- */ 680 681 const op_def zupath_l2_op_defs[] = 682 { 683 op_def_begin_level2(), 684 /* Insideness testing */ 685 {"1ineofill", zineofill}, 686 {"1infill", zinfill}, 687 {"1instroke", zinstroke}, 688 {"2inueofill", zinueofill}, 689 {"2inufill", zinufill}, 690 {"2inustroke", zinustroke}, 691 /* User paths */ 692 {"1uappend", zuappend}, 693 {"0ucache", zucache}, 694 {"1ueofill", zueofill}, 695 {"1ufill", zufill}, 696 {"1upath", zupath}, 697 {"1ustroke", zustroke}, 698 {"1ustrokepath", zustrokepath}, 699 op_def_end(0) 700 }; 701