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