438 lines
17 KiB
C
438 lines
17 KiB
C
// AGI v2 text plane: status line, display() rows, print() modal.
|
|
//
|
|
// Renders directly to the stage with an embedded 8x8 ASCII bitmap
|
|
// font; bypasses jlDrawText so glyph foreground/background can vary
|
|
// per-call without rebuilding a font surface for every color combo.
|
|
//
|
|
// Layout matches AGI's 40x25 text grid:
|
|
// Row 0 : status line (when statusLineOn)
|
|
// Rows 1..21 : picture (handled by agiPicBlit, not here)
|
|
// Rows 22..24 : message overlay area
|
|
//
|
|
// The print() modal centers a window over the picture region with
|
|
// a 1-character border and waits for user ack (HALT_PRINT_PENDING).
|
|
|
|
#include "agi.h"
|
|
|
|
#include "joey/draw.h"
|
|
#include "joey/surface.h"
|
|
#include "surfaceInternal.h"
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
|
|
#ifdef JOEYLIB_PLATFORM_IIGS
|
|
segment "AGITXT";
|
|
#endif
|
|
|
|
|
|
#define GLYPH_W 8u
|
|
#define GLYPH_H 8u
|
|
#define FIRST_CHAR 0x20u // space
|
|
#define LAST_CHAR 0x7Eu // tilde
|
|
#define GLYPH_COUNT (LAST_CHAR - FIRST_CHAR + 1u)
|
|
|
|
#define STAGE_W SURFACE_WIDTH
|
|
#define STAGE_H SURFACE_HEIGHT
|
|
|
|
|
|
// ----- Prototypes -----
|
|
|
|
static void drawChar(jlSurfaceT *stage, int16_t x, int16_t y, char ch, uint8_t fg, uint8_t bg);
|
|
static void drawCharTransparent(jlSurfaceT *stage, int16_t x, int16_t y, char ch, uint8_t fg);
|
|
static void drawString(jlSurfaceT *stage, int16_t x, int16_t y, const char *s, uint8_t fg, uint8_t bg);
|
|
static void drawWindow(jlSurfaceT *stage, int16_t x, int16_t y, uint8_t cols, uint8_t rows, uint8_t bg, uint8_t border);
|
|
static void fillRow(jlSurfaceT *stage, uint8_t row, uint8_t bg);
|
|
|
|
|
|
// 8x8 bitmap font for printable ASCII (0x20 = space .. 0x7E = tilde).
|
|
// Each glyph is 8 bytes; bit 7 is the leftmost pixel of each row.
|
|
// Lower-case letters map to upper-case via the ASCII normalizer in
|
|
// drawChar so the table only needs the upper half.
|
|
//
|
|
// Hand-pixeled to be readable rather than authentic. Each row is
|
|
// commented with the visual pattern (X = on, . = off).
|
|
static const uint8_t kFont[GLYPH_COUNT][GLYPH_H] = {
|
|
/* 0x20 ' ' */ {0,0,0,0,0,0,0,0},
|
|
/* 0x21 '!' */ {0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00},
|
|
/* 0x22 '"' */ {0x6C,0x6C,0x48,0x00,0x00,0x00,0x00,0x00},
|
|
/* 0x23 '#' */ {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00},
|
|
/* 0x24 '$' */ {0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00},
|
|
/* 0x25 '%' */ {0x66,0x66,0x0C,0x18,0x30,0x66,0x66,0x00},
|
|
/* 0x26 '&' */ {0x38,0x6C,0x38,0x76,0xDC,0xCC,0x76,0x00},
|
|
/* 0x27 '\''*/ {0x18,0x18,0x10,0x00,0x00,0x00,0x00,0x00},
|
|
/* 0x28 '(' */ {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00},
|
|
/* 0x29 ')' */ {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00},
|
|
/* 0x2A '*' */ {0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00},
|
|
/* 0x2B '+' */ {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00},
|
|
/* 0x2C ',' */ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30},
|
|
/* 0x2D '-' */ {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00},
|
|
/* 0x2E '.' */ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00},
|
|
/* 0x2F '/' */ {0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00},
|
|
/* 0x30 '0' */ {0x3C,0x66,0x6E,0x76,0x66,0x66,0x3C,0x00},
|
|
/* 0x31 '1' */ {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00},
|
|
/* 0x32 '2' */ {0x3C,0x66,0x06,0x1C,0x30,0x66,0x7E,0x00},
|
|
/* 0x33 '3' */ {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00},
|
|
/* 0x34 '4' */ {0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00},
|
|
/* 0x35 '5' */ {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00},
|
|
/* 0x36 '6' */ {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00},
|
|
/* 0x37 '7' */ {0x7E,0x66,0x0C,0x18,0x18,0x18,0x18,0x00},
|
|
/* 0x38 '8' */ {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00},
|
|
/* 0x39 '9' */ {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00},
|
|
/* 0x3A ':' */ {0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00},
|
|
/* 0x3B ';' */ {0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30},
|
|
/* 0x3C '<' */ {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00},
|
|
/* 0x3D '=' */ {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00},
|
|
/* 0x3E '>' */ {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00},
|
|
/* 0x3F '?' */ {0x3C,0x66,0x06,0x0C,0x18,0x00,0x18,0x00},
|
|
/* 0x40 '@' */ {0x3C,0x66,0x6E,0x6E,0x60,0x62,0x3C,0x00},
|
|
/* 0x41 'A' */ {0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x00},
|
|
/* 0x42 'B' */ {0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00},
|
|
/* 0x43 'C' */ {0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00},
|
|
/* 0x44 'D' */ {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00},
|
|
/* 0x45 'E' */ {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00},
|
|
/* 0x46 'F' */ {0x7E,0x60,0x60,0x78,0x60,0x60,0x60,0x00},
|
|
/* 0x47 'G' */ {0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00},
|
|
/* 0x48 'H' */ {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00},
|
|
/* 0x49 'I' */ {0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00},
|
|
/* 0x4A 'J' */ {0x1E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00},
|
|
/* 0x4B 'K' */ {0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00},
|
|
/* 0x4C 'L' */ {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00},
|
|
/* 0x4D 'M' */ {0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x00},
|
|
/* 0x4E 'N' */ {0x66,0x76,0x7E,0x7E,0x6E,0x66,0x66,0x00},
|
|
/* 0x4F 'O' */ {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00},
|
|
/* 0x50 'P' */ {0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00},
|
|
/* 0x51 'Q' */ {0x3C,0x66,0x66,0x66,0x6A,0x6C,0x36,0x00},
|
|
/* 0x52 'R' */ {0x7C,0x66,0x66,0x7C,0x6C,0x66,0x66,0x00},
|
|
/* 0x53 'S' */ {0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00},
|
|
/* 0x54 'T' */ {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00},
|
|
/* 0x55 'U' */ {0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00},
|
|
/* 0x56 'V' */ {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00},
|
|
/* 0x57 'W' */ {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00},
|
|
/* 0x58 'X' */ {0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00},
|
|
/* 0x59 'Y' */ {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00},
|
|
/* 0x5A 'Z' */ {0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00},
|
|
/* 0x5B '[' */ {0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00},
|
|
/* 0x5C '\\'*/ {0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00},
|
|
/* 0x5D ']' */ {0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00},
|
|
/* 0x5E '^' */ {0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00},
|
|
/* 0x5F '_' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF},
|
|
/* 0x60 '`' */ {0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00},
|
|
/* 0x61 'a' */ {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00},
|
|
/* 0x62 'b' */ {0x60,0x60,0x7C,0x66,0x66,0x66,0x7C,0x00},
|
|
/* 0x63 'c' */ {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00},
|
|
/* 0x64 'd' */ {0x06,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00},
|
|
/* 0x65 'e' */ {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00},
|
|
/* 0x66 'f' */ {0x1C,0x36,0x30,0x78,0x30,0x30,0x30,0x00},
|
|
/* 0x67 'g' */ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x3C},
|
|
/* 0x68 'h' */ {0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x00},
|
|
/* 0x69 'i' */ {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00},
|
|
/* 0x6A 'j' */ {0x06,0x00,0x06,0x06,0x06,0x06,0x66,0x3C},
|
|
/* 0x6B 'k' */ {0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00},
|
|
/* 0x6C 'l' */ {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00},
|
|
/* 0x6D 'm' */ {0x00,0x00,0x66,0x7F,0x7F,0x6B,0x63,0x00},
|
|
/* 0x6E 'n' */ {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00},
|
|
/* 0x6F 'o' */ {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00},
|
|
/* 0x70 'p' */ {0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60},
|
|
/* 0x71 'q' */ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x06},
|
|
/* 0x72 'r' */ {0x00,0x00,0x7C,0x66,0x60,0x60,0x60,0x00},
|
|
/* 0x73 's' */ {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00},
|
|
/* 0x74 't' */ {0x18,0x18,0x7E,0x18,0x18,0x18,0x0E,0x00},
|
|
/* 0x75 'u' */ {0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00},
|
|
/* 0x76 'v' */ {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00},
|
|
/* 0x77 'w' */ {0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00},
|
|
/* 0x78 'x' */ {0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00},
|
|
/* 0x79 'y' */ {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x3C},
|
|
/* 0x7A 'z' */ {0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00},
|
|
/* 0x7B '{' */ {0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00},
|
|
/* 0x7C '|' */ {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00},
|
|
/* 0x7D '}' */ {0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00},
|
|
/* 0x7E '~' */ {0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00}
|
|
};
|
|
|
|
|
|
// ----- Internal helpers (alphabetical) -----
|
|
|
|
static void drawChar(jlSurfaceT *stage, int16_t x, int16_t y, char ch, uint8_t fg, uint8_t bg) {
|
|
const uint8_t *glyph;
|
|
uint8_t row;
|
|
uint8_t col;
|
|
uint8_t bits;
|
|
uint8_t c;
|
|
uint8_t *stagePixels;
|
|
|
|
c = (uint8_t)ch;
|
|
if (c < FIRST_CHAR || c > LAST_CHAR) {
|
|
c = FIRST_CHAR; // unknown -> space
|
|
}
|
|
glyph = kFont[c - FIRST_CHAR];
|
|
|
|
// Chunky fast path: each glyph row is 8 bits == 4 stage bytes
|
|
// (each byte = 2 pixels in 4bpp packed). Build each byte by
|
|
// checking 2 bits at a time. Bypasses jlDrawPixel's per-pixel
|
|
// overhead which dominates text rendering on slow targets.
|
|
stagePixels = (stage != NULL) ? stage->pixels : NULL;
|
|
if (stagePixels != NULL && x >= 0 && y >= 0 &&
|
|
x + (int16_t)GLYPH_W <= (int16_t)SURFACE_WIDTH &&
|
|
y + (int16_t)GLYPH_H <= (int16_t)SURFACE_HEIGHT &&
|
|
(x & 1) == 0) {
|
|
uint16_t rowOff;
|
|
uint16_t byteX;
|
|
uint8_t bytePair;
|
|
uint8_t left;
|
|
uint8_t right;
|
|
|
|
byteX = (uint16_t)(x >> 1);
|
|
for (row = 0u; row < GLYPH_H; row++) {
|
|
bits = glyph[row];
|
|
rowOff = (uint16_t)((y + row) * SURFACE_BYTES_PER_ROW + byteX);
|
|
for (col = 0u; col < GLYPH_W; col += 2u) {
|
|
left = (uint8_t)((bits & (uint8_t)(0x80u >> col)) ? fg : bg);
|
|
right = (uint8_t)((bits & (uint8_t)(0x80u >> (col + 1u))) ? fg : bg);
|
|
bytePair = (uint8_t)((left << 4) | right);
|
|
stagePixels[rowOff + (col >> 1)] = bytePair;
|
|
}
|
|
}
|
|
surfaceMarkDirtyRect(stage, x, y, (int16_t)GLYPH_W, (int16_t)GLYPH_H);
|
|
return;
|
|
}
|
|
|
|
// Planar / out-of-bounds slow fallback.
|
|
for (row = 0u; row < GLYPH_H; row++) {
|
|
bits = glyph[row];
|
|
for (col = 0u; col < GLYPH_W; col++) {
|
|
uint8_t pixel;
|
|
|
|
pixel = ((bits >> (uint8_t)(7u - col)) & 0x01u) ? fg : bg;
|
|
jlDrawPixel(stage, (int16_t)(x + (int16_t)col), (int16_t)(y + (int16_t)row), pixel);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Draw a char with transparent background: only foreground bits are
|
|
// written, the surface pixels underneath show through. Chunky fast
|
|
// path reads each stage byte, modifies set nibbles, writes back.
|
|
static void drawCharTransparent(jlSurfaceT *stage, int16_t x, int16_t y, char ch, uint8_t fg) {
|
|
const uint8_t *glyph;
|
|
uint8_t row;
|
|
uint8_t col;
|
|
uint8_t bits;
|
|
uint8_t c;
|
|
uint8_t *stagePixels;
|
|
|
|
c = (uint8_t)ch;
|
|
if (c < FIRST_CHAR || c > LAST_CHAR) {
|
|
return; // unknown -> nothing to draw (transparent)
|
|
}
|
|
glyph = kFont[c - FIRST_CHAR];
|
|
stagePixels = (stage != NULL) ? stage->pixels : NULL;
|
|
if (stagePixels != NULL && x >= 0 && y >= 0 &&
|
|
x + (int16_t)GLYPH_W <= (int16_t)SURFACE_WIDTH &&
|
|
y + (int16_t)GLYPH_H <= (int16_t)SURFACE_HEIGHT &&
|
|
(x & 1) == 0) {
|
|
uint16_t rowOff;
|
|
uint16_t byteX;
|
|
uint8_t existing;
|
|
uint8_t leftMask;
|
|
uint8_t rightMask;
|
|
uint8_t nibbleFg;
|
|
|
|
byteX = (uint16_t)(x >> 1);
|
|
nibbleFg = (uint8_t)(fg & 0x0Fu);
|
|
for (row = 0u; row < GLYPH_H; row++) {
|
|
bits = glyph[row];
|
|
if (bits == 0u) { continue; } // entire row transparent
|
|
rowOff = (uint16_t)((y + row) * SURFACE_BYTES_PER_ROW + byteX);
|
|
for (col = 0u; col < GLYPH_W; col += 2u) {
|
|
leftMask = (uint8_t)(bits & (uint8_t)(0x80u >> col));
|
|
rightMask = (uint8_t)(bits & (uint8_t)(0x80u >> (col + 1u)));
|
|
if (leftMask == 0u && rightMask == 0u) { continue; }
|
|
existing = stagePixels[rowOff + (col >> 1)];
|
|
if (leftMask) { existing = (uint8_t)((existing & 0x0Fu) | (nibbleFg << 4)); }
|
|
if (rightMask) { existing = (uint8_t)((existing & 0xF0u) | nibbleFg); }
|
|
stagePixels[rowOff + (col >> 1)] = existing;
|
|
}
|
|
}
|
|
surfaceMarkDirtyRect(stage, x, y, (int16_t)GLYPH_W, (int16_t)GLYPH_H);
|
|
return;
|
|
}
|
|
|
|
// Slow fallback (planar or out-of-bounds).
|
|
for (row = 0u; row < GLYPH_H; row++) {
|
|
bits = glyph[row];
|
|
for (col = 0u; col < GLYPH_W; col++) {
|
|
if (bits & (uint8_t)(0x80u >> col)) {
|
|
jlDrawPixel(stage, (int16_t)(x + (int16_t)col), (int16_t)(y + (int16_t)row), fg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void drawString(jlSurfaceT *stage, int16_t x, int16_t y, const char *s, uint8_t fg, uint8_t bg) {
|
|
int16_t cur;
|
|
|
|
if (s == NULL) {
|
|
return;
|
|
}
|
|
cur = x;
|
|
while (*s != '\0') {
|
|
if (cur >= (int16_t)STAGE_W) {
|
|
return;
|
|
}
|
|
drawChar(stage, cur, y, *s, fg, bg);
|
|
cur = (int16_t)(cur + (int16_t)GLYPH_W);
|
|
s++;
|
|
}
|
|
}
|
|
|
|
|
|
static void drawWindow(jlSurfaceT *stage, int16_t x, int16_t y, uint8_t cols, uint8_t rows, uint8_t bg, uint8_t border) {
|
|
int16_t w;
|
|
int16_t h;
|
|
|
|
w = (int16_t)((int16_t)cols * (int16_t)GLYPH_W);
|
|
h = (int16_t)((int16_t)rows * (int16_t)GLYPH_H);
|
|
jlFillRect(stage, x, y, (uint16_t)(w + 4), (uint16_t)(h + 4), border);
|
|
jlFillRect(stage, (int16_t)(x + 2), (int16_t)(y + 2), (uint16_t)w, (uint16_t)h, bg);
|
|
}
|
|
|
|
|
|
static void fillRow(jlSurfaceT *stage, uint8_t row, uint8_t bg) {
|
|
jlFillRect(stage, 0, (int16_t)((int16_t)row * (int16_t)GLYPH_H), (uint16_t)STAGE_W, (uint16_t)GLYPH_H, bg);
|
|
}
|
|
|
|
|
|
// ----- Public API (alphabetical) -----
|
|
|
|
const uint16_t *agiTextAsciiMap(void) {
|
|
// Not used in this impl (we render direct), but expose so callers
|
|
// who want to use jlDrawText against agiTextFontSurface have the
|
|
// standard 16x6 layout map. Built lazily on first call.
|
|
static uint16_t map[256];
|
|
static bool built;
|
|
uint16_t i;
|
|
|
|
if (built) {
|
|
return map;
|
|
}
|
|
for (i = 0u; i < 256u; i++) {
|
|
if (i < FIRST_CHAR || i > LAST_CHAR) {
|
|
map[i] = (uint16_t)0xFFFFu;
|
|
} else {
|
|
uint8_t off;
|
|
|
|
off = (uint8_t)(i - FIRST_CHAR);
|
|
map[i] = (uint16_t)(((uint16_t)(off / 16u) << 8) | (uint16_t)(off % 16u));
|
|
}
|
|
}
|
|
built = true;
|
|
return map;
|
|
}
|
|
|
|
|
|
const jlSurfaceT *agiTextFontSurface(void) {
|
|
// No surface built in this impl (drawChar walks raw bits direct
|
|
// to the stage). Reserved for callers that prefer jlDrawText.
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void agiTextRender(const AgiVmT *vm, jlSurfaceT *stage) {
|
|
uint8_t r;
|
|
|
|
if (vm->statusLineOn) {
|
|
fillRow(stage, vm->statusLineRow, 15u); // white background
|
|
if (vm->textRows[vm->statusLineRow].text[0] != '\0') {
|
|
drawString(stage, 0, (int16_t)((int16_t)vm->statusLineRow * (int16_t)GLYPH_H),
|
|
vm->textRows[vm->statusLineRow].text, 0u, 15u);
|
|
}
|
|
}
|
|
// display() rows: walk every row that has content. Glyphs draw
|
|
// transparently (no bg fill) so any PIC art behind shows through
|
|
// where glyph pixels are 0. Filling the whole row's bg would
|
|
// obscure picture elements that overlap the text band (e.g.
|
|
// KQ3's "King's Quest III" title art).
|
|
for (r = 0u; r < AGI_TEXT_ROWS; r++) {
|
|
const char *p;
|
|
int16_t x;
|
|
int16_t y;
|
|
uint8_t fg;
|
|
|
|
if (r == vm->statusLineRow && vm->statusLineOn) {
|
|
continue; // status line handled above
|
|
}
|
|
if (vm->textRows[r].text[0] == '\0') {
|
|
continue;
|
|
}
|
|
y = (int16_t)((int16_t)r * (int16_t)GLYPH_H);
|
|
x = 0;
|
|
fg = vm->textRows[r].fg;
|
|
p = vm->textRows[r].text;
|
|
while (*p != '\0' && x < (int16_t)STAGE_W) {
|
|
if (*p != ' ') {
|
|
drawCharTransparent(stage, x, y, *p, fg);
|
|
}
|
|
x = (int16_t)(x + (int16_t)GLYPH_W);
|
|
p++;
|
|
}
|
|
}
|
|
|
|
if (vm->printModal.active) {
|
|
const char *p;
|
|
int16_t lineW;
|
|
int16_t maxW;
|
|
int16_t lineCount;
|
|
int16_t winX;
|
|
int16_t winY;
|
|
int16_t lineH;
|
|
int16_t textY;
|
|
int16_t curX;
|
|
|
|
// Measure: width = longest line in chars; height = lines.
|
|
lineW = 0;
|
|
maxW = 1;
|
|
lineCount = 1;
|
|
p = vm->printModal.message;
|
|
while (*p != '\0') {
|
|
if (*p == '\n') {
|
|
if (lineW > maxW) {
|
|
maxW = lineW;
|
|
}
|
|
lineW = 0;
|
|
lineCount++;
|
|
} else {
|
|
lineW++;
|
|
}
|
|
p++;
|
|
}
|
|
if (lineW > maxW) {
|
|
maxW = lineW;
|
|
}
|
|
if (maxW > (int16_t)AGI_TEXT_COLS) {
|
|
maxW = (int16_t)AGI_TEXT_COLS;
|
|
}
|
|
lineH = (int16_t)GLYPH_H;
|
|
winX = (int16_t)((STAGE_W - (maxW * (int16_t)GLYPH_W + 4)) / 2);
|
|
winY = (int16_t)((STAGE_H - (lineCount * lineH + 4)) / 2);
|
|
if (winX < 0) { winX = 0; }
|
|
if (winY < 0) { winY = 0; }
|
|
drawWindow(stage, winX, winY, (uint8_t)maxW, (uint8_t)lineCount, 15u, 4u);
|
|
|
|
textY = (int16_t)(winY + 2);
|
|
curX = (int16_t)(winX + 2);
|
|
p = vm->printModal.message;
|
|
while (*p != '\0') {
|
|
if (*p == '\n') {
|
|
textY = (int16_t)(textY + lineH);
|
|
curX = (int16_t)(winX + 2);
|
|
} else {
|
|
drawChar(stage, curX, textY, *p, 0u, 15u);
|
|
curX = (int16_t)(curX + (int16_t)GLYPH_W);
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
}
|