Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

9    Working with Bitmaps

User Interface

Exploring Palm OS®

A bitmap is a graphic displayed by Palm OS®. You create a bitmap resource using your resource editor, or you can create a bitmap programmatically.

This chapter covers:

Bitmap Format
Displaying a Bitmap on the Screen
Creating a Bitmap Programmatically
How Palm OS Displays Bitmaps
Summary of Bitmap Support

Before you read this chapter, it is strongly suggested that you be familiar with the concepts introduced in Chapter 1, "The Display," and Chapter 8, "Drawing."

Bitmap Format ^TOP^

Programmatically, a bitmap or bitmap family is represented by a BitmapType structure. This structure is simply a header. It is followed by the bitmap data in the same memory block. Bitmaps are also allowed to have their own color tables. When a bitmap has its own color table, it is stored between the bitmap header and the bitmap data.

This section describes how the BitmapType stores bitmap data. See "How Palm OS Displays Bitmaps" to learn how Palm OS uses this bitmap data to determine what to display on the screen.

Versions of Bitmap Support ^TOP^

The BitmapType structure contains one of four versions of bitmap encodings. (The version field specifies which one is used.)

  • Version 0 encoding is supported by all Palm OS releases. This encoding supports monochrome bitmaps only.
  • Version 1 encoding is supported on Palm OS 3.0 and later. This encoding supports 1, 2, and 4-bit grayscale bitmaps and adds support for bitmap families.
  • Version 2 encoding is supported on Palm OS 3.5 and later. This encoding supports 1, 2, 4, 8 and 16-bit color bitmaps, transparency indices, and RLE compression.

    With a version 2 bitmap, you can specify one index value as a transparent color at creation time. The transparency index is an alternative to masking. The system does not draw bits that have the transparency index value.

    When a bitmap with a transparency index is rendered at a depth other than the one at which it was created, the transparent color is first translated to the corresponding depth color, and the resulting color is named transparent. This may result in a group of colors becoming transparent.

  • Version 3 encoding is supported on Palm OS Garnet and later devices with high-density displays. Version 3 adds support for bitmaps of varying densities.

Bitmap Families ^TOP^

A BitmapType structure represents either a single bitmap or a bitmap family. A bitmap family is a group of bitmaps, each containing the same drawing but at a different bit depth (sometimes called the pixel depth) or density. Figure 9.1 shows a bitmap family for a single display density.

Figure 9.1  Single-density bitmap family

Bitmaps in a bitmap family are grouped by density. For backward compatibility, the linked list of low density bitmaps occur first, and remain ordered from low to high bit depths. If the family contains high-density bitmaps, the high-density bitmaps follow the low-density bitmaps, again ordered from low to high bit depths. If the family contains multiple densities, then the density sets are ordered from low to high density.

Figure 9.2 illustrates the process of traversing the bitmaps in a bitmap family.

Figure 9.2  Linked list of bitmaps in a bitmap family

Color Tables and Bitmaps ^TOP^

As mentioned previously, bitmaps can have their own color tables attached to them. If a bitmap has its own color table, the system must create a conversion table to convert the draw window before it can draw the bitmap. This conversion is a drain on performance, so using custom color tables with bitmaps is strongly discouraged.

You can still change the color table used by the current draw windows with the WinPalette() function before drawing the bitmap and change it back to the default afterwards. Note that WinPalette() no longer affects what is already drawn onto the screen. It only affects future drawing.

PNG Files ^TOP^

In addition to the bitmap format described in this section, Palm OS supports the display of PNG formatted files. To add a PNG file to your resource, use RAW_RESOURCE as shown in Listing 9.1.

Listing 9.1  Creating a PNG resource


<RAW_RESOURCE RESOURCE_ID="1000" COMMENT="PNG file">
   <RES_TYPE>'pngf'</RES_TYPE> 
   <DATA_FILE>"./MyPictures/Picture1.png"</DATA_FILE> 
</RAW_RESOURCE> 

To display the PNG on the screen, you must create a GcBitmapHandle for the resource using FrmGetBitmapHandle() or GcLoadBitmap(). This is described further in the next section. Note that you must create a GcBitmapHandle; the BitmapType structures do not accept PNG files.

Displaying a Bitmap on the Screen ^TOP^

There are two ways to statically display a bitmap on a screen:

  • If the bitmap should always be displayed at a fixed location, in your resource file create a form bitmap resource in addition to the bitmap. The form bitmap specifies where the bitmap should be displayed.
  • If the bitmap might move or you do not know the location where it should be displayed until runtime, use the GcDrawBitmapAt() function to display it. Because this is a graphics context function, as described in Chapter 8, "Drawing," you need to first obtain access to the graphics context. See Listing 9.2.

