Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simplest format to store uncompressed RGBA image data

Tags:

c

format

image

rgba

I'm currently creating a software tool that generates images that contain independent RGBA information for each pixel - meaning the red, green, blue and alpha components are supposed to be read and treated as separate entities in a single pixel (i.e. no alpha premultiplication or any other data combination tricks).

These images must be stored on disk. The intent is to store them in a way that the various pixels of the image can be easily read, later, by a simple C program, one pixel at a time. Another requirement is that the stored image itself should be visible by at least one OS tool (Preview for MacOS, Paint for Windows, web browsers, or any other commonly-used tools to view images in a computer).

What is the simplest image file format that I could use to store the data as described above? The closest I've found is TIFF; however, the spec requires alpha premultiplication for RGBA values. What other formats do you know that let me store image data in a uncompressed RGBA plain format?

Just to be clear, file size is not a concern. The order of the components in the pixel is also not a concern; BGRA is as equally valid as RGBA for all practical purposes in this project.

like image 683
Izhido Avatar asked Apr 22 '17 18:04

Izhido


2 Answers

All right, answering my own question. Apparently, the answer is, "I didn't look hard enough" :D .

In the Wikipedia article about the BMP image file format, there is an example of a 4x2 ARGB image with all its fields clearly explained.

Thank you, @Weather Vane for the hint.

I did find, however, a problem with the data in it. Preview for MacOS didn't like that particular image, with that specific format. I did some changes to the file, and managed to successfully generate a 4x2 ARGB bitmap that can be seen and used on MacOS and Windows.

The following is the code I used to generate the BMP file, for future reference:

#include <iostream>
#include <fstream>

unsigned char bmpData[] = // All values are little-endian
{
    0x42, 0x4D,             // Signature 'BM'
    0xaa, 0x00, 0x00, 0x00, // Size: 170 bytes
    0x00, 0x00,             // Unused
    0x00, 0x00,             // Unused
    0x8a, 0x00, 0x00, 0x00, // Offset to image data

    0x7c, 0x00, 0x00, 0x00, // DIB header size (124 bytes)
    0x04, 0x00, 0x00, 0x00, // Width (4px)
    0x02, 0x00, 0x00, 0x00, // Height (2px)
    0x01, 0x00,             // Planes (1)
    0x20, 0x00,             // Bits per pixel (32)
    0x03, 0x00, 0x00, 0x00, // Format (bitfield = use bitfields | no compression)
    0x20, 0x00, 0x00, 0x00, // Image raw size (32 bytes)
    0x13, 0x0B, 0x00, 0x00, // Horizontal print resolution (2835 = 72dpi * 39.3701)
    0x13, 0x0B, 0x00, 0x00, // Vertical print resolution (2835 = 72dpi * 39.3701)
    0x00, 0x00, 0x00, 0x00, // Colors in palette (none)
    0x00, 0x00, 0x00, 0x00, // Important colors (0 = all)
    0x00, 0x00, 0xFF, 0x00, // R bitmask (00FF0000)
    0x00, 0xFF, 0x00, 0x00, // G bitmask (0000FF00)
    0xFF, 0x00, 0x00, 0x00, // B bitmask (000000FF)
    0x00, 0x00, 0x00, 0xFF, // A bitmask (FF000000)
    0x42, 0x47, 0x52, 0x73, // sRGB color space
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Unused R, G, B entries for color space
    0x00, 0x00, 0x00, 0x00, // Unused Gamma X entry for color space
    0x00, 0x00, 0x00, 0x00, // Unused Gamma Y entry for color space
    0x00, 0x00, 0x00, 0x00, // Unused Gamma Z entry for color space

    0x00, 0x00, 0x00, 0x00, // Unknown
    0x00, 0x00, 0x00, 0x00, // Unknown
    0x00, 0x00, 0x00, 0x00, // Unknown
    0x00, 0x00, 0x00, 0x00, // Unknown

    // Image data:
    0xFF, 0x00, 0x00, 0x7F, // Bottom left pixel
    0x00, 0xFF, 0x00, 0x7F,
    0x00, 0x00, 0xFF, 0x7F,
    0xFF, 0xFF, 0xFF, 0x7F, // Bottom right pixel
    0xFF, 0x00, 0x00, 0xFF, // Top left pixel
    0x00, 0xFF, 0x00, 0xFF,
    0x00, 0x00, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF  // Top right pixel
};

int main(int argc, const char * argv[])
{
    std::fstream fs("test.bmp", std::ios_base::out | std::ios_base::binary);

    fs.write((const char *)bmpData, sizeof(bmpData));

    fs.close();

    std::cout << "The BMP has been written.\n";

    return 0;
}
like image 99
Izhido Avatar answered Nov 09 '22 18:11

Izhido


I realize this was already answered with RGBA BMP, but there's another option to look at: the Netpbm people have created a new format to extend PBM/PGM/PPM/PNM, and it can also store 16-bit depths and/or an alpha channel.

It's called Portable Arbitrary Map and uses the .pam extension. It functions similarly to the other binary PNM formats, except it has a P7 magic header number and a slightly extended header format. For example, to create a 4px by 2px RGBA image, with each channel having 8bpp, you'd use this header:

P7
WIDTH 4
HEIGHT 2
DEPTH 4
MAXVAL 255
TUPLTYPE RGB_ALPHA
ENDHDR

...followed by a flat dump of your pixel data.

You can see more of the details on the Netpbm page: http://netpbm.sourceforge.net/doc/pam.html

Currently .pam is not widely supported so it's missing a crucial piece of your question ("must be viewable using a built-in OS tool"). XnView and FFmpeg can work with it at least. Also, the Netpbm library ships with a pamtopng utility that does exactly what it says on the tin - convert even RGBA PAM to PNG-with-alpha. So, something to consider as an interchange format if you're writing your own tools.

like image 45
Greg Kennedy Avatar answered Nov 09 '22 20:11

Greg Kennedy