xref: /plan9/sys/src/cmd/gs/src/zdevice2.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1993, 1995, 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: zdevice2.c,v 1.10 2005/10/04 06:30:02 ray Exp $ */
18 /* Level 2 device operators */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "ghost.h"
22 #include "oper.h"
23 #include "dstack.h"		/* for dict_find_name */
24 #include "estack.h"
25 #include "idict.h"
26 #include "idparam.h"
27 #include "igstate.h"
28 #include "iname.h"
29 #include "iutil.h"
30 #include "store.h"
31 #include "gxdevice.h"
32 #include "gsstate.h"
33 
34 /* Exported for zfunc4.c */
35 int z2copy(i_ctx_t *);
36 
37 /* Forward references */
38 private int z2copy_gstate(i_ctx_t *);
39 private int push_callout(i_ctx_t *, const char *);
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 /* We export this for recognition in FunctionType 4 functions. */
45 int
z2copy(i_ctx_t * i_ctx_p)46 z2copy(i_ctx_t *i_ctx_p)
47 {
48     os_ptr op = osp;
49     int code = zcopy(i_ctx_p);
50 
51     if (code >= 0)
52 	return code;
53     if (!r_has_type(op, t_astruct))
54 	return code;
55     return z2copy_gstate(i_ctx_p);
56 }
57 
58 /* - .currentshowpagecount <count> true */
59 /* - .currentshowpagecount false */
60 private int
zcurrentshowpagecount(i_ctx_t * i_ctx_p)61 zcurrentshowpagecount(i_ctx_t *i_ctx_p)
62 {
63     os_ptr op = osp;
64     gx_device *dev = gs_currentdevice(igs);
65 
66     if ((*dev_proc(dev, get_page_device))(dev) == 0) {
67 	push(1);
68 	make_false(op);
69     } else {
70 	push(2);
71 	make_int(op - 1, dev->ShowpageCount);
72 	make_true(op);
73     }
74     return 0;
75 }
76 
77 /* - .currentpagedevice <dict> <bool> */
78 private int
zcurrentpagedevice(i_ctx_t * i_ctx_p)79 zcurrentpagedevice(i_ctx_t *i_ctx_p)
80 {
81     os_ptr op = osp;
82     gx_device *dev = gs_currentdevice(igs);
83 
84     push(2);
85     if ((*dev_proc(dev, get_page_device))(dev) != 0) {
86 	op[-1] = istate->pagedevice;
87 	make_true(op);
88     } else {
89 	make_null(op - 1);
90 	make_false(op);
91     }
92     return 0;
93 }
94 
95 /* <local_dict|null> .setpagedevice - */
96 private int
zsetpagedevice(i_ctx_t * i_ctx_p)97 zsetpagedevice(i_ctx_t *i_ctx_p)
98 {
99     os_ptr op = osp;
100     int code;
101 
102 /******
103     if ( igs->in_cachedevice )
104 	return_error(e_undefined);
105  ******/
106     if (r_has_type(op, t_dictionary)) {
107 	check_dict_read(*op);
108 #if 0	/****************/
109 	/*
110 	 * In order to avoid invalidaccess errors on setpagedevice,
111 	 * the dictionary must be allocated in local VM.
112 	 */
113 	if (!(r_is_local(op)))
114 	    return_error(e_invalidaccess);
115 #endif	/****************/
116 	/* Make the dictionary read-only. */
117 	code = zreadonly(i_ctx_p);
118 	if (code < 0)
119 	    return code;
120     } else {
121 	check_type(*op, t_null);
122     }
123     istate->pagedevice = *op;
124     pop(1);
125     return 0;
126 }
127 
128 /* Default Install/BeginPage/EndPage procedures */
129 /* that just call the procedure in the device. */
130 
131 /* - .callinstall - */
132 private int
zcallinstall(i_ctx_t * i_ctx_p)133 zcallinstall(i_ctx_t *i_ctx_p)
134 {
135     gx_device *dev = gs_currentdevice(igs);
136 
137     if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
138 	int code = (*dev->page_procs.install) (dev, igs);
139 
140 	if (code < 0)
141 	    return code;
142     }
143     return 0;
144 }
145 
146 /* <showpage_count> .callbeginpage - */
147 private int
zcallbeginpage(i_ctx_t * i_ctx_p)148 zcallbeginpage(i_ctx_t *i_ctx_p)
149 {
150     os_ptr op = osp;
151     gx_device *dev = gs_currentdevice(igs);
152 
153     check_type(*op, t_integer);
154     if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
155 	int code = (*dev->page_procs.begin_page)(dev, igs);
156 
157 	if (code < 0)
158 	    return code;
159     }
160     pop(1);
161     return 0;
162 }
163 
164 /* <showpage_count> <reason_int> .callendpage <flush_bool> */
165 private int
zcallendpage(i_ctx_t * i_ctx_p)166 zcallendpage(i_ctx_t *i_ctx_p)
167 {
168     os_ptr op = osp;
169     gx_device *dev = gs_currentdevice(igs);
170     int code;
171 
172     check_type(op[-1], t_integer);
173     check_type(*op, t_integer);
174     if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
175 	code = (*dev->page_procs.end_page)(dev, (int)op->value.intval, igs);
176 	if (code < 0)
177 	    return code;
178 	if (code > 1)
179 	    return_error(e_rangecheck);
180     } else {
181 	code = (op->value.intval == 2 ? 0 : 1);
182     }
183     make_bool(op - 1, code);
184     pop(1);
185     return 0;
186 }
187 
188 /* ------ Wrappers for operators that save the graphics state. ------ */
189 
190 /* When saving the state with the current device a page device, */
191 /* we need to make sure that the page device dictionary exists */
192 /* so that grestore can use it to reset the device parameters. */
193 /* This may have significant performance consequences, but we don't see */
194 /* any way around it. */
195 
196 /* Check whether we need to call out to create the page device dictionary. */
197 private bool
save_page_device(gs_state * pgs)198 save_page_device(gs_state *pgs)
199 {
200     return
201 	(r_has_type(&gs_int_gstate(pgs)->pagedevice, t_null) &&
202 	 (*dev_proc(gs_currentdevice(pgs), get_page_device))(gs_currentdevice(pgs)) != 0);
203 }
204 
205 /* - gsave - */
206 private int
z2gsave(i_ctx_t * i_ctx_p)207 z2gsave(i_ctx_t *i_ctx_p)
208 {
209     if (!save_page_device(igs))
210 	return gs_gsave(igs);
211     return push_callout(i_ctx_p, "%gsavepagedevice");
212 }
213 
214 /* - save - */
215 private int
z2save(i_ctx_t * i_ctx_p)216 z2save(i_ctx_t *i_ctx_p)
217 {
218     if (!save_page_device(igs))
219 	return zsave(i_ctx_p);
220     return push_callout(i_ctx_p, "%savepagedevice");
221 }
222 
223 /* - gstate <gstate> */
224 private int
z2gstate(i_ctx_t * i_ctx_p)225 z2gstate(i_ctx_t *i_ctx_p)
226 {
227     if (!save_page_device(igs))
228 	return zgstate(i_ctx_p);
229     return push_callout(i_ctx_p, "%gstatepagedevice");
230 }
231 
232 /* <gstate1> <gstate2> copy <gstate2> */
233 private int
z2copy_gstate(i_ctx_t * i_ctx_p)234 z2copy_gstate(i_ctx_t *i_ctx_p)
235 {
236     if (!save_page_device(igs))
237 	return zcopy_gstate(i_ctx_p);
238     return push_callout(i_ctx_p, "%copygstatepagedevice");
239 }
240 
241 /* <gstate> currentgstate <gstate> */
242 private int
z2currentgstate(i_ctx_t * i_ctx_p)243 z2currentgstate(i_ctx_t *i_ctx_p)
244 {
245     if (!save_page_device(igs))
246 	return zcurrentgstate(i_ctx_p);
247     return push_callout(i_ctx_p, "%currentgstatepagedevice");
248 }
249 
250 /* ------ Wrappers for operators that reset the graphics state. ------ */
251 
252 /* Check whether we need to call out to restore the page device. */
253 private bool
restore_page_device(const gs_state * pgs_old,const gs_state * pgs_new)254 restore_page_device(const gs_state * pgs_old, const gs_state * pgs_new)
255 {
256     gx_device *dev_old = gs_currentdevice(pgs_old);
257     gx_device *dev_new;
258     gx_device *dev_t1;
259     gx_device *dev_t2;
260     bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
261     	&gs_int_gstate(pgs_new)->pagedevice);
262 
263     if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
264 	return false;
265     /* If we are going to putdeviceparams in a callout, we need to */
266     /* unlock temporarily.  The device will be re-locked as needed */
267     /* by putdeviceparams from the pgs_old->pagedevice dict state. */
268     if (!samepagedevice)
269         dev_old->LockSafetyParams = false;
270     dev_new = gs_currentdevice(pgs_new);
271     if (dev_old != dev_new) {
272 	if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
273 	    return false;
274 	if (dev_t1 != dev_t2)
275 	    return true;
276     }
277     /*
278      * The current implementation of setpagedevice just sets new
279      * parameters in the same device object, so we have to check
280      * whether the page device dictionaries are the same.
281      */
282     return !samepagedevice;
283 }
284 
285 /* - grestore - */
286 private int
z2grestore(i_ctx_t * i_ctx_p)287 z2grestore(i_ctx_t *i_ctx_p)
288 {
289     if (!restore_page_device(igs, gs_state_saved(igs)))
290 	return gs_grestore(igs);
291     return push_callout(i_ctx_p, "%grestorepagedevice");
292 }
293 
294 /* - grestoreall - */
295 private int
z2grestoreall(i_ctx_t * i_ctx_p)296 z2grestoreall(i_ctx_t *i_ctx_p)
297 {
298     for (;;) {
299 	if (!restore_page_device(igs, gs_state_saved(igs))) {
300 	    bool done = !gs_state_saved(gs_state_saved(igs));
301 
302 	    gs_grestore(igs);
303 	    if (done)
304 		break;
305 	} else
306 	    return push_callout(i_ctx_p, "%grestoreallpagedevice");
307     }
308     return 0;
309 }
310 
311 /* <save> restore - */
312 private int
z2restore(i_ctx_t * i_ctx_p)313 z2restore(i_ctx_t *i_ctx_p)
314 {
315     while (gs_state_saved(gs_state_saved(igs))) {
316 	if (restore_page_device(igs, gs_state_saved(igs)))
317 	    return push_callout(i_ctx_p, "%restore1pagedevice");
318 	gs_grestore(igs);
319     }
320     if (restore_page_device(igs, gs_state_saved(igs)))
321 	return push_callout(i_ctx_p, "%restorepagedevice");
322     return zrestore(i_ctx_p);
323 }
324 
325 /* <gstate> setgstate - */
326 private int
z2setgstate(i_ctx_t * i_ctx_p)327 z2setgstate(i_ctx_t *i_ctx_p)
328 {
329     os_ptr op = osp;
330 
331     check_stype(*op, st_igstate_obj);
332     if (!restore_page_device(igs, igstate_ptr(op)))
333 	return zsetgstate(i_ctx_p);
334     return push_callout(i_ctx_p, "%setgstatepagedevice");
335 }
336 
337 /* ------ Initialization procedure ------ */
338 
339 const op_def zdevice2_l2_op_defs[] =
340 {
341     op_def_begin_level2(),
342     {"0.currentshowpagecount", zcurrentshowpagecount},
343     {"0.currentpagedevice", zcurrentpagedevice},
344     {"1.setpagedevice", zsetpagedevice},
345 		/* Note that the following replace prior definitions */
346 		/* in the indicated files: */
347     {"1copy", z2copy},		/* zdps1.c */
348     {"0gsave", z2gsave},	/* zgstate.c */
349     {"0save", z2save},		/* zvmem.c */
350     {"0gstate", z2gstate},	/* zdps1.c */
351     {"1currentgstate", z2currentgstate},	/* zdps1.c */
352     {"0grestore", z2grestore},	/* zgstate.c */
353     {"0grestoreall", z2grestoreall},	/* zgstate.c */
354     {"1restore", z2restore},	/* zvmem.c */
355     {"1setgstate", z2setgstate},	/* zdps1.c */
356 		/* Default Install/BeginPage/EndPage procedures */
357 		/* that just call the procedure in the device. */
358     {"0.callinstall", zcallinstall},
359     {"1.callbeginpage", zcallbeginpage},
360     {"2.callendpage", zcallendpage},
361     op_def_end(0)
362 };
363 
364 /* ------ Internal routines ------ */
365 
366 /* Call out to a PostScript procedure. */
367 private int
push_callout(i_ctx_t * i_ctx_p,const char * callout_name)368 push_callout(i_ctx_t *i_ctx_p, const char *callout_name)
369 {
370     int code;
371 
372     check_estack(1);
373     code = name_enter_string(imemory, callout_name, esp + 1);
374     if (code < 0)
375 	return code;
376     ++esp;
377     r_set_attrs(esp, a_executable);
378     return o_push_estack;
379 }
380