Graphics

From RFO-BASIC! Manual
Manual contents (Statement index)
Language features The basicsArraysData structuresInterrupt routinesUser-defined functions
Interfaces Audio playerBluetoothFilesGraphicsHTMLKeyboardSocketsSQLTelecom
Programming notes Coexisting with Android

The RFO-BASIC! graphics interface lets you combine text, shapes, and images on the graphics screen. This screen occupies the entire display of the Android device. Display of the graphics screen prevents the display of the text console, which is the simpler method of interaction between user and program, but does not destroy it. Pressing the BACK key recalls the text console. In addition, a GR.FRONT statement lets the program switch between the graphics screen and the text console. Statements that raise a pop-up window for user input (such as INPUT and SELECT) work normally even when the graphics screen is displayed.

Unlike the text console, the graphics screen gives the program precise control over size, position, color, and transparency.

The GR.OPEN statement initializes the graphics interface and puts the graphics screen on display. The GR.CLOSE statement ends graphics operations. Graphics operations also end when the program ends or is halted, such as after the user presses the MENU key and swipes it off the screen. To keep the graphics screen on display after the program has completed its computing, use a long PAUSE statement or an infinite loop with some way for the user to stop the program.

The GR.CLS statement clears the graphics screen and destroys all graphic elements. The GR.RENDER statement renders graphic elements onto the graphics screen. Nothing appears on the graphics screen until the program executes GR.RENDER. GR.RENDER achieves a flicker-free transition from what was displayed on the graphics screen, to elements the program specified more recently.

A typical use of graphics is to put elements on the graphics screen, and to update them based on real-world events such as received messages. There are two ways to achieve this:

  1. Code a main loop that begins with GR.CLS, uses graphics statements to place the desired graphic elements on the screen, and finally executes GR.RENDER.
  2. Do the above only once to specify the graphics screen's initial appearance. Then code a main loop in which the program may use GR.MODIFY to change the attributes of certain elements, or GR.HIDE and GR.SHOW to select which elements are displayed, and finally executes GR.RENDER.
Geometry

Graphics operations work in Portrait Mode and Landscape Mode. The GR.OPEN command can state that the program operates in one specific orientation, or can specify that the orientation will change as the user rotates the Android device.

