# Graphics

## Contents

- Introduction
- Simple graphics

- Drawing lines
- Clearing the screen
- Using colour
- Overwriting pixels
- Graphical text

- Windows

- Window IDs and the default window
- Graphics keywords and windows
- Creating new windows
- Closing windows
- When windows overlap
- Hiding windows
- The graphics cursor in windows
- Information about your windows
- Other window keywords

- Advanced graphics

- Bitmaps
- Masks
- Speed improvements
- Displaying a running clock
- User-defined fonts and cursors
- The text and graphics windows

## Introduction

OPL graphics allow you, for example, to:

- Draw lines and boxes.
- Fill areas with patterns.
- Display text in a variety of styles, at any position on the screen.
- Scroll areas of the screen.
- Manipulate windows and bit patterns.
- Read data back from the screen.
- Draw circles and ellipses.
- Set the pen width.

Graphics keywords begin with a `G`. In this manual a lower case `g` is used — for example, `gCOLOR` or `gBOX` — but you can type them using upper or lower case letters.

Some graphics keywords are mentioned only briefly in this topic. For more details about them, see Keyword Reference.

## Simple graphics

Section Contents

- Drawing lines

- Drawing dots
- Right and down, left and up
- Going off the screen

- Clearing the screen
- Using colour

- Colour modes
- Colour commands
- Colour literals and using single-integer colours

- Overwriting pixels

- Drawing rectangles
- Overwriting with any drawing command
- Other drawing keywords

- Graphical text

- Displaying text with gPRINT
- Fonts
- Text style
- Overwriting with gPRINT
- Other graphical text keywords

The dimensions of the screens, expressed as points across and down the screen, are given by the `gHEIGHT` and `gWIDTH` keywords. These points are sometimes referred to as pixels.

Each pixel is identified by two numbers, giving its position across and down from the top left corner of the screen. 0,0 denotes the pixel in the top left corner; 2,1 is the pixel 2 points across and one down, and so on.

Note that these co-ordinates are very different to the cursor positions set by the AT command.

OPL maintains a current position on the screen. Graphics commands which draw on the screen generally take effect at this position. Initially, the current position is the top left corner, 0,0.

### Drawing lines

Here is a simple procedure to draw a horizontal line in the middle of the screen:

        PROC lines:
              gMOVE 180,80
              gLINEBY 120,0
              GET
        ENDP

`gMOVE` moves the current position by the specified amount. In this case, it moves the current position 180 pixels right and 80 down, from 0,0 to 180,80. It does not display anything on the screen.

`gLINEBY` (g-line-by) draws a line from the current position (just set to 180,80) to a point at the distance you specify — in this case 120 to the right and 0 down, i.e. 300,80.

The end points of lines are not drawn: for `gLINEBY dx%,dy%`, point `gX+dx%,gY+dy%` is not drawn. Note, however, that OPL specially plots the point when the start and end-point coincide.

`gLINEBY` also has the effect of moving the current position to the end of the line it draws.

With both `gMOVE` and `gLINEBY`, you specify positions relative to the current position. Most OPL graphics commands do likewise. `gMOVE` and `gLINEBY`, however, do have corresponding commands which use absolute pixel positions. `gAT` moves to the pixel position you specify; `gLINETO` draws a line from the current position to an absolute position. The horizontal line procedure could instead be written:

        PROC lines:
              gAT 180,80
              gLINETO 300,80
              GET
        ENDP

`gAT` and `gLINETO` may be useful in very short graphics programs, and `gAT` is always the obvious command for moving to a particular point on the screen, before you start drawing. But once you do start drawing, use `gMOVE` and `gLINEBY`. They make it much easier to develop and change programs, and allow you to make useful graphics procedures which can display things anywhere you set the current position. Almost all graphics drawing commands use relative positioning for these reasons.

#### Drawing dots

You can set the pixel at the current position with `gLINEBY 0,0`.

#### Right and down, left and up

`gMOVE` and `gLINEBY` find the position to use by adding the numbers you specify on to the current position. If the numbers are positive, it moves to the right and down the screen. If you use negative numbers, however, you can specify positions to the left of and/or above the current position. For example, this procedure draws the same horizontal line as before, then another one above it:

        PROC lines2:
              gMOVE 180,80
              gLINEBY 120,0
              gMOVE 0,-20
              gLINEBY -120,0
              GET
        ENDP

The first two program lines are the same as before. `gLINEBY` moves the current position to the end of the line it draws, so after the first `gLINEBY` the current position is 300,80. The second `gMOVE` moves the current position up by 20 pixels; the second `gLINEBY` draws a line to a point 120 pixels to the left. The end pixel is never set.

#### Going off the screen

No error is reported if you try to draw off the edge of the screen. It is quite possible to leave the current position off the screen — for example, `gLINETO 650,80` will draw a line from the current position to some point on the right-hand screen edge, but the current position will finish as 650,80.

There’s no harm in the current position being off the screen. It allows you to write procedures to display a certain pattern at the current position, and not have to worry whether that position is too close to the screen edge for the whole pattern to fit on.

