Getting RGB values for each pixel from a 24bpp Bitmap for conversion to GBA format in C

StackOverflow https://stackoverflow.com/questions/1531268

  •  20-09-2019
  •  | 
  •  

Question

I want to read the RGB values for each pixel from a .bmp file, so I can convert the bmp into a format suitable for GBA (GameBoy Advance).

I need to get just the RGB for each pixel and then write this information to a file.

I am trying to use the <windows.h> structures:

typedef struct
{
    char signature[2];
    unsigned int fileSize;
    unsigned int reserved;
    unsigned int offset;
}BmpHeader;

typedef struct
{
    unsigned int headerSize;
    unsigned int width;
    unsigned int height;
    unsigned short planeCount;
    unsigned short bitDepth;
    unsigned int compression;
    unsigned int compressedImageSize;
    unsigned int horizontalResolution;
    unsigned int verticalResolution;
    unsigned int numColors;
    unsigned int importantColors;

}BmpImageInfo;

typedef struct
{
    unsigned char blue;
    unsigned char green;
    unsigned char red;
    unsigned char reserved;
}Rgb;

typedef struct
{
    BmpHeader header;
    BmpImageInfo info;
    Rgb colors[256];
    unsigned short image[1];
}BmpFile;

but I only need RGB struct. So let's say I read "in.bmp":

FILE *inFile, *outFile;
inFile = fopen("C://in.bmp", "rb");

Rgb Palette[256];
for(i=0;i<256;i++)
{
   fread(&Palette[i],sizeof(Rgb),1,inFile);
}

fclose(inFile);

Is this correct? How do I write only the RGB information to a file?

Was it helpful?

Solution

You need first to get the number of colors available in the embedded palette. This is available in the DIB Header.

Then you can read all color components that contain the palette.

You can see all header information like offset to know whereto seek : http://en.wikipedia.org/wiki/BMP_file_format.

This should work: (Edit: Add code to write in file)

FILE *inFile, *outFile;
BMPHeader header;
BMPImageInfo info;
RGB *palette, *p;
int i = 0;

inFile = fopen("C://in.bmp", "rb");
if( !inFile )
   return;

if( fread(&header, sizeof(BMPHeader), 1, inFile) != 1 )
   return; // Manage error and close file

if( fread&info, sizeof(BMPImageInfo), 1, inFile) != 1 )
   return; // Manage error and close file

if( info.numColors > 0 )
{
   palette = (RGB*)malloc(sizeof(RGB) * info.numColors);
   if( fread(palette, sizeof(RGB), info.numColors, inFile) != info.numColors )
      return; // manage error and close file
}

fclose(inFile)

// Binary method => if read later by another computer
outFile = fopen("path", "wb");
if( !outFile )
   return;

if( fwrite(&info.numColors, sizeof(unsigned int), 1, outFile) != 1 )
   return; // Manage Error and close file

if( fwrite(&palette, sizeof(RGB), info.numColors, outFile) != info.numColors )
   return; // Manage error and close file

fclose(outFile);

// Text method => if read later by human
outFile = fopen("path", "w");
if( !outFile )
   return;

for( i=0; i<info.numColors; ++i )
{
   p = &palette[i];
   if( fprintf(outFile, "R:%d, G:%d, B:%d\n", p->red, p->green, p->blue) < 0 )
      return; // Manage error and close file
}

fclose(outFile);

OTHER TIPS

If you are on windows you can use the LoadBitmap function from win32

then given the handle convert it to a DIB bitmap and get to the pixels that way

I did a bit of testing and extended Patrice's program a bit. I'm not a good C programmer, so all credit goes to him and my parts aren't as elegant as his - sorry for that.

Warning: huge source code ahead.

#include <stdio.h>
#pragma pack(2)


typedef struct
{
    char signature[2];
    unsigned int fileSize;
    unsigned int reserved;
    unsigned int offset;
} BmpHeader;

