328 lines
10 KiB
Text
328 lines
10 KiB
Text
== Tiles and Text
|
|
|
|
Declared in `<joey/tile.h>`.
|
|
|
|
A tile is not a separate object. It is the 8x8 region of a surface at block
|
|
coordinates `(bx, by)`, where `bx` runs 0..39 and `by` runs 0..24 -- 40x25
|
|
blocks across a 320x200 surface. The tile API moves 32-byte blocks between
|
|
surfaces or fills them.
|
|
|
|
The consequence is that everything else works on tiles too. You can paint tile
|
|
content with `jlDrawPixel` and `jlFillRect`, generate it procedurally, or edit
|
|
it at run time, because a "tileset" is just a surface you agree to treat as
|
|
one. Fonts, terrain sets and spritesheets are all the same thing.
|
|
|
|
=== Constants and Types
|
|
|
|
[cols="3,1,4", options="header"]
|
|
|===
|
|
|Name |Value |Meaning
|
|
|
|
|`TILE_PIXELS_PER_SIDE` |8 |Tile edge in pixels.
|
|
|`TILE_BYTES_PER_ROW` |4 |Bytes per tile row (8 pixels at 4bpp).
|
|
|`TILE_BYTES` |32 |Bytes in one tile.
|
|
|`TILE_BLOCKS_PER_ROW` |40 |Tile columns on a surface.
|
|
|`TILE_BLOCKS_PER_COL` |25 |Tile rows on a surface.
|
|
|`TILE_NO_GLYPH` |0xFFFF |`jlDrawText` map entry meaning "skip this character".
|
|
|`TILE_MAP_SKIP` |0xFF |`jlTileMapPaste` entry meaning "leave this cell alone".
|
|
|===
|
|
|
|
[source,c]
|
|
----
|
|
typedef struct jlTileT {
|
|
uint8_t pixels[TILE_BYTES];
|
|
} jlTileT;
|
|
----
|
|
|
|
A stack-allocatable 32-byte tile snapshot, so save-under style work does not
|
|
need a whole scratch surface.
|
|
|
|
[IMPORTANT]
|
|
====
|
|
A `jlTileT` is *target-native*: plane-major on the planar ports, chunky on the
|
|
others. Treat it as opaque. In particular, do not feed a tile captured by
|
|
`jlTileSnap` to `jlTilePasteMono`, which wants a chunky silhouette -- that is a
|
|
format mismatch and draws the wrong shape. Use `jlTilePasteGlyph` for 1-bit
|
|
glyph data instead.
|
|
====
|
|
|
|
=== jlTileCopy
|
|
|
|
[source,c]
|
|
----
|
|
void jlTileCopy(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy,
|
|
const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy);
|
|
----
|
|
|
|
Copies one 8x8 block between surfaces, opaquely.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`dstBx`, `dstBy` |`uint8_t` |Destination block coordinates.
|
|
|`src` |`const jlSurfaceT *` |Source surface. May be the same as `dst`.
|
|
|`srcBx`, `srcBy` |`uint8_t` |Source block coordinates.
|
|
|===
|
|
|
|
*Returns:* nothing. Out-of-range block coordinates on either side are silent
|
|
no-ops.
|
|
|
|
=== jlTileCopyMasked
|
|
|
|
[source,c]
|
|
----
|
|
void jlTileCopyMasked(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy,
|
|
const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy,
|
|
uint8_t transparentIndex);
|
|
----
|
|
|
|
As `jlTileCopy`, but source pixels equal to `transparentIndex` are skipped and
|
|
the destination keeps its original value. This is the call for fonts and for
|
|
any tileset with see-through pixels.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`dstBx`, `dstBy` |`uint8_t` |Destination block coordinates.
|
|
|`src` |`const jlSurfaceT *` |Source surface.
|
|
|`srcBx`, `srcBy` |`uint8_t` |Source block coordinates.
|
|
|`transparentIndex` |`uint8_t` |Source colour index treated as transparent.
|
|
|===
|
|
|
|
*Returns:* nothing.
|
|
|
|
=== jlTileFill
|
|
|
|
[source,c]
|
|
----
|
|
void jlTileFill(jlSurfaceT *s, uint8_t bx, uint8_t by, uint8_t colorIndex);
|
|
----
|
|
|
|
Fills one block with a solid colour. Equivalent to
|
|
`jlFillRect(s, bx*8, by*8, 8, 8, colorIndex)` but skips the clipping maths,
|
|
since block coordinates are already known to be in range.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`s` |`jlSurfaceT *` |Target surface.
|
|
|`bx`, `by` |`uint8_t` |Block coordinates.
|
|
|`colorIndex` |`uint8_t` |Colour index 0..15.
|
|
|===
|
|
|
|
*Returns:* nothing.
|
|
|
|
=== jlTileBankLoad
|
|
|
|
[source,c]
|
|
----
|
|
uint16_t jlTileBankLoad(const char *path, jlTileT *outTiles, uint16_t maxTiles,
|
|
bool *outValid, uint16_t *outPalette);
|
|
----
|
|
|
|
Loads tiles from a baked `.tbk` file. The file's per-target byte is checked
|
|
against the build platform and mismatches are refused.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`path` |`const char *` |File path.
|
|
|`outTiles` |`jlTileT *` |Receives the tiles. Caller-owned.
|
|
|`maxTiles` |`uint16_t` |Capacity of `outTiles`.
|
|
|`outValid` |`bool *` |Optional, may be `NULL`. Set `true` per loaded slot, so
|
|
loaded tiles can be told from leftovers in a fixed-size array.
|
|
|`outPalette` |`uint16_t *` |Optional, may be `NULL`. Receives the embedded
|
|
16-entry `$0RGB` palette if the file has one.
|
|
|===
|
|
|
|
*Returns:* the number of tiles written. Returns 0 if the file is missing, the
|
|
magic or target byte is wrong, or `maxTiles` is 0.
|
|
|
|
=== jlTileBankLoadToSurface
|
|
|
|
[source,c]
|
|
----
|
|
uint16_t jlTileBankLoadToSurface(const char *path, jlSurfaceT *dst,
|
|
uint16_t *outPalette);
|
|
----
|
|
|
|
Streams a baked `.tbk` straight onto a surface's block grid: tile `i` lands at
|
|
block `(i % 40, i / 40)`, the row-major layout the bake tools author against.
|
|
|
|
Use this instead of `jlTileBankLoad` for anything large. A 1000-tile font sheet
|
|
is 32000 bytes, which no IIgs program can hold in an array or in bank-0 BSS;
|
|
this needs only a small internal scratch buffer and one pass over the file.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`path` |`const char *` |File path.
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`outPalette` |`uint16_t *` |Optional, may be `NULL`. As `jlTileBankLoad`.
|
|
|===
|
|
|
|
*Returns:* the number of tiles pasted. Blocks beyond the file's tile count keep
|
|
their previous content.
|
|
|
|
=== jlTileSnap
|
|
|
|
[source,c]
|
|
----
|
|
void jlTileSnap(const jlSurfaceT *src, uint8_t bx, uint8_t by, jlTileT *out);
|
|
----
|
|
|
|
Captures one block into a caller-supplied `jlTileT`.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`src` |`const jlSurfaceT *` |Source surface.
|
|
|`bx`, `by` |`uint8_t` |Block coordinates.
|
|
|`out` |`jlTileT *` |Receives the 32-byte snapshot.
|
|
|===
|
|
|
|
*Returns:* nothing.
|
|
|
|
=== jlTilePaste
|
|
|
|
[source,c]
|
|
----
|
|
void jlTilePaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, const jlTileT *in);
|
|
----
|
|
|
|
Pastes a `jlTileT` back onto a surface, always opaquely. Use
|
|
`jlTileCopyMasked` if you need transparency.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`bx`, `by` |`uint8_t` |Block coordinates.
|
|
|`in` |`const jlTileT *` |Tile to paste.
|
|
|===
|
|
|
|
*Returns:* nothing.
|
|
|
|
=== jlTilePasteMono
|
|
|
|
[source,c]
|
|
----
|
|
void jlTilePasteMono(jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
|
const jlTileT *in, uint8_t fgColor, uint8_t bgColor);
|
|
----
|
|
|
|
Pastes a tile as a silhouette: any non-zero source pixel draws `fgColor`, zero
|
|
pixels draw `bgColor`. Useful when the bitmap is a fixed glyph but the colour
|
|
comes from a separate map, as in C64-style colour RAM. Opaque.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`bx`, `by` |`uint8_t` |Block coordinates.
|
|
|`in` |`const jlTileT *` |Source tile, read as a silhouette.
|
|
|`fgColor` |`uint8_t` |Colour for non-zero pixels. Masked to 4 bits.
|
|
|`bgColor` |`uint8_t` |Colour for zero pixels. Masked to 4 bits.
|
|
|===
|
|
|
|
*Returns:* nothing.
|
|
|
|
[WARNING]
|
|
====
|
|
This call wants a *chunky* silhouette, but a `jlTileT` from `jlTileSnap` is
|
|
target-native. Feeding one to this function on a planar port is a format
|
|
mismatch. For glyphs, use `jlTilePasteGlyph`, which takes unambiguous 1-bit
|
|
input.
|
|
====
|
|
|
|
=== jlTilePasteGlyph
|
|
|
|
[source,c]
|
|
----
|
|
void jlTilePasteGlyph(jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
|
const uint8_t *bits, uint8_t fgColor, uint8_t bgColor);
|
|
----
|
|
|
|
Pastes an 8x8 one-bit-per-pixel glyph: `bits[row]` is that row, most
|
|
significant bit leftmost. A set bit draws `fgColor`, a clear bit `bgColor`.
|
|
Opaque.
|
|
|
|
This is the shape a character generator already has -- a font ROM, a C64
|
|
charset, anything 1bpp -- so nothing needs expanding into a `jlTileT` first.
|
|
*Prefer this over `jlTilePasteMono` for glyphs.*
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`bx`, `by` |`uint8_t` |Block coordinates.
|
|
|`bits` |`const uint8_t *` |8 bytes, one per glyph row, MSB leftmost.
|
|
|`fgColor` |`uint8_t` |Set-bit colour. Masked to 4 bits.
|
|
|`bgColor` |`uint8_t` |Clear-bit colour. Masked to 4 bits.
|
|
|===
|
|
|
|
*Returns:* nothing.
|
|
|
|
=== jlDrawText
|
|
|
|
[source,c]
|
|
----
|
|
void jlDrawText(jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
|
const jlSurfaceT *fontSurface, const uint16_t *asciiMap,
|
|
const char *str);
|
|
----
|
|
|
|
Draws a NUL-terminated ASCII string using glyphs from a font surface. Glyph
|
|
colour 0 is transparent, so the background shows through. The cursor wraps at
|
|
the right edge and truncates at the bottom.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`bx`, `by` |`uint8_t` |Starting block coordinates.
|
|
|`fontSurface` |`const jlSurfaceT *` |Surface holding the glyphs.
|
|
|`asciiMap` |`const uint16_t *` |256 entries mapping ASCII code to glyph
|
|
position: low byte is source block X, high byte is source block Y. An entry of
|
|
`TILE_NO_GLYPH` skips that character, advancing the cursor without drawing.
|
|
|`str` |`const char *` |NUL-terminated string.
|
|
|===
|
|
|
|
*Returns:* nothing.
|
|
|
|
=== jlTileMapPaste
|
|
|
|
[source,c]
|
|
----
|
|
void jlTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
|
uint8_t wTiles, uint8_t hTiles,
|
|
const jlTileT *tiles, const uint8_t *map);
|
|
----
|
|
|
|
Pastes a whole rectangle of tiles in one call, so validation, dispatch and the
|
|
dirty-rectangle union are paid once for the rectangle instead of once per tile.
|
|
For anything larger than a few tiles this is substantially faster than looping
|
|
over `jlTilePaste`.
|
|
|
|
[cols="1,1,4", options="header"]
|
|
|===
|
|
|Parameter |Type |Description
|
|
|`dst` |`jlSurfaceT *` |Destination surface.
|
|
|`bx`, `by` |`uint8_t` |Top-left block coordinates.
|
|
|`wTiles`, `hTiles` |`uint8_t` |Rectangle size in tiles. Zero is a no-op.
|
|
|`tiles` |`const jlTileT *` |The tileset.
|
|
|`map` |`const uint8_t *` |`wTiles * hTiles` tileset indices, row-major, so
|
|
`map[row * wTiles + col]`. `TILE_MAP_SKIP` leaves that cell untouched.
|
|
|===
|
|
|
|
*Returns:* nothing. A rectangle whose edge crosses the surface boundary is
|
|
rejected as a no-op.
|
|
|
|
[WARNING]
|
|
====
|
|
Any map index other than `TILE_MAP_SKIP` must be a valid tileset index. The
|
|
batch does not know the tileset length and does *not* range-check it -- an
|
|
out-of-range index reads past the end of your array.
|
|
====
|