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

时间:2020-11-29 11:48:57

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

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

以下代码中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