nx_ventropy.nx source
↩ module page · 144 lines · 6608 B
1// nx_ventropy.nx -- sovereign ENTROPY CODING of a quantized 4x4 coefficient block (V-R3). After transform+quant
2// a block is mostly zeros clustered toward high frequency; this scans it in ZIG-ZAG order (low->high freq, so
3// zeros bunch at the end), run-length-codes (zeros-run, level) pairs, and bit-packs them with an EOB terminator
4// + variable-length signed levels. A near-static block -> ~1 byte; a few nonzeros -> a few bytes (vs 32 raw).
5// This is the CAVLC-family stage VP8/H.264 use. Composes nx_bitio (MSB bit writer/reader). license_tier: ORIGINAL
6import "nx_bitio.nx"
7
8// zig-zag scan order for a 4x4 block (row-major index visited k-th: low-frequency first)
9func ve_zz_idx(k: i64) -> i64 {
10 if k==0 { return 0 } if k==1 { return 1 } if k==2 { return 4 } if k==3 { return 8 }
11 if k==4 { return 5 } if k==5 { return 2 } if k==6 { return 3 } if k==7 { return 6 }
12 if k==8 { return 9 } if k==9 { return 12 } if k==10 { return 13 } if k==11 { return 10 }
13 if k==12 { return 7 } if k==13 { return 11 } if k==14 { return 14 }
14 return 15
15}
16// signed<->unsigned zig-zag (small magnitudes -> small codes)
17func ve_zze(v: i64) -> i64 { if v < 0 { return ((0 - v) << 1) - 1 } return v << 1 }
18func ve_zzd(z: i64) -> i64 { if (z & 1) == 1 { return 0 - ((z + 1) >> 1) } return z >> 1 }
19func ve_blen(z: i64) -> i64 { var n: i64 = 0; var x: i64 = z; while x > 0 { n = n + 1; x = x >> 1 } return n }
20// variable-length signed value: 5-bit length + that many bits of the zig-zag code
21func ve_vput(buf: *u8, bp: i64, v: i64) -> i64 {
22 let z: i64 = ve_zze(v); let nb: i64 = ve_blen(z)
23 var p: i64 = nx_bw_put(buf, bp, nb, 5)
24 if nb > 0 { p = nx_bw_put(buf, p, z, nb) }
25 return p
26}
27func ve_vlen(buf: *u8, bp: i64) -> i64 { return 5 + nx_br_get(buf, bp, 5) }
28func ve_vval(buf: *u8, bp: i64) -> i64 {
29 let nb: i64 = nx_br_get(buf, bp, 5)
30 var z: i64 = 0
31 if nb > 0 { z = nx_br_get(buf, bp + 5, nb) }
32 return ve_zzd(z)
33}
34
35// encode a 16-coeff block into buf STARTING AT bit offset bp0; returns the new bit position. Threading bp0 lets many
36// blocks pack into ONE contiguous bitstream (what the network actually transmits). Stream per block = repeated
37// [1][run:4][level:vlen] then [0]=EOB.
38func ve_encode_at(coeffs: *i64, buf: *u8, bp0: i64) -> i64 {
39 var bp: i64 = bp0
40 var run: i64 = 0
41 var k: i64 = 0
42 while k < 16 {
43 let val: i64 = coeffs[ve_zz_idx(k)]
44 if val == 0 { run = run + 1 } else {
45 bp = nx_bw_put(buf, bp, 1, 1)
46 bp = nx_bw_put(buf, bp, run, 4)
47 bp = ve_vput(buf, bp, val)
48 run = 0
49 }
50 k = k + 1
51 }
52 bp = nx_bw_put(buf, bp, 0, 1) // EOB (trailing zeros implied)
53 return bp
54}
55// decode a block from buf STARTING AT bit offset bp0 -> 16 coeffs (zeros for any not coded); returns the new bit
56// position (so the next block's decode resumes exactly where this one ended).
57func ve_decode_at(buf: *u8, coeffs: *i64, bp0: i64) -> i64 {
58 var i: i64 = 0
59 while i < 16 { coeffs[i] = 0; i = i + 1 }
60 var bp: i64 = bp0
61 var k: i64 = 0
62 var flag: i64 = nx_br_get(buf, bp, 1); bp = bp + 1
63 var go: i64 = 1
64 while go == 1 {
65 if flag == 0 { go = 0 } else {
66 let run: i64 = nx_br_get(buf, bp, 4); bp = bp + 4
67 let level: i64 = ve_vval(buf, bp); bp = bp + ve_vlen(buf, bp)
68 k = k + run
69 if k < 16 { coeffs[ve_zz_idx(k)] = level; k = k + 1 }
70 flag = nx_br_get(buf, bp, 1); bp = bp + 1
71 }
72 }
73 return bp
74}
75// encode a 16-coeff block -> bitstream from bit 0; returns bit count. (Single-block convenience over ve_encode_at.)
76func ve_encode(coeffs: *i64, buf: *u8) -> i64 { return ve_encode_at(coeffs, buf, 0) }
77// decode a bitstream from bit 0 -> 16 coeffs (zeros for any not coded). returns bits consumed.
78func ve_decode(buf: *u8, coeffs: *i64) -> i64 { return ve_decode_at(buf, coeffs, 0) }
79
80// COST-ONLY counters (no buffer writes): the exact bit count ve_encode_at / ve64_encode_at would emit for
81// these quantized coeffs. Used by the encoder's RD transform-size select (trial-costing both options is
82// free of stream side effects; the range coder's rate tracks this CAVLC cost monotonically, the standard
83// proxy real encoders use for CABAC-era mode decisions).
84func ve_cost(coeffs: *i64) -> i64 {
85 var bits: i64 = 0
86 var k: i64 = 0
87 while k < 16 {
88 let v: i64 = coeffs[ve_zz_idx(k)]
89 if v != 0 { bits = bits + 1 + 4 + 5 + ve_blen(ve_zze(v)) }
90 k = k + 1
91 }
92 return bits + 1
93}
94func ve64_cost(coeffs: *i64, zz8: *i64) -> i64 {
95 var bits: i64 = 0
96 var k: i64 = 0
97 while k < 64 {
98 let v: i64 = coeffs[zz8[k]]
99 if v != 0 { bits = bits + 1 + 6 + 5 + ve_blen(ve_zze(v)) }
100 k = k + 1
101 }
102 return bits + 1
103}
104
105// ---- 64-coeff (8x8) block variants for the variable-transform-size path (task #46 rung 2: per-MB 8x8/4x4
106// select, the HEVC-class larger-transform gain measured by nx_vcodec_tsize_rd_gate). Same syntax family as
107// the 4x4 block: repeated [1][run:6][level:vlen] then [0]=EOB -- the run field is 6 bits because a zeros-run
108// can reach 63. The zig-zag table zz8 (64 entries, JPEG 8x8 order) is caller-provided (vc_t8_init generates
109// it once) so this module stays table-free and the codec stays allocation-free. ----
110func ve64_encode_at(coeffs: *i64, buf: *u8, bp0: i64, zz8: *i64) -> i64 {
111 var bp: i64 = bp0
112 var run: i64 = 0
113 var k: i64 = 0
114 while k < 64 {
115 let val: i64 = coeffs[zz8[k]]
116 if val == 0 { run = run + 1 } else {
117 bp = nx_bw_put(buf, bp, 1, 1)
118 bp = nx_bw_put(buf, bp, run, 6)
119 bp = ve_vput(buf, bp, val)
120 run = 0
121 }
122 k = k + 1
123 }
124 bp = nx_bw_put(buf, bp, 0, 1) // EOB (trailing zeros implied)
125 return bp
126}
127func ve64_decode_at(buf: *u8, coeffs: *i64, bp0: i64, zz8: *i64) -> i64 {
128 var i: i64 = 0
129 while i < 64 { coeffs[i] = 0; i = i + 1 }
130 var bp: i64 = bp0
131 var k: i64 = 0
132 var flag: i64 = nx_br_get(buf, bp, 1); bp = bp + 1
133 var go: i64 = 1
134 while go == 1 {
135 if flag == 0 { go = 0 } else {
136 let run: i64 = nx_br_get(buf, bp, 6); bp = bp + 6
137 let level: i64 = ve_vval(buf, bp); bp = bp + ve_vlen(buf, bp)
138 k = k + run
139 if k < 64 { coeffs[zz8[k]] = level; k = k + 1 }
140 flag = nx_br_get(buf, bp, 1); bp = bp + 1
141 }
142 }
143 return bp
144}