### Clearing the screen

`gCLS` clears the screen.

### Using colour

Different EPOC devices have different display hardware. The number of different colours a device can display can be found using `gCOLORINFO` (see below). The `gCOLOR` and `gCOLORINFO` commands can be used to set any colour without error, but colours may be displayed differently — for example, on a greyscale machine, all colours are displayed in grey. On a 16-colour machine, many colours will be displayed as a dithering of supported colours.

It is possible to create windows with a greater colour depth then the display hardware allows — colours are dithered as described above. This is a processor-intensive operation.

#### Colour modes

OPL supports various colour modes:

- 2-grey mode; stored as 1 bpp. Monochrome.
- 4-grey mode; stored as 2 bpp.
- 16-grey mode; stored as 4 bpp.
- 256-grey mode; stored as 8 bpp.
- 16-colour mode; stored as 4 bpp (fixed palette; low-colour EGA).
- 256-colour mode; stored as 8 bpp (fixed palette).
- 65,536-colour mode; stored as 16 bpp (16-bit true colour).
- 16,777,216-colour mode; stored as 24 bpp (24-bit true colour).

By default the screen is in one of these modes — `gCOLORINFO` can be used to discover which. To enable drawing in non-standard colour modes, you need to use `DEFAULTWIN` mode at the start of your program. (Note that this also clears the screen.)

In general, deeper colour modes use more power. Also, note that setting the colour depth of any window using `DEFAULTWIN` or `gCREATE` any deeper than that of the screen consumes processor time — dithering is an expensive operation.

It is difficult to be more precise since the power consumption is also dependent on what is being displayed.

#### Colour commands

- `gCOLOR` — sets the foreground (pen) colour.
- `gCOLORBACKGROUND` — sets the background (paper) colour.
- `gCOLORINFO` — query EPOC for the maximum screen depth the device is capable of.

To set the colour used for graphics, use `gCOLOR red%,green%,blue%`. The `red%`, `green%` and `blue%` values specify a colour, which will be mapped to white, black or a dithered colour or grey. If the values of `red%`, `green%` and `blue%` are equal, then a pure grey results.

Note that if you use `gCOLOR`, colours with shades between the colours available will appear dithered, that is areas of the colour will have some pixels set to one colour and some to another so as to give the appearance of a colour between the two colours used. If `gCOLOR` is used in 2-colour mode, light greys will be mapped to white and dark greys to black.

In general, you should use as few colours as you can in your graphical displays, as some EPOC devices will consume less power.

`DEFAULTWIN` does not affect `PRINT` statements — it applies only to graphics and graphics text (see `gPRINT` later).

Constants for the modes of `DEFAULTWIN` and `gCREATE` (see below) are supplied in Const.oph.

#### Colour literals and using single-integer colours

OPL supports colours using the three-value RGB system which uses three numeric values to uniquely define a colour. For example, the colour 'dark red' consists of RGB values $80, $00, $00. However it is possible to combine these values into one long integer number: &800000. To improve ease-of-use of some colours, colour constants have been added to Const.oph:

              CONST KRgbBlack&=&000000
              CONST KRgbDarkGray&=&555555
              CONST KRgbDarkRed&=&800000
              CONST KRgbDarkGreen&=&008000
              CONST KRgbDarkYellow&=&808000
              CONST KRgbDarkBlue&=&000080
              CONST KRgbDarkMagenta&=&800080
              CONST KRgbDarkCyan&=&008080
              CONST KRgbRed&=&ff0000
              CONST KRgbGreen&=&00ff00
              CONST KRgbYellow&=&ffff00
              CONST KRgbBlue&=&0000ff
              CONST KRgbMagenta&=&ff00ff
              CONST KRgbCyan&=&00ffff
              CONST KRgbGray&=&aaaaaa
              CONST KRgbDitheredLightGray&=&cccccc
              CONST KRgb1in4DitheredGray&=&ededed
              CONST KRgbWhite&=&ffffff

These colour constants can be used to define and pass colours to procedures in a compact manner. Using a set of colour masks and colour position constants, the RGB values can be easily extracted. For example, using the following constants declared in Const.oph,

              REM RGB color masking:
              CONST KRgbRedPosition&=&10000
              CONST KRgbGreenPosition&=$100
              CONST KRgbBluePosition&=$1
              CONST KRgbColorMask&=$ff

the following type of procedure can be coded:

              PROC SetColor:(rgb&)
                LOCAL red%,green%,blue%
                red%=rgb&/KRgbRedPosition& AND KRgbColorMask&
                green%=rgb&/KRgbGreenPosition& AND KRgbColorMask&
                blue%=rgb& AND KRgbColorMask&
                gCOLOR red%,green%,blue%
              ENDP

which allows the three colour values to be stored and passed in a single long integer. For example: `SetColor:(KRgbMagenta&)`.

### Overwriting pixels

#### Drawing rectangles

