Chapters 6 and 8 described what a verb draws in prose. Eight figures now show it, and each one is produced by running the BASIC listing printed immediately above it -- so a picture cannot drift away from the code beside it, which is the way a screenshot goes wrong and the way nothing notices. tools/screenshot.c is a second SDL host, much smaller than the frontend: dummy video driver, software renderer, run to completion, read the target back, write a PNG. It draws no text layer on purpose, so a READY in the corner is not noise in a figure about BOX and no font has to be resolved. tools/docs_screenshots.sh reads the new screenshot=NAME fence tag straight out of the markdown. size=WxH is the second tag, and SCALE's figure uses it: the point being made is a 320x200 listing filling a larger window, which cannot be made on a 320x200 surface. Two gates, answering different questions. docs_examples fails a tagged block with no image, in both configurations, so a figure cannot be added and forgotten. docs_screenshots -- a CTest, AKGL build only -- re-renders every figure and compares byte for byte, so a listing edited without regenerating fails. Only the second catches a stale picture. The PNGs are checked in because a reader on the forge has no build tree, and docs/images/README.md says loudly that they are generated. Regenerating is never part of a build: the target is run deliberately, so a make cannot put eight binary diffs in front of whoever ran it. Drawing the BOX figure caught a defect in TODO.md itself. Deviation 16 claimed in bold that BOX fills on a negative angle while its own paragraph said the fill was filed rather than implemented. BOX cannot fill, and filled_rect is reached by no verb as a result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
225 lines
6.9 KiB
Markdown
225 lines
6.9 KiB
Markdown
# 6. Graphics
|
|
|
|
Everything in this chapter needs the SDL build and a graphics device. Without one each
|
|
verb refuses by name:
|
|
|
|
```basic requires=noakgl
|
|
10 DRAW 1, 0, 0 TO 100, 100
|
|
```
|
|
|
|
```output
|
|
? 10 : RUNTIME ERROR DRAW needs a graphics device and this runtime has none
|
|
|
|
```
|
|
|
|
## The coordinate space
|
|
|
|
**A drawing coordinate is a pixel of the host's window**, with (0, 0) at the top left.
|
|
On the standalone interpreter's 800 by 600 window, `DRAW 1, 799, 599` lands on the
|
|
bottom-right pixel and everything in between is reachable. A game embedding the
|
|
interpreter gets whatever size its own renderer is.
|
|
|
|
`RGR` is how a program finds out:
|
|
|
|
```basic requires=akgl
|
|
10 PRINT "THE SCREEN IS"
|
|
20 PRINT RGR(1)
|
|
30 PRINT "BY"
|
|
40 PRINT RGR(2)
|
|
50 DRAW 1, 0, 0 TO RGR(1) - 1, RGR(2) - 1
|
|
```
|
|
|
|
Subtracting one is not a wart, it is the last pixel: a window `RGR(1)` wide has
|
|
columns 0 through `RGR(1) - 1`.
|
|
|
|
**A C128 listing assumes 320 by 200 and will draw in the top-left corner.** Give it
|
|
the whole window by naming the space it was written for:
|
|
|
|
```basic requires=akgl
|
|
10 SCALE 1, 319, 199
|
|
20 BOX 1, 0, 0, 319, 199
|
|
```
|
|
|
|
That box is now the border of the window whatever size the window is. When no device
|
|
answers the size question at all, 320 by 200 is what the interpreter assumes — the
|
|
space a C128 listing was written for is the right thing to fall back to.
|
|
|
|
## Colour
|
|
|
|
`COLOR` binds a *source* to a palette index, and the drawing verbs name the source
|
|
rather than the colour:
|
|
|
|
```basic requires=akgl
|
|
10 COLOR 1, 3
|
|
20 DRAW 1, 10, 20
|
|
```
|
|
|
|
Sources are numbered 0 to 6; palette indices are 1 to 16, as on a C128. That
|
|
indirection is BASIC 7.0's, and it is why every drawing verb's first argument is a
|
|
small number that is not a colour.
|
|
|
|
## The verbs
|
|
|
|
### GRAPHIC
|
|
|
|
`GRAPHIC mode` chooses a screen mode; `GRAPHIC CLR` clears it. Mode 0 is text and
|
|
refuses to draw.
|
|
|
|
Every picture in this chapter is generated by running the listing above it; see
|
|
`MAINTENANCE.md` if you are editing one.
|
|
|
|
### DRAW
|
|
|
|
```basic requires=akgl screenshot=draw
|
|
10 COLOR 1, 8
|
|
20 DRAW 1, 20, 180 TO 90, 40 TO 160, 150 TO 230, 20 TO 300, 120
|
|
30 COLOR 2, 6
|
|
40 DRAW 2, 20, 190 TO 300, 190
|
|
50 LOCATE 160, 100
|
|
60 COLOR 3, 3
|
|
70 DRAW 3
|
|
```
|
|
|
|

|
|
|
|
One coordinate pair plots a point. Two or more, separated by `TO`, draw a polyline. A
|
|
bare `DRAW 3` — the last line above, and the single red pixel in the middle of the
|
|
picture — plots wherever `LOCATE` left the pixel cursor.
|
|
|
|
### BOX
|
|
|
|
```basic requires=akgl screenshot=box
|
|
10 COLOR 1, 8
|
|
20 BOX 1, 20, 30, 130, 140
|
|
30 COLOR 2, 6
|
|
40 BOX 2, 180, 30, 290, 140, 30
|
|
50 COLOR 3, 3
|
|
60 LOCATE 300, 190
|
|
70 BOX 3, 20, 160
|
|
```
|
|
|
|

