📄 coding.cpp
字号:
// 指向DIB的指针
LPBYTE lpDIB;
// 指向DIB象素指针
LPBYTE lpDIBBits;
// 锁定DIB
lpDIB = (LPBYTE) ::GlobalLock((HGLOBAL) hDIB);
// 找到DIB图像象素起始位置
lpDIBBits = m_clsDIB.FindDIBBits(lpDIB);
// 判断是否是24-bpp位图
if (m_clsDIB.DIBBitCount(lpDIB) != 24)
{
// 提示用户
MessageBox("请先将其转换为24位色位图,再进行处理!", "系统提示" , MB_ICONINFORMATION | MB_OK);
// 解除锁定
::GlobalUnlock((HGLOBAL) hDIB);
// 返回
return FALSE;
}
// 更改光标形状
BeginWaitCursor();
// DIB的宽度
LONG lWidth = m_clsDIB.DIBWidth(lpDIB);
// DIB的高度
LONG lHeight = m_clsDIB.DIBHeight(lpDIB);
// 计算图像每行的字节数
LONG lLineBytes = WIDTHBYTES(lWidth * 24);
//*************************************************************************
// PCX文件头
PCXHEADER pcxHdr;
// 给文件头赋值
// PCX标识码
pcxHdr.bManufacturer = 0x0A;
// PCX版本号
pcxHdr.bVersion = 5;
// PCX编码方式(1表示RLE编码)
pcxHdr.bEncoding = 1;
// 像素位数(24位色为8位)
pcxHdr.bBpp = 8;
// 图像相对于屏幕的左上角X坐标(以像素为单位)
pcxHdr.wLeft = 0;
// 图像相对于屏幕的左上角Y坐标(以像素为单位)
pcxHdr.wTop = 0;
// 图像相对于屏幕的右下角X坐标(以像素为单位)
pcxHdr.wRight = lWidth - 1;
// 图像相对于屏幕的右下角Y坐标(以像素为单位)
pcxHdr.wBottom = lHeight - 1;
// 图像的水平分辨率
pcxHdr.wXResolution = (WORD)lWidth;
// 图像的垂直分辨率
pcxHdr.wYResolution = (WORD)lHeight;
// 保留域,设定为0。
pcxHdr.bReserved = 0;
// 图像色彩平面数目
pcxHdr.bPlanes = 3;
// 图像的宽度(字节为单位),必须为偶数。
pcxHdr.wLineBytes = (WORD)lWidth;
// 图像调色板的类型,1表示彩色或者单色图像,2表示图像是灰度图。
pcxHdr.wPaletteType = 1;
// 制作该图像的屏幕宽度(像素为单位)
pcxHdr.wSrcWidth = 0;
// 制作该图像的屏幕高度(像素为单位)
pcxHdr.wSrcDepth = 0;
// 保留域,取值设定为0。
for (i = 0; i < 54; i ++)
{
pcxHdr.bFiller[i] = 0;
}
// 写入文件头
file.Write((LPBYTE)&pcxHdr, sizeof(PCXHEADER));
//*******************************************************************************
// 开始编码
// 开辟一片缓冲区(2被原始图像大小)以保存编码结果
lpDst = new BYTE[lHeight * lLineBytes * 2];
// 指明当前已经用了多少缓冲区(字节数)
dwBuffUsed = 0;
// 每行
for (i = 0; i < lHeight; i++)
{
for (k = 0; k < 3; k++)
{
// 指向DIB第i行,第0个象素的指针
lpSrc = (BYTE*)(lpDIBBits + lLineBytes * (lHeight - 1 - i) + RGB[k]);
// 给bChar1赋值
bChar1 = *lpSrc;
// 设置iCount为1
iCount = 1;
// 剩余列
for (j = 3; j < lLineBytes; j += 3)
{
// 指向DIB第i行,第j个象素的指针
bChar2 = *(lpSrc + j);
// 判断是否和bChar1相同并且iCount < 63
if ((bChar1 == bChar2) && (iCount < 63))
{
// 相同,计数加1
iCount ++;
}
else// 不同,或者iCount = 63
{
// 写入缓冲区
if ((iCount > 1) || (bChar1 >= 0xC0))
{
// 保存码长信息
lpDst[dwBuffUsed] = iCount | 0xC0;
// 保存bChar1
lpDst[dwBuffUsed + 1] = bChar1;
// 更新dwBuffUsed
dwBuffUsed += 2;
}
else
{
// 直接保存该值
lpDst[dwBuffUsed] = bChar1;
// 更新dwBuffUsed
dwBuffUsed ++;
}
// 重新给bChar1赋值
bChar1 = bChar2;
// 设置iCount为1
iCount = 1;
}
}
// 保存每行最后一部分编码
if ((iCount > 1) || (bChar1 >= 0xC0))
{
// 保存码长信息
lpDst[dwBuffUsed] = iCount | 0xC0;
// 保存bChar1
lpDst[dwBuffUsed + 1] = bChar1;
// 更新dwBuffUsed
dwBuffUsed += 2;
}
else
{
// 直接保存该值
lpDst[dwBuffUsed] = bChar1;
// 更新dwBuffUsed
dwBuffUsed ++;
}
}
}
// 写入编码结果
file.WriteHuge((LPBYTE)lpDst, dwBuffUsed);
// 释放内存
delete[] lpDst;
// 关闭文件
file.Close();
// 更改光标形状
EndWaitCursor();
// 返回
return TRUE;
}
/*************************************************************************
*
* 函数名称:
* PCXToDIB()
*
* 参数:
* CString strPath - 要装载的文件路径
*
* 返回值:
* HDIB - 成功返回DIB的句柄,否则返回NULL。
*
* 说明:
* 该函数将读取指定的PCX文件。读取的结果保存在DIB对象中。
*
*************************************************************************/
HDIB CCoding::PCXToDIB(CString strPath)
{
// PCX文件头
PCXHEADER pcxHdr;
// DIB大小(字节数)
DWORD dwDIBSize;
// DIB句柄
HDIB hDIB;
// DIB指针
LPBYTE pDIB;
// 循环变量
LONG i;
LONG j;
LONG k;
LONG l;
// 重复像素计数
int iCount;
// 调整RGB顺序
int RGB[3] = {2, 1, 0};
// 中间变量
BYTE bChar;
// 指向源图像象素的指针
BYTE * lpSrc;
// 指向编码后图像数据的指针
BYTE * lpDst;
// 临时指针
BYTE * lpTemp;
// 打开文件
CFile file;
file.Open(strPath, CFile::modeReadWrite);
// 尝试读取PCX文件头
if (file.Read((LPBYTE)&pcxHdr, sizeof(PCXHEADER)) != sizeof(PCXHEADER))
{
// 大小不对,返回NULL。
return NULL;
}
// 判断是否是24位色PCX文件,检查第一个字节是否是0x0A,
if ((pcxHdr.bManufacturer != 0x0A) || (pcxHdr.bBpp != 8) || (pcxHdr.bPlanes != 3))
{
// 非24位色PCX文件,返回NULL。
return NULL;
}
// 获取图像高度
LONG lHeight = pcxHdr.wBottom - pcxHdr.wTop + 1;
// 获取图像宽度
LONG lWidth = pcxHdr.wRight - pcxHdr.wLeft + 1;
// 计算图像每行的字节数
LONG lLineBytes = WIDTHBYTES(lWidth * 24);
// 计算DIB长度(字节)
dwDIBSize = sizeof(BITMAPINFOHEADER) + 1024 + lHeight * lLineBytes;
// 为DIB分配内存
hDIB = (HDIB) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwDIBSize);
if (hDIB == 0)
{
// 内存分配失败,返回NULL。
return NULL;
}
// 锁定
pDIB = (LPBYTE) ::GlobalLock((HGLOBAL) hDIB);
// 指向BITMAPINFOHEADER的指针
LPBITMAPINFOHEADER lpBI;
// 赋值
lpBI = (LPBITMAPINFOHEADER) pDIB;
// 给lpBI成员赋值
lpBI->biSize = 40;
lpBI->biWidth = lWidth;
lpBI->biHeight = lHeight;
lpBI->biPlanes = 1;
lpBI->biBitCount = 24;
lpBI->biCompression = BI_RGB;
lpBI->biSizeImage = lHeight * lLineBytes;
lpBI->biXPelsPerMeter = pcxHdr.wXResolution;
lpBI->biYPelsPerMeter = pcxHdr.wYResolution;
lpBI->biClrUsed = 0;
lpBI->biClrImportant = 0;
// 分配内存以读取编码后的象素
lpSrc = new BYTE[file.GetLength() - sizeof(PCXHEADER) - 769];
lpTemp = lpSrc;
// 读取编码后的象素
if (file.ReadHuge(lpSrc, file.GetLength() - sizeof(PCXHEADER) - 769) !=
file.GetLength() - sizeof(PCXHEADER) - 769 )
{
// 大小不对。
// 解除锁定
::GlobalUnlock((HGLOBAL) hDIB);
// 释放内存
::GlobalFree((HGLOBAL) hDIB);
// 返回NULL。
return NULL;
}
// 计算DIB中像素位置
lpDst = (BYTE *) m_clsDIB.FindDIBBits(pDIB);
// 一行一行解码
for (j = 0; j < lHeight; j++)
{
for (k = 0; k < 3; k++)
{
i = 0;
while (i < lWidth)
{
// 读取一个字节
bChar = *lpTemp;
lpTemp++;
if ((bChar & 0xC0) == 0xC0)
{
// 行程
iCount = bChar & 0x3F;
// 读取下一个字节
bChar = *lpTemp;
lpTemp++;
// bChar重复iCount次保存
for (l = 0; l < iCount; l++)
*(lpDst + (lHeight - j - 1) * lLineBytes + (i + l) * 3 + RGB[k]) = bChar;
// 已经读取像素的个数加iCount
i += iCount;
}
else
{
// 保存当前字节
*(lpDst + (lHeight - j - 1) * lLineBytes + i * 3 + RGB[k]) = bChar;
// 已经读取像素的个数加1
i += 1;
}
}
}
}
// 释放内存
delete lpSrc;
// 解除锁定
::GlobalUnlock((HGLOBAL) hDIB);
// 关闭文件
file.Close();
// 返回DIB句柄
return hDIB;
}
/*************************************************************************
*
* 函数名称:
* Huffman()
*
* 参数:
* HDIB hDIB - 待编码DIB句柄
* CString strPath - 要装载的文件路径
*
* 返回值:
* BOOL - 成功返回True,否则返回False。
*
* 说明:
* 该函数对指定DIB位图进行编码
*
*************************************************************************/
BOOL CCoding::Huffman(HDIB hDIB, CString strPath)
{
// 循环变量
LONG i;
LONG j;
LONG k;
// 灰度计数
int nNs[256];
// 灰度概率分布
float fPs[256];
// 映射关系
int iMap[256];
// Huffman编码
CString m_strCode [256];
// 变量初始化
memset(nNs, 0, sizeof(nNs));
// 指向DIB的指针
LPBYTE lpDIB;
// 指向DIB象素指针
LPBYTE lpDIBBits;
// 锁定DIB
lpDIB = (LPBYTE) ::GlobalLock((HGLOBAL) hDIB);
// 找到DIB图像象素起始位置
lpDIBBits = m_clsDIB.FindDIBBits(lpDIB);
// 判断是否是24-bpp位图
if (m_clsDIB.DIBBitCount(lpDIB) != 24)
{
// 提示用户
MessageBox("请先将其转换为24位色位图,再进行处理!", "系统提示" , MB_ICONINFORMATION | MB_OK);
// 解除锁定
::GlobalUnlock((HGLOBAL) hDIB);
// 返回
return FALSE;
}
// 更改光标形状
BeginWaitCursor();
//////////////////////////////////////////////////////////
// 计算灰度概率分布
// DIB的宽度
LONG lWidth = m_clsDIB.DIBWidth(lpDIB);
// DIB的高度
LONG lHeight = m_clsDIB.DIBHeight(lpDIB);
// 计算图像每行的字节数
LONG lLineBytes = WIDTHBYTES(lWidth * 24);
// 对各像素进行灰度分布统计
for (i = 0; i < lHeight; i ++)
{
for (j = 0; j < lLineBytes; j ++)
{
// 对各像素进行灰度统计
unsigned char V = *((unsigned char *)lpDIBBits + lLineBytes * i + j);
nNs[V]++;
}
}
// 计算灰度分布密度
for(i = 0; i < 256; i++)
fPs[i] = nNs[i] / (lHeight * lLineBytes * 1.0f);
// 初始化
for (i = 0; i < 256; i ++)
iMap[i] = i;
//////////////////////////////////////////////////////////
// 用冒泡法对fPs[]进行排序
for (j = 0; j < 256 - 1; j ++)
{
for (i = 0; i < 256 - j - 1; i ++)
{
if (fPs[i] > fPs[i + 1])
{
// 互换
float fTemp = fPs[i];
fPs[i] = fPs[i + 1];
fPs[i + 1] = fTemp;
// 更新映射关系
for (k = 0; k < 256; k ++)
{
// 判断是否是fPs[i]的子节点
if (iMap[k] == i)
{
// 映射到节点i+1
iMap[k] = i + 1;
}
else if (iMap[k] == i + 1)
{
// 映射到节点i
iMap[k] = i;
}
}
}
}
}
//////////////////////////////////////////////////////////
// 计算哈夫曼编码表
for (i = 0; i < 256 - 1; i ++)
{
// 寻找第一个不为0的概率灰度级
if (fPs[i] > 0)
break;
}
// 开始编码
for (i = i; i < 256 - 1; i ++)
{
// 更新m_strCode
for (k = 0; k < 256; k ++)
{
// 判断是否是fPs[i]的子节点并编码字符串
if (iMap[k] == i)
m_strCode[k] = "1" + m_strCode[k];
else if (iMap[k] == i + 1)
m_strCode[k] = "0" + m_strCode[k];
}
// 概率最小的两个概率相加,结果保存到fPs[i + 1]
fPs[i + 1] += fPs[i];
// 改变映射关系
for (k = 0; k < 256; k ++)
{
// 判断是否是fPs[i]的子节点
if (iMap[k] == i)
{
// 映射到节点i+1
iMap[k] = i + 1;
}
}
// 重新排序
for (j = i + 1; j < 256 - 1; j ++)
{
if (fPs[j] > fPs[j + 1])
{
// 互换
float fTemp = fPs[j];
fPs[j] = fPs[j + 1];
fPs[j + 1] = fTemp;
// 更新映射关系
for (k = 0; k < 256; k ++)
{
// 判断是否是fPs[i]的子节点
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -