The Becasso Add-On API V2.0

Introduction

This document describes version 2.0 of the API for designing add-ons for Becasso, Sum Software's Paint and Imaging application for the BeOS®.

Note that there are important changes in the API since version 1.5, and that these break compatibility.

Add-ons are loaded and linked to Becasso at run time. Becasso knows four types of add-ons: Filters, Generators, Transformers, and Capture Add-ons. There is no structural difference between the first three types, and it is up to the add-on itself to identify itself as either of these types at initialization time. This will allow Becasso to place the add-on under the corresponding menu item, and take appropriate action when the user selects the add-on.

There are, of course, functional differences between these three types of add-ons. Filters are probably the most common type of add-ons, comprising conventional blurring filters, Gaussian blurring filters, contrast enhancing filters, spread filters, despeckle filters, etc. Generators are add-ons that fill a given selection of the canvas with a new image, not necessarily as a function of the previous contents of the canvas. Examples would be a color gradient generator, a fractal generator, a plasma generator, or a simple texture painting add-on. Transformers, finally, generate a new image by performing some kind of operation on the existing canvas, not necessarily in the local way in which a filter does. Examples include add-ons that zoom or shrink a given selection, rotate it, flip it, or warp it in some special way.

One special feature of the way Becasso interacts with its add-ons is that it opens the way to interactive operation of the add-ons. With most other image processing software, the user has to go through a process of setting parameters, waiting for the result to compute, decide whether it is as desired, and if not, adjusting the parameters and re-entering the cycle. Becasso tries to shorten this process by showing the effects of a change in parameters directly on the canvas, if only in a 'preview' version.

When a filter is opened by the user, for example, a small rectangle is shown on the canvas, the contents of which are continuously fed through the filter. If the filter comprises lengthy calculations that would hurt responsiveness, it can supply a 'preview' version of its algorithm and only use the 'real' one when the user is satisfied with the settings.

To give another example, the Gradient generator can generate color gradients continuously as the user drags the mouse over the canvas and can visually determine when the gradient is placed just right.

Especially for add-on developers, there are command line options for Becasso which respectively disable add-on loading alltogether (the -x switch) or to enable "verbose" add-on loading (the -v switch). When launched from a terminal, Becasso will print out the names of each add-on loaded, and print out a report on the initialization of each.

Structure of a Becasso Add-On

A Becasso add-on needs to export the following five functions:
status_t addon_init (uint32 index, becasso_addon_info *info);
status_t addon_exit (void);
status_t addon_make_config (BView **view, BRect rect);
status_t process (Layer  *inLayer,  Selection  *inSelection,
                  Layer **outLayer, Selection **outSelection,
                  int32 mode, BRect *frame, bool final, BPoint point, uint32 buttons);
With version 2.0 of the API, the distinction between Capture add-ons and Editor add-ons has widened. A Capture add-on must export
BBitmap *bitmap (char *title);
instead of the process() function, and also export
status_t addon_open (BWindow *client, const char *name);
There are also several optional hook functions which an add-on may export:
void addon_color_changed (void);
void addon_mode_changed (void);

status_t addon_persist_settings (BMessage *settings);
status_t addon_apply_settings (BMessage *settings);
These are documented below. It is easiest to take one of the example add-ons and work from there as a framework. There is probably lots of code you can simply leave in place as some tasks are shared by all add-ons.

A Day in the Life of an Add-On

When an add-on is first loaded into memory (which is one of the first things Becasso does when it is launched), it's addon_init() function is called by Becasso. The index parameter is a unique number for each add-on. It is used in further 'correspondence' with Becasso. The info parameter is a special struct that needs to be filled in with all kinds of information about the add-on. The becasso_addon_info struct is defined as follows:
typedef struct
{
    char   name[80];         // The name as it appears in the menu
    uint32 index;            // A unique index assigned at init time
    int    type;             // Filter, Transformer or Generator
    int    version;          // Version of the add-on
    int    release;          // Release of the add-on
    int    becasso_version;  // Required Becasso version (other won't load)
    int    becasso_release;  // Written for release (older will warn)
    char   author[128];      // Author (company) of the add-on
    char   copyright[128];   // Copyright notice
    char   description[128]; // Explains what the add-on does
    uint8  does_preview;     // bitmask for various preview notifications
    uint32 flags;            // bitmask
} becasso_addon_info;
So the typical addon_init() function of an add-on looks like this:
status_t addon_init (uint32 index, becasso_addon_info *info)
{
    strcpy (info->name, "Blur");
    strcpy (info->author, "Sander Stoks");
    strcpy (info->copyright, "(c) 1997-2001 Sum Software");
    strcpy (info->description, "Applies a 3x3 blur kernel convolution");
    info->type            = BECASSO_FILTER;
    info->index           = index;
    info->version         = 1;
    info->release         = 0;
    info->becasso_version = 2;
    info->becasso_release = 0;
    info->does_preview    = PREVIEW_FULLSCALE;
    info->flags           = 0;
    return B_OK;
}
Most entires in the addon_info structure are self-explanatory.
The type is one of BECASSO_FILTER, BECASSO_TRANSFORMER, BECASSO_GENERATOR, or BECASSO_CAPTURE.

