Files
daedalus-fourier/tests/test_intra_pred_16x16.c
T
claude-noether cb3aef3dac h264: promote remaining intra prediction modes (17) to public API
Follows PR #26 (Intra_4x4 luma) with the same promotion pattern for
the rest of the intra prediction primitive set:

  Intra_16x16 luma   (4 modes, PR #13) — V/H/DC/Plane
  Intra_8x8  chroma  (4 modes, PR #14) — DC/H/V/Plane (4:2:0)
  Intra_8x8  luma    (9 modes, PRs #21 + #22) — High profile,
                                                 with 1-2-1 pre-filter

3 file moves via `git mv`, ~17 function renames stripping the `_ref`
suffix.  Test binaries rewired to link daedalus_core instead of
compiling the (now moved) ref files directly.  No code change — pure
plumbing for substitution-arc consumers.

26 intra prediction modes total now in the public API after this PR.

Verified on hertz:

  test_intra_pred_16x16:    5/5  PASS
  test_intra_pred_chroma8x8: 5/5  PASS
  test_intra_pred_8x8_luma: 11/11 PASS

All via public symbols (test binaries linked against daedalus_core).

Unblocks marfrit-packages substitution arc patch 0014 — wires
H264PredContext.pred4x4[], pred16x16[], pred8x8[], pred8x8l[]
through daedalus alongside the existing IDCT / deblock / qpel / DC
Hadamard substitutions.

After 0014 lands, the libavcodec.so built by marfrit-packages will
have EVERY hot-path pixel-math kernel of an H.264 8-bit 4:2:0
decode routing through daedalus — the substitution arc is feature-
complete for the campaign target (Pi 5 Firefox YouTube playback).
2026-05-25 15:37:44 +02:00

168 lines
6.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Tests the 4 H.264 Intra_16x16 luma prediction modes against
* spec-derived expected patterns. Same layout as the 4x4 test:
* a buffer that holds the 16x16 output plus 1-pixel top/left
* context and 1-pixel top-left corner.
*
* row 0: [tl][t0..t15]
* row 1: [l0][output row 0]
* row 2: [l1][output row 1]
* ...
* row 16: [l15][output row 15]
*
* Buffer dimensions: 17 rows × 17 cols, total 289 bytes.
* dst (passed to the pred fns) points at row 1 col 1.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
extern void daedalus_h264_pred_16x16_vertical(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_16x16_horizontal(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_16x16_dc(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_16x16_plane(uint8_t *dst, ptrdiff_t stride);
#define STRIDE 17
#define ROWS 17
static void set_ctx(uint8_t buf[ROWS][STRIDE], int tl,
const int t[16], const int l[16])
{
for (int r = 0; r < ROWS; r++)
for (int c = 0; c < STRIDE; c++) buf[r][c] = 0xff;
buf[0][0] = (uint8_t) tl;
for (int c = 0; c < 16; c++) buf[0][1 + c] = (uint8_t) t[c];
for (int r = 0; r < 16; r++) buf[1 + r][0] = (uint8_t) l[r];
}
static int check(const uint8_t buf[ROWS][STRIDE], const char *name,
uint8_t (*expect_at)(int r, int c, void *), void *cookie)
{
int diff = 0;
int first_r = 0, first_c = 0, first_got = 0, first_exp = 0;
for (int r = 0; r < 16; r++) {
for (int c = 0; c < 16; c++) {
uint8_t got = buf[1 + r][1 + c];
uint8_t exp = expect_at(r, c, cookie);
if (got != exp) {
if (diff == 0) {
first_r = r; first_c = c;
first_got = got; first_exp = exp;
}
diff++;
}
}
}
if (diff == 0)
printf(" %-30s PASS\n", name);
else
printf(" %-30s FAIL (%d/256 wrong, first r=%d c=%d got=%u exp=%u)\n",
name, diff, first_r, first_c, first_got, first_exp);
return diff == 0 ? 0 : 1;
}
/* Expectation helpers for each mode. */
static uint8_t expect_uniform(int r, int c, void *cookie)
{ (void)r; (void)c; return *(uint8_t *)cookie; }
struct vertical_ctx { const int *t; };
static uint8_t expect_vertical(int r, int c, void *cookie)
{ (void)r; return (uint8_t) ((struct vertical_ctx *)cookie)->t[c]; }
struct horizontal_ctx { const int *l; };
static uint8_t expect_horizontal(int r, int c, void *cookie)
{ (void)c; return (uint8_t) ((struct horizontal_ctx *)cookie)->l[r]; }
int main(void)
{
int fail = 0;
/* --- Mode 0 Vertical: each col = top[col] --- */
{
uint8_t buf[ROWS][STRIDE];
int t[16], l[16];
for (int i = 0; i < 16; i++) { t[i] = 10 + i; l[i] = 0; }
set_ctx(buf, 0, t, l);
daedalus_h264_pred_16x16_vertical(&buf[1][1], STRIDE);
struct vertical_ctx vc = { t };
fail |= check(buf, "Vertical (mode 0)", expect_vertical, &vc);
}
/* --- Mode 1 Horizontal: each row = left[row] --- */
{
uint8_t buf[ROWS][STRIDE];
int t[16] = {0}, l[16];
for (int i = 0; i < 16; i++) l[i] = 50 + i;
set_ctx(buf, 0, t, l);
daedalus_h264_pred_16x16_horizontal(&buf[1][1], STRIDE);
struct horizontal_ctx hc = { l };
fail |= check(buf, "Horizontal (mode 1)", expect_horizontal, &hc);
}
/* --- Mode 2 DC: ((sum + 16) >> 5) --- */
/* All top = 2, all left = 6: sum = 32 + 96 = 128, +16 = 144,
* >>5 = 144/32 = 4. */
{
uint8_t buf[ROWS][STRIDE];
int t[16], l[16];
for (int i = 0; i < 16; i++) { t[i] = 2; l[i] = 6; }
set_ctx(buf, 99, t, l);
daedalus_h264_pred_16x16_dc(&buf[1][1], STRIDE);
uint8_t exp_val = 4;
fail |= check(buf, "DC (mode 2)", expect_uniform, &exp_val);
}
/* --- Mode 3 Plane: uniform neighbours → uniform output --- */
/* H=V=0 when neighbours are uniform. a = 16*(p+p) = 32p.
* pred[y][x] = (32p + 0 + 0 + 16) >> 5 = (32p + 16) >> 5 = p
* (exact integer for any p, since 32p/32 = p and +16/32 = 0).
* Verifies the orientation-free portion of the formula. */
{
uint8_t buf[ROWS][STRIDE];
int t[16], l[16];
for (int i = 0; i < 16; i++) { t[i] = 100; l[i] = 100; }
set_ctx(buf, 100, t, l); /* uniform tl too — H/V sums actually zero */
daedalus_h264_pred_16x16_plane(&buf[1][1], STRIDE);
uint8_t exp_val = 100;
fail |= check(buf, "Plane (mode 3, uniform)", expect_uniform, &exp_val);
}
/* --- Mode 3 Plane: gradient sanity ---
* Top row = 0..15 (gradient), left col = 0..15, tl = 0.
* H = sum_{i=0..7} (i+1) * (t[8+i] - t[6-i] for i<7; or t[15]-tl=15 for i=7)
* = 1*(8-6) + 2*(9-5) + 3*(10-4) + 4*(11-3) + 5*(12-2) + 6*(13-1)
* + 7*(14-0) + 8*(15-0)
* = 2 + 8 + 18 + 32 + 50 + 72 + 98 + 120 = 400
* V = same shape on left col = 400
* b = (5*400 + 32) >> 6 = 2032 >> 6 = 31
* c = (5*400 + 32) >> 6 = 31
* a = 16 * (l[15] + t[15]) = 16 * (15 + 15) = 480
* pred[0][0] = (480 + 31*(-7) + 31*(-7) + 16) >> 5
* = (480 - 217 - 217 + 16) >> 5
* = 62 >> 5 = 1
* pred[15][15] = (480 + 31*8 + 31*8 + 16) >> 5
* = (480 + 248 + 248 + 16) >> 5
* = 992 >> 5 = 31
* Just spot-check those two corners. */
{
uint8_t buf[ROWS][STRIDE];
int t[16], l[16];
for (int i = 0; i < 16; i++) { t[i] = i; l[i] = i; }
set_ctx(buf, 0, t, l);
daedalus_h264_pred_16x16_plane(&buf[1][1], STRIDE);
uint8_t tl_actual = buf[1 + 0][1 + 0];
uint8_t br_actual = buf[1 + 15][1 + 15];
int spot_fail = 0;
if (tl_actual != 1) { fprintf(stderr, "Plane gradient pred[0][0] = %u, expected 1\n", tl_actual); spot_fail = 1; }
if (br_actual != 31) { fprintf(stderr, "Plane gradient pred[15][15] = %u, expected 31\n", br_actual); spot_fail = 1; }
if (!spot_fail) printf(" %-30s PASS (corners 1, 31)\n", "Plane (mode 3, gradient)");
else printf(" %-30s FAIL\n", "Plane (mode 3, gradient)");
fail |= spot_fail;
}
if (fail == 0) printf("\nALL Intra_16x16 mode references PASS\n");
else fprintf(stderr, "\n%d test(s) FAILED\n", fail);
return fail ? 1 : 0;
}