http://www.developer.com/

Back to article

Performing Graphics Operations with BREW Bitmaps


March 20, 2007

Bitmaps are the lifeblood of many graphics applications. Although BREW has a host of graphics primitives for two- and three-dimensional drawing, as well as a plethora of image decoders, there remain times where you just need to have access to the raw bits of an image. In this article, I take you on a quick tour of the kinds of bitmaps provided by BREW, as well as how to manipulate bitmaps and their contents. You'll learn how to create bitmaps, the difference between a conventional bitmap and a device-independent bitmap, how to blit bitmaps, and manipulate individual pixels within a bitmap.

Understanding the Difference Between Bitmaps

BREW bitmaps come in two basic types: device-independent bitmaps and device-dependent bitmaps. As their names imply, device independent bitmaps are inherently portable between devices, although this can come at the cost of performance when manipulating the bitmap. Device dependent bitmaps, on the other hand (also sometimes simply called native bitmaps), are in a format highly tuned to meet the needs of a specific handset's graphics architecture. Speaking of performance, it's important to realize which kinds of bitmap you're working with, because while BREW can convert bitmaps from one format to another (say, when performing a bit blit from one bitmap to another), this format conversion incurs a performance penalty. Regardless of the kind of bitmap you're accessing, you perform bitmap operations using the IBitmap interface.

A specific example of a device-independent bitmap is the IDIB, an interface that has an interesting property; it has fields associated with it. When working with an IDIB, you can obtain references to the pixel map itself as well as the palette map, and other sundries such as the bitmap's width, height, and color depth. I'll say a little more about the IDIB in "Accessing All the Pixels in a Bitmap," later in this article.

Creating Bitmaps

Unlike most BREW interfaces, you won't create a bitmap using ISHELL_CreateInstance. That's because the bitmap has some additional properties you need to set, such as the width and height of the bitmap. Instead, you typically use a factory, such as another bitmap, to create the bitmap using the CreateCompatibleBitmap method. This method creates another bitmap in the same format as the originating bitmap, letting you blit with high performance between the existing and new bitmaps.

Probably the most common pattern that occurs when working with bitmaps is to obtain a bitmap from the display—either the display's bitmap itself, or to create a new compatible bitmap from the display's bitmap. To do this, you write:

IDisplay *piDisplay = p->a.m_pIDisplay
IBitmap *piBitmap, *piScreenBitmap;
IDISPLAY_GetDeviceBitmap( piDisplay, &piScreenBitmap );
IBITMAP_CreateCompatibleBitmap( piScreenBitmap, &piBitmap, WIDTH,
                                HEIGHT );
IBITMAP_Release( piScreenBitmap );

After declaring local variables to contain the screen's bitmap and the new bitmap, this code:

  1. Obtains a reference to the bitmap used by the screen.
  2. Creates a bitmap in a format and color depth compatible with the screen bitmap of dimensions WIDTH and HEIGHT pixels.
  3. Releases the screen's bitmap.

If you need a device-independent bitmap, the process is a little different. You can obtain one in one of two ways: by getting one from the display, or by using IBITMAP_QueryInterface to get the device-independent analogue of an existing bitmap. The first way is better if you just need a device independent bitmap; the second is more useful if you need to capture the contents of an existing bitmap. Using IDisplay, your code might read:

IDisplay *piDisplay = p->a.m_pIDisplay
IDISPLAY_CreateDIBitmap( piDisplay, &piDIB, 16, WIDTH, HEIGHT );

Using IBITMAP_QueryInterface is just as easy:

IBitmap *piBitmap;    // This has to be initialized before we use
IDIB    *piDIB;       // it, of course
IBITMAP_QueryInterface( piBitmap, AEECLSID_DIB, (void **)&piDIB );

Manipulating Bitmaps

There are a variety of things you can do to bitmaps, including blit the contents of one bitmap into another, get or set the value of pixels in the bitmap, or set an entire scan line of pixels to a specific value. Here's some code that sets some random pixel values within a bitmap, and then blits that bitmap back to the screen:

#define NUM_PIXELS  60
#define WIDTH       60
#define HEIGHT      60

void BitmapExample( CApp *p )
{
   IDisplay *piDisplay = p->a.m_pIDisplay;
   IBitmap *piBitmap, *piScreenBitmap;
   byte abyRand[ 60 * ( 3 + 2 ) ];    // Sixty pixels, three colors,
   int i, r, g, b;                    // two axes
   NativeColor nativeColor;
   AEEPoint point;

   GETRAND( abyRand, sizeof( abyRand ) );

   IDISPLAY_ClearScreen( piDisplay );
   IDISPLAY_GetDeviceBitmap( piDisplay, &piScreenBitmap );
   IBITMAP_CreateCompatibleBitmap( piScreenBitmap, &piBitmap,
                                   WIDTH, HEIGHT );
   IBITMAP_Release( piScreenBitmap );
   for ( i = 0; i < NUM_PIXELS; i++ )
   {
      point.x = abyRand[ i * 5 + 0 ] % WIDTH;
      point.y = abyRand[ i * 5 + 1 ] % HEIGHT;
      r       = abyRand[ i * 5 + 2 ];
      g       = abyRand[ i * 5 + 3 ];
      b       = abyRand[ i * 5 + 4 ];

      nativeColor = IBITMAP_RGBToNative( piBitmap,
                                         MAKE_RGB( r, g, b ) );
      IBITMAP_SetPixels( piBitmap, 1, &point, nativeColor,
                         AEE_RO_COPY );
   }

   // Update the display bitmap.
   IDISPLAY_BitBlt( piDisplay, 0, 0, WIDTH, HEIGHT, piBitmap, 0, 0,
                    AEE_RO_COPY );
   IBITMAP_Release( piBitmap );
   IDISPLAY_Update( piDisplay );
}

