Brew Quick Start

From StoneHome

This document is intended for experienced programmers about to learn the Qualcomm BREW™ platform. This is not introductory material, and BREW is not a good learning platform. If you're a novice, start in the programming section with some of the tutorials first, instead.

BREW™ Quick Start

Contents

Brew is a fun, strange platform to write for. It's essentially an API to a set of standard tools intended to provide fast native implementations of common tasks like blitting, and to isolate the programmer from platform differences and provide a uniform device capability query interface.

Brew is a message pump style API, reminiscent in some ways of old 16-bit Winders programming. Most phones have a reasonably complete implementation of the Brew API, though you're best advised to stick to Brew 1.1 if you want a reliable wide-spread device base without library problems. Luckily, Brew 1.1 is enough to work with, unless you're one of those types that wants stereoscopic phong-shaded mipmapped polyblended vertex shaded voxel engines on cell phones. The 3D API in Brew 2 is deprecated as of Brew 3, so do not use it.

Langauge Issues

To begin with, there are a bunch of design-related oddities. Due to issues regarding relocatable code, Brew does not allow any globals or statics in your application. Instead, you are passed as the argument to your message pump a structure whose first member must be a platform type called AEEApplet, and whose remaining members are yours to define as your fake global data. Keeping track of that pointer means passing an extra argument around all over the place, which is a big pain in the butt and can keep you from getting some optimizations on stack argument passing. Instead, consider using that pointer to implement an application class, as its members (as long as they're not static) will work quite normally. Since your application will eventually be compiled by armcc, which implements the zero table optimization, this will be as fast as a normal sfunction - faster than a pointer lookup - so it's not just a convenience win.

'Course, to use classes, you need new. And, Brew doesn't provide new. Actually, it doesn't even provide malloc(); instead it provides MALLOC(), which is arguably preferable, since their malloc's semantics don't match those of normal malloc(). I have the pleasure of knowing one of the Brew team's leads; when asked about this design decision, he suggested that "good libraries provide hooks through *pfnalloc and *pfnrelease," which I believe is a debatable stance. This means that a wide range of correct libraries simply mysteriously don't work, and the diagnostic messages one gets for missing allocators in most compilers are god-awful.

Nonetheless, it's easily repaired. This example is C++ only, and thus supports the C++ Standard Library, but not the C Standard Library; therefore use the C++SL headers, not the CSL headers. Because of type optimization, this generally leads to tighter code anyway.

C++ allocators for BREW

void
*
operator
new
(size_t sz) {  
return
MALLOC(sz);

}  

void
operator
delete
(
void
*p) {

  FREE(p); }  

void
*
operator
new
[](size_t sz) {  
return
MALLOC(sz);

}  

void
operator
delete
[](
void
*p) {

  FREE(p);

}

Fixenated. Just make a header for this and #include it pretty much all over the place, and you have something resembling a real language implementation. Please realize that the allocators provided above should be considered in terms of (nothrow)new - the Brew platform does not provide exceptions. As a result, write everything expectiong (nothrow) behavior, and consider making it explicit throughout your code.

Toolchain Issues

Even when developing BREW 1.1 applications to ensure widest target range of phones, use the BREW 2.0 tools and emulator. The BREW 2 emulator is significantly better than the BREW 1.1 editor, and many of the BREW 1.1 tools, notably the resource editor and MIF editor, outright do not function when used with the BREW 2 emulator (though their failures are silent and the problem can be difficult to locate.) Just trust me: use the 2.0 emulator and toolchain.

Running your applications from a directory other than \{brewdir}\examples is significantly more problem-prone than it should be. Until you have known-solid applications to hammer the problems out with, it is best to use \{brewdir}\examples to store the apps you're working on.

Though both the documentation and toolset clearly indicate that a series of graphics formats are supported, it turns out that the BREW Emulator can only handle the API proprietary format, 8-bit palettized uncompressed windows bitmaps, or with BREW2.0 and above PNG without alpha and JPEG. Ignore the remaining suggested formats, such as GIF; they simply do not work on the emulator, making development a giant hassle.

Whenever convenient seperate your work into multiple translation units to ensure that the linker is allowed to discard as much as possible. Whereas this should be standard practice anyway, it's extremely important with cellular phone applications, where binary space is at a severe premium.

Graphics Issues

Whenever possible, use library implementations of graphics routines, as their implementations may vary between phones in order to take advantage of methods which are superior on a given handset. Be aware that The Right Way™ can vary between phones, and rely on the phone's BREW implementation to make graphics-related design decisions whenever possible. Avoid per-pixel drawing whenever possible. Prefer BLIT operations and operations which are conceptually equivalent to memset() / std::fill() whever possible. In general, it is better to blit two regions with 30 overlapping pixels than to blit six regions with no overlap. Take advantage of double-buffered graphics whenever possible. Do not be shy of the AEE_RO_TRANSPARENT raster operation: it's expensive, but not unusually or irreconsilably so. That said, do not waste it; better to branch and turn it off than to always leave it on.

Remember that none of BREW's supported image formats other than PNG support transparency either as alpha or masks of any sort, and that PNG support for alpha is disabled. Do not attempt to track transparency typically in your images. Transparency is recorded as pure magenta pixels - in 24-bit color, #FF00FF will turn out transparent when AEE_RO_TRANSPARENT is enabled for the paint pass. For that reason, be careful not to use pure magenta in images where transparency is needed, though magenta in images without transparency is perfectly safe.

In order to load a Windows Bitmap for display by BLIT, put it into a resource file as an image, load the image into an IImage pointer with LoadResData() (not LoadResImage().) That pointer is actually an offset to the picture data from the pointer's location, so to get a pointer to the picture data itself (which we'll call the texture) cast the IImage* to a byte*, and add the value of the IImage* as cast to a byte*; the results are a pointer to our texture. An example function is here. Next, call CONVERTBMP() on the texture pointer, then use IDISPLAY_BitBlt() to paint the resulting texture to screen.

In order to reduce the number of pointers, IImage structs, and to reduce the number of passes to system allocation and deallocation for images, as well as to prevent fragmentation, it is advantageous to use BREW's mechanism for splitting images into frames to support animation in nonanimated formats, even when you do not actualy intend to animate an image. For example, a well-based puzzle game which drops some number of similar cells (blocks or gems or spheres or whatever) will benefit greatly from providing the images as a series and then treating them as frames. BREW expects animation frames to be listed horizontally, meaning there is some offset jumping and span counting during painting; as such, framed images are slightly slower to paint than unframed. The difference is generally minor, and shouldn't be a concern unless targetting a particular handset; the difference is miniscule compared to the variation between phones.

Because of the wide range of available screen types, it is often helpful to describe the application ignorant of screen size, relying instead on numbers-as-strings and bitmaps in a resource file which can be replaced per phone. As long as metrics are tracked entirely within data in a resource file, then a new phone can be accomodated simply by swapping MIFs, which means that an application can be ported between BREW-supporting phones entirely without recompilation, often by an artist with relatively simple instructions such as "fit your work to these sizes, load them into the resource editor with these names and in this order, save, and go;" the same instructions can also be given to localization teams. Given that the cell market is significantly language-diverse, having easily localizable applications is important. Though describing an application's interface in terms of offsets and relative to variables can be laborious and confusing at first, in the long run it will save you quite a bit of time, debugging and effort. Because screen metrics are often counterintuitive, when testing your application test it with two sets of metrics whenever possible.

1