Skip to content

Commit b8d310c

Browse files
cursoragentomiq
andcommitted
docs: add blitter/surface spec draft; link from to-do.md
- RGC-BASIC: phased IMAGE COPY, off-screen buffers, TOSPRITE, export - CLI + WASM parity; STOS/AMOS-style editor-in-BASIC goals - README not linked per discussion Co-authored-by: Chris Garrett <chris@chrisg.com>
1 parent 10b0ecf commit b8d310c

2 files changed

Lines changed: 264 additions & 0 deletions

File tree

docs/rgc-blitter-surface-spec.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# RGC-BASIC: Blitter, Surfaces & In-Memory Images — Design Specification
2+
3+
**Status:** Draft for discussion and phased implementation
4+
**Audience:** Implementers (C/WASM/JS) and advanced BASIC authors (e.g. STOS/AMOS-style tools in BASIC)
5+
**Goal:** Add **rectangular copy** (blitter-style), **off-screen image buffers**, **region → sprite** capture, and **export** paths, with **feature parity** between **native** (`basic-gfx`) and **canvas WASM**, without breaking existing programs.
6+
7+
---
8+
9+
## 1. Vision
10+
11+
### 1.1 What we are building toward
12+
13+
- **AMOS/STOS-like workflow (subset):** Copy rectangles between buffers, scroll regions, build tile/sprite workflows from **BASIC** — enough to support **editors and utilities** written in RGC-BASIC (as the classic Amiga tools often were).
14+
- **One language, two hosts:** Same statements and functions behave the same in **terminal + gfx** where applicable, and in **WASM canvas** (with documented limits).
15+
- **Progressive disclosure:** Start with **bitmap-only** operations that map cleanly to existing `GfxVideoState` + software renderer; add **RGBA surfaces** and **export** when the foundation is solid.
16+
17+
### 1.2 What we are not building (v1)
18+
19+
- A full **Amiga blitter** (minterm, arbitrary masks, copper).
20+
- **Dual independent playfields** with hardware-style priorities (we already approximate with layers + z-order sprites).
21+
- **Binary compatibility** with AMOS/STOS file formats (`.abk`, etc.) — optional **later**; **PNG** remains the interchange format unless extended.
22+
23+
---
24+
25+
## 2. Design principles
26+
27+
1. **Explicit surfaces:** Every copy has a **named target** (default visible bitmap vs numbered off-screen image). No hidden global “blitter context” beyond current `SCREEN` mode.
28+
2. **Predictable coordinates:** All rects use **pixel coordinates** in the **same space** as `PSET`/`LINE` in bitmap mode (0..319, 0..199 for default 320×200 unless extended).
29+
3. **Clipping:** All operations **clip to destination bounds** (and optionally source); out-of-budget pixels are **no-ops**, not errors — unless `OPTION STRICT_CLIP` (optional future) is enabled.
30+
4. **Deterministic limits:** Max surface count, max dimensions, max export size — **documented** and **queryable** from BASIC (`IMAGESIZE?` / `SYSINFO` pattern — exact names TBD).
31+
5. **WASM parity:** Any feature that touches **memory** must have a **clear JS story** for **readback** (export) without re-entrancy bugs (follow `wasm_gfx_key_state_ptr` patterns: **export pointer + length**, or **copy-out** via async JS).
32+
33+
---
34+
35+
## 3. Current foundation (inventory)
36+
37+
### 3.1 Already in RGC-BASIC
38+
39+
| Area | Mechanism |
40+
|------|-----------|
41+
| Text + colour | `GfxVideoState.screen[]`, `color[]`, `SCROLL` offsets |
42+
| Hi-res bitmap | 320×200 1 bpp packed buffer (`gfx_bitmap_*`, `PSET`/`LINE`) |
43+
| PNG sprites | Slots, `LOADSPRITE`, `DRAWSPRITE`, tile sheets, `SPRITEMODULATE` |
44+
| Memory model | Virtual `POKE`/`PEEK` bases; 64K-style addressing |
45+
46+
### 3.2 Gaps this spec fills
47+
48+
- **No** general **rectangular copy** between two raster buffers.
49+
- **No** first-class **off-screen bitmap/RGBA** that BASIC names and addresses.
50+
- **No** **grab screen region → sprite slot** without an intermediate PNG file.
51+
- **No** **export** of a buffer to **PNG** or **raw bytes** for web download.
52+
53+
---
54+
55+
## 4. Conceptual model
56+
57+
### 4.1 Surface kinds (typed)
58+
59+
| Kind | Bits | Typical use |
60+
|------|------|-------------|
61+
| `BITMAP1` | 1 bpp, packed | Current `SCREEN 1` layer; fast; good for classic-style tools |
62+
| `RGBA8` | 32 bpp premultiplied or straight-alpha | Scratch pads, “canvas” for tools, future true-color ops |
63+
64+
**Rule:** Phase 1 implements **`BITMAP1` only** for **COPY** between **same or two buffers** of the same kind. **RGBA** surfaces are **Phase 2+** (see §7).
65+
66+
### 4.2 Surface identifiers
67+
68+
- **Index `0`:** Reserved for the **visible** hi-res bitmap attached to the current gfx session (when `SCREEN 1` is active). Operations when `SCREEN 0` may **error** or **no-op** with hint — **TBD** (prefer **runtime error** with hint: “BITMAP COPY requires SCREEN 1”).
69+
- **Index `1..N`:** **Off-screen** surfaces created with **`IMAGE CREATE`** (name TBD).
70+
71+
**Alternative syntax (discussion):** Named images: `IMAGE "work"` vs numeric `IMAGE 1`. Numeric scales better for arrays in BASIC; names aid readability. Spec recommends **numeric handles** + optional **alias** via `IMAGE NAME` later.
72+
73+
### 4.3 Coordinate system
74+
75+
- Origin **top-left** `(0,0)`, `x` right, `y` down.
76+
- All rectangles: `x, y, width, height` **or** `x1,y1 TO x2,y2`**one style** should win for consistency with `LINE` (which uses `x,y TO x,y`). **Recommendation:** mirror **`LINE`** / **`BAR`** style:
77+
`COPY sx,sy TO dx,dy, w, h` **or**
78+
`COPY sx1,sy1,sx2,sy2 TO dx,dy` (two corners) — pick one and document.
79+
80+
---
81+
82+
## 5. Feature set by phase
83+
84+
### Phase 1 — Bitmap rectangular copy (blitter-style, minimal)
85+
86+
**Scope:** 1 bpp buffers only; **copy** from **source rect** to **dest** in **same** image or **another** bitmap surface.
87+
88+
**Proposed statements (names illustrative):**
89+
90+
```
91+
REM Create off-screen bitmap WxH (1 bpp), returns handle in variable or uses next slot
92+
IMAGE CREATE img, 320, 200
93+
94+
REM Copy rectangle from surface src (0=screen bitmap) to surface dst at (dx,dy)
95+
REM Clipped; overlapping copy may need temp row buffer (implementation detail)
96+
IMAGE COPY src, sx, sy, sw, sh TO dst, dx, dy
97+
98+
REM Fill rect with 0 or 1 (bitmap pen)
99+
IMAGE FILL dst, x, y, w, h [, mode]
100+
101+
REM Optional: scroll region by (dx,dy) with fill edge (classic scroll)
102+
IMAGE SCROLL dst, x, y, w, h, dx, dy [, fill]
103+
```
104+
105+
**Semantics:**
106+
107+
- **`IMAGE COPY`:** Bit-level copy; **source and dest format must match** (Phase 1: both `BITMAP1`).
108+
- **Overlapping copy** on **same** buffer: must behave like **memmove**-safe (copy row-by-row in the correct direction) — specify in impl notes.
109+
- **Performance:** Native C memcpy of packed rows; WASM same, **no** per-pixel JS.
110+
111+
**Errors:**
112+
113+
- Invalid handle, negative size, `SCREEN 0` when `src` or `dst` is `0` — clear **`Hint:`** lines.
114+
115+
---
116+
117+
### Phase 2 — RGBA surfaces & blending (optional, larger)
118+
119+
**Scope:** Allocate **`RGBA8`** surfaces (same dimensions limits as WASM heap allows).
120+
121+
- `IMAGE CREATE RGBA img, w, h` → transparent black or undefined cleared.
122+
- Later: `IMAGE BLEND`, `IMAGE ALPHA`**defer** until Phase 1 is stable.
123+
124+
**Use case:** Paint programs, icon editors — **not required** for “AMOS Screen Copy demo” parity.
125+
126+
---
127+
128+
### Phase 3 — Region → sprite slot (“grab”)
129+
130+
**Goal:** Avoid round-tripping through disk for tools.
131+
132+
**Proposed:**
133+
134+
```
135+
REM Capture rectangle from bitmap surface src into sprite slot s as new texture
136+
REM Equivalent to: crop WxH from (sx,sy), upload as PNG-like RGBA internally
137+
IMAGE TOSPRITE src, sx, sy, sw, sh, slot [, tile_w, tile_h]
138+
```
139+
140+
**Semantics:**
141+
142+
- **Source:** For Phase 1, **`BITMAP1` only** — conversion **1 bpp → RGBA** uses **foreground/background indices** from current `COLOR` / `BACKGROUND` or explicit **palette** parameters — **must be specified** (e.g. pen 1 = white, 0 = black for capture, or sample from PETSCII layer — **avoid** unless defined).
143+
144+
**Simpler v1:** **`IMAGE TOSPRITE` only from `RGBA8` surface** (Phase 2), OR **`TOSPRITE` from bitmap** uses **fixed two-colour** conversion. Document chosen rule.
145+
146+
**Interaction with `LOADSPRITE`:** Same slot rules — **replaces** texture for that slot; **`SPRITEMODULATE`** reset policy matches **`LOADSPRITE`**.
147+
148+
---
149+
150+
### Phase 4 — Export (CLI + WASM)
151+
152+
**Goal:** **Extract** image bytes for **download** or **processing**.
153+
154+
**Options (pick 1–2 for v1):**
155+
156+
| Method | CLI | WASM |
157+
|--------|-----|------|
158+
| **Raw 1bpp** | Write to `OPEN OUT` file | `HEAPU8` slice + JS download blob |
159+
| **PPM/PGM** | Easy text PGM export | Same, or base64 string |
160+
| **PNG** | **stb_image_write** (add dependency) or **miniz** | Same compiled to WASM |
161+
162+
**Proposed API:**
163+
164+
```
165+
REM Write surface img raw 1bpp to file (path)
166+
IMAGE SAVE img, "out.raw" [, format]
167+
168+
REM WASM: also expose via intrinsic
169+
REM S$ = IMAGEEXPORT$(img, "png") ' returns empty if too large / fail
170+
```
171+
172+
**WASM contract:**
173+
174+
- **`_wasm_image_export_ptr(slot, format)`** → ptr + len, or
175+
- **Chunked** export to **FS** (`/tmp/out.png`) + JS reads **FS** and triggers download (pattern already used in Emscripten tutorials).
176+
177+
**CORS / UX:** Document that **download** is **IDE responsibility** (button calling JS), not automatic.
178+
179+
---
180+
181+
## 6. STOS/AMOS-style editors in BASIC (target scenarios)
182+
183+
These scenarios **drive** API completeness:
184+
185+
| Tool | Needs |
186+
|------|--------|
187+
| **Tile painter** | Bitmap surface, `PSET`/`LINE`, `IMAGE COPY` to stamp tiles, optional **`TOSPRITE`** for preview |
188+
| **Charset viewer** | Already partly `petscii-data.bas`; **`COPY`** helps **tile extract** |
189+
| **Map editor** | 2D array + **`IMAGE COPY`** tiles from sheet surface to map surface |
190+
| **Sprite grabber** | **`TOSPRITE`** from back-buffer |
191+
192+
**Editor loop:** `DO``GET` / `INKEY$` / `PEEK``IMAGE COPY``WAIT` / `SLEEP 1` — same as today.
193+
194+
---
195+
196+
## 7. API sketch (consolidated)
197+
198+
**Naming:** Replace **`IMAGE`** with **`BITMAP`** if we want to avoid confusion with “character image” — **open decision**.
199+
200+
| Statement / function | Phase |
201+
|---------------------|-------|
202+
| `IMAGE CREATE h, w, h` | 1 |
203+
| `IMAGE COPY …` | 1 |
204+
| `IMAGE FILL …` | 1 |
205+
| `IMAGE FREE h` | 1 |
206+
| `IMAGE TOSPRITE …` | 3 |
207+
| `IMAGE SAVE` / `IMAGEEXPORT$` | 4 |
208+
| `IMAGE CREATE RGBA …` | 2 |
209+
210+
**Introspection:**
211+
212+
- `IMAGEWIDTH(h)`, `IMAGEHEIGHT(h)`, `IMAGECOUNT()` or **`MAXIMAGES`**.
213+
214+
---
215+
216+
## 8. Implementation architecture (C)
217+
218+
### 8.1 Data structures
219+
220+
- Extend **`GfxVideoState`** or add **`GfxImageBank`**:
221+
- Array of **`GfxRasterSurface`** { `kind`, `w`, `h`, `stride`, `owned_ptr`, `bytes` }.
222+
- Surface `0` = **alias** to existing **`bitmap[]`** when `SCREEN 1`.
223+
224+
### 8.2 Native (Raylib)
225+
226+
- **COPY** is **CPU**; optional **future:** upload **RGBA** surface to **Texture2D** for fast preview (not required for Phase 1).
227+
228+
### 8.3 WASM
229+
230+
- Surfaces live in **Wasm linear memory**; **`gfx_canvas`** compositor reads **visible** bitmap as today.
231+
- **Export:** encode PNG in WASM **or** export **raw** and PNG-encode in **JS** (smaller WASM binary) — **tradeoff decision**.
232+
233+
### 8.4 Threading
234+
235+
- Same rules as sprites: **no OpenGL** from worker if **basic-gfx** worker thread; **COPY** is **CPU-only** → safe on worker **if** single-writer discipline holds.
236+
237+
---
238+
239+
## 9. Testing
240+
241+
- **Golden tests:** Small fixed patterns **COPY** and compare byte arrays (`tests/image_copy_test.c`).
242+
- **BASIC regressions:** `tests/image_copy.bas` — deterministic output hash or **PEEK** sample points.
243+
- **WASM:** Playwright: run program, **export**, compare **PNG** checksum or dimensions.
244+
245+
---
246+
247+
## 10. Open decisions (need your input)
248+
249+
1. **Keyword prefix:** `IMAGE` vs `BITMAP` vs `SURFACE`.
250+
2. **Rect syntax:** `w,h` vs `x1,y1,x2,y2` only.
251+
3. **`TOSPRITE` from 1 bpp:** **two-colour** fixed vs **palette** from PETSCII.
252+
4. **PNG in WASM:** encode in C vs JS.
253+
5. **Max surfaces / max WxH** on WASM (heap).
254+
255+
---
256+
257+
## 11. Summary
258+
259+
This spec aims for **honest parity** with **“screen copy + grab region + work buffer”** patterns from **AMOS/STOS**, not hardware emulation. **Phase 1** (**off-screen 1 bpp + `IMAGE COPY`**) unlocks most **editor-in-BASIC** value with **manageable** scope on **CLI and WASM**. **Export** and **RGBA** follow once the **surface allocator** and **COPY** semantics are proven.
260+
261+
---
262+
263+
*Document version: 0.1 — draft*

