joeylib2/examples/draw/draw.c

274 lines
9.8 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: jlDrawPixel + jlDrawLine (8-octant fan from cell center; pixel
// row of all 16 colors along the cell's bottom edge).
// TR: jlDrawRect + jlFillRect (concentric outlines + filled blocks at
// deliberately odd x / odd width to catch nibble-edge bugs).
// BL: jlDrawCircle + jlFillCircle (concentric outlines + a small filled
// disk at center).
// BR: jlTileCopy / jlTileCopyMasked / jlTileSnap+jlTilePaste / jlFloodFill.
//
// 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(jlSurfaceT *screen);
static void drawCellBorder(jlSurfaceT *screen, int16_t cx, int16_t cy);
static void drawAllCellBorders(jlSurfaceT *screen);
static void drawPrimitivesPixelLine(jlSurfaceT *screen);
static void drawPrimitivesRect(jlSurfaceT *screen);
static void drawPrimitivesCircle(jlSurfaceT *screen);
static void drawPrimitivesTileFlood(jlSurfaceT *screen);
static void waitForKey(void);
static void buildPalette(jlSurfaceT *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;
jlPaletteSet(screen, 0, colors);
jlScbSetRange(screen, 0, SURFACE_HEIGHT - 1, 0);
}
static void drawCellBorder(jlSurfaceT *screen, int16_t cx, int16_t cy) {
jlDrawRect(screen, cx, cy, CELL_W, CELL_H, C_BORDER);
}
static void drawAllCellBorders(jlSurfaceT *screen) {
drawCellBorder(screen, 0, 0);
drawCellBorder(screen, CELL_W, 0);
drawCellBorder(screen, 0, CELL_H);
drawCellBorder(screen, CELL_W, CELL_H);
}
// Top-left cell: jlDrawPixel + jlDrawLine.
//
// 8 lines fan out from the cell center (80, 50). Four are diagonal
// (the new ASM Bresenham path) and four are axis-aligned (jlDrawLine
// routes those to jlFillRect). A horizontal row of 14 pixels along the
// cell's bottom verifies jlDrawPixel: 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(jlSurfaceT *screen) {
int16_t cx;
int16_t cy;
int16_t i;
cx = CELL_W / 2; // 80
cy = CELL_H / 2; // 50
jlDrawLine(screen, cx, cy, cx + 70, cy, C_RED); // E (horizontal)
jlDrawLine(screen, cx, cy, cx + 60, cy - 40, C_GREEN); // NE (diagonal)
jlDrawLine(screen, cx, cy, cx, cy - 45, C_BLUE); // N (vertical)
jlDrawLine(screen, cx, cy, cx - 60, cy - 40, C_YELLOW); // NW
jlDrawLine(screen, cx, cy, cx - 70, cy, C_CYAN); // W
jlDrawLine(screen, cx, cy, cx - 60, cy + 40, C_MAGENTA); // SW
jlDrawLine(screen, cx, cy, cx, cy + 45, C_ORANGE); // S
jlDrawLine(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++) {
jlDrawPixel(screen, (int16_t)(10 + i * 10), (int16_t)(CELL_H - 6),
(uint8_t)((i + 1) & 0x0F));
}
}
// Top-right cell: jlDrawRect + jlFillRect.
//
// 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 (jlDrawRect collapses to a
// line via jlFillRect's 1-wide path).
static void drawPrimitivesRect(jlSurfaceT *screen) {
int16_t ox;
ox = CELL_W; // cell origin x
// Outer fill, even-aligned.
jlFillRect(screen, ox + 8, 8, 144, 84, C_RED);
// Inner outline, odd x to test partial-nibble edges.
jlDrawRect(screen, ox + 17, 17, 124, 64, C_YELLOW);
// Odd width fill, odd x.
jlFillRect(screen, ox + 25, 25, 35, 48, C_GREEN);
// 1-pixel vertical bar (degenerate rect through jlFillRect 1-wide path).
jlFillRect(screen, ox + 100, 25, 1, 48, C_BORDER);
// Odd-x odd-w narrow bar to specifically hit hasLeading + hasTrailing
// in halFastFillRect.
jlFillRect(screen, ox + 75, 25, 7, 48, C_CYAN);
}
// Bottom-left cell: jlDrawCircle + jlFillCircle.
//
// Concentric outlines at decreasing radii, alternating colors, plus a
// small filled disk at the center. Center is at the cell midpoint.
static void drawPrimitivesCircle(jlSurfaceT *screen) {
int16_t cx;
int16_t cy;
cx = CELL_W / 2;
cy = CELL_H + CELL_H / 2;
jlDrawCircle(screen, cx, cy, 45, C_BORDER);
jlDrawCircle(screen, cx, cy, 35, C_GREEN);
jlDrawCircle(screen, cx, cy, 25, C_YELLOW);
jlDrawCircle(screen, cx, cy, 15, C_CYAN);
jlFillCircle(screen, cx, cy, 8, C_MAGENTA);
}
// Bottom-right cell: tile + flood fill.
//
// Top portion: a 16x16 colored block, then jlTileSnap one of its 8x8
// quadrants and jlTilePaste the captured tile to a neighbor block;
// also jlTileCopy the same source quadrant to a third location to
// exercise the full surface-to-surface path. Then a jlTileCopyMasked
// 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: jlDrawRect outlines a closed region, jlFloodFillBounded
// fills its interior with a different color, stopping at the outline.
static void drawPrimitivesTileFlood(jlSurfaceT *screen) {
int16_t ox;
int16_t oy;
int16_t bx;
int16_t by;
int16_t i;
int16_t px;
jlTileT snapBuf;
ox = CELL_W; // 160
oy = CELL_H; // 100
// Source 16x16 block at (168, 108): a 4-quadrant pattern.
jlFillRect(screen, ox + 8, oy + 8, 8, 8, C_RED);
jlFillRect(screen, ox + 16, oy + 8, 8, 8, C_GREEN);
jlFillRect(screen, ox + 8, oy + 16, 8, 8, C_BLUE);
jlFillRect(screen, ox + 16, oy + 16, 8, 8, C_YELLOW);
// jlTileSnap the top-left red quadrant (block bx=21, by=13) and
// jlTilePaste it next to the 16x16 source as the 5th quadrant.
bx = (int16_t)((ox + 8) / 8);
by = (int16_t)((oy + 8) / 8);
jlTileSnap(screen, (uint8_t)bx, (uint8_t)by, &snapBuf);
jlTilePaste(screen, (uint8_t)(bx + 4), (uint8_t)by, &snapBuf);
// jlTileCopy from the green quadrant onto a fresh location below.
jlTileCopy(screen, (uint8_t)(bx + 4), (uint8_t)(by + 1),
screen, (uint8_t)(bx + 1), (uint8_t)by);
// jlTileCopyMasked 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.
jlFillRect(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++) {
jlDrawPixel(screen, (int16_t)(ox + 112 + px), (int16_t)(oy + 32 + i),
(uint8_t)((px & 1) ? C_MAGENTA : 0));
}
}
// jlTileCopyMasked: source at block (ox+112)/8 = 34..35, by 16
// -> dst at backdrop block (ox+80)/8 = 30..31, by 16
jlTileCopyMasked(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; jlFloodFillBounded
// from a point inside should fill with C_CYAN, stopping at the
// border.
jlDrawRect(screen, ox + 16, oy + 60, 64, 32, C_BORDER);
jlFloodFillBounded(screen, (int16_t)(ox + 48), (int16_t)(oy + 76),
C_CYAN, C_BORDER);
// Plain jlFloodFill: solid block then re-fill to a new color.
jlFillRect(screen, ox + 96, oy + 60, 48, 32, C_GREEN);
jlFloodFill(screen, (int16_t)(ox + 120), (int16_t)(oy + 76), C_GRAY);
}
static void waitForKey(void) {
jlWaitForAnyKey();
}
int main(void) {
jlConfigT config;
jlSurfaceT *screen;
config.codegenBytes = 8 * 1024;
config.maxSurfaces = 4;
config.audioBytes = 64UL * 1024;
if (!jlInit(&config)) {
fprintf(stderr, "jlInit failed: %s\n", jlLastError());
return 1;
}
screen = jlStageGet();
if (screen == NULL) {
fprintf(stderr, "jlStageGet returned NULL\n");
jlShutdown();
return 1;
}
buildPalette(screen);
jlSurfaceClear(screen, C_BG);
drawAllCellBorders(screen);
drawPrimitivesPixelLine(screen);
drawPrimitivesRect(screen);
drawPrimitivesCircle(screen);
drawPrimitivesTileFlood(screen);
jlStagePresent();
waitForKey();
jlShutdown();
return 0;
}