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