The code begins by getting the display's bitmap, from which I derive a compatible bitmap, just as I showed in the previous section. Next, I loop for a predetermined number of pixels, using the random data to determine both the coordinate and the RGB value of a pixel to change. I can't use this RGB value directly, however; first, I must convert it to the bitmap's native representation for the color using IBITMAP_RGBToNative. (There's a corresponding API, IBITMAP_NativeToRGB, that converts the internal representation to a RGB triplet.) Then, I set the selected pixel's color directly, using IBITMAP_SetPixels. SetPixels takes an array of pixels, and sets all the pixels in the array to the preset value; a more efficient way to do this if I had a lot of pixels of the same color would be to first generate pixel lists by color and then use one call to IBITMAP_SetPixels for each color. Finally, I update the display directly by bit blitting the mutated bitmap directly to the screen at the origin. IDISPLAY_BitBlt takes arguments specifying the destination rectangle of the bit blit first, followed by the source bitmap, and the offset into the source bitmap, and then the drawing mode used for the bit blit operation. Once complete, I need only clean up the bitmap I created in the first step and update the display.

Of course, you can also bit blit directly from one bitmap to another. This is done by using the destination bitmap's IBITMAP_BltIn method, which takes the same arguments as IDISPLAY_BitBlt (although, of course, the first argument is a valid bitmap, not an instance of IDisplay!). Bitmaps also offer the IBITMAP_BltOut method, which is used to blit bits out of the provided display; you don't normally use this directly, although BREW may invoke it on the source bitmap when you invoke IBITMAP_BltIn.

Of course, at times you might want to do more to a bitmap than simply set a handful of pixels or bit blit in another bitmap. You actually can use any of the BREW graphics primitives, including IImage, IDisplay, and IGraphics with a bitmap, although the process is a little awkward. To do this, you want to replace the bitmap used by the display with a target bitmap, perform whatever drawing you like, and then replace the default display bitmap. The easiest way to do this is to create a compatible bitmap of the desired size, and then use IDISPLAY_SetBitmap. Once you're done, invoke IDISPLAY_SetBitmap again, this time passing NULL to restore the original bitmap:

IDisplay *piDisplay = p->a.m_pIDisplay;
IBitmap *piBitmap, *piScreenBitmap;

GETRAND( abyRand, sizeof( abyRand ) );

IDISPLAY_ClearScreen( piDisplay );
IDISPLAY_GetDeviceBitmap( piDisplay, &piScreenBitmap );
IBITMAP_CreateCompatibleBitmap( piScreenBitmap, &piBitmap, WIDTH,
                                HEIGHT );
IBITMAP_Release( piScreenBitmap );
IDISPLAY_SetBitmap( piDisplay, piBitmap );

// Do all your drawing here as usual.

IDISPLAY_SetBitmap( piDisplay, NULL );

Accessing All the Pixels in a Bitmap

Sometimes, you might want to access all the pixels in a bitmap. This is the function of IDIB, an interface with public data you can access to obtain the pixels within a bitmap. IDIB has the following fields that are of interest:

  • The pBmp field points to the first pixel in the top row of the pixel data.
  • The pRGB field points to the color palette for indexed bitmaps, whereas the cntRGB field indicates the number of colors in the color palette. Beware—the colors here are in memory order, not necessarily RGB! On little-endian processors, be sure to use NHTOL to convert the pixel value.
  • The ncTransparent field is the color representing transparency in the bitmap.
  • The cx and cy fields indicate the width and height of the bitmap.
  • The nPitch field is the length of a row of pixels in the pixmap in bytes.
  • The nDepth field indicates the size of each pixel in bits; the nColorScheme indicates the encoding used to represent a color, such as 5 bits each for the red, green, and blue pixels.

Using an IDIB takes a bit of fussing because there's no guarantee what sort of bit depth (denoted by the nDepth field) and color encoding (encoded by the nColorScheme field) you'll get when you get an IDIB. The algorithm for reading pixels is essentially the same regardless, however:

  1. Beginning at pBmp, read a row of nPitch pixels.
  2. For each nDepth bits, if cntRGB is non-zero, look up the appropriate color in the pRGB array. If cntRGB is zero, decode the bits as a color in the format specified by nColorScheme.
  3. Repeat Steps 1 and 2 for cy iterations.

The IDIB interface has many limitations, making it essentially a read-only medium. Specifically, you can't draw to it using IDisplay, IGraphics, or any of the other drawing mechanisms, and in present versions of BREW, IBITMAP_BltOut is unsupported as well, making it impossible to blit out of an IDIB into another bitmap. If you want to do a lot of manipulation on an IDIB and then present the results on the handset, be prepared to do a bit of hacking—one approach is to create a Windows BMP bitmap using the IDIB data, and then invoke BREW's CONVERTBMP to obtain a bitmap again. Although this should definitely work in principle, be prepared for performance issues because you're going to impose a great deal of memory shuffling: first, the shuffling you do on the IDIB itself preparing the image, and then the conversion to a Windows BMP (typically setting up the header and then using MEMCPY to copy the pixmap), and finally a conversion back to a native bitmap again.

Conclusion

Access to raw bitmaps can be a crucial part of application development when there's something you need to do that isn't supported by BREW's IDisplay or IGraphics APIs. With the information you now have in hand, you should be well prepared to harness the power of BREW's bitmap interfaces.

About the Author

Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today's wireless devices. He is the author of several books on software development, including eBay Application Development and Software Development for the QUALCOMM BREW Platform, both available from Apress, and is an active Amateur Radio operator. Contact Ray at kf6gpe@lothlorien.com.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date