4

I am trying to figure out how to create a bitmap file in C++ VS. Currently I have taken in the file name and adding the ".bmp" extension to create the file. I want to know how I could change the pixels of the file by making it into different colors or patterns (ie. like a checkerboard) This is my function that I have and I believe that I have to send 3 different Bytes at a time in order to establish the color of the pixel.

void makeCheckerboardBMP(string fileName, int squaresize, int n) {
    ofstream ofs;
    ofs.open(fileName + ".bmp");
    writeHeader(ofs, n, n);

    for(int row = 0; row < n; row++) {
        for(int col = 0; col < n; col++) {

            if(col % 2 == 0) {
                ofs << 0;
                ofs << 0;
                ofs << 0;
            } else {
                ofs << 255;
                ofs << 255;
                ofs << 255;
            }
        }
    }
}

void writeHeader(ostream& out, int width, int height){
    if (width % 4 != 0) {
        cerr << "ERROR: There is a windows-imposed requirement on BMP that the width be a          
multiple of 4.\n";
        cerr << "Your width does not meet this requirement, hence this will fail.  You can fix     
this\n";
        cerr << "by increasing the width to a multiple of 4." << endl;
        exit(1);
    }

    BITMAPFILEHEADER tWBFH;
    tWBFH.bfType = 0x4d42;
    tWBFH.bfSize = 14 + 40 + (width*height*3);
    tWBFH.bfReserved1 = 0;
    tWBFH.bfReserved2 = 0;
    tWBFH.bfOffBits = 14 + 40;

    BITMAPINFOHEADER tW2BH;
    memset(&tW2BH,0,40);
    tW2BH.biSize = 40;
    tW2BH.biWidth = width;
    tW2BH.biHeight = height;
    tW2BH.biPlanes = 1;
    tW2BH.biBitCount = 24;
    tW2BH.biCompression = 0;

    out.write((char*)(&tWBFH),14);
    out.write((char*)(&tW2BH),40);
}
dragosht
  • 3,237
  • 2
  • 23
  • 32