The `gBOX` command draws a box outline. For example, `gBOX 100,20` draws a box from the current position to a point 100 pixels to the right and 20 down. If the current position were 200,40, the four corners of this box would be at 200,40, 300,40, 300,60 and 200,60.

If you have used `gCOLOR` as described earlier, the box is drawn in the colour selected.

`gBOX` does not change the current position.

`gFILL` draws a filled box in the same way as `gBOX` draws a box outline, but it has a third argument to say which pixels to set. If set to 0, the pixels which make up the box would be set. If set to 1, pixels are cleared; if set to 2, they are inverted, that is, pixels already set on the screen become cleared, and vice versa. The values 1 and 2 are used when overwriting areas of the screen which already have pixels set.

This procedure displays a "robot" face, using `gFILL` to draw set and cleared boxes:

        PROC face:
              gFILL 120,120,0                 REM set the entire face
              gMOVE 10,20 :gFILL 30,20,1      REM left eye
              gMOVE 70,0 :gFILL 30,20,1       REM right eye
              gMOVE -30,30 :gFILL 20,30,1     REM nose
              gMOVE -20,40 :gFILL 60,20,1     REM mouth
              GET
        ENDP

Before calling such a procedure, you would set the current position to be where you wanted the top left corner of the head.

You could make the robot wink with the following procedure, which inverts part of one eye:

        PROC wink:
              gMOVE 10,20                     REM move to left eye
              gFILL 30,14,2                   REM invert most of the eye
              PAUSE 10
              gFILL 30,14,2                   REM invert it back again
              GET
        ENDP

Again, you would set the current position before calling this.

The `gPATT` command can be used to draw a shaded filled rectangle. To do this, use `-1` as its first argument, then the same three arguments as for `gFILL` - width, height, and overwrite method. Overwrite methods 0, 1 and 2 apply only to the pixels which are ‘on’ in the shading pattern. Whatever was on the screen may still show through, as those pixels which are ‘clear’ in the shading pattern are left as they were.

To completely overwrite what was on the screen with the shaded pattern, `gPATT` has an extra overwrite method of 3. So, for example, `gPATT -1,120,120,3` in the first procedure would have displayed a shaded robot head, whatever may have been on the screen.

#### Overwriting with any drawing command

By using the `gGMODE` command, any drawing command such as `gLINEBY` or `gBOX` can be made to clear or invert pixels, instead of setting them. `gGMODE` determines the effect of all subsequent drawing commands.

The values are the same as for `gFILL`: `gGMODE 1` for clearing pixels, `gGMODE 2` for inverting pixels, and `gGMODE 0` for setting pixels again. (0 is the initial setting.)

For example, some white lines can give the robot a furrowed brow:

        PROC brow:
              gGMODE 1                REM gLINEBY will now clear pixels
              gMOVE 10,8 :gLINEBY 100,0
              gMOVE 0,4 :gLINEBY -100,0
              gGMODE 0
              GET
        ENDP

Constants for the modes are supplied in Const.oph.

#### Other drawing keywords

For more details of these keywords, see Keyword Reference.

- `gBUTTON`: draw a 3-D button (a picture of a key, not of an application button) enclosing supplied text. The button can be raised, depressed or sunken. The button may also enclose a bitmap with a mask (see the ‘Bitmaps’ section below).
- `gBORDER`, `gXBORDER`: draw 2-D/3-D borders.
- `gINVERT`: invert a rectangular area, except for its four corner pixels.
- `gCOPY`: copy a rectangular area from one position on the screen to another.
- `gSCROLL`: move a rectangular area from one position on the screen to another, or scroll the contents of the screen in any direction.
- `gPOLY`: draw a sequence of lines.
- `gCIRCLE`, `gELLIPSE`: draw a circle or ellipse which can be filled or empty.
- `gSETPENWIDTH`: draw with a different pen width.

### Graphical text

#### Displaying text with gPRINT

The `PRINT` command displays text in one font, in a screen area controlled by the `FONT` or `SCREEN` commands. You can, however, display text in a variety of fonts and styles, at any pixel position, with `gPRINT`.

`gPRINT` also allows you to draw text in colour if you have used the `gCOLOR` or `gCOLORBACKGROUND` commands previously. You can control the font and style used by OPL’s other text-drawing keywords, such as `PRINT` and `EDIT`. See The text and graphics windows (below).

`gPRINT` is a graphical version of `PRINT`, and displays a list of expressions in a similar way. Some examples:

        gPRINT "Hello",name$
        gPRINT a$
        gPRINT "Sin(PI/3) is",sin(pi/3)

Unlike `PRINT`, `gPRINT` does not end by moving to a new line. A comma between expressions is still displayed as a space, but a semicolon has no effect. `gPRINT` used on its own does nothing.

The first character displayed has its left side and baseline at the current position. The baseline is like a line on lined note paper graphically, this is the horizontal line which includes the lowest pixels of upper case characters. Some characters, such as ‘g’, ‘j’, ‘p’, ‘q’ and ‘y’, set pixels below the baseline.

After using `gPRINT`, the current position is at the end of the text so that you can print something else immediately beyond it. As with other graphics keywords, no error is reported if you try to display text off the edge of the screen.

