xref: /plan9/sys/src/cmd/gs/src/zcrd.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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