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