/* * Tests the 9 H.264 Intra_4x4 luma prediction modes against * spec-derived expected patterns. Goal: catch any mistake in * the reference (sign / shift / table mapping) before it lands * downstream. Each mode is exercised with a deterministic * neighbour context and checked against a hand-computed (or * spec-derived) expected 4x4 output. * * The test buffer layout reserves a 1-pixel top/left context border * + a 4-pixel top-right (for modes 3 / 7): * * row 0: [tl][t0 t1 t2 t3 t4 t5 t6 t7] <- TOP_STRIDE = 9 bytes * row 1: [l0][ 4x4 output goes here ] * row 2: [l1][ ] * row 3: [l2][ ] * row 4: [l3][ ] * * dst (passed to the pred fns) points at row 1 col 1. */ #include #include #include #include extern void daedalus_h264_pred_4x4_vertical(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_horizontal(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_dc(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_ddl(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_ddr(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_vr(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_hd(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_vl(uint8_t *dst, ptrdiff_t stride); extern void daedalus_h264_pred_4x4_hu(uint8_t *dst, ptrdiff_t stride); #define STRIDE 9 typedef void (*pred_fn)(uint8_t *dst, ptrdiff_t stride); /* Set up the buffer: 5 rows × STRIDE cols. * top-left = tl, top[0..7] = t[0..7], left[0..3] = l[0..3]. * The 4x4 output region (rows 1..4, cols 1..4) is filled with 0xff * sentinels so any unwritten cell shows up as 255 in the compare. */ static void set_ctx(uint8_t buf[5][STRIDE], int tl, const int t[8], const int l[4]) { for (int r = 0; r < 5; r++) for (int c = 0; c < STRIDE; c++) buf[r][c] = 0xff; buf[0][0] = (uint8_t) tl; for (int c = 0; c < 8; c++) buf[0][1 + c] = (uint8_t) t[c]; for (int r = 0; r < 4; r++) buf[1 + r][0] = (uint8_t) l[r]; } static int check(const uint8_t buf[5][STRIDE], const char *name, const uint8_t expect[4][4]) { int diff = 0; for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { uint8_t got = buf[1 + r][1 + c]; uint8_t exp = expect[r][c]; if (got != exp) { if (diff == 0) fprintf(stderr, "%s: first mismatch r=%d c=%d got=%u exp=%u\n", name, r, c, got, exp); diff++; } } } if (diff == 0) printf(" %-26s PASS\n", name); else printf(" %-26s FAIL (%d/16 bytes wrong)\n", name, diff); return diff == 0 ? 0 : 1; } int main(void) { int fail = 0; /* Mode 0 — Vertical: each col = top[col]. */ { uint8_t buf[5][STRIDE]; int tl = 0; int t[8] = { 10, 20, 30, 40, 0, 0, 0, 0 }; int l[4] = { 0, 0, 0, 0 }; set_ctx(buf, tl, t, l); daedalus_h264_pred_4x4_vertical(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {10,20,30,40}, {10,20,30,40}, {10,20,30,40}, {10,20,30,40} }; fail |= check(buf, "Vertical (mode 0)", exp); } /* Mode 1 — Horizontal: each row = left[row]. */ { uint8_t buf[5][STRIDE]; int t[8] = { 0,0,0,0, 0,0,0,0 }; int l[4] = { 50, 60, 70, 80 }; set_ctx(buf, 0, t, l); daedalus_h264_pred_4x4_horizontal(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {50,50,50,50}, {60,60,60,60}, {70,70,70,70}, {80,80,80,80} }; fail |= check(buf, "Horizontal (mode 1)", exp); } /* Mode 2 — DC: all 8 neighbours valid → ((sum + 4) >> 3) broadcast. * top sum = 4*1 = 4, left sum = 4*3 = 12, total 16, +4 = 20, * >>3 = 2. */ { uint8_t buf[5][STRIDE]; int t[8] = { 1,1,1,1, 0,0,0,0 }; int l[4] = { 3,3,3,3 }; set_ctx(buf, 99, t, l); /* tl unused for DC */ daedalus_h264_pred_4x4_dc(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {2,2,2,2}, {2,2,2,2}, {2,2,2,2}, {2,2,2,2} }; fail |= check(buf, "DC (mode 2)", exp); } /* Mode 3 — Diagonal_Down_Left: zz[i] = avg3(t[i], t[i+1], t[i+2]); * dst[r][c] = zz[c + r]. * With all t[]=100 → all zz=100 → all dst=100. */ { uint8_t buf[5][STRIDE]; int t[8] = { 100,100,100,100, 100,100,100,100 }; int l[4] = { 0,0,0,0 }; set_ctx(buf, 0, t, l); daedalus_h264_pred_4x4_ddl(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {100,100,100,100}, {100,100,100,100}, {100,100,100,100}, {100,100,100,100} }; fail |= check(buf, "DiagDownLeft (mode 3)", exp); } /* Mode 4 — Diagonal_Down_Right: zz[c-r] with c-r ∈ {-3..+3}. * If all 9 surrounding pixels = 200 → all zz = 200 → all dst = 200. */ { uint8_t buf[5][STRIDE]; int t[8] = { 200,200,200,200, 0,0,0,0 }; int l[4] = { 200,200,200,200 }; set_ctx(buf, 200, t, l); daedalus_h264_pred_4x4_ddr(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {200,200,200,200}, {200,200,200,200}, {200,200,200,200}, {200,200,200,200} }; fail |= check(buf, "DiagDownRight (mode 4)", exp); } /* Mode 5 — Vertical_Right. With all neighbours = 80 the 3-tap * (a+2b+c+2)>>2 and 2-tap (a+b+1)>>1 both yield 80. */ { uint8_t buf[5][STRIDE]; int t[8] = { 80,80,80,80, 0,0,0,0 }; int l[4] = { 80,80,80,80 }; set_ctx(buf, 80, t, l); daedalus_h264_pred_4x4_vr(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {80,80,80,80}, {80,80,80,80}, {80,80,80,80}, {80,80,80,80} }; fail |= check(buf, "VerticalRight (mode 5)", exp); } /* Mode 6 — Horizontal_Down. Same uniform-context degenerate case. */ { uint8_t buf[5][STRIDE]; int t[8] = { 120,120,120,120, 0,0,0,0 }; int l[4] = { 120,120,120,120 }; set_ctx(buf, 120, t, l); daedalus_h264_pred_4x4_hd(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {120,120,120,120}, {120,120,120,120}, {120,120,120,120}, {120,120,120,120} }; fail |= check(buf, "HorizontalDown (mode 6)", exp); } /* Mode 7 — Vertical_Left. Uniform context. */ { uint8_t buf[5][STRIDE]; int t[8] = { 64,64,64,64, 64,64,64,64 }; int l[4] = { 0,0,0,0 }; set_ctx(buf, 0, t, l); daedalus_h264_pred_4x4_vl(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {64,64,64,64}, {64,64,64,64}, {64,64,64,64}, {64,64,64,64} }; fail |= check(buf, "VerticalLeft (mode 7)", exp); } /* Mode 8 — Horizontal_Up. Uniform context. */ { uint8_t buf[5][STRIDE]; int t[8] = { 0,0,0,0, 0,0,0,0 }; int l[4] = { 200,200,200,200 }; set_ctx(buf, 0, t, l); daedalus_h264_pred_4x4_hu(&buf[1][1], STRIDE); uint8_t exp[4][4] = { {200,200,200,200}, {200,200,200,200}, {200,200,200,200}, {200,200,200,200} }; fail |= check(buf, "HorizontalUp (mode 8)", exp); } /* Asymmetric Vertical_Right test: detects orientation / * row-vs-col confusion. Top=10,20,30,40, Left=50,60,70, * top-left=5. Spec-derived expected output computed by hand * from §8.3.1.4.6. * * d[0][0] = (tl+t0+1)>>1 = (5+10+1)>>1 = 8 * d[0][1] = (t0+t1+1)>>1 = (10+20+1)>>1 = 15 * d[0][2] = (t1+t2+1)>>1 = (20+30+1)>>1 = 25 * d[0][3] = (t2+t3+1)>>1 = (30+40+1)>>1 = 35 * d[1][0] = avg3(l0,tl,t0) = (50+2*5+10+2)>>2 = 72/4 = 18 * d[1][1] = avg3(tl,t0,t1) = (5+20+20+2)>>2 = 47/4 = 11 * d[1][2] = avg3(t0,t1,t2) = (10+40+30+2)>>2 = 82/4 = 20 * d[1][3] = avg3(t1,t2,t3) = (20+60+40+2)>>2 = 122/4 = 30 * d[2][0] = avg3(tl,l0,l1) = (5+100+60+2)>>2 = 167/4 = 41 * d[2][1] = d[0][0] = 8 * d[2][2] = d[0][1] = 15 * d[2][3] = d[0][2] = 25 * d[3][0] = avg3(l0,l1,l2) = (50+120+70+2)>>2 = 242/4 = 60 * d[3][1] = d[1][0] = 18 * d[3][2] = d[1][1] = 11 * d[3][3] = d[1][2] = 20 */ { uint8_t buf[5][STRIDE]; int t[8] = { 10,20,30,40, 0,0,0,0 }; int l[4] = { 50,60,70,0 }; set_ctx(buf, 5, t, l); daedalus_h264_pred_4x4_vr(&buf[1][1], STRIDE); uint8_t exp[4][4] = { { 8,15,25,35}, {18,11,20,30}, {41, 8,15,25}, {60,18,11,20}, }; fail |= check(buf, "VR asym (sanity)", exp); } if (fail == 0) printf("\nALL %d intra-4x4 mode references PASS\n", 10); else fprintf(stderr, "\n%d test(s) FAILED\n", fail); return fail ? 1 : 0; }