可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 5 years ago.
Improve this question
I have an array of raw bytes and I want to make a bmp file from those bytes. That is, I have to fill bitmap header structre and other things, then write down the bytes so I have a bmp file in the proper format.
As I need this for some quick check only, I wonder if there is a portable method to do this - take raw bytes and save them as a bmp file. Any Windows version won't do as I'm writing that on Unix.
Alternatively, I can save those bytes as any other image format - I only need to have a quick look at the resulting picture.
回答1:
The following will give you a .ppm image from a byte array. The "P6"
specifies binary format 3 byte per pixel, but plain text is also supported and various forms of grayscale. The reason you should use this is that it is easy as can be and most *nix systems have a bunch of ppmto*-tools: ppmtobmp, ppmtojpeg, ..., ppmtopng... you name it.
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;
}
There is also ppmtocad... who would have guessed?
回答2:
This is the code I use for .bmp Greyscale images
To save as color bitmap, just make sure about not using the palette (for 24bits)
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 );
}
回答3:
You could use SOIL, it's lightweight, portable, and although aimed for OpenGL it can load images (and also save images) and give you back the raw data.
Here's some example usage usage (from the SOIL site)
/* 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
);
Here's the readable formats that SOIL has to offer:
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)
EDIT: You can also use stb_image (which is also cross-platform and portable) if you prefer, which has all documentation and such in the single file.
回答4:
Try EasyBMP, it is open-source cross-platform C++ library and it is just fun to create BMP files with it:
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");
回答5:
Boost GIL supports read/write to JPG, TIFF and PNG.
Being heavily template-based you can adapt your image format to the library. It may be an overkill for you tho.
回答6:
Just take a look at the typedefs for BITMAPFILEHEADER
and BITMAPINFOHEADER
from WinGDI, the fields are easy. Make sure you have 16-bit int for WORD and 32-bit int for DWORD. If you're doing RGBRGBRGB... it's super-easy to fill in the headers and write out the data...