profile.c revision e9438e2a82d35b6657e908ff38ec0303f432b655
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(image,source_profile,source_type, 436 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(&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(&cms_exception, 665 GetStringInfoDatum(icc_profile),(cmsUInt32Number) 666 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 size_t *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 StringInfo 1152 *profile; 1153 1154 unsigned char 1155 length_byte; 1156 1157 size_t 1158 count; 1159 1160 unsigned short 1161 id; 1162 1163 datum=GetStringInfoDatum(resource_block); 1164 length=GetStringInfoLength(resource_block); 1165 for (p=datum; p < (datum+length-16); ) 1166 { 1167 if (LocaleNCompare((char *) p,"8BIM",4) != 0) 1168 break; 1169 p+=4; 1170 p=ReadResourceShort(p,&id); 1171 p=ReadResourceByte(p,&length_byte); 1172 p+=length_byte; 1173 if (((length_byte+1) & 0x01) != 0) 1174 p++; 1175 if (p > (datum+length-4)) 1176 break; 1177 p=ReadResourceLong(p,&count); 1178 if ((p > (datum+length-count)) || (count > length)) 1179 break; 1180 switch (id) 1181 { 1182 case 0x03ed: 1183 { 1184 unsigned short 1185 resolution; 1186 1187 /* 1188 Resolution. 1189 */ 1190 p=ReadResourceShort(p,&resolution)+6; 1191 image->resolution.x=(double) resolution; 1192 p=ReadResourceShort(p,&resolution)+6; 1193 image->resolution.y=(double) resolution; 1194 break; 1195 } 1196 case 0x0404: 1197 { 1198 /* 1199 IPTC Profile 1200 */ 1201 profile=AcquireStringInfo(count); 1202 SetStringInfoDatum(profile,p); 1203 (void) SetImageProfile(image,"iptc",profile,exception); 1204 profile=DestroyStringInfo(profile); 1205 p+=count; 1206 break; 1207 } 1208 case 0x040c: 1209 { 1210 /* 1211 Thumbnail. 1212 */ 1213 p+=count; 1214 break; 1215 } 1216 case 0x040f: 1217 { 1218 /* 1219 ICC Profile. 1220 */ 1221 profile=AcquireStringInfo(count); 1222 SetStringInfoDatum(profile,p); 1223 (void) SetImageProfile(image,"icc",profile,exception); 1224 profile=DestroyStringInfo(profile); 1225 p+=count; 1226 break; 1227 } 1228 case 0x0422: 1229 { 1230 /* 1231 EXIF Profile. 1232 */ 1233 profile=AcquireStringInfo(count); 1234 SetStringInfoDatum(profile,p); 1235 (void) SetImageProfile(image,"exif",profile,exception); 1236 profile=DestroyStringInfo(profile); 1237 p+=count; 1238 break; 1239 } 1240 case 0x0424: 1241 { 1242 /* 1243 XMP Profile. 1244 */ 1245 profile=AcquireStringInfo(count); 1246 SetStringInfoDatum(profile,p); 1247 (void) SetImageProfile(image,"xmp",profile,exception); 1248 profile=DestroyStringInfo(profile); 1249 p+=count; 1250 break; 1251 } 1252 default: 1253 { 1254 p+=count; 1255 break; 1256 } 1257 } 1258 if ((count & 0x01) != 0) 1259 p++; 1260 } 1261 return(MagickTrue); 1262} 1263 1264MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name, 1265 const StringInfo *profile,ExceptionInfo *exception) 1266{ 1267 char 1268 key[MaxTextExtent], 1269 property[MaxTextExtent]; 1270 1271 MagickBooleanType 1272 status; 1273 1274 assert(image != (Image *) NULL); 1275 assert(image->signature == MagickSignature); 1276 if (image->debug != MagickFalse) 1277 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1278 if (image->profiles == (SplayTreeInfo *) NULL) 1279 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory, 1280 DestroyProfile); 1281 (void) CopyMagickString(key,name,MaxTextExtent); 1282 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles, 1283 ConstantString(key),CloneStringInfo(profile)); 1284 if ((status != MagickFalse) && 1285 ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0))) 1286 (void) GetProfilesFromResourceBlock(image,profile,exception); 1287 /* 1288 Inject profile into image properties. 1289 */ 1290 (void) FormatLocaleString(property,MaxTextExtent,"%s:*",name); 1291 (void) GetImageProperty(image,property,exception); 1292 return(status); 1293} 1294 1295/* 1296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1297% % 1298% % 1299% % 1300% S y n c I m a g e P r o f i l e s % 1301% % 1302% % 1303% % 1304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1305% 1306% SyncImageProfiles() synchronizes image properties with the image profiles. 1307% Currently we only support updating the EXIF resolution and orientation. 1308% 1309% The format of the SyncImageProfiles method is: 1310% 1311% MagickBooleanType SyncImageProfiles(Image *image) 1312% 1313% A description of each parameter follows: 1314% 1315% o image: the image. 1316% 1317*/ 1318 1319static inline int ReadProfileByte(unsigned char **p,size_t *length) 1320{ 1321 int 1322 c; 1323 1324 if (*length < 1) 1325 return(EOF); 1326 c=(int) (*(*p)++); 1327 (*length)--; 1328 return(c); 1329} 1330 1331static inline unsigned short ReadProfileShort(const EndianType endian, 1332 unsigned char *buffer) 1333{ 1334 unsigned short 1335 value; 1336 1337 if (endian == LSBEndian) 1338 { 1339 value=(unsigned short) ((buffer[1] << 8) | buffer[0]); 1340 return((unsigned short) (value & 0xffff)); 1341 } 1342 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) | 1343 ((unsigned char *) buffer)[1]); 1344 return((unsigned short) (value & 0xffff)); 1345} 1346 1347static inline size_t ReadProfileLong(const EndianType endian, 1348 unsigned char *buffer) 1349{ 1350 size_t 1351 value; 1352 1353 if (endian == LSBEndian) 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 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) | 1360 (buffer[2] << 8) | buffer[3]); 1361 return((size_t) (value & 0xffffffff)); 1362} 1363 1364static inline void WriteProfileLong(const EndianType endian, 1365 const size_t value,unsigned char *p) 1366{ 1367 unsigned char 1368 buffer[4]; 1369 1370 if (endian == LSBEndian) 1371 { 1372 buffer[0]=(unsigned char) value; 1373 buffer[1]=(unsigned char) (value >> 8); 1374 buffer[2]=(unsigned char) (value >> 16); 1375 buffer[3]=(unsigned char) (value >> 24); 1376 (void) CopyMagickMemory(p,buffer,4); 1377 return; 1378 } 1379 buffer[0]=(unsigned char) (value >> 24); 1380 buffer[1]=(unsigned char) (value >> 16); 1381 buffer[2]=(unsigned char) (value >> 8); 1382 buffer[3]=(unsigned char) value; 1383 (void) CopyMagickMemory(p,buffer,4); 1384} 1385 1386static void WriteProfileShort(const EndianType endian, 1387 const unsigned short value,unsigned char *p) 1388{ 1389 unsigned char 1390 buffer[2]; 1391 1392 if (endian == LSBEndian) 1393 { 1394 buffer[0]=(unsigned char) value; 1395 buffer[1]=(unsigned char) (value >> 8); 1396 (void) CopyMagickMemory(p,buffer,2); 1397 return; 1398 } 1399 buffer[0]=(unsigned char) (value >> 8); 1400 buffer[1]=(unsigned char) value; 1401 (void) CopyMagickMemory(p,buffer,2); 1402} 1403 1404MagickPrivate MagickBooleanType SyncImageProfiles(Image *image) 1405{ 1406#define MaxDirectoryStack 16 1407#define EXIF_DELIMITER "\n" 1408#define EXIF_NUM_FORMATS 12 1409#define TAG_EXIF_OFFSET 0x8769 1410#define TAG_INTEROP_OFFSET 0xa005 1411 1412 typedef struct _DirectoryInfo 1413 { 1414 unsigned char 1415 *directory; 1416 1417 size_t 1418 entry; 1419 } DirectoryInfo; 1420 1421 DirectoryInfo 1422 directory_stack[MaxDirectoryStack]; 1423 1424 EndianType 1425 endian; 1426 1427 size_t 1428 entry, 1429 length, 1430 number_entries; 1431 1432 ssize_t 1433 id, 1434 level, 1435 offset; 1436 1437 static int 1438 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; 1439 1440 StringInfo 1441 *profile; 1442 1443 unsigned char 1444 *directory, 1445 *exif; 1446 1447 /* 1448 Set EXIF resolution tag. 1449 */ 1450 profile=(StringInfo *) GetImageProfile(image,"EXIF"); 1451 if (profile == (StringInfo *) NULL) 1452 return(MagickTrue); 1453 length=GetStringInfoLength(profile); 1454 exif=GetStringInfoDatum(profile); 1455 while (length != 0) 1456 { 1457 if (ReadProfileByte(&exif,&length) != 0x45) 1458 continue; 1459 if (ReadProfileByte(&exif,&length) != 0x78) 1460 continue; 1461 if (ReadProfileByte(&exif,&length) != 0x69) 1462 continue; 1463 if (ReadProfileByte(&exif,&length) != 0x66) 1464 continue; 1465 if (ReadProfileByte(&exif,&length) != 0x00) 1466 continue; 1467 if (ReadProfileByte(&exif,&length) != 0x00) 1468 continue; 1469 break; 1470 } 1471 if (length < 16) 1472 return(MagickFalse); 1473 id=(ssize_t) ReadProfileShort(LSBEndian,exif); 1474 endian=LSBEndian; 1475 if (id == 0x4949) 1476 endian=LSBEndian; 1477 else 1478 if (id == 0x4D4D) 1479 endian=MSBEndian; 1480 else 1481 return(MagickFalse); 1482 if (ReadProfileShort(endian,exif+2) != 0x002a) 1483 return(MagickFalse); 1484 /* 1485 This the offset to the first IFD. 1486 */ 1487 offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4)); 1488 if ((offset < 0) || (size_t) offset >= length) 1489 return(MagickFalse); 1490 directory=exif+offset; 1491 level=0; 1492 entry=0; 1493 do 1494 { 1495 if (level > 0) 1496 { 1497 level--; 1498 directory=directory_stack[level].directory; 1499 entry=directory_stack[level].entry; 1500 } 1501 /* 1502 Determine how many entries there are in the current IFD. 1503 */ 1504 number_entries=ReadProfileShort(endian,directory); 1505 for ( ; entry < number_entries; entry++) 1506 { 1507 int 1508 components; 1509 1510 register unsigned char 1511 *p, 1512 *q; 1513 1514 size_t 1515 number_bytes; 1516 1517 ssize_t 1518 format, 1519 tag_value; 1520 1521 q=(unsigned char *) (directory+2+(12*entry)); 1522 tag_value=(ssize_t) ReadProfileShort(endian,q); 1523 format=(ssize_t) ReadProfileShort(endian,q+2); 1524 if ((format-1) >= EXIF_NUM_FORMATS) 1525 break; 1526 components=(ssize_t) ((int) ReadProfileLong(endian,q+4)); 1527 number_bytes=(size_t) components*format_bytes[format]; 1528 if ((ssize_t) number_bytes < components) 1529 break; /* prevent overflow */ 1530 if (number_bytes <= 4) 1531 p=q+8; 1532 else 1533 { 1534 ssize_t 1535 offset; 1536 1537 /* 1538 The directory entry contains an offset. 1539 */ 1540 offset=(ssize_t) ((int) ReadProfileLong(endian,q+8)); 1541 if ((size_t) (offset+number_bytes) > length) 1542 continue; 1543 if (~length < number_bytes) 1544 continue; /* prevent overflow */ 1545 p=(unsigned char *) (exif+offset); 1546 } 1547 switch (tag_value) 1548 { 1549 case 0x011a: 1550 { 1551 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p); 1552 (void) WriteProfileLong(endian,1UL,p+4); 1553 break; 1554 } 1555 case 0x011b: 1556 { 1557 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p); 1558 (void) WriteProfileLong(endian,1UL,p+4); 1559 break; 1560 } 1561 case 0x0112: 1562 { 1563 if (number_bytes == 4) 1564 { 1565 (void) WriteProfileLong(endian,(size_t) image->orientation,p); 1566 break; 1567 } 1568 (void) WriteProfileShort(endian,(unsigned short) image->orientation, 1569 p); 1570 break; 1571 } 1572 case 0x0128: 1573 { 1574 if (number_bytes == 4) 1575 { 1576 (void) WriteProfileLong(endian,(size_t) (image->units+1),p); 1577 break; 1578 } 1579 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p); 1580 break; 1581 } 1582 default: 1583 break; 1584 } 1585 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET)) 1586 { 1587 ssize_t 1588 offset; 1589 1590 offset=(ssize_t) ((int) ReadProfileLong(endian,p)); 1591 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2))) 1592 { 1593 directory_stack[level].directory=directory; 1594 entry++; 1595 directory_stack[level].entry=entry; 1596 level++; 1597 directory_stack[level].directory=exif+offset; 1598 directory_stack[level].entry=0; 1599 level++; 1600 if ((directory+2+(12*number_entries)) > (exif+length)) 1601 break; 1602 offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12* 1603 number_entries))); 1604 if ((offset != 0) && ((size_t) offset < length) && 1605 (level < (MaxDirectoryStack-2))) 1606 { 1607 directory_stack[level].directory=exif+offset; 1608 directory_stack[level].entry=0; 1609 level++; 1610 } 1611 } 1612 break; 1613 } 1614 } 1615 } while (level > 0); 1616 return(MagickTrue); 1617} 1618