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