diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/gs/src/zupath.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/gs/src/zupath.c')
-rwxr-xr-x | sys/src/cmd/gs/src/zupath.c | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/sys/src/cmd/gs/src/zupath.c b/sys/src/cmd/gs/src/zupath.c new file mode 100755 index 000000000..cf11f3d42 --- /dev/null +++ b/sys/src/cmd/gs/src/zupath.c @@ -0,0 +1,700 @@ +/* Copyright (C) 1990, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + For more information about licensing, please refer to + http://www.ghostscript.com/licensing/. For information on + commercial licensing, go to http://www.artifex.com/licensing/ or + contact Artifex Software, Inc., 101 Lucas Valley Road #110, + San Rafael, CA 94903, U.S.A., +1(415)492-9861. +*/ + +/* $Id: zupath.c,v 1.10 2004/08/04 19:36:13 stefan Exp $ */ +/* Operators related to user paths */ +#include "ghost.h" +#include "oper.h" +#include "oparc.h" +#include "idict.h" +#include "dstack.h" +#include "igstate.h" +#include "iname.h" +#include "iutil.h" +#include "store.h" +#include "stream.h" +#include "ibnum.h" +#include "gsmatrix.h" +#include "gsstate.h" +#include "gscoord.h" +#include "gspaint.h" +#include "gxfixed.h" +#include "gxdevice.h" +#include "gspath.h" +#include "gzpath.h" /* for saving path */ +#include "gzstate.h" /* for accessing path */ + +/* Imported data */ +extern const gx_device gs_hit_device; +extern const int gs_hit_detected; + +/* Forward references */ +private int upath_append(os_ptr, i_ctx_t *); +private int upath_stroke(i_ctx_t *, gs_matrix *); + +/* ---------------- Insideness testing ---------------- */ + +/* Forward references */ +private int in_test(i_ctx_t *, int (*)(gs_state *)); +private int in_path(os_ptr, i_ctx_t *, gx_device *); +private int in_path_result(i_ctx_t *, int, int); +private int in_utest(i_ctx_t *, int (*)(gs_state *)); +private int in_upath(i_ctx_t *, gx_device *); +private int in_upath_result(i_ctx_t *, int, int); + +/* <x> <y> ineofill <bool> */ +/* <userpath> ineofill <bool> */ +private int +zineofill(i_ctx_t *i_ctx_p) +{ + return in_test(i_ctx_p, gs_eofill); +} + +/* <x> <y> infill <bool> */ +/* <userpath> infill <bool> */ +private int +zinfill(i_ctx_t *i_ctx_p) +{ + return in_test(i_ctx_p, gs_fill); +} + +/* <x> <y> instroke <bool> */ +/* <userpath> instroke <bool> */ +private int +zinstroke(i_ctx_t *i_ctx_p) +{ + return in_test(i_ctx_p, gs_stroke); +} + +/* <x> <y> <userpath> inueofill <bool> */ +/* <userpath1> <userpath2> inueofill <bool> */ +private int +zinueofill(i_ctx_t *i_ctx_p) +{ + return in_utest(i_ctx_p, gs_eofill); +} + +/* <x> <y> <userpath> inufill <bool> */ +/* <userpath1> <userpath2> inufill <bool> */ +private int +zinufill(i_ctx_t *i_ctx_p) +{ + return in_utest(i_ctx_p, gs_fill); +} + +/* <x> <y> <userpath> inustroke <bool> */ +/* <x> <y> <userpath> <matrix> inustroke <bool> */ +/* <userpath1> <userpath2> inustroke <bool> */ +/* <userpath1> <userpath2> <matrix> inustroke <bool> */ +private int +zinustroke(i_ctx_t *i_ctx_p) +{ /* This is different because of the optional matrix operand. */ + os_ptr op = osp; + int code = gs_gsave(igs); + int spop, npop; + gs_matrix mat; + gx_device hdev; + + if (code < 0) + return code; + if ((spop = upath_stroke(i_ctx_p, &mat)) < 0) { + gs_grestore(igs); + return spop; + } + if ((npop = in_path(op - spop, i_ctx_p, &hdev)) < 0) { + gs_grestore(igs); + return npop; + } + if (npop > 1) /* matrix was supplied */ + code = gs_concat(igs, &mat); + if (code >= 0) + code = gs_stroke(igs); + return in_upath_result(i_ctx_p, npop + spop, code); +} + +/* ------ Internal routines ------ */ + +/* Do the work of the non-user-path insideness operators. */ +private int +in_test(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *)) +{ + os_ptr op = osp; + gx_device hdev; + int npop = in_path(op, i_ctx_p, &hdev); + int code; + + if (npop < 0) + return npop; + code = (*paintproc)(igs); + return in_path_result(i_ctx_p, npop, code); +} + +/* Set up a clipping path and device for insideness testing. */ +private int +in_path(os_ptr oppath, i_ctx_t *i_ctx_p, gx_device * phdev) +{ + int code = gs_gsave(igs); + int npop; + double uxy[2]; + + if (code < 0) + return code; + code = num_params(oppath, 2, uxy); + if (code >= 0) { /* Aperture is a single pixel. */ + gs_point dxy; + gs_fixed_rect fr; + + gs_transform(igs, uxy[0], uxy[1], &dxy); + fr.p.x = fixed_floor(float2fixed(dxy.x)); + fr.p.y = fixed_floor(float2fixed(dxy.y)); + fr.q.x = fr.p.x + fixed_1; + fr.q.y = fr.p.y + fixed_1; + code = gx_clip_to_rectangle(igs, &fr); + npop = 2; + } else { /* Aperture is a user path. */ + /* We have to set the clipping path without disturbing */ + /* the current path. */ + gx_path *ipath = igs->path; + gx_path save; + + gx_path_init_local(&save, imemory); + gx_path_assign_preserve(&save, ipath); + gs_newpath(igs); + code = upath_append(oppath, i_ctx_p); + if (code >= 0) + code = gx_clip_to_path(igs); + gx_path_assign_free(igs->path, &save); + npop = 1; + } + if (code < 0) { + gs_grestore(igs); + return code; + } + /* Install the hit detection device. */ + gx_set_device_color_1(igs); + gx_device_init((gx_device *) phdev, (const gx_device *)&gs_hit_device, + NULL, true); + phdev->width = phdev->height = max_int; + gx_device_fill_in_procs(phdev); + gx_set_device_only(igs, phdev); + return npop; +} + +/* Finish an insideness test. */ +private int +in_path_result(i_ctx_t *i_ctx_p, int npop, int code) +{ + os_ptr op = osp; + bool result; + + gs_grestore(igs); /* matches gsave in in_path */ + if (code == gs_hit_detected) + result = true; + else if (code == 0) /* completed painting without a hit */ + result = false; + else /* error */ + return code; + npop--; + pop(npop); + op -= npop; + make_bool(op, result); + return 0; + +} + +/* Do the work of the user-path insideness operators. */ +private int +in_utest(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *)) +{ + gx_device hdev; + int npop = in_upath(i_ctx_p, &hdev); + int code; + + if (npop < 0) + return npop; + code = (*paintproc)(igs); + return in_upath_result(i_ctx_p, npop, code); +} + +/* Set up a clipping path and device for insideness testing */ +/* with a user path. */ +private int +in_upath(i_ctx_t *i_ctx_p, gx_device * phdev) +{ + os_ptr op = osp; + int code = gs_gsave(igs); + int npop; + + if (code < 0) + return code; + if ((code = upath_append(op, i_ctx_p)) < 0 || + (npop = in_path(op - 1, i_ctx_p, phdev)) < 0 + ) { + gs_grestore(igs); + return code; + } + return npop + 1; +} + +/* Finish an insideness test with a user path. */ +private int +in_upath_result(i_ctx_t *i_ctx_p, int npop, int code) +{ + gs_grestore(igs); /* matches gsave in in_upath */ + return in_path_result(i_ctx_p, npop, code); +} + +/* ---------------- User paths ---------------- */ + +/* User path operator codes */ +typedef enum { + upath_op_setbbox = 0, + upath_op_moveto = 1, + upath_op_rmoveto = 2, + upath_op_lineto = 3, + upath_op_rlineto = 4, + upath_op_curveto = 5, + upath_op_rcurveto = 6, + upath_op_arc = 7, + upath_op_arcn = 8, + upath_op_arct = 9, + upath_op_closepath = 10, + upath_op_ucache = 11 +} upath_op; + +#define UPATH_MAX_OP 11 +#define UPATH_REPEAT 32 +static const byte up_nargs[UPATH_MAX_OP + 1] = { + 4, 2, 2, 2, 2, 6, 6, 5, 5, 5, 0, 0 +}; + +/* Declare operator procedures not declared in opextern.h. */ +int zsetbbox(i_ctx_t *); +private int zucache(i_ctx_t *); + +#undef zp +static const op_proc_t up_ops[UPATH_MAX_OP + 1] = { + zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto, + zcurveto, zrcurveto, zarc, zarcn, zarct, + zclosepath, zucache +}; + +/* - ucache - */ +private int +zucache(i_ctx_t *i_ctx_p) +{ + /* A no-op for now. */ + return 0; +} + +/* <userpath> uappend - */ +private int +zuappend(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + int code = gs_gsave(igs); + + if (code < 0) + return code; + if ((code = upath_append(op, i_ctx_p)) >= 0) + code = gs_upmergepath(igs); + gs_grestore(igs); + if (code < 0) + return code; + pop(1); + return 0; +} + +/* <userpath> ueofill - */ +private int +zueofill(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + int code = gs_gsave(igs); + + if (code < 0) + return code; + if ((code = upath_append(op, i_ctx_p)) >= 0) + code = gs_eofill(igs); + gs_grestore(igs); + if (code < 0) + return code; + pop(1); + return 0; +} + +/* <userpath> ufill - */ +private int +zufill(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + int code = gs_gsave(igs); + + if (code < 0) + return code; + if ((code = upath_append(op, i_ctx_p)) >= 0) + code = gs_fill(igs); + gs_grestore(igs); + if (code < 0) + return code; + pop(1); + return 0; +} + +/* <userpath> ustroke - */ +/* <userpath> <matrix> ustroke - */ +private int +zustroke(i_ctx_t *i_ctx_p) +{ + int code = gs_gsave(igs); + int npop; + + if (code < 0) + return code; + if ((code = npop = upath_stroke(i_ctx_p, NULL)) >= 0) + code = gs_stroke(igs); + gs_grestore(igs); + if (code < 0) + return code; + pop(npop); + return 0; +} + +/* <userpath> ustrokepath - */ +/* <userpath> <matrix> ustrokepath - */ +private int +zustrokepath(i_ctx_t *i_ctx_p) +{ + gx_path save; + int code, npop; + + /* Save and reset the path. */ + gx_path_init_local(&save, imemory); + gx_path_assign_preserve(&save, igs->path); + if ((code = npop = upath_stroke(i_ctx_p, NULL)) < 0 || + (code = gs_strokepath(igs)) < 0 + ) { + gx_path_assign_free(igs->path, &save); + return code; + } + gx_path_free(&save, "ustrokepath"); + pop(npop); + return 0; +} + +/* <with_ucache> upath <userpath> */ +/* We do all the work in a procedure that is also used to construct */ +/* the UnpaintedPath user path for ImageType 2 images. */ +int make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath, + bool with_ucache); +private int +zupath(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + + check_type(*op, t_boolean); + return make_upath(i_ctx_p, op, igs, igs->path, op->value.boolval); +} +int +make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath, + bool with_ucache) +{ + int size = (with_ucache ? 6 : 5); + gs_path_enum penum; + int op; + ref *next; + int code; + + /* Compute the size of the user path array. */ + { + gs_fixed_point pts[3]; + + gx_path_enum_init(&penum, ppath); + while ((op = gx_path_enum_next(&penum, pts)) != 0) { + switch (op) { + case gs_pe_moveto: + case gs_pe_lineto: + size += 3; + continue; + case gs_pe_curveto: + size += 7; + continue; + case gs_pe_closepath: + size += 1; + continue; + default: + return_error(e_unregistered); + } + } + } + code = ialloc_ref_array(rupath, a_all | a_executable, size, + "make_upath"); + if (code < 0) + return code; + /* Construct the path. */ + next = rupath->value.refs; + if (with_ucache) { + if ((code = name_enter_string(pgs->memory, "ucache", next)) < 0) + return code; + r_set_attrs(next, a_executable | l_new); + ++next; + } { + gs_rect bbox; + + if ((code = gs_upathbbox(pgs, &bbox, true)) < 0) { + /* + * Note: Adobe throws 'nocurrentpoint' error, but the PLRM + * not list this as a possible error from 'upath', so we + * set a reasonable default bbox instead. + */ + if (code != e_nocurrentpoint) + return code; + bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0; + } + make_real_new(next, bbox.p.x); + make_real_new(next + 1, bbox.p.y); + make_real_new(next + 2, bbox.q.x); + make_real_new(next + 3, bbox.q.y); + next += 4; + if ((code = name_enter_string(pgs->memory, "setbbox", next)) < 0) + return code; + r_set_attrs(next, a_executable | l_new); + ++next; + } + { + gs_point pts[3]; + + /* Patch the path in the gstate to set up the enumerator. */ + gx_path *save_path = pgs->path; + + pgs->path = ppath; + gs_path_enum_copy_init(&penum, pgs, false); + pgs->path = save_path; + while ((op = gs_path_enum_next(&penum, pts)) != 0) { + const char *opstr; + + switch (op) { + case gs_pe_moveto: + opstr = "moveto"; + goto ml; + case gs_pe_lineto: + opstr = "lineto"; + ml:make_real_new(next, pts[0].x); + make_real_new(next + 1, pts[0].y); + next += 2; + break; + case gs_pe_curveto: + opstr = "curveto"; + make_real_new(next, pts[0].x); + make_real_new(next + 1, pts[0].y); + make_real_new(next + 2, pts[1].x); + make_real_new(next + 3, pts[1].y); + make_real_new(next + 4, pts[2].x); + make_real_new(next + 5, pts[2].y); + next += 6; + break; + case gs_pe_closepath: + opstr = "closepath"; + break; + default: + return_error(e_unregistered); + } + if ((code = name_enter_string(pgs->memory, opstr, next)) < 0) + return code; + r_set_attrs(next, a_executable); + ++next; + } + } + return 0; +} + +/* ------ Internal routines ------ */ + +/* Append a user path to the current path. */ +private inline int +upath_append_aux(os_ptr oppath, i_ctx_t *i_ctx_p) +{ + ref opcodes; + check_read(*oppath); + gs_newpath(igs); +/****** ROUND tx AND ty ******/ + if (!r_is_array(oppath)) + return_error(e_typecheck); + + if ( r_size(oppath) == 2 && + array_get(imemory, oppath, 1, &opcodes) >= 0 && + r_has_type(&opcodes, t_string) + ) { /* 1st element is operands, 2nd is operators */ + ref operands; + int code, format; + int repcount = 1; + const byte *opp; + uint ocount, i = 0; + + array_get(imemory, oppath, 0, &operands); + code = num_array_format(&operands); + if (code < 0) + return code; + format = code; + opp = opcodes.value.bytes; + ocount = r_size(&opcodes); + while (ocount--) { + byte opx = *opp++; + + if (opx > UPATH_REPEAT) + repcount = opx - UPATH_REPEAT; + else if (opx > UPATH_MAX_OP) + return_error(e_rangecheck); + else { /* operator */ + do { + os_ptr op = osp; + byte opargs = up_nargs[opx]; + + while (opargs--) { + push(1); + code = num_array_get(imemory, &operands, format, i++, op); + switch (code) { + case t_integer: + r_set_type_attrs(op, t_integer, 0); + break; + case t_real: + r_set_type_attrs(op, t_real, 0); + break; + default: + return_error(e_typecheck); + } + } + code = (*up_ops[opx])(i_ctx_p); + if (code < 0) + return code; + } + while (--repcount); + repcount = 1; + } + } + } else { /* Ordinary executable array. */ + const ref *arp = oppath; + uint ocount = r_size(oppath); + long index = 0; + int argcount = 0; + op_proc_t oproc; + int opx, code; + + for (; index < ocount; index++) { + ref rup; + ref *defp; + os_ptr op = osp; + + array_get(imemory, arp, index, &rup); + switch (r_type(&rup)) { + case t_integer: + case t_real: + argcount++; + push(1); + *op = rup; + break; + case t_name: + if (!r_has_attr(&rup, a_executable)) + return_error(e_typecheck); + if (dict_find(systemdict, &rup, &defp) <= 0) + return_error(e_undefined); + if (r_btype(defp) != t_operator) + return_error(e_typecheck); + goto xop; + case t_operator: + defp = &rup; + xop:if (!r_has_attr(defp, a_executable)) + return_error(e_typecheck); + oproc = real_opproc(defp); + for (opx = 0; opx <= UPATH_MAX_OP; opx++) + if (oproc == up_ops[opx]) + break; + if (opx > UPATH_MAX_OP || argcount != up_nargs[opx]) + return_error(e_typecheck); + code = (*oproc)(i_ctx_p); + if (code < 0) + return code; + argcount = 0; + break; + default: + return_error(e_typecheck); + } + } + if (argcount) + return_error(e_typecheck); /* leftover args */ + } + return 0; +} +private int +upath_append(os_ptr oppath, i_ctx_t *i_ctx_p) +{ + int code = upath_append_aux(oppath, i_ctx_p); + + if (code < 0) + return code; + igs->current_point.x = fixed2float(igs->path->position.x); + igs->current_point.y = fixed2float(igs->path->position.y); + return 0; +} + +/* Append a user path to the current path, and then apply or return */ +/* a transformation if one is supplied. */ +private int +upath_stroke(i_ctx_t *i_ctx_p, gs_matrix *pmat) +{ + os_ptr op = osp; + int code, npop; + gs_matrix mat; + + if ((code = read_matrix(imemory, op, &mat)) >= 0) { + if ((code = upath_append(op - 1, i_ctx_p)) >= 0) { + if (pmat) + *pmat = mat; + else + code = gs_concat(igs, &mat); + } + npop = 2; + } else { + if ((code = upath_append(op, i_ctx_p)) >= 0) + if (pmat) + gs_make_identity(pmat); + npop = 1; + } + return (code < 0 ? code : npop); +} + +/* ---------------- Initialization procedure ---------------- */ + +const op_def zupath_l2_op_defs[] = +{ + op_def_begin_level2(), + /* Insideness testing */ + {"1ineofill", zineofill}, + {"1infill", zinfill}, + {"1instroke", zinstroke}, + {"2inueofill", zinueofill}, + {"2inufill", zinufill}, + {"2inustroke", zinustroke}, + /* User paths */ + {"1uappend", zuappend}, + {"0ucache", zucache}, + {"1ueofill", zueofill}, + {"1ufill", zufill}, + {"1upath", zupath}, + {"1ustroke", zustroke}, + {"1ustrokepath", zustrokepath}, + op_def_end(0) +}; |