之前说过需要Jpeg代码的可以发邮件联系我,因为最近比较忙,恕不能一一回复。只好在这里将主要的实现代码写下拉,大家需要的话可以自己研究。
一。CDib类实现BMP图像数据解析与Jpeg编码
Dib.h
// Dib.h: interface for the CDib class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_DIB_H__FE6314AF_2346_49AE_A383_AA4421DBBF12__INCLUDED_)
#define AFX_DIB_H__FE6314AF_2346_49AE_A383_AA4421DBBF12__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//戴玉超 2005,12,28
class CDib : public CObject
{
public:
CDib();
virtual ~CDib();
DWORD GetHeight() //得到图像的高度
{
return dwHeight;
}
DWORD GetWidth() //得到图像的宽度
{
return dwWidth;
}
BOOL Is24bit() //判断是否24位bmp图像
{
return m_bit24;
}
private:
DWORD dwHeight; // Height of the map
DWORD dwWidth; // Width of the map
BOOL m_bit24; // is a 24bit map
public:
BOOL RGB2YCbCr(double *rgb,double *yrr); // 实现RGB颜色空间到YCrCb颜色空间的变换
BOOL SetPalette(CDC *pDC); // 设置调色板
BOOL Draw(CDC* pDC, CPoint origin); // 图形绘制
BOOL SaveImage(LPCTSTR lpszFileName); // 保存图像
void LoadImage(LPCTSTR lpszFileName); // 加载图像
public:
BOOL Write32BitData(int nData); // 写入32位数据
BOOL Quant(double *data,int * table,unsigned char * QuantTable); // 根据给定的量化表实现8*8块的量化
BOOL DCT_1D(double *data,double *temp); // 一维DCT
BOOL DCT(double * sourcedata); // 二维DCT
BOOL YCbCr2RGB(double * yrr, double *rgb); // 实现YCrCb颜色空间到RGB颜色空间的变换
BOOL RunLengthCode(int * table, int * RLC_length); // 行程编码
void CDib::ShiftWrite(int huffmanbitnum,int huffmancode); // 按位写入数据
BOOL CDib::WriteSurplus(); // 写最后的位
BOOL SaveAsJpeg(LPCTSTR lpszFileName); //保存为Jpeg图像
BOOL Jpeg(); //完成 Jpeg压缩的全过程
int CDib::WriteData(int nPreDCdata,int * table,int *DChufco,int *DChufsi,int *AChufco,int *AChufsi); //写入数据
BOOL CDib::HuffmanTable(unsigned char *bit,unsigned char*val); //Huffman编码
public:
int HuffmanCode[251],HuffmanLength[251]; //Huffman码字和位
PBYTE pDibData; //数据
CFile file; //文件指针
struct RLC
{
int digit;
int length;
} rlc[64]; //记录8*8的游程数据
unsigned char * m_pDibBits; // pointer to the rgb data;
int m_nPaletteEntries; // 调色板数量
RGBQUAD * m_pPalette; // 调色板
CPalette m_Palette; //
DWORD m_dwDibSize; //文件大小
BITMAPINFOHEADER * m_pBIH; // bmp文件头信息
unsigned char * m_pDib; //指向数据的指针
int EncodeJpeg; //32位的数据缓冲区,用于把不等长的码串串成4个字节以便写入JPEG文件
int surplus; //32位数据缓冲区中剩余没使用的位长,使用数据缓冲区从高位到低位
};
#endif // !defined(AFX_DIB_H__FE6314AF_2346_49AE_A383_AA4421DBBF12__INCLUDED_)
Dib.cpp
// Dib.cpp: implementation of the CDib class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "JpegCompression.h"
#include "Dib.h"
#include "math.h"
#include "iostream.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
const double pi = 3.1415926535;
extern unsigned char LumQuantTable[64]; //亮度量化表
extern unsigned char ChrQuantTable[64]; //色差量化表
extern short ZigzagTable[8*8]; //ZigZag扫描
extern unsigned int LumDCValue[12]; //亮度DC码值
extern unsigned int ChrDCValue[12]; //色差DC码值
extern unsigned char LumACHuffmanVal[162];
extern unsigned char ChrACHuffmanVal[162];
extern unsigned char LumDCHuffmanBit[17]; //亮度DC哈夫曼表的位值
extern unsigned char LumDCHuffmanVal[12]; //亮度DC哈夫曼表的码值
extern unsigned char LumACHuffmanBit[17]; //亮度AC哈夫曼表的位值
extern unsigned char ChrDCHuffmanBit[17];
extern unsigned char ChrDCHuffmanVal[12];
extern unsigned char ChrACHuffmanBit[17];
extern int LumDCHuffmanLen[12];
extern int LumDCHuffmanCode[12];
extern int LumACHuffmanLen[162];
extern int LumACHuffmanCode[162];
extern int ChrDCHuffmanLen[12];
extern int ChrDCHuffmanCode[12];
extern int ChrACHuffmanLen[162];
extern int ChrACHuffmanCode[162];
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CDib::CDib() //构造函数
{
m_pDib = NULL;
pDibData = NULL;
m_bit24 = 0;
surplus = 32;
EncodeJpeg=0;
}
CDib::~CDib()
{
}
//加载图像
void CDib::LoadImage(LPCTSTR lpszFileName)
{
CFile cf;
LPBYTE pDib = NULL;
DWORD dwDibSize = 0;
try
{
if(!cf.Open(lpszFileName,CFile::modeReadWrite,NULL))
{
throw new CException;
}
dwDibSize = cf.GetLength() - sizeof(BITMAPFILEHEADER);
pDib = (LPBYTE)VirtualAlloc(NULL,dwDibSize,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if (pDib == NULL)
{
AfxMessageBox("Error in allocate space!");
}
BITMAPFILEHEADER * pBMPHeader = new BITMAPFILEHEADER;
if(cf.Read(pBMPHeader,sizeof(BITMAPFILEHEADER)) != sizeof(BITMAPFILEHEADER))
{
throw new CException;
}
if(pBMPHeader->bfType != 0x4d42)
{
throw new CException;
}
if(cf.ReadHuge(pDib,dwDibSize) != dwDibSize)
{
throw new CException;
}
if(m_pDib != NULL)
{
delete m_pDib;
}
m_pDib = pDib;
m_dwDibSize = dwDibSize;
m_pBIH = (BITMAPINFOHEADER *)pDib;
m_pPalette = (RGBQUAD *)&m_pDib[sizeof(BITMAPINFOHEADER)];
m_nPaletteEntries = 1<< m_pBIH->biBitCount;
if (m_pBIH->biBitCount > 8)
{
m_nPaletteEntries = 0;
}
else if (m_pBIH->biClrUsed != 0)
{
m_nPaletteEntries = m_pBIH->biClrUsed;
}
m_pDibBits = m_pDib + sizeof(BITMAPINFOHEADER) + m_nPaletteEntries * sizeof(RGBQUAD);
if(m_Palette.GetSafeHandle() != NULL)
{
m_Palette.DeleteObject();
}
if (m_nPaletteEntries != 0)
{
LOGPALETTE *pLogPal =
(LOGPALETTE *)new char[sizeof(LOGPALETTE) + m_nPaletteEntries * sizeof(PALETTEENTRY)];
if (pLogPal != NULL)
{
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = m_nPaletteEntries;
for(int i = 0; i < m_nPaletteEntries; i++)
{
pLogPal->palPalEntry[i].peRed = m_pPalette[i].rgbRed;
pLogPal->palPalEntry[i].peGreen = m_pPalette[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = m_pPalette[i].rgbBlue;
}
m_Palette.CreatePalette(pLogPal);
delete []pLogPal;
}
}
if (m_pBIH ->biBitCount != 24)
{
AfxMessageBox("This is not a 24_bit bitmap");
m_bit24 = FALSE;
}
else
{
m_bit24 = TRUE;
}
dwHeight = m_pBIH->biHeight;
dwWidth = m_pBIH->biWidth;
DWORD offset = pBMPHeader->bfOffBits;
if(m_bit24 == 1)
{
//pDibData存储从左上开始的图像数据
pDibData = (PBYTE)VirtualAlloc(NULL,dwHeight*dwWidth*3,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
memset(pDibData,0,dwHeight*dwWidth*3);
cf.Seek(offset,CFile::begin); //定位至位数据的开始处
for(LONG y = dwHeight - 1 ; y >= 0; y--)
{
cf.Read(pDibData + y * dwWidth * 3,dwWidth*3); //数组pDibData存储24位色BMP文件的图像数据
cf.Seek(dwWidth%4,CFile::current); //***** 此处很重要,位图行的字节数等于
} //行除以4乘以3,再加上行除以4的余。24位图每象素3字节
cf.Close();
}
else if(m_bit24 == 0)
{
//pDibData存储从左上开始的图像数据
pDibData = (PBYTE)VirtualAlloc(NULL,dwHeight*dwWidth,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
memset(pDibData,0,dwHeight*dwWidth);
cf.Seek(offset,CFile::begin); //定位至位数据的开始处
for(LONG y = dwHeight - 1 ; y >= 0; y--)
{
cf.Read(pDibData + y * dwWidth,dwWidth); //数组pDibData存储8位BMP文件的图像数据
}
cf.Close();
}
}
catch(CException* pe)
{
AfxMessageBox("Read error");
pe->Delete();
VirtualFree(pDib,dwDibSize,MEM_DECOMMIT|MEM_RESERVE);
}
}
//保存原始位图或者改动之后的位图于指定路径
BOOL CDib::SaveImage(LPCTSTR lpszFileName)
{
if(m_pDib == NULL)
{
return FALSE;
}
CFile cf;
if( !cf.Open(lpszFileName,CFile::modeCreate|CFile::modeWrite,NULL))
{
return FALSE;
}
BITMAPFILEHEADER *pBFH = new BITMAPFILEHEADER;
pBFH->bfType = 0x4d42;
pBFH->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*m_nPaletteEntries;
pBFH->bfSize = sizeof(BITMAPFILEHEADER) + m_dwDibSize;
pBFH->bfReserved1 = pBFH->bfReserved2 = 0;
try
{
cf.Write(pBFH,sizeof(BITMAPFILEHEADER));
cf.WriteHuge(m_pDib,m_dwDibSize);
}
catch(CException* pe)
{
pe->Delete();
AfxMessageBox("write error");
return FALSE;
}
return TRUE;
}
//实现图形显示
BOOL CDib::Draw(CDC *pDC, CPoint origin)
{
if(m_pDib == NULL)
{
return FALSE;
}
// 如果调色板不为空,则将调色板选入设备上下文
if(m_pPalette != NULL)
{
SelectPalette(pDC->GetSafeHdc(), m_Palette, TRUE);
}
pDC->SetStretchBltMode(COLORONCOLOR);
StretchDIBits(pDC->m_hDC,origin.x,origin.y,
m_pBIH->biWidth,m_pBIH->biHeight,0,0,
m_pBIH->biWidth,m_pBIH->biHeight,m_pDibBits,(BITMAPINFO *) m_pDib,BI_RGB,SRCCOPY);
return TRUE;
}
//对于使用调色板的情况设置调色板
BOOL CDib::SetPalette(CDC *pDC)
{
if (m_pDib == NULL)
{
return FALSE;
}
if (m_Palette.GetSafeHandle() == NULL)
{
return FALSE;
}
CPalette *pOldPalette;
pOldPalette = pDC->SelectPalette(&m_Palette,FALSE);
pDC->RealizePalette();
pDC->SelectPalette(pOldPalette,FALSE);
return TRUE;
}
//实现RGB颜色空间到YCbCr空间的变换
BOOL CDib::RGB2YCbCr(double *rgb, double *yrr)
{
double& R = rgb[0];
double& G = rgb[1];
double& B = rgb[2];
double& Y = yrr[0];
double& Cb = yrr[1];
double& Cr = yrr[2];
Y = 0.299 * R + 0.587 * G + 0.114 * B;
Cb = -0.1687 * R - 0.3313 * G + 0.5 * B + 128;
Cr = 0.5 * R - 0.4187 * G - 0.0813 * B + 128;
return TRUE;
}
//实现YCbCr颜色空间到RGB空间的变换
BOOL CDib::YCbCr2RGB(double *yrr, double *rgb)
{
double& R = rgb[0];
double& G = rgb[1];
double& B = rgb[2];
double& Y = yrr[0];
double& Cb = yrr[1];
double& Cr = yrr[2];
R = Y + 1.402 * (Cr - 128);
G = Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128);
B = Y + 1.772 * (Cb - 128);
return TRUE;
}
//对一个8*8单元进行DCT变换,没有结合FFT引入快速算法,而是采用先按照列后按照行进行两次一维DCT
BOOL CDib::DCT(double *sourcedata)
{
double * data;
double destdata[64];
data = sourcedata;
double c[8] = {0.707107,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
for(int i = 0; i < 8; i ++) // i hang
{
for(int k = 0; k < 8; k ++) // k lie
{
destdata[8*i + k] = 0;
for(int j = 0; j < 8; j++)
{
destdata[8*i + k] = destdata[8*i + k] + c[k] * cos((2 * j + 1)*k*pi/16)/2*data[8*i+j];
}
}
}
for(i = 0; i < 8; i ++)// i lie
{
for(int k = 0; k < 8; k ++)//
{
data[k*8 + i] = 0;
for(int j = 0; j < 8; j++)
{
data[k*8 + i] = data[k*8 + i] + c[k] * cos((2 * j + 1)*k*pi/16)/2*destdata[j*8 + i];
}
}
}
sourcedata = data;
for (i = 0; i < 8; i ++)
{
for (int j = 0; j < 8; j ++)
{
if (i + j > 12)
{
data[i*8 + j] = 0;
}
}
}
//此处是直接计算的方法,计算量较大
/*
double * data;
data = sourcedata;
double c[8] = {0.707107,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
double F[64];
for(int u = 0; u < 8; u++)
for(int v = 0; v < 8; v++)
{
F[u*8+v] = 0;
for(int i = 0; i < 8 ; i++)
for(int j = 0; j < 8; j++)
{
F[u*8+v] = F[u*8+v] + data[8*i + j]*cos((2*i + 1)*u*pi/16)*cos((2*j+1)*v*pi/16);
}
F[u*8+v] = F[u*8+v] * c[u] * c[v]/4;
}
*/
return TRUE;
}
//进行一维DCT,没有采用快速算法。以后可以考虑采用FFT加速算法,戴玉超20051231
BOOL CDib::DCT_1D(double *data,double *temp)
{
double c[8] = {0.707107,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
for(int i = 0 ; i < 8; i++)
{
temp[i] = 0;
for(int j = 0; j < 8; j ++)
{
temp[i] = temp[i] + c[i] * cos((2*j + 1)*i*pi/16) * data[j]/2;
}
}
return TRUE;
}
//实现对于8*8单元的量化,使用JPEG推荐的标准量化表
//均匀线性量化
BOOL CDib::Quant(double *data,int * table,unsigned char * QuantTable)
{
int * quant = new int [64];
for(int i = 0; i < 64; i ++)
{
if(data[i] > 0)
{
quant[i] = int(data[i]/QuantTable[i] + 0.5);
}
else
{
quant[i] = int(data[i]/QuantTable[i] - 0.5);
}
}
for(i = 0; i < 64; i ++)
{
table[ZigzagTable[i]] = quant[i];
}
delete [] quant;
return TRUE;
}
//向文件中写入32位数据
BOOL CDib::Write32BitData(int nData)
{
unsigned char buf[4],insert=0x00;
buf[0]=(nData&0xff000000)>>24; //把32位的JPEG编码整数由高位到低位存入buf数组中
buf[1]=(nData&0xff0000)>>16; //以便由高位到低位写入JPEG文件中,因直接写入,
buf[2]=(nData&0xff00)>>8; //将由低位到高位写入文件。
buf[3]=nData&0xff;
for(int i = 0 ; i < 4 ; i ++) //EncodeJpeg的32位已满,写入文件
{
file.Write(buf+i,1);
if(buf[i] == 0xff) //如果某一字节为0xFF,则需插入一个数0x00
file.Write(&insert,1); //已定义insert为0x00
}
return TRUE;
}
//游程编码子程序
BOOL CDib::RunLengthCode(int * table,int * RLC_length)
{
// init
for(int i = 0 ; i < 64 ; i ++)
{
rlc[i].length = 0;
rlc[i].digit = 0;
}
int j = 0; //零的个数
int k = 0; //8*8的数据块的游程长度
int temp; //由temp判断EOB的位置
for( i = 1 ; i < 64 ; i ++) //去除Z型扫描之后的第一个元素,即DC的值
{
if(table[i] != 0)
temp = i; //此循环判断从哪一个元素开始以后的元素全为零
}
for(i = 1;i <= temp ; i ++)
{
if(table[i] == 0)
{
if(j < 16)
j ++;
else
{
rlc[k].length = j-1; //当连续零的个数为16时,存为(15,0)。此时j=16,因此要减去1
rlc[k].digit = 0;
j = 0;
k ++;
}
}
else
{
rlc[k].length = j;
rlc[k].digit = table[i];
j = 0;
k ++;
}
}
if(temp != 63) //当游程数为63时,不能写结束标识符EOB,即(0,0)。temp=63说明最后一位不是零,而是非零数。
{
rlc[k].length = 0;
rlc[k].digit = 0; //写RLE编码的结束标志EOB,即(0,0)
k++; //k为RLE的总个数
} //K为游程的长度
*RLC_length = k;
return TRUE;
}
//进行Huffman编码,根据码值和位值生成Huffman编码
BOOL CDib::HuffmanTable(unsigned char *HuffmanBit,unsigned char*HuffmanVal)
{
int k=0,i=1,j=1;
int lastk;
char nCodeLength[162]; // 记录Huffman编码的位数
int code=0,si,p;
int nHuffmanCode[162];
while(i<=16) //
{
while(j<=HuffmanBit[i])
{
nCodeLength[k]=i;
k=k+1;
j=j+1;
}
i=i+1;
j=1;
}
nCodeLength[k]=0;
lastk=k;
p=0;
code=0;
si=nCodeLength[0];
while(nCodeLength[p])
{
while(((int)nCodeLength[p])==si)
{
nHuffmanCode[p]=code;
p++;
code++;
}
code = code << 1;
si++;
}
for(p=0;p<lastk;p++)
{
HuffmanCode[HuffmanVal[p]]=nHuffmanCode[p]; //HuffmanCode是哈夫曼的码表
HuffmanLength[HuffmanVal[p]]=nCodeLength[p]; //HuffmanLength是哈夫曼的码表长度
}
return TRUE;
}
//向文件中写入数据
int CDib::WriteData(int nPreDCdata,int * table,int *DChufco,int *DChufsi,int *AChufco,int *AChufsi)
{
int nDCdata = table[0];
int nDiffDC = nDCdata - nPreDCdata;
int nDCHuffmanLen = 0;
int nDCHuffmanCode = 0;
int nACHuffmanLen = 0;
int nACHuffmanCode = 0;
unsigned char nPow = 0;
// DC
if(nDiffDC != 0)
{
for(int i = 0; i < 12; i ++)
{
if((pow(2,i) <= abs(nDiffDC)) && (pow(2,i + 1) > abs(nDiffDC)))
{
nPow = i + 1;
}
}
}
else
nPow = 0;
if(nDiffDC > 0)
{
nDCHuffmanCode = DChufco[nPow];
nDCHuffmanLen = DChufsi[nPow];
ShiftWrite(nDCHuffmanLen,nDCHuffmanCode);
ShiftWrite(nPow,nDiffDC);
}
else if(nDiffDC < 0)
{
int nDCBitValue = int(pow(2,nPow) - 1 - abs(nDiffDC));
nDCHuffmanLen = DChufsi[nPow];
nDCHuffmanCode = DChufco[nPow];
ShiftWrite(nDCHuffmanLen,nDCHuffmanCode);
ShiftWrite(nPow,nDCBitValue);
}
else
{
ShiftWrite(2,0);
}
// AC
int nLength = 0;
RunLengthCode(table,&nLength);
for(int i = 0 ; i < nLength; i ++)
{
int zero_num = rlc[i].length;
int nDigit = rlc[i].digit;
if(nDigit != 0)
{
for(int j = 0; j < 11; j ++)
{
if((pow(2,j+1) > abs(nDigit))&&(pow(2,j) <= abs(nDigit)))
{
nPow = j + 1;
}
}
}
else
nPow = 0;
int nACBitValue = 0;
if(nDigit > 0)
{
nACBitValue = nDigit;
}
else
{
nACBitValue = int(pow(2,nPow) - 1 - abs(nDigit));
}
unsigned char huffmanval = (zero_num<<4)|nPow; //使零的个数与该游程值所在的分组合为一个字节,以便用哈夫曼码表
nACHuffmanCode = AChufco[huffmanval]; //得到哈夫曼码表
nACHuffmanLen = AChufsi[huffmanval]; //得到哈夫曼码表的位长度
ShiftWrite(nACHuffmanLen,nACHuffmanCode);
if(nDigit==0 && zero_num==0) //当是EOI(End of Image)时,不写数字位的0,否则要写数字位的0
{
}
else
ShiftWrite(nPow,nACBitValue); //游程不为(0,0)时,写入数据
}
return nDCdata;
}
//实现对于24位图Jpeg编码的完全过程
BOOL CDib::Jpeg()
{
DWORD width = 0 , height = 0; // 记录扩展后的图像行列数
DWORD dwWidth = 0;
DWORD dwHeight = 0;
int i = 0 ,j = 0 ; //循环变量
if(Is24bit() == TRUE) //能否被16整除
{
dwWidth = GetWidth();
dwHeight = GetHeight();
if(dwWidth%16==0)
width = dwWidth;
else
width=dwWidth + 16 - dwWidth%16;
if(dwHeight%16==0)
height = dwHeight;
else
height = dwHeight + 16 - dwHeight%16;
}
else if(Is24bit() == FALSE)
{
dwWidth = GetWidth();
dwHeight = GetHeight();
if(dwWidth%8 == 0)
width = dwWidth;
else
width = dwWidth + 8 - dwWidth%8;
if(dwHeight%8 == 0)
height = dwHeight;
else
height = dwHeight + 8 - dwHeight%8;
}
int LumDChufco[12],LumDChufsi[12]; //存储亮度DC数据的哈夫曼码表和码长,四个字节
int LumAChufco[251],LumAChufsi[251]; //存储亮度AC数据的哈夫曼码表和码长,四个字节
int ChrDChufco[12],ChrDChufsi[12]; //存储色差DC数据的哈夫曼码表和码长,四个字节
int ChrAChufco[251],ChrAChufsi[251]; //存储色差DC数据的哈夫曼码表和码长,四个字节
HuffmanTable(LumDCHuffmanBit,LumDCHuffmanVal); //得到亮度DC的HUFFMAN的码表和码长
for(i=0;i<12;i++)
{
LumDChufco[i]=HuffmanCode[i]; //DChufco是亮度DC哈夫曼的码表
LumDChufsi[i]=HuffmanLength[i]; //DChufsi是亮度DC哈夫曼的码长
}
HuffmanTable(LumACHuffmanBit,LumACHuffmanVal); //得到亮度AC的HUFFMAN的码表和码长
for(i=0;i<251;i++) //实际上用不完252个数,中间一些数没有用
{
LumAChufco[i]=HuffmanCode[i]; //AChufco是亮度哈夫曼的码表
LumAChufsi[i]=HuffmanLength[i]; //AChufsi是亮度哈夫曼的码长
}
HuffmanTable(ChrDCHuffmanBit,ChrDCHuffmanVal);//得到色度DC的HUFFMAN的码表和码长
for(i=0;i<12;i++)
{
ChrDChufco[i]=HuffmanCode[i]; //DChufco是色度哈夫曼的码表
ChrDChufsi[i]=HuffmanLength[i]; //DChufsi是色度哈夫曼的码长
}
HuffmanTable(ChrACHuffmanBit,ChrACHuffmanVal);//得到色度AC的HUFFMAN的码表和码长
for(i=0;i<251;i++)
{
ChrAChufco[i]=HuffmanCode[i]; //AChufco是色度哈夫曼的码表
ChrAChufsi[i]=HuffmanLength[i]; //AChufsi是色度哈夫曼的码长
}
if(Is24bit() == TRUE)
{
char *Y = NULL;
char *Cr = NULL;
char *Cb = NULL;
DWORD dwSize = width * height;
Y = (char *)VirtualAlloc(NULL,dwSize,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
Cr = (char *)VirtualAlloc(NULL,dwSize/4,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
Cb = (char *)VirtualAlloc(NULL,dwSize/4,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
memset(Y,0,dwSize);
memset(Cr,0,dwSize/4);
memset(Cb,0,dwSize/4);
//申请空间,存储扩展以后的原始图像;
PBYTE data = (PBYTE)VirtualAlloc(NULL,dwSize*3,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
memset(data,dwSize*3,0);
//通过赋值实现图像的扩展
for(i = 0; i < (int)dwHeight; i ++)
for(j = 0; j < (int)dwWidth; j ++)
{
int nPos1 = (i * width + j) * 3;
int nPos2 = (i * dwWidth + j) * 3;
data[nPos1] = pDibData[nPos2];
data[nPos1 + 1] = pDibData[nPos2 + 1];
data[nPos1 + 2] = pDibData[nPos2 + 2];
}
for(i = 0; i < (int)dwSize; i ++)
{
Y[i] = (int)(0.114*data[3 * i] + 0.587 * data[3 * i + 1]+0.299*data[3 * i + 2] ) - 128;
}
for(i = 0; i < (int)height/2; i ++)
for(j = 0; j < (int)width/2; j ++)
{
int nPos = 2 * i * width + j * 2;
Cr[i * width/2 + j] = (int)(-0.0813 * data[3 * nPos] - 0.4187 * data[3 * nPos + 1] + 0.5 * data[3 * nPos + 2]);
Cb[i * width/2 + j] = (int)(0.5 * data[3 * nPos] - 0.3313 * data[3 * nPos + 1] - 0.1687 * data[3 * nPos + 2]);
}
double dData[64] = {0.0};
int dQuantData[64] = {0};
int nPreLumDCData = 0;
int nPreCrDCData = 0;
int nPreCbDCData = 0;
//按照411模式采样并进行DCT变换,量化,写入数据
for(int ii = 0 ; ii < (int)height/16; ii ++)
for(int jj = 0; jj < (int)width/16; jj ++)
{
// 00左上角8*8块
for(i = 0; i < 8; i ++)
for(j = 0; j < 8; j ++)
{
int nPos = (ii * 16 + i) * width + jj * 16 + j;
dData[i * 8 + j] = Y[nPos]; // 16*ii + i 16 *jj + j
}
DCT(dData);
Quant(dData,dQuantData,LumQuantTable);
nPreLumDCData = WriteData(nPreLumDCData,dQuantData,LumDChufco,LumDChufsi,LumAChufco,LumAChufsi);
// 01右上角8*8图像快
for(i = 0; i < 8; i ++)
for(j = 0; j < 8; j ++)
{
int nPos = (ii * 16 + i) * width + jj * 16 + j + 8;
dData[i * 8 + j] = Y[nPos]; // 16*ii + i 16 *jj + j
}
DCT(dData);
Quant(dData,dQuantData,LumQuantTable);
nPreLumDCData = WriteData(nPreLumDCData,dQuantData,LumDChufco,LumDChufsi,LumAChufco,LumAChufsi);
// 10 左下角图像快
for(i = 0; i < 8; i ++)
for(j = 0; j < 8; j ++)
{
int nPos = (ii * 16 + i + 8) * width + jj * 16 + j;
dData[i * 8 + j] = Y[nPos]; // 16*ii + i 16 *jj + j
}
DCT(dData);
Quant(dData,dQuantData,LumQuantTable);
nPreLumDCData = WriteData(nPreLumDCData,dQuantData,LumDChufco,LumDChufsi,LumAChufco,LumAChufsi);
//11 右下角图像快
for(i = 0; i < 8; i ++)
for(j = 0; j < 8; j ++)
{
int nPos = (ii * 16 + i + 8) * width + jj * 16 + j + 8;
dData[i * 8 + j] = Y[nPos]; // 16*ii + i 16 *jj + j
}
DCT(dData);
Quant(dData,dQuantData,LumQuantTable);
nPreLumDCData = WriteData(nPreLumDCData,dQuantData,LumDChufco,LumDChufsi,LumAChufco,LumAChufsi);
// Cb数据
for(i = 0; i < 8; i ++)
for(j = 0; j < 8; j++)
{
int nPos = (ii * 8 + i) * width/2+ jj * 8 + j;
dData[i * 8 + j] = Cb[nPos];
}
DCT(dData);
Quant(dData,dQuantData,ChrQuantTable);
nPreCbDCData = WriteData(nPreCbDCData,dQuantData,ChrDChufco,ChrDChufsi,ChrAChufco,ChrAChufsi);
// Cr数据
for(i = 0; i < 8; i ++)
for(j = 0; j < 8; j++)
{
int nPos = (ii * 8 + i) * width/2 + jj * 8 + j;
dData[i * 8 + j] = Cr[nPos];
}
DCT(dData);
Quant(dData,dQuantData,ChrQuantTable);
nPreCrDCData = WriteData(nPreCrDCData,dQuantData,ChrDChufco,ChrDChufsi,ChrAChufco,ChrAChufsi);
}
VirtualFree(data,dwSize*3,MEM_DECOMMIT|MEM_RESERVE);
VirtualFree(Y,dwSize,MEM_DECOMMIT|MEM_RESERVE);
VirtualFree(Cr,dwSize/4,MEM_DECOMMIT|MEM_RESERVE);
VirtualFree(Cb,dwSize/4,MEM_DECOMMIT|MEM_RESERVE);
}
else if(Is24bit() == FALSE) //灰度图像
{
DWORD dwSize = width * height;
//申请空间,存储扩展以后的原始图像;
PBYTE data = (PBYTE)VirtualAlloc(NULL,dwSize,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
memset(data,dwSize,0);
//通过赋值实现图像的扩展
for(i = 0; i < (int)dwHeight; i ++)
for(j = 0; j < (int)dwWidth; j ++)
{
int nPos1 = i * width + j;
int nPos2 = i * dwWidth + j;
data[nPos1] = pDibData[nPos2];
}
double dData[64] = {0.0};
int LumPreDC = 0;
int dQuantData[64] = {0};
for(int ii = 0;ii< (int)height/8 ;ii ++)
{
for(int jj = 0;jj < (int)width/8 ;jj ++)
{
for(i = 0; i < 8; i ++)
{
for(j = 0 ; j < 8 ; j ++)
dData[ i * 8 + j] = data[(ii * 8 + i) * width + (jj + j)];
}
DCT(dData);
Quant(dData,dQuantData,LumQuantTable);
LumPreDC = WriteData(LumPreDC,dQuantData,LumDChufco,LumDChufsi,LumAChufco,LumAChufsi);
}
}
VirtualFree(data,dwSize,MEM_DECOMMIT|MEM_RESERVE);
}
return TRUE;
}
//以指定路径名存放Jpeg压缩结果,实现按照Jpeg格式存储数据
BOOL CDib::SaveAsJpeg(LPCTSTR lpszFileName)
{
if(!file.Open(lpszFileName,CFile::modeCreate|CFile::modeWrite))
return(FALSE);
unsigned short int filehead = 0xd8ff; //JPEG标识符FFD8(16bit)
//App0共18个字节,包括长度,标示符,版本号,密度单位,像素密度,像素数目,缩略图位图
unsigned char app0[18]={0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x00,0x00,0x01,0x00,0x01,0x00,0x00};
//量化表0的头,
unsigned char quant0[5]={0xFF,0xDB,0x00,0x43,0x00}; //量化表标记头的前几个字节,后接具体的量化表。
//量化表1的头
unsigned char quant1[5]={0xFF,0xDB,0x00,0x43,0x01};
unsigned char GreyFrame[13]={0xff,0xc0,0x00,0x0b,0x08,0,0,0,0,0x01,0x01,0x11,0x00};
unsigned char ColorFrame[19]={0xff,0xc0,0x00,0x11,0x08,0,0,0,0,0x03,0x01,0x22,0x00,
0x02,0x11,0x01,0x03,0x11,0x01 }; //真彩色帧开始的标识符
//亮度DC哈夫曼
unsigned char LumDchuffman[5]={0xff,0xc4,0x00,0x1f,0x00};
//亮度AC哈夫曼
unsigned char LumAchuffman[5]={0xff,0xc4,0x00,0xb5,0x10};
//色差DC哈夫曼
unsigned char ChrDchuffman[5]={0xff,0xc4,0x00,0x1f,0x01};
//色差AC哈夫曼
unsigned char ChrAchuffman[5]={0xff,0xc4,0x00,0xb5,0x11};
unsigned char GreySos[10]={0xff,0xda,0x00,0x08,0x01,0x01,0x00,0x00,0x3f,0x00};//扫描开始(SOS)标记符
unsigned char ColorSos[14]={0xff,0xda,0x00,0x0c,0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00};//扫描开始(SOS)标记符
unsigned char eoi[2]={0xff,0xd9}; //文件结束标识符End of Image
//写入图像的高度和宽度,采用两个字节,故分两次写入
GreyFrame[5] = ColorFrame[5] = GetHeight()/256;
GreyFrame[6] = ColorFrame[6] = GetHeight()%256; //图像的高度
GreyFrame[7] = ColorFrame[7] = GetWidth()/256;
GreyFrame[8] = ColorFrame[8] = GetWidth()%256; //图像的宽度
file.Write(&filehead,sizeof(unsigned short int)); //写JPEG标识符FF D8
file.Write(app0,18); //写app0
file.Write(quant0,5); //写量化表标记头前5个字节
file.Write(LumQuantTable,64); //写量化表的64个字节
if(m_bit24==1)
{
file.Write(quant1,5);
file.Write(ChrQuantTable,64);
file.Write(ColorFrame,19); //写帧开始(SOF)的标识符
}
else if(m_bit24==0)
{
file.Write(GreyFrame,13);
}
file.Write(LumDchuffman,5);
file.Write(LumDCHuffmanBit+1,16);/*从DCHuffmanBit数组的第二位开始写*/
file.Write(LumDCHuffmanVal,12);
file.Write(LumAchuffman,5);
file.Write(LumACHuffmanBit+1,16);/*从ACHuffmanBit数组的第二位开始写*/
file.Write(LumACHuffmanVal,162);
if(Is24bit() == TRUE)
{
file.Write(ChrDchuffman,5);
file.Write(ChrDCHuffmanBit+1,16);/*从DCHuffmanBit数组的第二位开始写*/
file.Write(ChrDCHuffmanVal,12);
file.Write(ChrAchuffman,5);
file.Write(ChrACHuffmanBit+1,16);/*从ACHuffmanBit数组的第二位开始写*/
file.Write(ChrACHuffmanVal,162);
}
if(Is24bit() == TRUE)
{
file.Write(ColorSos,14); //写真彩色图像SOS标记符
}
else if(Is24bit() == FALSE)
{
file.Write(GreySos,10); //写灰度图像SOS标记符
}
Jpeg(); //开始向JPEG文件中写入数据
WriteSurplus(); //写结尾部分
file.Write(eoi,2); //写JPEG文件结束标识符
file.Close();
return TRUE;
}
//写最后数据缓冲区中已移到高位的码串,使其凑成整字节,不足字节位时后加1
BOOL CDib::WriteSurplus()
{
unsigned char spare[4];
EncodeJpeg = EncodeJpeg|((int)pow(2,surplus)-1); //使数据缓冲区中有效位以外的所有位为1
if(surplus<32&&surplus>=24) //剩余位大于等于24,只写一个字节到JPEG文件
{
spare[0] = (EncodeJpeg&0xff000000)>>24;
file.Write(spare,1);
}
else if(surplus<24&&surplus>=16) //剩余位大于等于16而小于24,写2个字节
{
spare[0]=(EncodeJpeg&0xff000000)>>24;
spare[1]=(EncodeJpeg&0xff0000)>>16;
file.Write(spare,2);
}
else if(surplus<16&&surplus>=8) //剩余位大于等于8而小于16,写3个字节
{
spare[0]=(EncodeJpeg&0xff000000)>>24;
spare[1]=(EncodeJpeg&0xff0000)>>16;
spare[2]=(EncodeJpeg&0xff00)>>8;
file.Write(spare,3);
}
else //剩余位小于8,写4个字节
{
spare[0]=(EncodeJpeg&0xff000000)>>24;
spare[1]=(EncodeJpeg&0xff0000)>>16;
spare[2]=(EncodeJpeg&0xff00)>>8;
spare[3]=EncodeJpeg&0xff;
file.Write(spare,4);
}
EncodeJpeg=surplus=0;
return TRUE;
}
//将Huffman编码完成的数据写入,包括编码位数和码字
void CDib::ShiftWrite(int huffmanbitnum,int huffmancode)
{
int i,j,k;
if(huffmanbitnum<surplus) //需要编码的码串比数据缓冲区剩余的位少,可直接移入,不写入JPEG文件
{
EncodeJpeg=EncodeJpeg|(huffmancode<<(surplus-huffmanbitnum));//需要编码的码串移入数据缓冲区
surplus=surplus - huffmanbitnum; //当前数据缓冲区剩余的位数
}
else//需要编码的码串比数据缓冲区剩余的位多,截断码串分两次移入数据缓冲区,使EncodeJpeg=32之后写入文件
{
i = huffmanbitnum - surplus; //截断码串后第二次移入数据缓冲区的位长
j = (int)pow(2,surplus) - 1; //得到从低位到高位surplus个1,即(000111...11)
EncodeJpeg = EncodeJpeg|((huffmancode&(j<<i))>>i);
Write32BitData(EncodeJpeg); //32位数据缓冲区填满后写入JPEG文件
EncodeJpeg = 0; //数据缓冲区清零
k = (int)pow(2,i)-1; //
EncodeJpeg = (huffmancode&k)<<(32-i); //使被截断后剩余的码串移到32位数据缓冲区的高位
surplus = 32-i; //目前数据缓冲区剩余的位数
}
}
以上即为主要的代码。