xref: /plan9/sys/src/cmd/gs/src/gscrdp.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 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: gscrdp.c,v 1.6 2002/10/08 00:49:49 dan Exp $ */
18 /* CIE color rendering dictionary creation */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "string_.h"
22 #include "gx.h"
23 #include "gsdevice.h"
24 #include "gserrors.h"
25 #include "gsmatrix.h"		/* for gscolor2.h */
26 #include "gsstruct.h"
27 #include "gxcspace.h"
28 #include "gscolor2.h"		/* for gs_set/currentcolorrendering */
29 #include "gscrdp.h"
30 #include "gxarith.h"
31 
32 /* ---------------- Writing ---------------- */
33 
34 /* Internal procedures for writing parameter values. */
35 private void
store_vector3(float * p,const gs_vector3 * pvec)36 store_vector3(float *p, const gs_vector3 * pvec)
37 {
38     p[0] = pvec->u, p[1] = pvec->v, p[2] = pvec->w;
39 }
40 private int
write_floats(gs_param_list * plist,gs_param_name key,const float * values,int size,gs_memory_t * mem)41 write_floats(gs_param_list * plist, gs_param_name key,
42 	     const float *values, int size, gs_memory_t * mem)
43 {
44     float *p = (float *)
45 	gs_alloc_byte_array(mem, size, sizeof(float), "write_floats");
46     gs_param_float_array fa;
47 
48     if (p == 0)
49 	return_error(gs_error_VMerror);
50     memcpy(p, values, size * sizeof(float));
51 
52     fa.data = p;
53     fa.size = size;
54     fa.persistent = true;
55     return param_write_float_array(plist, key, &fa);
56 }
57 private int
write_vector3(gs_param_list * plist,gs_param_name key,const gs_vector3 * pvec,gs_memory_t * mem)58 write_vector3(gs_param_list * plist, gs_param_name key,
59 	      const gs_vector3 * pvec, gs_memory_t * mem)
60 {
61     float values[3];
62 
63     store_vector3(values, pvec);
64     return write_floats(plist, key, values, 3, mem);
65 }
66 private int
write_matrix3(gs_param_list * plist,gs_param_name key,const gs_matrix3 * pmat,gs_memory_t * mem)67 write_matrix3(gs_param_list * plist, gs_param_name key,
68 	      const gs_matrix3 * pmat, gs_memory_t * mem)
69 {
70     float values[9];
71 
72     if (!memcmp(pmat, &Matrix3_default, sizeof(*pmat)))
73 	return 0;
74     store_vector3(values, &pmat->cu);
75     store_vector3(values + 3, &pmat->cv);
76     store_vector3(values + 6, &pmat->cw);
77     return write_floats(plist, key, values, 9, mem);
78 }
79 private int
write_range3(gs_param_list * plist,gs_param_name key,const gs_range3 * prange,gs_memory_t * mem)80 write_range3(gs_param_list * plist, gs_param_name key,
81 	     const gs_range3 * prange, gs_memory_t * mem)
82 {
83     float values[6];
84 
85     if (!memcmp(prange, &Range3_default, sizeof(*prange)))
86 	return 0;
87     values[0] = prange->ranges[0].rmin, values[1] = prange->ranges[0].rmax;
88     values[2] = prange->ranges[1].rmin, values[3] = prange->ranges[1].rmax;
89     values[4] = prange->ranges[2].rmin, values[5] = prange->ranges[2].rmax;
90     return write_floats(plist, key, values, 6, mem);
91 }
92 private int
write_proc3(gs_param_list * plist,gs_param_name key,const gs_cie_render * pcrd,const gs_cie_render_proc3 * procs,const gs_range3 * domain,gs_memory_t * mem)93 write_proc3(gs_param_list * plist, gs_param_name key,
94 	    const gs_cie_render * pcrd, const gs_cie_render_proc3 * procs,
95 	    const gs_range3 * domain, gs_memory_t * mem)
96 {
97     float *values;
98     uint size = gx_cie_cache_size;
99     gs_param_float_array fa;
100     int i;
101 
102     if (!memcmp(procs, &Encode_default, sizeof(*procs)))
103 	return 0;
104     values = (float *)gs_alloc_byte_array(mem, size * 3, sizeof(float),
105 					  "write_proc3");
106 
107     if (values == 0)
108 	return_error(gs_error_VMerror);
109     for (i = 0; i < 3; ++i) {
110 	double base = domain->ranges[i].rmin;
111 	double scale = (domain->ranges[i].rmax - base) / (size - 1);
112 	int j;
113 
114 	for (j = 0; j < size; ++j)
115 	    values[i * size + j] =
116 		(*procs->procs[i]) (j * scale + base, pcrd);
117     }
118     fa.data = values;
119     fa.size = size * 3;
120     fa.persistent = true;
121     return param_write_float_array(plist, key, &fa);
122 }
123 
124 /* Write a CRD as a device parameter. */
125 int
param_write_cie_render1(gs_param_list * plist,gs_param_name key,gs_cie_render * pcrd,gs_memory_t * mem)126 param_write_cie_render1(gs_param_list * plist, gs_param_name key,
127 			gs_cie_render * pcrd, gs_memory_t * mem)
128 {
129     gs_param_dict dict;
130     int code, dcode;
131 
132     dict.size = 20;
133     if ((code = param_begin_write_dict(plist, key, &dict, false)) < 0)
134 	return code;
135     code = param_put_cie_render1(dict.list, pcrd, mem);
136     dcode = param_end_write_dict(plist, key, &dict);
137     return (code < 0 ? code : dcode);
138 }
139 
140 /* Write a CRD directly to a parameter list. */
141 int
param_put_cie_render1(gs_param_list * plist,gs_cie_render * pcrd,gs_memory_t * mem)142 param_put_cie_render1(gs_param_list * plist, gs_cie_render * pcrd,
143 		      gs_memory_t * mem)
144 {
145     int crd_type = GX_DEVICE_CRD1_TYPE;
146     int code = gs_cie_render_sample(pcrd); /* we need RenderTableT_is_id' */
147 
148     if (code < 0)
149 	return code;
150     if (pcrd->TransformPQR.proc_name) {
151 	gs_param_string pn, pd;
152 
153 	param_string_from_string(pn, pcrd->TransformPQR.proc_name);
154 	pn.size++;		/* include terminating null */
155 	pd.data = pcrd->TransformPQR.proc_data.data;
156 	pd.size = pcrd->TransformPQR.proc_data.size;
157 	pd.persistent = true;  /****** WRONG ******/
158 	if ((code = param_write_name(plist, "TransformPQRName", &pn)) < 0 ||
159 	    (code = param_write_string(plist, "TransformPQRData", &pd)) < 0
160 	    )
161 	    return code;
162     }
163     else if (pcrd->TransformPQR.proc != TransformPQR_default.proc) {
164 	/* We have no way to represent the procedure, so return an error. */
165 	return_error(gs_error_rangecheck);
166     }
167     if ((code = param_write_int(plist, "ColorRenderingType", &crd_type)) < 0 ||
168 	(code = write_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint, mem)) < 0
169 	)
170 	return code;
171     if (memcmp(&pcrd->points.BlackPoint, &BlackPoint_default,
172 	       sizeof(pcrd->points.BlackPoint))) {
173 	if ((code = write_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint, mem)) < 0)
174 	    return code;
175     }
176     if ((code = write_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR, mem)) < 0 ||
177 	(code = write_range3(plist, "RangePQR", &pcrd->RangePQR, mem)) < 0 ||
178     /* TransformPQR is handled separately */
179     (code = write_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN, mem)) < 0 ||
180 	(code = write_proc3(plist, "EncodeLMNValues", pcrd,
181 			    &pcrd->EncodeLMN, &pcrd->DomainLMN, mem)) < 0 ||
182 	(code = write_range3(plist, "RangeLMN", &pcrd->RangeLMN, mem)) < 0 ||
183     (code = write_matrix3(plist, "MatrixABC", &pcrd->MatrixABC, mem)) < 0 ||
184 	(code = write_proc3(plist, "EncodeABCValues", pcrd,
185 			    &pcrd->EncodeABC, &pcrd->DomainABC, mem)) < 0 ||
186 	(code = write_range3(plist, "RangeABC", &pcrd->RangeABC, mem)) < 0
187 	)
188 	return code;
189     if (pcrd->RenderTable.lookup.table) {
190 	int n = pcrd->RenderTable.lookup.n;
191 	int m = pcrd->RenderTable.lookup.m;
192 	int na = pcrd->RenderTable.lookup.dims[0];
193 	int *size = (int *)
194 	    gs_alloc_byte_array(mem, n + 1, sizeof(int), "RenderTableSize");
195 
196 	/*
197 	 * In principle, we should use gs_alloc_struct_array with a
198 	 * type descriptor for gs_param_string.  However, it is widely
199 	 * assumed that parameter lists are transient, and don't require
200 	 * accurate GC information; so we can get away with allocating
201 	 * the string table as bytes.
202 	 */
203 	gs_param_string *table =
204 	    (gs_param_string *)
205 	    gs_alloc_byte_array(mem, na, sizeof(gs_param_string),
206 				"RenderTableTable");
207 	gs_param_int_array ia;
208 
209 	if (size == 0 || table == 0)
210 	    code = gs_note_error(gs_error_VMerror);
211 	else {
212 	    memcpy(size, pcrd->RenderTable.lookup.dims, sizeof(int) * n);
213 
214 	    size[n] = m;
215 	    ia.data = size;
216 	    ia.size = n + 1;
217 	    ia.persistent = true;
218 	    code = param_write_int_array(plist, "RenderTableSize", &ia);
219 	}
220 	if (code >= 0) {
221 	    gs_param_string_array sa;
222 	    int a;
223 
224 	    for (a = 0; a < na; ++a)
225 		table[a].data = pcrd->RenderTable.lookup.table[a].data,
226 		    table[a].size = pcrd->RenderTable.lookup.table[a].size,
227 		    table[a].persistent = true;
228 	    sa.data = table;
229 	    sa.size = na;
230 	    sa.persistent = true;
231 	    code = param_write_string_array(plist, "RenderTableTable", &sa);
232 	    if (code >= 0 && !pcrd->caches.RenderTableT_is_identity) {
233 		/****** WRITE RenderTableTValues LIKE write_proc3 ******/
234 		uint size = gx_cie_cache_size;
235 		float *values =
236 		    (float *)gs_alloc_byte_array(mem, size * m,
237 						 sizeof(float),
238 						 "write_proc3");
239 		gs_param_float_array fa;
240 		int i;
241 
242 		if (values == 0)
243 		    return_error(gs_error_VMerror);
244 		for (i = 0; i < m; ++i) {
245 		    double scale = 255.0 / (size - 1);
246 		    int j;
247 
248 		    for (j = 0; j < size; ++j)
249 			values[i * size + j] =
250 			    frac2float((*pcrd->RenderTable.T.procs[i])
251 				       ((byte)(j * scale), pcrd));
252 		}
253 		fa.data = values;
254 		fa.size = size * m;
255 		fa.persistent = true;
256 		code = param_write_float_array(plist, "RenderTableTValues",
257 					       &fa);
258 	    }
259 	}
260 	if (code < 0) {
261 	    gs_free_object(mem, table, "RenderTableTable");
262 	    gs_free_object(mem, size, "RenderTableSize");
263 	    return code;
264 	}
265     }
266     return code;
267 }
268 
269 /* ---------------- Reading ---------------- */
270 
271 /* Internal procedures for reading parameter values. */
272 private void
load_vector3(gs_vector3 * pvec,const float * p)273 load_vector3(gs_vector3 * pvec, const float *p)
274 {
275     pvec->u = p[0], pvec->v = p[1], pvec->w = p[2];
276 }
277 private int
read_floats(gs_param_list * plist,gs_param_name key,float * values,int count)278 read_floats(gs_param_list * plist, gs_param_name key, float *values, int count)
279 {
280     gs_param_float_array fa;
281     int code = param_read_float_array(plist, key, &fa);
282 
283     if (code)
284 	return code;
285     if (fa.size != count)
286 	return_error(gs_error_rangecheck);
287     memcpy(values, fa.data, sizeof(float) * count);
288 
289     return 0;
290 }
291 private int
read_vector3(gs_param_list * plist,gs_param_name key,gs_vector3 * pvec,const gs_vector3 * dflt)292 read_vector3(gs_param_list * plist, gs_param_name key,
293 	     gs_vector3 * pvec, const gs_vector3 * dflt)
294 {
295     float values[3];
296     int code = read_floats(plist, key, values, 3);
297 
298     switch (code) {
299 	case 1:		/* not defined */
300 	    if (dflt)
301 		*pvec = *dflt;
302 	    break;
303 	case 0:
304 	    load_vector3(pvec, values);
305 	default:		/* error */
306 	    break;
307     }
308     return code;
309 }
310 private int
read_matrix3(gs_param_list * plist,gs_param_name key,gs_matrix3 * pmat)311 read_matrix3(gs_param_list * plist, gs_param_name key, gs_matrix3 * pmat)
312 {
313     float values[9];
314     int code = read_floats(plist, key, values, 9);
315 
316     switch (code) {
317 	case 1:		/* not defined */
318 	    *pmat = Matrix3_default;
319 	    break;
320 	case 0:
321 	    load_vector3(&pmat->cu, values);
322 	    load_vector3(&pmat->cv, values + 3);
323 	    load_vector3(&pmat->cw, values + 6);
324 	default:		/* error */
325 	    break;
326     }
327     return code;
328 }
329 private int
read_range3(gs_param_list * plist,gs_param_name key,gs_range3 * prange)330 read_range3(gs_param_list * plist, gs_param_name key, gs_range3 * prange)
331 {
332     float values[6];
333     int code = read_floats(plist, key, values, 6);
334 
335     switch (code) {
336 	case 1:		/* not defined */
337 	    *prange = Range3_default;
338 	    break;
339 	case 0:
340 	    prange->ranges[0].rmin = values[0];
341 	    prange->ranges[0].rmax = values[1];
342 	    prange->ranges[1].rmin = values[2];
343 	    prange->ranges[1].rmax = values[3];
344 	    prange->ranges[2].rmin = values[4];
345 	    prange->ranges[2].rmax = values[5];
346 	default:		/* error */
347 	    break;
348     }
349     return code;
350 }
351 private int
read_proc3(gs_param_list * plist,gs_param_name key,float values[gx_cie_cache_size * 3])352 read_proc3(gs_param_list * plist, gs_param_name key,
353 	   float values[gx_cie_cache_size * 3])
354 {
355     return read_floats(plist, key, values, gx_cie_cache_size * 3);
356 }
357 
358 /* Read a CRD from a device parameter. */
359 int
gs_cie_render1_param_initialize(gs_cie_render * pcrd,gs_param_list * plist,gs_param_name key,gx_device * dev)360 gs_cie_render1_param_initialize(gs_cie_render * pcrd, gs_param_list * plist,
361 				gs_param_name key, gx_device * dev)
362 {
363     gs_param_dict dict;
364     int code = param_begin_read_dict(plist, key, &dict, false);
365     int dcode;
366 
367     if (code < 0)
368 	return code;
369     code = param_get_cie_render1(pcrd, dict.list, dev);
370     dcode = param_end_read_dict(plist, key, &dict);
371     if (code < 0)
372 	return code;
373     if (dcode < 0)
374 	return dcode;
375     gs_cie_render_init(pcrd);
376     gs_cie_render_sample(pcrd);
377     return gs_cie_render_complete(pcrd);
378 }
379 
380 /* Define the structure for passing Encode values as "client data". */
381 typedef struct encode_data_s {
382     float lmn[gx_cie_cache_size * 3]; /* EncodeLMN */
383     float abc[gx_cie_cache_size * 3]; /* EncodeABC */
384     float t[gx_cie_cache_size * 4]; /* RenderTable.T */
385 } encode_data_t;
386 
387 /* Define procedures that retrieve the Encode values read from the list. */
388 private float
encode_from_data(floatp v,const float values[gx_cie_cache_size],const gs_range * range)389 encode_from_data(floatp v, const float values[gx_cie_cache_size],
390 		 const gs_range * range)
391 {
392     return (v <= range->rmin ? values[0] :
393 	    v >= range->rmax ? values[gx_cie_cache_size - 1] :
394 	    values[(int)((v - range->rmin) / (range->rmax - range->rmin) *
395 			 (gx_cie_cache_size - 1) + 0.5)]);
396 }
397 /*
398  * The repetitive boilerplate in the next 10 procedures really sticks in
399  * my craw, but I've got a mandate not to use macros....
400  */
401 private float
encode_lmn_0_from_data(floatp v,const gs_cie_render * pcrd)402 encode_lmn_0_from_data(floatp v, const gs_cie_render * pcrd)
403 {
404     const encode_data_t *data = pcrd->client_data;
405 
406     return encode_from_data(v, &data->lmn[0],
407 			    &pcrd->DomainLMN.ranges[0]);
408 }
409 private float
encode_lmn_1_from_data(floatp v,const gs_cie_render * pcrd)410 encode_lmn_1_from_data(floatp v, const gs_cie_render * pcrd)
411 {
412     const encode_data_t *data = pcrd->client_data;
413 
414     return encode_from_data(v, &data->lmn[gx_cie_cache_size],
415 			    &pcrd->DomainLMN.ranges[1]);
416 }
417 private float
encode_lmn_2_from_data(floatp v,const gs_cie_render * pcrd)418 encode_lmn_2_from_data(floatp v, const gs_cie_render * pcrd)
419 {
420     const encode_data_t *data = pcrd->client_data;
421 
422     return encode_from_data(v, &data->lmn[gx_cie_cache_size * 2],
423 			    &pcrd->DomainLMN.ranges[2]);
424 }
425 private float
encode_abc_0_from_data(floatp v,const gs_cie_render * pcrd)426 encode_abc_0_from_data(floatp v, const gs_cie_render * pcrd)
427 {
428     const encode_data_t *data = pcrd->client_data;
429 
430     return encode_from_data(v, &data->abc[0],
431 			    &pcrd->DomainABC.ranges[0]);
432 }
433 private float
encode_abc_1_from_data(floatp v,const gs_cie_render * pcrd)434 encode_abc_1_from_data(floatp v, const gs_cie_render * pcrd)
435 {
436     const encode_data_t *data = pcrd->client_data;
437 
438     return encode_from_data(v, &data->abc[gx_cie_cache_size],
439 			    &pcrd->DomainABC.ranges[1]);
440 }
441 private float
encode_abc_2_from_data(floatp v,const gs_cie_render * pcrd)442 encode_abc_2_from_data(floatp v, const gs_cie_render * pcrd)
443 {
444     const encode_data_t *data = pcrd->client_data;
445 
446     return encode_from_data(v, &data->abc[gx_cie_cache_size * 2],
447 			    &pcrd->DomainABC.ranges[2]);
448 }
449 private frac
render_table_t_0_from_data(byte v,const gs_cie_render * pcrd)450 render_table_t_0_from_data(byte v, const gs_cie_render * pcrd)
451 {
452     const encode_data_t *data = pcrd->client_data;
453 
454     return float2frac(encode_from_data(v / 255.0,
455 				       &data->t[0],
456 				       &Range3_default.ranges[0]));
457 }
458 private frac
render_table_t_1_from_data(byte v,const gs_cie_render * pcrd)459 render_table_t_1_from_data(byte v, const gs_cie_render * pcrd)
460 {
461     const encode_data_t *data = pcrd->client_data;
462 
463     return float2frac(encode_from_data(v / 255.0,
464 				       &data->t[gx_cie_cache_size],
465 				       &Range3_default.ranges[0]));
466 }
467 private frac
render_table_t_2_from_data(byte v,const gs_cie_render * pcrd)468 render_table_t_2_from_data(byte v, const gs_cie_render * pcrd)
469 {
470     const encode_data_t *data = pcrd->client_data;
471 
472     return float2frac(encode_from_data(v / 255.0,
473 				       &data->t[gx_cie_cache_size * 2],
474 				       &Range3_default.ranges[0]));
475 }
476 private frac
render_table_t_3_from_data(byte v,const gs_cie_render * pcrd)477 render_table_t_3_from_data(byte v, const gs_cie_render * pcrd)
478 {
479     const encode_data_t *data = pcrd->client_data;
480 
481     return float2frac(encode_from_data(v / 255.0,
482 				       &data->t[gx_cie_cache_size * 3],
483 				       &Range3_default.ranges[0]));
484 }
485 private const gs_cie_render_proc3 EncodeLMN_from_data = {
486     {encode_lmn_0_from_data, encode_lmn_1_from_data, encode_lmn_2_from_data}
487 };
488 private const gs_cie_render_proc3 EncodeABC_from_data = {
489     {encode_abc_0_from_data, encode_abc_1_from_data, encode_abc_2_from_data}
490 };
491 private const gs_cie_render_table_procs RenderTableT_from_data = {
492     {render_table_t_0_from_data, render_table_t_1_from_data,
493      render_table_t_2_from_data, render_table_t_3_from_data
494     }
495 };
496 
497 /* Read a CRD directly from a parameter list. */
498 int
param_get_cie_render1(gs_cie_render * pcrd,gs_param_list * plist,gx_device * dev)499 param_get_cie_render1(gs_cie_render * pcrd, gs_param_list * plist,
500 		      gx_device * dev)
501 {
502     encode_data_t data;
503     gs_param_int_array rt_size;
504     int crd_type;
505     int code, code_lmn, code_abc, code_rt, code_t;
506     gs_param_string pname, pdata;
507 
508     /* Reset the status to invalidate cached information. */
509     pcrd->status = CIE_RENDER_STATUS_BUILT;
510     if ((code = param_read_int(plist, "ColorRenderingType", &crd_type)) < 0 ||
511 	crd_type != GX_DEVICE_CRD1_TYPE ||
512 	(code = read_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint,
513 			     NULL)) < 0 ||
514 	(code = read_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint,
515 			     &BlackPoint_default)) < 0 ||
516 	(code = read_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR)) < 0 ||
517 	(code = read_range3(plist, "RangePQR", &pcrd->RangePQR)) < 0 ||
518 	/* TransformPQR is handled specially below. */
519 	(code = read_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN)) < 0 ||
520 	(code_lmn = code =
521 	 read_proc3(plist, "EncodeLMNValues", data.lmn)) < 0 ||
522 	(code = read_range3(plist, "RangeLMN", &pcrd->RangeLMN)) < 0 ||
523 	(code = read_matrix3(plist, "MatrixABC", &pcrd->MatrixABC)) < 0 ||
524 	(code_abc = code =
525 	 read_proc3(plist, "EncodeABCValues", data.abc)) < 0 ||
526 	(code = read_range3(plist, "RangeABC", &pcrd->RangeABC)) < 0
527 	)
528 	return code;
529     /* Handle the sampled functions. */
530     switch (code = param_read_string(plist, "TransformPQRName", &pname)) {
531 	default:		/* error */
532 	    return code;
533 	case 1:			/* missing */
534 	    pcrd->TransformPQR = TransformPQR_default;
535 	    break;
536 	case 0:			/* specified */
537 	    /* The procedure name must be null-terminated: */
538 	    /* see param_put_cie_render1 above. */
539 	    if (pname.size < 1 || pname.data[pname.size - 1] != 0)
540 		return_error(gs_error_rangecheck);
541 	    pcrd->TransformPQR.proc = TransformPQR_lookup_proc_name;
542 	    pcrd->TransformPQR.proc_name = (const char *)pname.data;
543 	    switch (code = param_read_string(plist, "TransformPQRData", &pdata)) {
544 		default:	/* error */
545 		    return code;
546 		case 1:		/* missing */
547 		    pcrd->TransformPQR.proc_data.data = 0;
548 		    pcrd->TransformPQR.proc_data.size = 0;
549 		    break;
550 		case 0:
551 		    pcrd->TransformPQR.proc_data.data = pdata.data;
552 		    pcrd->TransformPQR.proc_data.size = pdata.size;
553 	    }
554 	    pcrd->TransformPQR.driver_name = gs_devicename(dev);
555 	    break;
556     }
557     pcrd->client_data = &data;
558     if (code_lmn > 0)
559 	pcrd->EncodeLMN = Encode_default;
560     else
561 	pcrd->EncodeLMN = EncodeLMN_from_data;
562     if (code_abc > 0)
563 	pcrd->EncodeABC = Encode_default;
564     else
565 	pcrd->EncodeABC = EncodeABC_from_data;
566     code_rt = code = param_read_int_array(plist, "RenderTableSize", &rt_size);
567     if (code == 1) {
568 	if (pcrd->RenderTable.lookup.table) {
569 	    gs_free_object(pcrd->rc.memory,
570 		(void *)pcrd->RenderTable.lookup.table, /* break const */
571 		"param_get_cie_render1(RenderTable)");
572 	    pcrd->RenderTable.lookup.table = 0;
573 	}
574 	pcrd->RenderTable.T = RenderTableT_default;
575 	code_t = 1;
576     } else if (code < 0)
577 	return code;
578     else if (rt_size.size != 4)
579 	return_error(gs_error_rangecheck);
580     else {
581 	gs_param_string_array rt_values;
582 	gs_const_string *table;
583 	int n, m, j;
584 
585 	for (j = 0; j < rt_size.size; ++j)
586 	    if (rt_size.data[j] < 1)
587 		return_error(gs_error_rangecheck);
588 	code = param_read_string_array(plist, "RenderTableTable", &rt_values);
589 	if (code < 0)
590 	    return code;
591 	if (code > 0 || rt_values.size != rt_size.data[0])
592 	    return_error(gs_error_rangecheck);
593 	/* Note: currently n = 3 (rt_size.size = 4) always. */
594 	for (j = 0; j < rt_values.size; ++j)
595 	    if (rt_values.data[j].size !=
596 		rt_size.data[1] * rt_size.data[2] * rt_size.data[3])
597 		return_error(gs_error_rangecheck);
598 	pcrd->RenderTable.lookup.n = n = rt_size.size - 1;
599 	pcrd->RenderTable.lookup.m = m = rt_size.data[n];
600 	if (n > 4 || m > 4)
601 	    return_error(gs_error_rangecheck);
602 	memcpy(pcrd->RenderTable.lookup.dims, rt_size.data, n * sizeof(int));
603 	table =
604 	    gs_alloc_struct_array(pcrd->rc.memory,
605 				  pcrd->RenderTable.lookup.dims[0],
606 				  gs_const_string, &st_const_string_element,
607 				  "RenderTable table");
608 	if (table == 0)
609 	    return_error(gs_error_VMerror);
610 	for (j = 0; j < pcrd->RenderTable.lookup.dims[0]; ++j) {
611 	    table[j].data = rt_values.data[j].data;
612 	    table[j].size = rt_values.data[j].size;
613 	}
614 	pcrd->RenderTable.lookup.table = table;
615 	pcrd->RenderTable.T = RenderTableT_from_data;
616 	code_t = code = read_floats(plist, "RenderTableTValues", data.t,
617 				    gx_cie_cache_size * m);
618 	if (code > 0)
619 	    pcrd->RenderTable.T = RenderTableT_default;
620 	else if (code == 0)
621 	    pcrd->RenderTable.T = RenderTableT_from_data;
622     }
623     if ((code = gs_cie_render_init(pcrd)) >= 0 &&
624 	(code = gs_cie_render_sample(pcrd)) >= 0
625 	)
626 	code = gs_cie_render_complete(pcrd);
627     /* Clean up before exiting. */
628     pcrd->client_data = 0;
629     if (code_lmn == 0)
630 	pcrd->EncodeLMN = EncodeLMN_from_cache;
631     if (code_abc == 0)
632 	pcrd->EncodeABC = EncodeABC_from_cache;
633     if (code_t == 0)
634 	pcrd->RenderTable.T = RenderTableT_from_cache;
635     return code;
636 }
637