TweetFollow Us on Twitter

Porting Graphics To BeOS

Volume Number: 14 (1998)
Issue Number: 9
Column Tag: BeTech

Porting Mac Graphics To The BeOS

Written by Dan Parks Sydow
Edited by Bob Boonstra

Translating your Mac application's drawing routines to BeOS code

Overview of Graphics and the BeOS

For the most part, the scope of this article is limited to explaining how to convert your Mac application's graphics-related Toolbox calls to code that integrates into a Be application. To get a more general overview of Mac OS to BeOS porting issues, refer to the MacTech Magazine article "Porting Code to the BeOS" by Michael Rutman, Vol. 13, No. 1 (January 1997). If you don't know the fundamentals of BeOS programming, you'll need to learn how to make use of what Be refers to as the software kits. How to work with the software kits is a topic well beyond the scope of a magazine article - you'll want to refer to one or more of the BeOS programming resources listed at the end of this article. With that said, I still feel compelled to devote just a few paragraphs to summarizing how to use the kits to program the BeOS! After that I move to the real matter at hand - how to go about translating some commonly implemented Mac OS graphics code to BeOS graphics code.

Structure of the BeOS

Be applications run on BeOS-compatible computers, which now include PowerPC-based Macs and most Intel Pentium-based PCs. Between the hardware and a Be application lies the BeOS operating system software that consists of three layers: a microkernel layer that communicates with the computer's hardware, a server layer consisting of a number of servers that each handle the low-level work of common tasks (such as printing), and a software kit layer that holds several software kits - dynamically linked libraries (DLLs) that act as a programmer's interface to the servers and microkernel.

Kits and classes

Collectively, the software kits make up the BeOS application programming interface (API). Each kit consists of a number of object-oriented classes that a programmer makes use of when writing a BeOS program. Together, all the classes in the kits form an application framework. While the code that constitutes your Be application will most likely include objects derived from classes defined in many of the kits, it will always include objects derived from classes in two kits: the Application Kit and the Interface Kit.

A Be application defines an object of a class you derive from the BApplication class. This application object represents the application itself. The BApplication class, obviously enough, is defined in the Application Kit. Other classes in this kit establish a messaging system that makes applications aware of events (such as a click of a mouse button by the user). This kit also give applications the power to communicate with one another.

The Interface Kit is by far the largest of the software kits. The classes of this kit exist to supply applications with a graphical user interface that fully supports user-interaction. The definition of windows and the elements that are contained in windows (such as scroll bars, buttons, lists, and text) are all handled by classes in this kit. Any program that opens a window uses the Interface Kit. Most of the classes mentioned in this article (such as the BRect class for creating rectangles, the BRegion class for creating regions, and the BWindow class for creating windows) are a part of the Interface Kit. In the area of graphics, one of the most important Interface Kit classes is the BView class. Instances of this class - and of the many classes derived from it - are all types of views.

Views

In Macintosh programming, drawing takes place within a port in a window. Somewhat analogous to that on the Be side is the view. In a Be program, drawing doesn't take place directly within a window (a BWindow object). Instead, it takes place within a view (a BView object, or an object derived from the BView class). Unlike a Mac port, however, Be views can be nested. Views aren't readily discernible to the end-user - they exist as a programmer's means of establishing different drawing areas. Typically a window will hold an all-encompassing view, and then one or more views within that main view. Each view is a self-contained drawing environment, so each view can display text and graphics that appear different from that displayed in other views within the same window. That is, each view can display text in its own font, draw lines in a thickness different from lines drawn in other views, and so forth. This technique of allowing each BeOS view to keep track of its own drawing environment deviates from the Mac OS, where drawing parameters are stored in QuickDraw globals.

