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