I l @ ve RuBoard |
In 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 0000 2 , or 0x80. This occurs when (x%8) == 0 . The next bit is 0100 0000 2 , 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 to X_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 |