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