typedef struct tagTGAINFOHEADER
{
BYTE remarkSize;//图像描述长度,0表示无描述
BYTE bHasPal;//有无颜色表,1表示有
BYTE type;//1未压缩的颜色表图像,2,未压缩的RGB图像,9压缩的颜色表图像
WORD palStartIndex;//颜色表开始索张,一般为0000
WORD palEntries;//颜色表颜色个数,一般为0x100
BYTE palBitCount;//颜色表的色深,=24,表示24位色,一个颜色3个字节
WORD orgX;//图像水平开始点,一般为0000
WORD orgY;//图像垂直开始点,一般为0000
WORD width;//图像宽
WORD height;//图像高
BYTE bitCount;//一个像素用几个bit,8,8位256色,0x18,24位色
BYTE remark;//描述开始位,一般为空格0x20,其后的长度由remarkSize决定
}TGAINFOHEADER,*PTGAINFOHEADER,*LPTGAINFOHEADER;
type还可以是:
0 - 文件中没有图像数据
1 - 未压缩的,颜色表图像
2 - 未压缩的,rgb 图像
3 - 未压缩的,黑白图像
9 - runlength 编码的颜色表图像
10 - runlength 编码的 rgb 图像
11 - 压缩的,黑白图像
32 - 使用 huffman,delta 和 runlength 编码的颜色表图像
33 - 使用 huffman,delta 和 runlength 编码的颜色映射图像,4 趟四叉树类型处理。
TGA图像数据流不遵循4字节对齐,且行是正序排列,颜色表一般是BB GG RR顺序,结合这些特点,我们可以将TGA转换为BMP图像保存和显示.在此我们主要分析1,2和9的情况:
type=1,2时简单,只需DWORD对齐就行了.
type=9时,解压缩bit流:
数据开始时,第1个即为bFlag(标志码),是一个BYTE,最高位如果是1,则后面是重复数,重复的次数:count = (bFlag & 0x7F) + 1
最高位是0,则后面是原数,数据个数也是count = (bFlag & 0x7F) + 1
根据规律我们很容易找出第2个,第3个...标志码.
00 C3 82 B9 01 90 49 84 4B 81 2E 00 47 82 4B 02 47 3F 47 84 4B 01 3F 47 86 4B 06 49 4B 4B 45 47 3F
红色的是标志码.00是标志码,表示其后是1个原数C3,82是标志码,表示其后是3个B9,01是标志码,表示其后是2个原数90 49,84是标志码,表示其后是5个4B...
所以还原过来就是:
C3 B9 B9 B9 90 49 4B 4B 4B 4B 4B ...
这种算法不是以行为单位的压缩.
新建一个基于CDocument类的CPicDoc类,用于处理TGA和保存数据,新建一个基于CScrollView的CPicView类,用于滚屏显示TGA图像.(不要忘了新建一个基于CMDIChildWnd的框架类)
不要用以上结构,用指针,因为结构里有不连续的BYTE值,不对齐,引用成员时和sizeof时会得到错误的值,所以直接用索引来访问.
因为是正序行,所以用-height来正常显示.
BYTE* m_pBmData是CPicDoc中的一个成员变量,用于保存BMP全部数据,方便显示和保存.
BOOL CPicDoc::LoadTgaFile(LPCTSTR lpszFileName)
{
CFile file(lpszFileName, CFile::modeRead);
BOOL bResult = FALSE;
////////////header
BYTE tgaif[0x12];
file.Read(tgaif, sizeof(tgaif));
BYTE bHasPal = tgaif[1];//有无调色板
BYTE type = tgaif[2];//图片类型
WORD palEntries = *(WORD*)(tgaif + 0x05);//颜色表颜色个数
WORD tgWidth = *(WORD*)(tgaif + 0x0c);//TGA宽
WORD tgHeight = *(WORD*)(tgaif + 0x0e);//TGA高
BYTE bitCount = *(BYTE*)(tgaif + 0x10);//TGA bit out
int bmWidth = tgWidth % 4 == 0 ? tgWidth : tgWidth + 4 - (tgWidth % 4);//转换成4字节对齐的BMP宽
DWORD bmBitsSize = bmWidth * tgHeight * (bitCount / 8);//BMP图像流尺寸
DWORD bmInfoSize = sizeof(BITMAPINFOHEADER) + palEntries * sizeof(RGBQUAD);//BMP信息尺寸
DWORD bmSize = sizeof(BITMAPFILEHEADER) + bmInfoSize + bmBitsSize;//BMP文件尺寸
BITMAPFILEHEADER bmfh = {0x4d42, bmSize, 0, 0, sizeof(BITMAPFILEHEADER) + bmInfoSize};//BMP文件头
BITMAPINFOHEADER bmih = {0x28, tgWidth, - tgHeight, 1, bitCount, 0, 0, 0, 0, palEntries, 0};//BMP信息头
///////////////////////////
file.SeekToBegin();
int tgSize = file.GetLength();
BYTE* pTgBuf = new BYTE[tgSize];
file.Read(pTgBuf, tgSize);
m_pBmData = new BYTE[bmSize];
memset(m_pBmData, 0, bmSize);
memcpy(m_pBmData, &bmfh, sizeof(bmfh));//bitmap file header
memcpy(m_pBmData + sizeof(bmfh), &bmih, sizeof(bmih));//bitmap info header
BYTE* pTg = pTgBuf + 0x12 + *pTgBuf;
BYTE* pBm = m_pBmData + sizeof(bmfh) + sizeof(bmih);
if ((type == 1 || type == 9) && bHasPal == 1)
{
//////////palette
for(int i = 0; i < palEntries; i ++)
{
BYTE* p = pTg + i * 3;
RGBQUAD rgb = {*p, *(p + 1), *(p + 2), 0};
*(RGBQUAD*)(pBm + i * 4) = rgb;
}
/////////bits
pTg += palEntries * 3;//TGA数据开始位置
pBm += palEntries * sizeof(RGBQUAD);//BMP数据开始位置
if (type == 1)
{
AlignDOWD(pBm, pTg, tgWidth, tgHeight, bitCount / 8);//对齐DWORD
}
else if (type == 9)
{
//decompress bits
BYTE* pTempBits = new BYTE[bmBitsSize];
BYTE* pTemp = pTempBits;
while (pTg < pTgBuf + tgSize)
{
BYTE flags = *pTg;//标志
int size = (flags & 0x7F) + 1;//长度
pTg ++;
if (flags > 0x7F)
{
memset(pTemp, *pTg, size);//重复size次
pTg ++;
}
else
{
memcpy(pTemp, pTg, size);//拷贝size个原数
pTg += size;
}
pTemp += size;//目的指针
}
AlignDOWD(pBm, pTempBits, tgWidth, tgHeight, bitCount / 8);//对齐DWORD
delete pTempBits;
}
bResult = TRUE;
}
else if ((type == 2) && bHasPal == 0)
{
AlignDOWD(pBm, pTg, tgWidth, tgHeight, bitCount / 8);//对齐DWORD
bResult = TRUE;
}
delete pTgBuf;
return bResult;
}
void CPicDoc::AlignDOWD(BYTE *pDest, BYTE *pSrc, int nSrcWidth, int nSrcHeight, int nByte)
{
if (nSrcWidth % 4 == 0)
{
memcpy(pDest, pSrc, nSrcWidth * nSrcHeight * nByte);
}
else
{
int nDestWidth = nSrcWidth + 4 - (nSrcWidth % 4);
for (int rows = 0; rows < nSrcHeight; rows ++)
{
memcpy(pDest + rows * nDestWidth * nByte, pSrc + rows * nSrcWidth * nByte, nSrcWidth * nByte);
}
}
}
SIZE CPicDoc::GetPicSize()
{
BITMAPINFOHEADER* p = (BITMAPINFOHEADER*)(m_pBmData + sizeof(BITMAPFILEHEADER));
int cy = p->biHeight < 0 ? -p->biHeight : p->biHeight;
SIZE sz = {p->biWidth, cy};
return sz;
}
{
CFile file(lpszFileName, CFile::modeRead);
BOOL bResult = FALSE;
////////////header
BYTE tgaif[0x12];
file.Read(tgaif, sizeof(tgaif));
BYTE bHasPal = tgaif[1];//有无调色板
BYTE type = tgaif[2];//图片类型
WORD palEntries = *(WORD*)(tgaif + 0x05);//颜色表颜色个数
WORD tgWidth = *(WORD*)(tgaif + 0x0c);//TGA宽
WORD tgHeight = *(WORD*)(tgaif + 0x0e);//TGA高
BYTE bitCount = *(BYTE*)(tgaif + 0x10);//TGA bit out
int bmWidth = tgWidth % 4 == 0 ? tgWidth : tgWidth + 4 - (tgWidth % 4);//转换成4字节对齐的BMP宽
DWORD bmBitsSize = bmWidth * tgHeight * (bitCount / 8);//BMP图像流尺寸
DWORD bmInfoSize = sizeof(BITMAPINFOHEADER) + palEntries * sizeof(RGBQUAD);//BMP信息尺寸
DWORD bmSize = sizeof(BITMAPFILEHEADER) + bmInfoSize + bmBitsSize;//BMP文件尺寸
BITMAPFILEHEADER bmfh = {0x4d42, bmSize, 0, 0, sizeof(BITMAPFILEHEADER) + bmInfoSize};//BMP文件头
BITMAPINFOHEADER bmih = {0x28, tgWidth, - tgHeight, 1, bitCount, 0, 0, 0, 0, palEntries, 0};//BMP信息头
///////////////////////////
file.SeekToBegin();
int tgSize = file.GetLength();
BYTE* pTgBuf = new BYTE[tgSize];
file.Read(pTgBuf, tgSize);
m_pBmData = new BYTE[bmSize];
memset(m_pBmData, 0, bmSize);
memcpy(m_pBmData, &bmfh, sizeof(bmfh));//bitmap file header
memcpy(m_pBmData + sizeof(bmfh), &bmih, sizeof(bmih));//bitmap info header
BYTE* pTg = pTgBuf + 0x12 + *pTgBuf;
BYTE* pBm = m_pBmData + sizeof(bmfh) + sizeof(bmih);
if ((type == 1 || type == 9) && bHasPal == 1)
{
//////////palette
for(int i = 0; i < palEntries; i ++)
{
BYTE* p = pTg + i * 3;
RGBQUAD rgb = {*p, *(p + 1), *(p + 2), 0};
*(RGBQUAD*)(pBm + i * 4) = rgb;
}
/////////bits
pTg += palEntries * 3;//TGA数据开始位置
pBm += palEntries * sizeof(RGBQUAD);//BMP数据开始位置
if (type == 1)
{
AlignDOWD(pBm, pTg, tgWidth, tgHeight, bitCount / 8);//对齐DWORD
}
else if (type == 9)
{
//decompress bits
BYTE* pTempBits = new BYTE[bmBitsSize];
BYTE* pTemp = pTempBits;
while (pTg < pTgBuf + tgSize)
{
BYTE flags = *pTg;//标志
int size = (flags & 0x7F) + 1;//长度
pTg ++;
if (flags > 0x7F)
{
memset(pTemp, *pTg, size);//重复size次
pTg ++;
}
else
{
memcpy(pTemp, pTg, size);//拷贝size个原数
pTg += size;
}
pTemp += size;//目的指针
}
AlignDOWD(pBm, pTempBits, tgWidth, tgHeight, bitCount / 8);//对齐DWORD
delete pTempBits;
}
bResult = TRUE;
}
else if ((type == 2) && bHasPal == 0)
{
AlignDOWD(pBm, pTg, tgWidth, tgHeight, bitCount / 8);//对齐DWORD
bResult = TRUE;
}
delete pTgBuf;
return bResult;
}
void CPicDoc::AlignDOWD(BYTE *pDest, BYTE *pSrc, int nSrcWidth, int nSrcHeight, int nByte)
{
if (nSrcWidth % 4 == 0)
{
memcpy(pDest, pSrc, nSrcWidth * nSrcHeight * nByte);
}
else
{
int nDestWidth = nSrcWidth + 4 - (nSrcWidth % 4);
for (int rows = 0; rows < nSrcHeight; rows ++)
{
memcpy(pDest + rows * nDestWidth * nByte, pSrc + rows * nSrcWidth * nByte, nSrcWidth * nByte);
}
}
}
SIZE CPicDoc::GetPicSize()
{
BITMAPINFOHEADER* p = (BITMAPINFOHEADER*)(m_pBmData + sizeof(BITMAPFILEHEADER));
int cy = p->biHeight < 0 ? -p->biHeight : p->biHeight;
SIZE sz = {p->biWidth, cy};
return sz;
}
显示代码:
void CPicView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CPicDoc* pDoc = GetDocument();
CSize sizeTotal = pDoc->GetPicSize();
SetScrollSizes(MM_TEXT, sizeTotal);
}
void CPicView::OnDraw(CDC* pDC)
{
CPicDoc* pDoc = GetDocument();
HDC hDC = pDC->m_hDC;
SIZE sizeTotal = pDoc->GetPicSize();
BITMAPINFOHEADER* lpbmih = (BITMAPINFOHEADER*)(pDoc->m_pBmData + sizeof(BITMAPFILEHEADER));
BYTE* lpbInit = pDoc->m_pBmData + ((BITMAPFILEHEADER*)pDoc->m_pBmData)->bfOffBits;
::StretchDIBits(hDC, 0, 0, sizeTotal.cx, sizeTotal.cy,
0, 0, sizeTotal.cx, sizeTotal.cy, lpbInit, (BITMAPINFO*)lpbmih, DIB_RGB_COLORS, SRCCOPY);
}
{
CScrollView::OnInitialUpdate();
CPicDoc* pDoc = GetDocument();
CSize sizeTotal = pDoc->GetPicSize();
SetScrollSizes(MM_TEXT, sizeTotal);
}
void CPicView::OnDraw(CDC* pDC)
{
CPicDoc* pDoc = GetDocument();
HDC hDC = pDC->m_hDC;
SIZE sizeTotal = pDoc->GetPicSize();
BITMAPINFOHEADER* lpbmih = (BITMAPINFOHEADER*)(pDoc->m_pBmData + sizeof(BITMAPFILEHEADER));
BYTE* lpbInit = pDoc->m_pBmData + ((BITMAPFILEHEADER*)pDoc->m_pBmData)->bfOffBits;
::StretchDIBits(hDC, 0, 0, sizeTotal.cx, sizeTotal.cy,
0, 0, sizeTotal.cx, sizeTotal.cy, lpbInit, (BITMAPINFO*)lpbmih, DIB_RGB_COLORS, SRCCOPY);
}
保存成BITMAP图像,直接写入m_pBmData就行了,所以没有在此写出代码.以上代码在MFC下测试成功.
不要忘了在CPicDoc类构造函数中:
{
m_pBmData = NULL;
}
在CPicDoc类在析构函数中:
{
if (m_pBmData)
delete m_pBmData;
}