nx_av1_dct.nx source
↩ module page · 133 lines · 4997 B
1// nx_av1_dct.nx -- AV1/AV2 inverse DCT butterfly networks, 4 and 8 point.
2//
3// The residual path's core. Built on nx_av1_txfm.nx's 12-bit cosine table and
4// Round2, so every constant traces to one definition rather than being baked
5// in as a magic number.
6//
7// ANGLE MAPPING. The classic references express these constants as
8// cospi_N_64 = cos(N*pi/64). This table is a 256-step circle, so cos128(a) is
9// cos(a*pi/128) and the mapping is a = 2N: cospi_16_64 -> cos128(32),
10// cospi_8_64 -> cos128(16), cospi_24_64 -> cos128(48), cospi_4_64 ->
11// cos128(8), cospi_28_64 -> cos128(56). Halving that factor is the obvious
12// mistake and it produces a transform that still looks like a DCT -- the
13// basis functions are simply at the wrong frequencies, so the image decodes
14// blurred rather than broken.
15//
16// WHAT IS PROVEN HERE. The gate checks STRUCTURAL properties -- a DC-only
17// input must give a flat output, an odd basis index must give an
18// antisymmetric one, and the transform must be linear within rounding. These
19// catch a mis-wired butterfly, a wrong angle and a sign error. They do NOT
20// establish bit-exactness against libaom's reference vectors, which needs
21// those vectors; that is a separate, later step and is not claimed.
22//
23// genealogy_id: av1_spec_7_13_2_inverse_dct
24// lineage_id: nx_av1_dct_v1
25// license_tier: ORIGINAL
26
27import "nx_syscalls.nx"
28import "nx_av1_txfm.nx"
29const K_MAGIC_3784: i64 = 3784
30const K_MAGIC_1567: i64 = 1567
31
32// ===== 4-point inverse DCT ========================================
33//
34// Operates in place on four coefficients.
35
36func nx_av1_idct4(t: *i64, io: *i64) -> i64 {
37 // The odd pair rotates by cospi_24_64 and cospi_8_64 IN THAT ORDER.
38 // Swapping them keeps the output antisymmetric -- so the symmetry test
39 // still passes -- while putting the basis at the wrong frequency, which
40 // shows up only as a monotonicity failure. That swap was a live bug here,
41 // caught by T2's ordering assertion rather than its symmetry one.
42 let c32: i64 = nx_av1_cos128(t, 32) // cospi_16_64, ~1/sqrt2
43 let c8: i64 = nx_av1_cos128(t, 16) // cospi_8_64 = K_MAGIC_3784
44 let c24: i64 = nx_av1_cos128(t, 48) // cospi_24_64 = K_MAGIC_1567
45
46 let s0: i64 = nx_av1_round2((io[0] + io[2]) * c32, NX_TX_COS_BITS)
47 let s1: i64 = nx_av1_round2((io[0] - io[2]) * c32, NX_TX_COS_BITS)
48 let s2: i64 = nx_av1_round2(io[1] * c24 - io[3] * c8, NX_TX_COS_BITS)
49 let s3: i64 = nx_av1_round2(io[1] * c8 + io[3] * c24, NX_TX_COS_BITS)
50
51 io[0] = s0 + s3
52 io[1] = s1 + s2
53 io[2] = s1 - s2
54 io[3] = s0 - s3
55 return 1
56}
57
58// ===== 8-point inverse DCT ========================================
59//
60// Four stages. Stage 2 and stage 3 both contain the 1/sqrt2 rotation, which
61// is where a halved angle shows up first.
62
63func nx_av1_idct8(t: *i64, io: *i64) -> i64 {
64 let c32: i64 = nx_av1_cos128(t, 32)
65 let c56: i64 = nx_av1_cos128(t, 56)
66 let s56: i64 = nx_av1_sin128(t, 56)
67 let c24: i64 = nx_av1_cos128(t, 24)
68 let s24: i64 = nx_av1_sin128(t, 24)
69 let c48: i64 = nx_av1_cos128(t, 48)
70 let s48: i64 = nx_av1_sin128(t, 48)
71
72 let a: *i64 = sys_mmap(16 * 8) as *i64
73 let b: *i64 = sys_mmap(16 * 8) as *i64
74
75 // ---- stage 1: even terms pass through, odd terms rotate ----
76 a[0] = io[0]
77 a[1] = io[4]
78 a[2] = io[2]
79 a[3] = io[6]
80 a[4] = nx_av1_round2(io[1] * c56 - io[7] * s56, NX_TX_COS_BITS)
81 a[7] = nx_av1_round2(io[1] * s56 + io[7] * c56, NX_TX_COS_BITS)
82 a[5] = nx_av1_round2(io[5] * c24 - io[3] * s24, NX_TX_COS_BITS)
83 a[6] = nx_av1_round2(io[5] * s24 + io[3] * c24, NX_TX_COS_BITS)
84
85 // ---- stage 2 ----
86 b[0] = nx_av1_round2((a[0] + a[1]) * c32, NX_TX_COS_BITS)
87 b[1] = nx_av1_round2((a[0] - a[1]) * c32, NX_TX_COS_BITS)
88 b[2] = nx_av1_round2(a[2] * c48 - a[3] * s48, NX_TX_COS_BITS)
89 b[3] = nx_av1_round2(a[2] * s48 + a[3] * c48, NX_TX_COS_BITS)
90 b[4] = a[4] + a[5]
91 b[5] = a[4] - a[5]
92 b[6] = a[7] - a[6]
93 b[7] = a[6] + a[7]
94
95 // ---- stage 3 ----
96 a[0] = b[0] + b[3]
97 a[1] = b[1] + b[2]
98 a[2] = b[1] - b[2]
99 a[3] = b[0] - b[3]
100 a[4] = b[4]
101 a[5] = nx_av1_round2((b[6] - b[5]) * c32, NX_TX_COS_BITS)
102 a[6] = nx_av1_round2((b[5] + b[6]) * c32, NX_TX_COS_BITS)
103 a[7] = b[7]
104
105 // ---- stage 4 ----
106 io[0] = a[0] + a[7]
107 io[1] = a[1] + a[6]
108 io[2] = a[2] + a[5]
109 io[3] = a[3] + a[4]
110 io[4] = a[3] - a[4]
111 io[5] = a[2] - a[5]
112 io[6] = a[1] - a[6]
113 io[7] = a[0] - a[7]
114 return 1
115}
116
117// ===== the array permutation ======================================
118//
119// The spec feeds the butterfly network a bit-reversed coefficient order.
120
121func nx_av1_dct_permute(io: *i64, n_log2: i64) -> i64 {
122 if n_log2 <= 0 { return 0 }
123 let n: i64 = 1 << n_log2
124 let copy: *i64 = sys_mmap(n * 8 + 64) as *i64
125 var i: i64 = 0
126 while i < n { copy[i] = io[i]; i = i + 1 }
127 i = 0
128 while i < n {
129 io[i] = copy[nx_av1_brev(n_log2, i)]
130 i = i + 1
131 }
132 return 1
133}