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