// 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 centred 20000 left of x 160: every pixel on // the stage within a pixel of the circle, one on every row // far circle centred 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 #include #include #include #include #include #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; }