275 lines
9.7 KiB
C
275 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.
|
|
//
|
|
// 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.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;
|
|
}
|