When drawing an object onto the graphics screen, the location (typically of the object's upper-left corner) and the size of the object are stated as a number of pixels. The upper-left corner of the graphics screen is identified with horizontal location x=0 and vertical location y=0. Pixels to the right and below this corner are identified with increasing positive pixel addresses (as in Quadrant IV of a Cartesian coordinate system, but with positive y).

Coordinates are measured with respect to the physical screen, not to anything on it. (If you choose to show the Android Status Bar, anything you draw at the top of the screen is covered by the Status Bar. See GR.STATUSBAR.)

A program can use the GR.SCREEN statement to query the size of the graphics screen. If the program has allowed the orientation to shift, the size of the graphics screen may change if the user rotates the Android device during the program's execution. RFO-BASIC! attempts to reconstruct the graphics objects in the new orientation, but the program may elect to re-draw the entire screen (especially if the program prefers different uses or layouts based on the orientation). GR.SCREEN indicates that the device is in Portrait Mode if y > x.

When specifying the origin and size of text and graphics, RFO-BASIC! programs should not hard-code numbers but should specify some fraction of x and y as returned by GR.SCREEN. This lets the program present the same display on Android devices whose screens have more or fewer pixels. Future devices may have different ratios of vertical and horizontal pixels. The GR.SCALE statement can be used to shrink or stretch graphics in either dimension to adapt to them.

Transparency, color, and style

Single-byte integers, such as those used as parameters of GR.COLOR, affect objects drawn subsequently by specifying:

  • Whether they are opaque or transparent
  • Their color, in red/green/blue values
  • Whether their edges are stroked, the element is filled in, or both.

Color, transparency, and style are often stored together in a data structure called a paint.

General rules

The BASIC program can draw elements onto the graphics screen, such as lines, circles, text, and images. Whenever it does so:

  1. The element's color, transparency, and other style options are dictated by the current paint.
  2. The element receives a number, which the program must use if it refers to the element later, such as in a GR.MODIFY statement.
  3. The element's number is added to a display list. Elements have no "z address," but the GR.RENDER statement draws elements in the order in which they appear in the Display List. Elements drawn later appear "on top of" those drawn earlier. Programs can create custom display lists and thereby change the order in which elements are drawn.

An exception to this scheme is while BASIC is placed into Draw-Into Mode. In this case, BASIC draws every specified element into a specified bitmap, in the sequence in which the drawing statements are executed.

Major operations[edit]

GR.OPEN[edit]

Begin graphics operations

Synopsis
GR.OPEN {{<alpha_nexp>}{, <red_nexp>}{, <green_nexp>}{, <blue_nexp>}{, <status_lexp>}{, <orientation_nexp>}}
Description

The GR.OPEN statement opens the graphics screen and prepares BASIC to paint graphic elements into it. It is a run-time error to execute any other graphics statement without first executing GR.OPEN.

The <alpha_nexp> parameter specifies opacity (see the note at right) and the red, green, and blue parameters specify the graphics screen's background color. If <alpha_nexp> or any color parameter is omitted, GR.OPEN uses 255. (If all are omitted, 255,255,255,255 is used, specifying bright white.) RFO-BASIC! uses only the low-order 8 bits of these parameters. So a value of 257 does not specify very bright, but instead wraps around to 1.

If <status_lexp> has a value other than 0, the graphics screen incorporates the Android Status Bar. If it is 0 or omitted, the Android Status Bar is not present. If the Android Status Bar is displayed, screen geometry continues to be based on the total size of the Android display; it does not reflect a "client screen" remaining after the Status Bar. This means the program could draw elements that conflict with the Status Bar. (See GR.STATUSBAR.)

The <orientation_nexp> parameter specifies the initial orientation. Valid values are the same as for the GR.ORIENTATION statement. Notably, a value of -1 means that the orientation of the graphics screen depends on how the user holds the Android device and can change at any time while the program is running. If the expression is omitted, the graphics screen begins in Landscape Mode.

Examples
GR.OPEN ,,,,,-1

This statement defaults to a bright white screen without the Android Status Bar. The -1 means that the orientation at any moment will depend on how the user holds the device.

GR.OPEN ,,,,,1
GR.BITMAP.LOAD BackgroundBitmap, "../../Pictures/PortraitWP.png"
IF BackgroundBitmap > -1 THEN
 GR.COLOR 50
 GR.BITMAP.DRAW pBackg,BackgroundBitmap, 0,0
ENDIF

This example opens the graphics screen in Portrait Mode. Here, we look for a certain image (a PNG file) that we used as the device wallpaper. On finding it, we set GR.COLOR 50 (50/255 opacity) and draw the same wallpaper on the white graphics screen. Drawing the wallpaper nearly transparently draws a faded image so as not to interfere with elements the program draws later. You cannot make the actual Android home screen shine through by specifying a transparent graphics screen.

GR.CLOSE[edit]

End graphics operations

Synopsis
GR.CLOSE
Description

The GR.CLOSE statement ends graphics operations, causes the graphics screen to no longer be displayed, and raises the text console in its place. It does not end the BASIC program. However, if the program ends for some other reason, graphics shuts down as with the GR.CLOSE statement.

GR.CLS[edit]

Clear the graphics screen

Synopsis
GR.CLS
Description

The GR.CLS statement deletes all the data structures of the graphics subsystem, including:

  • All elements that have been drawn on the graphics screen (making their numbers invalid for future use)
  • All paints
  • All settings of GR.COLOR and GR.TEXT
  • The current display list

GR.CLS does not affect what is shown on the Android display. To display an empty graphics screen in the chosen background color, the program would have to execute GR.RENDER. However, typically, the program first draws a new set of elements that is to be displayed next.

GR.CLS does not delete bitmaps that have been loaded. They are not set to appear on the graphics screen, because the data structures that would achieve this have been deleted, but their bitmap numbers remain valid, and a program could re-draw them (without having to reload them) before executing GR.RENDER.

GR.RENDER[edit]

Render the graphics screen

Synopsis
GR.RENDER
Description

The GR.RENDER statement displays all the objects in the current working display list. That is, it causes every element that the program has drawn to physically appear on the graphics screen. An exception is objects in the display list that are inhibited from display using GR.HIDE.

GR.RENDER does not return to the caller until:

  • All the items in the working display list have been re-rendered, and
  • The screen has been refreshed (which typically happens 60 times per second).

This means that two consecutive GR.RENDER statements delay the BASIC program about 16.7 msec. There is no benefit to trying to render the screen faster than this.

A program designed to display smooth animation should not perform computations that take more than 16.7 msec between GR.RENDER commands; otherwise, some renderings may skip an Android screen refresh and the animation may become jerky. A program can use the CLOCK() or TIME() functions to measure the amount of time its computations are taking.

If RFO-BASIC! is running in the background (see the BACKGROUND() function and the HOME key), GR.RENDER suspends the program's execution until the user returns BASIC to the foreground.

The figures of 60 refreshes per second and 16.7 msec between refreshes may be different on some Android devices.

Example

Here is a working program that combines the GR.OPEN and GR.RENDER statements with others discussed later:

GR.OPEN                    % Start graphics; background assumed to be bright white, screen is cleared
GR.COLOR 255,0,0,0         % Specify opaque black drawing (bright white is the default paint color too)
GR.TEXT.SIZE 80            % Text lines are 80 pixels high
GR.TEXT.DRAW textline1, 0,100, "Hello, world!"
GR.RENDER                  % Make the text appear
PAUSE 2000                 % Stay on the graphics screen for 2 seconds before the program ends

The GR.TEXT.DRAW statement draws starting at (0,100), 100 pixels below the top of the screen. This is the baseline of the specified text. Drawing at (0,0) would position most of the text off the top of the screen.

GR.FRONT[edit]

Select between the graphics screen and the text console

Synopsis
GR.FRONT <flag_nexp>
Description

An RFO-BASIC! program can maintain both the graphics screen and the text console, but only one is visible at any time. The GR.FRONT statement makes this selection.

  • If <flag_nexp> is true (nonzero), then the graphics screen is the one the user sees. The BASIC program can still operate on the text console, such as by executing PRINT statements.
  • If <flag_nexp> is false (0), then the text console is the one the user sees. The BASIC program can still draw to the graphics screen, but GR.RENDER fails unless the program puts the graphics screen on display.

GR.SAVE[edit]

Save the graphics screen to a file

Synopsis
GR.SAVE <filename_sexp>{, <quality_nexp>}
Description

The GR.SAVE statement saves the graphics screen to a file. The <filename_sexp> parameter specifies the name of the file, which is assumed to be in the rfo-basic/data directory. Usually, the file is saved as a PNG file. If <filename_sexp> does not specify an extension, .png is appended to it. The <quality_nexp> parameter is ignored.

If <filename_sexp> ends with .jpg, then GR.SAVE writes a JPEG file. This is a "lossy" file format, more suitable for photographs of faces than to capture sharp edges and line-drawings. It does not save the exact image. The <quality_nexp> parameter, if present, must be a value from 0 through 100 and specifies the desired quality:

  • A value of 0 creates a small JPEG file with low resolution.
  • A value of 100 creates a larger JPEG file with the highest resolution the format can achieve.
  • If the parameter is omitted, the quality level is 50.

Another way to create files is to use GR.BITMAP.SAVE. If the RFO-BASIC! program has used Draw-Into Mode to draw graphic elements into a bitmap that can then be drawn on the graphics screen, then GR.BITMAP.SAVE stores the bitmap in a .BMP file for later use by an RFO-BASIC! or other program. These files accurately represent every pixel of the screen.

The GR.SAVE statement operates only on the graphics screen. It cannot save browser output created by the HTML statements, nor text output created by the PRINT statement.

Interacting with Android[edit]

Given that Android devices range from a flip-phone with a tiny screen to a Chromebook with a keyboard, one of the first steps of a BASIC program that uses the graphics screen is to discover the attributes and set the operating mode.

GR.ORIENTATION[edit]

Specify the screen orientation

Synopsis
GR.ORIENTATION <orientation_nexp>
Description

An Android device can be operated in one of two Modes:

  • Portrait Mode, in which the long dimension of the display is vertical
  • Landscape Mode, in which the long dimension of the display is horizontal.

The GR.ORIENTATION statement specifies which Mode the program will use. The <orientation_nexp> parameter can have one of the following values:

Value Meaning
-1 The orientation depends on how the user is holding the Android device. If the user then rotates the device, the orientation will change. For example, a GR.SCREEN statement executed after the user rotates the device returns height and width values that are exchanged.
0 The orientation is Landscape Mode, no matter how the user rotates the device. This is the initial orientation if the parameter in GR.OPEN is 0 or is omitted.
1 The orientation is Portrait Mode (rotated 90 degrees clockwise from Landscape Mode), no matter how the user rotates the device.
2 The orientation is Reverse Landscape Mode, no matter how the user rotates the device.
3 The orientation is Reverse Portrait Mode, no matter how the user rotates the device. This is upside-down for a cellphone. Some phones do not support upside-down operation.
Warning on unnecessary rotations

RFO-BASIC!'s initial mode is Portrait Mode. Using GR.ORIENTATION to specify a different mode results in a time-consuming operation. (See the blue sidebar above.) It is preferable to specify the desired orientation when opening the graphics screen with GR.OPEN. After the program changes the orientation, it may improve results to execute:

PAUSE 800
Warning on computing during a rotation

GR.RENDER executed during the time-consuming animation does not render. If the orientation is -1 and the user rotates the Android device, this interval could occur at any time. The information GR.SCREEN returns is only updated at the end of this interval. The information SCREEN.ROTATION and SCREEN.SIZE return is revised at the start of this interval. Therefore, the program should sense the screen orientation with SCREEN.ROTATION and dimensions with SCREEN.SIZE before using the GR. statements to build the display list for that orientation. The program may check the orientation again after it has built the display list to ensure the user did not rotate the device in the meantime. Even so, this is a race condition and it is possible the RFO-BASIC! program will render the graphics screen once in the old orientation.

SCREEN.ROTATION[edit]

Obtain the screen orientation

Synopsis
SCREEN.ROTATION <rotation_nvar>
Description

The SCREEN.ROTATION statement obtains the orientation of the Android device.

  • If the RFO-BASIC! program specified an orientation (0 through 3) in the GR.OPEN or GR.ORIENTATION statements, then this statement returns that value (as set most recently).
  • If the program specified -1, so that the orientation changes as the user rotates the device (-1), then SCREEN.ROTATION returns the current orientation, 0 through 3, as defined for GR.ORIENTATION. The value that SCREEN.ROTATION returns could change at any moment based on the user's handling of the device. SCREEN.ROTATION does not return -1.

SCREEN.SIZE[edit]

Obtain the screen dimensions

Synopsis
SCREEN.SIZE <size_narr>, <realsize_narr>, <density_nvar>
Description

The SCREEN.SIZE statement obtains information about the size in pixels of the screen. All parameters are optional.

  • The <size_narr> parameter returns a two-element array with the number of pixels available to the RFO-BASIC! program. The width (x dimension) is in size[1] and the height (y dimension) is in size[2].
  • The <realsize_narr> parameter returns a two-element array with the screen dimensions. Unlike <size_narr>, this includes areas of the screen with system decorations that might not be available to the RFO-BASIC! program.
  • The <density_nvar> parameter is set to one of the standard Android pixel density values in dots per inch, such as 120, 160, or 240 dpi. This might differ from the exact pixel density.

If the GR.OPEN or GR.ORIENTATION statement has permitted the mode to change depending on the manner in which the user holds the device, then the results that SCREEN.SIZE returns can change at any moment.

GR.SCREEN[edit]

Obtain the screen dimensions

Synopsis
GR.SCREEN <width_nvar>, <height_nvar>{, <density_nvar> }
Description

The GR.SCREEN statement obtains the dimensions of the Android display. These include:

  • The screen width, in pixels
  • The screen height, in pixels
  • Optionally, the pixel density, in dots per inch (dpi).

An RFO-BASIC! program could determine whether the device is in Portrait Mode or Landscape Mode with the following code (but see the warning at GR_ORIENTATION):

GR.SCREEN w, h
IsPortrait = (w < h)

If the GR.OPEN or GR.ORIENTATION statement has permitted the mode to change depending on the manner in which the user holds the device, then the results that GR.SCREEN returns can change at any moment.

The <density_nvar> parameter, if present, is set to one of the standard Android pixel density values, such as 120, 160, or 240 dpi. This might differ from the exact pixel density.

See also

The SCREEN.ROTATION and SCREEN.SIZE statements return this and additional information.

GR.STATUSBAR[edit]

Obtain information about the Android Status Bar

Synopsis
GR.STATUSBAR <height_nvar>, <showing_lvar>
Description

The Android Status Bar may appear at the top of the screen and may show information such as the strength of the cellular and WiFi signals, any notifications, and the time of day. An RFO-BASIC! program specifies whether the Status Bar is displayed using a parameter of GR.OPEN. If the program has selected display of the Status Bar, it does not change the screen geometry and the program must coexist with the Status Bar.

Most Android Devices implement the Status Bar by drawing text and symbols, in white, on top of any existing background, essentially adding drawing commands, for the information described above, to the end of the current Drawing List. Since the Status Bar is rendered last, you cannot inhibit it if you opted to have it. On older devices, you can draw a background of any desired color, including a white background that renders the Status Bar invisible. However, some Android Devices draw a black bar, then white text, to ensure that the text is readable. As of Android 8 (Oreo), user programs are not allowed to overwrite the Status Bar.

The GR.STATUSBAR statement obtains information on the Status Bar.

  • The logical variable <showing_lvar> is 1 (TRUE) if the Android Status Bar is showing; otherwise, it is 0. This depends solely on whether the program enabled it in the call to GR.OPEN.
  • The numeric variable <height_nvar> is set to the height of the Android Status Bar in pixels. The height and physical appearance of the Status Bar depends on the device, the revision, and the text size the user specified in Settings.

To work with all versions of Android, if an RFO-BASIC! program enables the Android Status Bar in the call to GR.OPEN, it should limit its graphics to y coordinates from the bottom of the Status Bar (<height_nvar>) to the bottom of the physical screen (the <height_nvar> parameter returned by GR.SCREEN, which may change from time to time if the user turns the device sideways).

GR.SCALE[edit]

Transform all rendered elements to specified dimensions

Synopsis
GR.SCALE <x_nexp>, <y_nexp>
Description

The GR.SCALE statement transforms all rendered elements by a factor of <x_nexp> horizontally, and by a factor of <y_nexp> vertically. This includes distorting text (condensing or expanding it). GR.SCALE applies to the entire screen, including elements specified before and after the GR.SCALE statement is executed.

For example, GR.SCALE 1,1 has no effect except to cancel any other scaling that might have been specified. GR.SCALE 2,1 stretches all elements to double width. Drawing a 20✕20 square at origin (30,50) achieves a 40✕20 rectangle with origin at (60,50).

Negative values of <x_nexp> and <y_nexp> are not valid. To create mirrored graphics and text, use Draw-Into Mode. Then use GR.BITMAP.SCALE to copy the bitmap into another bitmap, using negative scale factors, and draw this bitmap onto the graphics screen.

Example

GR.SCALE lets a BASIC program be written, assuming some screen size, and then transformed at the time it is run to fit the screen of the Android device on which the program is running. A program written to run on an 800✕480 screen will run on any Android device if it includes code such as the following:

!Specify the dimensions of the screen for which this program is designed
di_width = 800
di_height = 480

!Get the dimensions of the screen on which the program is running
GR.OPEN
GR.SCREEN actual_w, actual_h

!Make this program's graphics work on the actual screen
GR.SCALE actual_w/di_width, actual_h/di_height

The 800✕480 screen dimensions means the program is assuming the screen is in Landscape Mode. To work correctly when the device is in, or switches to, Portrait Mode, more complex code is needed.

GR.BRIGHTNESS[edit]

Select the light level of the display

Synopsis
GR.BRIGHTNESS <br_nexp>
Description

The GR.BRIGHTNESS statement sets the brightness of the Android display. A value of 0.01 is the darkest level and 1.00 is the brightest.

RFO-BASIC! provides no way to obtain the brightness the user selected via Android (such as using Settings / Display or the pull-down control). However, when the BASIC program ends, the screen illumination returns to that level.

Paints[edit]

A paint is an RFO-BASIC! data structure that holds all the implicit settings needed to draw a graphic element. These include:

  • Transparency, color, and drawing style, which are specified by GR.COLOR.
  • The anti-alias setting, which is specified by GR.SET.ANTIALIAS.
  • The line stroke width, which is specified by GR.SET.STROKE.

It also holds the following settings needed to render text characters on the graphics screen. These include:

  • The font, which is specified by GR.TEXT.SETFONT.
  • Text size, which is specified by GR.TEXT.SIZE.
  • The alignment of text, relative to the specified point on the graphics screen. Alignment is specified by GR.TEXT.ALIGN.
  • Bloating of text, which is specified by GR.TEXT.BOLD. This effect is comparable to boldfacing, but specifying a boldfaced typeface is preferable.
  • Skewing of text, which is specified by GR.TEXT.SKEW. This effect is comparable to italics, but specifying an italic typeface is preferable.
  • Whether the text is underlined, which is specified by GR.TEXT.UNDERLINE.
  • Whether the text is struck through, which is specified by GR.TEXT.STRIKE.
General syntax

The statements mentioned above take an optional parameter denoted as <paint_nexp>. This is the number of a paint.

  • If you omit <paint_nexp>, the statement creates a new paint and makes it the current paint. These settings affect all drawing until the program makes a further change. The statement does not return the number of the new paint, but the program can get it with GR.PAINT.GET.
  • If you include <paint_nexp>, its value must be the number of an existing paint. The statement modifies that paint. It does not modify future drawing statements unless the specified paint is also the current paint. Later, if the program modifies that paint, it immediately changes the appearance of every element drawn using that paint, the changes becoming visible the next time the program executes GR.RENDER. If you provide a number that is not the number of an existing paint, it is a run-time error.

There are only two reasons you need to be concerned with stored paint numbers:

  1. If your program follows neither of the options presented at the start of this article (see the blue box), or
  2. If you wish to instantly change the appearance of several graphic elements at once by giving them the same paint.

GR.COLOR[edit]

Specify transparency, color, and drawing style

Synopsis
GR.COLOR {{<alpha_nexp>}{, <red_nexp>}{, <green_nexp>}{, <blue_nexp>}{, <style_nexp>}{, <paint_nexp>}}
Description

The GR.COLOR statement specifies transparency, color, and drawing style for subsequent drawing statements.

  • The <alpha_nexp> parameter specifies transparency. A value of 0 means the element is completely invisible, yielding to material previously drawn on the graphics screen at its location. 255 means the element is opaque; it is visible, hiding anything previously drawn at its location. Values in between produce a mix of the new element and the background elements at the same location.
  • The <red_nexp>, <green_nexp>, and <blue_nexp> parameters specify the color in which to draw. A value of 0 means that color component is dark. A value of 255 specifies full brightness. So 0,0,0 signifies black, 255,0,0 signifies bright red, and 255,255,255 signifies white.
  • The <style_nexp> parameter has one of three values:
style_nexp Meaning Description
0 STROKE Trace the outline of the graphic element, using the current stroke width
1 FILL Fill in the interior of the graphic element
2 STROKE_AND_FILL Both trace the outline of the element and fill it in.
If drawing a circle, this is the difference between a filled-in circle and simply drawing the circumference. However, style also applies when drawing text. Selecting a style of 0 does not draw the text but only the outlines of each character. This is an interesting alternative to "graying out" text that is not relevant at the moment. A style of 2 is most common when drawing text.
  • The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of this section.

Omitting any parameter for transparency, color, and style means to make no change to that aspect of the paint.

Examples
GR.COLOR 255,0,0,0,2

This specifies opaque black and stroke-and-fill style. It applies to subsequent drawing statements that do not specify a paint by number. This example does not save the number of the newly created paint in a variable.

GR.COLOR 100

It is especially useful to omit parameters of GR.COLOR in order to change only certain settings. The above example changes only the transparency (to 100/255), leaving the color and drawing style unchanged. It draws subsequent elements in a dimmer intensity of the color already selected. For text, this is a way to "gray out" options that are not available to the user.

The output from the example program

Here is a complete RFO-BASIC! program that shows the effects of stroke versus fill:

GR.OPEN 255,0,0,0,,1
PAUSE 800          % Wait for any orientation change
GR.TEXT.SIZE 120   % Specify size of characters in pixels
GR.SET.STROKE 5    % Pick an exaggerated stroke width
GR.COLOR 255,255,255,255,0  % Fully opaque, white, STROKE

GR.TEXT.DRAW obj0,50,100, "#0"
GR.CIRCLE obj0c,100,200,50

GR.COLOR ,,,,1     % Change to FILL
GR.TEXT.DRAW obj1,300,100, "#1"
GR.CIRCLE obj1c,350,200,50

GR.COLOR ,,,,2     % Change to STROKE_AND_FILL
GR.TEXT.DRAW obj2,550,100, "#2"
GR.CIRCLE obj2c,600,200,50

GR.RENDER          % Render all graphics
PAUSE 4000         % Pause 4 seconds before program ends

It can be useful to draw the same element twice with different paint settings. You could fill a circle in a dim color and then stroke its outline in full intensity:

GR.COLOR 51,255,0,0,1     % Fill, red, 20% opaque
GR.CIRCLE obj3,600,200,50
GR.COLOR 255,  , , ,0     % Change to fully opaque and to stroke
GR.CIRCLE obj4,600,200,50

GR.SET.ANTIALIAS[edit]

The letter A, drawn without anti-aliasing (left) and with anti-aliasing (right). The pixels on an Android device are much smaller than shown and the jaggedness is less evident, but anti-aliasing still leads to more attractive graphics in most cases.

Control the Android anti-alias mode

Overview

Anti-aliasing is an Android feature that blurs adjacent pixels to create smoother curves. For example, with anti-aliasing enabled, when drawing a black circle on a white background, pixels on the edge of a circle are drawn in gray to avoid jaggedness (aliases) where the edge jumps from one tier of pixels to the next.

Anti-aliasing is enabled by default. It almost always produces a more attractive effect. When drawing polygons, anti-aliasing makes the corners appear to be slightly rounded.

When drawing single pixels that need to appear clearly, anti-aliasing should be disabled; otherwise, the pixel will be diffused among neighboring pixels. Anti-aliasing will also produce a less satisfactory result when drawing horizontal or vertical lines whose width is 1 pixel.

Synopsis
GR.SET.ANTIALIAS {{<enable_lexp>}{, <paint_nexp>}}
Description

The GR.SET.ANTIALIAS statement affects the anti-aliasing setting:

  • If <enable_lexp> is false (0), then anti-aliasing is disabled.
  • If <enable_lexp> is true (1), then anti-aliasing is enabled.
  • If <enable_lexp> is omitted, then the state of anti-aliasing is reversed.
  • The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of this section.

GR.SET.STROKE[edit]

A demonstration of anti-aliasing in conjunction with stroke width. (Your browser may perform antialiasing of its own that defeats the purpose of this illustration. You can click on the picture to enlarge it and view it without this effect.)

Set the stroke width

Overview

Most statements that draw an object rely on the stroke width. For example, to draw a text character, GR.TEXT.DRAW may draw the character's outline in the specified stroke width and may fill the interior of the character. If the stroke width is 0 or 1, any lines drawn are a single pixel wide. If the value is greater, the line width is the specified number of pixels.

Stroke width is closely related to the anti-aliasing setting specified with GR.SET.ANTIALIAS To draw a sharp single-pixel line, the stroke width must be 0 or 1, and anti-aliasing must be off. If anti-aliasing is on, the line will be blurred: It will lack full opacity and will be two pixels wide. In more detail:

  • With anti-aliasing, the Android device paints with varying opacity to draw a blurred line that is, on average, the number of pixels specified by the stroke width. This is the case even if the stroke width is not a counting number.
  • Without anti-aliasing, the Android device paints with the exact opacity of the paint to draw a line using the exact number of pixels specified by the stroke width. It is meaningless for the stroke width to not be a counting number, except when drawing diagonals.
Synopsis
GR.SET.STROKE {{<stroke_nexp>}{, <paint_nexp>}}
Description

The GR.SET.STROKE statement sets the stroke width to <stroke_nexp>.

  • The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of this section.
Example

This example draws white characters with a stroke width of 4, then draws them again in black without stroking the outline at all. This creates a white shadow that makes the black text visible even on a background such as a photograph.

GR.COLOR 255, 255,255,255, 0  % Specify opaque white and STROKE only
GR.SET.STROKE 4
GR.TEXT.DRAW p, xpos, ypos, DesiredText$
GR.COLOR 255, 0,0,0,       1  % Specify opaque black and FILL only
GR.TEXT.DRAW p, xpos, ypos, DesiredText$
GR.SET.STROKE 0               % Return stroke width to the default value

GR.PAINT.GET[edit]

Get the number of the current paint

Synopsis
GR.PAINT.GET <paint_nvar>
Description

The GR.PAINT.GET statement sets the required numeric variable to the number of the current paint.

Basic usage

You can use GR.COLOR and other statements without the <paint_nexp> parameter, to affect the drawing of upcoming elements. Each setting remains in effect unless the program changes it again. For example:

GR.OPEN  255,30,30,30,0,2    % Open graphics; dark gray background
GR.COLOR 255,100,120,150,0   % Set a bluish color
GR.TEXT.SIZE 80              % Set 80-pixel text
GR.TEXT.BOLD 1               % Enable bloating
GR.TEXT.DRAW obj1,200,200,"Hello" % Draw text using the above settings
GR.RENDER                    % Render it onto the physical screen

Each of the middle three statements creates a new paint data structure, copying the settings previously in effect and modifying them as the statement specifies. None of the statements provides a variable as <paint_nexp> to capture the number of this paint in order to specify it by number.

Advanced usage

By specifying <paint_nexp>, a program modifies a specified paint rather than the current paint. If it provides as <paint_nexp> a variable whose value is 0, the graphics statement creates a paint and returns the number of the paint in the parameter. For example:

GR.OPEN  255,30,30,30,0,2    % Open dark gray background. No paints exist
GR.COLOR 255,100,120,150,0   % Set a bluish color (creates a new paint)
GR.PAINT.GET NewPaint        % Get the number of the paint just created
GR.TEXT.SIZE 80,NewPaint     % Modify NewPaint further to set 80-pixel text
GR.TEXT.BOLD 1,NewPaint      % Modify NewPaint further to enable bloating
GR.TEXT.DRAW obj1,200,200,"Hello" % Draw text; NewPaint is the current paint
GR.RENDER                    % Render it onto the physical screen
PAUSE 4000                   % Pause 4 seconds before program ends

GR.COLOR created a new paint and assigned it a number (perhaps 1). In this case, GR.PAINT.GET sets NewPaint to the value 1. It remains the current paint. GR.TEXT.SIZE and GR.TEXT.BOLD make further changes to paint 1. Paint 1, as modified, governs drawing by GR.TEXT.DRAW. These statements have the same effect as in the previous example, but the program has created only a single paint, rather than three, as the previous example did.

GR.PAINT.RESET[edit]

Reset the current paint to the default settings

Synopsis
GR.PAINT.RESET {<paint_nexp>}
Description

The start of this section lists the settings contained in a paint. The GR.PAINT.RESET statement sets each back to its default value.

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of this section.

GR.PAINT.COPY[edit]

Copy one paint to another

Synopsis
GR.PAINT.COPY {{<src_nexp>}{, <dst_nexp>}}
Description

Given two paint numbers, the GR.PAINT.COPY statement copies all the settings from the source paint to the destination paint. If either parameter is omitted, GR.PAINT.COPY uses the current paint.

Examples
GR.PAINT.COPY s

As <dst_nexp> is omitted, this statement uses the settings of paint number s as the current paint.

GR.PAINT.COPY ,d

As <src_nexp> is omitted, this statement stores the settings of the current paint as paint number d.

GR.PAINT.COPY s,d

This statement modifies paint number d to have the same settings as paint number s.

Graphic objects[edit]

Creating a graphic object adds an element to the Display List. Each RFO-BASIC! statement has parameters that describe what to draw and where. Three other BASIC statements that create graphic objects are GR.TEXT.DRAW, GR.BITMAP.DRAW, and GR.CLIP.

Apart from points, lines, and arcs under certain settings, drawing an element may mean:

  1. Tracing it, according to the color and fill style, stroke width, and anti-aliasing setting specified by the current paint, then
  2. Filling it in, according to the color.

If the style specified by GR.COLOR is 0, BASIC traces the outline of the element. A value of 1 means to fill it in, and 2 means to do both.

GR.POINT[edit]

Draw a single pixel

Synopsis
GR.POINT <obj_nvar>, <x_nexp>, <y_nexp>
Description

The GR.POINT statement draws a point at the location (x, y) specified by <x_nexp> and <y_nexp>, and returns its object number in <obj_nvar>.

By default, the point is a single pixel. If the stroke width is greater than 1, GR.POINT draws a square centered at (x, y). The point's appearance is also affected by the anti-aliasing setting. Unless anti-aliasing is disabled, a single pixel will be blurred with adjacent pixels and may be hard to see.

Modifiable attributes

x, y

See also

The GR.SET.PIXELS statement, below, lets a BASIC program draw many pixels in a single operation, whose coordinates are specified in an array.

GR.LINE[edit]

Draw a line

Synopsis
GR.LINE <obj_nvar>, <x1_nexp>, <y1_nexp>, <x2_nexp>, <y2_nexp>
Description

The GR.LINE statement draws a line between (x1, y1) and (x2, y2), and returns its object number in <obj_nvar>.

The line's appearance is affected by the stroke width and the anti-aliasing setting. By default, anti-aliasing and the stroke width of 1 blur each point on a horizontal or vertical line over two side-by-side pixels. To draw horizontal or vertical lines a single pixel in width, the program should disable anti-aliasing.

Modifiable attributes

x1, y1, x2, y2

GR.RECT[edit]

Draw a rectangle

Synopsis
GR.RECT <obj_nvar>, <left_nexp>, <top_nexp>, <right_nexp>, <bottom_nexp>
Description

The GR.RECT statement draws a rectangle where one corner is at (left, top) and whose opposite corner is at (right, bottom), and returns its object number in <obj_nvar>.

The left and right edges can be exchanged, and the top and bottom edges can be exchanged, and the specified rectangle is drawn without a run-time error. If either pair has the same value, it specifies a width or a height of 0, in which a line segment or a single point may be drawn.

Modifiable attributes

bottom, left, right, top

GR.OVAL[edit]

Draw an oval

Synopsis
GR.OVAL <obj_nvar>, <left_nexp>, <top_nexp>, <right_nexp>, <bottom_nexp>
Description

The GR.OVAL statement draws an oval whose bounding box is a rectangle (as in GR.RECT, just above) extending from (left, top) to (right, bottom), and returns its object number in <obj_nvar>.

If the bounding box is a rectangle, then GR.OVAL draws an oval that is tangent with each side of the bounding box at its midpoint. The oval's major axis is always horizontal or vertical, depending on which dimension of the bounding box is longer; but it can be rotated. If the bounding box is a square, then GR.OVAL draws a circle whose diameter is | left−right | = | top−bottom |.

If the bounding box is a line segment or a single point, then GR.OVAL draws the same thing as GR.RECT with the same parameters would draw.

Modifiable attributes

bottom, left, right, top

See also

A circle can also be drawn by specifying its center and radius using GR.CIRCLE.

GR.CIRCLE[edit]

Draw a circle

Synopsis
GR.CIRCLE <obj_nvar>, <x_nexp>, <y_nexp>, <radius_nexp>
Description

The GR.CIRCLE statement draws a circle whose center is at (x, y) and whose radius is given in <radius_nexp>, and returns its object number in <obj_nvar>. As usual, depending on the style set by GR.COLOR, this may be a circle or a filled-in disc.

Modifiable attributes

radius, x, y

See also

A circle can also be drawn by specifying its horizontal and vertical boundaries, using GR.OVAL.

GR.ARC[edit]

The results of the example program with f=0 and s=0.
The results of the example program with f=0 and s=1.
The results of the example program with f=1 and s=0.
The results of the example program with f=1 and s=1.

Draw an arc

Synopsis
GR.ARC <obj_nvar>, <left_nexp>, <top_nexp>, <right_nexp>, <bottom_nexp>,
 <start_angle_nexp>, <sweep_angle_nexp>, <fill_mode_nexp>
Description

The GR.ARC statement draws an arc-shaped object based on the oval that fits entirely within the bounding box from (left, top) to (right, bottom) that GR.OVAL, would have drawn, and returns its object number in <obj_nvar>.

The arc is the portion of the oval starting at <start_angle_nexp>, measuring clockwise from the right edge of the bounding box, and continuing clockwise through <sweep_angle_nexp>. Both numbers are expressed in degrees.

GR.ARC has four effects.

  • The <fill_mode> parameter of GR.ARC determines whether the figure is an arc (plus the associated chord, if GR.COLOR says to fill it in), or a piece of pie:
  • The fill style of the current paint (set by a parameter of GR.COLOR) determines whether RFO-BASIC! traces a figure or fills it in.
fill_mode GR.COLOR
Fill Style
Effect
0 0 The arc is traced
0 nonzero A wedge is drawn consisting of the arc and the chord between its endpoints. This figure is filled in.
nonzero 0 A pie shape is traced consisting of the arc and lines between each endpoint of the arc and the center of the bounding box.
nonzero nonzero A pie shape is drawn, consisting of the arc and lines between each endpoint of the arc and the center of the bounding box. This figure is filled in.
Example

The following program produced the four illustrations shown in this section. The numbers shown in green were changed to produce the four effects:

f=0  % Fill mode
s=0  % Style
GR.OPEN                % Assumes white background
GR.COLOR 255,0,0,0     % Draw bounding rectangle in opaque black
GR.RECT obj1, 100,100, 500,300
GR.COLOR 255,255,0,0,s % Set opaque red and desired style
GR.SET.STROKE 5        % Wide red line to illustrate
GR.ARC  obj2, 100,100, 500,300, 30, 90, f
GR.RENDER              % Start at 30 deg, continue thru 30+90 deg
PAUSE 10000            % Keep it displayed for ten seconds
Modifiable attributes

bottom, fill_mode, left, right, start_angle, sweep_angle, top

GR.SET.PIXELS[edit]

Draw pixels according to an array

Synopsis
GR.SET.PIXELS <obj_nvar>, <pixels_narray> {, <x_nexp>, <y_nexp>}
Description

The GR.SET.PIXELS statement draws individual pixels on the screen according to a list of x and y coordinates contained in <pixels_narray>, and returns, in <obj_nvar>, an object number in the Display List that refers to the entire set of pixels.

The array can be specified in either of the following ways:

  • Without subscripts, to denote the entire array: name[]
  • With two numbers, denoting the starting position and number of elements: name[start,length]

The number of array elements must be even. In each pair of elements, the first is an x coordinate and the second is a y coordinate, and GR.SET.PIXELS draws a point at the(x, y) coordinate specified by the pair.

If the parameters <x_nexp> and <y_nexp> are present, they are a relocation to be added to each location in the array to determine where the corresponding point is drawn. This means that the array can describe a figure, relative to itself, and rely on <x_nexp> and <y_nexp> to specify where to put it on the screen. In addition, by using GR.MODIFY on these attributes, the program can easily move the figure around on the screen. Negative <x_nexp> and <y_nexp> values are valid and result in relocation to the left and upward, respectively. If <x_nexp> and <y_nexp> are omitted, no relocation occurs and the (x, y) pairs in the array are absolute screen positions.

Not only can the program use GR.MODIFY to change the relocation distances, it can assign new values to relevant elements in the array to change the figure that BASIC draws, the next time GR.RENDER occurs.

Modifiable attributes

x, y

GR.POLY[edit]

Draw a polygon from a BASIC list

Synopsis
GR.POLY <obj_nvar>, <list_nexp> {, <x_nexp>, <y_nexp>}
Description

The GR.POLY statement draws a closed polygon, and returns its object number in <obj_nvar>. The <list_nexp> is the number of a numeric List (see Data structures). Starting with list element 1, GR.POLY reads pairs of numbers from the list and interprets them as (x, y) coordinates of a point on the graphics screen. If the list has fewer than 4 elements, GR.POLY has no effect. Otherwise, GR.POLY draws lines from point to point, until reaching the end of the list, then draws a line back to the first point specified in the list. The process of drawing a line is as specified for the GR.LINE statement. It uses the settings in the current paint, which means that it may fill in the polygon if the fill style of GR.COLOR says to do so.

If the parameters <x_nexp> and <y_nexp> are present, they are a relocation to be added to each location in the list to determine where the corresponding point is drawn. As with GR.SET.PIXELS above, the list may describe a polygon relative to itself, and rely on <x_nexp> and <y_nexp> to specify where to put it on the screen. In addition, by using GR.MODIFY on these attributes, the program can easily move the polygon around on the screen. Negative <x_nexp> and <y_nexp> values are valid and result in relocation to the left and upward, respectively. If <x_nexp> and <y_nexp> are omitted, no relocation occurs and the (x, y) pairs in the list are absolute screen positions.

The same list can be used in different GR.POLY statements, and the statements may specify a paint that has a different color, stroke width, and fill mode.

The program can use GR.MODIFY to change the relocation distances, and to change the list number that defines the polygon. It can also assign new values to relevant elements in the list to change the shape of the polygon that BASIC draws, the next time GR.RENDER occurs. This includes adding and removing points.

When GR.POLY is executed, or when the program changes the list using GR.MODIFY, the specified list must have an even number of elements, 4 or greater, so as to specify at least a line segment. If the program modifies the list, then subsequent GR.RENDER operations silently ignore violations of this rule: If the list now contains an odd number of elements, GR.RENDER does not use the final coordinate pair; and if the list now contains fewer than 4 elements, then GR.RENDER does not draw the polygon at all.

Modifiable attributes

list, x, y

Examples

The program Sample Programs/f30_poly.bas, included in the RFO-BASIC! distribution, provides several working examples of GR.POLY.

GR_COLLISION()[edit]

Detect overlap of two graphic objects

Synopsis

GR_COLLISION(<object1_nvar>, <object2_nvar>)

Description

The GR_COLLISION() function takes as parameters two numeric variables, each containing a valid object pointer returned by one of the following statements:

  • GR.ARC
  • GR.BITMAP.DRAW
  • GR.CIRCLE
  • GR.OVAL
  • GR.POINT
  • GR.RECTANGLE
  • GR.TEXT.DRAW

The function returns TRUE (a nonzero value) if the two graphic objects overlap, and FALSE (0) if they do not overlap.

Overlap is defined in terms of the graphic object's rectangular bounding box. Overlap of the bounding boxes is not the same thing as a collision between the objects as drawn:

  • For arcs, circles, and ovals, the thing tested is a rectangle and not the curves drawn. In an on-screen pinball game, GR_COLLISION() does not detect when the pinballs touch. (It returns TRUE too early except if the balls are aligned horizontally or vertically.) Collision of two circles is easily detected by computing whether the distance between centers is less than or equal to the sum of the radii.
  • For text, the bottom of the rectangle is the baseline of the text. Thus, GR_COLLISION() does not detect a collision with the descenders of letters, nor with diacritical marks drawn beneath letters.
  • GR_COLLISION() does not take into account the stroke width (see GR.SET.STROKE). For example, the bounding box for a drawn point is a box 1 pixel high and 1 pixel wide, even though, with a stroke width greater than 1, the point would be drawn on many adjacent pixels. Likewise, if other graphic elements were fattened by being drawn using large stroke widths, GR_COLLISION() does not detect their extra area.
  • Bitmaps may have transparent regions. GR_COLLISION() does not sense pixels drawn on the screen to determine whether the opaque regions of drawn objects are actually touching or overlapping.

Graphic text[edit]

The text console makes it easy to output text, such as with the PRINT statement, but without any special effects, even color. An RFO-BASIC! program can draw text on the graphics screen and take detailed control over things like size, color, exact location, and other typography.

Every character in graphic text is a shape. In rendering that character, BASIC may trace its outline, as it would draw any line or arc; and may then fill in the shape, depending as usual on the fill style that is set by GR.COLOR.

Only the GR.TEXT.DRAW statement draws text onto the graphics screen. There are many other GR.TEXT statements that set the rules for drawing text. These settings are stored in a paint.

GR.TEXT.DRAW[edit]

Draw text on the graphics screen

Synopsis
GR.TEXT.DRAW <obj_nvar>, <x_nexp>, <y_nexp>, <text_sexp>
Description

The GR.TEXT.DRAW statement draws the contents of <text_sexp> as a line of text onto the graphics screen, using tracing and drawing settings, such as color, that are contained in the current paint. The entire line of text is an object, and GR.TEXT.DRAW returns its object number in <obj_nvar>.

The text is drawn starting at <x_nexp> and <y_nexp>, which are pixel positions on the graphics screen.

The horizontal position of the text, specified by <x_nexp>, is by default the left edge of the text. However, a program can use GR.TEXT.ALIGN to make GR.TEXT.DRAW statements accept the center or the right edge.

The vertical position of the text, specified by <y_nexp>, is the position of the text baseline. This is the line for the bottoms of any capital letters, and of most lowercase letters. On letters with descenders, such as "g", the character extends below the baseline. Consequently, the BASIC program should avoid two things:

  • Specifying <y_nexp> close to 0, to write to the "top line" of the graphics screen. This instead puts most of the text off the top of the screen. Only the descenders would be visible.
  • Specifying <y_nexp> close to the height of the screen, to write to the "bottom line." The program should specify a smaller number so that the descenders are on the screen. This may depend on the font being used.
Modifiable attributes

text, x, y (If using GR.MODIFY to modify text, the new value must be a string representing the new text to draw.)

Example

There is a primitive but complete example of drawing text at GR.RENDER.

See also

See Text measurement below for:

  • A more detailed discussion of the height of text drawn onto the graphics screen, including certain non-English characters.
  • BASIC statements that measure the dimensions of text if it were drawn on the graphics screen.
  • Formulas to tell if text will fit on the screen.

Fonts[edit]

A font is the style of the characters in which RFO-BASIC! draws text. Typical Android devices have a monospace font (a font in which all the characters have equal width), a serif font (in which characters have "feet" to enhance readability, like the fonts in a newspaper), and a sans-serif (without serif) font, which looks simpler and more modern.

Built-in fonts. Fonts called monospace, serif, and sans serif are available on all Android devices. More recent Android versions also support sans-serif, sans-serif-light, sans-serif-condensed, and sans-serif-thin.

User fonts. Users can use their own fonts loaded from files, such as TrueType fonts (.TTF) or collections (.TTC). Any RFO-BASIC! program that uses a user font must first load it using the FONT.LOAD statement. This assigns the user font a font pointer, which is an integer. Thereafter, the program can command text to be drawn in that font using the GR.TEXT.SETFONT statement. Selecting a user font in this way for use in the graphic display is the only thing RFO-BASIC! lets you do with a user font.

GR.TEXT.SETFONT[edit]

Set the font in which text is drawn

Synopsis
GR.TEXT.SETFONT {<font_exp> {, <style_sexp>} {,<paint_nexp>}}
Description

The GR.TEXT.SETFONT statement selects a font in which subsequent text is drawn.

The <font_exp> parameter specifies the font. If it is a string expression, it must specify one of the font families available on the Android device, or else the default font is used, which is usually sans serif. The string can use capital or lowercase letters.

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of the section on Paints.

Built-in fonts. If <font_exp> is a string, then <style_sexp> can specify the style, again using capital or lowercase letters. If it is not one of the following, or is omitted, then neither boldfacing nor italics is in effect:

  • bold (or b) for boldfacing
  • italic (or i) for italics
  • bold_italic (or bi) for both effects

You cannot pass the filename of a user font to GR.TEXT.SETFONT as a string value of <font_exp>. You must first load the user font, using FONT.LOAD, and specify the font pointer as a numeric value of <font_exp>, as explained next.

User fonts. If <font_exp> is a numerical expression, it must be a font pointer value to a user font that the FONT.LOAD statement returned. In this case, BASIC ignores <style_sexp>. You cannot load a font, such as Palatino, and specify italic, but you may be able to load Palatino Italic.

If <font_exp> is omitted entirely, then GR.TEXT.SETFONT uses the user font most recently loaded by FONT.LOAD and not deleted. If there are no such fonts, then it reverts to the default font.

GR.TEXT.TYPEFACE[edit]

Set the built-in font in which text is drawn (deprecated)

Synopsis
GR.TEXT.TYPEFACE {{<font_nexp>} {, <style_nexp>} {,<paint_nexp>}}
Description

The GR.TEXT.TYPEFACE statement selects a built-in font in which subsequent text is drawn. GR.TEXT.TYPEFACE is older and less powerful than the GR.TEXT.SETFONT statement described above. It is limited to the four built-in fonts that have numerical codes. The numerical parameters are optional, and have the following effects:

<font_nexp> <style_nexp>
Value Effect Value Effect
1 or omitted Default font (usually sans-serif) 1 or omitted Normal (not bold or italic)
2 Monospace
(<style_nexp> is ignored)
2 Bold
3 Sans-serif 3 Italic
4 Serif 4 Bold and italic

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of the section on Paints.

FONT.LOAD[edit]

Load a user font

Synopsis
FONT.LOAD <font_ptr_nvar>, <filename_sexp>
Description

The FONT.LOAD statement loads the user font whose filename is specified by <filename_sexp>. This is the filename (for example, Eurostib.TTF), not the name of the font (such as Eurostile Bold). The statement searches for a file with the specified name starting in the data directory. If you created a sibling fonts directory for user fonts, <filename_sexp> might be "../fonts/Eurostib.TTF".

The statement returns in <font_ptr_nvar> an integer that future GR.TEXT.SETFONT statements can use to select this user font. If the specified file does not exist or is not in a format that RFO-BASIC! supports, then the statement sets <font_ptr_nvar> to -1. In this case, the GETERROR$() function returns a text explanation of the error.

Successful use of FONT.LOAD does not make the user font "built-in." Built-in fonts are fonts like monospace that are available without using FONT.LOAD.

FONT.DELETE[edit]

Delete a user font

Synopsis
FONT.DELETE {<font_ptr_nexp>}
Description

The FONT.DELETE statement deletes any previously loaded font whose font pointer is contained in <font_ptr_nexp>. A statement without <font_ptr_nexp> deletes the most-recently-loaded font (highest-valued font pointer) that is not already deleted. Additional statements without the parameter continue deleting fonts in a last-to-first order.

Deleting a font reclaims the device memory that the font used to occupy. The value in <font_ptr_nexp> becomes an invalid font pointer and cannot be used in GR.TEXT.SETFONT statements. If the program loads the same font again, RFO-BASIC! does not assign it the same font pointer it had previously.

It is not an error to delete a font that has already been deleted. It is not an error to call FONT.DELETE if no fonts are loaded.

FONT.CLEAR[edit]

Delete all user fonts

Synopsis
FONT.CLEAR

The FONT.CLEAR statement deletes all user fonts. When user fonts are deleted, any use of GR.TEXT.SETFONT that specifies a user font (numeric or omitted <font_exp>) is invalid.

GR.TEXT.SIZE[edit]

Specify the size (height) of text

Synopsis
GR.TEXT.SIZE {{<size_nexp>}{,<paint_nexp>}}
Description

The GR.TEXT.SIZE statement specifies the height of text, in pixels. BASIC will render text in a size such that characters entirely fit in <size_nexp> pixels, even characters with diacritical marks above the letter, and characters with descenders, such as "g".

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of the section on Paints.

Usage

The <size_nexp> parameter should not be a constant. Using a fraction of the screen width or height, as detected by GR.SCREEN, lets the program produce similar output when it runs on an Android device with a different number of pixels horizontally or vertically.

Character width varies along with height, to preserve the font as designed. There is no option to stretch text by a specified percentage. To obtain characters wider or narrower than those designed into the font, here are several options:

  • Obtain a font with Condensed, Expanded, or Oblique style designed into it.
  • Use GR.SCALE to stretch or shrink everything written to the screen.
  • For the text that is to be condensed or expanded, use Draw-Into Mode to draw the text into a bitmap, then display the bitmap with altered dimensions. See the example at GR.BITMAP.DRAWINTO.END.

Other typography[edit]

GR.TEXT.UNDERLINE[edit]

Specify underlining

Synopsis
GR.TEXT.UNDERLINE {{<enabled_lexp>}{,<paint_nexp>}}
Description

The GR.TEXT.UNDERLINE statement determines whether text is drawn with a line beneath it. If <enabled_lexp> has the value 0, underlining is disabled. If it has any other value, underlining is enabled. If the parameter is omitted entirely, underlining is set to the opposite of what it was.

GR.TEXT.STRIKE[edit]

Specify strikethrough (overstrike)

Synopsis
GR.TEXT.STRIKE {{<enabled_lexp>}{,<paint_nexp>}}
Description

The GR.TEXT.STRIKE statement determines whether text is drawn with a line across it (struck out). If <enabled_lexp> has the value 0, strikethrough is disabled. If it has any other value, strikethrough is enabled. If the parameter is omitted entirely, strikethrough is set to the opposite of what it was.

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of the section on Paints.

Usage

Strikethrough is a technique sometimes used to show that text has been deleted, or that information on the screen is no longer in effect.

GR.TEXT.SKEW[edit]

Specify skew (italics)

Synopsis
GR.TEXT.SKEW {{<enabled_nexp>}{,<paint_nexp>}}
Description

The GR.TEXT.SKEW statement determines whether text is drawn skewed, that is, in italics. If <enabled_lexp> has the value 0, skewing is disabled. If it has any other value, skewing is enabled. If the parameter is omitted entirely, skewing is set to the opposite of what it was.

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of the section on Paints.

Usage

It is more attractive to use a font where font designers have designed italic characters, than to use a font where they have not, and ask BASIC to render italics by simple skewing. Therefore, instead of using GR.TEXT.SKEW, the following techniques are preferred:

  • Specify italics when selecting a font, such as with GR.TEXT.SETFONT "serif", "italic"
  • Load a user font designed with italics, such as Helvetica Italic

However, if the Android device has a font and does not have the corresponding italic font, then there is no alternative to GR.TEXT.SKEW except to not show italics at all.

GR.TEXT.BOLD[edit]

Specify boldfacing

Synopsis
GR.TEXT.BOLD {{<enabled_lexp>}{,<paint_nexp>}}
Description

The GR.TEXT.BOLD statement determines whether text is drawn in boldface. If <enabled_lexp> has the value 0, boldfacing is disabled. If it has any other value, boldfacing is enabled. If the parameter is omitted entirely, boldfacing is set to the opposite of what it was.

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of the section on Paints.

Usage

It is more attractive to use a font where font designers have designed boldfaced characters, than to use a font where they have not, and ask BASIC to render boldfaced characters by computation. Therefore, instead of using GR.TEXT.BOLD, the following techniques are preferred:

  • Specify boldfacing when selecting a font, such as with GR.TEXT.SETFONT "serif", "bold"
  • Load a user font designed with boldface, such as Helvetica Bold

However, if the Android device has a font and does not have the corresponding boldface font, then there is no alternative to GR.TEXT.BOLD except to not show boldfacing at all.

Yet another technique for producing fatter characters is to set the fill style of GR.COLOR to 2 (stroke-and-fill) and use GR.SET.STROKE to set a stroke width much greater than 1. (Suitable values will depend on the text size and on the number of pixels on the Android device.) However, again, this method produces boldfacing by algorithm, which is likely to be less attractive than using a font with boldfacing designed into the characters.

Related techniques[edit]

There are several useful techniques that can also be achieved by taking advantage of the fact that drawing text is divided into tracing and filling shapes.

Outline

By setting the fill style of GR.COLOR to 0 (stroke and do not fill), text can be drawn as an outline and not filled in. This is an alternative to graying-out text, for instance, to indicate to the user options that are temporarily irrelevant or disabled, with less confusion than removing the options from the screen entirely.

Shadowing

By setting GR.COLOR to opaque white with a fill style of stroke-only, drawing text, setting GR.COLOR to a dark color with a fill style of fill-only, then drawing the same text, a program can produce text with contrasting edges. For example, such text can be drawn on top of a bitmap with good visibility no matter whether it lands over a light or a dark area of the bitmap.

Text measurement[edit]

GR.TEXT.ALIGN[edit]

Specify text alignment

Synopsis
GR.TEXT.ALIGN {{<align_nexp>}{,<paint_nexp>}}
Description

The GR.TEXT.ALIGN statement governs how subsequent GR.TEXT.DRAW statements interpret their <x_nexp> parameters. This parameter normally marks the left edge of the box where GR.TEXT.DRAW draws the text. However:

  • If <align_nexp> is 2, then GR.TEXT.DRAW statements will interpret their <x_nexp> as the center of the text.
  • If <align_nexp> is 3, then GR.TEXT.DRAW statements will interpret their <x_nexp> as the right edge of the text.

If <align_nexp> has any other value, or is omitted entirely, GR.TEXT.DRAW reverts to positioning text so that the horizontal position parameter gives the left edge.

The <paint_nexp> parameter specifies the number of a paint. The specified changes affect that paint. If <paint_nexp> is omitted, the specified changes affect the current paint. See the overview of paints at the start of the section on Paints.

Usage

A program can locate text exactly where desired; GR.TEXT.ALIGN merely simplifies the job of specifying it. When drawing several lines that are to appear centered around the same horizontal position, specifying GR.TEXT.ALIGN 2 lets the program use this position as the <x_nexp> for all those lines. When drawing several lines that are to be right-justified, specifying GR.TEXT.ALIGN 3 lets the program supply the right edge to all the GR.TEXT.DRAW statements. Without this simplification, the program would have to compute the position of each line based on the rendered width of the text.

See also

The following Text measurement statements are how a program performs such computations.

GR.TEXT.HEIGHT[edit]

Measure the height of graphic text

Synopsis
GR.TEXT.HEIGHT {<height_nvar>} {, <up_nvar>} {, <down_nvar>}
Description
Portions of the characters in red are the descenders. The thicker gray line is the baseline. Characters such as the Polish letter "Ż" are off the bounds of this diagram.

The GR.TEXT.HEIGHT statement obtains information on the vertical size of text that would be drawn using the current paint. (RFO-BASIC! provides no way to compute the actual height occupied by text that was drawn.)

If <height_nvar> is present, GR.TEXT.HEIGHT sets it to the height in pixels of most text in most languages. If the program used GR.TEXT.SIZE, then this value is the value it specified.

Some characters require additional space. For example, "É" in French and Spanish, and "Ż" in Polish, have a diacritical mark above a capital letter. Any font that has this character renders it above the height specified in GR.TEXT.SIZE. The dot is above the entire illustration. If <up_nvar> is specified, GR.TEXT.HEIGHT sets it to the distance in pixels of the tallest character above the baseline. This is always a negative number, because it is a distance from the baseline back toward the top of the screen.

If <down_nvar> is specified, GR.TEXT.HEIGHT sets it to the height of the zone occupied by descenders. This is a positive value, and again, measured from the baseline. Therefore:

  • The sum of these two numbers (actually, down − up since up is a negative number) is the space that must be allowed to accommodate text in the most extreme case.
  • When GR.TEXT.DRAW draws text at vertical position y, the lowest vertical position that descenders could occupy is y + down, and the highest vertical position that a diacritical mark above a capital letter could occupy is y + up.
  • Excluding these foreign characters, text will not range above y + down − height.

GR.TEXT.WIDTH[edit]

Measure the width of graphic text

Synopsis
GR.TEXT.WIDTH <width_nvar>, <text_exp>
Description

The GR.TEXT.WIDTH returns, in <width_nvar>, the width of graphic text.

  • If <text_exp> is a string, then GR.TEXT.WIDTH returns the number of horizontal pixels it would take to draw that string as graphic text, using the settings in the current paint, such as the typeface, size, and style specified most recently.
  • If <text_exp> is numeric, then its value must be the object number returned by a GR.TEXT.DRAW statement, or else it is a run-time error. GR.TEXT.WIDTH returns the number of horizontal pixels that the GR.TEXT.DRAW statement would use if its text element were rendered right now. (If the program uses GR.MODIFY, or modifies the relevant paint, before calling GR.RENDER, then this value might become inaccurate.)

GR.GET.TEXTBOUNDS[edit]

Measure the size of text

Synopsis
GR.GET.TEXTBOUNDS <text_exp>, <left_nvar>, <top_nvar>, <right_nvar>, <bottom_nvar>
Description

The GR.GET.TEXTBOUNDS statement gets the boundary rectangle of a string as it would be drawn on the screen.

  • If <text_exp> is a string, then GR.GET.TEXTBOUNDS returns the rectangle that would contain that string as graphic text, using the settings in the current paint, such as the typeface, size, and style specified most recently.
  • If <text_exp> is numeric, then its value must be the object number returned by a GR.TEXT.DRAW statement, or else it is a run-time error. GR.GET.TEXTBOUNDS returns the rectangle that the GR.TEXT.DRAW statement would use if its text element were rendered right now. (If the program uses GR.MODIFY before GR.RENDER, then this value might become inaccurate.)

GR.GET.TEXTBOUNDS reports the dimensions as though a GR.TEXT.DRAW statement were written to draw the text at 0,0. To determine the actual screen space used to hold that text, one must add the x or y values passed to the GR.TEXT.DRAW statement.

Bitmaps[edit]

Bitmaps comprise the illustrations on the graphics screen. Instead of points, lines, shapes, or text, these elements come from a file, or through a use of Draw-Into Mode. The file may be of type .BMP, which describes each point (pixel) in the illustration, or of type .GIF or .JPG or .PNG, which use coding to compress the size of the file.

The GR.BITMAP.LOAD statement loads a bitmap from storage (internal storage or a device such as a microSD card) into Android memory and returns a bitmap pointer the BASIC program uses to refer to the bitmap. The GR.BITMAP.DRAW statement creates a separate object in the Display List. Like any other drawing statement, this object calls for the illustration to be drawn when the program calls GR.RENDER.

Even if a program uses GR.CLS to discard the Display List (typically to start over and regenerate the information to be rendered on the graphics screen), RFO-BASIC! does not discard bitmaps until the program ends or calls GR.BITMAP.DELETE. Programs structured with a main loop that calls GR.CLS, regenerates the graphics screen, and calls GR.RENDER to render the information, must not reload the bitmap on each pass through the main loop (unless they discard the bitmap after rendering), or they will quickly exhaust the available memory.

Whether or not the stored version of the bitmap uses techniques to reduce file size, the in-memory version of the bitmap uses four bytes of memory for each pixel. So a 400-by-400-pixel bitmap uses 400✕400✕4 = 640,000 bytes. Any BASIC statement that creates a bitmap will fail if there are insufficient memory bytes available to the program. In this case, the statement returns -1 as the bitmap pointer. The program should always check for this. If the returned pointer is -1, the program can call the GETERROR$() function to get more details about the error.

Some cases that exhaust the available Android memory might not be detected by the GR.BITMAP statements. Subsequent statements might fail, displaying an error message on the console screen. When the user returns to the editor, BASIC highlights a line near the one that exceeded the memory limit. This might not be the exact BASIC statement that exceeded it.

GR.BITMAP.LOAD[edit]

Load a bitmap

Synopsis
GR.BITMAP.LOAD <bptr_nvar>, <filename_sexp>
Description

The GR.BITMAP.LOAD statement creates a bitmap from the file specified in <filename_sexp>. This string is the name of a file containing the bitmap. It is assumed to reside in the <pref base drive>/rfo-basic/data/ directory. However, the string can contain relative pathnames, using ../, to specify a different directory. The bitmap may be on a device such as a microSD card, although the pathname that refers to such a device differs among Android devices.

If GR.BITMAP.LOAD loads a bitmap, <bptr_nvar> is the bitmap pointer the program uses to refer to the bitmap, such as in a subsequent GR.BITMAP.DRAW statement. If it does not load a bitmap, then it sets <bptr_nvar> to -1, which cannot be used as a bitmap pointer. The program should check for this case. It can call the GETERROR$() function for more information about the failure. This function may return:

  • The file or resource does not exist.
  • There is not enough memory available to create the bitmap.

Loaded bitmaps cannot be used with Draw-Into Mode. However, a program can create an empty bitmap, draw a loaded bitmap into it, and draw over the bitmap.

See also

After loading a bitmap, the program can resize it into another bitmap using GR.BITMAP.SCALE. The bitmap is not added to the Display List until the program draws it using GR.BITMAP.DRAW, and as always, it is not visible on the graphics screen until the program renders the Display List by calling GR.RENDER.

GR.BITMAP.SCALE[edit]

Load a scaled bitmap

Synopsis
GR.BITMAP.SCALE <new_bptr_nvar>, <bptr_nexp>, <width_nexp>, <height_nexp> {, <smoothing_lexp>}
Descrption

The GR.BITMAP.SCALE statement creates a bitmap whose contents are those of the previously loaded bitmap <bptr_nexp>, changed to the specified width and height. The statement sets <new_bptr_nvar> to a pointer that refers to the new (derived) bitmap. The original bitmap still exists. If the program loaded the original bitmap only to create a scaled bitmap, then it should delete the original bitmap using GR.BITMAP.DELETE.

GR.BITMAP.SCALE can fail just as GR.BITMAP.LOAD can. The program can react to such failures as it does in the case of GR.BITMAP.LOAD.

Neither <width_nexp> nor <height_nexp> may be zero. However, either or both may be negative. In this case, the scaled bitmap is flipped in that dimension (left-to-right or upside-down).

Normally, the scaled bitmap is smoothed. This uses image enhancement to increase the visual attractiveness of the scaled bitmap. If the optional <smoothing_lexp> parameter is present and if its value is 0, then the scaled bitmap is not smoothed. Instead, if either height or width is reduced from the original bitmap, this is achieved by removing entire rows or columns of pixels. If either height or width is increased from the original bitmap, this is achieved by duplicating entire rows or columns of pixels.

GR.BITMAP.CROP[edit]

Load a cropped bitmap

Synopsis
GR.BITMAP.CROP <new_bptr_nvar>, <bptr_nexp>, <x_nexp>, <y_nexp>, <width_nexp>, <height_nexp>
Descrption

The GR.BITMAP.CROP statement creates a bitmap whose contents are a rectangular piece of the previously loaded bitmap <bptr_nexp>, starting at the upper-left corner given by (<x_nexp>, <y_nexp>), and with the specified width and height. The statement sets <new_bptr_nvar> to a pointer that refers to the new (derived) bitmap. The original bitmap still exists. If the program loaded the original bitmap only to create a cropped bitmap, then it should delete the original bitmap using GR.BITMAP.DELETE.

The cropped bitmap is exactly the size of the specified rectangular region of the original bitmap. To obtain a bitmap with a different size — for example, to take a piece of the original bitmap and produce a bitmap of the same dimensions, consisting only of this piece — pass <new_bptr_nvar> as the <bptr_nexp> in a call to GR.BITMAP.SCALE.

GR.BITMAP.CROP can fail just as GR.BITMAP.LOAD can. The program can react to such failures as it does in the case of GR.BITMAP.LOAD.

GR.BITMAP.DRAW[edit]

Draw a bitmap

Synopsis
GR.BITMAP.DRAW <obj_nexp>, <bptr_nexp>, <x_nexp>, <y_nexp>
Description

The GR.BITMAP.DRAW statement draws the bitmap whose bitmap pointer is <bptr_nexp>. The statement creates an object in the Display List so that the bitmap appears on the graphics screen, with its upper-left corner at (<x_nexp>, <y_nexp>), when the program renders the graphics screen with GR.RENDER.

If the current paint has an alpha value other than 255 (opaque), then the bitmap is drawn on top of what was previously on that region of the graphics screen, with the specified degree of transparency.

Modifiable attributes

bitmap, x, y

GR.BITMAP.DELETE[edit]

Delete a bitmap

Synopsis
GR.BITMAP.DELETE <bptr_nexp>
Description

The GR.BITMAP.DELETE statement releases the specified bitmap from Android memory. The memory the bitmap used is available for other uses by the BASIC program.

The program should delete bitmaps from memory if it only loaded them to create a scaled bitmap using GR.BITMAP.SCALE, or a cropped bitmap using GR.BITMAP.CROP, once the bitmap has served that purpose.

Deleting a bitmap does not remove objects in the Display List that refer to the bitmap, such as those created by the GR.BITMAP.DRAW statement. Unless the program applies GR.HIDE to such objects, or uses GR.CLS and then regenerates the Display List to not refer to bitmaps it has deleted, the GR.RENDER statement will fail.

Other bitmap statements[edit]

The statements in this section can be used either with bitmaps loaded from storage by GR.BITMAP.LOAD, or blank bitmaps created by GR.BITMAP.CREATE and drawn into using Draw-Into Mode.

GR.BITMAP.SIZE <bptr_nexp>, <width_nvar>, <height_nvar>
The GR.BITMAP.SIZE statement returns the dimensions, in pixels, of the specified bitmap in <width_nvar> and <height_nvar>.


GR.BITMAP.SAVE <bptr_nexp>, <filename_sexp>{, <quality_nexp>}
The GR.BITMAP.SAVE statement saves the specified bitmap into a stored file whose name is <filename_sexp>, which is assumed to be in the directory <pref base drive>/rfo-basic/data/. If the filename ends in ".jpg" then the bitmap is saved as a file in the JPEG format at the quality level specified by <quality_nexp>. Otherwise, the bitmap is saved as a file in the PNG format. The <filename_sexp> and <quality_nexp> parameters are the same as for GR.SAVE.
GR.GET.BMPIXEL <bptr_nexp>, <x_nexp>, <y_nexp>, <alpha_nvar>, <red_nvar>, <green_nvar>, <blue_nvar>
The GR.GET.BMPIXEL statement analyzes a single pixel of the specified bitmap, at pixel coordinates (x, y), and returns in the variables its alpha (transparency), red, green, and blue components. The pixel coordinates must be valid for the size of the bitmap.
GR.BITMAP.FILL <bptr_nexp>, <x_nexp>, <y_nexp>
The GR.BITMAP.FILL statement performs a flood-fill of the specified bitmap. Starting with the pixel at coordinates (x, y), it sets that pixel, and any adjacent area of pixels of the same color, to the color of the current paint.
If the specified bitmap was drawn using anti-aliasing, then boundaries between graphic elements are blurred to appear smoother. This means that pixels on the boundary will not be exactly the same color as the rest of the area, and the flood-fill will not reach to the exact edge of the area.

Draw-Into Mode[edit]

Normally, the drawing statements of RFO-BASIC! build a Display List of objects, and calling GR.RENDER renders the corresponding elements onto the graphics screen. The objects can be moved, rotated, and modified, affecting their appearance the next time the program renders the Display List.

In Draw-Into Mode, BASIC instead draws every specified element into a specified bitmap. Elements do not receive numbers that would make it easy to modify the elements later, and they are not added to a Display List. It is still true that elements drawn into a bitmap can be modified by elements drawn into it later in time. The bitmap itself does not appear in the Display List until explicitly drawn with GR.BITMAP.DRAW, and does not appear on the graphics screen until the Display List is rendered with GR.RENDER.

GR.BITMAP.CREATE[edit]

Create a bitmap to be drawn into

Synopsis
GR.BITMAP.CREATE <bptr_nvar>, <width_nvar>, <height_nvar>
Description

The GR.BITMAP.CREATE statement creates an empty bitmap of the specified width and height. This is not limited to the size of the physical screen; the program can prepare a large bitmap and can crop pieces of it for actual display. The statement returns <bptr_nvar>, a bitmap pointer that the program can use to refer to the new bitmap.

The new bitmap requires width✕height✕4 bytes of memory. GR.BITMAP.CREATE can fail just as GR.BITMAP.LOAD can. The program can react to such failures as it does in the case of GR.BITMAP.LOAD.

GR.BITMAP.DRAWINTO.START[edit]

Start Draw-Into Mode

Synopsis
GR.BITMAP.DRAWINTO.START <bptr_nexp>
Description

The GR.BITMAP.DRAWINTO.START statement starts Draw-Into Mode. All subsequent drawing statements do not create objects in the Display List, but instead draw directly into the bitmap whose number is <bptr_nexp>. This must be a blank bitmap created by GR.BITMAP.CREATE; it cannot be a bitmap loaded from storage by GR.BITMAP.LOAD. However, preloaded bitmaps can be drawn into <bptr_nexp> during Draw-Into Mode.

While BASIC is in Draw-Into Mode, the <ptr_nexp> returned by any drawing statements is not valid, and the program cannot use any of the usual techniques to affect graphic elements before rendering them, such as moving, hiding, or modifying them.

The bitmap being drawn into has no effect on the graphics screen until the program exits Draw-Into Mode, draws the bitmap normally, using GR.BITMAP.DRAW, and renders the Display List using GR.RENDER.

GR.BITMAP.DRAWINTO.END[edit]

End Draw-Into Mode

Synopsis
GR.BITMAP.DRAWINTO.END
Description

The GR.BITMAP.DRAWINTO.END statement ends Draw-Into Mode and returns RFO-BASIC! to the usual mode, in which drawing statements assemble a Display List to be rendered on the graphics screen by GR.RENDER.

Having drawn into a bitmap during Draw-Into Mode, what the BASIC program does after ending Draw-Into Mode is use the bitmap it was drawing into: Either draw it or derive another bitmap from it by cropping or scaling.

Example

To draw condensed text if a Condensed font is not available, a program could draw the text into a bitmap(Bmp1), compress the entire bitmap into a new bitmap (Bmp2) using GR.BITMAP.SCALE, and draw that bitmap onto the graphics screen:

Output from the example.
OutdoorTmp = 23
GR.OPEN
GR.COLOR 255,0,0,0

!Temporary bitmap is 100 pixels high. GR.TEXT.DRAW puts the baseline at y=90.
!Text size of 80 means numerals are drawn between y=10 and y=90.
GR.BITMAP.CREATE Bmp1,200,100
GR.BITMAP.DRAWINTO.START Bmp1
 GR.TEXT.TYPEFACE 2,2
 GR.TEXT.SIZE 80
 GR.TEXT.DRAW unused,0,90,STR$(OutdoorTmp)
GR.BITMAP.DRAWINTO.END
GR.BITMAP.SCALE Bmp2,Bmp1,100,100   % Copy into new bitmap of half the width
GR.BITMAP.DRAW obj1,Bmp2,0,0        % Draw the second bitmap onto graphic screen
GR.TEXT.DRAW obj2,100,90,"Degrees"  % Add some uncompressed text
GR.RENDER
PAUSE 5000
END

Modifying the Display List[edit]

Every drawing of a point, line, shape, or string of text adds an object to the Display List. The elements are actually rendered on the screen of the Android device only when the program executes GR.RENDER.

As set out in the introduction, one way to structure a program that outputs to the graphics screen is to have a prologue that draws everything, and a main loop that modifies the objects in response to real-time inputs and then calls GR.RENDER.

Each section that defines a BASIC statement that draws, sets out a list of Modifiable attributes. These attributes can be modified by calling GR.MODIFY. In addition, any object in the Display List can be hidden with GR.HIDE — that is, set to inhibit display — and later re-enabled with GR.SHOW. All such modifications take effect the next time the graphics screen is rendered.

GR.MODIFY[edit]

Modify an object in the Display List

Synopsis
GR.MODIFY <obj_nexp>{, <tag_sexp>, <value_exp>}...
Description

The GR.MODIFY statement modifies one or more modifiable attributes of an object in the Display List. The <obj_nexp> parameter must have a value that is the number of such an object, as returned by a BASIC statement that draws a graphic element. The <tag_sexp> parameter must be the name of a modifiable attribute of that kind of object. The <value_exp> parameter contains the new value for that modifiable attribute. Most attributes require that <value_exp> be numeric, but the text attribute of GR.TEXT is a string expression. GR.MODIFY can change only one object at a time, but it can contain any number of tag/value pairs.

For any object in the Display List, the valid tags depend on the BASIC statement that drew the element and thus created the object. The associated GR. statement lists the valid tags, under Modifiable attributes. They are summarized as follows:

Statement Position Angle Other
GR.ARC top bottom left right start_angle sweep_angle fill_mode
GR.BITMAP x y bitmap
GR.CIRCLE x y radius
GR.CLIP top bottom left right RO
GR.GROUP list[1]
GR.LINE x1 y1 x2 y2
GR.OVAL top bottom left right
GR.PIXELS x y Can also modify the array of coordinates directly
GR.POINT x y
GR.POLY x y list
GR.RECT top bottom left right
GR.ROTATE x y angle
GR.TEXT x y text
  1. Bug. RFO-BASIC! subjects list to the same checks as the list in GR.POLY. Therefore, the replacement list must have 4, 6, 8, ... elements. A workaround is to include object numbers in the list multiple times. This bug is fixed in Oli-BASIC.

Every object has two additional modifiable attributes:

  • paint is the number of the paint that contains the settings governing the drawing of that object, such as color, transparency, stroke width, fill style, and so on. This tag can be modified with the number of another paint. For example, a circle that was drawn in opaque black paint can be modified to be drawn in transparent blue paint, the next time it is rendered. This is a simple alternative to re-drawing the element, which also consumes Android memory.
  • alpha is the transparency. Transparency is one component of a paint. However, by specifying alpha, the program can change an object's transparency without having to create or modify paints. This makes it easy to animate fade-out and fade-in effects. Transparency values range from 0 (invisible) to 255 (opaque). The value of 256 in GR.MODIFY is a special case that makes the object use the transparency value in the current paint.
Examples

The following GR.MODIFY statement changes text that was drawn, to reflect current conditions. If a program is using the graphics screen just as a gauge, its main loop may obtain those conditions somehow, execute one or more GR.MODIFY statements like this one, and then do GR.RENDER. The original GR.TEXT.DRAW fixed the size, style, and location of the text, and this GR.MODIFY does not modify those attributes:

GR.MODIFY text_ptr, "text", FORMAT$("-##%.%", OutdoorTemp)

Suppose BM_ptr is the object number of a bitmap drawn by GR.BITMAP.DRAW BM_ptr, galaxy_ptr, 400, 120. Then the program can move it rightward, from 400 to 420, with:

GR.MODIFY BM_ptr, "x", 420

The program can change the object to use a different (pre-loaded) bitmap:

GR.MODIFY BM_ptr, "bitmap", saturn_ptr

These two effects could be achieved in a single GR.MODIFY statement:

GR.MODIFY BM_ptr, "x", 420, "bitmap", saturn_ptr

The following code uses alpha to make an object fade in and out:

DO
 FOR a = 1 TO 255 STEP 10
 GR.MODIFY object, "alpha", a
 GR.RENDER
 PAUSE 20 
 NEXT a

 FOR a = 255 TO 1 STEP -10
 GR.MODIFY object, "alpha", a
 GR.RENDER
 PAUSE 20 
 NEXT a
UNTIL 0
See also

If using GR.MODIFY to change an object's x and y attributes to drag it across the screen, and if the object has been rotated, the program will typically also need to modify the rotation origin in the GR.ROTATE.START statement.

Some drawing statements allow their effects to be modified without using GR.MODIFY:

  • For GR.SET.PIXELS, the program can make changes to the array that specifies the (x, y) coordinates for the pixels to be drawn.
  • For GR.POLY, the program can modify the List structure that gives the (x, y) coordinates with which to draw the polygon.
  • The program can modify a paint, rather than using GR.MODIFY to make an object use a different paint.

GR.HIDE[edit]

Inhibit a graphic element from being rendered

Synopsis
GR.HIDE <obj_nexp>
Description

The GR.HIDE statement inhibits a graphic element from being rendered. The <obj_nexp> parameter must be the number of an object in the Display List. It may be the number of a group, in which case, all elements in the group are hidden.

GR.SHOW[edit]

Allow a graphic element to be rendered

Synopsis
GR.SHOW <obj_nexp>
Description

The GR.SHOW statement allows a graphic element to be rendered. The <obj_nexp> parameter must be the number of an object in the Display List. It may be the number of a group, in which case, all elements in the group are shown.

All graphic elements are renderable by default, though none is actually rendered until a call to GR.RENDER. There is no reason to use GR.SHOW unless the program had hidden elements by calling GR.HIDE.

GR.SHOW.TOGGLE[edit]

Reverse the hidden status of a graphic element

Synopsis
GR.SHOW.TOGGLE <obj_nexp>
Description

The GR.SHOW.TOGGLE statement reverses the hidden status of a graphic element. The <obj_nexp> parameter must be the number of an object in the Display List. If <obj_nexp> had been hidden, then GR.SHOW.TOGGLE un-hides it, like GR.SHOW. Otherwise, GR.SHOW.TOGGLE hides it, like GR.HIDE. It may be the number of a group, in which case, each element in the group has its hidden status reversed; those that were hidden become un-hidden, and those that were shown become hidden.

GR.MOVE[edit]

Move a graphic element

Synopsis
GR.MOVE <obj_nexp> {{, <dx_nexp>}{, <dy_nexp>}}
Description

The GR.MOVE statement moves a graphic element in the direction specified by (dx, dy). Both these parameters can be omitted, in which case they default to a value of 0 (the graphic element is not moved in that dimension).

The effect of GR.MOVE is to modify the x and y modifiable attributes of the specified object. Therefore, any subsequent GR.MOVE statement moves the element further in the direction the follow-on statement specifies. GR.MOVE is equivalent, but generally more concise, than modifying these attributes with GR.MODIFY.

The original x and y values are lost. If the program did not store the values, there is no BASIC statement that returns the element to where it was originally drawn.

The <obj_nexp> parameter can refer to a group, in which case all elements in that group move together (when the graphics screen is next rendered).

Examples
GR.MOVE circle1,1,0   % Move a circle one pixel to the right
GR.MOVE circle1,1     % Equivalent to omit dy
GR.MOVE circle1,0,-5  % Move the circle five pixels up
GR.MOVE circle1,,-5   % Equivalent to omit dx

Examining the Display List[edit]

Normally, a program stores in well-named variables the objects that it might elect to modify later. However, a program may create graphic elements in response to user input, and use the Display List as a database from which it can extract information later, if the need arises. The following statements query the Display List:

GR.GET.TYPE <obj_nexp>, <type_svar>
If <obj_nexp> is the number of an object in the Display List, then GR.GET.TYPE sets <type_svar> to a string value that indicates the BASIC statement that drew it. This may be: arc, bitmap, circle, line, oval, pixels, point, poly, rect, or text.
If <obj_nexp> is the number of a group, then GR.GET.TYPE sets <type_svar> to group. If it is the number of an object created by GR.CLIP, then <type_svar> is clip. If it is the number of an object created by GR.ROTATE, then <type_svar> is rotate. If it is not an object from the Display List, then <type_svar> is the empty string. The program can use GETERROR$() to get details about the error.
GR.GET.PARAMS <obj_nexp>, <param_array$[]>
If <obj_nexp> is the number of an object in the Display List, then GR.GET.PARAMS places in the one-dimensional string array <param_array$[]> all the modifiable attributes of that object, in no particular order. If the array exists, GR.GET.PARAMS overwrites it; otherwise, it creates a new array.
Once obtaining the names of the modifiable attributes with GR.GET.PARAMS, the program can obtain their current values with GR.GET.VALUE and can modify them with GR.MODIFY.
GR.GET.VALUE <obj_nexp> {, <tag_sexp>, <value_var>}...
If <obj_nexp> is the number of an object in the Display List, then for each <tag_sexp> containing a modifiable attribute of that object, GR.GET.VALUE sets <value_var> to the current value of that attribute. GR.GET.VALUE can query only one object at a time, but it can obtain any number of values from it.
Valid values for <tag_sexp> depend on what type of object <obj_nexp> is. This can be determined using GR.GET.TYPE, above. The program can also call GR.GET.PARAMS, below, to enumerate the modifiable attributes of <obj_nexp>. If any <tag_sexp> is not a modifiable attribute of <obj_nexp>, then the corresponding <value_var> is the empty string. Most attributes require that <value_var> be a string variable, but any time that <tag_sexp> contains text, the corresponding <value_var> must be a string variable. If the program violates any of these rules, GR.GET.VALUE does not set the corresponding <value_var>. The program can use GETERROR$() to get details about the error.

Groups[edit]

The RFO-BASIC! program can organize arbitrary objects from the Display List into a group. The group receives an object with a number unique to all others in the Display List. Grouping elements into a group does not affect their rendering, but lets the program easily specify changes to each member of the group.

The program can use the group object, as the <obj_nexp> parameter instead of an object describing a single graphic element, in the following statements:

  • In the GR.MOVE statement, to move each element in the group
  • In the GR.HIDE statement, to hide each element in the group
  • In the GR.SHOW statement, to un-hide each element in the group
  • In the GR.SHOW.TOGGLE statement, to reverse the hidden status of each element in the group

GR.GROUP[edit]

Create a group by naming specific elements

Synopsis
GR.GROUP <group_obj_nvar>{, <obj_nexp>}...
Description

The GR.GROUP statement creates a group and returns the number of its object in <group_obj_nvar>. The program follows this numeric variable with one or more numeric expressions, shown above as <obj_nexp>. Each of these must be a number of an object already in the Display List. Each object becomes a member of the new group.

GR.GROUP creates a List (see Data structures) to hold the numbers of the objects that are members of the group. The program can obtain the number of this List with GR.GET.VALUE and can change the list that belongs to the group using GR.MODIFY.

Modifiable attribute

list

Example

The following is a complete program that creates four red circles, then creates a group containing one of the circles, and uses its membership in the group to move that circle:

GR.OPEN ,,,,,1
GR.COLOR ,255,0,0,2
GR.CIRCLE c1,100,100,40
GR.CIRCLE c2,100,200,40
GR.CIRCLE c3,100,300,40
GR.CIRCLE c4,100,400,40
GR.RENDER : PAUSE 1000            % draw four red circles

GR.GROUP g, c1                    % create a group with one circle
GR.MOVE g, 0, 50                  % move whole group 0 up/down, 50 right
GR.RENDER : PAUSE 1000            % the circle moves

GR.GET.VALUE g, "list", gList     % get the number of the list the group is using
LIST.ADD gList, c2                % add another circle to the group’s list
GR.MOVE g, 0, 50
GR.RENDER : PAUSE 1000            % two circles move

LIST.ADD gList, c3                % add another circle to the group’s list
GR.MOVE g, 0, 50                  % three circles move
GR.RENDER : PAUSE 1000

GR.CLOSE : END

Other Group statements[edit]

GR.GROUP.LIST <group_obj_nvar>{, <list_nexp>}
Rather than create a BASIC List to hold a sequence of parameters, as GR.GROUP does, GR.GROUP.LIST uses an existing List (see Data structures). GR.GROUP.LIST performs no validation of the contents of the List. If <list_nexp> is not the number of an existing List, or if the parameter is omitted, then the resulting group contains no elements.
GR.GROUP.GETDL <group_obj_nvar>
The GR.GROUP.GETDL statement includes every object currently in the Display List in a new group.
GR.GROUP.NEWDL <group_obj_nvar>
The GR.GROUP.NEWDL statement discards the current Display List and installs the specified group as the Display List.

By using GR.GROUP.GETDL and GR.GROUP.NEWDL, the BASIC program could maintain multiple groups and substitute one or the other, from time to time, as the Display List with which to render the graphics screen.

Modifiable attribute

list

Rotation[edit]

The GR.ROTATE.START and GR.ROTATE.END statements add objects to the Display List. These objects do not draw anything by themselves. Instead, they provide that any objects between them in the Display List are rotated before being drawn.

The program must call both GR.ROTATE.START and GR.ROTATE.END to put both objects in the Display List, bracketing the graphic elements that are to be rotated. Any list of renderable objects to be rotated, such as a list passed to GR.NEWDL to create an alternate Display List, must include both objects.

GR.ROTATE.START[edit]

Start a rotation region

Synopsis
GR.ROTATE.START <angle_nexp>, <x_nexp>, <y_nexp>{, <obj_nvar>}
Description

The GR.ROTATE.START statement adds an object to the Display List that provides that subsequent objects are rotated before being drawn. The rotation is by <angle_nexp> degrees in the clockwise direction around the rotation origin, the point on the graphics screen given by (<x_nexp>, <y_nexp>) pixels.

Usage

If dragging a rotated object using GR.MODIFY, the program will typically also need to modify the rotation origin in the GR.ROTATE.START statement.

Modifiable attributes

angle, x, y

GR.ROTATE.END[edit]

End a rotation region

Synopsis
GR.ROTATE.END {<obj_nvar>}
Description

The GR.ROTATE.END statement adds an object to the Display List that ends a rotation region begun by GR.ROTATE.START. Subsequent objects are not rotated.

Modifiable attributes

None; a program modifies the attributes of a rotation region by applying GR.MODIFY to the object that GR.ROTATE.START creates.

Example
The circular, rotating "compass" background is drawn by the user-defined routine below.

The following user-defined routine shows how the compass point in the nearby illustration can be rotated in any desired direction. The compass point itself is drawn as three nearly parallel lines. The routine draws it pointing North but the two GR.ROTATE statements rotate it appropriately. You might instead define a polygon and pass its index to the routine, which would draw it with GR.POLY.

FN.DEF Compass(xcenter, ycenter, radius, bearing)
 GR.SCREEN w, h       % Get screen dimensions
 GR.SET.STROKE 3      % Temporarily draw with broad lines
                      % Caller specifies the hue;
 GR.COLOR 64,,,,0     % circle (outline, not disc) is drawn semi-transparent
 GR.CIRCLE ignore, xcenter,ycenter, radius
 GR.COLOR 64          % Set transparency for rotating compass point
!The following three GR.LINE elements are rotated by "bearing" degrees
 GR.ROTATE.START bearing, xcenter, ycenter, CompassObject
 radius *= 0.9
 GR.LINE ignore, xcenter,ycenter,          xcenter,ycenter-radius
 GR.LINE ignore, xcenter+(0.02*h),ycenter, xcenter,ycenter-radius
 GR.LINE ignore, xcenter-(0.02*h),ycenter, xcenter,ycenter-radius
 GR.ROTATE.END
 GR.SET.STROKE 0      % Return to the default stroke width
 FN.RTN CompassObject
 FN.END

If the main program regenerated and re-rendered the screen, it would call this routine each time. If the main program used GR.MODIFY on the display elements and then re-rendered, it would not have to call the routine again, but could rotate the compass point with:

GR.MODIFY CompassObject, "angle", NewBearing

Clip regions[edit]

Clipping means that the effect of an RFO-BASIC! drawing statement applies only to a rectangular sub-region of the graphics screen called a clip region. Clip regions can be combined to produce a clip region that is more complex than a single rectangle.

The manner in which clip regions are implemented and combined is specified using a "Region Operator."

GR.CLIP[edit]

The first example with RO=0 (Intersect)
The first example with RO=1 (Difference)
The first example with RO=2 (Replace) (deprecated)
The first example with RO=3 (Reverse Difference) (deprecated)
The first example with RO=4 (Union) (deprecated)
The first example with RO=5 (XOR) (deprecated)

Start a clip region

Synopsis
GR.CLIP <ptr_nexp>, <left_nexp>, <top_nexp>, <right_nexp>, <bottom_nexp>{, <RO_nexp>}
Description

The GR.CLIP statement does not draw anything, but places an object in the Display List that affects how subsequent drawing statements function. The statement defines a rectangle on the graphics screen by specifying one corner at pixel address (left, top) and the opposite corner at (right, bottom). Usually, any drawing statement that follows GR.CLIP affects only the pixels in this rectangle.

The optional <RO_nexp> parameter is the Region Operator. It affects the meaning of the rectangle. If GR.CLIP is used more than once, <RO_nexp> specifies how the new rectangle interacts with the clip region previously established.

<RO_nexp> value Effect for first GR.CLIP Effect for subsequent GR.CLIP
0 or omitted Clips drawing to the rectangle Intersect — The clip region is the space within both the rectangle and the previous clip region.
1 Clips drawing to everything outside the rectangle Difference — The clip region is the previous clip region, excluding the new rectangle.
2 (deprecated) same as 0 Replace — The rectangle is the clip region; previous clip regions are overridden.
3 (deprecated) inhibits all drawing Reverse difference — The clip region is the rectangle excluding the previous clip region.
4 (deprecated) no effect Union — The clip region is the space within either the rectangle or the previous clip region.
5 (deprecated) same as 1 XOR — The clip region is the space within either the rectangle or the previous clip region, but not both.
Modifiable attributes

bottom, left, right, RO, top.

Examples

The following program defines two clip regions (two intersecting rectangles) and a Region Operator of Intersect. It displays an image, subject to both clip regions. Then it uses GR.MODIFY to modify the Region Operator of the second GR.CLIP (which was initialized to 0 by the number in red) to each of the five other valid values, re-rendering the display for three seconds each:

GR.OPEN
GR.BITMAP.LOAD Bmp1,"Fox.jpg"       % Or use a photo in your rfo-basic/data directory
GR.CLIP obj1, 20,20,   300,150, 0   % Define the first clip region
GR.CLIP obj2, 100,100, 450,250, 0   % Define the second clip region
GR.BITMAP.DRAW obj3,Bmp1,0,0        % Draw clipped photo from upper left corner
GR.RENDER
PAUSE 3000                          % Leave it up for 3 seconds

FOR I=1 TO 5
 GR.MODIFY obj2, "RO", I     % Modify 2nd GR.CLIP to have another Region Operator
 GR.RENDER                   % Re-render it that way
 PAUSE 3000                  % Leave it up for 3 seconds
NEXT I
END

The results of this program are the illustrations on the right side of this section. It is not just that the photograph trimmed; anything the program draws is clipped in the same manner.

To cancel a clip region for subsequent drawing statements, a program would specify a new clip region, comprising the entire screen, with a Region Operator of Replace:

GR.SCREEN wid, ht
GR.CLIP 0,0, wid-1,ht-1, 2

Clipping in private variants[edit]

To get clipping to work on devices with Android API 28 and later, a private variant of BASIC must be used. These do not implement the unsupported Region Operators or maintain a complete stack of clip regions. However, clipping can be started and stopped with user-defined functions like these:

!DETERMINE WHICH BASIC VARIANT WE ARE RUNNING UNDER
FN.DEF BasicVariant$()
 PROGRAM.INFO b
 BUNDLE.CONTAIN b,"PkgName",hb
 BUNDLE.CONTAIN b,"_PackageName",oli
 BUNDLE.CLEAR b
 IF hb  > 0.0 THEN FN.RTN "H"  % hBasic returns a package name
 IF oli > 0.0 THEN FN.RTN "O"  % OliBasic calls it something else
 FN.RTN "R"                    % RFO-BASIC! does not
FN.END
!START A RUN OF CLIPPED DRAW COMMANDS
FN.DEF ClipStart(left, top, right, bottom)
 bv$ = BasicVariant$()
 IF     bv$ = "R" THEN  % RFO-BASIC!
  GR.CLIP       object, left, top, right, bottom, 0
 ELSEIF bv$ = "H" THEN  % HBasic
  GR.CLIP.START object, left, top, right, bottom, 0
 ELSEIF bv$ = "O" THEN  % OliBasic
!TBD
 ENDIF
 FN.RTN object
FN.END
!END A RUN OF CLIPPED DRAW COMMANDS
FN.DEF ClipEnd()
 bv$ = BasicVariant$()
 IF     bv$ = "R" THEN  % RFO-BASIC! bef. API 28
  GR.SCREEN wid, ht
  GR.CLIP     object, 0,0, wid-1,ht-1, 2
 ELSEIF bv$ = "H" THEN  % HBasic has a new statement
  GR.CLIP.END object
 ELSEIF bv$ = "O" THEN  % OliBasic
!TBD
 ENDIF
 FN.RTN object
FN.END

Sources[edit]

Examples[edit]