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