/src/ghostpdl/base/gsicc_lcms2mt.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 | | /* gsicc interface to LittleCMS2-MT */ |
18 | | |
19 | | #include "memory_.h" |
20 | | #include "lcms2mt.h" |
21 | | #include "lcms2mt_plugin.h" |
22 | | #include "gslibctx.h" |
23 | | #include "gserrors.h" |
24 | | #include "gp.h" |
25 | | #include "gsicc_cms.h" |
26 | | #include "gxdevice.h" |
27 | | |
28 | | #ifdef WITH_CAL |
29 | | #include "cal.h" |
30 | | #endif |
31 | | |
32 | | #define USE_LCMS2_LOCKING |
33 | | |
34 | | #ifdef USE_LCMS2_LOCKING |
35 | | #include "gxsync.h" |
36 | | #endif |
37 | | |
38 | | #define DUMP_CMS_BUFFER 0 |
39 | | #define DEBUG_LCMS_MEM 0 |
40 | | #define LCMS_BYTES_MASK T_BYTES(-1) /* leaves only mask for the BYTES (currently 7) */ |
41 | | #define LCMS_ENDIAN16_MASK T_ENDIAN16(-1) /* similarly, for ENDIAN16 bit */ |
42 | | |
43 | | #define gsicc_link_flags(hasalpha, planarIN, planarOUT, endianswapIN, endianswapOUT, bytesIN, bytesOUT) \ |
44 | 410M | ((hasalpha != 0) << 2 | \ |
45 | 410M | (planarIN != 0) << 5 | (planarOUT != 0) << 4 | \ |
46 | 410M | (endianswapIN != 0) << 3 | (endianswapOUT != 0) << 2 | \ |
47 | 410M | (bytesIN == 1) << 1 | (bytesOUT == 1)) |
48 | | |
49 | | typedef struct gsicc_lcms2mt_link_list_s { |
50 | | int flags; |
51 | | cmsHTRANSFORM *hTransform; |
52 | | struct gsicc_lcms2mt_link_list_s *next; |
53 | | } gsicc_lcms2mt_link_list_t; |
54 | | |
55 | | /* Only provide warning about issues in lcms if debug build */ |
56 | | static void |
57 | | gscms_error(cmsContext ContextID, |
58 | | cmsUInt32Number error_code, |
59 | | const char *error_text) |
60 | 1.48M | { |
61 | | #ifdef DEBUG |
62 | | gs_warn1("cmm error : %s",error_text); |
63 | | #endif |
64 | 1.48M | } |
65 | | |
66 | | static |
67 | | void *gs_lcms2_malloc(cmsContext id, unsigned int size) |
68 | 144M | { |
69 | 144M | void *ptr; |
70 | 144M | gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id); |
71 | | |
72 | | #if defined(SHARE_LCMS) && SHARE_LCMS==1 |
73 | | ptr = malloc(size); |
74 | | #else |
75 | 144M | ptr = gs_alloc_bytes(mem, size, "lcms"); |
76 | 144M | #endif |
77 | | |
78 | | #if DEBUG_LCMS_MEM |
79 | | gs_warn2("lcms malloc (%d) at 0x%x",size,ptr); |
80 | | #endif |
81 | 144M | return ptr; |
82 | 144M | } |
83 | | |
84 | | static |
85 | | void gs_lcms2_free(cmsContext id, void *ptr) |
86 | 144M | { |
87 | 144M | gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id); |
88 | 144M | if (ptr != NULL) { |
89 | | #if DEBUG_LCMS_MEM |
90 | | gs_warn1("lcms free at 0x%x",ptr); |
91 | | #endif |
92 | | |
93 | | #if defined(SHARE_LCMS) && SHARE_LCMS==1 |
94 | | free(ptr); |
95 | | #else |
96 | 144M | gs_free_object(mem, ptr, "lcms"); |
97 | 144M | #endif |
98 | 144M | } |
99 | 144M | } |
100 | | |
101 | | static |
102 | | void *gs_lcms2_realloc(cmsContext id, void *ptr, unsigned int size) |
103 | 5.15M | { |
104 | 5.15M | gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id); |
105 | 5.15M | void *ptr2; |
106 | | |
107 | 5.15M | if (ptr == 0) |
108 | 5.15M | return gs_lcms2_malloc(id, size); |
109 | 0 | if (size == 0) |
110 | 0 | { |
111 | 0 | gs_lcms2_free(id, ptr); |
112 | 0 | return NULL; |
113 | 0 | } |
114 | | #if defined(SHARE_LCMS) && SHARE_LCMS==1 |
115 | | ptr2 = realloc(ptr, size); |
116 | | #else |
117 | 0 | ptr2 = gs_resize_object(mem, ptr, size, "lcms"); |
118 | 0 | #endif |
119 | |
|
120 | | #if DEBUG_LCMS_MEM |
121 | | gs_warn3("lcms realloc (%x,%d) at 0x%x",ptr,size,ptr2); |
122 | | #endif |
123 | 0 | return ptr2; |
124 | 0 | } |
125 | | |
126 | | static cmsPluginMemHandler gs_cms_memhandler = |
127 | | { |
128 | | { |
129 | | cmsPluginMagicNumber, |
130 | | LCMS_VERSION, |
131 | | cmsPluginMemHandlerSig, |
132 | | NULL |
133 | | }, |
134 | | gs_lcms2_malloc, |
135 | | gs_lcms2_free, |
136 | | gs_lcms2_realloc, |
137 | | NULL, |
138 | | NULL, |
139 | | NULL, |
140 | | }; |
141 | | |
142 | | #ifdef USE_LCMS2_LOCKING |
143 | | |
144 | | static |
145 | | void *gs_lcms2_createMutex(cmsContext id) |
146 | 2.04M | { |
147 | 2.04M | gs_memory_t *mem = (gs_memory_t *)cmsGetContextUserData(id); |
148 | | |
149 | 2.04M | return gx_monitor_label(gx_monitor_alloc(mem), "lcms2"); |
150 | 2.04M | } |
151 | | |
152 | | static |
153 | | void gs_lcms2_destroyMutex(cmsContext id, void* mtx) |
154 | 2.04M | { |
155 | 2.04M | gx_monitor_free((gx_monitor_t *)mtx); |
156 | 2.04M | } |
157 | | |
158 | | static |
159 | | cmsBool gs_lcms2_lockMutex(cmsContext id, void* mtx) |
160 | 20.0M | { |
161 | 20.0M | return !gx_monitor_enter((gx_monitor_t *)mtx); |
162 | 20.0M | } |
163 | | |
164 | | static |
165 | | void gs_lcms2_unlockMutex(cmsContext id, void* mtx) |
166 | 20.0M | { |
167 | 20.0M | gx_monitor_leave((gx_monitor_t *)mtx); |
168 | 20.0M | } |
169 | | |
170 | | static cmsPluginMutex gs_cms_mutexhandler = |
171 | | { |
172 | | { |
173 | | cmsPluginMagicNumber, |
174 | | LCMS_VERSION, |
175 | | cmsPluginMutexSig, |
176 | | NULL |
177 | | }, |
178 | | gs_lcms2_createMutex, |
179 | | gs_lcms2_destroyMutex, |
180 | | gs_lcms2_lockMutex, |
181 | | gs_lcms2_unlockMutex |
182 | | }; |
183 | | |
184 | | #endif |
185 | | |
186 | | static int |
187 | | gscms_get_accuracy(gs_memory_t *mem) |
188 | 491k | { |
189 | 491k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
190 | | |
191 | 491k | switch (ctx->icc_color_accuracy) { |
192 | 0 | case 0: |
193 | 0 | return cmsFLAGS_LOWRESPRECALC; |
194 | 0 | case 1: |
195 | 0 | return 0; |
196 | 491k | case 2: |
197 | 491k | default: |
198 | 491k | return cmsFLAGS_HIGHRESPRECALC; |
199 | 491k | } |
200 | 491k | } |
201 | | |
202 | | /* Get the number of channels for the profile. |
203 | | Input count */ |
204 | | int |
205 | | gscms_get_input_channel_count(gcmmhprofile_t profile, gs_memory_t *memory) |
206 | 720k | { |
207 | 720k | cmsColorSpaceSignature colorspace; |
208 | 720k | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
209 | | |
210 | 720k | colorspace = cmsGetColorSpace(ctx, profile); |
211 | 720k | return cmsChannelsOf(ctx, colorspace); |
212 | 720k | } |
213 | | |
214 | | /* Get the number of output channels for the profile */ |
215 | | int |
216 | | gscms_get_output_channel_count(gcmmhprofile_t profile, gs_memory_t *memory) |
217 | 720k | { |
218 | 720k | cmsColorSpaceSignature colorspace; |
219 | 720k | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
220 | | |
221 | 720k | colorspace = cmsGetPCS(ctx, profile); |
222 | 720k | return cmsChannelsOf(ctx, colorspace); |
223 | 720k | } |
224 | | |
225 | | /* Get the number of colorant names in the clrt tag */ |
226 | | int |
227 | | gscms_get_numberclrtnames(gcmmhprofile_t profile, gs_memory_t *memory) |
228 | 0 | { |
229 | 0 | cmsNAMEDCOLORLIST *lcms_names; |
230 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
231 | |
|
232 | 0 | lcms_names = (cmsNAMEDCOLORLIST *)cmsReadTag(ctx, profile, |
233 | 0 | cmsSigColorantTableTag); |
234 | 0 | return cmsNamedColorCount(ctx, lcms_names); |
235 | 0 | } |
236 | | |
237 | | /* Get the nth colorant name in the clrt tag */ |
238 | | char* |
239 | | gscms_get_clrtname(gcmmhprofile_t profile, int colorcount, gs_memory_t *memory) |
240 | 0 | { |
241 | 0 | cmsNAMEDCOLORLIST *lcms_names; |
242 | 0 | char name[256]; |
243 | 0 | char *buf; |
244 | 0 | int length; |
245 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
246 | |
|
247 | 0 | lcms_names = (cmsNAMEDCOLORLIST *)cmsReadTag(ctx, profile, |
248 | 0 | cmsSigColorantTableTag); |
249 | 0 | if (colorcount >= cmsNamedColorCount(ctx, lcms_names)) return(NULL); |
250 | 0 | if (cmsNamedColorInfo(ctx, lcms_names, colorcount, name, NULL, NULL, NULL, |
251 | 0 | NULL) == 0) |
252 | 0 | return NULL; |
253 | 0 | length = strlen(name); |
254 | 0 | buf = (char*) gs_alloc_bytes(memory, length + 1, "gscms_get_clrtname"); |
255 | 0 | if (buf) |
256 | 0 | strcpy(buf, name); |
257 | 0 | return buf; |
258 | 0 | } |
259 | | |
260 | | /* Check if the profile is a device link type */ |
261 | | bool |
262 | | gscms_is_device_link(gcmmhprofile_t profile, gs_memory_t *memory) |
263 | 0 | { |
264 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
265 | |
|
266 | 0 | return cmsGetDeviceClass(ctx, profile) == cmsSigLinkClass; |
267 | 0 | } |
268 | | |
269 | | /* Needed for v2 profile creation */ |
270 | | int |
271 | | gscms_get_device_class(gcmmhprofile_t profile, gs_memory_t *memory) |
272 | 7 | { |
273 | 7 | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
274 | | |
275 | 7 | return cmsGetDeviceClass(ctx, profile); |
276 | 7 | } |
277 | | |
278 | | /* Check if the profile is a input type */ |
279 | | bool |
280 | | gscms_is_input(gcmmhprofile_t profile, gs_memory_t *memory) |
281 | 0 | { |
282 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
283 | |
|
284 | 0 | return cmsGetDeviceClass(ctx, profile) == cmsSigInputClass; |
285 | 0 | } |
286 | | |
287 | | /* Get the device space associated with this profile */ |
288 | | gsicc_colorbuffer_t |
289 | | gscms_get_profile_data_space(gcmmhprofile_t profile, gs_memory_t *memory) |
290 | 745k | { |
291 | 745k | cmsColorSpaceSignature colorspace; |
292 | 745k | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
293 | | |
294 | 745k | colorspace = cmsGetColorSpace(ctx, profile); |
295 | 745k | switch (colorspace) { |
296 | 0 | case cmsSigXYZData: |
297 | 0 | return gsCIEXYZ; |
298 | 447 | case cmsSigLabData: |
299 | 447 | return gsCIELAB; |
300 | 196k | case cmsSigRgbData: |
301 | 196k | return gsRGB; |
302 | 469k | case cmsSigGrayData: |
303 | 469k | return gsGRAY; |
304 | 77.8k | case cmsSigCmykData: |
305 | 77.8k | return gsCMYK; |
306 | 185 | default: |
307 | 185 | return gsNCHANNEL; |
308 | 745k | } |
309 | 745k | } |
310 | | |
311 | | /* Get ICC Profile handle from buffer */ |
312 | | gcmmhprofile_t |
313 | | gscms_get_profile_handle_mem(unsigned char *buffer, unsigned int input_size, |
314 | | gs_memory_t *mem) |
315 | 759k | { |
316 | 759k | cmsContext ctx = gs_lib_ctx_get_cms_context(mem); |
317 | | |
318 | 759k | cmsSetLogErrorHandler(ctx, gscms_error); |
319 | 759k | return cmsOpenProfileFromMem(ctx,buffer,input_size); |
320 | 759k | } |
321 | | |
322 | | /* Get ICC Profile handle from file ptr */ |
323 | | gcmmhprofile_t |
324 | | gscms_get_profile_handle_file(const char *filename, gs_memory_t *mem) |
325 | 0 | { |
326 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(mem); |
327 | |
|
328 | 0 | return cmsOpenProfileFromFile(ctx, filename, "r"); |
329 | 0 | } |
330 | | |
331 | | /* Transform an entire buffer */ |
332 | | int |
333 | | gscms_transform_color_buffer(gx_device *dev, gsicc_link_t *icclink, |
334 | | gsicc_bufferdesc_t *input_buff_desc, |
335 | | gsicc_bufferdesc_t *output_buff_desc, |
336 | | void *inputbuffer, void *outputbuffer) |
337 | 3.87M | { |
338 | 3.87M | gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle); |
339 | 3.87M | cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform; |
340 | 3.87M | cmsUInt32Number dwInputFormat, dwOutputFormat, num_src_lcms, num_des_lcms; |
341 | 3.87M | int hasalpha, planarIN, planarOUT, numbytesIN, numbytesOUT, swap_endianIN, swap_endianOUT; |
342 | 3.87M | int needed_flags = 0; |
343 | 3.87M | unsigned char *inputpos, *outputpos; |
344 | 3.87M | cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory); |
345 | | |
346 | | #if DUMP_CMS_BUFFER |
347 | | gp_file *fid_in, *fid_out; |
348 | | #endif |
349 | | /* Although little CMS does make assumptions about data types in its |
350 | | transformations we can change it after the fact by cloning from any |
351 | | other transform. We always create [0] which is no_alpha, chunky IN/OUT, |
352 | | no endian swap IN/OUT, 2-bytes_per_component IN/OUT. */ |
353 | | /* Set us to the proper output type */ |
354 | | /* Note, we could speed this up by passing back the encoded data type |
355 | | to the caller so that we could avoid having to go through this |
356 | | computation each time if they are doing multiple calls to this |
357 | | operation, but this is working on a buffer at a time. */ |
358 | | |
359 | | /* Now set if we have planar, num bytes, endian case, and alpha data to skip */ |
360 | | /* Planar -- pdf14 case for example */ |
361 | 3.87M | planarIN = input_buff_desc->is_planar; |
362 | 3.87M | planarOUT = output_buff_desc->is_planar; |
363 | | |
364 | | /* 8 or 16 byte input and output */ |
365 | 3.87M | numbytesIN = input_buff_desc->bytes_per_chan; |
366 | 3.87M | numbytesOUT = output_buff_desc->bytes_per_chan; |
367 | 3.87M | if (numbytesIN > 2 || numbytesOUT > 2) |
368 | 0 | return_error(gs_error_rangecheck); /* TODO: we don't support float */ |
369 | | |
370 | | /* endian */ |
371 | 3.87M | swap_endianIN = input_buff_desc->endian_swap; |
372 | 3.87M | swap_endianOUT = output_buff_desc->endian_swap; |
373 | | |
374 | | /* alpha, which is passed through unmolested */ |
375 | | /* TODO: Right now we always must have alpha last */ |
376 | | /* This is really only going to be an issue when we have interleaved alpha data */ |
377 | 3.87M | hasalpha = input_buff_desc->has_alpha; |
378 | | |
379 | 3.87M | needed_flags = gsicc_link_flags(hasalpha, planarIN, planarOUT, |
380 | 3.87M | swap_endianIN, swap_endianOUT, |
381 | 3.87M | numbytesIN, numbytesOUT); |
382 | 7.74M | while (link_handle->flags != needed_flags) { |
383 | 3.87M | if (link_handle->next == NULL) { |
384 | 5.18k | hTransform = NULL; |
385 | 5.18k | break; |
386 | 3.86M | } else { |
387 | 3.86M | link_handle = link_handle->next; |
388 | 3.86M | hTransform = link_handle->hTransform; |
389 | 3.86M | } |
390 | 3.87M | } |
391 | 3.87M | if (hTransform == NULL) { |
392 | | /* the variant we want wasn't present, clone it from the last on the list */ |
393 | 5.18k | gsicc_lcms2mt_link_list_t *new_link_handle = |
394 | 5.18k | (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(icclink->memory->non_gc_memory, |
395 | 5.18k | sizeof(gsicc_lcms2mt_link_list_t), |
396 | 5.18k | "gscms_transform_color_buffer"); |
397 | 5.18k | if (new_link_handle == NULL) { |
398 | 0 | return_error(gs_error_VMerror); |
399 | 0 | } |
400 | 5.18k | new_link_handle->next = NULL; /* new end of list */ |
401 | 5.18k | new_link_handle->flags = needed_flags; |
402 | 5.18k | hTransform = link_handle->hTransform; /* doesn't really matter which we start with */ |
403 | | /* Color space MUST be the same */ |
404 | 5.18k | dwInputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformInputFormat(ctx, hTransform))); |
405 | 5.18k | dwOutputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformOutputFormat(ctx, hTransform))); |
406 | | /* number of channels. This should not really be changing! */ |
407 | 5.18k | num_src_lcms = T_CHANNELS(cmsGetTransformInputFormat(ctx, hTransform)); |
408 | 5.18k | num_des_lcms = T_CHANNELS(cmsGetTransformOutputFormat(ctx, hTransform)); |
409 | 5.18k | if (num_src_lcms != input_buff_desc->num_chan || |
410 | 5.18k | num_des_lcms != output_buff_desc->num_chan) { |
411 | | /* We can't transform this. Someone is doing something odd */ |
412 | 0 | return_error(gs_error_unknownerror); |
413 | 0 | } |
414 | 5.18k | dwInputFormat = dwInputFormat | CHANNELS_SH(num_src_lcms); |
415 | 5.18k | dwOutputFormat = dwOutputFormat | CHANNELS_SH(num_des_lcms); |
416 | | /* set the remaining parameters, alpha, planar, num_bytes */ |
417 | 5.18k | dwInputFormat = dwInputFormat | EXTRA_SH(hasalpha); |
418 | 5.18k | dwOutputFormat = dwOutputFormat | EXTRA_SH(hasalpha); |
419 | 5.18k | dwInputFormat = dwInputFormat | PLANAR_SH(planarIN); |
420 | 5.18k | dwOutputFormat = dwOutputFormat | PLANAR_SH(planarOUT); |
421 | 5.18k | dwInputFormat = dwInputFormat | ENDIAN16_SH(swap_endianIN); |
422 | 5.18k | dwOutputFormat = dwOutputFormat | ENDIAN16_SH(swap_endianOUT); |
423 | 5.18k | dwInputFormat = dwInputFormat | BYTES_SH(numbytesIN); |
424 | 5.18k | dwOutputFormat = dwOutputFormat | BYTES_SH(numbytesOUT); |
425 | | |
426 | 5.18k | hTransform = cmsCloneTransformChangingFormats(ctx, hTransform, dwInputFormat, dwOutputFormat); |
427 | 5.18k | if (hTransform == NULL) |
428 | 0 | return_error(gs_error_unknownerror); |
429 | | /* Now we have a new hTransform to add to the list, BUT some other thread */ |
430 | | /* may have been working in the same one. Lock, check again and add to */ |
431 | | /* the (potentially new) end of the list */ |
432 | 5.18k | gx_monitor_enter(icclink->lock); |
433 | 5.18k | while (link_handle->next != NULL) { |
434 | 0 | if (link_handle->flags == needed_flags) { |
435 | | /* OOPS. Someone else did it while we were building it */ |
436 | 0 | cmsDeleteTransform(ctx, hTransform); |
437 | 0 | hTransform = link_handle->hTransform; |
438 | 0 | new_link_handle = NULL; |
439 | 0 | break; |
440 | 0 | } |
441 | 0 | link_handle = link_handle->next; |
442 | 0 | } |
443 | 5.18k | gx_monitor_leave(icclink->lock); |
444 | 5.18k | if (new_link_handle != NULL) { |
445 | 5.18k | new_link_handle->hTransform = hTransform; |
446 | 5.18k | link_handle->next = new_link_handle; /* link to end of list */ |
447 | 5.18k | } |
448 | 5.18k | } |
449 | | |
450 | 3.87M | inputpos = (byte *) inputbuffer; |
451 | 3.87M | outputpos = (byte *) outputbuffer; |
452 | 3.87M | cmsDoTransformLineStride(ctx, hTransform, |
453 | 3.87M | inputpos, outputpos, input_buff_desc->pixels_per_row, |
454 | 3.87M | input_buff_desc->num_rows, input_buff_desc->row_stride, |
455 | 3.87M | output_buff_desc->row_stride, input_buff_desc->plane_stride, |
456 | 3.87M | output_buff_desc->plane_stride); |
457 | | |
458 | | #if DUMP_CMS_BUFFER |
459 | | fid_in = gp_fopen(icclink->memory,"CM_Input.raw","ab"); |
460 | | fid_out = gp_fopen(icclink->memory,"CM_Output.raw","ab"); |
461 | | fwrite((unsigned char*) inputbuffer,sizeof(unsigned char), |
462 | | input_buff_desc->row_stride,fid_in); |
463 | | fwrite((unsigned char*) outputbuffer,sizeof(unsigned char), |
464 | | output_buff_desc->row_stride,fid_out); |
465 | | fclose(fid_in); |
466 | | fclose(fid_out); |
467 | | #endif |
468 | 3.87M | return 0; |
469 | 3.87M | } |
470 | | |
471 | | /* Transform a single color. We assume we have passed to us the proper number |
472 | | of elements of size gx_device_color. It is up to the caller to make sure |
473 | | the proper allocations for the colors are there. */ |
474 | | int |
475 | | gscms_transform_color(gx_device *dev, gsicc_link_t *icclink, void *inputcolor, |
476 | | void *outputcolor, int num_bytes) |
477 | 405M | { |
478 | 405M | return gscms_transform_color_const(dev, icclink, inputcolor, outputcolor, num_bytes); |
479 | 405M | } |
480 | | |
481 | | int |
482 | | gscms_transform_color_const(const gx_device *dev, gsicc_link_t *icclink, void *inputcolor, |
483 | | void *outputcolor, int num_bytes) |
484 | 405M | { |
485 | 405M | gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle); |
486 | 405M | cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform; |
487 | 405M | cmsUInt32Number dwInputFormat, dwOutputFormat; |
488 | 405M | cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory); |
489 | 405M | int big_endianIN, big_endianOUT, needed_flags; |
490 | | |
491 | | /* For a single color, we are going to use the link as it is |
492 | | with the exception of taking care of the word size. */ |
493 | 405M | if (num_bytes > 2) |
494 | 0 | return_error(gs_error_rangecheck); /* TODO: we don't support float */ |
495 | | |
496 | 405M | dwInputFormat = cmsGetTransformInputFormat(ctx, hTransform); |
497 | 405M | big_endianIN = T_ENDIAN16(dwInputFormat); |
498 | 405M | dwOutputFormat = cmsGetTransformOutputFormat(ctx, hTransform); |
499 | 405M | big_endianOUT = T_ENDIAN16(dwOutputFormat); |
500 | | |
501 | 405M | needed_flags = gsicc_link_flags(0, 0, 0, /* alpha and planar not used for single color */ |
502 | 405M | big_endianIN, big_endianOUT, |
503 | 405M | num_bytes, num_bytes); |
504 | 405M | while (link_handle->flags != needed_flags) { |
505 | 0 | if (link_handle->next == NULL) { |
506 | 0 | hTransform = NULL; |
507 | 0 | break; |
508 | 0 | } else { |
509 | 0 | link_handle = link_handle->next; |
510 | 0 | hTransform = link_handle->hTransform; |
511 | 0 | } |
512 | 0 | } |
513 | 405M | if (hTransform == NULL) { |
514 | 0 | gsicc_lcms2mt_link_list_t *new_link_handle = |
515 | 0 | (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(icclink->memory->non_gc_memory, |
516 | 0 | sizeof(gsicc_lcms2mt_link_list_t), |
517 | 0 | "gscms_transform_color_buffer"); |
518 | 0 | if (new_link_handle == NULL) { |
519 | 0 | return_error(gs_error_VMerror); |
520 | 0 | } |
521 | 0 | new_link_handle->next = NULL; /* new end of list */ |
522 | 0 | new_link_handle->flags = needed_flags; |
523 | 0 | hTransform = link_handle->hTransform; |
524 | | |
525 | | /* the variant we want wasn't present, clone it from the HEAD (no alpha, not planar) */ |
526 | 0 | dwInputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformInputFormat(ctx, hTransform))); |
527 | 0 | dwOutputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformOutputFormat(ctx, hTransform))); |
528 | 0 | dwInputFormat = dwInputFormat | CHANNELS_SH(T_CHANNELS(cmsGetTransformInputFormat(ctx, hTransform))); |
529 | 0 | dwOutputFormat = dwOutputFormat | CHANNELS_SH(T_CHANNELS(cmsGetTransformOutputFormat(ctx, hTransform))); |
530 | 0 | dwInputFormat = dwInputFormat | ENDIAN16_SH(big_endianIN); |
531 | 0 | dwOutputFormat = dwOutputFormat | ENDIAN16_SH(big_endianOUT); |
532 | 0 | dwInputFormat = dwInputFormat | BYTES_SH(num_bytes); |
533 | 0 | dwOutputFormat = dwOutputFormat | BYTES_SH(num_bytes); |
534 | | |
535 | | /* Get the transform with the settings we need */ |
536 | 0 | hTransform = cmsCloneTransformChangingFormats(ctx, hTransform, dwInputFormat, dwOutputFormat); |
537 | |
|
538 | 0 | if (hTransform == NULL) |
539 | 0 | return_error(gs_error_unknownerror); |
540 | | |
541 | | /* Now we have a new hTransform to add to the list, BUT some other thread */ |
542 | | /* may have been working in the same one. Lock, check again and add to */ |
543 | | /* the (potentially new) end of the list */ |
544 | 0 | gx_monitor_enter(icclink->lock); |
545 | 0 | while (link_handle->next != NULL) { |
546 | 0 | if (link_handle->flags == needed_flags) { |
547 | | /* OOPS. Someone else did it while we were building it */ |
548 | 0 | cmsDeleteTransform(ctx, hTransform); |
549 | 0 | hTransform = link_handle->hTransform; |
550 | 0 | new_link_handle = NULL; |
551 | 0 | break; |
552 | 0 | } |
553 | 0 | link_handle = link_handle->next; |
554 | 0 | } |
555 | 0 | gx_monitor_leave(icclink->lock); |
556 | 0 | if (new_link_handle != NULL) { |
557 | 0 | new_link_handle->hTransform = hTransform; |
558 | 0 | link_handle->next = new_link_handle; /* link to end of list */ |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | /* Do conversion */ |
563 | 405M | cmsDoTransform(ctx, hTransform, inputcolor, outputcolor, 1); |
564 | | |
565 | 405M | return 0; |
566 | 405M | } |
567 | | |
568 | | /* Get the flag to avoid having to the cmm do any white fix up, it such a flag |
569 | | exists for the cmm */ |
570 | | int |
571 | | gscms_avoid_white_fix_flag(gs_memory_t *memory) |
572 | 0 | { |
573 | 0 | return cmsFLAGS_NOWHITEONWHITEFIXUP; |
574 | 0 | } |
575 | | |
576 | | void |
577 | | gscms_get_link_dim(gcmmhlink_t link, int *num_inputs, int *num_outputs, |
578 | | gs_memory_t *memory) |
579 | 345k | { |
580 | 345k | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
581 | 345k | gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(link); |
582 | 345k | cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform; |
583 | | |
584 | 345k | *num_inputs = T_CHANNELS(cmsGetTransformInputFormat(ctx, hTransform)); |
585 | 345k | *num_outputs = T_CHANNELS(cmsGetTransformOutputFormat(ctx, hTransform)); |
586 | 345k | } |
587 | | |
588 | | /* Get the link from the CMS. TODO: Add error checking */ |
589 | | gcmmhlink_t |
590 | | gscms_get_link(gcmmhprofile_t lcms_srchandle, gcmmhprofile_t lcms_deshandle, |
591 | | gsicc_rendering_param_t *rendering_params, int cmm_flags, |
592 | | gs_memory_t *memory) |
593 | 491k | { |
594 | 491k | cmsUInt32Number src_data_type,des_data_type; |
595 | 491k | cmsColorSpaceSignature src_color_space,des_color_space; |
596 | 491k | int src_nChannels,des_nChannels; |
597 | 491k | int lcms_src_color_space, lcms_des_color_space; |
598 | 491k | unsigned int flag; |
599 | 491k | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
600 | 491k | gsicc_lcms2mt_link_list_t *link_handle; |
601 | | |
602 | | /* Check for case of request for a transfrom from a device link profile |
603 | | in that case, the destination profile is NULL */ |
604 | | |
605 | | /* First handle all the source stuff */ |
606 | 491k | src_color_space = cmsGetColorSpace(ctx, lcms_srchandle); |
607 | 491k | lcms_src_color_space = _cmsLCMScolorSpace(ctx, src_color_space); |
608 | | |
609 | | /* littlecms returns -1 for types it does not (but should) understand */ |
610 | 491k | if (lcms_src_color_space < 0) |
611 | 0 | lcms_src_color_space = 0; |
612 | 491k | src_nChannels = cmsChannelsOf(ctx, src_color_space); |
613 | | |
614 | | /* For now, just do single byte data, interleaved. We can change this |
615 | | when we use the transformation. */ |
616 | 491k | src_data_type = (COLORSPACE_SH(lcms_src_color_space)| |
617 | 491k | CHANNELS_SH(src_nChannels)|BYTES_SH(2)); |
618 | | |
619 | 491k | if (lcms_deshandle != NULL) { |
620 | 491k | des_color_space = cmsGetColorSpace(ctx, lcms_deshandle); |
621 | 491k | } else { |
622 | | /* We must have a device link profile. Use it's PCS space. */ |
623 | 0 | des_color_space = cmsGetPCS(ctx, lcms_srchandle); |
624 | 0 | } |
625 | 491k | lcms_des_color_space = _cmsLCMScolorSpace(ctx, des_color_space); |
626 | 491k | if (lcms_des_color_space < 0) |
627 | 0 | lcms_des_color_space = 0; |
628 | 491k | des_nChannels = cmsChannelsOf(ctx, des_color_space); |
629 | 491k | des_data_type = (COLORSPACE_SH(lcms_des_color_space)| |
630 | 491k | CHANNELS_SH(des_nChannels)|BYTES_SH(2)); |
631 | | |
632 | | /* Set up the flags */ |
633 | 491k | flag = gscms_get_accuracy(memory); |
634 | 491k | if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON |
635 | 491k | || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { |
636 | 469k | flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); |
637 | 469k | } |
638 | 491k | if (rendering_params->preserve_black == gsBLACKPRESERVE_KONLY) { |
639 | 0 | switch (rendering_params->rendering_intent) { |
640 | 0 | case INTENT_PERCEPTUAL: |
641 | 0 | rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_PERCEPTUAL; |
642 | 0 | break; |
643 | 0 | case INTENT_RELATIVE_COLORIMETRIC: |
644 | 0 | rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC; |
645 | 0 | break; |
646 | 0 | case INTENT_SATURATION: |
647 | 0 | rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_SATURATION; |
648 | 0 | break; |
649 | 0 | default: |
650 | 0 | break; |
651 | 0 | } |
652 | 0 | } |
653 | 491k | if (rendering_params->preserve_black == gsBLACKPRESERVE_KPLANE) { |
654 | 0 | switch (rendering_params->rendering_intent) { |
655 | 0 | case INTENT_PERCEPTUAL: |
656 | 0 | rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_PERCEPTUAL; |
657 | 0 | break; |
658 | 0 | case INTENT_RELATIVE_COLORIMETRIC: |
659 | 0 | rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC; |
660 | 0 | break; |
661 | 0 | case INTENT_SATURATION: |
662 | 0 | rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_SATURATION; |
663 | 0 | break; |
664 | 0 | default: |
665 | 0 | break; |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | /* Create the link */ |
670 | 491k | link_handle = (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(memory->non_gc_memory, |
671 | 491k | sizeof(gsicc_lcms2mt_link_list_t), |
672 | 491k | "gscms_transform_color_buffer"); |
673 | 491k | if (link_handle == NULL) |
674 | 0 | return NULL; |
675 | 491k | link_handle->hTransform = cmsCreateTransform(ctx, lcms_srchandle, src_data_type, |
676 | 491k | lcms_deshandle, des_data_type, |
677 | 491k | rendering_params->rendering_intent, |
678 | 491k | flag | cmm_flags); |
679 | 491k | if (link_handle->hTransform == NULL) { |
680 | 146k | int k; |
681 | | |
682 | | /* Add a bit of robustness here. Some profiles are ill-formed and |
683 | | do not have all the intents. If we failed due to a missing |
684 | | intent, lets go ahead and try each and see if we can get something |
685 | | that works. */ |
686 | 730k | for (k = 0; k <= gsABSOLUTECOLORIMETRIC; k++) { |
687 | 584k | link_handle->hTransform = cmsCreateTransform(ctx, lcms_srchandle, src_data_type, |
688 | 584k | lcms_deshandle, des_data_type, k, flag | cmm_flags); |
689 | 584k | if (link_handle->hTransform != NULL) |
690 | 0 | break; |
691 | 584k | } |
692 | | |
693 | 146k | if (link_handle->hTransform == NULL) { |
694 | 146k | gs_free_object(memory, link_handle, "gscms_get_link"); |
695 | 146k | return NULL; |
696 | 146k | } |
697 | 146k | } |
698 | | |
699 | 345k | link_handle->next = NULL; |
700 | 345k | link_handle->flags = gsicc_link_flags(0, 0, 0, 0, 0, /* no alpha, not planar, no endian swap */ |
701 | 345k | sizeof(gx_color_value), sizeof(gx_color_value)); |
702 | 345k | return link_handle; |
703 | | /* cmsFLAGS_HIGHRESPRECALC) cmsFLAGS_NOTPRECALC cmsFLAGS_LOWRESPRECALC*/ |
704 | 491k | } |
705 | | |
706 | | /* Get the link from the CMS, but include proofing and/or a device link |
707 | | profile. Note also, that the source may be a device link profile, in |
708 | | which case we will not have a destination profile but could still have |
709 | | a proof profile or an additional device link profile */ |
710 | | gcmmhlink_t |
711 | | gscms_get_link_proof_devlink(gcmmhprofile_t lcms_srchandle, |
712 | | gcmmhprofile_t lcms_proofhandle, |
713 | | gcmmhprofile_t lcms_deshandle, |
714 | | gcmmhprofile_t lcms_devlinkhandle, |
715 | | gsicc_rendering_param_t *rendering_params, |
716 | | bool src_dev_link, int cmm_flags, |
717 | | gs_memory_t *memory) |
718 | 0 | { |
719 | 0 | cmsUInt32Number src_data_type,des_data_type; |
720 | 0 | cmsColorSpaceSignature src_color_space,des_color_space; |
721 | 0 | int src_nChannels,des_nChannels; |
722 | 0 | int lcms_src_color_space, lcms_des_color_space; |
723 | 0 | cmsHPROFILE hProfiles[5]; |
724 | 0 | int nProfiles = 0; |
725 | 0 | unsigned int flag; |
726 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
727 | 0 | gsicc_lcms2mt_link_list_t *link_handle; |
728 | |
|
729 | 0 | link_handle = (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(memory->non_gc_memory, |
730 | 0 | sizeof(gsicc_lcms2mt_link_list_t), |
731 | 0 | "gscms_transform_color_buffer"); |
732 | 0 | if (link_handle == NULL) |
733 | 0 | return NULL; |
734 | 0 | link_handle->next = NULL; |
735 | 0 | link_handle->flags = gsicc_link_flags(0, 0, 0, 0, 0, /* no alpha, not planar, no endian swap */ |
736 | 0 | sizeof(gx_color_value), sizeof(gx_color_value)); |
737 | | /* Check if the rendering intent is something other than relative colorimetric |
738 | | and if we have a proofing profile. In this case we need to create the |
739 | | combined profile a bit different. LCMS does not allow us to use different |
740 | | intents in the cmsCreateMultiprofileTransform transform. Also, don't even |
741 | | think about doing this if someone has snuck in a source based device link |
742 | | profile into the mix */ |
743 | 0 | if (lcms_proofhandle != NULL && |
744 | 0 | rendering_params->rendering_intent != gsRELATIVECOLORIMETRIC && |
745 | 0 | !src_dev_link) { |
746 | | |
747 | | /* First handle the source to proof profile with its particular intent as |
748 | | a device link profile */ |
749 | 0 | cmsHPROFILE src_to_proof; |
750 | |
|
751 | 0 | link_handle = gscms_get_link(lcms_srchandle, lcms_proofhandle, |
752 | 0 | rendering_params, cmm_flags, memory); |
753 | 0 | if (link_handle->hTransform == NULL) { |
754 | 0 | gs_free_object(memory, link_handle, "gscms_get_link_proof_devlink"); |
755 | 0 | return NULL; |
756 | 0 | } |
757 | | |
758 | | /* Now mash that to a device link profile */ |
759 | 0 | flag = gscms_get_accuracy(memory); |
760 | 0 | if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || |
761 | 0 | rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { |
762 | 0 | flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); |
763 | 0 | } |
764 | 0 | src_to_proof = cmsTransform2DeviceLink(ctx, link_handle->hTransform, 3.4, flag); |
765 | 0 | cmsDeleteTransform(ctx, link_handle->hTransform); |
766 | |
|
767 | 0 | src_color_space = cmsGetColorSpace(ctx, src_to_proof); |
768 | 0 | lcms_src_color_space = _cmsLCMScolorSpace(ctx, src_color_space); |
769 | | |
770 | | /* littlecms returns -1 for types it does not (but should) understand */ |
771 | 0 | if (lcms_src_color_space < 0) |
772 | 0 | lcms_src_color_space = 0; |
773 | 0 | src_nChannels = cmsChannelsOf(ctx, src_color_space); |
774 | | |
775 | | /* For now, just do single byte data, interleaved. We can change this |
776 | | when we use the transformation. */ |
777 | 0 | src_data_type = (COLORSPACE_SH(lcms_src_color_space)| |
778 | 0 | CHANNELS_SH(src_nChannels)|BYTES_SH(2)); |
779 | 0 | if (lcms_devlinkhandle == NULL) { |
780 | 0 | des_color_space = cmsGetColorSpace(ctx, lcms_deshandle); |
781 | 0 | } else { |
782 | 0 | des_color_space = cmsGetPCS(ctx, lcms_devlinkhandle); |
783 | 0 | } |
784 | 0 | lcms_des_color_space = _cmsLCMScolorSpace(ctx, des_color_space); |
785 | 0 | if (lcms_des_color_space < 0) lcms_des_color_space = 0; |
786 | 0 | des_nChannels = cmsChannelsOf(ctx, des_color_space); |
787 | 0 | des_data_type = (COLORSPACE_SH(lcms_des_color_space)| |
788 | 0 | CHANNELS_SH(des_nChannels)|BYTES_SH(2)); |
789 | | |
790 | | /* Now, we need to go back through the proofing profile, to the |
791 | | destination and then to the device link profile if there was one. */ |
792 | 0 | hProfiles[nProfiles++] = src_to_proof; /* Src to proof with special intent */ |
793 | 0 | hProfiles[nProfiles++] = lcms_proofhandle; /* Proof to CIELAB */ |
794 | 0 | if (lcms_deshandle != NULL) { |
795 | 0 | hProfiles[nProfiles++] = lcms_deshandle; /* Our destination */ |
796 | 0 | } |
797 | | |
798 | | /* The output device link profile */ |
799 | 0 | if (lcms_devlinkhandle != NULL) { |
800 | 0 | hProfiles[nProfiles++] = lcms_devlinkhandle; |
801 | 0 | } |
802 | 0 | flag = gscms_get_accuracy(memory); |
803 | 0 | if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON |
804 | 0 | || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { |
805 | 0 | flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); |
806 | 0 | } |
807 | | |
808 | | /* Use relative colorimetric here */ |
809 | 0 | link_handle->hTransform = cmsCreateMultiprofileTransform(ctx, |
810 | 0 | hProfiles, nProfiles, src_data_type, des_data_type, |
811 | 0 | gsRELATIVECOLORIMETRIC, flag); |
812 | 0 | cmsCloseProfile(ctx, src_to_proof); |
813 | 0 | } else { |
814 | | /* First handle all the source stuff */ |
815 | 0 | src_color_space = cmsGetColorSpace(ctx, lcms_srchandle); |
816 | 0 | lcms_src_color_space = _cmsLCMScolorSpace(ctx, src_color_space); |
817 | | |
818 | | /* littlecms returns -1 for types it does not (but should) understand */ |
819 | 0 | if (lcms_src_color_space < 0) |
820 | 0 | lcms_src_color_space = 0; |
821 | 0 | src_nChannels = cmsChannelsOf(ctx, src_color_space); |
822 | | |
823 | | /* For now, just do single byte data, interleaved. We can change this |
824 | | when we use the transformation. */ |
825 | 0 | src_data_type = (COLORSPACE_SH(lcms_src_color_space)| |
826 | 0 | CHANNELS_SH(src_nChannels)|BYTES_SH(2)); |
827 | 0 | if (lcms_devlinkhandle == NULL) { |
828 | 0 | if (src_dev_link) { |
829 | 0 | des_color_space = cmsGetPCS(ctx, lcms_srchandle); |
830 | 0 | } else { |
831 | 0 | des_color_space = cmsGetColorSpace(ctx, lcms_deshandle); |
832 | 0 | } |
833 | 0 | } else { |
834 | 0 | des_color_space = cmsGetPCS(ctx, lcms_devlinkhandle); |
835 | 0 | } |
836 | 0 | lcms_des_color_space = _cmsLCMScolorSpace(ctx, des_color_space); |
837 | 0 | if (lcms_des_color_space < 0) lcms_des_color_space = 0; |
838 | 0 | des_nChannels = cmsChannelsOf(ctx, des_color_space); |
839 | 0 | des_data_type = (COLORSPACE_SH(lcms_des_color_space)| |
840 | 0 | CHANNELS_SH(des_nChannels)|BYTES_SH(2)); |
841 | | |
842 | | /* lcms proofing transform has a clunky API and can't include the device |
843 | | link profile if we have both. So use cmsCreateMultiprofileTransform |
844 | | instead and round trip the proofing profile. */ |
845 | 0 | hProfiles[nProfiles++] = lcms_srchandle; |
846 | | |
847 | | /* Note if source is device link, we cannot do any proofing */ |
848 | 0 | if (lcms_proofhandle != NULL && !src_dev_link) { |
849 | 0 | hProfiles[nProfiles++] = lcms_proofhandle; |
850 | 0 | hProfiles[nProfiles++] = lcms_proofhandle; |
851 | 0 | } |
852 | | |
853 | | /* This should be NULL if we have a source device link */ |
854 | 0 | if (lcms_deshandle != NULL) { |
855 | 0 | hProfiles[nProfiles++] = lcms_deshandle; |
856 | 0 | } |
857 | | |
858 | | /* Someone could have a device link at the output, giving us possibly two |
859 | | device link profiles to smash together */ |
860 | 0 | if (lcms_devlinkhandle != NULL) { |
861 | 0 | hProfiles[nProfiles++] = lcms_devlinkhandle; |
862 | 0 | } |
863 | 0 | flag = gscms_get_accuracy(memory); |
864 | 0 | if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON |
865 | 0 | || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { |
866 | 0 | flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); |
867 | 0 | } |
868 | 0 | link_handle->hTransform = cmsCreateMultiprofileTransform(ctx, |
869 | 0 | hProfiles, nProfiles, src_data_type, |
870 | 0 | des_data_type, rendering_params->rendering_intent, flag); |
871 | 0 | } |
872 | 0 | if (link_handle->hTransform == NULL) { |
873 | 0 | gs_free_object(memory, link_handle, "gscms_get_link_proof_devlink"); |
874 | 0 | return NULL; |
875 | 0 | } |
876 | 0 | return link_handle; |
877 | 0 | } |
878 | | |
879 | | /* Do any initialization if needed to the CMS */ |
880 | | void * |
881 | | gscms_create(gs_memory_t *memory) |
882 | 162k | { |
883 | 162k | cmsContext ctx; |
884 | | |
885 | | /* Set our own error handling function */ |
886 | 162k | ctx = cmsCreateContext((void *)&gs_cms_memhandler, memory); |
887 | 162k | if (ctx == NULL) |
888 | 0 | return NULL; |
889 | | |
890 | 162k | #ifdef USE_LCMS2_LOCKING |
891 | 162k | cmsPlugin(ctx, (void *)&gs_cms_mutexhandler); |
892 | 162k | #endif |
893 | | |
894 | | #ifdef WITH_CAL |
895 | | cmsPlugin(ctx, cal_cms_extensions2()); |
896 | | #endif |
897 | | |
898 | 162k | cmsSetLogErrorHandler(ctx, gscms_error); |
899 | | |
900 | 162k | return ctx; |
901 | 162k | } |
902 | | |
903 | | /* Do any clean up when done with the CMS if needed */ |
904 | | void |
905 | | gscms_destroy(void *cmsContext_) |
906 | 162k | { |
907 | 162k | cmsContext ctx = (cmsContext)cmsContext_; |
908 | 162k | if (ctx == NULL) |
909 | 0 | return; |
910 | | |
911 | 162k | cmsDeleteContext(ctx); |
912 | 162k | } |
913 | | |
914 | | /* Have the CMS release the link */ |
915 | | void |
916 | | gscms_release_link(gsicc_link_t *icclink) |
917 | 983k | { |
918 | 983k | cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory); |
919 | 983k | gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle); |
920 | | |
921 | 1.33M | while (link_handle != NULL) { |
922 | 351k | gsicc_lcms2mt_link_list_t *next_handle; |
923 | 351k | cmsDeleteTransform(ctx, link_handle->hTransform); |
924 | 351k | next_handle = link_handle->next; |
925 | 351k | gs_free_object(icclink->memory->non_gc_memory, link_handle, "gscms_release_link"); |
926 | 351k | link_handle = next_handle; |
927 | 351k | } |
928 | 983k | icclink->link_handle = NULL; |
929 | 983k | } |
930 | | |
931 | | /* Have the CMS release the profile handle */ |
932 | | void |
933 | | gscms_release_profile(void *profile, gs_memory_t *memory) |
934 | 755k | { |
935 | 755k | cmsHPROFILE profile_handle; |
936 | 755k | cmsContext ctx = gs_lib_ctx_get_cms_context(memory); |
937 | | |
938 | 755k | profile_handle = (cmsHPROFILE) profile; |
939 | 755k | cmsCloseProfile(ctx, profile_handle); |
940 | 755k | } |
941 | | |
942 | | /* Named color, color management */ |
943 | | /* Get a device value for the named color. Since there exist named color |
944 | | ICC profiles and littleCMS supports them, we will use |
945 | | that format in this example. However it should be noted |
946 | | that this object need not be an ICC named color profile |
947 | | but can be a proprietary type table. Some CMMs do not |
948 | | support named color profiles. In that case, or if |
949 | | the named color is not found, the caller should use an alternate |
950 | | tint transform or other method. If a proprietary |
951 | | format (nonICC) is being used, the operators given below must |
952 | | be implemented. In every case that I can imagine, this should be |
953 | | straight forward. Note that we allow the passage of a tint |
954 | | value also. Currently the ICC named color profile does not provide |
955 | | tint related information, only a value for 100% coverage. |
956 | | It is provided here for use in proprietary |
957 | | methods, which may be able to provide the desired effect. We will |
958 | | at the current time apply a direct tint operation to the returned |
959 | | device value. |
960 | | Right now I don't see any reason to have the named color profile |
961 | | ever return CIELAB. It will either return device values directly or |
962 | | it will return values defined by the output device profile */ |
963 | | |
964 | | int |
965 | | gscms_transform_named_color(gsicc_link_t *icclink, float tint_value, |
966 | | const char* ColorName, gx_color_value device_values[]) |
967 | 0 | { |
968 | 0 | gsicc_lcms2mt_link_list_t *link_handle = (gsicc_lcms2mt_link_list_t *)(icclink->link_handle); |
969 | 0 | cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link_handle->hTransform; |
970 | 0 | unsigned short *deviceptr = device_values; |
971 | 0 | int index; |
972 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory); |
973 | | |
974 | | /* Check if name is present in the profile */ |
975 | | /* If it is not return -1. Caller will need to use an alternate method */ |
976 | 0 | if((index = cmsNamedColorIndex(ctx, hTransform, ColorName)) < 0) |
977 | 0 | return -1; |
978 | | |
979 | | /* Get the device value. */ |
980 | | /* FIXME: This looks WRONG. Doesn't it need to use tint_value??? */ |
981 | | /* Also, the hTransform from link_handle is 2-byte IN, *NOT* float */ |
982 | 0 | cmsDoTransform(ctx, hTransform,&index,deviceptr,1); |
983 | 0 | return 0; |
984 | 0 | } |
985 | | |
986 | | /* Create a link to return device values inside the named color profile or link |
987 | | it with a destination profile and potentially a proofing profile. If the |
988 | | output_colorspace and the proof_color space are NULL, then we will be |
989 | | returning the device values that are contained in the named color profile. |
990 | | i.e. in namedcolor_information. Note that an ICC named color profile |
991 | | need NOT contain the device values but must contain the CIELAB values. */ |
992 | | void |
993 | | gscms_get_name2device_link(gsicc_link_t *icclink, |
994 | | gcmmhprofile_t lcms_srchandle, |
995 | | gcmmhprofile_t lcms_deshandle, |
996 | | gcmmhprofile_t lcms_proofhandle, |
997 | | gsicc_rendering_param_t *rendering_params) |
998 | 0 | { |
999 | 0 | cmsHTRANSFORM hTransform, hTransformNew; |
1000 | 0 | cmsUInt32Number dwOutputFormat; |
1001 | 0 | cmsUInt32Number lcms_proof_flag; |
1002 | 0 | int number_colors; |
1003 | 0 | cmsContext ctx = gs_lib_ctx_get_cms_context(icclink->memory); |
1004 | 0 | gsicc_lcms2mt_link_list_t *link_handle; |
1005 | |
|
1006 | 0 | icclink->link_handle = NULL; /* in case of failure */ |
1007 | | /* NOTE: We need to add a test here to check that we even HAVE |
1008 | | device values in here and NOT just CIELAB values */ |
1009 | 0 | if ( lcms_proofhandle != NULL ){ |
1010 | 0 | lcms_proof_flag = cmsFLAGS_GAMUTCHECK | cmsFLAGS_SOFTPROOFING; |
1011 | 0 | } else { |
1012 | 0 | lcms_proof_flag = 0; |
1013 | 0 | } |
1014 | | |
1015 | | /* Create the transform */ |
1016 | | /* ToDo: Adjust rendering intent */ |
1017 | 0 | hTransform = cmsCreateProofingTransform(ctx, |
1018 | 0 | lcms_srchandle, TYPE_NAMED_COLOR_INDEX, |
1019 | 0 | lcms_deshandle, TYPE_CMYK_8, |
1020 | 0 | lcms_proofhandle,INTENT_PERCEPTUAL, |
1021 | 0 | INTENT_ABSOLUTE_COLORIMETRIC, |
1022 | 0 | lcms_proof_flag); |
1023 | 0 | if (hTransform == NULL) |
1024 | 0 | return; /* bail */ |
1025 | | |
1026 | | /* In littleCMS there is no easy way to find out the size of the device |
1027 | | space returned by the named color profile until after the transform is made. |
1028 | | Hence we adjust our output format after creating the transform. It is |
1029 | | set to CMYK8 initially. */ |
1030 | 0 | number_colors = cmsNamedColorCount(ctx, cmsGetNamedColorList(hTransform)); |
1031 | | |
1032 | | /* NOTE: Output size of gx_color_value with no color space type check */ |
1033 | 0 | dwOutputFormat = (CHANNELS_SH(number_colors)|BYTES_SH(sizeof(gx_color_value))); |
1034 | | |
1035 | | /* Change the formatters */ |
1036 | 0 | hTransformNew = cmsCloneTransformChangingFormats(ctx, hTransform,TYPE_NAMED_COLOR_INDEX,dwOutputFormat); |
1037 | 0 | cmsDeleteTransform(ctx, hTransform); /* release the original after cloning */ |
1038 | 0 | if (hTransformNew == NULL) |
1039 | 0 | return; /* bail */ |
1040 | 0 | link_handle = (gsicc_lcms2mt_link_list_t *)gs_alloc_bytes(icclink->memory->non_gc_memory, |
1041 | 0 | sizeof(gsicc_lcms2mt_link_list_t), |
1042 | 0 | "gscms_transform_color_buffer"); |
1043 | 0 | if (link_handle == NULL) { |
1044 | 0 | cmsDeleteTransform(ctx, hTransformNew); |
1045 | 0 | return; /* bail */ |
1046 | 0 | } |
1047 | 0 | link_handle->flags = gsicc_link_flags(0, 0, 0, 0, 0, /* no alpha, not planar, no endian swap */ |
1048 | 0 | sizeof(gx_color_value), sizeof(gx_color_value)); |
1049 | 0 | link_handle->hTransform = hTransformNew; |
1050 | 0 | link_handle->next = NULL; |
1051 | 0 | icclink->link_handle = link_handle; |
1052 | |
|
1053 | 0 | cmsCloseProfile(ctx, lcms_srchandle); |
1054 | 0 | if(lcms_deshandle) |
1055 | 0 | cmsCloseProfile(ctx, lcms_deshandle); |
1056 | 0 | if(lcms_proofhandle) |
1057 | 0 | cmsCloseProfile(ctx, lcms_proofhandle); |
1058 | 0 | return; |
1059 | 0 | } |
1060 | | |
1061 | | bool |
1062 | | gscms_is_threadsafe(void) |
1063 | 983k | { |
1064 | 983k | return true; /* threads work correctly */ |
1065 | 983k | } |