Listing 9.2  Drawing a bitmap


GcHandle gc; 
FormType *formP; 
GcBitmapHandle gcBitmap; 
... 
 
//Obtain the graphics context and draw the bitmap. 
while (/* some condition */) { 
   //Load the bitmap from the resource file and convert it.  
   gcBitmap = FrmGetBitmapHandle(formP, myAppDB, bitmapRsc, 
      rscID, 0); 
   if (!gcBitmap) 
      return memErrNotEnoughSpace; 
 
   GcGetCurrentContext(gc); 
   if (gc) { 
      GcDrawBitmapAt(gc, gcBitmap, NULL, x, y); 
      GcReleaseContext(gc); 
   } 
} 

Before you can call GcDrawBitmapAt(), you must obtain a GcBitmapHandle for the bitmap. In most cases, you can do so using FrmGetBitmapHandle(), which loads the specified bitmap resource from the database, locks it, and converts it into a format that can be displayed on the screen more efficiently. For example, it converts big-endian bitmaps to little-endian format. This function also caches the bitmap handle and returns the cached handle on subsequent calls, ensuring that the bitmap is only loaded and converted once. The memory associated with the bitmap cache is freed when the form is freed.

If you're performing animation, doing a slide show, or otherwise drawing a lot of bitmaps to the form, don't use FrmGetBitmapHandle(). If you do, you may quickly fill the bitmap cache. Instead, load the bitmap (or PNG) from the resource file, and create the GcBitmapHandle using GcLoadBitmap(). In this case when you are finished with the bitmap, you'll need to call GcReleaseBitmap() to remove the memory associated with the GcBitmapHandle and then free the bitmap.

Alternatively, you can use GcDrawRawBitmapAt(), which converts a BitmapType to a GcBitmapHandle, draws the bitmap on the screen, and then removes the memory associated with the GcBitmapHandle. You are responsible for loading the bitmap from the resource and releasing it when you are finished.

Creating a Bitmap Programmatically ^TOP^

If you want to create or modify a bitmap programmatically, do the following:

  1. Use BmpCreate() to create a BitmapType structure.
  2. Pass the BitmapType to GcCreateBitmapContext(). Doing so creates a graphics context representing the bitmap.
  3. Draw to the graphics context returned from the previous step using the Graphics Context path definition and drawing functions described in Chapter 8, "Drawing."
  4. Release the graphics context when you are finished (GcReleaseContext()).

Listing 9.3  Programmatically creating a bitmap


BitmapType *bmpP; 
GcHandle gc; 
  
bmpP = BmpCreate(10, 10, 8, NULL, &error); 
if (bmpP) { 
  gc = GcCreateBitmapContext(bmpP); 
  if (gc) { 
      GcSetColor(gc, 0, 0, 0, 255); 
      GcRoundRect(gc, 0, 0, 50, 15, 3, 3); 
      GcPaint(gc); 
      /* etc */ 
      GcReleaseContext(gc); 
  } 
} 

A few items of note about programmatically creating a bitmap:

  • BmpCreate() always creates a version 2 bitmap, which is a low-density bitmap. To create a high-density bitmap, you need to call BmpCreateBitmapV3() after calling BmpCreate().
  • Even if you are creating a low-density bitmap, the coordinates that you pass to the graphics context drawing functions are interpreted as native coordinates by default. If you prefer to work in standard coordinates, call GcSetCoordinateSystem().

How Palm OS Displays Bitmaps ^TOP^

This section describes how Palm OS draws bitmaps. It covers how Palm OS chooses a bitmap from the bitmap family and how it deals with differences between the bitmap's density and the draw window's density.

Displaying Bitmaps from a Bitmap Family ^TOP^

The algorithm that is used to determine which bitmap to display depends upon the density of the draw window. The draw window is not always the screen. It can be an off-screen window created with the WinCreateOffscreenWindow() function or a bitmap context created with GcCreateBitmapContext().

If the draw window is single density, single-density bitmaps are always favored over double-density bitmaps, regardless of source bitmap depth. If the draw window is double density, however, the color domain match (color vs. grayscale) is favored over a double-density bitmap with a color domain mismatch. Listing 9.4 shows the algorithm used to determine which bitmap to display.

Listing 9.4  Algorithm for choosing which bitmap to display


If draw window is low density { 
    Favor low-density over double-density 
    If draw window is color { 
        Favor color bitmap 
    } else { 
        Favor grayscale, picking greatest depth 
          less than or equal to draw window's 
          depth 
    } 
} else { 
        If draw window is color { 
            Favor color 
        } else { 
            Favor grayscale 
        } 
    } 
} 

