The Interface Kit Table of Contents The Interface Kit Index

The New B_OP_ALPHA Drawing Mode

A new drawing mode has been added to BView's repertoire of drawing modes. Calling

	someView->SetDrawingMode(B_OP_ALPHA);
will cause subsequent drawing in someView to be done with the new alpha transparency features active. Using alpha transparency appropriately can make your program more useful (and better looking!). Here's a simple example from a demo program (with some blurriness thrown in thanks to conversion to JPG format):


What Is Alpha Transparency?

In early releases of the BeOS, all drawing colors were "solid". Drawing a red circle in a view, produced a solid red circle which hid anything "behind" it. It was possible to approximate transparency effects using appropriate patterns and drawing modes, but this was only an approximation--a close look at the screen would reveal that individual pixels were all either the red of the circle, or the original "background" color. This didn't look good, and didn't work at all well for objects that were supposed to be just slightly transparent or slightly opaque.

Recent release of the BeOS make use of the last 8 bits/pixel in a B_RGB_32_BIT color space. Previously, only 24 bits out of the 32 bits allocated to each pixel were used, 8 bits for each of the red, green, and blue components of a particular color. Now, the remaining 8 bits are used for the alpha channel, and denote a color's opacity, with higher alpha values denoting more opaque colors, and lower alpha values denoting more transparent colors.


Seeing Alpha Transparency in Use

