/src/ghostpdl/base/gscrd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, 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 | 1.01M | ENUM_PTRS_WITH(cie_render1_enum_ptrs, gs_cie_render *pcrd) return 0; |
40 | 339k | case 0: return ENUM_OBJ(pcrd->client_data); |
41 | 339k | case 1: return ENUM_OBJ(pcrd->RenderTable.lookup.table); |
42 | 339k | case 2: return (pcrd->RenderTable.lookup.table ? |
43 | 339k | ENUM_CONST_STRING(&pcrd->TransformPQR.proc_data) : |
44 | 339k | 0); |
45 | 1.01M | ENUM_PTRS_END |
46 | 339k | static RELOC_PTRS_WITH(cie_render1_reloc_ptrs, gs_cie_render *pcrd); |
47 | 339k | RELOC_OBJ_VAR(pcrd->client_data); |
48 | 339k | if (pcrd->RenderTable.lookup.table) |
49 | 0 | { |
50 | 0 | RELOC_OBJ_VAR(pcrd->RenderTable.lookup.table); |
51 | 0 | RELOC_CONST_STRING_VAR(pcrd->TransformPQR.proc_data); |
52 | 0 | } |
53 | 339k | RELOC_PTRS_END |
54 | | |
55 | | /* Default CRD procedures. */ |
56 | | |
57 | | static int |
58 | | tpqr_identity(int index, double in, const gs_cie_wbsd * pwbsd, |
59 | | gs_cie_render * pcrd, float *out) |
60 | 0 | { |
61 | 0 | *out = in; |
62 | 0 | return 0; |
63 | 0 | } |
64 | | |
65 | | static int |
66 | | tpqr_from_cache(int index, double in, const gs_cie_wbsd * pwbsd, |
67 | | gs_cie_render * pcrd, float *out) |
68 | 0 | { |
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 | 0 | *out = in; |
75 | 0 | return 0; |
76 | 0 | } |
77 | | |
78 | | static float |
79 | | render_identity(double in, const gs_cie_render * pcrd) |
80 | 499M | { |
81 | 499M | return in; |
82 | 499M | } |
83 | | static frac |
84 | | render_table_identity(byte in, const gs_cie_render * pcrd) |
85 | 0 | { |
86 | 0 | return byte2frac(in); |
87 | 0 | } |
88 | | |
89 | | /* Transformation procedures that just consult the cache. */ |
90 | | |
91 | | static float |
92 | | EncodeABC_cached_A(double in, const gs_cie_render * pcrd) |
93 | 0 | { |
94 | 0 | return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[0].floats); |
95 | 0 | } |
96 | | static float |
97 | | EncodeABC_cached_B(double in, const gs_cie_render * pcrd) |
98 | 0 | { |
99 | 0 | return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[1].floats); |
100 | 0 | } |
101 | | static float |
102 | | EncodeABC_cached_C(double in, const gs_cie_render * pcrd) |
103 | 0 | { |
104 | 0 | return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[2].floats); |
105 | 0 | } |
106 | | static float |
107 | | EncodeLMN_cached_L(double in, const gs_cie_render * pcrd) |
108 | 0 | { |
109 | 0 | return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN.caches[0].floats); |
110 | 0 | } |
111 | | static float |
112 | | EncodeLMN_cached_M(double in, const gs_cie_render * pcrd) |
113 | 0 | { |
114 | 0 | return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN.caches[1].floats); |
115 | 0 | } |
116 | | static float |
117 | | EncodeLMN_cached_N(double in, const gs_cie_render * pcrd) |
118 | 0 | { |
119 | 0 | return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN.caches[2].floats); |
120 | 0 | } |
121 | | |
122 | | static frac |
123 | | RTT_cached(byte in, const gs_cie_render * pcrd, int i) |
124 | 0 | { |
125 | 0 | return pcrd->caches.RenderTableT[i].fracs.values[ |
126 | 0 | in * (gx_cie_cache_size - 1) / 255 |
127 | 0 | ]; |
128 | 0 | } |
129 | | static frac |
130 | | RTT_cached_0(byte in, const gs_cie_render * pcrd) |
131 | 0 | { |
132 | 0 | return RTT_cached(in, pcrd, 0); |
133 | 0 | } |
134 | | static frac |
135 | | RTT_cached_1(byte in, const gs_cie_render * pcrd) |
136 | 0 | { |
137 | 0 | return RTT_cached(in, pcrd, 1); |
138 | 0 | } |
139 | | static frac |
140 | | RTT_cached_2(byte in, const gs_cie_render * pcrd) |
141 | 0 | { |
142 | 0 | return RTT_cached(in, pcrd, 2); |
143 | 0 | } |
144 | | static frac |
145 | | RTT_cached_3(byte in, const gs_cie_render * pcrd) |
146 | 0 | { |
147 | 0 | return RTT_cached(in, pcrd, 3); |
148 | 0 | } |
149 | | |
150 | | /* Define the TransformPQR trampoline procedure that looks up proc_name. */ |
151 | | |
152 | | static int |
153 | | tpqr_do_lookup(gs_cie_render *pcrd, const gx_device *dev_proto) |
154 | 0 | { |
155 | 0 | gx_device *dev; |
156 | 0 | gs_memory_t *mem = pcrd->rc.memory; |
157 | 0 | gs_c_param_list list; |
158 | 0 | gs_param_string proc_addr; |
159 | 0 | int code; |
160 | | |
161 | | /* Device prototypes are const, so we must create a copy. */ |
162 | 0 | code = gs_copydevice(&dev, dev_proto, mem); |
163 | 0 | if (code < 0) |
164 | 0 | return code; |
165 | 0 | gs_c_param_list_write(&list, mem); |
166 | 0 | code = param_request((gs_param_list *)&list, |
167 | 0 | pcrd->TransformPQR.proc_name); |
168 | 0 | if (code >= 0) { |
169 | 0 | code = gs_getdeviceparams(dev, (gs_param_list *)&list); |
170 | 0 | if (code >= 0) { |
171 | 0 | gs_c_param_list_read(&list); |
172 | 0 | code = param_read_string((gs_param_list *)&list, |
173 | 0 | pcrd->TransformPQR.proc_name, |
174 | 0 | &proc_addr); |
175 | 0 | if (code == 0 && proc_addr.size == sizeof(gs_cie_transform_proc)) { |
176 | 0 | memcpy(&pcrd->TransformPQR.proc, proc_addr.data, |
177 | 0 | sizeof(gs_cie_transform_proc)); |
178 | 0 | } else |
179 | 0 | code = gs_note_error(gs_error_rangecheck); |
180 | 0 | } |
181 | 0 | } |
182 | 0 | gs_c_param_list_release(&list); |
183 | 0 | gs_free_object(mem, dev, "tpqr_do_lookup(device)"); |
184 | 0 | return code; |
185 | 0 | } |
186 | | static int |
187 | | tpqr_lookup(int index, double in, const gs_cie_wbsd * pwbsd, |
188 | | gs_cie_render * pcrd, float *out) |
189 | 0 | { |
190 | 0 | const gx_device *const *dev_list; |
191 | 0 | int count = gs_lib_device_list(&dev_list, NULL); |
192 | 0 | int i; |
193 | 0 | int code; |
194 | |
|
195 | 0 | for (i = 0; i < count; ++i) |
196 | 0 | if (!strcmp(gs_devicename(dev_list[i]), |
197 | 0 | pcrd->TransformPQR.driver_name)) |
198 | 0 | break; |
199 | 0 | if (i < count) |
200 | 0 | code = tpqr_do_lookup(pcrd, dev_list[i]); |
201 | 0 | else |
202 | 0 | code = gs_note_error(gs_error_undefined); |
203 | 0 | if (code < 0) |
204 | 0 | return code; |
205 | 0 | return pcrd->TransformPQR.proc(index, in, pwbsd, pcrd, out); |
206 | 0 | } |
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 |
249 | | gs_cie_render1_build(gs_cie_render ** ppcrd, gs_memory_t * mem, |
250 | | client_name_t cname) |
251 | 162k | { |
252 | 162k | gs_cie_render *pcrd; |
253 | | |
254 | 162k | rc_alloc_struct_1(pcrd, gs_cie_render, &st_cie_render1, mem, |
255 | 162k | return_error(gs_error_VMerror), cname); |
256 | 162k | pcrd->id = gs_next_ids(mem, 1); |
257 | | /* Initialize pointers for the GC. */ |
258 | 162k | pcrd->client_data = 0; |
259 | 162k | pcrd->RenderTable.lookup.table = 0; |
260 | 162k | pcrd->status = CIE_RENDER_STATUS_BUILT; |
261 | 162k | *ppcrd = pcrd; |
262 | 162k | return 0; |
263 | 162k | } |
264 | | |
265 | | static bool render_proc3_equal(const gs_cie_render_proc3 *p1, const gs_cie_render_proc3 *p2) |
266 | 0 | { |
267 | 0 | int k; |
268 | |
|
269 | 0 | for (k = 0; k < 3; k++) { |
270 | 0 | if (p1->procs[k] != p2->procs[k]) |
271 | 0 | return false; |
272 | 0 | } |
273 | 0 | return true; |
274 | 0 | } |
275 | | |
276 | | static bool render_table_procs_equal(const gs_cie_render_table_procs *p1, |
277 | | const gs_cie_render_table_procs *p2) |
278 | 0 | { |
279 | 0 | int k; |
280 | |
|
281 | 0 | for (k = 0; k < 4; k++) { |
282 | 0 | if (p1->procs[k] != p2->procs[k]) |
283 | 0 | return false; |
284 | 0 | } |
285 | 0 | return true; |
286 | 0 | } |
287 | | |
288 | | /* |
289 | | * Initialize a CRD given all of the relevant parameters. |
290 | | * Any of the pointers except WhitePoint may be zero, meaning |
291 | | * use the default values. |
292 | | * |
293 | | * The actual point, matrix, range, and procedure values are copied into the |
294 | | * CRD, but only the pointer to the color lookup table is copied. |
295 | | * |
296 | | * If pfrom_crd is not NULL, then if the EncodeLMN, EncodeABC, or |
297 | | * RenderTable.T procedures indicate that the values exist only in the |
298 | | * cache, the corresponding values will be copied from pfrom_crd. |
299 | | * Note that NULL values for the individual pointers still represent |
300 | | * default values. |
301 | | */ |
302 | | int |
303 | | gs_cie_render1_init_from(const gs_memory_t *mem, |
304 | | gs_cie_render * pcrd, |
305 | | void *client_data, |
306 | | const gs_cie_render * pfrom_crd, |
307 | | const gs_vector3 * WhitePoint, |
308 | | const gs_vector3 * BlackPoint, |
309 | | const gs_matrix3 * MatrixPQR, |
310 | | const gs_range3 * RangePQR, |
311 | | const gs_cie_transform_proc3 * TransformPQR, |
312 | | const gs_matrix3 * MatrixLMN, |
313 | | const gs_cie_render_proc3 * EncodeLMN, |
314 | | const gs_range3 * RangeLMN, |
315 | | const gs_matrix3 * MatrixABC, |
316 | | const gs_cie_render_proc3 * EncodeABC, |
317 | | const gs_range3 * RangeABC, |
318 | | const gs_cie_render_table_t * RenderTable) |
319 | 0 | { |
320 | 0 | pcrd->id = gs_next_ids(mem, 1); |
321 | 0 | pcrd->client_data = client_data; |
322 | 0 | pcrd->points.WhitePoint = *WhitePoint; |
323 | 0 | pcrd->points.BlackPoint = |
324 | 0 | *(BlackPoint ? BlackPoint : &BlackPoint_default); |
325 | 0 | pcrd->MatrixPQR = *(MatrixPQR ? MatrixPQR : &Matrix3_default); |
326 | 0 | pcrd->RangePQR = *(RangePQR ? RangePQR : &Range3_default); |
327 | 0 | pcrd->TransformPQR = |
328 | 0 | *(TransformPQR ? TransformPQR : &TransformPQR_default); |
329 | 0 | pcrd->MatrixLMN = *(MatrixLMN ? MatrixLMN : &Matrix3_default); |
330 | 0 | pcrd->EncodeLMN = *(EncodeLMN ? EncodeLMN : &Encode_default); |
331 | 0 | if (pfrom_crd && render_proc3_equal(&pcrd->EncodeLMN, &EncodeLMN_from_cache)) |
332 | 0 | memcpy(&pcrd->caches.EncodeLMN, &pfrom_crd->caches.EncodeLMN, |
333 | 0 | sizeof(pcrd->caches.EncodeLMN)); |
334 | 0 | pcrd->RangeLMN = *(RangeLMN ? RangeLMN : &Range3_default); |
335 | 0 | pcrd->MatrixABC = *(MatrixABC ? MatrixABC : &Matrix3_default); |
336 | 0 | pcrd->EncodeABC = *(EncodeABC ? EncodeABC : &Encode_default); |
337 | 0 | if (pfrom_crd && render_proc3_equal(&pcrd->EncodeABC, &EncodeABC_from_cache)) |
338 | 0 | memcpy(pcrd->caches.EncodeABC, pfrom_crd->caches.EncodeABC, |
339 | 0 | sizeof(pcrd->caches.EncodeABC)); |
340 | 0 | pcrd->RangeABC = *(RangeABC ? RangeABC : &Range3_default); |
341 | 0 | if (RenderTable) { |
342 | 0 | pcrd->RenderTable = *RenderTable; |
343 | 0 | if (pfrom_crd && render_table_procs_equal(&pcrd->RenderTable.T, |
344 | 0 | &RenderTableT_from_cache)) { |
345 | 0 | memcpy(pcrd->caches.RenderTableT, pfrom_crd->caches.RenderTableT, |
346 | 0 | sizeof(pcrd->caches.RenderTableT)); |
347 | 0 | pcrd->caches.RenderTableT_is_identity = |
348 | 0 | pfrom_crd->caches.RenderTableT_is_identity; |
349 | 0 | } |
350 | 0 | } else { |
351 | 0 | pcrd->RenderTable.lookup.table = 0; |
352 | 0 | pcrd->RenderTable.T = RenderTableT_default; |
353 | 0 | } |
354 | 0 | pcrd->status = CIE_RENDER_STATUS_BUILT; |
355 | 0 | return 0; |
356 | 0 | } |
357 | | /* |
358 | | * Initialize a CRD without the option of copying cached values. |
359 | | */ |
360 | | int |
361 | | gs_cie_render1_initialize(const gs_memory_t *mem, |
362 | | gs_cie_render * pcrd, void *client_data, |
363 | | const gs_vector3 * WhitePoint, |
364 | | const gs_vector3 * BlackPoint, |
365 | | const gs_matrix3 * MatrixPQR, |
366 | | const gs_range3 * RangePQR, |
367 | | const gs_cie_transform_proc3 * TransformPQR, |
368 | | const gs_matrix3 * MatrixLMN, |
369 | | const gs_cie_render_proc3 * EncodeLMN, |
370 | | const gs_range3 * RangeLMN, |
371 | | const gs_matrix3 * MatrixABC, |
372 | | const gs_cie_render_proc3 * EncodeABC, |
373 | | const gs_range3 * RangeABC, |
374 | | const gs_cie_render_table_t * RenderTable) |
375 | 0 | { |
376 | 0 | return gs_cie_render1_init_from(mem, pcrd, client_data, NULL, |
377 | 0 | WhitePoint, BlackPoint, |
378 | 0 | MatrixPQR, RangePQR, TransformPQR, |
379 | 0 | MatrixLMN, EncodeLMN, RangeLMN, |
380 | 0 | MatrixABC, EncodeABC, RangeABC, |
381 | 0 | RenderTable); |
382 | 0 | } |