xref: /plan9/sys/src/cmd/gs/src/zupath.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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