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