== Surfaces and Presenting Declared in `` and ``. A surface is a 320x200 16-colour image with its own 200-entry SCB table and its own 16x16 palette set. Storage is target-native -- chunky 4bpp on the IIgs, DOS, DVX, and the hosted ports, planar on the Amiga, ST, and X68000 -- but every entry point speaks in colour indices, so your code never sees the difference. A new surface starts with the 16-colour EGA palette in all 16 of its palettes. === Constants [cols="3,1,4", options="header"] |=== |Name |Value |Meaning |`JL_SURFACE_WIDTH` |320 |Surface width in pixels. |`JL_SURFACE_HEIGHT` |200 |Surface height in pixels. |`JL_SURFACE_BYTES_PER_ROW` |160 |Bytes per row in chunky 4bpp form. |`JL_SURFACE_PIXELS_SIZE` |32000 |Total pixel bytes. |`JL_SURFACE_PALETTE_COUNT` |16 |Palettes per surface. |`JL_SURFACE_COLORS_PER_PALETTE` |16 |Colours per palette. |`JL_SURFACE_PALETTE_ENTRIES` |256 |Total palette entries per surface. |=== === jlSurfaceT [source,c] ---- typedef struct jlSurfaceT jlSurfaceT; ---- Opaque. Obtain one from `jlSurfaceCreate`, `jlStageGet`, or a loader. === jlSurfaceCreate [source,c] ---- jlSurfaceT *jlSurfaceCreate(void); ---- Allocates a new offscreen surface. Each one costs about 32 KB. On the Amiga that comes from chip RAM, because the blitter works on the planes there, so on a 512 KB machine, beside the display's own pages, only a few fit. *Parameters:* none. *Returns:* the surface, or `NULL` on failure (`jlLastError()` explains why). === jlSurfaceDestroy [source,c] ---- void jlSurfaceDestroy(jlSurfaceT *s); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`s` |`jlSurfaceT *` |Surface to release. `NULL` is a no-op, and passing the stage is also a no-op -- the library owns it. |=== *Returns:* nothing. === jlStageGet [source,c] ---- jlSurfaceT *jlStageGet(void); ---- Returns the library-owned stage: the back buffer that `jlStagePresent` flips to the display. Always valid between `jlInit` and `jlShutdown`. On the IIgs the stage's pixel buffer is pinned to bank `$01` Super Hi-Res space with shadowing inhibited, so writes run at full speed and stay isolated from the displayed framebuffer until the next present. *Parameters:* none. *Returns:* the stage surface. === jlSurfaceCopy [source,c] ---- void jlSurfaceCopy(jlSurfaceT *dst, const jlSurfaceT *src); ---- Copies pixels, SCBs, and palettes from `src` into `dst`. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`dst` |`jlSurfaceT *` |Destination. Must be valid. |`src` |`const jlSurfaceT *` |Source. Must be valid. |=== *Returns:* nothing. === jlSurfaceCopyRect [source,c] ---- void jlSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, int16_t x, int16_t y, uint16_t w, uint16_t h); ---- Copies a pixel rectangle from `src` to `dst` *at the same position* -- there is no repositioning, so the planar ports never have to shift bits. This is the erase half of the clean-buffer pattern: keep a pristine copy of the scene, and restore rectangles from it before redrawing sprites. Copies pixels only. SCBs and palettes are untouched. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`dst` |`jlSurfaceT *` |Destination surface. |`src` |`const jlSurfaceT *` |Source surface. |`x`, `y` |`int16_t` |Top-left of the rectangle, clipped to the surface. |`w`, `h` |`uint16_t` |Size in pixels. |=== *Returns:* nothing. [WARNING] ==== The rectangle is clipped to the surface and then *widened outward to the 16-pixel group grid on every port*: `x` rounds down and `x + w` rounds up to multiples of 16. Two consequences you own: * up to 15 extra pixels each side are copied, so a snapped erase can clobber on-screen content outside the rectangle you asked for; * it silently invalidates any live `jlSpriteBackupT` whose recorded region it touches. Erase *all* regions, then draw *all* sprites. Never mix this with per-sprite save and restore in the same region. ==== === jlSurfaceSaveFile [source,c] ---- bool jlSurfaceSaveFile(const jlSurfaceT *src, const char *path); ---- Writes a surface to disk in target-native form: 32000 pixel bytes, then 200 SCB bytes, then 512 bytes of `$0RGB` palette data. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`src` |`const jlSurfaceT *` |Surface to write. |`path` |`const char *` |Destination path. |=== *Returns:* `true` on success; `false` if the file could not be opened or the write failed. [IMPORTANT] ==== The format is *not portable*. No endianness or layout conversion is performed, so the same platform that wrote a file must be the one that reads it. Use this for runtime caches, not for shipped assets. ==== === jlSurfaceLoadFile [source,c] ---- bool jlSurfaceLoadFile(jlSurfaceT *dst, const char *path); ---- Reads a surface from disk, overwriting `dst`. The surface keeps its identity -- nothing is reallocated. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`dst` |`jlSurfaceT *` |Surface to overwrite. |`path` |`const char *` |Source path. |=== *Returns:* `true` on success; `false` if the file is missing, the wrong size, has an SCB byte above 15, or unreadable. A read that fails part way can leave the pixels partly overwritten; the SCBs and palettes are then untouched. Colour 0 of every palette loaded is set to black, as `jlPaletteSet` does. Neither file call sets `jlLastError`. === jlSurfaceHash [source,c] ---- uint32_t jlSurfaceHash(const jlSurfaceT *s); ---- Computes a 32-bit content hash over the surface: the packed chunky pixel byte stream, then the 200 SCB bytes, then the 256 palette entries. The important property is that *the same logical pixels hash the same on every port*, whatever the internal storage format. That makes it the tool for proving two platforms render identically -- which is exactly how the library's own cross-port validation works. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`s` |`const jlSurfaceT *` |Surface to hash. |=== *Returns:* the hash value. === jlStagePresent [source,c] ---- void jlStagePresent(void); ---- Flips the dirty regions of the stage to the display and clears the dirty state. Where the stage already has the display's layout this is a copy (the IIgs's 4bpp pixels; the planes of the Amiga, ST, and X68000); DOS, DVX, and the hosted ports expand each 4-bit pixel to their display's 8-bit or RGB pixels. Palette and SCB changes reach the display here too. Drawing primitives mark per-row dirty ranges as a side effect, so you only need to call this at the end of a frame and everything you drew appears. Rows nothing touched since the last present are skipped. *Parameters:* none. *Returns:* nothing. [NOTE] ==== Every call that changes the stage's pixels marks the rows it touched, and the API has no other way to write them, so there is nothing to mark by hand. ====