197 lines
5.8 KiB
C
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;
|
|
}
|