Robben_Ford_Fan_boy
Robben_Ford_Fan_boy

Reputation: 8720

C++ A generic method for writing a vector of Pixels to BMP

Is it possible to write a class that will take vector of Pixels:

struct Pixel
{
    unsigned char Blue;
    unsigned char Green;
    unsigned char Red;
    unsigned char Alpha;
};

And generate a BMP from the vector. I am attempting to start from a basic example, so I am trying with just 4 Pixels, so that is why some of the header values are hard coded.

Here is my header:

uint16_t const BITMAP_FILE_TYPE = 0x4d42; //BM, little-endian 
static uint32_t const BITS_PER_BYTE = 8;
static uint32_t const BITS_PER_PIXEL = 24; 

#pragma pack(1) 
struct BITMAPFILEHEADER {
       //uint16_t   bfType;
       uint32_t   bfSize;
       uint16_t   bfReserved1;
       uint16_t   bfReserved2;
       uint32_t   bfOffBits;
}; BITMAPFILEHEADER;

struct BITMAPINFOHEADER {
       uint32_t      biSize;
       int32_t       biWidth;
       int32_t       biHeight;
       uint16_t      biPlanes;
       uint16_t      biBitCount;
       uint32_t      biCompression;
       uint32_t      biSizeImage;
       int32_t       biXPelsPerMeter;
       int32_t       biYPelsPerMeter;
       uint32_t      biClrUsed;
       uint32_t      biClrImportant;
}; BITMAPINFOHEADER;
#pragma pack()

inline uint64_t CalcBitmapFileSize(uint32_t widthBytes, uint32_t height) {
       uint64_t fileSize = widthBytes * height;

       uint32_t padSize = (widthBytes) % sizeof(uint32_t);
       if (padSize > 0) {
              padSize = sizeof(uint32_t) - padSize;
              padSize *= height;
       }
       fileSize += padSize;
       fileSize += sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);


       return fileSize;
}

class BitMapFileWriter
{
public:
    BitMapFileWriter();
    void WriteBMPFile(vector<Pixel> pixelVec, string fileName, uint32_t WIDTH, uint32_t HEIGHT);
};

And here is my impl:

void BitMapFileWriter::WriteBMPFile(vector<Pixel> pixelVec, string fileName, uint32_t WIDTH, uint32_t HEIGHT)
{
       FILE             *filePtr;          // file pointer
       BITMAPFILEHEADER bitmapFileHeader;  // bitmap file header
       BITMAPINFOHEADER bitmapInfoHeader;  // bitmap info header
       memset(&bitmapFileHeader, 0, sizeof(bitmapFileHeader));
       memset(&bitmapInfoHeader, 0, sizeof(bitmapInfoHeader));

                                                                     // open file for writing binary mode
       filePtr = fopen("C:\\BMP\\4PixelOutPut.bmp", "wb");

       // define the bitmap file header
       //bitmapFileHeader.bfType = BITMAP_FILE_TYPE;
       unsigned short bfType=0x4d42;  
       bitmapFileHeader.bfSize = static_cast<uint32_t>(CalcBitmapFileSize(WIDTH * (BITS_PER_PIXEL / BITS_PER_BYTE), HEIGHT));
       bitmapFileHeader.bfReserved1 = 0;
       bitmapFileHeader.bfReserved2 = 0;
       bitmapFileHeader.bfOffBits = sizeof(bitmapFileHeader) + sizeof(bitmapInfoHeader);

       // define the bitmap information header
       bitmapInfoHeader.biSize = sizeof(bitmapInfoHeader);
       bitmapInfoHeader.biWidth = WIDTH;                      // bitmap width
       bitmapInfoHeader.biHeight = HEIGHT;                    // bitmap height
       bitmapInfoHeader.biPlanes = 1;
       bitmapInfoHeader.biBitCount = BITS_PER_PIXEL;                        
       bitmapInfoHeader.biCompression = 0;               // no compression
       bitmapInfoHeader.biSizeImage = (WIDTH * HEIGHT) * 4;          // width * height
       bitmapInfoHeader.biXPelsPerMeter = 5000;
       bitmapInfoHeader.biYPelsPerMeter = 5000;
       bitmapInfoHeader.biClrUsed = 0;
       bitmapInfoHeader.biClrImportant = 0;

       fwrite(&bfType,1,sizeof(bfType),filePtr);
                                                                                                   // write the bitmap file header
       fwrite(&bitmapFileHeader, 1, sizeof(BITMAPFILEHEADER), filePtr);

       // write the bitmap info header
       fwrite(&bitmapInfoHeader, 1, sizeof(BITMAPINFOHEADER), filePtr);

       for(int i=0; i<4; i++)
       {
           Pixel pixel = pixelVec[i];
           fwrite(&pixel.Blue, 1, 1, filePtr);
           fwrite(&pixel.Green, 1, 1, filePtr);
           fwrite(&pixel.Red, 1, 1, filePtr);
       }


       // close our file
       fclose(filePtr);
}