A demo application showing alpha transparency in use is available. It draws some squares in a view, using different alpha values and modes. You can drag the squares around and lay them on top of one another, to see the transparency effects in action. The code is included with this documentation. The application may also be available in compiled form on your CD. To compile it yourself, follow these instructions:

  1. Find the AlphaTransparencyDemo.cpp file.
    (If the link doesn't work, go to /boot/beos/documentation/Be Book/The Interface Kit/demos/AlphaTransparencyDemo.cpp.)
  2. Start up the BeIDE application, and create a new BeApp project.
  3. Add "AlphaTransparencyDemo.cpp" to your project.
  4. Compile the project.
This is experimental, unsupported code, and probably has many bugs, so be warned. (However, it should at least compile and come up properly--if it doesn't, please drop me a line at ken@be.com and tell me what went wrong.) The code is (as yet) not well commented or well structured, but you may be able to use it as a resource base when starting out with the alpha functionality.

Note: If you start this program in a terminal, then right-clicking on a pixel in the program's window will print to standard output some basic information about the values associated with that pixel.

An Intel-only executable of this demo is provided in /boot/beos/documentation/Be Book/The Interface Kit/demos.


Basic Drawing Using the Alpha Channel

Let's take a look at how to initialize colors, including the alpha channel, and what the effects on the screen would be when drawing normally using these colors (using the default B_OP_COPY mode), and when drawing with these colors using the B_OP_ALPHA mode. The standard structure for defining colors in the BeOS is the rgb_color structure. This structure is defined as four 8-bit unsigned integers, as follows:

	typedef struct { uint8 red; uint8 green; uint8 blue; uint8 alpha; } rgb_color;

To make use of pure red in a program, you might do something like this:

	myView->SetDrawingMode(B_OP_COPY);
	rgb_color pureRed;
	pureRed.red = 255; pureRed.green = 0; pureRed.blue = 0;
	myView->SetHighColor(pureRed);	

After executing the above code, any drawing with primitives done in myView would appear on the screen as pure, bright red. The fact that the alpha component of the color was not set in the above code is irrelevant, because when drawing in the B_OP_COPY mode (or in any drawing mode other than B_OP_ALPHA), the alpha component has no effect on what shows up on the screen.

Now, let's do something like this:

	myView->SetDrawingMode(B_OP_ALPHA);
	/* Disregard the following line for a moment--it is necessary, but will
		be explained a little later. */
	myView->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);

	rgb_color solidRed;
	solidRed.red = 255; solidRed.green = 0; solidRed.blue = 0; solidRed.alpha = 255;
	rgb_color translucentRed;
	translucentRed.red = 255; translucentRed.green = 0; translucentRed.blue = 0; translucentRed.alpha = 128;
	rgb_color faintRed;
	faintRed.red = 255; faintRed.green = 0; faintRed.blue = 0; faintRed.alpha = 10;

Each of the three different reds above is the same "color", in terms of their RGB values; they differ only in opacity. However, drawing onto the screen (using B_OP_ALPHA mode) produces very different results for each color. Let's take a look at code that fills the view with a rectangle of each red, and discuss what the effect will be.


More Sophisticated Alpha Drawing--Constant vs. Pixel Modes

In order to do any drawing using the new alpha features, you need to call View::SetHighColor() with an argument of B_OP_ALPHA, to set the alpha drawing mode. That is not the only alpha setting, though; once the alpha drawing mode is active, there are two further values which affect how alpha drawing is carried out, and both of these values are set through the View::SetBlendingMode() function. This function was used above, but not explained. Let's take a closer look now.

The type of View::SetBlendingMode() is:

		void SetBlendingMode(source_alpha, alpha_function);

The first argument can be one of the two constants B_CONSTANT_ALPHA or B_PIXEL_ALPHA. The second argument can be one of the two constants B_ALPHA_OVERLAY or B_ALPHA_COMPOSITE. For now, assume the second argument is always B_ALPHA_OVERLAY (this is by far the most common case). This section will explain the effects of the first argument.

For all the code above, we passed in B_CONSTANT_ALPHA as the first argument to SetBlendingMode(). B_CONSTANT_ALPHA instructs the internal BeOS drawing routines to use the alpha value from the current high color, when drawing in alpha mode. To put it more simply, when the drawing mode is B_OP_ALPHA and the first argument to SetBlendingMode() was given as B_CONSTANT_ALPHA, then the RGB color of primitive figures, and their transparency, are both determined by the current high color. If the high color is an opaque green, then the figures drawn will be an opaque green. If the high color is a transparent blue, then the figures drawn will be a transparent blue.

The situation is different when drawing bitmaps (using the View::DrawBitmap() command). There are two reasonable ways of determining the transparency of pixels from a bitmap, and both are useful in different circumstances.

Note that when drawing using the drawing primitives, the B_PIXEL_ALPHA mode ends up behaving identically to the B_CONSTANT_ALPHA mode, because when using StrokeEllipse(), FillRect(), etc, the "source bitmap" is the single pixel which defines the current high color--that single pixel is tiled repeatedly to fill the desired area.

George Hoffman (who wrote the code to do alpha-mode drawing) gave a good discussion of the difference between B_CONSTANT_ALPHA and B_PIXEL_ALPHA modes in this newsletter article.


Alpha Overlay vs. Alpha Composite

It's finally time to take a look at the last setting which can affect drawing using the B_OP_ALPHA drawing mode. This last setting is set by the second argument to SetBlendingMode(); this argument can be either B_ALPHA_OVERLAY or B_ALPHA_COMPOSITE.

The large majority of users will only ever need to use the B_ALPHA_OVERLAY value. B_ALPHA_COMPOSITE is used for compositing (creating) transparent bitmaps offscreen, which will later be drawn directly into a view or over top of an opaque bitmap using the B_ALPHA_OVERLAY and B_PIXEL_ALPHA settings. To understand why the B_ALPHA_COMPOSITE mode is necessary, we first need to look at what drawing using B_ALPHA_OVERLAY does, and the implicit assumptions it makes.

Alpha Overlay

The primary assumption made by B_ALPHA_OVERLAY is that you are drawing a (probably) transparent shape or source bitmap onto an opaque target. Assume, for example, that we are drawing a single transparent blue pixel over top of a single red pixel using B_ALPHA_OVERLAY and B_OP_ALPHA. The red pixel is implicitly assumed to be opaque, and the final result color will be a weighted average of blue and red, with the weight being determined by alpha value of the blue pixel. A higher alpha value (the blue pixel is more opaque) will result in more blue and less red in the final color; a lower alpha value (the blue pixel is more transparent) will result in less blue and more red in the final pixel. The alpha value of the red pixel does not have any affect on the final color--because the red is assumed opaque, any associated alpha value that pixel has is meaningless. (In fact, if you are drawing the blue pixel directly onto a view, i.e. over top of an on-screen red pixel, then the red pixel does not even have an associated alpha value. Your monitor, after all, has red, green, and blue phosphors, but no "alpha" phosphor.)

Alpha Composite

Drawing in alpha overlay mode is what is usually desired, but there is another operation which is sometimes useful, "compositing". Compositing is the process of assembling two or more transparent bitmaps offscreen, with the intent that the result will later be drawn over top of an opaque image or bitmap. Intuitively, this is like creating a stained glass window; once you've made it, you can put it in front of many different scenes to get different effects. When compositing, the pixel alpha values of the target bitmap (the one on which you are drawing), as well as the pixel alpha values of the source bitmap, affect the final image. This is in contrast to the B_ALPHA_OVERLAY mode, where the alpha values of the target bitmap do not affect the appearance of the final image.

It's hard to get a grasp on this the first time you encounter it, so let's take a look at how the B_ALPHA_COMPOSITE mode is used in one place internally in the BeOS; the Tracker, which uses it to generate the transparent versions of icons which are dragged around. Yes, you are about to get a peek into the innards of the BeOS! Due to the sensitive nature of this information, this HTML page will self-destruct in thirty seconds :-). The following function creates a new, partially transparent bitmap from an existing, completely opaque bitmap, by using alpha compositing.


	DraggableIcon::MouseDown(BPoint pt)
	{
		BRect rect(Bounds());
		/* This is the bitmap we'll be drawing into--the _target_ bitmap */
		BBitmap *dragBitmap = new BBitmap(rect, B_RGBA32, true);
		dragBitmap->Lock();
		/* We'll draw into the target through a view we attach to the target. */
		BView *view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
		dragBitmap->AddChild(view);
		/* A few finicky details to take care of before we get to the interesting part... */
		view->SetOrigin(0, 0);
		BRect clipRect(view->Bounds());
		BRegion newClip;
		newClip.Set(clipRect);
		view->ConstrainClippingRegion(&newClip);
	
		/* Set the high color to transparent black. The fact that it's black doesn't
			matter--that won't affect the outcome. It's the alpha setting that's important */
		view->SetHighColor(0, 0, 0, 0);
		/* Fill the target with completely transparent black. */
		view->FillRect(view->Bounds());
		/* Now, change into alpha drawing mode to accomplish the compositing */
		view->SetDrawingMode(B_OP_ALPHA);
		/* We'll be drawing the source bitmap using B_CONSTANT_ALPHA, i.e. the alpha value
			of the high color will be used as the alpha value for all of the pixels in
			the source bitmap. Set this to be half-transparent. */
		view->SetHighColor(0, 0, 0, 128);	
		/* Set the blending modes appropriately before drawing the source bitmap (the original, opaque icon) */
		view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
		/* Now, draw (composite) the source  icon bitmap over top of the transparent black target
			bitmap. This will modify the target bitmap to produce the desired result bitmap.
			The colors in the result bitmap will be calculated as follows:
				1) They will be a weighted sum of the background color (black) and the 
					color coming from the source bitmap pixels. Weighting is by the alpha values,
					i.e. the black from the target bitmap will be weighted by the alpha values
					in the target bitmap, and the pixels from the source bitmap will be weighted
					by the alpha of the current high color...
				2) But, since the alpha values in the target bitmap are 0 (completely
					transparent), the weighting given to the target pixels is 0, and they
					do not contribute at all to the final colors. The RGB values in the result
					bitmap are therefore just the RGB colors in the source bitmap.
				3) The alpha values in the result bitmap are calculated from the alpha values
					of the source and target bitmap. Generally speaking, the opacity of
					a result pixel will be greater than or equal to the most opaque of the
					corresponding source and target bitmap pixels. This is in accord with intuition--
					if you put two partially transparent pieces of glass on top of one another, the result is still
					transparent, but less transparent (more opaque) than either of the two
					original pieces. In this particular case, since the target pixels are completely
					transparent, the result pixels end up with the alpha value of the source bitmap--
					which, because of the use of the B_CONSTANT_ALPHA blending mode, is
					just the alpha value of the current high color. 
					
			NOTE: "bitmap", below, is the _source_ bitmap, and is a member of the DraggableIcon class */
		view->DrawBitmap(bitmap);
		view->Sync();
		dragBitmap->Unlock();
		DragMessage(&message, dragBitmap, B_OP_ALPHA, pt, target.Target(0));
	}

The above feat could not be accomplished using the B_ALPHA_OVERLAY setting, because when doing alpha overlays of semi-transparent source bitmaps, the color of the target always affects the RGB values of theoutcome. In the code above, the fact that the target was black was irrelevant--it could've been blue or purple, but because it was transparent, it did not affect the RGB values of the result.


Miscellaneous Notes and Summary


The Interface Kit Table of Contents The Interface Kit Index


The Be Book,
...in lovely HTML...
for BeOS Release 4.5.

Copyright © 1999 Be, Inc. All rights reserved.

Text last modified June 5, 1999.