Make GRAPHIC select the text plane
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m22s
akbasic CI Build / sanitizers (push) Failing after 4m43s
akbasic CI Build / coverage (push) Failing after 3m43s
akbasic CI Build / akgl_build (push) Failing after 4m48s
akbasic CI Build / mutation_test (push) Failing after 3m31s

Co-authored-by: Andrew Kesterson <andrew@aklabs.net>
This commit is contained in:
2026-08-03 08:08:34 -04:00
parent c5d13f00f6
commit 3342f2b569
13 changed files with 345 additions and 79 deletions

View File

@@ -62,22 +62,28 @@ small number that is not a colour.
### GRAPHIC
`GRAPHIC mode` chooses a screen mode; `GRAPHIC CLR` clears it. Mode 0 is text and
refuses to draw.
`GRAPHIC mode [, clear] [, split]` chooses the display composition; `GRAPHIC CLR`
clears the saved graphics state. `GRAPHIC 0` displays the text plane. `GRAPHIC 1`
selects a full bitmap plane: `PRINT` still updates the retained text screen, but it is
not visible until text mode returns. `GRAPHIC 2` is the explicit split-screen form;
its optional third argument names the first text row, and its default leaves six text
rows at the bottom. `WINDOW` restricts the current text region; it does not make a
full bitmap visible.
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
```basic requires=akgl screenshot=draw text=1
10 GRAPHIC 1, 1
20 COLOR 1, 8
30 DRAW 1, 20, 180 TO 90, 40 TO 160, 150 TO 230, 20 TO 300, 120
40 COLOR 2, 6
50 DRAW 2, 20, 190 TO 300, 190
60 LOCATE 160, 100
70 COLOR 3, 3
80 DRAW 3
```
![](images/draw.png)
@@ -88,14 +94,15 @@ 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
```basic requires=akgl screenshot=box text=1
10 GRAPHIC 1, 1
20 COLOR 1, 8
30 BOX 1, 20, 30, 130, 140
40 COLOR 2, 6
50 BOX 2, 180, 30, 290, 140, 30
60 COLOR 3, 3
70 LOCATE 300, 190
80 BOX 3, 20, 160
```
![](images/box.png)
@@ -110,13 +117,14 @@ 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
```basic requires=akgl screenshot=circle text=1
10 GRAPHIC 1, 1
20 COLOR 1, 8
30 CIRCLE 1, 80, 70, 60, 60
40 COLOR 2, 6
50 CIRCLE 2, 230, 70, 75, 45
60 COLOR 3, 3
70 CIRCLE 3, 160, 140, 130, 50, 90, 270
```
![](images/circle.png)
@@ -129,14 +137,15 @@ 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
```basic requires=akgl screenshot=paint text=1
10 GRAPHIC 1, 1
20 COLOR 1, 8
30 CIRCLE 1, 100, 100, 70, 70
40 BOX 1, 180, 50, 290, 150
50 COLOR 2, 6
60 PAINT 2, 100, 100
70 COLOR 3, 3
80 PAINT 3, 230, 100
```
![](images/paint.png)
@@ -153,13 +162,14 @@ 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
```basic requires=akgl screenshot=scale size=640x400 text=1
10 GRAPHIC 1, 1
20 COLOR 1, 3
30 BOX 1, 0, 0, 319, 199
40 SCALE 1, 319, 199
50 COLOR 2, 6
60 BOX 2, 0, 0, 319, 199
70 DRAW 2, 0, 0 TO 319, 199
```
![](images/scale.png)
@@ -212,15 +222,16 @@ parallel passes; see Chapter 13.
`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
```basic requires=akgl screenshot=shapes text=1
10 GRAPHIC 1, 1
20 COLOR 1, 8
30 CIRCLE 1, 40, 40, 30, 30
40 COLOR 2, 6
50 PAINT 2, 40, 40
60 SSHAPE A$, 8, 8, 72, 72
70 GSHAPE A$, 120, 20
80 GSHAPE A$, 200, 60
90 GSHAPE A$, 120, 120
```
![](images/shapes.png)
@@ -244,19 +255,11 @@ does with one, but you cannot store it or measure it.
text and the sprites, so a program draws its picture once and it is there on every frame
after. It does not have to redraw it, and it does not have to capture it into a sprite.
What covers it is the text layer, which repaints every row it owns — opaque, every frame,
for a reason `akbasic_sink_akgl_render()` explains — and by default it owns the whole
window. `WINDOW` shrinks it:
```basic norun
10 WINDOW 0, 0, 39, 1
20 GRAPHIC 1, 1
30 COLOR 1, 3
40 BOX 1, 20, 40, 300, 180
```
Two rows of text at the top, the rest of the window for drawing, and the box is still
there a thousand frames later.
`GRAPHIC 1` changes that order: the full bitmap owns the display, while the text layer
keeps its contents off-screen. Returning with `GRAPHIC 0` reveals what `PRINT` wrote.
Use `GRAPHIC 2, clear, split` when a program deliberately needs both planes; its text
rows begin at `split`. `WINDOW` can then narrow that text region further, but it is not
needed to make a full bitmap visible.
**A redraw also has to fit inside one batch.** The host runs a fixed number of source
lines and then presents, and presenting throws the drawing buffer away — so a run of

View File

@@ -116,6 +116,7 @@ typedef struct akbasic_TextSink
akerr_ErrorContext AKERR_NOIGNORE *(*moveto)(struct akbasic_TextSink *self, int col, int row);
akerr_ErrorContext AKERR_NOIGNORE *(*window)(struct akbasic_TextSink *self, int left, int top, int right, int bottom);
akerr_ErrorContext AKERR_NOIGNORE *(*grid)(struct akbasic_TextSink *self, int *columns, int *rows, int *cellw, int *cellh);
akerr_ErrorContext AKERR_NOIGNORE *(*graphic)(struct akbasic_TextSink *self, int mode, int split);
} akbasic_TextSink;
```
@@ -123,8 +124,8 @@ typedef struct akbasic_TextSink
supplies its own and draws into a text layer. `readline` is expected to set `*eof` rather
than block — that is how `INPUT` behaves sanely inside a frame.
**The last three are optional and may be NULL**, which is how `CHAR`, `WINDOW` and
`RWINDOW` know to refuse by name rather than pretending. Supply `grid` if your text layer
**The last four are optional and may be NULL**, which is how `CHAR`, `WINDOW`, `RWINDOW`
and `GRAPHIC` know to refuse by name rather than pretending. Supply `grid` if your text layer
has a character cell: it is the only way a script can find out how big one is, and
without it anything placing a character and a sprite at the same spot has to hardcode a
number measured against your font.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

After

Width:  |  Height:  |  Size: 735 B