2

I want to create a bitmap file header without any library. this is data of my image:

width = 3px;
height = 10px;
bits_per_pixle = 24;

How can i generate 54 bytes data for it's header then write into a file? I have a lot of problems with this. I don't know How to write data into file in hex. i dont know how to write size of file in 4 bytes , etc.. Can anyone help me? Thanks.

#include <iostream>
#include <fstream>
#include <iomanip>

using namespace std;

int main()
{
    int w = 3;
    int h = 10;
    int bits_per_pixle = 24;
    ofstream des("file.bmp", ios::binary);

    return 0;
}
Hadi
  • 544
  • 1
  • 8
  • 28
  • Doing anything 'without any library' is difficult as much of C++ is in libraries (including ofstream for instance). Could you clarify which libraries you are and aren't allowed to use. For instance how about Windows libraries? – john May 21 '19 at 06:28
  • To write the data 'in hex' (really you mean 'as binary', 'in hex' doesn't make any sense) use the `ostream::write` method. – john May 21 '19 at 06:34
  • Really all you need to do is fill out your 54 byte data structure and then make one call to `ostream::write` to write your data to a file. – john May 21 '19 at 06:36
  • @john i am allowed to use ofstream etc... but I should not use a an extra library to work with bitmap files. – Hadi May 21 '19 at 06:46
  • This might be interesting: [SO: Create image from unsigned char buffer](https://stackoverflow.com/a/56054487/7478597). I did this for a question tagged [tag:C] but it surely works in C++ as well (with small modifications, may be). – Scheff's Cat May 21 '19 at 07:07

2 Answers2

0

Use this structure: BITMAPINFOHEADER

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
Simo Simov
  • 97
  • 5
0

I have written a version of the function for you. I tested it, it works successfully.

The function createRawBitmap24() creates a bitmap image without compression with 24 bits per pixel:

struct Color24
{
    uint8_t red;
    uint8_t green;
    uint8_t blue;
};
typedef std::vector<std::vector<Color24>> Image24;
bool check(const Image24 & img)
{
    // check the shape
    bool valid(!img.empty());
    if(valid)
    {
        size_t line_size(img[0].size());
        for(size_t i = 1; valid && (i < img.size()); ++i)
        {
            if(img.at(i).size() != line_size)
                valid = false;
        }
    }
    else
        valid = false;
    return valid;
}
bool createRawBitmap24(const std::string & file_path, const Image24 & img)
{
    bool success(false);

    if(check(img))
    {
        // ----- create the image in bmp format -----

        // find the padding size
        uint8_t padding_size(0);
        size_t n = img[0].size()*3;
        while(((n+padding_size) % 4) != 0)
            ++padding_size;

        // build the raw content
        const uint32_t ris = static_cast<uint32_t>(img.size()*(img[0].size()*3 + padding_size)); // raw image size
        uint8_t * raw_content = new uint8_t[ris];

        size_t cursor(0);
        for(int i = static_cast<int>(img.size()-1); i >= 0; --i) // read lines in reverse order
        {
            for(const Color24 & pixel : img.at(static_cast<size_t>(i)))
            {
                raw_content[cursor++] = pixel.blue;
                raw_content[cursor++] = pixel.green;
                raw_content[cursor++] = pixel.red;
            }
            for(size_t j = 0; j < padding_size; ++j)
            {
                raw_content[cursor++] = 0xFF;
            }
        }

        // build the header
        const uint32_t hs(54); // header size
        const uint32_t fs = (hs + ris); // file size
        const uint32_t width = static_cast<uint32_t>(img[0].size()); // image width (without padding)
        const uint32_t height = static_cast<uint32_t>(img.size()); // image height (without padding)
        const uint32_t reso(2835); // resolution, ~72 DPI

        const uint8_t header[hs] = {
            'B', 'M',
            static_cast<uint8_t>(fs & 0xFF), static_cast<uint8_t>((fs >> 8) & 0xFF), static_cast<uint8_t>((fs >> 16) & 0xFF), static_cast<uint8_t>((fs >> 24) & 0xFF),
            0, 0,
            0, 0,
            static_cast<uint8_t>(hs & 0xFF), static_cast<uint8_t>((hs >> 8) & 0xFF), static_cast<uint8_t>((hs >> 16) & 0xFF), static_cast<uint8_t>((hs >> 24) & 0xFF),
            40, 0, 0, 0,
            static_cast<uint8_t>(width & 0xFF), static_cast<uint8_t>((width >> 8) & 0xFF), static_cast<uint8_t>((width >> 16) & 0xFF), static_cast<uint8_t>((width >> 24) & 0xFF),
            static_cast<uint8_t>(height & 0xFF), static_cast<uint8_t>((height >> 8) & 0xFF), static_cast<uint8_t>((height >> 16) & 0xFF), static_cast<uint8_t>((height >> 24) & 0xFF),
            1, 0,
            24, 0,
            0, 0, 0, 0,
            static_cast<uint8_t>(ris & 0xFF), static_cast<uint8_t>((ris >> 8) & 0xFF), static_cast<uint8_t>((ris >> 16) & 0xFF), static_cast<uint8_t>((ris >> 24) & 0xFF),
            static_cast<uint8_t>(reso & 0xFF), static_cast<uint8_t>((reso >> 8) & 0xFF), static_cast<uint8_t>((reso >> 16) & 0xFF), static_cast<uint8_t>((reso >> 24) & 0xFF),
            static_cast<uint8_t>(reso & 0xFF), static_cast<uint8_t>((reso >> 8) & 0xFF), static_cast<uint8_t>((reso >> 16) & 0xFF), static_cast<uint8_t>((reso >> 24) & 0xFF),
            0, 0, 0, 0,
            0, 0, 0, 0
        };

        // ----- Write the content in the file -----
        std::ofstream out_s(file_path, std::ofstream::binary);
        if(out_s)
        {
            for(size_t i = 0; i < hs; ++i)
                out_s << header[i];
            for(size_t i = 0; i < ris; ++i)
                out_s << raw_content[i];

            out_s.close();
            success = true;
        }
        delete [] raw_content;
    }
    return success;
}

To use it, you just have to call the function createRawBitmap24() and give as arguments the file path and the Image24 to create.
As you can see, Image24 is a typedef of std::vector<std::vector<Color24>>.
Color24 is the structure I created to handle RGB colors.

The types I used (uint8_t, uint16_t, uint32_t, ...) are to make it more readable. When the bitmap specification said 1, 2 or 4 bytes are needed for a header field, I used the corresponding explicit type. But if these types are not defined in your platform, you can replace them by unsigned char, unsigned short and unsigned int (just check with sizeof() if the sizes are the same to 1, 2 and 4). But if you can include cstdint, it should be fine.

Of course, you can adapt the function for your needs.

I hope it will help you.

Fareanor
  • 5,900
  • 2
  • 11
  • 37