code wiki / (root) / nx_ventropy.nx

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}