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