2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Compuserv Graphics Interchange Format %
20 % Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/blob-private.h"
45 #include "magick/cache.h"
46 #include "magick/color.h"
47 #include "magick/color-private.h"
48 #include "magick/colormap.h"
49 #include "magick/colormap-private.h"
50 #include "magick/colorspace.h"
51 #include "magick/exception.h"
52 #include "magick/exception-private.h"
53 #include "magick/image.h"
54 #include "magick/image-private.h"
55 #include "magick/list.h"
56 #include "magick/profile.h"
57 #include "magick/magick.h"
58 #include "magick/memory_.h"
59 #include "magick/monitor.h"
60 #include "magick/monitor-private.h"
61 #include "magick/option.h"
62 #include "magick/property.h"
63 #include "magick/quantize.h"
64 #include "magick/quantum-private.h"
65 #include "magick/static.h"
66 #include "magick/string_.h"
67 #include "magick/module.h"
72 #define MaximumLZWBits 12
73 #define MaximumLZWCode (1UL << MaximumLZWBits)
78 typedef struct _LZWCodeInfo
91 typedef struct _LZWStack
99 typedef struct _LZWInfo
127 Forward declarations.
130 GetNextLZWCode(LZWInfo *,const size_t);
132 static MagickBooleanType
133 WriteGIFImage(const ImageInfo *,Image *);
136 ReadBlobBlock(Image *,unsigned char *);
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143 % D e c o d e I m a g e %
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149 % DecodeImage uncompresses an image via GIF-coding.
151 % The format of the DecodeImage method is:
153 % MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
155 % A description of each parameter follows:
157 % o image: the address of a structure of type Image.
159 % o opacity: The colormap index associated with the transparent color.
163 static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
165 if (lzw_info->table[0] != (size_t *) NULL)
166 lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
168 if (lzw_info->table[1] != (size_t *) NULL)
169 lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
171 if (lzw_info->stack != (LZWStack *) NULL)
173 if (lzw_info->stack->codes != (size_t *) NULL)
174 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
175 lzw_info->stack->codes);
176 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
178 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
179 return((LZWInfo *) NULL);
182 static inline void ResetLZWInfo(LZWInfo *lzw_info)
184 lzw_info->bits=lzw_info->data_size+1;
185 lzw_info->maximum_code=1UL << lzw_info->bits;
186 lzw_info->slot=lzw_info->maximum_data_value+3;
187 lzw_info->genesis=MagickTrue;
190 static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
198 lzw_info=(LZWInfo *) AcquireAlignedMemory(1,sizeof(*lzw_info));
199 if (lzw_info == (LZWInfo *) NULL)
200 return((LZWInfo *) NULL);
201 (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info));
202 lzw_info->image=image;
203 lzw_info->data_size=data_size;
204 lzw_info->maximum_data_value=(1UL << data_size)-1;
205 lzw_info->clear_code=lzw_info->maximum_data_value+1;
206 lzw_info->end_code=lzw_info->maximum_data_value+2;
207 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
208 sizeof(*lzw_info->table));
209 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
210 sizeof(*lzw_info->table));
211 if ((lzw_info->table[0] == (size_t *) NULL) ||
212 (lzw_info->table[1] == (size_t *) NULL))
214 lzw_info=RelinquishLZWInfo(lzw_info);
215 return((LZWInfo *) NULL);
217 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
219 lzw_info->table[0][i]=0;
220 lzw_info->table[1][i]=(size_t) i;
222 ResetLZWInfo(lzw_info);
223 lzw_info->code_info.buffer[0]='\0';
224 lzw_info->code_info.buffer[1]='\0';
225 lzw_info->code_info.count=2;
226 lzw_info->code_info.bit=8*lzw_info->code_info.count;
227 lzw_info->code_info.eof=MagickFalse;
228 lzw_info->genesis=MagickTrue;
229 lzw_info->stack=(LZWStack *) AcquireAlignedMemory(1,sizeof(*lzw_info->stack));
230 if (lzw_info->stack == (LZWStack *) NULL)
232 lzw_info=RelinquishLZWInfo(lzw_info);
233 return((LZWInfo *) NULL);
235 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
236 MaximumLZWCode,sizeof(*lzw_info->stack->codes));
237 if (lzw_info->stack->codes == (size_t *) NULL)
239 lzw_info=RelinquishLZWInfo(lzw_info);
240 return((LZWInfo *) NULL);
242 lzw_info->stack->index=lzw_info->stack->codes;
243 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
247 static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
255 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
256 (lzw_info->code_info.eof == MagickFalse))
261 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
262 lzw_info->code_info.count-2];
263 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
264 lzw_info->code_info.count-1];
265 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
266 lzw_info->code_info.count=2;
267 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
268 lzw_info->code_info.count]);
270 lzw_info->code_info.count+=count;
272 lzw_info->code_info.eof=MagickTrue;
274 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
277 for (i=0; i < (ssize_t) bits; i++)
279 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
280 (1UL << (lzw_info->code_info.bit % 8))) != 0) << i;
281 lzw_info->code_info.bit++;
286 static inline ssize_t PopLZWStack(LZWStack *stack_info)
288 if (stack_info->index <= stack_info->codes)
291 return((ssize_t) *stack_info->index);
294 static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
296 if (stack_info->index >= stack_info->top)
298 *stack_info->index=value;
302 static int ReadBlobLZWByte(LZWInfo *lzw_info)
313 if (lzw_info->stack->index != lzw_info->stack->codes)
314 return(PopLZWStack(lzw_info->stack));
315 if (lzw_info->genesis != MagickFalse)
317 lzw_info->genesis=MagickFalse;
320 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,
322 lzw_info->last_code=lzw_info->first_code;
323 } while (lzw_info->first_code == lzw_info->clear_code);
324 return((int) lzw_info->first_code);
326 code=GetNextLZWCode(lzw_info,lzw_info->bits);
329 if ((size_t) code == lzw_info->clear_code)
331 ResetLZWInfo(lzw_info);
332 return(ReadBlobLZWByte(lzw_info));
334 if ((size_t) code == lzw_info->end_code)
336 if ((size_t) code < lzw_info->slot)
340 PushLZWStack(lzw_info->stack,lzw_info->first_code);
341 value=lzw_info->last_code;
344 while (value > lzw_info->maximum_data_value)
346 if ((size_t) count > MaximumLZWCode)
349 if ((size_t) value > MaximumLZWCode)
351 PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
352 value=lzw_info->table[0][value];
354 lzw_info->first_code=lzw_info->table[1][value];
355 PushLZWStack(lzw_info->stack,lzw_info->first_code);
356 if (lzw_info->slot < MaximumLZWCode)
358 lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
359 lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
361 if ((lzw_info->slot >= lzw_info->maximum_code) &&
362 (lzw_info->bits < MaximumLZWBits))
365 lzw_info->maximum_code=1UL << lzw_info->bits;
368 lzw_info->last_code=(size_t) code;
369 return(PopLZWStack(lzw_info->stack));
372 static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
397 Allocate decoder tables.
399 assert(image != (Image *) NULL);
400 assert(image->signature == MagickSignature);
401 if (image->debug != MagickFalse)
402 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
403 data_size=(unsigned char) ReadBlobByte(image);
404 if (data_size > MaximumLZWBits)
405 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
406 lzw_info=AcquireLZWInfo(image,data_size);
407 if (lzw_info == (LZWInfo *) NULL)
408 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
410 exception=(&image->exception);
413 for (y=0; y < (ssize_t) image->rows; y++)
424 q=GetAuthenticPixels(image,0,offset,image->columns,1,exception);
425 if (q == (PixelPacket *) NULL)
427 indexes=GetAuthenticIndexQueue(image);
428 for (x=0; x < (ssize_t) image->columns; )
430 c=ReadBlobLZWByte(lzw_info);
433 index=ConstrainColormapIndex(image,(size_t) c);
434 q->red=image->colormap[(ssize_t) index].red;
435 q->green=image->colormap[(ssize_t) index].green;
436 q->blue=image->colormap[(ssize_t) index].blue;
437 q->opacity=(ssize_t) index == opacity ? (Quantum) TransparentOpacity :
438 (Quantum) OpaqueOpacity;
443 if (x < (ssize_t) image->columns)
445 if (image->interlace == NoInterlace)
454 if (offset >= (ssize_t) image->rows)
464 if (offset >= (ssize_t) image->rows)
474 if (offset >= (ssize_t) image->rows)
487 if (SyncAuthenticPixels(image,exception) == MagickFalse)
490 lzw_info=RelinquishLZWInfo(lzw_info);
491 if (y < (ssize_t) image->rows)
492 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501 % E n c o d e I m a g e %
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507 % EncodeImage compresses an image via GIF-coding.
509 % The format of the EncodeImage method is:
511 % MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
512 % const size_t data_size)
514 % A description of each parameter follows:
516 % o image_info: the image info.
518 % o image: the address of a structure of type Image.
520 % o data_size: The number of bits in the compressed packet.
523 static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
524 const size_t data_size)
526 #define MaxCode(number_bits) ((1UL << (number_bits))-1)
527 #define MaxHashTable 5003
528 #define MaxGIFBits 12UL
529 #define MaxGIFTable (1UL << MaxGIFBits)
530 #define GIFOutputCode(code) \
536 datum|=(code) << bits; \
543 Add a character to current packet. \
545 packet[length++]=(unsigned char) (datum & 0xff); \
548 (void) WriteBlobByte(image,(unsigned char) length); \
549 (void) WriteBlob(image,length,packet); \
555 if (free_code > max_code) \
558 if (number_bits == MaxGIFBits) \
559 max_code=MaxGIFTable; \
561 max_code=MaxCode(number_bits); \
593 end_of_information_code,
601 Allocate encoder tables.
603 assert(image != (Image *) NULL);
604 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
605 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
606 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
607 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
608 sizeof(*hash_suffix));
609 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
610 (hash_prefix == (short *) NULL) ||
611 (hash_suffix == (unsigned char *) NULL))
613 if (packet != (unsigned char *) NULL)
614 packet=(unsigned char *) RelinquishMagickMemory(packet);
615 if (hash_code != (short *) NULL)
616 hash_code=(short *) RelinquishMagickMemory(hash_code);
617 if (hash_prefix != (short *) NULL)
618 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
619 if (hash_suffix != (unsigned char *) NULL)
620 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
624 Initialize GIF encoder.
626 number_bits=data_size;
627 max_code=MaxCode(number_bits);
628 clear_code=((short) 1UL << (data_size-1));
629 end_of_information_code=clear_code+1;
630 free_code=clear_code+2;
634 for (i=0; i < MaxHashTable; i++)
636 GIFOutputCode(clear_code);
643 for (y=0; y < (ssize_t) image->rows; y++)
645 register const IndexPacket
648 register const PixelPacket
654 p=GetVirtualPixels(image,0,offset,image->columns,1,&image->exception);
655 if (p == (const PixelPacket *) NULL)
657 indexes=GetVirtualIndexQueue(image);
659 waiting_code=(short) (*indexes);
660 for (x=(y == 0) ? 1 : 0; x < (ssize_t) image->columns; x++)
665 index=(IndexPacket) ((size_t) indexes[x] & 0xff);
667 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
668 if (k >= MaxHashTable)
670 next_pixel=MagickFalse;
672 if (hash_code[k] > 0)
674 if ((hash_prefix[k] == waiting_code) &&
675 (hash_suffix[k] == (unsigned char) index))
677 waiting_code=hash_code[k];
681 displacement=MaxHashTable-k;
687 if (hash_code[k] == 0)
689 if ((hash_prefix[k] == waiting_code) &&
690 (hash_suffix[k] == (unsigned char) index))
692 waiting_code=hash_code[k];
693 next_pixel=MagickTrue;
697 if (next_pixel == MagickTrue)
700 GIFOutputCode((size_t) waiting_code);
701 if (free_code < MaxGIFTable)
703 hash_code[k]=(short) free_code++;
704 hash_prefix[k]=waiting_code;
705 hash_suffix[k]=(unsigned char) index;
710 Fill the hash table with empty entries.
712 for (k=0; k < MaxHashTable; k++)
715 Reset compressor and issue a clear code.
717 free_code=clear_code+2;
718 GIFOutputCode(clear_code);
719 number_bits=data_size;
720 max_code=MaxCode(number_bits);
722 waiting_code=(short) index;
724 if (image_info->interlace == NoInterlace)
733 if (offset >= (ssize_t) image->rows)
743 if (offset >= (ssize_t) image->rows)
753 if (offset >= (ssize_t) image->rows)
768 Flush out the buffered code.
770 GIFOutputCode((size_t) waiting_code);
771 GIFOutputCode(end_of_information_code);
775 Add a character to current packet.
777 packet[length++]=(unsigned char) (datum & 0xff);
780 (void) WriteBlobByte(image,(unsigned char) length);
781 (void) WriteBlob(image,length,packet);
786 Flush accumulated data.
790 (void) WriteBlobByte(image,(unsigned char) length);
791 (void) WriteBlob(image,length,packet);
796 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
797 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
798 hash_code=(short *) RelinquishMagickMemory(hash_code);
799 packet=(unsigned char *) RelinquishMagickMemory(packet);
804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 % IsGIF() returns MagickTrue if the image format type, identified by the
815 % magick string, is GIF.
817 % The format of the IsGIF method is:
819 % MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
821 % A description of each parameter follows:
823 % o magick: compare image format pattern against these bytes.
825 % o length: Specifies the length of the magick string.
828 static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
832 if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
842 + R e a d B l o b B l o c k %
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
848 % ReadBlobBlock() reads data from the image file and returns it. The
849 % amount of data is determined by first reading a count byte. The number
850 % of bytes read is returned.
852 % The format of the ReadBlobBlock method is:
854 % size_t ReadBlobBlock(Image *image,unsigned char *data)
856 % A description of each parameter follows:
858 % o image: the image.
860 % o data: Specifies an area to place the information requested from
864 static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
872 assert(image != (Image *) NULL);
873 assert(image->signature == MagickSignature);
874 assert(data != (unsigned char *) NULL);
875 count=ReadBlob(image,1,&block_count);
878 return(ReadBlob(image,(size_t) block_count,data));
882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
886 % R e a d G I F I m a g e %
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892 % ReadGIFImage() reads a Compuserve Graphics image file and returns it.
893 % It allocates the memory necessary for the new Image structure and returns a
894 % pointer to the new image.
896 % The format of the ReadGIFImage method is:
898 % Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
900 % A description of each parameter follows:
902 % o image_info: the image info.
904 % o exception: return any errors or warnings in this structure.
908 static inline size_t MagickMax(const size_t x,const size_t y)
915 static inline size_t MagickMin(const size_t x,const size_t y)
922 static MagickBooleanType PingGIFImage(Image *image)
929 assert(image != (Image *) NULL);
930 assert(image->signature == MagickSignature);
931 if (image->debug != MagickFalse)
932 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
933 if (ReadBlob(image,1,&data_size) != 1)
934 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
935 if (data_size > MaximumLZWBits)
936 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
937 if (ReadBlob(image,1,&length) != 1)
938 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
941 if (ReadBlob(image,length,buffer) != (ssize_t) length)
942 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
943 if (ReadBlob(image,1,&length) != 1)
944 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
949 static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
951 #define BitSet(byte,bit) (((byte) & (bit)) == (bit))
952 #define LSBFirstOrder(x,y) (((y) << 8) | (x))
958 number_extensionss=0;
972 register unsigned char
983 header[MaxTextExtent],
996 assert(image_info != (const ImageInfo *) NULL);
997 assert(image_info->signature == MagickSignature);
998 if (image_info->debug != MagickFalse)
999 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1000 image_info->filename);
1001 assert(exception != (ExceptionInfo *) NULL);
1002 assert(exception->signature == MagickSignature);
1003 image=AcquireImage(image_info);
1004 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1005 if (status == MagickFalse)
1007 image=DestroyImageList(image);
1008 return((Image *) NULL);
1011 Determine if this a GIF file.
1013 count=ReadBlob(image,6,magick);
1014 if ((count != 6) || ((LocaleNCompare((char *) magick,"GIF87",5) != 0) &&
1015 (LocaleNCompare((char *) magick,"GIF89",5) != 0)))
1016 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1017 page.width=ReadBlobLSBShort(image);
1018 page.height=ReadBlobLSBShort(image);
1019 flag=(unsigned char) ReadBlobByte(image);
1020 background=(unsigned char) ReadBlobByte(image);
1021 c=(unsigned char) ReadBlobByte(image); /* reserved */
1022 global_colors=1UL << (((size_t) flag & 0x07)+1);
1023 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
1024 MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
1025 if (global_colormap == (unsigned char *) NULL)
1026 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1027 if (BitSet((int) flag,0x80) != 0)
1028 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
1036 count=ReadBlob(image,1,&c);
1039 if (c == (unsigned char) ';')
1040 break; /* terminator */
1041 if (c == (unsigned char) '!')
1044 GIF Extension block.
1047 count=ReadBlob(image,1,&c);
1050 global_colormap=(unsigned char *) RelinquishMagickMemory(
1052 ThrowReaderException(CorruptImageError,
1053 "UnableToReadExtensionBlock");
1060 Read graphics control extension.
1062 while (ReadBlobBlock(image,header) != 0) ;
1063 dispose=(size_t) (header[0] >> 2);
1064 delay=(size_t) ((header[2] << 8) | header[1]);
1065 if ((ssize_t) (header[0] & 0x01) == 0x01)
1066 opacity=(ssize_t) header[3];
1075 Read comment extension.
1077 comments=AcquireString((char *) NULL);
1080 count=(ssize_t) ReadBlobBlock(image,header);
1084 (void) ConcatenateString(&comments,(const char *) header);
1086 (void) SetImageProperty(image,"comment",comments);
1087 comments=DestroyString(comments);
1092 /* Read GIF application extension */
1098 Read Netscape Loop extension.
1101 if (ReadBlobBlock(image,header) != 0)
1102 loop=LocaleNCompare((char *) header,"NETSCAPE2.0",11) == 0 ?
1103 MagickTrue : MagickFalse;
1104 if (loop != MagickFalse)
1106 while (ReadBlobBlock(image,header) != 0)
1107 iterations=(size_t) ((header[2] << 8) | header[1]);
1113 name[MaxTextExtent];
1132 Store GIF application extension as a generic profile.
1134 i8bim=LocaleNCompare((char *) header,"MGK8BIM0000",11) == 0 ?
1135 MagickTrue : MagickFalse;
1136 icc=LocaleNCompare((char *) header,"ICCRGBG1012",11) == 0 ?
1137 MagickTrue : MagickFalse;
1138 iptc=LocaleNCompare((char *) header,"MGKIPTC0000",11) == 0 ?
1139 MagickTrue : MagickFalse;
1140 number_extensionss++;
1141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1142 " Reading GIF application extension");
1143 info=(unsigned char *) AcquireQuantumMemory(255UL,
1145 reserved_length=255;
1146 for (info_length=0; ; )
1148 block_length=(int) ReadBlobBlock(image,&info[info_length]);
1149 if (block_length == 0)
1151 info_length+=block_length;
1152 if (info_length > (reserved_length-255))
1154 reserved_length+=4096;
1155 info=(unsigned char *) ResizeQuantumMemory(info,
1156 (size_t) reserved_length,sizeof(*info));
1159 info=(unsigned char *) ResizeQuantumMemory(info,(size_t)
1160 (info_length+1),sizeof(*info));
1161 profile=AcquireStringInfo((size_t) info_length);
1162 SetStringInfoDatum(profile,(const unsigned char *) info);
1163 if (i8bim == MagickTrue)
1164 (void) CopyMagickString(name,"8bim",sizeof(name));
1165 else if (icc == MagickTrue)
1166 (void) CopyMagickString(name,"icc",sizeof(name));
1167 else if (iptc == MagickTrue)
1168 (void) CopyMagickString(name,"iptc",sizeof(name));
1170 (void) FormatMagickString(name,sizeof(name),"gif:%.11s",
1172 (void) SetImageProfile(image,name,profile);
1173 info=(unsigned char *) RelinquishMagickMemory(info);
1174 profile=DestroyStringInfo(profile);
1175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1176 " profile name=%s",name);
1182 while (ReadBlobBlock(image,header) != 0) ;
1187 if (c != (unsigned char) ',')
1189 if (image_count != 0)
1192 Allocate next image structure.
1194 AcquireNextImage(image_info,image);
1195 if (GetNextImageInList(image) == (Image *) NULL)
1197 image=DestroyImageList(image);
1198 global_colormap=(unsigned char *) RelinquishMagickMemory(
1200 return((Image *) NULL);
1202 image=SyncNextImageInList(image);
1206 Read image attributes.
1208 image->storage_class=PseudoClass;
1209 image->compression=LZWCompression;
1210 page.x=(ssize_t) ReadBlobLSBShort(image);
1211 page.y=(ssize_t) ReadBlobLSBShort(image);
1212 image->columns=ReadBlobLSBShort(image);
1213 image->rows=ReadBlobLSBShort(image);
1215 flag=(unsigned char) ReadBlobByte(image);
1216 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace :
1218 image->colors=BitSet((int) flag,0x80) == 0 ? global_colors :
1219 1UL << ((size_t) (flag & 0x07)+1);
1220 if (opacity >= (ssize_t) image->colors)
1222 image->page.width=page.width;
1223 image->page.height=page.height;
1224 image->page.y=page.y;
1225 image->page.x=page.x;
1227 image->ticks_per_second=100;
1228 image->dispose=(DisposeType) dispose;
1229 image->iterations=iterations;
1230 image->matte=opacity >= 0 ? MagickTrue : MagickFalse;
1234 if ((image->columns == 0) || (image->rows == 0))
1236 global_colormap=(unsigned char *) RelinquishMagickMemory(
1238 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1241 Inititialize colormap.
1243 if (AcquireImageColormap(image,image->colors) == MagickFalse)
1245 global_colormap=(unsigned char *) RelinquishMagickMemory(
1247 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1249 if (BitSet((int) flag,0x80) == 0)
1252 Use global colormap.
1255 for (i=0; i < (ssize_t) image->colors; i++)
1257 image->colormap[i].red=ScaleCharToQuantum(*p++);
1258 image->colormap[i].green=ScaleCharToQuantum(*p++);
1259 image->colormap[i].blue=ScaleCharToQuantum(*p++);
1262 image->colormap[i].opacity=(Quantum) TransparentOpacity;
1263 image->transparent_color=image->colormap[opacity];
1266 image->background_color=image->colormap[MagickMin(background,
1275 Read local colormap.
1277 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
1278 3*sizeof(*colormap));
1279 if (colormap == (unsigned char *) NULL)
1281 global_colormap=(unsigned char *) RelinquishMagickMemory(
1283 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1285 count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap);
1286 if (count != (ssize_t) (3*image->colors))
1288 global_colormap=(unsigned char *) RelinquishMagickMemory(
1290 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1291 ThrowReaderException(CorruptImageError,
1292 "InsufficientImageDataInFile");
1295 for (i=0; i < (ssize_t) image->colors; i++)
1297 image->colormap[i].red=ScaleCharToQuantum(*p++);
1298 image->colormap[i].green=ScaleCharToQuantum(*p++);
1299 image->colormap[i].blue=ScaleCharToQuantum(*p++);
1301 image->colormap[i].opacity=(Quantum) TransparentOpacity;
1303 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1305 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1306 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1311 if (image_info->ping != MagickFalse)
1312 status=PingGIFImage(image);
1314 status=DecodeImage(image,opacity);
1315 if ((image_info->ping == MagickFalse) && (status == MagickFalse))
1317 global_colormap=(unsigned char *) RelinquishMagickMemory(
1319 ThrowReaderException(CorruptImageError,"CorruptImage");
1321 if (image_info->number_scenes != 0)
1322 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
1325 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-
1327 if (status == MagickFalse)
1330 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1331 if ((image->columns == 0) || (image->rows == 0))
1332 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
1333 (void) CloseBlob(image);
1334 return(GetFirstImageInList(image));
1338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1342 % R e g i s t e r G I F I m a g e %
1346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1348 % RegisterGIFImage() adds properties for the GIF image format to
1349 % the list of supported formats. The properties include the image format
1350 % tag, a method to read and/or write the format, whether the format
1351 % supports the saving of more than one frame to the same file or blob,
1352 % whether the format supports native in-memory I/O, and a brief
1353 % description of the format.
1355 % The format of the RegisterGIFImage method is:
1357 % size_t RegisterGIFImage(void)
1360 ModuleExport size_t RegisterGIFImage(void)
1365 entry=SetMagickInfo("GIF");
1366 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1367 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1368 entry->magick=(IsImageFormatHandler *) IsGIF;
1369 entry->description=ConstantString("CompuServe graphics interchange format");
1370 entry->module=ConstantString("GIF");
1371 (void) RegisterMagickInfo(entry);
1372 entry=SetMagickInfo("GIF87");
1373 entry->decoder=(DecodeImageHandler *) ReadGIFImage;
1374 entry->encoder=(EncodeImageHandler *) WriteGIFImage;
1375 entry->magick=(IsImageFormatHandler *) IsGIF;
1376 entry->adjoin=MagickFalse;
1377 entry->description=ConstantString("CompuServe graphics interchange format");
1378 entry->version=ConstantString("version 87a");
1379 entry->module=ConstantString("GIF");
1380 (void) RegisterMagickInfo(entry);
1381 return(MagickImageCoderSignature);
1385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1389 % U n r e g i s t e r G I F I m a g e %
1393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395 % UnregisterGIFImage() removes format registrations made by the
1396 % GIF module from the list of supported formats.
1398 % The format of the UnregisterGIFImage method is:
1400 % UnregisterGIFImage(void)
1403 ModuleExport void UnregisterGIFImage(void)
1405 (void) UnregisterMagickInfo("GIF");
1406 (void) UnregisterMagickInfo("GIF87");
1410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1414 % W r i t e G I F I m a g e %
1418 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1420 % WriteGIFImage() writes an image to a file in the Compuserve Graphics
1423 % The format of the WriteGIFImage method is:
1425 % MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image)
1427 % A description of each parameter follows.
1429 % o image_info: the image info.
1431 % o image: The image.
1434 static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image)
1464 register unsigned char
1479 Open output image file.
1481 assert(image_info != (const ImageInfo *) NULL);
1482 assert(image_info->signature == MagickSignature);
1483 assert(image != (Image *) NULL);
1484 assert(image->signature == MagickSignature);
1485 if (image->debug != MagickFalse)
1486 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1487 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1488 if (status == MagickFalse)
1493 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
1494 sizeof(*global_colormap));
1495 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
1496 if ((global_colormap == (unsigned char *) NULL) ||
1497 (colormap == (unsigned char *) NULL))
1498 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1499 for (i=0; i < 768; i++)
1500 colormap[i]=(unsigned char) 0;
1504 write_info=CloneImageInfo(image_info);
1505 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1506 (void) WriteBlob(image,6,(unsigned char *) "GIF89a");
1509 (void) WriteBlob(image,6,(unsigned char *) "GIF87a");
1510 write_info->adjoin=MagickFalse;
1513 Determine image bounding box.
1515 page.width=image->columns;
1516 page.height=image->rows;
1519 if (write_info->adjoin != MagickFalse)
1520 for (next_image=image; next_image != (Image *) NULL; )
1522 page.x=next_image->page.x;
1523 page.y=next_image->page.y;
1524 if ((next_image->page.width+page.x) > page.width)
1525 page.width=next_image->page.width+page.x;
1526 if ((next_image->page.height+page.y) > page.height)
1527 page.height=next_image->page.height+page.y;
1528 next_image=GetNextImageInList(next_image);
1530 page.x=image->page.x;
1531 page.y=image->page.y;
1532 if ((image->page.width != 0) && (image->page.height != 0))
1534 (void) WriteBlobLSBShort(image,(unsigned short) page.width);
1535 (void) WriteBlobLSBShort(image,(unsigned short) page.height);
1537 Write images to file.
1539 interlace=write_info->interlace;
1540 if ((write_info->adjoin != MagickFalse) &&
1541 (GetNextImageInList(image) != (Image *) NULL))
1542 interlace=NoInterlace;
1546 if (image->colorspace != RGBColorspace)
1547 (void) TransformImageColorspace(image,RGBColorspace);
1549 if (IsOpaqueImage(image,&image->exception) != MagickFalse)
1551 if ((image->storage_class == DirectClass) || (image->colors > 256))
1552 (void) SetImageType(image,PaletteType);
1561 Identify transparent colormap index.
1563 if ((image->storage_class == DirectClass) || (image->colors > 256))
1564 (void) SetImageType(image,PaletteBilevelMatteType);
1565 for (i=0; i < (ssize_t) image->colors; i++)
1566 if (image->colormap[i].opacity != OpaqueOpacity)
1573 alpha=(MagickRealType) TransparentOpacity-(MagickRealType)
1574 image->colormap[i].opacity;
1575 beta=(MagickRealType) TransparentOpacity-(MagickRealType)
1576 image->colormap[opacity].opacity;
1582 (void) SetImageType(image,PaletteBilevelMatteType);
1583 for (i=0; i < (ssize_t) image->colors; i++)
1584 if (image->colormap[i].opacity != OpaqueOpacity)
1591 alpha=(Quantum) TransparentOpacity-(MagickRealType)
1592 image->colormap[i].opacity;
1593 beta=(Quantum) TransparentOpacity-(MagickRealType)
1594 image->colormap[opacity].opacity;
1601 image->colormap[opacity].red=image->transparent_color.red;
1602 image->colormap[opacity].green=image->transparent_color.green;
1603 image->colormap[opacity].blue=image->transparent_color.blue;
1606 if ((image->storage_class == DirectClass) || (image->colors > 256))
1607 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1608 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
1609 if ((1UL << bits_per_pixel) >= image->colors)
1612 for (i=0; i < (ssize_t) image->colors; i++)
1614 *q++=ScaleQuantumToChar(image->colormap[i].red);
1615 *q++=ScaleQuantumToChar(image->colormap[i].green);
1616 *q++=ScaleQuantumToChar(image->colormap[i].blue);
1618 for ( ; i < (ssize_t) (1UL << bits_per_pixel); i++)
1620 *q++=(unsigned char) 0x0;
1621 *q++=(unsigned char) 0x0;
1622 *q++=(unsigned char) 0x0;
1624 if ((GetPreviousImageInList(image) == (Image *) NULL) ||
1625 (write_info->adjoin == MagickFalse))
1628 Write global colormap.
1631 c|=(8-1) << 4; /* color resolution */
1632 c|=(bits_per_pixel-1); /* size of global colormap */
1633 (void) WriteBlobByte(image,(unsigned char) c);
1634 for (j=0; j < (ssize_t) image->colors; j++)
1635 if (IsColorEqual(&image->background_color,image->colormap+j))
1637 (void) WriteBlobByte(image,(unsigned char)
1638 (j == (ssize_t) image->colors ? 0 : j)); /* background color */
1639 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
1640 length=(size_t) (3*(1UL << bits_per_pixel));
1641 (void) WriteBlob(image,length,colormap);
1642 for (j=0; j < 768; j++)
1643 global_colormap[j]=colormap[j];
1645 if (LocaleCompare(write_info->magick,"GIF87") != 0)
1648 Write graphics control extension.
1650 (void) WriteBlobByte(image,(unsigned char) 0x21);
1651 (void) WriteBlobByte(image,(unsigned char) 0xf9);
1652 (void) WriteBlobByte(image,(unsigned char) 0x04);
1653 c=image->dispose << 2;
1656 (void) WriteBlobByte(image,(unsigned char) c);
1657 delay=(size_t) (100*image->delay/MagickMax((size_t)
1658 image->ticks_per_second,1));
1659 (void) WriteBlobLSBShort(image,(unsigned short) delay);
1660 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
1662 (void) WriteBlobByte(image,(unsigned char) 0x00);
1663 if ((LocaleCompare(write_info->magick,"GIF87") != 0) &&
1664 (GetImageProperty(image,"comment") != (const char *) NULL))
1676 Write Comment extension.
1678 (void) WriteBlobByte(image,(unsigned char) 0x21);
1679 (void) WriteBlobByte(image,(unsigned char) 0xfe);
1680 value=GetImageProperty(image,"comment");
1682 while (strlen(p) != 0)
1684 count=MagickMin(strlen(p),255);
1685 (void) WriteBlobByte(image,(unsigned char) count);
1686 for (i=0; i < (ssize_t) count; i++)
1687 (void) WriteBlobByte(image,(unsigned char) *p++);
1689 (void) WriteBlobByte(image,(unsigned char) 0x00);
1691 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
1692 (GetNextImageInList(image) != (Image *) NULL) &&
1693 (image->iterations != 1))
1696 Write Netscape Loop extension.
1698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1699 " Writing GIF Extension %s","NETSCAPE2.0");
1700 (void) WriteBlobByte(image,(unsigned char) 0x21);
1701 (void) WriteBlobByte(image,(unsigned char) 0xff);
1702 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1703 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
1704 (void) WriteBlobByte(image,(unsigned char) 0x03);
1705 (void) WriteBlobByte(image,(unsigned char) 0x01);
1706 (void) WriteBlobLSBShort(image,(unsigned short) image->iterations);
1707 (void) WriteBlobByte(image,(unsigned char) 0x00);
1709 ResetImageProfileIterator(image);
1718 name=GetNextImageProfile(image);
1719 if (name == (const char *) NULL)
1721 profile=GetImageProfile(image,name);
1722 if (profile != (StringInfo *) NULL)
1724 if ((LocaleCompare(name,"ICC") == 0) ||
1725 (LocaleCompare(name,"ICM") == 0) ||
1726 (LocaleCompare(name,"IPTC") == 0) ||
1727 (LocaleCompare(name,"8BIM") == 0) ||
1728 (LocaleNCompare(name,"gif:",4) == 0))
1739 datum=GetStringInfoDatum(profile);
1740 length=GetStringInfoLength(profile);
1741 (void) WriteBlobByte(image,(unsigned char) 0x21);
1742 (void) WriteBlobByte(image,(unsigned char) 0xff);
1743 (void) WriteBlobByte(image,(unsigned char) 0x0b);
1744 if ((LocaleCompare(name,"ICC") == 0) ||
1745 (LocaleCompare(name,"ICM") == 0))
1748 Write ICC extension.
1750 (void) WriteBlob(image,11,(unsigned char *)"ICCRGBG1012");
1751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1752 " Writing GIF Extension %s","ICCRGBG1012");
1755 if ((LocaleCompare(name,"IPTC") == 0))
1758 write IPTC extension.
1760 (void) WriteBlob(image,11,(unsigned char *)"MGKIPTC0000");
1761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1762 " Writing GIF Extension %s","MGKIPTC0000");
1765 if ((LocaleCompare(name,"8BIM") == 0))
1768 Write 8BIM extension>
1770 (void) WriteBlob(image,11,(unsigned char *)
1772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1773 " Writing GIF Extension %s","MGK8BIM0000");
1778 extension[MaxTextExtent];
1780 /* write generic extension */
1781 (void) CopyMagickString(extension,name+4,
1783 (void) WriteBlob(image,11,(unsigned char *) extension);
1784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1785 " Writing GIF Extension %s",name);
1788 while ((ssize_t) length > offset)
1793 if ((length-offset) < 255)
1794 block_length=length-offset;
1797 (void) WriteBlobByte(image,(unsigned char) block_length);
1798 (void) WriteBlob(image,(size_t) block_length,datum+offset);
1799 offset+=(ssize_t) block_length;
1801 (void) WriteBlobByte(image,(unsigned char) 0x00);
1806 (void) WriteBlobByte(image,','); /* image separator */
1808 Write the image header.
1810 page.x=image->page.x;
1811 page.y=image->page.y;
1812 if ((image->page.width != 0) && (image->page.height != 0))
1814 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
1815 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
1816 (void) WriteBlobLSBShort(image,(unsigned short) image->columns);
1817 (void) WriteBlobLSBShort(image,(unsigned short) image->rows);
1819 if (interlace != NoInterlace)
1820 c|=0x40; /* pixel data is interlaced */
1821 for (j=0; j < (ssize_t) (3*image->colors); j++)
1822 if (colormap[j] != global_colormap[j])
1824 if (j == (ssize_t) (3*image->colors))
1825 (void) WriteBlobByte(image,(unsigned char) c);
1829 c|=(bits_per_pixel-1); /* size of local colormap */
1830 (void) WriteBlobByte(image,(unsigned char) c);
1831 length=(size_t) (3*(1UL << bits_per_pixel));
1832 (void) WriteBlob(image,length,colormap);
1835 Write the image data.
1837 c=(int) MagickMax(bits_per_pixel,2);
1838 (void) WriteBlobByte(image,(unsigned char) c);
1839 status=EncodeImage(write_info,image,(size_t)
1840 MagickMax(bits_per_pixel,2)+1);
1841 if (status == MagickFalse)
1843 global_colormap=(unsigned char *) RelinquishMagickMemory(
1845 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1846 write_info=DestroyImageInfo(write_info);
1847 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1849 (void) WriteBlobByte(image,(unsigned char) 0x00);
1850 if (GetNextImageInList(image) == (Image *) NULL)
1852 image=SyncNextImageInList(image);
1854 status=SetImageProgress(image,SaveImagesTag,scene,
1855 GetImageListLength(image));
1856 if (status == MagickFalse)
1858 } while (write_info->adjoin != MagickFalse);
1859 (void) WriteBlobByte(image,';'); /* terminator */
1860 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
1861 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1862 write_info=DestroyImageInfo(write_info);
1863 (void) CloseBlob(image);