/src/ghostpdl/base/gxshade.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 | | /* Shading rendering support */ |
18 | | #include "math_.h" |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "gsrect.h" |
22 | | #include "gxcspace.h" |
23 | | #include "gscindex.h" |
24 | | #include "gscie.h" /* requires gscspace.h */ |
25 | | #include "gxdevcli.h" |
26 | | #include "gxgstate.h" |
27 | | #include "gxdht.h" /* for computing # of different colors */ |
28 | | #include "gxpaint.h" |
29 | | #include "gxshade.h" |
30 | | #include "gxshade4.h" |
31 | | #include "gsicc.h" |
32 | | #include "gsicc_cache.h" |
33 | | #include "gxcdevn.h" |
34 | | #include "gximage.h" |
35 | | |
36 | | /* Define a maximum smoothness value. */ |
37 | | /* smoothness > 0.2 produces severely blocky output. */ |
38 | | #define MAX_SMOOTHNESS 0.2 |
39 | | |
40 | | /* ================ Packed coordinate streams ================ */ |
41 | | |
42 | | /* Forward references */ |
43 | | static int cs_next_packed_value(shade_coord_stream_t *, int, uint *); |
44 | | static int cs_next_array_value(shade_coord_stream_t *, int, uint *); |
45 | | static int cs_next_packed_decoded(shade_coord_stream_t *, int, |
46 | | const float[2], float *); |
47 | | static int cs_next_array_decoded(shade_coord_stream_t *, int, |
48 | | const float[2], float *); |
49 | | static void cs_packed_align(shade_coord_stream_t *cs, int radix); |
50 | | static void cs_array_align(shade_coord_stream_t *cs, int radix); |
51 | | static bool cs_eod(const shade_coord_stream_t * cs); |
52 | | |
53 | | /* Initialize a packed value stream. */ |
54 | | void |
55 | | shade_next_init(shade_coord_stream_t * cs, |
56 | | const gs_shading_mesh_params_t * params, |
57 | | const gs_gstate * pgs) |
58 | 348 | { |
59 | 348 | cs->params = params; |
60 | 348 | cs->pctm = &pgs->ctm; |
61 | 348 | if (data_source_is_stream(params->DataSource)) { |
62 | | /* |
63 | | * Rewind the data stream iff it is reusable -- either a reusable |
64 | | * file or a reusable string. |
65 | | */ |
66 | 348 | stream *s = cs->s = params->DataSource.data.strm; |
67 | | |
68 | 348 | if ((s->file != 0 && s->file_limit != max_long) || |
69 | 348 | (s->file == 0 && s->strm == 0) |
70 | 348 | ) |
71 | 348 | sseek(s, 0); |
72 | 348 | } else { |
73 | 0 | s_init(&cs->ds, NULL); |
74 | 0 | sread_string(&cs->ds, params->DataSource.data.str.data, |
75 | 0 | params->DataSource.data.str.size); |
76 | 0 | cs->s = &cs->ds; |
77 | 0 | } |
78 | 348 | if (data_source_is_array(params->DataSource)) { |
79 | 0 | cs->get_value = cs_next_array_value; |
80 | 0 | cs->get_decoded = cs_next_array_decoded; |
81 | 0 | cs->align = cs_array_align; |
82 | 348 | } else { |
83 | 348 | cs->get_value = cs_next_packed_value; |
84 | 348 | cs->get_decoded = cs_next_packed_decoded; |
85 | 348 | cs->align = cs_packed_align; |
86 | 348 | } |
87 | 348 | cs->is_eod = cs_eod; |
88 | 348 | cs->left = 0; |
89 | 348 | cs->ds_EOF = false; |
90 | 348 | cs->first_patch = 1; |
91 | 348 | } |
92 | | |
93 | | /* Check for the End-Of-Data state form a stream. */ |
94 | | static bool |
95 | | cs_eod(const shade_coord_stream_t * cs) |
96 | 177 | { |
97 | 177 | return cs->ds_EOF; |
98 | 177 | } |
99 | | |
100 | | /* Get the next (integer) value from a packed value stream. */ |
101 | | /* 1 <= num_bits <= sizeof(uint) * 8. */ |
102 | | static int |
103 | | cs_next_packed_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue) |
104 | 4.06M | { |
105 | 4.06M | uint bits = cs->bits; |
106 | 4.06M | int left = cs->left; |
107 | | |
108 | 4.06M | if (left >= num_bits) { |
109 | | /* We can satisfy this request with the current buffered bits. */ |
110 | 0 | cs->left = left -= num_bits; |
111 | 0 | *pvalue = (bits >> left) & ((1 << num_bits) - 1); |
112 | 4.06M | } else { |
113 | | /* We need more bits. */ |
114 | 4.06M | int needed = num_bits - left; |
115 | 4.06M | uint value = bits & ((1 << left) - 1); /* all the remaining bits */ |
116 | | |
117 | 15.8M | for (; needed >= 8; needed -= 8) { |
118 | 11.7M | int b = sgetc(cs->s); |
119 | | |
120 | 11.7M | if (b < 0) { |
121 | 310 | cs->ds_EOF = true; |
122 | 310 | return_error(gs_error_rangecheck); |
123 | 310 | } |
124 | 11.7M | value = (value << 8) + b; |
125 | 11.7M | } |
126 | 4.06M | if (needed == 0) { |
127 | 4.06M | cs->left = 0; |
128 | 4.06M | *pvalue = value; |
129 | 4.06M | } else { |
130 | 0 | int b = sgetc(cs->s); |
131 | |
|
132 | 0 | if (b < 0) { |
133 | 0 | cs->ds_EOF = true; |
134 | 0 | return_error(gs_error_rangecheck); |
135 | 0 | } |
136 | 0 | cs->bits = b; |
137 | 0 | cs->left = left = 8 - needed; |
138 | 0 | *pvalue = (value << needed) + (b >> left); |
139 | 0 | } |
140 | 4.06M | } |
141 | 4.06M | return 0; |
142 | 4.06M | } |
143 | | |
144 | | /* |
145 | | * Get the next (integer) value from an unpacked array. Note that |
146 | | * num_bits may be 0 if we are reading a coordinate or color value. |
147 | | */ |
148 | | static int |
149 | | cs_next_array_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue) |
150 | 0 | { |
151 | 0 | float value; |
152 | 0 | uint read; |
153 | |
|
154 | 0 | if (sgets(cs->s, (byte *)&value, sizeof(float), &read) < 0 || |
155 | 0 | read != sizeof(float)) { |
156 | 0 | cs->ds_EOF = true; |
157 | 0 | return_error(gs_error_rangecheck); |
158 | 0 | } |
159 | 0 | if (value < 0 || (num_bits != 0 && num_bits < sizeof(uint) * 8 && |
160 | 0 | value >= (1 << num_bits)) || |
161 | 0 | value != (uint)value |
162 | 0 | ) |
163 | 0 | return_error(gs_error_rangecheck); |
164 | 0 | *pvalue = (uint) value; |
165 | 0 | return 0; |
166 | 0 | } |
167 | | |
168 | | /* Get the next decoded floating point value. */ |
169 | | static int |
170 | | cs_next_packed_decoded(shade_coord_stream_t * cs, int num_bits, |
171 | | const float decode[2], float *pvalue) |
172 | 3.99M | { |
173 | 3.99M | uint value; |
174 | 3.99M | int code = cs->get_value(cs, num_bits, &value); |
175 | 3.99M | double max_value = (double)(uint) |
176 | 3.99M | (num_bits == sizeof(uint) * 8 ? ~0 : ((1 << num_bits) - 1)); |
177 | 3.99M | double dvalue = (double)value; |
178 | | |
179 | 3.99M | if (code < 0) |
180 | 164 | return code; |
181 | 3.99M | *pvalue = |
182 | 3.99M | (decode == 0 ? dvalue / max_value : |
183 | 3.99M | decode[0] + dvalue * (decode[1] - decode[0]) / max_value); |
184 | 3.99M | return 0; |
185 | 3.99M | } |
186 | | |
187 | | /* Get the next floating point value from an array, without decoding. */ |
188 | | static int |
189 | | cs_next_array_decoded(shade_coord_stream_t * cs, int num_bits, |
190 | | const float decode[2], float *pvalue) |
191 | 0 | { |
192 | 0 | float value; |
193 | 0 | uint read; |
194 | |
|
195 | 0 | if (sgets(cs->s, (byte *)&value, sizeof(float), &read) < 0 || |
196 | 0 | read != sizeof(float) |
197 | 0 | ) { |
198 | 0 | cs->ds_EOF = true; |
199 | 0 | return_error(gs_error_rangecheck); |
200 | 0 | } |
201 | 0 | *pvalue = value; |
202 | 0 | return 0; |
203 | 0 | } |
204 | | |
205 | | static void |
206 | | cs_packed_align(shade_coord_stream_t *cs, int radix) |
207 | 572k | { |
208 | 572k | cs->left = cs->left / radix * radix; |
209 | 572k | } |
210 | | |
211 | | static void |
212 | | cs_array_align(shade_coord_stream_t *cs, int radix) |
213 | 0 | { |
214 | 0 | } |
215 | | |
216 | | /* Get the next flag value. */ |
217 | | /* Note that this always starts a new data byte. */ |
218 | | int |
219 | | shade_next_flag(shade_coord_stream_t * cs, int BitsPerFlag) |
220 | 70.3k | { |
221 | 70.3k | uint flag; |
222 | 70.3k | int code; |
223 | | |
224 | 70.3k | cs->left = 0; /* start a new byte if packed */ |
225 | 70.3k | code = cs->get_value(cs, BitsPerFlag, &flag); |
226 | 70.3k | return (code < 0 ? code : flag); |
227 | 70.3k | } |
228 | | |
229 | | /* Get one or more coordinate pairs. */ |
230 | | int |
231 | | shade_next_coords(shade_coord_stream_t * cs, gs_fixed_point * ppt, |
232 | | int num_points) |
233 | 972k | { |
234 | 972k | int num_bits = cs->params->BitsPerCoordinate; |
235 | 972k | const float *decode = cs->params->Decode; |
236 | 972k | int code = 0; |
237 | 972k | int i; |
238 | | |
239 | 2.33M | for (i = 0; i < num_points; ++i) { |
240 | 1.36M | float x, y; |
241 | | |
242 | 1.36M | if ((code = cs->get_decoded(cs, num_bits, decode, &x)) < 0 || |
243 | 1.36M | (code = cs->get_decoded(cs, num_bits, decode + 2, &y)) < 0 || |
244 | 1.36M | (code = gs_point_transform2fixed(cs->pctm, x, y, &ppt[i])) < 0 |
245 | 1.36M | ) |
246 | 133 | break; |
247 | 1.36M | } |
248 | 972k | return code; |
249 | 972k | } |
250 | | |
251 | | /* Get a color. Currently all this does is look up Indexed colors. */ |
252 | | int |
253 | | shade_next_color(shade_coord_stream_t * cs, float *pc) |
254 | 698k | { |
255 | 698k | const float *decode = cs->params->Decode + 4; /* skip coord decode */ |
256 | 698k | const gs_color_space *pcs = cs->params->ColorSpace; |
257 | 698k | gs_color_space_index index = gs_color_space_get_index(pcs); |
258 | 698k | int num_bits = cs->params->BitsPerComponent; |
259 | | |
260 | 698k | if (index == gs_color_space_index_Indexed) { |
261 | 0 | int ncomp = gs_color_space_num_components(gs_cspace_base_space(pcs)); |
262 | 0 | int ci; |
263 | 0 | float cf; |
264 | 0 | int code = cs->get_decoded(cs, num_bits, decode, &cf); |
265 | 0 | gs_client_color cc; |
266 | 0 | int i; |
267 | |
|
268 | 0 | if (code < 0) |
269 | 0 | return code; |
270 | 0 | if (cf < 0) |
271 | 0 | return_error(gs_error_rangecheck); |
272 | 0 | ci = (int)cf; |
273 | 0 | if (ci >= gs_cspace_indexed_num_entries(pcs)) |
274 | 0 | return_error(gs_error_rangecheck); |
275 | 0 | code = gs_cspace_indexed_lookup(pcs, ci, &cc); |
276 | 0 | if (code < 0) |
277 | 0 | return code; |
278 | 0 | for (i = 0; i < ncomp; ++i) |
279 | 0 | pc[i] = cc.paint.values[i]; |
280 | 698k | } else { |
281 | 698k | int i, code; |
282 | 698k | int ncomp = (cs->params->Function != 0 ? 1 : |
283 | 698k | gs_color_space_num_components(pcs)); |
284 | | |
285 | 1.96M | for (i = 0; i < ncomp; ++i) { |
286 | 1.26M | if ((code = cs->get_decoded(cs, num_bits, decode + i * 2, &pc[i])) < 0) |
287 | 32 | return code; |
288 | 1.26M | if (cs->params->Function) { |
289 | 499k | gs_function_params_t *params = &cs->params->Function->params; |
290 | | |
291 | 499k | if (pc[i] < params->Domain[i + i]) |
292 | 0 | pc[i] = params->Domain[i + i]; |
293 | 499k | else if (pc[i] > params->Domain[i + i + 1]) |
294 | 6 | pc[i] = params->Domain[i + i + 1]; |
295 | 499k | } |
296 | 1.26M | } |
297 | 698k | } |
298 | 698k | return 0; |
299 | 698k | } |
300 | | |
301 | | /* Get the next vertex for a mesh element. */ |
302 | | int |
303 | | shade_next_vertex(shade_coord_stream_t * cs, shading_vertex_t * vertex, patch_color_t *c) |
304 | 509k | { /* Assuming p->c == c, provides a non-const access. */ |
305 | 509k | int code = shade_next_coords(cs, &vertex->p, 1); |
306 | | |
307 | 509k | if (code >= 0) |
308 | 509k | code = shade_next_color(cs, c->cc.paint.values); |
309 | 509k | if (code >= 0) |
310 | 509k | cs->align(cs, 8); /* CET 09-47J.PS SpecialTestI04Test01. */ |
311 | 509k | return code; |
312 | 509k | } |
313 | | |
314 | | /* ================ Shading rendering ================ */ |
315 | | |
316 | | /* Initialize the common parts of the recursion state. */ |
317 | | int |
318 | | shade_init_fill_state(shading_fill_state_t * pfs, const gs_shading_t * psh, |
319 | | gx_device * dev, gs_gstate * pgs) |
320 | 86.8k | { |
321 | 86.8k | const gs_color_space *pcs = psh->params.ColorSpace; |
322 | 86.8k | float max_error = min(pgs->smoothness, MAX_SMOOTHNESS); |
323 | 86.8k | bool is_lab; |
324 | 86.8k | bool cs_lin_test; |
325 | 86.8k | int code; |
326 | | |
327 | | /* |
328 | | * There's no point in trying to achieve smoothness beyond what |
329 | | * the device can implement, i.e., the number of representable |
330 | | * colors times the number of halftone levels. |
331 | | */ |
332 | 86.8k | long num_colors = |
333 | 86.8k | max(dev->color_info.max_gray, dev->color_info.max_color) + 1; |
334 | 86.8k | const gs_range *ranges = 0; |
335 | 86.8k | int ci; |
336 | 86.8k | gsicc_rendering_param_t rendering_params; |
337 | | |
338 | 86.8k | pfs->cs_always_linear = false; |
339 | 86.8k | pfs->dev = dev; |
340 | 86.8k | pfs->pgs = pgs; |
341 | 86.8k | top: |
342 | 86.8k | pfs->direct_space = pcs; |
343 | 86.8k | pfs->num_components = gs_color_space_num_components(pcs); |
344 | 86.8k | switch ( gs_color_space_get_index(pcs) ) |
345 | 86.8k | { |
346 | 0 | case gs_color_space_index_Indexed: |
347 | 0 | pcs = gs_cspace_base_space(pcs); |
348 | 0 | goto top; |
349 | 0 | case gs_color_space_index_CIEDEFG: |
350 | 0 | ranges = pcs->params.defg->RangeDEFG.ranges; |
351 | 0 | break; |
352 | 0 | case gs_color_space_index_CIEDEF: |
353 | 0 | ranges = pcs->params.def->RangeDEF.ranges; |
354 | 0 | break; |
355 | 0 | case gs_color_space_index_CIEABC: |
356 | 0 | ranges = pcs->params.abc->RangeABC.ranges; |
357 | 0 | break; |
358 | 0 | case gs_color_space_index_CIEA: |
359 | 0 | ranges = &pcs->params.a->RangeA; |
360 | 0 | break; |
361 | 84.8k | case gs_color_space_index_ICC: |
362 | 84.8k | ranges = pcs->cmm_icc_profile_data->Range.ranges; |
363 | 84.8k | break; |
364 | 1.91k | default: |
365 | 1.91k | break; |
366 | 86.8k | } |
367 | 86.8k | if (num_colors <= 32) { |
368 | | /****** WRONG FOR MULTI-PLANE HALFTONES ******/ |
369 | 0 | num_colors *= pgs->dev_ht[HT_OBJTYPE_DEFAULT]->components[0].corder.num_levels; |
370 | 0 | } |
371 | 86.8k | if (psh->head.type == 2 || psh->head.type == 3) { |
372 | 86.4k | max_error *= 0.25; |
373 | 86.4k | num_colors *= 2; |
374 | 86.4k | } |
375 | 86.8k | if (max_error < 1.0 / num_colors) |
376 | 0 | max_error = 1.0 / num_colors; |
377 | 344k | for (ci = 0; ci < pfs->num_components; ++ci) |
378 | 257k | pfs->cc_max_error[ci] = |
379 | 257k | (ranges == 0 ? max_error : |
380 | 257k | max_error * (ranges[ci].rmax - ranges[ci].rmin)); |
381 | 86.8k | if (pgs->has_transparency && pgs->trans_device != NULL) { |
382 | 6.32k | pfs->trans_device = pgs->trans_device; |
383 | 80.4k | } else { |
384 | 80.4k | pfs->trans_device = dev; |
385 | 80.4k | } |
386 | | /* If the CS is PS based and we have not yet converted to the ICC form |
387 | | then go ahead and do that now */ |
388 | 86.8k | if (gs_color_space_is_PSCIE(pcs) && pcs->icc_equivalent == NULL) { |
389 | 0 | code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &(is_lab), pgs->memory); |
390 | 0 | if (code < 0) |
391 | 0 | return code; |
392 | 0 | } |
393 | 86.8k | rendering_params.black_point_comp = pgs->blackptcomp; |
394 | 86.8k | rendering_params.graphics_type_tag = GS_VECTOR_TAG; |
395 | 86.8k | rendering_params.override_icc = false; |
396 | 86.8k | rendering_params.preserve_black = gsBKPRESNOTSPECIFIED; |
397 | 86.8k | rendering_params.rendering_intent = pgs->renderingintent; |
398 | 86.8k | rendering_params.cmm = gsCMM_DEFAULT; |
399 | | /* Grab the icc link transform that we need now */ |
400 | 86.8k | if (pcs->cmm_icc_profile_data != NULL) { |
401 | 84.8k | pfs->icclink = gsicc_get_link(pgs, pgs->trans_device, pcs, NULL, |
402 | 84.8k | &rendering_params, pgs->memory); |
403 | 84.8k | if (pfs->icclink == NULL) |
404 | 4 | return_error(gs_error_VMerror); |
405 | 84.8k | } else { |
406 | 1.91k | if (pcs->icc_equivalent != NULL ) { |
407 | | /* We have a PS equivalent ICC profile. We may need to go |
408 | | through special range adjustments in this case */ |
409 | 0 | pfs->icclink = gsicc_get_link(pgs, pgs->trans_device, |
410 | 0 | pcs->icc_equivalent, NULL, |
411 | 0 | &rendering_params, pgs->memory); |
412 | 0 | if (pfs->icclink == NULL) |
413 | 0 | return_error(gs_error_VMerror); |
414 | 1.91k | } else { |
415 | 1.91k | pfs->icclink = NULL; |
416 | 1.91k | } |
417 | 1.91k | } |
418 | | /* Two possible cases of interest here for performance. One is that the |
419 | | * icclink is NULL, which could occur if the source space were DeviceN or |
420 | | * a separation color space, while at the same time, the output device |
421 | | * supports these colorants (e.g. a separation device). The other case is |
422 | | * that the icclink is the identity. This could happen for example if the |
423 | | * source space were CMYK and we are going out to a CMYK device. For both |
424 | | * of these cases we can avoid going through the standard |
425 | | * color mappings to determine linearity. This is true, provided that the |
426 | | * transfer function is linear. It is likely that we can improve |
427 | | * things even in cases where the transfer function is nonlinear, but for |
428 | | * now, we will punt on those and let them go through the longer processing |
429 | | * steps */ |
430 | 86.8k | if (pfs->icclink == NULL) |
431 | 1.91k | cs_lin_test = !(using_alt_color_space((gs_gstate*)pgs)); |
432 | 84.8k | else |
433 | 84.8k | cs_lin_test = pfs->icclink->is_identity; |
434 | | |
435 | 86.8k | if (cs_lin_test && !gx_has_transfer(pgs, dev->color_info.num_components)) { |
436 | 86.2k | pfs->cs_always_linear = true; |
437 | 86.2k | } |
438 | | |
439 | | #ifdef IGNORE_SPEC_MATCH_ADOBE_SHADINGS |
440 | | /* Per the spec. If the source space is DeviceN or Separation and the |
441 | | colorants are not supported (i.e. if we are using the alternate tint |
442 | | transform) the interpolation should occur in the source space to |
443 | | accommodate non-linear tint transform functions. |
444 | | e.g. We had a case where the transform function |
445 | | was an increasing staircase. Including that function in the |
446 | | gradient smoothness calculation gave us severe quantization. AR on |
447 | | the other hand is doing the interpolation in device color space |
448 | | and has a smooth result for that case. So AR is not following the spec. The |
449 | | bit below solves the issues for Type 4 and Type 5 shadings as |
450 | | this will avoid interpolations in source space. Type 6 and Type 7 will still |
451 | | have interpolations in the source space even if pfs->cs_always_linear == true. |
452 | | So the approach below does not solve those issues. To do that |
453 | | without changing the shading code, we could make a linear |
454 | | approximation to the alternate tint transform, which would |
455 | | ensure smoothness like what AR provides. |
456 | | */ |
457 | | if ((gs_color_space_get_index(pcs) == gs_color_space_index_DeviceN || |
458 | | gs_color_space_get_index(pcs) == gs_color_space_index_Separation) && |
459 | | using_alt_color_space((gs_gstate*)pgs) && (psh->head.type == 4 || |
460 | | psh->head.type == 5)) { |
461 | | pfs->cs_always_linear = true; |
462 | | } |
463 | | #endif |
464 | | |
465 | 86.8k | return 0; |
466 | 86.8k | } |
467 | | |
468 | | /* Fill one piece of a shading. */ |
469 | | int |
470 | | shade_fill_path(const shading_fill_state_t * pfs, gx_path * ppath, |
471 | | gx_device_color * pdevc, const gs_fixed_point *fill_adjust) |
472 | 0 | { |
473 | 0 | gx_fill_params params; |
474 | |
|
475 | 0 | params.rule = -1; /* irrelevant */ |
476 | 0 | params.adjust = *fill_adjust; |
477 | 0 | params.flatness = 0; /* irrelevant */ |
478 | 0 | return (*dev_proc(pfs->dev, fill_path)) (pfs->dev, pfs->pgs, ppath, |
479 | 0 | ¶ms, pdevc, NULL); |
480 | 0 | } |