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