nx_vtransform.nx source
↩ module page · 53 lines · 2662 B
1// nx_vtransform.nx -- sovereign 4x4 integer TRANSFORM + QUANTIZE for the motion-comp residual (V-R2). After
2// motion compensation the residual block is small + smooth; a separable 4x4 Walsh-Hadamard transform compacts
3// its energy into a few coefficients (mostly DC), then quantization zeros the small ones -> very few nonzero
4// values to entropy-code. This is the transform+quant stage VP8/H.264 use (they use an integer DCT; WHT is the
5// integer, EXACTLY-invertible first rung -- integer-DCT is a later refinement). Pure integer ops, no FPU/syscalls.
6// Block = 16 i64 row-major (a 4x4 residual). license_tier: ORIGINAL
7
8// 1D 4-point Walsh-Hadamard butterfly on the 4 elements at base, base+st, base+2st, base+3st (in place).
9func vt_wht4(b: *i64, base: i64, st: i64) -> i64 {
10 let s0: i64 = b[base]
11 let s1: i64 = b[base + st]
12 let s2: i64 = b[base + 2*st]
13 let s3: i64 = b[base + 3*st]
14 let a: i64 = s0 + s1
15 let d0: i64 = s0 - s1
16 let c: i64 = s2 + s3
17 let d1: i64 = s2 - s3
18 b[base] = a + c
19 b[base + st] = d0 + d1
20 b[base + 2*st] = a - c
21 b[base + 3*st] = d0 - d1
22 return 0
23}
24// 2D forward transform: WHT on the 4 rows, then the 4 columns.
25func vt_fwd(blk: *i64) -> i64 {
26 var r: i64 = 0; while r < 4 { vt_wht4(blk, r*4, 1); r = r + 1 }
27 var c: i64 = 0; while c < 4 { vt_wht4(blk, c, 4); c = c + 1 }
28 return 0
29}
30// 2D inverse: the WHT is its own inverse up to scale 16 (4 per 1D x 2 passes) -> apply forward then /16. Exact.
31func vt_inv(blk: *i64) -> i64 {
32 var r: i64 = 0; while r < 4 { vt_wht4(blk, r*4, 1); r = r + 1 }
33 var c: i64 = 0; while c < 4 { vt_wht4(blk, c, 4); c = c + 1 }
34 var i: i64 = 0; while i < 16 { blk[i] = blk[i] / 16; i = i + 1 }
35 return 0
36}
37// quantize coefficients by step q with a DEADZONE (truncate toward zero, like H.264/VP8): any coefficient with
38// magnitude < q becomes 0, so the small (high-freq) coeffs vanish -> real compaction. returns the NONZERO count.
39func vt_quant(blk: *i64, q: i64) -> i64 {
40 var nz: i64 = 0
41 var i: i64 = 0
42 while i < 16 {
43 let v: i64 = blk[i] / q // NishiLang integer divide truncates toward zero = deadzone
44 blk[i] = v
45 if v != 0 { nz = nz + 1 }
46 i = i + 1
47 }
48 return nz
49}
50// dequantize (scale back by q) before the inverse transform.
51func vt_dequant(blk: *i64, q: i64) -> i64 { var i: i64 = 0; while i < 16 { blk[i] = blk[i] * q; i = i + 1 } return 0 }
52// count nonzero entries of a block (the entropy-coding cost proxy).
53func vt_nonzero(blk: *i64) -> i64 { var nz: i64 = 0; var i: i64 = 0; while i < 16 { if blk[i] != 0 { nz = nz + 1 } i = i + 1 } return nz }