您的位置:首页 > 其它

将24位真彩色位图保存为256色(8位)位图

2009-09-15 20:08 603 查看
24位真彩色位图如果想保存位256色,即8位位图,关键是构造合适的调色板,同时考虑程序的运行效率。如果采用Windows自带的调色版来创建,则最后生成的图片失真太大,甚至惨不忍睹。下面采用一个更加优化的方式,使得生成的256色位图基本和24位的原图一致,函数代码如下:
void Save256Bmp(void)
{
     // TODO: 在此添加实现代码
     //::MessageBox(NULL, _T("test"), _T("Note"), MB_OK);
 
     //准备拷贝桌面图像
     HDC hDeskTopDC = ::GetDC(NULL);         //取得桌面DC
     HDC hMemDC = ::CreateCompatibleDC(hDeskTopDC);          //创建兼容DC
     int iScreenX = ::GetSystemMetrics(SM_CXSCREEN);
     int iScreenY = ::GetSystemMetrics(SM_CYSCREEN);
     HBITMAP hMemBmp = ::CreateCompatibleBitmap(hDeskTopDC, iScreenX, iScreenY);        //创建兼容位图
     HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hMemBmp);
 
     //拷贝图像
     ::BitBlt(hMemDC, 0, 0, iScreenX, iScreenY, hDeskTopDC, 0, 0, SRCCOPY);
 
     SaveDCToBmp(hMemDC, hMemBmp, _T("C:/temp.bmp"));
 
     //设置位图信息头结构
     BITMAPINFOHEADER tInfoHeader;
     tInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
     tInfoHeader.biWidth = iScreenX;
     tInfoHeader.biHeight = iScreenY;
     tInfoHeader.biPlanes = 1;
     tInfoHeader.biBitCount = 24;  // 256色位图是位,即一个颜色点占个字节
     tInfoHeader.biCompression = BI_RGB;
     tInfoHeader.biSizeImage = 0;
     tInfoHeader.biXPelsPerMeter = 0;
     tInfoHeader.biYPelsPerMeter = 0;
     tInfoHeader.biClrUsed = 0;
     tInfoHeader.biClrImportant = 0;
 
     DWORD   SrcBufSize; // 源图缓冲区大小
     DWORD OffBits;
     DWORD DstBufSize;
     LPBITMAPINFOHEADER   lpImgData;  
 
     HLOCAL   hTempImgData;  
     LPBITMAPINFOHEADER   lpTempImgData;  
      
     HDC       hDc;  
     HFILE   hf;  
     BITMAPFILEHEADER   DstBf;  
     BITMAPINFOHEADER   DstBi;  
     LOGPALETTE   *pPal;  
     HPALETTE       hPrevPalette;    
     HLOCAL   hPal;  
     WORD       i,j;  
     int   Red,Green,Blue,ClrIndex;  
     DWORD   ColorHits[4096];  
     WORD   ColorIndex[4096];  
 
     int DstLineBytes = 0;
     if (tInfoHeader.biWidth % 4 == 0)
     {
         DstLineBytes = tInfoHeader.biWidth * 3;
     }
     else
     {
         DstLineBytes = (tInfoHeader.biWidth + 4 - tInfoHeader.biWidth % 4 ) * 3; //计算大于图像宽度的最小的的倍速
     }
     //DstLineBytes = (DWORD)WIDTHBYTES(tInfoHeader.biWidth * 8);  // 计算图像每行象素所占的字节数目,设置成的整数倍,参数是一行像素的位数总和
     DstBufSize = (DWORD)DstLineBytes * tInfoHeader.biHeight;  //图像数据的字节大小  
 
     //分配内存
     BYTE* pBuffer = new BYTE[DstBufSize];
     if (pBuffer == NULL)  
     {  
         MessageBox(NULL,_T("Error   alloc   memory!"), _T("Error   Message"),MB_OK| MB_ICONEXCLAMATION);  
         return   FALSE;  
     }  
 
     LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pBuffer;
     *lpbi = tInfoHeader;
     int bRet = ::GetDIBits(hMemDC, hMemBmp, 0, tInfoHeader.biHeight, pBuffer,(LPBITMAPINFO)&tInfoHeader, DIB_RGB_COLORS);
 
     //ColorHits为记录颜色使用频率的数组,ColorIndex为记录颜色索引值的数组 
     memset(ColorHits, 0, 4096 * sizeof(DWORD));  
     memset(ColorIndex, 0, 4096 * sizeof(WORD));  
     BYTE *       lpPtr;
     for(int y=0; y < tInfoHeader.biHeight; y++)  
     {  
         lpPtr = pBuffer + (DstBufSize - DstLineBytes - y * DstLineBytes);  
         for(int x = 0; x < tInfoHeader.biWidth; x++)  
         {  
              //R,G,B各取位 
              Blue=(int)(*(lpPtr++) & 0xf0);  
              Green=(int)(*(lpPtr++) & 0xf0);  
              Red=(int)(*(lpPtr++) & 0xf0);            
              ClrIndex=(Blue<<4)   +   Green   +(Red   >>4);  //拼成一个位整数            
              ColorHits[ClrIndex]++;      //相应的数组元素加
         }
     }
 
     //将为零的元素清除出去
     DWORD PalCounts=0;   
     for   (ClrIndex = 0;   ClrIndex < 4096; ClrIndex++)  
     {  
         if(ColorHits[ClrIndex] != 0)  
         {  
              ColorHits[PalCounts] = ColorHits[ClrIndex];   // 比如白色的颜色数量是个,付给使用到的颜色数组
              ColorIndex[PalCounts]=ClrIndex;      //注意调整相应的索引值 
              PalCounts++;   //颜色数加 
         }  
     } 
 
     //用起泡排序将PalCounts种颜色按从大到小的顺序排列 
     DWORD dwTmp;
     for (i = 0;   i < PalCounts-1; i++) 
     {
         for (j = i + 1; j < PalCounts; j++)  
         {   
              if (ColorHits[j] > ColorHits[i])   // 把大的值排到前面
              {  
                   dwTmp = ColorHits[i];  
                   ColorHits[i] = ColorHits[j];  
                   ColorHits[j] = dwTmp;      
                   //注意调整相应的索引值 
                   dwTmp = ColorIndex[i];  
                   ColorIndex[i] = ColorIndex[j];  
                   ColorIndex[j] = (WORD)dwTmp;  
              }  
         } 
     }
 
     //为新的调色板分配内存 
     RGBQUAD tRGBQTmp[256];   // 临时的颜色表信息,后面要用到
     for   (i   =   0;   i   <   256;   i++)    
     {  
         //由位索引值得到R,G,B的最高位值 
         tRGBQTmp[i].rgbRed=(BYTE)((ColorIndex[i]   &   0x00f)   <<   4);  
         tRGBQTmp[i].rgbGreen=(BYTE)((ColorIndex[i]   &   0x0f0));  
         tRGBQTmp[i].rgbBlue=(BYTE)((ColorIndex[i]   &   0xf00)   >>   4);  
         tRGBQTmp[i].rgbReserved =(BYTE)0;
        
          
         ColorHits[i] = i;   //ColorHits作为颜色记数的作用已经完成了,下面的作用是记录位索引值对应的调色板中的索引值
     }
 
     //其余的颜色依据最小平方误差近似为前中最接近的一种 
     long   ColorError1,ColorError2;
     if (PalCounts > 256)  
     {  
         for (i = 256; i < PalCounts; i++)  
         {  
              //ColorError1记录最小平方误差,一开始赋一个很大的值 
              ColorError1=1000000000;  
              //由位索引值得到R,G,B的最高位值 
              Blue = (long)((ColorIndex[i] & 0xf00) >> 4);  
              Green = (long)((ColorIndex[i] & 0x0f0));  
              Red = (long)((ColorIndex[i] & 0x00f) << 4);  
              ClrIndex   =   0;  
              for   (j   =   0;   j   <   256;   j++)  
              {  
                   //ColorError2计算当前的平方误差 
                   ColorError2=(long)(Blue - tRGBQTmp[j].rgbBlue)*  
                       (Blue - tRGBQTmp[j].rgbBlue)+   (long)(Green - tRGBQTmp[j].rgbGreen)*  
                       (Green - tRGBQTmp[j].rgbGreen)+  
                       (long)(Red - tRGBQTmp[j].rgbRed)*  
                       (Red - tRGBQTmp[j].rgbRed);    
                   if   (ColorError2   <   ColorError1)  
                   {   //找到更小的了 
                       ColorError1   =   ColorError2;  
                       ClrIndex   =   j;   //记录对应的调色板的索引值 
                   }  
              }  
              //ColorHits记录位索引值对应的调色板中的索引值 
              ColorHits[i]   =   ClrIndex;  
         }
     }
 
     // 把颜色数据替换成颜色表里的索引
     BYTE * pRetIndex = new BYTE[tInfoHeader.biWidth * tInfoHeader.biHeight];
     if (pRetIndex == NULL)
     {
         return FALSE;
     }
     int iOffset = 0;
     for(int y = 0; y < tInfoHeader.biHeight;y++) 
     {  
         lpPtr = pBuffer +  y * DstLineBytes;    
         for(int x = 0; x < tInfoHeader.biWidth; x++)  
         {  
              //R,G,B各取位 
              Blue=(int)(*(lpPtr++)   &   0xf0);  
              Green=(int)(*(lpPtr++)   &   0xf0);  
              Red=(int)(*(lpPtr++)   &   0xf0);  
              //拼成一个位整数 
              ClrIndex=(Blue<<4)   +   Green   +(Red   >>4);  
              for (i = 0; i < PalCounts; i++)
              {
                   if (ClrIndex == ColorIndex[i])  
                   {  
                       //根据索引值取得对应的调色板中的索引值 
                       pRetIndex[iOffset++] = (unsigned   char)ColorHits[i];  
                       break;  
                   }
              }
         }  
     }  
    
     //产生新的位图   