While `CURSOR ON` displays a flashing cursor for ordinary text displayed with PRINT, `CURSOR 1` switches on a cursor for graphical text which is displayed at the current position. `CURSOR OFF` removes either cursor.

#### Fonts

The `gFONT` command sets the font to be used by subsequent `gPRINT` commands.

A large set of fonts which can be used with `gFONT` is provided in the ROM.

The special font number `&9a` is set aside to give a machine’s default graphics font; this is the font used initially for graphics text. The default font is Arial 15, whose UID is `KFontArialNormal15&`. So `gFONT` KFontArialNormal15& or `gFONT &9a` both set the standard font, which `gPRINT` normally uses.

Fonts are identified by a 32-bit UID. You can directly specify the fonts by UID by using the definitions listed in the header file Const.oph. The Squashed and Tiny fonts are used for the toolbar in bold style.

For example, normal Arial proportional font with height 8 pixels has UID given by,

        CONST KFontArialNormal8&=268435954

so you could use

        INCLUDE "Const.oph"
        ...
        gFONT KFontArialNormal8&

See `gINFO32` and `FONT` in the Keyword Reference if you need to find out more information about fonts.

This program shows you examples of the fonts. (`!!!` is displayed to emphasise the monospaced fonts) It displays a variety of fonts, using their UIDs.

        INCLUDE "Const.oph"

        PROC fonts:
              showfontbyuid:(KFontCourierNormal8&,15,"Courier 8")
              showfontbyuid:(KFontTimesNormal8&,25,"Times 8")
              showfontbyuid:(KFontTimesNormal11&,38,"Times 11")
              showfontbyuid:(KFontTimesNormal13&,53,"Times 13")
              showfontbyuid:(KFontTimesNormal15&,71,"Times 15")
              showfontbyuid:(KFontArialNormal8&,81,"Arial 8")
              showfontbyuid:(KFontArialNormal11&,94,"Arial 11")
              showfontbyuid:(KFontArialNormal13&,109,"Arial 13")
              showfontbyuid:(KFontArialNormal15&,127,"Arial 15")
              showfontbyuid:(KFontTiny4&,135,"Tiny 4")
        ENDP

        PROC showfontbyuid:(font&,y%,str$)
              gFONT font&
              gAT 20,y% :gPRINT font%
              gAT 50,y% :gPRINT str$
              gAT 150,y% :gPRINT "!!!"
        ENDP

#### Text style

The `gSTYLE` command sets the text style to be used by subsequent `gPRINT` commands.

Choose from these styles:

`gSTYLE 1` | bold
`gSTYLE 2` | underlined
`gSTYLE 4` | inverse
`gSTYLE 8` | double height
`gSTYLE 16` | mono
`gSTYLE 32` | italic

Constants for these values are supplied in Const.oph.

The ‘mono’ style is not proportionally spaced - each character is displayed with the same width, in the same way that `PRINT` displays characters (by default). A proportional font can be displayed as a mono-spaced font by setting the ‘mono’ style. See the previous section for the list of mono-spaced and proportional fonts.

N.B.: It is inefficient to use the ‘mono’ style to display a font which is already mono-spaced.

You can combine these styles by adding the relevant numbers together. `gSTYLE 12` sets the text style to inverse and double-height (4+8=12). Here’s an example of this style:

        PROC style:
              gAT 20,50 :gFONT 11
              gSTYLE 12 :gPRINT "Attention!"
              GET
        ENDP

Use `gSTYLE 0` to reset to normal style.

The bold style provides a way to make any font appear bold. Most system fonts look reasonably bold already. Note that using the bold style sometimes causes a change of font; if you use `gINFO` you may see the font name change.

Note that fonts which are always bold are available. Using a bold style with these fonts results in a double bold font. It is not necessary to use these fonts to produce bold font: you can of course use just the normal fonts in a bold style.

#### Overwriting with gPRINT

`gPRINT` normally displays text as if writing it with a pen - the pixels that make up each letter are set, and that is all. If you’re using areas of the screen which already have some pixels set, or even have all the pixels set, use `gTMODE` to change the way `gPRINT` displays the text.

`gTMODE` controls the display of text in the same way as `gGMODE` controls the display of lines and boxes. The values you use with `gTMODE` are similar to those for `gGMODE`: `gTMODE 1` for clearing pixels, `gTMODE 2` for inverting pixels, and `gTMODE 0` for setting pixels again. There is also `gTMODE 3` which sets the pixels of each character while clearing the character’s background. This is very useful as it guarantees that the text is readable.

Constants for these values are supplied in Const.oph.

