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

部署运行你感兴趣的模型镜像

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++)  

         {  

              //RGB各取位 

              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++)    

     {  

         //由位索引值得到RGB的最高位值 

         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;  

              //由位索引值得到RGB的最高位值 

              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++)  

         {  

              //RGB各取位 

              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);

 

}

您可能感兴趣的与本文相关的镜像

Qwen-Image-Edit-2509

Qwen-Image-Edit-2509

图片编辑
Qwen

Qwen-Image-Edit-2509 是阿里巴巴通义千问团队于2025年9月发布的最新图像编辑AI模型,主要支持多图编辑,包括“人物+人物”、“人物+商品”等组合玩法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值