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