And calling it: BitMapFileWriter bmfw; bmfw.WriteBMPFile(pixelVec,"c:\BMP\4PixelBMP.txt", 2, 2);

Upvotes: 0

Views: 1015

Answers (1)

Christopher Oicles
Christopher Oicles

Reputation: 3107

I found this class lying around which you might be able to use as reference. It's a "surface" class with members for width, height and a std::vector of (32-bit) pixel colors.

Its Save member function saves the surface to a .bmp file. It could easily be transformed into into a stand-alone function taking a width, height and vector.

The Writer class wraps an ofstream object and supplies the file output interface used in Save.

The BIH struct should look familiar, and the rest of Surface's member functions provide a very minimal drawing API.

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>

struct Color {
    union {
        struct { unsigned char b,g,r,a; };
        unsigned char bytes[4];
        unsigned int ref;
    };
    Color(unsigned int ref=0) : ref(ref) {}
};

class Surface {
    int width, height;
    std::vector<Color> pixels;
public:
    void Fill(Color color) { std::fill(pixels.begin(), pixels.end(), color); }
    void HLine(int left, int y, int len, Color color) {
        if(y < 0 || y >= height || left >= width) { return; }
        if(left < 0) { len += left; left = 0; }
        if(left + len > width) { len = width - left; }
        int offset = y * width + left;
        std::fill(pixels.begin() + offset, pixels.begin() + offset + len, color); 
    }
    void RectFill(int x, int y, int w, int h, Color color) {
        for(int i=0; i<h; ++i) { HLine(x, y + i, w, color); }
    }
    Surface(int width, int height) : 
        width(width), 
        height(height),
        pixels(width*height, Color())
    {}
    template <typename I>       Color& operator () (const I& x, const I& y)       { return pixels[y*width+x]; }
    template <typename I> const Color& operator () (const I& x, const I& y) const { return pixels[y*width+x]; }

    class Writer {
        std::ofstream ofs;
    public:
        Writer(const char* filename) : ofs(filename, std::ios_base::out | std::ios_base::binary) {}
        void operator () (const void* pbuf, int size) { ofs.write(static_cast<const char*>(pbuf), size); }
        template <typename T> void operator () (const T& obj) { operator () (&obj, sizeof(obj)); }
    };

    struct BIH {
        unsigned int   sz;
        int            width, height;
        unsigned short planes;
        short          bits;
        unsigned int   compress, szimage;
        int            xppm, yppm;
        unsigned int   clrused, clrimp;
    };

    void Save(const char* filename) const {
        Writer w(filename);;
        w("BM", 2);
        BIH bih = {sizeof(bih)};
        bih.width = width;
        bih.height = -height;
        bih.planes = 1;
        bih.bits = 32;
        const unsigned int headersize = sizeof(bih) + 14;
        const int szbuf = int(sizeof(Color) * pixels.size());
        const unsigned int filesize = static_cast<unsigned int>(headersize + szbuf);
        w(filesize);
        const unsigned short z = 0;
        w(z);
        w(z);
        w(headersize);
        w(bih);
        w(pixels.data(), szbuf);
    }
};

int main() {
    Surface surf(600, 600);
    Color color;
    for(int j=0; j<12; ++j) {
        color.g = 0;
        for(int i=0; i<12; ++i) {
            color.b = 255 - (color.g + color.r) / 2;
            surf.RectFill(i*50, j*50, 50, 50, color);
            color.g += 21;
        }
        color.r += 21;
    }
    surf.Save("MyBmpFile.bmp");
}

Upvotes: 1

Related Questions