📄 gif.cpp
字号:
#include "stdafx.h"
#include "..\..\Include\Pic\Image.h"
//===================================================================
BOOL FCImage::AddGifFrame (PCTSTR szFileName, bool bGolPal,
DWORD dwDelay, DWORD dwTransIndex)
{
BOOL bRet = FALSE ;
BYTE * pStart = NULL, * pCurr ;
HANDLE hMap = NULL ;
HANDLE hFile = INVALID_HANDLE_VALUE ;
__try
{
if (pFooDib->hBitmap == NULL)
__leave ;
if (this->ColorBits() > 8) // 支持1, 4, 8位色
__leave ;
// 获得文件大小
WIN32_FILE_ATTRIBUTE_DATA file_data ;
if (::GetFileAttributesEx (szFileName, GetFileExInfoStandard, &file_data) == 0)
__leave ;
pStart = this->__fooGifOpenFileForAddFrame (szFileName, &hFile, &hMap, file_data.nFileSizeLow + this->GetPitch()*Height()*2) ;
if (pStart == NULL)
__leave ;
pCurr = pStart + file_data.nFileSizeLow - 1 ; // 文件结尾 ==> 0x3B
if (*pCurr != 0x3B)
__leave ;
// 加入图形控制扩充块
BYTE ExtBlk[13] = {0x21, 0xF9, 4, 0, 0, 0, 0, 0,
0x2C, 0, 0, 0, 0} ;
* (WORD *) &ExtBlk[4] = dwDelay / 10 ;
ExtBlk[3] |= (dwTransIndex == -1) ? 0 : 1 ; // 透明色标志
ExtBlk[6] = dwTransIndex ;
CopyMemory (pCurr, ExtBlk, 13) ;
pCurr += 13 ;
* (WORD *) pCurr = (WORD) this->Width() ;
* (WORD *) &pCurr[2] = (WORD) this->Height() ;
pCurr[4] = 0 ;
if (!bGolPal)
pCurr[4] |= 0x80 | (this->ColorBits() - 1) ; // 局部调色板
pCurr += 5 ;
if (!bGolPal) // 加入区域调色板
{
int iColorNum = 1 << this->ColorBits() ;
RGBQUAD * Palette = new RGBQUAD [iColorNum] ;
this->GetColorTable (0, iColorNum, Palette) ;
for (int i = 0 ; i < iColorNum ; i++)
{
*pCurr++ = Palette[i].rgbRed ;
*pCurr++ = Palette[i].rgbGreen ;
*pCurr++ = Palette[i].rgbBlue ;
}
delete[] Palette ;
}
// 压缩
BYTE * pEncode, * pSrc ;
pSrc = pEncode = (BYTE *) ::VirtualAlloc (NULL, Width()*Height()*2, MEM_COMMIT, PAGE_READWRITE) ;
if (pEncode == NULL)
__leave ;
DWORD dwWrite = this->__fooGifEncode (pEncode) ;
// 压缩数据打包
int iPack = dwWrite / 0xFF, // 生成块数
iLeft = dwWrite % 0xFF ; // 剩余字节
*pCurr++ = (this->ColorBits () == 1) ? 2 : this->ColorBits () ; // 写入最小码长度
while (iPack-- > 0) // 拷贝整块
{
*pCurr++ = 0xFF ;
CopyMemory (pCurr, pSrc, 0xFF) ;
pCurr += 0xFF ; pSrc += 0xFF ;
}
if (iLeft != 0) // 移动最后一个块
{
*pCurr++ = iLeft ;
CopyMemory (pCurr, pSrc, iLeft) ;
pCurr += iLeft ; pSrc += iLeft ;
}
::VirtualFree (pEncode, 0, MEM_RELEASE) ;
*pCurr++ = 0 ;
*pCurr++ = 0x3B ; // 设结尾
::UnmapViewOfFile (pStart) ;
::CloseHandle (hMap) ;
::SetFilePointer (hFile, pCurr - pStart, NULL, FILE_BEGIN) ;
::SetEndOfFile (hFile) ; // 结束头信息
::CloseHandle (hFile) ;
pStart = NULL ; hMap = NULL ;
hFile = INVALID_HANDLE_VALUE ;
bRet = TRUE ;
}
__finally
{
this->__fooImageUnmapFile (pStart, hMap, hFile) ;
}
return bRet ;
}
//===================================================================
BOOL FCImage::SaveGif (PCTSTR szFileName, bool bGolPal)
{
BOOL bRet = FALSE ;
BYTE * pStart = NULL, * pCurr ;
HANDLE hMap = NULL ;
HANDLE hFile = INVALID_HANDLE_VALUE ;
__try
{
if (pFooDib->hBitmap == NULL)
__leave ;
if (this->ColorBits() > 8) // 支持1, 4, 8位色
__leave ;
// 创建文件
pStart = this->__fooImageSaveFile (szFileName, &hFile, &hMap, 1024 * 4) ;
if (pStart == NULL)
__leave ;
// 写头
CopyMemory (pStart, "GIF89a", 6) ;
* (WORD *) &pStart[6] = this->Width() ; // 屏幕宽
* (WORD *) &pStart[8] = this->Height() ; // 屏幕高
pCurr = pStart + 13 ;
// 使用此帧调色板作为全局调色板
if (bGolPal)
{
pStart[10] = 0x80 | (this->ColorBits() - 1) ; // 设置全局调色板标志
// 设置全局调色板条目
int iColorNum = 1 << this->ColorBits() ;
RGBQUAD * Palette = new RGBQUAD [iColorNum] ;
this->GetColorTable (0, iColorNum, Palette) ;
for (int i = 0 ; i < iColorNum ; i++)
{
*pCurr++ = Palette[i].rgbRed ;
*pCurr++ = Palette[i].rgbGreen ;
*pCurr++ = Palette[i].rgbBlue ;
}
delete[] Palette ;
}
*pCurr++ = 0x3B ; // 头结束, 设置标志给 AddGifFrame ()
::UnmapViewOfFile (pStart) ;
::CloseHandle (hMap) ;
::SetFilePointer (hFile, pCurr - pStart, NULL, FILE_BEGIN) ;
::SetEndOfFile (hFile) ; // 结束头信息
::CloseHandle (hFile) ;
pStart = NULL ; hMap = NULL ;
hFile = INVALID_HANDLE_VALUE ;
// 加入第一帧
bRet = this->AddGifFrame (szFileName, bGolPal) ;
}
__finally
{
this->__fooImageUnmapFile ((BYTE *)pStart, hMap, hFile) ;
}
return bRet ;
}
//===================================================================
BOOL FCImage::LoadGif (PCTSTR resName, PCTSTR resType)
{
HRSRC res = ::FindResource (NULL, resName, resType) ;
HGLOBAL gol = ::LoadResource (NULL, res) ;
BYTE * pGifData = (BYTE *) ::LockResource (gol) ;
this->__fooGifScanFile (pGifData, ::SizeofResource (NULL, res)) ;
return this->LoadGifNextFrame () ;
}
//===================================================================
BOOL FCImage::LoadGif (PCTSTR szFileName)
{
BOOL bRet = FALSE ;
BYTE * pStart = NULL ;
HANDLE hMap = NULL ;
HANDLE hFile = INVALID_HANDLE_VALUE ;
__try
{
// 映射文件
pStart = this->__fooImageReadFile (szFileName, &hFile, &hMap) ;
if (pStart == NULL)
__leave ;
this->__fooGifScanFile (pStart, ::GetFileSize(hFile, NULL)) ;
if (!this->LoadGifNextFrame ()) // 读第一帧
__leave ;
bRet = TRUE ;
}
__finally
{
this->__fooImageUnmapFile ((BYTE *)pStart, hMap, hFile) ;
}
return bRet ;
}
//===================================================================
BOOL FCImage::LoadGifFrame (int iNumber)
{
m_pGifInfo->wCurrentFrame = max (0, min (iNumber - 1, m_pGifInfo->wTotalFrame)) ;
return (this->LoadGifNextFrame () == 1) ;
}
//===================================================================
int FCImage::LoadGifNextFrame ()
{
if (m_pGifInfo == NULL) // 未调用__fooGifScanFile 或失败
return 0 ;
if (m_pGifInfo->wCurrentFrame >= m_pGifInfo->wTotalFrame)
return 2 ; // 当前帧已是最后一帧
BYTE * pStart = NULL, * pFrame = NULL ;
int iRet = 0 ;
__try
{
pStart = m_pGifInfo->pStart ;
if (pStart == NULL)
__leave ;
// 定位当前帧的位置, 在0x2C后的第一个字节 + 4
pFrame = pStart + m_pGifInfo->pFrameIndexArray[m_pGifInfo->wCurrentFrame + 1] + 4 ;
// 创建DIB, 所有位色GIF一律解码为8位色DIB
if (!this->Create (* (WORD *) pFrame, * (WORD *) &pFrame[2], 8))
__leave ;
pFrame += 4 ;
m_pGifInfo->byInterlace = *pFrame & 0x40 ; // 交错显示
// 设置调色板
int iPalNum = 1 << ((*pFrame & 0x07) + 1) ;
RGBQUAD * Palette = new RGBQUAD [256] ;
if (*pFrame++ & 0x80) // 区域调色板
for (int i = 0 ; i < iPalNum ; i++)
{
Palette[i].rgbRed = *pFrame++ ;
Palette[i].rgbGreen = *pFrame++ ;
Palette[i].rgbBlue = *pFrame++ ;
}
else // 为全局调色板
{
iPalNum = m_pGifInfo->wGolPalNum ;
BYTE * pBak = pStart + 13 ;
for (int i = 0 ; i < iPalNum ; i++)
{
Palette[i].rgbRed = *pBak++ ;
Palette[i].rgbGreen = *pBak++ ;
Palette[i].rgbBlue = *pBak++ ;
}
}
this->SetColorTable (0, iPalNum, Palette) ;
delete[] Palette ;
// 解包 (因为GIF文件数据打包存放) ---------------------+
BYTE * pbyBak = pFrame ; // 保存指针, 先计算大小
pbyBak++ ; // Skip min-code length
while (*pbyBak != 0) // Skip data sub block
pbyBak += *pbyBak + 1 ;
BYTE * pIndata = new BYTE [pbyBak - pFrame] ;
pbyBak = pIndata ;
*pbyBak++ = *pFrame++ ; // copy min-code length
while (*pFrame != 0) // copy data sub block
{
CopyMemory (pbyBak, pFrame + 1, *pFrame) ;
pbyBak += *pFrame ;
pFrame += *pFrame + 1 ;
} // 解包完成, 未解压数据==>pIndata --------------------+
// 开始解压缩
this->__fooGifDecode (pIndata) ;
delete[] pIndata ;
m_pGifInfo->wCurrentFrame++ ;
iRet = 1 ;
}
__finally
{
}
return iRet ;
}
//===================================================================
bool FCImage::__fooGifScanFile (BYTE * pStart, int iFileSize)
{
bool bRet = false ;
BYTE * pCurr, * pEnd ;
__try
{
if (pStart == NULL)
__leave ;
this->__fooGifInitInfo (iFileSize) ; // 初始化m_pGifInfo
CopyMemory (m_pGifInfo->pStart, pStart, iFileSize) ;
pStart = m_pGifInfo->pStart ;
CopyMemory (&m_pGifInfo->byVersion, pStart + 3, 3) ; // Version
pCurr = pStart + 10 ; // ==>屏幕描述信息
pEnd = pStart + iFileSize ;
if (*pCurr & 0x80) // 存在全局调色板
{
m_pGifInfo->wGolPalNum = 1 << ((*pCurr & 0x07) + 1) ;
pCurr += m_pGifInfo->wGolPalNum * 3 ;
}
pCurr += 3 ;
while (pCurr < pEnd)
switch (*pCurr++)
{
case 0x21 : // 4种扩充块
switch (*pCurr++)
{
case 0xf9 : // GIF 图形控制扩充块
pCurr++ ;
m_pGifInfo->byTransFlag = *pCurr++ & 0x01 ; // 透明色标志
m_pGifInfo->wDelayTime = (* (WORD*) pCurr) * 10 ;
pCurr += 2 ;
m_pGifInfo->byTransparencyIndex = *pCurr ;
pCurr += 2 ;
break ;
case 0xfe : // GIF 注解说明控制块
case 0x01 : // GIF 图形文本扩充块
case 0xff : // GIF 应用程序扩充块
default :
while (*pCurr != 0) // Skip sub block
pCurr += *pCurr + 1 ;
pCurr++ ; // Terminator '\0'
}
break ;
case 0x2C : // 图像描述块
m_pGifInfo->pFrameIndexArray[++m_pGifInfo->wTotalFrame] = pCurr - pStart ;
pCurr += 8 ;
m_pGifInfo->byInterlace = *pCurr & 0x40 ; // 交错
if (*pCurr & 0x80) // 是否有区域调色板
pCurr += 3 * (1 << ((*pCurr & 0x07) + 1)) ;
pCurr++ ;
m_pGifInfo->byBitCount = *pCurr++ ; // Skip min-code length
while (*pCurr != 0) // Skip data sub block
pCurr += *pCurr + 1 ;
pCurr++ ; // Terminator '\0'
break ;
case 0x3B : // End of file
bRet = true ;
__leave ; // 成功退出
case 0x00 :
break ;
default :
__leave ; // 错误
} // End of switch
}
__finally
{
if (!bRet)
this->__fooGifFreeInfo () ;
}
return bRet ;
}
//===================================================================
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -