So You Have a Bunch of Resources, Now What Do You Do?

A few months ago I wrote about QuickRes, our new tool for viewing and
editing program resources of all types.  If you haven't yet used it,
the current version (updated since that last Newsletter article) can
be found at:

	Intel: ftp://ftp.be.com/pub/bebits/development/tool_kits/QuickRes-x86.zip
	PPC: ftp://ftp.be.com/pub/bebits/development/tool_kits/QuickRes-ppc.zip

The question I have seen asked most often about resources is how to use
them in an application, and in particular how to use a bitmap stored in a resource
to create a BBitmap that can be drawn into a view.  Conveniently, the Translation Kit
includes two functions that make this easy to accomplish:

	BBitmap* myBitmap = BTranslationUtils::GetBitmap('type', "name");
	BBitmap* myBitmap = BTranslationUtils::GetBitmap('type', id);

While very convenient, there are a number of issues and limitations related
to these functions that you should be aware of:

* They use the translation kit to create the bitmap.  Currently, this means
  that all translator add-ons will be loaded into your application
  the first time you call GetBitmap().  For a small program this can
  impose a noticable startup penalty.

* Every call to GetBitmap() generates a new bitmap.  Not only does this
  include the overhead for creating the BBitmap object itself, but also
  for running the raw resource data through a translator.

* You can only use these to retrieve your application's resources.
  Add ons are not able to (easily) retrieve bitmaps from their own
  resources.

Another Way

BResourceSet is a class I wrote to address the limitations of GetBitmap().
This class is a wrapper around one or more BResources objects, providing
a similar API along with additional methods for directly retrieving
BBitmap, BCursor, and BMessage objects from the resource data.

You can find the source code <<HERE>>.  Also included is a sample
application, LoadResources, that demonstrates using the class within
an add-on to display an icon.

Using BResourceSet is very simple, requiring three steps:

1. Create a single BResourceSet instance that will provide access to
   all of your image's resources.

2. Call the AddResources() methods one or more times to point this
   instance at the resource data it will load.

3. Use FindBitmap(), FindCursor(), and FindMessage() to retrieve data from
   your resources.

The BResourceSet class keeps a cache of all resource data it has found.
For example, the first time you request a bitmap, it will load the bitmap
data from its resources, create and store the BBitmap object, and return
a pointer to you.  Everytime there-after it will immediate find the
existing BBitmap object and return the same one to you, without any further
effort.  All objects are returned as const pointers -- BResourceSet
will free the data for you when it is deleted, so you shouldn't delete the
objects yourself.

The API

There are three pieces to the BResourceSet API -- telling it where to look
for resources, loading resources, and generating objects from resources.
The first two are public; the last is protected, to be used by subclasses
to extend the data formats that can be handled.

You tell BResourceSet where to find its resource by calling the
AddResources() methods.  There are two flavors of this method: the first is
given an actual BResources object, which BResourceSet then takes ownership
of and manages.  The second provides an address of some code in your program,
and creates a new BResources object for the image at that location.  This
second form is particularily useful for add-ons, as it can be complicated to
track back to where your add-on lives on disk (and thus create a BResources
object on the file).

Two additional functions are available, AddDirectory() and AddEnvDirectory().
These tell the BResourceSet to also look in the given directories when
resource data is requested by name.

The core method for finding resource data is FindResource().  This works
identically to the BResources implementation, returning the raw resource
data found.  In addition, you can use the FindBitmap(), FindCursor(), and
FindMessage() to return objects of these types created from that raw resource
data.  The BResourceSet class manages all of this data, so you should never
free it yourself, nor use it after deleting the BResourceSet instance that it
came from.

Finally, subclasses can implement the functions GenerateBitmap(),
GenerateCursor(), and GenerateMessage() to create an object of the respective
type from raw resource data.  The default implementation of these is typically
all you need.

In the case of bitmaps, BResourceSet has built-in code for instantiating
bitmaps from archived messages and PNG data streams.  This means that you
normally won't need to link against the Translation Kit, though you <b>will</b>
need to link with the static libraries libpng.a and libz.a for the PNG
decoding code.  Linking with these libraries will bloat your code a fair
amount, so if this is a concern you could modify BResourceSet to only natively
handle archived bitmaps.  If you would like to be able to read bitmaps of any type,
you can override GenerateBitmap() to go through the translation kit.

(One warning about using PNG images with QuickRes: the PNG Translator in
R5 has a number of frustrating quirks when used with QuickRes, particularily
in bitmaps with an alpha channel.  The next release of BeOS will fix these
problems.)

An Example

To show BResourceSet in action, I have included a modified version of the
old "LoadAddon" example
(http://www-classic.be.com/aboutbe/benewsletter/Issue103.html#Workshop).
To build it, compile both of the projects (the application first,
then the add-on in the Effects directory) or in the terminal use the
command "make -f makefile.all".

The part that is interesting here is in the add-on, where we use
BResourceSet to load a bitmap image for its button.  To get the BResourceSet
up-and-running, we have:

	static int32 gResourcesCreated = 0;
	static BResourceSet gResources;
	
	BResourceSet& Resources()
	{
		// Return the BResourceSet object for this image, initializing
		// if needed.
		if ((gResourcesCreated&2) != 0) {
			// Quick return if already initialized.
			return gResources;
		} else if (atomic_or(&gResourcesCreated, 1) == 0) {
			// Not yet initialized -- do so.
			gResources.AddResources(&gResourcesCreated);
			// Mark that we are done.
			atomic_or(&gResourcesCreated, 2);
		} else {
			// Wait for resources to be initialized.
			while ((gResourcesCreated&2) == 0) snooze(50000);
		}
		return gResources;
	}

This function simply returns the BResourceSet instance for the add-on
image, initializing it if that has not yet been done.  Notice how it
passes in an address of your application's image -- the gResourcesCreated
variable, which is in the image's data section.  The use of
atomic_or() makes the function thread-safe (the BResourceSet object
itself is already thread-safe), and the class is instantiated as
a global so that it will be destroyed automatically when the add-on is
unloaded.

Retrieving our bitmap from the add-on's resources is now just a matter
of:

	const BBitmap* bm = Resources().FindBitmap(B_PNG_FORMAT, "BeOS Logo");

And it can be used for whatever nefarious purpose we wish.  In this case,
it is placed into a little BBitmapButton convenience class that displays it.

I hope this class gets you on your way to using resources throughout your
BeOS applications.  Dig in to the class and enhance it if you want --
its very easy to add your own data types to the API -- or find other uses
for it.  I am told that it makes simple user interface skinning very easy
to implement...
