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