📄 gif.cpp
字号:
// Gif.cpp: implementation of the CGif class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <io.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include "Gif.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Global variables
//
GIF89 Gif89 = {-1,-1,-1,0};
GIFSCREEN GifScreen;
static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
0x001F, 0x003F, 0x007F, 0x00FF,
0x01FF, 0x03FF, 0x07FF, 0x0FFF,
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CGif::CGif()
{
ZeroDataBlock = FALSE;
m_strGIFError = "No Error"; // yet
m_pDib = NULL;
cur_accum = 0;
cur_bits = 0;
maxbits = BITS;
maxmaxcode = (code_int)1 << BITS;
free_ent = 0;
clear_flg = 0;
m_pDib = NULL;
}
CGif::CGif(CDib *pDib)
{
ZeroDataBlock = FALSE;
m_strGIFError = "No Error"; // yet
m_pDib = NULL;
cur_accum = 0;
cur_bits = 0;
maxbits = BITS;
maxmaxcode = (code_int)1 << BITS;
free_ent = 0;
clear_flg = 0;
m_pDib = NULL;
SetDib(pDib);
}
CGif::~CGif()
{
if (m_pDib != NULL)
delete m_pDib;
}
// free allocate memory
void CGif::FreeBuffer(BYTE *Buffer)
{
delete[] Buffer;
}
// get error string
CString CGif::GetErrorString()
{
return m_strGIFError;
}
// load gif file
BOOL CGif::Load(LPCTSTR lpstrFileName)
{
UINT uWidth, uHeight, uWidthDW;
// read the GIF to a packed buffer of RGB bytes
BYTE *lpTmpBuffer = ReadGIFFile(lpstrFileName, &uWidth, &uHeight);
if (lpTmpBuffer == NULL)
return FALSE;
// do this before DWORD-alignment!!!
// swap red and blue for display
BGRFromRGB(lpTmpBuffer, uWidth, uHeight);
// now DWORD-align for display
BYTE *lpBuffer = MakeDwordAlign(lpTmpBuffer, uWidth, uHeight, &uWidthDW);
FreeBuffer(lpTmpBuffer);
// flip for display
VertFlipBuf(lpBuffer, uWidthDW, uHeight);
BITMAPINFOHEADER bmiHeader;
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = uWidth;
bmiHeader.biHeight = uHeight;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24;
bmiHeader.biCompression = BI_RGB;
bmiHeader.biSizeImage = 0;
bmiHeader.biXPelsPerMeter = 0;
bmiHeader.biYPelsPerMeter = 0;
bmiHeader.biClrUsed = 0;
bmiHeader.biClrImportant = 0;
// Allocate enough memory for the new CF_DIB, and copy bits
DWORD dwHeaderSize = sizeof(BITMAPINFOHEADER);
DWORD dwBitsSize = WIDTHBYTES(uWidth*24) * uHeight;
HDIB hDIB = GlobalAlloc(GHND, dwHeaderSize + dwBitsSize);
if (hDIB == NULL)
return FALSE;
LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
memcpy(lpDIB, (LPBYTE)&bmiHeader, dwHeaderSize);
memcpy(FindDIBBits((LPBYTE)lpDIB), lpBuffer, dwBitsSize);
FreeBuffer(lpBuffer);
if (m_pDib != NULL)
delete m_pDib;
m_pDib = new CDib();
m_pDib->Attach(hDIB);
return TRUE;
}
// save gif file
BOOL CGif::Save(LPCTSTR lpstrFileName, CDib* pDib)
{
if (pDib == NULL)
pDib = m_pDib;
if (pDib == NULL)
return FALSE;
HDIB hDib = CopyHandle(pDib->GetHandle());
if (hDib == NULL)
return FALSE;
CDib* pDibTmp = new CDib;
pDibTmp->Attach(hDib);
//if (pDibTmp->GetBitCount() != 24)
// pDibTmp->ConvertFormat(24);
UINT uWidth = pDibTmp->GetWidth();
UINT uHeight = pDibTmp->GetHeight();
// convert to 8-bit image
if (pDibTmp->GetBitCount() != 8)
pDibTmp->ColorQuantize(8);
// get 256 color r, g, b
PALETTEENTRY PaletteColors[256];
pDibTmp->GetPalette()->GetPaletteEntries(0, 256, PaletteColors);
int red[256], blue[256], green[256];
for (int i=0;i<256;i++)
{
red[i] = PaletteColors[i].peRed;
green[i] = PaletteColors[i].peGreen;
blue[i] = PaletteColors[i].peBlue;
}
// convert from DIB format (DWORD aligned, vertically flipped, red and blue swapped)
BYTE *tmp = ClearDwordAlign(pDibTmp->GetBitsPtr(), uWidth, uHeight, 8);
if (tmp == NULL)
return FALSE;
// convert from DIB
VertFlipBuf(tmp, uWidth, uHeight);
BOOL bSuccess = WriteGIFFile(lpstrFileName,
tmp, uWidth, uHeight,
0, // background color
red, green, blue);
delete pDibTmp;
FreeBuffer(tmp);
return bSuccess;
}
LPBYTE CGif::ReadGIFFile(LPCTSTR lpstrFileName, UINT *uWidth, UINT *uHeight)
{
BYTE buf[16];
BYTE c;
BYTE localColorMap[3][MAXCOLORMAPSIZE];
int useGlobalColormap;
int bitPixel;
int imageCount =0;
char version[4];
FILE *fd;
int w=0;
int h=0;
CString strFileName = lpstrFileName;
if (strFileName.IsEmpty())
{
m_strGIFError="No Name Given";
return NULL;
}
BYTE *bigBuf;
fd=fopen(lpstrFileName,"rb");
if (fd==NULL)
{
m_strGIFError="Cant open GIF :\n" + strFileName;
return NULL;
}
// read GIF file header
if (!ReadOK(fd,buf,6))
{
m_strGIFError="Error reading GIF Magic #\n"+strFileName;
fclose(fd);
return NULL;
}
// need the string "GIF" in the header
if (strncmp((char *)buf,"GIF",3)!=0)
{
m_strGIFError="Error, "+strFileName+" is not a valid .GIF file";
fclose(fd);
return NULL;
}
strncpy(version,(char *)(buf+3),3);
version[3]='\0';
// only handle v 87a and 89a
if ((strcmp(version,"87a")!=0)&&(strcmp(version,"89a")!=0))
{
m_strGIFError="Error, Bad GIF Version number";
fclose(fd);
return NULL;
}
// screen description
if (!ReadOK(fd,buf,7))
{
m_strGIFError="Error, failed to GIF read screen descriptor.\nGiving up";
fclose(fd);
return NULL;
}
GifScreen.Width = LM_to_uint((BYTE)buf[0],(BYTE)buf[1]);
GifScreen.Height = LM_to_uint((BYTE)buf[2],(BYTE)buf[3]);
GifScreen.BitPixel = 2 << ((BYTE)buf[4] & 0x07);
GifScreen.ColorResolution = ((((BYTE)buf[4] & 0x70) >> 3) + 1);
GifScreen.BackGround= (BYTE)buf[5]; // background color...
GifScreen.AspectRatio= (BYTE)buf[6];
// read colormaps
if (BitSet((BYTE)buf[4],LOCALCOLORMAP))
{
if (!ReadColorMap(fd,GifScreen.BitPixel,GifScreen.ColorMap))
{
m_strGIFError="Error reading GIF colormap";
fclose(fd);
return NULL;
}
}
// non-square pixels, so what?
if ((GifScreen.AspectRatio!=0 ) && (GifScreen.AspectRatio!=49))
{
m_strGIFError="Non-square pixels in GIF image.\nIgnoring that fact...";
}
// there can be multiple images in a GIF file... uh?
// what the hell do we do with multiple images?
// so, we'll be interested in just the first image, cause we're lazy
for(;;)
{
// read a byte;
if (!ReadOK(fd,&c,1))
{
m_strGIFError="Unexpected EOF in GIF.\nGiving up";
fclose(fd);
return NULL;
}
// image terminator
if (c==';')
{
}
if (c=='!')
{
if (!ReadOK(fd,&c,1))
{
m_strGIFError="Error on extension read.\nGiving up";
fclose(fd);
return NULL;
}
DoExtension(fd,c);
continue;
}
if (c!=',')
{
// Ignoring c
continue;
}
// read image header
if (!ReadOK(fd,buf,9))
{
m_strGIFError="Error on dimension read\nGiving up";
fclose(fd);
return NULL;
}
useGlobalColormap=!BitSet((BYTE)buf[8],LOCALCOLORMAP);
bitPixel=1<<(((BYTE)buf[8]&0x07)+1);
// let's see if we have enough mem to continue?
long bufsize;
if ((int)buf[5]>4)
{
//AfxMessageBox("This GIF file claims to be > 2000 bytes wide!",MB_OK | MB_ICONINFORMATION);
}
if ((int)buf[7]>4)
{
//AfxMessageBox("This GIF file claims to be > 2000 bytes high!",MB_OK | MB_ICONINFORMATION);
}
w=LM_to_uint((BYTE)buf[4],(BYTE)buf[5]);
h=LM_to_uint((BYTE)buf[6],(BYTE)buf[7]);
if ((w<0) || (h<0))
{
m_strGIFError="Negative image dimensions!\nGiving up";
fclose(fd);
return NULL;
}
bufsize=(long)w*(long)h;
bufsize*=3;
bigBuf= (BYTE *) new char [bufsize];
if (bigBuf==NULL)
{
m_strGIFError="Out of Memory in GIFRead";
fclose(fd);
return NULL;
}
if (!useGlobalColormap)
{
if (!ReadColorMap(fd,bitPixel,localColorMap))
{
m_strGIFError="Error reading GIF colormap\nGiving up";
delete [] bigBuf;
fclose(fd);
return NULL;
}
//read image
if (!ReadImage(fd, bigBuf, w, h, localColorMap, BitSet((BYTE)buf[8],INTERLACE)))
{
m_strGIFError="Error reading GIF file\nLocalColorMap\nGiving up";
delete [] bigBuf;
fclose(fd);
return NULL;
}
}
else
{
if (!ReadImage(fd, bigBuf, w, h, GifScreen.ColorMap, BitSet((BYTE)buf[8],INTERLACE)))
{
m_strGIFError="Error reading GIF file\nGIFScreen Colormap\nGiving up";
delete [] bigBuf;
fclose(fd);
return NULL;
}
}
break;
}
*uWidth=w;
*uHeight=h;
fclose(fd);
return bigBuf;
}
int CGif::ReadColorMap(FILE *fd, int number, BYTE buffer[3][MAXCOLORMAPSIZE])
{
int i;
BYTE rgb[3];
for (i=0;i < number; ++i)
{
if (!ReadOK(fd,rgb,sizeof(rgb)))
{
return FALSE;
}
buffer[CM_RED][i]=rgb[0];
buffer[CM_GREEN][i]=rgb[1];
buffer[CM_BLUE][i]=rgb[2];
}
return TRUE;
}
int CGif::DoExtension(FILE *fd, int label)
{
static char buf[256];
char *str;
switch(label)
{
case 0x01 :
str="Plain Text Ext";
break;
case 0xff :
str= "Appl ext";
break;
case 0xfe :
str="Comment Ext";
while (GetDataBlock(fd,(BYTE *)buf)!=0);
return FALSE;
case 0XF9 :
str="Graphic Ctrl Ext";
(void)GetDataBlock(fd,(BYTE *)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];
while (GetDataBlock(fd,(BYTE *)buf)!=0);
return FALSE;
default :
str=buf;
sprintf(buf,"UNKNOWN (0x%02x)",label);
break;
}
while (GetDataBlock(fd,(BYTE *)buf)!=0);
return FALSE;
}
int CGif::GetDataBlock(FILE *fd, BYTE *buf)
{
BYTE count;
if (!ReadOK(fd,&count,1))
{
//m_strGIFError="Error in GIF DataBlock Size";
return -1;
}
ZeroDataBlock=count==0;
if ((count!=0) && (!ReadOK(fd,buf,count)))
{
//m_strGIFError="Error reading GIF datablock";
return -1;
}
return count;
}
int CGif::GetCode(FILE *fd, int code_size, int flag)
{
static BYTE buf[280];
static int curbit, lastbit, done, last_byte;
int i,j,ret;
BYTE count;
if (flag)
{
curbit=0;
lastbit=0;
done=FALSE;
return 0;
}
if ((curbit+code_size) >=lastbit)
{
if (done)
{
if (curbit >=lastbit)
{
//m_strGIFError="Ran off the end of my bits";
return 0;
}
return -1;
}
buf[0]=buf[last_byte-2];
buf[1]=buf[last_byte-1];
if ((count=GetDataBlock(fd,&buf[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|=((buf[i/8]&(1<<(i% 8)))!=0)<<j;
curbit+=code_size;
return ret;
}
int CGif::LZWReadByte(FILE *fd, 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 unsigned short next[1<<MAX_LZW_BITS];
static BYTE vals[1<<MAX_LZW_BITS];
static BYTE stack [1<<(MAX_LZW_BITS+1)];
static BYTE *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 = clear_code+2;
max_code_size=2*clear_code;
GetCode(fd,0,TRUE);
fresh=TRUE;
for(i=0;i<clear_code;++i)
{
next[i]=0;
vals[i]=i;
}
for (;i<(1<<MAX_LZW_BITS);++i)
next[i]=vals[0]=0;
sp=stack;
return 0;
}
else if (fresh)
{
fresh=FALSE;
do
{
firstcode=oldcode=GetCode(fd,code_size,FALSE);
} while (firstcode==clear_code);
return firstcode;
}
if (sp > stack)
return *--sp;
while ((code= GetCode(fd,code_size,FALSE)) >=0)
{
if (code==clear_code)
{
for (i=0;i<clear_code;++i)
{
next[i]=0;
vals[i]=i;
}
for (;i<(1<<MAX_LZW_BITS);++i)
next[i]=vals[i]=0;
code_size=set_code_size+1;
max_code_size=2*clear_code;
max_code=clear_code+2;
sp=stack;
firstcode=oldcode=GetCode(fd,code_size,FALSE);
return firstcode;
}
else if (code==end_code)
{
int count;
BYTE buf[260];
if (ZeroDataBlock)
return -2;
while ((count=GetDataBlock(fd,buf)) >0);
if (count!=0)
//AfxMessageBox("Missing EOD in GIF data stream (common occurrence)",MB_OK);
return -2;
}
incode = code;
if (code >= max_code)
{
*sp++=firstcode;
code=oldcode;
}
while (code >=clear_code)
{
*sp++=vals[code];
if (code==(int)next[code])
{
//m_strGIFError="Circular table entry, big GIF Error!";
return -1;
}
code=next[code];
}
*sp++ = firstcode=vals[code];
if ((code=max_code) <(1<<MAX_LZW_BITS))
{
next[code]=oldcode;
vals[code]=firstcode;
++max_code;
if ((max_code >=max_code_size) &&
(max_code_size < (1<<MAX_LZW_BITS)))
{
max_code_size*=2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -