我有一个原始字节数组,我想用这些字节制作一个 bmp 文件。也就是说,我必须填写位图头结构和其他内容,然后写下字节,这样我就有了一个格式正确的 bmp 文件。
由于我只需要进行一些快速检查,我想知道是否有一种可移植的方法来执行此操作 - 获取原始字节并将它们保存为 bmp 文件。任何 Windows 版本都不会像我在 Unix 上写的那样。
或者,我可以将这些字节保存为任何其他图像格式 - 我只需要快速查看生成的图片。
这是我用于 .bmp 灰度图像的代码
要保存为彩色位图,只需确保不使用调色板(对于 24 位)
void SaveBitmapToFile( BYTE* pBitmapBits, LONG lWidth, LONG lHeight,WORD wBitsPerPixel, LPCTSTR lpszFileName )
{
RGBQUAD palette[256];
for(int i = 0; i < 256; ++i)
{
palette[i].rgbBlue = (byte)i;
palette[i].rgbGreen = (byte)i;
palette[i].rgbRed = (byte)i;
}
BITMAPINFOHEADER bmpInfoHeader = {0};
// Set the size
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
// Bit count
bmpInfoHeader.biBitCount = wBitsPerPixel;
// Use all colors
bmpInfoHeader.biClrImportant = 0;
// Use as many colors according to bits per pixel
bmpInfoHeader.biClrUsed = 0;
// Store as un Compressed
bmpInfoHeader.biCompression = BI_RGB;
// Set the height in pixels
bmpInfoHeader.biHeight = lHeight;
// Width of the Image in pixels
bmpInfoHeader.biWidth = lWidth;
// Default number of planes
bmpInfoHeader.biPlanes = 1;
// Calculate the image size in bytes
bmpInfoHeader.biSizeImage = lWidth* lHeight * (wBitsPerPixel/8);
BITMAPFILEHEADER bfh = {0};
// This value should be values of BM letters i.e 0x4D42
// 0x4D = M 0×42 = B storing in reverse order to match with endian
bfh.bfType = 'B'+('M' << 8);
// <<8 used to shift ‘M’ to end
// Offset to the RGBQUAD
bfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + sizeof(RGBQUAD) * 256;
// Total size of image including size of headers
bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage;
// Create the file in disk to write
HANDLE hFile = CreateFile( lpszFileName,GENERIC_WRITE, 0,NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
if( !hFile ) // return if error opening file
{
return;
}
DWORD dwWritten = 0;
// Write the File header
WriteFile( hFile, &bfh, sizeof(bfh), &dwWritten , NULL );
// Write the bitmap info header
WriteFile( hFile, &bmpInfoHeader, sizeof(bmpInfoHeader), &dwWritten, NULL );
// Write the palette
WriteFile( hFile, &palette[0], sizeof(RGBQUAD) * 256, &dwWritten, NULL );
// Write the RGB Data
if(lWidth%4 == 0)
{
WriteFile( hFile, pBitmapBits, bmpInfoHeader.biSizeImage, &dwWritten, NULL );
}
else
{
char* empty = new char[ 4 - lWidth % 4];
for(int i = 0; i < lHeight; ++i)
{
WriteFile( hFile, &pBitmapBits[i * lWidth], lWidth, &dwWritten, NULL );
WriteFile( hFile, empty, 4 - lWidth % 4, &dwWritten, NULL );
}
}
// Close the file handle
CloseHandle( hFile );
}
您可以使用SOIL,它是轻量级的、可移植的,虽然针对 OpenGL,但它可以加载图像(也可以保存图像)并返回原始数据。
这是一些使用示例(来自 SOIL 站点)
/* load an image as a heightmap, forcing greyscale (so channels should be 1) */
int width, height, channels;
unsigned char *ht_map = SOIL_load_image
(
"terrain.tga",
&width, &height, &channels,
SOIL_LOAD_L
);
以下是 SOIL 必须提供的可读格式:
Readable Image Formats:
BMP - non-1bpp, non-RLE (from stb_image documentation)
PNG - non-interlaced (from stb_image documentation)
JPG - JPEG baseline (from stb_image documentation)
TGA - greyscale or RGB or RGBA or indexed, uncompressed or RLE
DDS - DXT1/2/3/4/5, uncompressed, cubemaps (can't read 3D DDS files yet)
PSD - (from stb_image documentation)
HDR - converted to LDR, unless loaded with *HDR* functions (RGBE or RGBdivA or RGBdivA2)
编辑:如果您愿意,您也可以使用stb_image(它也是跨平台和可移植的),它在单个文件中包含所有文档等。
以下将为您提供来自字节数组的.ppm图像。指定每像素 3 字节的"P6"
二进制格式,但也支持纯文本和各种形式的灰度。你应该使用它的原因是它很简单,而且大多数 *nix 系统都有一堆 ppmto*-工具:ppmtobmp、ppmtojpeg、...、ppmtopng... 你可以命名它。
typedef struct {
int width;
int height;
uint8_t *data;
size_t size;
} ppm_image;
size_t ppm_save(ppm_image *img, FILE *outfile) {
size_t n = 0;
n += fprintf(outfile, "P6\n# THIS IS A COMMENT\n%d %d\n%d\n",
img->width, img->height, 0xFF);
n += fwrite(img->data, 1, img->width * img->height * 3, outfile);
return n;
}
还有 ppmtocad……谁能猜到?
试试EasyBMP,它是开源的跨平台 C++ 库,用它创建 BMP 文件很有趣:
BMP AnImage;
// Set size to 640 × 480
AnImage.SetSize(640,480);
// Set its color depth to 32-bits
AnImage.SetBitDepth(32);
// Set one of the pixels
AnImage(14,18)->Red = 255;
AnImage(14,18)->Green = 255;
AnImage(14,18)->Blue = 255;
AnImage(14,18)->Alpha = 0;
AnImage.WriteToFile("Output.bmp");
Boost GIL支持对 JPG、TIFF 和 PNG 的读/写。
由于高度基于模板,您可以使图像格式适应库。这对你来说可能有点过头了。
只需查看 WinGDI 的 typedefBITMAPFILEHEADER
和BITMAPINFOHEADER
来自 WinGDI 的 typedef,这些字段很简单。确保您有 16 位 int 用于 WORD 和 32 位 int 用于 DWORD。如果您正在做 RGBRGBRGB... 填写标题并写出数据非常容易...