profile.c revision e9dbde130208643c1c27df9cf273a9d87870efe7
1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% PPPP RRRR OOO FFFFF IIIII L EEEEE % 7% P P R R O O F I L E % 8% PPPP RRRR O O FFF I L EEE % 9% P R R O O F I L E % 10% P R R OOO F IIIII LLLLL EEEEE % 11% % 12% % 13% MagickCore Image Profile Methods % 14% % 15% Software Design % 16% Cristy % 17% July 1992 % 18% % 19% % 20% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization % 21% dedicated to making software imaging solutions freely available. % 22% % 23% You may not use this file except in compliance with the License. You may % 24% obtain a copy of the License at % 25% % 26% http://www.imagemagick.org/script/license.php % 27% % 28% Unless required by applicable law or agreed to in writing, software % 29% distributed under the License is distributed on an "AS IS" BASIS, % 30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31% See the License for the specific language governing permissions and % 32% limitations under the License. % 33% % 34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35% 36% 37*/ 38 39/* 40 Include declarations. 41*/ 42#include "MagickCore/studio.h" 43#include "MagickCore/attribute.h" 44#include "MagickCore/cache.h" 45#include "MagickCore/color.h" 46#include "MagickCore/colorspace-private.h" 47#include "MagickCore/configure.h" 48#include "MagickCore/exception.h" 49#include "MagickCore/exception-private.h" 50#include "MagickCore/hashmap.h" 51#include "MagickCore/image.h" 52#include "MagickCore/memory_.h" 53#include "MagickCore/monitor.h" 54#include "MagickCore/monitor-private.h" 55#include "MagickCore/option.h" 56#include "MagickCore/pixel-accessor.h" 57#include "MagickCore/profile.h" 58#include "MagickCore/profile-private.h" 59#include "MagickCore/property.h" 60#include "MagickCore/quantum.h" 61#include "MagickCore/quantum-private.h" 62#include "MagickCore/resource_.h" 63#include "MagickCore/splay-tree.h" 64#include "MagickCore/string_.h" 65#include "MagickCore/thread-private.h" 66#include "MagickCore/token.h" 67#include "MagickCore/utility.h" 68#if defined(MAGICKCORE_LCMS_DELEGATE) 69#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H) 70#include <wchar.h> 71#include <lcms/lcms2.h> 72#elif defined(MAGICKCORE_HAVE_LCMS2_H) 73#include <wchar.h> 74#include "lcms2.h" 75#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H) 76#include <lcms/lcms.h> 77#else 78#include "lcms.h" 79#endif 80#endif 81 82/* 83 Define declarations. 84*/ 85#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000) 86#define cmsSigCmykData icSigCmykData 87#define cmsSigGrayData icSigGrayData 88#define cmsSigLabData icSigLabData 89#define cmsSigLuvData icSigLuvData 90#define cmsSigRgbData icSigRgbData 91#define cmsSigXYZData icSigXYZData 92#define cmsSigYCbCrData icSigYCbCrData 93#define cmsSigLinkClass icSigLinkClass 94#define cmsColorSpaceSignature icColorSpaceSignature 95#define cmsUInt32Number DWORD 96#define cmsSetLogErrorHandler(handler) cmsSetErrorHandler(handler) 97#define cmsCreateTransformTHR(context,source_profile,source_type, \ 98 target_profile,target_type,intent,flags) cmsCreateTransform(source_profile, \ 99 source_type,target_profile,target_type,intent,flags); 100#define cmsOpenProfileFromMemTHR(context,profile,length) \ 101 cmsOpenProfileFromMem(profile,length) 102#endif 103 104/* 105 Forward declarations 106*/ 107static MagickBooleanType 108 SetImageProfileInternal(Image *,const char *,const StringInfo *, 109 const MagickBooleanType,ExceptionInfo *); 110 111static void 112 WriteTo8BimProfile(Image *,const char*,const StringInfo *); 113 114/* 115 Typedef declarations 116*/ 117struct _ProfileInfo 118{ 119 char 120 *name; 121 122 size_t 123 length; 124 125 unsigned char 126 *info; 127 128 size_t 129 signature; 130}; 131 132typedef struct _CMSExceptionInfo 133{ 134 Image 135 *image; 136 137 ExceptionInfo 138 *exception; 139} CMSExceptionInfo; 140 141/* 142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 143% % 144% % 145% % 146% C l o n e I m a g e P r o f i l e s % 147% % 148% % 149% % 150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 151% 152% CloneImageProfiles() clones one or more image profiles. 153% 154% The format of the CloneImageProfiles method is: 155% 156% MagickBooleanType CloneImageProfiles(Image *image, 157% const Image *clone_image) 158% 159% A description of each parameter follows: 160% 161% o image: the image. 162% 163% o clone_image: the clone image. 164% 165*/ 166MagickExport MagickBooleanType CloneImageProfiles(Image *image, 167 const Image *clone_image) 168{ 169 assert(image != (Image *) NULL); 170 assert(image->signature == MagickSignature); 171 if (image->debug != MagickFalse) 172 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 173 assert(clone_image != (const Image *) NULL); 174 assert(clone_image->signature == MagickSignature); 175 if (clone_image->profiles != (void *) NULL) 176 { 177 if (image->profiles != (void *) NULL) 178 DestroyImageProfiles(image); 179 image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles, 180 (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo); 181 } 182 return(MagickTrue); 183} 184 185/* 186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 187% % 188% % 189% % 190% D e l e t e I m a g e P r o f i l e % 191% % 192% % 193% % 194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 195% 196% DeleteImageProfile() deletes a profile from the image by its name. 197% 198% The format of the DeleteImageProfile method is: 199% 200% MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name) 201% 202% A description of each parameter follows: 203% 204% o image: the image. 205% 206% o name: the profile name. 207% 208*/ 209MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name) 210{ 211 assert(image != (Image *) NULL); 212 assert(image->signature == MagickSignature); 213 if (image->debug != MagickFalse) 214 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 215 if (image->profiles == (SplayTreeInfo *) NULL) 216 return(MagickFalse); 217 WriteTo8BimProfile(image,name,(StringInfo *) NULL); 218 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name)); 219} 220 221/* 222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 223% % 224% % 225% % 226% D e s t r o y I m a g e P r o f i l e s % 227% % 228% % 229% % 230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 231% 232% DestroyImageProfiles() releases memory associated with an image profile map. 233% 234% The format of the DestroyProfiles method is: 235% 236% void DestroyImageProfiles(Image *image) 237% 238% A description of each parameter follows: 239% 240% o image: the image. 241% 242*/ 243MagickExport void DestroyImageProfiles(Image *image) 244{ 245 if (image->profiles != (SplayTreeInfo *) NULL) 246 image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles); 247} 248 249/* 250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 251% % 252% % 253% % 254% G e t I m a g e P r o f i l e % 255% % 256% % 257% % 258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 259% 260% GetImageProfile() gets a profile associated with an image by name. 261% 262% The format of the GetImageProfile method is: 263% 264% const StringInfo *GetImageProfile(const Image *image,const char *name) 265% 266% A description of each parameter follows: 267% 268% o image: the image. 269% 270% o name: the profile name. 271% 272*/ 273MagickExport const StringInfo *GetImageProfile(const Image *image, 274 const char *name) 275{ 276 char 277 key[MaxTextExtent]; 278 279 const StringInfo 280 *profile; 281 282 assert(image != (Image *) NULL); 283 assert(image->signature == MagickSignature); 284 if (image->debug != MagickFalse) 285 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 286 if (image->profiles == (SplayTreeInfo *) NULL) 287 return((StringInfo *) NULL); 288 (void) CopyMagickString(key,name,MaxTextExtent); 289 profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *) 290 image->profiles,key); 291 return(profile); 292} 293 294/* 295%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 296% % 297% % 298% % 299% G e t N e x t I m a g e P r o f i l e % 300% % 301% % 302% % 303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 304% 305% GetNextImageProfile() gets the next profile name for an image. 306% 307% The format of the GetNextImageProfile method is: 308% 309% char *GetNextImageProfile(const Image *image) 310% 311% A description of each parameter follows: 312% 313% o hash_info: the hash info. 314% 315*/ 316MagickExport char *GetNextImageProfile(const Image *image) 317{ 318 assert(image != (Image *) NULL); 319 assert(image->signature == MagickSignature); 320 if (image->debug != MagickFalse) 321 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 322 if (image->profiles == (SplayTreeInfo *) NULL) 323 return((char *) NULL); 324 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles)); 325} 326 327/* 328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 329% % 330% % 331% % 332% P r o f i l e I m a g e % 333% % 334% % 335% % 336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 337% 338% ProfileImage() associates, applies, or removes an ICM, IPTC, or generic 339% profile with / to / from an image. If the profile is NULL, it is removed 340% from the image otherwise added or applied. Use a name of '*' and a profile 341% of NULL to remove all profiles from the image. 342% 343% ICC and ICM profiles are handled as follows: If the image does not have 344% an associated color profile, the one you provide is associated with the 345% image and the image pixels are not transformed. Otherwise, the colorspace 346% transform defined by the existing and new profile are applied to the image 347% pixels and the new profile is associated with the image. 348% 349% The format of the ProfileImage method is: 350% 351% MagickBooleanType ProfileImage(Image *image,const char *name, 352% const void *datum,const size_t length,const MagickBooleanType clone) 353% 354% A description of each parameter follows: 355% 356% o image: the image. 357% 358% o name: Name of profile to add or remove: ICC, IPTC, or generic profile. 359% 360% o datum: the profile data. 361% 362% o length: the length of the profile. 363% 364% o clone: should be MagickFalse. 365% 366*/ 367 368#if defined(MAGICKCORE_LCMS_DELEGATE) 369 370static unsigned short **DestroyPixelThreadSet(unsigned short **pixels) 371{ 372 register ssize_t 373 i; 374 375 assert(pixels != (unsigned short **) NULL); 376 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++) 377 if (pixels[i] != (unsigned short *) NULL) 378 pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]); 379 pixels=(unsigned short **) RelinquishMagickMemory(pixels); 380 return(pixels); 381} 382 383static unsigned short **AcquirePixelThreadSet(const size_t columns, 384 const size_t channels) 385{ 386 register ssize_t 387 i; 388 389 unsigned short 390 **pixels; 391 392 size_t 393 number_threads; 394 395 number_threads=(size_t) GetMagickResourceLimit(ThreadResource); 396 pixels=(unsigned short **) AcquireQuantumMemory(number_threads, 397 sizeof(*pixels)); 398 if (pixels == (unsigned short **) NULL) 399 return((unsigned short **) NULL); 400 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels)); 401 for (i=0; i < (ssize_t) number_threads; i++) 402 { 403 pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels* 404 sizeof(**pixels)); 405 if (pixels[i] == (unsigned short *) NULL) 406 return(DestroyPixelThreadSet(pixels)); 407 } 408 return(pixels); 409} 410 411static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform) 412{ 413 register ssize_t 414 i; 415 416 assert(transform != (cmsHTRANSFORM *) NULL); 417 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++) 418 if (transform[i] != (cmsHTRANSFORM) NULL) 419 cmsDeleteTransform(transform[i]); 420 transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform); 421 return(transform); 422} 423 424static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image, 425 const cmsHPROFILE source_profile,const cmsUInt32Number source_type, 426 const cmsHPROFILE target_profile,const cmsUInt32Number target_type, 427 const int intent,const cmsUInt32Number flags) 428{ 429 cmsHTRANSFORM 430 *transform; 431 432 register ssize_t 433 i; 434 435 size_t 436 number_threads; 437 438 number_threads=(size_t) GetMagickResourceLimit(ThreadResource); 439 transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads, 440 sizeof(*transform)); 441 if (transform == (cmsHTRANSFORM *) NULL) 442 return((cmsHTRANSFORM *) NULL); 443 (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform)); 444 for (i=0; i < (ssize_t) number_threads; i++) 445 { 446 transform[i]=cmsCreateTransformTHR((cmsContext) image,source_profile, 447 source_type,target_profile,target_type,intent,flags); 448 if (transform[i] == (cmsHTRANSFORM) NULL) 449 return(DestroyTransformThreadSet(transform)); 450 } 451 return(transform); 452} 453#endif 454 455#if defined(MAGICKCORE_LCMS_DELEGATE) 456#if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000) 457static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity, 458 const char *message) 459{ 460 CMSExceptionInfo 461 *cms_exception; 462 463 ExceptionInfo 464 *exception; 465 466 Image 467 *image; 468 469 cms_exception=(CMSExceptionInfo *) context; 470 image=cms_exception->image; 471 exception=cms_exception->exception; 472 if (image == (Image *) NULL) 473 { 474 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning, 475 "UnableToTransformColorspace","`%s'","unknown context"); 476 return; 477 } 478 if (image->debug != MagickFalse) 479 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s", 480 severity,message != (char *) NULL ? message : "no message"); 481 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning, 482 "UnableToTransformColorspace","`%s'",image->filename); 483} 484#else 485static int CMSExceptionHandler(int severity,const char *message) 486{ 487 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s", 488 severity,message != (char *) NULL ? message : "no message"); 489 return(1); 490} 491#endif 492#endif 493 494MagickExport MagickBooleanType ProfileImage(Image *image,const char *name, 495 const void *datum,const size_t length,ExceptionInfo *exception) 496{ 497#define ProfileImageTag "Profile/Image" 498#define ThrowProfileException(severity,tag,context) \ 499{ \ 500 if (source_profile != (cmsHPROFILE) NULL) \ 501 (void) cmsCloseProfile(source_profile); \ 502 if (target_profile != (cmsHPROFILE) NULL) \ 503 (void) cmsCloseProfile(target_profile); \ 504 ThrowBinaryException(severity,tag,context); \ 505} 506 507 MagickBooleanType 508 status; 509 510 StringInfo 511 *profile; 512 513 assert(image != (Image *) NULL); 514 assert(image->signature == MagickSignature); 515 if (image->debug != MagickFalse) 516 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 517 assert(name != (const char *) NULL); 518 if ((datum == (const void *) NULL) || (length == 0)) 519 { 520 char 521 **arguments, 522 *names; 523 524 int 525 number_arguments; 526 527 register ssize_t 528 i; 529 530 /* 531 Delete image profile(s). 532 */ 533 names=ConstantString(name); 534 (void) SubstituteString(&names,","," "); 535 arguments=StringToArgv(names,&number_arguments); 536 names=DestroyString(names); 537 if (arguments == (char **) NULL) 538 return(MagickTrue); 539 ResetImageProfileIterator(image); 540 for (name=GetNextImageProfile(image); name != (const char *) NULL; ) 541 { 542 for (i=1; i < (ssize_t) number_arguments; i++) 543 { 544 if ((*arguments[i] == '!') && 545 (LocaleCompare(name,arguments[i]+1) == 0)) 546 break; 547 if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse) 548 { 549 (void) DeleteImageProfile(image,name); 550 break; 551 } 552 } 553 name=GetNextImageProfile(image); 554 } 555 for (i=0; i < (ssize_t) number_arguments; i++) 556 arguments[i]=DestroyString(arguments[i]); 557 arguments=(char **) RelinquishMagickMemory(arguments); 558 return(MagickTrue); 559 } 560 /* 561 Add a ICC, IPTC, or generic profile to the image. 562 */ 563 status=MagickTrue; 564 profile=AcquireStringInfo((size_t) length); 565 SetStringInfoDatum(profile,(unsigned char *) datum); 566 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0)) 567 status=SetImageProfile(image,name,profile,exception); 568 else 569 { 570 const StringInfo 571 *icc_profile; 572 573 icc_profile=GetImageProfile(image,"icc"); 574 if ((icc_profile != (const StringInfo *) NULL) && 575 (CompareStringInfo(icc_profile,profile) == 0)) 576 { 577 const char 578 *value; 579 580 value=GetImageProperty(image,"exif:ColorSpace",exception); 581 (void) value; 582 /* Future. 583 if (LocaleCompare(value,"1") != 0) 584 (void) SetsRGBImageProfile(image,exception); 585 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception); 586 if (LocaleCompare(value,"R98.") != 0) 587 (void) SetsRGBImageProfile(image,exception); 588 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception); 589 if (LocaleCompare(value,"R03.") != 0) 590 (void) SetAdobeRGB1998ImageProfile(image,exception); 591 */ 592 icc_profile=GetImageProfile(image,"icc"); 593 } 594 if ((icc_profile != (const StringInfo *) NULL) && 595 (CompareStringInfo(icc_profile,profile) == 0)) 596 { 597 profile=DestroyStringInfo(profile); 598 return(MagickTrue); 599 } 600#if !defined(MAGICKCORE_LCMS_DELEGATE) 601 (void) ThrowMagickException(exception,GetMagickModule(), 602 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn", 603 "'%s' (LCMS)",image->filename); 604#else 605 { 606 cmsHPROFILE 607 source_profile; 608 609 CMSExceptionInfo 610 cms_exception; 611 612 /* 613 Transform pixel colors as defined by the color profiles. 614 */ 615 cmsSetLogErrorHandler(CMSExceptionHandler); 616 cms_exception.image=image; 617 cms_exception.exception=exception; 618 (void) cms_exception; 619 source_profile=cmsOpenProfileFromMemTHR((cmsContext) &cms_exception, 620 GetStringInfoDatum(profile),(cmsUInt32Number) 621 GetStringInfoLength(profile)); 622 if (source_profile == (cmsHPROFILE) NULL) 623 ThrowBinaryException(ResourceLimitError, 624 "ColorspaceColorProfileMismatch",name); 625 if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) && 626 (icc_profile == (StringInfo *) NULL)) 627 status=SetImageProfile(image,name,profile,exception); 628 else 629 { 630 CacheView 631 *image_view; 632 633 ColorspaceType 634 source_colorspace, 635 target_colorspace; 636 637 cmsColorSpaceSignature 638 signature; 639 640 cmsHPROFILE 641 target_profile; 642 643 cmsHTRANSFORM 644 *restrict transform; 645 646 cmsUInt32Number 647 flags, 648 source_type, 649 target_type; 650 651 int 652 intent; 653 654 MagickBooleanType 655 status; 656 657 MagickOffsetType 658 progress; 659 660 size_t 661 source_channels, 662 target_channels; 663 664 ssize_t 665 y; 666 667 unsigned short 668 **restrict source_pixels, 669 **restrict target_pixels; 670 671 target_profile=(cmsHPROFILE) NULL; 672 if (icc_profile != (StringInfo *) NULL) 673 { 674 target_profile=source_profile; 675 source_profile=cmsOpenProfileFromMemTHR((cmsContext) 676 &cms_exception,GetStringInfoDatum(icc_profile), 677 (cmsUInt32Number) GetStringInfoLength(icc_profile)); 678 if (source_profile == (cmsHPROFILE) NULL) 679 ThrowProfileException(ResourceLimitError, 680 "ColorspaceColorProfileMismatch",name); 681 } 682 switch (cmsGetColorSpace(source_profile)) 683 { 684 case cmsSigCmykData: 685 { 686 source_colorspace=CMYKColorspace; 687 source_type=(cmsUInt32Number) TYPE_CMYK_16; 688 source_channels=4; 689 break; 690 } 691 case cmsSigGrayData: 692 { 693 source_colorspace=GRAYColorspace; 694 source_type=(cmsUInt32Number) TYPE_GRAY_16; 695 source_channels=1; 696 break; 697 } 698 case cmsSigLabData: 699 { 700 source_colorspace=LabColorspace; 701 source_type=(cmsUInt32Number) TYPE_Lab_16; 702 source_channels=3; 703 break; 704 } 705 case cmsSigLuvData: 706 { 707 source_colorspace=YUVColorspace; 708 source_type=(cmsUInt32Number) TYPE_YUV_16; 709 source_channels=3; 710 break; 711 } 712 case cmsSigRgbData: 713 { 714 source_colorspace=sRGBColorspace; 715 source_type=(cmsUInt32Number) TYPE_RGB_16; 716 source_channels=3; 717 break; 718 } 719 case cmsSigXYZData: 720 { 721 source_colorspace=XYZColorspace; 722 source_type=(cmsUInt32Number) TYPE_XYZ_16; 723 source_channels=3; 724 break; 725 } 726 case cmsSigYCbCrData: 727 { 728 source_colorspace=YCbCrColorspace; 729 source_type=(cmsUInt32Number) TYPE_YCbCr_16; 730 source_channels=3; 731 break; 732 } 733 default: 734 { 735 source_colorspace=UndefinedColorspace; 736 source_type=(cmsUInt32Number) TYPE_RGB_16; 737 source_channels=3; 738 break; 739 } 740 } 741 signature=cmsGetPCS(source_profile); 742 if (target_profile != (cmsHPROFILE) NULL) 743 signature=cmsGetColorSpace(target_profile); 744 switch (signature) 745 { 746 case cmsSigCmykData: 747 { 748 target_colorspace=CMYKColorspace; 749 target_type=(cmsUInt32Number) TYPE_CMYK_16; 750 target_channels=4; 751 break; 752 } 753 case cmsSigLabData: 754 { 755 target_colorspace=LabColorspace; 756 target_type=(cmsUInt32Number) TYPE_Lab_16; 757 target_channels=3; 758 break; 759 } 760 case cmsSigGrayData: 761 { 762 target_colorspace=GRAYColorspace; 763 target_type=(cmsUInt32Number) TYPE_GRAY_16; 764 target_channels=1; 765 break; 766 } 767 case cmsSigLuvData: 768 { 769 target_colorspace=YUVColorspace; 770 target_type=(cmsUInt32Number) TYPE_YUV_16; 771 target_channels=3; 772 break; 773 } 774 case cmsSigRgbData: 775 { 776 target_colorspace=sRGBColorspace; 777 target_type=(cmsUInt32Number) TYPE_RGB_16; 778 target_channels=3; 779 break; 780 } 781 case cmsSigXYZData: 782 { 783 target_colorspace=XYZColorspace; 784 target_type=(cmsUInt32Number) TYPE_XYZ_16; 785 target_channels=3; 786 break; 787 } 788 case cmsSigYCbCrData: 789 { 790 target_colorspace=YCbCrColorspace; 791 target_type=(cmsUInt32Number) TYPE_YCbCr_16; 792 target_channels=3; 793 break; 794 } 795 default: 796 { 797 target_colorspace=UndefinedColorspace; 798 target_type=(cmsUInt32Number) TYPE_RGB_16; 799 target_channels=3; 800 break; 801 } 802 } 803 if ((source_colorspace == UndefinedColorspace) || 804 (target_colorspace == UndefinedColorspace)) 805 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch", 806 name); 807 if ((source_colorspace == GRAYColorspace) && 808 (IsImageGray(image,exception) == MagickFalse)) 809 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch", 810 name); 811 if ((source_colorspace == CMYKColorspace) && 812 (image->colorspace != CMYKColorspace)) 813 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch", 814 name); 815 if ((source_colorspace == XYZColorspace) && 816 (image->colorspace != XYZColorspace)) 817 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch", 818 name); 819 if ((source_colorspace == YCbCrColorspace) && 820 (image->colorspace != YCbCrColorspace)) 821 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch", 822 name); 823 if ((source_colorspace != CMYKColorspace) && 824 (source_colorspace != LabColorspace) && 825 (source_colorspace != XYZColorspace) && 826 (source_colorspace != YCbCrColorspace) && 827 (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)) 828 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch", 829 name); 830 switch (image->rendering_intent) 831 { 832 case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break; 833 case PerceptualIntent: intent=INTENT_PERCEPTUAL; break; 834 case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break; 835 case SaturationIntent: intent=INTENT_SATURATION; break; 836 default: intent=INTENT_PERCEPTUAL; break; 837 } 838 flags=cmsFLAGS_HIGHRESPRECALC; 839#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION) 840 if (image->black_point_compensation != MagickFalse) 841 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION; 842#endif 843 transform=AcquireTransformThreadSet(image,source_profile, 844 source_type,target_profile,target_type,intent,flags); 845 if (transform == (cmsHTRANSFORM *) NULL) 846 ThrowProfileException(ImageError,"UnableToCreateColorTransform", 847 name); 848 /* 849 Transform image as dictated by the source & target image profiles. 850 */ 851 source_pixels=AcquirePixelThreadSet(image->columns,source_channels); 852 target_pixels=AcquirePixelThreadSet(image->columns,target_channels); 853 if ((source_pixels == (unsigned short **) NULL) || 854 (target_pixels == (unsigned short **) NULL)) 855 { 856 transform=DestroyTransformThreadSet(transform); 857 ThrowProfileException(ResourceLimitError, 858 "MemoryAllocationFailed",image->filename); 859 } 860 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 861 { 862 target_pixels=DestroyPixelThreadSet(target_pixels); 863 source_pixels=DestroyPixelThreadSet(source_pixels); 864 transform=DestroyTransformThreadSet(transform); 865 if (source_profile != (cmsHPROFILE) NULL) 866 (void) cmsCloseProfile(source_profile); 867 if (target_profile != (cmsHPROFILE) NULL) 868 (void) cmsCloseProfile(target_profile); 869 return(MagickFalse); 870 } 871 if (target_colorspace == CMYKColorspace) 872 (void) SetImageColorspace(image,target_colorspace,exception); 873 status=MagickTrue; 874 progress=0; 875 image_view=AcquireAuthenticCacheView(image,exception); 876#if defined(MAGICKCORE_OPENMP_SUPPORT) 877 #pragma omp parallel for schedule(static,4) shared(status) \ 878 magick_threads(image,image,image->rows,1) 879#endif 880 for (y=0; y < (ssize_t) image->rows; y++) 881 { 882 const int 883 id = GetOpenMPThreadId(); 884 885 MagickBooleanType 886 sync; 887 888 register ssize_t 889 x; 890 891 register Quantum 892 *restrict q; 893 894 register unsigned short 895 *p; 896 897 if (status == MagickFalse) 898 continue; 899 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 900 exception); 901 if (q == (Quantum *) NULL) 902 { 903 status=MagickFalse; 904 continue; 905 } 906 p=source_pixels[id]; 907 for (x=0; x < (ssize_t) image->columns; x++) 908 { 909 *p++=ScaleQuantumToShort(GetPixelRed(image,q)); 910 if (source_channels > 1) 911 { 912 *p++=ScaleQuantumToShort(GetPixelGreen(image,q)); 913 *p++=ScaleQuantumToShort(GetPixelBlue(image,q)); 914 } 915 if (source_channels > 3) 916 *p++=ScaleQuantumToShort(GetPixelBlack(image,q)); 917 q+=GetPixelChannels(image); 918 } 919 cmsDoTransform(transform[id],source_pixels[id],target_pixels[id], 920 (unsigned int) image->columns); 921 p=target_pixels[id]; 922 q-=image->columns*GetPixelChannels(image); 923 for (x=0; x < (ssize_t) image->columns; x++) 924 { 925 if (target_channels == 1) 926 SetPixelGray(image,ScaleShortToQuantum(*p),q); 927 else 928 SetPixelRed(image,ScaleShortToQuantum(*p),q); 929 p++; 930 if (target_channels > 1) 931 { 932 SetPixelGreen(image,ScaleShortToQuantum(*p),q); 933 p++; 934 SetPixelBlue(image,ScaleShortToQuantum(*p),q); 935 p++; 936 } 937 if (target_channels > 3) 938 { 939 SetPixelBlack(image,ScaleShortToQuantum(*p),q); 940 p++; 941 } 942 q+=GetPixelChannels(image); 943 } 944 sync=SyncCacheViewAuthenticPixels(image_view,exception); 945 if (sync == MagickFalse) 946 status=MagickFalse; 947 if (image->progress_monitor != (MagickProgressMonitor) NULL) 948 { 949 MagickBooleanType 950 proceed; 951 952#if defined(MAGICKCORE_OPENMP_SUPPORT) 953 #pragma omp critical (MagickCore_ProfileImage) 954#endif 955 proceed=SetImageProgress(image,ProfileImageTag,progress++, 956 image->rows); 957 if (proceed == MagickFalse) 958 status=MagickFalse; 959 } 960 } 961 image_view=DestroyCacheView(image_view); 962 (void) SetImageColorspace(image,target_colorspace,exception); 963 switch (signature) 964 { 965 case cmsSigRgbData: 966 { 967 image->type=image->alpha_trait != BlendPixelTrait ? 968 TrueColorType : TrueColorMatteType; 969 break; 970 } 971 case cmsSigCmykData: 972 { 973 image->type=image->alpha_trait != BlendPixelTrait ? 974 ColorSeparationType : ColorSeparationMatteType; 975 break; 976 } 977 case cmsSigGrayData: 978 { 979 image->type=image->alpha_trait != BlendPixelTrait ? 980 GrayscaleType : GrayscaleMatteType; 981 break; 982 } 983 default: 984 break; 985 } 986 target_pixels=DestroyPixelThreadSet(target_pixels); 987 source_pixels=DestroyPixelThreadSet(source_pixels); 988 transform=DestroyTransformThreadSet(transform); 989 if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass) 990 status=SetImageProfile(image,name,profile,exception); 991 if (target_profile != (cmsHPROFILE) NULL) 992 (void) cmsCloseProfile(target_profile); 993 } 994 (void) cmsCloseProfile(source_profile); 995 } 996#endif 997 } 998 profile=DestroyStringInfo(profile); 999 return(status); 1000} 1001 1002/* 1003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1004% % 1005% % 1006% % 1007% R e m o v e I m a g e P r o f i l e % 1008% % 1009% % 1010% % 1011%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1012% 1013% RemoveImageProfile() removes a named profile from the image and returns its 1014% value. 1015% 1016% The format of the RemoveImageProfile method is: 1017% 1018% void *RemoveImageProfile(Image *image,const char *name) 1019% 1020% A description of each parameter follows: 1021% 1022% o image: the image. 1023% 1024% o name: the profile name. 1025% 1026*/ 1027MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name) 1028{ 1029 StringInfo 1030 *profile; 1031 1032 assert(image != (Image *) NULL); 1033 assert(image->signature == MagickSignature); 1034 if (image->debug != MagickFalse) 1035 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1036 if (image->profiles == (SplayTreeInfo *) NULL) 1037 return((StringInfo *) NULL); 1038 WriteTo8BimProfile(image,name,(StringInfo *) NULL); 1039 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *) 1040 image->profiles,name); 1041 return(profile); 1042} 1043 1044/* 1045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1046% % 1047% % 1048% % 1049% R e s e t P r o f i l e I t e r a t o r % 1050% % 1051% % 1052% % 1053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1054% 1055% ResetImageProfileIterator() resets the image profile iterator. Use it in 1056% conjunction with GetNextImageProfile() to iterate over all the profiles 1057% associated with an image. 1058% 1059% The format of the ResetImageProfileIterator method is: 1060% 1061% ResetImageProfileIterator(Image *image) 1062% 1063% A description of each parameter follows: 1064% 1065% o image: the image. 1066% 1067*/ 1068MagickExport void ResetImageProfileIterator(const Image *image) 1069{ 1070 assert(image != (Image *) NULL); 1071 assert(image->signature == MagickSignature); 1072 if (image->debug != MagickFalse) 1073 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1074 if (image->profiles == (SplayTreeInfo *) NULL) 1075 return; 1076 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles); 1077} 1078 1079/* 1080%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1081% % 1082% % 1083% % 1084% S e t I m a g e P r o f i l e % 1085% % 1086% % 1087% % 1088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1089% 1090% SetImageProfile() adds a named profile to the image. If a profile with the 1091% same name already exists, it is replaced. This method differs from the 1092% ProfileImage() method in that it does not apply CMS color profiles. 1093% 1094% The format of the SetImageProfile method is: 1095% 1096% MagickBooleanType SetImageProfile(Image *image,const char *name, 1097% const StringInfo *profile) 1098% 1099% A description of each parameter follows: 1100% 1101% o image: the image. 1102% 1103% o name: the profile name, for example icc, exif, and 8bim (8bim is the 1104% Photoshop wrapper for iptc profiles). 1105% 1106% o profile: A StringInfo structure that contains the named profile. 1107% 1108*/ 1109 1110static void *DestroyProfile(void *profile) 1111{ 1112 return((void *) DestroyStringInfo((StringInfo *) profile)); 1113} 1114 1115static inline const unsigned char *ReadResourceByte(const unsigned char *p, 1116 unsigned char *quantum) 1117{ 1118 *quantum=(*p++); 1119 return(p); 1120} 1121 1122static inline const unsigned char *ReadResourceBytes(const unsigned char *p, 1123 const ssize_t count,unsigned char *quantum) 1124{ 1125 register ssize_t 1126 i; 1127 1128 for (i=0; i < count; i++) 1129 *quantum++=(*p++); 1130 return(p); 1131} 1132 1133static inline const unsigned char *ReadResourceLong(const unsigned char *p, 1134 unsigned int *quantum) 1135{ 1136 *quantum=(size_t) (*p++ << 24); 1137 *quantum|=(size_t) (*p++ << 16); 1138 *quantum|=(size_t) (*p++ << 8); 1139 *quantum|=(size_t) (*p++ << 0); 1140 return(p); 1141} 1142 1143static inline const unsigned char *ReadResourceShort(const unsigned char *p, 1144 unsigned short *quantum) 1145{ 1146 *quantum=(unsigned short) (*p++ << 8); 1147 *quantum|=(unsigned short) (*p++ << 0); 1148 return(p); 1149}static inline void WriteResourceLong(unsigned char *p, 1150 const unsigned int quantum) 1151{ 1152 unsigned char 1153 buffer[4]; 1154 1155 buffer[0]=(unsigned char) (quantum >> 24); 1156 buffer[1]=(unsigned char) (quantum >> 16); 1157 buffer[2]=(unsigned char) (quantum >> 8); 1158 buffer[3]=(unsigned char) quantum; 1159 (void) CopyMagickMemory(p,buffer,4); 1160} 1161 1162static void WriteTo8BimProfile(Image *image,const char *name, 1163 const StringInfo *iptc_profile) 1164{ 1165 1166 const unsigned char 1167 *datum, 1168 *s; 1169 1170 register const unsigned char 1171 *p; 1172 1173 size_t 1174 length; 1175 1176 StringInfo 1177 *profile; 1178 1179 ssize_t 1180 count; 1181 1182 unsigned char 1183 length_byte; 1184 1185 unsigned int 1186 value; 1187 1188 unsigned short 1189 id; 1190 1191 if (LocaleCompare(name,"iptc") != 0) 1192 return; 1193 profile=(StringInfo *)GetValueFromSplayTree((SplayTreeInfo *)image->profiles, 1194 "8bim"); 1195 if (profile == (StringInfo *) NULL) 1196 return; 1197 datum=GetStringInfoDatum(profile); 1198 length=GetStringInfoLength(profile); 1199 for (p=datum; p < (datum+length-16); ) 1200 { 1201 s=p; 1202 if (LocaleNCompare((char *) p,"8BIM",4) != 0) 1203 break; 1204 p+=4; 1205 p=ReadResourceShort(p,&id); 1206 p=ReadResourceByte(p,&length_byte); 1207 p+=length_byte; 1208 if (((length_byte+1) & 0x01) != 0) 1209 p++; 1210 if (p > (datum+length-4)) 1211 break; 1212 p=ReadResourceLong(p,&value); 1213 count=(ssize_t) value; 1214 if ((p > (datum+length-count)) || (count > length)) 1215 break; 1216 if ((count & 0x01) != 0) 1217 count++; 1218 if (id == 0x0404) 1219 { 1220 size_t 1221 offset, 1222 rest; 1223 1224 ssize_t 1225 new_count; 1226 1227 StringInfo 1228 *new_profile; 1229 1230 new_count=0; 1231 rest=(datum+length)-(p+count); 1232 if (iptc_profile == (StringInfo *) NULL) 1233 { 1234 offset=(s-datum); 1235 new_profile=AcquireStringInfo(offset+rest); 1236 (void) CopyMagickMemory(new_profile->datum,datum,offset); 1237 } 1238 else 1239 { 1240 offset=(p-datum); 1241 new_count=iptc_profile->length; 1242 if ((new_count & 0x01) != 0) 1243 new_count++; 1244 new_profile=AcquireStringInfo(offset+new_count+rest); 1245 (void) CopyMagickMemory(new_profile->datum,datum,offset-4); 1246 WriteResourceLong(new_profile->datum+offset-4, 1247 iptc_profile->length); 1248 (void) CopyMagickMemory(new_profile->datum+offset, 1249 iptc_profile->datum,iptc_profile->length); 1250 } 1251 (void) CopyMagickMemory(new_profile->datum+offset+new_count,p+count, 1252 rest); 1253 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles, 1254 ConstantString("8bim"),CloneStringInfo(new_profile)); 1255 new_profile=DestroyStringInfo(new_profile); 1256 break; 1257 } 1258 else 1259 p+=count; 1260 } 1261} 1262 1263static void GetProfilesFromResourceBlock(Image *image, 1264 const StringInfo *resource_block,ExceptionInfo *exception) 1265{ 1266 const unsigned char 1267 *datum; 1268 1269 register const unsigned char 1270 *p; 1271 1272 size_t 1273 length; 1274 1275 ssize_t 1276 count; 1277 1278 StringInfo 1279 *profile; 1280 1281 unsigned char 1282 length_byte; 1283 1284 unsigned int 1285 value; 1286 1287 unsigned short 1288 id; 1289 1290 datum=GetStringInfoDatum(resource_block); 1291 length=GetStringInfoLength(resource_block); 1292 for (p=datum; p < (datum+length-16); ) 1293 { 1294 if (LocaleNCompare((char *) p,"8BIM",4) != 0) 1295 break; 1296 p+=4; 1297 p=ReadResourceShort(p,&id); 1298 p=ReadResourceByte(p,&length_byte); 1299 p+=length_byte; 1300 if (((length_byte+1) & 0x01) != 0) 1301 p++; 1302 if (p > (datum+length-4)) 1303 break; 1304 p=ReadResourceLong(p,&value); 1305 count=(ssize_t) value; 1306 if ((p > (datum+length-count)) || (count > length)) 1307 break; 1308 switch (id) 1309 { 1310 case 0x03ed: 1311 { 1312 unsigned int 1313 resolution; 1314 1315 unsigned short 1316 units; 1317 1318 /* 1319 Resolution. 1320 */ 1321 p=ReadResourceLong(p,&resolution); 1322 image->resolution.x=((double) resolution)/65536.0; 1323 p=ReadResourceShort(p,&units)+2; 1324 p=ReadResourceLong(p,&resolution)+4; 1325 image->resolution.y=((double) resolution)/65536.0; 1326 /* 1327 Values are always stored as pixels per inch. 1328 */ 1329 if ((ResolutionType) units != PixelsPerCentimeterResolution) 1330 image->units=PixelsPerInchResolution; 1331 else 1332 { 1333 image->units=PixelsPerCentimeterResolution; 1334 image->resolution.x/=2.54; 1335 image->resolution.y/=2.54; 1336 } 1337 break; 1338 } 1339 case 0x0404: 1340 { 1341 /* 1342 IPTC Profile 1343 */ 1344 profile=AcquireStringInfo(count); 1345 SetStringInfoDatum(profile,p); 1346 (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue, 1347 exception); 1348 profile=DestroyStringInfo(profile); 1349 p+=count; 1350 break; 1351 } 1352 case 0x040c: 1353 { 1354 /* 1355 Thumbnail. 1356 */ 1357 p+=count; 1358 break; 1359 } 1360 case 0x040f: 1361 { 1362 /* 1363 ICC Profile. 1364 */ 1365 profile=AcquireStringInfo(count); 1366 SetStringInfoDatum(profile,p); 1367 (void) SetImageProfileInternal(image,"icc",profile,MagickTrue, 1368 exception); 1369 profile=DestroyStringInfo(profile); 1370 p+=count; 1371 break; 1372 } 1373 case 0x0422: 1374 { 1375 /* 1376 EXIF Profile. 1377 */ 1378 profile=AcquireStringInfo(count); 1379 SetStringInfoDatum(profile,p); 1380 (void) SetImageProfileInternal(image,"exif",profile,MagickTrue, 1381 exception); 1382 profile=DestroyStringInfo(profile); 1383 p+=count; 1384 break; 1385 } 1386 case 0x0424: 1387 { 1388 /* 1389 XMP Profile. 1390 */ 1391 profile=AcquireStringInfo(count); 1392 SetStringInfoDatum(profile,p); 1393 (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue, 1394 exception); 1395 profile=DestroyStringInfo(profile); 1396 p+=count; 1397 break; 1398 } 1399 default: 1400 { 1401 p+=count; 1402 break; 1403 } 1404 } 1405 if ((count & 0x01) != 0) 1406 p++; 1407 } 1408} 1409 1410static MagickBooleanType SetImageProfileInternal(Image *image,const char *name, 1411 const StringInfo *profile,const MagickBooleanType recursive, 1412 ExceptionInfo *exception) 1413{ 1414 char 1415 key[MaxTextExtent], 1416 property[MaxTextExtent]; 1417 1418 MagickBooleanType 1419 status; 1420 1421 assert(image != (Image *) NULL); 1422 assert(image->signature == MagickSignature); 1423 if (image->debug != MagickFalse) 1424 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1425 if (image->profiles == (SplayTreeInfo *) NULL) 1426 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory, 1427 DestroyProfile); 1428 (void) CopyMagickString(key,name,MaxTextExtent); 1429 LocaleLower(key); 1430 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles, 1431 ConstantString(key),CloneStringInfo(profile)); 1432 if (status != MagickFalse) 1433 { 1434 if (LocaleCompare(name,"8bim") == 0) 1435 GetProfilesFromResourceBlock(image,profile,exception); 1436 else if (recursive == MagickFalse) 1437 WriteTo8BimProfile(image,name,profile); 1438 } 1439 /* 1440 Inject profile into image properties. 1441 */ 1442 (void) FormatLocaleString(property,MaxTextExtent,"%s:*",name); 1443 (void) GetImageProperty(image,property,exception); 1444 return(status); 1445} 1446 1447MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name, 1448 const StringInfo *profile,ExceptionInfo *exception) 1449{ 1450 return(SetImageProfileInternal(image,name,profile,MagickFalse,exception)); 1451} 1452 1453/* 1454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1455% % 1456% % 1457% % 1458% S y n c I m a g e P r o f i l e s % 1459% % 1460% % 1461% % 1462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1463% 1464% SyncImageProfiles() synchronizes image properties with the image profiles. 1465% Currently we only support updating the EXIF resolution and orientation. 1466% 1467% The format of the SyncImageProfiles method is: 1468% 1469% MagickBooleanType SyncImageProfiles(Image *image) 1470% 1471% A description of each parameter follows: 1472% 1473% o image: the image. 1474% 1475*/ 1476 1477static inline int ReadProfileByte(unsigned char **p,size_t *length) 1478{ 1479 int 1480 c; 1481 1482 if (*length < 1) 1483 return(EOF); 1484 c=(int) (*(*p)++); 1485 (*length)--; 1486 return(c); 1487} 1488 1489static inline unsigned short ReadProfileShort(const EndianType endian, 1490 unsigned char *buffer) 1491{ 1492 unsigned short 1493 value; 1494 1495 if (endian == LSBEndian) 1496 { 1497 value=(unsigned short) ((buffer[1] << 8) | buffer[0]); 1498 return((unsigned short) (value & 0xffff)); 1499 } 1500 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) | 1501 ((unsigned char *) buffer)[1]); 1502 return((unsigned short) (value & 0xffff)); 1503} 1504 1505static inline size_t ReadProfileLong(const EndianType endian, 1506 unsigned char *buffer) 1507{ 1508 size_t 1509 value; 1510 1511 if (endian == LSBEndian) 1512 { 1513 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) | 1514 (buffer[1] << 8 ) | (buffer[0])); 1515 return((size_t) (value & 0xffffffff)); 1516 } 1517 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) | 1518 (buffer[2] << 8) | buffer[3]); 1519 return((size_t) (value & 0xffffffff)); 1520} 1521 1522static inline size_t ReadProfileMSBLong(unsigned char **p, 1523 size_t *length) 1524{ 1525 size_t 1526 value; 1527 1528 if (*length < 4) 1529 return(0); 1530 1531 value=ReadProfileLong(MSBEndian,*p); 1532 (*length)-=4; 1533 *p+=4; 1534 return(value); 1535} 1536 1537static inline unsigned short ReadProfileMSBShort(unsigned char **p, 1538 size_t *length) 1539{ 1540 unsigned short 1541 value; 1542 1543 if (*length < 2) 1544 return(0); 1545 1546 value=ReadProfileShort(MSBEndian,*p); 1547 (*length)-=2; 1548 *p+=2; 1549 return(value); 1550} 1551 1552static inline void WriteProfileLong(const EndianType endian, 1553 const size_t value,unsigned char *p) 1554{ 1555 unsigned char 1556 buffer[4]; 1557 1558 if (endian == LSBEndian) 1559 { 1560 buffer[0]=(unsigned char) value; 1561 buffer[1]=(unsigned char) (value >> 8); 1562 buffer[2]=(unsigned char) (value >> 16); 1563 buffer[3]=(unsigned char) (value >> 24); 1564 (void) CopyMagickMemory(p,buffer,4); 1565 return; 1566 } 1567 buffer[0]=(unsigned char) (value >> 24); 1568 buffer[1]=(unsigned char) (value >> 16); 1569 buffer[2]=(unsigned char) (value >> 8); 1570 buffer[3]=(unsigned char) value; 1571 (void) CopyMagickMemory(p,buffer,4); 1572} 1573 1574static void WriteProfileShort(const EndianType endian, 1575 const unsigned short value,unsigned char *p) 1576{ 1577 unsigned char 1578 buffer[2]; 1579 1580 if (endian == LSBEndian) 1581 { 1582 buffer[0]=(unsigned char) value; 1583 buffer[1]=(unsigned char) (value >> 8); 1584 (void) CopyMagickMemory(p,buffer,2); 1585 return; 1586 } 1587 buffer[0]=(unsigned char) (value >> 8); 1588 buffer[1]=(unsigned char) value; 1589 (void) CopyMagickMemory(p,buffer,2); 1590} 1591 1592static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile) 1593{ 1594 size_t 1595 count, 1596 length; 1597 1598 unsigned char 1599 *p; 1600 1601 unsigned short 1602 id; 1603 1604 length=GetStringInfoLength(profile); 1605 p=GetStringInfoDatum(profile); 1606 while(length != 0) 1607 { 1608 if (ReadProfileByte(&p,&length) != 0x38) 1609 continue; 1610 if (ReadProfileByte(&p,&length) != 0x42) 1611 continue; 1612 if (ReadProfileByte(&p,&length) != 0x49) 1613 continue; 1614 if (ReadProfileByte(&p,&length) != 0x4D) 1615 continue; 1616 if (length < 7) 1617 return(MagickFalse); 1618 id=ReadProfileMSBShort(&p,&length); 1619 count=ReadProfileByte(&p,&length); 1620 if (count > length) 1621 return(MagickFalse); 1622 p+=count; 1623 if ((*p & 0x01) == 0) 1624 p++; 1625 count=ReadProfileMSBLong(&p,&length); 1626 if (count > length) 1627 return(MagickFalse); 1628 if (id == 0x3ED && count == 16) 1629 { 1630 if (image->units == PixelsPerCentimeterResolution) 1631 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*2.54* 1632 65536.0),p); 1633 else 1634 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x* 1635 65536.0),p); 1636 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4); 1637 if (image->units == PixelsPerCentimeterResolution) 1638 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*2.54* 1639 65536.0),p+8); 1640 else 1641 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y* 1642 65536.0),p+8); 1643 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12); 1644 } 1645 p+=count; 1646 length-=count; 1647 } 1648 return(MagickTrue); 1649} 1650 1651MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile) 1652{ 1653#define MaxDirectoryStack 16 1654#define EXIF_DELIMITER "\n" 1655#define EXIF_NUM_FORMATS 12 1656#define TAG_EXIF_OFFSET 0x8769 1657#define TAG_INTEROP_OFFSET 0xa005 1658 1659 typedef struct _DirectoryInfo 1660 { 1661 unsigned char 1662 *directory; 1663 1664 size_t 1665 entry; 1666 } DirectoryInfo; 1667 1668 DirectoryInfo 1669 directory_stack[MaxDirectoryStack]; 1670 1671 EndianType 1672 endian; 1673 1674 size_t 1675 entry, 1676 length, 1677 number_entries; 1678 1679 ssize_t 1680 id, 1681 level, 1682 offset; 1683 1684 static int 1685 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; 1686 1687 unsigned char 1688 *directory, 1689 *exif; 1690 1691 /* 1692 Set EXIF resolution tag. 1693 */ 1694 length=GetStringInfoLength(profile); 1695 exif=GetStringInfoDatum(profile); 1696 if (length < 16) 1697 return(MagickFalse); 1698 id=(ssize_t) ReadProfileShort(LSBEndian,exif); 1699 if ((id != 0x4949) && (id != 0x4D4D)) 1700 { 1701 while (length != 0) 1702 { 1703 if (ReadProfileByte(&exif,&length) != 0x45) 1704 continue; 1705 if (ReadProfileByte(&exif,&length) != 0x78) 1706 continue; 1707 if (ReadProfileByte(&exif,&length) != 0x69) 1708 continue; 1709 if (ReadProfileByte(&exif,&length) != 0x66) 1710 continue; 1711 if (ReadProfileByte(&exif,&length) != 0x00) 1712 continue; 1713 if (ReadProfileByte(&exif,&length) != 0x00) 1714 continue; 1715 break; 1716 } 1717 if (length < 16) 1718 return(MagickFalse); 1719 id=(ssize_t) ReadProfileShort(LSBEndian,exif); 1720 } 1721 endian=LSBEndian; 1722 if (id == 0x4949) 1723 endian=LSBEndian; 1724 else 1725 if (id == 0x4D4D) 1726 endian=MSBEndian; 1727 else 1728 return(MagickFalse); 1729 if (ReadProfileShort(endian,exif+2) != 0x002a) 1730 return(MagickFalse); 1731 /* 1732 This the offset to the first IFD. 1733 */ 1734 offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4)); 1735 if ((offset < 0) || (size_t) offset >= length) 1736 return(MagickFalse); 1737 directory=exif+offset; 1738 level=0; 1739 entry=0; 1740 do 1741 { 1742 if (level > 0) 1743 { 1744 level--; 1745 directory=directory_stack[level].directory; 1746 entry=directory_stack[level].entry; 1747 } 1748 /* 1749 Determine how many entries there are in the current IFD. 1750 */ 1751 number_entries=ReadProfileShort(endian,directory); 1752 for ( ; entry < number_entries; entry++) 1753 { 1754 int 1755 components; 1756 1757 register unsigned char 1758 *p, 1759 *q; 1760 1761 size_t 1762 number_bytes; 1763 1764 ssize_t 1765 format, 1766 tag_value; 1767 1768 q=(unsigned char *) (directory+2+(12*entry)); 1769 tag_value=(ssize_t) ReadProfileShort(endian,q); 1770 format=(ssize_t) ReadProfileShort(endian,q+2); 1771 if ((format-1) >= EXIF_NUM_FORMATS) 1772 break; 1773 components=(ssize_t) ((int) ReadProfileLong(endian,q+4)); 1774 number_bytes=(size_t) components*format_bytes[format]; 1775 if ((ssize_t) number_bytes < components) 1776 break; /* prevent overflow */ 1777 if (number_bytes <= 4) 1778 p=q+8; 1779 else 1780 { 1781 ssize_t 1782 offset; 1783 1784 /* 1785 The directory entry contains an offset. 1786 */ 1787 offset=(ssize_t) ((int) ReadProfileLong(endian,q+8)); 1788 if ((size_t) (offset+number_bytes) > length) 1789 continue; 1790 if (~length < number_bytes) 1791 continue; /* prevent overflow */ 1792 p=(unsigned char *) (exif+offset); 1793 } 1794 switch (tag_value) 1795 { 1796 case 0x011a: 1797 { 1798 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p); 1799 (void) WriteProfileLong(endian,1UL,p+4); 1800 break; 1801 } 1802 case 0x011b: 1803 { 1804 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p); 1805 (void) WriteProfileLong(endian,1UL,p+4); 1806 break; 1807 } 1808 case 0x0112: 1809 { 1810 if (number_bytes == 4) 1811 { 1812 (void) WriteProfileLong(endian,(size_t) image->orientation,p); 1813 break; 1814 } 1815 (void) WriteProfileShort(endian,(unsigned short) image->orientation, 1816 p); 1817 break; 1818 } 1819 case 0x0128: 1820 { 1821 if (number_bytes == 4) 1822 { 1823 (void) WriteProfileLong(endian,(size_t) (image->units+1),p); 1824 break; 1825 } 1826 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p); 1827 break; 1828 } 1829 default: 1830 break; 1831 } 1832 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET)) 1833 { 1834 ssize_t 1835 offset; 1836 1837 offset=(ssize_t) ((int) ReadProfileLong(endian,p)); 1838 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2))) 1839 { 1840 directory_stack[level].directory=directory; 1841 entry++; 1842 directory_stack[level].entry=entry; 1843 level++; 1844 directory_stack[level].directory=exif+offset; 1845 directory_stack[level].entry=0; 1846 level++; 1847 if ((directory+2+(12*number_entries)) > (exif+length)) 1848 break; 1849 offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12* 1850 number_entries))); 1851 if ((offset != 0) && ((size_t) offset < length) && 1852 (level < (MaxDirectoryStack-2))) 1853 { 1854 directory_stack[level].directory=exif+offset; 1855 directory_stack[level].entry=0; 1856 level++; 1857 } 1858 } 1859 break; 1860 } 1861 } 1862 } while (level > 0); 1863 return(MagickTrue); 1864} 1865 1866MagickPrivate MagickBooleanType SyncImageProfiles(Image *image) 1867{ 1868 MagickBooleanType 1869 status; 1870 1871 StringInfo 1872 *profile; 1873 1874 status=MagickTrue; 1875 profile=(StringInfo *) GetImageProfile(image,"8BIM"); 1876 if (profile != (StringInfo *) NULL) 1877 if (Sync8BimProfile(image,profile) == MagickFalse) 1878 status=MagickFalse; 1879 profile=(StringInfo *) GetImageProfile(image,"EXIF"); 1880 if (profile != (StringInfo *) NULL) 1881 if (SyncExifProfile(image,profile) == MagickFalse) 1882 status=MagickFalse; 1883 return(status); 1884} 1885