1f07f3cd70
PR #23 added the Hadamard as a test-only spec reference; this PR promotes it to a public symbol in src/ so consumers (the eventual marfrit-packages substitution-arc patch 0011) can link against it. New: void daedalus_h264_chroma_dc_hadamard_2x2(int16_t c[4]); — operates in-place on 4 int16, no QP-dependent scaling (caller composes that themselves per §8.5.11.2). The src/ implementation is byte-for-byte identical to the test-only ref in tests/h264_chroma_dc_hadamard_ref.c (kept as a separate spec-validation copy). A new "public API parity" test case verifies the two produce identical output for a non-trivial input. Pure CPU primitive — no substrate-dispatch wrapper because the work is 4 adds + 4 subs; the substrate machinery would cost more than the kernel itself. Verified on hertz: $ ./build/test_chroma_dc_hadamard all-uniform 5 PASS col gradient [0,10,0,10] PASS row gradient [0,0,10,10] PASS anti-diagonal [10,0,0,10] PASS asymmetric [1,2,3,4] PASS sign-alternating [-5,5,-5,5] PASS double-Hadamard = 4*orig PASS public API parity vs _ref PASS ALL chroma DC Hadamard tests PASS $ nm -g build/libdaedalus_core.a | grep chroma_dc_hadamard 0000000000000000 T daedalus_h264_chroma_dc_hadamard_2x2 Unblocks marfrit-packages 0011 (substituting H264DSPContext.chroma_dc_dequant_idct, which composes the Hadamard + qmul scaling).
137 lines
4.5 KiB
C
137 lines
4.5 KiB
C
/*
|
|
* Tests the H.264 chroma DC 2x2 Hadamard primitive against
|
|
* spec-derived expected outputs.
|
|
*
|
|
* f[0,0] = c[0,0] + c[0,1] + c[1,0] + c[1,1] "sum"
|
|
* f[0,1] = c[0,0] - c[0,1] + c[1,0] - c[1,1] "col-diff"
|
|
* f[1,0] = c[0,0] + c[0,1] - c[1,0] - c[1,1] "row-diff"
|
|
* f[1,1] = c[0,0] - c[0,1] - c[1,0] + c[1,1] "anti-diag"
|
|
*/
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
extern void daedalus_h264_chroma_dc_hadamard_2x2_ref(int16_t c[4]);
|
|
extern void daedalus_h264_chroma_dc_hadamard_2x2(int16_t c[4]); /* public API */
|
|
|
|
static int check(const char *name, int16_t in[4], int16_t expect[4])
|
|
{
|
|
int16_t c[4]; memcpy(c, in, sizeof(c));
|
|
daedalus_h264_chroma_dc_hadamard_2x2_ref(c);
|
|
int fail = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (c[i] != expect[i]) {
|
|
fprintf(stderr, "%s: c[%d] = %d, expected %d\n",
|
|
name, i, c[i], expect[i]);
|
|
fail = 1;
|
|
}
|
|
}
|
|
if (!fail) printf(" %-32s PASS\n", name);
|
|
else printf(" %-32s FAIL\n", name);
|
|
return fail;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
int fail = 0;
|
|
|
|
/* Test 1: All-same input.
|
|
* c = [5, 5, 5, 5]
|
|
* f[0,0] = 20, f[0,1] = 0, f[1,0] = 0, f[1,1] = 0
|
|
*/
|
|
{ int16_t in[4] = { 5, 5, 5, 5 };
|
|
int16_t ex[4] = { 20, 0, 0, 0 };
|
|
fail |= check("all-uniform 5", in, ex); }
|
|
|
|
/* Test 2: Single-axis variation (col 1 = 0, col 2 = 10).
|
|
* c = [0, 10, 0, 10]
|
|
* f[0,0] = 0+10+0+10 = 20
|
|
* f[0,1] = 0-10+0-10 = -20
|
|
* f[1,0] = 0+10-0-10 = 0
|
|
* f[1,1] = 0-10-0+10 = 0
|
|
*/
|
|
{ int16_t in[4] = { 0, 10, 0, 10 };
|
|
int16_t ex[4] = { 20, -20, 0, 0 };
|
|
fail |= check("col gradient [0,10,0,10]", in, ex); }
|
|
|
|
/* Test 3: Row gradient.
|
|
* c = [0, 0, 10, 10]
|
|
* f[0,0] = 20, f[0,1] = 0, f[1,0] = 0-20 = -20, f[1,1] = 0
|
|
*/
|
|
{ int16_t in[4] = { 0, 0, 10, 10 };
|
|
int16_t ex[4] = { 20, 0, -20, 0 };
|
|
fail |= check("row gradient [0,0,10,10]", in, ex); }
|
|
|
|
/* Test 4: Anti-diagonal pattern.
|
|
* c = [10, 0, 0, 10]
|
|
* f[0,0] = 20
|
|
* f[0,1] = 10-0+0-10 = 0
|
|
* f[1,0] = 10+0-0-10 = 0
|
|
* f[1,1] = 10-0-0+10 = 20
|
|
*/
|
|
{ int16_t in[4] = { 10, 0, 0, 10 };
|
|
int16_t ex[4] = { 20, 0, 0, 20 };
|
|
fail |= check("anti-diagonal [10,0,0,10]", in, ex); }
|
|
|
|
/* Test 5: Asymmetric — all bands non-zero.
|
|
* c = [1, 2, 3, 4]
|
|
* f[0,0] = 10
|
|
* f[0,1] = 1-2+3-4 = -2
|
|
* f[1,0] = 1+2-3-4 = -4
|
|
* f[1,1] = 1-2-3+4 = 0
|
|
*/
|
|
{ int16_t in[4] = { 1, 2, 3, 4 };
|
|
int16_t ex[4] = { 10, -2, -4, 0 };
|
|
fail |= check("asymmetric [1,2,3,4]", in, ex); }
|
|
|
|
/* Test 6: Negative inputs (Hadamard is linear, so signs preserve).
|
|
* c = [-5, 5, -5, 5]
|
|
* f[0,0] = -5+5-5+5 = 0
|
|
* f[0,1] = -5-5-5-5 = -20
|
|
* f[1,0] = -5+5+5-5 = 0
|
|
* f[1,1] = -5-5+5+5 = 0
|
|
*/
|
|
{ int16_t in[4] = { -5, 5, -5, 5 };
|
|
int16_t ex[4] = { 0, -20, 0, 0 };
|
|
fail |= check("sign-alternating [-5,5,-5,5]", in, ex); }
|
|
|
|
/* Test 7: Inverse-property check. H * H = 4*I for the unscaled
|
|
* 2x2 Hadamard. So applying twice multiplies each by 4.
|
|
* c = [1, 2, 3, 4]
|
|
* First Hadamard: [10, -2, -4, 0]
|
|
* Second Hadamard: [4, 8, 12, 16]
|
|
*/
|
|
{ int16_t in[4] = { 1, 2, 3, 4 };
|
|
int16_t ex[4] = { 4, 8, 12, 16 };
|
|
int16_t c[4]; memcpy(c, in, sizeof(c));
|
|
daedalus_h264_chroma_dc_hadamard_2x2_ref(c);
|
|
daedalus_h264_chroma_dc_hadamard_2x2_ref(c);
|
|
int local_fail = 0;
|
|
for (int i = 0; i < 4; i++) if (c[i] != ex[i]) local_fail = 1;
|
|
printf(" %-32s %s\n", "double-Hadamard = 4*orig",
|
|
local_fail ? "FAIL" : "PASS");
|
|
fail |= local_fail;
|
|
}
|
|
|
|
/* Test 8: public API parity. The public symbol must produce
|
|
* byte-identical output to the test-only ref for the same input.
|
|
* If the src/ Hadamard ever drifts from the spec, this catches it. */
|
|
{
|
|
int16_t input[4] = { 7, -11, 23, -42 };
|
|
int16_t a[4], b[4];
|
|
memcpy(a, input, sizeof(a));
|
|
memcpy(b, input, sizeof(b));
|
|
daedalus_h264_chroma_dc_hadamard_2x2_ref(a);
|
|
daedalus_h264_chroma_dc_hadamard_2x2(b);
|
|
int local_fail = 0;
|
|
for (int i = 0; i < 4; i++) if (a[i] != b[i]) local_fail = 1;
|
|
printf(" %-32s %s\n", "public API parity vs _ref",
|
|
local_fail ? "FAIL" : "PASS");
|
|
fail |= local_fail;
|
|
}
|
|
|
|
if (fail == 0) printf("\nALL chroma DC Hadamard tests PASS\n");
|
|
else fprintf(stderr, "\n%d test(s) FAILED\n", fail);
|
|
return fail ? 1 : 0;
|
|
}
|