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 | | /* CIE color rendering operators */ |
18 | | #include "math_.h" |
19 | | #include "ghost.h" |
20 | | #include "oper.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gscspace.h" |
23 | | #include "gscolor2.h" |
24 | | #include "gscrd.h" |
25 | | #include "gscrdp.h" |
26 | | #include "estack.h" |
27 | | #include "ialloc.h" |
28 | | #include "idict.h" |
29 | | #include "idparam.h" |
30 | | #include "igstate.h" |
31 | | #include "icie.h" |
32 | | #include "iparam.h" |
33 | | #include "ivmspace.h" |
34 | | #include "store.h" /* for make_null */ |
35 | | |
36 | | /* Forward references */ |
37 | | static int zcrd1_proc_params(const gs_memory_t *mem, os_ptr op, ref_cie_render_procs * pcprocs); |
38 | | static int zcrd1_params(os_ptr op, gs_cie_render * pcrd, |
39 | | ref_cie_render_procs * pcprocs, gs_memory_t * mem); |
40 | | /* - currentcolorrendering <dict> */ |
41 | | static int |
42 | | zcurrentcolorrendering(i_ctx_t *i_ctx_p) |
43 | 162k | { |
44 | 162k | os_ptr op = osp; |
45 | | |
46 | 162k | push(1); |
47 | 162k | *op = istate->colorrendering.dict; |
48 | 162k | return 0; |
49 | 162k | } |
50 | | |
51 | | /* <dict> .buildcolorrendering1 <crd> */ |
52 | | static int |
53 | | zbuildcolorrendering1(i_ctx_t *i_ctx_p) |
54 | 162k | { |
55 | 162k | os_ptr op = osp; |
56 | 162k | gs_memory_t *mem = gs_gstate_memory(igs); |
57 | 162k | int code; |
58 | 162k | es_ptr ep = esp; |
59 | 162k | gs_cie_render *pcrd; |
60 | 162k | ref_cie_render_procs procs; |
61 | | |
62 | 162k | check_op(1); |
63 | 162k | check_read_type(*op, t_dictionary); |
64 | 162k | check_dict_read(*op); |
65 | 162k | code = gs_cie_render1_build(&pcrd, mem, ".buildcolorrendering1"); |
66 | 162k | if (code < 0) |
67 | 0 | return code; |
68 | 162k | code = zcrd1_params(op, pcrd, &procs, mem); |
69 | 162k | if (code < 0 ) { |
70 | 0 | rc_free_struct(pcrd, ".buildcolorrendering1"); |
71 | 0 | esp = ep; |
72 | 0 | return code; |
73 | 0 | } |
74 | | /****** FIX refct ******/ |
75 | | /*rc_decrement(pcrd, ".buildcolorrendering1"); *//* build sets rc = 1 */ |
76 | 162k | istate->colorrendering.dict = *op; |
77 | 162k | make_istruct_new(op, a_readonly, pcrd); |
78 | 162k | return (esp == ep ? 0 : o_push_estack); |
79 | 162k | } |
80 | | |
81 | | /* <dict> .builddevicecolorrendering1 <crd> */ |
82 | | static int |
83 | | zbuilddevicecolorrendering1(i_ctx_t *i_ctx_p) |
84 | 0 | { |
85 | 0 | os_ptr op = osp; |
86 | 0 | gs_memory_t *mem = gs_gstate_memory(igs); |
87 | 0 | dict_param_list list; |
88 | 0 | gs_cie_render *pcrd = 0; |
89 | 0 | int code; |
90 | |
|
91 | 0 | check_op(1); |
92 | 0 | check_type(*op, t_dictionary); |
93 | 0 | code = dict_param_list_read(&list, op, NULL, false, iimemory); |
94 | 0 | if (code < 0) |
95 | 0 | return code; |
96 | 0 | code = gs_cie_render1_build(&pcrd, mem, ".builddevicecolorrendering1"); |
97 | 0 | if (code >= 0) { |
98 | 0 | code = param_get_cie_render1(pcrd, (gs_param_list *) & list, |
99 | 0 | gs_currentdevice(igs)); |
100 | 0 | if (code >= 0) { |
101 | | /****** FIX refct ******/ |
102 | | /*rc_decrement(pcrd, ".builddevicecolorrendering1"); *//* build sets rc = 1 */ |
103 | 0 | } |
104 | 0 | } |
105 | 0 | iparam_list_release(&list); |
106 | 0 | if (code < 0) { |
107 | 0 | rc_free_struct(pcrd, ".builddevicecolorrendering1"); |
108 | 0 | return code; |
109 | 0 | } |
110 | 0 | istate->colorrendering.dict = *op; |
111 | 0 | make_istruct_new(op, a_readonly, pcrd); |
112 | 0 | return 0; |
113 | 0 | } |
114 | | |
115 | | /* <dict> <crd> .setcolorrendering1 - */ |
116 | | static int |
117 | | zsetcolorrendering1(i_ctx_t *i_ctx_p) |
118 | 162k | { |
119 | 162k | os_ptr op = osp; |
120 | 162k | es_ptr ep = esp; |
121 | 162k | ref_cie_render_procs procs; |
122 | 162k | int code; |
123 | | |
124 | 162k | check_op(2); |
125 | 162k | check_type(op[-1], t_dictionary); |
126 | 162k | check_stype(*op, st_cie_render1); |
127 | 162k | code = zcrd1_proc_params(imemory, op - 1, &procs); |
128 | 162k | if (code < 0) |
129 | 0 | return code; |
130 | 162k | code = gs_setcolorrendering(igs, r_ptr(op, gs_cie_render)); |
131 | 162k | if (code < 0) |
132 | 0 | return code; |
133 | 162k | if (gs_cie_cs_common(igs) != 0 && |
134 | 162k | (code = cie_cache_joint(i_ctx_p, &procs, gs_cie_cs_common(igs), igs)) < 0 |
135 | 162k | ) |
136 | 0 | return code; |
137 | 162k | istate->colorrendering.dict = op[-1]; |
138 | 162k | istate->colorrendering.procs = procs; |
139 | 162k | pop(2); |
140 | 162k | return (esp == ep ? 0 : o_push_estack); |
141 | 162k | } |
142 | | |
143 | | /* <dict> <crd> .setdevicecolorrendering1 - */ |
144 | | static int |
145 | | zsetdevicecolorrendering1(i_ctx_t *i_ctx_p) |
146 | 0 | { |
147 | 0 | os_ptr op = osp; |
148 | 0 | int code; |
149 | 0 | ref_cie_render_procs procs; |
150 | |
|
151 | 0 | check_op(2); |
152 | 0 | check_type(op[-1], t_dictionary); |
153 | 0 | check_stype(*op, st_cie_render1); |
154 | 0 | code = gs_setcolorrendering(igs, r_ptr(op, gs_cie_render)); |
155 | 0 | if (code < 0) |
156 | 0 | return code; |
157 | 0 | refset_null((ref *)&procs, sizeof(procs) / sizeof(ref)); |
158 | 0 | if (gs_cie_cs_common(igs) != 0 && |
159 | 0 | (code = cie_cache_joint(i_ctx_p, &procs, gs_cie_cs_common(igs), igs)) < 0 |
160 | 0 | ) |
161 | 0 | return code; |
162 | 0 | istate->colorrendering.dict = op[-1]; |
163 | 0 | refset_null((ref *)&istate->colorrendering.procs, |
164 | 0 | sizeof(istate->colorrendering.procs) / sizeof(ref)); |
165 | 0 | pop(2); |
166 | 0 | return 0; |
167 | 0 | } |
168 | | |
169 | | /* Get ColorRenderingType 1 procedures from the PostScript dictionary. */ |
170 | | static int |
171 | | zcrd1_proc_params(const gs_memory_t *mem, |
172 | | os_ptr op, ref_cie_render_procs * pcprocs) |
173 | 324k | { |
174 | 324k | int code; |
175 | 324k | ref *pRT; |
176 | | |
177 | 324k | code = dict_proc3_param(mem, op, "EncodeLMN", &pcprocs->EncodeLMN); |
178 | 324k | if (code < 0) |
179 | 0 | return code; |
180 | 324k | code = dict_proc3_param(mem, op, "EncodeABC", &pcprocs->EncodeABC); |
181 | 324k | if (code < 0) |
182 | 0 | return code; |
183 | 324k | code = dict_proc3_param(mem, op, "TransformPQR", &pcprocs->TransformPQR); |
184 | 324k | if (code < 0) |
185 | 0 | return code; |
186 | 324k | if (code == 1) |
187 | 0 | return gs_note_error(gs_error_undefined); |
188 | 324k | if (dict_find_string(op, "RenderTable", &pRT) > 0) { |
189 | 0 | const ref *prte; |
190 | 0 | int size; |
191 | 0 | int i; |
192 | |
|
193 | 0 | check_read_type(*pRT, t_array); |
194 | 0 | size = r_size(pRT); |
195 | 0 | if (size < 5) |
196 | 0 | return_error(gs_error_rangecheck); |
197 | 0 | prte = pRT->value.const_refs; |
198 | 0 | for (i = 5; i < size; i++) |
199 | 0 | check_proc_only(prte[i]); |
200 | 0 | make_const_array(&pcprocs->RenderTableT, a_readonly | r_space(pRT), |
201 | 0 | size - 5, prte + 5); |
202 | 0 | } else |
203 | 324k | make_null(&pcprocs->RenderTableT); |
204 | 324k | return 0; |
205 | 324k | } |
206 | | |
207 | | /* Get ColorRenderingType 1 parameters from the PostScript dictionary. */ |
208 | | static int |
209 | | zcrd1_params(os_ptr op, gs_cie_render * pcrd, |
210 | | ref_cie_render_procs * pcprocs, gs_memory_t * mem) |
211 | 162k | { |
212 | 162k | int code; |
213 | 162k | int ignore; |
214 | 162k | gx_color_lookup_table *const prtl = &pcrd->RenderTable.lookup; |
215 | 162k | ref *pRT; |
216 | | |
217 | 162k | if ((code = dict_int_param(op, "ColorRenderingType", 1, 1, 0, &ignore)) < 0) |
218 | 0 | return code; |
219 | 162k | if ((code = zcrd1_proc_params(mem, op, pcprocs)) < 0) |
220 | 0 | return code; |
221 | | |
222 | 162k | if ((code = dict_matrix3_param(mem, op, "MatrixLMN", &pcrd->MatrixLMN)) < 0) |
223 | 0 | return code; |
224 | 162k | if ((code = dict_range3_param(mem, op, "RangeLMN", &pcrd->RangeLMN)) < 0) |
225 | 0 | return code; |
226 | 162k | if ((code = dict_matrix3_param(mem, op, "MatrixABC", &pcrd->MatrixABC)) < 0) |
227 | 0 | return code; |
228 | 162k | if ((code = dict_range3_param(mem, op, "RangeABC", &pcrd->RangeABC)) < 0) |
229 | 0 | return code; |
230 | 162k | if ((code = cie_points_param(mem, op, &pcrd->points)) < 0) |
231 | 0 | return code; |
232 | 162k | if ((code = dict_matrix3_param(mem, op, "MatrixPQR", &pcrd->MatrixPQR)) < 0) |
233 | 0 | return code; |
234 | 162k | if ((code = dict_range3_param(mem,op, "RangePQR", &pcrd->RangePQR)) < 0) |
235 | 0 | return code; |
236 | | |
237 | 162k | if (dict_find_string(op, "RenderTable", &pRT) > 0) { |
238 | 0 | const ref *prte; |
239 | |
|
240 | 0 | check_read_type(*pRT, t_array); |
241 | 0 | prte = pRT->value.const_refs; |
242 | | /* Finish unpacking and checking the RenderTable parameter. */ |
243 | 0 | check_type_only(prte[4], t_integer); |
244 | 0 | if (!(prte[4].value.intval == 3 || prte[4].value.intval == 4)) |
245 | 0 | return_error(gs_error_rangecheck); |
246 | 0 | prtl->n = 3; |
247 | 0 | prtl->m = prte[4].value.intval; |
248 | 0 | if (r_size(pRT) != prtl->m + 5) |
249 | 0 | return_error(gs_error_rangecheck); |
250 | 0 | code = cie_table_param(pRT, prtl, mem); |
251 | 0 | if (code < 0) |
252 | 0 | return code; |
253 | 162k | } else { |
254 | 162k | prtl->table = 0; |
255 | 162k | } |
256 | 162k | pcrd->EncodeLMN = Encode_default; |
257 | 162k | pcrd->EncodeABC = Encode_default; |
258 | 162k | pcrd->TransformPQR = TransformPQR_default; |
259 | 162k | pcrd->RenderTable.T = RenderTableT_default; |
260 | 162k | return 0; |
261 | 162k | } |
262 | | |
263 | | /* ------ Internal procedures ------ */ |
264 | | |
265 | | /* Load the joint caches. */ |
266 | | static int |
267 | | cie_exec_tpqr(i_ctx_t *), |
268 | | cie_post_exec_tpqr(i_ctx_t *), |
269 | | cie_tpqr_finish(i_ctx_t *); |
270 | | int |
271 | | cie_cache_joint(i_ctx_t *i_ctx_p, const ref_cie_render_procs * pcrprocs, |
272 | | const gs_cie_common *pcie, gs_gstate * pgs) |
273 | 0 | { |
274 | 0 | const gs_cie_render *pcrd = gs_currentcolorrendering(pgs); |
275 | 0 | gx_cie_joint_caches *pjc = gx_unshare_cie_caches(pgs); |
276 | 0 | gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_gstate_memory(pgs); |
277 | 0 | ref pqr_procs; |
278 | 0 | uint space; |
279 | 0 | int code; |
280 | 0 | int i; |
281 | |
|
282 | 0 | if (pcrd == 0) /* cache is not set up yet */ |
283 | 0 | return 0; |
284 | 0 | if (pjc == 0) /* must already be allocated */ |
285 | 0 | return_error(gs_error_VMerror); |
286 | 0 | if (r_has_type(&pcrprocs->TransformPQR, t_null)) { |
287 | | /* |
288 | | * This CRD came from a driver, not from a PostScript dictionary. |
289 | | * Resample TransformPQR in C code. |
290 | | */ |
291 | 0 | return gs_cie_cs_complete(pgs, true); |
292 | 0 | } |
293 | 0 | gs_cie_compute_points_sd(pjc, pcie, pcrd); |
294 | | /* We use global memory for this array because it's going to be pushed onto |
295 | | the exec stack, if it triggers an error and a restore happens, we don't want |
296 | | a dangling stack reference to a restored away object. |
297 | | */ |
298 | 0 | code = gs_alloc_ref_array(iimemory_global, &pqr_procs, a_readonly, 3 * (1 + 4 + 4 * 6), |
299 | 0 | "cie_cache_common"); |
300 | 0 | if (code < 0) |
301 | 0 | return code; |
302 | | /* When we're done, deallocate the procs and complete the caches. */ |
303 | 0 | check_estack(3); |
304 | 0 | code = cie_cache_push_finish(i_ctx_p, cie_tpqr_finish, imem, pgs); |
305 | 0 | if (code < 0) |
306 | 0 | return code; |
307 | | |
308 | 0 | *++esp = pqr_procs; |
309 | 0 | space = r_space(&pqr_procs); |
310 | 0 | for (i = 0; i < 3; i++) { |
311 | 0 | ref *p = pqr_procs.value.refs + 3 + (4 + 4 * 6) * i; |
312 | 0 | const float *ppt = (float *)&pjc->points_sd; |
313 | 0 | int j; |
314 | |
|
315 | 0 | make_array(pqr_procs.value.refs + i, a_readonly | a_executable | space, |
316 | 0 | 4, p); |
317 | 0 | make_array(p, a_readonly | space, 4 * 6, p + 4); |
318 | 0 | p[1] = pcrprocs->TransformPQR.value.refs[i]; |
319 | 0 | make_oper(p + 2, 0, cie_exec_tpqr); |
320 | 0 | make_oper(p + 3, 0, cie_post_exec_tpqr); |
321 | 0 | for (j = 0, p += 4; j < 4 * 6; j++, p++, ppt++) |
322 | 0 | make_real(p, *ppt); |
323 | 0 | } |
324 | 0 | return cie_prepare_cache3(i_ctx_p, &pcrd->RangePQR, |
325 | 0 | pqr_procs.value.const_refs, |
326 | 0 | pjc->TransformPQR.caches, |
327 | 0 | pjc, imem, "Transform.PQR"); |
328 | 0 | } |
329 | | |
330 | | /* Private operator to shuffle arguments for the TransformPQR procedure: */ |
331 | | /* v [ws wd bs bd] proc -> -mark- ws wd bs bd v proc + exec */ |
332 | | static int |
333 | | cie_exec_tpqr(i_ctx_t *i_ctx_p) |
334 | 0 | { |
335 | 0 | os_ptr op = osp; |
336 | 0 | const ref *ppt = op[-1].value.const_refs; |
337 | 0 | uint space = r_space(op - 1); |
338 | 0 | int i; |
339 | |
|
340 | 0 | check_op(3); |
341 | 0 | push(4); |
342 | 0 | *op = op[-4]; /* proc */ |
343 | 0 | op[-1] = op[-6]; /* v */ |
344 | 0 | for (i = 0; i < 4; i++) |
345 | 0 | make_const_array(op - 5 + i, a_readonly | space, |
346 | 0 | 6, ppt + i * 6); |
347 | 0 | make_mark(op - 6); |
348 | 0 | return zexec(i_ctx_p); |
349 | 0 | } |
350 | | |
351 | | /* Remove extraneous values from the stack after executing */ |
352 | | /* the TransformPQR procedure. -mark- ... v -> v */ |
353 | | static int |
354 | | cie_post_exec_tpqr(i_ctx_t *i_ctx_p) |
355 | 0 | { |
356 | 0 | os_ptr op = osp; |
357 | 0 | uint count = ref_stack_counttomark(&o_stack); |
358 | 0 | ref vref; |
359 | |
|
360 | 0 | if (count < 2) |
361 | 0 | return_error(gs_error_unmatchedmark); |
362 | 0 | vref = *op; |
363 | 0 | ref_stack_pop(&o_stack, count - 1); |
364 | 0 | *osp = vref; |
365 | 0 | return 0; |
366 | 0 | } |
367 | | |
368 | | /* Free the procs array and complete the joint caches. */ |
369 | | static int |
370 | | cie_tpqr_finish(i_ctx_t *i_ctx_p) |
371 | 0 | { |
372 | 0 | os_ptr op = osp; |
373 | 0 | gs_gstate *pgs = r_ptr(op, gs_gstate); |
374 | 0 | gs_cie_render *pcrd = |
375 | 0 | (gs_cie_render *)gs_currentcolorrendering(pgs); /* break const */ |
376 | 0 | int code; |
377 | |
|
378 | 0 | ifree_ref_array(op - 1, "cie_tpqr_finish"); |
379 | 0 | pcrd->TransformPQR = TransformPQR_from_cache; |
380 | 0 | code = gs_cie_cs_complete(pgs, false); |
381 | 0 | pop(2); |
382 | 0 | return code; |
383 | 0 | } |
384 | | |
385 | | /* Ws Bs Wd Bd Ps .transformPQR_scale_wb[012] Pd |
386 | | |
387 | | The default TransformPQR procedure is implemented in C, rather than |
388 | | PostScript, as a speed optimization. |
389 | | |
390 | | This TransformPQR implements a relative colorimetric intent by scaling |
391 | | the XYZ values relative to the white and black points. |
392 | | */ |
393 | | static int |
394 | | ztpqr_scale_wb_common(i_ctx_t *i_ctx_p, int idx) |
395 | 0 | { |
396 | 0 | os_ptr op = osp; |
397 | 0 | double a[4], Ps; /* a[0] = ws, a[1] = bs, a[2] = wd, a[3] = bd */ |
398 | 0 | double result; |
399 | 0 | int code; |
400 | 0 | int i; |
401 | |
|
402 | 0 | check_op(4); |
403 | 0 | code = real_param(op, &Ps); |
404 | 0 | if (code < 0) return code; |
405 | | |
406 | 0 | for (i = 0; i < 4; i++) { |
407 | 0 | ref tmp; |
408 | |
|
409 | 0 | code = array_get(imemory, op - 4 + i, idx, &tmp); |
410 | 0 | if (code >= 0) |
411 | 0 | code = real_param(&tmp, &a[i]); |
412 | 0 | if (code < 0) return code; |
413 | 0 | } |
414 | | |
415 | 0 | if (a[0] == a[1]) |
416 | 0 | return_error(gs_error_undefinedresult); |
417 | 0 | result = a[3] + (a[2] - a[3]) * (Ps - a[1]) / (a[0] - a[1]); |
418 | 0 | make_real(op - 4, result); |
419 | 0 | pop(4); |
420 | 0 | return 0; |
421 | 0 | } |
422 | | |
423 | | /* Ws Bs Wd Bd Ps .TransformPQR_scale_wb0 Pd */ |
424 | | static int |
425 | | ztpqr_scale_wb0(i_ctx_t *i_ctx_p) |
426 | 0 | { |
427 | 0 | return ztpqr_scale_wb_common(i_ctx_p, 3); |
428 | 0 | } |
429 | | |
430 | | /* Ws Bs Wd Bd Ps .TransformPQR_scale_wb2 Pd */ |
431 | | static int |
432 | | ztpqr_scale_wb1(i_ctx_t *i_ctx_p) |
433 | 0 | { |
434 | 0 | return ztpqr_scale_wb_common(i_ctx_p, 4); |
435 | 0 | } |
436 | | |
437 | | /* Ws Bs Wd Bd Ps .TransformPQR_scale_wb2 Pd */ |
438 | | static int |
439 | | ztpqr_scale_wb2(i_ctx_t *i_ctx_p) |
440 | 0 | { |
441 | 0 | return ztpqr_scale_wb_common(i_ctx_p, 5); |
442 | 0 | } |
443 | | |
444 | | /* ------ Initialization procedure ------ */ |
445 | | |
446 | | const op_def zcrd_l2_op_defs[] = |
447 | | { |
448 | | op_def_begin_level2(), |
449 | | {"0currentcolorrendering", zcurrentcolorrendering}, |
450 | | {"2.setcolorrendering1", zsetcolorrendering1}, |
451 | | {"2.setdevicecolorrendering1", zsetdevicecolorrendering1}, |
452 | | {"1.buildcolorrendering1", zbuildcolorrendering1}, |
453 | | {"1.builddevicecolorrendering1", zbuilddevicecolorrendering1}, |
454 | | /* Internal "operators" */ |
455 | | {"3%cie_exec_tpqr", cie_exec_tpqr}, |
456 | | {"2%cie_post_exec_tpqr", cie_post_exec_tpqr}, |
457 | | {"1%cie_tpqr_finish", cie_tpqr_finish}, |
458 | | {"5.TransformPQR_scale_WB0", ztpqr_scale_wb0}, |
459 | | {"5.TransformPQR_scale_WB1", ztpqr_scale_wb1}, |
460 | | {"5.TransformPQR_scale_WB2", ztpqr_scale_wb2}, |
461 | | op_def_end(0) |
462 | | }; |