You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1949 lines
63 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% GGGG IIIII FFFFF %
% G I F %
% G GG I FFF %
% G G I F %
% GGG IIIII F %
% %
% %
% Read/Write Compuserv Graphics Interchange Format %
% %
% Software Design %
% Cristy %
% July 1992 %
% %
% %
% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% obtain a copy of the License at %
% %
% https://imagemagick.org/script/license.php %
% %
% Unless required by applicable law or agreed to in writing, software %
% distributed under the License is distributed on an "AS IS" BASIS, %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
% See the License for the specific language governing permissions and %
% limitations under the License. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/
/*
Include declarations.
*/
#include "MagickCore/studio.h"
#include "MagickCore/attribute.h"
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/color.h"
#include "MagickCore/color-private.h"
#include "MagickCore/colormap.h"
#include "MagickCore/colormap-private.h"
#include "MagickCore/colorspace.h"
#include "MagickCore/colorspace-private.h"
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
#include "MagickCore/list.h"
#include "MagickCore/profile.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/option.h"
#include "MagickCore/pixel.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/property.h"
#include "MagickCore/quantize.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/resource_.h"
#include "MagickCore/static.h"
#include "MagickCore/string_.h"
#include "MagickCore/string-private.h"
#include "MagickCore/module.h"
/*
Define declarations.
*/
#define MaximumLZWBits 12
#define MaximumLZWCode (1UL << MaximumLZWBits)
/*
Typdef declarations.
*/
typedef struct _LZWCodeInfo
{
unsigned char
buffer[280];
size_t
count,
bit;
MagickBooleanType
eof;
} LZWCodeInfo;
typedef struct _LZWStack
{
size_t
*codes,
*index,
*top;
} LZWStack;
typedef struct _LZWInfo
{
Image
*image;
LZWStack
*stack;
MagickBooleanType
genesis;
size_t
data_size,
maximum_data_value,
clear_code,
end_code,
bits,
first_code,
last_code,
maximum_code,
slot,
*table[2];
LZWCodeInfo
code_info;
} LZWInfo;
/*
Forward declarations.
*/
static inline int
GetNextLZWCode(LZWInfo *,const size_t);
static MagickBooleanType
WriteGIFImage(const ImageInfo *,Image *,ExceptionInfo *);
static ssize_t
ReadBlobBlock(Image *,unsigned char *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% D e c o d e I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DecodeImage uncompresses an image via GIF-coding.
%
% The format of the DecodeImage method is:
%
% MagickBooleanType DecodeImage(Image *image,const ssize_t opacity)
%
% A description of each parameter follows:
%
% o image: the address of a structure of type Image.
%
% o opacity: The colormap index associated with the transparent color.
%
*/
static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info)
{
if (lzw_info->table[0] != (size_t *) NULL)
lzw_info->table[0]=(size_t *) RelinquishMagickMemory(
lzw_info->table[0]);
if (lzw_info->table[1] != (size_t *) NULL)
lzw_info->table[1]=(size_t *) RelinquishMagickMemory(
lzw_info->table[1]);
if (lzw_info->stack != (LZWStack *) NULL)
{
if (lzw_info->stack->codes != (size_t *) NULL)
lzw_info->stack->codes=(size_t *) RelinquishMagickMemory(
lzw_info->stack->codes);
lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack);
}
lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info);
return((LZWInfo *) NULL);
}
static inline void ResetLZWInfo(LZWInfo *lzw_info)
{
size_t
one;
lzw_info->bits=lzw_info->data_size+1;
one=1;
lzw_info->maximum_code=one << lzw_info->bits;
lzw_info->slot=lzw_info->maximum_data_value+3;
lzw_info->genesis=MagickTrue;
}
static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size)
{
LZWInfo
*lzw_info;
ssize_t
i;
size_t
one;
lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info));
if (lzw_info == (LZWInfo *) NULL)
return((LZWInfo *) NULL);
(void) memset(lzw_info,0,sizeof(*lzw_info));
lzw_info->image=image;
lzw_info->data_size=data_size;
one=1;
lzw_info->maximum_data_value=(one << data_size)-1;
lzw_info->clear_code=lzw_info->maximum_data_value+1;
lzw_info->end_code=lzw_info->maximum_data_value+2;
lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
sizeof(**lzw_info->table));
lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode,
sizeof(**lzw_info->table));
if ((lzw_info->table[0] == (size_t *) NULL) ||
(lzw_info->table[1] == (size_t *) NULL))
{
lzw_info=RelinquishLZWInfo(lzw_info);
return((LZWInfo *) NULL);
}
(void) memset(lzw_info->table[0],0,MaximumLZWCode*
sizeof(**lzw_info->table));
(void) memset(lzw_info->table[1],0,MaximumLZWCode*
sizeof(**lzw_info->table));
for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++)
{
lzw_info->table[0][i]=0;
lzw_info->table[1][i]=(size_t) i;
}
ResetLZWInfo(lzw_info);
lzw_info->code_info.buffer[0]='\0';
lzw_info->code_info.buffer[1]='\0';
lzw_info->code_info.count=2;
lzw_info->code_info.bit=8*lzw_info->code_info.count;
lzw_info->code_info.eof=MagickFalse;
lzw_info->genesis=MagickTrue;
lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack));
if (lzw_info->stack == (LZWStack *) NULL)
{
lzw_info=RelinquishLZWInfo(lzw_info);
return((LZWInfo *) NULL);
}
lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL*
MaximumLZWCode,sizeof(*lzw_info->stack->codes));
if (lzw_info->stack->codes == (size_t *) NULL)
{
lzw_info=RelinquishLZWInfo(lzw_info);
return((LZWInfo *) NULL);
}
lzw_info->stack->index=lzw_info->stack->codes;
lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode;
return(lzw_info);
}
static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits)
{
int
code;
ssize_t
i;
size_t
one;
while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) &&
(lzw_info->code_info.eof == MagickFalse))
{
ssize_t
count;
lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[
lzw_info->code_info.count-2];
lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[
lzw_info->code_info.count-1];
lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2);
lzw_info->code_info.count=2;
count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[
lzw_info->code_info.count]);
if (count > 0)
lzw_info->code_info.count+=count;
else
lzw_info->code_info.eof=MagickTrue;
}
if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count))
return(-1);
code=0;
one=1;
for (i=0; i < (ssize_t) bits; i++)
{
code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] &
(one << (lzw_info->code_info.bit % 8))) != 0) << i;
lzw_info->code_info.bit++;
}
return(code);
}
static inline int PopLZWStack(LZWStack *stack_info)
{
if (stack_info->index <= stack_info->codes)
return(-1);
stack_info->index--;
return((int) *stack_info->index);
}
static inline void PushLZWStack(LZWStack *stack_info,const size_t value)
{
if (stack_info->index >= stack_info->top)
return;
*stack_info->index=value;
stack_info->index++;
}
static int ReadBlobLZWByte(LZWInfo *lzw_info)
{
int
code;
size_t
one,
value;
ssize_t
count;
if (lzw_info->stack->index != lzw_info->stack->codes)
return(PopLZWStack(lzw_info->stack));
if (lzw_info->genesis != MagickFalse)
{
lzw_info->genesis=MagickFalse;
do
{
lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,lzw_info->bits);
lzw_info->last_code=lzw_info->first_code;
} while (lzw_info->first_code == lzw_info->clear_code);
return((int) lzw_info->first_code);
}
code=GetNextLZWCode(lzw_info,lzw_info->bits);
if (code < 0)
return(code);
if ((size_t) code == lzw_info->clear_code)
{
ResetLZWInfo(lzw_info);
return(ReadBlobLZWByte(lzw_info));
}
if ((size_t) code == lzw_info->end_code)
return(-1);
if ((size_t) code < lzw_info->slot)
value=(size_t) code;
else
{
PushLZWStack(lzw_info->stack,lzw_info->first_code);
value=lzw_info->last_code;
}
count=0;
while (value > lzw_info->maximum_data_value)
{
if ((size_t) count > MaximumLZWCode)
return(-1);
count++;
if ((size_t) value > MaximumLZWCode)
return(-1);
PushLZWStack(lzw_info->stack,lzw_info->table[1][value]);
value=lzw_info->table[0][value];
}
lzw_info->first_code=lzw_info->table[1][value];
PushLZWStack(lzw_info->stack,lzw_info->first_code);
one=1;
if (lzw_info->slot < MaximumLZWCode)
{
lzw_info->table[0][lzw_info->slot]=lzw_info->last_code;
lzw_info->table[1][lzw_info->slot]=lzw_info->first_code;
lzw_info->slot++;
if ((lzw_info->slot >= lzw_info->maximum_code) &&
(lzw_info->bits < MaximumLZWBits))
{
lzw_info->bits++;
lzw_info->maximum_code=one << lzw_info->bits;
}
}
lzw_info->last_code=(size_t) code;
return(PopLZWStack(lzw_info->stack));
}
static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity,
ExceptionInfo *exception)
{
int
c;
LZWInfo
*lzw_info;
size_t
pass;
ssize_t
index,
offset,
y;
unsigned char
data_size;
/*
Allocate decoder tables.
*/
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
data_size=(unsigned char) ReadBlobByte(image);
if (data_size > MaximumLZWBits)
ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
lzw_info=AcquireLZWInfo(image,data_size);
if (lzw_info == (LZWInfo *) NULL)
ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
image->filename);
pass=0;
offset=0;
for (y=0; y < (ssize_t) image->rows; y++)
{
ssize_t
x;
Quantum
*magick_restrict q;
q=QueueAuthenticPixels(image,0,offset,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; )
{
c=ReadBlobLZWByte(lzw_info);
if (c < 0)
break;
index=ConstrainColormapIndex(image,(ssize_t) c,exception);
SetPixelIndex(image,(Quantum) index,q);
SetPixelViaPixelInfo(image,image->colormap+index,q);
SetPixelAlpha(image,index == opacity ? TransparentAlpha : OpaqueAlpha,q);
x++;
q+=GetPixelChannels(image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (x < (ssize_t) image->columns)
break;
if (image->interlace == NoInterlace)
offset++;
else
{
switch (pass)
{
case 0:
default:
{
offset+=8;
break;
}
case 1:
{
offset+=8;
break;
}
case 2:
{
offset+=4;
break;
}
case 3:
{
offset+=2;
break;
}
}
if ((pass == 0) && (offset >= (ssize_t) image->rows))
{
pass++;
offset=4;
}
if ((pass == 1) && (offset >= (ssize_t) image->rows))
{
pass++;
offset=2;
}
if ((pass == 2) && (offset >= (ssize_t) image->rows))
{
pass++;
offset=1;
}
}
}
lzw_info=RelinquishLZWInfo(lzw_info);
if (y < (ssize_t) image->rows)
ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
return(MagickTrue);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% E n c o d e I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% EncodeImage compresses an image via GIF-coding.
%
% The format of the EncodeImage method is:
%
% MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
% const size_t data_size)
%
% A description of each parameter follows:
%
% o image_info: the image info.
%
% o image: the address of a structure of type Image.
%
% o data_size: The number of bits in the compressed packet.
%
*/
static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image,
const size_t data_size,ExceptionInfo *exception)
{
#define MaxCode(number_bits) ((one << (number_bits))-1)
#define MaxHashTable 5003
#define MaxGIFBits 12UL
#define MaxGIFTable (1UL << MaxGIFBits)
#define GIFOutputCode(code) \
{ \
/* \
Emit a code. \
*/ \
if (bits > 0) \
datum|=(size_t) (code) << bits; \
else \
datum=(size_t) (code); \
bits+=number_bits; \
while (bits >= 8) \
{ \
/* \
Add a character to current packet. Maximum packet size is 255.
*/ \
packet[length++]=(unsigned char) (datum & 0xff); \
if (length == 255) \
{ \
(void) WriteBlobByte(image,(unsigned char) length); \
(void) WriteBlob(image,length,packet); \
length=0; \
} \
datum>>=8; \
bits-=8; \
} \
if (free_code > max_code) \
{ \
number_bits++; \
if (number_bits == MaxGIFBits) \
max_code=MaxGIFTable; \
else \
max_code=MaxCode(number_bits); \
} \
}
Quantum
index;
short
*hash_code,
*hash_prefix,
waiting_code;
size_t
bits,
clear_code,
datum,
end_of_information_code,
free_code,
length,
max_code,
next_pixel,
number_bits,
one,
pass;
ssize_t
displacement,
offset,
k,
y;
unsigned char
*packet,
*hash_suffix;
/*
Allocate encoder tables.
*/
assert(image != (Image *) NULL);
one=1;
packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet));
hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code));
hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix));
hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable,
sizeof(*hash_suffix));
if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
(hash_prefix == (short *) NULL) ||
(hash_suffix == (unsigned char *) NULL))
{
if (packet != (unsigned char *) NULL)
packet=(unsigned char *) RelinquishMagickMemory(packet);
if (hash_code != (short *) NULL)
hash_code=(short *) RelinquishMagickMemory(hash_code);
if (hash_prefix != (short *) NULL)
hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
if (hash_suffix != (unsigned char *) NULL)
hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
return(MagickFalse);
}
/*
Initialize GIF encoder.
*/
(void) memset(packet,0,256*sizeof(*packet));
(void) memset(hash_code,0,MaxHashTable*sizeof(*hash_code));
(void) memset(hash_prefix,0,MaxHashTable*sizeof(*hash_prefix));
(void) memset(hash_suffix,0,MaxHashTable*sizeof(*hash_suffix));
number_bits=data_size;
max_code=MaxCode(number_bits);
clear_code=((short) one << (data_size-1));
end_of_information_code=clear_code+1;
free_code=clear_code+2;
length=0;
datum=0;
bits=0;
GIFOutputCode(clear_code);
/*
Encode pixels.
*/
offset=0;
pass=0;
waiting_code=0;
for (y=0; y < (ssize_t) image->rows; y++)
{
const Quantum
*magick_restrict p;
ssize_t
x;
p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
if (y == 0)
{
waiting_code=(short) GetPixelIndex(image,p);
p+=GetPixelChannels(image);
}
for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++)
{
/*
Probe hash table.
*/
next_pixel=MagickFalse;
displacement=1;
index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff);
p+=GetPixelChannels(image);
k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code);
if (k >= MaxHashTable)
k-=MaxHashTable;
if (k < 0)
continue;
if (hash_code[k] > 0)
{
if ((hash_prefix[k] == waiting_code) &&
(hash_suffix[k] == (unsigned char) index))
{
waiting_code=hash_code[k];
continue;
}
if (k != 0)
displacement=MaxHashTable-k;
for ( ; ; )
{
k-=displacement;
if (k < 0)
k+=MaxHashTable;
if (hash_code[k] == 0)
break;
if ((hash_prefix[k] == waiting_code) &&
(hash_suffix[k] == (unsigned char) index))
{
waiting_code=hash_code[k];
next_pixel=MagickTrue;
break;
}
}
if (next_pixel != MagickFalse)
continue;
}
GIFOutputCode(waiting_code);
if (free_code < MaxGIFTable)
{
hash_code[k]=(short) free_code++;
hash_prefix[k]=waiting_code;
hash_suffix[k]=(unsigned char) index;
}
else
{
/*
Fill the hash table with empty entries.
*/
for (k=0; k < MaxHashTable; k++)
hash_code[k]=0;
/*
Reset compressor and issue a clear code.
*/
free_code=clear_code+2;
GIFOutputCode(clear_code);
number_bits=data_size;
max_code=MaxCode(number_bits);
}
waiting_code=(short) index;
}
if (image_info->interlace == NoInterlace)
offset++;
else
switch (pass)
{
case 0:
default:
{
offset+=8;
if (offset >= (ssize_t) image->rows)
{
pass++;
offset=4;
}
break;
}
case 1:
{
offset+=8;
if (offset >= (ssize_t) image->rows)
{
pass++;
offset=2;
}
break;
}
case 2:
{
offset+=4;
if (offset >= (ssize_t) image->rows)
{
pass++;
offset=1;
}
break;
}
case 3:
{
offset+=2;
break;
}
}
}
/*
Flush out the buffered code.
*/
GIFOutputCode(waiting_code);
GIFOutputCode(end_of_information_code);
if (bits > 0)
{
/*
Add a character to current packet. Maximum packet size is 255.
*/
packet[length++]=(unsigned char) (datum & 0xff);
if (length == 255)
{
(void) WriteBlobByte(image,(unsigned char) length);
(void) WriteBlob(image,length,packet);
length=0;
}
}
/*
Flush accumulated data.
*/
if (length > 0)
{
(void) WriteBlobByte(image,(unsigned char) length);
(void) WriteBlob(image,length,packet);
}
/*
Free encoder memory.
*/
hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix);
hash_prefix=(short *) RelinquishMagickMemory(hash_prefix);
hash_code=(short *) RelinquishMagickMemory(hash_code);
packet=(unsigned char *) RelinquishMagickMemory(packet);
return(MagickTrue);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s G I F %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsGIF() returns MagickTrue if the image format type, identified by the
% magick string, is GIF.
%
% The format of the IsGIF method is:
%
% MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o magick: compare image format pattern against these bytes.
%
% o length: Specifies the length of the magick string.
%
*/
static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length)
{
if (length < 4)
return(MagickFalse);
if (LocaleNCompare((char *) magick,"GIF8",4) == 0)
return(MagickTrue);
return(MagickFalse);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
+ R e a d B l o b B l o c k %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ReadBlobBlock() reads data from the image file and returns it. The
% amount of data is determined by first reading a count byte. The number
% of bytes read is returned.
%
% The format of the ReadBlobBlock method is:
%
% ssize_t ReadBlobBlock(Image *image,unsigned char *data)
%
% A description of each parameter follows:
%
% o image: the image.
%
% o data: Specifies an area to place the information requested from
% the file.
%
*/
static ssize_t ReadBlobBlock(Image *image,unsigned char *data)
{
ssize_t
count;
unsigned char
block_count;
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
assert(data != (unsigned char *) NULL);
count=ReadBlob(image,1,&block_count);
if (count != 1)
return(0);
count=ReadBlob(image,(size_t) block_count,data);
if (count != (ssize_t) block_count)
return(0);
return(count);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d G I F I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ReadGIFImage() reads a Compuserve Graphics image file and returns it.
% It allocates the memory necessary for the new Image structure and returns a
% pointer to the new image.
%
% The format of the ReadGIFImage method is:
%
% Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image_info: the image info.
%
% o exception: return any errors or warnings in this structure.
%
*/
static void *DestroyGIFProfile(void *profile)
{
return((void *) DestroyStringInfo((StringInfo *) profile));
}
static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception)
{
unsigned char
buffer[256],
length,
data_size;
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
if (ReadBlob(image,1,&data_size) != 1)
ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
if (data_size > MaximumLZWBits)
ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
if (ReadBlob(image,1,&length) != 1)
ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
while (length != 0)
{
if (ReadBlob(image,length,buffer) != (ssize_t) length)
ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
if (ReadBlob(image,1,&length) != 1)
ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename);
}
return(MagickTrue);
}
static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define BitSet(byte,bit) (((byte) & (bit)) == (bit))
#define LSBFirstOrder(x,y) (((y) << 8) | (x))
#define ThrowGIFException(exception,message) \
{ \
if (profiles != (LinkedListInfo *) NULL) \
profiles=DestroyLinkedList(profiles,DestroyGIFProfile); \
if (global_colormap != (unsigned char *) NULL) \
global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap); \
if (meta_image != (Image *) NULL) \
meta_image=DestroyImage(meta_image); \
ThrowReaderException((exception),(message)); \
}
Image
*image,
*meta_image;
LinkedListInfo
*profiles;
MagickBooleanType
status;
ssize_t
i;
unsigned char
*p;
size_t
duration,
global_colors,
image_count,
local_colors,
one;
ssize_t
count,
opacity;
unsigned char
background,
buffer[257],
c,
flag,
*global_colormap;
/*
Open image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
if (image_info->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
image_info->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
image=AcquireImage(image_info,exception);
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == MagickFalse)
{
image=DestroyImageList(image);
return((Image *) NULL);
}
/*
Determine if this a GIF file.
*/
count=ReadBlob(image,6,buffer);
if ((count != 6) || ((LocaleNCompare((char *) buffer,"GIF87",5) != 0) &&
(LocaleNCompare((char *) buffer,"GIF89",5) != 0)))
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
(void) memset(buffer,0,sizeof(buffer));
meta_image=AcquireImage(image_info,exception); /* metadata container */
meta_image->page.width=ReadBlobLSBShort(image);
meta_image->page.height=ReadBlobLSBShort(image);
meta_image->iterations=1;
flag=(unsigned char) ReadBlobByte(image);
profiles=(LinkedListInfo *) NULL;
background=(unsigned char) ReadBlobByte(image);
c=(unsigned char) ReadBlobByte(image); /* reserved */
one=1;
global_colors=one << (((size_t) flag & 0x07)+1);
global_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
MagickMax(global_colors,256),3UL*sizeof(*global_colormap));
if (global_colormap == (unsigned char *) NULL)
ThrowGIFException(ResourceLimitError,"MemoryAllocationFailed");
(void) memset(global_colormap,0,3*MagickMax(global_colors,256)*
sizeof(*global_colormap));
if (BitSet((int) flag,0x80) != 0)
{
count=ReadBlob(image,(size_t) (3*global_colors),global_colormap);
if (count != (ssize_t) (3*global_colors))
ThrowGIFException(CorruptImageError,"InsufficientImageDataInFile");
}
duration=0;
opacity=(-1);
image_count=0;
for ( ; ; )
{
count=ReadBlob(image,1,&c);
if (count != 1)
break;
if (c == (unsigned char) ';')
break; /* terminator */
if (c == (unsigned char) '!')
{
/*
GIF Extension block.
*/
(void) memset(buffer,0,sizeof(buffer));
count=ReadBlob(image,1,&c);
if (count != 1)
ThrowGIFException(CorruptImageError,"UnableToReadExtensionBlock");
switch (c)
{
case 0xf9:
{
/*
Read graphics control extension.
*/
while (ReadBlobBlock(image,buffer) != 0) ;
meta_image->dispose=(DisposeType) ((buffer[0] >> 2) & 0x07);
meta_image->delay=((size_t) buffer[2] << 8) | buffer[1];
if ((ssize_t) (buffer[0] & 0x01) == 0x01)
opacity=(ssize_t) buffer[3];
break;
}
case 0xfe:
{
char
*comments;
size_t
extent,
offset;
comments=AcquireString((char *) NULL);
extent=MagickPathExtent;
for (offset=0; ; offset+=count)
{
count=ReadBlobBlock(image,buffer);
if (count == 0)
break;
buffer[count]='\0';
if ((ssize_t) (count+offset+MagickPathExtent) >= (ssize_t) extent)
{
extent<<=1;
comments=(char *) ResizeQuantumMemory(comments,extent+
MagickPathExtent,sizeof(*comments));
if (comments == (char *) NULL)
ThrowGIFException(ResourceLimitError,
"MemoryAllocationFailed");
}
(void) CopyMagickString(&comments[offset],(char *) buffer,extent-
offset);
}
(void) SetImageProperty(meta_image,"comment",comments,exception);
comments=DestroyString(comments);
break;
}
case 0xff:
{
MagickBooleanType
loop;
/*
Read Netscape Loop extension.
*/
loop=MagickFalse;
if (ReadBlobBlock(image,buffer) != 0)
loop=LocaleNCompare((char *) buffer,"NETSCAPE2.0",11) == 0 ?
MagickTrue : MagickFalse;
if (loop != MagickFalse)
while (ReadBlobBlock(image,buffer) != 0)
{
meta_image->iterations=((size_t) buffer[2] << 8) | buffer[1];
if (meta_image->iterations != 0)
meta_image->iterations++;
}
else
{
char
name[MagickPathExtent];
int
block_length,
info_length,
reserved_length;
MagickBooleanType
i8bim,
icc,
iptc,
magick;
StringInfo
*profile;
unsigned char
*info;
/*
Store GIF application extension as a generic profile.
*/
icc=LocaleNCompare((char *) buffer,"ICCRGBG1012",11) == 0 ?
MagickTrue : MagickFalse;
magick=LocaleNCompare((char *) buffer,"ImageMagick",11) == 0 ?
MagickTrue : MagickFalse;
i8bim=LocaleNCompare((char *) buffer,"MGK8BIM0000",11) == 0 ?
MagickTrue : MagickFalse;
iptc=LocaleNCompare((char *) buffer,"MGKIPTC0000",11) == 0 ?
MagickTrue : MagickFalse;
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Reading GIF application extension");
reserved_length=255;
info=(unsigned char *) AcquireQuantumMemory((size_t)
reserved_length,sizeof(*info));
if (info == (unsigned char *) NULL)
ThrowGIFException(ResourceLimitError,
"MemoryAllocationFailed");
(void) memset(info,0,reserved_length*sizeof(*info));
for (info_length=0; ; )
{
block_length=(int) ReadBlobBlock(image,info+info_length);
if (block_length == 0)
break;
info_length+=block_length;
if (info_length > (reserved_length-255))
{
reserved_length+=4096;
info=(unsigned char *) ResizeQuantumMemory(info,(size_t)
reserved_length,sizeof(*info));
if (info == (unsigned char *) NULL)
{
info=(unsigned char *) RelinquishMagickMemory(info);
ThrowGIFException(ResourceLimitError,
"MemoryAllocationFailed");
}
}
}
profile=BlobToStringInfo(info,(size_t) info_length);
if (profile == (StringInfo *) NULL)
{
info=(unsigned char *) RelinquishMagickMemory(info);
ThrowGIFException(ResourceLimitError,
"MemoryAllocationFailed");
}
if (i8bim != MagickFalse)
(void) CopyMagickString(name,"8bim",sizeof(name));
else if (icc != MagickFalse)
(void) CopyMagickString(name,"icc",sizeof(name));
else if (iptc != MagickFalse)
(void) CopyMagickString(name,"iptc",sizeof(name));
else if (magick != MagickFalse)
{
(void) CopyMagickString(name,"magick",sizeof(name));
meta_image->gamma=StringToDouble((char *) info+6,
(char **) NULL);
}
else
(void) FormatLocaleString(name,sizeof(name),"gif:%.11s",
buffer);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" profile name=%s",name);
info=(unsigned char *) RelinquishMagickMemory(info);
if (magick != MagickFalse)
profile=DestroyStringInfo(profile);
else
{
if (profiles == (LinkedListInfo *) NULL)
profiles=NewLinkedList(0);
SetStringInfoName(profile,name);
(void) AppendValueToLinkedList(profiles,profile);
}
}
break;
}
default:
{
while (ReadBlobBlock(image,buffer) != 0) ;
break;
}
}
}
if (c != (unsigned char) ',')
continue;
image_count++;
if (image_count != 1)
{
/*
Allocate next image structure.
*/
AcquireNextImage(image_info,image,exception);
if (GetNextImageInList(image) == (Image *) NULL)
{
status=MagickFalse;
break;
}
image=SyncNextImageInList(image);
}
/*
Read image attributes.
*/
meta_image->page.x=(ssize_t) ReadBlobLSBShort(image);
meta_image->page.y=(ssize_t) ReadBlobLSBShort(image);
meta_image->scene=image->scene;
(void) CloneImageProperties(image,meta_image);
DestroyImageProperties(meta_image);
image->storage_class=PseudoClass;
image->compression=LZWCompression;
image->columns=ReadBlobLSBShort(image);
image->rows=ReadBlobLSBShort(image);
image->depth=8;
flag=(unsigned char) ReadBlobByte(image);
image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace;
local_colors=BitSet((int) flag,0x80) == 0 ? global_colors : one <<
((size_t) (flag & 0x07)+1);
image->colors=local_colors;
if (opacity == (ssize_t) image->colors)
image->colors++;
else if (opacity > (ssize_t) image->colors)
opacity=(-1);
image->ticks_per_second=100;
image->alpha_trait=opacity >= 0 ? BlendPixelTrait : UndefinedPixelTrait;
if ((image->columns == 0) || (image->rows == 0))
ThrowGIFException(CorruptImageError,"NegativeOrZeroImageSize");
/*
Inititialize colormap.
*/
if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
ThrowGIFException(ResourceLimitError,"MemoryAllocationFailed");
if (BitSet((int) flag,0x80) == 0)
{
/*
Use global colormap.
*/
p=global_colormap;
for (i=0; i < (ssize_t) image->colors; i++)
{
image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
if (i == opacity)
{
image->colormap[i].alpha=(double) TransparentAlpha;
image->transparent_color=image->colormap[opacity];
}
}
image->background_color=image->colormap[MagickMin((ssize_t) background,
(ssize_t) image->colors-1)];
}
else
{
unsigned char
*colormap;
/*
Read local colormap.
*/
colormap=(unsigned char *) AcquireQuantumMemory((size_t)
MagickMax(local_colors,256),3UL*sizeof(*colormap));
if (colormap == (unsigned char *) NULL)
ThrowGIFException(ResourceLimitError,"MemoryAllocationFailed");
(void) memset(colormap,0,3*MagickMax(local_colors,256)*
sizeof(*colormap));
count=ReadBlob(image,(3*local_colors)*sizeof(*colormap),colormap);
if (count != (ssize_t) (3*local_colors))
{
colormap=(unsigned char *) RelinquishMagickMemory(colormap);
ThrowGIFException(CorruptImageError,"InsufficientImageDataInFile");
}
p=colormap;
for (i=0; i < (ssize_t) image->colors; i++)
{
image->colormap[i].red=(double) ScaleCharToQuantum(*p++);
image->colormap[i].green=(double) ScaleCharToQuantum(*p++);
image->colormap[i].blue=(double) ScaleCharToQuantum(*p++);
if (i == opacity)
image->colormap[i].alpha=(double) TransparentAlpha;
}
colormap=(unsigned char *) RelinquishMagickMemory(colormap);
}
if (image->gamma == 1.0)
{
for (i=0; i < (ssize_t) image->colors; i++)
if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
break;
(void) SetImageColorspace(image,i == (ssize_t) image->colors ?
GRAYColorspace : RGBColorspace,exception);
}
if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
if (image->scene >= (image_info->scene+image_info->number_scenes-1))
break;
status=SetImageExtent(image,image->columns,image->rows,exception);
if (status == MagickFalse)
{
if (profiles != (LinkedListInfo *) NULL)
profiles=DestroyLinkedList(profiles,DestroyGIFProfile);
global_colormap=(unsigned char *) RelinquishMagickMemory(
global_colormap);
meta_image=DestroyImage(meta_image);
return(DestroyImageList(image));
}
/*
Decode image.
*/
if (image_info->ping != MagickFalse)
status=PingGIFImage(image,exception);
else
status=DecodeImage(image,opacity,exception);
if ((image_info->ping == MagickFalse) && (status == MagickFalse))
ThrowGIFException(CorruptImageError,"CorruptImage");
if (profiles != (LinkedListInfo *) NULL)
{
StringInfo
*profile;
/*
Set image profiles.
*/
ResetLinkedListIterator(profiles);
profile=(StringInfo *) GetNextValueInLinkedList(profiles);
while (profile != (StringInfo *) NULL)
{
(void) SetImageProfile(image,GetStringInfoName(profile),profile,
exception);
profile=(StringInfo *) GetNextValueInLinkedList(profiles);
}
profiles=DestroyLinkedList(profiles,DestroyGIFProfile);
}
duration+=image->delay*image->iterations;
if (image_info->number_scenes != 0)
if (image->scene >= (image_info->scene+image_info->number_scenes-1))
break;
opacity=(-1);
status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
image->scene-1,image->scene);
if (status == MagickFalse)
break;
}
image->duration=duration;
if (profiles != (LinkedListInfo *) NULL)
profiles=DestroyLinkedList(profiles,DestroyGIFProfile);
meta_image=DestroyImage(meta_image);
global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
if ((image->columns == 0) || (image->rows == 0))
ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
(void) CloseBlob(image);
if (status == MagickFalse)
return(DestroyImageList(image));
return(GetFirstImageInList(image));
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r G I F I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% RegisterGIFImage() adds properties for the GIF image format to
% the list of supported formats. The properties include the image format
% tag, a method to read and/or write the format, whether the format
% supports the saving of more than one frame to the same file or blob,
% whether the format supports native in-memory I/O, and a brief
% description of the format.
%
% The format of the RegisterGIFImage method is:
%
% size_t RegisterGIFImage(void)
%
*/
ModuleExport size_t RegisterGIFImage(void)
{
MagickInfo
*entry;
entry=AcquireMagickInfo("GIF","GIF",
"CompuServe graphics interchange format");
entry->decoder=(DecodeImageHandler *) ReadGIFImage;
entry->encoder=(EncodeImageHandler *) WriteGIFImage;
entry->magick=(IsImageFormatHandler *) IsGIF;
entry->mime_type=ConstantString("image/gif");
(void) RegisterMagickInfo(entry);
entry=AcquireMagickInfo("GIF","GIF87",
"CompuServe graphics interchange format");
entry->decoder=(DecodeImageHandler *) ReadGIFImage;
entry->encoder=(EncodeImageHandler *) WriteGIFImage;
entry->magick=(IsImageFormatHandler *) IsGIF;
entry->flags^=CoderAdjoinFlag;
entry->version=ConstantString("version 87a");
entry->mime_type=ConstantString("image/gif");
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r G I F I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% UnregisterGIFImage() removes format registrations made by the
% GIF module from the list of supported formats.
%
% The format of the UnregisterGIFImage method is:
%
% UnregisterGIFImage(void)
%
*/
ModuleExport void UnregisterGIFImage(void)
{
(void) UnregisterMagickInfo("GIF");
(void) UnregisterMagickInfo("GIF87");
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W r i t e G I F I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% WriteGIFImage() writes an image to a file in the Compuserve Graphics
% image format.
%
% The format of the WriteGIFImage method is:
%
% MagickBooleanType WriteGIFImage(const ImageInfo *image_info,
% Image *image,ExceptionInfo *exception)
%
% A description of each parameter follows.
%
% o image_info: the image info.
%
% o image: The image.
%
% o exception: return any errors or warnings in this structure.
%
*/
static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image,
ExceptionInfo *exception)
{
int
c;
ImageInfo
*write_info;
MagickBooleanType
status;
MagickOffsetType
scene;
RectangleInfo
page;
ssize_t
i;
unsigned char
*q;
size_t
bits_per_pixel,
delay,
imageListLength,
length,
one;
ssize_t
j,
opacity;
unsigned char
*colormap,
*global_colormap;
/*
Open output image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
if (status == MagickFalse)
return(status);
/*
Allocate colormap.
*/
global_colormap=(unsigned char *) AcquireQuantumMemory(768UL,
sizeof(*global_colormap));
colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap));
if ((global_colormap == (unsigned char *) NULL) ||
(colormap == (unsigned char *) NULL))
{
if (global_colormap != (unsigned char *) NULL)
global_colormap=(unsigned char *) RelinquishMagickMemory(
global_colormap);
if (colormap != (unsigned char *) NULL)
colormap=(unsigned char *) RelinquishMagickMemory(colormap);
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
}
for (i=0; i < 768; i++)
colormap[i]=(unsigned char) 0;
/*
Write GIF header.
*/
write_info=CloneImageInfo(image_info);
if (LocaleCompare(write_info->magick,"GIF87") != 0)
(void) WriteBlob(image,6,(unsigned char *) "GIF89a");
else
{
(void) WriteBlob(image,6,(unsigned char *) "GIF87a");
write_info->adjoin=MagickFalse;
}
/*
Determine image bounding box.
*/
page.width=image->columns;
if (image->page.width > page.width)
page.width=image->page.width;
page.height=image->rows;
if (image->page.height > page.height)
page.height=image->page.height;
page.x=image->page.x;
page.y=image->page.y;
(void) WriteBlobLSBShort(image,(unsigned short) page.width);
(void) WriteBlobLSBShort(image,(unsigned short) page.height);
/*
Write images to file.
*/
scene=0;
one=1;
imageListLength=GetImageListLength(image);
do
{
(void) TransformImageColorspace(image,sRGBColorspace,exception);
opacity=(-1);
if (IsImageOpaque(image,exception) != MagickFalse)
{
if ((image->storage_class == DirectClass) || (image->colors > 256))
(void) SetImageType(image,PaletteType,exception);
}
else
{
double
alpha,
beta;
/*
Identify transparent colormap index.
*/
if ((image->storage_class == DirectClass) || (image->colors > 256))
(void) SetImageType(image,PaletteBilevelAlphaType,exception);
for (i=0; i < (ssize_t) image->colors; i++)
if (image->colormap[i].alpha != OpaqueAlpha)
{
if (opacity < 0)
{
opacity=i;
continue;
}
alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
if (alpha < beta)
opacity=i;
}
if (opacity == -1)
{
(void) SetImageType(image,PaletteBilevelAlphaType,exception);
for (i=0; i < (ssize_t) image->colors; i++)
if (image->colormap[i].alpha != OpaqueAlpha)
{
if (opacity < 0)
{
opacity=i;
continue;
}
alpha=fabs(image->colormap[i].alpha-TransparentAlpha);
beta=fabs(image->colormap[opacity].alpha-TransparentAlpha);
if (alpha < beta)
opacity=i;
}
}
if (opacity >= 0)
{
image->colormap[opacity].red=image->transparent_color.red;
image->colormap[opacity].green=image->transparent_color.green;
image->colormap[opacity].blue=image->transparent_color.blue;
}
}
if ((image->storage_class == DirectClass) || (image->colors > 256))
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++)
if ((one << bits_per_pixel) >= image->colors)
break;
q=colormap;
for (i=0; i < (ssize_t) image->colors; i++)
{
*q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
*q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
*q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
}
for ( ; i < (ssize_t) (one << bits_per_pixel); i++)
{
*q++=(unsigned char) 0x0;
*q++=(unsigned char) 0x0;
*q++=(unsigned char) 0x0;
}
if ((GetPreviousImageInList(image) == (Image *) NULL) ||
(write_info->adjoin == MagickFalse))
{
/*
Write global colormap.
*/
c=0x80;
c|=(8-1) << 4; /* color resolution */
c|=(bits_per_pixel-1); /* size of global colormap */
(void) WriteBlobByte(image,(unsigned char) c);
for (j=0; j < (ssize_t) image->colors; j++)
if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j))
break;
(void) WriteBlobByte(image,(unsigned char)
(j == (ssize_t) image->colors ? 0 : j)); /* background color */
(void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */
length=(size_t) (3*(one << bits_per_pixel));
(void) WriteBlob(image,length,colormap);
for (j=0; j < 768; j++)
global_colormap[j]=colormap[j];
}
if (LocaleCompare(write_info->magick,"GIF87") != 0)
{
const char
*value;
/*
Write graphics control extension.
*/
(void) WriteBlobByte(image,(unsigned char) 0x21);
(void) WriteBlobByte(image,(unsigned char) 0xf9);
(void) WriteBlobByte(image,(unsigned char) 0x04);
c=image->dispose << 2;
if (opacity >= 0)
c|=0x01;
(void) WriteBlobByte(image,(unsigned char) c);
delay=(size_t) (100*image->delay/MagickMax((size_t)
image->ticks_per_second,1));
(void) WriteBlobLSBShort(image,(unsigned short) delay);
(void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity :
0));
(void) WriteBlobByte(image,(unsigned char) 0x00);
value=GetImageProperty(image,"comment",exception);
if (value != (const char *) NULL)
{
const char
*p;
size_t
count;
/*
Write comment extension.
*/
(void) WriteBlobByte(image,(unsigned char) 0x21);
(void) WriteBlobByte(image,(unsigned char) 0xfe);
for (p=value; *p != '\0'; )
{
count=MagickMin(strlen(p),255);
(void) WriteBlobByte(image,(unsigned char) count);
for (i=0; i < (ssize_t) count; i++)
(void) WriteBlobByte(image,(unsigned char) *p++);
}
(void) WriteBlobByte(image,(unsigned char) 0x00);
}
if ((GetPreviousImageInList(image) == (Image *) NULL) &&
(GetNextImageInList(image) != (Image *) NULL) &&
(image->iterations != 1))
{
/*
Write Netscape Loop extension.
*/
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Writing GIF Extension %s","NETSCAPE2.0");
(void) WriteBlobByte(image,(unsigned char) 0x21);
(void) WriteBlobByte(image,(unsigned char) 0xff);
(void) WriteBlobByte(image,(unsigned char) 0x0b);
(void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0");
(void) WriteBlobByte(image,(unsigned char) 0x03);
(void) WriteBlobByte(image,(unsigned char) 0x01);
(void) WriteBlobLSBShort(image,(unsigned short) (image->iterations ?
image->iterations-1 : 0));
(void) WriteBlobByte(image,(unsigned char) 0x00);
}
if ((image->gamma != 1.0f/2.2f))
{
char
attributes[MagickPathExtent];
ssize_t
count;
/*
Write ImageMagick extension.
*/
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Writing GIF Extension %s","ImageMagick");
(void) WriteBlobByte(image,(unsigned char) 0x21);
(void) WriteBlobByte(image,(unsigned char) 0xff);
(void) WriteBlobByte(image,(unsigned char) 0x0b);
(void) WriteBlob(image,11,(unsigned char *) "ImageMagick");
count=FormatLocaleString(attributes,MagickPathExtent,"gamma=%g",
image->gamma);
(void) WriteBlobByte(image,(unsigned char) count);
(void) WriteBlob(image,(size_t) count,(unsigned char *) attributes);
(void) WriteBlobByte(image,(unsigned char) 0x00);
}
ResetImageProfileIterator(image);
for ( ; ; )
{
char
*name;
const StringInfo
*profile;
name=GetNextImageProfile(image);
if (name == (const char *) NULL)
break;
profile=GetImageProfile(image,name);
if (profile != (StringInfo *) NULL)
{
if ((LocaleCompare(name,"ICC") == 0) ||
(LocaleCompare(name,"ICM") == 0) ||
(LocaleCompare(name,"IPTC") == 0) ||
(LocaleCompare(name,"8BIM") == 0) ||
(LocaleNCompare(name,"gif:",4) == 0))
{
ssize_t
offset;
unsigned char
*datum;
datum=GetStringInfoDatum(profile);
length=GetStringInfoLength(profile);
(void) WriteBlobByte(image,(unsigned char) 0x21);
(void) WriteBlobByte(image,(unsigned char) 0xff);
(void) WriteBlobByte(image,(unsigned char) 0x0b);
if ((LocaleCompare(name,"ICC") == 0) ||
(LocaleCompare(name,"ICM") == 0))
{
/*
Write ICC extension.
*/
(void) WriteBlob(image,11,(unsigned char *) "ICCRGBG1012");
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Writing GIF Extension %s","ICCRGBG1012");
}
else
if ((LocaleCompare(name,"IPTC") == 0))
{
/*
Write IPTC extension.
*/
(void) WriteBlob(image,11,(unsigned char *) "MGKIPTC0000");
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Writing GIF Extension %s","MGKIPTC0000");
}
else
if ((LocaleCompare(name,"8BIM") == 0))
{
/*
Write 8BIM extension.
*/
(void) WriteBlob(image,11,(unsigned char *)
"MGK8BIM0000");
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Writing GIF Extension %s","MGK8BIM0000");
}
else
{
char
extension[MagickPathExtent];
/*
Write generic extension.
*/
(void) CopyMagickString(extension,name+4,
sizeof(extension));
(void) WriteBlob(image,11,(unsigned char *) extension);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Writing GIF Extension %s",name);
}
offset=0;
while ((ssize_t) length > offset)
{
size_t
block_length;
if ((length-offset) < 255)
block_length=length-offset;
else
block_length=255;
(void) WriteBlobByte(image,(unsigned char) block_length);
(void) WriteBlob(image,(size_t) block_length,datum+offset);
offset+=(ssize_t) block_length;
}
(void) WriteBlobByte(image,(unsigned char) 0x00);
}
}
}
}
(void) WriteBlobByte(image,','); /* image separator */
/*
Write the image header.
*/
page.x=image->page.x;
page.y=image->page.y;
if ((image->page.width != 0) && (image->page.height != 0))
page=image->page;
(void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x));
(void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y));
(void) WriteBlobLSBShort(image,(unsigned short) image->columns);
(void) WriteBlobLSBShort(image,(unsigned short) image->rows);
c=0x00;
if (write_info->interlace != NoInterlace)
c|=0x40; /* pixel data is interlaced */
for (j=0; j < (ssize_t) (3*image->colors); j++)
if (colormap[j] != global_colormap[j])
break;
if (j == (ssize_t) (3*image->colors))
(void) WriteBlobByte(image,(unsigned char) c);
else
{
c|=0x80;
c|=(bits_per_pixel-1); /* size of local colormap */
(void) WriteBlobByte(image,(unsigned char) c);
length=(size_t) (3*(one << bits_per_pixel));
(void) WriteBlob(image,length,colormap);
}
/*
Write the image data.
*/
c=(int) MagickMax(bits_per_pixel,2);
(void) WriteBlobByte(image,(unsigned char) c);
status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1,
exception);
if (status == MagickFalse)
{
global_colormap=(unsigned char *) RelinquishMagickMemory(
global_colormap);
colormap=(unsigned char *) RelinquishMagickMemory(colormap);
write_info=DestroyImageInfo(write_info);
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
}
(void) WriteBlobByte(image,(unsigned char) 0x00);
if (GetNextImageInList(image) == (Image *) NULL)
break;
image=SyncNextImageInList(image);
scene++;
status=SetImageProgress(image,SaveImagesTag,scene,imageListLength);
if (status == MagickFalse)
break;
} while (write_info->adjoin != MagickFalse);
(void) WriteBlobByte(image,';'); /* terminator */
global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap);
colormap=(unsigned char *) RelinquishMagickMemory(colormap);
write_info=DestroyImageInfo(write_info);
(void) CloseBlob(image);
return(MagickTrue);
}