I've written myself a little C program to write a bitmap file from scratch. It works partially and I can't seem to find out why it doesn't work in certain cases. What works:
- Setting a constant color
- Setting a variable color using the loop index WHEN AT THE SAME TIME specifying an offset larger than 10.
What does not work:
- Setting a variable color without offset or an offset smaller or equal to 10
What's (probably) not the problem:
- image size is 1024 x 1024 -> no padding bytes required !
- The bitmap file is recognized by the windows "photo viewer". I also tried paint and gimp: the format is recognised and I get the same images.
For the whole code see below, here are the parts I deem important:
#pragma pack(push, 1) // no padding !
struct bmpHeader {
uint16_t hdrField; // set to 'B' 'M'
uint32_t fileSize;
uint16_t res1;
uint16_t res2;
uint32_t imgDataOffset;
};
struct bmpCoreHeader {
uint32_t hdrSize;
uint16_t width;
uint16_t height;
uint16_t colorPlanes; // 1
uint16_t bitsPerPixel; // 24
};
struct pixel {
uint8_t blue;
uint8_t green;
uint8_t red;
};
And the loop used for filling:
for (uint32_t i = 0; i < cHdr.width; i )
{
for (uint32_t j = 0; j < cHdr.height; j )
{
imageData->at(i).at(j).blue = 0;//j % 256;
imageData->at(i).at(j).red = i % 128 0;
imageData->at(i).at(j).green = 0;
}
//std::cout << i << ": ";
//std::cout << imageData->at(i).at(0).blue << std::endl;
}
I expect the above loop to produce an image that creates black-to-red fades. What it actually creates: (cropped image to half height, full width)
You can find the following artifacts on the boundary between the colors:
HOWEVER, when I set an offset like the following:
imageData->at(i).at(j).red = i % 128 11;
I get:
Any offset below 11 produces the same faulty picture. I have no idea of what's going on here.
Here is the full code:
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdint>
#include <array>
constexpr uint16_t width = 1024;
constexpr uint16_t height = 1024;
#pragma pack(push, 1)
struct bmpHeader {
uint16_t hdrField;
uint32_t fileSize;
uint16_t res1;
uint16_t res2;
uint32_t imgDataOffset;
};
struct bmpCoreHeader {
uint32_t hdrSize;
uint16_t width;
uint16_t height;
uint16_t colorPlanes;
uint16_t bitsPerPixel;
};
struct pixel {
uint8_t blue;
uint8_t green;
uint8_t red;
//uint8_t padding;
};
#pragma pack(pop)
int main()
{
bmpHeader hdr;
hdr.hdrField = 0x4d42;
// size is set at the end
hdr.fileSize = 0;
hdr.res1 = 0;
hdr.res2 = 0;
hdr.imgDataOffset = sizeof(bmpHeader) sizeof(bmpCoreHeader);
bmpCoreHeader cHdr = { 0 };
cHdr.hdrSize = sizeof(cHdr);
cHdr.width = width;
cHdr.height = height;
cHdr.colorPlanes = 1;
cHdr.bitsPerPixel = 24;
std::array<std::array<pixel, width>, height>* imageData = new std::array<std::array<pixel, width>, height>;
hdr.fileSize = sizeof(bmpHeader) sizeof(bmpCoreHeader) sizeof(*imageData);
for (uint32_t i = 0; i < cHdr.width; i )
{
for (uint32_t j = 0; j < cHdr.height; j )
{
imageData->at(i).at(j).blue = 0;//j % 256;
imageData->at(i).at(j).red = i % 128 9;
imageData->at(i).at(j).green = 0;
}
//std::cout << i << ": ";
//std::cout << imageData->at(i).at(0).blue << std::endl;
}
char* bmpContent = new char[sizeof(bmpHeader) sizeof(bmpCoreHeader) sizeof(*imageData)];
std::cout << "Image data size: " << sizeof(*imageData) << std::endl;
memcpy(bmpContent, &hdr, sizeof(bmpHeader));
memcpy(bmpContent sizeof(bmpHeader), &cHdr, sizeof(bmpCoreHeader));
memcpy(bmpContent sizeof(bmpHeader) sizeof(bmpCoreHeader), imageData, sizeof(*imageData));
std::fstream bmpFile("image.bmp", std::fstream::out);
if (!bmpFile.is_open())
{
std::cout << "Could not open / create BMP file" << std::endl;
return 1;
}
bmpFile.write(bmpContent, hdr.fileSize);
return 0;
}
Any input is greatly appreciated =)
CodePudding user response:
The file needs to be opened as binary, otherwise any 0A
is replaced with 0D0A
which will mess up alignment
std::ofstream bmpFile("image.bmp", std::ios_base::binary);
(ref https://docs.microsoft.com/en-us/cpp/standard-library/binary-output-files)