/src/graphicsmagick/magick/profile.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | % Copyright (C) 2003-2023 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % |
6 | | % This program is covered by multiple licenses, which are described in |
7 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
8 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
9 | | % |
10 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
11 | | % % |
12 | | % % |
13 | | % % |
14 | | % PPPP RRRR OOO FFFFF IIIII L EEEEE % |
15 | | % P P R R O O F I L E % |
16 | | % PPPP RRRR O O FFF I L EEE % |
17 | | % P R R O O F I L E % |
18 | | % P R R OOO F IIIII LLLLL EEEEE % |
19 | | % % |
20 | | % GraphicsMagick Image Profile Methods % |
21 | | % % |
22 | | % % |
23 | | % Software Design % |
24 | | % John Cristy % |
25 | | % Bill Radcliffe % |
26 | | % Bob Friesenhahn % |
27 | | % % |
28 | | % % |
29 | | % % |
30 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
31 | | % |
32 | | % |
33 | | % |
34 | | */ |
35 | | |
36 | | /* |
37 | | Include declarations. |
38 | | */ |
39 | | #include "magick/studio.h" |
40 | | #include "magick/analyze.h" |
41 | | #include "magick/color.h" |
42 | | #include "magick/composite.h" |
43 | | #include "magick/log.h" |
44 | | #include "magick/map.h" |
45 | | #include "magick/monitor.h" |
46 | | #include "magick/omp_data_view.h" |
47 | | #include "magick/pixel_iterator.h" |
48 | | #include "magick/profile.h" |
49 | | #include "magick/quantize.h" |
50 | | #include "magick/resize.h" |
51 | | #include "magick/transform.h" |
52 | | #include "magick/utility.h" |
53 | | #if defined(HasLCMS) |
54 | | # if defined(HAVE_LCMS2_LCMS2_H) |
55 | | # include <lcms2/lcms2.h> |
56 | | # elif defined(HAVE_LCMS2_H) |
57 | | # include <lcms2.h> |
58 | | # else |
59 | | # error "LCMS 2 header missing!" |
60 | | # endif |
61 | | #endif |
62 | | |
63 | | /* |
64 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
65 | | % % |
66 | | % % |
67 | | % % |
68 | | % A l l o c a t e I m a g e P r o f i l e I t e r a t o r % |
69 | | % % |
70 | | % % |
71 | | % % |
72 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
73 | | % |
74 | | % AllocateImageProfileIterator allocates an iterator to traverse the |
75 | | % image profile list. It is an error (i.e. will surely crash) to invoke |
76 | | % DeleteImageProfile() on the profile that the iterator is currently |
77 | | % referencing. However, it is safe to delete a profile that the iterator |
78 | | % is not currently referencing. Inserting additional profiles does not |
79 | | % invalidate the current iterator. |
80 | | % |
81 | | % |
82 | | % The format of the AllocateImageProfileIterator method is: |
83 | | % |
84 | | % ImageProfileIterator AllocateImageProfileIterator(const Image *image) |
85 | | % |
86 | | % A description of each parameter follows: |
87 | | % |
88 | | % o image: The image. |
89 | | % |
90 | | */ |
91 | | MagickExport ImageProfileIterator |
92 | | AllocateImageProfileIterator(const Image *image) |
93 | 82.0k | { |
94 | 82.0k | if (!image->profiles) |
95 | 74.5k | return 0; |
96 | | |
97 | 7.49k | return (ImageProfileIterator) MagickMapAllocateIterator(image->profiles); |
98 | 82.0k | } |
99 | | |
100 | | /* |
101 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
102 | | % % |
103 | | % % |
104 | | % % |
105 | | % A p p e n d I m a g e P r o f i l e % |
106 | | % % |
107 | | % % |
108 | | % % |
109 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
110 | | % |
111 | | % AppendImageProfile adds a named profile to the image. If a profile with the |
112 | | % same name already exists, then the new profile data is appended to the |
113 | | % existing profile. If a null profile address is supplied, then an existing |
114 | | % profile is removed. The profile is copied into the image. Note that this |
115 | | % function does not execute CMS color profiles. Any existing CMS color |
116 | | % profile is simply added/updated. Use the ProfileImage() function in order |
117 | | % to execute a CMS color profile. |
118 | | % |
119 | | % The format of the AppendImageProfile method is: |
120 | | % |
121 | | % MaickPassFail AppendImageProfile(Image *image,const char *name, |
122 | | % const unsigned char *profile_chunk, |
123 | | % const size_t chunk_length) |
124 | | % |
125 | | % A description of each parameter follows: |
126 | | % |
127 | | % o image: The image. |
128 | | % |
129 | | % o name: Profile name. Valid names are "8BIM", "ICM", "IPTC", XMP, or any |
130 | | % unique text string. |
131 | | % |
132 | | % o profile_chunk: Address of profile chunk to add or append. Pass zero |
133 | | % to remove an existing profile. |
134 | | % |
135 | | % o length: The length of the profile chunk to add or append. |
136 | | % |
137 | | */ |
138 | | MagickExport MagickPassFail |
139 | | AppendImageProfile(Image *image, |
140 | | const char *name, |
141 | | const unsigned char *profile_chunk, |
142 | | const size_t chunk_length) |
143 | 0 | { |
144 | 0 | const unsigned char |
145 | 0 | *existing_profile; |
146 | |
|
147 | 0 | size_t |
148 | 0 | existing_length; |
149 | |
|
150 | 0 | MagickPassFail |
151 | 0 | status; |
152 | |
|
153 | 0 | status=MagickFail; |
154 | 0 | existing_length=0; |
155 | 0 | existing_profile=(const unsigned char *) NULL; |
156 | 0 | if (profile_chunk != (const unsigned char *) NULL) |
157 | 0 | existing_profile=GetImageProfile(image,name,&existing_length); |
158 | |
|
159 | 0 | if ((profile_chunk == (const unsigned char *) NULL) || |
160 | 0 | (existing_profile == (const unsigned char *) NULL)) |
161 | 0 | { |
162 | 0 | status=SetImageProfile(image,name,profile_chunk,chunk_length); |
163 | 0 | } |
164 | 0 | else |
165 | 0 | { |
166 | 0 | unsigned char |
167 | 0 | *profile; |
168 | |
|
169 | 0 | size_t |
170 | 0 | profile_length; |
171 | |
|
172 | 0 | profile_length=existing_length+chunk_length; |
173 | 0 | if ((profile_length < existing_length) || |
174 | 0 | ((profile=MagickAllocateMemory(unsigned char *,(size_t) profile_length)) == |
175 | 0 | (unsigned char *) NULL)) |
176 | 0 | ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed, |
177 | 0 | (char *) NULL); |
178 | 0 | (void) memcpy(profile,existing_profile,existing_length); |
179 | 0 | (void) memcpy(profile+existing_length,profile_chunk,chunk_length); |
180 | 0 | status=SetImageProfile(image,name,profile,profile_length); |
181 | 0 | MagickFreeMemory(profile); |
182 | 0 | } |
183 | | |
184 | 0 | return status; |
185 | 0 | } |
186 | | |
187 | | /* |
188 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
189 | | % % |
190 | | % % |
191 | | % % |
192 | | % D e a l l o c a t e I m a g e P r o f i l e I t e r a t o r % |
193 | | % % |
194 | | % % |
195 | | % % |
196 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
197 | | % |
198 | | % DeallocateImageProfileIterator deallocates an image profile iterator. |
199 | | % |
200 | | % The format of the DeallocateImageProfileIterator method is: |
201 | | % |
202 | | % void DeallocateImageProfileIterator(ImageProfileIterator profile_iterator) |
203 | | % |
204 | | % A description of each parameter follows: |
205 | | % |
206 | | % o profile_iterator: Profile iterator to deallocate. |
207 | | % |
208 | | */ |
209 | | MagickExport void |
210 | | DeallocateImageProfileIterator(ImageProfileIterator profile_iterator) |
211 | 11.0k | { |
212 | 11.0k | if (profile_iterator != 0) |
213 | 7.49k | MagickMapDeallocateIterator((MagickMapIterator) profile_iterator); |
214 | 11.0k | } |
215 | | |
216 | | /* |
217 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
218 | | % % |
219 | | % % |
220 | | % % |
221 | | % D e l e t e I m a g e P r o f i l e % |
222 | | % % |
223 | | % % |
224 | | % % |
225 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
226 | | % |
227 | | % DeleteImageProfile removes a named profile from the image. |
228 | | % |
229 | | % The format of the DeleteImageProfile method is: |
230 | | % |
231 | | % unsigned int DeleteImageProfile(Image *image,const char *name) |
232 | | % |
233 | | % A description of each parameter follows: |
234 | | % |
235 | | % o image: The image. |
236 | | % |
237 | | % o name: Profile name. Valid names are "8BIM", "ICM", & "IPTC" or a |
238 | | % generic profile name. |
239 | | % |
240 | | */ |
241 | | MagickExport MagickPassFail |
242 | | DeleteImageProfile(Image *image,const char *name) |
243 | 0 | { |
244 | 0 | return(SetImageProfile(image,name,0,0)); |
245 | 0 | } |
246 | | |
247 | | /* |
248 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
249 | | % % |
250 | | % % |
251 | | % % |
252 | | % G e t I m a g e P r o f i l e % |
253 | | % % |
254 | | % % |
255 | | % % |
256 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
257 | | % |
258 | | % GetImageProfile returns a pointer to the named image profile if it is |
259 | | % present. A null pointer is returned if the named profile is not present. |
260 | | % |
261 | | % Older versions of this function stored profiles named "8BIM" and "IPTC" |
262 | | % in the same storage location. This is no longer the case. However, |
263 | | % GetImageProfile() will try the alternate name if the specifically |
264 | | % requested profile name is not available. |
265 | | % |
266 | | % The format of the GetImageProfile method is: |
267 | | % |
268 | | % const unsigned char *GetImageProfile(const Image* image, |
269 | | % const char *name, size_t *length) |
270 | | % |
271 | | % A description of each parameter follows: |
272 | | % |
273 | | % o image: The image. |
274 | | % |
275 | | % o name: Profile name. Valid names are "8BIM", "EXIF", "ICM", "IPTC", |
276 | | % "XMP" or any unique text string. |
277 | | % |
278 | | % o length: Updated with profile length if profile is present. Set to NULL |
279 | | % if length is not needed. |
280 | | % |
281 | | */ |
282 | | MagickExport const unsigned char * |
283 | | GetImageProfile(const Image* image, const char *name, size_t *length) |
284 | 436k | { |
285 | 436k | const unsigned char |
286 | 436k | *profile = 0; |
287 | | |
288 | 436k | size_t |
289 | 436k | profile_length=0; |
290 | | |
291 | 436k | assert(image != (Image *) NULL); |
292 | 436k | assert(image->signature == MagickSignature); |
293 | 436k | assert(name != NULL); |
294 | | |
295 | 436k | if (length) |
296 | 436k | *length=0; |
297 | | |
298 | 436k | if (!image->profiles) |
299 | 404k | return 0; |
300 | | |
301 | 31.9k | profile=MagickMapAccessEntry(image->profiles,name,&profile_length); |
302 | | |
303 | 31.9k | if (!profile) |
304 | 12.4k | { |
305 | | /* |
306 | | Support common alias names and work-alikes. |
307 | | */ |
308 | 12.4k | if (LocaleCompare("ICC",name) == 0) |
309 | 23 | profile=MagickMapAccessEntry(image->profiles,"ICM",&profile_length); |
310 | 12.4k | else if (LocaleCompare("ICM",name) == 0) |
311 | 178 | profile=MagickMapAccessEntry(image->profiles,"ICC",&profile_length); |
312 | 12.2k | else if (LocaleCompare("IPTC",name) == 0) |
313 | 163 | profile=MagickMapAccessEntry(image->profiles,"8BIM",&profile_length); |
314 | 12.0k | else if (LocaleCompare("8BIM",name) == 0) |
315 | 1.76k | profile=MagickMapAccessEntry(image->profiles,"IPTC",&profile_length); |
316 | 12.4k | } |
317 | | |
318 | 31.9k | if (length) |
319 | 31.9k | *length=profile_length; |
320 | | |
321 | 31.9k | return profile; |
322 | 436k | } |
323 | | |
324 | | /* |
325 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
326 | | % % |
327 | | % % |
328 | | % % |
329 | | % N e x t I m a g e P r o f i l e % |
330 | | % % |
331 | | % % |
332 | | % % |
333 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
334 | | % |
335 | | % NextImageProfile iterates forward to the next image profile. The profile |
336 | | % name is returned along with the profile data, and length. If there are |
337 | | % no more entries in the list, then MagickFail is returned. |
338 | | % |
339 | | % The format of the AllocateImageProfileIterator method is: |
340 | | % |
341 | | % MagickPassFail NextImageProfile(ImageProfileIterator profile_iterator, |
342 | | % const char **name, const unsigned char **profile, |
343 | | % size_t *length) |
344 | | % |
345 | | % A description of each parameter follows: |
346 | | % |
347 | | % o profile_iterator: Profile iterator. |
348 | | % |
349 | | % o name: Address of pointer to update with address of name. |
350 | | % |
351 | | % o profile: Address of pointer to update with location of profile data. |
352 | | % |
353 | | % o length: Address of parameter to update with profile length. |
354 | | % |
355 | | */ |
356 | | MagickExport MagickPassFail |
357 | | NextImageProfile(ImageProfileIterator profile_iterator, |
358 | | const char **name, |
359 | | const unsigned char **profile, |
360 | | size_t *length) |
361 | 18.9k | { |
362 | 18.9k | MagickMapIterator |
363 | 18.9k | map_iterator; |
364 | | |
365 | 18.9k | MagickPassFail |
366 | 18.9k | status; |
367 | | |
368 | 18.9k | assert(name != (const char **) NULL); |
369 | 18.9k | assert(length != (size_t *) NULL); |
370 | | |
371 | 18.9k | if (profile_iterator == 0) |
372 | 3.59k | return (MagickFail); |
373 | | |
374 | 15.3k | map_iterator=(MagickMapIterator) profile_iterator; |
375 | 15.3k | status=MagickMapIterateNext(map_iterator,name); |
376 | 15.3k | if (status != MagickFail) |
377 | 10.8k | *profile=MagickMapDereferenceIterator(map_iterator,length); |
378 | 15.3k | return (status); |
379 | 18.9k | } |
380 | | |
381 | | /* |
382 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
383 | | % % |
384 | | % % |
385 | | % % |
386 | | % P r o f i l e I m a g e % |
387 | | % % |
388 | | % % |
389 | | % % |
390 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
391 | | % |
392 | | % ProfileImage() adds, applies, or removes a ICM, IPTC, or generic profile |
393 | | % from an image. If the profile is NULL, it is removed from the image |
394 | | % otherwise added (or applied). Use a name of '*' and a profile of NULL to |
395 | | % remove all profiles from the image. Ownership of the profile is |
396 | | % transferred to GraphicsMagick (it should not be altered or deallocated) |
397 | | % unless the clone option is set to True. |
398 | | % |
399 | | % ICC ICM profiles are a special case and are handled as follows: |
400 | | % |
401 | | % If there is no ICM profile currently associated with the image, then |
402 | | % the profile is simply associated with the image and the image pixels |
403 | | % are not altered. |
404 | | % |
405 | | % If there is already a ICM profile associated with the image, then |
406 | | % the colorspace transform described by the existing and new profiles |
407 | | % is applied to the image pixels, and the new profile is associated |
408 | | % with the image. |
409 | | % |
410 | | % The format of the ProfileImage method is: |
411 | | % |
412 | | % unsigned int ProfileImage(Image *image,const char *name, |
413 | | % unsigned char *profile,const size_t length,unsigned int clone) |
414 | | % |
415 | | % A description of each parameter follows: |
416 | | % |
417 | | % o image: The image. |
418 | | % |
419 | | % o name: Name of profile to add or remove: ICM, IPTC, or generic profile. |
420 | | % |
421 | | % o profile: The profile. Can not be 'const' due to 'clone' option but |
422 | | % is treated as 'const' if 'clone' is set to MagickTrue. |
423 | | % |
424 | | % o length: The length of the profile. |
425 | | % |
426 | | % o clone: If set True, then copy the profile rather than taking |
427 | | % ownership of it. |
428 | | % |
429 | | % |
430 | | */ |
431 | | #if defined(HasLCMS) |
432 | | |
433 | | typedef struct _ProfilePacket |
434 | | { |
435 | | unsigned short |
436 | | red, |
437 | | green, |
438 | | blue, |
439 | | black; |
440 | | } ProfilePacket; |
441 | | |
442 | | typedef struct _TransformInfo |
443 | | { |
444 | | Image *image; /* image handle */ |
445 | | cmsHPROFILE source_profile; /* input profile */ |
446 | | cmsHPROFILE target_profile; /* output profile */ |
447 | | cmsUInt32Number source_type; /* input pixel format */ |
448 | | cmsUInt32Number target_type; /* output pixel format */ |
449 | | int intent; /* rendering intent */ |
450 | | cmsUInt32Number flags; /* create transform flags */ |
451 | | ThreadViewDataSet *transform; /* Thread-specific transforms */ |
452 | | ColorspaceType source_colorspace; /* source image transform colorspace */ |
453 | | ColorspaceType target_colorspace; /* target image transform colorspace */ |
454 | | unsigned long signature; /* structure validation signature */ |
455 | | } TransformInfo; |
456 | | |
457 | | static void |
458 | | lcmsReplacementErrorHandler(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText) |
459 | 0 | { |
460 | 0 | TransformInfo |
461 | 0 | *xform; |
462 | |
|
463 | 0 | ExceptionType |
464 | 0 | type=TransformError; |
465 | |
|
466 | 0 | xform=(TransformInfo *) ContextID; |
467 | |
|
468 | 0 | switch(ErrorCode) |
469 | 0 | { |
470 | 0 | default: |
471 | 0 | type=TransformWarning; |
472 | 0 | break; |
473 | 0 | } |
474 | | |
475 | 0 | (void) LogMagickEvent(type,GetMagickModule(),"lcms: #%u, %s", |
476 | 0 | ErrorCode,(ErrorText != (char *) NULL) ? ErrorText : "No error text"); |
477 | |
|
478 | 0 | if (xform != (TransformInfo *) NULL) |
479 | 0 | { |
480 | 0 | ThrowException2(&xform->image->exception,type,"UnableToTransformColorspace", |
481 | 0 | (ErrorText != (char *) NULL) ? ErrorText : "No error text"); |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | | static MagickPassFail |
486 | | ProfileImagePixels(void *mutable_data, /* User provided mutable data */ |
487 | | const void *immutable_data, /* User provided immutable data */ |
488 | | Image * restrict image, /* Modify image */ |
489 | | PixelPacket * restrict pixels, /* Pixel row */ |
490 | | IndexPacket * restrict indexes, /* Pixel row indexes */ |
491 | | const long npixels, /* Number of pixels in row */ |
492 | | ExceptionInfo *exception) /* Exception report */ |
493 | 0 | { |
494 | 0 | const TransformInfo |
495 | 0 | *xform = (const TransformInfo *) immutable_data; |
496 | |
|
497 | 0 | register long |
498 | 0 | i; |
499 | |
|
500 | 0 | cmsHTRANSFORM |
501 | 0 | transform; |
502 | |
|
503 | 0 | const ColorspaceType |
504 | 0 | source_colorspace = xform->source_colorspace; |
505 | |
|
506 | 0 | const ColorspaceType |
507 | 0 | target_colorspace = xform->target_colorspace; |
508 | |
|
509 | 0 | ProfilePacket |
510 | 0 | alpha, |
511 | 0 | beta; |
512 | |
|
513 | 0 | ARG_NOT_USED(mutable_data); |
514 | 0 | ARG_NOT_USED(exception); |
515 | |
|
516 | 0 | transform=(cmsHTRANSFORM) AccessThreadViewData(xform->transform); |
517 | | |
518 | | /* |
519 | | TODO: This may be optimized to use PixelPackets instead |
520 | | of moving PixelPacket components to and from ProfilePackets. |
521 | | The transform types below, then, must match #ifdef's in the |
522 | | PixelPacket struct definition and should be optimized |
523 | | based on Quantum size. Some (if not all?) YCbCr and LUV |
524 | | profiles are (TIFF) scanline oriented, so transforming |
525 | | one pixel at a time does not work for those profiles. |
526 | | |
527 | | Notice that the performance penalty of transforming only |
528 | | one pixel at a time is very small and probably not worth |
529 | | optimizing. |
530 | | */ |
531 | |
|
532 | 0 | for (i=0; i < npixels; i++) |
533 | 0 | { |
534 | 0 | alpha.red=ScaleQuantumToShort(pixels[i].red); |
535 | 0 | if (source_colorspace != GRAYColorspace) |
536 | 0 | { |
537 | 0 | alpha.green=ScaleQuantumToShort(pixels[i].green); |
538 | 0 | alpha.blue=ScaleQuantumToShort(pixels[i].blue); |
539 | 0 | if (source_colorspace == CMYKColorspace) |
540 | 0 | alpha.black=ScaleQuantumToShort(pixels[i].opacity); |
541 | 0 | } |
542 | 0 | cmsDoTransform(transform,&alpha,&beta,1); |
543 | 0 | pixels[i].red=ScaleShortToQuantum(beta.red); |
544 | 0 | if (IsGrayColorspace(target_colorspace)) |
545 | 0 | { |
546 | 0 | pixels[i].green=pixels[i].red; |
547 | 0 | pixels[i].blue=pixels[i].red; |
548 | 0 | } |
549 | 0 | else |
550 | 0 | { |
551 | 0 | pixels[i].green=ScaleShortToQuantum(beta.green); |
552 | 0 | pixels[i].blue=ScaleShortToQuantum(beta.blue); |
553 | 0 | } |
554 | 0 | if ((image->matte) && (NULL != indexes)) |
555 | 0 | { |
556 | 0 | if ((source_colorspace == CMYKColorspace) && |
557 | 0 | (target_colorspace != CMYKColorspace)) |
558 | 0 | pixels[i].opacity=indexes[i]; |
559 | 0 | else |
560 | 0 | if ((source_colorspace != CMYKColorspace) && |
561 | 0 | (target_colorspace == CMYKColorspace)) |
562 | 0 | indexes[i]=pixels[i].opacity; |
563 | 0 | } |
564 | 0 | if (target_colorspace == CMYKColorspace) |
565 | 0 | pixels[i].opacity=ScaleShortToQuantum(beta.black); |
566 | 0 | } |
567 | |
|
568 | 0 | return MagickPass; |
569 | 0 | } |
570 | | |
571 | | static void MagickFreeCMSTransform(void * cmsTransformVoid) |
572 | 0 | { |
573 | 0 | cmsHTRANSFORM |
574 | 0 | cmsTransform=(cmsHTRANSFORM) cmsTransformVoid; |
575 | |
|
576 | 0 | if (cmsTransform != (cmsHTRANSFORM) NULL) |
577 | 0 | cmsDeleteTransform(cmsTransform); |
578 | 0 | } |
579 | | |
580 | | static const char * |
581 | | PixelTypeToString(int pixel_type) |
582 | 0 | { |
583 | 0 | const char * |
584 | 0 | result=""; |
585 | |
|
586 | 0 | switch (pixel_type) |
587 | 0 | { |
588 | 0 | case PT_ANY: result="ANY" ; break; |
589 | 0 | case PT_GRAY: result="GRAY" ; break; |
590 | 0 | case PT_RGB: result="RGB" ; break; |
591 | 0 | case PT_CMY: result="CMY" ; break; |
592 | 0 | case PT_CMYK: result="CMYK" ; break; |
593 | 0 | case PT_YCbCr: result="YCbCr" ; break; |
594 | 0 | case PT_YUV: result="YUV (Lu'v')"; break; |
595 | 0 | case PT_XYZ: result="XYZ" ; break; |
596 | 0 | case PT_Lab: result="Lab" ; break; |
597 | 0 | case PT_YUVK: result="YUVK (Lu'v'K)" ; break; |
598 | 0 | case PT_HSV: result="HSV" ; break; |
599 | 0 | case PT_HLS: result="HLS" ; break; |
600 | 0 | case PT_Yxy: result="Yxy" ; break; |
601 | | |
602 | | #if defined(PT_HiFi) |
603 | | case PT_HiFi: result="HiFi" ; break; |
604 | | #endif |
605 | | #if defined(PT_HiFi7) |
606 | | case PT_HiFi7: result="HiFi7" ; break; |
607 | | #endif |
608 | | #if defined(PT_HiFi8) |
609 | | case PT_HiFi8: result="HiFi8" ; break; |
610 | | #endif |
611 | | #if defined(PT_HiFi9) |
612 | | case PT_HiFi9: result="HiFi9" ; break; |
613 | | #endif |
614 | | #if defined(PT_HiFi10) |
615 | | case PT_HiFi10: result="HiFi10"; break; |
616 | | #endif |
617 | | #if defined(PT_HiFi11) |
618 | | case PT_HiFi11: result="HiFi11"; break; |
619 | | #endif |
620 | | #if defined(PT_HiFi12) |
621 | | case PT_HiFi12: result="HiFi12"; break; |
622 | | #endif |
623 | | #if defined(PT_HiFi13) |
624 | | case PT_HiFi13: result="HiFi13"; break; |
625 | | #endif |
626 | | #if defined(PT_HiFi14) |
627 | | case PT_HiFi14: result="HiFi14"; break; |
628 | | #endif |
629 | | #if defined(PT_HiFi15) |
630 | | case PT_HiFi15: result="HiFi15"; break; |
631 | | #endif |
632 | | |
633 | 0 | #if defined(PT_MCH1) |
634 | 0 | case PT_MCH1: result="MCH1" ; break; |
635 | 0 | #endif |
636 | 0 | #if defined(PT_MCH2) |
637 | 0 | case PT_MCH2: result="MCH2" ; break; |
638 | 0 | #endif |
639 | 0 | #if defined(PT_MCH3) |
640 | 0 | case PT_MCH3: result="MCH3" ; break; |
641 | 0 | #endif |
642 | 0 | #if defined(PT_MCH4) |
643 | 0 | case PT_MCH4: result="MCH4" ; break; |
644 | 0 | #endif |
645 | 0 | #if defined(PT_MCH5) |
646 | 0 | case PT_MCH5: result="MCH5" ; break; |
647 | 0 | #endif |
648 | 0 | #if defined(PT_MCH6) |
649 | 0 | case PT_MCH6: result="MCH6" ; break; |
650 | 0 | #endif |
651 | 0 | #if defined(PT_MCH7) |
652 | 0 | case PT_MCH7: result="MCH7" ; break; |
653 | 0 | #endif |
654 | 0 | #if defined(PT_MCH8) |
655 | 0 | case PT_MCH8: result="MCH8" ; break; |
656 | 0 | #endif |
657 | 0 | #if defined(PT_MCH9) |
658 | 0 | case PT_MCH9: result="MCH9" ; break; |
659 | 0 | #endif |
660 | 0 | #if defined(PT_MCH10) |
661 | 0 | case PT_MCH10: result="MCH10" ; break; |
662 | 0 | #endif |
663 | 0 | #if defined(PT_MCH11) |
664 | 0 | case PT_MCH11: result="MCH11" ; break; |
665 | 0 | #endif |
666 | 0 | #if defined(PT_MCH12) |
667 | 0 | case PT_MCH12: result="MCH12" ; break; |
668 | 0 | #endif |
669 | 0 | #if defined(PT_MCH13) |
670 | 0 | case PT_MCH13: result="MCH13" ; break; |
671 | 0 | #endif |
672 | 0 | #if defined(PT_MCH14) |
673 | 0 | case PT_MCH14: result="MCH14" ; break; |
674 | 0 | #endif |
675 | 0 | #if defined(PT_MCH15) |
676 | 0 | case PT_MCH15: result="MCH15" ; break; |
677 | 0 | #endif |
678 | 0 | #if defined(PT_LabV2) |
679 | 0 | case PT_LabV2: result="LabV2" ; break; |
680 | 0 | #endif |
681 | 0 | } |
682 | | |
683 | 0 | return result; |
684 | 0 | } |
685 | | |
686 | | #endif /* defined(HasLCMS) */ |
687 | | |
688 | 0 | #define ProfileImageText "[%s] Color Transform Pixels..." |
689 | | MagickExport MagickPassFail |
690 | | ProfileImage(Image *image,const char *name,unsigned char *profile, |
691 | | const size_t length,MagickBool clone) |
692 | 0 | { |
693 | 0 | MagickPassFail |
694 | 0 | status=MagickPass; |
695 | |
|
696 | 0 | assert(image != (Image *) NULL); |
697 | 0 | assert(image->signature == MagickSignature); |
698 | 0 | if (name == (const char *) NULL) |
699 | 0 | ThrowBinaryException3(OptionError,NoProfileNameWasGiven, |
700 | 0 | UnableToAddOrRemoveProfile); |
701 | 0 | if ((profile == (const unsigned char *) NULL) || (length == 0)) |
702 | 0 | { |
703 | | /* |
704 | | Remove an ICM, IPTC, or generic profile from the image. |
705 | | */ |
706 | 0 | char |
707 | 0 | arg_string[MaxTextExtent], |
708 | 0 | profile_remove[MaxTextExtent]; |
709 | |
|
710 | 0 | const char |
711 | 0 | *profile_name; |
712 | |
|
713 | 0 | size_t |
714 | 0 | profile_length; |
715 | |
|
716 | 0 | const unsigned char * |
717 | 0 | profile_info; |
718 | |
|
719 | 0 | ImageProfileIterator |
720 | 0 | profile_iterator; |
721 | |
|
722 | 0 | char |
723 | 0 | **argv; |
724 | |
|
725 | 0 | int |
726 | 0 | argc; |
727 | |
|
728 | 0 | long |
729 | 0 | i; |
730 | |
|
731 | 0 | (void) strlcpy(arg_string,name,sizeof(arg_string)); |
732 | 0 | LocaleUpper(arg_string); |
733 | 0 | for (i=0; arg_string[i] != '\0'; i++) |
734 | 0 | if (arg_string[i] == ',') |
735 | 0 | arg_string[i] = ' '; |
736 | 0 | argv=StringToArgv(arg_string,&argc); |
737 | 0 | profile_iterator=AllocateImageProfileIterator(image); |
738 | 0 | profile_remove[0]=0; |
739 | 0 | while(NextImageProfile(profile_iterator,&profile_name,&profile_info, |
740 | 0 | &profile_length) != MagickFail) |
741 | 0 | { |
742 | 0 | if (strlen(profile_remove)) |
743 | 0 | { |
744 | 0 | (void) DeleteImageProfile(image,profile_remove); |
745 | 0 | profile_remove[0]=0; |
746 | 0 | } |
747 | 0 | for (i=1 ; i < argc ; i++) |
748 | 0 | { |
749 | 0 | if ((*argv[i] == '!') && (LocaleCompare(profile_name,argv[i]+1) == 0)) |
750 | 0 | break; |
751 | 0 | if (GlobExpression(profile_name,argv[i])) |
752 | 0 | { |
753 | 0 | (void) strlcpy(profile_remove,profile_name,sizeof(profile_remove)); |
754 | 0 | break; |
755 | 0 | } |
756 | 0 | } |
757 | 0 | } |
758 | 0 | DeallocateImageProfileIterator(profile_iterator); |
759 | 0 | if (strlen(profile_remove)) |
760 | 0 | (void) DeleteImageProfile(image,profile_remove); |
761 | |
|
762 | 0 | for(i=0; argv[i] != NULL; i++) |
763 | 0 | MagickFreeMemory(argv[i]); |
764 | 0 | MagickFreeMemory(argv); |
765 | |
|
766 | 0 | return(MagickPass); |
767 | 0 | } |
768 | | /* |
769 | | Add a ICM, IPTC, or generic profile to the image. |
770 | | */ |
771 | 0 | if ((LocaleCompare("8bim",name) == 0) || (LocaleCompare("iptc",name) == 0)) |
772 | 0 | { |
773 | 0 | if (clone) |
774 | 0 | { |
775 | 0 | (void) SetImageProfile(image,name,profile,length); |
776 | 0 | } |
777 | 0 | else |
778 | 0 | { |
779 | 0 | (void) SetImageProfile(image,name,profile,length); |
780 | 0 | MagickFreeMemory(profile); |
781 | 0 | } |
782 | 0 | return(MagickPass); |
783 | 0 | } |
784 | 0 | if (LocaleCompare("icm",name) == 0) |
785 | 0 | { |
786 | 0 | const unsigned char |
787 | 0 | *existing_profile; |
788 | |
|
789 | 0 | size_t |
790 | 0 | existing_profile_length=0; |
791 | | |
792 | | /* Check for identical input and output profiles. Return on identity. */ |
793 | 0 | existing_profile=GetImageProfile(image,"ICM",&existing_profile_length); |
794 | |
|
795 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
796 | 0 | "New Profile: %lu bytes, Existing Profile: %lu bytes", |
797 | 0 | (unsigned long) length, |
798 | 0 | (unsigned long) existing_profile_length); |
799 | |
|
800 | 0 | if ((length != 0) && (length == existing_profile_length) && |
801 | 0 | (memcmp(existing_profile,profile,length) == 0)) |
802 | 0 | { |
803 | 0 | return(MagickPass); |
804 | 0 | } |
805 | | |
806 | | /* Convert to new colors if we have both an old and a new profile. */ |
807 | 0 | if ((existing_profile_length != 0) && (length != 0)) |
808 | 0 | { |
809 | 0 | #if defined(HasLCMS) |
810 | |
|
811 | 0 | TransformInfo |
812 | 0 | xform; |
813 | |
|
814 | 0 | MagickBool |
815 | 0 | transform_colormap; |
816 | | |
817 | | /* |
818 | | Transform pixel colors as defined by the color profiles. |
819 | | */ |
820 | 0 | (void) memset(&xform,0,sizeof(xform)); |
821 | 0 | xform.signature=MagickSignature; |
822 | 0 | xform.image=image; |
823 | 0 | cmsSetLogErrorHandler(lcmsReplacementErrorHandler); |
824 | |
|
825 | 0 | xform.source_profile=cmsOpenProfileFromMemTHR((cmsContext) &xform, |
826 | 0 | (unsigned char *) existing_profile, |
827 | 0 | (cmsUInt32Number) existing_profile_length); |
828 | 0 | xform.target_profile=cmsOpenProfileFromMemTHR((cmsContext) &xform, |
829 | 0 | (unsigned char *) profile, |
830 | 0 | (cmsUInt32Number) length); |
831 | 0 | if ((xform.source_profile == (cmsHPROFILE) NULL) || |
832 | 0 | (xform.target_profile == (cmsHPROFILE) NULL)) |
833 | 0 | ThrowBinaryException3(ResourceLimitError,UnableToManageColor, |
834 | 0 | UnableToOpenColorProfile); |
835 | |
|
836 | 0 | switch (cmsGetColorSpace(xform.source_profile)) |
837 | 0 | { |
838 | 0 | case cmsSigXYZData: |
839 | 0 | { |
840 | 0 | xform.source_colorspace=XYZColorspace; |
841 | 0 | xform.source_type=TYPE_XYZ_16; |
842 | 0 | break; |
843 | 0 | } |
844 | 0 | case cmsSigLabData: |
845 | 0 | { |
846 | 0 | xform.source_colorspace=LABColorspace; |
847 | 0 | xform.source_type=TYPE_Lab_16; |
848 | 0 | break; |
849 | 0 | } |
850 | 0 | case cmsSigCmykData: |
851 | 0 | { |
852 | 0 | xform.source_colorspace=CMYKColorspace; |
853 | 0 | xform.source_type=TYPE_CMYK_16; |
854 | 0 | break; |
855 | 0 | } |
856 | 0 | case cmsSigYCbCrData: |
857 | 0 | { |
858 | 0 | xform.source_colorspace=YCbCrColorspace; |
859 | 0 | xform.source_type=TYPE_YCbCr_16; |
860 | 0 | break; |
861 | 0 | } |
862 | 0 | case cmsSigLuvData: |
863 | 0 | { |
864 | 0 | xform.source_colorspace=YUVColorspace; |
865 | 0 | xform.source_type=TYPE_YUV_16; |
866 | 0 | break; |
867 | 0 | } |
868 | 0 | case cmsSigGrayData: |
869 | 0 | { |
870 | 0 | xform.source_colorspace=GRAYColorspace; |
871 | 0 | xform.source_type=TYPE_GRAY_16; |
872 | 0 | break; |
873 | 0 | } |
874 | 0 | case cmsSigRgbData: |
875 | 0 | { |
876 | 0 | xform.source_colorspace=RGBColorspace; |
877 | 0 | xform.source_type=TYPE_RGB_16; |
878 | 0 | break; |
879 | 0 | } |
880 | 0 | default: |
881 | 0 | { |
882 | 0 | xform.source_colorspace=UndefinedColorspace; |
883 | 0 | xform.source_type=TYPE_RGB_16; |
884 | 0 | break; |
885 | 0 | } |
886 | 0 | } |
887 | 0 | switch (cmsGetColorSpace(xform.target_profile)) |
888 | 0 | { |
889 | 0 | case cmsSigXYZData: |
890 | 0 | { |
891 | 0 | xform.target_colorspace=XYZColorspace; |
892 | 0 | xform.target_type=TYPE_XYZ_16; |
893 | 0 | break; |
894 | 0 | } |
895 | 0 | case cmsSigLabData: |
896 | 0 | { |
897 | 0 | xform.target_colorspace=LABColorspace; |
898 | 0 | xform.target_type=TYPE_Lab_16;; |
899 | 0 | break; |
900 | 0 | } |
901 | 0 | case cmsSigCmykData: |
902 | 0 | { |
903 | 0 | xform.target_colorspace=CMYKColorspace; |
904 | 0 | xform.target_type=TYPE_CMYK_16; |
905 | 0 | break; |
906 | 0 | } |
907 | 0 | case cmsSigYCbCrData: |
908 | 0 | { |
909 | 0 | xform.target_colorspace=YCbCrColorspace; |
910 | 0 | xform.target_type=TYPE_YCbCr_16; |
911 | 0 | break; |
912 | 0 | } |
913 | 0 | case cmsSigLuvData: |
914 | 0 | { |
915 | 0 | xform.target_colorspace=YUVColorspace; |
916 | 0 | xform.target_type=TYPE_YUV_16; |
917 | 0 | break; |
918 | 0 | } |
919 | 0 | case cmsSigGrayData: |
920 | 0 | { |
921 | 0 | xform.target_colorspace=GRAYColorspace; |
922 | 0 | xform.target_type=TYPE_GRAY_16; |
923 | 0 | break; |
924 | 0 | } |
925 | 0 | case cmsSigRgbData: |
926 | 0 | { |
927 | 0 | xform.target_colorspace=RGBColorspace; |
928 | 0 | xform.target_type=TYPE_RGB_16; |
929 | 0 | break; |
930 | 0 | } |
931 | 0 | default: |
932 | 0 | { |
933 | 0 | xform.target_colorspace=UndefinedColorspace; |
934 | 0 | xform.target_type=TYPE_RGB_16; |
935 | 0 | break; |
936 | 0 | } |
937 | 0 | } |
938 | | |
939 | | /* Colorspace undefined */ |
940 | 0 | if ((xform.source_colorspace == UndefinedColorspace) || |
941 | 0 | (xform.target_colorspace == UndefinedColorspace)) |
942 | 0 | { |
943 | 0 | (void) cmsCloseProfile(xform.source_profile); |
944 | 0 | (void) cmsCloseProfile(xform.target_profile); |
945 | 0 | ThrowBinaryException3(ImageError,UnableToAssignProfile, |
946 | 0 | ColorspaceColorProfileMismatch); |
947 | 0 | } |
948 | | /* Gray colorspace */ |
949 | 0 | if (IsGrayColorspace(xform.source_colorspace) && |
950 | 0 | !IsGrayImage(image,&image->exception)) |
951 | 0 | { |
952 | 0 | (void) cmsCloseProfile(xform.source_profile); |
953 | 0 | (void) cmsCloseProfile(xform.target_profile); |
954 | 0 | ThrowBinaryException3(ImageError,UnableToAssignProfile, |
955 | 0 | ColorspaceColorProfileMismatch); |
956 | 0 | } |
957 | | /* CMYK colorspace */ |
958 | 0 | if (IsCMYKColorspace(xform.source_colorspace) && |
959 | 0 | !IsCMYKColorspace(image->colorspace)) |
960 | 0 | { |
961 | 0 | (void) cmsCloseProfile(xform.source_profile); |
962 | 0 | (void) cmsCloseProfile(xform.target_profile); |
963 | 0 | ThrowBinaryException3(ImageError,UnableToAssignProfile, |
964 | 0 | ColorspaceColorProfileMismatch); |
965 | 0 | } |
966 | | /* YCbCr colorspace */ |
967 | 0 | if (IsYCbCrColorspace(xform.source_colorspace) && |
968 | 0 | !IsYCbCrColorspace(image->colorspace)) |
969 | 0 | { |
970 | 0 | (void) cmsCloseProfile(xform.source_profile); |
971 | 0 | (void) cmsCloseProfile(xform.target_profile); |
972 | 0 | ThrowBinaryException3(ImageError,UnableToAssignProfile, |
973 | 0 | ColorspaceColorProfileMismatch); |
974 | 0 | } |
975 | | /* Verify that source colorspace type is supported */ |
976 | 0 | if (!IsGrayColorspace(xform.source_colorspace) && |
977 | 0 | !IsCMYKColorspace(xform.source_colorspace) && |
978 | 0 | !IsLABColorspace(xform.source_colorspace) && |
979 | 0 | !IsYCbCrColorspace(xform.source_colorspace) && |
980 | 0 | !IsRGBColorspace(image->colorspace)) |
981 | 0 | { |
982 | 0 | (void) cmsCloseProfile(xform.source_profile); |
983 | 0 | (void) cmsCloseProfile(xform.target_profile); |
984 | 0 | ThrowBinaryException3(ImageError,UnableToAssignProfile, |
985 | 0 | ColorspaceColorProfileMismatch); |
986 | 0 | } |
987 | | |
988 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
989 | 0 | "Source pixel format: COLORSPACE=%s SWAPFIRST=%d " |
990 | 0 | "FLAVOR=%d PLANAR=%d ENDIAN16=%d DOSWAP=%d " |
991 | 0 | "EXTRA=%d CHANNELS=%d BYTES=%d", |
992 | 0 | PixelTypeToString((int) T_COLORSPACE(xform.source_type)), |
993 | 0 | (int) T_SWAPFIRST(xform.source_type), |
994 | 0 | (int) T_FLAVOR(xform.source_type), |
995 | 0 | (int) T_PLANAR(xform.source_type), |
996 | 0 | (int) T_ENDIAN16(xform.source_type), |
997 | 0 | (int) T_DOSWAP(xform.source_type), |
998 | 0 | (int) T_EXTRA(xform.source_type), |
999 | 0 | (int) T_CHANNELS(xform.source_type), |
1000 | 0 | (int) T_BYTES(xform.source_type)); |
1001 | |
|
1002 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1003 | 0 | "Target pixel format: COLORSPACE=%s SWAPFIRST=%d " |
1004 | 0 | "FLAVOR=%d PLANAR=%d ENDIAN16=%d DOSWAP=%d " |
1005 | 0 | "EXTRA=%d CHANNELS=%d BYTES=%d", |
1006 | 0 | PixelTypeToString((int) T_COLORSPACE(xform.target_type)), |
1007 | 0 | (int) T_SWAPFIRST(xform.target_type), |
1008 | 0 | (int) T_FLAVOR(xform.target_type), |
1009 | 0 | (int) T_PLANAR(xform.target_type), |
1010 | 0 | (int) T_ENDIAN16(xform.target_type), |
1011 | 0 | (int) T_DOSWAP(xform.target_type), |
1012 | 0 | (int) T_EXTRA(xform.target_type), |
1013 | 0 | (int) T_CHANNELS(xform.target_type), |
1014 | 0 | (int) T_BYTES(xform.target_type)); |
1015 | |
|
1016 | 0 | switch (image->rendering_intent) |
1017 | 0 | { |
1018 | 0 | case AbsoluteIntent: |
1019 | 0 | xform.intent=INTENT_ABSOLUTE_COLORIMETRIC; |
1020 | 0 | break; |
1021 | 0 | case PerceptualIntent: |
1022 | 0 | xform.intent=INTENT_PERCEPTUAL; |
1023 | 0 | break; |
1024 | 0 | case RelativeIntent: |
1025 | 0 | xform.intent=INTENT_RELATIVE_COLORIMETRIC; |
1026 | 0 | break; |
1027 | 0 | case SaturationIntent: |
1028 | 0 | xform.intent=INTENT_SATURATION; |
1029 | 0 | break; |
1030 | 0 | default: |
1031 | 0 | xform.intent=INTENT_PERCEPTUAL; |
1032 | 0 | break; |
1033 | 0 | } |
1034 | | |
1035 | | /* |
1036 | | Transform just the colormap if the image is colormapped and we're |
1037 | | not transforming from gray to RGB/CMYK. A gray to RGB/CMYK |
1038 | | transformation must create a direct class image for the new image |
1039 | | to match the color profile even if the new image only has gray |
1040 | | colors. CMYK images are never color mapped. |
1041 | | */ |
1042 | 0 | transform_colormap=(image->storage_class == PseudoClass) && |
1043 | 0 | (xform.target_colorspace != CMYKColorspace) && |
1044 | 0 | ((xform.source_colorspace != GRAYColorspace) || |
1045 | 0 | (xform.source_colorspace == xform.target_colorspace)); |
1046 | | |
1047 | | /* build pre-computed transforms? */ |
1048 | 0 | xform.flags=(transform_colormap ? cmsFLAGS_NOOPTIMIZE : 0); |
1049 | |
|
1050 | 0 | xform.transform=AllocateThreadViewDataSet(MagickFreeCMSTransform, |
1051 | 0 | image,&image->exception); |
1052 | 0 | if (xform.transform == (ThreadViewDataSet *) NULL) |
1053 | 0 | status=MagickFail; |
1054 | 0 | if (status != MagickFail) |
1055 | 0 | { |
1056 | 0 | cmsHTRANSFORM |
1057 | 0 | transform; |
1058 | |
|
1059 | 0 | unsigned int |
1060 | 0 | index; |
1061 | |
|
1062 | 0 | for (index=0 ; index < GetThreadViewDataSetAllocatedViews(xform.transform); index++) |
1063 | 0 | { |
1064 | 0 | transform=cmsCreateTransformTHR((cmsContext) &xform,/* transform handle */ |
1065 | 0 | xform.source_profile, /* input profile */ |
1066 | 0 | xform.source_type, /* input pixel format */ |
1067 | 0 | xform.target_profile, /* output profile */ |
1068 | 0 | xform.target_type, /* output pixel format */ |
1069 | 0 | xform.intent, /* rendering intent */ |
1070 | 0 | xform.flags /* pre-computed transforms? */ |
1071 | 0 | ); |
1072 | 0 | if (transform == (cmsHTRANSFORM) NULL) |
1073 | 0 | { |
1074 | 0 | status=MagickFail; |
1075 | 0 | break; |
1076 | 0 | } |
1077 | 0 | AssignThreadViewData(xform.transform,index,transform); |
1078 | 0 | } |
1079 | 0 | } |
1080 | 0 | (void) cmsCloseProfile(xform.source_profile); |
1081 | 0 | (void) cmsCloseProfile(xform.target_profile); |
1082 | 0 | if (status == MagickFail) |
1083 | 0 | { |
1084 | 0 | DestroyThreadViewDataSet(xform.transform); |
1085 | 0 | ThrowBinaryException3(ResourceLimitError,UnableToManageColor, |
1086 | 0 | UnableToCreateColorTransform); |
1087 | 0 | } |
1088 | | |
1089 | 0 | if (transform_colormap) |
1090 | 0 | { |
1091 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1092 | 0 | "Performing pseudo class color conversion"); |
1093 | |
|
1094 | 0 | (void) ProfileImagePixels(NULL, |
1095 | 0 | &xform, |
1096 | 0 | image, |
1097 | 0 | image->colormap, |
1098 | 0 | (IndexPacket *) NULL, |
1099 | 0 | image->colors, |
1100 | 0 | &image->exception); |
1101 | |
|
1102 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1103 | 0 | "Completed pseudo class color conversion"); |
1104 | 0 | status &= SyncImage(image); |
1105 | 0 | } |
1106 | 0 | else |
1107 | 0 | { |
1108 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1109 | 0 | "Performing direct class color conversion"); |
1110 | 0 | if (image->storage_class == PseudoClass) |
1111 | 0 | { |
1112 | 0 | status &= SyncImage(image); |
1113 | 0 | image->storage_class=DirectClass; |
1114 | 0 | } |
1115 | 0 | if (xform.target_colorspace == CMYKColorspace) |
1116 | 0 | image->colorspace=xform.target_colorspace; |
1117 | |
|
1118 | 0 | status=PixelIterateMonoModify(ProfileImagePixels, |
1119 | 0 | NULL, |
1120 | 0 | ProfileImageText, |
1121 | 0 | NULL,&xform,0,0,image->columns,image->rows, |
1122 | 0 | image,&image->exception); |
1123 | |
|
1124 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1125 | 0 | "Completed direct class color conversion"); |
1126 | |
|
1127 | 0 | } |
1128 | 0 | image->colorspace=xform.target_colorspace; |
1129 | | /* |
1130 | | We can't be sure black and white stays exactly black and white |
1131 | | and that gray colors transform to gray colors. |
1132 | | */ |
1133 | 0 | image->is_grayscale=IsGrayColorspace(xform.target_colorspace); |
1134 | 0 | image->is_monochrome=False; |
1135 | 0 | DestroyThreadViewDataSet(xform.transform); |
1136 | | |
1137 | | /* |
1138 | | Throw away the old profile after conversion before we |
1139 | | assign a new one. |
1140 | | */ |
1141 | 0 | DeleteImageProfile(image,"ICM"); |
1142 | | #else |
1143 | | ThrowBinaryException(MissingDelegateError,LCMSLibraryIsNotAvailable, |
1144 | | image->filename); |
1145 | | #endif |
1146 | 0 | } |
1147 | | |
1148 | | /* |
1149 | | TODO: If the image *did not* already have a color profile, |
1150 | | verify that the colorspace of the new profile is valid for the |
1151 | | colorspace of the image. If LCMS is not available we should |
1152 | | refuse to assign a new profile (just like we're refusing a |
1153 | | conversion above) as we can't be sure the assignment is valid. |
1154 | | We might be trying to assign a CMYK profile to an RGB image, |
1155 | | for instance. |
1156 | | */ |
1157 | | |
1158 | 0 | (void) SetImageProfile(image,"ICM",profile,length); |
1159 | 0 | if (!clone) |
1160 | 0 | MagickFreeMemory(profile); |
1161 | 0 | return(status); |
1162 | 0 | } |
1163 | | |
1164 | 0 | status &= SetImageProfile(image,name,profile,length); |
1165 | 0 | if (!clone) |
1166 | 0 | MagickFreeMemory(profile); |
1167 | |
|
1168 | 0 | return(status); |
1169 | 0 | } |
1170 | | |
1171 | | /* |
1172 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1173 | | % % |
1174 | | % % |
1175 | | % % |
1176 | | % S e t I m a g e P r o f i l e % |
1177 | | % % |
1178 | | % % |
1179 | | % % |
1180 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1181 | | % |
1182 | | % SetImageProfile adds a named profile to the image. If a profile with the |
1183 | | % same name already exists, then it is replaced. If a null profile address |
1184 | | % is supplied, then an existing profile is removed. The profile is copied |
1185 | | % into the image. Note that this function does not execute CMS color |
1186 | | % profiles. Any existing CMS color profile is simply replaced. Use the |
1187 | | % ProfileImage() function in order to execute a CMS color profile. |
1188 | | % |
1189 | | % Older versions of this function stored profiles named "8BIM" and "IPTC" |
1190 | | % in the same storage location. This is no longer the case. However, |
1191 | | % GetImageProfile() will try the alternate name if the specifically |
1192 | | % requested profile name is not available. Note that when trying to remove |
1193 | | % a profile, it may be necessary to remove both names in order for an |
1194 | | % "IPTC" profile to no longer be included in output file formats. |
1195 | | % |
1196 | | % The format of the SetImageProfile method is: |
1197 | | % |
1198 | | % unsigned int SetImageProfile(Image *image,const char *name, |
1199 | | % const unsigned char *profile,const size_t length) |
1200 | | % |
1201 | | % A description of each parameter follows: |
1202 | | % |
1203 | | % o image: The image. |
1204 | | % |
1205 | | % o name: Profile name. Valid names are "8BIM", EXIF, "ICM", "IPTC", |
1206 | | % XMP, or any unique text string. |
1207 | | % |
1208 | | % o profile: Address of profile to add. Pass zero to remove an existing |
1209 | | % profile. |
1210 | | % |
1211 | | % o length: The length of the profile. |
1212 | | % |
1213 | | */ |
1214 | | MagickExport MagickPassFail |
1215 | | SetImageProfile(Image *image,const char *name, const unsigned char *profile, |
1216 | | const size_t length) |
1217 | 138k | { |
1218 | 138k | char |
1219 | 138k | ucase_name[MaxTextExtent]; |
1220 | | |
1221 | 138k | unsigned int |
1222 | 138k | status = MagickPass; |
1223 | | |
1224 | 138k | assert(image != (Image *) NULL); |
1225 | 138k | assert(image->signature == MagickSignature); |
1226 | 138k | assert(name != NULL); |
1227 | | |
1228 | 138k | if (strlcpy(ucase_name,name,sizeof(ucase_name)) >= sizeof(ucase_name)) |
1229 | 0 | { |
1230 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1231 | 0 | "Profile name too long! (%s)",name); |
1232 | 0 | return MagickFail; |
1233 | 0 | } |
1234 | 138k | LocaleUpper(ucase_name); |
1235 | | |
1236 | 138k | if ((profile == 0) && (image->profiles != 0)) |
1237 | 1 | { |
1238 | | /* |
1239 | | Remove existing entry. |
1240 | | */ |
1241 | 1 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1242 | 1 | "Removing %s profile",name); |
1243 | 1 | status &= MagickMapRemoveEntry(image->profiles,name); |
1244 | 1 | } |
1245 | 138k | else |
1246 | 138k | { |
1247 | | /* |
1248 | | Add or replace entry. |
1249 | | */ |
1250 | 138k | if (image->profiles == 0) |
1251 | 46.9k | image->profiles=MagickMapAllocateMap(MagickMapCopyResourceLimitedBlob, |
1252 | 46.9k | MagickMapDeallocateResourceLimitedBlob); |
1253 | | |
1254 | 138k | if (image->profiles == 0) |
1255 | 0 | ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed, |
1256 | 138k | UnableToAddOrRemoveProfile); |
1257 | | |
1258 | 138k | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1259 | 138k | "Adding %s profile with length %ld bytes",name, |
1260 | 138k | (unsigned long) length); |
1261 | 138k | if ((profile != 0) && (length != 0)) |
1262 | 138k | { |
1263 | 138k | status &= MagickMapAddEntry(image->profiles,name,profile,length, |
1264 | 138k | &image->exception); |
1265 | 138k | } |
1266 | 138k | } |
1267 | 138k | return (status); |
1268 | 138k | } |