Aleksandar Ilic
Aleksandar Ilic

Reputation: 35

How do I create BMP files using c++?

I'm trying to create a class that saves BMP image given the pixels. This is my .h:

#ifndef _IMAGE_SAVER_
#define _IMAGE_SAVER_

#include <vector>
#include <fstream>
#include <sstream>
#include <string>
#include <bitset>

//#define _DEBUG_

#ifdef _DEBUG_
#include <iostream>
#endif

#include <stdint.h>

typedef struct BMFileHeader {

    uint16_t _bfType = 19778;
    uint32_t _bfSize;
    uint16_t _bfReserved1 = 0;
    uint16_t _bfReserved2 = 0;
    uint32_t _bfOffBits = 54;
} BMFileHeader;

typedef struct BMInfoHeader {

    uint32_t _biSize = 40;
    uint32_t _biWidth;
    uint32_t _biHeight;
    uint16_t _biPlanes = 1;
    uint16_t _biBitCount = 24;
    uint32_t _biCompression = 0;
    uint32_t _biSizeImage = 0;
    uint32_t _biXPelsPerMeter = 3780;
    uint32_t _biYPelsPerMeter = 3780;
    uint32_t _biClrUser = 0;
    uint32_t _biClrImportant = 0;
} BMInfoHeader;

typedef struct RGBQuad {

    uint8_t _blue;
    uint8_t _green;
    uint8_t _red;
} RGBQuad;

class ImageSaver
{
public:
    ImageSaver() = delete;
    ImageSaver(const uint32_t& height, const uint32_t& width, const std::vector<RGBQuad>& pixels) :         _RGBQuad_Vector(pixels) {
        this->_Info_Header._biHeight = height;
        this->_Info_Header._biWidth = width;
    };

    void saveImage(const std::string& fileName);

protected:
    void setFileSize();
    void createImageFile(const std::string& fileName);  

private:
    BMFileHeader _File_Header;
    BMInfoHeader _Info_Header;
    std::vector<RGBQuad> _RGBQuad_Vector;
};

#endif

this is my .cpp:

#include "ImageSaver.h"

void ImageSaver::saveImage(const std::string& fileName)
{

    this->setFileSize();
    
    this->createImageFile(fileName);
}

void ImageSaver::setFileSize()
{

    uint32_t height = this->_Info_Header._biHeight;
    uint32_t width = this->_Info_Header._biWidth;
    
    this->_File_Header._bfSize = 3 * (height * width) + 54;
}

void ImageSaver::createImageFile(const std::string& fileName)
{

    std::ofstream imageFile(fileName + ".bmp", std::ios::binary);
    imageFile.write((char*)&this->_File_Header, 14);
    imageFile.write((char*)&this->_Info_Header, 40);
    size_t numberOfPixels = this->_Info_Header._biHeight * this->_Info_Header._biWidth;
    for (int i = 0; i < numberOfPixels; i++) {
        imageFile.write((char*)&(this->_RGBQuad_Vector[i]), 3);
    }
    imageFile.close();
}

and this is my main.cpp:

#include "ImageSaver.h"

#include <vector>
#include <iostream>


int main() {


    std::vector<RGBQuad> pixels;

    for (unsigned i = 0; i < 512 * 512; i++) {
        RGBQuad pixel;
        pixel._blue = 0;
        pixel._green = 255;
        pixel._red = 255;
        pixels.push_back(pixel);
    }

    ImageSaver im(512, 512, pixels);
    im.saveImage("scene1");

    std::cout << "ESTOP0" << std::endl;

    return 0;
}

Whenever I try to create an image file, it says that that image file is corrupt, even though it seems to me that I've been following BMP format properly. I've analyzed raw binary data and data seems (to me) to be correct. GIMP can open the picture, but it gives a black 512x512 picture which is not what I'm going for here.

Upvotes: 2

Views: 230

Answers (1)

Alex Guteniev
Alex Guteniev

Reputation: 13644

Certainly you miss #pragma pack. Wrap your structures with #pragma pack(push,1)/#pragma pack(pop)

When structures represent memory layout and should have no padding, even if having unaligned data, pack structures.

It is very helpful to have static_assert on expected sizeof value for such cases.

Additionally, when you pack them, the RGBQuad will become only 3 bytes. If you really want triple, suggest renaming it to triple. Otherwise add dummy or alpha byte to complete it to quad.

Upvotes: 4

Related Questions