A bitmap is a sequence of bits used to represent pixels on a display. A picture is said to be digitized or rasterized (or even pixelated) when it has been reduced to an array of pixels or bits. There are various forms of bitmap representation. Bitmap compression is used to reduce storage on bitmaps, giving rise to various storage mechanisms for bitmaps (e.g. .gif format). Windows has several uncompressed formats for bitmaps. Another means of storing pictorial information is metafiles - which is a vector format for pictures that will be discussed in later topics. Each format has its place. Bitmaps tend to be used for storing photographic images; whereas, metafiles are good for generated images such as architectural drawings. Bitmaps tend to require large amounts of storage and have color and device dependencies. Bitmaps may be scaled; however, interpolation or dropping of rows and columns may degrade the image. Vector graphics, on the other hand, is perfect for scaling of images without any loss of precision and it is very efficient in memory usage (but unsuitable for photographs).
Each pixel in a bitmap image corresponds to one or more bits in a bitmap. The number of colors in a bitmap is 2 to the power of the number of bits/pixel. For example, if a bitmap has 4 bits/pixel it can represent 24 == 16 colors. If a bitmap can represent 256 colors it has 8 bits/pixel and "full color" bitmaps have 24 bits/pixel.
Windows supports a type of bitmap called a device independent bitmap (DIB). Device independent bitmaps include a separate color table and can be displayed on any raster output device. Device independent bitmaps are converted to the nearest colors that are available on a given device (e.g. display). Standard types of bitmaps may be referred to as device dependent bitmaps (DDB). A device independent bitmap is not a windows graphics object. Windows cannot store a device independent bitmap. Maintaining a device independent bitmap is the responsibility of the application program. When a device independent bitmap is passed to the graphics subsystem, it is converted to a device dependent bitmap.
Device independent bitmaps may be stored in a file which usually has the extension .bmp or .dib. A device independent bitmap file begins with the data structure bitmap_file. This is followed by an instance of the class bitmap_information. All members of bitmap_information following the member bit_count may be set to zero or may be absent. Thus, the smallest bitmap information structure is 16 bytes. If the member used is zero and the number of bits/pixel is 1, 4 or 8, the bitmap_information structure is followed by a color table. The color table consists of two or more red_green_blue structures. The number of red_green_blue instances is usually determined by the member bit_count; where two structures are required for 1 color bit, 16 color structures are required for 4 color bits and 256 structures are required for 8 color bits. If the member used is non-zero, then it contains the number of color structures required.
Following the color table lies an array of bits that define the bitmap. These may be indices into the color table or actual color information depending upon the number of bits per pixel. The first row of bits corresponds to the bottom row of pixels in the bitmap (bottom-up representation). Each row begins with the leftmost pixels. Each pixel corresponds to 1, 4, 8 or 24 bits.
A monochromatic bitmap has 1 color bit per pixel. The first pixel in each row is represented by the most significant bit of the first byte in each row. If the bit is clear, the color of the pixel may be obtained from the first red_green_blue structure; otherwise, it is obtained from the second red_green_blue structure. A 16 color bitmap has 4 bits/pixel and the first pixel in each row is represented by the most significant nibble in each row (a nibble being 4 bits). The color of the pixels is obtained by using the 4 bits as an index into the color table (which in this case contains 16 entries). For a 256 color bitmap, each byte corresponds to 1 pixel. The value of that byte acts as an index into the color table which has 256 entries. If the bitmap has 24 color bits per pixel, no color table is present and each set of three bytes is a red, green, blue value for the pixel. In all cases, the rows of bitmap data are a multiple of 4 bytes and may be padded to ensure that this is the case.
When a bitmap is read into memory, it is set to contain everything but the bitmap_file header at the beginning. When in this format, the bitmap is said to be in 'packed device independent bitmap' format (packed DIB). In this case, the bitmap begins with a bitmap_information header, followed by the color table (if any), followed by the bitmap's bits.
A packed device independent bitmap may be:
The operating system has two functions that display a device independent bitmap from its packed form. The most general is stretch_device_independent_bits, which allows the bitmap to be stretched or compressed and which uses raster operations in the process. A simpler function that displays the bitmap at its given size without any raster operations is set_device_independent_bits_to_device.
As previously noted, the function create_device_dependent_bitmap converts a device independent bitmap to a device dependent bitmap object.
The operating system provides quite a few functions for creating device dependent bitmaps. These are shown in the table below.
load_bitmap | Loads a bitmap from an attached resource file or loads a system bitmap. |
load_image | Loads a bitmap from a resource file or from a bitmap file. |
create_compatible_bitmap | creates a bitmap compatible with a given device context. |
create_device_dependent_bitmap | creates a device dependent bitmap from a device independent bitmap. |
create_device_independent_bitmap_section | creates a device independent bitmap that can be written to directly. |
create_bitmap | creates a bitmap of the given dimensions, color planes and bits/pixel. |
create_bitmap_indirect | creates a bitmap (optimized for monochromatic bitmaps). |
copy_image | Copies a bitmap, scaling to the specified dimensions. |
The function create_device_dependent_bitmap was mentioned previously and it creates a device dependent bitmap given a device independent bitmap. The function create_compatible_bitmap produces a bitmap that is compatible with the color format of the device associated with a specified device context. The function create_bitmap requires the specification of the number of color planes and the number of bits/pixel. The color data may also be supplied, but if left null, an uninitialized bitmap is created.
Monochromatic bitmaps are relatively simple and therefore present an easy way to visualize bitmaps. For example, consider the bitmap shown below, where each box represents a pixel.
This diagram may be written down as a series of bits or hexadecimal digits - as follows (the hexadecimal has been padded to be a multiple of 16).
0 1 0 1 0 0 0 1 0 1 1 1 0 1 1 1 0 0 0 1 = 51 77 10 00 0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00 0 0 0 1 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 13 77 50 00 0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 = 51 11 10 00
The width (in pixels) is 20 whereas the height is 5 scan lines.
The bits may be stored as follows.
byte bit_array = { 0x51, 0x77, 0x10, 0x00, 0x57, 0x77, 0x50, 0x00, 0x13, 0x77, 0x50, 0x00, 0x57, 0x77, 0x50, 0x00, 0x51, 0x11, 0x10, 0x00 };
A bitmap_definition structure may then be set as follows.
bitmap_definition bitmap_define = {0, 20, 5, 4, 1, 1, bit_array};
A bitmap may then be created as follows
handle bitmap_handle = create_bitmap_indirect(&bitmap_define);
Yet another approach to creating a bitmap is shown below.
handle bitmap_handle = create_bitmap(20,5,1,1,(void*)bit_array);
Two functions that allow for the display of a device independent bitmap on an output device are:
However, there is no function that allows for the display of a device dependent bitmap to a device context associated with a display. The function
select_object(device_context,bitmap_handle);
may only be applied to a memory device context. A memory device context may be created through the function create_memory_device_context. A memory device context has a display surface that exists only in memory (that is, it cannot be directly displayed). When a memory device context is created, all of its attributes are set to their normal default values and it contains exactly 1 monochromatic pixel. The size of the memory device context is changed when a bitmap is selected into the device context. after this, the display surface of the memory device context matches that of the bitmap that was selected. With the default window and viewport origins, the logical point (0,0) of the device context corresponds to the upper-left corner of the selected bitmap. If the selected bitmap already contains a picture; that picture is now part of the display surface of the device context. Any drawing operations made to the memory device context are reflected in the associated bitmap bits. In short, the bitmap is the display surface of the memory device context. Once a memory device context containing a bitmap has been created, bit block transfers between device contexts may be used to render the associated bitmap to a normal display device context.
To summarize the previous section, a bitmap may not be directly selected into a non-memory device context. Instead, a memory device context must be created, the bitmap selected into the memory device context and then a bit block transfer (bitblt) may be used to copy the memory device context to a normal device context. There are three types of bit block transfers:
The pattern block transfer involves only a single device context, and in this sense, it is simpler (and less powerful) than the other two functions. The pattern block transfer copies the current brush pattern to a specified rectangle using raster operations.
Windows has 256 raster operation codes. A 32 bit raster operation code consists of a high unsigned char containing a number between 0 and 255 with a low unsigned char that assists the device driver in constructing the logical operation. Fifteen of the raster operation codes have names (see enum raster_operation).
Because a pattern block transfer uses only a destination device context and a pattern (that is, no source device context), only a subset of 16 of the possible 256 raster operation codes may be applied. These are listed in the table below.
Pattern (P) | 1 | 1 | 0 | 0 | operation | Raster Code | name |
Destination (d) | 1 | 1 | 0 | 0 | |||
0 | 0 | 0 | 0 | 0 | 0x000042 | raster_operation::Black | |
0 | 0 | 0 | 1 | ~(P | d) | 0x0500a9 | ||
0 | 0 | 1 | 0 | ~P & d | 0x0a0329 | ||
0 | 0 | 1 | 1 | ~P | 0x0f0001 | ||
0 | 1 | 0 | 0 | P & ~d | 0x500325 | ||
0 | 1 | 0 | 1 | ~d | 0x550009 | raster_operation::DestinationInvert | |
0 | 1 | 1 | 0 | P ^ d | 0x5a0049 | raster_operation::PatternInvert | |
0 | 1 | 1 | 1 | ~(P & d) | 0x5f00e9 | ||
1 | 0 | 0 | 0 | P & d | 0xa000c9 | ||
1 | 0 | 0 | 1 | ~(P ^ d) | 0xa50065 | ||
1 | 0 | 1 | 0 | d | 0xaa0029 | ||
1 | 0 | 1 | 1 | ~P | d | 0xaf0229 | ||
1 | 1 | 0 | 0 | P | 0xf00021 | raster_operation::Patterncopy | |
1 | 1 | 0 | 1 | P | ~d | 0xf50225 | ||
1 | 1 | 1 | 0 | P | d | 0xfa0089 | ||
1 | 1 | 1 | 1 | 1 | 0xff0062 | raster_operation::White |
Some of the more common uses of a patterned block transfers will now be considered. To draw a black rectangle, the following code may be used.
pattern_bit_block_transfer(h,x_Destination,y_Destination,x_Width,y_height,raster_operation::black);
To draw a white rectangle, the following code may be used.
pattern_bit_block_transfer(h,x_Destination,y_Destination,x_width,y_height,raster_operation::white);
To color invert a rectangle, the following code may be used.
pattern_bit_block_transfer(h,x_destination,y_destination,x_width,y_height,raster_operation::destination_invert);
When an application calls the function fill_rectangle to fill a rectangle with a given brush, the operating system performs patterned block transfer to achieve the result. Inverting a rectangle also is achieved through a patterned block transfer.
A standard bit block transfer is achieved through the function:
bit_block_transfer
whereas a rectangle of bits may be stretched or contracted via the function:
stretch_bit_block_transfer.
These functions combine the destination rectangle with the source rectangle and the destination brush. Any of the available 256 raster operations may be used. See enum raster_operation for descriptions of the 15 raster operations that have names. The first of these functions is the standard way to copy a bitmap to a display device context. As previously mentioned, it is not possible to copy a bitmap directly to a device context. Instead, a memory device context is created, the bitmap is selected into that device context and a bit block transfer is used to copy the bitmap to the target device context - where it is displayed. Bit block transfers are quick and the concept is a powerful and general one. Note that bit block transfers perform color conversions when required. Mapping mode coordinate conversions are also performed for the width and height of the rectangles - which are expressed in logical coordinates for both device contexts.