root / ImageMagick / trunk / coders / miff.c

Revision 12079, 72.0 kB (checked in by cristy, 5 days ago)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                        M   M  IIIII  FFFFF  FFFFF                           %
7%                        MM MM    I    F      F                               %
8%                        M M M    I    FFF    FFF                             %
9%                        M   M    I    F      F                               %
10%                        M   M  IIIII  F      F                               %
11%                                                                             %
12%                                                                             %
13%                      Read/Write MIFF Image Format.                          %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2008 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/*
40  Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/color.h"
46#include "magick/color-private.h"
47#include "magick/colorspace.h"
48#include "magick/constitute.h"
49#include "magick/exception.h"
50#include "magick/exception-private.h"
51#include "magick/hashmap.h"
52#include "magick/geometry.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/magick.h"
57#include "magick/memory_.h"
58#include "magick/monitor.h"
59#include "magick/monitor-private.h"
60#include "magick/option.h"
61#include "magick/pixel.h"
62#include "magick/profile.h"
63#include "magick/property.h"
64#include "magick/quantum-private.h"
65#include "magick/static.h"
66#include "magick/statistic.h"
67#include "magick/string_.h"
68#include "magick/module.h"
69#if defined(MAGICKCORE_ZLIB_DELEGATE)
70#include "zlib.h"
71#endif
72#if defined(MAGICKCORE_BZLIB_DELEGATE)
73#include "bzlib.h"
74#endif
75
76/*
77  Forward declarations.
78*/
79static MagickBooleanType
80  WriteMIFFImage(const ImageInfo *,Image *);
81
82/*
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84%                                                                             %
85%                                                                             %
86%                                                                             %
87%   I s M I F F                                                               %
88%                                                                             %
89%                                                                             %
90%                                                                             %
91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92%
93%  IsMIFF() returns MagickTrue if the image format type, identified by the
94%  magick string, is MIFF.
95%
96%  The format of the IsMIFF method is:
97%
98%      MagickBooleanType IsMIFF(const unsigned char *magick,const size_t length)
99%
100%  A description of each parameter follows:
101%
102%    o magick: This string is generally the first few bytes of an image file
103%      or blob.
104%
105%    o length: Specifies the length of the magick string.
106%
107*/
108static MagickBooleanType IsMIFF(const unsigned char *magick,const size_t length)
109{
110  if (length < 14)
111    return(MagickFalse);
112  if (LocaleNCompare((char *) magick,"id=ImageMagick",14) == 0)
113    return(MagickTrue);
114  return(MagickFalse);
115}
116
117/*
118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119%                                                                             %
120%                                                                             %
121%                                                                             %
122%   R e a d M I F F I m a g e                                                 %
123%                                                                             %
124%                                                                             %
125%                                                                             %
126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127%
128%  ReadMIFFImage() reads a MIFF image file and returns it.  It allocates the
129%  memory necessary for the new Image structure and returns a pointer to the
130%  new image.
131%
132%  The format of the ReadMIFFImage method is:
133%
134%      Image *ReadMIFFImage(const ImageInfo *image_info,
135%        ExceptionInfo *exception)
136%
137%  Decompression code contributed by Kyle Shorter.
138%
139%  A description of each parameter follows:
140%
141%    o image_info: the image info.
142%
143%    o exception: return any errors or warnings in this structure.
144%
145*/
146
147#if defined(MAGICKCORE_BZLIB_DELEGATE)
148static void *AcquireBZIPMemory(void *context,int items,int size)
149{
150  (void) context;
151  return((void *) AcquireQuantumMemory((size_t) items,(size_t) size));
152}
153#endif
154
155#if defined(MAGICKCORE_ZLIB_DELEGATE)
156static voidpf AcquireZIPMemory(voidpf context,unsigned int items,
157  unsigned int size)
158{
159  (void) context;
160  return((voidpf) AcquireQuantumMemory(items,size));
161}
162#endif
163
164static inline size_t MagickMax(const size_t x,const size_t y)
165{
166  if (x > y)
167    return(x);
168  return(y);
169}
170
171static inline size_t MagickMin(const size_t x,const size_t y)
172{
173  if (x < y)
174    return(x);
175  return(y);
176}
177
178static void PushRunlengthPacket(Image *image,const unsigned char *pixels,
179  size_t *length,PixelPacket *pixel,IndexPacket *index)
180{
181  const unsigned char
182    *p;
183
184  p=pixels;
185  if (image->storage_class == PseudoClass)
186    {
187      *index=(IndexPacket) 0;
188      switch (image->depth)
189      {
190        case 32:
191        {
192          *index=ConstrainColormapIndex(image,
193            (*p << 24) | (*(p+1) << 16) | (*(p+2) << 8) | *(p+3));
194          p+=4;
195          break;
196        }
197        case 16:
198        {
199          *index=ConstrainColormapIndex(image,(*p << 8) | *(p+1));
200          p+=2;
201          break;
202        }
203        case 8:
204        {
205          *index=ConstrainColormapIndex(image,*p);
206          p++;
207          break;
208        }
209        default:
210          (void) ThrowMagickException(&image->exception,GetMagickModule(),
211            CorruptImageError,"ImageDepthNotSupported","`%s'",image->filename);
212      }
213      *pixel=image->colormap[(long) *index];
214      switch (image->depth)
215      {
216        case 8:
217        {
218          unsigned char
219            quantum;
220
221          if (image->matte != MagickFalse)
222            {
223              p=PushCharPixel(p,&quantum);
224              pixel->opacity=ScaleCharToQuantum(quantum);
225            }
226          break;
227        }
228        case 16:
229        {
230          unsigned short
231            quantum;
232
233          if (image->matte != MagickFalse)
234            {
235              p=PushShortPixel(MSBEndian,p,&quantum);
236              pixel->opacity=(Quantum) (quantum >> (image->depth-
237                MAGICKCORE_QUANTUM_DEPTH));
238            }
239          break;
240        }
241        case 32:
242        {
243          unsigned long
244            quantum;
245
246          if (image->matte != MagickFalse)
247            {
248              p=PushLongPixel(MSBEndian,p,&quantum);
249              pixel->opacity=(Quantum) (quantum >> (image->depth-
250                MAGICKCORE_QUANTUM_DEPTH));
251            }
252          break;
253        }
254        default:
255          (void) ThrowMagickException(&image->exception,GetMagickModule(),
256            CorruptImageError,"ImageDepthNotSupported","`%s'",image->filename);
257      }
258      *length=(size_t) (*p++)+1;
259      return;
260    }
261  switch (image->depth)
262  {
263    case 8:
264    {
265      unsigned char
266        quantum;
267
268      p=PushCharPixel(p,&quantum);
269      pixel->red=ScaleCharToQuantum(quantum);
270      p=PushCharPixel(p,&quantum);
271      pixel->green=ScaleCharToQuantum(quantum);
272      p=PushCharPixel(p,&quantum);
273      pixel->blue=ScaleCharToQuantum(quantum);
274      if (image->matte != MagickFalse)
275        {
276          p=PushCharPixel(p,&quantum);
277          pixel->opacity=ScaleCharToQuantum(quantum);
278        }
279      if (image->colorspace == CMYKColorspace)
280        {
281          p=PushCharPixel(p,&quantum);
282          *index=ScaleCharToQuantum(quantum);
283        }
284      break;
285    }
286    case 16:
287    {
288      unsigned short
289        quantum;
290
291      p=PushShortPixel(MSBEndian,p,&quantum);
292      pixel->red=(Quantum) (quantum >> (image->depth-MAGICKCORE_QUANTUM_DEPTH));
293      p=PushShortPixel(MSBEndian,p,&quantum);
294      pixel->green=(Quantum) (quantum >> (image->depth-
295        MAGICKCORE_QUANTUM_DEPTH));
296      p=PushShortPixel(MSBEndian,p,&quantum);
297      pixel->blue=(Quantum) (quantum >> (image->depth-
298        MAGICKCORE_QUANTUM_DEPTH));
299      if (image->matte != MagickFalse)
300        {
301          p=PushShortPixel(MSBEndian,p,&quantum);
302          pixel->opacity=(Quantum) (quantum >> (image->depth-
303            MAGICKCORE_QUANTUM_DEPTH));
304        }
305      if (image->colorspace == CMYKColorspace)
306        {
307          p=PushShortPixel(MSBEndian,p,&quantum);
308          *index=(IndexPacket) (quantum >> (image->depth-
309            MAGICKCORE_QUANTUM_DEPTH));
310        }
311      break;
312    }
313    case 32:
314    {
315      unsigned long
316        quantum;
317
318      p=PushLongPixel(MSBEndian,p,&quantum);
319      pixel->red=(Quantum) (quantum >> (image->depth-MAGICKCORE_QUANTUM_DEPTH));
320      p=PushLongPixel(MSBEndian,p,&quantum);
321      pixel->green=(Quantum) (quantum >> (image->depth-
322        MAGICKCORE_QUANTUM_DEPTH));
323      p=PushLongPixel(MSBEndian,p,&quantum);
324      pixel->blue=(Quantum) (quantum >> (image->depth-
325        MAGICKCORE_QUANTUM_DEPTH));
326      if (image->matte != MagickFalse)
327        {
328          p=PushLongPixel(MSBEndian,p,&quantum);
329          pixel->opacity=(Quantum) (quantum >> (image->depth-
330            MAGICKCORE_QUANTUM_DEPTH));
331        }
332      if (image->colorspace == CMYKColorspace)
333        {
334          p=PushLongPixel(MSBEndian,p,&quantum);
335          *index=(IndexPacket) (quantum >> (image->depth-
336            MAGICKCORE_QUANTUM_DEPTH));
337        }
338      break;
339    }
340    default:
341      (void) ThrowMagickException(&image->exception,GetMagickModule(),
342        CorruptImageError,"ImageDepthNotSupported","`%s'",image->filename);
343  }
344  *length=(size_t) (*p++)+1;
345}
346
347#if defined(MAGICKCORE_ZLIB_DELEGATE)
348static void RelinquishZIPMemory(voidpf context,voidpf memory)
349{
350  (void) context;
351  memory=RelinquishMagickMemory(memory);
352}
353#endif
354
355#if defined(MAGICKCORE_BZLIB_DELEGATE)
356static void RelinquishBZIPMemory(void *context,void *memory)
357{
358  (void) context;
359  memory=RelinquishMagickMemory(memory);
360}
361#endif
362
363static Image *ReadMIFFImage(const ImageInfo *image_info,
364  ExceptionInfo *exception)
365{
366#define BZipMaxExtent(x)  ((x)+((x)/100)+600)
367#define ZipMaxExtent(x)  ((x)+(((x)+7) >> 3)+(((x)+63) >> 6)+11)
368
369#if defined(MAGICKCORE_BZLIB_DELEGATE)
370  bz_stream
371    bzip_info;
372#endif
373
374  char
375    id[MaxTextExtent],
376    keyword[MaxTextExtent],
377    *options;
378
379  const unsigned char
380    *p;
381
382  double
383    version;
384
385  GeometryInfo
386    geometry_info;
387
388  Image
389    *image;
390
391  IndexPacket
392    index;
393
394  int
395    c,
396    code;
397
398  LinkedListInfo
399    *profiles;
400
401  MagickOffsetType
402    offset;
403
404  long
405    y;
406
407  MagickBooleanType
408    status;
409
410  MagickStatusType
411    flags;
412
413  PixelPacket
414    pixel;
415
416  QuantumFormatType
417    quantum_format;
418
419  QuantumInfo
420    *quantum_info;
421
422  QuantumType
423    quantum_type;
424
425  register IndexPacket
426    *indexes;
427
428  register long
429    i,
430    x;
431
432  register PixelPacket
433    *q;
434
435  size_t
436    length,
437    packet_size;
438
439  ssize_t
440    count;
441
442  unsigned char
443    *compress_pixels,
444    *pixels;
445
446  unsigned long
447    colors;
448
449#if defined(MAGICKCORE_ZLIB_DELEGATE)
450  z_stream
451    zip_info;
452#endif
453
454  /*
455    Open image file.
456  */
457  assert(image_info != (const ImageInfo *) NULL);
458  assert(image_info->signature == MagickSignature);
459  if (image_info->debug != MagickFalse)
460    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
461      image_info->filename);
462  assert(exception != (ExceptionInfo *) NULL);
463  assert(exception->signature == MagickSignature);
464  image=AcquireImage(image_info);
465  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
466  if (status == MagickFalse)
467    {
468      image=DestroyImageList(image);
469      return((Image *) NULL);
470    }
471  /*
472    Decode image header;  header terminates one character beyond a ':'.
473  */
474  c=ReadBlobByte(image);
475  if (c == EOF)
476    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
477  code=0;
478  *id='\0';
479  (void) ResetMagickMemory(keyword,0,sizeof(keyword));
480  version=0.0;
481  do
482  {
483    /*
484      Decode image header;  header terminates one character beyond a ':'.
485    */
486    length=MaxTextExtent;
487    options=AcquireString((char *) NULL);
488    quantum_format=UndefinedQuantumFormat;
489    profiles=(LinkedListInfo *) NULL;
490    colors=0;
491    image->depth=8UL;
492    image->compression=NoCompression;
493    while ((isgraph(c) != MagickFalse) && (c != (int) ':'))
494    {
495      register char
496        *p;
497
498      if (c == (int) '{')
499        {
500          char
501            *comment;
502
503          /*
504            Read comment-- any text between { }.
505          */
506          length=MaxTextExtent;
507          comment=AcquireString((char *) NULL);
508          for (p=comment; comment != (char *) NULL; p++)
509          {
510            c=ReadBlobByte(image);
511            if ((c == EOF) || (c == (int) '}'))
512              break;
513            if ((size_t) (p-comment+1) >= length)
514              {
515                *p='\0';
516                length<<=1;
517                comment=(char *) ResizeQuantumMemory(comment,length+
518                  MaxTextExtent,sizeof(*comment));
519                if (comment == (char *) NULL)
520                  break;
521                p=comment+strlen(comment);
522              }
523            *p=(char) c;
524          }
525          if (comment == (char *) NULL)
526            ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
527          *p='\0';
528          (void) SetImageProperty(image,"comment",comment);
529          comment=DestroyString(comment);
530          c=ReadBlobByte(image);
531        }
532      else
533        if (isalnum(c) != MagickFalse)
534          {
535            /*
536              Get the keyword.
537            */
538            p=keyword;
539            do
540            {
541              if (isspace((int) ((unsigned char) c)) != 0)
542                break;
543              if (c == (int) '=')
544                break;
545              if ((size_t) (p-keyword) < MaxTextExtent)
546                *p++=(char) c;
547              c=ReadBlobByte(image);
548            } while (c != EOF);
549            *p='\0';
550            p=options;
551            while (isspace((int) ((unsigned char) c)) != 0)
552              c=ReadBlobByte(image);
553            if (c == (int) '=')
554              {
555                /*
556                  Get the keyword value.
557                */
558                c=ReadBlobByte(image);
559                while ((c != (int) '}') && (c != EOF))
560                {
561                  if ((size_t) (p-options+1) >= length)
562                    {
563                      *p='\0';
564                      length<<=1;
565                      options=(char *) ResizeQuantumMemory(options,length+
566                        MaxTextExtent,sizeof(*options));
567                      if (options == (char *) NULL)
568                        break;
569                      p=options+strlen(options);
570                    }
571                  if (options == (char *) NULL)
572                    ThrowReaderException(ResourceLimitError,
573                      "MemoryAllocationFailed");
574                  *p++=(char) c;
575                  c=ReadBlobByte(image);
576                  if (*options != '{')
577                    if (isspace((int) ((unsigned char) c)) != 0)
578                      break;
579                }
580              }
581            *p='\0';
582            if (*options == '{')
583              (void) CopyMagickString(options,options+1,MaxTextExtent);
584            /*
585              Assign a value to the specified keyword.
586            */
587            switch (*keyword)
588            {
589              case 'b':
590              case 'B':
591              {
592                if (LocaleCompare(keyword,"background-color") == 0)
593                  {
594                    (void) QueryColorDatabase(options,&image->background_color,
595                      exception);
596                    break;
597                  }
598                if (LocaleCompare(keyword,"blue-primary") == 0)
599                  {
600                    flags=ParseGeometry(options,&geometry_info);
601                    image->chromaticity.blue_primary.x=geometry_info.rho;
602                    image->chromaticity.blue_primary.y=geometry_info.sigma;
603                    if ((flags & SigmaValue) == 0)
604                      image->chromaticity.blue_primary.y=
605                        image->chromaticity.blue_primary.x;
606                    break;
607                  }
608                if (LocaleCompare(keyword,"border-color") == 0)
609                  {
610                    (void) QueryColorDatabase(options,&image->border_color,
611                      exception);
612                    break;
613                  }
614                (void) SetImageProperty(image,keyword,options);
615                break;
616              }
617              case 'c':
618              case 'C':
619              {
620                if (LocaleCompare(keyword,"class") == 0)
621                  {
622                    image->storage_class=(ClassType) ParseMagickOption(
623                      MagickClassOptions,MagickFalse,options);
624                    break;
625                  }
626                if (LocaleCompare(keyword,"colors") == 0)
627                  {
628                    colors=(unsigned long) atol(options);
629                    break;
630                  }
631                if (LocaleCompare(keyword,"colorspace") == 0)
632                  {
633                    image->colorspace=(ColorspaceType) ParseMagickOption(
634                      MagickColorspaceOptions,MagickFalse,options);
635                    break;
636                  }
637                if (LocaleCompare(keyword,"compression") == 0)
638                  {
639                    image->compression=(CompressionType) ParseMagickOption(
640                      MagickCompressOptions,MagickFalse,options);
641                    break;
642                  }
643                if (LocaleCompare(keyword,"columns") == 0)
644                  {
645                    image->columns=(unsigned long) atol(options);
646                    break;
647                  }
648                (void) SetImageProperty(image,keyword,options);
649                break;
650              }
651              case 'd':
652              case 'D':
653              {
654                if (LocaleCompare(keyword,"delay") == 0)
655                  {
656                    image->delay=(unsigned long) atol(options);
657                    break;
658                  }
659                if (LocaleCompare(keyword,"depth") == 0)
660                  {
661                    image->depth=(unsigned long) atol(options);
662                    break;
663                  }
664                if (LocaleCompare(keyword,"dispose") == 0)
665                  {
666                    image->dispose=(DisposeType) ParseMagickOption(
667                      MagickDisposeOptions,MagickFalse,options);
668                    break;
669                  }
670                (void) SetImageProperty(image,keyword,options);
671                break;
672              }
673              case 'e':
674              case 'E':
675              {
676                if (LocaleCompare(keyword,"endian") == 0)
677                  {
678                    image->endian=(EndianType) ParseMagickOption(
679                      MagickEndianOptions,MagickFalse,options);
680                    break;
681                  }
682                (void) SetImageProperty(image,keyword,options);
683                break;
684              }
685              case 'g':
686              case 'G':
687              {
688                if (LocaleCompare(keyword,"gamma") == 0)
689                  {
690                    image->gamma=atof(options);
691                    break;
692                  }
693                if (LocaleCompare(keyword,"green-primary") == 0)
694                  {
695                    flags=ParseGeometry(options,&geometry_info);
696                    image->chromaticity.green_primary.x=geometry_info.rho;
697                    image->chromaticity.green_primary.y=geometry_info.sigma;
698                    if ((flags & SigmaValue) == 0)
699                      image->chromaticity.green_primary.y=
700                        image->chromaticity.green_primary.x;
701                    break;
702                  }
703                (void) SetImageProperty(image,keyword,options);
704                break;
705              }
706              case 'i':
707              case 'I':
708              {
709                if (LocaleCompare(keyword,"id") == 0)
710                  {
711                    (void) CopyMagickString(id,options,MaxTextExtent);
712                    break;
713                  }
714                if (LocaleCompare(keyword,"iterations") == 0)
715                  {
716                    image->iterations=(unsigned long) atol(options);
717                    break;
718                  }
719                (void) SetImageProperty(image,keyword,options);
720                break;
721              }
722              case 'm':
723              case 'M':
724              {
725                if (LocaleCompare(keyword,"matte") == 0)
726                  {
727                    image->matte=(MagickBooleanType) ParseMagickOption(
728                      MagickBooleanOptions,MagickFalse,options);
729                    break;
730                  }
731                if (LocaleCompare(keyword,"matte-color") == 0)
732                  {
733                    (void) QueryColorDatabase(options,&image->matte_color,
734                      exception);
735                    break;
736                  }
737                if (LocaleCompare(keyword,"montage") == 0)
738                  {
739                    (void) CloneString(&image->montage,options);
740                    break;
741                  }
742                (void) SetImageProperty(image,keyword,options);
743                break;
744              }
745              case 'o':
746              case 'O':
747              {
748                if (LocaleCompare(keyword,"opaque") == 0)
749                  {
750                    image->matte=(MagickBooleanType) ParseMagickOption(
751                      MagickBooleanOptions,MagickFalse,options);
752                    break;
753                  }
754                if (LocaleCompare(keyword,"orientation") == 0)
755                  {
756                    image->orientation=(OrientationType) ParseMagickOption(
757                      MagickOrientationOptions,MagickFalse,options);
758                    break;
759                  }
760                (void) SetImageProperty(image,keyword,options);
761                break;
762              }
763              case 'p':
764              case 'P':
765              {
766                if (LocaleCompare(keyword,"page") == 0)
767                  {
768                    char
769                      *geometry;
770
771                    geometry=GetPageGeometry(options);
772                    (void) ParseAbsoluteGeometry(geometry,&image->page);
773                    geometry=DestroyString(geometry);
774                    break;
775                  }
776                if (LocaleNCompare(keyword,"profile-",8) == 0)
777                  {
778                    StringInfo
779                      *profile;
780
781                    if (profiles == (LinkedListInfo *) NULL)
782                      profiles=NewLinkedList(0);
783                    (void) AppendValueToLinkedList(profiles,
784                      AcquireString(keyword+8));
785                    profile=AcquireStringInfo((size_t) atol(options));
786                    (void) SetImageProfile(image,keyword+8,profile);
787                    profile=DestroyStringInfo(profile);
788                    break;
789                  }
790                (void) SetImageProperty(image,keyword,options);
791                break;
792              }
793              case 'q':
794              case 'Q':
795              {
796                if (LocaleCompare(keyword,"quality") == 0)
797                  {
798                    image->quality=(unsigned long) atol(options);
799                    break;
800                  }
801                if (LocaleCompare(keyword,"quantum-format") == 0)
802                  {
803                    quantum_format=(QuantumFormatType) ParseMagickOption(
804                      MagickQuantumFormatOptions,MagickFalse,options);
805                    break;
806                  }
807                (void) SetImageProperty(image,keyword,options);
808                break;
809              }
810              case 'r':
811              case 'R':
812              {
813                if (LocaleCompare(keyword,"red-primary") == 0)
814                  {
815                    flags=ParseGeometry(options,&geometry_info);
816                    image->chromaticity.red_primary.x=geometry_info.rho;
817                    image->chromaticity.red_primary.y=geometry_info.sigma;
818                    if ((flags & SigmaValue) == 0)
819                      image->chromaticity.red_primary.y=
820                        image->chromaticity.red_primary.x;
821                    break;
822                  }
823                if (LocaleCompare(keyword,"rendering-intent") == 0)
824                  {
825                    image->rendering_intent=(RenderingIntent) ParseMagickOption(
826                      MagickIntentOptions,MagickFalse,options);
827                    break;
828                  }
829                if (LocaleCompare(keyword,"resolution") == 0)
830                  {
831                    flags=ParseGeometry(options,&geometry_info);
832                    image->x_resolution=geometry_info.rho;
833                    image->y_resolution=geometry_info.sigma;
834                    if ((flags & SigmaValue) == 0)
835                      image->y_resolution=image->x_resolution;
836                    break;
837                  }
838                if (LocaleCompare(keyword,"rows") == 0)
839                  {
840                    image->rows=(unsigned long) atol(options);
841                    break;
842                  }
843                (void) SetImageProperty(image,keyword,options);
844                break;
845              }
846              case 's':
847              case 'S':
848              {
849                if (LocaleCompare(keyword,"scene") == 0)
850                  {
851                    image->scene=(unsigned long) atol(options);
852                    break;
853                  }
854                (void) SetImageProperty(image,keyword,options);
855                break;
856              }
857              case 't':
858              case 'T':
859              {
860                if (LocaleCompare(keyword,"ticks-per-second") == 0)
861                  {
862                    image->ticks_per_second=atol(options);
863                    break;
864                  }
865                if (LocaleCompare(keyword,"tile-offset") == 0)
866                  {
867                    char
868                      *geometry;
869
870                    geometry=GetPageGeometry(options);
871                    (void) ParseAbsoluteGeometry(geometry,&image->tile_offset);
872                    geometry=DestroyString(geometry);
873                    break;
874                  }
875                if (LocaleCompare(keyword,"type") == 0)
876                  {
877                    image->type=(ImageType) ParseMagickOption(MagickTypeOptions,
878                      MagickFalse,options);
879                    break;
880                  }
881                (void) SetImageProperty(image,keyword,options);
882                break;
883              }
884              case 'u':
885              case 'U':
886              {
887                if (LocaleCompare(keyword,"units") == 0)
888                  {
889                    image->units=(ResolutionType) ParseMagickOption(
890                      MagickResolutionOptions,MagickFalse,options);
891                    break;
892                  }
893                (void) SetImageProperty(image,keyword,options);
894                break;
895              }
896              case 'v':
897              case 'V':
898              {
899                if (LocaleCompare(keyword,"version") == 0)
900                  {
901                    version=atof(options);
902                    break;
903                  }
904                (void) SetImageProperty(image,keyword,options);
905                break;
906              }
907              case 'w':
908              case 'W':
909              {
910                if (LocaleCompare(keyword,"white-point") == 0)
911                  {
912                    flags=ParseGeometry(options,&geometry_info);
913                    image->chromaticity.white_point.x=geometry_info.rho;
914                    image->chromaticity.white_point.y=geometry_info.rho;
915                    if ((flags & SigmaValue) != 0)
916                      image->chromaticity.white_point.y=
917                        image->chromaticity.white_point.x;
918                    break;
919                  }
920                (void) SetImageProperty(image,keyword,options);
921                break;
922              }
923              default:
924              {
925                (void) SetImageProperty(image,keyword,options);
926                break;
927              }
928            }
929          }
930        else
931          c=ReadBlobByte(image);
932      while (isspace((int) ((unsigned char) c)) != 0)
933        c=ReadBlobByte(image);
934    }
935    options=DestroyString(options);
936    (void) ReadBlobByte(image);
937    /*
938      Verify that required image information is defined.
939    */
940    if ((LocaleCompare(id,"ImageMagick") != 0) ||
941        (image->storage_class == UndefinedClass) ||
942        (image->columns == 0) || (image->rows == 0))
943      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
944    if (image->montage != (char *) NULL)
945      {
946        register char
947          *p;
948
949        /*
950          Image directory.
951        */
952        length=MaxTextExtent;
953        image->directory=AcquireString((char *) NULL);
954        p=image->directory;
955        do
956        {
957          *p='\0';
958          if ((strlen(image->directory)+MaxTextExtent) >= length)
959            {
960              /*
961                Allocate more memory for the image directory.
962              */
963              length<<=1;
964              image->directory=(char *) ResizeQuantumMemory(image->directory,
965                length+MaxTextExtent,sizeof(*image->directory));
966              if (image->directory == (char *) NULL)
967                ThrowReaderException(CorruptImageError,"UnableToReadImageData");
968              p=image->directory+strlen(image->directory);
969            }
970          c=ReadBlobByte(image);
971          *p++=(char) c;
972        } while (c != (int) '\0');
973      }
974    if (profiles != (LinkedListInfo *) NULL)
975      {
976        const char
977          *name;
978
979        const StringInfo
980          *profile;
981
982        /*
983          Read image profiles.