This procedure shows the various effects possible via `gTMODE`:

        PROC tmode:
              DEFAULTWIN 2
              gFONT 11 :gSTYLE 0
              gAT 160,0 :gFILL 160,80,0              REM Black box
              gAT 220,0 :gFILL 40,80,1               REM White box
              gAT 180,20 :gTMODE 0 :gPRINT "ABCDEFGHIJK"
              gAT 180,35 :gTMODE 1 :gPRINT "ABCDEFGHIJK"
              gAT 180,50 :gTMODE 2 :gPRINT "ABCDEFGHIJK"
              gAT 180,65 :gTMODE 3 :gPRINT "ABCDEFGHIJK"
              gCOLOR $50,$50,$50
              gAT 160,80 :gFILL 160,80,0            REM Grey box
              gAT 220,80 :gFILL 40,80,1             REM White box
              gAT 180,100 :gTMODE 0 :gPRINT "ABCDEFGHIJK"
              gAT 180,115 :gTMODE 1 :gPRINT "ABCDEFGHIJK"
              gAT 180,130 :gTMODE 2 :gPRINT "ABCDEFGHIJK"
              gAT 180,145 :gTMODE 3 :gPRINT "ABCDEFGHIJK"
              GET
        ENDP

#### Other graphical text keywords

- `gPRINTB`: display text left aligned, right aligned or centred, in a cleared box. The `gTMODE` setting is ignored.
- `gXPRINT`: display text underlined/highlighted.
- `gPRINTCLIP`: display text clipped to whole characters.
- `gTWIDTH`: find width required by text.

All of these keywords take the current font and style into account, and work on a single string. They display text in the colour specified by `gCOLOR`.

## Windows

Section Contents

- Window IDs and the default window
- Graphics keywords and windows
- Creating new windows
- Closing windows
- When windows overlap
- Hiding windows
- The graphics cursor in windows
- Information about your windows
- Other window keywords

So far, you’ve used the whole of the screen for displaying graphics. You can, however, use windows - rectangular areas of the screen.

Sprites (described in About OPXs) can display non-rectangular shapes. OPL allows a program to use up to sixty-four windows at any one time.

### Window IDs and the default window

Each window has an ID number, allowing you to specify which window you want to work with at any time.

When a program first runs, it has one window called the default window. Its ID is 1, it is the full size of the screen, and initially all graphics commands operate on it. (This is why ‘0,0’ has so far referred to the top left of the screen: it is true for the default window.)

Other windows you create will have IDs from 2 to 64. When you make another window it becomes the current window, and all subsequent graphics commands operate on it.

In the discussion and examples above, we used only the default window. However, everything actually applies to the current window. For example, if you make a small window current and try to draw a very long line, the current position moves off past the window edge, and only that part of the line which fits in the window is displayed.

### Graphics keywords and windows

For OPL graphics keywords, positions apply to the window you are using at any given time. The point 0,0 means the top left corner of the current window, not the top left corner of the screen.

Each window can be created in any of the 3 colour modes by specifying the last argument in the `gCREATE` command (see below and Keyword Reference). `gCOLOR` can be used to specify the current pen colour and `gSETPENWIDTH` to specify the pen width. The default is 4-colour mode, as for the default window.

For the default window, the special command `DEFAULTWIN` is required to change colour modes because that window is automatically created for you in 4-colour mode; `DEFAULTWIN` clears the default window and resets colour mode to that specified. All other windows must be created in the colour mode in which they are required: it may not be changed once they are created.

Once a window has been created with a certain colour mode, colours specified by `gCOLOR` work in the exactly the same way as in the default window.

`gCOLOR`, `gCOLORBACKGROUND`, `gGMODE`, `gTMODE`, `gFONT` and `gSTYLE` can all be used with created windows in exactly the same way as with the default window, as described earlier. They change the settings for the current window only; all the settings are remembered for each window.

### Creating new windows

The `gCREATE` function sets up a new window on the screen. It returns an ID number for the window. Whenever you want to change to this window, use `gUSE` with this ID.

You can create a window with any valid colour mode by specifying the last optional parameter to `gCREATE`.

Here is an example using `gCREATE` and `gUSE`, contrasting the point 20,20 in the created window with 20,20 in the default window.

        PROC windows:
              LOCAL id%
              id%=gCREATE(60,40,320,30,1,2)    REM 16-grey mode
              gBORDER 0 :gAT 20,20 :gLINEBY 0,0
              gPRINT " 20,20 (new)"
              GET
              gUSE 1 :gAT 20,20 :gLINEBY 0,0
              gPRINT " 20,20 (default)"
              GET
              gUSE id%
              gCOLOR $88,$88,$88               REM mid grey
              gPRINT " Back"
              gCOLOR 0,0,0                     REM black
              gPRINT " (with 16 colours)"
              GET
        ENDP

The line `id%=gCREATE(60,40,320,30,1,2)` creates a window with its top left corner at 60,40 on the screen. The window is set to be 320 pixels wide and 30 pixels deep. (You can use any integer values for these arguments, even if it creates the window partially or even totally off the screen.) The fifth argument to `gCREATE` specifies whether the window should immediately be visible or not; 0 means invisible, 1 (as here) means visible. The sixth argument specifies the colour mode. Constants for different colour modes are supplied in Const.oph.

If the sixth argument is not supplied at all (e.g. `id%=gCREATE(60,40,320,30,1)`) the window will have the default colour mode.

