Jpeg压缩实现

之前说过需要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;      //目前数据缓冲区剩余的位数
 }
}

以上即为主要的代码。


 

你可能感兴趣的:(null,table,delete,存储,insert,扩展)