The Interface Kit Table of Contents | The Interface Kit Index |
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):
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.
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:
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.
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.
First,
BRect rect = myView->Bounds();
myView->SetHighColor(solidRed);
myView->FillRect(rect);
The above code will completely fill the view with pure red pixels. Anything previously displayed in the view will be overwritten. The effect is the same as if we had drawn with the "pureRed" color using B_OP_COPY as the drawing mode.
Next,
BRect rect = myView->Bounds();
myView->SetHighColor(translucentRed);
myView->FillRect(rect);
Here, the red we are using is only "half-opaque" (alpha value of 128), and the effect will be that though everything previously visible in the view will be given a strong red tint, it will still be visible. The view will appear as if it still holds whatever it previously displayed, but is being looked at through rose-colored glasses.
Finally,
BRect rect = myView->Bounds();
myView->SetHighColor(faintRed);
myView->FillRect(rect);
Here, because the high color is almost completely transparent, whatever is displayed in the view will simply be given a faint pink tint--as if we were looking at it through a piece of cellophane very slightly tinted with red.
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.
For example, if you are writing a game and want to simulate
"patchy fog", you could define a solid gray bitmap with varying levels of transparency.
Using this bitmap as a source bitmap, and drawing it over top of a scene using pixel-by-pixel
alpha transparency, would produce an ultimate on-screen image where parts of the scene
were more or less hidden by fog, depending on whether those pixels in the source bitmap
were more or less transparent. If you pass B_PIXEL_ALPHA
as the first argument to
SetBlendingMode()
, bitmaps
will be drawn in this manner.
SetBlendingMode()
with a first argument of B_CONSTANT_ALPHA
, and to set the high color to some
color (any RGB values) that has the desired alpha value. When this is done, a
call to DrawBitmap()
will draw the bitmap using the RGB value from the bitmap
pixels, but using the constant alpha value specified in the high color.
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.
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.
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.)
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.
B_OP_ALPHA
mode can only be used in B_RGB_32_BIT or B_RGB_16_BIT color spaces. To get transparency in other
color spaces, you can draw in a 16- or 32-bit color space, and then translate the final image into the appropriate
color space.
DrawBitmap()
), B_CONSTANT_ALPHA
will cause the transparency
of the bitmap to determined by the alpha value of the view's current high
color. B_PIXEL_ALPHA
mode means that the transparency of the bitmap
will be determined on a pixel-by-pixel basis, from the alpha value stored with
each pixel.
B_ALPHA_OVERLAY
is used to draw a transparent shape or bitmap over top
of an image that is implicitly assumed to be opaque. Colors in the final result are
determined by:
B_ALPHA_COMPOSITE
is used to create a transparent offscreen bitmap, by compositing
together existing bitmaps. When using this drawing mode, both the source and target bitmaps are assumed to have
meaningful alpha values associated with each pixel. The RGB colors in the final bitmap are a weighted mix of
the colors in the two contributing bitmaps, with the weights being determined by the alpha values of the
contributing bitmaps. The alpha values in the final bitmap will be determined by the alpha values in the
two contributing bitmaps; basically the final bitmap will always be at least as opaque as the most opaque
of the contributing bitmaps, and if both contributing bitmaps have alpha values greater than 0, then the
result bitmap will be more opaque than either of the contributing bitmaps (subject to rounding error). The Interface Kit Table of Contents | The Interface Kit Index |
Copyright © 1999 Be, Inc. All rights reserved.
Text last modified