The does_preview can be set to

NULL
(if the add-on can't generate previews - this should be avoided)
PREVIEW_FULLSCALE
this designates the add-on can generate "live" previews
PREVIEW_2x2
should be used for very computationally expensive add-ons; Becasso will generate a 2x2 scaled down version of the current layer and pass that to the add-on
PREVIEW_MOUSE
Becasso will pass mouse events on to the add-on (for interactive previewing, like in the Gradient and Ripple add-ons). If it is not set, Becasso will change the shape of the cursor while the add-on is open to signal the user that using the mouse on the canvas is "blocked" (such as in the Tile and Wave add-ons)
LAYER_AND_SELECTION
should be set when an add-on modifies the layer and selection at the same time (such as the Scale add-on)

The flags bitmask can be used to signify certain aspects of your add-on. For the v2.0 API, the only values are LAYER_ONLY and SELECTION_ONLY. These are mutually exclusive, and should be set when your add-on only makes sense for either layers or selections, respectively (an example would be the BumpMap add-on, which uses the selection to modify the layer, but doesn't make sense when operated in Selection Mode).

The becasso_version and becasso_release can be used if your add-on uses functionality that was added in a specific version of Becasso. If an older version of Becasso finds an add-on designed for a newer version, it will skip loading that add-on because it might be incompatible. It is therefore a good idea to set these values to the lowest version of Becasso which will be able to use your add-ons.

Note to developers familiar with the v1.5 API: The addon_init() function used to be the place to initialize the window for the addon settings. You derived this window from the generic AddOnWindow, and start the window thread at the end of the addon_init() function by calling Run() on it. This has changed for the v2.0 API. The reason is that every add-on otherwise would have its own window (a leftover from the early days of Becasso, when multiple add-ons could be opened simultaneously; this feature was removed because it didn't make much sense), and thus had its own thread. When you had lots of add-ons, that meant there would be dozens of window threads being spawned.
When you manage to complete your initialization successfully, you signal this to Becasso by returning B_OK. If anything else is returned, Becasso will print a warning message and leave the add-on alone.

The next time the add-on hears from Becasso is when the user selects it from the menu. In that case, Becasso will call addon_make_config(), so the add-on can provide Becasso with its own configuration view which Becasso will then add to a window, add some common buttons and a status bar, and show on screen. Typically, an implementation of addon_make_config() looks like this:

status_t addon_make_config (BView **view, BRect rect)
{
    *view = new ConfigView (rect);
    return B_OK;
}
where it is assumed that you have defined your own ConfigView class (derived from BView). If your add-on has no controls or parameters, you may simply set view to NULL. The rect parameter is only passed as a convenience. It's width will be set to the default add-on window width (don't rely on the height). You are free to resize your view to however big or small you like. Becasso will enlarge it to a minimum width if you make it too small, though.

Note that since you have no access to the add-on window's MessageReceived() and are expected to override the view's MessageReceived() to take care of any messages sent by your controls, the target of your controls needs to be set to the view. You can't do this in the view constructor, because obviously the view "doesn't exist yet" at that point. Therefore, you would override AttachedToWindow() to iterate over your controls and call SetTarget(this) on them. If you have an intricate custom view with child views that have their own MessageReceived() hooks, you still need to do this. For most common cases, you don't need to worry about the target of your controls. Becasso, after attaching your view to the add-on window, will take care of iterating over your entire view hierarchy, setting the target of any BControls and BMenuFields (i.e. popup menus) to the view itself. (This should take care of all interface widgets - if you need any other kind of views added here, please let us know.).

If your add-on uses things like temporary lookup-tables, the best time to set them up would be in addon_make_config() (because that is always called just before the add-on window is opened and the add-on is ready to use).

Note to developers familiar with the v1.5 API: the addon_open() function is now only used for Capture add-ons - see below.
See also: addon_refresh_config() and the ADDON_RESIZED message.

When the user is done with the add-on, Becasso will call addon_close(). If you had allocated lookup tables in addon_make_config() and you want to get rid of them before going to sleep, this is the time to do it. Otherwise, simply return B_OK. (If you return a non-zero error code from this function, Becasso will print it out if ran from a Terminal, for debugging purposes.)

When Becasso exits, it calls the addon_exit() function of each add-on. Most add-ons can simply return B_OK here, but this provision is for add-ons that hold on to some kind of global resource (a Capture add-on would be very likely to do so). You don't have to clear the becasso_addon_info struct.

Of course, the real action happens when the user actually uses the add-on. There are various scenarios for that, but they all have in common that Becasso calls the process() function. This function has quite an array of parameters, which are detailed below:

Layer *inLayer

The current layer of the canvas.

Selection *inSelection

The current selection map, if a selection is made, and NULL if there isn't. In that case, the default action of an add-on is to operate on the entire canvas.

Layer **outLayer

This can point to a valid Layer, or be NULL. In the first case, the add-on must write its results in the given layer. If it is NULL, the add-on can choose whether to allocate a new Layer, point outLayer to inLayer, or overwrite the data in inLayer directly and leave outLayer NULL. Becasso will handle each of these cases.

Selection **outSelection

Ditto as above.

int32 mode

The current drawing mode of Becasso. This is either M_DRAW or M_SELECT, when BecassoBecasso is in drawing or selection mode, respectively. An add-on should modify the part of the layer that corresponds to the selection map only when mode is M_DRAW, and modify the selection map itself when mode is M_SELECT.

BRect *frame

This contains the minimal rectangle in which the current selection fits, or the entire bounds of the canvas when there is no selection made. Most add-ons can ignore frame as it is mainly useful for speedups, since not the entire selection map needs to be checked. Note that it is the responsability of the add-on that frame contains the 'dirty' rectangle upon exit, as only that part of the canvas will be refreshed. The add-on can also alter frame when it has changed the minimal rectangle, for instance when a transformer has enlarged part of the canvas. Even in that case, it cannot exceed the bounds of the canvas.

bool final

This variable controls whether the add-on should try to save time by using a 'preview version' of its algorithm (a.k.a. 'quick-and-dirty'). For example, anti-aliasing of the result should be switched off then final is false. A more detailed description of what should be done when is given below.

BPoint point

In interactive operation of the add-on, this contains the current mouse position.

uint32 buttons

In interactive operation of the add-on, this contains the current state of the mouse buttons.

As always, the return value should be zero unless something strange happened underway.

Calling Scenarios

Under different circumstances, the process() function of an add-on is called with different parameter meanings. Most of the information about the desired behavior can be extracted out of the inSelection, mode, and buttons parameters.

When inSelection is NULL, this means that the user hasn't made a selection. The default operation of an add-on is to apply to the entire canvas.

When final is false, the add-on is asked to provide a quick-and-dirty 'preview' of its working.

When the add-on is operated interactively, it depends on the type of add-on what happens. For filters, simply a small portion of the current layer is passed to process(), and the final flag is set to false. This happens everytime the user moves the preview rectangle by ALT-dragging the mouse across the canvas, or whenever the contents of the preview rectangle are changed (for instance when the user draws through it with a drawing tool).

For generators and transformers, the interactive operation is more elaborate. The add-on then receives a continuous stream of process() calls with the current mouse position in point and the button mask in buttons. During this time, the final flag is set to false. When the user releases the mouse button, a final call to process() is made with buttons set to zero but final still false. This allows for a three-way granularity in image quality: when a generator or transformer is very time-consuming, it might be better not to start the calculation in final quality until the user clicks the Apply button on its window. Of course, this only happens when you told Becasso that you're interested in mouse events at all, by setting the PREVIEW_MOUSE flag in addon_init().

To preserve the orthogonality of the user interface of Becasso, add-ons should be prepared to operate on either the current layer or the current selection. Sometimes it is more intuitive to operate on both (for instance when a transformer scales the selected part of the canvas, it should also scale the selection itself). Of course, it doesn't make sense for all add-ons to operate on the selection – the BumpMap add-on for instance uses the selection to modify the actual layer contents, but would not make any sense when called upon the selection itself.
Note that you should be prepared to handle either case (by checking the mode and simply returning if it's not to your liking), even if you told Becasso about your preference via LAYER_ONLY or SELECTION_ONLY in addon_init().

Since filter calculations can sometimes be quite complex and lengthy, it is good practise to keep the user informed of what's going on. Becasso will signal activity by means of the rotating crosshair cursor, but this only signals that work is being done, not how much longer the user needs to wait for the result.
Add-on windows contain a progress bar, which is a standard BStatusBar object. Furthermore, there is a "Stop" button which the user can press if (s)he finds the calculations to take too long, and the add-on should check whether this button is pressed every now and then (for instance, at the same time as the progress bar is updated). This button starts out in disabled state (since there is nothing to stop!) so the add-on should signal when it starts a complex and lengthy calculation by calling the addon_start() function which Becasso exports.
The progress bar can be updated simply by calling the addon_update_statusbar (float delta) function exported by Becasso, where delta is a float that determines the amount of work done since the last call.
If the user has clicked the "Stop" button in the meantime, the addon_stop() function will return true. The best place for the update/checking is in the outermost loop of the calculation. Don't update the bar for every pixel calculated, as this would have far too much overhead. And also don't use the progress bar if process() is called with final set to false, otherwise the bar would be flickering constantly as the user moves the preview rectangle across the canvas.

Lastly, when the add-on is finished, the add-on should call addon_done() to reset the progres bar and disable the "Stop" button.

Note to developers familiar with the v1.5 API: These new functions exported by Becasso replace the old way of doing things with methods of the AddOnWindow class.
The possible return values for the process() function are
ADDON_OK
Everything went fine
ADDON_ABORT
Processing was aborted, either because you detected that the user pressed the "Stop" button (via addon_stop()), or because you decided to abort processing yourself for some reason. Becasso will discard the partly processed Layer and/or Selection.
ADDON_UNKNOWN
Return this in case you stumble across any unexpected data (such as a new drawing mode added in a later version of Becasso).
In summary, a typical process() function of a filter addon would look something like this:
status_t process (Layer *inLayer, Selection *inSelection,
                  Layer **outLayer, Selection **outSelection, int32 mode,
                  BRect *frame, bool final, BPoint point, uint32 buttons)
{
    int error = ADDON_OK;
    if (*outLayer == NULL && mode == M_DRAW)
        *outLayer = new Layer (*inLayer);

    if (*outSelection == NULL && mode == M_SELECT)
        *outSelection = new Selection (inLayer->Bounds());

    if (*outLayer) (*outLayer)->Lock();
    if (*outSelection) (*outSelection)->Lock();

    int h = frame->Height();
    int w = frame->Width();

    float delta = 100.0/h;  // For the progress bar

    if (final)
        addon_start();

    switch (mode)
    {
    case M_DRAW:
        for (int y = 0; y < h; y++)
        {
            if (final)
            {
                addon_update_statusbar (delta);
                if (addon_stop())
                {
                    error = ADDON_ABORT;
                    break;
                }
            }
	    for (int x = 0; x < w; x++)
	    	do_the_calculation();
        }
        break;
    case M_SELECT:
	// Likewise...
        break;
    default:
        fprintf (stderr, "MyAddOn: Invalid mode\n");
        error = ADDON_UNKNOWN;
        break;
    }

    if (*outSelection) (*outSelection)->Unlock();
    if (*outLayer) (*outLayer)->Unlock();

    if (final) addon_done();

    return (error);
}
For a generator or a transformer add-on, there would be code like
status_t process (...)
{
    static BPoint firstpoint = BPoint (0, 0);
    static BPoint lastpoint  = BPoint (0, 0);
    static bool entry = false;

    if (!entry && buttons && !final)  // First entry of a realtime drag
    {
        firstpoint = point;
        entry = true;
    }
    if (!final && !buttons)  // Exit a realtime drag (buttons released)
    {
        lastpoint = point;
        entry = false;
    }

    // Lots of code

    if (final)           // Apply clicked
        slow_generator (firstpoint, lastpoint);
    else if (!buttons)   // End of a realtime drag
        medium_generator (firstpoint, lastpoint);
    else                 // We're in a realtime drag here...
        fast_generator (firstpoint, point);

    // Some more code
}
As a final summary of the "program flow" between Becasso and an add-on, the following schema might clarify (by Michael Pfeiffer).

Capture add-ons

Capture add-ons are somewhat different from edit add-ons in that they need to export a different set of functions, and that the user can have multiple add-ons open at the same time. Capture add-ons don't export addon_make_config(), and instead of process(), they export BBitmap *bitmap (char *title). Also, they do export status_t addon_open (void). This function is called when the user selects the add-on from a menu. You are relatively free in what you do in this function - most likely, you will be instantiating and opening your own window. However you capture, grab, or generate images is your own business - whenever you're ready (when the user presses a "Grab" button for instance) you send a BMessage (CAPTURE_READY) to Becasso with your index added, and it will take care of the rest (involving calling your bitmap() function to collect the actual bitmap). See the included CaptureTest.cpp for an example.

Becasso Support for Add-Ons

Add-ons link against a copy of the Becasso executable, renamed _APP_. Becasso exports a number of classes and useful functions for use in your add-ons.

Note to developers familiar with the v1.5 API: The AddOnWindow class is obsolete.

Optional Hook Functions

void addon_color_changed (void)
This hook function is called (if you provide it) when either the foreground or background color, or the current pattern has changed. Note that Becasso will also call process(), so in most cases that should suffice even if you use either of these. This hook is only useful if you cache these colors somehow (or use them as the basis of a calculation you do outside of process().
void addon_mode_changed (void)
This hook function is called (if you provide it) when the current mode has changed. Note that Becasso will also call process(), so in most cases that should suffice. In some cases though, you might want to change certain aspects of your UI (in that case, you can call addon_refresh_config(). See also currentmode().
The following two optional hook functions are for persistance of filter settings. Support for this was added in Becasso version 2.3

status_t addon_persist_settings (BMessage *settings)

This hook function is called when the add-on is about to be unloaded. You should add your add-on specific settings to the BMessage. These should be simple name-value pairs; allowed are strings, int32 values, and float values. For an example, see the OilPaint add-on sample code.
status_t addon_apply_settings (BMessage *settings)
This hook function is used to re-apply persisted filter settings. For an example, see the OilPaint add-on sample code.

Becasso-Exported Add-On Functions

void addon_start (void)
Call this when you are about to start doing processor-intensive stuff in process(). It will reset the status bar and enable the "Stop" button on the add-on window.
bool addon_stop (void)
Check the return value of this function periodically inside your main process() loop (for example, in the outer loop iterating over the rows of pixels - better not in the inner loop). A good place to do this is right next to addon_update_statusbar(). If this function returns true, the user has clicked the "Stop" button, and you should end your operation with ADDON_ABORT.
void addon_done (void)
Call this function at the end of a normal run of your process() code. This will disable the "Stop" button again.
void addon_update_statusbar (float delta, const char *text = NULL, const char *trailingText = NULL)
Update the status bar by "delta" (note that the statusbar uses percentages, so you should have it update from 0 to 100). This function should only be used for a final, high-quality rendering of your add-on, and not in the real-time preview.
void addon_reset_statusbar (const char *label = NULL, const char *trailingText = NULL)
Use this to set the status bar in the add-on window back to zero. Note that addon_done() does this for you.
void addon_preview (void)
Call this function to request a new preview. You generally do this after your parameters have changed (you would probably see this function being called in your ConfigView's MessageReceived() method).
void addon_refresh_config (void)
Call this function if you want to change your GUI in a more or less drastic way. For example, your add-on could have different parameters depending in whether it is being run in selection mode or in drawing mode, and you could call this function from the addon_mode_changed() hook to let Becasso know. It will then get rid of your old config view and call addon_make_config() for your new one. If you were able to take care of refreshing your view yourself but have resized it, you can inform Becasso with the message below.
The ADDON_RESIZED message
If your add-on has changed its config view for some reason (for instance, because you have a popup selecting a multitude of different options, or because you were informed of a mode change (through addon_mode_changed()) and have changed your UI as a result), you can inform the add-on window by sending a simple ADDON_RESIZE message. This can be as simple as
    ResizeTo (newWidth, newHeight);
    Window()->PostMessage (ADDON_RESIZE);
Note that you must make sure your view has the right risizing mask. If that contains B_FOLLOW_RIGHT and/or B_FOLLOW_BOTTOM, your view will resize twice, with undesirable results.

Layer

A layer is in fact nothing more than a BBitmap in the B_RGB_32_BIT color space, along with some additional variables. There are more methods in the Layer class than are explained below, for instance for setting whether the layer is hidden or visible, but these should not be called from within an add-on.
Defined in: Layer.h
Layer (BRect bounds, const char *name)
Layer (const Layer& layer)
The first constructor takes a rectangle for size and a name as it will appear in the Layer Manager window of Becasso. The second version takes an existing Layer and models the new one after it (i.e. copy the name, size, and all other settings). Note that there is no color space parameter; Becasso layers are always 32 bit BGRA bitmaps. For convenience, AddOnSupport.h defines bgra_pixel as the type for the pixels in a Layer.
Note that the order in which the channels are stored is blue, green, red, alpha, i.e. not RGBA!
void ClearTo (bgra_pixel p)
Clears the entire layer to the value given by p.

Selection

A Selection is a BBitmap in B_GREYSCALE_8_BIT color space, although the values are not meant to represent colors in the BeOS palette. It was B_COLOR_8_BIT in Becasso 1.3 due to a bug introduced in BeOS R3, but this was restored to B_GREYSCALE_8_BIT since R4.5 fixed this problem, and it reflects the nature of a selection more truthfully. Being an 8bit bitmap means that its width in pixels need not necessarily be the same as its width in bytes, due to padding. Be sure to take this into account when using the raw bitmap data! Becasso more or less treats a Selection map as a separate alpha channel. When a given pixel is non-zero, the corresponding pixel in the current layer is selected. Becasso does not use a simple binary selection map, so every pixel can have 255 values of 'selectedness', which come into play for instance when the selection is cut and pasted. An add-on should also take this into account whenever appropriate, i.e. a value of 0 stands for 'leave the corresponding pixel as it was', and 255 means 'maximum appliance of the filter'.
Defined in: Selection.h
Selection (BRect bounds)
Selection (const Selection &selection)
AddOnSupport.h defines grey_pixel as a type for the pixels in a selection map.
void ClearTo (grey_pixel p)
Clears the entire selection map to the value given by p.

Slider

Becasso also exports a Slider class, which is convenient for settings in an add-on window. Since R3, the BeOS also has a slider class (and a TabView, see below) but these classes were added to Becasso before that, and should still be used to keep the same look and feel across add-ons.
Defined in: Slider.h
Slider (BRect frame, float sep, const char *name,
        float min, float max, float step,
	BMessage *msg, orientation posture = B_HORIZONTAL,
        int size = 0, const char *fmt = "%.0f")
Slider is derived from BView. The frame parameter is self-explanatory; the sep parameter is the width in pixels that will be taken by the text label name. The minimum and maximum values are designated by min and max, and step is the minimum change of value. Whenever the value of the slider is changed by the user, either by dragging or by clicking next to the knob, the given msg will be sent, with a float entry with the name "value". Although the presence of a B_HORIZONTAL default suggests the possibility of vertical sliders, this is presently unimplemented. The parameter size designates the size of the slider knob. If zero (the default) it is adjusted to the maximum value. The final parameter is a format string, like in the printf argument, which designates how the value should be written in the knob.
float Value ()
This returns the current value of the slider.
void SetValue (float v)
With this method, you can set the value of the slider from within your code. The slider defaults to the minimum position. Calling SetValue does not result in a message being sent.

TabView

A TabView is the set of "overlaying" views as visible in, for instance, the Fill tool attribute window. You can use these tabviews in your own add-on windows as well.
Defined in: TabView.h
TabView (BRect frame, const char *name,
         uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP)
TabView is derived from BView. The parameters are the same as with a normal BView. To aid in the layout of your views, there is a value TAB_HEIGHT defined which is the extra height you should take into account, which is taken by the tabs themselves.
void AddView (BView *view, const char *tab)
This method adds the view to the TabView. The tab parameter designates the name to be drawn in the tab; it should not be more than MAX_TAB characters in length.
void RaiseView (int n)
By calling this method, you can "raise" one of the tabviews manually, but this is seldom needed. Once added to the tabview, everything is handled fully automatically. The tabs are numbered in the order in which they are added.

Miscellaneous

Becasso exports a variety of convenient functions for add-ons. They are defined in the AddOnSupport.h file and are documented below.
bgra_pixel average4 (bgra_pixel a, bgra_pixel b, bgra_pixel c, bgra_pixel d)

bgra_pixel average6 (bgra_pixel a, bgra_pixel b, bgra_pixel c,
                     bgra_pixel d, bgra_pixel e, bgra_pixel f)

bgra_pixel average9 (bgra_pixel a, bgra_pixel b, bgra_pixel c,
                     bgra_pixel d, bgra_pixel e, bgra_pixel f,
                     bgra_pixel g, bgra_pixel h, bgra_pixel i)
Since pixel data packed in a 32 bit integer cannot simply be manipulated with the normal operators, Becasso offers these functions for the often-used case of averaging entire pixel values. The Blur filter code uses these functions quite extensively. These functions are highly optimized. See the Tips and Tricks Department below.
bgra_pixel pixelblend (bgra_pixel d, bgra_pixel s)
This function returns an bgra_pixel in which the two arguments are blended, taking the alpha channel of s into account. An alpha value of 0 means that s is fully transparent, so the result will be d. When the alpha value of s is 255, it is fully opaque, so there won't be any d shining through. Any intermediate value results in the corresponding mixing of the colors.
uint8 clip8 (int32 c)
Returns the value of c, clipped to the range 0..255.
rgb_color highcolor (void)
rgb_color lowcolor (void)
Return the currently selected high and low colors (in the corresponding menu buttons of the Becasso main menu).
pattern currentpattern (void)
Returns the currently selected pattern from the rightmost menu button of the Becasso main window.
int32 currentmode (void)
Returns the currently selected mode of Becasso (either M_DRAW or M_SELECT). Note that this is passed to the process() function as a parameter, but you might need this function if your add-on changes its UI depending on the mode. You are informed about mode changes while your add-on is open by providing an addon_mode_changed() hook (see Optional Hook Functions, above).
rgb_color *highpalette (void)
rgb_color *lowpalette (void)
Return the current foreground and background palettes, respectively. Note that you receive a pointer to a copy of the palette, which means you are free to do with it whatever you please, as long as you delete [] it yourself.
int highpalettesize (void)
int lowpalettesize (void)
Return the number of colors in the foreground and background palettes, respectively. This is currently always 256, but who knows what might happen in the future.
rgb_color closesthigh (rgb_color a)
rgb_color closestlow (rgb_color a)
Return the color from the foreground or background palette, respectively, which Becasso thinks is closest to the given color a.
rgb_color contrastingcolor (rgb_color a, rgb_color b)
Returns a color that contrasts with both a and b. An illustration of its use is given in the Gradient generator example code, where a color is needed to contrast with both 'end points' of the gradient. The default color is red, unless one (or both) of a and b is 'reddish', in which case either black or white is returned, depending on the average 'brightness' of a and b.
bgra_pixel weighted_average     (bgra_pixel a, uint8 wa, bgra_pixel b, uint8 wb)
bgra_pixel weighted_average_rgb (rgb_color  a, uint8 wa, rgb_color  b, uint8 wb)
These functions return a color that is the weighted average of a and b, via the formula
result = (a*wa + b*wb)/(wa + wb),
naturally taking the separate color channels into account.
int Scale (BBitmap *src, BBitmap *srcmap, BBitmap *dest, BBitmap *destmap)
This function performes an anti-aliased scaling (using MMX if available). src and dest are BBitmaps in B_RGB_32_BIT color space, and srcmap and destmap are BBitmaps in B_COLOR_8_BIT color space. Either src and dest, or srcmap and destmap can be NULL, in which case only the other bitmap is scaled. If all arguments are non-NULL, a 32 bit bitmap and an 8 bit bitmap (for instance a Layer and its corresponding Selection) can be scaled with one call.

This function can handle arbitrary scaling factors, both enlarging or shrinking, and with different horizontal and vertical factors. See the Scale add-on sample code for an example. Note that the Be-supplied DrawBitmap call offers similar functionality, but that this one does not anti-alias, which has considerable impact on the result. Anti-aliasing does slow the process down considerably, so for realtime preview it is recommended you use the method with DrawBitmap.

void AddWithAlpha (BBitmap *src, BBitmap *dest, long x, long y)
Both src and dest must be 32 bit BBitmaps. The dest bitmap is alpha blended on the src bitmap, at the coordinates (x, y) in the coordinate space of src. The bitmaps need not be of the same size and dest need not be contained within src, i.e. the necessary clipping is performed.

This function is actually used by Becasso to paste a selection.

void CutOrCopy (BBitmap *src, BBitmap *dest, BBitmap *selection,
                long offx, long offy, bool cut)
This function expects src and dest to be 32 but BBitmaps, and selection to be an 8 bit selection map. It copies src selectively into dest via the selection map, i.e. the resulting alpha channel is taken from the selection map. The copying starts at offset (offx, offy) and fills the entire dest bitmap. If cut is true, the selection is also 'cut' (i.e. deleted) from src.

This function is actually used by Becasso to cut/copy a selection. If selection == NULL, Becasso assumes a full selection.

rgb_color hsv2rgb (hsv_color c)
rgb_color bgra2rgb (bgra_pixel c)
rgb_color cmyk2rgb (cmyk_pixel c)
hsv_color rgb2hsv (rgb_color c)
hsv_color bgra2hsv (bgra_pixel c)
hsv_color cmyk2hsv (cmyk_pixel c)
bgra_pixel cmyk2bgra (cmyk_pixel c)
bgra_pixel rgb2bgra (rgb_color c)
bgra_pixel hsv2bgra (hsv_color c)
cmyk_pixel bgra2cmyk (bgra_pixel c)
cmyk_pixel rgb2cmyk (rgb_color c)
cmyk_pixel hsv2cmyk (hsv_color c)
These functions convert pixels between various color spaces. The types bgra_pixel and cmyk_pixel are actually uint32's, with the color components in there as indicated by the name.
float diff (rgb_color a, rgb_color b)
Returns the luminance distance between the two given colors, as used for example to determine the closest match in the palette.
uchar clipchar (float x)
float clipone (float x)
float clipdegr (float x)
These functions return the value passed in, clipped to the given range. I.e. 0 <= clipchar (x) <= 255, 0 <= clipone (x) <= 1, and 0 <= clipdegr(x) < 360.
Becasso further exports a number of rgb_color definitions, so that after including Colors.h, you can use Red, Green, Blue, Black, White, and several others.

Tips and Tricks Department

Apart from the usual optimizations (like putting the smallest loop of two nested loops on the outside), there are a number of specific tricks that can speed up your code dramatically. The example filters do not make heavy use of these kind of tricks since they often obfuscate the code. The functions exported by Becasso, as mentioned above, are quite optimized and some of the tricks might be handy for your own use.

Extracting color channels out of a bgra_pixel

It is not very wise to cast a bgra_pixel pointer to a char pointer and extract the channel data this way, because this would result in four consecutive byte memory reads. On most modern processors, extracting a single longword on a 4-byte aligned memory address is much faster. Unfortunately, the shifting and masking required to extract the color channels from a single 32-bit longword is CPU-specific. Becasso offers a set of macros that take care of this. You can then get at the respective color channels like so:
   bgra_pixel pixel = *pixelpointer;
   uint8 red   = RED (pixel);
   uint8 green = GREEN (pixel);
   uint8 blue  = BLUE (pixel);
   uint8 alpha = ALPHA (pixel);
These are actually macros, defined in BecassoAddOn.h, which take care of the Endianness issue when creating code that should compile on both Intel Architecture and PowerPC machines. Take a look in that header file for a lot more helpful macros.

Avoid casting float <--> integer

Whereas on modern processors there need not be any principal 'fear' for floating point arithmetic, it is very costly to constantly cast between floats and integers.

On PPC, pre-increment is faster than post-increment

Although a little bit less clear to read, it is a good idea to use pre-increment on PPC chips instead of post-increment, since the PPC can execute the pre-increment 'for free'. So it is smart to change your loops from
   bgra_pixel *src  = (bgra_pixel *) inLayer->Bits();
   bgra_pixel *dest = (bgra_pixel *) outLayer->Bits();
   for (uint32 i = 0; i < layer->BitsLength()/4; i++)
   {
      *dest++ = do_something_with_pixel (*src++);
   }
to
   bgra_pixel *src  = (bgra_pixel *) inLayer->Bits() - 1; // Note the -1 !
   bgra_pixel *dest = (bgra_pixel *) outLayer->Bits() - 1;
   for (uint32 i = 0; i < layer->BitsLength()/4; i++)
   {
      *(++dest) = do_something_with_pixel (*(++src));
   }

This documentation is © 1997-2001, Sum Software.
Last modification: 14 December 2001.
Be and the BeOS are registered trademarks of Be, Incorporated. Trademarks used herein belong to their respective owners.