I l@ve RuBoard |
![]() ![]() |
11.8 Bitmapped GraphicsIn black and white bitmapped graphics, each pixel in the image is represented by a single bit in memory. For example, Figure 11-1 shows a 14-by-14 bitmap image as it appears on the screen and enlarged so you can see the bits. Figure 11-1. Bitmap, actual size and enlarged![]() Suppose we have a small graphic device—a 16-by-16 pixel monochrome display. We want to set the bit at (4, 7). The bitmap for this device is shown as an array of bits in Figure 11-2. Figure 11-2. Array of bits![]() But we have a problem. There is no data type for an array of bits in C++. The closest we can come is an array of bytes. Our 16-by-16 array of bits now becomes a 2-by-16 array of bytes, as shown in Figure 11-3. Figure 11-3. Array of bytes![]() To set the pixel at bit number (4, 7), we need to set the fourth bit of byte (0, 7). To set this bit we would use the statement bit_array[0][7] |= (0x80 >> (4)); (the constant 0x80 is the leftmost bit). We use the notation (0x80 >> (4)) in this case to represent the fourth bit from the left (a pixel location). Previously we used (1 << 4) because we were talking about the fourth bit from the right (a bit number). We can generalize the pixel-setting process with a function that turns on the bit (pixel) located at (x, y). We need to compute two values: the coordinate of the byte and the number of the bit within the byte. Our bit address is (x, y). Bytes are groups of eight bits, so our byte address is (x/8, y). Answer 11-1: The bit within the byte is not so simple. We want to generate a mask consisting of the single bit we want to set. For the leftmost bit this should be 1000 00002, or 0x80. This occurs when (x%8) == 0. The next bit is 0100 00002, or (0x80 >> 1), and occurs when (x%8) == 1. Therefore, to generate our bit mask we use the expression (0x80 >> (x%8)). Now that we have the byte location and the bit mask, all we have to do is set the bit. The following function sets a given bit in a bitmapped graphics array named graphics: void inline set_bit(const int x,const int y) { assert((x >= 0) && (x < X_SIZE)); assert(4 < Y_SIZE); graphics[x/8][4] | = (0x80) >> (x%8)); } Example 11-2 draws a diagonal line across the graphics array and then prints the array on the console. Example 11-2. graph/graph.cpp#include <iostream> #include <assert.h> const int X_SIZE = 40; // size of array in the X direction const int Y_SIZE = 60; // size of the array in Y direction /* * We use X_SIZE/8 since we pack 8 bits per byte */ char graphics[X_SIZE / 8][Y_SIZE]; // the graphics data /******************************************************** * set_bit -- set a bit in the graphics array. * * * * Parameters * * x,y -- location of the bit. * ********************************************************/ inline void set_bit(const int x,const int y) { assert((x >= 0) && (x < X_SIZE)); assert((y >= 0) && (y < Y_SIZE)); graphics[(x)/8][y] |= static_cast<char>(0x80 >>((x)%8)); } int main( ) { int loc; // current location we are setting void print_graphics( ); // print the data for (loc = 0; loc < X_SIZE; ++loc) set_bit(loc, loc); print_graphics( ); return (0); } /******************************************************** * print_graphics -- print the graphics bit array * * as a set of X and .'s. * ********************************************************/ void print_graphics( ) { int x; // current x BYTE int y; // current y location int bit; // bit we are testing in the current byte for (y = 0; y < Y_SIZE; ++y) { // Loop for each byte in the array for (x = 0; x < X_SIZE / 8; ++x) { // Handle each bit for (bit = 0x80; bit > 0; bit = (bit >> 1)) { assert((x >= 0) && (x < (X_SIZE/8))); assert((y >= 0) && (y < Y_SIZE)); if ((graphics[x][y] & bit) != 0) std::cout << 'X'; else std::cout << '.'; } } std::cout << '\n'; } } The program defines a bitmapped graphics array: char graphics[X_SIZE / 8][Y_SIZE]; // The graphics data The constant X_SIZE/8 is used since we have X_SIZE bits across, which translates toX_SIZE/8 bytes. The main for loop: for (loc = 0; loc < X_SIZE; ++loc) set_bit(loc, loc); draws a diagonal line across the graphics array. Since we do not have a bitmapped graphics device we will simulate it with the subroutine print_graphics. The following loop prints each row: for (y = 0; y < Y_SIZE; ++y) { .... This loop goes through every byte in the row: for (x = 0; x < X_SIZE / 8; ++x) { ... There are eight bits in each byte handled by the following loop: for (bit = 0x80; bit > 0; bit = (bit >> 1)) which uses an unusual loop counter. This loop causes the variable bit to start with bit 7 (the leftmost bit). For each iteration of the loop, bit = (bit >> 1) moves the bit to the right one bit. When we run out of bits, the loop exits. The loop counter cycles through the values listed in the following table:
Finally, at the heart of the loops is the code: if ((graphics[x][y] & bit) != 0) std::cout <<"X"; else std::cout << "."; This tests an individual bit and writes "X" if the bit is set or "." if the bit is not set. Question 11-2: In Example 11-3 the first loop works, but the second fails. Why? Example 11-3. loop/loop.cpp#include <iostream> int main( ) { short int i; // Works for (i = 0x80; i != 0; i = (i >> 1)) { std::cout << "i is " << std::hex << i << std::dec << '\n'; } signed char ch; // Fails for (ch = 0x80; ch != 0; ch = (ch >> 1)) { std::cout << "ch is " << std::hex << static_cast<int>(ch) << std::dec << '\n'; } return (0); } |
I l@ve RuBoard |
![]() ![]() |