|
|
|
|
Corners, and an optional rotation angle — the green box is the same box turned 30
|
|
degrees about its own centre. Two coordinates instead of four take the other corner
|
|
from the pixel cursor, which is the red box.
|
|
|
|
**`BOX` always outlines; it cannot fill.** BASIC 7.0 selects fill with a seventh
|
|
argument and that is not implemented here — see Chapter 13. `PAINT` is the fill you
|
|
have.
|
|
|
|
### CIRCLE
|
|
|
|
```basic requires=akgl screenshot=circle
|
|
10 COLOR 1, 8
|
|
20 CIRCLE 1, 80, 70, 60, 60
|
|
30 COLOR 2, 6
|
|
40 CIRCLE 2, 230, 70, 75, 45
|
|
50 COLOR 3, 3
|
|
60 CIRCLE 3, 160, 140, 130, 50, 90, 270
|
|
```
|
|
|
|

|
|
|
|
Source, centre, then the two radii — equal radii give a circle and unequal ones an
|
|
ellipse. Two further arguments are a start and an end angle, which is what makes the
|
|
red arc: 90 to 270 is the bottom half, because angles here are degrees clockwise from
|
|
straight up, the same convention `MOVSPR` uses. Beyond those come a rotation and the
|
|
degree increment, and a large increment is what turns a circle into a polygon.
|
|
|
|
### PAINT
|
|
|
|
```basic requires=akgl screenshot=paint
|
|
10 COLOR 1, 8
|
|
20 CIRCLE 1, 100, 100, 70, 70
|
|
30 BOX 1, 180, 50, 290, 150
|
|
40 COLOR 2, 6
|
|
50 PAINT 2, 100, 100
|
|
60 COLOR 3, 3
|
|
70 PAINT 3, 230, 100
|
|
```
|
|
|
|

|
|
|
|
Flood-fills the region containing a point, stopping at whatever is already drawn — so
|
|
the outline you fill inside can come from any verb. If the region is too large for the
|
|
fill's own working space it stops and reports rather than leaving a half-painted screen
|
|
with no explanation.
|
|
|
|
### LOCATE
|
|
|
|
Moves the pixel cursor, which is where a bare `DRAW` plots and where a `BOX` with two
|
|
coordinates finishes.
|
|
|
|
### SCALE
|
|
|
|
```basic requires=akgl screenshot=scale size=640x400
|
|
10 COLOR 1, 3
|
|
20 BOX 1, 0, 0, 319, 199
|
|
30 SCALE 1, 319, 199
|
|
40 COLOR 2, 6
|
|
50 BOX 2, 0, 0, 319, 199
|
|
60 DRAW 2, 0, 0 TO 319, 199
|
|
```
|
|
|
|

|
|
|
|
That picture is 640 by 400, and both boxes name the same four numbers. The red one is
|
|
drawn with `SCALE` off, so its coordinates are pixels and it covers exactly the
|
|
top-left 320 by 200 of the window — which is what a C128 listing does here. The green
|
|
one is drawn after `SCALE 1, 319, 199` and fills the window, and the diagonal confirms
|
|
that 319, 199 reaches the last pixel rather than stopping one short of it.
|
|
|
|
Turns on user coordinates and gives their maxima. With it on, your coordinates are
|
|
mapped onto the drawing surface: 0 is the first pixel and the maximum you gave is the
|
|
*last* one, so `DRAW 1, 1023, 1023` above reaches the bottom-right corner rather than
|
|
missing it by a pixel. `SCALE 1` on its own uses 7.0's 1023 by 1023. `SCALE 0` turns
|
|
it off and coordinates go back to being window pixels.
|
|
|
|
### RGR
|
|
|
|
`RGR(0)` is the current `GRAPHIC` mode. `RGR(1)` and `RGR(2)` are the drawing
|
|
surface's width and height in pixels — those two are ours rather than 7.0's, and they
|
|
are what a program needs to use a window whose size it did not choose. All three
|
|
refuse when there is no graphics device, except `RGR(0)`, which is a mode this
|
|
interpreter recorded rather than a screen it has to go and measure.
|
|
|
|
### WIDTH
|
|
|
|
`WIDTH 1` or `WIDTH 2` sets how thick a drawn line is. A thick line is drawn as
|
|
parallel passes; see Chapter 13.
|
|
|
|
## Saving and stamping regions
|
|
|
|
`SSHAPE` copies a rectangle off the screen and `GSHAPE` stamps it back:
|
|
|
|
```basic requires=akgl screenshot=shapes
|
|
10 COLOR 1, 8
|
|
20 CIRCLE 1, 40, 40, 30, 30
|
|
30 COLOR 2, 6
|
|
40 PAINT 2, 40, 40
|
|
50 SSHAPE A$, 8, 8, 72, 72
|
|
60 GSHAPE A$, 120, 20
|
|
70 GSHAPE A$, 200, 60
|
|
80 GSHAPE A$, 120, 120
|
|
```
|
|
|
|

|
|
|
|
One disc drawn, captured, and stamped three times.
|
|
|
|
**`A$` holds a handle, not the pixels.** On a C128 the string holds the bitmap, so a
|
|
program could save it to disk or take its `LEN`. Here a string is a fixed 255 bytes and
|
|
the region is a device surface, so what goes in the string is a reference to it —
|
|
`SHAPE:0`. You can pass it to `GSHAPE` and to `SPRSAV`, which is everything BASIC ever
|
|
does with one, but you cannot store it or measure it.
|
|
|
|
## What is not here
|
|
|
|
`FILTER` parses and then refuses: there is no filter stage to configure. See Chapter 7.
|
|
|
|
The graphics verbs draw straight to the renderer rather than into a display list, so
|
|
anything drawn is overwritten by the text layer on the next frame. A program that wants
|
|
its drawing to persist has to redraw it. This is recorded as a defect rather than a
|
|
design; see Chapter 13.
|