Table 9.1 provides the results of applying this algorithm. The two left columns represent the draw window's depth and density. The third column lists the bitmap selection preferences, ordered from best to worst (a 'd' in this third column indicates double-density).

Table 9.1  Double-density algorithm results 

Draw Window

Bitmap selection preferences

Depth

Density

1

Single

1, 2, 4, 8, 16

2

Single

2, 1, 4, 8, 16

4

Single

4, 2, 1, 8, 16

8

Single

8, 16, 4, 2, 1

16

Single

16, 8, 4, 2, 1

1

Double

1d, 2d, 4d, 1, 2, 4, 8d, 16d, 8, 16

2

Double

2d, 1d, 4d, 2, 1, 4, 8d, 16d, 8, 16

4

Double

4d, 2d, 1d, 4, 2, 1, 8d, 16d, 8, 16

8

Double

8d, 16d, 8, 16, 4d, 2d, 1d, 4, 2, 1

16

Double

16d, 8d, 16, 8, 4d, 2d, 1d, 4, 2, 1

Drawing High-Density Bitmaps ^TOP^

The rendering system uses the density field in the source and destination bitmaps (where the destination bitmap is the bitmap representing the draw window) to determine an appropriate scaling factor. Because standard density bitmaps must be scaled for high-density displays, some handhelds with high-density screens may use graphic accelerators. Nevertheless, the software rendering system incorporates pixel-scaling logic for when the destination is an off-screen window.

The coordinate system affects the placement and dimensions of graphic primitives. It does not affect bitmap contents, however. You can create bitmaps that contain either low- or high-density bitmap data. Palm OS uses the coordinate system to determine where to place the top left corner of the bitmap on the screen, while the rendering system uses the bitmap structure's density field to determine if it needs to stretch or shrink the bitmap data as it blits.

When scaling down from a density of kDensityDouble to kDensityLow, the software shrinks the bitmap data. The result is almost always a poorer quality image when compared with a bitmap originally generated with a density of kDensityLow.

The following examples demonstrate the above concepts.

  • An application draws a low-density bitmap to a double-density screen.

    The source data is a 16 by 16 bitmap. The application calls


    GcSetCoordinateSystem(gc, kCoordinatesStandard); 
    GcDrawBitmapAt(gc, bitmapH, NULL, 31, 23); 
    

    with the intention of placing the bitmap on the screen beginning at standard coordinates (31, 23). Because the device has a double-density screen, the scaling factor is 2.0. The coordinates are converted to native coordinates (62, 46).

    The rendering system recognizes the bitmap as low density, based upon the version of its BitmapType structure, and pixel-doubles the source data when blitting to the double-density screen.

    Figure 9.3 shows the source data on the left, with low-density window coordinates for the top-left and bottom-right corners. The illustration on the right shows the result as displayed on the screen, with top-left coordinates scaled and bitmap data pixel-doubled.

Figure 9.3  Low-density bitmap on a double-density screen

Note that it's not necessary to use standard coordinates to display low-density bitmaps. The coordinate system only determines where the bitmap is displayed, not how the bitmap is displayed. The advantage to using standard coordinates is that you only have to compute one set of coordinates for all supported displays.

  • An application draws a double-density bitmap to a double-density screen.

    The source data is a 32 by 32 double-density bitmap:


    GcDrawBitmapAt(gc, bitmapH, NULL, 61, 45); 
    

    By default, the Graphics Context functions use native coordinates, which in this case are double-density coordinates. The double-density coordinates (61, 45) allow the application to position the bitmap more precisely on the screen; these coordinates are equivalent to coordinates (30.5, 22.5) in the standard coordinate system.

    Because the screen bitmap has the same density, the rendering system copies the source data to the screen unchanged.

Figure 9.4  Double-density bitmap on a double-density screen

  • An application draws a double-density bitmap to a low-density screen.

    If an application includes only high-density bitmaps, the rendering system needs to shrink them when drawing them to the screen. The application can determine the screen density like this:


    uint32_t density; 
    err = WinScreenGetAttribute(winScreenDensity, &density); 
    

    Understanding that the destination is low density, the application uses the standard coordinate system:


    GcSetCoordinateSystem(gc, kCoordinatesStandard); 
    GcDrawBitmapAt(gc, bitmapH, NULL, 31, 23); 
    

    Because the destination window is low density, and because the passed coordinates are standard coordinates, Palm OS does not scale the passed coordinates. The rendering system, however, recognizes that the source bitmap has a density of kDensityDouble and shrinks the data to one-half the original size when blitting it to the low-density screen.

Figure 9.5  Double-density bitmap on a low-density screen

The result, shown in Figure 9.5 on the right, is poor. Because of this, for an application to look good on both low and high-density screens it should include both low and high-density bitmaps.

Summary of Bitmap Support ^TOP^