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
zineofill(i_ctx_t * i_ctx_p)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
zinfill(i_ctx_t * i_ctx_p)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
zinstroke(i_ctx_t * i_ctx_p)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
zinueofill(i_ctx_t * i_ctx_p)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
zinufill(i_ctx_t * i_ctx_p)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
zinustroke(i_ctx_t * i_ctx_p)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
in_test(i_ctx_t * i_ctx_p,int (* paintproc)(gs_state *))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
in_path(os_ptr oppath,i_ctx_t * i_ctx_p,gx_device * phdev)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
in_path_result(i_ctx_t * i_ctx_p,int npop,int code)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
in_utest(i_ctx_t * i_ctx_p,int (* paintproc)(gs_state *))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
in_upath(i_ctx_t * i_ctx_p,gx_device * phdev)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
in_upath_result(i_ctx_t * i_ctx_p,int npop,int code)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
zucache(i_ctx_t * i_ctx_p)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
zuappend(i_ctx_t * i_ctx_p)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
zueofill(i_ctx_t * i_ctx_p)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
zufill(i_ctx_t * i_ctx_p)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
zustroke(i_ctx_t * i_ctx_p)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
zustrokepath(i_ctx_t * i_ctx_p)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
zupath(i_ctx_t * i_ctx_p)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
make_upath(i_ctx_t * i_ctx_p,ref * rupath,gs_state * pgs,gx_path * ppath,bool with_ucache)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
upath_append_aux(os_ptr oppath,i_ctx_t * i_ctx_p)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
upath_append(os_ptr oppath,i_ctx_t * i_ctx_p)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
upath_stroke(i_ctx_t * i_ctx_p,gs_matrix * pmat)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