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.
1766 lines
59 KiB
1766 lines
59 KiB
//+-----------------------------------------------------------------------------------
|
|
//
|
|
// Microsoft
|
|
// Copyright (c) Microsoft Corporation, 1999
|
|
//
|
|
// File: src\time\src\loadgif.cpp
|
|
//
|
|
// Contents: gif decoder, copied from direct animation source: danim\src\appel\util\loadgif.cpp
|
|
//
|
|
//------------------------------------------------------------------------------------
|
|
//#include <wininetp.h>
|
|
|
|
//bw #include "headers.h"
|
|
|
|
// #define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <windows.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <ole2.h>
|
|
#include <math.h>
|
|
|
|
|
|
#include <windowsx.h>
|
|
|
|
/*lint ++flb*/
|
|
|
|
//bw DeclareTag(tagImageDecode, "Image Decode", "Image Decode Filters");
|
|
|
|
const long COLORKEY_NOT_SET = -1;
|
|
|
|
/*--
|
|
Structs from IE img.hxx
|
|
--*/
|
|
|
|
void * __cdecl
|
|
_calloc(size_t num, size_t size)
|
|
{
|
|
void * pv = malloc(num * size);
|
|
if (NULL == pv)
|
|
{
|
|
return NULL;
|
|
}
|
|
ZeroMemory(pv, num * size);
|
|
return pv;
|
|
}
|
|
|
|
enum
|
|
{
|
|
gifNoneSpecified = 0, // no disposal method specified
|
|
gifNoDispose = 1, // do not dispose, leave the bits there
|
|
gifRestoreBkgnd = 2, // replace the image with the background color
|
|
gifRestorePrev = 3 // replace the image with the previous pixels
|
|
};
|
|
|
|
typedef struct _GCEDATA // data from GIF Graphic control extension
|
|
{
|
|
unsigned int uiDelayTime; // frame duration, initialy 1/100ths seconds
|
|
// converted to milliseconds
|
|
unsigned int uiDisposalMethod; // 0 - none specified.
|
|
// 1 - do not dispose - leave bits in place.
|
|
// 2 - replace with background color.
|
|
// 3 - restore previous bits
|
|
// >3 - not yet defined
|
|
BOOL fTransparent; // TRUE is ucTransIndex describes transparent color
|
|
unsigned char ucTransIndex; // transparent index
|
|
|
|
} GCEDATA;
|
|
|
|
typedef struct _GIFFRAME
|
|
{
|
|
struct _GIFFRAME *pgfNext;
|
|
GCEDATA gced; // animation parameters for frame.
|
|
int top; // bounds relative to the GIF logical screen
|
|
int left;
|
|
int width;
|
|
int height;
|
|
unsigned char *ppixels; // pointer to image pixel data
|
|
int cColors; // number of entries in pcolors
|
|
PALETTEENTRY *pcolors;
|
|
PBITMAPINFO pbmi;
|
|
HRGN hrgnVis; // region describing currently visible portion of the frame
|
|
int iRgnKind; // region type for hrgnVis
|
|
} GIFFRAME, *PGIFFRAME;
|
|
|
|
typedef struct {
|
|
BOOL fAnimating; // TRUE if animation is (still) running
|
|
DWORD dwLoopIter; // current iteration of looped animation, not actually used for Netscape compliance reasons
|
|
_GIFFRAME * pgfDraw; // last frame we need to draw
|
|
DWORD dwNextTimeMS; // Time to display pgfDraw->pgfNext, or next iteration
|
|
} GIFANIMATIONSTATE, *PGIFANIMATIONSTATE;
|
|
|
|
#define dwGIFVerUnknown ((DWORD)0) // unknown version of GIF file
|
|
#define dwGIFVer87a ((DWORD)87) // GIF87a file format
|
|
#define dwGIFVer89a ((DWORD)89) // GIF89a file format.
|
|
|
|
typedef struct _GIFANIMDATA
|
|
{
|
|
BOOL fAnimated; // TRUE if cFrames and pgf define a GIF animation
|
|
BOOL fLooped; // TRUE if we've seen a Netscape loop block
|
|
BOOL fHasTransparency; // TRUE if a frame is transparent, or if a frame does
|
|
// not cover the entire logical screen.
|
|
BOOL fNoBWMapping; // TRUE if we saw more than two colors in anywhere in the file.
|
|
DWORD dwGIFVer; // GIF Version <see defines above> we need to special case 87a backgrounds
|
|
unsigned short cLoops; // A la Netscape, we will treat this as
|
|
// "loop forever" if it is zero.
|
|
PGIFFRAME pgf; // animation frame entries
|
|
PALETTEENTRY *pcolorsGlobal; // GIF global colors - NULL after GIF prepared for screen
|
|
PGIFFRAME pgfLastProg; // remember the last frame to be drawn during decoding
|
|
DWORD dwLastProgTimeMS; // time at which pgfLastProg was displayed.
|
|
|
|
} GIFANIMDATA, *PGIFANIMDATA;
|
|
|
|
/** End Structs **/
|
|
|
|
#define MAXCOLORMAPSIZE 256
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define CM_RED 0
|
|
#define CM_GREEN 1
|
|
#define CM_BLUE 2
|
|
|
|
#define MAX_LWZ_BITS 12
|
|
|
|
#define INTERLACE 0x40
|
|
#define LOCALCOLORMAP 0x80
|
|
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
|
|
|
|
#define LM_to_uint(a,b) ((((unsigned int) b)<<8)|((unsigned int)a))
|
|
|
|
#define dwIndefiniteGIFThreshold 300 // 300 seconds == 5 minutes
|
|
// If the GIF runs longer than
|
|
// this, we will assume the author
|
|
// intended an indefinite run.
|
|
#define dwMaxGIFBits 13107200 // keep corrupted GIFs from causing
|
|
// us to allocate _too_ big a buffer.
|
|
// This one is 1280 X 1024 X 10.
|
|
|
|
typedef struct _GIFSCREEN
|
|
{
|
|
unsigned long Width;
|
|
unsigned long Height;
|
|
unsigned char ColorMap[3][MAXCOLORMAPSIZE];
|
|
unsigned long BitPixel;
|
|
unsigned long ColorResolution;
|
|
unsigned long Background;
|
|
unsigned long AspectRatio;
|
|
}
|
|
GIFSCREEN;
|
|
|
|
typedef struct _GIF89
|
|
{
|
|
long transparent;
|
|
long delayTime;
|
|
long inputFlag;
|
|
long disposal;
|
|
}
|
|
GIF89;
|
|
|
|
#define MAX_STACK_SIZE ((1 << (MAX_LWZ_BITS)) * 2)
|
|
#define MAX_TABLE_SIZE (1 << MAX_LWZ_BITS)
|
|
typedef struct _GIFINFO
|
|
{
|
|
IStream *stream;
|
|
GIF89 Gif89;
|
|
long lGifLoc;
|
|
long ZeroDataBlock;
|
|
|
|
/*
|
|
** Pulled out of nextCode
|
|
*/
|
|
long curbit, lastbit, get_done;
|
|
long last_byte;
|
|
long return_clear;
|
|
/*
|
|
** Out of nextLWZ
|
|
*/
|
|
unsigned short *pstack, *sp;
|
|
long stacksize;
|
|
long code_size, set_code_size;
|
|
long max_code, max_code_size;
|
|
long clear_code, end_code;
|
|
|
|
/*
|
|
* Were statics in procedures
|
|
*/
|
|
unsigned char buf[280];
|
|
unsigned short *table[2];
|
|
long tablesize;
|
|
long firstcode, oldcode;
|
|
|
|
} GIFINFO,*PGIFINFO;
|
|
|
|
/*
|
|
DirectAnimation wrapper class for GIF info
|
|
*/
|
|
class CImgGif
|
|
{
|
|
// Class methods
|
|
public:
|
|
CImgGif();
|
|
~CImgGif();
|
|
|
|
unsigned char * ReadGIFMaster();
|
|
BOOL Read(unsigned char *buffer, long len);
|
|
long ReadColorMap(long number, unsigned char buffer[3][MAXCOLORMAPSIZE]);
|
|
long DoExtension(long label);
|
|
long GetDataBlock(unsigned char *buf);
|
|
unsigned char * ReadImage(long len, long height, BOOL fInterlace, BOOL fGIFFrame);
|
|
long readLWZ();
|
|
long nextLWZ();
|
|
long nextCode(long code_size);
|
|
BOOL initLWZ(long input_code_size);
|
|
unsigned short * growStack();
|
|
BOOL growTables();
|
|
BITMAPINFO * FinishDithering();
|
|
|
|
// Data members
|
|
public:
|
|
LPCSTR _szFileName;
|
|
BOOL _fInterleaved;
|
|
BOOL _fInvalidateAll;
|
|
int _yLogRow;
|
|
GIFINFO _gifinfo;
|
|
GIFANIMATIONSTATE _gas;
|
|
GIFANIMDATA _gad;
|
|
PALETTEENTRY _ape[256];
|
|
int _xWidth;
|
|
int _yHeight;
|
|
LONG _lTrans;
|
|
BYTE * _pbBits;
|
|
|
|
} GIFIMAGE;
|
|
|
|
|
|
CImgGif::CImgGif() {
|
|
_gifinfo.pstack = NULL;
|
|
_gifinfo.table[0] = NULL;
|
|
_gifinfo.table[1] = NULL;
|
|
}
|
|
|
|
CImgGif::~CImgGif() {
|
|
free(_gifinfo.pstack);
|
|
free(_gifinfo.table[0]);
|
|
free(_gifinfo.table[1]);
|
|
PGIFFRAME nextPgf, curPgf;
|
|
curPgf = _gad.pgf;
|
|
while(curPgf != NULL) {
|
|
nextPgf = curPgf->pgfNext;
|
|
free(curPgf->ppixels);
|
|
free(curPgf->pcolors);
|
|
free(curPgf->pbmi);
|
|
free(curPgf);
|
|
curPgf = nextPgf;
|
|
}
|
|
}
|
|
|
|
static int GetColorMode() { return 0; };
|
|
|
|
#ifndef DEBUG
|
|
#pragma optimize("t",on)
|
|
#endif
|
|
|
|
BOOL CImgGif::Read(unsigned char *buffer, long len)
|
|
{
|
|
DWORD lenout = 0;
|
|
/* read len characters into buffer */
|
|
_gifinfo.stream->Read(buffer,len,&lenout);
|
|
|
|
return ((long)lenout == len);
|
|
}
|
|
|
|
long CImgGif::ReadColorMap(long number, unsigned char buffer[3][MAXCOLORMAPSIZE])
|
|
{
|
|
long i;
|
|
unsigned char rgb[3];
|
|
|
|
for (i = 0; i < number; ++i)
|
|
{
|
|
if (!Read(rgb, sizeof(rgb)))
|
|
{
|
|
//bw //bw TraceTag((tagImageDecode, "bad gif colormap."));
|
|
return (TRUE);
|
|
}
|
|
buffer[CM_RED][i] = rgb[0];
|
|
buffer[CM_GREEN][i] = rgb[1];
|
|
buffer[CM_BLUE][i] = rgb[2];
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
long
|
|
CImgGif::GetDataBlock(unsigned char *buf)
|
|
{
|
|
unsigned char count;
|
|
|
|
count = 0;
|
|
if (!Read(&count, 1))
|
|
{
|
|
return -1;
|
|
}
|
|
_gifinfo.ZeroDataBlock = count == 0;
|
|
|
|
if ((count != 0) && (!Read(buf, count)))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return ((long) count);
|
|
}
|
|
|
|
#define MIN_CODE_BITS 5
|
|
#define MIN_STACK_SIZE 64
|
|
#define MINIMUM_CODE_SIZE 2
|
|
|
|
BOOL CImgGif::initLWZ(long input_code_size)
|
|
{
|
|
if(input_code_size < MINIMUM_CODE_SIZE)
|
|
return FALSE;
|
|
|
|
_gifinfo.set_code_size = input_code_size;
|
|
_gifinfo.code_size = _gifinfo.set_code_size + 1;
|
|
_gifinfo.clear_code = 1 << _gifinfo.set_code_size;
|
|
_gifinfo.end_code = _gifinfo.clear_code + 1;
|
|
_gifinfo.max_code_size = 2 * _gifinfo.clear_code;
|
|
_gifinfo.max_code = _gifinfo.clear_code + 2;
|
|
|
|
_gifinfo.curbit = _gifinfo.lastbit = 0;
|
|
_gifinfo.last_byte = 2;
|
|
_gifinfo.get_done = FALSE;
|
|
|
|
_gifinfo.return_clear = TRUE;
|
|
|
|
if(input_code_size >= MIN_CODE_BITS)
|
|
_gifinfo.stacksize = ((1 << (input_code_size)) * 2);
|
|
else
|
|
_gifinfo.stacksize = MIN_STACK_SIZE;
|
|
|
|
if ( _gifinfo.pstack != NULL )
|
|
free( _gifinfo.pstack );
|
|
if ( _gifinfo.table[0] != NULL )
|
|
free( _gifinfo.table[0] );
|
|
if ( _gifinfo.table[1] != NULL )
|
|
free( _gifinfo.table[1] );
|
|
|
|
|
|
_gifinfo.table[0] = 0;
|
|
_gifinfo.table[1] = 0;
|
|
_gifinfo.pstack = 0;
|
|
|
|
_gifinfo.pstack = (unsigned short *) malloc((_gifinfo.stacksize)*sizeof(unsigned short));
|
|
if(_gifinfo.pstack == 0){
|
|
goto ErrorExit;
|
|
}
|
|
_gifinfo.sp = _gifinfo.pstack;
|
|
|
|
// Initialize the two tables.
|
|
_gifinfo.tablesize = (_gifinfo.max_code_size);
|
|
|
|
_gifinfo.table[0] = (unsigned short *) malloc((_gifinfo.tablesize)*sizeof(unsigned short));
|
|
_gifinfo.table[1] = (unsigned short *) malloc((_gifinfo.tablesize)*sizeof(unsigned short));
|
|
if((_gifinfo.table[0] == 0) || (_gifinfo.table[1] == 0)){
|
|
goto ErrorExit;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
ErrorExit:
|
|
if(_gifinfo.pstack){
|
|
free(_gifinfo.pstack);
|
|
_gifinfo.pstack = 0;
|
|
}
|
|
|
|
if(_gifinfo.table[0]){
|
|
free(_gifinfo.table[0]);
|
|
_gifinfo.table[0] = 0;
|
|
}
|
|
|
|
if(_gifinfo.table[1]){
|
|
free(_gifinfo.table[1]);
|
|
_gifinfo.table[1] = 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
long CImgGif::nextCode(long code_size)
|
|
{
|
|
static const long maskTbl[16] =
|
|
{
|
|
0x0000, 0x0001, 0x0003, 0x0007,
|
|
0x000f, 0x001f, 0x003f, 0x007f,
|
|
0x00ff, 0x01ff, 0x03ff, 0x07ff,
|
|
0x0fff, 0x1fff, 0x3fff, 0x7fff,
|
|
};
|
|
long i, j, ret, end;
|
|
unsigned char *buf = &_gifinfo.buf[0];
|
|
|
|
if (_gifinfo.return_clear)
|
|
{
|
|
_gifinfo.return_clear = FALSE;
|
|
return _gifinfo.clear_code;
|
|
}
|
|
|
|
end = _gifinfo.curbit + code_size;
|
|
|
|
if (end >= _gifinfo.lastbit)
|
|
{
|
|
long count;
|
|
|
|
if (_gifinfo.get_done)
|
|
{
|
|
return -1;
|
|
}
|
|
buf[0] = buf[_gifinfo.last_byte - 2];
|
|
buf[1] = buf[_gifinfo.last_byte - 1];
|
|
|
|
if ((count = GetDataBlock(&buf[2])) == 0)
|
|
_gifinfo.get_done = TRUE;
|
|
if (count < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
_gifinfo.last_byte = 2 + count;
|
|
_gifinfo.curbit = (_gifinfo.curbit - _gifinfo.lastbit) + 16;
|
|
_gifinfo.lastbit = (2 + count) * 8;
|
|
|
|
end = _gifinfo.curbit + code_size;
|
|
|
|
// Okay, bug 30784 time. It's possible that we only got 1
|
|
// measly byte in the last data block. Rare, but it does happen.
|
|
// In that case, the additional byte may still not supply us with
|
|
// enough bits for the next code, so, as Mars Needs Women, IE
|
|
// Needs Data.
|
|
if ( end >= _gifinfo.lastbit && !_gifinfo.get_done )
|
|
{
|
|
// protect ourselve from the ( theoretically impossible )
|
|
// case where between the last data block, the 2 bytes from
|
|
// the block preceding that, and the potential 0xFF bytes in
|
|
// the next block, we overflow the buffer.
|
|
// Since count should always be 1,
|
|
//bw Assert ( count == 1 );
|
|
// there should be enough room in the buffer, so long as someone
|
|
// doesn't shrink it.
|
|
if ( count + 0x101 >= sizeof( _gifinfo.buf ) )
|
|
{
|
|
//bw Assert ( FALSE ); //
|
|
return -1;
|
|
}
|
|
|
|
if ((count = GetDataBlock(&buf[2 + count])) == 0)
|
|
_gifinfo.get_done = TRUE;
|
|
if (count < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
_gifinfo.last_byte += count;
|
|
_gifinfo.lastbit = _gifinfo.last_byte * 8;
|
|
|
|
end = _gifinfo.curbit + code_size;
|
|
}
|
|
}
|
|
|
|
j = end / 8;
|
|
i = _gifinfo.curbit / 8;
|
|
|
|
if (i == j)
|
|
ret = buf[i];
|
|
else if (i + 1 == j)
|
|
ret = buf[i] | (((long) buf[i + 1]) << 8);
|
|
else
|
|
ret = buf[i] | (((long) buf[i + 1]) << 8) | (((long) buf[i + 2]) << 16);
|
|
|
|
ret = (ret >> (_gifinfo.curbit % 8)) & maskTbl[code_size];
|
|
|
|
_gifinfo.curbit += code_size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Grows the stack and returns the top of the stack.
|
|
unsigned short *
|
|
CImgGif::growStack()
|
|
{
|
|
long index;
|
|
unsigned short *lp;
|
|
|
|
if (_gifinfo.stacksize >= MAX_STACK_SIZE) return 0;
|
|
|
|
index = long(_gifinfo.sp - _gifinfo.pstack);
|
|
lp = (unsigned short *)realloc(_gifinfo.pstack, (_gifinfo.stacksize)*2*sizeof(unsigned short));
|
|
if(lp == 0)
|
|
return 0;
|
|
|
|
_gifinfo.pstack = lp;
|
|
_gifinfo.sp = &(_gifinfo.pstack[index]);
|
|
_gifinfo.stacksize = (_gifinfo.stacksize)*2;
|
|
lp = &(_gifinfo.pstack[_gifinfo.stacksize]);
|
|
return lp;
|
|
}
|
|
|
|
BOOL
|
|
CImgGif::growTables()
|
|
{
|
|
unsigned short *lp;
|
|
|
|
lp = (unsigned short *) realloc(_gifinfo.table[0], (_gifinfo.max_code_size)*sizeof(unsigned short));
|
|
if(lp == 0){
|
|
return FALSE;
|
|
}
|
|
_gifinfo.table[0] = lp;
|
|
|
|
lp = (unsigned short *) realloc(_gifinfo.table[1], (_gifinfo.max_code_size)*sizeof(unsigned short));
|
|
if(lp == 0){
|
|
return FALSE;
|
|
}
|
|
_gifinfo.table[1] = lp;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
inline
|
|
long CImgGif::readLWZ()
|
|
{
|
|
return((_gifinfo.sp > _gifinfo.pstack) ? *--(_gifinfo.sp) : nextLWZ());
|
|
}
|
|
|
|
#define CODE_MASK 0xffff
|
|
long CImgGif::nextLWZ()
|
|
{
|
|
long code, incode;
|
|
unsigned short usi;
|
|
unsigned short *table0 = _gifinfo.table[0];
|
|
unsigned short *table1 = _gifinfo.table[1];
|
|
unsigned short *pstacktop = &(_gifinfo.pstack[_gifinfo.stacksize]);
|
|
|
|
while ((code = nextCode(_gifinfo.code_size)) >= 0)
|
|
{
|
|
if (code == _gifinfo.clear_code)
|
|
{
|
|
/* corrupt GIFs can make this happen */
|
|
if (_gifinfo.clear_code >= (1 << MAX_LWZ_BITS))
|
|
{
|
|
return -2;
|
|
}
|
|
|
|
|
|
_gifinfo.code_size = _gifinfo.set_code_size + 1;
|
|
_gifinfo.max_code_size = 2 * _gifinfo.clear_code;
|
|
_gifinfo.max_code = _gifinfo.clear_code + 2;
|
|
|
|
if(!growTables())
|
|
return -2;
|
|
|
|
table0 = _gifinfo.table[0];
|
|
table1 = _gifinfo.table[1];
|
|
|
|
_gifinfo.tablesize = _gifinfo.max_code_size;
|
|
|
|
|
|
for (usi = 0; usi < _gifinfo.clear_code; ++usi)
|
|
{
|
|
table1[usi] = usi;
|
|
}
|
|
memset(table0,0,sizeof(unsigned short )*(_gifinfo.tablesize));
|
|
memset(&table1[_gifinfo.clear_code],0,sizeof(unsigned short)*((_gifinfo.tablesize)-_gifinfo.clear_code));
|
|
_gifinfo.sp = _gifinfo.pstack;
|
|
do
|
|
{
|
|
_gifinfo.firstcode = _gifinfo.oldcode = nextCode(_gifinfo.code_size);
|
|
}
|
|
while (_gifinfo.firstcode == _gifinfo.clear_code);
|
|
|
|
return _gifinfo.firstcode;
|
|
}
|
|
if (code == _gifinfo.end_code)
|
|
{
|
|
long count;
|
|
unsigned char buf[260];
|
|
|
|
if (_gifinfo.ZeroDataBlock)
|
|
{
|
|
return -2;
|
|
}
|
|
|
|
while ((count = GetDataBlock(buf)) > 0)
|
|
;
|
|
|
|
if (count != 0)
|
|
return -2;
|
|
}
|
|
|
|
incode = code;
|
|
|
|
if (code >= _gifinfo.max_code)
|
|
{
|
|
if (_gifinfo.sp >= pstacktop){
|
|
pstacktop = growStack();
|
|
if(pstacktop == 0)
|
|
return -2;
|
|
}
|
|
*(_gifinfo.sp)++ = (unsigned short)((CODE_MASK ) & (_gifinfo.firstcode));
|
|
code = _gifinfo.oldcode;
|
|
}
|
|
|
|
#if FEATURE_FAST
|
|
// (andyp) easy speedup here for ie3.1 (too late for ie3.0):
|
|
//
|
|
// 1. move growStack code out of loop (use max 12-bit/4k slop).
|
|
// 2. do "sp = _gifinfo.sp" so it will get enreg'ed.
|
|
// 3. un-inline growStack (and growTables).
|
|
// 4. change short's to int's (benefits win32) (esp. table1 & table2)
|
|
// (n.b. int not long, so we'll keep win3.1 perf)
|
|
// 5. change long's to int's (benefits win16) (esp. code).
|
|
//
|
|
// together these will make the loop very tight w/ everything kept
|
|
// enregistered and no 66 overrides.
|
|
//
|
|
// one caveat is that on average this loop iterates 4x so it's
|
|
// not clear how much the speedup will really gain us until we
|
|
// look at the outer loop as well.
|
|
#endif
|
|
while (code >= _gifinfo.clear_code)
|
|
{
|
|
if (_gifinfo.sp >= pstacktop){
|
|
pstacktop = growStack();
|
|
if(pstacktop == 0)
|
|
return -2;
|
|
}
|
|
*(_gifinfo.sp)++ = table1[code];
|
|
if (code == (long)(table0[code]))
|
|
{
|
|
return (code);
|
|
}
|
|
code = (long)(table0[code]);
|
|
}
|
|
|
|
if (_gifinfo.sp >= pstacktop){
|
|
pstacktop = growStack();
|
|
if(pstacktop == 0)
|
|
return -2;
|
|
}
|
|
_gifinfo.firstcode = (long)table1[code];
|
|
*(_gifinfo.sp)++ = table1[code];
|
|
|
|
if ((code = _gifinfo.max_code) < (1 << MAX_LWZ_BITS))
|
|
{
|
|
table0[code] = (USHORT)(_gifinfo.oldcode) & CODE_MASK;
|
|
table1[code] = (USHORT)(_gifinfo.firstcode) & CODE_MASK;
|
|
++_gifinfo.max_code;
|
|
if ((_gifinfo.max_code >= _gifinfo.max_code_size) && (_gifinfo.max_code_size < ((1 << MAX_LWZ_BITS))))
|
|
{
|
|
_gifinfo.max_code_size *= 2;
|
|
++_gifinfo.code_size;
|
|
if(!growTables())
|
|
return -2;
|
|
|
|
table0 = _gifinfo.table[0];
|
|
table1 = _gifinfo.table[1];
|
|
|
|
// Tables have been reallocated to the correct size but initialization
|
|
// still remains to be done. This initialization is different from
|
|
// the first time initialization of these tables.
|
|
memset(&(table0[_gifinfo.tablesize]),0,
|
|
sizeof(unsigned short )*(_gifinfo.max_code_size - _gifinfo.tablesize));
|
|
|
|
memset(&(table1[_gifinfo.tablesize]),0,
|
|
sizeof(unsigned short )*(_gifinfo.max_code_size - _gifinfo.tablesize));
|
|
|
|
_gifinfo.tablesize = (_gifinfo.max_code_size);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
_gifinfo.oldcode = incode;
|
|
|
|
if (_gifinfo.sp > _gifinfo.pstack)
|
|
return ((long)(*--(_gifinfo.sp)));
|
|
}
|
|
return code;
|
|
}
|
|
|
|
#ifndef DEBUG
|
|
// Return to default optimization flags
|
|
#pragma optimize("",on)
|
|
#endif
|
|
|
|
unsigned char *
|
|
CImgGif::ReadImage(long len, long height, BOOL fInterlace, BOOL fGIFFrame)
|
|
{
|
|
unsigned char *dp, c;
|
|
long v;
|
|
long xpos = 0, ypos = 0, pass = 0;
|
|
unsigned char *image;
|
|
long padlen = ((len + 3) / 4) * 4;
|
|
DWORD cbImage = 0;
|
|
char buf[256]; // need a buffer to read trailing blocks ( up to terminator ) into
|
|
//ULONG ulCoversImg = IMGBITS_PARTIAL;
|
|
|
|
/*
|
|
** Initialize the Compression routines
|
|
*/
|
|
if (!Read(&c, 1))
|
|
{
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
** If this is an "uninteresting picture" ignore it.
|
|
*/
|
|
|
|
cbImage = padlen * height * sizeof(char);
|
|
|
|
if ( cbImage > dwMaxGIFBits
|
|
|| (image = (unsigned char *) _calloc(1, cbImage)) == NULL)
|
|
{
|
|
//bw TraceTag((tagImageDecode, "Cannot allocate space for gif image data\n"));
|
|
return (NULL);
|
|
}
|
|
|
|
if (c == 1)
|
|
{
|
|
// Netscape seems to field these bogus GIFs by filling treating them
|
|
// as transparent. While not the optimal way to simulate this effect,
|
|
// we'll fake it by pushing the initial code size up to a safe value,
|
|
// consuming the input, and returning a buffer full of the transparent
|
|
// color or zero, if no transparency is indicated.
|
|
if (initLWZ(MINIMUM_CODE_SIZE))
|
|
while (readLWZ() >= 0);
|
|
else {
|
|
//bw TraceTag((tagImageDecode, "GIF: failed LZW decode.\n"));
|
|
return (NULL);
|
|
}
|
|
|
|
if (_gifinfo.Gif89.transparent != -1)
|
|
FillMemory(image, cbImage, (BYTE)_gifinfo.Gif89.transparent);
|
|
else // fall back on the background color
|
|
FillMemory(image, cbImage, 0);
|
|
|
|
return image;
|
|
}
|
|
else if (initLWZ(c) == FALSE)
|
|
{
|
|
free(image);
|
|
//bw TraceTag((tagImageDecode, "GIF: failed LZW decode.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
if (!fGIFFrame)
|
|
_pbBits = image;
|
|
|
|
if (fInterlace)
|
|
{
|
|
long i;
|
|
long pass = 0, step = 8;
|
|
|
|
if (!fGIFFrame && (height > 4))
|
|
_fInterleaved = TRUE;
|
|
|
|
for (i = 0; i < height; i++)
|
|
{
|
|
// message("readimage, logical=%d, offset=%d\n", i, padlen * ((height-1) - ypos));
|
|
dp = &image[padlen * ((height-1) - ypos)];
|
|
for (xpos = 0; xpos < len; xpos++)
|
|
{
|
|
if ((v = readLWZ()) < 0)
|
|
goto abort;
|
|
|
|
*dp++ = (unsigned char) v;
|
|
}
|
|
ypos += step;
|
|
while (ypos >= height)
|
|
{
|
|
if (pass++ > 0)
|
|
step /= 2;
|
|
ypos = step / 2;
|
|
/*if (!fGIFFrame && pass == 1)
|
|
{
|
|
ulCoversImg = IMGBITS_TOTAL;
|
|
}*/
|
|
}
|
|
if (!fGIFFrame)
|
|
{
|
|
_yLogRow = i;
|
|
|
|
/*if ((i & PROG_INTERVAL) == 0)
|
|
{
|
|
// Post ProgDraw (IE code has delay-logic)
|
|
OnProg(FALSE, ulCoversImg);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
/*if (!fGIFFrame)
|
|
{
|
|
OnProg(TRUE, ulCoversImg);
|
|
}*/
|
|
|
|
if (!fGIFFrame && height <= 4)
|
|
{
|
|
_yLogRow = height-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
if (!fGIFFrame)
|
|
_yLogRow = -1;
|
|
|
|
for (ypos = height-1; ypos >= 0; ypos--)
|
|
{
|
|
dp = &image[padlen * ypos];
|
|
for (xpos = 0; xpos < len; xpos++)
|
|
{
|
|
if ((v = readLWZ()) < 0)
|
|
goto abort;
|
|
|
|
*dp++ = (unsigned char) v;
|
|
}
|
|
if (!fGIFFrame)
|
|
{
|
|
_yLogRow++;
|
|
// message("readimage, logical=%d, offset=%d\n", _yLogRow, padlen * ypos);
|
|
/*if ((_yLogRow & PROG_INTERVAL) == 0)
|
|
{
|
|
// Post ProgDraw (IE code has delay-logic)
|
|
OnProg(FALSE, ulCoversImg);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
/*if (!fGIFFrame)
|
|
{
|
|
OnProg(TRUE, ulCoversImg);
|
|
}*/
|
|
}
|
|
|
|
// consume blocks up to image block terminator so we can proceed to the next image
|
|
while (GetDataBlock((unsigned char *) buf) > 0)
|
|
;
|
|
return (image);
|
|
|
|
abort:
|
|
/*if (!fGIFFrame)
|
|
OnProg(TRUE, ulCoversImg);*/
|
|
return NULL;
|
|
}
|
|
|
|
long CImgGif::DoExtension(long label)
|
|
{
|
|
unsigned char buf[256];
|
|
int count;
|
|
|
|
switch (label)
|
|
{
|
|
case 0x01: /* Plain Text Extension */
|
|
break;
|
|
case 0xff: /* Application Extension */
|
|
// Is it the Netscape looping extension
|
|
count = GetDataBlock((unsigned char *) buf);
|
|
if (count >= 11)
|
|
{
|
|
char *szNSExt = "NETSCAPE2.0";
|
|
|
|
if ( memcmp( buf, szNSExt, strlen( szNSExt ) ) == 0 )
|
|
{ // if it has their signature, get the data subblock with the iter count
|
|
count = GetDataBlock((unsigned char *) buf);
|
|
if ( count >= 3 )
|
|
{
|
|
_gad.fLooped = TRUE;
|
|
_gad.cLoops = (buf[2] << 8) | buf[1];
|
|
}
|
|
}
|
|
}
|
|
while (GetDataBlock((unsigned char *) buf) > 0)
|
|
;
|
|
return FALSE;
|
|
break;
|
|
case 0xfe: /* Comment Extension */
|
|
while (GetDataBlock((unsigned char *) buf) > 0)
|
|
{
|
|
//bw TraceTag((tagImageDecode, "GIF comment: %s\n", buf));
|
|
}
|
|
return FALSE;
|
|
case 0xf9: /* Graphic Control Extension */
|
|
count = GetDataBlock((unsigned char *) buf);
|
|
if (count >= 3)
|
|
{
|
|
_gifinfo.Gif89.disposal = (buf[0] >> 2) & 0x7;
|
|
_gifinfo.Gif89.inputFlag = (buf[0] >> 1) & 0x1;
|
|
_gifinfo.Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
|
|
if ((buf[0] & 0x1) != 0)
|
|
_gifinfo.Gif89.transparent = buf[3];
|
|
else
|
|
_gifinfo.Gif89.transparent = -1;
|
|
}
|
|
while (GetDataBlock((unsigned char *) buf) > 0)
|
|
;
|
|
return FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
while (GetDataBlock((unsigned char *) buf) > 0)
|
|
;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL IsGifHdr(BYTE * pb)
|
|
{
|
|
return(pb[0] == 'G' && pb[1] == 'I' && pb[2] == 'F'
|
|
&& pb[3] == '8' && (pb[4] == '7' || pb[4] == '9') && pb[5] == 'a');
|
|
}
|
|
|
|
|
|
PBITMAPINFO x_8BPIBitmap(int xsize, int ysize)
|
|
{
|
|
PBITMAPINFO pbmi;
|
|
|
|
if (GetColorMode() == 8)
|
|
{
|
|
pbmi = (PBITMAPINFO) _calloc(1, sizeof(BITMAPINFOHEADER) + 256 * sizeof(WORD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 8;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 256;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
}
|
|
else
|
|
{
|
|
pbmi = (PBITMAPINFO) _calloc(1, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 8;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 256;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
}
|
|
return pbmi;
|
|
}
|
|
|
|
/*
|
|
For color images.
|
|
This routine should only be used when drawing to an 8 bit palette screen.
|
|
It always creates a DIB in DIB_PAL_COLORS format.
|
|
*/
|
|
PBITMAPINFO BIT_Make_DIB_PAL_Header(int xsize, int ysize)
|
|
{
|
|
int i;
|
|
PBITMAPINFO pbmi;
|
|
WORD *pw;
|
|
|
|
pbmi = x_8BPIBitmap(xsize, ysize);
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pw = (WORD *) pbmi->bmiColors;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
pw[i] = (WORD)i;
|
|
}
|
|
|
|
return pbmi;
|
|
}
|
|
|
|
/*
|
|
For color images.
|
|
This routine is used when drawing to the nonpalette screens. It always creates
|
|
DIBs in DIB_RGB_COLORS format.
|
|
If there is a transparent color, it is modified in the palette to be the
|
|
background color for the window.
|
|
*/
|
|
PBITMAPINFO BIT_Make_DIB_RGB_Header_Screen(int xsize, int ysize,
|
|
int cEntries, PALETTEENTRY * rgpe, int transparent)
|
|
{
|
|
int i;
|
|
PBITMAPINFO pbmi;
|
|
|
|
pbmi = x_8BPIBitmap(xsize, ysize);
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < cEntries; i++)
|
|
{
|
|
pbmi->bmiColors[i].rgbRed = rgpe[i].peRed;
|
|
pbmi->bmiColors[i].rgbGreen = rgpe[i].peGreen;
|
|
pbmi->bmiColors[i].rgbBlue = rgpe[i].peBlue;
|
|
pbmi->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
|
|
/*
|
|
if (transparent != -1)
|
|
{
|
|
COLORREF color;
|
|
|
|
color = PREF_GetBackgroundColor();
|
|
pbmi->bmiColors[transparent].rgbRed = GetRValue(color);
|
|
pbmi->bmiColors[transparent].rgbGreen = GetGValue(color);
|
|
pbmi->bmiColors[transparent].rgbBlue = GetBValue(color);
|
|
}
|
|
*/
|
|
return pbmi;
|
|
}
|
|
|
|
unsigned char *
|
|
CImgGif::ReadGIFMaster()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
unsigned char buf[16];
|
|
unsigned char c;
|
|
unsigned char localColorMap[3][MAXCOLORMAPSIZE];
|
|
long useGlobalColormap;
|
|
long imageCount = 0;
|
|
long imageNumber = 1;
|
|
unsigned char *image = NULL;
|
|
unsigned long i;
|
|
GIFSCREEN GifScreen;
|
|
long bitPixel;
|
|
PGIFFRAME pgfLast = NULL;
|
|
PGIFFRAME pgfNew;
|
|
|
|
_gifinfo.ZeroDataBlock = 0;
|
|
|
|
/*
|
|
* Initialize GIF89 extensions
|
|
*/
|
|
_gifinfo.Gif89.transparent = -1;
|
|
_gifinfo.Gif89.delayTime = 5;
|
|
_gifinfo.Gif89.inputFlag = -1;
|
|
_gifinfo.Gif89.disposal = 0;
|
|
_gifinfo.lGifLoc = 0;
|
|
|
|
// initialize our animation fields
|
|
_gad.fAnimated = FALSE; // set to TRUE if we see more than one image
|
|
_gad.fLooped = FALSE; // TRUE if we've seen a Netscape loop block
|
|
_gad.fHasTransparency = FALSE; // until proven otherwise
|
|
_gad.fNoBWMapping = FALSE;
|
|
_gad.dwGIFVer = dwGIFVerUnknown;
|
|
_gad.cLoops = 0;
|
|
_gad.pgf = NULL;
|
|
_gad.pcolorsGlobal = NULL;
|
|
|
|
if (!Read(buf, 6))
|
|
{
|
|
//bw TraceTag((tagImageDecode, "GIF: error reading magic number\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
if (!IsGifHdr(buf)) {
|
|
//bw TraceTag((tagImageDecode, "GIF: Malformed header\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
_gad.dwGIFVer = (buf[4] == '7') ? dwGIFVer87a : dwGIFVer89a;
|
|
|
|
if (!Read(buf, 7))
|
|
{
|
|
//bw TraceTag((tagImageDecode, "GIF: failed to read screen descriptor\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
GifScreen.Width = LM_to_uint(buf[0], buf[1]);
|
|
GifScreen.Height = LM_to_uint(buf[2], buf[3]);
|
|
GifScreen.BitPixel = 2 << (buf[4] & 0x07);
|
|
GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
|
|
GifScreen.Background = buf[5];
|
|
GifScreen.AspectRatio = buf[6];
|
|
|
|
if (BitSet(buf[4], LOCALCOLORMAP))
|
|
{ /* Global Colormap */
|
|
int scale = 65536 / MAXCOLORMAPSIZE;
|
|
|
|
if (ReadColorMap(GifScreen.BitPixel, GifScreen.ColorMap))
|
|
{
|
|
//bw TraceTag((tagImageDecode, "error reading global colormap\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
for (i = 0; i < GifScreen.BitPixel; i++)
|
|
{
|
|
int tmp;
|
|
|
|
tmp = (BYTE) (GifScreen.ColorMap[0][i]);
|
|
_ape[i].peRed = (BYTE) (GifScreen.ColorMap[0][i]);
|
|
_ape[i].peGreen = (BYTE) (GifScreen.ColorMap[1][i]);
|
|
_ape[i].peBlue = (BYTE) (GifScreen.ColorMap[2][i]);
|
|
_ape[i].peFlags = (BYTE) 0;
|
|
}
|
|
for (i = GifScreen.BitPixel; i < MAXCOLORMAPSIZE; i++)
|
|
{
|
|
_ape[i].peRed = (BYTE) 0;
|
|
_ape[i].peGreen = (BYTE) 0;
|
|
_ape[i].peBlue = (BYTE) 0;
|
|
_ape[i].peFlags = (BYTE) 0;
|
|
}
|
|
}
|
|
|
|
if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49)
|
|
{
|
|
float r;
|
|
r = ((float) (GifScreen.AspectRatio) + (float) 15.0) / (float) 64.0;
|
|
//bw TraceTag((tagImageDecode, "Warning: non-square pixels!\n"));
|
|
}
|
|
|
|
for (;; ) // our appetite now knows no bounds save termination or error
|
|
{
|
|
if (!Read(&c, 1))
|
|
{
|
|
//bw TraceTag((tagImageDecode, "EOF / read error on image data\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
if (c == ';')
|
|
{ /* GIF terminator */
|
|
if (imageCount < imageNumber)
|
|
{
|
|
//bw TraceTag((tagImageDecode, "No images found in file\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (c == '!')
|
|
{ /* Extension */
|
|
if (!Read(&c, 1))
|
|
{
|
|
//bw TraceTag((tagImageDecode, "EOF / read error on extension function code\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
DoExtension(c);
|
|
continue;
|
|
}
|
|
|
|
if (c != ',')
|
|
{ /* Not a valid start character */
|
|
break;
|
|
}
|
|
|
|
++imageCount;
|
|
|
|
if (!Read(buf, 9))
|
|
{
|
|
//bw TraceTag((tagImageDecode, "couldn't read left/top/width/height\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
|
|
|
|
bitPixel = 1 << ((buf[8] & 0x07) + 1);
|
|
|
|
/*
|
|
* We only want to set width and height for the imageNumber
|
|
* we are requesting.
|
|
*/
|
|
if (imageCount == imageNumber)
|
|
{
|
|
// Replicate some of Netscape's special cases:
|
|
// Don't use the logical screen if it's a GIF87a and the topLeft of the first image is at the origin.
|
|
// Don't use the logical screen if the first image spills out of the logical screen.
|
|
// These are artifacts of primitive authoring tools falling into the hands of hapless users.
|
|
RECT rectImage; // rect defining bounds of GIF
|
|
RECT rectLS; // rect defining bounds of GIF logical screen.
|
|
RECT rectSect; // intersection of image an logical screen
|
|
BOOL fNoSpill; // True if the image doesn't spill out of the logical screen
|
|
BOOL fGoofy87a; // TRUE if its one of the 87a pathologies that Netscape special cases
|
|
|
|
rectImage.left = LM_to_uint(buf[0], buf[1]);
|
|
rectImage.top = LM_to_uint(buf[2], buf[3]);
|
|
rectImage.right = rectImage.left + LM_to_uint(buf[4], buf[5]);
|
|
rectImage.bottom = rectImage.top + LM_to_uint(buf[6], buf[7]);
|
|
rectLS.left = rectLS.top = 0;
|
|
rectLS.right = GifScreen.Width;
|
|
rectLS.bottom = GifScreen.Height;
|
|
IntersectRect( &rectSect, &rectImage, &rectLS );
|
|
fNoSpill = EqualRect( &rectImage, &rectSect );
|
|
fGoofy87a = FALSE;
|
|
if (_gad.dwGIFVer == dwGIFVer87a)
|
|
{
|
|
// netscape ignores the logical screen if the image is flush against
|
|
// either the upper left or lower right corner
|
|
fGoofy87a = (rectImage.top == 0 && rectImage.left == 0) ||
|
|
(rectImage.bottom == rectLS.bottom &&
|
|
rectImage.right == rectLS.right);
|
|
}
|
|
|
|
if (!fGoofy87a && fNoSpill)
|
|
{
|
|
_xWidth = GifScreen.Width;
|
|
_yHeight = GifScreen.Height;
|
|
}
|
|
else
|
|
{
|
|
// Something is amiss. Fall back to the image's dimensions.
|
|
|
|
// If the sizes match, but the image is offset, or we're ignoring
|
|
// the logical screen cuz it's a goofy 87a, then pull it back to
|
|
// to the origin
|
|
if ((LM_to_uint(buf[4], buf[5]) == GifScreen.Width &&
|
|
LM_to_uint(buf[6], buf[7]) == GifScreen.Height) ||
|
|
fGoofy87a)
|
|
{
|
|
buf[0] = buf[1] = 0; // left corner to zero
|
|
buf[2] = buf[3] = 0; // top to zero.
|
|
}
|
|
|
|
_xWidth = LM_to_uint(buf[4], buf[5]);
|
|
_yHeight = LM_to_uint(buf[6], buf[7]);
|
|
}
|
|
|
|
_lTrans = _gifinfo.Gif89.transparent;
|
|
|
|
// Post WHKNOWN
|
|
//OnSize(_xWidth, _yHeight, _lTrans);
|
|
}
|
|
|
|
if (!useGlobalColormap)
|
|
{
|
|
if (ReadColorMap(bitPixel, localColorMap))
|
|
{
|
|
//bw TraceTag((tagImageDecode, "error reading local colormap\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// We allocate a frame record for each imag in the GIF stream, including
|
|
// the first/primary image.
|
|
pgfNew = (PGIFFRAME) _calloc(1, sizeof(GIFFRAME));
|
|
|
|
if ( pgfNew == NULL )
|
|
{
|
|
//bw TraceTag((tagImageDecode, "not enough memory for GIF frame\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
if ( _gifinfo.Gif89.delayTime != -1 )
|
|
{
|
|
// we have a fresh control extension for this block
|
|
|
|
// convert to milliseconds
|
|
pgfNew->gced.uiDelayTime = _gifinfo.Gif89.delayTime * 10;
|
|
|
|
|
|
//REVIEW(seanf): crude hack to cope with 'degenerate animations' whose timing is set to some
|
|
// small value becaue of the delays imposed by Netscape's animation process
|
|
if ( pgfNew->gced.uiDelayTime <= 50 ) // assume these small values imply Netscape encoding delay
|
|
pgfNew->gced.uiDelayTime = 100; // pick a larger value s.t. the frame will be visible
|
|
pgfNew->gced.uiDisposalMethod = _gifinfo.Gif89.disposal;
|
|
pgfNew->gced.fTransparent = _gifinfo.Gif89.transparent != -1;
|
|
pgfNew->gced.ucTransIndex = (unsigned char)_gifinfo.Gif89.transparent;
|
|
|
|
}
|
|
else
|
|
{ // fake one up s.t. GIFs that rely solely on Netscape's delay to time their animations will play
|
|
// The spec says that the scope of one of these blocks is the image after the block.
|
|
// Netscape says 'until further notice'. So we play it their way up to a point. We
|
|
// propagate the disposal method and transparency. Since Netscape doesn't honor the timing
|
|
// we use our default timing for these images.
|
|
pgfNew->gced.uiDelayTime = 100;
|
|
pgfNew->gced.uiDisposalMethod = _gifinfo.Gif89.disposal;
|
|
pgfNew->gced.fTransparent = _gifinfo.Gif89.transparent != -1;
|
|
pgfNew->gced.ucTransIndex = (unsigned char)_gifinfo.Gif89.transparent;
|
|
}
|
|
|
|
pgfNew->top = LM_to_uint(buf[2], buf[3]); // bounds relative to the GIF logical screen
|
|
pgfNew->left = LM_to_uint(buf[0], buf[1]);
|
|
pgfNew->width = LM_to_uint(buf[4], buf[5]);
|
|
pgfNew->height = LM_to_uint(buf[6], buf[7]);
|
|
|
|
// Images that are offset, or do not cover the full logical screen are 'transparent' in the
|
|
// sense that they require us to matte the frame onto the background.
|
|
|
|
if (!_gad.fHasTransparency && (pgfNew->gced.fTransparent ||
|
|
pgfNew->top != 0 ||
|
|
pgfNew->left != 0 ||
|
|
(UINT)pgfNew->width != (UINT)GifScreen.Width ||
|
|
(UINT)pgfNew->height != (UINT)GifScreen.Height))
|
|
{
|
|
_gad.fHasTransparency = TRUE;
|
|
//if (_lTrans == -1)
|
|
// OnTrans(0);
|
|
}
|
|
|
|
// We don't need to allocate a handle for the simple region case.
|
|
// FrancisH says Windows is too much of a cheapskate to allow us the simplicity
|
|
// of allocating the region once and modifying as needed. Well, okay, he didn't
|
|
// put it that way...
|
|
pgfNew->hrgnVis = NULL;
|
|
pgfNew->iRgnKind = NULLREGION;
|
|
|
|
if (!useGlobalColormap)
|
|
{
|
|
// remember that we saw a local color table and only map two-color images
|
|
// if we have a homogenous color environment
|
|
_gad.fNoBWMapping = _gad.fNoBWMapping || bitPixel > 2;
|
|
|
|
// CALLOC will set unused colors to <0,0,0,0>
|
|
pgfNew->pcolors = (PALETTEENTRY *) _calloc(MAXCOLORMAPSIZE, sizeof(PALETTEENTRY));
|
|
if ( pgfNew->pcolors == NULL )
|
|
{
|
|
DeleteRgn( pgfNew->hrgnVis );
|
|
free( pgfNew );
|
|
|
|
//bw TraceTag((tagImageDecode, "not enough memory for GIF frame colors\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (ULONG)bitPixel; ++i)
|
|
{
|
|
pgfNew->pcolors[i].peRed = localColorMap[CM_RED][i];
|
|
pgfNew->pcolors[i].peGreen = localColorMap[CM_GREEN][i];
|
|
pgfNew->pcolors[i].peBlue = localColorMap[CM_BLUE][i];
|
|
}
|
|
pgfNew->cColors = bitPixel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( _gad.pcolorsGlobal == NULL )
|
|
{ // Whoa! Somebody's interested in the global color table
|
|
// CALLOC will set unused colors to <0,0,0,0>
|
|
_gad.pcolorsGlobal = (PALETTEENTRY *) _calloc(MAXCOLORMAPSIZE, sizeof(PALETTEENTRY));
|
|
_gad.fNoBWMapping = _gad.fNoBWMapping || GifScreen.BitPixel > 2;
|
|
if ( _gad.pcolorsGlobal != NULL )
|
|
{
|
|
CopyMemory(_gad.pcolorsGlobal, _ape,
|
|
GifScreen.BitPixel * sizeof(PALETTEENTRY) );
|
|
}
|
|
else
|
|
{
|
|
DeleteRgn( pgfNew->hrgnVis );
|
|
free( pgfNew );
|
|
//bw TraceTag((tagImageDecode, "not enough memory for GIF frame colors\n"));
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
}
|
|
pgfNew->cColors = GifScreen.BitPixel;
|
|
pgfNew->pcolors = _gad.pcolorsGlobal;
|
|
}
|
|
|
|
// Get this in here so that GifStrectchDIBits can use it during progressive
|
|
// rendering.
|
|
if ( _gad.pgf == NULL )
|
|
_gad.pgf = pgfNew;
|
|
|
|
pgfNew->ppixels = ReadImage(LM_to_uint(buf[4], buf[5]), // width
|
|
LM_to_uint(buf[6], buf[7]), // height
|
|
BitSet(buf[8], INTERLACE),
|
|
imageCount != imageNumber);
|
|
|
|
if ( pgfNew->ppixels != NULL )
|
|
{
|
|
// Oh JOY of JOYS! We got the pixels!
|
|
if (pgfLast != NULL)
|
|
{
|
|
int transparent = (pgfNew->gced.fTransparent) ? (int) pgfNew->gced.ucTransIndex : -1;
|
|
|
|
_gad.fAnimated = TRUE; // say multi-image == animated
|
|
|
|
if (GetColorMode() == 8) // palettized, use DIB_PAL_COLORS
|
|
{ // This will also dither the bits to the screen palette
|
|
|
|
pgfNew->pbmi = BIT_Make_DIB_PAL_Header(pgfNew->width, pgfNew->height);
|
|
//if (x_Dither(pgfNew->ppixels, pgfNew->pcolors, pgfNew->width, pgfNew->height, transparent))
|
|
// goto exitPoint;
|
|
}
|
|
else // give it an RGB header
|
|
{
|
|
pgfNew->pbmi = BIT_Make_DIB_RGB_Header_Screen(
|
|
pgfNew->width,
|
|
pgfNew->height,
|
|
pgfNew->cColors, pgfNew->pcolors,
|
|
transparent);
|
|
}
|
|
|
|
// Okay, so we've done any mapping on the GIFFRAME, so there's
|
|
// no need to keep the pcolors around. Let's go can clear out
|
|
// the pcolors.
|
|
// REVIEW(seanf): This assumes a common palette is used by all
|
|
// clients of the image
|
|
if ( pgfNew->pcolors != NULL && pgfNew->pcolors != _gad.pcolorsGlobal )
|
|
free( pgfNew->pcolors );
|
|
pgfNew->pcolors = NULL;
|
|
|
|
pgfLast->pgfNext = pgfNew;
|
|
|
|
// Do something to here to get the new frame on the screen.
|
|
|
|
_fInvalidateAll = TRUE;
|
|
//super::OnProg(FALSE, IMGBITS_TOTAL);
|
|
}
|
|
else
|
|
{ // first frame
|
|
_gad.pgf = pgfNew;
|
|
|
|
_gad.pgfLastProg = pgfNew;
|
|
_gad.dwLastProgTimeMS = 0;
|
|
// set up a temporary animation state for use in progressive draw
|
|
_gas.fAnimating = TRUE;
|
|
_gas.dwLoopIter = 0;
|
|
_gas.pgfDraw = pgfNew;
|
|
|
|
if ( imageCount == imageNumber )
|
|
image = pgfNew->ppixels;
|
|
}
|
|
pgfLast = pgfNew;
|
|
}
|
|
|
|
// make the _gifinfo.Gif89.delayTime stale, so we know if we got a new
|
|
// GCE for the next image
|
|
_gifinfo.Gif89.delayTime = -1;
|
|
|
|
}
|
|
|
|
if ( imageCount > imageNumber )
|
|
_gad.fAnimated = TRUE; // say multi-image == animated
|
|
|
|
#ifdef FEATURE_GIF_ANIMATION_LONG_LOOP_GOES_INFINITE
|
|
// RAID #23709 - If an animation is sufficiently long, we treat it as indefinite...
|
|
// Indefinite stays indefinite.
|
|
// 5/29/96 - JCordell sez we shouldn't introduce this gratuitous NS incompatibility.
|
|
// We'll keep it around inside this ifdef in case we decide we want it.
|
|
if ( _gad.fLooped &&
|
|
(_gad.dwLoopDurMS * _gad.cLoops) / 1000 > dwIndefiniteGIFThreshold ) // if longer than five minutes
|
|
_gad.cLoops = 0; // set to indefinite looping.
|
|
#endif // FEATURE_GIF_ANIMATION_LONG_LOOP_GOES_INFINITE
|
|
|
|
done:
|
|
return image;
|
|
}
|
|
|
|
BITMAPINFO *
|
|
CImgGif::FinishDithering()
|
|
{
|
|
BITMAPINFO * pbmi;
|
|
|
|
if (GetColorMode() == 8)
|
|
{
|
|
pbmi = BIT_Make_DIB_PAL_Header(_gad.pgf->width, _gad.pgf->height);
|
|
}
|
|
else
|
|
{
|
|
pbmi = BIT_Make_DIB_RGB_Header_Screen(_gad.pgf->width, _gad.pgf->height,
|
|
_gad.pgf->cColors, _gad.pgf->pcolors, _lTrans);
|
|
}
|
|
|
|
return pbmi;
|
|
}
|
|
|
|
//#include <vector>
|
|
//#define vector std::vector
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Member: LoadGifImage
|
|
//
|
|
// Overview: Given an IStream, decode an image into an array of bitmaps
|
|
//
|
|
// Arguments: pStream data source
|
|
// colorKeys pointer to where to store colorKey data
|
|
// numBitmaps where to store number of bitmaps
|
|
// delays where to store delay array
|
|
// loop where to store number of times to loop
|
|
// ppBitMaps where to store bitmaps
|
|
//
|
|
// Returns: S_OK on success otherwise error code
|
|
//
|
|
//------------------------------------------------------------------------
|
|
HRESULT
|
|
LoadGifImage(IStream *stream,
|
|
COLORREF **colorKeys,
|
|
int *numBitmaps,
|
|
int **delays,
|
|
double *loop,
|
|
HBITMAP **ppBitMaps)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
/*
|
|
The odd approach here lets us keep the original IE GIF code unchanged while removing
|
|
DA specific inserts (except error reporting). The progressive rendering and palette
|
|
dithering found in the IE code is also not supported yet.
|
|
*/
|
|
CImgGif gifimage;
|
|
gifimage._szFileName = NULL;
|
|
gifimage._gifinfo.stream = stream;
|
|
BYTE *pbBits = gifimage.ReadGIFMaster();
|
|
|
|
if (pbBits) {
|
|
gifimage._pbBits = pbBits;
|
|
gifimage._gad.pgf->pbmi = gifimage.FinishDithering();
|
|
}
|
|
|
|
/*
|
|
Extract information from GIF decoder, and format it into an array of bitmaps.
|
|
*/
|
|
*delays = NULL;
|
|
/*vector<>*/HBITMAP vhbmp;
|
|
/*vector<>*/COLORREF vcolorKey;
|
|
/*vector<>*/int vdelay;
|
|
LPVOID image = NULL;
|
|
LPVOID lastBits = pbBits;
|
|
LPVOID bitsBeforeLastBits = NULL;
|
|
HBITMAP *hImage = NULL;
|
|
PBITMAPINFO pbmi = NULL;
|
|
HBITMAP hbm;
|
|
PGIFFRAME pgf = gifimage._gad.pgf;
|
|
PGIFFRAME pgfOld = NULL;
|
|
bool fUseOffset = false;
|
|
bool fFirstFrame = true;
|
|
long pgfWidth,pgfHeight, // animation frame dims
|
|
fullWidth,fullHeight, // main frame dims
|
|
fullPad, pgfPad, // row padding vals
|
|
fullSize, pgfSize;
|
|
unsigned int disp = 0;
|
|
int i = 0;
|
|
|
|
// TODO: Dither global palette to display palette
|
|
|
|
fullWidth = gifimage._xWidth;
|
|
fullHeight = gifimage._yHeight;
|
|
fullPad = (((fullWidth + 3) / 4) * 4) - fullWidth;
|
|
fullSize = (fullPad+fullWidth)*fullHeight;
|
|
|
|
if (NULL == pgf)
|
|
{
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
// Assert(pgf);
|
|
pbmi = pgf->pbmi;
|
|
if (pbmi == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
// TODO: It would be nice to pass local palettes up so they could
|
|
// be mapped to system palettes.
|
|
|
|
// Check to see if frame is offset from logical frame
|
|
if(pgf->top != 0 ||
|
|
pgf->left != 0 ||
|
|
pgf->width != fullWidth ||
|
|
pgf->height != fullHeight)
|
|
{
|
|
fUseOffset = true;
|
|
pgfWidth = pbmi->bmiHeader.biWidth;
|
|
pgfHeight = pbmi->bmiHeader.biHeight;
|
|
pbmi->bmiHeader.biWidth = fullWidth;
|
|
pbmi->bmiHeader.biHeight = fullHeight;
|
|
pgfPad = (((pgfWidth + 3) / 4) * 4) - pgfWidth;
|
|
pgfSize = (pgfPad+pgfWidth)*pgfHeight;
|
|
}
|
|
|
|
hbm = CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, (LPVOID *) &image, NULL, 0);
|
|
if(!hbm)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
// Correctly composite bitmaps based on disposal method specified
|
|
disp = pgf->gced.uiDisposalMethod;
|
|
// If the frame is offset, fill it with
|
|
if( (disp == gifRestorePrev) && (bitsBeforeLastBits != NULL) )
|
|
memcpy(image, bitsBeforeLastBits, fullSize);
|
|
else if( (disp == gifRestoreBkgnd) || (disp == gifRestorePrev) || fFirstFrame ) // fill with bgColor
|
|
memset(image, pgf->gced.ucTransIndex, fullSize);
|
|
else // fill with last frames data
|
|
memcpy(image, lastBits, fullSize);
|
|
|
|
|
|
// For offset gifs allocate an image the size of the first frame
|
|
// and then fill in the bits at the offset location.
|
|
if(fUseOffset) {
|
|
for(i=0; i<pgfHeight; i++) {
|
|
BYTE *dst, *src;
|
|
// the destination is the address of the image data plus the frame and row offset.
|
|
int topOffset = fullHeight - pgfHeight - pgf->top;
|
|
dst = (BYTE*)image +
|
|
( ((topOffset + i) *(fullPad+fullWidth)) + pgf->left );
|
|
// copy from the frame's nth row
|
|
src = pgf->ppixels + i*(pgfPad+pgfWidth);
|
|
for(int j=0; j<pgfWidth; j++) {
|
|
// copy the frame row data, excluding transparent bytes
|
|
if(src[j] != pgf->gced.ucTransIndex)
|
|
dst[j] = src[j];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Overwritten accumulated bits with current bits. If the
|
|
// new image contains transparency we need to take it into
|
|
// account. Since this is slower, special case it.
|
|
if(pgf->gced.fTransparent) {
|
|
for(i=0; i<((fullPad+fullWidth)*fullHeight); i++) {
|
|
if(pgf->ppixels[i] != pgf->gced.ucTransIndex)
|
|
((BYTE*)image)[i] = ((BYTE*)pgf->ppixels)[i];
|
|
}
|
|
}
|
|
else // Otherwise, just copy over the offset window's bytes
|
|
memcpy(image, pgf->ppixels, (fullPad+fullWidth)*fullHeight);
|
|
}
|
|
|
|
/*
|
|
If we got a transparent color extension, convert it to a COLORREF
|
|
*/
|
|
COLORREF colorKey = COLORKEY_NOT_SET;
|
|
if (pgf->gced.fTransparent) {
|
|
int transparent = pgf->gced.ucTransIndex;
|
|
colorKey = RGB(pgf->pbmi->bmiColors[transparent].rgbRed,
|
|
pgf->pbmi->bmiColors[transparent].rgbGreen,
|
|
pgf->pbmi->bmiColors[transparent].rgbBlue);
|
|
}
|
|
|
|
// vcolorKey.push_back(colorKey);
|
|
vcolorKey = colorKey;
|
|
|
|
// biao change : vhbmp.push_back(hbm);
|
|
vhbmp = hbm;
|
|
|
|
/*
|
|
The delay times are frame specific and can be different, these
|
|
should be propagated as an array to the sampling code.
|
|
*/
|
|
// vdelay.push_back(pgf->gced.uiDelayTime);
|
|
vdelay = pgf->gced.uiDelayTime;
|
|
|
|
bitsBeforeLastBits = lastBits;
|
|
lastBits = image;
|
|
fUseOffset = false;
|
|
if(pgf->pgfNext == NULL)
|
|
break;
|
|
pgfOld = pgf;
|
|
pgf = pgf->pgfNext;
|
|
fFirstFrame = FALSE;
|
|
}
|
|
|
|
|
|
// The number of times to loop are also propagated. Note we add one because
|
|
// all other GIF decoders appear to treat the loop as the number of times to
|
|
// loop AFTER the first run through the frames.
|
|
if (gifimage._gad.cLoops == 0 && gifimage._gad.fLooped != 0)
|
|
{
|
|
*loop = 0; // HUGE_VAL;
|
|
}
|
|
else
|
|
{
|
|
*loop = gifimage._gad.cLoops;
|
|
}
|
|
|
|
*numBitmaps = 1;
|
|
|
|
// Since the vector will go out of scope, move contents over to heap
|
|
hImage = (HBITMAP *)malloc(1 * sizeof(HBITMAP));
|
|
if (NULL == hImage)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
*delays = (int*)malloc(1 * sizeof(int));
|
|
if (NULL == *delays)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
*colorKeys = (COLORREF*)malloc( sizeof(COLORREF) * 1 );
|
|
if (NULL == *colorKeys)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
for(i=0; i < 1; i++) {
|
|
hImage[i] = vhbmp; // biao fix [i];
|
|
(*colorKeys)[i] = vcolorKey; // [i];
|
|
(*delays)[i] = vdelay; //[i];
|
|
}
|
|
|
|
*ppBitMaps = hImage;
|
|
|
|
hr = S_OK;
|
|
done:
|
|
if (FAILED(hr))
|
|
{
|
|
free(hImage);
|
|
free(*delays);
|
|
free(*colorKeys);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*lint --flb*/
|
|
|
|
BOOL Gif2Bmp(LPSTREAM pStream, HBITMAP** ppBmp)
|
|
{
|
|
HRESULT hr;
|
|
HBITMAP * phBMPs = NULL;
|
|
int numGifs = 0;
|
|
double loop = 0;
|
|
int * pDelays = NULL;
|
|
COLORREF * pColorKeys = NULL;
|
|
|
|
hr = LoadGifImage(pStream,
|
|
&pColorKeys,
|
|
&numGifs,
|
|
&pDelays,
|
|
&loop,
|
|
&phBMPs);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*ppBmp = phBMPs;
|
|
|
|
return TRUE;
|
|
}
|