fileInfo.biCompression = BI_RGB;
16位位图使用
BI_BITFIELDS
压缩。此外,16位位图必须填充颜色表,以显示它是使用555格式、565格式还是其他格式。
24位和16位位图需要填充。尽管如此,如果以字节为单位的宽度是4的倍数,这不是问题。一般来说,您不能一个像素接一个像素地读/写,因为填充会将所有内容丢弃。相反,做两个环来穿过高度和宽度。像素大小也取决于填充。
请注意,您可以对或gdi+或wic执行相同的操作。可以将位图更改为不同的格式
PixelFormat16bppRGB555, PixelFormat16bppRGB565, PixelFormat16bppARGB1555, PixelFormat24bppRGB...
GDI+示例:
int main()
{
Gdiplus::GdiplusStartupInput tmp;
ULONG_PTR token;
Gdiplus::GdiplusStartup(&token, &tmp, NULL);
auto *source = Gdiplus::Bitmap::FromFile(L"test.bmp");
auto *destination = source->Clone(0, 0, source->GetWidth(), source->GetHeight(),
PixelFormat16bppRGB565);
CLSID clsid_bmp;
CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &clsid_bmp);
destination->Save(L"copy.bmp", &clsid_bmp);
delete destination;
delete source;
Gdiplus::GdiplusShutdown(token);
return 0;
}
自制版本:(使用
std::vector
为了记忆,而不是
new/delete
)
void Convert32to24(const wchar_t* file, std::vector<BYTE> &src, int width, int height)
{
int width_in_bytes_32 = width * 4;
int width_in_bytes_24 = ((width * 24 + 31) / 32) * 4;
DWORD size = width_in_bytes_24 * height;
std::vector<BYTE> dst(size);
for(int h = 0; h < height; h++)
for(int w = 0; w < width; w++)
{
int i = h * width_in_bytes_32 + w * 4;
int j = h * width_in_bytes_24 + w * 3;
dst[j + 0] = src[i + 0];
dst[j + 1] = src[i + 1];
dst[j + 2] = src[i + 2];
}
BITMAPFILEHEADER bf = { 'MB', 54 + size, 0, 0, 54 };
BITMAPINFOHEADER bi = { sizeof(bi), width, height, 1, 24, BI_RGB };
std::ofstream fout(file, std::ios::binary);
fout.write((char*)&bf, sizeof(bf));
fout.write((char*)&bi, sizeof(bi));
fout.write((char*)&dst[0], size);
}
void Convert32to16(const wchar_t* file, std::vector<BYTE> &src, int width, int height)
{
int width_in_bytes_32 = width * 4;
int width_in_bytes_16 = ((width * 16 + 31) / 32) * 4;
DWORD size = width_in_bytes_16 * height;
std::vector<BYTE> dst(size);
for(int h = 0; h < height; h++)
for(int w = 0; w < width; w++)
{
int i = h * width_in_bytes_32 + w * 4;
int j = h * width_in_bytes_16 + w * 2;
//555 format, each color is from 0 to 32, instead of 0 to 256
uint16_t blu = (uint16_t)(src[i + 0] * 31.f / 255.f);
uint16_t grn = (uint16_t)(src[i + 1] * 31.f / 255.f);
uint16_t red = (uint16_t)(src[i + 2] * 31.f / 255.f);
uint16_t sum = (red) | (grn << 5) | (blu << 10);
memcpy(&dst[j], &sum, 2);
}
BITMAPFILEHEADER bf = { 'MB', 54 + size, 0, 0, 54 };
BITMAPINFOHEADER bi = { sizeof(bi), width, height, 1, 16, BI_BITFIELDS };
std::ofstream fout(file, std::ios::binary);
fout.write((char*)&bf, sizeof(bf));
fout.write((char*)&bi, sizeof(bi));
//555 format
COLORREF color[]{
0b0000000000011111,//31
0b0000001111100000,//31 << 5
0b0111110000000000 //31 << 10
};
fout.write((char*)&color, sizeof(color));
fout.write((char*)&dst[0], size);
}
int main()
{
const wchar_t* file_32 = L"E:\\TestImage.bmp";
const wchar_t* file_16 = L"E:\\OutputImage16Bit.bmp";
const wchar_t* file_24 = L"E:\\OutputImage24Bit.bmp";
BITMAPFILEHEADER bh;
BITMAPINFOHEADER bi;
std::ifstream fin(file_32, std::ios::binary);
if(!fin)
return 0;
fin.read((char*)&bh, sizeof(bh));
fin.read((char*)&bi, sizeof(bi));
if(bi.biBitCount != 32)
return 0;
std::vector<BYTE> source(bh.bfSize);
fin.read((char*)&source[0], bh.bfSize);
Convert32to16(file_16, source, bi.biWidth, bi.biHeight);
Convert32to24(file_24, source, bi.biWidth, bi.biHeight);
return 0;
}