joeylib2/tests/host/drawHost.c
2026-10-19 15:46:47 -05:00

197 lines
5.8 KiB
C

// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
// See LICENSE.md for license information.
// SPDX-License-Identifier: MIT
//
// drawHost.c - Host-cc harness for the shared line and circle fallbacks
// (src/core/draw.c) at the far ends of their coordinates, where 16-bit
// state once overflowed: a line whose ends lie more than 32767 apart never
// finished, and a huge circle plotted pixels far from itself. Each case is
// checked against the true geometry, not against another Bresenham:
// long flat line (-20000,50)-(20000,60) crosses the stage on row 55
// long tall line (10,-20000)-(11,20000) puts one pixel on every row,
// in column 10 or 11
// huge circle r 20000 centered 20000 left of x 160: every pixel on
// the stage within a pixel of the circle, one on every row
// far circle centered near the int16_t limit: nothing on the stage
// An alarm fails a case that does not finish.
//
// Built by scripts/check-draw.sh with -DJOEYLIB_PLATFORM_BLANK.
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "joey/joey.h"
#define INK 7u
#define CASE_SECONDS 10u
#define FLAT_ROW 55
#define TALL_X_LOW 10
#define TALL_X_HIGH 11
#define HUGE_R 20000u
#define HUGE_RIGHT_X 160
#define HUGE_CY 100
#define HUGE_TOLERANCE 1.0
#define FAR_CX 32700
#define FAR_R 100u
static bool check(const char *name, bool ok, const char *detail);
static uint32_t inked(jlSurfaceT *stage);
static bool runFarCircle(jlSurfaceT *stage);
static bool runFlatLine(jlSurfaceT *stage);
static bool runHugeCircle(jlSurfaceT *stage);
static bool runTallLine(jlSurfaceT *stage);
static bool check(const char *name, bool ok, const char *detail) {
printf(" %s: %s%s%s\n", name, ok ? "PASS" : "FAIL", detail[0] != '\0' ? " -- " : "", detail);
return ok;
}
static uint32_t inked(jlSurfaceT *stage) {
uint32_t count;
int16_t x;
int16_t y;
count = 0u;
for (y = 0; y < JL_SURFACE_HEIGHT; y++) {
for (x = 0; x < JL_SURFACE_WIDTH; x++) {
if (jlSamplePixel(stage, x, y) == INK) {
count++;
}
}
}
return count;
}
static bool runFarCircle(jlSurfaceT *stage) {
char detail[80];
jlSurfaceClear(stage, 0u);
alarm(CASE_SECONDS);
jlDrawCircle(stage, FAR_CX, HUGE_CY, FAR_R, INK);
jlFillCircle(stage, FAR_CX, HUGE_CY, FAR_R, INK);
alarm(0);
snprintf(detail, sizeof(detail), "%lu pixels on the stage", (unsigned long)inked(stage));
return check("far circle", inked(stage) == 0u, detail);
}
static bool runFlatLine(jlSurfaceT *stage) {
uint32_t count;
int16_t x;
bool ok;
char detail[80];
jlSurfaceClear(stage, 0u);
alarm(CASE_SECONDS);
jlDrawLine(stage, -20000, 50, 20000, 60, INK);
alarm(0);
ok = true;
for (x = 0; x < JL_SURFACE_WIDTH; x++) {
if (jlSamplePixel(stage, x, FLAT_ROW) != INK) {
ok = false;
}
}
count = inked(stage);
snprintf(detail, sizeof(detail), "%lu pixels, row %d %s", (unsigned long)count, FLAT_ROW, ok ? "whole" : "broken");
return check("long flat line", ok && count == JL_SURFACE_WIDTH, detail);
}
static bool runHugeCircle(jlSurfaceT *stage) {
double cx;
double distance;
double worst;
int16_t x;
int16_t y;
uint16_t rowsInked;
bool rowInked;
char detail[96];
jlSurfaceClear(stage, 0u);
cx = (double)HUGE_RIGHT_X - (double)HUGE_R;
alarm(CASE_SECONDS);
jlDrawCircle(stage, (int16_t)cx, HUGE_CY, HUGE_R, INK);
alarm(0);
worst = 0.0;
rowsInked = 0u;
for (y = 0; y < JL_SURFACE_HEIGHT; y++) {
rowInked = false;
for (x = 0; x < JL_SURFACE_WIDTH; x++) {
if (jlSamplePixel(stage, x, y) != INK) {
continue;
}
rowInked = true;
distance = fabs(hypot((double)x - cx, (double)(y - HUGE_CY)) - (double)HUGE_R);
if (distance > worst) {
worst = distance;
}
}
if (rowInked) {
rowsInked++;
}
}
snprintf(detail, sizeof(detail), "%u rows inked, farthest pixel %.2f from the circle", rowsInked, worst);
return check("huge circle", rowsInked == JL_SURFACE_HEIGHT && worst <= HUGE_TOLERANCE, detail);
}
static bool runTallLine(jlSurfaceT *stage) {
int16_t x;
int16_t y;
uint16_t onRow;
bool ok;
char detail[80];
jlSurfaceClear(stage, 0u);
alarm(CASE_SECONDS);
jlDrawLine(stage, TALL_X_LOW, -20000, TALL_X_HIGH, 20000, INK);
alarm(0);
ok = true;
for (y = 0; y < JL_SURFACE_HEIGHT; y++) {
onRow = 0u;
for (x = 0; x < JL_SURFACE_WIDTH; x++) {
if (jlSamplePixel(stage, x, y) == INK) {
onRow++;
if (x != TALL_X_LOW && x != TALL_X_HIGH) {
ok = false;
}
}
}
if (onRow != 1u) {
ok = false;
}
}
snprintf(detail, sizeof(detail), "%lu pixels", (unsigned long)inked(stage));
return check("long tall line", ok, detail);
}
int main(void) {
jlConfigT config;
jlSurfaceT *stage;
bool ok;
config.codegenBytes = 0u;
config.noAudio = true;
if (!jlInit(&config)) {
printf("drawHost: FAIL -- jlInit: %s\n", jlLastError());
return 1;
}
stage = jlStageGet();
printf("drawHost: line and circle fallbacks at the far ends of their coordinates\n");
ok = true;
ok &= runFlatLine(stage);
ok &= runTallLine(stage);
ok &= runHugeCircle(stage);
ok &= runFarCircle(stage);
jlShutdown();
printf("drawHost: %s\n", ok ? "PASS" : "FAIL");
return ok ? 0 : 1;
}