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