www.pudn.com > GIFTOBMP.rar > GIFTOBMP.C
/*
Convert in-memory face GIF file to BMP format.
based on the NETPBM utility GIFTOPNM:
*/
/* +-------------------------------------------------------------------+ */
/* | Copyright 1990, 1991, 1993, David Koblas. (koblas@netcom.com) | */
/* | Permission to use, copy, modify, and distribute this software | */
/* | and its documentation for any purpose and without fee is hereby | */
/* | granted, provided that the above copyright notice appear in all | */
/* | copies and that both that copyright notice and this permission | */
/* | notice appear in supporting documentation. This software is | */
/* | provided "as is" without express or implied warranty. | */
/* +-------------------------------------------------------------------+ */
#include "netfone.h"
#define MAXCOLORMAPSIZE 256
#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 PBM_TYPE 1
#define PGM_TYPE 2
#define PPM_TYPE 3
typedef *BYTE LPBYTE;
static LPBYTE gifFile; // In-memory GIF file pointer
static long gifAddr; // "Seek address" in GIF file
static LPBYTE bmpFile = NULL; // In-memory BMP file being constructed
#define ReadOK(file, buffer, len) ((_fmemcpy(buffer, gifFile + gifAddr, len), gifAddr += len), TRUE)
#define LM_to_uint(a, b) (((b) << 8) | (a))
struct GifScreenStruct {
unsigned int Width;
unsigned int Height;
unsigned char ColorMap[3][MAXCOLORMAPSIZE];
unsigned int BitPixel;
unsigned int ColorResolution;
unsigned int Background;
unsigned int AspectRatio;
int GrayScale;
};
static struct {
int transparent;
int delayTime;
int inputFlag;
int disposal;
} Gif89 = { -1, -1, -1, 0 };
/* GIFtoBMP uses a lot of storage for LZW decoding, colour map
tables etc. To avoid busting DGROUP in 16 bit medium memory
model or going to large model (which would block multiple
instances running at once), we define these buffers as members
of a "context" structure which is allocated from the global heap
for the duration of our processing. */
static struct context {
struct GifScreenStruct c_GifScreen;
unsigned char c_GetCodebuf[280];
int c_LZWtable[2][(1 << MAX_LWZ_BITS)];
int c_LZWstack[(1 << (MAX_LWZ_BITS)) * 2];
unsigned char c_localColorMap[3][MAXCOLORMAPSIZE];
} FAR *GIFcontext = NULL;
// Now define shortcuts to access items in the context
#define GifScreen GIFcontext->c_GifScreen
#define GetCodebuf GIFcontext->c_GetCodebuf
#define LZWtable GIFcontext->c_LZWtable
#define LZWstack GIFcontext->c_LZWstack
#define localColorMap GIFcontext->c_localColorMap
#define pm_error(x) { fatal = TRUE; \
if (GIFcontext != NULL) { GlobalFreePtr(GIFcontext); GIFcontext = NULL; } \
if (bmpFile != NULL) { GlobalFreePtr(bmpFile); bmpFile = NULL; } \
return FALSE; }
static int fatal;
static int ZeroDataBlock = FALSE;
// READCOLORMAP -- Read colour palette table
static int ReadColorMap(int number, unsigned char FAR buffer[3][MAXCOLORMAPSIZE])
{
int i;
unsigned char rgb[3];
int flag;
flag = FALSE;
for (i = 0; i < number; ++i) {
ReadOK(fd, rgb, sizeof(rgb));
buffer[CM_RED][i] = rgb[0];
buffer[CM_GREEN][i] = rgb[1];
buffer[CM_BLUE][i] = rgb[2];
flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
}
return TRUE;
}
// GETDATABLOCK -- Read a variable-sized data block
static int GetDataBlock(LPBYTE buf)
{
unsigned char count;
ReadOK(fd, &count, 1);
ZeroDataBlock = count == 0;
if (count == 0) {
return -1;
}
ReadOK(fd, buf, count);
return count;
}
// GETCODE -- Read LZW code
static int GetCode(int code_size, int flag)
{
static int curbit, lastbit, done, last_byte;
int i, j, ret;
unsigned char count;
if (flag) {
curbit = 0;
lastbit = 0;
done = FALSE;
return 0;
}
if ((curbit + code_size) >= lastbit) {
if (done) {
return -1;
}
GetCodebuf[0] = GetCodebuf[last_byte - 2];
GetCodebuf[1] = GetCodebuf[last_byte - 1];
if ((count = GetDataBlock(&GetCodebuf[2])) == 0)
done = TRUE;
last_byte = 2 + count;
curbit = (curbit - lastbit) + 16;
lastbit = (2 + count) * 8 ;
}
ret = 0;
for (i = curbit, j = 0; j < code_size; ++i, ++j)
ret |= ((GetCodebuf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
curbit += code_size;
return ret;
}
// LWZREADBYTE -- Read next uncompressed byte from the compressed stream
static int LWZReadByte(int flag, int input_code_size)
{
static int fresh = FALSE;
int code, incode;
static int code_size, set_code_size;
static int max_code, max_code_size;
static int firstcode, oldcode;
static int clear_code, end_code;
static int FAR *sp;
register int i;
if (flag) {
set_code_size = input_code_size;
code_size = set_code_size + 1;
clear_code = 1 << set_code_size ;
end_code = clear_code + 1;
max_code_size = 2 * clear_code;
max_code = clear_code+2;
GetCode(0, TRUE);
fresh = TRUE;
for (i = 0; i < clear_code; ++i) {
LZWtable[0][i] = 0;
LZWtable[1][i] = i;
}
for (; i < (1 << MAX_LWZ_BITS); ++i)
LZWtable[0][i] = LZWtable[1][0] = 0;
sp = LZWstack;
return 0;
} else if (fresh) {
fresh = FALSE;
do {
firstcode = oldcode =
GetCode(code_size, FALSE);
} while (firstcode == clear_code);
return firstcode;
}
if (sp > LZWstack)
return *--sp;
while ((code = GetCode(code_size, FALSE)) >= 0) {
if (code == clear_code) {
for (i = 0; i < clear_code; ++i) {
LZWtable[0][i] = 0;
LZWtable[1][i] = i;
}
for (; i < (1 << MAX_LWZ_BITS); ++i)
LZWtable[0][i] = LZWtable[1][i] = 0;
code_size = set_code_size + 1;
max_code_size = 2 * clear_code;
max_code = clear_code + 2;
sp = LZWstack;
firstcode = oldcode =
GetCode(code_size, FALSE);
return firstcode;
} else if (code == end_code) {
int count;
unsigned char buf[260];
if (ZeroDataBlock)
return -2;
while ((count = GetDataBlock(buf)) > 0) ;
return -2;
}
incode = code;
if (code >= max_code) {
*sp++ = firstcode;
code = oldcode;
}
while (code >= clear_code) {
*sp++ = LZWtable[1][code];
if (code == LZWtable[0][code]) {
fatal = TRUE;
return -1;
}
code = LZWtable[0][code];
}
*sp++ = firstcode = LZWtable[1][code];
if ((code = max_code) <(1 << MAX_LWZ_BITS)) {
LZWtable[0][code] = oldcode;
LZWtable[1][code] = firstcode;
++max_code;
if ((max_code >= max_code_size) &&
(max_code_size < (1 << MAX_LWZ_BITS))) {
max_code_size *= 2;
++code_size;
}
}
oldcode = incode;
if (sp > LZWstack)
return *--sp;
}
return code;
}
// DOEXTENSION -- Process extension items
static int DoExtension(int label)
{
char buf[256];
#ifdef GIF_DEBUG
char *str;
#endif
switch (label) {
case 0x01: /* Plain Text Extension */
#ifdef GIF_DEBUG
str = "Plain Text Extension";
#endif
break;
case 0xff: /* Application Extension */
#ifdef GIF_DEBUG
str = "Application Extension";
#endif
break;
case 0xfe: /* Comment Extension */
#ifdef GIF_DEBUG
str = "Comment Extension";
#endif
// This line should fix many crashes because John Walker was
// originally checking for a zero return, which would never
// occur because if the block is zero length, GetDataBlock()
// returns -1, never 0.
while (GetDataBlock((unsigned char *) buf) != -1) {
}
return FALSE;
case 0xf9: /* Graphic Control Extension */
#ifdef GIF_DEBUG
str = "Graphic Control Extension";
#endif
(void) GetDataBlock((unsigned char *) buf);
Gif89.disposal = (buf[0] >> 2) & 0x7;
Gif89.inputFlag = (buf[0] >> 1) & 0x1;
Gif89.delayTime = LM_to_uint(buf[1],buf[2]);
if ((buf[0] & 0x1) != 0)
Gif89.transparent = buf[3];
// This line should fix many crashes because John Walker was
// originally checking for a zero return, which would never
// occur because if the block is zero length, GetDataBlock()
// returns -1, never 0.
while (GetDataBlock((unsigned char *) buf) != -1) ;
return FALSE;
default:
break;
}
// This line should fix many crashes because John Walker was
// originally checking for a zero return, which would never
// occur because if the block is zero length, GetDataBlock()
// returns -1, never 0.
while (GetDataBlock((unsigned char *) buf) != -1) ;
return FALSE;
}
/* READIMAGE -- Read all components of image and, if successful,
create the in-memory .BMP file. */
static int ReadImage(int len, int height,
int ncolours, unsigned char FAR cmap[3][MAXCOLORMAPSIZE],
int interlace, int ignore)
{
unsigned char c;
int v, linelen;
int xpos = 0, ypos = 0, pass = 0;
DWORD bfsize;
BYTE _huge *pixels;
LPBITMAPFILEHEADER bfh;
LPBITMAPINFO bi;
LPBITMAPINFOHEADER bh;
// Initialize the Compression routines
ReadOK(fd, &c, 1);
if (LWZReadByte(TRUE, c) < 0)
return FALSE;
// If this is an "uninteresting picture" ignore it.
if (ignore) {
while (LWZReadByte(FALSE, c) >= 0) ;
return FALSE;
}
// Allocate the in-memory bitmap file.
linelen = (len + 3) & (~3);
bmpFile = (LPBYTE) GlobalAllocPtr(GPTR, bfsize =
sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
256 * sizeof(RGBQUAD) + (linelen * ((DWORD) height)));
bfh = (LPBITMAPFILEHEADER) bmpFile;
bi = (LPBITMAPINFO) (bmpFile + sizeof(BITMAPFILEHEADER));
bh = &(bi->bmiHeader);
pixels = bmpFile + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
256 * sizeof(RGBQUAD);
// Build file header
_fmemcpy(&bfh->bfType, "BM", 2);
bfh->bfSize = bfsize;
bfh->bfOffBits = ((LPBYTE) pixels) - bmpFile;
// Build BITMAPINFOHEADER
bh->biSize = sizeof(BITMAPINFOHEADER);
bh->biWidth = len;
bh->biHeight = height;
bh->biPlanes = 1;
bh->biBitCount = 8;
bh->biCompression = BI_RGB;
bh->biSizeImage = 0;
bh->biClrUsed = bh->biClrImportant = ncolours;
// Transcribe colour table to the bmiColors list
for (v = 0; v < ncolours; v++) {
bi->bmiColors[v].rgbRed = cmap[CM_RED][v];
bi->bmiColors[v].rgbGreen = cmap[CM_GREEN][v];
bi->bmiColors[v].rgbBlue = cmap[CM_BLUE][v];
}
while ((v = LWZReadByte(FALSE, c)) >= 0 ) {
if (ypos > 0 && ypos < height) {
pixels[(linelen * ((DWORD) ((height - 1) - ypos))) + xpos] = v;
}
++xpos;
if (xpos == len) {
xpos = 0;
if (interlace) {
switch (pass) {
case 0:
case 1:
ypos += 8; break;
case 2:
ypos += 4; break;
case 3:
ypos += 2; break;
}
if (ypos >= height) {
++pass;
switch (pass) {
case 1:
ypos = 4; break;
case 2:
ypos = 2; break;
case 3:
ypos = 1; break;
default:
goto fini;
}
}
} else {
++ypos;
}
}
if (ypos >= height)
break;
}
fini:
return TRUE;
}
/* GIFTOBMP -- Convert an in-memory GIF file to an in-memory
.BMP file. Returns NULL if anything went wrong
in the conversion process. */
LPBYTE GIFtoBMP(LPBYTE gif, int imageNumber)
{
unsigned char buf[16];
unsigned char c;
int useGlobalColormap;
int bitPixel;
int imageCount = 0;
char version[4];
gifFile = gif;
gifAddr = 0;
fatal = FALSE;
ReadOK(fd, buf, 6);
if (strncmp((char *) buf,"GIF",3) != 0)
pm_error("not a GIF file");
strncpy(version, (char *) buf + 3, 3);
version[3] = '\0';
if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0))
pm_error("bad version number, not '87a' or '89a'");
GIFcontext = (struct context FAR *) GlobalAllocPtr(GPTR, sizeof(struct context));
if (GIFcontext == NULL) {
return NULL;
}
ReadOK(fd,buf,7);
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 */
if (!ReadColorMap(GifScreen.BitPixel, GifScreen.ColorMap))
pm_error("error reading global colormap");
}
for (;;) {
ReadOK(fd, &c, 1);
if (c == ';') { /* GIF terminator */
if (imageCount < imageNumber)
pm_error("Too few images found in file");
if (GIFcontext != NULL) {
GlobalFreePtr(GIFcontext);
GIFcontext = NULL;
}
return bmpFile;
}
if (c == '!') { /* Extension */
ReadOK(fd, &c, 1);
DoExtension(c);
continue;
}
if (c != ',') { /* Not a valid start character */
continue;
}
++imageCount;
ReadOK(fd, buf, 9);
useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
bitPixel = 1 << ((buf[8] & 0x07) + 1);
if (!useGlobalColormap) {
if (!ReadColorMap(bitPixel, localColorMap))
pm_error("error reading local colormap");
ReadImage(LM_to_uint(buf[4], buf[5]),
LM_to_uint(buf[6], buf[7]),
bitPixel, localColorMap,
BitSet(buf[8], INTERLACE), imageCount != imageNumber);
} else {
ReadImage(LM_to_uint(buf[4], buf[5]),
LM_to_uint(buf[6], buf[7]),
GifScreen.BitPixel, GifScreen.ColorMap,
BitSet(buf[8], INTERLACE), imageCount != imageNumber);
}
}
}