Saul
Saul

Reputation: 311

Changing monochrome pattern for bitmap file using current loop

I am trying to change the pattern of the current bitmap file but I am having trouble changing my nested for loop to do this. I am trying to get a bitmap file with 8 horizontal bars, 32 pixels in height, alternating black and white. What I currently get now are 64 vertical bars alternating black and white. The bitmap image dimensions are 256 pixels by 256 pixels.

I have messed around with the nested for loop in my code that is responsible for storing the color white (0x0f) in my multidimensional bits array. I have noticed that if I change the white to black (0x0f to 0x00) the entire bitmap file turns black. Below is the part of the code I am focusing on to output the pattern, under that I have the entire code.

// Build monochrome array of bits in image
    for (int i = 0; i < IMAGE_SIZE; i++) {
        for (int j = 0; j < IMAGE_SIZE / 8; j++) {
            bits[i][j] = 0x0f;
        }
    }
#include <iostream>
#include <fstream>
#include "windows.h"
using namespace std;
// The following defines the size of the square image in pixels.
#define IMAGE_SIZE 256
int main(int argc, char* argv[])
{

    BITMAPFILEHEADER bmfh;

    BITMAPINFOHEADER bmih;

    char colorTable[8] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff };

    // The following defines the array which holds the image.  The row length 
    // (number of columns) is the height divided by 8, since there are 8 bits
    // in a byte.
    char bits[IMAGE_SIZE][IMAGE_SIZE / 8];

    // Define and open the output file. 
    ofstream bmpOut("foo.bmp", ios::out + ios::binary);
    if (!bmpOut) {
        cout << "...could not open file, ending.";
        return -1;
    }
    // Initialize the bit map file header with static values.
    bmfh.bfType = 0x4d42;
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmih) + sizeof(colorTable);
    bmfh.bfSize = bmfh.bfOffBits + sizeof(bits);

    // Initialize the bit map information header with static values.
    bmih.biSize = 40;
    bmih.biWidth = IMAGE_SIZE;
    bmih.biHeight = IMAGE_SIZE;
    bmih.biPlanes = 1;
    bmih.biBitCount = 1;
    bmih.biCompression = 0;
    bmih.biSizeImage = 0;
    bmih.biXPelsPerMeter = 2835;  // magic number, see Wikipedia entry
    bmih.biYPelsPerMeter = 2835;
    bmih.biClrUsed = 0;
    bmih.biClrImportant = 0;

    // Build monochrome array of bits in image
    for (int i = 0; i < IMAGE_SIZE; i++) {
        for (int j = 0; j < IMAGE_SIZE / 8; j++) {
            bits[i][j] = 0x0f;
        }
    }

    // Write out the bit map.  
    char* workPtr;
    workPtr = (char*)&bmfh;
    bmpOut.write(workPtr, 14);
    workPtr = (char*)&bmih;
    bmpOut.write(workPtr, 40);
    workPtr = &colorTable[0];
    bmpOut.write(workPtr, 8);
    workPtr = &bits[0][0];
    bmpOut.write(workPtr, IMAGE_SIZE*IMAGE_SIZE / 8);
    bmpOut.close();

    // showing result
    system("mspaint foo.bmp");

    // Done.
    return 0;
}

Upvotes: 0

Views: 250

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596672

You are not populating your array of bits correctly.

You are creating a 1-bit (monochrome) bottom-up bitmap. Every individual bit in your array represents a different pixel, where a 0 bit refers to the first color and a 1 bit refers to the second color in your color table. And the first row in the bitmap image is the last row in the array, and the last row in the bitmap image is the first row in the array.

0x0F hex is 00001111 binary. You are setting every 8-bit char in your array to 0x0f, so you are creating an alternating pattern of 4 0000 bits followed by 4 1111 bits for every row, eg:

00001111 00001111 00001111 ... (for 29 more bytes)
00001111 00001111 00001111 ... (for 29 more bytes)
00001111 00001111 00001111 ... (for 29 more bytes)
... (for 253 more rows)

For a 256x256 bitmap, that produces 64 alternating vertical bars that are each 4 pixels wide.

To get the effect you want - 8 alternating horizontal bars that are each 32 pixels high - you need to set every bit in a given row to either all 0s or all 1s, and then you need to alternate that pattern in groups of 32 complete rows, eg:

00000000 00000000 00000000 ... (for 29 more bytes)
00000000 00000000 00000000 ... (for 29 more bytes)
00000000 00000000 00000000 ... (for 29 more bytes)
... (for 29 more rows)

11111111 11111111 11111111 ... (for 29 more bytes)
11111111 11111111 11111111 ... (for 29 more bytes)
11111111 11111111 11111111 ... (for 29 more bytes)
... (for 29 more rows)

