1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% M M PPPP CCCC % 7% MM MM P P C % 8% M M M PPPP C % 9% M M P C % 10% M M P CCCC % 11% % 12% % 13% Read/Write Magick Persistant Cache Image Format % 14% % 15% Software Design % 16% Cristy % 17% March 2000 % 18% % 19% % 20% Copyright 1999-2016 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/* 41 Include declarations. 42*/ 43#include "MagickCore/studio.h" 44#include "MagickCore/artifact.h" 45#include "MagickCore/attribute.h" 46#include "MagickCore/blob.h" 47#include "MagickCore/blob-private.h" 48#include "MagickCore/cache.h" 49#include "MagickCore/color.h" 50#include "MagickCore/color-private.h" 51#include "MagickCore/colormap.h" 52#include "MagickCore/constitute.h" 53#include "MagickCore/exception.h" 54#include "MagickCore/exception-private.h" 55#include "MagickCore/geometry.h" 56#include "MagickCore/image.h" 57#include "MagickCore/image-private.h" 58#include "MagickCore/linked-list.h" 59#include "MagickCore/list.h" 60#include "MagickCore/magick.h" 61#include "MagickCore/memory_.h" 62#include "MagickCore/module.h" 63#include "MagickCore/monitor.h" 64#include "MagickCore/monitor-private.h" 65#include "MagickCore/option.h" 66#include "MagickCore/profile.h" 67#include "MagickCore/property.h" 68#include "MagickCore/quantum-private.h" 69#include "MagickCore/static.h" 70#include "MagickCore/statistic.h" 71#include "MagickCore/string_.h" 72#include "MagickCore/string-private.h" 73#include "MagickCore/utility.h" 74#include "MagickCore/version-private.h" 75 76/* 77 Forward declarations. 78*/ 79static MagickBooleanType 80 WriteMPCImage(const ImageInfo *,Image *,ExceptionInfo *); 81 82/* 83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 84% % 85% % 86% % 87% I s M P C % 88% % 89% % 90% % 91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 92% 93% IsMPC() returns MagickTrue if the image format type, identified by the 94% magick string, is an Magick Persistent Cache image. 95% 96% The format of the IsMPC method is: 97% 98% MagickBooleanType IsMPC(const unsigned char *magick,const size_t length) 99% 100% A description of each parameter follows: 101% 102% o magick: compare image format pattern against these bytes. 103% 104% o length: Specifies the length of the magick string. 105% 106*/ 107static MagickBooleanType IsMPC(const unsigned char *magick,const size_t length) 108{ 109 if (length < 14) 110 return(MagickFalse); 111 if (LocaleNCompare((const char *) magick,"id=MagickCache",14) == 0) 112 return(MagickTrue); 113 return(MagickFalse); 114} 115 116/* 117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 118% % 119% % 120% % 121% R e a d C A C H E I m a g e % 122% % 123% % 124% % 125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 126% 127% ReadMPCImage() reads an Magick Persistent Cache image file and returns 128% it. It allocates the memory necessary for the new Image structure and 129% returns a pointer to the new image. 130% 131% The format of the ReadMPCImage method is: 132% 133% Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception) 134% 135% Decompression code contributed by Kyle Shorter. 136% 137% A description of each parameter follows: 138% 139% o image_info: the image info. 140% 141% o exception: return any errors or warnings in this structure. 142% 143*/ 144static Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception) 145{ 146 char 147 cache_filename[MagickPathExtent], 148 id[MagickPathExtent], 149 keyword[MagickPathExtent], 150 *options; 151 152 const unsigned char 153 *p; 154 155 GeometryInfo 156 geometry_info; 157 158 Image 159 *image; 160 161 int 162 c; 163 164 LinkedListInfo 165 *profiles; 166 167 MagickBooleanType 168 status; 169 170 MagickOffsetType 171 offset; 172 173 MagickStatusType 174 flags; 175 176 register ssize_t 177 i; 178 179 size_t 180 depth, 181 length; 182 183 ssize_t 184 count; 185 186 StringInfo 187 *profile; 188 189 unsigned int 190 signature; 191 192 /* 193 Open image file. 194 */ 195 assert(image_info != (const ImageInfo *) NULL); 196 assert(image_info->signature == MagickCoreSignature); 197 if (image_info->debug != MagickFalse) 198 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 199 image_info->filename); 200 assert(exception != (ExceptionInfo *) NULL); 201 assert(exception->signature == MagickCoreSignature); 202 image=AcquireImage(image_info,exception); 203 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 204 if (status == MagickFalse) 205 { 206 image=DestroyImageList(image); 207 return((Image *) NULL); 208 } 209 (void) CopyMagickString(cache_filename,image->filename,MagickPathExtent); 210 AppendImageFormat("cache",cache_filename); 211 c=ReadBlobByte(image); 212 if (c == EOF) 213 { 214 image=DestroyImage(image); 215 return((Image *) NULL); 216 } 217 *id='\0'; 218 (void) ResetMagickMemory(keyword,0,sizeof(keyword)); 219 offset=0; 220 do 221 { 222 /* 223 Decode image header; header terminates one character beyond a ':'. 224 */ 225 profiles=(LinkedListInfo *) NULL; 226 length=MagickPathExtent; 227 options=AcquireString((char *) NULL); 228 signature=GetMagickSignature((const StringInfo *) NULL); 229 image->depth=8; 230 image->compression=NoCompression; 231 while ((isgraph(c) != MagickFalse) && (c != (int) ':')) 232 { 233 register char 234 *p; 235 236 if (c == (int) '{') 237 { 238 char 239 *comment; 240 241 /* 242 Read comment-- any text between { }. 243 */ 244 length=MagickPathExtent; 245 comment=AcquireString((char *) NULL); 246 for (p=comment; comment != (char *) NULL; p++) 247 { 248 c=ReadBlobByte(image); 249 if (c == (int) '\\') 250 c=ReadBlobByte(image); 251 else 252 if ((c == EOF) || (c == (int) '}')) 253 break; 254 if ((size_t) (p-comment+1) >= length) 255 { 256 *p='\0'; 257 length<<=1; 258 comment=(char *) ResizeQuantumMemory(comment,length+ 259 MagickPathExtent,sizeof(*comment)); 260 if (comment == (char *) NULL) 261 break; 262 p=comment+strlen(comment); 263 } 264 *p=(char) c; 265 } 266 if (comment == (char *) NULL) 267 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 268 *p='\0'; 269 (void) SetImageProperty(image,"comment",comment,exception); 270 comment=DestroyString(comment); 271 c=ReadBlobByte(image); 272 } 273 else 274 if (isalnum(c) != MagickFalse) 275 { 276 /* 277 Get the keyword. 278 */ 279 length=MagickPathExtent; 280 p=keyword; 281 do 282 { 283 if (c == (int) '=') 284 break; 285 if ((size_t) (p-keyword) < (MagickPathExtent-1)) 286 *p++=(char) c; 287 c=ReadBlobByte(image); 288 } while (c != EOF); 289 *p='\0'; 290 p=options; 291 while (isspace((int) ((unsigned char) c)) != 0) 292 c=ReadBlobByte(image); 293 if (c == (int) '=') 294 { 295 /* 296 Get the keyword value. 297 */ 298 c=ReadBlobByte(image); 299 while ((c != (int) '}') && (c != EOF)) 300 { 301 if ((size_t) (p-options+1) >= length) 302 { 303 *p='\0'; 304 length<<=1; 305 options=(char *) ResizeQuantumMemory(options,length+ 306 MagickPathExtent,sizeof(*options)); 307 if (options == (char *) NULL) 308 break; 309 p=options+strlen(options); 310 } 311 *p++=(char) c; 312 c=ReadBlobByte(image); 313 if (c == '\\') 314 { 315 c=ReadBlobByte(image); 316 if (c == (int) '}') 317 { 318 *p++=(char) c; 319 c=ReadBlobByte(image); 320 } 321 } 322 if (*options != '{') 323 if (isspace((int) ((unsigned char) c)) != 0) 324 break; 325 } 326 if (options == (char *) NULL) 327 ThrowReaderException(ResourceLimitError, 328 "MemoryAllocationFailed"); 329 } 330 *p='\0'; 331 if (*options == '{') 332 (void) CopyMagickString(options,options+1,strlen(options)); 333 /* 334 Assign a value to the specified keyword. 335 */ 336 switch (*keyword) 337 { 338 case 'a': 339 case 'A': 340 { 341 if (LocaleCompare(keyword,"alpha-color") == 0) 342 { 343 (void) QueryColorCompliance(options,AllCompliance, 344 &image->alpha_color,exception); 345 break; 346 } 347 if (LocaleCompare(keyword,"alpha-trait") == 0) 348 { 349 ssize_t 350 alpha_trait; 351 352 alpha_trait=ParseCommandOption(MagickPixelTraitOptions, 353 MagickFalse,options); 354 if (alpha_trait < 0) 355 break; 356 image->alpha_trait=(PixelTrait) alpha_trait; 357 break; 358 } 359 (void) SetImageProperty(image,keyword,options,exception); 360 break; 361 } 362 case 'b': 363 case 'B': 364 { 365 if (LocaleCompare(keyword,"background-color") == 0) 366 { 367 (void) QueryColorCompliance(options,AllCompliance, 368 &image->background_color,exception); 369 break; 370 } 371 if (LocaleCompare(keyword,"blue-primary") == 0) 372 { 373 flags=ParseGeometry(options,&geometry_info); 374 image->chromaticity.blue_primary.x=geometry_info.rho; 375 image->chromaticity.blue_primary.y=geometry_info.sigma; 376 if ((flags & SigmaValue) == 0) 377 image->chromaticity.blue_primary.y= 378 image->chromaticity.blue_primary.x; 379 break; 380 } 381 if (LocaleCompare(keyword,"border-color") == 0) 382 { 383 (void) QueryColorCompliance(options,AllCompliance, 384 &image->border_color,exception); 385 break; 386 } 387 (void) SetImageProperty(image,keyword,options,exception); 388 break; 389 } 390 case 'c': 391 case 'C': 392 { 393 if (LocaleCompare(keyword,"class") == 0) 394 { 395 ssize_t 396 storage_class; 397 398 storage_class=ParseCommandOption(MagickClassOptions, 399 MagickFalse,options); 400 if (storage_class < 0) 401 break; 402 image->storage_class=(ClassType) storage_class; 403 break; 404 } 405 if (LocaleCompare(keyword,"colors") == 0) 406 { 407 image->colors=StringToUnsignedLong(options); 408 break; 409 } 410 if (LocaleCompare(keyword,"colorspace") == 0) 411 { 412 ssize_t 413 colorspace; 414 415 colorspace=ParseCommandOption(MagickColorspaceOptions, 416 MagickFalse,options); 417 if (colorspace < 0) 418 break; 419 image->colorspace=(ColorspaceType) colorspace; 420 break; 421 } 422 if (LocaleCompare(keyword,"compression") == 0) 423 { 424 ssize_t 425 compression; 426 427 compression=ParseCommandOption(MagickCompressOptions, 428 MagickFalse,options); 429 if (compression < 0) 430 break; 431 image->compression=(CompressionType) compression; 432 break; 433 } 434 if (LocaleCompare(keyword,"columns") == 0) 435 { 436 image->columns=StringToUnsignedLong(options); 437 break; 438 } 439 (void) SetImageProperty(image,keyword,options,exception); 440 break; 441 } 442 case 'd': 443 case 'D': 444 { 445 if (LocaleCompare(keyword,"delay") == 0) 446 { 447 image->delay=StringToUnsignedLong(options); 448 break; 449 } 450 if (LocaleCompare(keyword,"depth") == 0) 451 { 452 image->depth=StringToUnsignedLong(options); 453 break; 454 } 455 if (LocaleCompare(keyword,"dispose") == 0) 456 { 457 ssize_t 458 dispose; 459 460 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse, 461 options); 462 if (dispose < 0) 463 break; 464 image->dispose=(DisposeType) dispose; 465 break; 466 } 467 (void) SetImageProperty(image,keyword,options,exception); 468 break; 469 } 470 case 'e': 471 case 'E': 472 { 473 if (LocaleCompare(keyword,"endian") == 0) 474 { 475 ssize_t 476 endian; 477 478 endian=ParseCommandOption(MagickEndianOptions,MagickFalse, 479 options); 480 if (endian < 0) 481 break; 482 image->endian=(EndianType) endian; 483 break; 484 } 485 if (LocaleCompare(keyword,"error") == 0) 486 { 487 image->error.mean_error_per_pixel=StringToDouble(options, 488 (char **) NULL); 489 break; 490 } 491 (void) SetImageProperty(image,keyword,options,exception); 492 break; 493 } 494 case 'g': 495 case 'G': 496 { 497 if (LocaleCompare(keyword,"gamma") == 0) 498 { 499 image->gamma=StringToDouble(options,(char **) NULL); 500 break; 501 } 502 if (LocaleCompare(keyword,"green-primary") == 0) 503 { 504 flags=ParseGeometry(options,&geometry_info); 505 image->chromaticity.green_primary.x=geometry_info.rho; 506 image->chromaticity.green_primary.y=geometry_info.sigma; 507 if ((flags & SigmaValue) == 0) 508 image->chromaticity.green_primary.y= 509 image->chromaticity.green_primary.x; 510 break; 511 } 512 (void) SetImageProperty(image,keyword,options,exception); 513 break; 514 } 515 case 'i': 516 case 'I': 517 { 518 if (LocaleCompare(keyword,"id") == 0) 519 { 520 (void) CopyMagickString(id,options,MagickPathExtent); 521 break; 522 } 523 if (LocaleCompare(keyword,"iterations") == 0) 524 { 525 image->iterations=StringToUnsignedLong(options); 526 break; 527 } 528 (void) SetImageProperty(image,keyword,options,exception); 529 break; 530 } 531 case 'm': 532 case 'M': 533 { 534 if (LocaleCompare(keyword,"magick-signature") == 0) 535 { 536 signature=(unsigned int) StringToUnsignedLong(options); 537 break; 538 } 539 if (LocaleCompare(keyword,"maximum-error") == 0) 540 { 541 image->error.normalized_maximum_error=StringToDouble( 542 options,(char **) NULL); 543 break; 544 } 545 if (LocaleCompare(keyword,"mean-error") == 0) 546 { 547 image->error.normalized_mean_error=StringToDouble(options, 548 (char **) NULL); 549 break; 550 } 551 if (LocaleCompare(keyword,"montage") == 0) 552 { 553 (void) CloneString(&image->montage,options); 554 break; 555 } 556 (void) SetImageProperty(image,keyword,options,exception); 557 break; 558 } 559 case 'o': 560 case 'O': 561 { 562 if (LocaleCompare(keyword,"orientation") == 0) 563 { 564 ssize_t 565 orientation; 566 567 orientation=ParseCommandOption(MagickOrientationOptions, 568 MagickFalse,options); 569 if (orientation < 0) 570 break; 571 image->orientation=(OrientationType) orientation; 572 break; 573 } 574 (void) SetImageProperty(image,keyword,options,exception); 575 break; 576 } 577 case 'p': 578 case 'P': 579 { 580 if (LocaleCompare(keyword,"page") == 0) 581 { 582 char 583 *geometry; 584 585 geometry=GetPageGeometry(options); 586 (void) ParseAbsoluteGeometry(geometry,&image->page); 587 geometry=DestroyString(geometry); 588 break; 589 } 590 if (LocaleCompare(keyword,"pixel-intensity") == 0) 591 { 592 ssize_t 593 intensity; 594 595 intensity=ParseCommandOption(MagickPixelIntensityOptions, 596 MagickFalse,options); 597 if (intensity < 0) 598 break; 599 image->intensity=(PixelIntensityMethod) intensity; 600 break; 601 } 602 if ((LocaleNCompare(keyword,"profile:",8) == 0) || 603 (LocaleNCompare(keyword,"profile-",8) == 0)) 604 { 605 if (profiles == (LinkedListInfo *) NULL) 606 profiles=NewLinkedList(0); 607 (void) AppendValueToLinkedList(profiles, 608 AcquireString(keyword+8)); 609 profile=BlobToStringInfo((const void *) NULL,(size_t) 610 StringToLong(options)); 611 if (profile == (StringInfo *) NULL) 612 ThrowReaderException(ResourceLimitError, 613 "MemoryAllocationFailed"); 614 (void) SetImageProfile(image,keyword+8,profile,exception); 615 profile=DestroyStringInfo(profile); 616 break; 617 } 618 (void) SetImageProperty(image,keyword,options,exception); 619 break; 620 } 621 case 'q': 622 case 'Q': 623 { 624 if (LocaleCompare(keyword,"quality") == 0) 625 { 626 image->quality=StringToUnsignedLong(options); 627 break; 628 } 629 (void) SetImageProperty(image,keyword,options,exception); 630 break; 631 } 632 case 'r': 633 case 'R': 634 { 635 if (LocaleCompare(keyword,"red-primary") == 0) 636 { 637 flags=ParseGeometry(options,&geometry_info); 638 image->chromaticity.red_primary.x=geometry_info.rho; 639 if ((flags & SigmaValue) != 0) 640 image->chromaticity.red_primary.y=geometry_info.sigma; 641 break; 642 } 643 if (LocaleCompare(keyword,"rendering-intent") == 0) 644 { 645 ssize_t 646 rendering_intent; 647 648 rendering_intent=ParseCommandOption(MagickIntentOptions, 649 MagickFalse,options); 650 if (rendering_intent < 0) 651 break; 652 image->rendering_intent=(RenderingIntent) rendering_intent; 653 break; 654 } 655 if (LocaleCompare(keyword,"resolution") == 0) 656 { 657 flags=ParseGeometry(options,&geometry_info); 658 image->resolution.x=geometry_info.rho; 659 image->resolution.y=geometry_info.sigma; 660 if ((flags & SigmaValue) == 0) 661 image->resolution.y=image->resolution.x; 662 break; 663 } 664 if (LocaleCompare(keyword,"rows") == 0) 665 { 666 image->rows=StringToUnsignedLong(options); 667 break; 668 } 669 (void) SetImageProperty(image,keyword,options,exception); 670 break; 671 } 672 case 's': 673 case 'S': 674 { 675 if (LocaleCompare(keyword,"scene") == 0) 676 { 677 image->scene=StringToUnsignedLong(options); 678 break; 679 } 680 (void) SetImageProperty(image,keyword,options,exception); 681 break; 682 } 683 case 't': 684 case 'T': 685 { 686 if (LocaleCompare(keyword,"ticks-per-second") == 0) 687 { 688 image->ticks_per_second=(ssize_t) StringToLong(options); 689 break; 690 } 691 if (LocaleCompare(keyword,"tile-offset") == 0) 692 { 693 char 694 *geometry; 695 696 geometry=GetPageGeometry(options); 697 (void) ParseAbsoluteGeometry(geometry,&image->tile_offset); 698 geometry=DestroyString(geometry); 699 } 700 if (LocaleCompare(keyword,"type") == 0) 701 { 702 ssize_t 703 type; 704 705 type=ParseCommandOption(MagickTypeOptions,MagickFalse, 706 options); 707 if (type < 0) 708 break; 709 image->type=(ImageType) type; 710 break; 711 } 712 (void) SetImageProperty(image,keyword,options,exception); 713 break; 714 } 715 case 'u': 716 case 'U': 717 { 718 if (LocaleCompare(keyword,"units") == 0) 719 { 720 ssize_t 721 units; 722 723 units=ParseCommandOption(MagickResolutionOptions, 724 MagickFalse,options); 725 if (units < 0) 726 break; 727 image->units=(ResolutionType) units; 728 break; 729 } 730 (void) SetImageProperty(image,keyword,options,exception); 731 break; 732 } 733 case 'w': 734 case 'W': 735 { 736 if (LocaleCompare(keyword,"white-point") == 0) 737 { 738 flags=ParseGeometry(options,&geometry_info); 739 image->chromaticity.white_point.x=geometry_info.rho; 740 image->chromaticity.white_point.y=geometry_info.sigma; 741 if ((flags & SigmaValue) == 0) 742 image->chromaticity.white_point.y= 743 image->chromaticity.white_point.x; 744 break; 745 } 746 (void) SetImageProperty(image,keyword,options,exception); 747 break; 748 } 749 default: 750 { 751 (void) SetImageProperty(image,keyword,options,exception); 752 break; 753 } 754 } 755 } 756 else 757 c=ReadBlobByte(image); 758 while (isspace((int) ((unsigned char) c)) != 0) 759 c=ReadBlobByte(image); 760 } 761 options=DestroyString(options); 762 (void) ReadBlobByte(image); 763 /* 764 Verify that required image information is defined. 765 */ 766 if ((LocaleCompare(id,"MagickCache") != 0) || 767 (image->storage_class == UndefinedClass) || 768 (image->compression == UndefinedCompression) || (image->columns == 0) || 769 (image->rows == 0)) 770 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 771 if (signature != GetMagickSignature((const StringInfo *) NULL)) 772 ThrowReaderException(CacheError,"IncompatibleAPI"); 773 if (image->montage != (char *) NULL) 774 { 775 register char 776 *p; 777 778 /* 779 Image directory. 780 */ 781 length=MagickPathExtent; 782 image->directory=AcquireString((char *) NULL); 783 p=image->directory; 784 do 785 { 786 *p='\0'; 787 if ((strlen(image->directory)+MagickPathExtent) >= length) 788 { 789 /* 790 Allocate more memory for the image directory. 791 */ 792 length<<=1; 793 image->directory=(char *) ResizeQuantumMemory(image->directory, 794 length+MagickPathExtent,sizeof(*image->directory)); 795 if (image->directory == (char *) NULL) 796 ThrowReaderException(CorruptImageError,"UnableToReadImageData"); 797 p=image->directory+strlen(image->directory); 798 } 799 c=ReadBlobByte(image); 800 *p++=(char) c; 801 } while (c != (int) '\0'); 802 } 803 if (profiles != (LinkedListInfo *) NULL) 804 { 805 const char 806 *name; 807 808 const StringInfo 809 *profile; 810 811 register unsigned char 812 *p; 813 814 /* 815 Read image profiles. 816 */ 817 ResetLinkedListIterator(profiles); 818 name=(const char *) GetNextValueInLinkedList(profiles); 819 while (name != (const char *) NULL) 820 { 821 profile=GetImageProfile(image,name); 822 if (profile != (StringInfo *) NULL) 823 { 824 p=GetStringInfoDatum(profile); 825 count=ReadBlob(image,GetStringInfoLength(profile),p); 826 } 827 name=(const char *) GetNextValueInLinkedList(profiles); 828 } 829 profiles=DestroyLinkedList(profiles,RelinquishMagickMemory); 830 } 831 depth=GetImageQuantumDepth(image,MagickFalse); 832 if (image->storage_class == PseudoClass) 833 { 834 /* 835 Create image colormap. 836 */ 837 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) 838 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 839 if (image->colors != 0) 840 { 841 size_t 842 packet_size; 843 844 unsigned char 845 *colormap; 846 847 /* 848 Read image colormap from file. 849 */ 850 packet_size=(size_t) (3UL*depth/8UL); 851 colormap=(unsigned char *) AcquireQuantumMemory(image->colors, 852 packet_size*sizeof(*colormap)); 853 if (colormap == (unsigned char *) NULL) 854 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 855 count=ReadBlob(image,packet_size*image->colors,colormap); 856 if (count != (ssize_t) (packet_size*image->colors)) 857 ThrowReaderException(CorruptImageError, 858 "InsufficientImageDataInFile"); 859 p=colormap; 860 switch (depth) 861 { 862 default: 863 ThrowReaderException(CorruptImageError, 864 "ImageDepthNotSupported"); 865 case 8: 866 { 867 unsigned char 868 pixel; 869 870 for (i=0; i < (ssize_t) image->colors; i++) 871 { 872 p=PushCharPixel(p,&pixel); 873 image->colormap[i].red=ScaleCharToQuantum(pixel); 874 p=PushCharPixel(p,&pixel); 875 image->colormap[i].green=ScaleCharToQuantum(pixel); 876 p=PushCharPixel(p,&pixel); 877 image->colormap[i].blue=ScaleCharToQuantum(pixel); 878 } 879 break; 880 } 881 case 16: 882 { 883 unsigned short 884 pixel; 885 886 for (i=0; i < (ssize_t) image->colors; i++) 887 { 888 p=PushShortPixel(MSBEndian,p,&pixel); 889 image->colormap[i].red=ScaleShortToQuantum(pixel); 890 p=PushShortPixel(MSBEndian,p,&pixel); 891 image->colormap[i].green=ScaleShortToQuantum(pixel); 892 p=PushShortPixel(MSBEndian,p,&pixel); 893 image->colormap[i].blue=ScaleShortToQuantum(pixel); 894 } 895 break; 896 } 897 case 32: 898 { 899 unsigned int 900 pixel; 901 902 for (i=0; i < (ssize_t) image->colors; i++) 903 { 904 p=PushLongPixel(MSBEndian,p,&pixel); 905 image->colormap[i].red=ScaleLongToQuantum(pixel); 906 p=PushLongPixel(MSBEndian,p,&pixel); 907 image->colormap[i].green=ScaleLongToQuantum(pixel); 908 p=PushLongPixel(MSBEndian,p,&pixel); 909 image->colormap[i].blue=ScaleLongToQuantum(pixel); 910 } 911 break; 912 } 913 } 914 colormap=(unsigned char *) RelinquishMagickMemory(colormap); 915 } 916 } 917 if (EOFBlob(image) != MagickFalse) 918 { 919 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", 920 image->filename); 921 break; 922 } 923 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) 924 if (image->scene >= (image_info->scene+image_info->number_scenes-1)) 925 break; 926 status=SetImageExtent(image,image->columns,image->rows,exception); 927 if (status == MagickFalse) 928 return(DestroyImageList(image)); 929 /* 930 Attach persistent pixel cache. 931 */ 932 status=PersistPixelCache(image,cache_filename,MagickTrue,&offset,exception); 933 if (status == MagickFalse) 934 ThrowReaderException(CacheError,"UnableToPersistPixelCache"); 935 /* 936 Proceed to next image. 937 */ 938 do 939 { 940 c=ReadBlobByte(image); 941 } while ((isgraph(c) == MagickFalse) && (c != EOF)); 942 if (c != EOF) 943 { 944 /* 945 Allocate next image structure. 946 */ 947 AcquireNextImage(image_info,image,exception); 948 if (GetNextImageInList(image) == (Image *) NULL) 949 { 950 image=DestroyImageList(image); 951 return((Image *) NULL); 952 } 953 image=SyncNextImageInList(image); 954 status=SetImageProgress(image,LoadImagesTag,TellBlob(image), 955 GetBlobSize(image)); 956 if (status == MagickFalse) 957 break; 958 } 959 } while (c != EOF); 960 (void) CloseBlob(image); 961 return(GetFirstImageInList(image)); 962} 963 964/* 965%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 966% % 967% % 968% % 969% R e g i s t e r M P C I m a g e % 970% % 971% % 972% % 973%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 974% 975% RegisterMPCImage() adds properties for the Cache image format to 976% the list of supported formats. The properties include the image format 977% tag, a method to read and/or write the format, whether the format 978% supports the saving of more than one frame to the same file or blob, 979% whether the format supports native in-memory I/O, and a brief 980% description of the format. 981% 982% The format of the RegisterMPCImage method is: 983% 984% size_t RegisterMPCImage(void) 985% 986*/ 987ModuleExport size_t RegisterMPCImage(void) 988{ 989 MagickInfo 990 *entry; 991 992 entry=AcquireMagickInfo("MPC","CACHE", 993 "Magick Persistent Cache image format"); 994 entry->module=ConstantString("CACHE"); 995 entry->flags|=CoderStealthFlag; 996 (void) RegisterMagickInfo(entry); 997 entry=AcquireMagickInfo("MPC","MPC","Magick Persistent Cache image format"); 998 entry->decoder=(DecodeImageHandler *) ReadMPCImage; 999 entry->encoder=(EncodeImageHandler *) WriteMPCImage; 1000 entry->magick=(IsImageFormatHandler *) IsMPC; 1001 (void) RegisterMagickInfo(entry); 1002 return(MagickImageCoderSignature); 1003} 1004 1005/* 1006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1007% % 1008% % 1009% % 1010% U n r e g i s t e r M P C I m a g e % 1011% % 1012% % 1013% % 1014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1015% 1016% UnregisterMPCImage() removes format registrations made by the 1017% MPC module from the list of supported formats. 1018% 1019% The format of the UnregisterMPCImage method is: 1020% 1021% UnregisterMPCImage(void) 1022% 1023*/ 1024ModuleExport void UnregisterMPCImage(void) 1025{ 1026 (void) UnregisterMagickInfo("CACHE"); 1027 (void) UnregisterMagickInfo("MPC"); 1028} 1029 1030/* 1031%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1032% % 1033% % 1034% % 1035% W r i t e M P C I m a g e % 1036% % 1037% % 1038% % 1039%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1040% 1041% WriteMPCImage() writes an Magick Persistent Cache image to a file. 1042% 1043% The format of the WriteMPCImage method is: 1044% 1045% MagickBooleanType WriteMPCImage(const ImageInfo *image_info, 1046% Image *image,ExceptionInfo *exception) 1047% 1048% A description of each parameter follows: 1049% 1050% o image_info: the image info. 1051% 1052% o image: the image. 1053% 1054% o exception: return any errors or warnings in this structure. 1055% 1056*/ 1057static MagickBooleanType WriteMPCImage(const ImageInfo *image_info,Image *image, 1058 ExceptionInfo *exception) 1059{ 1060 char 1061 buffer[MagickPathExtent], 1062 cache_filename[MagickPathExtent]; 1063 1064 const char 1065 *property, 1066 *value; 1067 1068 MagickBooleanType 1069 status; 1070 1071 MagickOffsetType 1072 offset, 1073 scene; 1074 1075 register ssize_t 1076 i; 1077 1078 size_t 1079 depth; 1080 1081 /* 1082 Open persistent cache. 1083 */ 1084 assert(image_info != (const ImageInfo *) NULL); 1085 assert(image_info->signature == MagickCoreSignature); 1086 assert(image != (Image *) NULL); 1087 assert(image->signature == MagickCoreSignature); 1088 if (image->debug != MagickFalse) 1089 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1090 assert(exception != (ExceptionInfo *) NULL); 1091 assert(exception->signature == MagickCoreSignature); 1092 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 1093 if (status == MagickFalse) 1094 return(status); 1095 (void) CopyMagickString(cache_filename,image->filename,MagickPathExtent); 1096 AppendImageFormat("cache",cache_filename); 1097 scene=0; 1098 offset=0; 1099 do 1100 { 1101 /* 1102 Write persistent cache meta-information. 1103 */ 1104 depth=GetImageQuantumDepth(image,MagickTrue); 1105 if ((image->storage_class == PseudoClass) && 1106 (image->colors > (size_t) (GetQuantumRange(image->depth)+1))) 1107 (void) SetImageStorageClass(image,DirectClass,exception); 1108 (void) WriteBlobString(image,"id=MagickCache\n"); 1109 (void) FormatLocaleString(buffer,MagickPathExtent,"magick-signature=%u\n", 1110 GetMagickSignature((const StringInfo *) NULL)); 1111 (void) WriteBlobString(image,buffer); 1112 (void) FormatLocaleString(buffer,MagickPathExtent, 1113 "class=%s colors=%.20g alpha-trait=%s\n",CommandOptionToMnemonic( 1114 MagickClassOptions,image->storage_class),(double) image->colors, 1115 CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t) 1116 image->alpha_trait)); 1117 (void) WriteBlobString(image,buffer); 1118 (void) FormatLocaleString(buffer,MagickPathExtent, 1119 "columns=%.20g rows=%.20g depth=%.20g\n",(double) image->columns, 1120 (double) image->rows,(double) image->depth); 1121 (void) WriteBlobString(image,buffer); 1122 if (image->type != UndefinedType) 1123 { 1124 (void) FormatLocaleString(buffer,MagickPathExtent,"type=%s\n", 1125 CommandOptionToMnemonic(MagickTypeOptions,image->type)); 1126 (void) WriteBlobString(image,buffer); 1127 } 1128 if (image->colorspace != UndefinedColorspace) 1129 { 1130 (void) FormatLocaleString(buffer,MagickPathExtent,"colorspace=%s\n", 1131 CommandOptionToMnemonic(MagickColorspaceOptions,image->colorspace)); 1132 (void) WriteBlobString(image,buffer); 1133 } 1134 if (image->intensity != UndefinedPixelIntensityMethod) 1135 { 1136 (void) FormatLocaleString(buffer,MagickPathExtent, 1137 "pixel-intensity=%s\n",CommandOptionToMnemonic( 1138 MagickPixelIntensityOptions,image->intensity)); 1139 (void) WriteBlobString(image,buffer); 1140 } 1141 if (image->endian != UndefinedEndian) 1142 { 1143 (void) FormatLocaleString(buffer,MagickPathExtent,"endian=%s\n", 1144 CommandOptionToMnemonic(MagickEndianOptions,image->endian)); 1145 (void) WriteBlobString(image,buffer); 1146 } 1147 if (image->compression != UndefinedCompression) 1148 { 1149 (void) FormatLocaleString(buffer,MagickPathExtent, 1150 "compression=%s quality=%.20g\n",CommandOptionToMnemonic( 1151 MagickCompressOptions,image->compression),(double) image->quality); 1152 (void) WriteBlobString(image,buffer); 1153 } 1154 if (image->units != UndefinedResolution) 1155 { 1156 (void) FormatLocaleString(buffer,MagickPathExtent,"units=%s\n", 1157 CommandOptionToMnemonic(MagickResolutionOptions,image->units)); 1158 (void) WriteBlobString(image,buffer); 1159 } 1160 if ((image->resolution.x != 0) || (image->resolution.y != 0)) 1161 { 1162 (void) FormatLocaleString(buffer,MagickPathExtent, 1163 "resolution=%gx%g\n",image->resolution.x,image->resolution.y); 1164 (void) WriteBlobString(image,buffer); 1165 } 1166 if ((image->page.width != 0) || (image->page.height != 0)) 1167 { 1168 (void) FormatLocaleString(buffer,MagickPathExtent, 1169 "page=%.20gx%.20g%+.20g%+.20g\n",(double) image->page.width,(double) 1170 image->page.height,(double) image->page.x,(double) image->page.y); 1171 (void) WriteBlobString(image,buffer); 1172 } 1173 else 1174 if ((image->page.x != 0) || (image->page.y != 0)) 1175 { 1176 (void) FormatLocaleString(buffer,MagickPathExtent,"page=%+ld%+ld\n", 1177 (long) image->page.x,(long) image->page.y); 1178 (void) WriteBlobString(image,buffer); 1179 } 1180 if ((image->tile_offset.x != 0) || (image->tile_offset.y != 0)) 1181 { 1182 (void) FormatLocaleString(buffer,MagickPathExtent, 1183 "tile-offset=%+ld%+ld\n",(long) image->tile_offset.x,(long) 1184 image->tile_offset.y); 1185 (void) WriteBlobString(image,buffer); 1186 } 1187 if ((GetNextImageInList(image) != (Image *) NULL) || 1188 (GetPreviousImageInList(image) != (Image *) NULL)) 1189 { 1190 if (image->scene == 0) 1191 (void) FormatLocaleString(buffer,MagickPathExtent, 1192 "iterations=%.20g delay=%.20g ticks-per-second=%.20g\n",(double) 1193 image->iterations,(double) image->delay,(double) 1194 image->ticks_per_second); 1195 else 1196 (void) FormatLocaleString(buffer,MagickPathExtent,"scene=%.20g " 1197 "iterations=%.20g delay=%.20g ticks-per-second=%.20g\n", 1198 (double) image->scene,(double) image->iterations,(double) 1199 image->delay,(double) image->ticks_per_second); 1200 (void) WriteBlobString(image,buffer); 1201 } 1202 else 1203 { 1204 if (image->scene != 0) 1205 { 1206 (void) FormatLocaleString(buffer,MagickPathExtent,"scene=%.20g\n", 1207 (double) image->scene); 1208 (void) WriteBlobString(image,buffer); 1209 } 1210 if (image->iterations != 0) 1211 { 1212 (void) FormatLocaleString(buffer,MagickPathExtent, 1213 "iterations=%.20g\n",(double) image->iterations); 1214 (void) WriteBlobString(image,buffer); 1215 } 1216 if (image->delay != 0) 1217 { 1218 (void) FormatLocaleString(buffer,MagickPathExtent,"delay=%.20g\n", 1219 (double) image->delay); 1220 (void) WriteBlobString(image,buffer); 1221 } 1222 if (image->ticks_per_second != UndefinedTicksPerSecond) 1223 { 1224 (void) FormatLocaleString(buffer,MagickPathExtent, 1225 "ticks-per-second=%.20g\n",(double) image->ticks_per_second); 1226 (void) WriteBlobString(image,buffer); 1227 } 1228 } 1229 if (image->gravity != UndefinedGravity) 1230 { 1231 (void) FormatLocaleString(buffer,MagickPathExtent,"gravity=%s\n", 1232 CommandOptionToMnemonic(MagickGravityOptions,image->gravity)); 1233 (void) WriteBlobString(image,buffer); 1234 } 1235 if (image->dispose != UndefinedDispose) 1236 { 1237 (void) FormatLocaleString(buffer,MagickPathExtent,"dispose=%s\n", 1238 CommandOptionToMnemonic(MagickDisposeOptions,image->dispose)); 1239 (void) WriteBlobString(image,buffer); 1240 } 1241 if (image->rendering_intent != UndefinedIntent) 1242 { 1243 (void) FormatLocaleString(buffer,MagickPathExtent, 1244 "rendering-intent=%s\n",CommandOptionToMnemonic(MagickIntentOptions, 1245 image->rendering_intent)); 1246 (void) WriteBlobString(image,buffer); 1247 } 1248 if (image->gamma != 0.0) 1249 { 1250 (void) FormatLocaleString(buffer,MagickPathExtent,"gamma=%g\n", 1251 image->gamma); 1252 (void) WriteBlobString(image,buffer); 1253 } 1254 if (image->chromaticity.white_point.x != 0.0) 1255 { 1256 /* 1257 Note chomaticity points. 1258 */ 1259 (void) FormatLocaleString(buffer,MagickPathExtent,"red-primary=" 1260 "%g,%g green-primary=%g,%g blue-primary=%g,%g\n", 1261 image->chromaticity.red_primary.x,image->chromaticity.red_primary.y, 1262 image->chromaticity.green_primary.x, 1263 image->chromaticity.green_primary.y, 1264 image->chromaticity.blue_primary.x, 1265 image->chromaticity.blue_primary.y); 1266 (void) WriteBlobString(image,buffer); 1267 (void) FormatLocaleString(buffer,MagickPathExtent, 1268 "white-point=%g,%g\n",image->chromaticity.white_point.x, 1269 image->chromaticity.white_point.y); 1270 (void) WriteBlobString(image,buffer); 1271 } 1272 if (image->orientation != UndefinedOrientation) 1273 { 1274 (void) FormatLocaleString(buffer,MagickPathExtent, 1275 "orientation=%s\n",CommandOptionToMnemonic(MagickOrientationOptions, 1276 image->orientation)); 1277 (void) WriteBlobString(image,buffer); 1278 } 1279 if (image->profiles != (void *) NULL) 1280 { 1281 const char 1282 *name; 1283 1284 const StringInfo 1285 *profile; 1286 1287 /* 1288 Generic profile. 1289 */ 1290 ResetImageProfileIterator(image); 1291 for (name=GetNextImageProfile(image); name != (const char *) NULL; ) 1292 { 1293 profile=GetImageProfile(image,name); 1294 if (profile != (StringInfo *) NULL) 1295 { 1296 (void) FormatLocaleString(buffer,MagickPathExtent, 1297 "profile:%s=%.20g\n",name,(double) 1298 GetStringInfoLength(profile)); 1299 (void) WriteBlobString(image,buffer); 1300 } 1301 name=GetNextImageProfile(image); 1302 } 1303 } 1304 if (image->montage != (char *) NULL) 1305 { 1306 (void) FormatLocaleString(buffer,MagickPathExtent,"montage=%s\n", 1307 image->montage); 1308 (void) WriteBlobString(image,buffer); 1309 } 1310 ResetImagePropertyIterator(image); 1311 property=GetNextImageProperty(image); 1312 while (property != (const char *) NULL) 1313 { 1314 (void) FormatLocaleString(buffer,MagickPathExtent,"%s=",property); 1315 (void) WriteBlobString(image,buffer); 1316 value=GetImageProperty(image,property,exception); 1317 if (value != (const char *) NULL) 1318 { 1319 size_t 1320 length; 1321 1322 length=strlen(value); 1323 for (i=0; i < (ssize_t) length; i++) 1324 if (isspace((int) ((unsigned char) value[i])) != 0) 1325 break; 1326 if ((i == (ssize_t) length) && (i != 0)) 1327 (void) WriteBlob(image,length,(const unsigned char *) value); 1328 else 1329 { 1330 (void) WriteBlobByte(image,'{'); 1331 if (strchr(value,'}') == (char *) NULL) 1332 (void) WriteBlob(image,length,(const unsigned char *) value); 1333 else 1334 for (i=0; i < (ssize_t) length; i++) 1335 { 1336 if (value[i] == (int) '}') 1337 (void) WriteBlobByte(image,'\\'); 1338 (void) WriteBlobByte(image,value[i]); 1339 } 1340 (void) WriteBlobByte(image,'}'); 1341 } 1342 } 1343 (void) WriteBlobByte(image,'\n'); 1344 property=GetNextImageProperty(image); 1345 } 1346 (void) WriteBlobString(image,"\f\n:\032"); 1347 if (image->montage != (char *) NULL) 1348 { 1349 /* 1350 Write montage tile directory. 1351 */ 1352 if (image->directory != (char *) NULL) 1353 (void) WriteBlobString(image,image->directory); 1354 (void) WriteBlobByte(image,'\0'); 1355 } 1356 if (image->profiles != 0) 1357 { 1358 const char 1359 *name; 1360 1361 const StringInfo 1362 *profile; 1363 1364 /* 1365 Write image profiles. 1366 */ 1367 ResetImageProfileIterator(image); 1368 name=GetNextImageProfile(image); 1369 while (name != (const char *) NULL) 1370 { 1371 profile=GetImageProfile(image,name); 1372 (void) WriteBlob(image,GetStringInfoLength(profile), 1373 GetStringInfoDatum(profile)); 1374 name=GetNextImageProfile(image); 1375 } 1376 } 1377 if (image->storage_class == PseudoClass) 1378 { 1379 size_t 1380 packet_size; 1381 1382 unsigned char 1383 *colormap, 1384 *q; 1385 1386 /* 1387 Allocate colormap. 1388 */ 1389 packet_size=(size_t) (3UL*depth/8UL); 1390 colormap=(unsigned char *) AcquireQuantumMemory(image->colors, 1391 packet_size*sizeof(*colormap)); 1392 if (colormap == (unsigned char *) NULL) 1393 return(MagickFalse); 1394 /* 1395 Write colormap to file. 1396 */ 1397 q=colormap; 1398 for (i=0; i < (ssize_t) image->colors; i++) 1399 { 1400 switch (depth) 1401 { 1402 default: 1403 ThrowWriterException(CorruptImageError,"ImageDepthNotSupported"); 1404 case 32: 1405 { 1406 unsigned int 1407 pixel; 1408 1409 pixel=ScaleQuantumToLong(image->colormap[i].red); 1410 q=PopLongPixel(MSBEndian,pixel,q); 1411 pixel=ScaleQuantumToLong(image->colormap[i].green); 1412 q=PopLongPixel(MSBEndian,pixel,q); 1413 pixel=ScaleQuantumToLong(image->colormap[i].blue); 1414 q=PopLongPixel(MSBEndian,pixel,q); 1415 break; 1416 } 1417 case 16: 1418 { 1419 unsigned short 1420 pixel; 1421 1422 pixel=ScaleQuantumToShort(image->colormap[i].red); 1423 q=PopShortPixel(MSBEndian,pixel,q); 1424 pixel=ScaleQuantumToShort(image->colormap[i].green); 1425 q=PopShortPixel(MSBEndian,pixel,q); 1426 pixel=ScaleQuantumToShort(image->colormap[i].blue); 1427 q=PopShortPixel(MSBEndian,pixel,q); 1428 break; 1429 } 1430 case 8: 1431 { 1432 unsigned char 1433 pixel; 1434 1435 pixel=(unsigned char) ScaleQuantumToChar(image->colormap[i].red); 1436 q=PopCharPixel(pixel,q); 1437 pixel=(unsigned char) ScaleQuantumToChar( 1438 image->colormap[i].green); 1439 q=PopCharPixel(pixel,q); 1440 pixel=(unsigned char) ScaleQuantumToChar(image->colormap[i].blue); 1441 q=PopCharPixel(pixel,q); 1442 break; 1443 } 1444 } 1445 } 1446 (void) WriteBlob(image,packet_size*image->colors,colormap); 1447 colormap=(unsigned char *) RelinquishMagickMemory(colormap); 1448 } 1449 /* 1450 Initialize persistent pixel cache. 1451 */ 1452 status=PersistPixelCache(image,cache_filename,MagickFalse,&offset, 1453 exception); 1454 if (status == MagickFalse) 1455 ThrowWriterException(CacheError,"UnableToPersistPixelCache"); 1456 if (GetNextImageInList(image) == (Image *) NULL) 1457 break; 1458 image=SyncNextImageInList(image); 1459 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1460 { 1461 status=image->progress_monitor(SaveImagesTag,scene, 1462 GetImageListLength(image),image->client_data); 1463 if (status == MagickFalse) 1464 break; 1465 } 1466 scene++; 1467 } while (image_info->adjoin != MagickFalse); 1468 (void) CloseBlob(image); 1469 return(status); 1470} 1471