user2557642
  • 45
  • 1
  • 1
  • 4
  • 1
    (1) Did you close the stream? (2) You can modify the size by modifying the header (it's a property in the header) – Willem Van Onsem Sep 16 '13 at 22:48
  • @CommuSoft When an object goes out of scope its destructor is invoked. `ofstream`s destructor calls `close`. So yes, the stream does get closed. – IInspectable Sep 16 '13 at 22:56
  • Yes I did close the stream – user2557642 Sep 16 '13 at 22:57
  • @IInspectable: Well in some programming languages you have to close the stream, and I wouldn't count on such methods. Since you don't know when the destructor will be called, it can take ages. And keeping a file open isn't a good idea since it can cause conflicts with other programs or cause the OS to kill the program when you have too much open files. :D – Willem Van Onsem Sep 16 '13 at 22:59
  • @CommuSoft The question is tagged [`c++`](http://stackoverflow.com/tags/c++/info); your reference to *"some programming languages"* is surprising. In the code above you **do** see when the destructor runs. `ofs` is locally scoped; the lifetime is easy to guess. As for the *"too much open files"* argument: Get a real OS. – IInspectable Sep 16 '13 at 23:23
  • I disagree. First of all, if you know 30 languages you start generalizing. It's also part of a healthy coding style not to use that much implicit actions. Second: most OS (W7, Linux,...) will kill a program with too much open files. I think Windows uses a default threshold of `100` and in Linux you can modify it at free will... Of course one can claim that `100` is quite large, but if you run programs on servers for ages, such thresholds can be reached. – Willem Van Onsem Sep 16 '13 at 23:32
  • @CommuSoft If you need to generalize you don't know any of the 30 languages well enough. This question is specific to C++. Your question was answered in the code even before you asked. I pointed out that the answer is there. And you still argue that because some other language might interpret the construct differently is reason enough to not trust that C++ does precisely what I described? You're in for a fun future with your compilers. – IInspectable Sep 17 '13 at 00:22
  • Well I'm writing compilers myself (with a focus on garbage collection and thus by extent finalizers) :)... And with all I have seen the past 10 years languages are moving towards convergence... I don't argue C++ cannot be trusted, but everybody can write unreadable code. C++ was design to build efficient code. Todays programming languages like Haskell put more focus on development time. I disagree you don't know any of the languages well enough. Nearly all concepts are recycled everywhere, do you think Mixins just came out of the blue? – Willem Van Onsem Sep 17 '13 at 00:52
  • A half year ago, I made a glossary describing general programming language concepts. It can be found here: http://www.scribd.com/doc/122614039/Comparative-Programming-Languages-Glossary It is based on Programming Language Theory (http://en.wikipedia.org/wiki/Programming_language_theory). Furthermore the ACM devoted a Special Interest Group to program languages called SIGPLAN, therefore I think one can state there is general knowledge about programming languages. – Willem Van Onsem Sep 17 '13 at 01:07
  • @CommuSoft Great. This question is tagged C++. C++ implements deterministic garbage collection. – IInspectable Sep 17 '13 at 07:27

3 Answers3

6

These are the two functions I am using for my code (one greyscale, one RGB saving).

Might give you a hint whats going wrong. Note: they are done to work, not to be efficient.

void SaveBitmapToFile( BYTE* pBitmapBits, LONG lWidth, LONG lHeight,WORD wBitsPerPixel, LPCTSTR lpszFileName )
{
    RGBQUAD palette[256];
    for(int i = 0; i < 256; ++i)
    {
        palette[i].rgbBlue = (byte)i;
        palette[i].rgbGreen = (byte)i;
        palette[i].rgbRed = (byte)i;
    }

    BITMAPINFOHEADER bmpInfoHeader = {0};
    // Set the size
    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    // Bit count
    bmpInfoHeader.biBitCount = wBitsPerPixel;
    // Use all colors
    bmpInfoHeader.biClrImportant = 0;
    // Use as many colors according to bits per pixel
    bmpInfoHeader.biClrUsed = 0;
    // Store as un Compressed
    bmpInfoHeader.biCompression = BI_RGB;
    // Set the height in pixels
    bmpInfoHeader.biHeight = lHeight;
    // Width of the Image in pixels
    bmpInfoHeader.biWidth = lWidth;
    // Default number of planes
    bmpInfoHeader.biPlanes = 1;
    // Calculate the image size in bytes
    bmpInfoHeader.biSizeImage = lWidth* lHeight * (wBitsPerPixel/8);

    BITMAPFILEHEADER bfh = {0};
    // This value should be values of BM letters i.e 0x4D42
    // 0x4D = M 0×42 = B storing in reverse order to match with endian

    bfh.bfType = 'B'+('M' << 8);
    // <<8 used to shift ‘M’ to end

    // Offset to the RGBQUAD
    bfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + sizeof(RGBQUAD) * 256;
    // Total size of image including size of headers
    bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage;
    // Create the file in disk to write
    HANDLE hFile = CreateFile( lpszFileName,GENERIC_WRITE, 0,NULL,
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);

    if( !hFile ) // return if error opening file
    {
        return;
    }

    DWORD dwWritten = 0;
    // Write the File header
    WriteFile( hFile, &bfh, sizeof(bfh), &dwWritten , NULL );
    // Write the bitmap info header
    WriteFile( hFile, &bmpInfoHeader, sizeof(bmpInfoHeader), &dwWritten, NULL );
    // Write the palette
    WriteFile( hFile, &palette[0], sizeof(RGBQUAD) * 256, &dwWritten, NULL );
    // Write the RGB Data
    if(lWidth%4 == 0)
    {
        WriteFile( hFile, pBitmapBits, bmpInfoHeader.biSizeImage, &dwWritten, NULL );
    }
    else
    {
        char* empty = new char[ 4 - lWidth % 4];
        for(int i = 0; i < lHeight; ++i)
        {
            WriteFile( hFile, &pBitmapBits[i * lWidth], lWidth, &dwWritten, NULL );
            WriteFile( hFile, empty,  4 - lWidth % 4, &dwWritten, NULL );
        }
    }
    // Close the file handle
    CloseHandle( hFile );
}

void SaveBitmapToFileColor( BYTE* pBitmapBits, LONG lWidth, LONG lHeight,WORD wBitsPerPixel, LPCTSTR lpszFileName )
{

    BITMAPINFOHEADER bmpInfoHeader = {0};
    // Set the size
    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    // Bit count
    bmpInfoHeader.biBitCount = wBitsPerPixel;
    // Use all colors
    bmpInfoHeader.biClrImportant = 0;
    // Use as many colors according to bits per pixel
    bmpInfoHeader.biClrUsed = 0;
    // Store as un Compressed
    bmpInfoHeader.biCompression = BI_RGB;
    // Set the height in pixels
    bmpInfoHeader.biHeight = lHeight;
    // Width of the Image in pixels
    bmpInfoHeader.biWidth = lWidth;
    // Default number of planes
    bmpInfoHeader.biPlanes = 1;
    // Calculate the image size in bytes
    bmpInfoHeader.biSizeImage = lWidth* lHeight * (wBitsPerPixel/8);

    BITMAPFILEHEADER bfh = {0};
    // This value should be values of BM letters i.e 0x4D42
    // 0x4D = M 0×42 = B storing in reverse order to match with endian

    bfh.bfType = 'B'+('M' << 8);
    // <<8 used to shift ‘M’ to end

    // Offset to the RGBQUAD
    bfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
    // Total size of image including size of headers
    bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage;
    // Create the file in disk to write
    HANDLE hFile = CreateFile( lpszFileName,GENERIC_WRITE, 0,NULL,
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);

    if( !hFile ) // return if error opening file
    {
        return;
    }

    DWORD dwWritten = 0;
    // Write the File header
    WriteFile( hFile, &bfh, sizeof(bfh), &dwWritten , NULL );
    // Write the bitmap info header
    WriteFile( hFile, &bmpInfoHeader, sizeof(bmpInfoHeader), &dwWritten, NULL );
    // Write the palette
    //WriteFile( hFile, &palette[0], sizeof(RGBQUAD) * 256, &dwWritten, NULL );
    // Write the RGB Data
    if(lWidth%4 == 0)
    {
        WriteFile( hFile, pBitmapBits, bmpInfoHeader.biSizeImage, &dwWritten, NULL );
    }
    else
    {
        char* empty = new char[ 4 - lWidth % 4];
        for(int i = 0; i < lHeight; ++i)
        {
            WriteFile( hFile, &pBitmapBits[i * lWidth], lWidth, &dwWritten, NULL );
            WriteFile( hFile, empty,  4 - lWidth % 4, &dwWritten, NULL );
        }
    }
    // Close the file handle
    CloseHandle( hFile );
}
SinisterMJ
  • 3,425
  • 2
  • 33
  • 53
  • Your implementations do not work reliably. Your calculation of `biSizeImage` does not take scanline padding into account. For uncompressed RGB images this is best set to 0 anyway. Your calculation of `bfOffBits` fails to account for alignment requirements. Use `offsetof(BITMAPINFO, bmiColors[256])` instead. You are dumping genuinely random bytes, too (`empty`). There are more issues and it shows once again: Don't roll your own. Use the [Windows Imaging Component](http://msdn.microsoft.com/en-us/library/windows/desktop/ee719902.aspx) - it's there for a reason. – IInspectable Jan 30 '14 at 11:37
4

Given your writeHeader is properly implemented this is almost correct. You need to fix 2 issues though:

  1. You are writing one int per color channel. This should be one byte instead. You need to cast the literals to unsigned char.
  2. Scanlines in bitmaps need to be DWORD-aligned. After your inner loop over col you need to write additional bytes to account for this, unless the size in bytes of the row is a multiple of four.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • Thanks a lot for the advice!!! Changing the int to an unsigned char works. Now the next problem that I have is actually the size of the checkerboard itself. It is actually very small compared to the bitmap image that I wanted to actually generate. – user2557642 Sep 16 '13 at 23:05
1

You need to force the output to be written in binary format, not text, this is chosen when you open your file/create your stream and to output all the values as bytes, not integers, this can be done in a number of ways possibly the easiest being write chr(0) or chr(255) - you also need to start your file with a header section - there are a number of formats that make this too long to go into in an answer here - some of them are down to preference as much as anything. There is a good summary in Wikipedia.

Basically you have to inform the receiving applications which format you are using, the number of rows, columns and how the colours are stored.

Steve Barnes
  • 27,618
  • 6
  • 63
  • 73