xref: /plan9/sys/src/cmd/gs/src/zdps1.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1990, 1996, 1997, 1998, 1999, 2000 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: zdps1.c,v 1.8 2005/10/04 06:30:02 ray Exp $ */
18 /* Level 2 / Display PostScript graphics extensions */
19 #include "ghost.h"
20 #include "oper.h"
21 #include "gsmatrix.h"
22 #include "gspath.h"
23 #include "gspath2.h"
24 #include "gsstate.h"
25 #include "ialloc.h"
26 #include "igstate.h"
27 #include "ivmspace.h"
28 #include "store.h"
29 #include "stream.h"
30 #include "ibnum.h"
31 
32 /* Forward references */
33 private int gstate_unshare(i_ctx_t *);
34 
35 /* Declare exported procedures (for zupath.c) */
36 int zsetbbox(i_ctx_t *);
37 
38 /* Structure descriptors */
39 public_st_igstate_obj();
40 
41 /* Extend the `copy' operator to deal with gstates. */
42 /* This is done with a hack -- we know that gstates are the only */
43 /* t_astruct subtype that implements copy. */
44 private int
z1copy(i_ctx_t * i_ctx_p)45 z1copy(i_ctx_t *i_ctx_p)
46 {
47     os_ptr op = osp;
48     int code = zcopy(i_ctx_p);
49 
50     if (code >= 0)
51 	return code;
52     if (!r_has_type(op, t_astruct))
53 	return code;
54     return zcopy_gstate(i_ctx_p);
55 }
56 
57 /* ------ Graphics state ------ */
58 
59 /* <bool> setstrokeadjust - */
60 private int
zsetstrokeadjust(i_ctx_t * i_ctx_p)61 zsetstrokeadjust(i_ctx_t *i_ctx_p)
62 {
63     os_ptr op = osp;
64 
65     check_type(*op, t_boolean);
66     gs_setstrokeadjust(igs, op->value.boolval);
67     pop(1);
68     return 0;
69 }
70 
71 /* - currentstrokeadjust <bool> */
72 private int
zcurrentstrokeadjust(i_ctx_t * i_ctx_p)73 zcurrentstrokeadjust(i_ctx_t *i_ctx_p)
74 {
75     os_ptr op = osp;
76 
77     push(1);
78     make_bool(op, gs_currentstrokeadjust(igs));
79     return 0;
80 }
81 
82 /* ------ Graphics state objects ------ */
83 
84 /*
85  * Check to make sure that all the elements of a graphics state
86  * can be stored in the given allocation space.
87  */
88 /* ****** DOESN'T CHECK THE NON-REFS. ****** */
89 private int
gstate_check_space(i_ctx_t * i_ctx_p,int_gstate * isp,uint space)90 gstate_check_space(i_ctx_t *i_ctx_p, int_gstate *isp, uint space)
91 {
92     /*
93      * ****** WORKAROUND ALERT ******
94      * This code doesn't check the space of the non-refs, or copy their
95      * contents, so it can create dangling references from global VM to
96      * local VM.  Because of this, we simply disallow writing into gstates
97      * in global VM (including creating them in the first place) if the
98      * save level is greater than 0.
99      * ****** WORKAROUND ALERT ******
100      */
101 #if 1				/* ****** WORKAROUND ****** */
102     if (space != avm_local && imemory_save_level(iimemory) > 0)
103 	return_error(e_invalidaccess);
104 #endif				/* ****** END ****** */
105 #define gsref_check(p) store_check_space(space, p)
106     int_gstate_map_refs(isp, gsref_check);
107 #undef gsref_check
108     return 0;
109 }
110 
111 /* - gstate <gstate> */
112 int
zgstate(i_ctx_t * i_ctx_p)113 zgstate(i_ctx_t *i_ctx_p)
114 {
115     os_ptr op = osp;
116 
117     int code = gstate_check_space(i_ctx_p, istate, icurrent_space);
118     igstate_obj *pigo;
119     gs_state *pnew;
120     int_gstate *isp;
121 
122     if (code < 0)
123 	return code;
124     pigo = ialloc_struct(igstate_obj, &st_igstate_obj, "gstate");
125     if (pigo == 0)
126 	return_error(e_VMerror);
127     pnew = gs_state_copy(igs, imemory);
128     if (pnew == 0) {
129 	ifree_object(pigo, "gstate");
130 	return_error(e_VMerror);
131     }
132     isp = gs_int_gstate(pnew);
133     int_gstate_map_refs(isp, ref_mark_new);
134     push(1);
135     /*
136      * Since igstate_obj isn't a ref, but only contains a ref, save won't
137      * clear its l_new bit automatically, and restore won't set it
138      * automatically; we have to make sure this ref is on the changes chain.
139      */
140     make_iastruct(op, a_all, pigo);
141     make_null(&pigo->gstate);
142     ref_save(op, &pigo->gstate, "gstate");
143     make_istruct_new(&pigo->gstate, 0, pnew);
144     return 0;
145 }
146 
147 /* copy for gstates */
148 int
zcopy_gstate(i_ctx_t * i_ctx_p)149 zcopy_gstate(i_ctx_t *i_ctx_p)
150 {
151     os_ptr op = osp;
152     os_ptr op1 = op - 1;
153     gs_state *pgs;
154     gs_state *pgs1;
155     int_gstate *pistate;
156     gs_memory_t *mem;
157     int code;
158 
159     check_stype(*op, st_igstate_obj);
160     check_stype(*op1, st_igstate_obj);
161     check_write(*op);
162     code = gstate_unshare(i_ctx_p);
163     if (code < 0)
164 	return code;
165     pgs = igstate_ptr(op);
166     pgs1 = igstate_ptr(op1);
167     pistate = gs_int_gstate(pgs);
168     code = gstate_check_space(i_ctx_p, gs_int_gstate(pgs1), r_space(op));
169     if (code < 0)
170 	return code;
171 #define gsref_save(p) ref_save(op, p, "copygstate")
172     int_gstate_map_refs(pistate, gsref_save);
173 #undef gsref_save
174     mem = gs_state_swap_memory(pgs, imemory);
175     code = gs_copygstate(pgs, pgs1);
176     gs_state_swap_memory(pgs, mem);
177     if (code < 0)
178 	return code;
179     int_gstate_map_refs(pistate, ref_mark_new);
180     *op1 = *op;
181     pop(1);
182     return 0;
183 }
184 
185 /* <gstate> currentgstate <gstate> */
186 int
zcurrentgstate(i_ctx_t * i_ctx_p)187 zcurrentgstate(i_ctx_t *i_ctx_p)
188 {
189     os_ptr op = osp;
190     gs_state *pgs;
191     int_gstate *pistate;
192     int code;
193     gs_memory_t *mem;
194 
195     check_stype(*op, st_igstate_obj);
196     check_write(*op);
197     code = gstate_unshare(i_ctx_p);
198     if (code < 0)
199 	return code;
200     pgs = igstate_ptr(op);
201     pistate = gs_int_gstate(pgs);
202     code = gstate_check_space(i_ctx_p, istate, r_space(op));
203     if (code < 0)
204 	return code;
205 #define gsref_save(p) ref_save(op, p, "currentgstate")
206     int_gstate_map_refs(pistate, gsref_save);
207 #undef gsref_save
208     mem = gs_state_swap_memory(pgs, imemory);
209     code = gs_currentgstate(pgs, igs);
210     gs_state_swap_memory(pgs, mem);
211     if (code < 0)
212 	return code;
213     int_gstate_map_refs(pistate, ref_mark_new);
214     return 0;
215 }
216 
217 /* <gstate> setgstate - */
218 int
zsetgstate(i_ctx_t * i_ctx_p)219 zsetgstate(i_ctx_t *i_ctx_p)
220 {
221     os_ptr op = osp;
222     int code;
223 
224     check_stype(*op, st_igstate_obj);
225     check_read(*op);
226     code = gs_setgstate(igs, igstate_ptr(op));
227     if (code < 0)
228 	return code;
229     pop(1);
230     return 0;
231 }
232 
233 /* ------ Rectangles ------- */
234 
235 /*
236  * We preallocate a short list for rectangles, because
237  * the rectangle operators usually will involve very few rectangles.
238  */
239 #define MAX_LOCAL_RECTS 5
240 typedef struct local_rects_s {
241     gs_rect *pr;
242     uint count;
243     gs_rect rl[MAX_LOCAL_RECTS];
244 } local_rects_t;
245 
246 /* Forward references */
247 private int rect_get(local_rects_t *, os_ptr, gs_memory_t *);
248 private void rect_release(local_rects_t *, gs_memory_t *);
249 
250 /* <x> <y> <width> <height> .rectappend - */
251 /* <numarray|numstring> .rectappend - */
252 private int
zrectappend(i_ctx_t * i_ctx_p)253 zrectappend(i_ctx_t *i_ctx_p)
254 {
255     os_ptr op = osp;
256     local_rects_t lr;
257     int npop = rect_get(&lr, op, imemory);
258     int code;
259 
260     if (npop < 0)
261 	return npop;
262     code = gs_rectappend(igs, lr.pr, lr.count);
263     rect_release(&lr, imemory);
264     if (code < 0)
265 	return code;
266     pop(npop);
267     return 0;
268 }
269 
270 /* <x> <y> <width> <height> rectclip - */
271 /* <numarray|numstring> rectclip - */
272 private int
zrectclip(i_ctx_t * i_ctx_p)273 zrectclip(i_ctx_t *i_ctx_p)
274 {
275     os_ptr op = osp;
276     local_rects_t lr;
277     int npop = rect_get(&lr, op, imemory);
278     int code;
279 
280     if (npop < 0)
281 	return npop;
282     code = gs_rectclip(igs, lr.pr, lr.count);
283     rect_release(&lr, imemory);
284     if (code < 0)
285 	return code;
286     pop(npop);
287     return 0;
288 }
289 
290 /* <x> <y> <width> <height> rectfill - */
291 /* <numarray|numstring> rectfill - */
292 private int
zrectfill(i_ctx_t * i_ctx_p)293 zrectfill(i_ctx_t *i_ctx_p)
294 {
295     os_ptr op = osp;
296     local_rects_t lr;
297     int npop = rect_get(&lr, op, imemory);
298     int code;
299 
300     if (npop < 0)
301 	return npop;
302     code = gs_rectfill(igs, lr.pr, lr.count);
303     rect_release(&lr, imemory);
304     if (code < 0)
305 	return code;
306     pop(npop);
307     return 0;
308 }
309 
310 /* <x> <y> <width> <height> rectstroke - */
311 /* <numarray|numstring> rectstroke - */
312 private int
zrectstroke(i_ctx_t * i_ctx_p)313 zrectstroke(i_ctx_t *i_ctx_p)
314 {
315     os_ptr op = osp;
316     gs_matrix mat;
317     local_rects_t lr;
318     int npop, code;
319 
320     if (read_matrix(imemory, op, &mat) >= 0) {
321 	/* Concatenate the matrix to the CTM just before stroking the path. */
322 	npop = rect_get(&lr, op - 1, imemory);
323 	if (npop < 0)
324 	    return npop;
325 	code = gs_rectstroke(igs, lr.pr, lr.count, &mat);
326 	npop++;
327     } else {
328 	/* No matrix. */
329 	npop = rect_get(&lr, op, imemory);
330 	if (npop < 0)
331 	    return npop;
332 	code = gs_rectstroke(igs, lr.pr, lr.count, (gs_matrix *) 0);
333     }
334     rect_release(&lr, imemory);
335     if (code < 0)
336 	return code;
337     pop(npop);
338     return 0;
339 }
340 
341 /* --- Internal routines --- */
342 
343 /* Get rectangles from the stack. */
344 /* Return the number of elements to pop (>0) if OK, <0 if error. */
345 private int
rect_get(local_rects_t * plr,os_ptr op,gs_memory_t * mem)346 rect_get(local_rects_t * plr, os_ptr op, gs_memory_t *mem)
347 {
348     int format, code;
349     uint n, count;
350     gs_rect *pr;
351     double rv[4];
352 
353     switch (r_type(op)) {
354 	case t_array:
355 	case t_mixedarray:
356 	case t_shortarray:
357 	case t_string:
358 	    code = num_array_format(op);
359 	    if (code < 0)
360 		return code;
361 	    format = code;
362 	    count = num_array_size(op, format);
363 	    if (count % 4)
364 		return_error(e_rangecheck);
365 	    count /= 4;
366 	    break;
367 	default:		/* better be 4 numbers */
368 	    code = num_params(op, 4, rv);
369 	    if (code < 0)
370 		return code;
371 	    plr->pr = plr->rl;
372 	    plr->count = 1;
373 	    plr->rl[0].q.x = (plr->rl[0].p.x = rv[0]) + rv[2];
374 	    plr->rl[0].q.y = (plr->rl[0].p.y = rv[1]) + rv[3];
375 	    return 4;
376     }
377     plr->count = count;
378     if (count <= MAX_LOCAL_RECTS)
379 	pr = plr->rl;
380     else {
381 	pr = (gs_rect *)gs_alloc_byte_array(mem, count, sizeof(gs_rect),
382 					    "rect_get");
383 	if (pr == 0)
384 	    return_error(e_VMerror);
385     }
386     plr->pr = pr;
387     for (n = 0; n < count; n++, pr++) {
388 	ref rnum;
389 	int i;
390 
391 	for (i = 0; i < 4; i++) {
392 	    code = num_array_get(mem, (const ref *)op, format,
393 				 (n << 2) + i, &rnum);
394 	    switch (code) {
395 		case t_integer:
396 		    rv[i] = rnum.value.intval;
397 		    break;
398 		case t_real:
399 		    rv[i] = rnum.value.realval;
400 		    break;
401 		default:	/* code < 0 */
402 		    return code;
403 	    }
404 	}
405 	pr->q.x = (pr->p.x = rv[0]) + rv[2];
406 	pr->q.y = (pr->p.y = rv[1]) + rv[3];
407     }
408     return 1;
409 }
410 
411 /* Release the rectangle list if needed. */
412 private void
rect_release(local_rects_t * plr,gs_memory_t * mem)413 rect_release(local_rects_t * plr, gs_memory_t *mem)
414 {
415     if (plr->pr != plr->rl)
416 	gs_free_object(mem, plr->pr, "rect_release");
417 }
418 
419 /* ------ Graphics state ------ */
420 
421 /* <llx> <lly> <urx> <ury> setbbox - */
422 int
zsetbbox(i_ctx_t * i_ctx_p)423 zsetbbox(i_ctx_t *i_ctx_p)
424 {
425     os_ptr op = osp;
426     double box[4];
427 
428     int code = num_params(op, 4, box);
429 
430     if (code < 0)
431 	return code;
432     if ((code = gs_setbbox(igs, box[0], box[1], box[2], box[3])) < 0)
433 	return code;
434     pop(4);
435     return 0;
436 }
437 
438 /* ------ Initialization procedure ------ */
439 
440 const op_def zdps1_l2_op_defs[] =
441 {
442     op_def_begin_level2(),
443 		/* Graphics state */
444     {"0currentstrokeadjust", zcurrentstrokeadjust},
445     {"1setstrokeadjust", zsetstrokeadjust},
446 		/* Graphics state objects */
447     {"1copy", z1copy},
448     {"1currentgstate", zcurrentgstate},
449     {"0gstate", zgstate},
450     {"1setgstate", zsetgstate},
451 		/* Rectangles */
452     {"1.rectappend", zrectappend},
453     {"1rectclip", zrectclip},
454     {"1rectfill", zrectfill},
455     {"1rectstroke", zrectstroke},
456 		/* Graphics state components */
457     {"4setbbox", zsetbbox},
458     op_def_end(0)
459 };
460 
461 /* ------ Internal routines ------ */
462 
463 /* Ensure that a gstate is not shared with an outer save level. */
464 /* *op is of type t_astruct(igstate_obj). */
465 private int
gstate_unshare(i_ctx_t * i_ctx_p)466 gstate_unshare(i_ctx_t *i_ctx_p)
467 {
468     os_ptr op = osp;
469     ref *pgsref = &r_ptr(op, igstate_obj)->gstate;
470     gs_state *pgs = r_ptr(pgsref, gs_state);
471     gs_state *pnew;
472     int_gstate *isp;
473 
474     if (!ref_must_save(pgsref))
475 	return 0;
476     /* Copy the gstate. */
477     pnew = gs_gstate(pgs);
478     if (pnew == 0)
479 	return_error(e_VMerror);
480     isp = gs_int_gstate(pnew);
481     int_gstate_map_refs(isp, ref_mark_new);
482     ref_do_save(op, pgsref, "gstate_unshare");
483     make_istruct_new(pgsref, 0, pnew);
484     return 0;
485 }
486