I'm trying to write image data to a BMP file. I found this website which proposes the following struct for the file header:
typedef struct { // Total: 54 bytes
uint16_t type; // Magic identifier: 0x4d42
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset; // Offset to image data in bytes from beginning of file (54 bytes)
uint32_t dib_header_size; // DIB Header size in bytes (40 bytes)
int32_t width_px; // Width of the image
int32_t height_px; // Height of image
uint16_t num_planes; // Number of color planes
uint16_t bits_per_pixel; // Bits per pixel
uint32_t compression; // Compression type
uint32_t image_size_bytes; // Image size in bytes
int32_t x_resolution_ppm; // Pixels per meter
int32_t y_resolution_ppm; // Pixels per meter
uint32_t num_colors; // Number of colors
uint32_t important_colors; // Important colors
} BMPHeader;
I know that C/C++ does not guarantee that the data from a struct will be stored contiguously, So simply writing the header struct to the file like so:
BMPHeader header(width, height, bytespp, fileSize);
int num_read = fwrite(&header, BMP_HEADER_SIZE, 1, fp);
or so:
BMPHeader* header = new BMPHeader(width, height, bytespp, fileSize);
int num_read = fwrite(header, BMP_HEADER_SIZE, 1, fp);
using the following constructor (this is probably unnecessary information, but including it incase it might be useful):
BMPHeader(
unsigned int width, unsigned int height, unsigned short bytespp, unsigned int fileSize) :
type(MAGIC_VALUE), size(fileSize),
reserved1(RESERVED), reserved2(RESERVED), offset(BMP_HEADER_SIZE),
dib_header_size(40), width_px(width), height_px(height), num_planes(NUM_PLANE),
bits_per_pixel(bytespp*BITS_PER_BYTE), compression(COMPRESSION),
image_size_bytes(width * height * bytespp), x_resolution_ppm(0x00),
y_resolution_ppm(0x00), num_colors(0x00), important_colors(IMPORTANT_COLORS)
{
}
won't work since there is no guarantee that the variables will be stored in the order that they are written, nor is there a guarantee that there won't be padding between the variables.
Regardless, I tried it anyways and, unsurprisingly, it didn't work.
My question is this: Is there any way to write the data to a file in a more elegant way than having to individually write each member variable to the file in the order that I want?
I tried instantiating the struct both onto the heap as a BMPHeader*
, as well as on the stack.
I also tried using a union like so:
struct BMPHeader
{
union
{
struct
{
unsigned short type; // Magic identifier: 0x4d42
unsigned int size; // File size in bytes
unsigned short reserved1; // Not used
unsigned short reserved2; // Not used
unsigned int offset; // Offset to image data in bytes from beginning of file (54 bytes)
unsigned int dib_header_size; // DIB Header size in bytes (40 bytes)
unsigned int width_px; // Width of the image
unsigned int height_px; // Height of image
unsigned short num_planes; // Number of color planes
unsigned short bits_per_pixel; // Bits per pixel
unsigned int compression; // Compression type
unsigned int image_size_bytes; // Image size in bytes
unsigned int x_resolution_ppm; // Pixels per meter
unsigned int y_resolution_ppm; // Pixels per meter
unsigned int num_colors; // Number of colors
unsigned int important_colors; // Important colors
};
unsigned char raw[54];
};
to see if I could write the array to the file, but I ran into the same issue.
As Yksisarvinen and n. m. will see y'all on Reddit mentioned in the comments above, I didn't include the pragma pack
directive.
Here is the change that fixed it:
#pragma pack(push, 1)
struct BMPHeader
{
uint16_t type; // Magic identifier: 0x4d42
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset; // Offset to image data in bytes from beginning of file (54 bytes)
uint32_t dib_header_size; // DIB Header size in bytes (40 bytes)
uint32_t width_px; // Width of the image
uint32_t height_px; // Height of image
uint16_t num_planes; // Number of color planes
uint16_t bits_per_pixel; // Bits per pixel
uint32_t compression; // Compression type
uint32_t image_size_bytes; // Image size in bytes
uint32_t x_resolution_ppm; // Pixels per meter
uint32_t y_resolution_ppm; // Pixels per meter
uint32_t num_colors; // Number of colors
uint32_t important_colors; // Important colors
BMPHeader(
unsigned int width, unsigned int height, unsigned short bytespp, unsigned int fileSize) :
type(MAGIC_VALUE), size(fileSize),
reserved1(RESERVED), reserved2(RESERVED), offset(BMP_HEADER_SIZE),
dib_header_size(40), width_px(width), height_px(height), num_planes(NUM_PLANE),
bits_per_pixel(bytespp*BITS_PER_BYTE), compression(COMPRESSION),
image_size_bytes(width * height * bytespp), x_resolution_ppm(0x00),
y_resolution_ppm(0x00), num_colors(0x00), important_colors(IMPORTANT_COLORS)
{
}
};
#pragma pack(pop)
Thanks all!