A portable function to create a bmp file from raw bytes?

14,049

Solution 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?

Solution 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 );
}

Solution 3

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");

Solution 4

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.

Solution 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.

Share:
14,049
Alexander Kulyakhtin
Author by

Alexander Kulyakhtin

Updated on June 05, 2022

Comments

  • Alexander Kulyakhtin
    Alexander Kulyakhtin almost 2 years

    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.

  • Alexander Kulyakhtin
    Alexander Kulyakhtin over 11 years
    Thanks a lot, it has worked out
  • CreativeMind
    CreativeMind almost 10 years
    Perfect. Worked for me. Only change required is to bmpInfoHeader.biHeight = -lHeight; for Top-to-Bottom image.
  • CreativeMind
    CreativeMind almost 10 years
    What to do if I want to convert raw data into HBITMAP ?