typedef struct
{
    unsigned int headerSize;
    unsigned int width;
    unsigned int height;
    unsigned short planeCount;
    unsigned short bitDepth;
    unsigned int compression;
    unsigned int compressedImageSize;
    unsigned int horizontalResolution;
    unsigned int verticalResolution;
    unsigned int numColors;
    unsigned int importantColors;

} BmpImageInfo;

typedef struct
{
    unsigned char blue;
    unsigned char green;
    unsigned char red;
    //unsigned char reserved; Removed for convenience in fread; info.bitDepth/8 doesn't seem to work for some reason
} Rgb;


int main( int argc, char **argv ) {

    FILE *inFile;
    BmpHeader header;
    BmpImageInfo info;
    Rgb *palette;
    int i = 0;

    printf( "Opening file %s for reading.\n", argv[1] );

    inFile = fopen( argv[1], "rb" );
    if( !inFile ) {
        printf( "Error opening file %s.\n", argv[1] );
        return -1;
    }

    if( fread(&header, 1, sizeof(BmpHeader), inFile) != sizeof(BmpHeader) ) {
        printf( "Error reading bmp header.\n" );
        return -1;
    }

    if( fread(&info, 1, sizeof(BmpImageInfo), inFile) != sizeof(BmpImageInfo) ) {
        printf( "Error reading image info.\n" );
        return -1;
    }

    if( info.numColors > 0 ) {
        printf( "Reading palette.\n" );
        palette = (Rgb*)malloc(sizeof(Rgb) * info.numColors);
        if( fread(palette, sizeof(Rgb), info.numColors, inFile) != (info.numColors * sizeof(Rgb)) ) {
            printf( "Error reading palette.\n" );
            return -1; // error
        }
    }

    printf( "Opening file %s for writing.\n", argv[2] );
    FILE *outFile = fopen( argv[2], "wb" );
    if( !outFile ) {
        printf( "Error opening outputfile.\n" );
        return -1;
    }
    Rgb *pixel = (Rgb*) malloc( sizeof(Rgb) );
    int read, j;
    for( j=0; j<info.height; j++ ) {
        printf( "------ Row %d\n", j+1 );
        read = 0;
        for( i=0; i<info.width; i++ ) {
            if( fread(pixel, 1, sizeof(Rgb), inFile) != sizeof(Rgb) ) {
                printf( "Error reading pixel!\n" );
                return -1;
            }
            read += sizeof(Rgb);
            printf( "Pixel %d: %3d %3d %3d\n", i+1, pixel->red, pixel->green, pixel->blue );
        }
        if( read % 4 != 0 ) {
            read = 4 - (read%4);
            printf( "Padding: %d bytes\n", read );
            fread( pixel, read, 1, inFile );
        }
    }

    printf( "Done.\n" );
    fclose(inFile);
    fclose(outFile);

    printf( "\nBMP-Info:\n" );
    printf( "Width x Height: %i x %i\n", info.width, info.height );
    printf( "Depth: %i\n", (int)info.bitDepth );

    return 0;

}

