Alien-FreeImage
view release on metacpan or search on metacpan
src/Source/FreeImage/PluginGIF.cpp view on Meta::CPAN
#define GIF_BLOCK_IMAGE_DESCRIPTOR 0x2C
#define GIF_BLOCK_EXTENSION 0x21
#define GIF_BLOCK_TRAILER 0x3B
#define GIF_EXT_PLAINTEXT 0x01
#define GIF_EXT_GRAPHIC_CONTROL 0xF9
#define GIF_EXT_COMMENT 0xFE
#define GIF_EXT_APPLICATION 0xFF
#define GIF_INTERLACE_PASSES 4
static int g_GifInterlaceOffset[GIF_INTERLACE_PASSES] = {0, 4, 2, 1};
static int g_GifInterlaceIncrement[GIF_INTERLACE_PASSES] = {8, 8, 4, 2};
// ==========================================================
// Helpers Functions
// ==========================================================
static BOOL
FreeImage_SetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, WORD id, FREE_IMAGE_MDTYPE type, DWORD count, DWORD length, const void *value)
{
BOOL bResult = FALSE;
FITAG *tag = FreeImage_CreateTag();
if(tag) {
FreeImage_SetTagKey(tag, key);
FreeImage_SetTagID(tag, id);
FreeImage_SetTagType(tag, type);
FreeImage_SetTagCount(tag, count);
FreeImage_SetTagLength(tag, length);
FreeImage_SetTagValue(tag, value);
if(model == FIMD_ANIMATION) {
TagLib& s = TagLib::instance();
// get the tag description
const char *description = s.getTagDescription(TagLib::ANIMATION, id);
FreeImage_SetTagDescription(tag, description);
}
// store the tag
bResult = FreeImage_SetMetadata(model, dib, key, tag);
FreeImage_DeleteTag(tag);
}
return bResult;
}
static BOOL
FreeImage_GetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FREE_IMAGE_MDTYPE type, FITAG **tag)
{
if( FreeImage_GetMetadata(model, dib, key, tag) ) {
if( FreeImage_GetTagType(*tag) == type ) {
return TRUE;
}
}
return FALSE;
}
StringTable::StringTable()
{
m_buffer = NULL;
firstPixelPassed = 0; // Still no pixel read
// Maximum number of entries in the map is MAX_LZW_CODE * 256
// (aka 2**12 * 2**8 => a 20 bits key)
// This Map could be optmized to only handle MAX_LZW_CODE * 2**(m_bpp)
m_strmap = new(std::nothrow) int[1<<20];
}
StringTable::~StringTable()
{
if( m_buffer != NULL ) {
delete [] m_buffer;
}
if( m_strmap != NULL ) {
delete [] m_strmap;
m_strmap = NULL;
}
}
void StringTable::Initialize(int minCodeSize)
{
m_done = false;
m_bpp = 8;
m_minCodeSize = minCodeSize;
m_clearCode = 1 << m_minCodeSize;
if(m_clearCode > MAX_LZW_CODE) {
m_clearCode = MAX_LZW_CODE;
}
m_endCode = m_clearCode + 1;
m_partial = 0;
m_partialSize = 0;
m_bufferSize = 0;
ClearCompressorTable();
ClearDecompressorTable();
}
BYTE *StringTable::FillInputBuffer(int len)
{
if( m_buffer == NULL ) {
m_buffer = new(std::nothrow) BYTE[len];
m_bufferRealSize = len;
} else if( len > m_bufferRealSize ) {
delete [] m_buffer;
m_buffer = new(std::nothrow) BYTE[len];
m_bufferRealSize = len;
}
m_bufferSize = len;
m_bufferPos = 0;
m_bufferShift = 8 - m_bpp;
return m_buffer;
}
void StringTable::CompressStart(int bpp, int width)
{
m_bpp = bpp;
m_slack = (8 - ((width * bpp) % 8)) % 8;
m_partial |= m_clearCode << m_partialSize;
m_partialSize += m_codeSize;
ClearCompressorTable();
}
int StringTable::CompressEnd(BYTE *buf)
{
int len = 0;
//output code for remaining prefix
m_partial |= m_prefix << m_partialSize;
m_partialSize += m_codeSize;
while( m_partialSize >= 8 ) {
*buf++ = (BYTE)m_partial;
m_partial >>= 8;
m_partialSize -= 8;
len++;
}
//add the end of information code and flush the entire buffer out
m_partial |= m_endCode << m_partialSize;
m_partialSize += m_codeSize;
while( m_partialSize > 0 ) {
*buf++ = (BYTE)m_partial;
m_partial >>= 8;
m_partialSize -= 8;
len++;
}
//most this can be is 4 bytes. 7 bits in m_partial to start + 12 for the
//last code + 12 for the end code = 31 bits total.
return len;
}
bool StringTable::Compress(BYTE *buf, int *len)
{
if( m_bufferSize == 0 || m_done ) {
return false;
}
int mask = (1 << m_bpp) - 1;
BYTE *bufpos = buf;
while( m_bufferPos < m_bufferSize ) {
//get the current pixel value
char ch = (char)((m_buffer[m_bufferPos] >> m_bufferShift) & mask);
// The next prefix is :
src/Source/FreeImage/PluginGIF.cpp view on Meta::CPAN
static const char * DLL_CALLCONV
Format() {
return "GIF";
}
static const char * DLL_CALLCONV
Description() {
return "Graphics Interchange Format";
}
static const char * DLL_CALLCONV
Extension() {
return "gif";
}
static const char * DLL_CALLCONV
RegExpr() {
return "^GIF";
}
static const char * DLL_CALLCONV
MimeType() {
return "image/gif";
}
static BOOL DLL_CALLCONV
Validate(FreeImageIO *io, fi_handle handle) {
char buf[6];
if( io->read_proc(buf, 6, 1, handle) < 1 ) {
return FALSE;
}
BOOL bResult = FALSE;
if( !strncmp(buf, "GIF", 3) ) {
if( buf[3] >= '0' && buf[3] <= '9' && buf[4] >= '0' && buf[4] <= '9' && buf[5] >= 'a' && buf[5] <= 'z' ) {
bResult = TRUE;
}
}
io->seek_proc(handle, -6, SEEK_CUR);
return bResult;
}
static BOOL DLL_CALLCONV
SupportsExportDepth(int depth) {
return (depth == 1) ||
(depth == 4) ||
(depth == 8);
}
static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type) {
return (type == FIT_BITMAP) ? TRUE : FALSE;
}
// ----------------------------------------------------------
static void *DLL_CALLCONV
Open(FreeImageIO *io, fi_handle handle, BOOL read) {
GIFinfo *info = new(std::nothrow) GIFinfo;
if( info == NULL ) {
return NULL;
}
// 25/02/2008 MDA: Not safe to memset GIFinfo structure with VS 2008 (safe iterators),
// perform initialization in constructor instead.
// memset(info, 0, sizeof(GIFinfo));
info->read = read;
if( read ) {
try {
//Header
if( !Validate(io, handle) ) {
throw FI_MSG_ERROR_MAGIC_NUMBER;
}
io->seek_proc(handle, 6, SEEK_CUR);
//Logical Screen Descriptor
io->seek_proc(handle, 4, SEEK_CUR);
BYTE packed;
if( io->read_proc(&packed, 1, 1, handle) < 1 ) {
throw "EOF reading Logical Screen Descriptor";
}
if( io->read_proc(&info->background_color, 1, 1, handle) < 1 ) {
throw "EOF reading Logical Screen Descriptor";
}
io->seek_proc(handle, 1, SEEK_CUR);
//Global Color Table
if( packed & GIF_PACKED_LSD_HAVEGCT ) {
info->global_color_table_offset = io->tell_proc(handle);
info->global_color_table_size = 2 << (packed & GIF_PACKED_LSD_GCTSIZE);
io->seek_proc(handle, 3 * info->global_color_table_size, SEEK_CUR);
}
//Scan through all the rest of the blocks, saving offsets
size_t gce_offset = 0;
BYTE block = 0;
while( block != GIF_BLOCK_TRAILER ) {
if( io->read_proc(&block, 1, 1, handle) < 1 ) {
throw "EOF reading blocks";
}
if( block == GIF_BLOCK_IMAGE_DESCRIPTOR ) {
info->image_descriptor_offsets.push_back(io->tell_proc(handle));
//GCE may be 0, meaning no GCE preceded this ID
info->graphic_control_extension_offsets.push_back(gce_offset);
gce_offset = 0;
io->seek_proc(handle, 8, SEEK_CUR);
if( io->read_proc(&packed, 1, 1, handle) < 1 ) {
throw "EOF reading Image Descriptor";
}
//Local Color Table
if( packed & GIF_PACKED_ID_HAVELCT ) {
io->seek_proc(handle, 3 * (2 << (packed & GIF_PACKED_ID_LCTSIZE)), SEEK_CUR);
}
//LZW Minimum Code Size
io->seek_proc(handle, 1, SEEK_CUR);
} else if( block == GIF_BLOCK_EXTENSION ) {
BYTE ext;
if( io->read_proc(&ext, 1, 1, handle) < 1 ) {
throw "EOF reading extension";
}
if( ext == GIF_EXT_GRAPHIC_CONTROL ) {
//overwrite previous offset if more than one GCE found before an ID
gce_offset = io->tell_proc(handle);
} else if( ext == GIF_EXT_COMMENT ) {
info->comment_extension_offsets.push_back(io->tell_proc(handle));
} else if( ext == GIF_EXT_APPLICATION ) {
info->application_extension_offsets.push_back(io->tell_proc(handle));
}
} else if( block == GIF_BLOCK_TRAILER ) {
continue;
} else {
throw "Invalid GIF block found";
}
//Data Sub-blocks
BYTE len;
if( io->read_proc(&len, 1, 1, handle) < 1 ) {
throw "EOF reading sub-block";
}
while( len != 0 ) {
io->seek_proc(handle, len, SEEK_CUR);
if( io->read_proc(&len, 1, 1, handle) < 1 ) {
throw "EOF reading sub-block";
}
}
}
} catch (const char *msg) {
FreeImage_OutputMessageProc(s_format_id, msg);
delete info;
return NULL;
}
} else {
//Header
io->write_proc((void *)"GIF89a", 6, 1, handle);
}
return info;
}
static void DLL_CALLCONV
Close(FreeImageIO *io, fi_handle handle, void *data) {
if( data == NULL ) {
return;
}
GIFinfo *info = (GIFinfo *)data;
if( !info->read ) {
//Trailer
BYTE b = GIF_BLOCK_TRAILER;
io->write_proc(&b, 1, 1, handle);
}
delete info;
}
static int DLL_CALLCONV
PageCount(FreeImageIO *io, fi_handle handle, void *data) {
if( data == NULL ) {
return 0;
}
GIFinfo *info = (GIFinfo *)data;
return (int) info->image_descriptor_offsets.size();
}
static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
if( data == NULL ) {
return NULL;
}
GIFinfo *info = (GIFinfo *)data;
if( page == -1 ) {
page = 0;
}
if( page < 0 || page >= (int)info->image_descriptor_offsets.size() ) {
return NULL;
}
FIBITMAP *dib = NULL;
try {
bool have_transparent = false, no_local_palette = false, interlaced = false;
int disposal_method = GIF_DISPOSAL_LEAVE, delay_time = 0, transparent_color = 0;
WORD left, top, width, height;
BYTE packed, b;
WORD w;
//playback pages to generate what the user would see for this frame
if( (flags & GIF_PLAYBACK) == GIF_PLAYBACK ) {
//Logical Screen Descriptor
io->seek_proc(handle, 6, SEEK_SET);
WORD logicalwidth, logicalheight;
io->read_proc(&logicalwidth, 2, 1, handle);
io->read_proc(&logicalheight, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&logicalwidth);
SwapShort(&logicalheight);
#endif
//set the background color with 0 alpha
RGBQUAD background;
if( info->global_color_table_offset != 0 && info->background_color < info->global_color_table_size ) {
io->seek_proc(handle, (long)(info->global_color_table_offset + (info->background_color * 3)), SEEK_SET);
io->read_proc(&background.rgbRed, 1, 1, handle);
io->read_proc(&background.rgbGreen, 1, 1, handle);
io->read_proc(&background.rgbBlue, 1, 1, handle);
} else {
background.rgbRed = 0;
background.rgbGreen = 0;
background.rgbBlue = 0;
}
background.rgbReserved = 0;
//allocate entire logical area
dib = FreeImage_Allocate(logicalwidth, logicalheight, 32);
if( dib == NULL ) {
throw FI_MSG_ERROR_DIB_MEMORY;
}
//fill with background color to start
int x, y;
RGBQUAD *scanline;
for( y = 0; y < logicalheight; y++ ) {
scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
for( x = 0; x < logicalwidth; x++ ) {
*scanline++ = background;
}
}
//cache some info about each of the pages so we can avoid decoding as many of them as possible
std::vector<PageInfo> pageinfo;
int start = page, end = page;
while( start >= 0 ) {
//Graphic Control Extension
io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[start] + 1), SEEK_SET);
io->read_proc(&packed, 1, 1, handle);
have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
//Image Descriptor
io->seek_proc(handle, (long)(info->image_descriptor_offsets[start]), SEEK_SET);
io->read_proc(&left, 2, 1, handle);
io->read_proc(&top, 2, 1, handle);
io->read_proc(&width, 2, 1, handle);
io->read_proc(&height, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&left);
SwapShort(&top);
SwapShort(&width);
SwapShort(&height);
#endif
pageinfo.push_back(PageInfo(disposal_method, left, top, width, height));
if( start != end ) {
if( left == 0 && top == 0 && width == logicalwidth && height == logicalheight ) {
if( disposal_method == GIF_DISPOSAL_BACKGROUND ) {
pageinfo.pop_back();
start++;
break;
} else if( disposal_method != GIF_DISPOSAL_PREVIOUS ) {
if( !have_transparent ) {
break;
}
}
}
}
start--;
}
if( start < 0 ) {
start = 0;
}
//draw each page into the logical area
delay_time = 0;
for( page = start; page <= end; page++ ) {
PageInfo &info = pageinfo[end - page];
//things we can skip having to decode
src/Source/FreeImage/PluginGIF.cpp view on Meta::CPAN
break; // If data is corrupt, don't calculate in invalid scanline
}
scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left;
BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1);
for( x = 0; x < info.width; x++ ) {
if( !have_transparent || *pageline != transparent_color ) {
*scanline = pal[*pageline];
scanline->rgbReserved = 255;
}
scanline++;
pageline++;
}
}
//copy frame time
if( page == end ) {
FITAG *tag;
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, pagedib, "FrameTime", FIDT_LONG, &tag) ) {
delay_time = *(LONG *)FreeImage_GetTagValue(tag);
}
}
FreeImage_Unload(pagedib);
}
}
//setup frame time
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
return dib;
}
//get the actual frame image data for a single frame
//Image Descriptor
io->seek_proc(handle, (long)info->image_descriptor_offsets[page], SEEK_SET);
io->read_proc(&left, 2, 1, handle);
io->read_proc(&top, 2, 1, handle);
io->read_proc(&width, 2, 1, handle);
io->read_proc(&height, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&left);
SwapShort(&top);
SwapShort(&width);
SwapShort(&height);
#endif
io->read_proc(&packed, 1, 1, handle);
interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false;
no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true;
int bpp = 8;
if( (flags & GIF_LOAD256) == 0 ) {
if( !no_local_palette ) {
int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
if( size <= 2 ) bpp = 1;
else if( size <= 16 ) bpp = 4;
} else if( info->global_color_table_offset != 0 ) {
if( info->global_color_table_size <= 2 ) bpp = 1;
else if( info->global_color_table_size <= 16 ) bpp = 4;
}
}
dib = FreeImage_Allocate(width, height, bpp);
if( dib == NULL ) {
throw FI_MSG_ERROR_DIB_MEMORY;
}
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", ANIMTAG_FRAMELEFT, FIDT_SHORT, 1, 2, &left);
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", ANIMTAG_FRAMETOP, FIDT_SHORT, 1, 2, &top);
b = no_local_palette ? 1 : 0;
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", ANIMTAG_NOLOCALPALETTE, FIDT_BYTE, 1, 1, &b);
b = interlaced ? 1 : 0;
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", ANIMTAG_INTERLACED, FIDT_BYTE, 1, 1, &b);
//Palette
RGBQUAD *pal = FreeImage_GetPalette(dib);
if( !no_local_palette ) {
int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
int i = 0;
while( i < size ) {
io->read_proc(&pal[i].rgbRed, 1, 1, handle);
io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
i++;
}
} else if( info->global_color_table_offset != 0 ) {
long pos = io->tell_proc(handle);
io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);
int i = 0;
while( i < info->global_color_table_size ) {
io->read_proc(&pal[i].rgbRed, 1, 1, handle);
io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
i++;
}
io->seek_proc(handle, pos, SEEK_SET);
} else {
//its legal to have no palette, but we're going to generate *something*
for( int i = 0; i < 256; i++ ) {
pal[i].rgbRed = (BYTE)i;
pal[i].rgbGreen = (BYTE)i;
pal[i].rgbBlue = (BYTE)i;
}
}
//LZW Minimum Code Size
io->read_proc(&b, 1, 1, handle);
StringTable *stringtable = new(std::nothrow) StringTable;
stringtable->Initialize(b);
//Image Data Sub-blocks
int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0;
BYTE *scanline = FreeImage_GetScanLine(dib, height - 1);
BYTE buf[4096];
io->read_proc(&b, 1, 1, handle);
while( b ) {
io->read_proc(stringtable->FillInputBuffer(b), b, 1, handle);
int size = sizeof(buf);
while( stringtable->Decompress(buf, &size) ) {
for( int i = 0; i < size; i++ ) {
scanline[xpos] |= (buf[i] & mask) << shift;
if( shift > 0 ) {
shift -= bpp;
} else {
xpos++;
shift = 8 - bpp;
}
if( ++x >= width ) {
if( interlaced ) {
y += g_GifInterlaceIncrement[interlacepass];
if( y >= height && ++interlacepass < GIF_INTERLACE_PASSES ) {
y = g_GifInterlaceOffset[interlacepass];
}
} else {
y++;
}
if( y >= height ) {
stringtable->Done();
break;
}
x = xpos = 0;
shift = 8 - bpp;
scanline = FreeImage_GetScanLine(dib, height - y - 1);
}
}
size = sizeof(buf);
}
io->read_proc(&b, 1, 1, handle);
}
if( page == 0 ) {
size_t idx;
//Logical Screen Descriptor
io->seek_proc(handle, 6, SEEK_SET);
WORD logicalwidth, logicalheight;
io->read_proc(&logicalwidth, 2, 1, handle);
io->read_proc(&logicalheight, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&logicalwidth);
SwapShort(&logicalheight);
#endif
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", ANIMTAG_LOGICALWIDTH, FIDT_SHORT, 1, 2, &logicalwidth);
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", ANIMTAG_LOGICALHEIGHT, FIDT_SHORT, 1, 2, &logicalheight);
//Global Color Table
if( info->global_color_table_offset != 0 ) {
RGBQUAD globalpalette[256];
src/Source/FreeImage/PluginGIF.cpp view on Meta::CPAN
}
//Graphic Control Extension
if( info->graphic_control_extension_offsets[page] != 0 ) {
io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[page] + 1), SEEK_SET);
io->read_proc(&packed, 1, 1, handle);
io->read_proc(&w, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&w);
#endif
io->read_proc(&b, 1, 1, handle);
have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
delay_time = w * 10; //convert cs to ms
transparent_color = b;
if( have_transparent ) {
int size = 1 << bpp;
if( transparent_color <= size ) {
BYTE table[256];
memset(table, 0xFF, size);
table[transparent_color] = 0;
FreeImage_SetTransparencyTable(dib, table, size);
}
}
}
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
b = (BYTE)disposal_method;
FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", ANIMTAG_DISPOSALMETHOD, FIDT_BYTE, 1, 1, &b);
delete stringtable;
} catch (const char *msg) {
if( dib != NULL ) {
FreeImage_Unload(dib);
}
FreeImage_OutputMessageProc(s_format_id, msg);
return NULL;
}
return dib;
}
static BOOL DLL_CALLCONV
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
if( data == NULL ) {
return FALSE;
}
//GIFinfo *info = (GIFinfo *)data;
if( page == -1 ) {
page = 0;
}
try {
BYTE packed, b;
WORD w;
FITAG *tag;
int bpp = FreeImage_GetBPP(dib);
if( bpp != 1 && bpp != 4 && bpp != 8 ) {
throw "Only 1, 4, or 8 bpp images supported";
}
bool have_transparent = false, no_local_palette = false, interlaced = false;
int disposal_method = GIF_DISPOSAL_BACKGROUND, delay_time = 100, transparent_color = 0;
WORD left = 0, top = 0, width = (WORD)FreeImage_GetWidth(dib), height = (WORD)FreeImage_GetHeight(dib);
WORD output_height = height;
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", FIDT_SHORT, &tag) ) {
left = *(WORD *)FreeImage_GetTagValue(tag);
}
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", FIDT_SHORT, &tag) ) {
top = *(WORD *)FreeImage_GetTagValue(tag);
}
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", FIDT_BYTE, &tag) ) {
no_local_palette = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
}
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", FIDT_BYTE, &tag) ) {
interlaced = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
}
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", FIDT_LONG, &tag) ) {
delay_time = *(LONG *)FreeImage_GetTagValue(tag);
}
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", FIDT_BYTE, &tag) ) {
disposal_method = *(BYTE *)FreeImage_GetTagValue(tag);
}
RGBQUAD *pal = FreeImage_GetPalette(dib);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&left);
SwapShort(&top);
SwapShort(&width);
SwapShort(&height);
#endif
if( page == 0 ) {
//gather some info
WORD logicalwidth = width; // width has already been swapped...
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", FIDT_SHORT, &tag) ) {
logicalwidth = *(WORD *)FreeImage_GetTagValue(tag);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&logicalwidth);
#endif
}
WORD logicalheight = height; // height has already been swapped...
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", FIDT_SHORT, &tag) ) {
logicalheight = *(WORD *)FreeImage_GetTagValue(tag);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&logicalheight);
#endif
}
RGBQUAD *globalpalette = NULL;
int globalpalette_size = 0;
if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", FIDT_PALETTE, &tag) ) {
globalpalette_size = FreeImage_GetTagCount(tag);
if( globalpalette_size >= 2 ) {
globalpalette = (RGBQUAD *)FreeImage_GetTagValue(tag);
}
}
//Logical Screen Descriptor
io->write_proc(&logicalwidth, 2, 1, handle);
src/Source/FreeImage/PluginGIF.cpp view on Meta::CPAN
FreeImage_FindCloseMetadata(mdhandle);
}
}
//Graphic Control Extension
if( FreeImage_IsTransparent(dib) ) {
int count = FreeImage_GetTransparencyCount(dib);
BYTE *table = FreeImage_GetTransparencyTable(dib);
for( int i = 0; i < count; i++ ) {
if( table[i] == 0 ) {
have_transparent = true;
transparent_color = i;
break;
}
}
}
io->write_proc((void *)"\x21\xF9\x04", 3, 1, handle);
b = (BYTE)((disposal_method << 2) & GIF_PACKED_GCE_DISPOSAL);
if( have_transparent ) b |= GIF_PACKED_GCE_HAVETRANS;
io->write_proc(&b, 1, 1, handle);
//Notes about delay time for GIFs:
//IE5/IE6 have a minimum and default of 100ms
//Mozilla/Firefox/Netscape 6+/Opera have a minimum of 20ms and a default of 100ms if <20ms is specified or the GCE is absent
//Netscape 4 has a minimum of 10ms if 0ms is specified, but will use 0ms if the GCE is absent
w = (WORD)(delay_time / 10); //convert ms to cs
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&w);
#endif
io->write_proc(&w, 2, 1, handle);
b = (BYTE)transparent_color;
io->write_proc(&b, 1, 1, handle);
b = 0;
io->write_proc(&b, 1, 1, handle);
//Image Descriptor
b = GIF_BLOCK_IMAGE_DESCRIPTOR;
io->write_proc(&b, 1, 1, handle);
io->write_proc(&left, 2, 1, handle);
io->write_proc(&top, 2, 1, handle);
io->write_proc(&width, 2, 1, handle);
io->write_proc(&height, 2, 1, handle);
packed = 0;
if( !no_local_palette ) packed |= GIF_PACKED_ID_HAVELCT | ((bpp - 1) & GIF_PACKED_ID_LCTSIZE);
if( interlaced ) packed |= GIF_PACKED_ID_INTERLACED;
io->write_proc(&packed, 1, 1, handle);
//Local Color Table
if( !no_local_palette ) {
int palsize = 1 << bpp;
for( int i = 0; i < palsize; i++ ) {
io->write_proc(&pal[i].rgbRed, 1, 1, handle);
io->write_proc(&pal[i].rgbGreen, 1, 1, handle);
io->write_proc(&pal[i].rgbBlue, 1, 1, handle);
}
}
//LZW Minimum Code Size
b = (BYTE)(bpp == 1 ? 2 : bpp);
io->write_proc(&b, 1, 1, handle);
StringTable *stringtable = new(std::nothrow) StringTable;
stringtable->Initialize(b);
stringtable->CompressStart(bpp, width);
//Image Data Sub-blocks
int y = 0, interlacepass = 0, line = FreeImage_GetLine(dib);
BYTE buf[255], *bufptr = buf; //255 is the max sub-block length
int size = sizeof(buf);
b = sizeof(buf);
while( y < output_height ) {
memcpy(stringtable->FillInputBuffer(line), FreeImage_GetScanLine(dib, output_height - y - 1), line);
while( stringtable->Compress(bufptr, &size) ) {
bufptr += size;
if( bufptr - buf == sizeof(buf) ) {
io->write_proc(&b, 1, 1, handle);
io->write_proc(buf, sizeof(buf), 1, handle);
size = sizeof(buf);
bufptr = buf;
} else {
size = (int)(sizeof(buf) - (bufptr - buf));
}
}
if( interlaced ) {
y += g_GifInterlaceIncrement[interlacepass];
if( y >= output_height && ++interlacepass < GIF_INTERLACE_PASSES ) {
y = g_GifInterlaceOffset[interlacepass];
}
} else {
y++;
}
}
size = (int)(bufptr - buf);
BYTE last[4];
w = (WORD)stringtable->CompressEnd(last);
if( size + w >= sizeof(buf) ) {
//one last full size sub-block
io->write_proc(&b, 1, 1, handle);
io->write_proc(buf, size, 1, handle);
io->write_proc(last, sizeof(buf) - size, 1, handle);
//and possibly a tiny additional sub-block
b = (BYTE)(w - (sizeof(buf) - size));
if( b > 0 ) {
io->write_proc(&b, 1, 1, handle);
io->write_proc(last + w - b, b, 1, handle);
}
} else {
//last sub-block less than full size
b = (BYTE)(size + w);
io->write_proc(&b, 1, 1, handle);
io->write_proc(buf, size, 1, handle);
io->write_proc(last, w, 1, handle);
}
//Block Terminator
b = 0;
io->write_proc(&b, 1, 1, handle);
delete stringtable;
} catch (const char *msg) {
FreeImage_OutputMessageProc(s_format_id, msg);
( run in 0.646 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )