xref: /plan9/sys/src/cmd/gs/src/gsdps1.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1991, 1992, 1994, 1996, 1997, 1998 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: gsdps1.c,v 1.10 2003/09/15 10:04:01 igor Exp $ */
18 /* Display PostScript graphics additions for Ghostscript library */
19 #include "math_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsmatrix.h"		/* for gscoord.h */
23 #include "gscoord.h"
24 #include "gspaint.h"
25 #include "gxdevice.h"
26 #include "gxfixed.h"
27 #include "gxmatrix.h"
28 #include "gxhldevc.h"
29 #include "gspath.h"
30 #include "gspath2.h"		/* defines interface */
31 #include "gzpath.h"
32 #include "gzcpath.h"
33 #include "gzstate.h"
34 
35 /*
36  * Define how much rounding slop setbbox should leave,
37  * in device coordinates.  Because of rounding in transforming
38  * path coordinates to fixed point, the minimum realistic value is:
39  *
40  *      #define box_rounding_slop_fixed (fixed_epsilon)
41  *
42  * But even this isn't enough to compensate for cumulative rounding error
43  * in rmoveto or rcurveto.  Instead, we somewhat arbitrarily use:
44  */
45 #define box_rounding_slop_fixed (fixed_epsilon * 3)
46 
47 /* ------ Graphics state ------ */
48 
49 /* Set the bounding box for the current path. */
50 int
gs_setbbox(gs_state * pgs,floatp llx,floatp lly,floatp urx,floatp ury)51 gs_setbbox(gs_state * pgs, floatp llx, floatp lly, floatp urx, floatp ury)
52 {
53     gs_rect ubox, dbox;
54     gs_fixed_rect obox, bbox;
55     gx_path *ppath = pgs->path;
56     int code;
57 
58     if (llx > urx || lly > ury)
59 	return_error(gs_error_rangecheck);
60     /* Transform box to device coordinates. */
61     ubox.p.x = llx;
62     ubox.p.y = lly;
63     ubox.q.x = urx;
64     ubox.q.y = ury;
65     if ((code = gs_bbox_transform(&ubox, &ctm_only(pgs), &dbox)) < 0)
66 	return code;
67     /* Round the corners in opposite directions. */
68     /* Because we can't predict the magnitude of the dbox values, */
69     /* we add/subtract the slop after fixing. */
70     if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
71 	dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) ||
72 	dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ||
73 	dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
74 	)
75 	return_error(gs_error_limitcheck);
76     bbox.p.x =
77 	(fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed;
78     bbox.p.y =
79 	(fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed;
80     bbox.q.x =
81 	(fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed;
82     bbox.q.y =
83 	(fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed;
84     if (gx_path_bbox(ppath, &obox) >= 0) {	/* Take the union of the bboxes. */
85 	ppath->bbox.p.x = min(obox.p.x, bbox.p.x);
86 	ppath->bbox.p.y = min(obox.p.y, bbox.p.y);
87 	ppath->bbox.q.x = max(obox.q.x, bbox.q.x);
88 	ppath->bbox.q.y = max(obox.q.y, bbox.q.y);
89     } else {			/* empty path *//* Just set the bbox. */
90 	ppath->bbox = bbox;
91     }
92     ppath->bbox_set = 1;
93     return 0;
94 }
95 
96 /* ------ Rectangles ------ */
97 
98 /* Append a list of rectangles to a path. */
99 int
gs_rectappend(gs_state * pgs,const gs_rect * pr,uint count)100 gs_rectappend(gs_state * pgs, const gs_rect * pr, uint count)
101 {
102     for (; count != 0; count--, pr++) {
103 	floatp px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y;
104 	int code;
105 
106 	/* Ensure counter-clockwise drawing. */
107 	if ((qx >= px) != (qy >= py))
108 	    qx = px, px = pr->q.x;	/* swap x values */
109 	if ((code = gs_moveto(pgs, px, py)) < 0 ||
110 	    (code = gs_lineto(pgs, qx, py)) < 0 ||
111 	    (code = gs_lineto(pgs, qx, qy)) < 0 ||
112 	    (code = gs_lineto(pgs, px, qy)) < 0 ||
113 	    (code = gs_closepath(pgs)) < 0
114 	    )
115 	    return code;
116     }
117     return 0;
118 }
119 
120 /* Clip to a list of rectangles. */
121 int
gs_rectclip(gs_state * pgs,const gs_rect * pr,uint count)122 gs_rectclip(gs_state * pgs, const gs_rect * pr, uint count)
123 {
124     int code;
125     gx_path save;
126 
127     gx_path_init_local(&save, pgs->memory);
128     gx_path_assign_preserve(&save, pgs->path);
129     gs_newpath(pgs);
130     if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
131 	(code = gs_clip(pgs)) < 0
132 	) {
133 	gx_path_assign_free(pgs->path, &save);
134 	return code;
135     }
136     gx_path_free(&save, "gs_rectclip");
137     gs_newpath(pgs);
138     return 0;
139 }
140 
141 /* Fill a list of rectangles. */
142 /* We take the trouble to do this efficiently in the simple cases. */
143 int
gs_rectfill(gs_state * pgs,const gs_rect * pr,uint count)144 gs_rectfill(gs_state * pgs, const gs_rect * pr, uint count)
145 {
146     const gs_rect *rlist = pr;
147     gx_clip_path *pcpath;
148     uint rcount = count;
149     int code;
150     gx_device * pdev = pgs->device;
151     gx_device_color *pdc = pgs->dev_color;
152     const gs_imager_state *pis = (const gs_imager_state *)pgs;
153     bool hl_color_available = gx_hld_is_hl_color_available(pis, pdc);
154     gs_fixed_rect empty = {{0, 0}, {0, 0}};
155     bool hl_color = (hl_color_available &&
156 		dev_proc(pdev, fill_rectangle_hl_color)(pdev,
157 		    	    &empty, pis, pdc, NULL) == 0);
158 
159     gx_set_dev_color(pgs);
160     if ((is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
161 	 is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) &&
162 	gx_effective_clip_path(pgs, &pcpath) >= 0 &&
163 	clip_list_is_rectangle(gx_cpath_list(pcpath)) &&
164 	(hl_color ||
165 	 pdc->type == gx_dc_type_pure ||
166 	 pdc->type == gx_dc_type_ht_binary ||
167 	 pdc->type == gx_dc_type_ht_colored
168 	 /* DeviceN todo: add wts case */) &&
169 	gs_state_color_load(pgs) >= 0 &&
170 	(*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics)
171 	<= 1 &&
172         (!pgs->overprint || !pgs->effective_overprint_mode)
173 	) {
174 	uint i;
175 	gs_fixed_rect clip_rect;
176 
177 	gx_cpath_inner_box(pcpath, &clip_rect);
178 	for (i = 0; i < count; ++i) {
179 	    gs_fixed_point p, q;
180 	    gs_fixed_rect draw_rect;
181 
182 	    if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 ||
183 		gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0
184 		) {		/* Switch to the slow algorithm. */
185 		goto slow;
186 	    }
187 	    draw_rect.p.x = min(p.x, q.x);
188 	    draw_rect.p.y = min(p.y, q.y);
189 	    draw_rect.q.x = max(p.x, q.x);
190 	    draw_rect.q.y = max(p.y, q.y);
191 	    if (hl_color) {
192 		rect_intersect(draw_rect, clip_rect);
193 		if (draw_rect.p.x < draw_rect.q.x &&
194 		    draw_rect.p.y < draw_rect.q.y) {
195 		    code = dev_proc(pdev, fill_rectangle_hl_color)(pdev,
196 			     &draw_rect, pis, pdc, pcpath);
197 		    if (code < 0)
198 			return code;
199 		}
200 	    } else {
201 		int x, y, w, h;
202 
203 		draw_rect.p.x -= pgs->fill_adjust.x;
204 		draw_rect.p.y -= pgs->fill_adjust.x;
205 		draw_rect.q.x += pgs->fill_adjust.x;
206 		draw_rect.q.y += pgs->fill_adjust.x;
207 		rect_intersect(draw_rect, clip_rect);
208 		x = fixed2int_pixround(draw_rect.p.x);
209 		y = fixed2int_pixround(draw_rect.p.y);
210 		w = fixed2int_pixround(draw_rect.q.x) - x;
211 		h = fixed2int_pixround(draw_rect.q.y) - y;
212 		if (w > 0 && h > 0)
213     		    if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0)
214 			goto slow;
215 	    }
216 	}
217 	return 0;
218       slow:rlist = pr + i;
219 	rcount = count - i;
220     } {
221 	bool do_save = !gx_path_is_null(pgs->path);
222 
223 	if (do_save) {
224 	    if ((code = gs_gsave(pgs)) < 0)
225 		return code;
226 	    gs_newpath(pgs);
227 	}
228 	if ((code = gs_rectappend(pgs, rlist, rcount)) < 0 ||
229 	    (code = gs_fill(pgs)) < 0
230 	    )
231 	    DO_NOTHING;
232 	if (do_save)
233 	    gs_grestore(pgs);
234 	else if (code < 0)
235 	    gs_newpath(pgs);
236     }
237     return code;
238 }
239 
240 /* Stroke a list of rectangles. */
241 /* (We could do this a lot more efficiently.) */
242 int
gs_rectstroke(gs_state * pgs,const gs_rect * pr,uint count,const gs_matrix * pmat)243 gs_rectstroke(gs_state * pgs, const gs_rect * pr, uint count,
244 	      const gs_matrix * pmat)
245 {
246     bool do_save = pmat != NULL || !gx_path_is_null(pgs->path);
247     int code;
248 
249     if (do_save) {
250 	if ((code = gs_gsave(pgs)) < 0)
251 	    return code;
252 	gs_newpath(pgs);
253     }
254     if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
255 	(pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
256 	(code = gs_stroke(pgs)) < 0
257 	)
258 	DO_NOTHING;
259     if (do_save)
260 	gs_grestore(pgs);
261     else if (code < 0)
262 	gs_newpath(pgs);
263     return code;
264 }
265