Files
daedalus-fourier/tests/test_intra_pred_8x8_luma.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

171 lines
7.2 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 H.264 Intra_8x8 luma prediction modes against spec-derived
* expectations. Buffer layout is 9 rows × 17 cols (extra cols for the
* top-right extension that DDL/VL need; not exercised by V/H/DC but
* already in-place for the eventual directional-modes follow-up):
*
* row 0: [tl][t0..t15] — 17 bytes
* row 1: [l0][output row 0 ..] — 17 bytes
* ...
* row 8: [l7][output row 7 ..]
*/
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
extern void daedalus_h264_pred_8x8l_vertical(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_horizontal(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_dc(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_ddl(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_ddr(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_vr(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_hd(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_vl(uint8_t *dst, ptrdiff_t stride);
extern void daedalus_h264_pred_8x8l_hu(uint8_t *dst, ptrdiff_t stride);
#define STRIDE 17
#define ROWS 9
static void set_ctx(uint8_t buf[ROWS][STRIDE], int tl,
const int t[16], const int l[8])
{
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 < 8; r++) buf[1 + r][0] = (uint8_t) l[r];
}
static int check_uniform(const uint8_t buf[ROWS][STRIDE], const char *name,
uint8_t expect_val)
{
int diff = 0;
for (int r = 0; r < 8; r++)
for (int c = 0; c < 8; c++)
if (buf[1+r][1+c] != expect_val) diff++;
if (diff == 0) printf(" %-30s PASS\n", name);
else printf(" %-30s FAIL (%d/64 wrong, expected %u)\n", name, diff, expect_val);
return diff == 0 ? 0 : 1;
}
int main(void)
{
int fail = 0;
/* Mode 0 Vertical with uniform top → uniform output.
* Filtered top[c] = (a + 2*a + a + 2) >> 2 = a for uniform a. */
{
uint8_t buf[ROWS][STRIDE];
int t[16], l[8];
for (int i = 0; i < 16; i++) t[i] = 50;
for (int j = 0; j < 8; j++) l[j] = 0;
set_ctx(buf, 50, t, l);
daedalus_h264_pred_8x8l_vertical(&buf[1][1], STRIDE);
fail |= check_uniform(buf, "Vertical (mode 0, uniform top)", 50);
}
/* Mode 1 Horizontal with uniform left → uniform output. */
{
uint8_t buf[ROWS][STRIDE];
int t[16] = {0}, l[8];
for (int j = 0; j < 8; j++) l[j] = 70;
set_ctx(buf, 70, t, l);
daedalus_h264_pred_8x8l_horizontal(&buf[1][1], STRIDE);
fail |= check_uniform(buf, "Horizontal (mode 1, uniform left)", 70);
}
/* Mode 2 DC with all-uniform neighbours → uniform output.
* Filtered top[c] = top for uniform; filtered left[j] = left.
* sum = 8*a + 8*a + 8 = 16a + 8. >> 4 = a (exact when +8 rounds). */
{
uint8_t buf[ROWS][STRIDE];
int t[16], l[8];
for (int i = 0; i < 16; i++) t[i] = 33;
for (int j = 0; j < 8; j++) l[j] = 33;
set_ctx(buf, 33, t, l);
daedalus_h264_pred_8x8l_dc(&buf[1][1], STRIDE);
fail |= check_uniform(buf, "DC (mode 2, uniform)", 33);
}
/* Mode 0 Vertical with NON-uniform top: gradient 0..15. Filtered
* top[c] for c in 1..14 = (t[c-1] + 2*t[c] + t[c+1] + 2) >> 2
* = (c-1 + 2c + c+1 + 2) >> 2
* = (4c + 2) >> 2 = c (since (4c+2)/4 = c with rounding).
* Wait — (4c + 2) >> 2 = c + 0 (since 4c is divisible by 4 and +2 rounds
* BELOW 4, doesn't change anything). So filtered = c for c=1..14.
* filt[0] (top-left) = (t[0] + 2*tl + l[0] + 2) >> 2 (not exercised
* directly by Vertical mode).
* filt[top 0] = (tl + 2*t[0] + t[1] + 2) >> 2 = (0 + 0 + 1 + 2) >> 2 = 0
* (tl=0, t[0]=0, t[1]=1)
* filt[top 15] = (t[14] + 3*t[15] + 2) >> 2 = (14 + 45 + 2) >> 2
* = 61 >> 2 = 15
*
* So Vertical output col 0 = filt[top 0] = 0, col 1 = filt[top 1] = 1,
* ..., col 7 = filt[top 7] = 7. Same for all 8 rows. */
{
uint8_t buf[ROWS][STRIDE];
int t[16], l[8] = {0};
for (int i = 0; i < 16; i++) t[i] = i;
set_ctx(buf, 0, t, l);
daedalus_h264_pred_8x8l_vertical(&buf[1][1], STRIDE);
int diff = 0;
for (int r = 0; r < 8; r++)
for (int c = 0; c < 8; c++)
if (buf[1+r][1+c] != c) diff++;
if (diff == 0) printf(" %-30s PASS (filtered gradient)\n", "Vertical (mode 0, gradient)");
else printf(" %-30s FAIL (%d/64 wrong)\n", "Vertical (mode 0, gradient)", diff);
fail |= (diff == 0) ? 0 : 1;
}
/* Mode 1 Horizontal gradient: left = 0..7. Filtered left:
* filt[left 0] = (tl + 2*l[0] + l[1] + 2) >> 2 = (0 + 0 + 1 + 2) >> 2 = 0
* filt[left j] for j=1..6 = (l[j-1] + 2*l[j] + l[j+1] + 2) >> 2 = j
* (same arithmetic as top)
* filt[left 7] = (l[6] + 3*l[7] + 2) >> 2 = (6 + 21 + 2) >> 2 = 7
* So Horizontal output row 0 = 0, row 7 = 7. */
{
uint8_t buf[ROWS][STRIDE];
int t[16] = {0}, l[8];
for (int j = 0; j < 8; j++) l[j] = j;
set_ctx(buf, 0, t, l);
daedalus_h264_pred_8x8l_horizontal(&buf[1][1], STRIDE);
int diff = 0;
for (int r = 0; r < 8; r++)
for (int c = 0; c < 8; c++)
if (buf[1+r][1+c] != r) diff++;
if (diff == 0) printf(" %-30s PASS (filtered gradient)\n", "Horizontal (mode 1, gradient)");
else printf(" %-30s FAIL (%d/64 wrong)\n", "Horizontal (mode 1, gradient)", diff);
fail |= (diff == 0) ? 0 : 1;
}
/* Directional modes — uniform-context sanity tests. With all
* neighbours = N, the 1-2-1 filter produces uniform N, and any
* 3-tap / 2-tap on uniform N produces N. So every directional
* mode should output uniform N on uniform input. */
{
typedef void (*pred_fn_t)(uint8_t *dst, ptrdiff_t stride);
struct { const char *name; pred_fn_t fn; } modes[] = {
{ "DDL (mode 3, uniform)", daedalus_h264_pred_8x8l_ddl },
{ "DDR (mode 4, uniform)", daedalus_h264_pred_8x8l_ddr },
{ "VR (mode 5, uniform)", daedalus_h264_pred_8x8l_vr },
{ "HD (mode 6, uniform)", daedalus_h264_pred_8x8l_hd },
{ "VL (mode 7, uniform)", daedalus_h264_pred_8x8l_vl },
{ "HU (mode 8, uniform)", daedalus_h264_pred_8x8l_hu },
};
for (size_t i = 0; i < sizeof(modes)/sizeof(modes[0]); i++) {
uint8_t buf[ROWS][STRIDE];
int t[16], l[8];
for (int k = 0; k < 16; k++) t[k] = 120;
for (int k = 0; k < 8; k++) l[k] = 120;
set_ctx(buf, 120, t, l);
modes[i].fn(&buf[1][1], STRIDE);
fail |= check_uniform(buf, modes[i].name, 120);
}
}
if (fail == 0) printf("\nALL Intra_8x8 luma PASS (9 modes — V, H, DC, DDL, DDR, VR, HD, VL, HU)\n");
else fprintf(stderr, "\n%d test(s) FAILED\n", fail);
return fail ? 1 : 0;
}