to-do.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
4. **Graphic layers and scrolling**~**Viewport `SCROLL dx, dy`** + **`SCROLLX()`/`SCROLLY()`** (single shared offset for text/bitmap + sprites) — done.~ Full **per-layer** stack (background vs tiles vs HUD) still open.
6060
5. ~**Tilemap handling (sheet)**~**`LOADSPRITE slot, "path.png", tw, th`** + **`DRAWSPRITETILE slot, x, y, tile_index [, z]`** + **`SPRITETILES(slot)`**. Full world tile *grid* storage in BASIC still manual.
6161
6. **Sprite animation**~Per-slot frame via **`SPRITEFRAME`** + tile-aware **`DRAWSPRITE`** — done.~ Optional frame rate / timing helpers still open.
62+
7. **Blitter-style ops, off-screen buffers, grab-to-sprite, export** — Not started. Draft design (phased: `IMAGE COPY` / off-screen 1bpp surfaces → `TOSPRITE` → PNG/raw export; CLI + WASM parity): **`docs/rgc-blitter-surface-spec.md`**. Goal: STOS/AMOS-like map/sprite **utilities written in BASIC**; README link deferred while the design is discussed.
6263

6364
* **Browser / WASM** (see `docs/browser-wasm-plan.md`, `web/README.md`, `README.md`)
6465
* ~**Emscripten builds**~`make basic-wasm``web/basic.js` + `basic.wasm`; `make basic-wasm-canvas``basic-canvas.js` + `basic-canvas.wasm` (GFX_VIDEO: PETSCII + `SCREEN 1` bitmap + PNG sprites via `gfx_software_sprites.c` / stb_image, parity with basic-gfx). Asyncify for `SLEEP`, `INPUT`, `GET` / `INKEY$`.

0 commit comments

Comments
 (0)