This program reads out the pixel information stored in the file. It takes the padding into account but only works with bmps with a 24 bits per pixel color depth (If you need other depths, you'll have to customize the Rgb struct). Hope this helps you, but as I said, it's just an extension of Patrice's code.

Here's a sample output from my testfile:

$ ./a.out test.bmp out.txt
Opening file test.bmp for reading.
Opening file out.txt for writing.
------ Row 1
Pixel 1:   0   0   0
Pixel 2:   0   0   0
Pixel 3:   0   0   0
Pixel 4:   0   0   0
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 2
Pixel 1:   0   0   0
Pixel 2: 232  33  33
Pixel 3:   0   0   0
Pixel 4: 232  33  33
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 3
Pixel 1:   0   0   0
Pixel 2:   0   0   0
Pixel 3: 232  33  33
Pixel 4:   0   0   0
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 4
Pixel 1:   0   0   0
Pixel 2: 232  33  33
Pixel 3:   0   0   0
Pixel 4: 232  33  33
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 5
Pixel 1:   0   0   0
Pixel 2:   0   0   0
Pixel 3:   0   0   0
Pixel 4:   0   0   0
Pixel 5:   0   0   0
Padding: 1 bytes
Done.

BMP-Info:
Width x Height: 5 x 5
Depth: 24

Edit: Yes, my image is displaying a red cross. Note that the image is stored upside-down so row 1 of the file is actually row 5 of the image. D'oh forgot to write something to file the code opens, but this is left as an excercise up to you ;).

If you're guaranteed that it's an uncompressed 24bpp bitmap and its width is divisible by 4, it's relatively simple:

  1. Read a BmpHeader at the start of the file.
  2. Without seeking, read a BmpImageInfo.
  3. Seek to the BmpHeader's offset bytes from the beginning of the file. Note that there is no palette (at least, not one we care about) in 24-bit bitmaps!
  4. Read BGR triplets (in that order, and there's no reserved unused member here). There will be (BmpImageInfo's members) width * abs(height) triplets. As I recall, if height is positive (the standard case), the first line of color you read will be the bottom line of the image, going up from there. If height is negative, though, the first line of color in the file is the top of the image, going down from there.

If you can't make those guarantees, then things get a good deal more complicated.

Disclaimer: I'm gratuitously tooting my own horn here. Years ago I wrote a tiny (one source file) utility to do exactly what you're talking about, in a portable (100% ANSI C) way: glbmp libbmpread. Its source might shed some light on your problem--it handles uncompresed (no RLE) bitmaps of any bit depth, and should run fine on a GBA.

See wikipedia page of .bmp file format which provides lots of information about the structure of the file and should help you to parse it.

http://en.wikipedia.org/wiki/BMP_file_format

In your code you first have to read the starting address of the data (specified in file header) and the height of the image. Then seek to this position. After that you read in one row pixel by pixel (the header specifies how many bits per pixel) and have to take care of the 32-bit padding at the end of it. Note that in a 24-bit image (RGB) the information is stored in BGR order. Also if the height value is not negative, the image is stored upside down.

I'm not familiar with the BMP file format, but wouldn't you need to read in the header information first? Something like:

BmpHeader header;
fread(&header,sizeof(BmpHeader),1,inFile);

and read in detailed image information, which you'll need:

BmpImageInfo info;
fread(&info,sizeof(BmpImageInfo),1,inFile);

and read in palette information as well.

once you have that you will know the file size and data offset. You could pre-allocate sufficient space and read in all at once, which may be simplest. Alternatively you could read in in chunks and parse as you're going (reduces chance of not having enough memory, but parsing is more complicated).

You'll also know from the info section if compression is enabled, image dimensions etc.

If you're reading in all at once, jump to the offset of the data, and do something like:

Rgb* rgb = offset;
blueVal = rgb->blue;
greenVal = rgb->green;
redVal = rgb->red;
rgb += sizeof( Rgb );

and so on. Obviously that code isn't checking for errors, end of buffer, and so on, so you'll need to do that. You'll probably also have to read in palette information to make sense of the image data.

Or, as someone else said, look at the format spec on Wikipedia

If the BMP file has a palette then the below code should work:

  FILE *inFile, *outFile;
  inFile = fopen("C:/in.bmp", "rb");
  Rgb Palette[256];
  if ( inFile ) {
    // Bypass headers
    fseek(inFile, sizeof(BmpHeader) + sizeof(BmpImageInfo), SEEK_SET);
    // Load the whole palette
    fread(Palette, sizeof(Palette), 1, inFile);
    fclose(inFile);
  }

You might find it useful to look at some C code I wrote many years ago for reading and writing BMP files, located at:

http://david.tribble.com/src/bmp/bmp.html

I believe it handles the various pixel bit sizes (1/2/4/8/24) as well as RLE compression.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top