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