joeylib2/examples/draw/draw.c

277 lines
9.7 KiB
C

// Drawing primitive smoke test. Lays out a 2x2 grid of cells, each
// exercising one family of primitives. On screen each cell should show
// a clear visual signal that the underlying inner loops (C or ASM)
// produced the expected pixel pattern.
//
// TL: drawPixel + drawLine (8-octant fan from cell center; pixel
// row of all 16 colors along the cell's bottom edge).
// TR: drawRect + fillRect (concentric outlines + filled blocks at
// deliberately odd x / odd width to catch nibble-edge bugs).
// BL: drawCircle + fillCircle (concentric outlines + a small filled
// disk at center).
// BR: tileCopy / tileCopyMasked / tileSnap+tilePaste / floodFill.
//
// Runs in HOST_MODE_TAKEOVER and holds the frame until the user
// presses ESC / RETURN / SPACE.
#include <stdio.h>
#include <joey/joey.h>
#define CELL_W 160
#define CELL_H 100
// Color slots (palette 0). Color 0 is the library-forced black.
#define C_BG 0
#define C_BORDER 1 // white
#define C_RED 2
#define C_GREEN 3
#define C_BLUE 4
#define C_YELLOW 5
#define C_CYAN 6
#define C_MAGENTA 7
#define C_ORANGE 8
#define C_GRAY 9
static void buildPalette(SurfaceT *screen);
static void drawCellBorder(SurfaceT *screen, int16_t cx, int16_t cy);
static void drawAllCellBorders(SurfaceT *screen);
static void drawPrimitivesPixelLine(SurfaceT *screen);
static void drawPrimitivesRect(SurfaceT *screen);
static void drawPrimitivesCircle(SurfaceT *screen);
static void drawPrimitivesTileFlood(SurfaceT *screen);
static void waitForKey(void);
static void buildPalette(SurfaceT *screen) {
uint16_t colors[SURFACE_COLORS_PER_PALETTE];
// 16 distinct $0RGB entries. Index 0 is forced to black anyway.
colors[0] = 0x000;
colors[1] = 0xFFF; // white
colors[2] = 0xF00; // red
colors[3] = 0x0F0; // green
colors[4] = 0x00F; // blue
colors[5] = 0xFF0; // yellow
colors[6] = 0x0FF; // cyan
colors[7] = 0xF0F; // magenta
colors[8] = 0xF80; // orange
colors[9] = 0x888; // mid gray
colors[10] = 0x800;
colors[11] = 0x080;
colors[12] = 0x008;
colors[13] = 0x880;
colors[14] = 0x088;
colors[15] = 0x808;
paletteSet(screen, 0, colors);
scbSetRange(screen, 0, SURFACE_HEIGHT - 1, 0);
}
static void drawCellBorder(SurfaceT *screen, int16_t cx, int16_t cy) {
drawRect(screen, cx, cy, CELL_W, CELL_H, C_BORDER);
}
static void drawAllCellBorders(SurfaceT *screen) {
drawCellBorder(screen, 0, 0);
drawCellBorder(screen, CELL_W, 0);
drawCellBorder(screen, 0, CELL_H);
drawCellBorder(screen, CELL_W, CELL_H);
}
// Top-left cell: drawPixel + drawLine.
//
// 8 lines fan out from the cell center (80, 50). Four are diagonal
// (the new ASM Bresenham path) and four are axis-aligned (drawLine
// routes those to fillRect). A horizontal row of 14 pixels along the
// cell's bottom verifies drawPixel: each pixel uses a different color
// index so the leftmost ones at color 0 are invisible (they are bg)
// and 1..13 progress through the palette.
static void drawPrimitivesPixelLine(SurfaceT *screen) {
int16_t cx;
int16_t cy;
int16_t i;
cx = CELL_W / 2; // 80
cy = CELL_H / 2; // 50
drawLine(screen, cx, cy, cx + 70, cy, C_RED); // E (horizontal)
drawLine(screen, cx, cy, cx + 60, cy - 40, C_GREEN); // NE (diagonal)
drawLine(screen, cx, cy, cx, cy - 45, C_BLUE); // N (vertical)
drawLine(screen, cx, cy, cx - 60, cy - 40, C_YELLOW); // NW
drawLine(screen, cx, cy, cx - 70, cy, C_CYAN); // W
drawLine(screen, cx, cy, cx - 60, cy + 40, C_MAGENTA); // SW
drawLine(screen, cx, cy, cx, cy + 45, C_ORANGE); // S
drawLine(screen, cx, cy, cx + 60, cy + 40, C_GRAY); // SE
// Pixel row: 14 single-pixel writes at consecutive x to exercise
// both odd and even nibble paths.
for (i = 0; i < 14; i++) {
drawPixel(screen, (int16_t)(10 + i * 10), (int16_t)(CELL_H - 6),
(uint8_t)((i + 1) & 0x0F));
}
}
// Top-right cell: drawRect + fillRect.
//
// Four nested rectangles with deliberately odd x/y/w/h to exercise
// the partial-byte (nibble) edge handling in halFastFillRect. The
// outermost is filled, the next outline-only, then filled with odd
// width, then a 1-pixel-wide vertical bar (drawRect collapses to a
// line via fillRect's 1-wide path).
static void drawPrimitivesRect(SurfaceT *screen) {
int16_t ox;
ox = CELL_W; // cell origin x
// Outer fill, even-aligned.
fillRect(screen, ox + 8, 8, 144, 84, C_RED);
// Inner outline, odd x to test partial-nibble edges.
drawRect(screen, ox + 17, 17, 124, 64, C_YELLOW);
// Odd width fill, odd x.
fillRect(screen, ox + 25, 25, 35, 48, C_GREEN);
// 1-pixel vertical bar (degenerate rect through fillRect 1-wide path).
fillRect(screen, ox + 100, 25, 1, 48, C_BORDER);
// Odd-x odd-w narrow bar to specifically hit hasLeading + hasTrailing
// in halFastFillRect.
fillRect(screen, ox + 75, 25, 7, 48, C_CYAN);
}
// Bottom-left cell: drawCircle + fillCircle.
//
// Concentric outlines at decreasing radii, alternating colors, plus a
// small filled disk at the center. Center is at the cell midpoint.
static void drawPrimitivesCircle(SurfaceT *screen) {
int16_t cx;
int16_t cy;
cx = CELL_W / 2;
cy = CELL_H + CELL_H / 2;
drawCircle(screen, cx, cy, 45, C_BORDER);
drawCircle(screen, cx, cy, 35, C_GREEN);
drawCircle(screen, cx, cy, 25, C_YELLOW);
drawCircle(screen, cx, cy, 15, C_CYAN);
fillCircle(screen, cx, cy, 8, C_MAGENTA);
}
// Bottom-right cell: tile + flood fill.
//
// Top portion: a 16x16 colored block, then tileSnap one of its 8x8
// quadrants and tilePaste the captured tile to a neighbor block;
// also tileCopy the same source quadrant to a third location to
// exercise the full surface-to-surface path. Then a tileCopyMasked
// case: paint a 2x1 (16x8) "stripe" containing a transparent color 0
// pattern interleaved with color, paste it over a solid backdrop with
// transparent=0; the backdrop should show through the transparent
// nibbles.
//
// Bottom portion: drawRect outlines a closed region, floodFillBounded
// fills its interior with a different color, stopping at the outline.
static void drawPrimitivesTileFlood(SurfaceT *screen) {
int16_t ox;
int16_t oy;
int16_t bx;
int16_t by;
int16_t i;
int16_t px;
TileT snapBuf;
ox = CELL_W; // 160
oy = CELL_H; // 100
// Source 16x16 block at (168, 108): a 4-quadrant pattern.
fillRect(screen, ox + 8, oy + 8, 8, 8, C_RED);
fillRect(screen, ox + 16, oy + 8, 8, 8, C_GREEN);
fillRect(screen, ox + 8, oy + 16, 8, 8, C_BLUE);
fillRect(screen, ox + 16, oy + 16, 8, 8, C_YELLOW);
// tileSnap the top-left red quadrant (block bx=21, by=13) and
// tilePaste it next to the 16x16 source as the 5th quadrant.
bx = (int16_t)((ox + 8) / 8);
by = (int16_t)((oy + 8) / 8);
tileSnap(screen, (uint8_t)bx, (uint8_t)by, &snapBuf);
tilePaste(screen, (uint8_t)(bx + 4), (uint8_t)by, &snapBuf);
// tileCopy from the green quadrant onto a fresh location below.
tileCopy(screen, (uint8_t)(bx + 4), (uint8_t)(by + 1),
screen, (uint8_t)(bx + 1), (uint8_t)by);
// tileCopyMasked test: build a "transparent" striped pattern at
// (208, 132). The tile's source has color 0 in alternating
// nibbles. Paste it onto a solid orange backdrop so transparent
// nibbles let the orange show through.
fillRect(screen, ox + 80, oy + 32, 16, 8, C_ORANGE); // backdrop
// Build a vertical-stripe source at (240, 132): col-pixel = (px % 2 ? color : 0)
for (i = 0; i < 8; i++) {
for (px = 0; px < 16; px++) {
drawPixel(screen, (int16_t)(ox + 112 + px), (int16_t)(oy + 32 + i),
(uint8_t)((px & 1) ? C_MAGENTA : 0));
}
}
// tileCopyMasked: source at block (ox+112)/8 = 34..35, by 16
// -> dst at backdrop block (ox+80)/8 = 30..31, by 16
tileCopyMasked(screen, (uint8_t)((ox + 80) / 8), (uint8_t)((oy + 32) / 8),
screen, (uint8_t)((ox + 112) / 8), (uint8_t)((oy + 32) / 8),
0);
// Flood-fill region: a small bordered rectangle in the cell's
// lower portion. Outline drawn in C_BORDER; floodFillBounded
// from a point inside should fill with C_CYAN, stopping at the
// border.
drawRect(screen, ox + 16, oy + 60, 64, 32, C_BORDER);
floodFillBounded(screen, (int16_t)(ox + 48), (int16_t)(oy + 76),
C_CYAN, C_BORDER);
// Plain floodFill: solid block then re-fill to a new color.
fillRect(screen, ox + 96, oy + 60, 48, 32, C_GREEN);
floodFill(screen, (int16_t)(ox + 120), (int16_t)(oy + 76), C_GRAY);
}
static void waitForKey(void) {
joeyWaitForAnyKey();
}
int main(void) {
JoeyConfigT config;
SurfaceT *screen;
config.hostMode = HOST_MODE_TAKEOVER;
config.codegenBytes = 8 * 1024;
config.maxSurfaces = 4;
config.audioBytes = 64UL * 1024;
config.assetBytes = 128UL * 1024;
if (!joeyInit(&config)) {
fprintf(stderr, "joeyInit failed: %s\n", joeyLastError());
return 1;
}
screen = stageGet();
if (screen == NULL) {
fprintf(stderr, "stageGet returned NULL\n");
joeyShutdown();
return 1;
}
buildPalette(screen);
surfaceClear(screen, C_BG);
drawAllCellBorders(screen);
drawPrimitivesPixelLine(screen);
drawPrimitivesRect(screen);
drawPrimitivesCircle(screen);
drawPrimitivesTileFlood(screen);
stagePresent();
waitForKey();
joeyShutdown();
return 0;
}