Skip to main content

oximedia_codec/simd/av1/
transform.rs

1//! AV1 transform SIMD operations.
2//!
3//! Implements DCT, ADST, and identity transforms for AV1 encoding/decoding.
4//! Supports block sizes from 4x4 to 64x64.
5
6use crate::simd::traits::SimdOpsExt;
7
8/// AV1 transform SIMD operations.
9pub struct TransformSimd<S> {
10    simd: S,
11}
12
13impl<S: SimdOpsExt> TransformSimd<S> {
14    /// Create a new transform SIMD instance.
15    #[inline]
16    pub const fn new(simd: S) -> Self {
17        Self { simd }
18    }
19
20    /// Forward DCT 4x4 transform.
21    ///
22    /// Input: 4x4 block of residuals (i16)
23    /// Output: 4x4 block of transform coefficients (i16)
24    pub fn forward_dct_4x4(&self, input: &[i16; 16], output: &mut [i16; 16]) {
25        use crate::simd::types::I16x8;
26
27        // Load input rows
28        let mut rows = [I16x8::zero(); 4];
29        for i in 0..4 {
30            for j in 0..4 {
31                rows[i][j] = input[i * 4 + j];
32            }
33        }
34
35        // 1D DCT on rows
36        self.dct_4_1d(&mut rows);
37
38        // Transpose
39        let rows = self.simd.transpose_4x4_i16(&rows);
40
41        // 1D DCT on columns (transposed rows)
42        let mut cols = rows;
43        self.dct_4_1d(&mut cols);
44
45        // Transpose back and store
46        let result = self.simd.transpose_4x4_i16(&cols);
47        for i in 0..4 {
48            for j in 0..4 {
49                output[i * 4 + j] = result[i][j];
50            }
51        }
52    }
53
54    /// Inverse DCT 4x4 transform.
55    pub fn inverse_dct_4x4(&self, input: &[i16; 16], output: &mut [i16; 16]) {
56        use crate::simd::types::I16x8;
57
58        let mut rows = [I16x8::zero(); 4];
59        for i in 0..4 {
60            for j in 0..4 {
61                rows[i][j] = input[i * 4 + j];
62            }
63        }
64
65        // 1D IDCT on rows
66        self.idct_4_1d(&mut rows);
67
68        // Transpose
69        let rows = self.simd.transpose_4x4_i16(&rows);
70
71        // 1D IDCT on columns
72        let mut cols = rows;
73        self.idct_4_1d(&mut cols);
74
75        // Transpose back and store
76        let result = self.simd.transpose_4x4_i16(&cols);
77        for i in 0..4 {
78            for j in 0..4 {
79                output[i * 4 + j] = result[i][j];
80            }
81        }
82    }
83
84    /// Forward DCT 8x8 transform.
85    pub fn forward_dct_8x8(&self, input: &[i16; 64], output: &mut [i16; 64]) {
86        use crate::simd::types::I16x8;
87
88        // Load input rows
89        let mut rows = [I16x8::zero(); 8];
90        for i in 0..8 {
91            for j in 0..8 {
92                rows[i][j] = input[i * 8 + j];
93            }
94        }
95
96        // 1D DCT on rows
97        self.dct_8_1d(&mut rows);
98
99        // Transpose
100        let rows = self.simd.transpose_8x8_i16(&rows);
101
102        // 1D DCT on columns
103        let mut cols = rows;
104        self.dct_8_1d(&mut cols);
105
106        // Transpose back and store
107        let result = self.simd.transpose_8x8_i16(&cols);
108        for i in 0..8 {
109            for j in 0..8 {
110                output[i * 8 + j] = result[i][j];
111            }
112        }
113    }
114
115    /// Inverse DCT 8x8 transform.
116    pub fn inverse_dct_8x8(&self, input: &[i16; 64], output: &mut [i16; 64]) {
117        use crate::simd::types::I16x8;
118
119        let mut rows = [I16x8::zero(); 8];
120        for i in 0..8 {
121            for j in 0..8 {
122                rows[i][j] = input[i * 8 + j];
123            }
124        }
125
126        // 1D IDCT on rows
127        self.idct_8_1d(&mut rows);
128
129        // Transpose
130        let rows = self.simd.transpose_8x8_i16(&rows);
131
132        // 1D IDCT on columns
133        let mut cols = rows;
134        self.idct_8_1d(&mut cols);
135
136        // Transpose back and store
137        let result = self.simd.transpose_8x8_i16(&cols);
138        for i in 0..8 {
139            for j in 0..8 {
140                output[i * 8 + j] = result[i][j];
141            }
142        }
143    }
144
145    /// Forward ADST 4x4 transform.
146    ///
147    /// Asymmetric Discrete Sine Transform used for directional prediction residuals.
148    pub fn forward_adst_4x4(&self, input: &[i16; 16], output: &mut [i16; 16]) {
149        use crate::simd::types::I16x8;
150
151        let mut rows = [I16x8::zero(); 4];
152        for i in 0..4 {
153            for j in 0..4 {
154                rows[i][j] = input[i * 4 + j];
155            }
156        }
157
158        // 1D ADST on rows
159        self.adst_4_1d(&mut rows);
160
161        // Transpose
162        let rows = self.simd.transpose_4x4_i16(&rows);
163
164        // 1D ADST on columns
165        let mut cols = rows;
166        self.adst_4_1d(&mut cols);
167
168        // Transpose back and store
169        let result = self.simd.transpose_4x4_i16(&cols);
170        for i in 0..4 {
171            for j in 0..4 {
172                output[i * 4 + j] = result[i][j];
173            }
174        }
175    }
176
177    /// Inverse ADST 4x4 transform.
178    pub fn inverse_adst_4x4(&self, input: &[i16; 16], output: &mut [i16; 16]) {
179        use crate::simd::types::I16x8;
180
181        let mut rows = [I16x8::zero(); 4];
182        for i in 0..4 {
183            for j in 0..4 {
184                rows[i][j] = input[i * 4 + j];
185            }
186        }
187
188        // 1D inverse ADST on rows
189        self.iadst_4_1d(&mut rows);
190
191        // Transpose
192        let rows = self.simd.transpose_4x4_i16(&rows);
193
194        // 1D inverse ADST on columns
195        let mut cols = rows;
196        self.iadst_4_1d(&mut cols);
197
198        // Transpose back and store
199        let result = self.simd.transpose_4x4_i16(&cols);
200        for i in 0..4 {
201            for j in 0..4 {
202                output[i * 4 + j] = result[i][j];
203            }
204        }
205    }
206
207    /// Identity transform (no transform, just scaling).
208    pub fn identity_4x4(&self, input: &[i16; 16], output: &mut [i16; 16]) {
209        // Identity transform for AV1: output = input * sqrt(2)
210        // Approximated as: output = (input * 181 + 128) >> 8
211        for i in 0..16 {
212            let scaled = i32::from(input[i]) * 181 + 128;
213            output[i] = (scaled >> 8) as i16;
214        }
215    }
216
217    // ========================================================================
218    // Internal 1D Transform Helpers
219    // ========================================================================
220
221    /// 1D DCT-4 on 4 vectors.
222    fn dct_4_1d(&self, rows: &mut [crate::simd::types::I16x8; 4]) {
223        // Simplified DCT-4 using butterfly operations
224        // Stage 1: butterflies
225        let (s0, s3) = self.simd.butterfly_i16x8(rows[0], rows[3]);
226        let (s1, s2) = self.simd.butterfly_i16x8(rows[1], rows[2]);
227
228        // Stage 2: butterflies and rotations
229        let (x0, x1) = self.simd.butterfly_i16x8(s0, s1);
230        let (x3, x2) = self.simd.butterfly_i16x8(s3, s2);
231
232        rows[0] = x0;
233        rows[1] = x2;
234        rows[2] = x1;
235        rows[3] = x3;
236    }
237
238    /// 1D IDCT-4 on 4 vectors.
239    fn idct_4_1d(&self, rows: &mut [crate::simd::types::I16x8; 4]) {
240        // Inverse DCT-4
241        let t0 = rows[0];
242        let t1 = rows[2];
243        let t2 = rows[1];
244        let t3 = rows[3];
245
246        // Stage 1
247        let (s0, s1) = self.simd.butterfly_i16x8(t0, t2);
248        let (s3, s2) = self.simd.butterfly_i16x8(t3, t1);
249
250        // Stage 2
251        let (x0, x3) = self.simd.butterfly_i16x8(s0, s3);
252        let (x1, x2) = self.simd.butterfly_i16x8(s1, s2);
253
254        rows[0] = x0;
255        rows[1] = x1;
256        rows[2] = x2;
257        rows[3] = x3;
258    }
259
260    /// 1D DCT-8 on 8 vectors.
261    fn dct_8_1d(&self, rows: &mut [crate::simd::types::I16x8; 8]) {
262        // Simplified DCT-8 butterfly structure
263        // Stage 1
264        let (s0, s7) = self.simd.butterfly_i16x8(rows[0], rows[7]);
265        let (s1, s6) = self.simd.butterfly_i16x8(rows[1], rows[6]);
266        let (s2, s5) = self.simd.butterfly_i16x8(rows[2], rows[5]);
267        let (s3, s4) = self.simd.butterfly_i16x8(rows[3], rows[4]);
268
269        // Stage 2
270        let (t0, t3) = self.simd.butterfly_i16x8(s0, s3);
271        let (t1, t2) = self.simd.butterfly_i16x8(s1, s2);
272        let (t4, t7) = self.simd.butterfly_i16x8(s4, s7);
273        let (t5, t6) = self.simd.butterfly_i16x8(s5, s6);
274
275        // Stage 3
276        let (u0, u1) = self.simd.butterfly_i16x8(t0, t1);
277        let (u2, u3) = self.simd.butterfly_i16x8(t2, t3);
278        let (u4, u5) = self.simd.butterfly_i16x8(t4, t5);
279        let (u6, u7) = self.simd.butterfly_i16x8(t6, t7);
280
281        rows[0] = u0;
282        rows[1] = u4;
283        rows[2] = u2;
284        rows[3] = u6;
285        rows[4] = u1;
286        rows[5] = u5;
287        rows[6] = u3;
288        rows[7] = u7;
289    }
290
291    /// 1D IDCT-8 on 8 vectors.
292    fn idct_8_1d(&self, rows: &mut [crate::simd::types::I16x8; 8]) {
293        // Inverse DCT-8
294        let t0 = rows[0];
295        let t4 = rows[1];
296        let t2 = rows[2];
297        let t6 = rows[3];
298        let t1 = rows[4];
299        let t5 = rows[5];
300        let t3 = rows[6];
301        let t7 = rows[7];
302
303        // Stage 1
304        let (s0, s1) = self.simd.butterfly_i16x8(t0, t1);
305        let (s2, s3) = self.simd.butterfly_i16x8(t2, t3);
306        let (s4, s5) = self.simd.butterfly_i16x8(t4, t5);
307        let (s6, s7) = self.simd.butterfly_i16x8(t6, t7);
308
309        // Stage 2
310        let (u0, u3) = self.simd.butterfly_i16x8(s0, s3);
311        let (u1, u2) = self.simd.butterfly_i16x8(s1, s2);
312        let (u4, u7) = self.simd.butterfly_i16x8(s4, s7);
313        let (u5, u6) = self.simd.butterfly_i16x8(s5, s6);
314
315        // Stage 3
316        let (x0, x7) = self.simd.butterfly_i16x8(u0, u7);
317        let (x1, x6) = self.simd.butterfly_i16x8(u1, u6);
318        let (x2, x5) = self.simd.butterfly_i16x8(u2, u5);
319        let (x3, x4) = self.simd.butterfly_i16x8(u3, u4);
320
321        rows[0] = x0;
322        rows[1] = x1;
323        rows[2] = x2;
324        rows[3] = x3;
325        rows[4] = x4;
326        rows[5] = x5;
327        rows[6] = x6;
328        rows[7] = x7;
329    }
330
331    /// 1D ADST-4 on 4 vectors.
332    fn adst_4_1d(&self, rows: &mut [crate::simd::types::I16x8; 4]) {
333        // ADST-4 approximation using rotations
334        // This is a simplified version for demonstration
335        let s0 = rows[0];
336        let s1 = rows[1];
337        let s2 = rows[2];
338        let s3 = rows[3];
339
340        // Apply ADST matrix (simplified)
341        let t0 = self.simd.add_i16x8(s0, s3);
342        let t1 = self.simd.add_i16x8(s1, s2);
343        let t2 = self.simd.sub_i16x8(s1, s2);
344        let t3 = self.simd.sub_i16x8(s0, s3);
345
346        rows[0] = t0;
347        rows[1] = t2;
348        rows[2] = t1;
349        rows[3] = t3;
350    }
351
352    /// 1D inverse ADST-4 on 4 vectors.
353    fn iadst_4_1d(&self, rows: &mut [crate::simd::types::I16x8; 4]) {
354        // Inverse ADST-4 (transpose of forward ADST)
355        let t0 = rows[0];
356        let t2 = rows[1];
357        let t1 = rows[2];
358        let t3 = rows[3];
359
360        let s0 = self.simd.add_i16x8(t0, t3);
361        let s1 = self.simd.add_i16x8(t1, t2);
362        let s2 = self.simd.sub_i16x8(t1, t2);
363        let s3 = self.simd.sub_i16x8(t0, t3);
364
365        rows[0] = s0;
366        rows[1] = s1;
367        rows[2] = s2;
368        rows[3] = s3;
369    }
370}