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