`gCREATE` automatically makes the created window the current window, and sets the current position in it to 0,0. It returns an ID number for this window, which in this example is saved in the variable `id%`.

The `gBORDER 0` command draws a border one pixel wide around the current window. Here this helps show the position and size of the window. (`gBORDER` can draw a variety of borders. You can display the 3-D style borders seen in menus and dialogs, with the `gXBORDER` keyword.)

The program then sets the pixel at 20,20 in this new window, using `gLINEBY 0,0`.

`gUSE 1` goes back to using the default window. The program then shows 20,20 in this window.

Finally, `gUSE id%` goes back to the created window again, and a final message is displayed, in grey and black.

Note that each window has its own current position. The current position in the created window is remembered while the program goes back to the default window. All the other settings, such as the font, style and grey setting are also remembered.

### Closing windows

When you’ve finished with a particular window, close it with `gCLOSE` followed by its ID - for example, `gCLOSE 2`. You can create and close as many windows as you like, as long as there are only 64 or fewer open at any one time.

If you close the current window, the default window (ID=1) becomes current.

An error is raised if you try to close the default window.

### When windows overlap

Windows can overlap on the screen, or even hide each other entirely. Use the `gORDER` command to control the foreground/background positions of overlapping windows.

`gORDER 3,1` sets the window whose ID is 3 to be in the foreground. This guarantees that it will be wholly visible. `gORDER 3,2` makes it second in the list; unless the foreground window overlaps it, it too will be visible.

Any position greater than the number of windows you have is interpreted as the end of the list. `gORDER 3,9` will therefore always force the window whose ID is 3 to the background, behind all others.

Note in particular that making a window the current window with `gUSE` does not bring it to the foreground. You can make a background window current and draw all kinds of things to it, but nothing will happen on the screen until you bring it to the foreground with `gORDER`.

When a window is first created with `gCREATE` it always becomes the foreground window as well as the current window.

### Hiding windows

If you are going to use several drawing commands on a particular window, you may like to make it invisible while doing so. When you then make it visible again, having completed the drawing commands, the whole pattern appears on the screen in one go, instead of being built up piece by piece.

Use `gVISIBLE ON` and `gVISIBLE OFF` to perform this function on the current window. You can also make new windows invisible as you create them, by using 0 as the fifth argument to the `gCREATE` command, and you can hide windows behind other windows.

### The graphics cursor in windows

To make the graphics cursor appear in a particular window, use the `CURSOR` command with the ID of the window. It will appear flashing at the current position in that window, provided it is not obscured by some other window.

The window you specify does not have to be the current window, and does not become current; you can have the cursor in one window while displaying graphical text in another. If you want to move to a different window and put the graphics cursor in it, you must use both `gUSE` and `CURSOR`.

Since the default window always has an ID of 1, `CURSOR 1` will, as mentioned earlier, put the graphics cursor in it.

`CURSOR OFF` turns off the cursor, wherever it is.

### Information about your windows

You don’t have to keep a complete copy of all the information pertaining to each window you use. These functions return information about the current window:

- `gIDENTITY` returns its ID number.
- `gRANK` returns its foreground/background position, from 1 to 8.
- `gWIDTH` and `gHEIGHT` return its size.
- `gORIGINX` and `gORIGINY` return its screen position.
- `gX` and `gY` return the current position.
- `gINFO32` returns information about the font, style, colour settings, overwrite modes and cursor in use.

### Other window keywords

- `gSETWIN` changes the position, and optionally the size, of the current window.

N.B.: You can use this command on the default window, if you wish, but you must also use the `SCREEN` command to ensure that the text window — the area for PRINT commands to use — is wholly contained within the default window. See The text and graphics windows (below).

- `gSCROLL` scrolls all or part of both black and grey planes of the current window.
- `gPATT` fills an area in the current window with repetitions of another window, or with a shaded pattern.
- `gCOPY` copies an area from another window into the current window, or from one position in the current window to another.

It is inadvisable to use `gCOPY` to copy from windows as it is very slow. It should only be used for copying from bitmaps to windows or other bitmaps.

- `gSAVEBIT` saves part or all of a window as a bitmap file.
- `gPEEKLINE` reads back a horizontal line of data in the specified mode (see Keyword Reference) from a specified window.

## Advanced graphics

Section Contents

- Bitmaps
- Masks
- Speed improvements
- Displaying a running clock
- User-defined fonts and cursors
- The text and graphics windows

This section should provide a taste of some of the more exotic things you can do with OPL graphics.

### Bitmaps

A bitmap is an area in memory which acts just like an off-screen window. You can create bitmaps with `gCREATEBIT`.

It is possible to create bitmaps in any valid colour mode by specifying the optional third argument when using `gCREATEBIT`. The default is 2-bpp mode (black and white). Note, however, that black and white bitmaps differ from black and white windows. In particular, if you draw in 16 colours to a black and white bitmap then greys appear as dithered black and white, whereas if you draw exactly the same to a 2-colour graphics window you just get dark greys mapped to black and light greys mapped to white. This enables grey printing on black and white printers.