//   hf=_lcreat("c://256.bmp",0); 
     FILE * pFile = _tfopen(_T("c://256.bmp"), _T("w+b"));
     if (pFile == NULL)
     {
         return FALSE;
     }
 
     BITMAPFILEHEADER    tFileHead;  //文件头
     BITMAPINFO Info;
     Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
     Info.bmiHeader.biWidth = iScreenX;
     Info.bmiHeader.biHeight = iScreenY;
     Info.bmiHeader.biPlanes = 1;
     Info.bmiHeader.biBitCount = 8;  // 256色位图是位,即一个颜色点占个字节
     Info.bmiHeader.biCompression = BI_RGB;
     Info.bmiHeader.biSizeImage = 0;
     Info.bmiHeader.biXPelsPerMeter = 0;
     Info.bmiHeader.biYPelsPerMeter = 0;
     Info.bmiHeader.biClrUsed = 0;
     Info.bmiHeader.biClrImportant = 0;
 
     tFileHead.bfType = (WORD)('M'<<8)|'B';    //位图文件头位“BM”
     tFileHead.bfSize = sizeof(BITMAPFILEHEADER) + DstBufSize;  // BMP文件的字节大小
     tFileHead.bfReserved1 = 0;
     tFileHead.bfReserved2 = 0;
     tFileHead.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
 
     fwrite((LPSTR)&tFileHead,sizeof(BITMAPFILEHEADER), 1, pFile);   // 写文件头   
     fwrite((LPSTR)&Info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
     fwrite(&tRGBQTmp, sizeof(RGBQUAD) * 256, 1, pFile);
     fwrite((LPSTR)pRetIndex, tInfoHeader.biWidth * tInfoHeader.biHeight, 1, pFile);  // 写索引数据
    
     fclose(pFile);  
 
     //释放内存和资源 
     delete []pBuffer;
     delete []pRetIndex;
 
     SelectObject(hMemDC, hOldBmp);
     ::DeleteObject(hMemBmp);
     ::DeleteDC(hMemDC);
 
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息