📄 jpegdoc.cpp
字号:
time( &tBegin );
if(!file.Open(pszFilename,CFile::modeCreate|CFile::modeWrite))
return(FALSE);
unsigned short int filehead=0xd8ff; //JPEG标识符FFD8(16bit)
unsigned char app0[18]={0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x00,0x00,0x01,0x00,0x01,0x00,0x00};
unsigned char quant0[5]={0xFF,0xDB,0x00,0x43,0x00}; //量化表标记头的前几个字节,后接具体的量化表。
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 }; //真彩色帧开始的标识符
unsigned char LumDchuffman[5]={0xff,0xc4,0x00,0x1f,0x00};
unsigned char LumAchuffman[5]={0xff,0xc4,0x00,0xb5,0x10};
unsigned char ChrDchuffman[5]={0xff,0xc4,0x00,0x1f,0x01};
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]=height/256;
GreyFrame[6]=ColorFrame[6]=height%256; //图像的高度
GreyFrame[7]=ColorFrame[7]=width/256;
GreyFrame[8]=ColorFrame[8]=width%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(m_bit24==1)
{
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(m_bit24==1)
{
file.Write(ColorSos,14); //写真彩色图像SOS标记符
}
else if(m_bit24==0)
{
file.Write(GreySos,10); //写灰度图像SOS标记符
}
WriteHuffmanData(); //开始向JPEG文件中写入数据
WriteSurplus();
file.Write(eoi,2); //写JPEG文件结束标识符
file.Close();
time( &tEnd );
double nCostTime = difftime( tEnd, tBegin );
CString strCostTime;
strCostTime.Format("JPEG编码完成!\n共花%.0lf秒", nCostTime);
AfxMessageBox(strCostTime);
WaitCursorEnd(); //结束等待光标
return(TRUE);
}
//////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////
BOOL CDib::Draw(CDC *pDC,int nX,int nY)
{
// 如果没有数据,不能显示
if(m_pDib==NULL)
return(FALSE);
// 得用StretchDIBits绘制位图
/* 函数原型为:
int StretchDIBits
( HDC hdc, //设备上下文的句柄
int XDest,int YDest, //目标矩形的左上角X和Y坐标值
int nDestWidth,int nDestHeight, //目标矩形的宽度和高度
int XSrc,int YSrc, //源矩形的左上角X和Y坐标值
int nSrcWidth,int nSrcHeight, //源矩形的宽度和高度
CONST VOID *lpBits, //address of bitmap bits
CONST BITMAPINFO *lpBitsInfo, //指向BITMAPINFO结构的指针
UNIT iUsage, //使用标志,用于指明颜色是采用RGB还是调色板的索引值
DWORD dwRop); //指定图像操作代码
最后一个参数dwRop有以下一些常用的代码:
BLACKNESS 输出黑色
DSTINVERT 反转目的位图
MERGECOPY 用与操作把图案(Pattern)与源位图融合起来
MERGEPAINT 用或操作把反转的源位图与目的位图融合起来
NOTSRCCOPY 把源位图反转然后拷贝目的地
NOTSRCERASE 用或操作融合源和目的位图,然后再反转
PATCOPY 把图案拷贝到目的位图中
PATINVERT 用异或操作把图案与目的位图相融合
PATPAINT 用或操作融合图案和反转的源位图,然后用或操作把结果与目的位图融合
SRCAND 用与操作融合源位图和目的位图
SRCCOPY 把源位图拷贝到目的位图
SRCERASE 先反转目的位图,再用与操作将其源位图融合
SRCINVERT 用异或操作融合源位图和目的位图
SRCPAINT 用或操作融合源位图和目的位图
WHITENESS 输出白色
*/
StretchDIBits(pDC->m_hDC,nX,nY,m_pBIH->biWidth,m_pBIH->biHeight,0,0,
m_pBIH->biWidth,m_pBIH->biHeight,m_pDibBits,
(BITMAPINFO *)m_pBIH,BI_RGB,SRCCOPY);
return(TRUE);
}
///////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////
BOOL CDib::SetPalette(CDC *pDC)
{
// 如果没有数据,不能设置调色板
if(m_pDib==NULL)
return(FALSE);
// 检查是不有调色板句柄,大于8位的位图其句柄为NULL
if(m_Palette.GetSafeHandle()==NULL)
return(TRUE);
// 选择并实现调色板
CPalette *pOldPalette;
pOldPalette=pDC->SelectPalette(&m_Palette,FALSE);
pDC->RealizePalette();
pDC->SelectPalette(pOldPalette,FALSE);
return(TRUE);
}
void CDib::Show(CDC *pDC,int nX,int nY)
{
/*SetDIBitsToDevice的函数据原型
int SetDIBitsToDevice(
HDC hdc, //设备上下文的句柄
int XDest,int YDest, //目标矩形的左上角X和Y坐标
DWORD dwWidth,DWORD dwHeight, //源矩形的宽度和高度
int XSrc,int YSrc, //源矩形的左下角的X和Y坐标
UINT uStartScan, //矩阵中的第一个扫描线
UINT cScanLines, //扫描线的数目
CONST VOID *lpvBits, //指向DIB颜色数据的指针
CONST BITMAPINFO *lpbmi, //指向BITMAPINFO结构的指针
UINT fuColorUse); //用于指明颜色是用用RGB还是调色板的索引值
*/
// CWnd test;
// CRect cr;
// test.GetClientRect(cr);
// int xx=cr.right/2;//,yy=cr.bottom/2;
for(LONG y=0;y<height;y++)
SetDIBitsToDevice(pDC->GetSafeHdc(),nX,nY+y,width,
1,0,0,0,1,data[y],(BITMAPINFO *)m_pBIH,DIB_RGB_COLORS);
}
/*************************************************************************
* 函数名称: Pre_DCT()
* 参数:
* double * sourcedata - 指向时域值的指针
* 返回值: 无
* 说明: 该函数用来实现8*8快速离散余弦变换。
************************************************************************/
void CDib::Pre_DCT(double * sourcedata)
{
double const C1=0.9808;
double const C2=0.9239;
double const C3=0.8315;
double const C4=0.7071;
double const C5=0.5556;
double const C6=0.3827;
double const C7=0.1951;
double S18,S27,S36,S45,S1845,S2736;
double D18,D27,D36,D45,D1845,D2736;
double * data;
int i;
/* for(i=0;i<64;i++)
{
sourcedata[i]=sourcedata[i]-128;//图象数据减去128
}
*/
// 后面有专门的地方使亮度值减去128
// 行DCT变换
data=sourcedata;
for(i=0;i<8;i++)
{
S18=data[0]+data[7];
S27=data[1]+data[6];
S36=data[2]+data[5];
S45=data[3]+data[4];
S1845=S18+S45;
S2736=S27+S36;
D18=data[0]-data[7];
D27=data[1]-data[6];
D36=data[2]-data[5];
D45=data[3]-data[4];
D1845=S18-S45;
D2736=S27-S36;
data[0]=0.5*(C4*(S1845+S2736));
data[1]=0.5*(C1*D18+C3*D27+C5*D36+C7*D45);
data[2]=0.5*(C2*D1845+C6*D2736);
data[3]=0.5*(C3*D18-C7*D27-C1*D36-C5*D45);
data[4]=0.5*(C4*(S1845-S2736));
data[5]=0.5*(C5*D18-C1*D27+C7*D36+C3*D45);
data[6]=0.5*(C6*D1845-C2*D2736);
data[7]=0.5*(C7*D18-C5*D27+C3*D36-C1*D45);
data+=8;
}
/////////////////////////////////////////////////////
// 列DCT变换
data=sourcedata;
for(i=0;i<8;i++)
{
S18=data[0*8]+data[7*8];
S27=data[1*8]+data[6*8];
S36=data[2*8]+data[5*8];
S45=data[3*8]+data[4*8];
S1845=S18+S45;
S2736=S27+S36;
D18=data[0*8]-data[7*8];
D27=data[1*8]-data[6*8];
D36=data[2*8]-data[5*8];
D45=data[3*8]-data[4*8];
D1845=S18-S45;
D2736=S27-S36;
data[0*8]=0.5*(C4*(S1845+S2736));
data[1*8]=0.5*(C1*D18+C3*D27+C5*D36+C7*D45);
data[2*8]=0.5*(C2*D1845+C6*D2736);
data[3*8]=0.5*(C3*D18-C7*D27-C1*D36-C5*D45);
data[4*8]=0.5*(C4*(S1845-S2736));
data[5*8]=0.5*(C5*D18-C1*D27+C7*D36+C3*D45);
data[6*8]=0.5*(C6*D1845-C2*D2736);
data[7*8]=0.5*(C7*D18-C5*D27+C3*D36-C1*D45);
data++;
}
}
///////////////////////////////////////////////////////
//
// 对8*8的图像块进行DCT变换,量化之后进行Z型扫描
//
///////////////////////////////////////////////////////
BOOL CDib::DCT(double * x,int n,unsigned char * QuantTable)
{
double * Light=new double[64];
int i;
for(i=0;i<n;i++)
Light[i]=x[i];
Pre_DCT(Light); //二维DCT变换
// 对DCT系数进行量化,此处可调整量化矩阵,以调整JPEG文件的压缩比
int quant_val[64];
for(i=0;i<64;i++)
{
if(Light[i]>=0)
quant_val[i]=(int)(Light[i]/QuantTable[i]+0.5);
else
quant_val[i]=(int)(Light[i]/QuantTable[i]-0.5);
}
// 对量化矩阵进行Z型扫描
for(i=0;i<64;i++)
table[zig[i]]=quant_val[i];
delete Light;
////////////////////////////////
return TRUE;
}
//////////////////////////////////////////////////////
//
// 游程编码子程序
//
//////////////////////////////////////////////////////
int CDib::RLEProg()
{
// C_RLE RLE[64];
///////初始化RLE编码数组///////////
for(int i=0;i<64;i++)
{
RLE[i].zero_num=0;
RLE[i].digit=0;
}
///////开始RLE编码////////////////
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
{
RLE[k].zero_num=j-1; //当连续零的个数为16时,存为(15,0)。此时j=16,因此要减去1
RLE[k].digit=0;
j=0;
k++;
}
}
else
{
RLE[k].zero_num=j;
RLE[k].digit=table[i];
j=0;
k++;
}
}
if(temp!=63) //当游程数为63时,不能写结束标识符EOB,即(0,0)。temp=63说明最后一位不是零,而是非零数。
{
RLE[k].zero_num=0;
RLE[k].digit=0; //写RLE编码的结束标志EOB,即(0,0)
k++; //k为RLE的总个数
}
return k; //K为游程的长度
}
////////////////////////////////////////////////////////////
// 此函数可得到哈夫曼码表和码长
// val为JPEG文件中存储的哈夫曼码表
// bit为JPEG文件中存储的哈夫曼码表长度
// 由这两个参数可得到实际使用的不等长的哈夫曼码表
// 此处使用的是固定的哈夫曼码
////////////////////////////////////////////////////////////
// 此处代码是参考的书上的程序流程图
BOOL CDib::HuffmanTable(unsigned char *bit,unsigned char*val)
{
int k=0,i=1,j=1;
int lastk;
char huffsize[162];
/////////////////////////////
int code=0,si,p;
int huffcode[162];
/////////////////////////////
// int ehufco[251],ehufsi[251];
while(i<=16) //
{
while(j<=bit[i])
{
huffsize[k]=i;
k=k+1;
j=j+1;
}
i=i+1;
j=1;
}
huffsize[k]=0;
lastk=k;
p=0;
code=0;
si=huffsize[0];
while(huffsize[p])
{
while(((int)huffsize[p])==si)
{
huffcode[p]=code;
p++;
code++;
}
code<<=1;
si++;
}
for(p=0;p<lastk;p++)
{
ehufco[val[p]]=huffcode[p]; //ehufco是哈夫曼的码表
ehufsi[val[p]]=huffsize[p]; //ehufsi是哈夫曼的码表长度
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////
//
//此函数功能是把一幅图像经DCT,量化之后的数据由哈夫曼编码后依次存入文件
//
//////////////////////////////////////////////////////////////////////
BOOL CDib::WriteHuffmanData()
{
LONG r_width,r_height;
int i,j;
if(m_bit24==1) //能否被16整除
{
if(width%16==0)
r_width=width;
else
r_width=width+16-width%16;
if(height%16==0)
r_height=height;
else
r_height=height+16-height%16;
}
else if(m_bit24==0)
{
if(width%8==0)
r_width=width;
else
r_width=width+8-width%8;
if(height%8==0)
r_height=height;
else
r_height=height+8-height%8;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -