/* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: zgstate.c,v 1.2.6.1.2.1 2003/01/17 00:49:06 giles Exp $ */ /* Graphics state operators */ #include "math_.h" #include "ghost.h" #include "oper.h" #include "ialloc.h" #include "icremap.h" #include "idict.h" #include "istruct.h" #include "igstate.h" #include "gsmatrix.h" #include "store.h" /* Structure descriptors */ private_st_int_gstate(); private_st_int_remap_color_info(); /* ------ Utilities ------ */ private int zset_real(i_ctx_t *i_ctx_p, int (*set_proc)(P2(gs_state *, floatp))) { os_ptr op = osp; double param; int code = real_param(op, ¶m); if (code < 0) return_op_typecheck(op); code = set_proc(igs, param); if (!code) pop(1); return code; } private int zset_bool(i_ctx_t *i_ctx_p, void (*set_proc)(P2(gs_state *, bool))) { os_ptr op = osp; check_type(*op, t_boolean); set_proc(igs, op->value.boolval); pop(1); return 0; } private int zcurrent_bool(i_ctx_t *i_ctx_p, bool (*current_proc)(P1(const gs_state *))) { os_ptr op = osp; push(1); make_bool(op, current_proc(igs)); return 0; } /* ------ Operations on the entire graphics state ------ */ /* "Client" procedures */ private void *gs_istate_alloc(P1(gs_memory_t * mem)); private int gs_istate_copy(P2(void *to, const void *from)); private void gs_istate_free(P2(void *old, gs_memory_t * mem)); private const gs_state_client_procs istate_procs = { gs_istate_alloc, gs_istate_copy, gs_istate_free }; /* Initialize the graphics stack. */ gs_state * int_gstate_alloc(const gs_dual_memory_t * dmem) { int_gstate *iigs; ref proc0; int_remap_color_info_t *prci; gs_ref_memory_t *lmem = dmem->space_local; gs_ref_memory_t *gmem = dmem->space_global; gs_state *pgs = gs_state_alloc((gs_memory_t *)lmem); iigs = gs_alloc_struct((gs_memory_t *)lmem, int_gstate, &st_int_gstate, "int_gstate_alloc(int_gstate)"); int_gstate_map_refs(iigs, make_null); make_empty_array(&iigs->dash_pattern, a_all); gs_alloc_ref_array(lmem, &proc0, a_readonly + a_executable, 2, "int_gstate_alloc(proc0)"); make_oper(proc0.value.refs, 0, zpop); make_real(proc0.value.refs + 1, 0.0); iigs->black_generation = proc0; iigs->undercolor_removal = proc0; /* * Even though the gstate itself is allocated in local VM, the * container for the color remapping procedure must be allocated in * global VM so that the gstate can be copied into global VM. */ prci = gs_alloc_struct((gs_memory_t *)gmem, int_remap_color_info_t, &st_int_remap_color_info, "int_gstate_alloc(remap color info)"); make_struct(&iigs->remap_color_info, imemory_space(gmem), prci); clear_pagedevice(iigs); gs_state_set_client(pgs, iigs, &istate_procs); /* PostScript code wants limit clamping enabled. */ gs_setlimitclamp(pgs, true); /* * gsave and grestore only work properly * if there are always at least 2 entries on the stack. * We count on the PostScript initialization code to do a gsave. */ return pgs; } /* - gsave - */ int zgsave(i_ctx_t *i_ctx_p) { return gs_gsave(igs); } /* - grestore - */ int zgrestore(i_ctx_t *i_ctx_p) { return gs_grestore(igs); } /* - grestoreall - */ int zgrestoreall(i_ctx_t *i_ctx_p) { return gs_grestoreall(igs); } /* - initgraphics - */ private int zinitgraphics(i_ctx_t *i_ctx_p) { /* gs_initgraphics does a setgray; we must clear the interpreter's */ /* cached copy of the color space object. */ int code = gs_initgraphics(igs); if (code >= 0) make_null(&istate->colorspace.array); return code; } /* ------ Operations on graphics state elements ------ */ /* setlinewidth - */ private int zsetlinewidth(i_ctx_t *i_ctx_p) { os_ptr op = osp; /* * The Red Book doesn't say anything about this, but Adobe * interpreters return (or perhaps store) the absolute value * of the width. */ double width; int code = real_param(op, &width); if (code < 0) return_op_typecheck(op); code = gs_setlinewidth(igs, fabs(width)); if (code >= 0) pop(1); return code; } /* - currentlinewidth */ private int zcurrentlinewidth(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_real(op, gs_currentlinewidth(igs)); return 0; } /* .setlinecap - */ private int zsetlinecap(i_ctx_t *i_ctx_p) { os_ptr op = osp; int param; int code = int_param(op, max_int, ¶m); if (code < 0 || (code = gs_setlinecap(igs, (gs_line_cap) param)) < 0) return code; pop(1); return 0; } /* - currentlinecap */ private int zcurrentlinecap(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_int(op, (int)gs_currentlinecap(igs)); return 0; } /* .setlinejoin - */ private int zsetlinejoin(i_ctx_t *i_ctx_p) { os_ptr op = osp; int param; int code = int_param(op, max_int, ¶m); if (code < 0 || (code = gs_setlinejoin(igs, (gs_line_join) param)) < 0) return code; pop(1); return 0; } /* - currentlinejoin */ private int zcurrentlinejoin(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_int(op, (int)gs_currentlinejoin(igs)); return 0; } /* setmiterlimit - */ private int zsetmiterlimit(i_ctx_t *i_ctx_p) { return zset_real(i_ctx_p, gs_setmiterlimit); } /* - currentmiterlimit */ private int zcurrentmiterlimit(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_real(op, gs_currentmiterlimit(igs)); return 0; } /* setdash - */ private int zsetdash(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr op1 = op - 1; double offset; int code = real_param(op, &offset); uint i, n; gs_memory_t *mem = imemory; float *pattern; if (code < 0) return_op_typecheck(op); if (!r_is_array(op1)) return_op_typecheck(op1); /* Adobe interpreters apparently don't check the array for */ /* read access, so we won't either. */ /*check_read(*op1); */ /* Unpack the dash pattern and check it */ n = r_size(op1); pattern = (float *)gs_alloc_byte_array(mem, n, sizeof(float), "setdash"); if (pattern == 0) return_error(e_VMerror); for (i = 0, code = 0; i < n && code >= 0; ++i) { ref element; array_get(op1, (long)i, &element); code = float_param(&element, &pattern[i]); } if (code >= 0) code = gs_setdash(igs, pattern, n, offset); gs_free_object(mem, pattern, "setdash"); /* gs_setdash copies this */ if (code < 0) return code; ref_assign(&istate->dash_pattern, op1); pop(2); return code; } /* - currentdash */ private int zcurrentdash(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(2); ref_assign(op - 1, &istate->dash_pattern); make_real(op, gs_currentdash_offset(igs)); return 0; } /* setflat - */ private int zsetflat(i_ctx_t *i_ctx_p) { return zset_real(i_ctx_p, gs_setflat); } /* - currentflat */ private int zcurrentflat(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_real(op, gs_currentflat(igs)); return 0; } /* ------ Extensions ------ */ /* .setaccuratecurves - */ private int zsetaccuratecurves(i_ctx_t *i_ctx_p) { return zset_bool(i_ctx_p, gs_setaccuratecurves); } /* - .currentaccuratecurves */ private int zcurrentaccuratecurves(i_ctx_t *i_ctx_p) { return zcurrent_bool(i_ctx_p, gs_currentaccuratecurves); } /* .setcurvejoin - */ private int zsetcurvejoin(i_ctx_t *i_ctx_p) { os_ptr op = osp; int code; check_type(*op, t_integer); if (op->value.intval < -1 || op->value.intval > max_int) return_error(e_rangecheck); code = gs_setcurvejoin(igs, (int)op->value.intval); if (code < 0) return code; pop(1); return 0; } /* - .currentcurvejoin */ private int zcurrentcurvejoin(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_int(op, gs_currentcurvejoin(igs)); return 0; } /* .setfilladjust2 - */ private int zsetfilladjust2(i_ctx_t *i_ctx_p) { os_ptr op = osp; double adjust[2]; int code = num_params(op, 2, adjust); if (code < 0) return code; code = gs_setfilladjust(igs, adjust[0], adjust[1]); if (code < 0) return code; pop(2); return 0; } /* - .currentfilladjust2 */ private int zcurrentfilladjust2(i_ctx_t *i_ctx_p) { os_ptr op = osp; gs_point adjust; push(2); gs_currentfilladjust(igs, &adjust); make_real(op - 1, adjust.x); make_real(op, adjust.y); return 0; } /* .setdashadapt - */ private int zsetdashadapt(i_ctx_t *i_ctx_p) { return zset_bool(i_ctx_p, gs_setdashadapt); } /* - .currentdashadapt */ private int zcurrentdashadapt(i_ctx_t *i_ctx_p) { return zcurrent_bool(i_ctx_p, gs_currentdashadapt); } /* .setdotlength - */ private int zsetdotlength(i_ctx_t *i_ctx_p) { os_ptr op = osp; double length; int code = real_param(op - 1, &length); if (code < 0) return code; check_type(*op, t_boolean); code = gs_setdotlength(igs, length, op->value.boolval); if (code < 0) return code; pop(2); return 0; } /* - .currentdotlength */ private int zcurrentdotlength(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(2); make_real(op - 1, gs_currentdotlength(igs)); make_bool(op, gs_currentdotlength_absolute(igs)); return 0; } /* - .setdotorientation - */ private int zsetdotorientation(i_ctx_t *i_ctx_p) { return gs_setdotorientation(igs); } /* - .dotorientation - */ private int zdotorientation(i_ctx_t *i_ctx_p) { return gs_dotorientation(igs); } /* .setlimitclamp - */ private int zsetlimitclamp(i_ctx_t *i_ctx_p) { return zset_bool(i_ctx_p, gs_setlimitclamp); } /* - .currentlimitclamp */ private int zcurrentlimitclamp(i_ctx_t *i_ctx_p) { return zcurrent_bool(i_ctx_p, gs_currentlimitclamp); } /* ------ Initialization procedure ------ */ /* We need to split the table because of the 16-element limit. */ const op_def zgstate1_op_defs[] = { {"0.currentaccuratecurves", zcurrentaccuratecurves}, {"0.currentcurvejoin", zcurrentcurvejoin}, {"0currentdash", zcurrentdash}, {"0.currentdashadapt", zcurrentdashadapt}, {"0.currentdotlength", zcurrentdotlength}, {"0.currentfilladjust2", zcurrentfilladjust2}, {"0currentflat", zcurrentflat}, {"0.currentlimitclamp", zcurrentlimitclamp}, {"0currentlinecap", zcurrentlinecap}, {"0currentlinejoin", zcurrentlinejoin}, {"0currentlinewidth", zcurrentlinewidth}, {"0currentmiterlimit", zcurrentmiterlimit}, {"0.dotorientation", zdotorientation}, {"0grestore", zgrestore}, {"0grestoreall", zgrestoreall}, op_def_end(0) }; const op_def zgstate2_op_defs[] = { {"0gsave", zgsave}, {"0initgraphics", zinitgraphics}, {"1.setaccuratecurves", zsetaccuratecurves}, {"1.setcurvejoin", zsetcurvejoin}, {"2setdash", zsetdash}, {"1.setdashadapt", zsetdashadapt}, {"2.setdotlength", zsetdotlength}, {"0.setdotorientation", zsetdotorientation}, {"2.setfilladjust2", zsetfilladjust2}, {"1.setlimitclamp", zsetlimitclamp}, {"1setflat", zsetflat}, {"1.setlinecap", zsetlinecap}, {"1.setlinejoin", zsetlinejoin}, {"1setlinewidth", zsetlinewidth}, {"1setmiterlimit", zsetmiterlimit}, op_def_end(0) }; /* ------ Internal routines ------ */ /* Allocate the interpreter's part of a graphics state. */ private void * gs_istate_alloc(gs_memory_t * mem) { return gs_alloc_struct(mem, int_gstate, &st_int_gstate, "int_gsave"); } /* Copy the interpreter's part of a graphics state. */ private int gs_istate_copy(void *to, const void *from) { *(int_gstate *) to = *(const int_gstate *)from; return 0; } /* Free the interpreter's part of a graphics state. */ private void gs_istate_free(void *old, gs_memory_t * mem) { gs_free_object(mem, old, "int_grestore"); }