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