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