A further benefit is that the file size will be smaller if the bitmap is saved, with just 1 bpp used for black and white bitmaps.

Bitmaps have the following uses:

- You can manipulate an image in a bitmap before copying it with `gPATT` or `gCOPY` to a window on the screen. This is generally faster than manipulating an image in a hidden window.
- You can load bitmap files into bitmaps in memory using `gLOADBIT`, then copy them to on-screen windows using `gCOPY` or `gPATT`.

OPL treats a bitmap as the equivalent of a window in most cases:

- Both are identified by ID numbers. Only one window or bitmap is current at any one time, set by gUSE.
- If you use bitmaps as well as windows, the total number must be 64 or fewer.
- The top left corner of the current bitmap is still referred to as 0,0, even though it is not on the screen at all.

Together, windows and bitmaps are known as drawables — places you can draw to.

Most graphics keywords can be used with bitmaps in the same way as with windows, but remember that a bitmap corresponds to only one plane in a window. Once you have drawn to it, you might copy it to the appropriate plane of a window.

The keywords that can be used with bitmaps include: `gLINEBY`, `gLINETO`, `gBOX`, `gFILL`, `gCIRCLE`, `gELLIPSE` , `gCOLOR`, `gSETPENWIDTH`, `gUSE`, `gBORDER`, `gCLOSE`, `gCLS`, `gCOPY`, `gGMODE`, `gFONT`, `gIDENTITY`, `gPATT`, `gPEEKLINE`, `gSAVEBIT`, `gSCROLL`, `gTMODE`, `gWIDTH`, `gHEIGHT`, and `gINFO32`. These keywords are described earlier in this topic.

### Masks

There are several keywords that require an understanding of masks. In some cases the mask is a bitmap file, e.g. `gBUTTON` (see earlier in this topic and also Keyword Reference) and for `ICON` (see Advanced Topics), and in some cases it is an integer containing a bit-mask, e.g. for `POINTERFILTER`.

In all these cases, however, the principle is the same. The pixels or bits which are set in the mask specify pixels or bits in some other argument which are to be used. Pixels and bits which are clear in the mask specify pixels and bits that are not to be used from the other argument.

For example, when using `gBUTTON` with identical bitmap and mask, cleared pixels on the bitmap are drawn in the background colour of the button (i.e. they are clear) while set pixels are drawn on the button as they appear on the bitmap. This is generally how buttons on toolbars appear.

### Speed improvements

The screen is usually updated whenever you display anything on it. `gUPDATE OFF` switches off this feature. The screen will be updated as few times as possible, although you can force an update by using the `gUPDATE` command on its own. (An update is also forced by `GET`, `KEY` and by all graphics keywords which return a value, other than `gX`, `gY`, `gWIDTH` and `gHEIGHT`).

This can result in a considerable speed improvement in some cases. You might, for example, use `gUPDATE OFF`, then a sequence of graphics commands, followed by `gUPDATE`. You should certainly use `gUPDATE OFF` if you are about to write exclusively to bitmaps.

`gUPDATE ON` returns to normal screen updating.

Also, remember that scrolling and moving windows require every pixel in a window to be redrawn.

The `gPOLY` command draws a sequence of lines, as if by `gLINEBY` and `gMOVE` commands. If you have to draw a lot of lines (or dots, with `gLINEBY 0,0`), `gPOLY` can greatly reduce the time taken to do so.

### Displaying a running clock

`gCLOCK` displays or removes a running clock showing the system time. The clock can be digital or conventional, and can use many different formats. See Keyword Reference for full details.

### User-defined fonts and cursors

If you have a user-defined font you can load it into memory with `gLOADFONT`.

`gLOADFONT` returns a file ID, which can be used only with `gUNLOADFONT`. The maximum number of font files which may be loaded at any one time is 16. To use the fonts in a loaded font you need to use the UIDs specified in the font file itself.

You can use four extra arguments with the `CURSOR` command. Three of these specify the ascent, width and height of the cursor. The ascent is the number of pixels (-128 to 127) by which the top of the cursor should be above the baseline of the current font. The height and width arguments should both be between 0 and 255. For example, `CURSOR 1,12,4,14` sets a cursor 4 pixels wide by 14 high in the default window (ID=1), with the cursor top at 12 pixels above the font baseline.

If you do not use these arguments, the cursor is 2 pixels wide, and has the same height and ascent as the current font.

By default the cursor has square corners, is black and is flashing. Supply the fifth argument as 2 for non-flashing or 4 for grey. You can add these together - e.g. use 6 for a grey, non-flashing cursor.

Note that the `gINFO32` command returns information about the cursor and font.

### The text and graphics windows

`PRINT` displays mono-spaced text in the text window. You can change the text window font (i.e. that used by `PRINT`) using the `FONT` keyword.

You can use any of those fonts listed in Const.oph. Initially Courier 11 is used.

