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