/src/ghostpdl/base/gsdps1.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Display PostScript graphics additions for Ghostscript library */ |
18 | | #include "math_.h" |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "gsmatrix.h" /* for gscoord.h */ |
22 | | #include "gscoord.h" |
23 | | #include "gspaint.h" |
24 | | #include "gxdevice.h" |
25 | | #include "gxfixed.h" |
26 | | #include "gxmatrix.h" |
27 | | #include "gxhldevc.h" |
28 | | #include "gspath.h" |
29 | | #include "gspath2.h" /* defines interface */ |
30 | | #include "gzpath.h" |
31 | | #include "gzcpath.h" |
32 | | #include "gzstate.h" |
33 | | #include "gsutil.h" |
34 | | #include "gxdevsop.h" |
35 | | |
36 | | /* |
37 | | * Define how much rounding slop setbbox should leave, |
38 | | * in device coordinates. Because of rounding in transforming |
39 | | * path coordinates to fixed point, the minimum realistic value is: |
40 | | * |
41 | | * #define box_rounding_slop_fixed (fixed_epsilon) |
42 | | * |
43 | | * But even this isn't enough to compensate for cumulative rounding error |
44 | | * in rmoveto or rcurveto. Instead, we somewhat arbitrarily use: |
45 | | */ |
46 | 32 | #define box_rounding_slop_fixed (fixed_epsilon * 3) |
47 | | |
48 | | /* ------ Graphics state ------ */ |
49 | | |
50 | | /* Set the bounding box for the current path. */ |
51 | | int |
52 | | gs_setbbox(gs_gstate * pgs, double llx, double lly, double urx, double ury) |
53 | 19 | { |
54 | 19 | gs_rect ubox, dbox; |
55 | 19 | gs_fixed_rect obox, bbox; |
56 | 19 | gx_path *ppath = pgs->path; |
57 | 19 | int code; |
58 | | |
59 | 19 | if (llx > urx || lly > ury) |
60 | 6 | return_error(gs_error_rangecheck); |
61 | | /* Transform box to device coordinates. */ |
62 | 13 | ubox.p.x = llx; |
63 | 13 | ubox.p.y = lly; |
64 | 13 | ubox.q.x = urx; |
65 | 13 | ubox.q.y = ury; |
66 | 13 | if ((code = gs_bbox_transform(&ubox, &ctm_only(pgs), &dbox)) < 0) |
67 | 0 | return code; |
68 | | /* Round the corners in opposite directions. */ |
69 | | /* Because we can't predict the magnitude of the dbox values, */ |
70 | | /* we add/subtract the slop after fixing. */ |
71 | 13 | if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) || |
72 | 13 | dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) || |
73 | 13 | dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) || |
74 | 13 | dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) |
75 | 13 | ) |
76 | 5 | return_error(gs_error_limitcheck); |
77 | 8 | bbox.p.x = |
78 | 8 | (fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed; |
79 | 8 | bbox.p.y = |
80 | 8 | (fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed; |
81 | 8 | bbox.q.x = |
82 | 8 | (fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed; |
83 | 8 | bbox.q.y = |
84 | 8 | (fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed; |
85 | 8 | if (gx_path_bbox_set(ppath, &obox) >= 0) { /* Take the union of the bboxes. */ |
86 | 0 | ppath->bbox.p.x = min(obox.p.x, bbox.p.x); |
87 | 0 | ppath->bbox.p.y = min(obox.p.y, bbox.p.y); |
88 | 0 | ppath->bbox.q.x = max(obox.q.x, bbox.q.x); |
89 | 0 | ppath->bbox.q.y = max(obox.q.y, bbox.q.y); |
90 | 8 | } else { /* empty path *//* Just set the bbox. */ |
91 | 8 | ppath->bbox = bbox; |
92 | 8 | } |
93 | 8 | ppath->bbox_set = 1; |
94 | 8 | return 0; |
95 | 13 | } |
96 | | |
97 | | /* ------ Rectangles ------ */ |
98 | | |
99 | | /* Append a list of rectangles to a path. */ |
100 | | static int |
101 | | gs_rectappend_compat(gs_gstate * pgs, const gs_rect * pr, uint count, bool clip) |
102 | 324k | { |
103 | 324k | bool CPSI_mode = gs_currentcpsimode(pgs->memory); |
104 | | |
105 | 649k | for (; count != 0; count--, pr++) { |
106 | 324k | double px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y; |
107 | 324k | int code; |
108 | | |
109 | 324k | if (CPSI_mode) { |
110 | | /* We believe that the result must be independent |
111 | | on the device initial matrix. |
112 | | Particularly for the correct dashing |
113 | | the starting point and the contour direction |
114 | | must be same with any device initial matrix. |
115 | | Only way to provide it is to choose the starting point |
116 | | and the direction in the user space. */ |
117 | 0 | if (clip) { |
118 | | /* CPSI starts a clippath with the upper right corner. */ |
119 | | /* Debugged with CET 11-11.PS page 6 item much13.*/ |
120 | 0 | if ((code = gs_moveto(pgs, qx, qy)) < 0 || |
121 | 0 | (code = gs_lineto(pgs, qx, py)) < 0 || |
122 | 0 | (code = gs_lineto(pgs, px, py)) < 0 || |
123 | 0 | (code = gs_lineto(pgs, px, qy)) < 0 || |
124 | 0 | (code = gs_closepath(pgs)) < 0 |
125 | 0 | ) |
126 | 0 | return code; |
127 | 0 | } else { |
128 | | /* Debugged with CET 12-12.PS page 10 item more20.*/ |
129 | 0 | if (px > qx) { |
130 | 0 | px = qx; qx = pr->p.x; |
131 | 0 | } |
132 | 0 | if (py > qy) { |
133 | 0 | py = qy; qy = pr->p.y; |
134 | 0 | } |
135 | 0 | if ((code = gs_moveto(pgs, px, py)) < 0 || |
136 | 0 | (code = gs_lineto(pgs, qx, py)) < 0 || |
137 | 0 | (code = gs_lineto(pgs, qx, qy)) < 0 || |
138 | 0 | (code = gs_lineto(pgs, px, qy)) < 0 || |
139 | 0 | (code = gs_closepath(pgs)) < 0 |
140 | 0 | ) |
141 | 0 | return code; |
142 | 0 | } |
143 | 324k | } else { |
144 | | /* Ensure counter-clockwise drawing. */ |
145 | 324k | if ((qx >= px) != (qy >= py)) |
146 | 9.06k | qx = px, px = pr->q.x; /* swap x values */ |
147 | 324k | if ((code = gs_moveto(pgs, px, py)) < 0 || |
148 | 324k | (code = gs_lineto(pgs, qx, py)) < 0 || |
149 | 324k | (code = gs_lineto(pgs, qx, qy)) < 0 || |
150 | 324k | (code = gs_lineto(pgs, px, qy)) < 0 || |
151 | 324k | (code = gs_closepath(pgs)) < 0 |
152 | 324k | ) |
153 | 0 | return code; |
154 | 324k | } |
155 | 324k | } |
156 | 324k | return 0; |
157 | 324k | } |
158 | | int |
159 | | gs_rectappend(gs_gstate * pgs, const gs_rect * pr, uint count) |
160 | 10.9k | { |
161 | 10.9k | return gs_rectappend_compat(pgs, pr, count, false); |
162 | 10.9k | } |
163 | | |
164 | | /* Clip to a list of rectangles. */ |
165 | | int |
166 | | gs_rectclip(gs_gstate * pgs, const gs_rect * pr, uint count) |
167 | 313k | { |
168 | 313k | int code; |
169 | 313k | gx_path save; |
170 | | |
171 | 313k | gx_path_init_local(&save, pgs->memory); |
172 | 313k | gx_path_assign_preserve(&save, pgs->path); |
173 | 313k | gs_newpath(pgs); |
174 | 313k | if ((code = gs_rectappend_compat(pgs, pr, count, true)) < 0 || |
175 | 313k | (code = gs_clip(pgs)) < 0 |
176 | 313k | ) { |
177 | 0 | gx_path_assign_free(pgs->path, &save); |
178 | 0 | return code; |
179 | 0 | } |
180 | 313k | gx_path_free(&save, "gs_rectclip"); |
181 | 313k | gs_newpath(pgs); |
182 | 313k | return 0; |
183 | 313k | } |
184 | | |
185 | | /* Setup for black vector handling */ |
186 | | static inline bool black_vectors(gs_gstate *pgs, gx_device *dev) |
187 | 41.9k | { |
188 | 41.9k | if (dev->icc_struct != NULL && dev->icc_struct->blackvector && |
189 | 41.9k | pgs->black_textvec_state == NULL) { |
190 | 0 | return gsicc_setup_blacktextvec(pgs, dev, false); |
191 | 0 | } |
192 | 41.9k | return false; |
193 | 41.9k | } |
194 | | |
195 | | /* Fill a list of rectangles. */ |
196 | | /* We take the trouble to do this efficiently in the simple cases. */ |
197 | | int |
198 | | gs_rectfill(gs_gstate * pgs, const gs_rect * pr, uint count) |
199 | 41.9k | { |
200 | 41.9k | const gs_rect *rlist = pr; |
201 | 41.9k | gx_clip_path *pcpath; |
202 | 41.9k | uint rcount = count; |
203 | 41.9k | int code; |
204 | 41.9k | gx_device * pdev = pgs->device; |
205 | 41.9k | gx_device_color *pdc = gs_currentdevicecolor_inline(pgs); |
206 | 41.9k | const gs_gstate *pgs2 = (const gs_gstate *)pgs; |
207 | 41.9k | bool hl_color_available = gx_hld_is_hl_color_available(pgs2, pdc); |
208 | 41.9k | bool hl_color = (hl_color_available && |
209 | 41.9k | dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_hlcolor, |
210 | 41.9k | NULL, 0)); |
211 | 41.9k | bool center_of_pixel = (pgs->fill_adjust.x == 0 && pgs->fill_adjust.y == 0); |
212 | 41.9k | bool black_vector = false; |
213 | | |
214 | | /* Processing a fill object operation */ |
215 | 41.9k | ensure_tag_is_set(pgs, pgs->device, GS_VECTOR_TAG); /* NB: may unset_dev_color */ |
216 | | |
217 | 41.9k | black_vector = black_vectors(pgs, pgs->device); /* Set vector fill to black */ |
218 | | |
219 | 41.9k | code = gx_set_dev_color(pgs); |
220 | 41.9k | if (code != 0) |
221 | 4 | goto exit; |
222 | | |
223 | 41.9k | if ( !(pgs->device->page_uses_transparency || |
224 | 41.9k | dev_proc(pgs->device, dev_spec_op)(pgs->device, |
225 | 41.9k | gxdso_is_pdf14_device, &(pgs->device), |
226 | 41.9k | sizeof(pgs->device))) && |
227 | 41.9k | (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) || |
228 | 41.5k | is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) && |
229 | 41.9k | gx_effective_clip_path(pgs, &pcpath) >= 0 && |
230 | 41.9k | clip_list_is_rectangle(gx_cpath_list(pcpath)) && |
231 | 41.9k | (hl_color || |
232 | 41.4k | pdc->type == gx_dc_type_pure || |
233 | 41.4k | pdc->type == gx_dc_type_ht_binary || |
234 | 41.4k | pdc->type == gx_dc_type_ht_colored) && |
235 | 41.9k | gs_gstate_color_load(pgs) >= 0 && |
236 | 41.9k | (*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics) |
237 | 40.9k | <= 1 && |
238 | 41.9k | (!pgs->overprint || !gs_currentcolor_eopm(pgs)) |
239 | 41.9k | ) { |
240 | 40.9k | uint i; |
241 | 40.9k | gs_fixed_rect clip_rect; |
242 | | |
243 | 40.9k | gx_cpath_inner_box(pcpath, &clip_rect); |
244 | | /* We should never plot anything for an empty clip rectangle */ |
245 | 40.9k | if ((clip_rect.p.x >= clip_rect.q.x) && |
246 | 40.9k | (clip_rect.p.y >= clip_rect.q.y)) |
247 | 24 | goto exit; |
248 | 74.0k | for (i = 0; i < count; ++i) { |
249 | 40.9k | gs_fixed_point p, q; |
250 | 40.9k | gs_fixed_rect draw_rect; |
251 | | |
252 | 40.9k | if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 || |
253 | 40.9k | gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0 |
254 | 40.9k | ) { /* Switch to the slow algorithm. */ |
255 | 7.29k | goto slow; |
256 | 7.29k | } |
257 | 33.6k | draw_rect.p.x = min(p.x, q.x); |
258 | 33.6k | draw_rect.p.y = min(p.y, q.y); |
259 | 33.6k | draw_rect.q.x = max(p.x, q.x); |
260 | 33.6k | draw_rect.q.y = max(p.y, q.y); |
261 | 33.6k | if (hl_color) { |
262 | 7.61k | rect_intersect(draw_rect, clip_rect); |
263 | | /* We do pass on 0 extant rectangles to high level |
264 | | devices. It isn't clear how a client and an output |
265 | | device should interact if one uses a center of |
266 | | pixel algorithm and the other uses any part of |
267 | | pixel. For now we punt and just pass the high |
268 | | level rectangle on without adjustment. */ |
269 | 7.61k | if (draw_rect.p.x <= draw_rect.q.x && |
270 | 7.61k | draw_rect.p.y <= draw_rect.q.y) { |
271 | 6.93k | code = dev_proc(pdev, fill_rectangle_hl_color)(pdev, |
272 | 6.93k | &draw_rect, pgs2, pdc, pcpath); |
273 | 6.93k | if (code < 0) |
274 | 0 | goto exit; |
275 | 6.93k | } |
276 | 26.0k | } else { |
277 | 26.0k | int x, y, w, h; |
278 | | |
279 | 26.0k | rect_intersect(draw_rect, clip_rect); |
280 | 26.0k | if (center_of_pixel) { |
281 | 0 | draw_rect.p.x = fixed_rounded(draw_rect.p.x); |
282 | 0 | draw_rect.p.y = fixed_rounded(draw_rect.p.y); |
283 | 0 | draw_rect.q.x = fixed_rounded(draw_rect.q.x); |
284 | 0 | draw_rect.q.y = fixed_rounded(draw_rect.q.y); |
285 | 26.0k | } else { /* any part of pixel rule - touched */ |
286 | 26.0k | draw_rect.p.x = fixed_floor(draw_rect.p.x); |
287 | 26.0k | draw_rect.p.y = fixed_floor(draw_rect.p.y); |
288 | 26.0k | draw_rect.q.x = fixed_ceiling(draw_rect.q.x); |
289 | 26.0k | draw_rect.q.y = fixed_ceiling(draw_rect.q.y); |
290 | 26.0k | } |
291 | 26.0k | x = fixed2int(draw_rect.p.x); |
292 | 26.0k | y = fixed2int(draw_rect.p.y); |
293 | 26.0k | w = fixed2int(draw_rect.q.x) - x; |
294 | 26.0k | h = fixed2int(draw_rect.q.y) - y; |
295 | | /* clients that use the "any part of pixel" rule also |
296 | | fill 0 areas. This is true of current graphics |
297 | | library clients but not a general rule. */ |
298 | 26.0k | if (!center_of_pixel) { |
299 | 26.0k | if (w == 0) |
300 | 5.30k | w = 1; |
301 | | /* yes Adobe Acrobat 8, seems to back up the y |
302 | | coordinate when the width is 0, sigh. */ |
303 | 26.0k | if (h == 0) { |
304 | 2.35k | y--; |
305 | 2.35k | h = 1; |
306 | 2.35k | } |
307 | 26.0k | } |
308 | 26.0k | if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0) |
309 | 593 | goto slow; |
310 | 26.0k | } |
311 | 33.6k | } |
312 | 33.0k | code = 0; |
313 | 33.0k | goto exit; |
314 | 7.89k | slow:rlist = pr + i; |
315 | 7.89k | rcount = count - i; |
316 | 8.88k | } { |
317 | 8.88k | bool do_save = !gx_path_is_null(pgs->path); |
318 | | |
319 | 8.88k | if (do_save) { |
320 | 16 | if ((code = gs_gsave(pgs)) < 0) |
321 | 0 | goto exit; |
322 | 16 | code = gs_newpath(pgs); |
323 | 16 | } |
324 | 8.88k | if ((code >= 0) && |
325 | 8.88k | (((code = gs_rectappend(pgs, rlist, rcount)) < 0) || |
326 | 8.88k | ((code = gs_fill(pgs)) < 0)) |
327 | 8.88k | ) |
328 | 8.88k | DO_NOTHING; |
329 | 8.88k | if (do_save) |
330 | 16 | gs_grestore(pgs); |
331 | 8.86k | else if (code < 0) |
332 | 0 | gs_newpath(pgs); |
333 | 8.88k | } |
334 | | |
335 | 41.9k | exit: |
336 | 41.9k | if (black_vector) { |
337 | | /* Restore color */ |
338 | 0 | gsicc_restore_blacktextvec(pgs, false); |
339 | 0 | } |
340 | 41.9k | return code; |
341 | 8.88k | } |
342 | | |
343 | | /* Stroke a list of rectangles. */ |
344 | | /* (We could do this a lot more efficiently.) */ |
345 | | int |
346 | | gs_rectstroke(gs_gstate * pgs, const gs_rect * pr, uint count, |
347 | | const gs_matrix * pmat) |
348 | 2.05k | { |
349 | 2.05k | bool do_save = pmat != NULL || !gx_path_is_null(pgs->path); |
350 | 2.05k | int code; |
351 | | |
352 | 2.05k | if (do_save) { |
353 | 22 | if ((code = gs_gsave(pgs)) < 0) |
354 | 0 | return code; |
355 | 22 | gs_newpath(pgs); |
356 | 22 | } |
357 | 2.05k | if ((code = gs_rectappend(pgs, pr, count)) < 0 || |
358 | 2.05k | (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) || |
359 | 2.05k | (code = gs_stroke(pgs)) < 0 |
360 | 2.05k | ) |
361 | 2.05k | DO_NOTHING; |
362 | 2.05k | if (do_save) |
363 | 22 | gs_grestore(pgs); |
364 | 2.03k | else if (code < 0) |
365 | 0 | gs_newpath(pgs); |
366 | 2.05k | return code; |
367 | 2.05k | } |