It should be noted that when using the console keywords `PRINT`, `AT`, `SCREEN`, etc. the use of proportional fonts such as Arial and Times may produce some unexpected behaviour because it is assumed in all cases that mono-spaced font is being used. The reason for this is so that use of keywords such as `AT`, `SCREEN` in the console is independent of whether the font is proportional or monospaced. An example of this behaviour may be seen when using inverted text: the rectangle to invert is calculated assuming that the font is mono-spaced, and hence the area inverted is larger than the text printed when using a proportional font. Another example is that a new line will be used before it appears necessary when using a proportional font, since the number of characters which will fit on a line is also calculated assuming the font is mono-spaced.

The text window is in fact part of the default graphics window. If you have other graphics windows in front of the default window, they may therefore hide any text you display with `PRINT`.

Initially the text window is very slightly smaller than the default graphics window which is full-screen size. They are not the same because the text window height and width always fits a whole number of characters of the current text window font. If you use the `FONT` command to change the font of the text window, this first sets the default graphics window to the maximum size that will fit in the screen and then resizes the text window to be as large as possible inside it.

You can also use the `STYLE` keyword to set the style for all characters subsequently written to the text window. This allows the mixing of different styles in the text window.

Use the same values as listed for `gSTYLE` (above).

To find out exactly where the text window is positioned, use `SCREENINFO info%()`. This sets `info%(1)/info%(2)` to the number of pixels from the left/top of the default window to the left/top of the text window. (These are called the margins.) `info%(7)` and `info%(8)` are the text window’s character width and height respectively.

N.B.: The margins are fully determined by the font being used and therefore change from their initial value only when `FONT` is used. You cannot choose your own margins. `gSETWIN` and `SCREEN` do not change the margins, so you can use FONT to select a font (also clearing the screen), followed by `SCREENINFO` to find out the size of the margins with that font, and finally `gSETWIN` and `SCREEN` to change the sizes and positions of the default window and text window taking the margins into account (see example below). The margins will be the same after calling `gSETWIN` and `SCREEN` as they were after `FONT`.

It is not generally recommended to use both the text and graphics windows. Graphics commands provide much finer control over the screen display than is possible in the text window, so it is not easy to mix the two.

If you do need to use the text window, for example to use keywords like `EDIT`, it’s easy to use `SCREEN` to place it out of the way of your graphics windows. You can, however, use it on top of a graphics window - for example, you might want to use `EDIT` to simulate an edit box in the graphics window. Use `gSETWIN` to change the default window to about the size and position of the desired edit box. The text window moves with it - you must then make it the same size, or slightly smaller, with the `SCREEN` command. Use 1,1 as the last two arguments to `SCREEN`, to keep its top left corner fixed. `gORDER 1,1` will then bring the default window to the front, and with it the text window. EDIT can then be used.

Here is an example program which uses this technique - moving an ‘edit box’, hiding it while you edit, then finally letting you move it around.

        PROC gsetw1:
              LOCAL a$(100),w%,h%,g$(1),factor%,info%(10)
              LOCAL margx%,margy%,chrw%,chrh%,defw%,defh%
              SCREENINFO info%()           REM get text window info
              margx%=info%(1) :margy%=info%(2)
              chrw%=info%(7) :chrh%=info%(8)
              defw%=23*chrw%+2*margx%   REM new default window width
              defh%=chrh%+2*margy%      REM ... and height
              w%=gWIDTH :h%=gHEIGHT
              gSETWIN w%/4+margx%,h%/4+margy%,defw%,defh%
              SCREEN 23,1,1,1              REM text window
              PRINT "Text win:"; :GET
              gCREATE(w%*0.1,h%*0.1,w%*0.8,h%*0.8,1)     REM new window
              gPATT -1,gWIDTH,gHEIGHT,0                  REM shade it
              gAT 2,h%*0.7 :gTMODE 3
              gPRINT "Graphics window 2"
              gORDER 1,0                REM back to default+text window
              EDIT a$                   REM you can see this edit
              gORDER 1,65               REM to background
              CLS
              a$=""
              PRINT "Hidden:";
              GIPRINT "Edit in hidden edit box"
              EDIT a$                   REM YOU CAN’T SEE THIS EDIT
              GIPRINT ""
              gORDER 1,0 :GET           REM now here it is
              gUSE 1                    REM graphics go to default window
              DO                        REM move default/text window
                                        REM around
                  CLS
                  PRINT "U,D,L,R,Quit";
                  g$=UPPER$(GET$)
                  IF KMOD=2            REM Shift key moves quickly
                      factor%=10
                  ELSE
                      factor%=1
                  ENDIF
                  IF g$="U"
                      gSETWIN gORIGINX,gORIGINY-factor%
                  ELSEIF g$="D"
                      gSETWIN gORIGINX,gORIGINY+factor%
                  ELSEIF g$="L"
                      gSETWIN gORIGINX-factor%,gORIGINY
                  ELSEIF g$="R"
                      gSETWIN gORIGINX+factor%,gORIGINY
                  ENDIF
              UNTIL g$="Q" OR g$=CHR$(27)
        ENDP