/src/ghostpdl/pcl/pxl/pxink.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 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 | | /* pxink.c */ |
18 | | /* PCL XL ink setting operators */ |
19 | | |
20 | | #include "math_.h" |
21 | | #include "stdio_.h" /* for NULL */ |
22 | | #include "memory_.h" |
23 | | #include "gdebug.h" |
24 | | #include "gstypes.h" |
25 | | #include "gsmemory.h" |
26 | | #include "pxoper.h" |
27 | | #include "pxstate.h" |
28 | | #include "gxarith.h" |
29 | | #include "gsstate.h" |
30 | | #include "gxcspace.h" /* must precede gscolor2.h */ |
31 | | #include "gscolor2.h" |
32 | | #include "gscoord.h" |
33 | | #include "gsimage.h" |
34 | | #include "gscie.h" |
35 | | #include "gscrd.h" |
36 | | #include "gspath.h" |
37 | | #include "gxdevice.h" |
38 | | #include "gxht.h" |
39 | | #include "gxstate.h" |
40 | | #include "plht.h" |
41 | | #include "pxptable.h" |
42 | | #include "gzstate.h" |
43 | | #include "gxdevsop.h" /* Special ops, in this case pattern management */ |
44 | | #include "gxcolor2.h" /* Required for definition of gs_pattern1_instance_t for high level patterns */ |
45 | | #include "pxgstate.h" /* Prototype for px_high_level_pattern */ |
46 | | #include "gxpcolor.h" /* Prototype for gx_pattern_cache_add_dummy_entry */ |
47 | | #include "gxpath.h" /* Prototype for gx_clip_to_rectangle */ |
48 | | |
49 | | /* |
50 | | * Contrary to the documentation, SetColorSpace apparently doesn't set the |
51 | | * brush or pen to black. To produce this behavior, uncomment the |
52 | | * following #define. |
53 | | */ |
54 | | #define SET_COLOR_SPACE_NO_SET_BLACK |
55 | | |
56 | | /* ---------------- Utilities ---------------- */ |
57 | | |
58 | | /* ------ Halftones ------ */ |
59 | | |
60 | | /* Define a transfer function without gamma correction. */ |
61 | | static float |
62 | | identity_transfer(double tint, const gx_transfer_map * ignore_map) |
63 | 450k | { |
64 | 450k | return tint; |
65 | 450k | } |
66 | | |
67 | | /* Set the default halftone screen. */ |
68 | | static const byte order16x16[256] = { |
69 | | #if 0 |
70 | | /* |
71 | | * The following is a standard 16x16 ordered dither, except that |
72 | | * the very last pass goes in the order (0,1,2,3) rather than |
73 | | * (0,3,1,2). This leads to excessive cancellation when the source |
74 | | * and paint halftones interact, but it's better than the standard |
75 | | * order, which has inadequate cancellation. |
76 | | * |
77 | | * This matrix is generated by the following call: |
78 | | * [ <00 88 08 80> <00 44 04 40> <00 22 02 20> <00 01 10 11> ] |
79 | | * { } makedither |
80 | | */ |
81 | | 0, 64, 32, 96, 8, 72, 40, 104, 2, 66, 34, 98, 10, 74, 42, 106, |
82 | | 128, 192, 160, 224, 136, 200, 168, 232, 130, 194, 162, 226, 138, 202, 170, |
83 | | 234, |
84 | | 48, 112, 16, 80, 56, 120, 24, 88, 50, 114, 18, 82, 58, 122, 26, 90, |
85 | | 176, 240, 144, 208, 184, 248, 152, 216, 178, 242, 146, 210, 186, 250, 154, |
86 | | 218, |
87 | | 12, 76, 44, 108, 4, 68, 36, 100, 14, 78, 46, 110, 6, 70, 38, 102, |
88 | | 140, 204, 172, 236, 132, 196, 164, 228, 142, 206, 174, 238, 134, 198, 166, |
89 | | 230, |
90 | | 60, 124, 28, 92, 52, 116, 20, 84, 62, 126, 30, 94, 54, 118, 22, 86, |
91 | | 188, 252, 156, 220, 180, 244, 148, 212, 190, 254, 158, 222, 182, 246, 150, |
92 | | 214, |
93 | | 3, 67, 35, 99, 11, 75, 43, 107, 1, 65, 33, 97, 9, 73, 41, 105, |
94 | | 131, 195, 163, 227, 139, 203, 171, 235, 129, 193, 161, 225, 137, 201, 169, |
95 | | 233, |
96 | | 51, 115, 19, 83, 59, 123, 27, 91, 49, 113, 17, 81, 57, 121, 25, 89, |
97 | | 179, 243, 147, 211, 187, 251, 155, 219, 177, 241, 145, 209, 185, 249, 153, |
98 | | 217, |
99 | | 15, 79, 47, 111, 7, 71, 39, 103, 13, 77, 45, 109, 5, 69, 37, 101, |
100 | | 143, 207, 175, 239, 135, 199, 167, 231, 141, 205, 173, 237, 133, 197, 165, |
101 | | 229, |
102 | | 63, 127, 31, 95, 55, 119, 23, 87, 61, 125, 29, 93, 53, 117, 21, 85, |
103 | | 191, 255, 159, 223, 183, 247, 151, 215, 189, 253, 157, 221, 181, 245, 149, |
104 | | 213 |
105 | | # define source_phase_x 1 |
106 | | # define source_phase_y 1 |
107 | | #else |
108 | | /* |
109 | | * The following is a 45 degree spot screen with the spots enumerated |
110 | | * in a defined order. This matrix is generated by the following call: |
111 | | |
112 | | /gamma_transfer { |
113 | | dup dup 0 le exch 1 ge or not { |
114 | | dup 0.17 lt |
115 | | { 3 mul } |
116 | | { dup 0.35 lt { 0.78 mul 0.38 add } { 0.53 mul 0.47 add } ifelse } |
117 | | ifelse |
118 | | } if |
119 | | } def |
120 | | |
121 | | [ [0 136 8 128 68 204 76 196] |
122 | | [18 33 17 34 1 2 19 35 50 49 32 16 3 48 0 51 |
123 | | -15 -14 20 36 66 65 47 31 -13 64 15 52 -30 81 30 37] |
124 | | ] /gamma_transfer load makedither |
125 | | |
126 | | */ |
127 | | 38, 11, 14, 32, 165, 105, 90, 171, 38, 12, 14, 33, 161, 101, 88, 167, |
128 | | 30, 6, 0, 16, 61, 225, 231, 125, 30, 6, 1, 17, 63, 222, 227, 122, |
129 | | 27, 3, 8, 19, 71, 242, 205, 110, 28, 4, 9, 20, 74, 246, 208, 106, |
130 | | 35, 24, 22, 40, 182, 46, 56, 144, 36, 25, 22, 41, 186, 48, 58, 148, |
131 | | 152, 91, 81, 174, 39, 12, 15, 34, 156, 95, 84, 178, 40, 13, 16, 34, |
132 | | 69, 212, 235, 129, 31, 7, 2, 18, 66, 216, 239, 133, 32, 8, 2, 18, |
133 | | 79, 254, 203, 114, 28, 4, 10, 20, 76, 250, 199, 118, 29, 5, 10, 21, |
134 | | 193, 44, 54, 142, 36, 26, 23, 42, 189, 43, 52, 139, 37, 26, 24, 42, |
135 | | 39, 12, 15, 33, 159, 99, 87, 169, 38, 11, 14, 33, 163, 103, 89, 172, |
136 | | 31, 7, 1, 17, 65, 220, 229, 123, 30, 6, 1, 17, 62, 223, 233, 127, |
137 | | 28, 4, 9, 20, 75, 248, 210, 108, 27, 3, 9, 19, 72, 244, 206, 112, |
138 | | 36, 25, 23, 41, 188, 49, 60, 150, 35, 25, 22, 41, 184, 47, 57, 146, |
139 | | 157, 97, 85, 180, 40, 13, 16, 35, 154, 93, 83, 176, 39, 13, 15, 34, |
140 | | 67, 218, 240, 135, 32, 8, 3, 19, 70, 214, 237, 131, 31, 7, 2, 18, |
141 | | 78, 252, 197, 120, 29, 5, 11, 21, 80, 255, 201, 116, 29, 5, 10, 21, |
142 | | 191, 43, 51, 137, 37, 27, 24, 43, 195, 44, 53, 140, 37, 26, 23, 42 |
143 | | # define source_phase_x 4 |
144 | | # define source_phase_y 0 |
145 | | #endif |
146 | | }; |
147 | | |
148 | | /* Set the size for a default halftone screen. */ |
149 | | static void |
150 | | px_set_default_screen_size(px_state_t * pxs, int method) |
151 | 1.75k | { |
152 | 1.75k | px_gstate_t *pxgs = pxs->pxgs; |
153 | | |
154 | 1.75k | pxgs->halftone.width = pxgs->halftone.height = 16; |
155 | 1.75k | } |
156 | | |
157 | | /* If necessary, set the halftone in the graphics state. */ |
158 | | int |
159 | | px_set_halftone(px_state_t * pxs) |
160 | 3.81k | { |
161 | 3.81k | px_gstate_t *pxgs = pxs->pxgs; |
162 | 3.81k | int code; |
163 | | |
164 | 3.81k | if (pxgs->halftone.set) |
165 | 2.05k | return 0; |
166 | 1.75k | if (pxgs->halftone.method != eDownloaded) { |
167 | 1.75k | gs_string thresh; |
168 | | |
169 | 1.75k | thresh.data = (byte *) order16x16; |
170 | 1.75k | thresh.size = 256; |
171 | 1.75k | code = pl_set_pcl_halftone(pxs->pgs, |
172 | 1.75k | /* set transfer */ identity_transfer, |
173 | 1.75k | /* width */ 16, /*height */ 16, |
174 | 1.75k | /* dither data */ thresh, |
175 | 1.75k | /* x phase */ (int)pxgs->halftone.origin.x, |
176 | | /* y phase */ |
177 | 1.75k | (int)pxgs->halftone.origin.y); |
178 | 1.75k | } else { /* downloaded */ |
179 | 0 | int ht_width, ht_height; |
180 | |
|
181 | 0 | switch (pxs->orientation) { |
182 | 0 | case ePortraitOrientation: |
183 | 0 | case eReversePortrait: |
184 | 0 | ht_width = pxgs->halftone.width; |
185 | 0 | ht_height = pxgs->halftone.height; |
186 | 0 | break; |
187 | 0 | case eLandscapeOrientation: |
188 | 0 | case eReverseLandscape: |
189 | 0 | ht_width = pxgs->halftone.height; |
190 | 0 | ht_height = pxgs->halftone.width; |
191 | 0 | break; |
192 | 0 | default: |
193 | 0 | return -1; |
194 | 0 | } |
195 | | |
196 | 0 | code = pl_set_pcl_halftone(pxs->pgs, |
197 | 0 | /* set transfer */ identity_transfer, |
198 | 0 | /* width */ ht_width, /*height */ |
199 | 0 | ht_height, |
200 | | /* dither data */ |
201 | 0 | pxgs->halftone.thresholds, |
202 | 0 | /* x phase */ (int)pxgs->halftone.origin.x, |
203 | | /* y phase */ |
204 | 0 | (int)pxgs->halftone.origin.y); |
205 | 0 | if (code < 0) |
206 | 0 | gs_free_string(pxs->memory, pxgs->halftone.thresholds.data, |
207 | 0 | pxgs->halftone.thresholds.size, |
208 | 0 | "px_set_halftone(thresholds)"); |
209 | 0 | else { |
210 | 0 | gs_free_string(pxs->memory, (byte *) pxgs->dither_matrix.data, |
211 | 0 | pxgs->dither_matrix.size, |
212 | 0 | "px_set_halftone(dither_matrix)"); |
213 | 0 | pxgs->dither_matrix = pxgs->halftone.thresholds; |
214 | 0 | } |
215 | 0 | pxgs->halftone.thresholds.data = 0; |
216 | 0 | pxgs->halftone.thresholds.size = 0; |
217 | 0 | } |
218 | 1.75k | if (code < 0) |
219 | 0 | return code; |
220 | 1.75k | pxgs->halftone.set = true; |
221 | | /* Cached patterns have already been halftoned, so clear the cache. */ |
222 | 1.75k | px_purge_pattern_cache(pxs, eSessionPattern); |
223 | 1.75k | return 0; |
224 | 1.75k | } |
225 | | |
226 | | /* ------ Patterns ------ */ |
227 | | |
228 | | /* |
229 | | * The library caches patterns in their fully rendered form, i.e., after |
230 | | * halftoning. In order to avoid seams or anomalies, we have to replicate |
231 | | * the pattern so that its size is an exact multiple of the halftone size. |
232 | | */ |
233 | | |
234 | | static uint |
235 | | ilcm(uint x, uint y) |
236 | 336 | { |
237 | 336 | return x * (y / igcd(x, y)); |
238 | 336 | } |
239 | | |
240 | | /* Render a pattern. */ |
241 | | static int |
242 | | px_paint_pattern(const gs_client_color * pcc, gs_gstate * pgs) |
243 | 0 | { |
244 | 0 | const gs_client_pattern *ppat = gs_getpattern(pcc); |
245 | 0 | const px_pattern_t *pattern = (px_pattern_t *)gs_get_pattern_client_data(pcc); |
246 | 0 | const byte *dp = pattern->data; |
247 | 0 | gs_image_enum *penum = NULL; |
248 | 0 | gs_image_t image; |
249 | 0 | int code; |
250 | 0 | int num_components = |
251 | 0 | (pattern->params.indexed || pattern->params.color_space == eGray ? |
252 | 0 | 1 : 3); |
253 | 0 | uint rep_width = pattern->params.width; |
254 | 0 | uint rep_height = pattern->params.height; |
255 | 0 | uint full_width = (uint) ppat->XStep; |
256 | 0 | uint full_height = (uint) ppat->YStep; |
257 | 0 | uint bits_per_row, bytes_per_row; |
258 | 0 | int x; |
259 | |
|
260 | 0 | code = |
261 | 0 | px_image_color_space(&image, &pattern->params, &pattern->palette, |
262 | 0 | pgs); |
263 | 0 | if (code < 0) |
264 | 0 | return code; |
265 | | |
266 | 0 | bits_per_row = rep_width * image.BitsPerComponent * num_components; |
267 | 0 | bytes_per_row = (bits_per_row + 7) >> 3; |
268 | | /* |
269 | | * As noted above, in general, we have to replicate the original |
270 | | * pattern to a multiple that avoids halftone seams. If the |
271 | | * number of bits per row is a multiple of 8, we can do this with |
272 | | * a single image; otherwise, we need one image per X replica. |
273 | | * To simplify the code, we always use the (slightly) slower method. |
274 | | */ |
275 | 0 | image.Width = rep_width; |
276 | 0 | image.Height = full_height; |
277 | 0 | image.CombineWithColor = true; |
278 | 0 | for (x = 0; x < full_width; x += rep_width) { |
279 | 0 | int y; |
280 | |
|
281 | 0 | image.ImageMatrix.tx = (float)-x; |
282 | 0 | penum = gs_image_enum_alloc(gs_gstate_memory(pgs), "px_paint_pattern"); |
283 | 0 | if (penum == NULL) { |
284 | 0 | code = gs_note_error(gs_error_VMerror); |
285 | 0 | break; |
286 | 0 | } |
287 | 0 | code = gs_image_init(penum, &image, 0, false, pgs); |
288 | 0 | if (code < 0) |
289 | 0 | break; |
290 | 0 | for (y = 0; y < full_height; ++y) { |
291 | 0 | const byte *row = dp + (y % rep_height) * bytes_per_row; |
292 | 0 | uint used; /* better named not_used */ |
293 | |
|
294 | 0 | code = gs_image_next(penum, row, bytes_per_row, &used); |
295 | 0 | if (code < 0) |
296 | 0 | break; |
297 | 0 | } |
298 | 0 | code = gs_image_cleanup_and_free_enum(penum, pgs); |
299 | 0 | penum = NULL; |
300 | 0 | if (code < 0) |
301 | 0 | break; |
302 | 0 | } |
303 | 0 | if (code < 0) { |
304 | 0 | (void)gs_image_cleanup_and_free_enum(penum, pgs); |
305 | 0 | penum = NULL; |
306 | 0 | } |
307 | 0 | return code; |
308 | 0 | } |
309 | | |
310 | | int px_high_level_pattern(gs_gstate * pgs) |
311 | 0 | { |
312 | 0 | gs_matrix m; |
313 | 0 | gs_rect bbox; |
314 | 0 | gs_fixed_rect clip_box; |
315 | 0 | int code; |
316 | 0 | gx_device_color *pdc = gs_currentdevicecolor_inline(pgs); |
317 | 0 | const gs_client_pattern *ppat = gs_getpattern(&pdc->ccolor); |
318 | 0 | gs_color_space *pcs; |
319 | 0 | gs_pattern1_instance_t *pinst = |
320 | 0 | (gs_pattern1_instance_t *)gs_currentcolor(pgs)->pattern; |
321 | 0 | const px_pattern_t *pattern = (const px_pattern_t *)gs_get_pattern_client_data(gs_currentcolor(pgs)); |
322 | |
|
323 | 0 | code = gx_pattern_cache_add_dummy_entry(pgs, pinst, |
324 | 0 | pgs->device->color_info.depth); |
325 | 0 | if (code < 0) |
326 | 0 | return code; |
327 | | |
328 | 0 | code = gs_gsave(pgs); |
329 | 0 | if (code < 0) |
330 | 0 | return code; |
331 | | |
332 | 0 | dev_proc(pgs->device, get_initial_matrix)(pgs->device, &m); |
333 | 0 | gs_setmatrix(pgs, &m); |
334 | 0 | code = gs_bbox_transform(&ppat->BBox, &ctm_only(pgs), &bbox); |
335 | 0 | if (code < 0) { |
336 | 0 | gs_grestore(pgs); |
337 | 0 | return code; |
338 | 0 | } |
339 | 0 | clip_box.p.x = float2fixed(bbox.p.x); |
340 | 0 | clip_box.p.y = float2fixed(bbox.p.y); |
341 | 0 | clip_box.q.x = float2fixed(bbox.q.x); |
342 | 0 | clip_box.q.y = float2fixed(bbox.q.y); |
343 | 0 | code = gx_clip_to_rectangle(pgs, &clip_box); |
344 | 0 | if (code < 0) { |
345 | 0 | gs_grestore(pgs); |
346 | 0 | return code; |
347 | 0 | } |
348 | | |
349 | 0 | { |
350 | 0 | pattern_accum_param_s param; |
351 | 0 | param.pinst = (void *)pinst; |
352 | 0 | param.graphics_state = (void *)pgs; |
353 | 0 | param.pinst_id = pinst->id; |
354 | |
|
355 | 0 | code = (*dev_proc(pgs->device, dev_spec_op))((gx_device *)pgs->device, |
356 | 0 | gxdso_pattern_start_accum, ¶m, sizeof(pattern_accum_param_s)); |
357 | 0 | } |
358 | |
|
359 | 0 | if (code < 0) { |
360 | 0 | gs_grestore(pgs); |
361 | 0 | return code; |
362 | 0 | } |
363 | | |
364 | | /* set the color space, the 'current' space is pattern, we need to set the |
365 | | * proper space for the image that we draw. |
366 | | */ |
367 | 0 | switch (pattern->params.color_space) { |
368 | 0 | case eGray: |
369 | 0 | pcs = gs_cspace_new_DeviceGray(pgs->memory); |
370 | 0 | if (pcs == NULL) { |
371 | 0 | gs_grestore(pgs); |
372 | 0 | return_error(errorInsufficientMemory); |
373 | 0 | } |
374 | 0 | break; |
375 | 0 | case eRGB: |
376 | 0 | case eSRGB: |
377 | 0 | pcs = gs_cspace_new_DeviceRGB(pgs->memory); |
378 | 0 | if (pcs == NULL) { |
379 | 0 | gs_grestore(pgs); |
380 | 0 | return_error(errorInsufficientMemory); |
381 | 0 | } |
382 | 0 | break; |
383 | 0 | default: |
384 | 0 | gs_grestore(pgs); |
385 | 0 | return_error(errorIllegalAttributeValue); |
386 | 0 | } |
387 | 0 | gs_setcolorspace(pgs, pcs); |
388 | |
|
389 | 0 | code = px_paint_pattern(&pdc->ccolor, pgs); |
390 | 0 | if (code < 0) |
391 | 0 | return code; |
392 | | |
393 | 0 | code = gs_grestore(pgs); |
394 | 0 | if (code < 0) |
395 | 0 | return code; |
396 | 0 | { |
397 | 0 | pattern_accum_param_s param; |
398 | 0 | param.pinst = (void *)pinst; |
399 | 0 | param.graphics_state = (void *)pgs; |
400 | 0 | param.pinst_id = pinst->id; |
401 | |
|
402 | 0 | code = dev_proc(pgs->device, dev_spec_op)(pgs->device, |
403 | 0 | gxdso_pattern_finish_accum, ¶m, sizeof(pattern_accum_param_s)); |
404 | 0 | } |
405 | |
|
406 | 0 | return code; |
407 | 0 | } |
408 | | |
409 | | static int px_remap_pattern(const gs_client_color *pcc, gs_gstate *pgs) |
410 | 0 | { |
411 | 0 | const gs_client_pattern *ppat = gs_getpattern(pcc); |
412 | 0 | int code = 0; |
413 | | |
414 | | /* pgs->device is the newly created pattern accumulator, but we want to test the device |
415 | | * that is 'behind' that, the actual output device, so we use the one from |
416 | | * the saved graphics state. |
417 | | */ |
418 | 0 | if (pgs->have_pattern_streams) |
419 | 0 | code = dev_proc(pcc->pattern->saved->device, dev_spec_op)(pcc->pattern->saved->device, |
420 | 0 | gxdso_pattern_can_accum, (void *)ppat, ppat->uid.id); |
421 | |
|
422 | 0 | if (code == 1) { |
423 | | /* Device handles high-level patterns, so return 'remap'. |
424 | | * This closes the internal accumulator device, as we no longer need |
425 | | * it, and the error trickles back up to the PDL client. The client |
426 | | * must then take action to start the device's accumulator, draw the |
427 | | * pattern, close the device's accumulator and generate a cache entry. |
428 | | * See px_high_level_pattern above. |
429 | | */ |
430 | 0 | return_error(gs_error_Remap_Color); |
431 | 0 | } else { |
432 | 0 | return px_paint_pattern(pcc, pgs); |
433 | 0 | } |
434 | 0 | } |
435 | | |
436 | | /* Create the rendering of a pattern. */ |
437 | | static int |
438 | | render_pattern(gs_client_color * pcc, const px_pattern_t * pattern, |
439 | | const px_value_t * porigin, const px_value_t * pdsize, |
440 | | px_state_t * pxs) |
441 | 168 | { |
442 | 168 | px_gstate_t *pxgs = pxs->pxgs; |
443 | 168 | uint rep_width = pattern->params.width; |
444 | 168 | uint rep_height = pattern->params.height; |
445 | 168 | uint full_width, full_height; |
446 | 168 | gs_gstate *pgs = pxs->pgs; |
447 | 168 | gs_client_pattern templat; |
448 | | |
449 | | /* |
450 | | * If halftoning may occur, replicate the pattern so we don't get |
451 | | * halftone seams. |
452 | | */ |
453 | 168 | { |
454 | 168 | gx_device *dev = gs_currentdevice(pgs); |
455 | | |
456 | 168 | if (!gx_device_must_halftone(dev)) { /* No halftoning. */ |
457 | 0 | full_width = rep_width; |
458 | 0 | full_height = rep_height; |
459 | 168 | } else { |
460 | 168 | full_width = ilcm(rep_width, pxgs->halftone.width); |
461 | 168 | full_height = ilcm(rep_height, pxgs->halftone.height); |
462 | | /* |
463 | | * If the pattern would be enormous, don't replicate it. |
464 | | * This is a HACK. |
465 | | */ |
466 | 168 | if (full_width > 10000) |
467 | 0 | full_width = rep_width; |
468 | 168 | if (full_height > 10000) |
469 | 1 | full_height = rep_height; |
470 | 168 | } |
471 | 168 | } |
472 | | /* Construct a Pattern for the library, and render it. */ |
473 | 168 | gs_pattern1_init(&templat); |
474 | 168 | uid_set_UniqueID(&templat.uid, pattern->id); |
475 | 168 | templat.PaintType = 1; |
476 | 168 | templat.TilingType = 1; |
477 | 168 | templat.BBox.p.x = 0; |
478 | 168 | templat.BBox.p.y = 0; |
479 | 168 | templat.BBox.q.x = full_width; |
480 | 168 | templat.BBox.q.y = full_height; |
481 | 168 | templat.XStep = (float)full_width; |
482 | 168 | templat.YStep = (float)full_height; |
483 | 168 | templat.PaintProc = px_remap_pattern; |
484 | 168 | { |
485 | 168 | gs_matrix mat; |
486 | 168 | gs_point dsize; |
487 | 168 | int code; |
488 | | |
489 | 168 | if (porigin) |
490 | 0 | gs_make_translation(real_value(porigin, 0), |
491 | 0 | real_value(porigin, 1), &mat); |
492 | 168 | else |
493 | 168 | gs_make_identity(&mat); |
494 | | |
495 | 168 | if (pdsize) { |
496 | 0 | dsize.x = real_value(pdsize, 0); |
497 | 0 | dsize.y = real_value(pdsize, 1); |
498 | 168 | } else { |
499 | 168 | dsize.x = pattern->params.dest_width; |
500 | 168 | dsize.y = pattern->params.dest_height; |
501 | 168 | } |
502 | 168 | gs_matrix_scale(&mat, dsize.x / rep_width, dsize.y / rep_height, |
503 | 168 | &mat); |
504 | | /* |
505 | | * gs_makepattern will make a copy of the current gstate. |
506 | | * We don't want this copy to contain any circular back pointers |
507 | | * to px_pattern_ts: such pointers are unnecessary, because |
508 | | * px_paint_pattern doesn't use the pen and brush (in fact, |
509 | | * it doesn't even reference the px_gstate_t). We also want to |
510 | | * reset the path and clip path. The easiest (although not by |
511 | | * any means the most efficient) way to do this is to do a gsave, |
512 | | * reset the necessary things, do the makepattern, and then do |
513 | | * a grestore. |
514 | | */ |
515 | 168 | code = gs_gsave(pgs); |
516 | 168 | if (code < 0) |
517 | 0 | return code; |
518 | 168 | { |
519 | 168 | px_gstate_t *pxgs = pxs->pxgs; |
520 | | |
521 | 168 | px_gstate_rc_adjust(pxgs, -1, pxgs->memory); |
522 | 168 | pxgs->brush.type = pxgs->pen.type = pxpNull; |
523 | 168 | gs_newpath(pgs); |
524 | 168 | px_initclip(pxs); |
525 | 168 | } |
526 | 168 | { |
527 | 168 | gs_color_space *pcs; |
528 | | |
529 | | /* set the color space */ |
530 | 168 | switch (pattern->params.color_space) { |
531 | 168 | case eGray: |
532 | 168 | pcs = gs_cspace_new_DeviceGray(pxgs->memory); |
533 | 168 | if (pcs == NULL) |
534 | 0 | return_error(errorInsufficientMemory); |
535 | 168 | break; |
536 | 168 | case eRGB: |
537 | 0 | case eSRGB: |
538 | 0 | pcs = gs_cspace_new_DeviceRGB(pxgs->memory); |
539 | 0 | if (pcs == NULL) |
540 | 0 | return_error(errorInsufficientMemory); |
541 | 0 | break; |
542 | 0 | default: |
543 | 0 | return_error(errorIllegalAttributeValue); |
544 | 168 | } |
545 | 168 | gs_setcolorspace(pgs, pcs); |
546 | 168 | } |
547 | 168 | code = gs_makepattern(pcc, &templat, &mat, pgs, NULL); |
548 | 168 | pcc->pattern->client_data = (void *)pattern; |
549 | 168 | gs_grestore(pgs); |
550 | 168 | return code; |
551 | 168 | } |
552 | 168 | } |
553 | | |
554 | | /* ------ Brush/pen ------ */ |
555 | | |
556 | | /* Check parameters and execute SetBrushSource or SetPenSource: */ |
557 | | /* pxaRGBColor, pxaGrayLevel, pxaNullBrush/Pen, pxaPatternSelectID, */ |
558 | | /* pxaPatternOrigin, pxaNewDestinationSize */ |
559 | | static ulong |
560 | | int_type_max(px_data_type_t type) |
561 | 13.1k | { |
562 | 13.1k | return |
563 | 13.1k | (type & pxd_ubyte ? 255 : |
564 | 13.1k | type & pxd_uint16 ? 65535 : |
565 | 0 | type & pxd_sint16 ? 32767 : type & pxd_uint32 ? (ulong) 0xffffffff : |
566 | 0 | /* type & pxd_sint32 */ 0x7fffffff); |
567 | 13.1k | } |
568 | | static real |
569 | | fraction_value(const px_value_t * pv, int i) |
570 | 3.62k | { |
571 | 3.62k | px_data_type_t type = pv->type; |
572 | 3.62k | real v; |
573 | | |
574 | 3.62k | if (type & pxd_real32) |
575 | 0 | return pv->value.ra[i]; |
576 | 3.62k | v = (real) pv->value.ia[i]; |
577 | 3.62k | return (v < 0 ? 0 : v / int_type_max(type)); |
578 | 3.62k | } |
579 | | |
580 | | /* we use an enumeration instead of index numbers improve readability in this |
581 | | "very busy" routine */ |
582 | | typedef enum |
583 | | { |
584 | | aRGBColor = 0, |
585 | | aGrayLevel = 1, |
586 | | aPrimaryArray = 2, |
587 | | aPrimaryDepth = 3, |
588 | | aNullBrushPen = 4, |
589 | | aPatternSelectID = 5, |
590 | | aPatternOrigin = 6, |
591 | | aNewDestinationSize = 7 |
592 | | } pxl_source_t; |
593 | | |
594 | | /* given a paint type decide if a halftone is necessary */ |
595 | | static bool |
596 | | px_needs_halftone(const gs_memory_t * mem, px_paint_t * ppt) |
597 | 13.5k | { |
598 | 13.5k | bool needs_halftone; |
599 | | |
600 | 13.5k | if (ppt->type == pxpPattern) |
601 | 168 | needs_halftone = true; |
602 | 13.3k | else if (ppt->type == pxpNull) |
603 | 6.53k | needs_halftone = false; |
604 | 6.81k | else if (ppt->type == pxpGray) |
605 | 3.62k | needs_halftone = ppt->value.gray != 0 && ppt->value.gray != 1; |
606 | 3.18k | else if (ppt->type == pxpRGB || ppt->type == pxpSRGB) { /* rgb or srgb */ |
607 | 3.18k | int i; |
608 | | |
609 | 3.18k | needs_halftone = false; |
610 | 8.24k | for (i = 0; i < 3; i++) { |
611 | 6.78k | if (ppt->value.rgb[i] != 0 && ppt->value.rgb[i] != 1) { |
612 | 1.71k | needs_halftone = true; |
613 | 1.71k | break; |
614 | 1.71k | } |
615 | 6.78k | } |
616 | 3.18k | } else { |
617 | 0 | dmprintf(mem, "unknown paint type\n"); |
618 | 0 | needs_halftone = true; |
619 | 0 | } |
620 | 13.5k | return needs_halftone; |
621 | 13.5k | } |
622 | | |
623 | | static int |
624 | | set_source(const px_args_t * par, px_state_t * pxs, px_paint_t * ppt) |
625 | 13.5k | { |
626 | 13.5k | px_gstate_t *pxgs = pxs->pxgs; |
627 | 13.5k | int code = 0; |
628 | | |
629 | | /* pxaPatternSelectID */ |
630 | 13.5k | if (par->pv[aPatternSelectID]) { |
631 | 169 | px_value_t key; |
632 | 169 | void *value; |
633 | 169 | px_pattern_t *pattern; |
634 | 169 | gs_client_color ccolor; |
635 | 169 | int code; |
636 | | |
637 | 169 | if (par->pv[aRGBColor] || par->pv[aGrayLevel] |
638 | 169 | || par->pv[aNullBrushPen]) |
639 | 0 | return_error(errorIllegalAttributeCombination); |
640 | 169 | key.type = pxd_array | pxd_ubyte; |
641 | 169 | key.value.array.data = (byte *) & par->pv[aPatternSelectID]->value.i; |
642 | 169 | key.value.array.size = sizeof(int32_t); |
643 | 169 | if (!(px_dict_find(&pxgs->temp_pattern_dict, &key, &value) || |
644 | 169 | px_dict_find(&pxs->page_pattern_dict, &key, &value) || |
645 | 169 | px_dict_find(&pxs->session_pattern_dict, &key, &value)) |
646 | 169 | ) |
647 | 1 | return_error(errorRasterPatternUndefined); |
648 | 168 | pattern = value; |
649 | | |
650 | 168 | px_set_halftone(pxs); |
651 | 168 | code = render_pattern(&ccolor, pattern, par->pv[aPatternOrigin], |
652 | 168 | par->pv[aNewDestinationSize], pxs); |
653 | | /* |
654 | | * We don't use px_paint_rc_adjust(... 1 ...) here, because |
655 | | * gs_makepattern creates pattern instances with a reference |
656 | | * count already set to 1. |
657 | | */ |
658 | 168 | rc_increment(pattern); |
659 | 168 | if (code < 0) |
660 | 0 | return code; |
661 | 168 | px_paint_rc_adjust(ppt, -1, pxs->memory); |
662 | 168 | ppt->type = pxpPattern; |
663 | 168 | ppt->value.pattern.pattern = pattern; |
664 | 168 | ppt->value.pattern.color = ccolor; |
665 | | /* not pxaPatternSelectID but we have a pattern origin or |
666 | | newdestination size */ |
667 | 13.3k | } else if (par->pv[aPatternOrigin] || par->pv[aNewDestinationSize]) { |
668 | 0 | return_error(errorIllegalAttributeCombination); |
669 | 13.3k | } else if (par->pv[aRGBColor]) { |
670 | 3.18k | const px_value_t *prgb = par->pv[aRGBColor]; |
671 | 3.18k | int i; |
672 | | |
673 | 3.18k | if (par->pv[aGrayLevel] || par->pv[aNullBrushPen]) |
674 | 0 | return_error(errorIllegalAttributeCombination); |
675 | 3.18k | if (pxgs->color_space != eRGB && pxgs->color_space != eSRGB) |
676 | 5 | return_error(errorColorSpaceMismatch); |
677 | 3.18k | px_paint_rc_adjust(ppt, -1, pxs->memory); |
678 | 3.18k | ppt->type = pxpRGB; |
679 | | |
680 | 12.7k | for (i = 0; i < 3; ++i) |
681 | 9.54k | if (prgb->type & pxd_any_real) |
682 | 0 | ppt->value.rgb[i] = real_elt(prgb, i); |
683 | 9.54k | else { |
684 | 9.54k | int32_t v = integer_elt(prgb, i); |
685 | | |
686 | 9.54k | ppt->value.rgb[i] = |
687 | 9.54k | (v < 0 ? 0 : (real) v / int_type_max(prgb->type)); |
688 | 9.54k | } |
689 | 10.1k | } else if (par->pv[aGrayLevel]) { /* pxaGrayLevel */ |
690 | 3.62k | if (par->pv[aNullBrushPen]) |
691 | 0 | return_error(errorIllegalAttributeCombination); |
692 | 3.62k | if (pxgs->color_space != eGray) |
693 | 0 | return_error(errorColorSpaceMismatch); |
694 | 3.62k | px_paint_rc_adjust(ppt, -1, pxs->memory); |
695 | 3.62k | ppt->type = pxpGray; |
696 | 3.62k | ppt->value.gray = fraction_value(par->pv[aGrayLevel], 0); |
697 | 6.53k | } else if (par->pv[aNullBrushPen]) { /* pxaNullBrush/Pen */ |
698 | 6.53k | px_paint_rc_adjust(ppt, -1, pxs->memory); |
699 | 6.53k | ppt->type = pxpNull; |
700 | 6.53k | } else if (par->pv[aPrimaryDepth] && par->pv[aPrimaryArray]) { |
701 | 0 | px_paint_rc_adjust(ppt, -1, pxs->memory); |
702 | 0 | if (pxgs->color_space == eRGB) |
703 | 0 | ppt->type = pxpRGB; |
704 | 0 | else if (pxgs->color_space == eGray) |
705 | 0 | ppt->type = pxpGray; |
706 | 0 | else if (pxgs->color_space == eSRGB) |
707 | 0 | ppt->type = pxpSRGB; |
708 | 0 | else { |
709 | 0 | dmprintf1(pxgs->memory, "Warning unknown color space %d\n", |
710 | 0 | pxgs->color_space); |
711 | 0 | ppt->type = pxpGray; |
712 | 0 | } |
713 | | /* NB depth?? - for range checking */ |
714 | 0 | if (ppt->type == pxpRGB || ppt->type == pxpSRGB) { |
715 | 0 | ppt->value.rgb[0] = |
716 | 0 | (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0; |
717 | 0 | ppt->value.rgb[1] = |
718 | 0 | (float)par->pv[aPrimaryArray]->value.array.data[1] / 255.0; |
719 | 0 | ppt->value.rgb[2] = |
720 | 0 | (float)par->pv[aPrimaryArray]->value.array.data[2] / 255.0; |
721 | 0 | } else |
722 | | /* NB figure out reals and ints */ |
723 | 0 | ppt->value.gray = |
724 | 0 | (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0; |
725 | 0 | } else |
726 | 7 | return_error(errorMissingAttribute); |
727 | | /* |
728 | | * Update the halftone to the most recently set one. |
729 | | * This will do the wrong thing if we set the brush or pen source, |
730 | | * set the halftone, and then set the other source, but we have |
731 | | * no way to handle this properly with the current library. |
732 | | */ |
733 | 13.5k | if (px_needs_halftone(pxs->memory, ppt)) |
734 | 1.88k | code = px_set_halftone(pxs); |
735 | 13.5k | return code; |
736 | 13.5k | } |
737 | | |
738 | | /* Set up a brush or pen for drawing. */ |
739 | | /* If it is a pattern, SetBrush/PenSource guaranteed that it is compatible */ |
740 | | /* with the current color space. */ |
741 | | int |
742 | | px_set_paint(const px_paint_t * ppt, px_state_t * pxs) |
743 | 11.1k | { |
744 | 11.1k | gs_gstate *pgs = pxs->pgs; |
745 | 11.1k | px_paint_type_t type; |
746 | | |
747 | 11.1k | type = ppt->type; |
748 | 11.1k | switch (type) { |
749 | 0 | case pxpNull: |
750 | 0 | return gs_setnullcolor(pgs); |
751 | 7.85k | case pxpRGB: |
752 | 7.85k | return gs_setrgbcolor(pgs, ppt->value.rgb[0], ppt->value.rgb[1], |
753 | 7.85k | ppt->value.rgb[2]); |
754 | 3.16k | case pxpGray: |
755 | 3.16k | return gs_setgray(pgs, ppt->value.gray); |
756 | 88 | case pxpPattern: |
757 | 88 | return gs_setpattern(pgs, &ppt->value.pattern.color); |
758 | 0 | case pxpSRGB: |
759 | 0 | return gs_setrgbcolor(pgs, |
760 | 0 | ppt->value.rgb[0], |
761 | 0 | ppt->value.rgb[1], |
762 | 0 | ppt->value.rgb[2]); |
763 | 0 | default: /* can't happen */ |
764 | 0 | return_error(errorIllegalAttributeValue); |
765 | 11.1k | } |
766 | 11.1k | } |
767 | | |
768 | | /* ---------------- Operators ---------------- */ |
769 | | |
770 | | const byte apxSetBrushSource[] = { |
771 | | 0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth, |
772 | | pxaNullBrush, pxaPatternSelectID, |
773 | | pxaPatternOrigin, pxaNewDestinationSize, 0 |
774 | | }; |
775 | | |
776 | | int |
777 | | pxSetBrushSource(px_args_t * par, px_state_t * pxs) |
778 | 6.98k | { |
779 | 6.98k | return set_source(par, pxs, &pxs->pxgs->brush); |
780 | 6.98k | } |
781 | | |
782 | | const byte apxSetColorSpace[] = { |
783 | | 0, pxaColorSpace, pxaColorimetricColorSpace, pxaXYChromaticities, |
784 | | pxaWhiteReferencePoint, |
785 | | pxaCRGBMinMax, pxaGammaGain, pxaPaletteDepth, pxaPaletteData, 0 |
786 | | }; |
787 | | |
788 | | /* it appears the 4600 does not support CRGB define this to enable support */ |
789 | | /* #define SUPPORT_COLORIMETRIC */ |
790 | | |
791 | | int |
792 | | pxSetColorSpace(px_args_t * par, px_state_t * pxs) |
793 | 3.19k | { |
794 | 3.19k | px_gstate_t *pxgs = pxs->pxgs; |
795 | 3.19k | pxeColorSpace_t cspace; |
796 | | |
797 | 3.19k | if (par->pv[0]) |
798 | 3.19k | cspace = par->pv[0]->value.i; |
799 | 1 | else if (par->pv[1]) |
800 | 0 | cspace = par->pv[1]->value.i; |
801 | 1 | else |
802 | 1 | return_error(errorIllegalAttributeValue); |
803 | | |
804 | 3.19k | if (par->pv[6] && par->pv[7]) { |
805 | 0 | int ncomp = |
806 | 0 | ((cspace == eRGB || cspace == eSRGB) ? 3 : 1); |
807 | 0 | uint size = par->pv[7]->value.array.size; |
808 | |
|
809 | 0 | if (!(size == ncomp << 1 || size == ncomp << 4 || size == ncomp << 8) |
810 | 0 | ) { |
811 | | /* The HP printers we've tested appear to truncate |
812 | | this value and not produce an error on overflow */ |
813 | 0 | if (size > ncomp << 8) |
814 | 0 | size = ncomp << 8; |
815 | 0 | else |
816 | 0 | return_error(errorIllegalAttributeValue); |
817 | 0 | } |
818 | | /* The palette is in an array, but we want a string. */ |
819 | 0 | { |
820 | 0 | if (pxgs->palette.data && !pxgs->palette_is_shared && |
821 | 0 | pxgs->palette.size != size) { |
822 | 0 | gs_free_string(pxs->memory, (byte *) pxgs->palette.data, |
823 | 0 | pxgs->palette.size, |
824 | 0 | "pxSetColorSpace(old palette)"); |
825 | 0 | pxgs->palette.data = 0; |
826 | 0 | pxgs->palette.size = 0; |
827 | 0 | } |
828 | 0 | if (pxgs->palette.data == 0 || pxgs->palette_is_shared) { |
829 | 0 | byte *pdata = gs_alloc_string(pxs->memory, size, |
830 | 0 | "pxSetColorSpace(palette)"); |
831 | |
|
832 | 0 | if (pdata == 0) |
833 | 0 | return_error(errorInsufficientMemory); |
834 | 0 | pxgs->palette.data = pdata; |
835 | 0 | pxgs->palette.size = size; |
836 | 0 | } |
837 | 0 | memcpy((void *)pxgs->palette.data, par->pv[7]->value.array.data, |
838 | 0 | size); |
839 | 0 | } |
840 | 3.19k | } else if (par->pv[6] || par->pv[7]) |
841 | 0 | return_error(errorMissingAttribute); |
842 | 3.19k | else if (pxgs->palette.data) { |
843 | 0 | if (!pxgs->palette_is_shared) |
844 | 0 | gs_free_string(pxs->memory, (byte *) pxgs->palette.data, |
845 | 0 | pxgs->palette.size, |
846 | 0 | "pxSetColorSpace(old palette)"); |
847 | 0 | pxgs->palette.data = 0; |
848 | 0 | pxgs->palette.size = 0; |
849 | 0 | } |
850 | 3.19k | pxgs->palette_is_shared = false; |
851 | 3.19k | pxgs->color_space = cspace; |
852 | | #ifndef SET_COLOR_SPACE_NO_SET_BLACK |
853 | | { |
854 | | px_paint_rc_adjust(&pxgs->brush, -1, pxs->memory); |
855 | | pxgs->brush.type = pxpGray; |
856 | | pxgs->brush.value.gray = 0; |
857 | | } |
858 | | { |
859 | | px_paint_rc_adjust(&pxgs->pen, -1, pxs->memory); |
860 | | pxgs->pen.type = pxpGray; |
861 | | pxgs->pen.value.gray = 0; |
862 | | } |
863 | | #endif |
864 | 3.19k | return 0; |
865 | 3.19k | } |
866 | | |
867 | | const byte apxSetHalftoneMethod[] = { |
868 | | 0, pxaDitherOrigin, pxaDeviceMatrix, pxaDitherMatrixDataType, |
869 | | pxaDitherMatrixSize, pxaDitherMatrixDepth, pxaAllObjectTypes, |
870 | | pxaTextObjects, pxaVectorObjects, pxaRasterObjects, 0 |
871 | | }; |
872 | | |
873 | | int |
874 | | pxSetHalftoneMethod(px_args_t * par, px_state_t * pxs) |
875 | 2.06k | { |
876 | 2.06k | gs_gstate *pgs = pxs->pgs; |
877 | 2.06k | px_gstate_t *pxgs = pxs->pxgs; |
878 | 2.06k | pxeDitherMatrix_t method; |
879 | | |
880 | 2.06k | if (par->pv[5] || par->pv[6] || par->pv[7] || par->pv[8]) |
881 | | /* Placeholder to support halftones per object type. */ |
882 | 218 | ; |
883 | | |
884 | 2.06k | if (par->pv[1]) { /* Internal halftone */ |
885 | 1.75k | if (par->pv[2] || par->pv[3] || par->pv[4]) |
886 | 0 | return_error(errorIllegalAttributeCombination); |
887 | 1.75k | method = par->pv[1]->value.i; |
888 | 1.75k | px_set_default_screen_size(pxs, method); |
889 | 1.75k | pxs->download_string.data = 0; |
890 | 1.75k | pxs->download_string.size = 0; |
891 | 1.75k | } else if (par->pv[2] && par->pv[3] && par->pv[4]) { /* Dither matrix */ |
892 | 0 | uint width = par->pv[3]->value.ia[0]; |
893 | 0 | uint source_width = (width + 3) & ~3; |
894 | 0 | uint height = par->pv[3]->value.ia[1]; |
895 | 0 | uint size = width * height; |
896 | 0 | uint source_size = source_width * height; |
897 | |
|
898 | 0 | if (par->source.position == 0) { |
899 | 0 | byte *data; |
900 | |
|
901 | 0 | if (par->source.available == 0) |
902 | 0 | return pxNeedData; |
903 | 0 | data = gs_alloc_string(pxs->memory, size, "dither matrix"); |
904 | 0 | if (data == 0) |
905 | 0 | return_error(errorInsufficientMemory); |
906 | 0 | pxs->download_string.data = data; |
907 | 0 | pxs->download_string.size = size; |
908 | 0 | } |
909 | 0 | while (par->source.position < source_size) { |
910 | 0 | uint source_x = par->source.position % source_width; |
911 | 0 | uint source_y = par->source.position / source_width; |
912 | 0 | uint used; |
913 | |
|
914 | 0 | if (par->source.available == 0) |
915 | 0 | return pxNeedData; |
916 | 0 | if (source_x >= width) { /* Skip padding bytes at end of row. */ |
917 | 0 | used = min(par->source.available, source_width - source_x); |
918 | 0 | } else { /* Read data. */ |
919 | 0 | const byte *src = par->source.data; |
920 | 0 | byte *dest = pxs->download_string.data; |
921 | 0 | byte *pdata_min = dest; |
922 | 0 | byte *pdata_max = pdata_min + pxs->download_string.size; |
923 | 0 | uint i; |
924 | 0 | int skip; |
925 | |
|
926 | 0 | used = min(par->source.available, width - source_x); |
927 | | /* |
928 | | * The documentation doesn't say this, but we have to |
929 | | * rotate the dither matrix to match the orientation, |
930 | | * remembering that we have a Y-inverted coordinate |
931 | | * system. This is quite a nuisance! |
932 | | */ |
933 | 0 | switch (pxs->orientation) { |
934 | 0 | case ePortraitOrientation: |
935 | 0 | dest += source_y * width + source_x; |
936 | 0 | skip = 1; |
937 | 0 | break; |
938 | 0 | case eLandscapeOrientation: |
939 | 0 | dest += (width - 1 - source_x) * height + source_y; |
940 | 0 | skip = -(int)height; |
941 | 0 | break; |
942 | 0 | case eReversePortrait: |
943 | 0 | dest += (height - 1 - source_y) * width + |
944 | 0 | width - 1 - source_x; |
945 | 0 | skip = -1; |
946 | 0 | break; |
947 | 0 | case eReverseLandscape: |
948 | 0 | dest += (source_x + 1) * height - source_y - 1; |
949 | 0 | skip = height; |
950 | 0 | break; |
951 | 0 | default: |
952 | 0 | return -1; |
953 | 0 | } |
954 | 0 | if ((dest < pdata_min) || (pdata_max < dest + ((used - 1) * (int64_t)skip))) |
955 | 0 | return_error(gs_error_rangecheck); |
956 | 0 | for (i = 0; i < used; ++i, ++src, dest += skip) { |
957 | 0 | *dest = *src; |
958 | 0 | } |
959 | 0 | } |
960 | 0 | par->source.position += used; |
961 | 0 | par->source.available -= used; |
962 | 0 | par->source.data += used; |
963 | |
|
964 | 0 | } |
965 | 0 | pxgs->halftone.width = width; |
966 | 0 | pxgs->halftone.height = height; |
967 | 0 | method = eDownloaded; |
968 | 0 | } else |
969 | 301 | return 0; |
970 | 1.75k | if (par->pv[0]) |
971 | 0 | gs_transform(pgs, real_value(par->pv[0], 0), |
972 | 0 | real_value(par->pv[0], 1), &pxgs->halftone.origin); |
973 | 1.75k | else |
974 | 1.75k | gs_transform(pgs, 0.0, 0.0, &pxgs->halftone.origin); |
975 | 1.75k | pxgs->halftone.thresholds = pxs->download_string; |
976 | 1.75k | pxgs->halftone.method = method; |
977 | 1.75k | pxgs->halftone.set = false; |
978 | 1.75k | return 0; |
979 | 2.06k | } |
980 | | |
981 | | const byte apxSetPenSource[] = { |
982 | | 0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth, |
983 | | pxaNullPen, pxaPatternSelectID, |
984 | | pxaPatternOrigin, pxaNewDestinationSize, 0 |
985 | | }; |
986 | | |
987 | | int |
988 | | pxSetPenSource(px_args_t * par, px_state_t * pxs) |
989 | 6.53k | { |
990 | 6.53k | return set_source(par, pxs, &pxs->pxgs->pen); |
991 | 6.53k | } |
992 | | |
993 | | const byte apxSetColorTreatment[] = |
994 | | { 0, pxaColorTreatment, pxaAllObjectTypes, pxaTextObjects, |
995 | | pxaVectorObjects, pxaRasterObjects, 0 }; |
996 | | |
997 | | int |
998 | | pxSetColorTreatment(px_args_t * par, px_state_t * pxs) |
999 | 335 | { |
1000 | 335 | return 0; |
1001 | 335 | } |
1002 | | |
1003 | | const byte apxSetNeutralAxis[] = { 0, pxaAllObjectTypes, pxaTextObjects, |
1004 | | pxaVectorObjects, pxaRasterObjects, 0 |
1005 | | }; |
1006 | | |
1007 | | int |
1008 | | pxSetNeutralAxis(px_args_t * par, px_state_t * pxs) |
1009 | 226 | { |
1010 | 226 | return 0; |
1011 | 226 | } |
1012 | | |
1013 | | const byte apxSetColorTrapping[] = { pxaAllObjectTypes, 0, 0 }; |
1014 | | |
1015 | | int |
1016 | | pxSetColorTrapping(px_args_t * par, px_state_t * pxs) |
1017 | 225 | { |
1018 | 225 | return 0; |
1019 | 225 | } |
1020 | | |
1021 | | const byte apxSetAdaptiveHalftoning[] = |
1022 | | { 0, pxaAllObjectTypes, pxaTextObjects, pxaVectorObjects, |
1023 | | pxaRasterObjects, 0 }; |
1024 | | |
1025 | | int |
1026 | | pxSetAdaptiveHalftoning(px_args_t * par, px_state_t * pxs) |
1027 | 225 | { |
1028 | 225 | return 0; |
1029 | 225 | } |