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 */
79 static 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 */
IsMPC(const unsigned char * magick,const size_t length)107 static 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 */
ReadMPCImage(const ImageInfo * image_info,ExceptionInfo * exception)144 static 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 */
RegisterMPCImage(void)987 ModuleExport 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 */
UnregisterMPCImage(void)1024 ModuleExport 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 */
WriteMPCImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)1057 static 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