1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
7
This software is distributed under license and may not be copied, modified
8
or distributed except as expressly authorized under the terms of that
9
license. Refer to licensing information at http://www.artifex.com/
10
or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11
San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
14
/* $Id: gsdps1.c 8613 2008-03-28 16:30:25Z leonardo $ */
15
/* Display PostScript graphics additions for Ghostscript library */
19
#include "gsmatrix.h" /* for gscoord.h */
27
#include "gspath2.h" /* defines interface */
34
* Define how much rounding slop setbbox should leave,
35
* in device coordinates. Because of rounding in transforming
36
* path coordinates to fixed point, the minimum realistic value is:
38
* #define box_rounding_slop_fixed (fixed_epsilon)
40
* But even this isn't enough to compensate for cumulative rounding error
41
* in rmoveto or rcurveto. Instead, we somewhat arbitrarily use:
43
#define box_rounding_slop_fixed (fixed_epsilon * 3)
45
/* ------ Graphics state ------ */
47
/* Set the bounding box for the current path. */
49
gs_setbbox(gs_state * pgs, floatp llx, floatp lly, floatp urx, floatp ury)
52
gs_fixed_rect obox, bbox;
53
gx_path *ppath = pgs->path;
56
if (llx > urx || lly > ury)
57
return_error(gs_error_rangecheck);
58
/* Transform box to device coordinates. */
63
if ((code = gs_bbox_transform(&ubox, &ctm_only(pgs), &dbox)) < 0)
65
/* Round the corners in opposite directions. */
66
/* Because we can't predict the magnitude of the dbox values, */
67
/* we add/subtract the slop after fixing. */
68
if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
69
dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) ||
70
dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ||
71
dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
73
return_error(gs_error_limitcheck);
75
(fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed;
77
(fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed;
79
(fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed;
81
(fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed;
82
if (gx_path_bbox_set(ppath, &obox) >= 0) { /* Take the union of the bboxes. */
83
ppath->bbox.p.x = min(obox.p.x, bbox.p.x);
84
ppath->bbox.p.y = min(obox.p.y, bbox.p.y);
85
ppath->bbox.q.x = max(obox.q.x, bbox.q.x);
86
ppath->bbox.q.y = max(obox.q.y, bbox.q.y);
87
} else { /* empty path *//* Just set the bbox. */
94
/* ------ Rectangles ------ */
96
/* Append a list of rectangles to a path. */
98
gs_rectappend_compat(gs_state * pgs, const gs_rect * pr, uint count, bool clip)
100
extern bool CPSI_mode;
102
for (; count != 0; count--, pr++) {
103
floatp px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y;
107
/* We believe that the result must be independent
108
on the device initial matrix.
109
Particularly for the correct dashing
110
the starting point and the contour direction
111
must be same with any device initial matrix.
112
Only way to provide it is to choose the starting point
113
and the direction in the user space. */
115
/* CPSI starts a clippath with the upper right corner. */
116
/* Debugged with CET 11-11.PS page 6 item much13.*/
117
if ((code = gs_moveto(pgs, qx, qy)) < 0 ||
118
(code = gs_lineto(pgs, qx, py)) < 0 ||
119
(code = gs_lineto(pgs, px, py)) < 0 ||
120
(code = gs_lineto(pgs, px, qy)) < 0 ||
121
(code = gs_closepath(pgs)) < 0
125
/* Debugged with CET 12-12.PS page 10 item more20.*/
127
px = qx; qx = pr->p.x;
130
py = qy; qy = pr->p.y;
132
if ((code = gs_moveto(pgs, px, py)) < 0 ||
133
(code = gs_lineto(pgs, qx, py)) < 0 ||
134
(code = gs_lineto(pgs, qx, qy)) < 0 ||
135
(code = gs_lineto(pgs, px, qy)) < 0 ||
136
(code = gs_closepath(pgs)) < 0
141
/* Ensure counter-clockwise drawing. */
142
if ((qx >= px) != (qy >= py))
143
qx = px, px = pr->q.x; /* swap x values */
144
if ((code = gs_moveto(pgs, px, py)) < 0 ||
145
(code = gs_lineto(pgs, qx, py)) < 0 ||
146
(code = gs_lineto(pgs, qx, qy)) < 0 ||
147
(code = gs_lineto(pgs, px, qy)) < 0 ||
148
(code = gs_closepath(pgs)) < 0
156
gs_rectappend(gs_state * pgs, const gs_rect * pr, uint count)
158
return gs_rectappend_compat(pgs, pr, count, false);
161
/* Clip to a list of rectangles. */
163
gs_rectclip(gs_state * pgs, const gs_rect * pr, uint count)
168
gx_path_init_local(&save, pgs->memory);
169
gx_path_assign_preserve(&save, pgs->path);
171
if ((code = gs_rectappend_compat(pgs, pr, count, true)) < 0 ||
172
(code = gs_clip(pgs)) < 0
174
gx_path_assign_free(pgs->path, &save);
177
gx_path_free(&save, "gs_rectclip");
182
/* Fill a list of rectangles. */
183
/* We take the trouble to do this efficiently in the simple cases. */
185
gs_rectfill(gs_state * pgs, const gs_rect * pr, uint count)
187
const gs_rect *rlist = pr;
188
gx_clip_path *pcpath;
191
gx_device * pdev = pgs->device;
192
gx_device_color *pdc = pgs->dev_color;
193
const gs_imager_state *pis = (const gs_imager_state *)pgs;
194
bool hl_color_available = gx_hld_is_hl_color_available(pis, pdc);
195
gs_fixed_rect empty = {{0, 0}, {0, 0}};
196
bool hl_color = (hl_color_available &&
197
dev_proc(pdev, fill_rectangle_hl_color)(pdev,
198
&empty, pis, pdc, NULL) == 0);
200
/* Processing a fill object operation */
201
gs_set_object_tag(pgs, GS_PATH_TAG);
203
gx_set_dev_color(pgs);
204
if ((is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
205
is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) &&
206
gx_effective_clip_path(pgs, &pcpath) >= 0 &&
207
clip_list_is_rectangle(gx_cpath_list(pcpath)) &&
209
pdc->type == gx_dc_type_pure ||
210
pdc->type == gx_dc_type_ht_binary ||
211
pdc->type == gx_dc_type_ht_colored
212
/* DeviceN todo: add wts case */) &&
213
gs_state_color_load(pgs) >= 0 &&
214
(*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics)
216
(!pgs->overprint || !pgs->effective_overprint_mode)
219
gs_fixed_rect clip_rect;
221
gx_cpath_inner_box(pcpath, &clip_rect);
222
for (i = 0; i < count; ++i) {
224
gs_fixed_rect draw_rect;
226
if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 ||
227
gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0
228
) { /* Switch to the slow algorithm. */
231
draw_rect.p.x = min(p.x, q.x);
232
draw_rect.p.y = min(p.y, q.y);
233
draw_rect.q.x = max(p.x, q.x);
234
draw_rect.q.y = max(p.y, q.y);
236
rect_intersect(draw_rect, clip_rect);
237
if (draw_rect.p.x < draw_rect.q.x &&
238
draw_rect.p.y < draw_rect.q.y) {
239
code = dev_proc(pdev, fill_rectangle_hl_color)(pdev,
240
&draw_rect, pis, pdc, pcpath);
247
draw_rect.p.x -= max(pgs->fill_adjust.x - fixed_epsilon, 0);
248
draw_rect.p.y -= max(pgs->fill_adjust.y - fixed_epsilon, 0);
249
draw_rect.q.x += pgs->fill_adjust.x;
250
draw_rect.q.y += pgs->fill_adjust.y;
251
rect_intersect(draw_rect, clip_rect);
252
x = fixed2int_pixround(draw_rect.p.x);
253
y = fixed2int_pixround(draw_rect.p.y);
254
w = fixed2int_pixround(draw_rect.q.x) - x;
255
h = fixed2int_pixround(draw_rect.q.y) - y;
257
if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0)
265
bool do_save = !gx_path_is_null(pgs->path);
268
if ((code = gs_gsave(pgs)) < 0)
272
if ((code = gs_rectappend(pgs, rlist, rcount)) < 0 ||
273
(code = gs_fill(pgs)) < 0
284
/* Stroke a list of rectangles. */
285
/* (We could do this a lot more efficiently.) */
287
gs_rectstroke(gs_state * pgs, const gs_rect * pr, uint count,
288
const gs_matrix * pmat)
290
bool do_save = pmat != NULL || !gx_path_is_null(pgs->path);
294
if ((code = gs_gsave(pgs)) < 0)
298
if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
299
(pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
300
(code = gs_stroke(pgs)) < 0