Storing (16, 24, 32)bpp bitmaps in binary files.
Paul whoknows

Due to the complexity of my game I have decided to create my own file format for graphic data storage.
I never did this before, so I want to begin with something simple.
I want to begin writing simple bitmaps to binary files.
Let's say I want to store 5 frames of a 100x100x16bpp bitmap in a binary file.
I guess I will need something like this:

typedef struct {
  int frame_number;
  char bytes[10000];  /* How many bytes do I need to store a single pixel in 16bpp? */
}my_frame;

I want to LEARN how to deal whit this, so detailed explanation, theory, and/or links will be accepted! thanks!
C only please.

Arthur Kalliokoski

I don't have one with me, but I use

typedef struct
{
 int width;   //pixels, not bytes per line
 int height;
 int color_depth; //in bytes, not bits per pixel
 int rgba;        //set to 1 for rgba (when color_depth == 4) or 0 for bgra
 int version;     //very helpful when you change this struct!
}TEXHEADER;

so size of image data is width*height*color_depth. You could also have another int for how many bytes past TEXHEADER the actual pixel data starts to make room for variable sized comment strings or something.

I just seen you want 5 frames, maybe add yet another int for how many times the pixel data is repeated?

[EDIT] I just remembered that the color_depth doesn't discriminate between 15/16 bits per pixel, but I don't use 15, or 8 for that matter.

Paul whoknows

Thanks very much!
Another int added for frames, just like you said, but to make life easier I won't allow different size frames...just for now.

typedef struct
{
 int width;   //pixels, not bytes per line
 int height;
 int color_depth; //in bytes, not bits per pixel
 int rgba;        //set to 1 for rgba (when color_depth == 4) or 0 for bgra
 int version;     //very helpful when you change this struct!
 int frames;    //amount of frames  
}TEXHEADER;

So the header should look like this:
0000 Width 0001 0002 0003 0004 Height 0005 0006 0007 0008 color depth 0009 000A 000B 000C rgba 000D 000E 000F 0010 version 0011 0012 0013 0014 frames 0015 0016 0017 0018 Begin of data . .

I don't understand the rgba flag, what's the diference between bgra or rgba?
I am not sure about how I should store my bytes, little or big endian???

Arthur Kalliokoski

Assuming for OpenGL:

rgba vs. bgra is the order the red, blue, green values are stored, Windows uses bgr and I've read on an Nvidia page that they've optimized bgr as opposed to rgb due to Windows. Some older cards may not handle the bgr order, check the flags. If you're worried about porting to other cpu's, you could either store/load the bytes individually or (probably faster) swap them around after loading from disk. I haven't had a big-endian computer (well, Motorola 8 bit doesn't have an endianness) but

union
{
 int a;
 char b[4];
}endian;

int is_small_endian = 0;

endian.a = 1;
is_small_endian = endian.b[3];  //little endian would set b[0]

Dustin Dettmer
#define POST_PACKED __attribute__((packed))

struct TEXHEADER
{
 int32_t width;   //pixels, not bytes per line
 int32_t height;
 int32_t color_depth; //in bytes, not bits per pixel
 int32_t rgba;        //set to 1 for rgba (when color_depth == 4) or 0 for bgra
 int32_t version;     //very helpful when you change this struct!
 int32_t frames;    //amount of frames  
} POST_PACKED;

Now you're rollin.

orz

That doesn't actually change the size of the struct in question (try a sizeof on it both ways, on any platform gcc supports today), and it breaks compatibility with some compilers.

edit: okay, I'm just whining pointlessly; the #define could be #ifdefed to something equivalent on another compiler to improve compatibility, and while packing it doesn't change this particular struct, it is vaguely appropriate for structs corresponding to formats on disk.

Paul whoknows
Quote:

union
{
 int a;
 char b[4];
}endian;

That's amazing! really useful, thanks!

Quote:

#define POST_PACKED __attribute__((packed))

Dustin I don't know what are you doing here, please can you comment it?
I do never use structs, I use typedefs structs instead, something wrong with that?
Problems with ifferent size of ints isn't a problem right now, but perhaps this would be a better alternative:

typedef struct
{
 /* width */
 char width[4];
 char height[4];
.
.
. and so on

EDITED!!!

Dustin Dettmer
Quote:

Dustin I don't know what are you doing here, please can you comment it?
I do never use structs, I use typedefs structs instead, something wrong with that?
Problems with ifferent size of ints isn't a problem right now, but perhaps this would be a better alternative:

If you're going to be accessing the data of a struct manually, then specify the packed option. Don't do it and the god of coding karma will strike you down.

You might as well just use the struct name, unless you're trying to conform to an old C standard. typedef went out of style decades ago.

Different sized ints are always a problem.

Paul whoknows
Quote:

if you're going to be accessing the data of a struct manually, then specify the packed option. Don't do it and the god of coding karma will strike you down.

The keyword _attribute_ doesn't exist in my Borland 5.0 compiler! is that ANSI C supported?

Quote:

You might as well just use the struct name, unless you're trying to conform to an old C standard.

Like I said at the beginning of this post I am using C.

orz
Quote:

The keyword _attribute_ doesn't exist in my Borland 5.0 compiler! is that ANSI C supported?

It's gcc-only. There are equivalents in most compilers though. I don't know about Borland. Try looking up "packing" or "alignment", or anything about controlling the actual layout of structure members in memory in any docs you have.

Ron Ofir

Isn't packing only relevant when you directly write the struct to disk? I thikn it's better to simply write the fields one after another insteda of all of them at the same time.

orz
Quote:

Isn't packing only relevant when you directly write the struct to disk?

Pretty much. It can effect the size of the struct in memory as well, but that's not usually important.

Quote:

I thikn it's better to simply write the fields one after another insteda of all of them at the same time.

Some people find it easier to code / maintain / examine / debug / etc to let that happen automatically in a single fwrite call or whatever.

Thomas Fjellstrom
Quote:

Some people find it easier to code / maintain / examine / debug / etc to let that happen automatically in a single fwrite call or whatever.

And now you have endian dependant files. Not as important as it once was, but if you ever want those images to load properly on a BigEndian machine, never write the struct directly.

orz
Quote:

And now you have endian dependant files. Not as important as it once was, but if you ever want those images to load properly on a BigEndian machine, never write the struct directly.

Some people use the structs with endian-independant integer types (ie plain ints on one endianness, #ifdefed to classes that emulate non-native endiannes ints on the other endianness). Not a very popular practice admittedly, but I know a few.

Thomas Fjellstrom

The "best" (IMO) method is just to store and read the ints in a single endian, and convert to the proper endian on load (if needed).

for example, you can use:

// set 1
pack_igetw(); pack_iputw(); // 16 bit
pack_igetl(); pack_iputl(); // 32 bit

// set 2
pack_mgetw(); pack_mputw();
pack_mgetl(); pack_mputl();

Use one or the other, and your ints will be properly saved and loaded, without having to worry about endianess.

Thread #588101. Printed from Allegro.cc