图解 bmp 文件(BITMAP,windows位图文件)格式

本文详细介绍了解析BMP图像文件的方法,包括不同位深下的颜色数据处理流程,并提供了具体的代码示例。涵盖了从单色到增强真彩色的各种情况,以及如何在Windows环境下高效地绘制BMP图像。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

既然是图解,此处省去介绍....直接上图

以下代码中SetPixel()为在屏幕中打点的函数,在单片机中由自己实现,

根据不同的位深来解析,都是根据以上图片方式,推导出来的算法.

并没有实现压缩算法,图像高度为负的情况,并且未优化,图像较大时速度慢

(pmybmp指向bmp文件的数组)

 

//windows下加入缓冲提高速度

HDC hdc=GetDC(hWnd);//second method
HDC hmdc = CreateCompatibleDC(hdc);
HBITMAP hbmp = CreateCompatibleBitmap(hdc, bminfohdr.biWidth, bminfohdr.biHeight);
SelectObject(hmdc, hbmp);

 

 

 

 

 

        char *pmybmp = (char*)BMP_WAVE;
        BITMAPFILEHEADER bmfilehdr;
        BITMAPINFOHEADER bminfohdr; 

        memcpy(&bmfilehdr, &pmybmp[0], sizeof(BITMAPFILEHEADER));//14
        memcpy(&bminfohdr, &pmybmp[14], sizeof(BITMAPINFOHEADER));//40
        printf("\n..............................\n");
        printf("\nsizeof(BITMAPFILEHEADER)=%d\n", sizeof(BITMAPFILEHEADER));
        printf("bmfilehdr.bfType=%#02x\n", bmfilehdr.bfType);
        printf("bmfilehdr.bfSize=%#04x,bmp文件大小:%d字节\n", bmfilehdr.bfSize, bmfilehdr.bfSize);
        printf("bmfilehdr.bfOffBits=%#04x (到RGBQUAD偏移字节)\n", bmfilehdr.bfOffBits);
        printf("sizeof(BITMAPINFOHEADER)=%d\n", sizeof(BITMAPINFOHEADER));
        printf("bminfohdr.biSize=%#04x  ( 结构体BITMAPINFOHEADER大小,=sizeof(BITMAPINFOHEADER) )\n", bminfohdr.biSize);
        printf("bminfohdr.biWidth=%#04x  (位图宽:%dpixels)\n", bminfohdr.biWidth, bminfohdr.biWidth);
        printf("bminfohdr.biHeight=%#04x  (位图高:%dpixels)\n", bminfohdr.biHeight, bminfohdr.biHeight);
        printf("bminfohdr.biPlanes=%#02x  (平面数)\n", bminfohdr.biPlanes);
        printf("bminfohdr.biBitCount=%#02x  (%d位深,1:单色,4:16色,8:256色,16:高彩色,24:真彩色,32:增强真彩色)\n", bminfohdr.biBitCount, bminfohdr.biBitCount);
        printf("bminfohdr.biCompression=%d  (压缩算法:0:BI_RGB,1:BI_RLE8,2:BI_RLE4,3:BI_BITFIELEDS,4:BI_JPEG,5:BI_PNG)\n", bminfohdr.biCompression);
        printf("bminfohdr.biSizeImage=%d  (位图缓冲,BI_RGB常为0)\n", bminfohdr.biSizeImage);
        printf("bminfohdr.biXPelsPerMeter=%d  (水平分辨率,pixels/meter)\n", bminfohdr.biXPelsPerMeter);
        printf("bminfohdr.biYPelsPerMeter=%d  (垂直分辨率,pixels/meter)\n", bminfohdr.biYPelsPerMeter);
        printf("bminfohdr.biClrUsed=%d  (位深为1~8时,用于指定调用板用到的颜色数,0表示全部都用到)\n", bminfohdr.biClrUsed);
        printf("bminfohdr.biClrImportant=%d  (0表示所有色彩都重要)\n", bminfohdr.biClrImportant);
  
           switch (bminfohdr.biBitCount)//位深,bpp,bit per pixel
           {
               //色彩位数
           case 32://0x20,增强真彩色,RGBA	///bminfohdr.biCompression == BI_RGB ||bminfohdr.biCompression ==BI_BITFIELDS
           {

               for (long h = 0; h < bminfohdr.biHeight; h++)
               {
                   for (long w = 0; w < bminfohdr.biWidth; w++)
                   {
                       unsigned long idxRGB = bmfilehdr.bfOffBits + w*bminfohdr.biBitCount / 8 + (bminfohdr.biWidth*bminfohdr.biBitCount / 8)*h;
                       //由索引到调色板,0XBB,0XGG,0XGG  //bmfilehdr.bfOffBits指向 调色板 的开头
                       RGBQUAD *pixelclr = (RGBQUAD*)&pmybmp[idxRGB];
                       SetPixel(hmdc, w, bminfohdr.biHeight - h - 1, RGB(pixelclr->rgbRed, pixelclr->rgbGreen, pixelclr->rgbBlue)); // bminfohdr.biHeight>0,图像上下倒置,所以要bminfohdr.biHeight - h
                   }

               }

           }

           break;

           case 24://24bit/pixel,0x18,真彩色,16777216色
           {
               long pad0 = (4 - bminfohdr.biBitCount / 8 * bminfohdr.biWidth % 4) % 4;//补0字节数,bitcount 为位数要除以8!!!
               for (long h = 0; h < bminfohdr.biHeight; h++)
               {
                   for (long w = 0; w < bminfohdr.biWidth; w++)
                   {
                       unsigned long idxRGB = bmfilehdr.bfOffBits + w*bminfohdr.biBitCount / 8 + (bminfohdr.biWidth*bminfohdr.biBitCount / 8 + pad0)*h;
                       //由索引到调色板,0XBB,0XGG,0XGG  //bmfilehdr.bfOffBits指向 调色板 的开头
                       RGBTRIPLE *pixelclr = (RGBTRIPLE*)&pmybmp[idxRGB];
                       // printf("index:%d,RGB(%d,%d,%d)\n", idxRGB, pixelclr->rgbtRed, pixelclr->rgbtGreen, pixelclr->rgbtBlue);
                       SetPixel(hmdc, w, bminfohdr.biHeight - h - 1, RGB(pixelclr->rgbtRed, pixelclr->rgbtGreen, pixelclr->rgbtBlue)); // bminfohdr.biHeight>0,图像上下倒置,所以要bminfohdr.biHeight - h
                   }

               }
          }

           break;

           case 16://0x10,高彩色,65536色,RGB565,RGB444,RGB555
               switch (bminfohdr.biSize)
               {
               case 0x28://X1RGB555,最高位空
               {
                   int pad0 = 32 - bminfohdr.biBitCount*bminfohdr.biWidth % 32;
                   for (long h = 0; h < bminfohdr.biHeight; h++)
                   {
                       for (long w = 0; w < bminfohdr.biWidth; w++)
                       {
                           unsigned long idxRGB = bmfilehdr.bfOffBits + w*bminfohdr.biBitCount / 8 + (bminfohdr.biWidth*bminfohdr.biBitCount + pad0)*h / 8;
                           //由索引到调色板,0XBB,0XGG,0XGG  //bmfilehdr.bfOffBits指向 调色板 的开头
                           USHORT color = *(USHORT*)&pmybmp[idxRGB];
                           RGBTRIPLE pixelclr;
                           pixelclr.rgbtRed = ((0x7c00 & color) >> 7);//RED:D6D5D4D3D2,即每个分量只有5位,靠MSB位置处放
                           pixelclr.rgbtGreen = ((0x03e0 & color) >> 2);//GREEN:D6D5D4D3D2
                           pixelclr.rgbtBlue = ((0x001f & color) << 3);//BLUE:D6D5D4D3D2
                           SetPixel(hmdc, w, bminfohdr.biHeight - h - 1, RGB(pixelclr.rgbtRed, pixelclr.rgbtGreen, pixelclr.rgbtBlue)); // bminfohdr.biHeight>0,图像上下倒置,所以要bminfohdr.biHeight - h
                       }

                   }
               }

               break;
               case 0x38://根据掩膜来取RGB,X4RGB444,RGB565
               {
                   if (bminfohdr.biCompression == BI_BITFIELDS)
                   {
                       struct CLRMASK
                       {
                           DWORD RedMask, GreenMask, BlueMask, AlphaMask;
                       };
                       int rr = 0, rg = 0, rb = 0;
                       CLRMASK *clr = (CLRMASK*)&pmybmp[icolor_table];
                       //printf("icolor_table:%02X,RGBA_MASK:%04x,%04x,%04x,%04x\n", icolor_table, clr->RedMask, clr->GreenMask, clr->BlueMask, clr->AlphaMask);

                       int pad0 = (32 - bminfohdr.biBitCount*bminfohdr.biWidth % 32) % 32;
                       //cmd.puts("...............,.............\n");
                       if (clr->RedMask == 0xf800 && clr->GreenMask == 0x07e0 && clr->BlueMask == 0x001f) { rr = 8; rg = 3; rb = 3; }
                       else if (clr->RedMask == 0x0f00 && clr->GreenMask == 0x00f0 && clr->BlueMask == 0x000f) { rr = 4; rg = 0; rb = 4; }
                       for (long h = 0; h < bminfohdr.biHeight; h++)
                       {
                           for (long w = 0; w < bminfohdr.biWidth; w++)
                           {
                               unsigned long idxRGB = bmfilehdr.bfOffBits + w*bminfohdr.biBitCount / 8 + (bminfohdr.biWidth*bminfohdr.biBitCount + pad0)*h / 8;
                               //由索引到调色板,0XBB,0XGG,0XGG  //bmfilehdr.bfOffBits指向 调色板 的开头
                               USHORT color = *(USHORT*)&pmybmp[idxRGB];
                               RGBTRIPLE pixelclr;
                               pixelclr.rgbtRed = (BYTE)((clr->RedMask & color) >> rr);//RED:D6D5D4D3D2,即每个分量只有5位,靠MSB位置处放
                               pixelclr.rgbtGreen = (BYTE)((clr->GreenMask & color) >> rg);//GREEN:D6D5D4D3D2
                               pixelclr.rgbtBlue = (BYTE)((clr->BlueMask & color) << rb);//BLUE:D6D5D4D3D2
                               SetPixel(hmdc, w, bminfohdr.biHeight - h - 1, RGB(pixelclr.rgbtRed, pixelclr.rgbtGreen, pixelclr.rgbtBlue)); // bminfohdr.biHeight>0,图像上下倒置,所以要bminfohdr.biHeight - h
                           }

                       }
                   }


               }
               break;
               }
               break;

           case 8://8bit/pixel,256色
           {
               long pad0 = (4 - bminfohdr.biBitCount * bminfohdr.biWidth / 8 % 4) % 4;//补0字节数
               //if (pad0 == 4) pad0 = 0;
               for (long h = 0; h < bminfohdr.biHeight; h++)
               {
                   for (long w = 0; w < bminfohdr.biWidth + pad0; w++)
                   {
                       //位图颜色数据索引
                       unsigned long	ibmoffset = w + (bminfohdr.biWidth + pad0) * h;
                       unsigned char	 iclrtbl = pmybmp[bmfilehdr.bfOffBits + ibmoffset];
                       //由索引到调色板,0XBB,0XGG,0XGG,0XAA  //icolor_table指向 调色板 的开头
                       RGBQUAD *pixelclr = (RGBQUAD*)&pmybmp[icolor_table + iclrtbl * 4];
                       SetPixel(hmdc, w, bminfohdr.biHeight - h - 1, RGB(pixelclr->rgbRed, pixelclr->rgbGreen, pixelclr->rgbBlue)); // bminfohdr.biHeight>0,图像上下倒置,所以要bminfohdr.biHeight - h
                   }

               }
               //	printf("FINISHED\n");
           }
           break;

           case 4: //4bit/pixel,16色位图
                //	bminfohdr.biCompression
           {
               int pad0 = (32 - bminfohdr.biBitCount*bminfohdr.biWidth % 32) % 32;
               for (long h = 0; h < bminfohdr.biHeight; h++)
               {
                   for (long w = 0; w < bminfohdr.biWidth; w++)
                   {
                       //索引,4位的索引,即半字节索引
                       unsigned long	ibmoffset = bmfilehdr.bfOffBits + w / 2 + (bminfohdr.biBitCount*bminfohdr.biWidth + pad0) / 8 * h;
                       unsigned char	 iclrtbl = (pmybmp[ibmoffset] >> ((1 - (w & 0x1)) * 4)) & 0x0f;
                       //由索引到调色板,0XBB,0XGG,0XGG,0XAA 
                       //icolor_table指向 调色板 的开头
                       RGBQUAD *pixelclr = (RGBQUAD*)&pmybmp[icolor_table + iclrtbl * 4];
                       SetPixel(hmdc, w, bminfohdr.biHeight - h - 1, RGB(pixelclr->rgbRed, pixelclr->rgbGreen, pixelclr->rgbBlue)); // bminfohdr.biHeight>0,图像上下倒置,所以要bminfohdr.biHeight - h
                   }
               }
           }
           break;

           case 1://单色,一般为黑白,1=白,0=黑,但其实两是两种可定义的颜色,即双色位图 
           {
               int pad0 = (32 - bminfohdr.biBitCount*bminfohdr.biWidth % 32) % 32;
               for (long h = 0; h < bminfohdr.biHeight; h++)
               {
                   for (long w = 0; w < bminfohdr.biWidth; w++)
                   {
                       long idx = (w + h*(bminfohdr.biWidth + pad0)) / 8;
                       unsigned char  mask = (1 << (7 - w % 8));
                       unsigned char dat = pmybmp[bmfilehdr.bfOffBits + idx];

                       SetPixel(hmdc, w, bminfohdr.biHeight - h - 1, (dat & mask) ? RGB(255, 255, 255) : RGB(0, 0, 0)); // bminfohdr.biHeight>0,图像上下倒置,所以要bminfohdr.biHeight - h
                   }
               }
           }
           break;
         }

  

//windows下加入缓冲提高速度      
BitBlt(hdc,1,1,bminfohdr.biWidth,bminfohdr.biHeight, hmdc, 0, 0, SRCCOPY);
DeleteObject(hbmp);
DeleteDC(hmdc);
ReleaseDC(hWnd,hdc);


补充:在WINDOWS上可以用一条语句代替以上代码,速度非常之快,超乎想象,不知道WINDOWS内部是怎么实现的    
 SetDIBitsToDevice(hdc, 0, 0, bminfohdr.biWidth, bminfohdr.biHeight, 0, 0, 0, bminfohdr.biHeight, (pmybmp+ bmfilehdr.bfOffBits), (BITMAPINFO*)&pmybmp[14], DIB_RGB_COLORS); 
//lpvBits :Pointer to DIB color data stored as an array of bytes.
//BITMAPFILEHEADER.bfOffBits:Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值