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