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