Skip to main content

wmv_decoder/
na_simple_idct.rs

1#[inline(always)]
2fn clip_u8(x: i32) -> u8 {
3    if x < 0 {
4        0
5    } else if x > 255 {
6        255
7    } else {
8        x as u8
9    }
10}
11
12// --- Constants (BIT_DEPTH=8) ---
13const W1: i64 = 22725;
14const W2: i64 = 21407;
15const W3: i64 = 19266;
16const W4: i64 = 16383;
17const W5: i64 = 12873;
18const W6: i64 = 8867;
19const W7: i64 = 4520;
20
21const ROW_SHIFT: i32 = 11;
22const COL_SHIFT: i32 = 20;
23const DC_SHIFT: i32 = 3;
24
25// ((1<<(COL_SHIFT-1))/W4) in upstream (integer division)
26const COL_RND_W4_DIV: i16 = ((1i64 << (COL_SHIFT - 1)) / W4) as i16;
27
28#[inline(always)]
29fn idct_row_cond_dc_int16_8bit(row: &mut [i16; 8]) {
30    // DC-only shortcut (matches upstream's int16 path semantics).
31    if row[1] == 0
32        && row[2] == 0
33        && row[3] == 0
34        && row[4] == 0
35        && row[5] == 0
36        && row[6] == 0
37        && row[7] == 0
38    {
39        let t: i16 = (((row[0] as i32) << DC_SHIFT) as i16);
40        *row = [t; 8];
41        return;
42    }
43
44    // Use i64 for safety; upstream uses carefully-sized unsigned intermediates.
45    let r0 = row[0] as i64;
46    let r1 = row[1] as i64;
47    let r2 = row[2] as i64;
48    let r3 = row[3] as i64;
49    let r4 = row[4] as i64;
50    let r5 = row[5] as i64;
51    let r6 = row[6] as i64;
52    let r7 = row[7] as i64;
53
54    let mut a0 = W4 * r0 + (1i64 << (ROW_SHIFT - 1));
55    let mut a1 = a0;
56    let mut a2 = a0;
57    let mut a3 = a0;
58
59    a0 += W2 * r2;
60    a1 += W6 * r2;
61    a2 -= W6 * r2;
62    a3 -= W2 * r2;
63
64    let mut b0 = W1 * r1 + W3 * r3;
65    let mut b1 = W3 * r1 - W7 * r3;
66    let mut b2 = W5 * r1 - W1 * r3;
67    let mut b3 = W7 * r1 - W5 * r3;
68
69    if r4 != 0 || r5 != 0 || r6 != 0 || r7 != 0 {
70        a0 += W4 * r4 + W6 * r6;
71        a1 += -W4 * r4 - W2 * r6;
72        a2 += -W4 * r4 + W2 * r6;
73        a3 += W4 * r4 - W6 * r6;
74
75        b0 += W5 * r5 + W7 * r7;
76        b1 += -W1 * r5 - W5 * r7;
77        b2 += W7 * r5 + W3 * r7;
78        b3 += W3 * r5 - W1 * r7;
79    }
80
81    let rs = ROW_SHIFT as i64;
82    row[0] = ((a0 + b0) >> rs) as i16;
83    row[7] = ((a0 - b0) >> rs) as i16;
84    row[1] = ((a1 + b1) >> rs) as i16;
85    row[6] = ((a1 - b1) >> rs) as i16;
86    row[2] = ((a2 + b2) >> rs) as i16;
87    row[5] = ((a2 - b2) >> rs) as i16;
88    row[3] = ((a3 + b3) >> rs) as i16;
89    row[4] = ((a3 - b3) >> rs) as i16;
90}
91
92#[inline(always)]
93fn idct_sparse_col_int16_8bit(block: &mut [i16; 64], col: usize) {
94    // Column elements are block[col + 8*r]
95    let c0 = block[col + 8 * 0] as i64;
96    let c1 = block[col + 8 * 1] as i64;
97    let c2 = block[col + 8 * 2] as i64;
98    let c3 = block[col + 8 * 3] as i64;
99    let c4 = block[col + 8 * 4] as i64;
100    let c5 = block[col + 8 * 5] as i64;
101    let c6 = block[col + 8 * 6] as i64;
102    let c7 = block[col + 8 * 7] as i64;
103
104    let mut a0 = W4 * (c0 + COL_RND_W4_DIV as i64);
105    let mut a1 = a0;
106    let mut a2 = a0;
107    let mut a3 = a0;
108
109    a0 += W2 * c2;
110    a1 += W6 * c2;
111    a2 -= W6 * c2;
112    a3 -= W2 * c2;
113
114    let mut b0 = W1 * c1 + W3 * c3;
115    let mut b1 = W3 * c1 - W7 * c3;
116    let mut b2 = W5 * c1 - W1 * c3;
117    let mut b3 = W7 * c1 - W5 * c3;
118
119    if c4 != 0 {
120        a0 += W4 * c4;
121        a1 += -W4 * c4;
122        a2 += -W4 * c4;
123        a3 += W4 * c4;
124    }
125    if c5 != 0 {
126        b0 += W5 * c5;
127        b1 += -W1 * c5;
128        b2 += W7 * c5;
129        b3 += W3 * c5;
130    }
131    if c6 != 0 {
132        a0 += W6 * c6;
133        a1 += -W2 * c6;
134        a2 += W2 * c6;
135        a3 += -W6 * c6;
136    }
137    if c7 != 0 {
138        b0 += W7 * c7;
139        b1 += -W5 * c7;
140        b2 += W3 * c7;
141        b3 += -W1 * c7;
142    }
143
144    let cs = COL_SHIFT as i64;
145    block[col + 8 * 0] = ((a0 + b0) >> cs) as i16;
146    block[col + 8 * 1] = ((a1 + b1) >> cs) as i16;
147    block[col + 8 * 2] = ((a2 + b2) >> cs) as i16;
148    block[col + 8 * 3] = ((a3 + b3) >> cs) as i16;
149    block[col + 8 * 4] = ((a3 - b3) >> cs) as i16;
150    block[col + 8 * 5] = ((a2 - b2) >> cs) as i16;
151    block[col + 8 * 6] = ((a1 - b1) >> cs) as i16;
152    block[col + 8 * 7] = ((a0 - b0) >> cs) as i16;
153}
154
155#[inline(always)]
156fn idct_sparse_col_add_int16_8bit(
157    dest: &mut [u8],
158    dest_off: usize,
159    line_size: usize,
160    block: &[i16; 64],
161    col: usize,
162) {
163    let c0 = block[col + 8 * 0] as i64;
164    let c1 = block[col + 8 * 1] as i64;
165    let c2 = block[col + 8 * 2] as i64;
166    let c3 = block[col + 8 * 3] as i64;
167    let c4 = block[col + 8 * 4] as i64;
168    let c5 = block[col + 8 * 5] as i64;
169    let c6 = block[col + 8 * 6] as i64;
170    let c7 = block[col + 8 * 7] as i64;
171
172    let mut a0 = W4 * (c0 + COL_RND_W4_DIV as i64);
173    let mut a1 = a0;
174    let mut a2 = a0;
175    let mut a3 = a0;
176
177    a0 += W2 * c2;
178    a1 += W6 * c2;
179    a2 -= W6 * c2;
180    a3 -= W2 * c2;
181
182    let mut b0 = W1 * c1 + W3 * c3;
183    let mut b1 = W3 * c1 - W7 * c3;
184    let mut b2 = W5 * c1 - W1 * c3;
185    let mut b3 = W7 * c1 - W5 * c3;
186
187    if c4 != 0 {
188        a0 += W4 * c4;
189        a1 += -W4 * c4;
190        a2 += -W4 * c4;
191        a3 += W4 * c4;
192    }
193    if c5 != 0 {
194        b0 += W5 * c5;
195        b1 += -W1 * c5;
196        b2 += W7 * c5;
197        b3 += W3 * c5;
198    }
199    if c6 != 0 {
200        a0 += W6 * c6;
201        a1 += -W2 * c6;
202        a2 += W2 * c6;
203        a3 += -W6 * c6;
204    }
205    if c7 != 0 {
206        b0 += W7 * c7;
207        b1 += -W5 * c7;
208        b2 += W3 * c7;
209        b3 += -W1 * c7;
210    }
211
212    let cs = COL_SHIFT as i64;
213    let vals = [
214        ((a0 + b0) >> cs) as i32,
215        ((a1 + b1) >> cs) as i32,
216        ((a2 + b2) >> cs) as i32,
217        ((a3 + b3) >> cs) as i32,
218        ((a3 - b3) >> cs) as i32,
219        ((a2 - b2) >> cs) as i32,
220        ((a1 - b1) >> cs) as i32,
221        ((a0 - b0) >> cs) as i32,
222    ];
223
224    for r in 0..8usize {
225        let off = dest_off + r * line_size + col;
226        if off < dest.len() {
227            let cur = dest[off] as i32;
228            dest[off] = clip_u8(cur + vals[r]);
229        }
230    }
231}
232
233/// In-place 8x8 IDCT: equivalent to upstream `ff_simple_idct_int16_8bit`.
234pub fn ff_simple_idct_int16_8bit(block: &mut [i16; 64]) {
235    for r in 0..8usize {
236        let mut row = [0i16; 8];
237        for c in 0..8usize {
238            row[c] = block[r * 8 + c];
239        }
240        idct_row_cond_dc_int16_8bit(&mut row);
241        for c in 0..8usize {
242            block[r * 8 + c] = row[c];
243        }
244    }
245    for c in 0..8usize {
246        idct_sparse_col_int16_8bit(block, c);
247    }
248}
249
250/// Add an 8x8 IDCT block into destination: equivalent to upstream `ff_simple_idct_add_int16_8bit`.
251pub fn ff_simple_idct_add_int16_8bit(
252    dest: &mut [u8],
253    dest_off: usize,
254    line_size: usize,
255    block: &mut [i16; 64],
256) {
257    // Row transform in-place
258    for r in 0..8usize {
259        let mut row = [0i16; 8];
260        for c in 0..8usize {
261            row[c] = block[r * 8 + c];
262        }
263        idct_row_cond_dc_int16_8bit(&mut row);
264        for c in 0..8usize {
265            block[r * 8 + c] = row[c];
266        }
267    }
268    // Column add (without overwriting block, like upstream idctSparseColAdd)
269    let tmp = *block;
270    for c in 0..8usize {
271        idct_sparse_col_add_int16_8bit(dest, dest_off, line_size, &tmp, c);
272    }
273}
274
275// --- WMV2 ABT helpers (ported from upstream simple_idct.c) ---
276
277const CN_SHIFT: i32 = 12;
278const RN_SHIFT: i32 = 15;
279const C_SHIFT: i32 = 17; // (4+1+12)
280const R_SHIFT: i32 = 11;
281
282// Values computed exactly as upstream C_FIX/R_FIX with M_SQRT2 and +0.5 rounding.
283const C1: i64 = 3784;
284const C2: i64 = 1567;
285const C3: i64 = 2896;
286
287const R1: i64 = 30274;
288const R2: i64 = 12540;
289const R3: i64 = 23170;
290
291#[inline(always)]
292fn idct4col_add(
293    dest: &mut [u8],
294    dest_off: usize,
295    line_size: usize,
296    col: &[i16; 64],
297    col_idx: usize,
298) {
299    // col points to block + i (column i), but in upstream idct4col_add reads col[8*0..8*3]
300    let a0 = col[col_idx + 8 * 0] as i64;
301    let a1 = col[col_idx + 8 * 1] as i64;
302    let a2 = col[col_idx + 8 * 2] as i64;
303    let a3 = col[col_idx + 8 * 3] as i64;
304
305    let c0 = (a0 + a2) * C3 + (1i64 << (C_SHIFT - 1));
306    let c2 = (a0 - a2) * C3 + (1i64 << (C_SHIFT - 1));
307    let c1 = a1 * C1 + a3 * C2;
308    let c3 = a1 * C2 - a3 * C1;
309
310    let out = [
311        ((c0 + c1) >> C_SHIFT) as i32,
312        ((c2 + c3) >> C_SHIFT) as i32,
313        ((c2 - c3) >> C_SHIFT) as i32,
314        ((c0 - c1) >> C_SHIFT) as i32,
315    ];
316
317    for r in 0..4usize {
318        let off = dest_off + r * line_size;
319        if off < dest.len() {
320            let cur = dest[off] as i32;
321            dest[off] = clip_u8(cur + out[r]);
322        }
323    }
324}
325
326#[inline(always)]
327fn idct4row(row: &mut [i16; 8]) {
328    // Operates on row[0..3] only (upstream's idct4row)
329    let a0 = row[0] as i64;
330    let a1 = row[1] as i64;
331    let a2 = row[2] as i64;
332    let a3 = row[3] as i64;
333
334    let c0 = (a0 + a2) * R3 + (1i64 << (R_SHIFT - 1));
335    let c2 = (a0 - a2) * R3 + (1i64 << (R_SHIFT - 1));
336    let c1 = a1 * R1 + a3 * R2;
337    let c3 = a1 * R2 - a3 * R1;
338
339    row[0] = ((c0 + c1) >> R_SHIFT) as i16;
340    row[1] = ((c2 + c3) >> R_SHIFT) as i16;
341    row[2] = ((c2 - c3) >> R_SHIFT) as i16;
342    row[3] = ((c0 - c1) >> R_SHIFT) as i16;
343}
344
345/// WMV2 ABT: add an 8x4 IDCT block (top or bottom half). Equivalent to upstream `ff_simple_idct84_add`.
346pub fn ff_simple_idct84_add(
347    dest: &mut [u8],
348    dest_off: usize,
349    line_size: usize,
350    block: &mut [i16; 64],
351) {
352    // IDCT8 on each of the first 4 rows
353    for r in 0..4usize {
354        let mut row = [0i16; 8];
355        for c in 0..8usize {
356            row[c] = block[r * 8 + c];
357        }
358        idct_row_cond_dc_int16_8bit(&mut row);
359        for c in 0..8usize {
360            block[r * 8 + c] = row[c];
361        }
362    }
363    let snap = *block;
364    for c in 0..8usize {
365        idct4col_add(dest, dest_off + c, line_size, &snap, c);
366    }
367}
368
369/// WMV2 ABT: add a 4x8 IDCT block (left or right half). Equivalent to upstream `ff_simple_idct48_add`.
370pub fn ff_simple_idct48_add(
371    dest: &mut [u8],
372    dest_off: usize,
373    line_size: usize,
374    block: &mut [i16; 64],
375) {
376    // IDCT4 on each line (8 rows)
377    for r in 0..8usize {
378        let mut row = [0i16; 8];
379        for c in 0..8usize {
380            row[c] = block[r * 8 + c];
381        }
382        idct4row(&mut row);
383        for c in 0..8usize {
384            block[r * 8 + c] = row[c];
385        }
386    }
387    // IDCT8 and store for first 4 columns
388    let snap = *block;
389    for c in 0..4usize {
390        idct_sparse_col_add_int16_8bit(dest, dest_off, line_size, &snap, c);
391    }
392}