Because all drawing must take place in a view, everything you see within a window appears in a view. A scroll bar, button, picture, or section of text each lies within its own view. The Be window titled "Hello" in Figure 1, for example, holds several views. Each radio button resides in its own view, as does the one pushbutton. The framed text also exists in its own view. There may also be one large view that holds all these individual views (you're looking at the window as an end-user, so you can't determine if that's true or not!).

Figure 1. A Be window holds a number of views.

A view is capable of responding to a message that's sent from the system to a BWindow object and then on to the view. This messaging system is the principle on which menus and controls work. It is also a topic worthy of much examination, and is beyond the scope of this article. There is one messaging topic worthy of note here, however - window graphics updating, or refreshing.

In the Mac world, a program becomes aware of the need to update a window when the program's event loop call to WaitNextEvent() obtains an event of type updateEvt from the system event queue. On the Be side, when a view needs updating a message is automatically issued by the system. This message is sent to the affected window, and from there it is directed to the proper view. If more than one of a window's views needs updating, more than one message will be sent to the window (one per affected view). The BView class (from which all views are derived) includes a Draw() member function that automatically gets invoked when a view object receives such a message. This Draw() function performs the drawing appropriate for that one view.

Drawing the contents of a BView is often application-specific. In such an instance it's up to your application to override the Draw() function of that view, and to supply the view-drawing code within the new version of the Draw() function. Consider the window shown in Figure 1. If the entire window is obscured, then brought back into view, a number of Draw() functions will be automatically invoked. A radio button is an object of the BRadioButton class, which is derived from the BControl class, which itself is derived from the BView class - so each radio button has a Draw() function. Similarly, the pushbutton is a view - the BButton class is also derived from the BControl class. Each of these three controls have a Draw() function that gets invoked. Fortunately for you, the programmer, these Draw() functions know how to draw the appropriate control without any help from your code. The framed text is of more interest to you. The rectangle and text reside in an object of an application-defined class derived from the generic BView class. Here the BView version of Draw() needs to be overridden and implemented such that it draws the desired contents. Most of the graphics-drawing code covered in this article will become a part of the Draw() function (or of an application-defined function invoked by Draw()) for one of your program's views.

Color

Hopefully your Macintosh application holds the user's attention by making heavy use of color. If it does, and you're interested in porting it to the BeOS, you're in luck - just about any use of color in your Mac program can be easily duplicated in its Be counterpart.

RGB colors

In the Mac OS, QuickDraw allows you to define colors using the RGB color space. In short, a color space is a scheme, or system, for representing colors as numbers. A QuickDraw color is made up of the combination of three color components: red, green, and blue. The Toolbox RGBColor data type consists of three 2-byte unsigned short fields - one for each color component. Each of the three color components is defined by a value with a range of 0 to 65,535. A component value of 0 means none of that component is present in the overall color, while a value of 65,535 means that the component is intensely present in the overall color. The BeOS is capable of defining colors using any of several color spaces, including the default system that defines colors using the RGB system. In Be's version of the RGB system, 32 bits are used to define the color of a single pixel. The Be data type rgb_color is a struct with four fields. Each field (red, green, blue, and alpha) is of type uint8 (an unsigned 8-bit integer), so each component can have a value in the range of 0 (no presence of that component in the color) to 255 (full presence). The remaining eight bits (the alpha field) are reserved for a future implementation of a byte that will be used to specify a transparency level for the overall color. When implemented, an alpha value of 0 will result in a color that is completely transparent, while an alpha value of 255 will result in a color that is completely opaque.

Converting a Mac color to a BeOS color is as easy as redefining your RGBColor variable as an rgb_color variable, scaling the value of each of the three color components accordingly (from a range of 0 to 65,535 on the Mac OS to a range of 0 to 255 on the BeOS). To establish a BeOS color you can assign values to the four fields of an rgb_color variable at the time of declaration. In the following snippet note that the unused alpha field is set to 255 in preparation for the time when Be supports transparency:

rgb_color   blueColor = {0, 0, 255, 255};      // {r, g, b, a}

You can also choose to assign each component individually at any time after the variable is declared:

rgb_color   redColor;

redColor.red   = 255;
redColor.green = 0;
redColor.blue  = 0;
redColor.alpha = 255;

Using a color

On the Mac, you typically make use of an RGBColor variable by passing it to the Toolbox function RGBForeColor() to set the foreground color - the color used in all subsequent drawing routines. On the BeOS, an rgb_color variable is most often used in the setting of two colors that a view keeps track of: the view's high color and low color. While these names seem to imply that the two colors must be quite different from one another, that needn't be the case. The high and low colors are simply two colors that can be used individually or mixed together in drawing operations. When drawing takes place in the view, you specify whether the current high color, the current low color, or a mix of the two colors should be used. The BView member functions SetHighColor() and SetLowColor() alter the current high and low colors of a view. Pass SetHighColor() an rgb_color and that color becomes the new high color - and remains as such until the next call to SetHighColor(). The SetLowColor() routine works the same way. This next snippet uses the just-defined redColor and blueColor variables to set a view's high color to red and its low color to blue:

SetHighColor(redColor);
SetLowColor(blueColor);

A call to one of the color-setting functions doesn't actually draw anything - it only sets two color characteristics of a view. Exactly how the high and low colors affect subsequent shape drawing is a topic best covered after the upcoming discussion on patterns.

Allowing the user to choose a color

Assigning numerical values to the red, green, and blue components of a color is something a programmer might not have a problem with - but the end-user shouldn't be forced to select a desired color using this somewhat unfriendly technique. On the Mac, users choose an RGB color by means of the Color Picker. The RGB Picker in the Appearance control panel provides an example of a typical use of the Color Picker. Your own Macintosh program can bring up the Color Picker at any time by calling the Toolbox function GetColor(). Analogous to the Color Picker on the Be is the color control. Your Be program creates an object of the BColorControl class in order to allow a user to select an RGB color - without the user knowing anything about the RGB color system or RGB values. What the BColorControl object displays to the user depends on the number of colors the user's monitor is currently displaying. The user can set that parameter by choosing Screen from the preferences menu in the Tracker. The Screen preferences window itself includes a BColorControl object, so that preferences window is worth examining. Figure 2 shows the Screen window for a monitor set to devote 8 bits to each pixel. Here the color control is the matrix, or block of squares, at the bottom of the Screen window. In Figure 3, with the monitor adjusted to use 32 bits for each pixel, the color control appears as the four horizontal bands at the bottom of the Screen window.

Figure 2. The Be Screen preferences, which includes a color control, at 8 bits per pixel.

Figure 3. The Be Screen preferences, which includes a color control, at 32 bits per pixel.

To include a color control in your own program, declare a BColorControl object and the variables that will be used as arguments to the BColorControl constructor. Then use new to create the new object:

BColorControl             *theColorControl;
BPoint                    leftTop(20.0, 50.0);
color_control_layout      matrix = B_CELLS_32x8;
long                      cellSide = 10;

theColorControl = new BColorControl(   leftTop, matrix,
                               cellSide, "MyColorCntl");

The leftTop argument indicates the pixel coordinate of the left-top corner of the color control. The matrix argument is of the Be-defined data type color_control_layout. When the user has the monitor set to use 8 bits per pixel, the 256 system colors are displayed in a matrix. This parameter specifies how these squares should be arranged. Use one of five Be-defined constants here: B_CELLS_4x64, B_CELLS_8x32, B_CELLS_16x16, B_CELLS_32x8, B_CELLS_64x4. The two numbers in the name of each constant represent the number of columns and rows, respectively, that the colored squares are to be placed in. For instance, the B_CELLS_32x8 constant will display the 256 colors in eight rows, with 32 colored squares in each row. The cellSide argument determines the pixel size of each colored square in the matrix. A value of 10, for instance, results in 256 squares that are each 10-pixels-by-10-pixels in size. The final argument provides a name for the color control object. The BColorControl class is derived from the BControl class, which in turn is derived from the BView class. Every view has a name that can be used elsewhere in your code to access the view. The name can be supplied using a string (as shown in the above snippet) or by passing in a constant variable that was defined as a character constant:

const char *name = "MyColorCntrl";

I've discussed the BColorControl constructor arguments as if they will be used when the user has the monitor set to use 8 bits for the display of color in each pixel. For the argument values used in the previous snippet, the resulting color control looks like the one displayed in the Screen window in Figure 2. If the user instead has the monitor set to 32 bits for each pixel, the same arguments are used in the display of four bands, three of which the user clicks on in order to create a single color (the top, gray band represents the currently unimplemented alpha, or color transparency level component). Here, instead of being used in the determination of the arrangement and size of a matrix of color squares, the arguments are now used to determine the shape and overall size the four bands occupy.

When a window displays a color control, the user selects a color by clicking on its cell (if the user's monitor is set to 8 bits per pixel) or by clicking on a color intensity in each of the three color component bands (if the user's monitor is set to 32 bits per pixel). In either case, the BColorControl object always keeps track of the currently selected color. Your program can obtain this color at any time via a call to the BColorControl member function ValueAsColor(). Here a program obtains the user's current color choice and saves it in the variable userColorChoice:

rgb_color   userColorChoice;

userColorChoice = theColorControl->ValueAsColor();

While devoting 32 bits per pixel to a color is a popular technique for defining a program's colors, note that Release 3 of the BeOS includes a 16 bits per pixel option that functions similarly to the 32 bit per pixel option discussed above.

Patterns

Both on the Mac OS and the BeOS, a pattern is an 8-pixel-by-8-pixel area that can be "poured" into any specified area in order to give that entire area a uniform look.

Be-defined patterns

The Mac OS provides five predefined patterns that are a part of the QDGlobals data structure qd: dkGray, ltGray, gray, black, and white. The BeOS has no analogous data structure, but it does supply you with three patterns that are always available for your program's use. Each of these patterns is represented by a Be-defined constant: B_SOLID_HIGH is a solid fill of the current high color, B_SOLID_LOW is a solid fill of the current low color, and B_MIXED_COLORS is a checkerboard pattern of alternating current high color and current low color pixels. Earlier you saw how to define a color and then use it to set the high or low color of a view. Now that you know about patterns, you're ready for an example of using an RGB color. This next bit of code draws a solid red rectangle:

BRect         theRect(20.0, 10.0, 220.0, 110.0); // l, t, r, b
rgb_color   redColor = {255, 0, 0, 255};

SetHighColor(redColor);
FillRect(theRect);

The above snippet introduces the BRect class. I cover this class in more depth ahead, but I've brought it into play now because I need to draw something here! The BRect class is used to create a BRect object, which serves to define a rectangle. Unlike most classes in the BeOS API, the BRect is thought of more as a primitive data type than as a class: it's not derived from any other class, all its data members are public, and it includes no virtual member functions. Typically, you create a rectangle object by simply declaring a BRect variable - you don't use new to allocate memory for the object. As shown above, assigning pixel coordinates to the rectangle can be done by listing them in the order left, top, right, bottom at the rectangle object's declaration. Like the Mac OS, the BeOS uses a coordinate system based on coordinate pairs: the top-left pixel is considered pixel (0.0, 0.0). Pixel numbering increases as you move to the right, and as you move down. Unlike in the Mac OS, pixels are floating point values in the BeOS. Integral BeOS coordinate values fall in the center of a screen pixel, unlike the Mac OS, where the coordinate value represents the upper left corner of a screen pixel. Again like the Mac OS, on the BeOS the screen itself is a global coordinate system, and each window has its own local coordinate system. Additionally, every view in a BeOS window has its own coordinate system.

In general, to draw in a view you call a BView drawing function. In particular, to draw a rectangle in a view you call the BView function FillRect(). Consider yourself forgiven if your first thought was that FillRect() must be a member function of the BRect class rather than the BView class! By default, FillRect() fills the specified rectangle with the high color. The above snippet sets the high color to red - so that's the color the rectangle theRect will be. You can also tell FillRect() to use a particular pattern (whether one of the three Be-defined patterns or an application-defined pattern) by including an optional second argument. This next snippet defines a blue color and uses it in the setting of the low color. The line that follows draws the rectangle theRect in blue:

rgb_color   blueColor = {0, 0, 255, 255};

SetLowColor(blueColor);
FillRect(theRect, B_SOLID_LOW);

Creating a pattern

For monochrome patterns on the Mac, you can use the above-mentioned qd patterns, the 38 patterns that make up the System pattern list, or define your own 'PAT ' pattern resources in a resource editor. For color patterns you can define your own 'ppat' pixel pattern resources. On the BeOS you define patterns within your source code. Using a variable of the data type pattern you specify whether each of the 64 pixels of a pattern should be filled with the current high color or current low color. Here's the pattern data type:

typedef struct {
   uchar   data[8];
} pattern;

To create a pattern you declare a pattern variable and initialize each of its eight fields with a hexadecimal pair. Each of the eight elements in the pattern array is one byte in size, so each can hold a single unsigned value in the range of 0 to 255. Each of the hexadecimal pairs in each of the eight rows in a pattern falls into this range (0x00 = 0, 0xff = 255). To create a pattern variable, determine the hexadecimal pairs for the pattern and assign the variable those values. In Figure 4 you see that I drew an 8-by-8 grid and filled in the squares that I want to be represented by the high color, and left blank the squares I want to be represented by the low color. The left side of the figure shows the binary representation of each row in the pattern, with a row looked at four bits at a time. The right side of the figure shows the corresponding hexadecimal value for each row.

Figure 4. The binary and hexadecimal representations of a pattern.

To convert a Macintosh pattern, view it in your Mac resource editor and determine which pixels you want "on" and which you want "off." Then write down the numerical representation of the pattern, as I did in Figure 4. At this time you won't be concerned with pattern color - just consider that an "on" pixel will be one color, and an "off" pixel will be a different color.

In Figure 4, the binary representation of each pixel row is for the benefit of showing how I came up with the hexadecimal row value. When initializing the pattern variable, use the hexadecimal values - like this:

pattern   stripePattern = {   0xcc, 0x66, 0x33, 0x99,
                              0xcc, 0x66, 0x33, 0x99};

Using a pattern

On the Mac, you make use of a pattern in a variety of ways. If the pattern is one of the five qd-defined patterns, you simply use a pointer to it in a drawing routine - as in passing &qd.black to FillRect(). If the pattern is included in a pattern list (a resource of type 'PAT#'), then you use a call to the Toolbox function GetIndPattern() to load the desired pattern to use. If the pattern is an individual resource of type 'PAT ', then you load it by calling the Toolbox function GetPattern(). You've seen that on the BeOS there is no qd global data structure, and that patterns don't exist as resources. So in all cases you should draw out the Mac pattern and define it as a BeOS pattern variable, as shown above.

On its own, a Be pattern variable holds no color information. When it comes time to use a pattern variable, the colors used in the pattern are dependent on the current high and low colors. In this next snippet a striped pattern is defined, and the high color is set to red and the low color to blue. When the rectangle theRect is filled with the striped pattern, it will end up with red and blue diagonal stripes:

BRect       theRect(20.0, 10.0, 220.0, 110.0);
rgb_color   redColor = {255, 0, 0, 255};
rgb_color   blueColor = {0, 0, 255, 255};
pattern     stripePattern = {   0xcc, 0x66, 0x33, 0x99,
                                0xcc, 0x66, 0x33, 0x99};
SetHighColor(redColor);
SetLowColor(blueColor);

FillRect(theRect, stripePattern);

Drawing Pen

Like the Mac OS, the BeOS uses the concept of an imaginary graphics pen when carrying out drawing operations. On both operating systems, the pen exists as a way to summarize and express properties of a drawing environment.

Pen Location

Mac programmers call the Toolbox functions Move() and MoveTo() to establish the starting location for the next drawing operation. On the BeOS convert calls to these routines to calls to the BView functions MovePenBy() and MovePenTo(). Pass the Be function MovePenTo() a pair of float values representing the coordinates of the pixel to move to:

MovePenTo(20.0, 70.0);

Like MovePenTo(), the MovePenBy() function moves the starting location for drawing. MovePenTo() moves the pen relative to the view's origin. MovePenBy() moves the pen relative to its current location in the view. Consider this snippet:

MovePenTo(20.0, 70.0);
MovePenBy(40.0, 10.0);

The call to MovePenTo() moves the pen to the location 20 pixels right of the view origin and 70 pixels down from the top of the view. That places the pen at the point (20.0, 70.0). The call to MovePenBy() uses this current location as the reference and moves the pen 40 pixels to the left and 10 pixels down. The result is that, relative to the view's origin, the pen is at the point (60.0, 80.0).

As with the Mac Toolbox function Move(), negative values for the BeOS function MovePenBy() move the pen "backwards" in the view. A negative horizontal argument moves the pen to the left, while a negative vertical argument moves the pen up.

Pen Size

On the Mac OS, the pen's size - the thickness at which lines are drawn - is by default one pixel in width and one pixel in height. This pen size can be altered with a call to the Toolbox function PenSize(). On the BeOS, the pen has a single thickness which results in lines having the same width and height. You call the BView function SetPenSize() to alter this pen diameter:

SetPenSize(3.0);

When altering the pen size, the best technique is to obtain and save the current pen size, change it, perform the desired drawing using the new pen size, then restore the pen to the saved size. The BView member function PenSize() allows you to do that. When invoked, PenSize() returns a float that holds the current thickness of the pen:

float   savedPenSize;

savedPenSize = PenSize();

Shapes

I've already demonstrated how to use the graphics pen, what the coordinate system is, and how to set up and draw a rectangle. As you may have guessed, you're well on your way to drawing just about any shape in your Be program.

Points

On the Mac, a point is represented by a variable of the type Point. The Point is a data structure consisting of two short values: h is the horizontal coordinate of the point, while v is the vertical coordinate. On the BeOS, a point is represented by a BPoint object. The BPoint object consists of two floating point values: x denotes the horizontal coordinate, while y denotes the vertical coordinate.

A BPoint object can have values assigned to its coordinate pair members either at the time of declaration or anytime thereafter. At declaration, one of the BPoint constructors can be used to take care of the task of assigning values to the BPoint members:

BPoint      thePoint(40.0, 70.0);

If the assignments are to be made after the object's declaration, then this can be done either by using the BPoint member function Set() or by assigning values directly to the x and y data members:

BPoint      thePoint;
BPoint      anotherPoint;

thePoint.Set(100.0, 200.0);
anotherPoint.x = 100.0;
anotherPoint.y = 200.0;

Like the BRect class, the BPoint class acts much like a data type. You create a point object by simply declaring a BPoint variable (as opposed to allocating memory using new), and you can alter its data members either by direct assignment or by invoking the Set() member function.

Lines

To draw a line on the Mac you position the graphics pen using calls to Move() or MoveTo(), then invoke the Toolbox function Line() or LineTo(). To draw the same line under the BeOS, call MovePenBy() or MovePenTo(), then invoke the BView function StrokeLine().

BPoint      start(50.0, 50.0);
BPoint      end(150.0, 50.0);

MovePenTo(start);
StrokeLine(end);

Optionally, you can also omit the pen-placement and simply pass StrokeLine() two parameters: each a BPoint object that represents the starting and ending point of the line, respectively:

StrokeLine(start, end);

Both versions of StrokeLine() offer a final optional parameter that can be used to specify a pattern in which the line is drawn. Here the diagonal stripe pattern discussed earlier in this article is used. The high color is set to red, and the low color is left in its default state of white. Additionally, the pen size is set to 10.0. The result is a line 10 pixels in thickness with diagonal red stripes running through it:

rgb_color   redColor = {255, 0, 0, 255};
BPoint      start(50.0, 50.0);
BPoint      end(150.0, 50.0);

SetHighColor(redColor);
SetPenSize(10.0);
StrokeLine(start, end, stripePattern);

Rectangles

I've already described how to set up and fill a rectangle - so I can be brief here as I formalize the explanations. The BRect class has four floating-point data members (left, top, right, and bottom) that are used to specify the coordinates of a rectangle. You can assign these data members values in a variety of ways. At the rectangle variable's declaration you can assign the members values individually:

BRect   theRect(10.0, 30.0, 110.0, 130.0);

Alternatively, you can set up the rectangle by specifying the left-top coordinate and right-bottom coordinate:

BPoint     leftTopPt(10.0, 30.0);
BPoint     rightBottomPt(110.0, 130.0);
BRect      anotherRect(leftTopPt, rightBottomPt);

On a Mac you set up a rectangle either by making direct assignments to a Rect variable's left, top, right, and bottom fields, or by calling the Toolbox function SetRect(). On the BeOS a BRect object's data members are similarly given values: use direct assignments or call the BRect function Set().

BRect      theRect;
BRect      anotherRect;

theRect.left     = 10.0;
theRect.top      = 30.0;
theRect.right    = 110.0;
theRect.bottom   = 130.0;

anotherRect.Set(10.0, 30.0, 110.0, 130.0);

The BRect class also contains member functions LeftTop(), LeftBottom(), RightTop(), and RightBottom() that provide access to any of the points in the rectangle. Similarly, there are functions like SetLeftTop() to modify those point values.

After setting up a rectangle in your Macintosh program, you frame it by calling FrameRect(), fill it with the current pattern (as established by a call to the Toolbox function PenPat() or PenPixPat()) by calling PaintRect(), or fill it with a specified pattern by calling FillRect(). In a Be program, frame the rectangle by calling StrokeRect(), and fill it by calling FillRect(). Both StrokeRect() and FillRect() allow for an optional second argument that specifies the pattern to use in drawing the rectangle. If omitted, a solid pattern using the current high color (B_SOLID_HIGH) is used by default.

BRect   theRect(10.0, 10.0, 160.0, 160.0);
BRect   anotherRect(30.0, 30.0, 140.0, 140.0);

StrokeRect(theRect, B_MIXED_COLORS);
FillRect(anotherRect, stripePattern);

Round rectangles

Both the Mac OS and the BeOS rely on the rectangle (rather than a unique round rectangle data type or class) as the basis for a rectangle with rounded corners. On the Mac you've used the Macintosh Toolbox functions FrameRoundRect(), PaintRoundRect() and FillRoundRect(). On the BeOS you use the BView functions StrokeRoundRect() and FillRoundRect(). For your Be round rectangle, first declare a BRect variable and set up the rectangle. Then pass that rectangle and the amount of rounding that is to be applied to each corner as arguments to StrokeRoundRect() or FillRoundRect(). The degree of rounding is determined just as it is for a Mac round rectangle: the second and third arguments together specify the shape of an ellipse (the second parameter establishes the ellipse radius along the x-axis, while the third parameter establishes the ellipse radius along the y-axis). Such an ellipse can be thought of as being set in each corner of the rectangle. Then, only one quarter of the ellipse is actually drawn in order to form a rounded corner. The following snippet demonstrates how to create a round rectangle that's filled with the current low color:

BRect   theRect(30.0, 30.0, 130.0, 130.0);

FillRoundRect(theRect, 20.0, 20.0);

Ellipses

After defining a rectangle, an ellipse is drawn by inscribing an oval within the boundaries of the rectangle. The rectangle itself isn't drawn - it simply serves to specify the size of the ellipse. This is similar to how an ellipse is drawn in a Mac program. In the Mac OS you pass a pointer to a rectangle to FrameOval(), PaintOval(), or FillOval(). In the BeOS, you use the BView member functions StrokeEllipse() and FillEllipse():

BRect   theRect(30.0, 30.0, 130.0, 130.0);

FillEllipse(theRect, B_SOLID_LOW);
StrokeEllipse(theRect, B_SOLID_HIGH);

Polygons

A polygon is a closed shape that has three or more sides. Your Macintosh program creates a polygon by declaring a PolyHandle variable and then calling OpenPoly() and ClosePoly(). Between the open and close calls should be a MoveTo() call that positions the graphics pen, and a series of calls to LineTo() that then draw the lines that make up the edges of the polygon. Rather than a series of edge-defining lines, the BeOS treats a polygon as a series of vertex-defining points. To create a Be polygon, first declare an array of BPoints. The number of points should be the number of polygon vertices. Use the BPoint function Set() to define each vertex. Then declare and create a new BPolygon object. The BPolygon constructor accepts as its arguments the array of BPoints along with an int32 (32-bit integer) value that specifies how many points are in the array. Here's how a BPolygon object representing a three sided polygon might be defined:

BPoint        pointArray[3]; 
int32         numPoints = 3;
BPolygon      *thePolygon;

pointArray[0].Set(50.0, 100.0);
pointArray[1].Set(150.0, 20.0);
pointArray[2].Set(250.0, 100.0);

thePolygon = new BPolygon(pointArray, numPoints); 

On the Mac, after a call to ClosePoly() is made, the lines that make up the polygon are treated as a single entity. The polygon can be drawn using the Toolbox functions FramePoly(), PaintPoly(), and FillPoly(). In your Be program, once a BPolygon object is defined, its outline can be drawn by calling the BView member function StrokePolygon(), or it can be filled in by calling FillPolygon(). As with other drawing routines, omitting the pattern argument tells the function to use B_SOLID_HIGH as the fill pattern. If you want to use a different pattern, specify it as a second argument. Here I'm filling the just-defined polygon with my earlier-created striped pattern:

FillPolygon(thePolygon, stripePattern);  

Regions

A Macintosh region is formed much as a Macintosh polygon is: a series of calls to Toolbox drawing functions define the area that's to be considered one region. A new RgnHandle is created using a call to NewRgn(), and OpenRgn() and CloseRgn() calls frame the region-defining Toolbox calls. A BeOS region is instead created by defining any number of rectangles that are then grouped together into a single BRegion object. The rectangles that make up a region can vary in size, and can be defined such that they form one continuous shape or any number of seemingly unrelated shapes (that is, the one region can be composed of non-touching rectangles). To set up a region, first create a new BRegion object. Then define a rectangle and add that rectangle to the BRegion object. The steps of defining and adding rectangles to the BRegion object can be repeated until the area that is to be considered a single region is complete. In the following snippet two rectangles are defined and added to a BRegion object:

BRect     theRect;
BRegion   *theRegion;

theRegion = new BRegion();
theRect.Set(20.0, 20.0, 70.0, 70.0);
theRegion->Include(theRect);

theRect.Set(50.0, 50.0, 150.0, 100.0);
theRegion->Include(theRect);

On a Mac you frame and fill a region using calls to FrameRgn() and FillRgn(). Because a Be region can consist of any number of overlapping rectangles, outlining each individual rectangle would result in lines running through the content area of the region - so no StrokeRegion() function exists. Instead, display the region by invoking the BView member function FillRegion(). Here the region that was created in the previous snippet is filled with the default B_SOLID_HIGH pattern:

FillRegion(theRegion);

One important use of a region is in testing for the inclusion of a point within an area. On the Mac you use the Toolbox function PtInRgn() to see if the cursor was over the area of a region when the mouse button was clicked. On the BeOS, test a point for inclusion in a region by calling the BRegion member function Contains(). Pass this routine the BPoint object to test, and Contains() returns a bool (Boolean) value that indicates whether or not the tested point lies within the region. In this next snippet a region consisting of just a single rectangle is defined. A point is then defined and tested for inclusion in this region:

BRect   theRect(20.0, 20.0, 70.0, 70.0);
BRegion   *theRegion;
BPoint      thePoint(60.0, 90.0);

theRegion = new BRegion();
theRegion->Include(theRect);
FillRegion(theRegion);
  
if (theRegion->Contains(thePoint))    // point in region
   // do something
else                                     // point not in region
   // do something else

Last Word

For learning new programming techniques, there's no better way to get the complete picture than to view a well-commented source code listing for a complete program. Rather than use up valuable magazine pages, I've opted to make such a listing available at MacTech's web site. After downloading, view the code by opening the GraphicsEx.cp file from within the BeIDE (if you have the BeOS, you have a version of Metrowerks' CodeWarrior for BeOS). To make use of the code, create a new BeIDE project, add GraphicsEx.cp to it, and then choose Run from the Project menu.

I've included a number of graphics-porting considerations in this article, but of course there are many more. How to move your Mac application's picture-drawing and bitmap-handling code to the BeOS are a couple of topics that come to mind. For information on these subjects, as well as more general BeOS programming information, refer to the BeOS API reference book "Be Developer's Guide" by O'Reilly & Associates, or to my own BeOS tutorial "Programming the Be Operating System," also by O'Reilly & Associates.


Dan Parks Sydow is the author of over a dozen programming books, "Programming the Be Operating System" by O'Reilly & Associates. Dan recently had the pleasure of visiting the Menlo Park, CA headquarters of Be, Inc. to talk with Be Chairman and CEO Jean-Louis Gassee and a number of Be engineers. He left thoroughly convinced that the BeOS is destined to be an important digital media tool for both programmers and end-users.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »

Price Scanner via MacPrices.net

13-inch M2 MacBook Airs in stock today at App...
Apple has 13″ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
We’ve updated our iPhone Price Tracker with the latest carrier deals on Apple’s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more

Jobs Board

Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.