/* Standalone regression for the diagnostic mesh overlay (src/mesh_overlay.c). * It links only the overlay module, so it needs neither a catalog, ray tracing, * FFTW, nor an output writer. Every assertion targets observable behavior: * terminal-category colors, half-edge switching, AA coverage, deduplication, * deterministic ordering, clipping safety and clean failure on bad input. */ #include "mesh_overlay.h" #include #include #include #include #include #include #include static int fail(const char *message) { fprintf(stderr, "%s\n", message); return -1; } static LensVertex make_vertex(double x, double y, int traced, RayOutcome outcome) { LensVertex vertex; memset(&vertex, 0, sizeof vertex); vertex.image_x = x; vertex.image_y = y; vertex.traced = traced; vertex.outcome = outcome; return vertex; } static int channel_at(const unsigned char *rgb, int width, int x, int y, int channel) { return rgb[3 * ((size_t)y * (size_t)width + (size_t)x) + (size_t)channel]; } static int test_default_settings(void) { const MeshOverlaySettings settings = mesh_overlay_default_settings(); static const unsigned char expected[MESH_OVERLAY_CATEGORY_COUNT][3] = { {0x7F, 0x84, 0x9C}, {0xCB, 0xA6, 0xF7}, {0xF9, 0xE2, 0xAF}, {0xF3, 0x8B, 0xA8}, {0x89, 0xB4, 0xFA}}; if (settings.opacity != 0.5) return fail("default opacity is not 0.5"); for (int category = 0; category < MESH_OVERLAY_CATEGORY_COUNT; ++category) for (int channel = 0; channel < 3; ++channel) if (settings.colors[category][channel] != expected[category][channel]) return fail("default palette mismatch"); return 0; } static int test_parse_color(void) { unsigned char rgb[3] = {1, 2, 3}; if (mesh_overlay_parse_color("#7F849C", rgb) != 0 || rgb[0] != 0x7F || rgb[1] != 0x84 || rgb[2] != 0x9C) return fail("parse uppercase failed"); if (mesh_overlay_parse_color("#7f849c", rgb) != 0 || rgb[0] != 0x7F || rgb[1] != 0x84 || rgb[2] != 0x9C) return fail("parse lowercase failed"); if (mesh_overlay_parse_color("#000000", rgb) != 0 || rgb[0] || rgb[1] || rgb[2]) return fail("parse black failed"); static const char *const bad[] = {"", "#", "7F849C", "#7F849", "#7F849C0", "#GG849C", "#7F84 9C", "#7F849c ", " #7F849C", "#12345g", "#12345G0"}; for (size_t i = 0; i < sizeof bad / sizeof *bad; ++i) if (mesh_overlay_parse_color(bad[i], rgb) != -1) return fail("accepted an invalid color string"); if (mesh_overlay_parse_color(NULL, rgb) != -1) return fail("accepted NULL text"); if (mesh_overlay_parse_color("#7F849C", NULL) != -1) return fail("accepted NULL output"); return 0; } /* Deduplication, canonical ordering and per-vertex category assignment. */ static int test_prepare_dedup_categories(void) { LensVertex vertices[4] = { make_vertex(10, 10, 1, RAY_OUTCOME_ESCAPED), make_vertex(30, 10, 1, RAY_OUTCOME_DARK), make_vertex(10, 30, 1, RAY_OUTCOME_UNRESOLVED), make_vertex(30, 30, 1, RAY_OUTCOME_INCOMPLETE)}; LensTriangle triangles[2] = {{{0, 1, 2}, 0, 0, 0}, {{0, 2, 3}, 0, 0, 0}}; FrameLensMesh mesh = {.vertices = vertices, .vertex_count = 4, .triangles = triangles, .triangle_count = 2}; MeshOverlayLines lines = {0}; if (mesh_overlay_prepare(&mesh, &lines) != 0) return fail("prepare failed on a valid quad"); /* The shared diagonal (0,2) must appear exactly once. */ if (lines.count != 5) { mesh_overlay_lines_destroy(&lines); return fail("unique edge count is not 5"); } static const struct { unsigned char c0, c1; } expected[5] = { {MESH_OVERLAY_CATEGORY_ESCAPE, MESH_OVERLAY_CATEGORY_DARK}, {MESH_OVERLAY_CATEGORY_ESCAPE, MESH_OVERLAY_CATEGORY_UNRESOLVED}, {MESH_OVERLAY_CATEGORY_ESCAPE, MESH_OVERLAY_CATEGORY_INCOMPLETE}, {MESH_OVERLAY_CATEGORY_DARK, MESH_OVERLAY_CATEGORY_UNRESOLVED}, {MESH_OVERLAY_CATEGORY_UNRESOLVED, MESH_OVERLAY_CATEGORY_INCOMPLETE}}; for (size_t i = 0; i < lines.count; ++i) if (lines.lines[i].category0 != expected[i].c0 || lines.lines[i].category1 != expected[i].c1) { mesh_overlay_lines_destroy(&lines); return fail("edge category or deterministic order mismatch"); } if (lines.lines[0].x0 != 10 || lines.lines[0].y0 != 10 || lines.lines[0].x1 != 30 || lines.lines[0].y1 != 10) { mesh_overlay_lines_destroy(&lines); return fail("edge coordinates mismatch"); } /* Lines must copy coordinates, never alias the mutable mesh. */ vertices[0].image_x = 999; if (lines.lines[0].x0 != 10) { mesh_overlay_lines_destroy(&lines); return fail("overlay lines alias the live mesh"); } mesh_overlay_lines_destroy(&lines); if (lines.lines != NULL || lines.count != 0) return fail("destroy did not reset the handle"); mesh_overlay_lines_destroy(NULL); return 0; } static int test_untraced_category(void) { LensVertex vertices[3] = { make_vertex(10, 10, 1, RAY_OUTCOME_ESCAPED), /* traced == 0 must win over the stale outcome value. */ make_vertex(30, 10, 0, RAY_OUTCOME_ESCAPED), make_vertex(10, 30, 1, RAY_OUTCOME_INCOMPLETE)}; LensTriangle triangle = {{0, 1, 2}, 0, 0, 0}; FrameLensMesh mesh = {.vertices = vertices, .vertex_count = 3, .triangles = &triangle, .triangle_count = 1}; MeshOverlayLines lines = {0}; if (mesh_overlay_prepare(&mesh, &lines) != 0) return fail("prepare failed for untraced mesh"); int saw_untraced = 0; for (size_t i = 0; i < lines.count; ++i) if (lines.lines[i].category0 == MESH_OVERLAY_CATEGORY_UNTRACED || lines.lines[i].category1 == MESH_OVERLAY_CATEGORY_UNTRACED) saw_untraced = 1; mesh_overlay_lines_destroy(&lines); return saw_untraced ? 0 : fail("untraced vertex category missing"); } /* Boundary edges are emitted regardless of winding, and reversing the winding * cannot change the deterministic output. */ static int test_boundary_and_winding(void) { LensVertex vertices[3] = { make_vertex(10, 10, 1, RAY_OUTCOME_ESCAPED), make_vertex(30, 10, 1, RAY_OUTCOME_DARK), make_vertex(10, 30, 1, RAY_OUTCOME_UNRESOLVED)}; LensTriangle forward[1] = {{{0, 1, 2}, 0, 0, 0}}; LensTriangle reversed[1] = {{{2, 1, 0}, 0, 0, 0}}; FrameLensMesh mesh_a = {.vertices = vertices, .vertex_count = 3, .triangles = forward, .triangle_count = 1}; FrameLensMesh mesh_b = {.vertices = vertices, .vertex_count = 3, .triangles = reversed, .triangle_count = 1}; MeshOverlayLines a = {0}, b = {0}; if (mesh_overlay_prepare(&mesh_a, &a) != 0 || mesh_overlay_prepare(&mesh_b, &b) != 0) { mesh_overlay_lines_destroy(&a); mesh_overlay_lines_destroy(&b); return fail("prepare failed for single triangle"); } int ok = a.count == 3 && b.count == 3 && memcmp(a.lines, b.lines, a.count * sizeof *a.lines) == 0; mesh_overlay_lines_destroy(&a); mesh_overlay_lines_destroy(&b); return ok ? 0 : fail("boundary/winding determinism failed"); } /* Off-mesh probe witnesses have no triangle edge and must never be emitted or * drawn as a vertex dot. */ static int test_isolated_witness_not_drawn(void) { LensVertex vertices[4] = { make_vertex(10, 50, 1, RAY_OUTCOME_ESCAPED), make_vertex(30, 50, 1, RAY_OUTCOME_ESCAPED), make_vertex(20, 30, 1, RAY_OUTCOME_ESCAPED), make_vertex(90, 90, 1, RAY_OUTCOME_ESCAPED)}; vertices[3].diagnostic_probe = 1; vertices[3].probe_edge[0] = 0; vertices[3].probe_edge[1] = 1; LensTriangle triangle = {{0, 1, 2}, 0, 0, 0}; FrameLensMesh mesh = {.vertices = vertices, .vertex_count = 4, .triangles = &triangle, .triangle_count = 1}; const int width = 100, height = 100; MeshOverlayLines lines = {0}; unsigned char *rgb = calloc((size_t)width * height * 3, 1); const MeshOverlaySettings settings = mesh_overlay_default_settings(); if (rgb == NULL || mesh_overlay_prepare(&mesh, &lines) != 0) { free(rgb); mesh_overlay_lines_destroy(&lines); return fail("prepare failed for witness mesh"); } if (lines.count != 3) { free(rgb); mesh_overlay_lines_destroy(&lines); return fail("isolated witness added an edge"); } if (mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) != 0) { free(rgb); mesh_overlay_lines_destroy(&lines); return fail("draw failed for witness mesh"); } const int witness_painted = channel_at(rgb, width, 90, 90, 0) != 0 || channel_at(rgb, width, 90, 90, 1) != 0 || channel_at(rgb, width, 90, 90, 2) != 0; free(rgb); mesh_overlay_lines_destroy(&lines); return witness_painted ? fail("isolated witness vertex was drawn as a dot") : 0; } static int test_draw_category_colors(void) { MeshOverlaySettings settings = mesh_overlay_default_settings(); settings.opacity = 1.0; const int width = 80, height = 40; for (int category = 0; category < MESH_OVERLAY_CATEGORY_COUNT; ++category) { unsigned char *rgb = calloc((size_t)width * height * 3, 1); if (rgb == NULL) return fail("allocation failed"); MeshOverlayLine line = {.x0 = 10, .y0 = 20, .x1 = 70, .y1 = 20, .category0 = (unsigned char)category, .category1 = (unsigned char)category}; MeshOverlayLines lines = {.lines = &line, .count = 1}; int ok = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == 0; for (int channel = 0; ok && channel < 3; ++channel) if (channel_at(rgb, width, 40, 20, channel) != settings.colors[category][channel]) ok = 0; free(rgb); if (!ok) return fail("category color mismatch"); } return 0; } static int test_halves_and_switch(void) { MeshOverlaySettings settings = mesh_overlay_default_settings(); settings.opacity = 1.0; const int width = 60, height = 40; unsigned char *rgb = calloc((size_t)width * height * 3, 1); if (rgb == NULL) return fail("allocation failed"); MeshOverlayLine line = {.x0 = 10, .y0 = 20, .x1 = 50, .y1 = 20, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_DARK}; MeshOverlayLines lines = {.lines = &line, .count = 1}; int ok = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == 0; /* The switch sits at the major-axis midpoint x = 30. Interior pixels are * fully covered, so each must equal exactly one endpoint color: the whole * edge is rasterized once, never as two blends that would smear the switch. */ for (int channel = 0; ok && channel < 3; ++channel) { if (channel_at(rgb, width, 11, 20, channel) != settings.colors[MESH_OVERLAY_CATEGORY_ESCAPE][channel] || channel_at(rgb, width, 29, 20, channel) != settings.colors[MESH_OVERLAY_CATEGORY_ESCAPE][channel] || channel_at(rgb, width, 30, 20, channel) != settings.colors[MESH_OVERLAY_CATEGORY_DARK][channel] || channel_at(rgb, width, 49, 20, channel) != settings.colors[MESH_OVERLAY_CATEGORY_DARK][channel]) ok = 0; } free(rgb); return ok ? 0 : fail("half-edge color switch failed"); } static int test_antialiasing(void) { MeshOverlaySettings settings = mesh_overlay_default_settings(); settings.opacity = 1.0; const int width = 60, height = 60; unsigned char *rgb = calloc((size_t)width * height * 3, 1); if (rgb == NULL) return fail("allocation failed"); MeshOverlayLine line = {.x0 = 10, .y0 = 10, .x1 = 50, .y1 = 20, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; MeshOverlayLines lines = {.lines = &line, .count = 1}; int ok = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == 0; const int full = settings.colors[MESH_OVERLAY_CATEGORY_ESCAPE][0]; int partial = 0; for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) { const int value = channel_at(rgb, width, x, y, 0); if (value > 0 && value < full) ++partial; } free(rgb); return ok && partial > 0 ? 0 : fail("no antialiased partial coverage"); } static int test_subpixel_and_zero_length(void) { MeshOverlaySettings settings = mesh_overlay_default_settings(); settings.opacity = 1.0; unsigned char pixels[8 * 8 * 3] = {0}; MeshOverlayLine line = {.x0 = 2.1, .y0 = 3.0, .x1 = 2.4, .y1 = 3.0, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; MeshOverlayLines lines = {.lines = &line, .count = 1}; if (mesh_overlay_draw_rgb8(&lines, pixels, 8, 8, &settings)) return fail("subpixel draw failed"); for (int channel = 0; channel < 3; ++channel) if (channel_at(pixels, 8, 2, 3, channel) != lround((line.x1 - line.x0) * settings.colors[0][channel])) return fail("subpixel edge applied overlapping endpoint blends"); memset(pixels, 0, sizeof pixels); line.x1 = line.x0; if (mesh_overlay_draw_rgb8(&lines, pixels, 8, 8, &settings)) return fail("zero-length draw failed"); for (size_t i = 0; i < sizeof pixels; ++i) if (pixels[i]) return fail("zero-length edge became a vertex dot"); return 0; } static int test_high_white_background(void) { const MeshOverlaySettings settings = mesh_overlay_default_settings(); const int width = 80, height = 40; unsigned char *rgb = malloc((size_t)width * height * 3); if (rgb == NULL) return fail("allocation failed"); memset(rgb, 255, (size_t)width * height * 3); MeshOverlayLine line = {.x0 = 10, .y0 = 20, .x1 = 60, .y1 = 20, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; MeshOverlayLines lines = {.lines = &line, .count = 1}; int ok = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == 0; for (int channel = 0; ok && channel < 3; ++channel) { const long expected = lround(255.0 * (1.0 - settings.opacity) + settings.colors[MESH_OVERLAY_CATEGORY_ESCAPE][channel] * settings.opacity); if (channel_at(rgb, width, 40, 20, channel) != expected) ok = 0; } /* Still clearly visible against white. */ if (ok && channel_at(rgb, width, 40, 20, 0) == 255) ok = 0; free(rgb); return ok ? 0 : fail("overlay not visible on a high-white background"); } static int test_opacity_extremes(void) { const int width = 60, height = 40; MeshOverlayLine line = {.x0 = 10, .y0 = 20, .x1 = 50, .y1 = 20, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; MeshOverlayLines lines = {.lines = &line, .count = 1}; unsigned char *rgb = malloc((size_t)width * height * 3); if (rgb == NULL) return fail("allocation failed"); MeshOverlaySettings settings = mesh_overlay_default_settings(); memset(rgb, 0x33, (size_t)width * height * 3); settings.opacity = 0.0; if (mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) != 0) { free(rgb); return fail("draw failed at opacity 0"); } for (int i = 0; i < width * height * 3; ++i) if (rgb[i] != 0x33) { free(rgb); return fail("opacity 0 changed the image"); } memset(rgb, 0x00, (size_t)width * height * 3); settings.opacity = 1.0; int ok = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == 0; for (int channel = 0; ok && channel < 3; ++channel) if (channel_at(rgb, width, 30, 20, channel) != settings.colors[MESH_OVERLAY_CATEGORY_ESCAPE][channel]) ok = 0; free(rgb); return ok ? 0 : fail("opacity 1 did not apply the full color"); } static int test_clipping_and_huge_coordinates(void) { const MeshOverlaySettings settings = mesh_overlay_default_settings(); const int width = 64, height = 64; unsigned char *rgb = calloc((size_t)width * height * 3, 1); if (rgb == NULL) return fail("allocation failed"); /* A horizontal line far beyond both image edges must still paint row 30 and * terminate in bounded time. */ MeshOverlayLine huge = {.x0 = -1e15, .y0 = 30, .x1 = 1e15, .y1 = 30, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_DARK}; MeshOverlayLines lines = {.lines = &huge, .count = 1}; int ok = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == 0; int painted = 0; for (int x = 0; x < width; ++x) if (channel_at(rgb, width, x, 30, 0) != 0) painted = 1; if (!ok || !painted) { free(rgb); return fail("huge coordinate line was not clipped into view"); } /* A fully offscreen line leaves the buffer untouched. */ memset(rgb, 0, (size_t)width * height * 3); MeshOverlayLine offscreen = {.x0 = 1000, .y0 = 1000, .x1 = 2000, .y1 = 1000, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; lines.lines = &offscreen; if (mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) != 0) { free(rgb); return fail("offscreen line returned an error"); } for (int i = 0; i < width * height * 3; ++i) if (rgb[i] != 0) { free(rgb); return fail("offscreen line painted the image"); } /* A huge diagonal must not overflow the integer conversions. */ MeshOverlayLine diagonal = {.x0 = -1e12, .y0 = -1e12, .x1 = 1e12, .y1 = 1e12, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_DARK}; lines.lines = &diagonal; if (mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) != 0) { free(rgb); return fail("huge diagonal returned an error"); } free(rgb); return 0; } static int test_extreme_magnitudes(void) { const MeshOverlaySettings settings = mesh_overlay_default_settings(); const int width = 64, height = 64; const size_t bytes = (size_t)width * height * 3; unsigned char *rgb = malloc(bytes); if (rgb == NULL) return fail("allocation failed"); /* -DBL_MAX..+DBL_MAX overflows the endpoint difference to infinity: the whole * batch must be rejected before painting and the buffer left untouched. */ MeshOverlayLine bad = {.x0 = -DBL_MAX, .y0 = 10, .x1 = DBL_MAX, .y1 = 10, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; MeshOverlayLines lines = {.lines = &bad, .count = 1}; memset(rgb, 0x5A, bytes); int rejected = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == -1; int unchanged = 1; for (size_t i = 0; i < bytes; ++i) if (rgb[i] != 0x5A) unchanged = 0; if (!rejected || !unchanged) { free(rgb); return fail("+-DBL_MAX x-delta not rejected unchanged"); } bad.x0 = 10; bad.x1 = 10; bad.y0 = -DBL_MAX; bad.y1 = DBL_MAX; memset(rgb, 0x5A, bytes); rejected = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == -1; unchanged = 1; for (size_t i = 0; i < bytes; ++i) if (rgb[i] != 0x5A) unchanged = 0; if (!rejected || !unchanged) { free(rgb); return fail("+-DBL_MAX y-delta not rejected unchanged"); } /* Same-sign DBL_MAX endpoints have a finite difference and a finite (non * overflowing) midpoint; the segment is entirely offscreen, so it is skipped * safely without painting. */ MeshOverlayLine same = {.x0 = DBL_MAX, .y0 = 10, .x1 = DBL_MAX, .y1 = 30, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_DARK}; lines.lines = &same; memset(rgb, 0x5A, bytes); if (mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) != 0) { free(rgb); return fail("same-sign DBL_MAX segment was not handled safely"); } for (size_t i = 0; i < bytes; ++i) if (rgb[i] != 0x5A) { free(rgb); return fail("offscreen same-sign DBL_MAX segment painted the image"); } free(rgb); return 0; } static int test_clipped_midpoint_uses_original(void) { MeshOverlaySettings settings = mesh_overlay_default_settings(); settings.opacity = 1.0; const int width = 64, height = 40; unsigned char *rgb = calloc((size_t)width * height * 3, 1); if (rgb == NULL) return fail("allocation failed"); /* The original midpoint is -40, so the entire visible span [0, 19] lies in * the second half and every visible pixel must use category1. A midpoint * recomputed from the clipped endpoints would wrongly color the left half. */ MeshOverlayLine line = {.x0 = -100, .y0 = 20, .x1 = 20, .y1 = 20, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_DARK}; MeshOverlayLines lines = {.lines = &line, .count = 1}; int ok = mesh_overlay_draw_rgb8(&lines, rgb, width, height, &settings) == 0; static const int probes[] = {0, 1, 5, 10, 19}; for (size_t p = 0; ok && p < sizeof probes / sizeof *probes; ++p) for (int channel = 0; channel < 3; ++channel) if (channel_at(rgb, width, probes[p], 20, channel) != settings.colors[MESH_OVERLAY_CATEGORY_DARK][channel]) ok = 0; free(rgb); return ok ? 0 : fail("clipped edge did not use the original midpoint category"); } static int test_invalid_arguments(void) { MeshOverlaySettings settings = mesh_overlay_default_settings(); unsigned char pixels[4 * 4 * 3] = {0}; MeshOverlayLines lines = {0}; if (mesh_overlay_prepare(NULL, &lines) != -1) return fail("prepare accepted NULL mesh"); if (mesh_overlay_prepare(NULL, NULL) != -1) return fail("prepare accepted NULL lines"); FrameLensMesh empty = {0}; if (mesh_overlay_prepare(&empty, &lines) != 0 || lines.count != 0 || lines.lines != NULL) return fail("empty mesh did not produce an empty edge set"); LensVertex vertices[3] = { make_vertex(10, 10, 1, RAY_OUTCOME_ESCAPED), make_vertex(30, 10, 1, RAY_OUTCOME_ESCAPED), make_vertex(10, 30, 1, RAY_OUTCOME_ESCAPED)}; LensTriangle triangle = {{0, 1, 2}, 0, 0, 0}; FrameLensMesh mesh = {.vertices = vertices, .vertex_count = 2, /* vertex 2 is out of range */ .triangles = &triangle, .triangle_count = 1}; if (mesh_overlay_prepare(&mesh, &lines) != -1) return fail("prepare accepted an out-of-range vertex"); mesh.vertex_count = 3; vertices[2].image_x = NAN; if (mesh_overlay_prepare(&mesh, &lines) != -1) return fail("prepare accepted a nonfinite coordinate"); vertices[2].image_x = 10; mesh.triangles = NULL; if (mesh_overlay_prepare(&mesh, &lines) != -1) return fail("prepare accepted NULL triangles"); FrameLensMesh overflow = {0}; overflow.triangle_count = SIZE_MAX; /* > SIZE_MAX / 3, rejected before use */ if (mesh_overlay_prepare(&overflow, &lines) != -1) return fail("prepare accepted an overflowing triangle count"); MeshOverlayLine line = {.x0 = 1, .y0 = 1, .x1 = 3, .y1 = 1, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; MeshOverlayLines one = {.lines = &line, .count = 1}; if (mesh_overlay_draw_rgb8(NULL, pixels, 4, 4, &settings) != -1) return fail("draw accepted NULL lines"); if (mesh_overlay_draw_rgb8(&one, NULL, 4, 4, &settings) != -1) return fail("draw accepted NULL pixels"); if (mesh_overlay_draw_rgb8(&one, pixels, 0, 4, &settings) != -1) return fail("draw accepted zero width"); if (mesh_overlay_draw_rgb8(&one, pixels, 4, -1, &settings) != -1) return fail("draw accepted negative height"); if (mesh_overlay_draw_rgb8(&one, pixels, 4, 4, NULL) != -1) return fail("draw accepted NULL settings"); MeshOverlayLines null_lines = {.lines = NULL, .count = 1}; if (mesh_overlay_draw_rgb8(&null_lines, pixels, 4, 4, &settings) != -1) return fail("draw accepted a NULL line array with a nonzero count"); /* Dimensions too near INT_MAX would overflow the raster's y+1/x+1 casts. */ if (mesh_overlay_draw_rgb8(&one, pixels, INT_MAX, 1, &settings) != -1) return fail("draw accepted a width near INT_MAX"); if (mesh_overlay_draw_rgb8(&one, pixels, 1, INT_MAX, &settings) != -1) return fail("draw accepted a height near INT_MAX"); /* An unallocatable line count must be rejected before the array dereference. */ MeshOverlayLines overflow_lines = {.lines = &line, .count = SIZE_MAX}; if (mesh_overlay_draw_rgb8(&overflow_lines, pixels, 4, 4, &settings) != -1) return fail("draw accepted an overflowing line count"); MeshOverlaySettings bad = settings; bad.opacity = NAN; if (mesh_overlay_draw_rgb8(&one, pixels, 4, 4, &bad) != -1) return fail("draw accepted NaN opacity"); bad.opacity = 1.5; if (mesh_overlay_draw_rgb8(&one, pixels, 4, 4, &bad) != -1) return fail("draw accepted opacity above 1"); bad.opacity = -0.1; if (mesh_overlay_draw_rgb8(&one, pixels, 4, 4, &bad) != -1) return fail("draw accepted negative opacity"); MeshOverlayLine bad_category = {.x0 = 1, .y0 = 1, .x1 = 3, .y1 = 1, .category0 = 99, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; MeshOverlayLines bad_lines = {.lines = &bad_category, .count = 1}; if (mesh_overlay_draw_rgb8(&bad_lines, pixels, 4, 4, &settings) != -1) return fail("draw accepted an out-of-range category"); MeshOverlayLine bad_coord = {.x0 = NAN, .y0 = 1, .x1 = 3, .y1 = 1, .category0 = MESH_OVERLAY_CATEGORY_ESCAPE, .category1 = MESH_OVERLAY_CATEGORY_ESCAPE}; bad_lines.lines = &bad_coord; if (mesh_overlay_draw_rgb8(&bad_lines, pixels, 4, 4, &settings) != -1) return fail("draw accepted a nonfinite coordinate"); MeshOverlayLines none = {.lines = NULL, .count = 0}; if (mesh_overlay_draw_rgb8(&none, pixels, 4, 4, &settings) != 0) return fail("draw failed on an empty edge set"); return 0; } static int test_rgba_layer(void) { LensVertex vertices[3] = { make_vertex(2, 4, 1, RAY_OUTCOME_ESCAPED), make_vertex(12, 4, 1, RAY_OUTCOME_ESCAPED), make_vertex(2, 12, 1, RAY_OUTCOME_ESCAPED)}; LensTriangle triangles[2] = {{{0, 1, 2}, 0, 0, 0}, {{2, 1, 0}, 0, 0, 0}}; FrameLensMesh mesh = {.vertices = vertices, .vertex_count = 3, .triangles = triangles, .triangle_count = 1}; MeshOverlaySettings settings = mesh_overlay_default_settings(); settings.opacity = 0.5; MeshOverlayLayer layer = {0}, duplicate = {0}; if (mesh_overlay_build_layer(&mesh, 16, 16, &settings, &layer)) return fail("RGBA layer build failed"); int ok = layer.width == 16 && layer.height == 16; const unsigned char *on_edge = &layer.rgba[4 * (4 * 16 + 5)]; for (int c = 0; c < 3; ++c) ok &= on_edge[c] == lround(settings.colors[0][c] * settings.opacity); ok &= on_edge[3] == 128; for (size_t p = 0; p < 16 * 16; ++p) for (int c = 0; c < 3; ++c) ok &= layer.rgba[4 * p + c] <= layer.rgba[4 * p + 3]; mesh.triangle_count = 2; ok &= mesh_overlay_build_layer(&mesh, 16, 16, &settings, &duplicate) == 0; if (duplicate.rgba != NULL) ok &= memcmp(layer.rgba, duplicate.rgba, 16 * 16 * 4) == 0; unsigned char image[16 * 16 * 3]; memset(image, 255, sizeof image); ok &= mesh_overlay_composite_rgb8(&layer, image, 16, 16) == 0; for (int c = 0; c < 3; ++c) ok &= image[3 * (4 * 16 + 5) + c] == on_edge[c] + 127; ok &= image[0] == 255; /* no overlay coverage, not a full-image gray tint */ mesh_overlay_layer_destroy(&layer); mesh_overlay_layer_destroy(&duplicate); ok &= layer.rgba == NULL && layer.width == 0 && layer.height == 0; settings.opacity = 0.0; ok &= mesh_overlay_build_layer(&mesh, 16, 16, &settings, &layer) == 0; if (layer.rgba != NULL) for (size_t i = 0; i < 16 * 16 * 4; ++i) ok &= layer.rgba[i] == 0; unsigned char before[sizeof image]; memcpy(before, image, sizeof image); ok &= mesh_overlay_composite_rgb8(&layer, image, 16, 16) == 0; ok &= memcmp(before, image, sizeof image) == 0; ok &= mesh_overlay_composite_rgb8(&layer, image, 15, 16) == -1; mesh_overlay_layer_destroy(&layer); ok &= mesh_overlay_build_layer(&mesh, 0, 16, &settings, &layer) == -1; ok &= mesh_overlay_build_layer(&mesh, INT_MAX, INT_MAX, &settings, &layer) == -1; vertices[0].image_x = NAN; ok &= mesh_overlay_build_layer(&mesh, 16, 16, &settings, &layer) == -1; ok &= layer.rgba == NULL; return ok ? 0 : fail("RGBA premultiplication/composition/ownership regression"); } static int test_rgba_composition_extremes(void) { unsigned char rgba[] = {0, 0, 0, 0, 20, 40, 60, 255, 10, 20, 30, 128}; const MeshOverlayLayer layer = {.rgba = rgba, .width = 3, .height = 1}; unsigned char rgb[] = {100, 110, 120, 100, 100, 100, 100, 100, 100}; const unsigned char expected[] = {100, 110, 120, 20, 40, 60, 60, 70, 80}; return mesh_overlay_composite_rgb8(&layer, rgb, 3, 1) == 0 && memcmp(rgb, expected, sizeof rgb) == 0 ? 0 : fail("RGBA transparent/opaque/partial-alpha composition"); } int main(void) { if (test_default_settings() || test_parse_color() || test_prepare_dedup_categories() || test_untraced_category() || test_boundary_and_winding() || test_isolated_witness_not_drawn() || test_draw_category_colors() || test_halves_and_switch() || test_antialiasing() || test_subpixel_and_zero_length() || test_high_white_background() || test_opacity_extremes() || test_clipping_and_huge_coordinates() || test_extreme_magnitudes() || test_clipped_midpoint_uses_original() || test_invalid_arguments() || test_rgba_layer() || test_rgba_composition_extremes()) return 1; return 0; }