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