... (repeat the above 4 more times)

Try something more like this instead:

// Build monochrome array of bits in image
bool isWhite = false;
for (int i = 0; i < IMAGE_SIZE; ) {
    char ch = isWhite ? 0xFF : 0x00;
    int row = (IMAGE_SIZE - 1) - i; // use row = i for a top-down bitmap ...

    for (int col = 0; col < (IMAGE_SIZE / 8); ++col) {
        bits[row][col] = ch;
    }

    // alternatively to the above loop:
    // memset(bits[row], isWhite ? 0xFF : 0x00, IMAGE_SIZE / 8);

    if ((++i % 32) == 0) isWhite = !isWhite;
}

Or:

// Build monochrome array of bits in image
bool isWhite = true;
for (int i = 0; i < IMAGE_SIZE; ++i) {
    if ((i % 32) == 0) isWhite = !isWhite;
    int row = (IMAGE_SIZE - 1) - i; // use row = i for a top-down bitmap ...

    char ch = isWhite ? 0xFF : 0x00;
    for (int col = 0; col < (IMAGE_SIZE / 8); ++col) {
        bits[row][col] = ch;
    }

    // alternatively to the above loop:
    // memset(bits[row], isWhite ? 0xFF : 0x00, IMAGE_SIZE / 8);
}

Or:

// Build monochrome array of bits in image
for (int i = 0; i < IMAGE_SIZE; ++i) {
    char ch = ((i % 64) < 32) ? 0x00 : 0xFF;
    int row = (IMAGE_SIZE - 1) - i; // use row = i for a top-down bitmap ...

    for (int col = 0; col < IMAGE_SIZE / 8; ++col) {
        bits[row][col] = ch;
    }

    // alternatively to the above loop:
    // memset(bits[row], ((i % 64) < 32) ? 0x00 : 0xFF, IMAGE_SIZE / 8);
}

That being said, I would suggest a few additional tweaks to the rest of your code:

#include <iostream>
#include <fstream>
#include <windows.h>
//#include <string.h> // if using memset() above...

// The following defines the size of the square image in pixels.
#define IMAGE_SIZE 256

// The following defines the size of each row in bytes.
#define BYTES_PER_ROW (IMAGE_SIZE / sizeof(BYTE))

int main()
{
    // Define and open the output file. 
    std::ofstream bmpOut("foo.bmp", std::ios::binary);
    if (!bmpOut)
    {
        std::cerr << "could not open file, ending.";
        return -1;
    }

    BITMAPFILEHEADER bmfh;
    BITMAPINFOHEADER bmih;
    RGBQUAD colorTable[2] = { {0x00,0x00,0x00,0x00}, {0xFF,0xFF,0xFF,0x00} };

    // The following defines the array which holds the image bits.  The row length 
    // (number of columns) is the height divided by 8, since there are 8 bits
    // in a byte.
    BYTE bits[IMAGE_SIZE][BYTES_PER_ROW];

    // Initialize the bitmap file header with static values.
    bmfh.bfType = 0x4d42;
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmih) + sizeof(colorTable);
    bmfh.bfSize = bmfh.bfOffBits + sizeof(bits);

    // Initialize the bitmap information header with static values.
    bmih.biSize = sizeof(bmih);
    bmih.biWidth = IMAGE_SIZE;
    bmih.biHeight = IMAGE_SIZE; // positive for bottom-up, negative for top-down
    bmih.biPlanes = 1;
    bmih.biBitCount = 1;
    bmih.biCompression = BI_RGB;
    bmih.biSizeImage = 0;
    bmih.biXPelsPerMeter = 2835;  // magic number, see Wikipedia entry
    bmih.biYPelsPerMeter = 2835;
    bmih.biClrUsed = 0;
    bmih.biClrImportant = 0;

    // Build monochrome array of bits in image, see above...

    // Write out the bitmap.  
    bmpOut.write(reinterpret_cast<char*>(&bmfh), sizeof(bmfh));
    bmpOut.write(reinterpret_cast<char*>(&bmih), sizeof(bmih));
    bmpOut.write(reinterpret_cast<char*>(&colorTable), sizeof(colorTable));
    bmpOut.write(reinterpret_cast<char*>(&bits), sizeof(bits));

    if (!bmpOut)
    {
        std::cerr << "could not write file, ending.";
        return -1;
    }

    bmpOut.close();

    // showing result
    ShellExecuteA(NULL, NULL, "foo.bmp", NULL, NULL, SW_SHOW);

    // Done.
    return 0;
}

Upvotes: 0

Related Questions