Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 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 | | /* ICCBased color operators */ |
18 | | |
19 | | #include "math_.h" |
20 | | #include "memory_.h" |
21 | | #include "ghost.h" |
22 | | #include "oper.h" |
23 | | #include "gsstruct.h" |
24 | | #include "gxcspace.h" /* gscolor2.h requires gscspace.h */ |
25 | | #include "stream.h" |
26 | | #include "files.h" |
27 | | #include "gscolor2.h" |
28 | | #include "gsicc.h" |
29 | | #include "estack.h" |
30 | | #include "idict.h" |
31 | | #include "idparam.h" |
32 | | #include "igstate.h" |
33 | | #include "icie.h" |
34 | | #include "ialloc.h" |
35 | | #include "store.h" |
36 | | #include "zicc.h" |
37 | | #include "gsicc_manage.h" |
38 | | #include "gx.h" |
39 | | #include "gxgstate.h" |
40 | | #include "gsicc_create.h" |
41 | | #include "gsicc_profilecache.h" |
42 | | #include "gxdevice.h" /* for output intent setting */ |
43 | | #include "gsicc_cache.h" |
44 | | |
45 | | int seticc(i_ctx_t * i_ctx_p, int ncomps, ref *ICCdict, float *range_buff) |
46 | 41 | { |
47 | 41 | int code, k; |
48 | 41 | gs_color_space * pcs; |
49 | 41 | ref * pstrmval; |
50 | 41 | ref * phashval = NULL; |
51 | 41 | stream * s = 0L; |
52 | 41 | cmm_profile_t *picc_profile = NULL; |
53 | 41 | int i, expected = 0; |
54 | 41 | ref * pnameval; |
55 | 41 | static const char *const icc_std_profile_names[] = { |
56 | 41 | GSICC_STANDARD_PROFILES |
57 | 41 | }; |
58 | 41 | static const char *const icc_std_profile_keys[] = { |
59 | 41 | GSICC_STANDARD_PROFILES_KEYS |
60 | 41 | }; |
61 | | |
62 | | /* If we are override ICC mode, we won't use the profile */ |
63 | 41 | if (!gs_currentoverrideicc(igs) && |
64 | 41 | dict_find_string(ICCdict, ".hash", &phashval) == 1 && |
65 | 41 | r_has_type(phashval, t_integer)) { |
66 | 0 | pcs = gsicc_find_cs(phashval->value.intval, igs); |
67 | 0 | if (pcs != NULL && gs_color_space_num_components(pcs) == ncomps) { |
68 | | /* Set the color space. We are done. */ |
69 | 0 | code = gs_setcolorspace(igs, pcs); |
70 | | /* Remove the ICC dict from the stack */ |
71 | 0 | ref_stack_pop(&o_stack, 1); |
72 | 0 | return code; |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | /* verify the DataSource entry */ |
77 | 41 | if (dict_find_string(ICCdict, "DataSource", &pstrmval) <= 0) |
78 | 0 | return_error(gs_error_undefined); |
79 | 41 | check_read_file(i_ctx_p, s, pstrmval); |
80 | | |
81 | | /* build the color space object */ |
82 | 41 | code = gs_cspace_build_ICC(&pcs, NULL, gs_gstate_memory(igs)->stable_memory); |
83 | 41 | if (code < 0) |
84 | 0 | return gs_rethrow(code, "building color space object"); |
85 | | /* For now, dump the profile into a buffer |
86 | | and obtain handle from the buffer when we need it. |
87 | | We may want to change this later. |
88 | | This depends to some degree on what the CMS is capable of doing. |
89 | | I don't want to get bogged down on stream I/O at this point. |
90 | | Note also, if we are going to be putting these into the clist we will |
91 | | want to have this buffer. */ |
92 | | /* Check if we have the /Name entry. This is used to associate with |
93 | | specs that have enumerated types to indicate sRGB sGray etc */ |
94 | 41 | if (dict_find_string(ICCdict, "Name", &pnameval) > 0 && r_has_type(pnameval, t_string)){ |
95 | 0 | const char *str = (const char *)pnameval->value.bytes; |
96 | 0 | size_t size = r_size(pnameval); |
97 | | |
98 | | /* Compare this to the standard profile names */ |
99 | 0 | for (k = 0; k < GSICC_NUMBER_STANDARD_PROFILES; k++) { |
100 | 0 | if ( strlen(icc_std_profile_keys[k]) == size && |
101 | 0 | memcmp(icc_std_profile_keys[k], str, size) == 0) { |
102 | 0 | picc_profile = gsicc_get_profile_handle_file(icc_std_profile_names[k], |
103 | 0 | strlen(icc_std_profile_names[k]), gs_gstate_memory(igs)); |
104 | 0 | break; |
105 | 0 | } |
106 | 0 | } |
107 | 41 | } else { |
108 | 41 | picc_profile = gsicc_profile_new(s, gs_gstate_memory(igs), NULL, 0); |
109 | 41 | if (picc_profile == NULL) |
110 | 0 | return gs_throw(gs_error_VMerror, "Creation of ICC profile failed"); |
111 | | /* We have to get the profile handle due to the fact that we need to know |
112 | | if it has a data space that is CIELAB */ |
113 | 41 | picc_profile->profile_handle = |
114 | 41 | gsicc_get_profile_handle_buffer(picc_profile->buffer, |
115 | 41 | picc_profile->buffer_size, |
116 | 41 | gs_gstate_memory(igs)); |
117 | 41 | } |
118 | 41 | if (picc_profile == NULL || picc_profile->profile_handle == NULL) { |
119 | | /* Free up everything, the profile is not valid. We will end up going |
120 | | ahead and using a default based upon the number of components */ |
121 | 0 | rc_decrement(picc_profile,"seticc"); |
122 | 0 | rc_decrement(pcs,"seticc"); |
123 | 0 | return -1; |
124 | 0 | } |
125 | 41 | code = gsicc_set_gscs_profile(pcs, picc_profile, gs_gstate_memory(igs)); |
126 | 41 | if (code < 0) { |
127 | 0 | rc_decrement(picc_profile,"seticc"); |
128 | 0 | rc_decrement(pcs,"seticc"); |
129 | 0 | return code; |
130 | 0 | } |
131 | 41 | picc_profile->num_comps = ncomps; |
132 | | |
133 | 41 | picc_profile->data_cs = |
134 | 41 | gscms_get_profile_data_space(picc_profile->profile_handle, |
135 | 41 | picc_profile->memory); |
136 | 41 | switch (picc_profile->data_cs) { |
137 | 0 | case gsCIEXYZ: |
138 | 0 | case gsCIELAB: |
139 | 41 | case gsRGB: |
140 | 41 | expected = 3; |
141 | 41 | break; |
142 | 0 | case gsGRAY: |
143 | 0 | expected = 1; |
144 | 0 | break; |
145 | 0 | case gsCMYK: |
146 | 0 | expected = 4; |
147 | 0 | break; |
148 | 0 | case gsNCHANNEL: |
149 | 0 | case gsNAMED: /* Silence warnings */ |
150 | 0 | case gsUNDEFINED: /* Silence warnings */ |
151 | 0 | break; |
152 | 41 | } |
153 | 41 | if (!expected || ncomps != expected) { |
154 | 0 | rc_decrement(picc_profile,"seticc"); |
155 | 0 | rc_decrement(pcs,"seticc"); |
156 | 0 | return_error(gs_error_rangecheck); |
157 | 0 | } |
158 | | |
159 | | /* Lets go ahead and get the hash code and check if we match one of the default spaces */ |
160 | | /* Later we may want to delay this, but for now lets go ahead and do it */ |
161 | 41 | gsicc_init_hash_cs(picc_profile, igs); |
162 | | |
163 | | /* Set the range according to the data type that is associated with the |
164 | | ICC input color type. Occasionally, we will run into CIELAB to CIELAB |
165 | | profiles for spot colors in PDF documents. These spot colors are typically described |
166 | | as separation colors with tint transforms that go from a tint value |
167 | | to a linear mapping between the CIELAB white point and the CIELAB tint |
168 | | color. This results in a CIELAB value that we need to use to fill. We |
169 | | need to detect this to make sure we do the proper scaling of the data. For |
170 | | CIELAB images in PDF, the source is always normal 8 or 16 bit encoded data |
171 | | in the range from 0 to 255 or 0 to 65535. In that case, there should not |
172 | | be any encoding and decoding to CIELAB. The PDF content will not include |
173 | | an ICC profile but will set the color space to \Lab. In this case, we use |
174 | | our seticc_lab operation to install the LAB to LAB profile, but we detect |
175 | | that we did that through the use of the is_lab flag in the profile descriptor. |
176 | | When then avoid the CIELAB encode and decode */ |
177 | 41 | if (picc_profile->data_cs == gsCIELAB) { |
178 | | /* If the input space to this profile is CIELAB, then we need to adjust the limits */ |
179 | | /* See ICC spec ICC.1:2004-10 Section 6.3.4.2 and 6.4. I don't believe we need to |
180 | | worry about CIEXYZ profiles or any of the other odds ones. Need to check that though |
181 | | at some point. */ |
182 | 0 | picc_profile->Range.ranges[0].rmin = 0.0; |
183 | 0 | picc_profile->Range.ranges[0].rmax = 100.0; |
184 | 0 | picc_profile->Range.ranges[1].rmin = -128.0; |
185 | 0 | picc_profile->Range.ranges[1].rmax = 127.0; |
186 | 0 | picc_profile->Range.ranges[2].rmin = -128.0; |
187 | 0 | picc_profile->Range.ranges[2].rmax = 127.0; |
188 | 0 | picc_profile->islab = true; |
189 | 41 | } else { |
190 | 164 | for (i = 0; i < ncomps; i++) { |
191 | 123 | picc_profile->Range.ranges[i].rmin = range_buff[2 * i]; |
192 | 123 | picc_profile->Range.ranges[i].rmax = range_buff[2 * i + 1]; |
193 | 123 | } |
194 | 41 | } |
195 | | /* Now see if we are in an overide situation. We have to wait until now |
196 | | in case this is an LAB profile which we will not overide */ |
197 | 41 | if (gs_currentoverrideicc(igs) && picc_profile->data_cs != gsCIELAB) { |
198 | | /* Free up the profile structure */ |
199 | 0 | switch( picc_profile->data_cs ) { |
200 | 0 | case gsRGB: |
201 | 0 | pcs->cmm_icc_profile_data = igs->icc_manager->default_rgb; |
202 | 0 | break; |
203 | 0 | case gsGRAY: |
204 | 0 | pcs->cmm_icc_profile_data = igs->icc_manager->default_gray; |
205 | 0 | break; |
206 | 0 | case gsCMYK: |
207 | 0 | pcs->cmm_icc_profile_data = igs->icc_manager->default_cmyk; |
208 | 0 | break; |
209 | 0 | default: |
210 | 0 | break; |
211 | 0 | } |
212 | | /* Have one increment from the color space. Having these tied |
213 | | together is not really correct. Need to fix that. ToDo. MJV */ |
214 | 0 | rc_adjust(picc_profile, -2, "seticc"); /* NB: May free the profile and set picc_profile to 0 */ |
215 | 0 | rc_increment(pcs->cmm_icc_profile_data); |
216 | 0 | } |
217 | | /* Set the color space. We are done. */ |
218 | 41 | code = gs_setcolorspace(igs, pcs); |
219 | | /* The context has taken a reference to the colorspace. We no longer need |
220 | | * ours, so drop it. */ |
221 | 41 | rc_decrement_only(pcs, "seticc"); |
222 | 41 | if (picc_profile != NULL) { |
223 | | /* In this case, we already have a ref count of 2 on the icc profile |
224 | | one for when it was created and one for when it was set. We really |
225 | | only want one here so adjust */ |
226 | 41 | rc_decrement(picc_profile,"seticc"); |
227 | 41 | if (code >= 0) { |
228 | | /* Save this colorspace in the iccprofile_cache */ |
229 | 41 | code = gsicc_add_cs(igs, pcs, picc_profile->hashcode); |
230 | 41 | if (code >= 0) { |
231 | | /* should be an integer, but if for some reason it isn't, don't update */ |
232 | 41 | if (phashval && r_has_type(phashval, t_integer)) |
233 | 0 | phashval->value.intval = picc_profile->hashcode; |
234 | 41 | } |
235 | 41 | } |
236 | 41 | } |
237 | | /* Remove the ICC dict from the stack */ |
238 | 41 | ref_stack_pop(&o_stack, 1); |
239 | 41 | return code; |
240 | 41 | } |
241 | | |
242 | | /* |
243 | | * <dict> .set_outputintent - |
244 | | * |
245 | | * Set and use the specified output intent. |
246 | | * |
247 | | */ |
248 | | static int |
249 | | zset_outputintent(i_ctx_t * i_ctx_p) |
250 | 0 | { |
251 | 0 | os_ptr op = osp; |
252 | 0 | int code = 0; |
253 | 0 | gx_device *dev = gs_currentdevice(igs); |
254 | 0 | cmm_dev_profile_t *dev_profile; |
255 | 0 | stream * s = 0L; |
256 | 0 | ref * pnval; |
257 | 0 | ref * pstrmval; |
258 | 0 | int ncomps, dev_comps; |
259 | 0 | cmm_profile_t *picc_profile; |
260 | 0 | int expected = 0; |
261 | 0 | gs_color_space_index index; |
262 | 0 | gsicc_manager_t *icc_manager = igs->icc_manager; |
263 | 0 | cmm_profile_t *source_profile = NULL; |
264 | |
|
265 | 0 | check_op(1); |
266 | 0 | check_type(*op, t_dictionary); |
267 | 0 | check_dict_read(*op); |
268 | 0 | if_debug0m(gs_debug_flag_icc, imemory, "[icc] Using OutputIntent\n"); |
269 | | |
270 | | /* Get the device structure */ |
271 | 0 | code = dev_proc(dev, get_profile)(dev, &dev_profile); |
272 | 0 | if (code < 0) |
273 | 0 | return code; |
274 | | |
275 | 0 | if (dev_profile == NULL) { |
276 | 0 | code = gsicc_init_device_profile_struct(dev, NULL, 0); |
277 | 0 | if (code < 0) |
278 | 0 | return code; |
279 | 0 | code = dev_proc(dev, get_profile)(dev, &dev_profile); |
280 | 0 | if (code < 0) |
281 | 0 | return code; |
282 | 0 | } |
283 | 0 | if (dev_profile->oi_profile != NULL) { |
284 | 0 | return 0; /* Allow only one setting of this object */ |
285 | 0 | } |
286 | 0 | code = dict_find_string(op, "N", &pnval); |
287 | 0 | if (code < 0) |
288 | 0 | return code; |
289 | 0 | if (code == 0) |
290 | 0 | return_error(gs_error_undefined); |
291 | 0 | if (r_type(pnval) != t_integer) |
292 | 0 | return gs_note_error(gs_error_typecheck); |
293 | 0 | ncomps = pnval->value.intval; |
294 | | |
295 | | /* verify the DataSource entry. Creat profile from stream */ |
296 | 0 | if (dict_find_string(op, "DataSource", &pstrmval) <= 0) |
297 | 0 | return_error(gs_error_undefined); |
298 | 0 | check_read_file(i_ctx_p, s, pstrmval); |
299 | | |
300 | 0 | picc_profile = gsicc_profile_new(s, gs_gstate_memory(igs), NULL, 0); |
301 | 0 | if (picc_profile == NULL) |
302 | 0 | return gs_throw(gs_error_VMerror, "Creation of ICC profile failed"); |
303 | 0 | picc_profile->num_comps = ncomps; |
304 | 0 | picc_profile->profile_handle = |
305 | 0 | gsicc_get_profile_handle_buffer(picc_profile->buffer, |
306 | 0 | picc_profile->buffer_size, |
307 | 0 | gs_gstate_memory(igs)); |
308 | 0 | if (picc_profile->profile_handle == NULL) { |
309 | 0 | rc_decrement(picc_profile,"zset_outputintent"); |
310 | 0 | return -1; |
311 | 0 | } |
312 | 0 | picc_profile->data_cs = |
313 | 0 | gscms_get_profile_data_space(picc_profile->profile_handle, |
314 | 0 | picc_profile->memory); |
315 | 0 | switch (picc_profile->data_cs) { |
316 | 0 | case gsCIEXYZ: |
317 | 0 | case gsCIELAB: |
318 | 0 | case gsRGB: |
319 | 0 | expected = 3; |
320 | 0 | source_profile = icc_manager->default_rgb; |
321 | 0 | break; |
322 | 0 | case gsGRAY: |
323 | 0 | expected = 1; |
324 | 0 | source_profile = icc_manager->default_gray; |
325 | 0 | break; |
326 | 0 | case gsCMYK: |
327 | 0 | expected = 4; |
328 | 0 | source_profile = icc_manager->default_cmyk; |
329 | 0 | break; |
330 | 0 | case gsNCHANNEL: |
331 | 0 | expected = 0; |
332 | 0 | break; |
333 | 0 | case gsNAMED: |
334 | 0 | case gsUNDEFINED: |
335 | 0 | break; |
336 | 0 | } |
337 | 0 | if (expected && ncomps != expected) { |
338 | 0 | rc_decrement(picc_profile,"zset_outputintent"); |
339 | 0 | return_error(gs_error_rangecheck); |
340 | 0 | } |
341 | 0 | gsicc_init_hash_cs(picc_profile, igs); |
342 | | |
343 | | /* All is well with the profile. Lets set the stuff that needs to be set */ |
344 | 0 | dev_profile->oi_profile = picc_profile; |
345 | 0 | picc_profile->name = (char *) gs_alloc_bytes(picc_profile->memory, |
346 | 0 | MAX_DEFAULT_ICC_LENGTH, |
347 | 0 | "zset_outputintent"); |
348 | 0 | if (picc_profile->name == NULL) { |
349 | 0 | rc_decrement(picc_profile,"zset_outputintent"); |
350 | 0 | return_error(gs_error_VMerror); |
351 | 0 | } |
352 | 0 | strncpy(picc_profile->name, OI_PROFILE, strlen(OI_PROFILE)); |
353 | 0 | picc_profile->name[strlen(OI_PROFILE)] = 0; |
354 | 0 | picc_profile->name_length = strlen(OI_PROFILE); |
355 | | /* Set the range of the profile */ |
356 | 0 | gsicc_set_icc_range(&picc_profile); |
357 | | |
358 | | /* If the output device has a different number of componenets, then we are |
359 | | going to set the output intent as the proofing profile, unless the |
360 | | proofing profile has already been set. |
361 | | |
362 | | If the device has the same number of components (and color model) then as |
363 | | the profile we will use this as the output profile, unless someone has |
364 | | explicitly set the output profile. |
365 | | |
366 | | Finally, we will use the output intent profile for the default profile |
367 | | of the proper Device profile in the icc manager, again, unless someone |
368 | | has explicitly set this default profile. */ |
369 | |
|
370 | 0 | dev_comps = dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]->num_comps; |
371 | 0 | index = gsicc_get_default_type(dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]); |
372 | 0 | if (ncomps == dev_comps && index < gs_color_space_index_DevicePixel) { |
373 | | /* The OI profile is the same type as the profile for the device and a |
374 | | "default" profile for the device was not externally set. So we go |
375 | | ahead and use the OI profile as the device profile. Care needs to be |
376 | | taken here to keep from screwing up any device parameters. We will |
377 | | use a keyword of OIProfile for the user/device parameter to indicate |
378 | | its usage. Also, note conflicts if one is setting object dependent |
379 | | color management */ |
380 | 0 | rc_assign(dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE], picc_profile, |
381 | 0 | "zset_outputintent"); |
382 | 0 | if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used for device profile\n"); |
383 | 0 | } else { |
384 | 0 | if (dev_profile->proof_profile == NULL) { |
385 | | /* This means that we should use the OI profile as the proofing |
386 | | profile. Note that if someone already has specified a |
387 | | proofing profile it is unclear what they are trying to do |
388 | | with the output intent. In this case, we will use it |
389 | | just for the source data below */ |
390 | 0 | dev_profile->proof_profile = picc_profile; |
391 | 0 | rc_increment(picc_profile); |
392 | 0 | if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used for proof profile\n"); |
393 | 0 | } |
394 | 0 | } |
395 | | /* Now the source colors. See which source color space needs to use the |
396 | | output intent ICC profile */ |
397 | 0 | index = gsicc_get_default_type(source_profile); |
398 | 0 | if (index < gs_color_space_index_DevicePixel) { |
399 | | /* source_profile is currently the default. Set it to the OI profile */ |
400 | 0 | switch (picc_profile->data_cs) { |
401 | 0 | case gsGRAY: |
402 | 0 | if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source Gray\n"); |
403 | 0 | rc_assign(icc_manager->default_gray, picc_profile, |
404 | 0 | "zset_outputintent"); |
405 | 0 | break; |
406 | 0 | case gsRGB: |
407 | 0 | if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source RGB\n"); |
408 | 0 | rc_assign(icc_manager->default_rgb, picc_profile, |
409 | 0 | "zset_outputintent"); |
410 | 0 | break; |
411 | 0 | case gsCMYK: |
412 | 0 | if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source CMYK\n"); |
413 | 0 | rc_assign(icc_manager->default_cmyk, picc_profile, |
414 | 0 | "zset_outputintent"); |
415 | 0 | break; |
416 | 0 | default: |
417 | 0 | break; |
418 | 0 | } |
419 | 0 | } |
420 | | /* Remove the output intent dict from the stack */ |
421 | 0 | pop(1); |
422 | 0 | return code; |
423 | 0 | } |
424 | | |
425 | | /* Install a ICC type color space and use the ICC LABLUT profile. */ |
426 | | int |
427 | | seticc_lab(i_ctx_t * i_ctx_p, float *white, float *black, float *range_buff) |
428 | 0 | { |
429 | 0 | int code; |
430 | 0 | gs_color_space * pcs; |
431 | 0 | int i; |
432 | | |
433 | | /* build the color space object */ |
434 | 0 | code = gs_cspace_build_ICC(&pcs, NULL, gs_gstate_memory(igs)); |
435 | 0 | if (code < 0) |
436 | 0 | return gs_rethrow(code, "building color space object"); |
437 | | /* record the current space as the alternative color space */ |
438 | | /* Get the lab profile. It may already be set in the icc manager. |
439 | | If not then lets populate it. */ |
440 | 0 | if (igs->icc_manager->lab_profile == NULL ) { |
441 | | /* This can't happen as the profile |
442 | | should be initialized during the |
443 | | setting of the user params */ |
444 | 0 | return gs_rethrow(code, "cannot find lab icc profile"); |
445 | 0 | } |
446 | | /* Assign the LAB to LAB profile to this color space */ |
447 | 0 | code = gsicc_set_gscs_profile(pcs, igs->icc_manager->lab_profile, gs_gstate_memory(igs)); |
448 | 0 | if (code < 0) |
449 | 0 | return gs_rethrow(code, "installing the lab profile"); |
450 | 0 | pcs->cmm_icc_profile_data->Range.ranges[0].rmin = 0.0; |
451 | 0 | pcs->cmm_icc_profile_data->Range.ranges[0].rmax = 100.0; |
452 | 0 | for (i = 1; i < 3; i++) { |
453 | 0 | pcs->cmm_icc_profile_data->Range.ranges[i].rmin = |
454 | 0 | range_buff[2 * (i-1)]; |
455 | 0 | pcs->cmm_icc_profile_data->Range.ranges[i].rmax = |
456 | 0 | range_buff[2 * (i-1) + 1]; |
457 | 0 | } |
458 | | /* Set the color space. We are done. */ |
459 | 0 | code = gs_setcolorspace(igs, pcs); |
460 | 0 | return code; |
461 | 0 | } |
462 | | |
463 | | /* Install an ICC space from the PDF CalRGB or CalGray types */ |
464 | | int |
465 | | seticc_cal(i_ctx_t * i_ctx_p, float *white, float *black, float *gamma, |
466 | | float *matrix, int num_colorants, ulong dictkey) |
467 | 0 | { |
468 | 0 | int code; |
469 | 0 | gs_color_space * pcs; |
470 | 0 | gs_memory_t *mem = igs->memory; |
471 | 0 | int i; |
472 | 0 | cmm_profile_t *cal_profile; |
473 | | |
474 | | /* See if the color space is in the profile cache */ |
475 | 0 | pcs = gsicc_find_cs(dictkey, igs); |
476 | 0 | if (pcs != NULL && gs_color_space_num_components(pcs) != num_colorants) { |
477 | 0 | pcs = NULL; |
478 | 0 | dictkey = 0; |
479 | 0 | } |
480 | 0 | if (pcs == NULL ) { |
481 | | /* build the color space object. Since this is cached |
482 | | in the profile cache which is a member variable |
483 | | of the graphic state, we will want to use stable |
484 | | memory here */ |
485 | 0 | code = gs_cspace_build_ICC(&pcs, NULL, mem->stable_memory); |
486 | 0 | if (code < 0) |
487 | 0 | return gs_rethrow(code, "building color space object"); |
488 | | /* There is no alternate for this. Perhaps we should set DeviceRGB? */ |
489 | 0 | pcs->base_space = NULL; |
490 | | /* Create the ICC profile from the CalRGB or CalGray parameters */ |
491 | 0 | cal_profile = gsicc_create_from_cal(white, black, gamma, matrix, |
492 | 0 | mem->stable_memory, num_colorants); |
493 | 0 | if (cal_profile == NULL) |
494 | 0 | return gs_rethrow(gs_error_VMerror, "creating the cal profile failed"); |
495 | | /* Assign the profile to this color space */ |
496 | 0 | code = gsicc_set_gscs_profile(pcs, cal_profile, mem->stable_memory); |
497 | | /* profile is created with ref count of 1, gsicc_set_gscs_profile() |
498 | | * increments the ref count, so we need to decrement it here. |
499 | | */ |
500 | 0 | rc_decrement(cal_profile, "seticc_cal"); |
501 | 0 | if (code < 0) |
502 | 0 | return gs_rethrow(code, "installing the cal profile"); |
503 | 0 | for (i = 0; i < num_colorants; i++) { |
504 | 0 | pcs->cmm_icc_profile_data->Range.ranges[i].rmin = 0; |
505 | 0 | pcs->cmm_icc_profile_data->Range.ranges[i].rmax = 1; |
506 | 0 | } |
507 | | /* Add the color space to the profile cache */ |
508 | 0 | code = gsicc_add_cs(igs, pcs,dictkey); |
509 | 0 | if (code < 0) |
510 | 0 | return gs_rethrow(code, "adding the colour space"); |
511 | 0 | } |
512 | | /* Set the color space. We are done. */ |
513 | 0 | code = gs_setcolorspace(igs, pcs); |
514 | 0 | return code; |
515 | 0 | } |
516 | | |
517 | | static int |
518 | | znumicc_components(i_ctx_t * i_ctx_p) |
519 | 0 | { |
520 | 0 | ref * pnval; |
521 | 0 | ref * pstrmval; |
522 | 0 | stream * s; |
523 | 0 | int ncomps, expected = 0, code; |
524 | 0 | cmm_profile_t *picc_profile; |
525 | 0 | os_ptr op = osp; |
526 | |
|
527 | 0 | check_op(1); |
528 | 0 | check_type(*op, t_dictionary); |
529 | 0 | check_dict_read(*op); |
530 | | |
531 | 0 | code = dict_find_string(op, "N", &pnval); |
532 | 0 | if (code < 0) |
533 | 0 | return code; |
534 | 0 | if (code == 0) |
535 | 0 | return_error(gs_error_undefined); |
536 | 0 | if (r_type(pnval) != t_integer) |
537 | 0 | return gs_note_error(gs_error_typecheck); |
538 | 0 | ncomps = pnval->value.intval; |
539 | | /* verify the DataSource entry. Create profile from stream */ |
540 | 0 | if (dict_find_string(op, "DataSource", &pstrmval) <= 0) |
541 | 0 | return_error(gs_error_undefined); |
542 | 0 | check_read_file(i_ctx_p, s, pstrmval); |
543 | | |
544 | 0 | picc_profile = gsicc_profile_new(s, gs_gstate_memory(igs), NULL, 0); |
545 | 0 | if (picc_profile == NULL) |
546 | 0 | return gs_throw(gs_error_VMerror, "Creation of ICC profile failed"); |
547 | | |
548 | 0 | picc_profile->num_comps = ncomps; |
549 | 0 | picc_profile->profile_handle = |
550 | 0 | gsicc_get_profile_handle_buffer(picc_profile->buffer, |
551 | 0 | picc_profile->buffer_size, |
552 | 0 | gs_gstate_memory(igs)); |
553 | 0 | if (picc_profile->profile_handle == NULL) { |
554 | 0 | rc_decrement(picc_profile,"znumicc_components"); |
555 | 0 | make_int(op, expected); |
556 | 0 | return 0; |
557 | 0 | } |
558 | 0 | picc_profile->data_cs = |
559 | 0 | gscms_get_profile_data_space(picc_profile->profile_handle, |
560 | 0 | picc_profile->memory); |
561 | |
|
562 | 0 | switch (picc_profile->data_cs) { |
563 | 0 | case gsCIEXYZ: |
564 | 0 | case gsCIELAB: |
565 | 0 | case gsRGB: |
566 | 0 | expected = 3; |
567 | 0 | break; |
568 | 0 | case gsGRAY: |
569 | 0 | expected = 1; |
570 | 0 | break; |
571 | 0 | case gsCMYK: |
572 | 0 | expected = 4; |
573 | 0 | break; |
574 | 0 | case gsNCHANNEL: |
575 | 0 | expected = 0; |
576 | 0 | break; |
577 | 0 | case gsNAMED: |
578 | 0 | case gsUNDEFINED: |
579 | 0 | expected = -1; |
580 | 0 | break; |
581 | 0 | } |
582 | | |
583 | 0 | make_int(op, expected); |
584 | |
|
585 | 0 | rc_decrement(picc_profile,"zset_outputintent"); |
586 | 0 | return 0; |
587 | 0 | } |
588 | | |
589 | | const op_def zicc_op_defs[] = { |
590 | | { "1.set_outputintent", zset_outputintent }, |
591 | | { "1.numicc_components", znumicc_components }, |
592 | | op_def_end(0) |
593 | | }; |