Bitmaps


Bitmaps

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).

Color Information in Bitmaps

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.

Device Independent Bitmaps (DIB)

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 BitmapFile. This is followed by an instance of the class BitmapInformation. All members of BitmapInformation following the member BitCount 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 BitmapInformation structure is followed by a color table. The color table consists of two or more RedGreenBlue structures. The number of RedGreenBlue instances is usually determined by the member BitCount, 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 RedGreenBlue structure; otherwise, it is obtained from the second RedGreenBlue 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.

Packed Device Independent Bitmaps

When a bitmap is read into memory, it is set to contain everything but the BitmapFile 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 BitmapInformation header, followed by the color table (if any), followed by the bitmap's bits.

A packed device independent bitmap may be:

Displaying Device Independent Bitmaps

The operating system has two functions that display a device independent bitmap from its packed form. The most general is StretchDeviceIndependentBits, 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 SetDeviceIndependentBitsToDevice.

Converting Device Independent Bitmaps to Bitmap Objects

As previously noted, the function CreateDeviceDependentBitmap converts a device independent bitmap to a device dependent Bitmap object.

Device Dependent Bitmap Objects

The operating system provides quite a few functions for creating device dependent bitmaps. These are shown in the table below.

LoadBitmap Loads a bitmap from an attached resource file or loads a system bitmap.
LoadImage Loads a bitmap from a resource file or from a bitmap file.
CreateBitmap - Compatible Creates a bitmap compatible with a given device context.
CreateDeviceDependentBitmap Creates a device dependent bitmap from a device independent bitmap.
CreateDeviceIndependentBitmapSection Creates a device independent bitmap that can be written to directly.
CreateBitmap Creates a bitmap of the given dimensions, color planes and bits/pixel.
CreateBitmap - Indirect Creates a bitmap (optimized for monochromatic bitmaps).
CopyImage Copies a bitmap, scaling to the specified dimensions.

The function CreateDeviceDependentBitmap was mentioned previously and it creates a device dependent bitmap given a device independent bitmap. The function CreateBitmap - Compatible produces a bitmap that is compatible with the color format of the device associated with a specified device context. The function CreateBitmap 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

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.

unsigned char BitArray = { 0x51, 0x77, 0x10, 0x00,
                  0x57, 0x77, 0x50, 0x00,
                  0x13, 0x77, 0x50, 0x00,
                  0x57, 0x77, 0x50, 0x00,
                  0x51, 0x11, 0x10, 0x00 };

A BitmapDefinition structure may then be set as follows.

BitmapDefinition BitmapDefine = {0, 20, 5, 4, 1, 1, BitArray};

A bitmap may then be created as follows

Handle BitmapHandle = CreateBitmap(&BitmapDefine);

Yet another approach to creating a bitmap is shown below.

Handle BitmapHandle = CreateBitmap(20,5,1,1,(Handle)BitArray);

The Memory Device Context

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

SelectObject(DeviceContext,BitmapHandle);

may only be applied to a memory device context. A memory device context may be created through the function CreateMemoryDeviceContext. 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.

Bit Block Transfers

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

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 Part containing a number between 0 and 255 with a low Part that assists the device driver in constructing the logical operation. Fifteen of the raster operation codes have names (see enum RasterOperation).

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 RasterOperation::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 RasterOperation::DestinationInvert
0 1 1 0 P ^ D 0x5a0049 RasterOperation::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 RasterOperation::PatternCopy
1 1 0 1 P | ~D 0xf50225
1 1 1 0 P | D 0xfa0089
1 1 1 1 1 0xff0062 RasterOperation::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.

PatternBitBlockTransfer(h,xDestination,yDestination,xWidth,yHeight,RasterOperation::Black);

To draw a white rectangle, the following code may be used.

PatternBitBlockTransfer(h,xDestination,yDestination,xWidth,yHeight,RasterOperation::White);

To color invert a rectangle, the following code may be used.

PatternBitBlockTransfer(h,xDestination,yDestination,xWidth,yHeight,RasterOperation::DestinationInvert);

When an application calls the function FillRectangle 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.

Standard and Stretch Bit Block Transfers

A standard bit block transfer is achieved through the function:

BitBlockTransfer

whereas a rectangle of bits may be stretched or contracted via the function:

StretchBitBlockTransfer.

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 RasterOperation 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.