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 "gscdefs.h"		/* for gs_lib_device_list */
23 #include "gsdevice.h"
24 #include "gserrors.h"
25 #include "gsmatrix.h"		/* for gscolor2.h */
26 #include "gsparam.h"
27 #include "gsstruct.h"
28 #include "gsutil.h"
29 #include "gxcspace.h"
30 #include "gscolor2.h"		/* for gs_set/currentcolorrendering */
31 #include "gscrd.h"
32 
33 /* Import gs_lib_device_list() */
34 extern_gs_lib_device_list();
35 
36 /* Allocator structure type */
37 public_st_cie_render1();
38 static
39 ENUM_PTRS_WITH(cie_render1_enum_ptrs, gs_cie_render *pcrd) return 0;
40 case 0: return ENUM_OBJ(pcrd->client_data);
41 case 1: return ENUM_OBJ(pcrd->RenderTable.lookup.table);
42 case 2: return (pcrd->RenderTable.lookup.table ?
43                 ENUM_CONST_STRING(&pcrd->TransformPQR.proc_data) :
44                 0);
45 ENUM_PTRS_END
46 static RELOC_PTRS_WITH(cie_render1_reloc_ptrs, gs_cie_render *pcrd);
47 RELOC_OBJ_VAR(pcrd->client_data);
48 if (pcrd->RenderTable.lookup.table)
49 {
50 RELOC_OBJ_VAR(pcrd->RenderTable.lookup.table);
51 RELOC_CONST_STRING_VAR(pcrd->TransformPQR.proc_data);
52 }
53 RELOC_PTRS_END
54 
55 /* Default CRD procedures. */
56 
57 static int
tpqr_identity(int index,floatp in,const gs_cie_wbsd * pwbsd,gs_cie_render * pcrd,float * out)58 tpqr_identity(int index, floatp in, const gs_cie_wbsd * pwbsd,
59               gs_cie_render * pcrd, float *out)
60 {
61     *out = in;
62     return 0;
63 }
64 
65 static int
tpqr_from_cache(int index,floatp in,const gs_cie_wbsd * pwbsd,gs_cie_render * pcrd,float * out)66 tpqr_from_cache(int index, floatp in, const gs_cie_wbsd * pwbsd,
67                 gs_cie_render * pcrd, float *out)
68 {
69     /*
70      * Since the TransformPQR cache is in the joint caches, not in the
71      * CRD cache, we can't actually implement this procedure.
72      * Instead, the place that calls it checks for it specially.
73      */
74     *out = in;
75     return 0;
76 }
77 
78 static float
render_identity(floatp in,const gs_cie_render * pcrd)79 render_identity(floatp in, const gs_cie_render * pcrd)
80 {
81     return in;
82 }
83 static frac
render_table_identity(byte in,const gs_cie_render * pcrd)84 render_table_identity(byte in, const gs_cie_render * pcrd)
85 {
86     return byte2frac(in);
87 }
88 
89 /* Transformation procedures that just consult the cache. */
90 
91 static float
EncodeABC_cached_A(floatp in,const gs_cie_render * pcrd)92 EncodeABC_cached_A(floatp in, const gs_cie_render * pcrd)
93 {
94     return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[0].floats);
95 }
96 static float
EncodeABC_cached_B(floatp in,const gs_cie_render * pcrd)97 EncodeABC_cached_B(floatp in, const gs_cie_render * pcrd)
98 {
99     return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[1].floats);
100 }
101 static float
EncodeABC_cached_C(floatp in,const gs_cie_render * pcrd)102 EncodeABC_cached_C(floatp in, const gs_cie_render * pcrd)
103 {
104     return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[2].floats);
105 }
106 static float
EncodeLMN_cached_L(floatp in,const gs_cie_render * pcrd)107 EncodeLMN_cached_L(floatp in, const gs_cie_render * pcrd)
108 {
109     return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN.caches[0].floats);
110 }
111 static float
EncodeLMN_cached_M(floatp in,const gs_cie_render * pcrd)112 EncodeLMN_cached_M(floatp in, const gs_cie_render * pcrd)
113 {
114     return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN.caches[1].floats);
115 }
116 static float
EncodeLMN_cached_N(floatp in,const gs_cie_render * pcrd)117 EncodeLMN_cached_N(floatp in, const gs_cie_render * pcrd)
118 {
119     return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN.caches[2].floats);
120 }
121 
122 static frac
RTT_cached(byte in,const gs_cie_render * pcrd,int i)123 RTT_cached(byte in, const gs_cie_render * pcrd, int i)
124 {
125     return pcrd->caches.RenderTableT[i].fracs.values[
126         in * (gx_cie_cache_size - 1) / 255
127     ];
128 }
129 static frac
RTT_cached_0(byte in,const gs_cie_render * pcrd)130 RTT_cached_0(byte in, const gs_cie_render * pcrd)
131 {
132     return RTT_cached(in, pcrd, 0);
133 }
134 static frac
RTT_cached_1(byte in,const gs_cie_render * pcrd)135 RTT_cached_1(byte in, const gs_cie_render * pcrd)
136 {
137     return RTT_cached(in, pcrd, 1);
138 }
139 static frac
RTT_cached_2(byte in,const gs_cie_render * pcrd)140 RTT_cached_2(byte in, const gs_cie_render * pcrd)
141 {
142     return RTT_cached(in, pcrd, 2);
143 }
144 static frac
RTT_cached_3(byte in,const gs_cie_render * pcrd)145 RTT_cached_3(byte in, const gs_cie_render * pcrd)
146 {
147     return RTT_cached(in, pcrd, 3);
148 }
149 
150 /* Define the TransformPQR trampoline procedure that looks up proc_name. */
151 
152 static int
tpqr_do_lookup(gs_cie_render * pcrd,const gx_device * dev_proto)153 tpqr_do_lookup(gs_cie_render *pcrd, const gx_device *dev_proto)
154 {
155     gx_device *dev;
156     gs_memory_t *mem = pcrd->rc.memory;
157     gs_c_param_list list;
158     gs_param_string proc_addr;
159     int code;
160 
161     /* Device prototypes are const, so we must create a copy. */
162     code = gs_copydevice(&dev, dev_proto, mem);
163     if (code < 0)
164         return code;
165     gs_c_param_list_write(&list, mem);
166     code = param_request((gs_param_list *)&list,
167                          pcrd->TransformPQR.proc_name);
168     if (code >= 0) {
169         code = gs_getdeviceparams(dev, (gs_param_list *)&list);
170         if (code >= 0) {
171             gs_c_param_list_read(&list);
172             code = param_read_string((gs_param_list *)&list,
173                                      pcrd->TransformPQR.proc_name,
174                                      &proc_addr);
175             if (code == 0 && proc_addr.size == sizeof(gs_cie_transform_proc)) {
176                 memcpy(&pcrd->TransformPQR.proc, proc_addr.data,
177                        sizeof(gs_cie_transform_proc));
178             } else
179                 code = gs_note_error(gs_error_rangecheck);
180         }
181     }
182     gs_c_param_list_release(&list);
183     gs_free_object(mem, dev, "tpqr_do_lookup(device)");
184     return code;
185 }
186 static int
tpqr_lookup(int index,floatp in,const gs_cie_wbsd * pwbsd,gs_cie_render * pcrd,float * out)187 tpqr_lookup(int index, floatp in, const gs_cie_wbsd * pwbsd,
188             gs_cie_render * pcrd, float *out)
189 {
190     const gx_device *const *dev_list;
191     int count = gs_lib_device_list(&dev_list, NULL);
192     int i;
193     int code;
194 
195     for (i = 0; i < count; ++i)
196         if (!strcmp(gs_devicename(dev_list[i]),
197                     pcrd->TransformPQR.driver_name))
198             break;
199     if (i < count)
200         code = tpqr_do_lookup(pcrd, dev_list[i]);
201     else
202         code = gs_note_error(gs_error_undefined);
203     if (code < 0)
204         return code;
205     return pcrd->TransformPQR.proc(index, in, pwbsd, pcrd, out);
206 }
207 
208 /* Default vectors. */
209 const gs_cie_transform_proc3 TransformPQR_default = {
210     tpqr_identity,
211     0,				/* proc_name */
212     {0, 0},			/* proc_data */
213     0				/* driver_name */
214 };
215 const gs_cie_transform_proc3 TransformPQR_from_cache = {
216     tpqr_from_cache,
217     0,				/* proc_name */
218     {0, 0},			/* proc_data */
219     0				/* driver_name */
220 };
221 const gs_cie_transform_proc TransformPQR_lookup_proc_name = tpqr_lookup;
222 const gs_cie_render_proc3 Encode_default = {
223     {render_identity, render_identity, render_identity}
224 };
225 const gs_cie_render_proc3 EncodeLMN_from_cache = {
226     {EncodeLMN_cached_L, EncodeLMN_cached_M, EncodeLMN_cached_N}
227 };
228 const gs_cie_render_proc3 EncodeABC_from_cache = {
229     {EncodeABC_cached_A, EncodeABC_cached_B, EncodeABC_cached_C}
230 };
231 const gs_cie_render_table_procs RenderTableT_default = {
232     {render_table_identity, render_table_identity, render_table_identity,
233      render_table_identity
234     }
235 };
236 const gs_cie_render_table_procs RenderTableT_from_cache = {
237     {RTT_cached_0, RTT_cached_1, RTT_cached_2, RTT_cached_3}
238 };
239 
240 /*
241  * Allocate and minimally initialize a CRD.  Note that this procedure sets
242  * the reference count of the structure to 1, not 0.  gs_setcolorrendering
243  * will increment the reference count again, so unless you want the
244  * structure to stay allocated permanently (or until a garbage collection),
245  * you should call rc_decrement(pcrd, "client name") *after* calling
246  * gs_setcolorrendering.
247  */
248 int
gs_cie_render1_build(gs_cie_render ** ppcrd,gs_memory_t * mem,client_name_t cname)249 gs_cie_render1_build(gs_cie_render ** ppcrd, gs_memory_t * mem,
250                      client_name_t cname)
251 {
252     gs_cie_render *pcrd;
253 
254     rc_alloc_struct_1(pcrd, gs_cie_render, &st_cie_render1, mem,
255                       return_error(gs_error_VMerror), cname);
256     pcrd->id = gs_next_ids(mem, 1);
257     /* Initialize pointers for the GC. */
258     pcrd->client_data = 0;
259     pcrd->RenderTable.lookup.table = 0;
260     pcrd->status = CIE_RENDER_STATUS_BUILT;
261     *ppcrd = pcrd;
262     return 0;
263 }
264 
265 /*
266  * Initialize a CRD given all of the relevant parameters.
267  * Any of the pointers except WhitePoint may be zero, meaning
268  * use the default values.
269  *
270  * The actual point, matrix, range, and procedure values are copied into the
271  * CRD, but only the pointer to the color lookup table is copied.
272  *
273  * If pfrom_crd is not NULL, then if the EncodeLMN, EncodeABC, or
274  * RenderTable.T procedures indicate that the values exist only in the
275  * cache, the corresponding values will be copied from pfrom_crd.
276  * Note that NULL values for the individual pointers still represent
277  * default values.
278  */
279 int
gs_cie_render1_init_from(const gs_memory_t * mem,gs_cie_render * pcrd,void * client_data,const gs_cie_render * pfrom_crd,const gs_vector3 * WhitePoint,const gs_vector3 * BlackPoint,const gs_matrix3 * MatrixPQR,const gs_range3 * RangePQR,const gs_cie_transform_proc3 * TransformPQR,const gs_matrix3 * MatrixLMN,const gs_cie_render_proc3 * EncodeLMN,const gs_range3 * RangeLMN,const gs_matrix3 * MatrixABC,const gs_cie_render_proc3 * EncodeABC,const gs_range3 * RangeABC,const gs_cie_render_table_t * RenderTable)280 gs_cie_render1_init_from(const gs_memory_t *mem,
281                          gs_cie_render * pcrd,
282                          void *client_data,
283                          const gs_cie_render * pfrom_crd,
284                          const gs_vector3 * WhitePoint,
285                          const gs_vector3 * BlackPoint,
286                          const gs_matrix3 * MatrixPQR,
287                          const gs_range3 * RangePQR,
288                          const gs_cie_transform_proc3 * TransformPQR,
289                          const gs_matrix3 * MatrixLMN,
290                          const gs_cie_render_proc3 * EncodeLMN,
291                          const gs_range3 * RangeLMN,
292                          const gs_matrix3 * MatrixABC,
293                          const gs_cie_render_proc3 * EncodeABC,
294                          const gs_range3 * RangeABC,
295                          const gs_cie_render_table_t * RenderTable)
296 {
297     pcrd->id = gs_next_ids(mem, 1);
298     pcrd->client_data = client_data;
299     pcrd->points.WhitePoint = *WhitePoint;
300     pcrd->points.BlackPoint =
301         *(BlackPoint ? BlackPoint : &BlackPoint_default);
302     pcrd->MatrixPQR = *(MatrixPQR ? MatrixPQR : &Matrix3_default);
303     pcrd->RangePQR = *(RangePQR ? RangePQR : &Range3_default);
304     pcrd->TransformPQR =
305         *(TransformPQR ? TransformPQR : &TransformPQR_default);
306     pcrd->MatrixLMN = *(MatrixLMN ? MatrixLMN : &Matrix3_default);
307     pcrd->EncodeLMN = *(EncodeLMN ? EncodeLMN : &Encode_default);
308     if (pfrom_crd &&
309         !memcmp(&pcrd->EncodeLMN, &EncodeLMN_from_cache,
310                 sizeof(EncodeLMN_from_cache))
311         )
312         memcpy(&pcrd->caches.EncodeLMN, &pfrom_crd->caches.EncodeLMN,
313                sizeof(pcrd->caches.EncodeLMN));
314     pcrd->RangeLMN = *(RangeLMN ? RangeLMN : &Range3_default);
315     pcrd->MatrixABC = *(MatrixABC ? MatrixABC : &Matrix3_default);
316     pcrd->EncodeABC = *(EncodeABC ? EncodeABC : &Encode_default);
317     if (pfrom_crd &&
318         !memcmp(&pcrd->EncodeABC, &EncodeABC_from_cache,
319                 sizeof(EncodeABC_from_cache))
320         )
321         memcpy(pcrd->caches.EncodeABC, pfrom_crd->caches.EncodeABC,
322                sizeof(pcrd->caches.EncodeABC));
323     pcrd->RangeABC = *(RangeABC ? RangeABC : &Range3_default);
324     if (RenderTable) {
325         pcrd->RenderTable = *RenderTable;
326         if (pfrom_crd &&
327             !memcmp(&pcrd->RenderTable.T, &RenderTableT_from_cache,
328                     sizeof(RenderTableT_from_cache))
329             ) {
330             memcpy(pcrd->caches.RenderTableT, pfrom_crd->caches.RenderTableT,
331                    sizeof(pcrd->caches.RenderTableT));
332             pcrd->caches.RenderTableT_is_identity =
333                 pfrom_crd->caches.RenderTableT_is_identity;
334         }
335     } else {
336         pcrd->RenderTable.lookup.table = 0;
337         pcrd->RenderTable.T = RenderTableT_default;
338     }
339     pcrd->status = CIE_RENDER_STATUS_BUILT;
340     return 0;
341 }
342 /*
343  * Initialize a CRD without the option of copying cached values.
344  */
345 int
gs_cie_render1_initialize(const gs_memory_t * mem,gs_cie_render * pcrd,void * client_data,const gs_vector3 * WhitePoint,const gs_vector3 * BlackPoint,const gs_matrix3 * MatrixPQR,const gs_range3 * RangePQR,const gs_cie_transform_proc3 * TransformPQR,const gs_matrix3 * MatrixLMN,const gs_cie_render_proc3 * EncodeLMN,const gs_range3 * RangeLMN,const gs_matrix3 * MatrixABC,const gs_cie_render_proc3 * EncodeABC,const gs_range3 * RangeABC,const gs_cie_render_table_t * RenderTable)346 gs_cie_render1_initialize(const gs_memory_t *mem,
347                           gs_cie_render * pcrd, void *client_data,
348                           const gs_vector3 * WhitePoint,
349                           const gs_vector3 * BlackPoint,
350                           const gs_matrix3 * MatrixPQR,
351                           const gs_range3 * RangePQR,
352                           const gs_cie_transform_proc3 * TransformPQR,
353                           const gs_matrix3 * MatrixLMN,
354                           const gs_cie_render_proc3 * EncodeLMN,
355                           const gs_range3 * RangeLMN,
356                           const gs_matrix3 * MatrixABC,
357                           const gs_cie_render_proc3 * EncodeABC,
358                           const gs_range3 * RangeABC,
359                           const gs_cie_render_table_t * RenderTable)
360 {
361     return gs_cie_render1_init_from(mem, pcrd, client_data, NULL,
362                                     WhitePoint, BlackPoint,
363                                     MatrixPQR, RangePQR, TransformPQR,
364                                     MatrixLMN, EncodeLMN, RangeLMN,
365                                     MatrixABC, EncodeABC, RangeABC,
366                                     RenderTable);
367 }
368