/* Copyright (C) 1991, 1992, 1994, 1996, 1997, 1998 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: gsdps1.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* Display PostScript graphics additions for Ghostscript library */ #include "math_.h" #include "gx.h" #include "gserrors.h" #include "gsmatrix.h" /* for gscoord.h */ #include "gscoord.h" #include "gspaint.h" #include "gxdevice.h" #include "gxfixed.h" #include "gxmatrix.h" #include "gspath.h" #include "gspath2.h" /* defines interface */ #include "gzpath.h" #include "gzcpath.h" #include "gzstate.h" /* * Define how much rounding slop setbbox should leave, * in device coordinates. Because of rounding in transforming * path coordinates to fixed point, the minimum realistic value is: * * #define box_rounding_slop_fixed (fixed_epsilon) * * But even this isn't enough to compensate for cumulative rounding error * in rmoveto or rcurveto. Instead, we somewhat arbitrarily use: */ #define box_rounding_slop_fixed (fixed_epsilon * 3) /* ------ Graphics state ------ */ /* Set the bounding box for the current path. */ int gs_setbbox(gs_state * pgs, floatp llx, floatp lly, floatp urx, floatp ury) { gs_rect ubox, dbox; gs_fixed_rect obox, bbox; gx_path *ppath = pgs->path; int code; if (llx > urx || lly > ury) return_error(gs_error_rangecheck); /* Transform box to device coordinates. */ ubox.p.x = llx; ubox.p.y = lly; ubox.q.x = urx; ubox.q.y = ury; if ((code = gs_bbox_transform(&ubox, &ctm_only(pgs), &dbox)) < 0) return code; /* Round the corners in opposite directions. */ /* Because we can't predict the magnitude of the dbox values, */ /* we add/subtract the slop after fixing. */ if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) || dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) || dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) || dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ) return_error(gs_error_limitcheck); bbox.p.x = (fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed; bbox.p.y = (fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed; bbox.q.x = (fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed; bbox.q.y = (fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed; if (gx_path_bbox(ppath, &obox) >= 0) { /* Take the union of the bboxes. */ ppath->bbox.p.x = min(obox.p.x, bbox.p.x); ppath->bbox.p.y = min(obox.p.y, bbox.p.y); ppath->bbox.q.x = max(obox.q.x, bbox.q.x); ppath->bbox.q.y = max(obox.q.y, bbox.q.y); } else { /* empty path *//* Just set the bbox. */ ppath->bbox = bbox; } ppath->bbox_set = 1; return 0; } /* ------ Rectangles ------ */ /* Append a list of rectangles to a path. */ int gs_rectappend(gs_state * pgs, const gs_rect * pr, uint count) { for (; count != 0; count--, pr++) { floatp px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y; int code; /* Ensure counter-clockwise drawing. */ if ((qx >= px) != (qy >= py)) qx = px, px = pr->q.x; /* swap x values */ if ((code = gs_moveto(pgs, px, py)) < 0 || (code = gs_lineto(pgs, qx, py)) < 0 || (code = gs_lineto(pgs, qx, qy)) < 0 || (code = gs_lineto(pgs, px, qy)) < 0 || (code = gs_closepath(pgs)) < 0 ) return code; } return 0; } /* Clip to a list of rectangles. */ int gs_rectclip(gs_state * pgs, const gs_rect * pr, uint count) { int code; gx_path save; gx_path_init_local(&save, pgs->memory); gx_path_assign_preserve(&save, pgs->path); gs_newpath(pgs); if ((code = gs_rectappend(pgs, pr, count)) < 0 || (code = gs_clip(pgs)) < 0 ) { gx_path_assign_free(pgs->path, &save); return code; } gx_path_free(&save, "gs_rectclip"); gs_newpath(pgs); return 0; } /* Fill a list of rectangles. */ /* We take the trouble to do this efficiently in the simple cases. */ int gs_rectfill(gs_state * pgs, const gs_rect * pr, uint count) { const gs_rect *rlist = pr; gx_clip_path *pcpath; uint rcount = count; int code; gx_set_dev_color(pgs); if ((is_fzero2(pgs->ctm.xy, pgs->ctm.yx) || is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) && gx_effective_clip_path(pgs, &pcpath) >= 0 && clip_list_is_rectangle(gx_cpath_list(pcpath)) && gs_state_color_load(pgs) >= 0 && (*dev_proc(pgs->device, get_alpha_bits)) (pgs->device, go_graphics) <= 1 ) { uint i; gs_fixed_rect clip_rect; gx_cpath_inner_box(pcpath, &clip_rect); for (i = 0; i < count; ++i) { gs_fixed_point p, q; gs_fixed_rect draw_rect; int x, y, w, h; if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 || gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0 ) { /* Switch to the slow algorithm. */ goto slow; } draw_rect.p.x = min(p.x, q.x) - pgs->fill_adjust.x; draw_rect.p.y = min(p.y, q.y) - pgs->fill_adjust.y; draw_rect.q.x = max(p.x, q.x) + pgs->fill_adjust.x; draw_rect.q.y = max(p.y, q.y) + pgs->fill_adjust.y; rect_intersect(draw_rect, clip_rect); x = fixed2int_pixround(draw_rect.p.x); y = fixed2int_pixround(draw_rect.p.y); w = fixed2int_pixround(draw_rect.q.x) - x; h = fixed2int_pixround(draw_rect.q.y) - y; if (w > 0 && h > 0) { if (gx_fill_rectangle(x, y, w, h, pgs->dev_color, pgs) < 0) goto slow; } } return 0; slow:rlist = pr + i; rcount = count - i; } { bool do_save = !gx_path_is_null(pgs->path); if (do_save) { if ((code = gs_gsave(pgs)) < 0) return code; gs_newpath(pgs); } if ((code = gs_rectappend(pgs, rlist, rcount)) < 0 || (code = gs_fill(pgs)) < 0 ) DO_NOTHING; if (do_save) gs_grestore(pgs); else if (code < 0) gs_newpath(pgs); } return code; } /* Stroke a list of rectangles. */ /* (We could do this a lot more efficiently.) */ int gs_rectstroke(gs_state * pgs, const gs_rect * pr, uint count, const gs_matrix * pmat) { bool do_save = pmat != NULL || !gx_path_is_null(pgs->path); int code; if (do_save) { if ((code = gs_gsave(pgs)) < 0) return code; gs_newpath(pgs); } if ((code = gs_rectappend(pgs, pr, count)) < 0 || (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) || (code = gs_stroke(pgs)) < 0 ) DO_NOTHING; if (do_save) gs_grestore(pgs); else if (code < 0) gs_newpath(pgs); return code; }