Skip to main content

atmos/os_lib/
h264_decode.rs

1// h264_decode.rs - H.264 baseline スライス/マクロブロック層 + CAVLC 残差復号 + 再構成。
2//
3// h264.rs(BitReader / SPS-PPS / イントラ予測 / IDCT)の上に、I スライスの
4// 画素復元を実装する。検証はゴールデン素材(h264_golden.rs)で行う。
5// 現状の対応: I スライス、I_16x16 と I_4x4、CAVLC、4:2:0。
6// 未対応: P/B、デブロッキング、CABAC、スライスグループ。
7#![allow(dead_code, clippy::needless_range_loop)]
8
9use super::h264::{
10    dequant_4x4, extract_rbsp, intra_16x16_predict, intra_4x4_predict, intra_chroma_predict,
11    inverse_transform_4x4, parse_avcc, parse_pps, parse_sps, split_annexb, split_avcc, BitReader,
12    Pps, Sps, NORM_ADJUST_4X4,
13};
14use alloc::vec::Vec;
15
16// 4x4 ジグザグスキャン(スキャン位置 -> ラスタ index)。
17const ZIGZAG_4X4: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
18
19// 1 MB 内の 16 個の輝度 4x4 ブロックのラスタ左上座標(画素)。
20// ブロック index は標準の 4x4 luma block 走査順(8x8 ごとに z スキャン)。
21const LUMA_4X4_BLOCK_XY: [(usize, usize); 16] = [
22    (0, 0),
23    (4, 0),
24    (0, 4),
25    (4, 4),
26    (8, 0),
27    (12, 0),
28    (8, 4),
29    (12, 4),
30    (0, 8),
31    (4, 8),
32    (0, 12),
33    (4, 12),
34    (8, 8),
35    (12, 8),
36    (8, 12),
37    (12, 12),
38];
39
40fn clip1(v: i32) -> i32 {
41    v.clamp(0, 255)
42}
43
44// ===================== CAVLC テーブル =====================
45// 各エントリ: (len, code, total_coeff, trailing_ones)。プレフィックスフリーなので
46// ビットを1つずつ読みながら (len, code) 完全一致で確定する。
47
48type TokEntry = (u8, u16, u8, u8);
49
50// Table 9-5: 0 <= nC < 2
51const COEFF_TOKEN_0: &[TokEntry] = &[
52    (1, 0b1, 0, 0),
53    (6, 0b000101, 1, 0),
54    (2, 0b01, 1, 1),
55    (8, 0b00000111, 2, 0),
56    (6, 0b000100, 2, 1),
57    (3, 0b001, 2, 2),
58    (9, 0b000000111, 3, 0),
59    (8, 0b00000110, 3, 1),
60    (7, 0b0000101, 3, 2),
61    (5, 0b00011, 3, 3),
62    (10, 0b0000000111, 4, 0),
63    (9, 0b000000110, 4, 1),
64    (8, 0b00000101, 4, 2),
65    (6, 0b000011, 4, 3),
66    (11, 0b00000000111, 5, 0),
67    (10, 0b0000000110, 5, 1),
68    (9, 0b000000101, 5, 2),
69    (7, 0b0000100, 5, 3),
70    (13, 0b0000000001111, 6, 0),
71    (11, 0b00000000110, 6, 1),
72    (10, 0b0000000101, 6, 2),
73    (8, 0b00000100, 6, 3),
74    (13, 0b0000000001011, 7, 0),
75    (13, 0b0000000001110, 7, 1),
76    (11, 0b00000000101, 7, 2),
77    (9, 0b000000100, 7, 3),
78    (13, 0b0000000001000, 8, 0),
79    (13, 0b0000000001010, 8, 1),
80    (13, 0b0000000001101, 8, 2),
81    (10, 0b0000000100, 8, 3),
82    (14, 0b00000000001111, 9, 0),
83    (14, 0b00000000001110, 9, 1),
84    (13, 0b0000000001001, 9, 2),
85    (11, 0b00000000100, 9, 3),
86    (14, 0b00000000001011, 10, 0),
87    (14, 0b00000000001010, 10, 1),
88    (14, 0b00000000001101, 10, 2),
89    (13, 0b0000000001100, 10, 3),
90    (15, 0b000000000001111, 11, 0),
91    (15, 0b000000000001110, 11, 1),
92    (14, 0b00000000001001, 11, 2),
93    (14, 0b00000000001100, 11, 3),
94    (15, 0b000000000001011, 12, 0),
95    (15, 0b000000000001010, 12, 1),
96    (15, 0b000000000001101, 12, 2),
97    (14, 0b00000000001000, 12, 3),
98    (16, 0b0000000000001111, 13, 0),
99    (15, 0b000000000000001, 13, 1),
100    (15, 0b000000000001001, 13, 2),
101    (15, 0b000000000001100, 13, 3),
102    (16, 0b0000000000001011, 14, 0),
103    (16, 0b0000000000001110, 14, 1),
104    (16, 0b0000000000001101, 14, 2),
105    (15, 0b000000000001000, 14, 3),
106    (16, 0b0000000000000111, 15, 0),
107    (16, 0b0000000000001010, 15, 1),
108    (16, 0b0000000000001001, 15, 2),
109    (16, 0b0000000000001100, 15, 3),
110    (16, 0b0000000000000100, 16, 0),
111    (16, 0b0000000000000110, 16, 1),
112    (16, 0b0000000000000101, 16, 2),
113    (16, 0b0000000000001000, 16, 3),
114];
115
116// Table 9-5: chroma DC, 4:2:0(nC == -1)
117const COEFF_TOKEN_CHROMA_DC: &[TokEntry] = &[
118    (2, 0b01, 0, 0),
119    (6, 0b000111, 1, 0),
120    (1, 0b1, 1, 1),
121    (6, 0b000100, 2, 0),
122    (6, 0b000110, 2, 1),
123    (3, 0b001, 2, 2),
124    (6, 0b000011, 3, 0),
125    (7, 0b0000011, 3, 1),
126    (7, 0b0000010, 3, 2),
127    (6, 0b000101, 3, 3),
128    (6, 0b000010, 4, 0),
129    (8, 0b00000011, 4, 1),
130    (8, 0b00000010, 4, 2),
131    (7, 0b0000000, 4, 3),
132];
133
134/// coeff_token を読み (total_coeff, trailing_ones) を返す。table は上記のいずれか。
135fn read_coeff_token(r: &mut BitReader, table: &[TokEntry]) -> Option<(u8, u8)> {
136    let mut code: u16 = 0;
137    for len in 1..=16u8 {
138        code = (code << 1) | (r.read_bit() as u16);
139        for &(l, c, tc, t1) in table {
140            if l == len && c == code {
141                return Some((tc, t1));
142            }
143        }
144    }
145    None
146}
147
148// Table 9-5: 2 <= nC < 4
149const COEFF_TOKEN_2: &[TokEntry] = &[
150    (2, 0b11, 0, 0),
151    (6, 0b001011, 1, 0),
152    (2, 0b10, 1, 1),
153    (6, 0b000111, 2, 0),
154    (5, 0b00111, 2, 1),
155    (3, 0b011, 2, 2),
156    (7, 0b0000111, 3, 0),
157    (6, 0b001010, 3, 1),
158    (6, 0b001001, 3, 2),
159    (4, 0b0101, 3, 3),
160    (8, 0b00000111, 4, 0),
161    (6, 0b000110, 4, 1),
162    (6, 0b000101, 4, 2),
163    (4, 0b0100, 4, 3),
164    (8, 0b00000100, 5, 0),
165    (7, 0b0000110, 5, 1),
166    (7, 0b0000101, 5, 2),
167    (5, 0b00110, 5, 3),
168    (9, 0b000000111, 6, 0),
169    (8, 0b00000110, 6, 1),
170    (8, 0b00000101, 6, 2),
171    (6, 0b001000, 6, 3),
172    (11, 0b00000001111, 7, 0),
173    (9, 0b000000110, 7, 1),
174    (9, 0b000000101, 7, 2),
175    (6, 0b000100, 7, 3),
176    (11, 0b00000001011, 8, 0),
177    (11, 0b00000001110, 8, 1),
178    (11, 0b00000001101, 8, 2),
179    (7, 0b0000100, 8, 3),
180    (12, 0b000000001111, 9, 0),
181    (11, 0b00000001010, 9, 1),
182    (11, 0b00000001001, 9, 2),
183    (9, 0b000000100, 9, 3),
184    (12, 0b000000001011, 10, 0),
185    (12, 0b000000001110, 10, 1),
186    (12, 0b000000001101, 10, 2),
187    (11, 0b00000001100, 10, 3),
188    (12, 0b000000001000, 11, 0),
189    (12, 0b000000001010, 11, 1),
190    (12, 0b000000001001, 11, 2),
191    (11, 0b00000001000, 11, 3),
192    (13, 0b0000000001111, 12, 0),
193    (13, 0b0000000001110, 12, 1),
194    (13, 0b0000000001101, 12, 2),
195    (12, 0b000000001100, 12, 3),
196    (13, 0b0000000001011, 13, 0),
197    (13, 0b0000000001010, 13, 1),
198    (13, 0b0000000001001, 13, 2),
199    (13, 0b0000000001100, 13, 3),
200    (13, 0b0000000000111, 14, 0),
201    (14, 0b00000000001011, 14, 1),
202    (13, 0b0000000000110, 14, 2),
203    (13, 0b0000000001000, 14, 3),
204    (14, 0b00000000001001, 15, 0),
205    (14, 0b00000000001000, 15, 1),
206    (14, 0b00000000001010, 15, 2),
207    (13, 0b0000000000001, 15, 3),
208    (14, 0b00000000000111, 16, 0),
209    (14, 0b00000000000110, 16, 1),
210    (14, 0b00000000000101, 16, 2),
211    (14, 0b00000000000100, 16, 3),
212];
213
214// Table 9-5: 4 <= nC < 8
215const COEFF_TOKEN_4: &[TokEntry] = &[
216    (4, 0b1111, 0, 0),
217    (6, 0b001111, 1, 0),
218    (4, 0b1110, 1, 1),
219    (6, 0b001011, 2, 0),
220    (5, 0b01111, 2, 1),
221    (4, 0b1101, 2, 2),
222    (6, 0b001000, 3, 0),
223    (5, 0b01100, 3, 1),
224    (5, 0b01110, 3, 2),
225    (4, 0b1100, 3, 3),
226    (7, 0b0001111, 4, 0),
227    (5, 0b01010, 4, 1),
228    (5, 0b01011, 4, 2),
229    (4, 0b1011, 4, 3),
230    (7, 0b0001011, 5, 0),
231    (5, 0b01000, 5, 1),
232    (5, 0b01001, 5, 2),
233    (4, 0b1010, 5, 3),
234    (7, 0b0001001, 6, 0),
235    (6, 0b001110, 6, 1),
236    (6, 0b001101, 6, 2),
237    (4, 0b1001, 6, 3),
238    (7, 0b0001000, 7, 0),
239    (6, 0b001010, 7, 1),
240    (6, 0b001001, 7, 2),
241    (4, 0b1000, 7, 3),
242    (8, 0b00001111, 8, 0),
243    (7, 0b0001110, 8, 1),
244    (7, 0b0001101, 8, 2),
245    (5, 0b01101, 8, 3),
246    (8, 0b00001011, 9, 0),
247    (8, 0b00001110, 9, 1),
248    (7, 0b0001010, 9, 2),
249    (6, 0b001100, 9, 3),
250    (9, 0b000001111, 10, 0),
251    (8, 0b00001010, 10, 1),
252    (8, 0b00001101, 10, 2),
253    (7, 0b0001100, 10, 3),
254    (9, 0b000001011, 11, 0),
255    (9, 0b000001110, 11, 1),
256    (8, 0b00001001, 11, 2),
257    (8, 0b00001100, 11, 3),
258    (9, 0b000001000, 12, 0),
259    (9, 0b000001010, 12, 1),
260    (9, 0b000001101, 12, 2),
261    (8, 0b00001000, 12, 3),
262    (10, 0b0000001101, 13, 0),
263    (9, 0b000000111, 13, 1),
264    (9, 0b000001001, 13, 2),
265    (9, 0b000001100, 13, 3),
266    (10, 0b0000001001, 14, 0),
267    (10, 0b0000001100, 14, 1),
268    (10, 0b0000001011, 14, 2),
269    (10, 0b0000001010, 14, 3),
270    (10, 0b0000000101, 15, 0),
271    (10, 0b0000001000, 15, 1),
272    (10, 0b0000000111, 15, 2),
273    (10, 0b0000000110, 15, 3),
274    (10, 0b0000000001, 16, 0),
275    (10, 0b0000000100, 16, 1),
276    (10, 0b0000000011, 16, 2),
277    (10, 0b0000000010, 16, 3),
278];
279
280/// coeff_token を nC に応じて復号。nC>=8 は 6bit FLC、nC==-1 はクロマ DC 表。
281fn read_coeff_token_nc(r: &mut BitReader, n_c: i32) -> Option<(u8, u8)> {
282    if n_c == -1 {
283        return read_coeff_token(r, COEFF_TOKEN_CHROMA_DC);
284    }
285    if n_c >= 8 {
286        let code = r.read_bits(6);
287        if code == 3 {
288            return Some((0, 0));
289        }
290        return Some(((code >> 2) as u8 + 1, (code & 3) as u8));
291    }
292    let table = if n_c < 2 {
293        COEFF_TOKEN_0
294    } else if n_c < 4 {
295        COEFF_TOKEN_2
296    } else {
297        COEFF_TOKEN_4
298    };
299    read_coeff_token(r, table)
300}
301
302// Table 9-4: coded_block_pattern の me(v) 復号。codeNum -> cbp(Intra_4x4, ChromaArrayType=1/2)。
303const CBP_INTRA_MAP: [u8; 48] = [
304    47, 31, 15, 0, 23, 27, 29, 30, 7, 11, 13, 14, 39, 43, 45, 46, 16, 3, 5, 10, 12, 19, 21, 26, 28,
305    35, 37, 42, 44, 1, 2, 4, 8, 17, 18, 20, 24, 6, 9, 22, 25, 32, 33, 34, 36, 40, 38, 41,
306];
307
308// Table 9-4: coded_block_pattern の me(v) 復号(インター MB 用、ffmpeg golomb_to_inter_cbp)。
309const CBP_INTER_MAP: [u8; 48] = [
310    0, 16, 1, 2, 4, 8, 32, 3, 5, 10, 12, 15, 47, 7, 11, 13, 14, 6, 9, 31, 35, 37, 42, 44, 33, 34,
311    36, 40, 39, 43, 45, 46, 17, 18, 20, 24, 19, 21, 26, 28, 23, 27, 29, 30, 22, 25, 38, 41,
312];
313
314// デブロッキング閾値(Table 8-16 / 8-17、ffmpeg の値から生成)。
315const ALPHA_TABLE: [u8; 52] = [
316    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 17, 20,
317    22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 71, 80, 90, 101, 113, 127, 144, 162, 182, 203, 226,
318    255, 255,
319];
320const BETA_TABLE: [u8; 52] = [
321    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 6, 6, 7, 7, 8, 8,
322    9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
323];
324// TC0_TABLE[indexA][bS-1](bS=1,2,3)。
325const TC0_TABLE: [[u8; 3]; 52] = [
326    [0, 0, 0],
327    [0, 0, 0],
328    [0, 0, 0],
329    [0, 0, 0],
330    [0, 0, 0],
331    [0, 0, 0],
332    [0, 0, 0],
333    [0, 0, 0],
334    [0, 0, 0],
335    [0, 0, 0],
336    [0, 0, 0],
337    [0, 0, 0],
338    [0, 0, 0],
339    [0, 0, 0],
340    [0, 0, 0],
341    [0, 0, 0],
342    [0, 0, 0],
343    [0, 0, 1],
344    [0, 0, 1],
345    [0, 0, 1],
346    [0, 0, 1],
347    [0, 1, 1],
348    [0, 1, 1],
349    [1, 1, 1],
350    [1, 1, 1],
351    [1, 1, 1],
352    [1, 1, 1],
353    [1, 1, 2],
354    [1, 1, 2],
355    [1, 1, 2],
356    [1, 1, 2],
357    [1, 2, 3],
358    [1, 2, 3],
359    [2, 2, 3],
360    [2, 2, 4],
361    [2, 3, 4],
362    [2, 3, 4],
363    [3, 3, 5],
364    [3, 4, 6],
365    [3, 4, 6],
366    [4, 5, 7],
367    [4, 5, 8],
368    [4, 6, 9],
369    [5, 7, 10],
370    [6, 8, 11],
371    [6, 8, 13],
372    [7, 10, 14],
373    [8, 11, 16],
374    [9, 12, 18],
375    [10, 13, 20],
376    [11, 15, 23],
377    [13, 17, 25],
378];
379
380// Table 9-7/9-8: total_zeros(maxNumCoeff=16)。行 = total_coeff(1..15)、
381// 各行 entry: (len, code) を total_zeros=0,1,2,... の順で並べる。
382const TOTAL_ZEROS_16: &[&[(u8, u16)]] = &[
383    &[
384        (1, 0b1),
385        (3, 0b011),
386        (3, 0b010),
387        (4, 0b0011),
388        (4, 0b0010),
389        (5, 0b00011),
390        (5, 0b00010),
391        (6, 0b000011),
392        (6, 0b000010),
393        (7, 0b0000011),
394        (7, 0b0000010),
395        (8, 0b00000011),
396        (8, 0b00000010),
397        (9, 0b000000011),
398        (9, 0b000000010),
399        (9, 0b000000001),
400    ],
401    &[
402        (3, 0b111),
403        (3, 0b110),
404        (3, 0b101),
405        (3, 0b100),
406        (3, 0b011),
407        (4, 0b0101),
408        (4, 0b0100),
409        (4, 0b0011),
410        (4, 0b0010),
411        (5, 0b00011),
412        (5, 0b00010),
413        (6, 0b000011),
414        (6, 0b000010),
415        (6, 0b000001),
416        (6, 0b000000),
417    ],
418    &[
419        (4, 0b0101),
420        (3, 0b111),
421        (3, 0b110),
422        (3, 0b101),
423        (4, 0b0100),
424        (4, 0b0011),
425        (3, 0b100),
426        (3, 0b011),
427        (4, 0b0010),
428        (5, 0b00011),
429        (5, 0b00010),
430        (6, 0b000001),
431        (5, 0b00001),
432        (6, 0b000000),
433    ],
434    &[
435        (5, 0b00011),
436        (3, 0b111),
437        (4, 0b0101),
438        (4, 0b0100),
439        (3, 0b110),
440        (3, 0b101),
441        (3, 0b100),
442        (4, 0b0011),
443        (3, 0b011),
444        (4, 0b0010),
445        (5, 0b00010),
446        (5, 0b00001),
447        (5, 0b00000),
448    ],
449    &[
450        (4, 0b0101),
451        (4, 0b0100),
452        (4, 0b0011),
453        (3, 0b111),
454        (3, 0b110),
455        (3, 0b101),
456        (3, 0b100),
457        (3, 0b011),
458        (4, 0b0010),
459        (5, 0b00001),
460        (4, 0b0001),
461        (5, 0b00000),
462    ],
463    &[
464        (6, 0b000001),
465        (5, 0b00001),
466        (3, 0b111),
467        (3, 0b110),
468        (3, 0b101),
469        (3, 0b100),
470        (3, 0b011),
471        (3, 0b010),
472        (4, 0b0001),
473        (3, 0b001),
474        (6, 0b000000),
475    ],
476    &[
477        (6, 0b000001),
478        (5, 0b00001),
479        (3, 0b101),
480        (3, 0b100),
481        (3, 0b011),
482        (2, 0b11),
483        (3, 0b010),
484        (4, 0b0001),
485        (3, 0b001),
486        (6, 0b000000),
487    ],
488    &[
489        (6, 0b000001),
490        (4, 0b0001),
491        (5, 0b00001),
492        (3, 0b011),
493        (2, 0b11),
494        (2, 0b10),
495        (3, 0b010),
496        (3, 0b001),
497        (6, 0b000000),
498    ],
499    &[
500        (6, 0b000001),
501        (6, 0b000000),
502        (4, 0b0001),
503        (2, 0b11),
504        (2, 0b10),
505        (3, 0b001),
506        (2, 0b01),
507        (5, 0b00001),
508    ],
509    &[
510        (5, 0b00001),
511        (5, 0b00000),
512        (3, 0b001),
513        (2, 0b11),
514        (2, 0b10),
515        (2, 0b01),
516        (4, 0b0001),
517    ],
518    &[
519        (4, 0b0000),
520        (4, 0b0001),
521        (3, 0b001),
522        (3, 0b010),
523        (1, 0b1),
524        (3, 0b011),
525    ],
526    &[(4, 0b0000), (4, 0b0001), (2, 0b01), (1, 0b1), (3, 0b001)],
527    &[(3, 0b000), (3, 0b001), (1, 0b1), (2, 0b01)],
528    &[(2, 0b00), (2, 0b01), (1, 0b1)],
529    &[(1, 0b0), (1, 0b1)],
530];
531
532// Table 9-9(a): total_zeros for chroma DC(maxNumCoeff=4)。行 = total_coeff(1..3)。
533const TOTAL_ZEROS_CHROMA: &[&[(u8, u16)]] = &[
534    &[(1, 0b1), (2, 0b01), (3, 0b001), (3, 0b000)],
535    &[(1, 0b1), (2, 0b01), (2, 0b00)],
536    &[(1, 0b1), (1, 0b0)],
537];
538
539fn read_total_zeros(r: &mut BitReader, total_coeff: u8, tables: &[&[(u8, u16)]]) -> u8 {
540    let row = tables[(total_coeff - 1) as usize];
541    let mut code: u16 = 0;
542    for len in 1..=9u8 {
543        code = (code << 1) | (r.read_bit() as u16);
544        for (tz, &(l, c)) in row.iter().enumerate() {
545            if l == len && c == code {
546                return tz as u8;
547            }
548        }
549    }
550    0
551}
552
553// Table 9-10: run_before。index = min(zerosLeft,7)-1、entry order = run_before 0,1,2,...
554const RUN_BEFORE: &[&[(u8, u16)]] = &[
555    &[(1, 0b1), (1, 0b0)],
556    &[(1, 0b1), (2, 0b01), (2, 0b00)],
557    &[(2, 0b11), (2, 0b10), (2, 0b01), (2, 0b00)],
558    &[(2, 0b11), (2, 0b10), (2, 0b01), (3, 0b001), (3, 0b000)],
559    &[
560        (2, 0b11),
561        (2, 0b10),
562        (3, 0b011),
563        (3, 0b010),
564        (3, 0b001),
565        (3, 0b000),
566    ],
567    &[
568        (2, 0b11),
569        (3, 0b000),
570        (3, 0b001),
571        (3, 0b011),
572        (3, 0b010),
573        (3, 0b101),
574        (3, 0b100),
575    ],
576    &[
577        (3, 0b111),
578        (3, 0b110),
579        (3, 0b101),
580        (3, 0b100),
581        (3, 0b011),
582        (3, 0b010),
583        (3, 0b001),
584        (4, 0b0001),
585        (5, 0b00001),
586        (6, 0b000001),
587        (7, 0b0000001),
588        (8, 0b00000001),
589        (9, 0b000000001),
590        (10, 0b0000000001),
591        (11, 0b00000000001),
592    ],
593];
594
595fn read_run_before(r: &mut BitReader, zeros_left: i32) -> i32 {
596    let idx = (zeros_left.min(7) - 1).max(0) as usize;
597    let row = RUN_BEFORE[idx];
598    let mut code: u16 = 0;
599    for len in 1..=11u8 {
600        code = (code << 1) | (r.read_bit() as u16);
601        for (run, &(l, c)) in row.iter().enumerate() {
602            if l == len && c == code {
603                return run as i32;
604            }
605        }
606    }
607    0
608}
609
610/// CAVLC で 1 残差ブロックを復号。スキャン順の係数配列と total_coeff を返す。
611/// n_c: 近傍係数数(-1=chroma DC, >=8=FLC)。tz_tables: total_zeros VLC 群。
612fn residual_block_cavlc(
613    r: &mut BitReader,
614    max_num_coeff: usize,
615    n_c: i32,
616    tz_tables: &[&[(u8, u16)]],
617) -> ([i32; 16], u8) {
618    let mut coeff = [0i32; 16];
619    let (total_coeff, trailing_ones) = match read_coeff_token_nc(r, n_c) {
620        Some(v) => v,
621        None => return (coeff, 0),
622    };
623    let ret_total = total_coeff;
624    if total_coeff == 0 {
625        return (coeff, 0);
626    }
627    let total_coeff = total_coeff as usize;
628    let trailing_ones = trailing_ones as usize;
629
630    // レベル復号(高周波から)
631    let mut level = [0i32; 16];
632    for i in 0..trailing_ones {
633        level[i] = if r.read_bit() == 1 { -1 } else { 1 };
634    }
635    let mut suffix_length: u32 = if total_coeff > 10 && trailing_ones < 3 {
636        1
637    } else {
638        0
639    };
640    for i in trailing_ones..total_coeff {
641        let mut level_prefix = 0u32;
642        while r.read_bit() == 0 {
643            level_prefix += 1;
644            if level_prefix > 31 {
645                break;
646            }
647        }
648        let mut suffix_size = suffix_length;
649        if level_prefix == 14 && suffix_length == 0 {
650            suffix_size = 4;
651        } else if level_prefix >= 15 {
652            suffix_size = level_prefix - 3;
653        }
654        let level_suffix = if suffix_size > 0 {
655            r.read_bits(suffix_size) as i32
656        } else {
657            0
658        };
659        let mut level_code = ((level_prefix.min(15) << suffix_length) as i32) + level_suffix;
660        if level_prefix >= 15 && suffix_length == 0 {
661            level_code += 15;
662        }
663        if level_prefix >= 16 {
664            level_code += (1 << (level_prefix - 3)) - 4096;
665        }
666        if i == trailing_ones && trailing_ones < 3 {
667            level_code += 2;
668        }
669        level[i] = if level_code % 2 == 0 {
670            (level_code + 2) >> 1
671        } else {
672            (-level_code - 1) >> 1
673        };
674        if suffix_length == 0 {
675            suffix_length = 1;
676        }
677        if level[i].unsigned_abs() > (3u32 << (suffix_length - 1)) && suffix_length < 6 {
678            suffix_length += 1;
679        }
680    }
681
682    // total_zeros
683    let total_zeros = if total_coeff < max_num_coeff {
684        read_total_zeros(r, total_coeff as u8, tz_tables) as i32
685    } else {
686        0
687    };
688
689    // run_before
690    let mut run = [0i32; 16];
691    let mut zeros_left = total_zeros;
692    for i in 0..total_coeff {
693        if i < total_coeff - 1 && zeros_left > 0 {
694            let rb = read_run_before(r, zeros_left);
695            run[i] = rb;
696            zeros_left -= rb;
697        } else if i == total_coeff - 1 {
698            run[i] = zeros_left;
699        } else {
700            run[i] = 0;
701        }
702    }
703
704    // スキャン順へ配置(高周波 level[0] を高 index に)
705    let mut coeff_ctr: i32 = -1;
706    for i in (0..total_coeff).rev() {
707        coeff_ctr += run[i] + 1;
708        if (coeff_ctr as usize) < 16 {
709            coeff[coeff_ctr as usize] = level[i];
710        }
711    }
712    (coeff, ret_total)
713}
714
715// ===================== 変換(DC 用 Hadamard) =====================
716
717/// I_16x16 輝度 DC の 4x4 逆 Hadamard 変換(in-place、raster)。丸め無し。
718fn inverse_hadamard_4x4(b: &mut [i32; 16]) {
719    for i in 0..4 {
720        let o = i * 4;
721        let (a0, a1, a2, a3) = (
722            b[o] + b[o + 2],
723            b[o] - b[o + 2],
724            b[o + 1] - b[o + 3],
725            b[o + 1] + b[o + 3],
726        );
727        b[o] = a0 + a3;
728        b[o + 1] = a1 + a2;
729        b[o + 2] = a1 - a2;
730        b[o + 3] = a0 - a3;
731    }
732    for j in 0..4 {
733        let (a0, a1, a2, a3) = (
734            b[j] + b[8 + j],
735            b[j] - b[8 + j],
736            b[4 + j] - b[12 + j],
737            b[4 + j] + b[12 + j],
738        );
739        b[j] = a0 + a3;
740        b[4 + j] = a1 + a2;
741        b[8 + j] = a1 - a2;
742        b[12 + j] = a0 - a3;
743    }
744}
745
746/// I_16x16 輝度 DC のスケーリング(8.5.10)。
747fn scale_luma_dc(f: &mut [i32; 16], qp: i32) {
748    let m = (qp % 6) as usize;
749    let ls = 16 * NORM_ADJUST_4X4[m][0];
750    let shift = qp / 6;
751    for v in f.iter_mut() {
752        *v = if qp >= 36 {
753            (*v * ls) << (shift - 6)
754        } else {
755            (*v * ls + (1 << (5 - shift))) >> (6 - shift)
756        };
757    }
758}
759
760// ===================== スライス/MB 復号 =====================
761
762pub struct DecodedYuv {
763    pub width: u32,
764    pub height: u32,
765    pub y: Vec<u8>,
766    pub u: Vec<u8>,
767    pub v: Vec<u8>,
768}
769
770/// YUV420 → RGB24(BT.601、各画素 [R,G,B])。描画経路用。
771pub fn yuv_to_rgb(yuv: &DecodedYuv) -> Vec<u8> {
772    let w = yuv.width as usize;
773    let h = yuv.height as usize;
774    let cw = (yuv.width / 2) as usize;
775    let mut rgb = alloc::vec![0u8; w * h * 3];
776    let mut oi = 0usize;
777    for y in 0..h {
778        let yrow = y * w;
779        let crow = (y >> 1) * cw;
780        for x in 0..w {
781            let yy = yuv.y[yrow + x] as i32;
782            let ci = crow + (x >> 1);
783            let cu = yuv.u[ci] as i32 - 128;
784            let cv = yuv.v[ci] as i32 - 128;
785            let r = yy + ((91881 * cv) >> 16);
786            let g = yy - ((22554 * cu + 46802 * cv) >> 16);
787            let b = yy + ((116130 * cu) >> 16);
788            rgb[oi] = r.clamp(0, 255) as u8;
789            rgb[oi + 1] = g.clamp(0, 255) as u8;
790            rgb[oi + 2] = b.clamp(0, 255) as u8;
791            oi += 3;
792        }
793    }
794    rgb
795}
796
797/// MP4/AVCC 経路用のステートフルなビデオデコーダ。参照フレームを保持して P を復号する。
798pub struct VideoDecoder {
799    sps: Option<Sps>,
800    pps: Option<Pps>,
801    length_size: usize,
802    reference: Option<DecodedYuv>,
803}
804
805impl Default for VideoDecoder {
806    fn default() -> Self {
807        Self::new()
808    }
809}
810
811impl VideoDecoder {
812    pub fn new() -> Self {
813        Self {
814            sps: None,
815            pps: None,
816            length_size: 4,
817            reference: None,
818        }
819    }
820
821    /// avcC から SPS/PPS と NAL 長さフィールドサイズを取り込む。
822    pub fn init_avcc(&mut self, avcc: &[u8]) {
823        if let Some((ls, sps_list, pps_list)) = parse_avcc(avcc) {
824            self.length_size = ls;
825            if let Some(p) = sps_list.first() {
826                if p.len() > 1 {
827                    self.sps = parse_sps(&extract_rbsp(&p[1..])).ok();
828                }
829            }
830            if let Some(p) = pps_list.first() {
831                if p.len() > 1 {
832                    self.pps = parse_pps(&extract_rbsp(&p[1..])).ok();
833                }
834            }
835        }
836    }
837
838    /// AVCC の 1 サンプル(= 1 アクセスユニット)を復号し、RGB フレームを返す。
839    pub fn decode_sample_rgb(&mut self, sample: &[u8]) -> Option<(u32, u32, Vec<u8>)> {
840        let nals = split_avcc(sample, self.length_size);
841        let mut out = None;
842        for nu in &nals {
843            match nu.unit_type {
844                7 => self.sps = parse_sps(&nu.rbsp).ok(),
845                8 => self.pps = parse_pps(&nu.rbsp).ok(),
846                1 | 5 => {
847                    let (s, p) = match (self.sps.clone(), self.pps.clone()) {
848                        (Some(s), Some(p)) => (s, p),
849                        _ => continue,
850                    };
851                    if let Ok(frame) =
852                        decode_slice(&s, &p, &nu.rbsp, nu.unit_type == 5, self.reference.as_ref())
853                    {
854                        out = Some((frame.width, frame.height, yuv_to_rgb(&frame)));
855                        self.reference = Some(frame);
856                    }
857                }
858                _ => {}
859            }
860        }
861        out
862    }
863}
864
865/// デコーダ状態。YUV 平面に加え、4x4 ブロック単位の近傍情報グリッドを持つ。
866struct DecState {
867    yuv: DecodedYuv,
868    blk_w: usize,             // 輝度 4x4 ブロック数(横)= width/4
869    nnz_luma: Vec<u8>,        // 各輝度 4x4 ブロックの total_coeff(nC 計算用)
870    mode_luma: Vec<i8>,       // 各輝度 4x4 ブロックの Intra4x4 モード(-1=非I4x4/不可用)
871    decoded: Vec<bool>,       // 各輝度 4x4 ブロックが再構成済みか(top-right 可用性用)
872    cblk_w: usize,            // クロマ 4x4 ブロック数(横)= (width/2)/4
873    nnz_chroma: [Vec<u8>; 2], // Cb/Cr の各クロマ 4x4 ブロックの total_coeff
874    mb_w: usize,              // MB 数(横)
875    mb_qp: Vec<i32>,          // 各 MB の QPy(デブロッキング用)
876    mv_l0: Vec<[i16; 2]>,     // 各輝度 4x4 ブロックの動きベクトル(1/4 画素単位、P 用)
877    is_inter: Vec<bool>,      // 各輝度 4x4 ブロックがインター予測か(MV 予測・デブロック用)
878    blk_h: usize,             // 輝度 4x4 ブロック数(縦)
879}
880
881/// 近傍 2 ブロックの nnz から nC を求める(-1 は不可用)。
882fn combine_nc(na: i32, nb: i32) -> i32 {
883    if na >= 0 && nb >= 0 {
884        (na + nb + 1) >> 1
885    } else if na >= 0 {
886        na
887    } else if nb >= 0 {
888        nb
889    } else {
890        0
891    }
892}
893
894impl DecState {
895    fn li(&self, bx4: usize, by4: usize) -> usize {
896        by4 * self.blk_w + bx4
897    }
898
899    /// 輝度 4x4 ブロック (bx4,by4) の nC(左/上ブロックの nnz から)。
900    fn luma_nc(&self, bx4: usize, by4: usize) -> i32 {
901        let na = if bx4 > 0 {
902            self.nnz_luma[self.li(bx4 - 1, by4)] as i32
903        } else {
904            -1
905        };
906        let nb = if by4 > 0 {
907            self.nnz_luma[self.li(bx4, by4 - 1)] as i32
908        } else {
909            -1
910        };
911        combine_nc(na, nb)
912    }
913
914    /// Intra4x4 予測モードの予測値(左/上ブロックのモードの min)。
915    fn pred_i4x4_mode(&self, bx4: usize, by4: usize) -> i32 {
916        if bx4 == 0 || by4 == 0 {
917            return 2;
918        } // どちらか不可用 -> DC
919        let ma = self.mode_luma[self.li(bx4 - 1, by4)];
920        let mb = self.mode_luma[self.li(bx4, by4 - 1)];
921        let ma = if ma >= 0 { ma as i32 } else { 2 };
922        let mb = if mb >= 0 { mb as i32 } else { 2 };
923        ma.min(mb)
924    }
925
926    /// 輝度 4x4 ブロックの近傍サンプルを収集し intra_4x4_predict で予測を返す。
927    /// px,py はフレーム画素座標(ブロック左上)。
928    fn predict_luma_4x4(&self, px: usize, py: usize, mode: u8) -> [i32; 16] {
929        let w = self.yuv.width as usize;
930        let top_avail = py > 0;
931        let left_avail = px > 0;
932        let bx4 = px / 4;
933        let by4 = py / 4;
934        let mut top = [0i32; 8];
935        let mut left = [0i32; 4];
936        let mut corner = 0i32;
937        if top_avail {
938            for x in 0..4 {
939                top[x] = self.yuv.y[(py - 1) * w + px + x] as i32;
940            }
941            // top-right: 右上 4x4 ブロックが再構成済みかつフレーム内なら使用、無ければ top[3] を複製
942            let tr_ok = by4 > 0 && bx4 + 1 < self.blk_w && self.decoded[self.li(bx4 + 1, by4 - 1)];
943            if tr_ok {
944                for x in 0..4 {
945                    top[4 + x] = self.yuv.y[(py - 1) * w + px + 4 + x] as i32;
946                }
947            } else {
948                for x in 0..4 {
949                    top[4 + x] = top[3];
950                }
951            }
952        }
953        if left_avail {
954            for y in 0..4 {
955                left[y] = self.yuv.y[(py + y) * w + px - 1] as i32;
956            }
957        }
958        if top_avail && left_avail {
959            corner = self.yuv.y[(py - 1) * w + px - 1] as i32;
960        }
961        intra_4x4_predict(mode, &top, corner, &left, top_avail, left_avail)
962    }
963}
964
965/// 単一スライス(I または P、IDR/非IDR、4:2:0、CAVLC)をデコードして YUV 平面を返す。
966/// reference は P スライスの動き補償に使う直前のフレーム。
967pub fn decode_slice(
968    sps: &Sps,
969    pps: &Pps,
970    rbsp: &[u8],
971    is_idr: bool,
972    reference: Option<&DecodedYuv>,
973) -> Result<DecodedYuv, &'static str> {
974    if pps.entropy_coding_mode_flag != 0 {
975        return Err("CABAC not supported");
976    }
977    // 不正/巨大 SPS による OOM・範囲外を防ぐ寸法サニティ(1080p=120x68 MB なので 300 で十分)。
978    if sps.width_mbs == 0 || sps.height_mbs == 0 || sps.width_mbs > 300 || sps.height_mbs > 300 {
979        return Err("invalid frame dimensions");
980    }
981    let width = sps.width_mbs * 16;
982    let height = sps.height_mbs * 16;
983    let cw = width / 2;
984    let ch = height / 2;
985    let yuv = DecodedYuv {
986        width,
987        height,
988        y: alloc::vec![0u8; (width * height) as usize],
989        u: alloc::vec![128u8; (cw * ch) as usize],
990        v: alloc::vec![128u8; (cw * ch) as usize],
991    };
992    let blk_w = (width / 4) as usize;
993    let blk_h = (height / 4) as usize;
994    let cblk_w = (cw / 4) as usize;
995    let cblk_h = (ch / 4) as usize;
996    let mut st = DecState {
997        yuv,
998        blk_w,
999        nnz_luma: alloc::vec![0u8; blk_w * blk_h],
1000        mode_luma: alloc::vec![-1i8; blk_w * blk_h],
1001        decoded: alloc::vec![false; blk_w * blk_h],
1002        cblk_w,
1003        nnz_chroma: [
1004            alloc::vec![0u8; cblk_w * cblk_h],
1005            alloc::vec![0u8; cblk_w * cblk_h],
1006        ],
1007        mb_w: sps.width_mbs as usize,
1008        mb_qp: alloc::vec![0i32; (sps.width_mbs * sps.height_mbs) as usize],
1009        mv_l0: alloc::vec![[0i16; 2]; blk_w * blk_h],
1010        is_inter: alloc::vec![false; blk_w * blk_h],
1011        blk_h,
1012    };
1013
1014    let mut r = BitReader::new(rbsp);
1015
1016    // ---- スライスヘッダ ----
1017    let _first_mb_in_slice = r.read_ue();
1018    let slice_type = r.read_ue();
1019    let is_i = slice_type % 5 == 2 || slice_type % 5 == 4;
1020    let is_p = slice_type.is_multiple_of(5);
1021    if !is_i && !is_p {
1022        return Err("only I/P slices supported");
1023    }
1024    if is_p {
1025        // P スライスの場合、事前に参照フレーム(reference)が存在し、
1026        // かつ解像度が一致していることを安全に検証し、パニックを防止します。
1027        match reference {
1028            None => return Err("P slice without reference frame"),
1029            Some(r) if r.width != width || r.height != height => {
1030                return Err("reference dimension mismatch")
1031            }
1032            _ => {}
1033        }
1034    }
1035    let _pps_id = r.read_ue();
1036    let _frame_num = r.read_bits(sps.log2_max_frame_num);
1037    if is_idr {
1038        let _idr_pic_id = r.read_ue();
1039    }
1040    if sps.pic_order_cnt_type == 0 {
1041        let _pic_order_cnt_lsb = r.read_bits(sps.log2_max_pic_order_cnt_lsb);
1042        if pps.bottom_field_pic_order_in_frame_present_flag != 0 {
1043            let _delta = r.read_se();
1044        }
1045    }
1046    if pps.redundant_pic_cnt_present_flag != 0 {
1047        let _redundant_pic_cnt = r.read_ue();
1048    }
1049    // P スライス: 参照リスト関連
1050    if is_p {
1051        let num_ref_idx_override = r.read_bit();
1052        if num_ref_idx_override == 1 {
1053            let _num_ref_idx_l0_active_minus1 = r.read_ue();
1054        }
1055        // ref_pic_list_modification(truncated データでの無限ループ防止に上限付き)。
1056        if r.read_bit() == 1 {
1057            for _ in 0..64 {
1058                let idc = r.read_ue();
1059                if idc == 3 || r.is_eof() {
1060                    break;
1061                }
1062                let _ = r.read_ue();
1063            }
1064        }
1065    }
1066    // dec_ref_pic_marking
1067    if is_idr {
1068        let _no_output_of_prior_pics = r.read_bit();
1069        let _long_term_reference = r.read_bit();
1070    } else {
1071        let adaptive = r.read_bit();
1072        if adaptive == 1 {
1073            for _ in 0..64 {
1074                let mmco = r.read_ue();
1075                if mmco == 0 || r.is_eof() {
1076                    break;
1077                }
1078                if mmco == 1 || mmco == 3 {
1079                    let _ = r.read_ue();
1080                }
1081                if mmco == 2 {
1082                    let _ = r.read_ue();
1083                }
1084                if mmco == 3 || mmco == 6 {
1085                    let _ = r.read_ue();
1086                }
1087                if mmco == 4 {
1088                    let _ = r.read_ue();
1089                }
1090            }
1091        }
1092    }
1093    let slice_qp_delta = r.read_se();
1094    let slice_qp = pps.pic_init_qp + slice_qp_delta;
1095    let mut disable_deblock_idc = 0u32;
1096    let mut alpha_off = 0i32;
1097    let mut beta_off = 0i32;
1098    if pps.deblocking_filter_control_present_flag != 0 {
1099        disable_deblock_idc = r.read_ue();
1100        if disable_deblock_idc != 1 {
1101            alpha_off = r.read_se() * 2;
1102            beta_off = r.read_se() * 2;
1103        }
1104    }
1105
1106    // ---- マクロブロック ----
1107    let mb_w = sps.width_mbs;
1108    let mb_h = sps.height_mbs;
1109    let total_mb = (mb_w * mb_h) as usize;
1110    let mut qp = slice_qp;
1111
1112    if is_i {
1113        for mb_idx in 0..total_mb {
1114            let mb_x = (mb_idx as u32 % mb_w) * 16;
1115            let mb_y = (mb_idx as u32 / mb_w) * 16;
1116            if let Err(e) = decode_mb(&mut r, &mut st, mb_x, mb_y, &mut qp, pps) {
1117                crate::println!(
1118                    "  H264 decode stopped at mb {} (of {}): {}",
1119                    mb_idx,
1120                    total_mb,
1121                    e
1122                );
1123                return Err(e);
1124            }
1125        }
1126    } else {
1127        // P スライス: mb_skip_run で連続スキップ、その後 1 つの符号化 MB。
1128        // アンラップを安全な ok_or と ? 演算子に置き換え、参照フレーム不足時に早期エラー返却します。
1129        let refr = reference.ok_or("P slice without reference frame")?;
1130        let mut mb_idx = 0usize;
1131        while mb_idx < total_mb {
1132            let skip_run = r.read_ue() as usize;
1133            for _ in 0..skip_run {
1134                if mb_idx >= total_mb {
1135                    break;
1136                }
1137                let mb_x = (mb_idx as u32 % mb_w) * 16;
1138                let mb_y = (mb_idx as u32 / mb_w) * 16;
1139                decode_pskip(&mut st, mb_x, mb_y, qp, refr);
1140                mb_idx += 1;
1141            }
1142            if mb_idx >= total_mb {
1143                break;
1144            }
1145            let mb_x = (mb_idx as u32 % mb_w) * 16;
1146            let mb_y = (mb_idx as u32 / mb_w) * 16;
1147            if let Err(e) = decode_mb_p(&mut r, &mut st, mb_x, mb_y, &mut qp, pps, refr) {
1148                crate::println!(
1149                    "  H264 P decode stopped at mb {} (of {}): {}",
1150                    mb_idx,
1151                    total_mb,
1152                    e
1153                );
1154                return Err(e);
1155            }
1156            mb_idx += 1;
1157        }
1158    }
1159
1160    // ---- デブロッキングフィルタ(disable_idc != 1 のとき)----
1161    if disable_deblock_idc != 1 {
1162        deblock_frame(
1163            &mut st,
1164            mb_w as usize,
1165            mb_h as usize,
1166            alpha_off,
1167            beta_off,
1168            pps.chroma_qp_index_offset,
1169        );
1170    }
1171
1172    Ok(st.yuv)
1173}
1174
1175#[allow(clippy::too_many_arguments)]
1176fn decode_mb(
1177    r: &mut BitReader,
1178    st: &mut DecState,
1179    mb_x: u32,
1180    mb_y: u32,
1181    qp: &mut i32,
1182    pps: &Pps,
1183) -> Result<(), &'static str> {
1184    let mb_type = r.read_ue();
1185    if mb_type == 25 {
1186        return decode_mb_ipcm(r, st, mb_x, mb_y);
1187    }
1188    if mb_type == 0 {
1189        decode_mb_i4x4(r, st, mb_x, mb_y, qp, pps)
1190    } else if (1..=24).contains(&mb_type) {
1191        decode_mb_i16x16(r, st, mb_x, mb_y, qp, mb_type, pps)
1192    } else {
1193        Err("unsupported mb_type (non-I)")
1194    }
1195}
1196
1197/// I_PCM マクロブロック: 生の画素値をそのまま格納(予測・変換なし)。
1198fn decode_mb_ipcm(
1199    r: &mut BitReader,
1200    st: &mut DecState,
1201    mb_x: u32,
1202    mb_y: u32,
1203) -> Result<(), &'static str> {
1204    r.byte_align();
1205    let w = st.yuv.width as usize;
1206    let cw = (st.yuv.width / 2) as usize;
1207    let (mx, my) = (mb_x as usize, mb_y as usize);
1208    for by in 0..16 {
1209        for bx in 0..16 {
1210            st.yuv.y[(my + by) * w + mx + bx] = r.read_bits(8) as u8;
1211        }
1212    }
1213    let (cmx, cmy) = (mx / 2, my / 2);
1214    for by in 0..8 {
1215        for bx in 0..8 {
1216            st.yuv.u[(cmy + by) * cw + cmx + bx] = r.read_bits(8) as u8;
1217        }
1218    }
1219    for by in 0..8 {
1220        for bx in 0..8 {
1221            st.yuv.v[(cmy + by) * cw + cmx + bx] = r.read_bits(8) as u8;
1222        }
1223    }
1224    // 近傍情報: I_PCM ブロックは nC 計算上 totalCoeff=16 扱い、モードは非I4x4(-1)。
1225    let bx0 = mx / 4;
1226    let by0 = my / 4;
1227    for dy in 0..4 {
1228        for dx in 0..4 {
1229            let li = st.li(bx0 + dx, by0 + dy);
1230            st.nnz_luma[li] = 16;
1231            st.mode_luma[li] = -1;
1232            st.decoded[li] = true;
1233        }
1234    }
1235    let cbx0 = cmx / 4;
1236    let cby0 = cmy / 4;
1237    for comp in 0..2 {
1238        for dy in 0..2 {
1239            for dx in 0..2 {
1240                st.nnz_chroma[comp][(cby0 + dy) * st.cblk_w + cbx0 + dx] = 16;
1241            }
1242        }
1243    }
1244    // I_PCM の QPy はデブロッキング上 0 とみなす(8.7.2)。
1245    st.mb_qp[(my / 16) * st.mb_w + (mx / 16)] = 0;
1246    Ok(())
1247}
1248
1249fn median3(a: i16, b: i16, c: i16) -> i16 {
1250    a + b + c - a.min(b).min(c) - a.max(b).max(c)
1251}
1252
1253/// 16x16 パーティションの明示的 MV 予測(8.4.1.3、単一参照 refIdx=0 前提)。
1254/// 重要: 「利用可能(デコード済み・画面内)」と「一致(インターで refIdx=0)」を区別する。
1255/// C(右上)の D 代用は C が *利用不可* のときのみ(イントラで利用可能なら代用しない)。
1256fn mv_predict_16x16(st: &DecState, mb_x: u32, mb_y: u32) -> [i16; 2] {
1257    let bx0 = (mb_x / 4) as i32;
1258    let by0 = (mb_y / 4) as i32;
1259    // (利用可能, 一致, MV)。一致 = インターで refIdx=0。イントラは利用可能だが不一致(mv=0)。
1260    let getn = |bx: i32, by: i32| -> (bool, bool, [i16; 2]) {
1261        if bx >= 0 && by >= 0 && (bx as usize) < st.blk_w && (by as usize) < st.blk_h {
1262            let li = (by as usize) * st.blk_w + bx as usize;
1263            if st.decoded[li] {
1264                if st.is_inter[li] {
1265                    return (true, true, st.mv_l0[li]);
1266                }
1267                return (true, false, [0, 0]); // イントラ: 利用可能・不一致
1268            }
1269        }
1270        (false, false, [0, 0])
1271    };
1272    let (_a_av, a_m, mva) = getn(bx0 - 1, by0); // 左
1273    let (_b_av, b_m, mvb) = getn(bx0, by0 - 1); // 上
1274    let (c_av, c_m0, mvc0) = getn(bx0 + 4, by0 - 1); // 右上
1275    let (mut c_m, mut mvc) = (c_m0, mvc0);
1276    if !c_av {
1277        // C が *利用不可* のときのみ D(左上)で代用。
1278        let (_d_av, d_m, mvd) = getn(bx0 - 1, by0 - 1);
1279        c_m = d_m;
1280        mvc = mvd;
1281    }
1282    // mbB と mbC が(画面位置として)不可用で mbA 可用なら mvp=mvA。
1283    let b_mb_avail = mb_y > 0;
1284    let c_mb_avail = mb_y > 0 && (bx0 + 4 < st.blk_w as i32 || mb_x > 0);
1285    if !b_mb_avail && !c_mb_avail && mb_x > 0 {
1286        return mva;
1287    }
1288    let matches = a_m as i32 + b_m as i32 + c_m as i32;
1289    if matches == 1 {
1290        if a_m {
1291            return mva;
1292        }
1293        if b_m {
1294            return mvb;
1295        }
1296        return mvc;
1297    }
1298    [
1299        median3(mva[0], mvb[0], mvc[0]),
1300        median3(mva[1], mvb[1], mvc[1]),
1301    ]
1302}
1303
1304/// P_Skip の動きベクトル導出(8.4.1.1)。
1305fn predict_pskip_mv(st: &DecState, mb_x: u32, mb_y: u32) -> [i16; 2] {
1306    let bx0 = (mb_x / 4) as usize;
1307    let by0 = (mb_y / 4) as usize;
1308    let a_avail = mb_x > 0;
1309    let b_avail = mb_y > 0;
1310    if !a_avail || !b_avail {
1311        return [0, 0];
1312    }
1313    let mva = st.mv_l0[by0 * st.blk_w + bx0 - 1];
1314    let mvb = st.mv_l0[(by0 - 1) * st.blk_w + bx0];
1315    // refIdx は単一参照で 0 前提。いずれかが zero-MV なら 0。
1316    if mva == [0, 0] || mvb == [0, 0] {
1317        return [0, 0];
1318    }
1319    let _ = (bx0, by0);
1320    mv_predict(st, mb_x as usize, mb_y as usize, 16, MvDir::None)
1321}
1322
1323/// 6タップ補間のコア(8.4.2.2.1)。get(dx,dy) でサンプル取得(クランプ有無は呼び出し側)。
1324fn interp_luma_core<F: Fn(isize, isize) -> i32>(get: F, fx: i32, fy: i32) -> i32 {
1325    let tap =
1326        |a: i32, b: i32, c: i32, d: i32, e: i32, f: i32| a - 5 * b + 20 * c + 20 * d - 5 * e + f;
1327    if fx == 0 && fy == 0 {
1328        return get(0, 0);
1329    }
1330    // 水平半画素(行 dy), 垂直半画素(列 dx)
1331    let hh = |dy: isize| -> i32 {
1332        clip1(
1333            (tap(
1334                get(-2, dy),
1335                get(-1, dy),
1336                get(0, dy),
1337                get(1, dy),
1338                get(2, dy),
1339                get(3, dy),
1340            ) + 16)
1341                >> 5,
1342        )
1343    };
1344    let vh = |dx: isize| -> i32 {
1345        clip1(
1346            (tap(
1347                get(dx, -2),
1348                get(dx, -1),
1349                get(dx, 0),
1350                get(dx, 1),
1351                get(dx, 2),
1352                get(dx, 3),
1353            ) + 16)
1354                >> 5,
1355        )
1356    };
1357    let jj = || -> i32 {
1358        let vi = |dx: isize| {
1359            tap(
1360                get(dx, -2),
1361                get(dx, -1),
1362                get(dx, 0),
1363                get(dx, 1),
1364                get(dx, 2),
1365                get(dx, 3),
1366            )
1367        };
1368        clip1((tap(vi(-2), vi(-1), vi(0), vi(1), vi(2), vi(3)) + 512) >> 10)
1369    };
1370    match (fx, fy) {
1371        (1, 0) => (get(0, 0) + hh(0) + 1) >> 1,
1372        (2, 0) => hh(0),
1373        (3, 0) => (get(1, 0) + hh(0) + 1) >> 1,
1374        (0, 1) => (get(0, 0) + vh(0) + 1) >> 1,
1375        (0, 2) => vh(0),
1376        (0, 3) => (get(0, 1) + vh(0) + 1) >> 1,
1377        (2, 2) => jj(),
1378        (1, 1) => (hh(0) + vh(0) + 1) >> 1,
1379        (3, 1) => (hh(0) + vh(1) + 1) >> 1,
1380        (1, 3) => (vh(0) + hh(1) + 1) >> 1,
1381        (3, 3) => (vh(1) + hh(1) + 1) >> 1,
1382        (2, 1) => (hh(0) + jj() + 1) >> 1,
1383        (1, 2) => (vh(0) + jj() + 1) >> 1,
1384        (3, 2) => (jj() + vh(1) + 1) >> 1,
1385        (2, 3) => (jj() + hh(1) + 1) >> 1,
1386        _ => get(0, 0),
1387    }
1388}
1389
1390/// 端のクランプ付きで 1 サンプル補間(フレーム境界をまたぐパーティション用)。
1391fn interp_luma_sample(refr: &DecodedYuv, x: isize, y: isize, fx: i32, fy: i32) -> i32 {
1392    let w = refr.width as isize;
1393    let h = refr.height as isize;
1394    interp_luma_core(
1395        |dx, dy| {
1396            let sx = (x + dx).clamp(0, w - 1);
1397            let sy = (y + dy).clamp(0, h - 1);
1398            refr.y[(sy * w + sx) as usize] as i32
1399        },
1400        fx,
1401        fy,
1402    )
1403}
1404
1405/// 輝度パーティション pw×ph @ (px,py) の動き補償(サブ画素対応)。
1406fn mc_part_luma(
1407    st: &mut DecState,
1408    px: usize,
1409    py: usize,
1410    pw: usize,
1411    ph: usize,
1412    mv: [i16; 2],
1413    refr: &DecodedYuv,
1414) {
1415    let w = st.yuv.width as usize;
1416    let h = st.yuv.height as usize;
1417    let fx = (mv[0] & 3) as i32;
1418    let fy = (mv[1] & 3) as i32;
1419    let ix = (mv[0] >> 2) as isize;
1420    let iy = (mv[1] >> 2) as isize;
1421    // 高速パス: 整数 MV かつフットプリントが完全に範囲内なら行コピー(クランプ・補間不要)。
1422    if fx == 0 && fy == 0 {
1423        let sx0 = px as isize + ix;
1424        let sy0 = py as isize + iy;
1425        if sx0 >= 0
1426            && sy0 >= 0
1427            && sx0 + pw as isize <= w as isize
1428            && sy0 + ph as isize <= h as isize
1429        {
1430            let (sx0, sy0) = (sx0 as usize, sy0 as usize);
1431            for y in 0..ph {
1432                let s = (sy0 + y) * w + sx0;
1433                let d = (py + y) * w + px;
1434                st.yuv.y[d..d + pw].copy_from_slice(&refr.y[s..s + pw]);
1435            }
1436            return;
1437        }
1438    }
1439    // 高速パス: フットプリント + 6タップのハロー(±2/+3)が範囲内ならクランプ無しで補間。
1440    let wi = w as isize;
1441    let hi = h as isize;
1442    let halo_ok = px as isize + ix - 2 >= 0
1443        && py as isize + iy - 2 >= 0
1444        && px as isize + ix + pw as isize + 2 < wi
1445        && py as isize + iy + ph as isize + 2 < hi;
1446    if halo_ok {
1447        // separable 高速パス: 中央(j)を使う位置は垂直6タップ中間値を行ごとに 1 回だけ計算し
1448        // 水平方向で再利用する(per-pixel の 7×6タップ → 約 2×6タップ/画素)。
1449        let needs_j = matches!((fx, fy), (2, 2) | (2, 1) | (2, 3) | (1, 2) | (3, 2));
1450        if needs_j {
1451            let base_x = px as isize + ix;
1452            let base_y = py as isize + iy;
1453            let tap = |a: i32, b: i32, c: i32, d: i32, e: i32, f: i32| {
1454                a - 5 * b + 20 * c + 20 * d - 5 * e + f
1455            };
1456            let g = |rx: isize, ry: isize| refr.y[(ry * wi + rx) as usize] as i32;
1457            let mut vrow = [0i32; 21]; // pw≤16, +5 列分の垂直中間値(未丸め)
1458            for y in 0..ph {
1459                let ry = base_y + y as isize;
1460                for c in 0..pw + 5 {
1461                    let rx = base_x + c as isize - 2;
1462                    vrow[c] = tap(
1463                        g(rx, ry - 2),
1464                        g(rx, ry - 1),
1465                        g(rx, ry),
1466                        g(rx, ry + 1),
1467                        g(rx, ry + 2),
1468                        g(rx, ry + 3),
1469                    );
1470                }
1471                for x in 0..pw {
1472                    let j = clip1(
1473                        (tap(
1474                            vrow[x],
1475                            vrow[x + 1],
1476                            vrow[x + 2],
1477                            vrow[x + 3],
1478                            vrow[x + 4],
1479                            vrow[x + 5],
1480                        ) + 512)
1481                            >> 10,
1482                    );
1483                    let rx = base_x + x as isize;
1484                    let val = match (fx, fy) {
1485                        (2, 2) => j,
1486                        (2, 1) => {
1487                            let b = clip1(
1488                                (tap(
1489                                    g(rx - 2, ry),
1490                                    g(rx - 1, ry),
1491                                    g(rx, ry),
1492                                    g(rx + 1, ry),
1493                                    g(rx + 2, ry),
1494                                    g(rx + 3, ry),
1495                                ) + 16)
1496                                    >> 5,
1497                            );
1498                            (b + j + 1) >> 1
1499                        }
1500                        (2, 3) => {
1501                            let s = clip1(
1502                                (tap(
1503                                    g(rx - 2, ry + 1),
1504                                    g(rx - 1, ry + 1),
1505                                    g(rx, ry + 1),
1506                                    g(rx + 1, ry + 1),
1507                                    g(rx + 2, ry + 1),
1508                                    g(rx + 3, ry + 1),
1509                                ) + 16)
1510                                    >> 5,
1511                            );
1512                            (j + s + 1) >> 1
1513                        }
1514                        (1, 2) => {
1515                            let hp = clip1(
1516                                (tap(
1517                                    g(rx, ry - 2),
1518                                    g(rx, ry - 1),
1519                                    g(rx, ry),
1520                                    g(rx, ry + 1),
1521                                    g(rx, ry + 2),
1522                                    g(rx, ry + 3),
1523                                ) + 16)
1524                                    >> 5,
1525                            );
1526                            (hp + j + 1) >> 1
1527                        }
1528                        (3, 2) => {
1529                            let m = clip1(
1530                                (tap(
1531                                    g(rx + 1, ry - 2),
1532                                    g(rx + 1, ry - 1),
1533                                    g(rx + 1, ry),
1534                                    g(rx + 1, ry + 1),
1535                                    g(rx + 1, ry + 2),
1536                                    g(rx + 1, ry + 3),
1537                                ) + 16)
1538                                    >> 5,
1539                            );
1540                            (j + m + 1) >> 1
1541                        }
1542                        _ => j,
1543                    };
1544                    st.yuv.y[(py + y) * w + px + x] = val as u8;
1545                }
1546            }
1547            return;
1548        }
1549        for y in 0..ph {
1550            for x in 0..pw {
1551                let bx = px as isize + x as isize + ix;
1552                let by = py as isize + y as isize + iy;
1553                let v = interp_luma_core(
1554                    |dx, dy| refr.y[((by + dy) * wi + bx + dx) as usize] as i32,
1555                    fx,
1556                    fy,
1557                );
1558                st.yuv.y[(py + y) * w + px + x] = v as u8;
1559            }
1560        }
1561        return;
1562    }
1563    // フレーム境界をまたぐ低速パス。needs_j 位置(jj() が絡む)は per-pixel だと
1564    // 縦6タップ×横6タップ=36タップに膨らむため、halo_ok 分岐と同じ「行ごとに縦の中間値を
1565    // 1回だけ計算し横方向で使い回す」separable 手法をクランプ付きで適用する。
1566    let needs_j = matches!((fx, fy), (2, 2) | (2, 1) | (2, 3) | (1, 2) | (3, 2));
1567    if needs_j {
1568        let base_x = px as isize + ix;
1569        let base_y = py as isize + iy;
1570        let tap =
1571            |a: i32, b: i32, c: i32, d: i32, e: i32, f: i32| a - 5 * b + 20 * c + 20 * d - 5 * e + f;
1572        let g = |rx: isize, ry: isize| {
1573            let cx = rx.clamp(0, wi - 1);
1574            let cy = ry.clamp(0, hi - 1);
1575            refr.y[(cy * wi + cx) as usize] as i32
1576        };
1577        let mut vrow = [0i32; 21]; // pw≤16, +5 列分の垂直中間値(未丸め)
1578        for y in 0..ph {
1579            let ry = base_y + y as isize;
1580            for c in 0..pw + 5 {
1581                let rx = base_x + c as isize - 2;
1582                vrow[c] = tap(
1583                    g(rx, ry - 2),
1584                    g(rx, ry - 1),
1585                    g(rx, ry),
1586                    g(rx, ry + 1),
1587                    g(rx, ry + 2),
1588                    g(rx, ry + 3),
1589                );
1590            }
1591            for x in 0..pw {
1592                let j = clip1(
1593                    (tap(
1594                        vrow[x],
1595                        vrow[x + 1],
1596                        vrow[x + 2],
1597                        vrow[x + 3],
1598                        vrow[x + 4],
1599                        vrow[x + 5],
1600                    ) + 512)
1601                        >> 10,
1602                );
1603                let rx = base_x + x as isize;
1604                let val = match (fx, fy) {
1605                    (2, 2) => j,
1606                    (2, 1) => {
1607                        let b = clip1(
1608                            (tap(
1609                                g(rx - 2, ry),
1610                                g(rx - 1, ry),
1611                                g(rx, ry),
1612                                g(rx + 1, ry),
1613                                g(rx + 2, ry),
1614                                g(rx + 3, ry),
1615                            ) + 16)
1616                                >> 5,
1617                        );
1618                        (b + j + 1) >> 1
1619                    }
1620                    (2, 3) => {
1621                        let s = clip1(
1622                            (tap(
1623                                g(rx - 2, ry + 1),
1624                                g(rx - 1, ry + 1),
1625                                g(rx, ry + 1),
1626                                g(rx + 1, ry + 1),
1627                                g(rx + 2, ry + 1),
1628                                g(rx + 3, ry + 1),
1629                            ) + 16)
1630                                >> 5,
1631                        );
1632                        (j + s + 1) >> 1
1633                    }
1634                    (1, 2) => {
1635                        let hp = clip1(
1636                            (tap(
1637                                g(rx, ry - 2),
1638                                g(rx, ry - 1),
1639                                g(rx, ry),
1640                                g(rx, ry + 1),
1641                                g(rx, ry + 2),
1642                                g(rx, ry + 3),
1643                            ) + 16)
1644                                >> 5,
1645                        );
1646                        (hp + j + 1) >> 1
1647                    }
1648                    (3, 2) => {
1649                        let m = clip1(
1650                            (tap(
1651                                g(rx + 1, ry - 2),
1652                                g(rx + 1, ry - 1),
1653                                g(rx + 1, ry),
1654                                g(rx + 1, ry + 1),
1655                                g(rx + 1, ry + 2),
1656                                g(rx + 1, ry + 3),
1657                            ) + 16)
1658                                >> 5,
1659                        );
1660                        (j + m + 1) >> 1
1661                    }
1662                    _ => j,
1663                };
1664                st.yuv.y[(py + y) * w + px + x] = val as u8;
1665            }
1666        }
1667        return;
1668    }
1669    for y in 0..ph {
1670        for x in 0..pw {
1671            let bx = px as isize + x as isize + ix;
1672            let by = py as isize + y as isize + iy;
1673            st.yuv.y[(py + y) * w + px + x] = interp_luma_sample(refr, bx, by, fx, fy) as u8;
1674        }
1675    }
1676}
1677
1678/// クロマのサブ画素補間(8.4.2.2.2、双線形 1/8 画素)。
1679fn interp_chroma_sample(
1680    plane: &[u8],
1681    cw: isize,
1682    ch: isize,
1683    x: isize,
1684    y: isize,
1685    fx: i32,
1686    fy: i32,
1687) -> i32 {
1688    let get = |dx: isize, dy: isize| -> i32 {
1689        let sx = (x + dx).clamp(0, cw - 1);
1690        let sy = (y + dy).clamp(0, ch - 1);
1691        plane[(sy * cw + sx) as usize] as i32
1692    };
1693    let a = get(0, 0);
1694    let b = get(1, 0);
1695    let c = get(0, 1);
1696    let d = get(1, 1);
1697    ((8 - fx) * (8 - fy) * a + fx * (8 - fy) * b + (8 - fx) * fy * c + fx * fy * d + 32) >> 6
1698}
1699
1700/// クロマパーティション(輝度 pw×ph @ (px,py) に対応)の動き補償(サブ画素 1/8)。
1701fn mc_part_chroma(
1702    st: &mut DecState,
1703    px: usize,
1704    py: usize,
1705    pw: usize,
1706    ph: usize,
1707    mv: [i16; 2],
1708    refr: &DecodedYuv,
1709) {
1710    let cw = (st.yuv.width / 2) as isize;
1711    let chh = (st.yuv.height / 2) as isize;
1712    let cx = px / 2;
1713    let cy = py / 2;
1714    let cpw = pw / 2;
1715    let cph = ph / 2;
1716    let fx = (mv[0] & 7) as i32;
1717    let fy = (mv[1] & 7) as i32;
1718    let ix = (mv[0] >> 3) as isize;
1719    let iy = (mv[1] >> 3) as isize;
1720    let cwz = cw as usize;
1721    let integer = fx == 0 && fy == 0;
1722    let sx0 = cx as isize + ix;
1723    let sy0 = cy as isize + iy;
1724    let in_bounds = sx0 >= 0 && sy0 >= 0 && sx0 + cpw as isize <= cw && sy0 + cph as isize <= chh;
1725    for comp in 0..2 {
1726        let (dst, src) = if comp == 0 {
1727            (&mut st.yuv.u, &refr.u)
1728        } else {
1729            (&mut st.yuv.v, &refr.v)
1730        };
1731        if integer && in_bounds {
1732            let (sx0, sy0) = (sx0 as usize, sy0 as usize);
1733            for y in 0..cph {
1734                let s = (sy0 + y) * cwz + sx0;
1735                let d = (cy + y) * cwz + cx;
1736                dst[d..d + cpw].copy_from_slice(&src[s..s + cpw]);
1737            }
1738            continue;
1739        }
1740        for y in 0..cph {
1741            for x in 0..cpw {
1742                let bx = cx as isize + x as isize + ix;
1743                let by = cy as isize + y as isize + iy;
1744                dst[(cy + y) * cwz + cx + x] =
1745                    interp_chroma_sample(src, cw, chh, bx, by, fx, fy) as u8;
1746            }
1747        }
1748    }
1749}
1750
1751/// パーティションの MC(輝度 + クロマ)。
1752fn mc_part(
1753    st: &mut DecState,
1754    px: usize,
1755    py: usize,
1756    pw: usize,
1757    ph: usize,
1758    mv: [i16; 2],
1759    refr: &DecodedYuv,
1760) {
1761    mc_part_luma(st, px, py, pw, ph, mv, refr);
1762    mc_part_chroma(st, px, py, pw, ph, mv, refr);
1763}
1764
1765/// パーティションの MV をグリッドへ格納(後続パーティション/MB の予測用)。
1766fn store_part_mv(st: &mut DecState, px: usize, py: usize, pw: usize, ph: usize, mv: [i16; 2]) {
1767    let bx0 = px / 4;
1768    let by0 = py / 4;
1769    for dy in 0..ph / 4 {
1770        for dx in 0..pw / 4 {
1771            let li = (by0 + dy) * st.blk_w + bx0 + dx;
1772            st.mv_l0[li] = mv;
1773            st.is_inter[li] = true;
1774            st.mode_luma[li] = -1;
1775            st.nnz_luma[li] = 0;
1776            st.decoded[li] = true;
1777        }
1778    }
1779}
1780
1781/// MV 予測の方向ヒント(16x8/8x16 の特例)。
1782#[derive(Clone, Copy, PartialEq)]
1783enum MvDir {
1784    None,
1785    A,
1786    B,
1787    C,
1788}
1789
1790/// パーティション (px,py,pw) の近傍 A/B/C を返す: ((一致,mv) x3)。
1791/// 一致 = インターで refIdx=0。C は利用不可なら D(左上)で代用。
1792fn part_neighbors(
1793    st: &DecState,
1794    px: usize,
1795    py: usize,
1796    pw: usize,
1797) -> ((bool, [i16; 2]), (bool, [i16; 2]), (bool, [i16; 2])) {
1798    let bx0 = (px / 4) as i32;
1799    let by0 = (py / 4) as i32;
1800    let getn = |bx: i32, by: i32| -> (bool, bool, [i16; 2]) {
1801        if bx >= 0 && by >= 0 && (bx as usize) < st.blk_w && (by as usize) < st.blk_h {
1802            let li = (by as usize) * st.blk_w + bx as usize;
1803            if st.decoded[li] {
1804                if st.is_inter[li] {
1805                    return (true, true, st.mv_l0[li]);
1806                }
1807                return (true, false, [0, 0]);
1808            }
1809        }
1810        (false, false, [0, 0])
1811    };
1812    let (_a, am, mva) = getn(bx0 - 1, by0);
1813    let (_b, bm, mvb) = getn(bx0, by0 - 1);
1814    let (cav, cm0, mvc0) = getn(bx0 + (pw / 4) as i32, by0 - 1);
1815    let (mut cm, mut mvc) = (cm0, mvc0);
1816    if !cav {
1817        let (_d, dm, mvd) = getn(bx0 - 1, by0 - 1);
1818        cm = dm;
1819        mvc = mvd;
1820    }
1821    ((am, mva), (bm, mvb), (cm, mvc))
1822}
1823
1824/// パーティションの MV 予測(8.4.1.3、単一参照 refIdx=0、方向ヒント対応)。
1825fn mv_predict(st: &DecState, px: usize, py: usize, pw: usize, dir: MvDir) -> [i16; 2] {
1826    let ((am, mva), (bm, mvb), (cm, mvc)) = part_neighbors(st, px, py, pw);
1827    match dir {
1828        MvDir::A if am => return mva,
1829        MvDir::B if bm => return mvb,
1830        MvDir::C if cm => return mvc,
1831        _ => {}
1832    }
1833    // mbB と mbC が画面位置として不可用で mbA 可用なら mvp=mvA。
1834    let bx0 = (px / 4) as i32;
1835    let b_pic = (py / 4) > 0;
1836    let c_pic = (py / 4) > 0 && ((bx0 + (pw / 4) as i32) < st.blk_w as i32 || (px / 4) > 0);
1837    if !b_pic && !c_pic && (px / 4) > 0 {
1838        return mva;
1839    }
1840    let matches = am as i32 + bm as i32 + cm as i32;
1841    if matches == 1 {
1842        if am {
1843            return mva;
1844        }
1845        if bm {
1846            return mvb;
1847        }
1848        return mvc;
1849    }
1850    [
1851        median3(mva[0], mvb[0], mvc[0]),
1852        median3(mva[1], mvb[1], mvc[1]),
1853    ]
1854}
1855
1856/// インター MB(P_Skip 含む)の近傍グリッドを更新。
1857fn set_mb_inter_grids(st: &mut DecState, mb_x: u32, mb_y: u32, mv: [i16; 2], qp: i32) {
1858    let bx0 = (mb_x / 4) as usize;
1859    let by0 = (mb_y / 4) as usize;
1860    for dy in 0..4 {
1861        for dx in 0..4 {
1862            let li = (by0 + dy) * st.blk_w + bx0 + dx;
1863            st.mv_l0[li] = mv;
1864            st.nnz_luma[li] = 0;
1865            st.mode_luma[li] = -1;
1866            st.is_inter[li] = true;
1867            st.decoded[li] = true;
1868        }
1869    }
1870    let cbx0 = (mb_x / 8) as usize;
1871    let cby0 = (mb_y / 8) as usize;
1872    for comp in 0..2 {
1873        for dy in 0..2 {
1874            for dx in 0..2 {
1875                st.nnz_chroma[comp][(cby0 + dy) * st.cblk_w + cbx0 + dx] = 0;
1876            }
1877        }
1878    }
1879    st.mb_qp[(mb_y / 16) as usize * st.mb_w + (mb_x / 16) as usize] = qp;
1880}
1881
1882/// P_Skip マクロブロックのデコード。
1883fn decode_pskip(st: &mut DecState, mb_x: u32, mb_y: u32, qp: i32, refr: &DecodedYuv) {
1884    let mv = predict_pskip_mv(st, mb_x, mb_y);
1885    let (px, py) = (mb_x as usize, mb_y as usize);
1886    mc_part(st, px, py, 16, 16, mv, refr);
1887    store_part_mv(st, px, py, 16, 16, mv);
1888    st.mb_qp[(mb_y / 16) as usize * st.mb_w + (mb_x / 16) as usize] = qp;
1889}
1890
1891/// インター MB の輝度残差を MC 予測へ加算(4x4 ブロックごと)。
1892fn add_inter_luma_residual(
1893    r: &mut BitReader,
1894    st: &mut DecState,
1895    mb_x: u32,
1896    mb_y: u32,
1897    cbp_luma: u32,
1898    qp: i32,
1899) {
1900    let w = st.yuv.width as usize;
1901    let bx0 = (mb_x / 4) as usize;
1902    let by0 = (mb_y / 4) as usize;
1903    for blk in 0..16 {
1904        let (bx, by) = LUMA_4X4_BLOCK_XY[blk];
1905        let bx4 = bx0 + bx / 4;
1906        let by4 = by0 + by / 4;
1907        let li = by4 * st.blk_w + bx4;
1908        if cbp_luma & (1 << (blk / 4)) != 0 {
1909            let nc = st.luma_nc(bx4, by4);
1910            let (scan, tc) = residual_block_cavlc(r, 16, nc, TOTAL_ZEROS_16);
1911            st.nnz_luma[li] = tc;
1912            let mut block = [0i32; 16];
1913            for k in 0..16 {
1914                block[ZIGZAG_4X4[k]] = scan[k];
1915            }
1916            dequant_4x4(&mut block, qp);
1917            inverse_transform_4x4(&mut block);
1918            for yy in 0..4 {
1919                for xx in 0..4 {
1920                    let px = mb_x as usize + bx + xx;
1921                    let py = mb_y as usize + by + yy;
1922                    let cur = st.yuv.y[py * w + px] as i32;
1923                    st.yuv.y[py * w + px] = clip1(cur + block[yy * 4 + xx]) as u8;
1924                }
1925            }
1926        } else {
1927            st.nnz_luma[li] = 0;
1928        }
1929    }
1930}
1931
1932/// インター MB のクロマ残差を MC 予測へ加算(DC 2x2 Hadamard + AC)。
1933fn add_inter_chroma_residual(
1934    r: &mut BitReader,
1935    st: &mut DecState,
1936    mb_x: u32,
1937    mb_y: u32,
1938    cbp_chroma: u32,
1939    qp_y: i32,
1940    chroma_qp_off: i32,
1941) {
1942    let cw = (st.yuv.width / 2) as usize;
1943    let cmb_x = (mb_x / 2) as usize;
1944    let cmb_y = (mb_y / 2) as usize;
1945    let cbx0 = cmb_x / 4;
1946    let cby0 = cmb_y / 4;
1947    let qpc = chroma_qp(qp_y, chroma_qp_off);
1948    let mut dc = [[0i32; 4]; 2];
1949    if cbp_chroma != 0 {
1950        for comp in 0..2 {
1951            let (scan, _) = residual_block_cavlc(r, 4, -1, TOTAL_ZEROS_CHROMA);
1952            let f = inverse_hadamard_2x2([scan[0], scan[1], scan[2], scan[3]]);
1953            for i in 0..4 {
1954                dc[comp][i] = scale_chroma_dc(f[i], qpc);
1955            }
1956        }
1957    }
1958    let mut ac = [[[0i32; 16]; 4]; 2];
1959    if cbp_chroma == 2 {
1960        for comp in 0..2 {
1961            for blk in 0..4 {
1962                let bx = (blk % 2) * 4;
1963                let by = (blk / 2) * 4;
1964                let cbx4 = cbx0 + bx / 4;
1965                let cby4 = cby0 + by / 4;
1966                let nc = chroma_nc(st, comp, cbx4, cby4);
1967                let (scan, tc) = residual_block_cavlc(r, 15, nc, TOTAL_ZEROS_16);
1968                for k in 0..15 {
1969                    ac[comp][blk][ZIGZAG_4X4[k + 1]] = scan[k];
1970                }
1971                st.nnz_chroma[comp][cby4 * st.cblk_w + cbx4] = tc;
1972            }
1973        }
1974    }
1975    if cbp_chroma == 0 {
1976        return;
1977    }
1978    for comp in 0..2 {
1979        let plane: &mut Vec<u8> = if comp == 0 {
1980            &mut st.yuv.u
1981        } else {
1982            &mut st.yuv.v
1983        };
1984        for blk in 0..4 {
1985            let bx = (blk % 2) * 4;
1986            let by = (blk / 2) * 4;
1987            let mut block = ac[comp][blk];
1988            dequant_4x4(&mut block, qpc);
1989            block[0] = dc[comp][blk];
1990            inverse_transform_4x4(&mut block);
1991            for yy in 0..4 {
1992                for xx in 0..4 {
1993                    let px = cmb_x + bx + xx;
1994                    let py = cmb_y + by + yy;
1995                    let cur = plane[py * cw + px] as i32;
1996                    plane[py * cw + px] = clip1(cur + block[yy * 4 + xx]) as u8;
1997                }
1998            }
1999        }
2000    }
2001}
2002
2003/// P スライスの符号化 MB。intra MB(mb_type>=5)は intra デコーダへ委譲。
2004/// P_8x8 / P_8x8ref0: 4 つの 8x8 サブ MB、各 sub_mb_type でさらに分割。
2005fn decode_p8x8(
2006    r: &mut BitReader,
2007    st: &mut DecState,
2008    px: usize,
2009    py: usize,
2010    refr: &DecodedYuv,
2011) -> Result<(), &'static str> {
2012    let mut sub_types = [0u32; 4];
2013    for s in sub_types.iter_mut() {
2014        *s = r.read_ue();
2015        if *s > 3 {
2016            return Err("bad sub_mb_type");
2017        }
2018    }
2019    // ref_idx: 単一参照のため読まない。
2020    for (i, &stype) in sub_types.iter().enumerate() {
2021        let sx = px + (i % 2) * 8;
2022        let sy = py + (i / 2) * 8;
2023        let parts: &[(usize, usize, usize, usize)] = match stype {
2024            0 => &[(0, 0, 8, 8)],
2025            1 => &[(0, 0, 8, 4), (0, 4, 8, 4)],
2026            2 => &[(0, 0, 4, 8), (4, 0, 4, 8)],
2027            _ => &[(0, 0, 4, 4), (4, 0, 4, 4), (0, 4, 4, 4), (4, 4, 4, 4)],
2028        };
2029        for &(ox, oy, pw, ph) in parts {
2030            let ppx = sx + ox;
2031            let ppy = sy + oy;
2032            let mvd_x = r.read_se() as i16;
2033            let mvd_y = r.read_se() as i16;
2034            let mvp = mv_predict(st, ppx, ppy, pw, MvDir::None);
2035            let mv = [mvp[0].wrapping_add(mvd_x), mvp[1].wrapping_add(mvd_y)];
2036            mc_part(st, ppx, ppy, pw, ph, mv, refr);
2037            store_part_mv(st, ppx, ppy, pw, ph, mv);
2038        }
2039    }
2040    Ok(())
2041}
2042
2043/// インター: mb_type 0=16x16, 1=16x8, 2=8x16, 3/4=P_8x8。
2044#[allow(clippy::too_many_arguments)]
2045fn decode_mb_p(
2046    r: &mut BitReader,
2047    st: &mut DecState,
2048    mb_x: u32,
2049    mb_y: u32,
2050    qp: &mut i32,
2051    pps: &Pps,
2052    refr: &DecodedYuv,
2053) -> Result<(), &'static str> {
2054    let mb_type = r.read_ue();
2055    if mb_type >= 5 {
2056        let it = mb_type - 5;
2057        if it == 25 {
2058            return decode_mb_ipcm(r, st, mb_x, mb_y);
2059        }
2060        if it == 0 {
2061            return decode_mb_i4x4(r, st, mb_x, mb_y, qp, pps);
2062        }
2063        if (1..=24).contains(&it) {
2064            return decode_mb_i16x16(r, st, mb_x, mb_y, qp, it, pps);
2065        }
2066        return Err("bad intra mb_type in P slice");
2067    }
2068    // インター: 単一参照 (refIdx=0) 前提なので ref_idx_l0 は読まない。
2069    let px = mb_x as usize;
2070    let py = mb_y as usize;
2071    // 1 パーティション分の mvd を読み、MV 予測 + MC + 格納を行うクロージャ的処理。
2072    let do_part = |r: &mut BitReader,
2073                   st: &mut DecState,
2074                   ppx: usize,
2075                   ppy: usize,
2076                   pw: usize,
2077                   ph: usize,
2078                   dir: MvDir| {
2079        let mvd_x = r.read_se() as i16;
2080        let mvd_y = r.read_se() as i16;
2081        let mvp = mv_predict(st, ppx, ppy, pw, dir);
2082        let mv = [mvp[0].wrapping_add(mvd_x), mvp[1].wrapping_add(mvd_y)];
2083        mc_part(st, ppx, ppy, pw, ph, mv, refr);
2084        store_part_mv(st, ppx, ppy, pw, ph, mv);
2085    };
2086    match mb_type {
2087        0 => {
2088            // P_L0_16x16
2089            do_part(r, st, px, py, 16, 16, MvDir::None);
2090        }
2091        1 => {
2092            // P_L0_L0_16x8(上→下)。mvd は両パートを順に読む。
2093            // 16x8 は上=Prefer B, 下=Prefer A。
2094            do_part(r, st, px, py, 16, 8, MvDir::B);
2095            do_part(r, st, px, py + 8, 16, 8, MvDir::A);
2096        }
2097        2 => {
2098            // P_L0_L0_8x16(左→右)。左=Prefer A, 右=Prefer C。
2099            do_part(r, st, px, py, 8, 16, MvDir::A);
2100            do_part(r, st, px + 8, py, 8, 16, MvDir::C);
2101        }
2102        3 | 4 => {
2103            // P_8x8 / P_8x8ref0
2104            decode_p8x8(r, st, px, py, refr)?;
2105        }
2106        _ => return Err("unknown P mb_type"),
2107    }
2108
2109    let codenum = r.read_ue() as usize;
2110    if codenum >= 48 {
2111        return Err("inter cbp codeNum out of range");
2112    }
2113    let cbp = CBP_INTER_MAP[codenum] as u32;
2114    let cbp_luma = cbp & 0xf;
2115    let cbp_chroma = cbp >> 4;
2116    let cur_qp = if cbp != 0 {
2117        let d = r.read_se();
2118        *qp = (*qp + d + 52) % 52;
2119        *qp
2120    } else {
2121        *qp
2122    };
2123    st.mb_qp[(mb_y / 16) as usize * st.mb_w + (mb_x / 16) as usize] = cur_qp;
2124
2125    add_inter_luma_residual(r, st, mb_x, mb_y, cbp_luma, cur_qp);
2126    add_inter_chroma_residual(
2127        r,
2128        st,
2129        mb_x,
2130        mb_y,
2131        cbp_chroma,
2132        cur_qp,
2133        pps.chroma_qp_index_offset,
2134    );
2135    Ok(())
2136}
2137
2138/// 輝度 4x4 ブロックの再構成(予測 + 残差)を行い、グリッドを更新する。
2139fn reconstruct_luma_4x4(st: &mut DecState, px: usize, py: usize, mode: u8, residual: &[i32; 16]) {
2140    let pred = st.predict_luma_4x4(px, py, mode);
2141    let w = st.yuv.width as usize;
2142    for yy in 0..4 {
2143        for xx in 0..4 {
2144            let v = clip1(pred[yy * 4 + xx] + residual[yy * 4 + xx]);
2145            st.yuv.y[(py + yy) * w + px + xx] = v as u8;
2146        }
2147    }
2148    let di = st.li(px / 4, py / 4);
2149    st.decoded[di] = true;
2150}
2151
2152fn decode_mb_i16x16(
2153    r: &mut BitReader,
2154    st: &mut DecState,
2155    mb_x: u32,
2156    mb_y: u32,
2157    qp: &mut i32,
2158    mb_type: u32,
2159    pps: &Pps,
2160) -> Result<(), &'static str> {
2161    let m1 = mb_type - 1;
2162    let i16_pred = (m1 % 4) as u8;
2163    let cbp_chroma = (m1 / 4) % 3;
2164    let cbp_luma = if m1 / 12 != 0 { 15 } else { 0 };
2165
2166    let intra_chroma_pred_mode = r.read_ue() as u8;
2167    let mb_qp_delta = r.read_se();
2168    *qp = (*qp + mb_qp_delta + 52) % 52;
2169    let cur_qp = *qp;
2170    st.mb_qp[(mb_y / 16) as usize * st.mb_w + (mb_x / 16) as usize] = cur_qp;
2171
2172    // 輝度 DC(16 係数, Hadamard)。nC は近傍 4x4 ブロックの nnz から。
2173    let bx0 = (mb_x / 4) as usize;
2174    let by0 = (mb_y / 4) as usize;
2175    let dc_nc = st.luma_nc(bx0, by0);
2176    let (dc_scan, _) = residual_block_cavlc(r, 16, dc_nc, TOTAL_ZEROS_16);
2177    let mut dc_block = [0i32; 16];
2178    for k in 0..16 {
2179        dc_block[ZIGZAG_4X4[k]] = dc_scan[k];
2180    }
2181    inverse_hadamard_4x4(&mut dc_block);
2182    scale_luma_dc(&mut dc_block, cur_qp);
2183
2184    // 輝度 AC(cbp_luma のとき各 4x4)。
2185    let mut luma_ac = [[0i32; 16]; 16];
2186    for blk in 0..16 {
2187        let (bx, by) = LUMA_4X4_BLOCK_XY[blk];
2188        let bx4 = bx0 + bx / 4;
2189        let by4 = by0 + by / 4;
2190        let li = st.li(bx4, by4);
2191        if cbp_luma != 0 {
2192            let nc = st.luma_nc(bx4, by4);
2193            let (ac_scan, tc) = residual_block_cavlc(r, 15, nc, TOTAL_ZEROS_16);
2194            for k in 0..15 {
2195                luma_ac[blk][ZIGZAG_4X4[k + 1]] = ac_scan[k];
2196            }
2197            st.nnz_luma[li] = tc;
2198        } else {
2199            st.nnz_luma[li] = 0;
2200        }
2201    }
2202
2203    // 輝度予測(I_16x16、MB 全体)。
2204    let w = st.yuv.width as usize;
2205    let mut top = [0i32; 16];
2206    let mut left = [0i32; 16];
2207    let top_avail = mb_y > 0;
2208    let left_avail = mb_x > 0;
2209    if top_avail {
2210        for x in 0..16 {
2211            top[x] = st.yuv.y[(mb_y as usize - 1) * w + mb_x as usize + x] as i32;
2212        }
2213    }
2214    if left_avail {
2215        for y in 0..16 {
2216            left[y] = st.yuv.y[(mb_y as usize + y) * w + mb_x as usize - 1] as i32;
2217        }
2218    }
2219    let corner = if top_avail && left_avail {
2220        st.yuv.y[(mb_y as usize - 1) * w + mb_x as usize - 1] as i32
2221    } else {
2222        0
2223    };
2224    let pred = intra_16x16_predict(i16_pred, &top, corner, &left, top_avail, left_avail);
2225
2226    // 輝度再構成。
2227    for blk in 0..16 {
2228        let (bx, by) = LUMA_4X4_BLOCK_XY[blk];
2229        let dc_idx = (by / 4) * 4 + (bx / 4);
2230        let mut block = luma_ac[blk];
2231        dequant_4x4(&mut block, cur_qp);
2232        block[0] = dc_block[dc_idx];
2233        inverse_transform_4x4(&mut block);
2234        for yy in 0..4 {
2235            for xx in 0..4 {
2236                let px = mb_x as usize + bx + xx;
2237                let py = mb_y as usize + by + yy;
2238                let p = pred[(by + yy) * 16 + (bx + xx)];
2239                st.yuv.y[py * w + px] = clip1(p + block[yy * 4 + xx]) as u8;
2240            }
2241        }
2242        let di = st.li((mb_x as usize + bx) / 4, (mb_y as usize + by) / 4);
2243        st.decoded[di] = true;
2244    }
2245
2246    decode_chroma(
2247        r,
2248        st,
2249        mb_x,
2250        mb_y,
2251        cbp_chroma,
2252        intra_chroma_pred_mode,
2253        cur_qp,
2254        pps.chroma_qp_index_offset,
2255    )
2256}
2257
2258fn decode_mb_i4x4(
2259    r: &mut BitReader,
2260    st: &mut DecState,
2261    mb_x: u32,
2262    mb_y: u32,
2263    qp: &mut i32,
2264    pps: &Pps,
2265) -> Result<(), &'static str> {
2266    let bx0 = (mb_x / 4) as usize;
2267    let by0 = (mb_y / 4) as usize;
2268
2269    // 16 個の Intra4x4 予測モードを読む。
2270    let mut modes = [0u8; 16];
2271    for blk in 0..16 {
2272        let (bx, by) = LUMA_4X4_BLOCK_XY[blk];
2273        let bx4 = bx0 + bx / 4;
2274        let by4 = by0 + by / 4;
2275        let pred_mode = st.pred_i4x4_mode(bx4, by4);
2276        let mode = if r.read_bit() == 1 {
2277            pred_mode
2278        } else {
2279            let rem = r.read_bits(3) as i32;
2280            if rem < pred_mode {
2281                rem
2282            } else {
2283                rem + 1
2284            }
2285        };
2286        modes[blk] = mode as u8;
2287        let li = st.li(bx4, by4);
2288        st.mode_luma[li] = mode as i8;
2289    }
2290
2291    let intra_chroma_pred_mode = r.read_ue() as u8;
2292    let codenum = r.read_ue() as usize;
2293    if codenum >= 48 {
2294        return Err("cbp codeNum out of range");
2295    }
2296    let cbp = CBP_INTRA_MAP[codenum] as u32;
2297    let cbp_luma = cbp & 0xf;
2298    let cbp_chroma = cbp >> 4;
2299
2300    let cur_qp = if cbp != 0 {
2301        let mb_qp_delta = r.read_se();
2302        *qp = (*qp + mb_qp_delta + 52) % 52;
2303        *qp
2304    } else {
2305        *qp
2306    };
2307    st.mb_qp[(mb_y / 16) as usize * st.mb_w + (mb_x / 16) as usize] = cur_qp;
2308
2309    // ブロックごとに残差を読み、予測 + 再構成。
2310    for blk in 0..16 {
2311        let (bx, by) = LUMA_4X4_BLOCK_XY[blk];
2312        let bx4 = bx0 + bx / 4;
2313        let by4 = by0 + by / 4;
2314        let mut block = [0i32; 16];
2315        let li = st.li(bx4, by4);
2316        if cbp_luma & (1 << (blk / 4)) != 0 {
2317            let nc = st.luma_nc(bx4, by4);
2318            let (scan, tc) = residual_block_cavlc(r, 16, nc, TOTAL_ZEROS_16);
2319            for k in 0..16 {
2320                block[ZIGZAG_4X4[k]] = scan[k];
2321            }
2322            st.nnz_luma[li] = tc;
2323        } else {
2324            st.nnz_luma[li] = 0;
2325        }
2326        dequant_4x4(&mut block, cur_qp);
2327        inverse_transform_4x4(&mut block);
2328        reconstruct_luma_4x4(
2329            st,
2330            mb_x as usize + bx,
2331            mb_y as usize + by,
2332            modes[blk],
2333            &block,
2334        );
2335    }
2336
2337    decode_chroma(
2338        r,
2339        st,
2340        mb_x,
2341        mb_y,
2342        cbp_chroma,
2343        intra_chroma_pred_mode,
2344        cur_qp,
2345        pps.chroma_qp_index_offset,
2346    )
2347}
2348
2349/// 輝度 QP -> クロマ QP(Table 8-15、qPi>=30 の写像)。
2350fn chroma_qp(qp_y: i32, offset: i32) -> i32 {
2351    let qpi = (qp_y + offset).clamp(0, 51);
2352    if qpi < 30 {
2353        qpi
2354    } else {
2355        const MAP: [i32; 22] = [
2356            29, 30, 31, 32, 32, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 39, 39, 39, 39,
2357        ];
2358        MAP[(qpi - 30) as usize]
2359    }
2360}
2361
2362/// 2x2 クロマ DC の逆 Hadamard 変換(raster 2x2、c[0..3])。
2363fn inverse_hadamard_2x2(c: [i32; 4]) -> [i32; 4] {
2364    [
2365        c[0] + c[1] + c[2] + c[3],
2366        c[0] - c[1] + c[2] - c[3],
2367        c[0] + c[1] - c[2] - c[3],
2368        c[0] - c[1] - c[2] + c[3],
2369    ]
2370}
2371
2372/// クロマ DC のスケーリング(8.5.11.1): dcC = ((f * LevelScale(qPc%6,0,0)) << (qPc/6)) >> 5。
2373fn scale_chroma_dc(f: i32, qpc: i32) -> i32 {
2374    let ls = 16 * NORM_ADJUST_4X4[(qpc % 6) as usize][0];
2375    ((f * ls) << (qpc / 6)) >> 5
2376}
2377
2378/// クロマ 4x4 ブロックの nC(クロマプレーン内の左/上 nnz から)。
2379fn chroma_nc(st: &DecState, comp: usize, cbx4: usize, cby4: usize) -> i32 {
2380    let na = if cbx4 > 0 {
2381        st.nnz_chroma[comp][cby4 * st.cblk_w + cbx4 - 1] as i32
2382    } else {
2383        -1
2384    };
2385    let nb = if cby4 > 0 {
2386        st.nnz_chroma[comp][(cby4 - 1) * st.cblk_w + cbx4] as i32
2387    } else {
2388        -1
2389    };
2390    combine_nc(na, nb)
2391}
2392
2393/// クロマ復号(4:2:0)。予測 + 残差(DC: 2x2 Hadamard、AC: cbp_chroma==2 のとき)。
2394fn decode_chroma(
2395    r: &mut BitReader,
2396    st: &mut DecState,
2397    mb_x: u32,
2398    mb_y: u32,
2399    cbp_chroma: u32,
2400    chroma_pred_mode: u8,
2401    qp_y: i32,
2402    chroma_qp_offset: i32,
2403) -> Result<(), &'static str> {
2404    let cw = (st.yuv.width / 2) as usize;
2405    let cmb_x = (mb_x / 2) as usize;
2406    let cmb_y = (mb_y / 2) as usize;
2407    let cbx0 = cmb_x / 4;
2408    let cby0 = cmb_y / 4;
2409    let qpc = chroma_qp(qp_y, chroma_qp_offset);
2410
2411    // 各成分の DC(2x2)と AC(4 ブロック)を読む。
2412    // 残差は raster 順: 成分0(Cb) DC, 成分1(Cr) DC, 次に AC(cbp_chroma==2)。
2413    let mut dc = [[0i32; 4]; 2];
2414    if cbp_chroma != 0 {
2415        for comp in 0..2 {
2416            let (scan, _) = residual_block_cavlc(r, 4, -1, TOTAL_ZEROS_CHROMA);
2417            // chroma DC スキャン順は raster(2x2)そのまま
2418            let raw = [scan[0], scan[1], scan[2], scan[3]];
2419            let f = inverse_hadamard_2x2(raw);
2420            for i in 0..4 {
2421                dc[comp][i] = scale_chroma_dc(f[i], qpc);
2422            }
2423        }
2424    }
2425    let mut ac = [[[0i32; 16]; 4]; 2]; // [comp][block][coeff(raster)]
2426    if cbp_chroma == 2 {
2427        for comp in 0..2 {
2428            for blk in 0..4 {
2429                let bx = (blk % 2) * 4;
2430                let by = (blk / 2) * 4;
2431                let cbx4 = cbx0 + bx / 4;
2432                let cby4 = cby0 + by / 4;
2433                let nc = chroma_nc(st, comp, cbx4, cby4);
2434                let (scan, tc) = residual_block_cavlc(r, 15, nc, TOTAL_ZEROS_16);
2435                for k in 0..15 {
2436                    ac[comp][blk][ZIGZAG_4X4[k + 1]] = scan[k];
2437                }
2438                st.nnz_chroma[comp][cby4 * st.cblk_w + cbx4] = tc;
2439            }
2440        }
2441    }
2442
2443    // 予測 + 再構成(成分ごと)。
2444    let ca_t = cmb_y > 0;
2445    let ca_l = cmb_x > 0;
2446    for comp in 0..2 {
2447        let plane: &mut Vec<u8> = if comp == 0 {
2448            &mut st.yuv.u
2449        } else {
2450            &mut st.yuv.v
2451        };
2452        let mut ct = [0i32; 8];
2453        let mut cl = [0i32; 8];
2454        if ca_t {
2455            for x in 0..8 {
2456                ct[x] = plane[(cmb_y - 1) * cw + cmb_x + x] as i32;
2457            }
2458        }
2459        if ca_l {
2460            for y in 0..8 {
2461                cl[y] = plane[(cmb_y + y) * cw + cmb_x - 1] as i32;
2462            }
2463        }
2464        let corner = if ca_t && ca_l {
2465            plane[(cmb_y - 1) * cw + cmb_x - 1] as i32
2466        } else {
2467            0
2468        };
2469        let cpred = intra_chroma_predict(chroma_pred_mode, &ct, corner, &cl, ca_t, ca_l);
2470
2471        for blk in 0..4 {
2472            let bx = (blk % 2) * 4;
2473            let by = (blk / 2) * 4;
2474            let mut block = ac[comp][blk];
2475            dequant_4x4(&mut block, qpc);
2476            block[0] = dc[comp][blk]; // DC は別経路
2477            inverse_transform_4x4(&mut block);
2478            for yy in 0..4 {
2479                for xx in 0..4 {
2480                    let px = cmb_x + bx + xx;
2481                    let py = cmb_y + by + yy;
2482                    let p = cpred[(by + yy) * 8 + (bx + xx)];
2483                    plane[py * cw + px] = clip1(p + block[yy * 4 + xx]) as u8;
2484                }
2485            }
2486        }
2487    }
2488    Ok(())
2489}
2490
2491// ===================== デブロッキングフィルタ =====================
2492
2493fn iclip(v: i32, lo: i32, hi: i32) -> i32 {
2494    v.clamp(lo, hi)
2495}
2496
2497/// indexA/indexB から (alpha, beta, tc0) を引く。
2498fn deblock_thresh(qpav: i32, alpha_off: i32, beta_off: i32, bs: u8) -> (i32, i32, i32) {
2499    let ia = iclip(qpav + alpha_off, 0, 51) as usize;
2500    let ib = iclip(qpav + beta_off, 0, 51) as usize;
2501    // bS=4 は強フィルタで tc0 を使わない(TC0_TABLE は bS=1..3 のみ)。
2502    let tc0 = if (1..=3).contains(&bs) {
2503        TC0_TABLE[ia][(bs - 1) as usize] as i32
2504    } else {
2505        0
2506    };
2507    (ALPHA_TABLE[ia] as i32, BETA_TABLE[ib] as i32, tc0)
2508}
2509
2510/// 輝度 1 エッジ(長さ len)をフィルタ。vert: 垂直エッジ。(ex,ey)=q 側左上(フレーム座標)。
2511#[allow(clippy::too_many_arguments)]
2512fn deblock_luma_edge(
2513    plane: &mut [u8],
2514    w: usize,
2515    vert: bool,
2516    ex: usize,
2517    ey: usize,
2518    len: usize,
2519    bs: u8,
2520    alpha: i32,
2521    beta: i32,
2522    tc0: i32,
2523) {
2524    if bs == 0 || alpha == 0 {
2525        return;
2526    }
2527    let across: isize = if vert { 1 } else { w as isize };
2528    for i in 0..len {
2529        let p0i: isize = if vert {
2530            ((ey + i) * w + (ex - 1)) as isize
2531        } else {
2532            ((ey - 1) * w + (ex + i)) as isize
2533        };
2534        let idx = |o: isize| -> usize { (p0i + o * across) as usize };
2535        let p0 = plane[idx(0)] as i32;
2536        let p1 = plane[idx(-1)] as i32;
2537        let p2 = plane[idx(-2)] as i32;
2538        let p3 = plane[idx(-3)] as i32;
2539        let q0 = plane[idx(1)] as i32;
2540        let q1 = plane[idx(2)] as i32;
2541        let q2 = plane[idx(3)] as i32;
2542        if (p0 - q0).abs() >= alpha || (p1 - p0).abs() >= beta || (q1 - q0).abs() >= beta {
2543            continue;
2544        }
2545        let ap = (p2 - p0).abs();
2546        let aq = (q2 - q0).abs();
2547        if bs < 4 {
2548            let mut tc = tc0;
2549            if ap < beta {
2550                tc += 1;
2551            }
2552            if aq < beta {
2553                tc += 1;
2554            }
2555            let delta = iclip(((q0 - p0) * 4 + (p1 - q1) + 4) >> 3, -tc, tc);
2556            plane[idx(0)] = clip1(p0 + delta) as u8;
2557            plane[idx(1)] = clip1(q0 - delta) as u8;
2558            if ap < beta {
2559                let d = iclip((p2 + ((p0 + q0 + 1) >> 1) - 2 * p1) >> 1, -tc0, tc0);
2560                plane[idx(-1)] = clip1(p1 + d) as u8;
2561            }
2562            if aq < beta {
2563                let d = iclip((q2 + ((p0 + q0 + 1) >> 1) - 2 * q1) >> 1, -tc0, tc0);
2564                plane[idx(2)] = clip1(q1 + d) as u8;
2565            }
2566        } else {
2567            let strong = (p0 - q0).abs() < (alpha >> 2) + 2;
2568            if ap < beta && strong {
2569                plane[idx(0)] = clip1((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) as u8;
2570                plane[idx(-1)] = clip1((p2 + p1 + p0 + q0 + 2) >> 2) as u8;
2571                plane[idx(-2)] = clip1((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) as u8;
2572            } else {
2573                plane[idx(0)] = clip1((2 * p1 + p0 + q1 + 2) >> 2) as u8;
2574            }
2575            if aq < beta && strong {
2576                let q3 = plane[idx(4)] as i32;
2577                plane[idx(1)] = clip1((q2 + 2 * q1 + 2 * q0 + 2 * p0 + p1 + 4) >> 3) as u8;
2578                plane[idx(2)] = clip1((q2 + q1 + q0 + p0 + 2) >> 2) as u8;
2579                plane[idx(3)] = clip1((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) as u8;
2580            } else {
2581                plane[idx(1)] = clip1((2 * q1 + q0 + p1 + 2) >> 2) as u8;
2582            }
2583        }
2584    }
2585}
2586
2587/// クロマ 1 エッジ(4:2:0、p0/q0 のみ修正)。
2588#[allow(clippy::too_many_arguments)]
2589fn deblock_chroma_edge(
2590    plane: &mut [u8],
2591    cw: usize,
2592    vert: bool,
2593    ex: usize,
2594    ey: usize,
2595    len: usize,
2596    bs: u8,
2597    alpha: i32,
2598    beta: i32,
2599    tc0: i32,
2600) {
2601    if bs == 0 || alpha == 0 {
2602        return;
2603    }
2604    let across: isize = if vert { 1 } else { cw as isize };
2605    for i in 0..len {
2606        let p0i: isize = if vert {
2607            ((ey + i) * cw + (ex - 1)) as isize
2608        } else {
2609            ((ey - 1) * cw + (ex + i)) as isize
2610        };
2611        let idx = |o: isize| -> usize { (p0i + o * across) as usize };
2612        let p0 = plane[idx(0)] as i32;
2613        let p1 = plane[idx(-1)] as i32;
2614        let q0 = plane[idx(1)] as i32;
2615        let q1 = plane[idx(2)] as i32;
2616        if (p0 - q0).abs() >= alpha || (p1 - p0).abs() >= beta || (q1 - q0).abs() >= beta {
2617            continue;
2618        }
2619        if bs < 4 {
2620            let tc = tc0 + 1;
2621            let delta = iclip(((q0 - p0) * 4 + (p1 - q1) + 4) >> 3, -tc, tc);
2622            plane[idx(0)] = clip1(p0 + delta) as u8;
2623            plane[idx(1)] = clip1(q0 - delta) as u8;
2624        } else {
2625            plane[idx(0)] = clip1((2 * p1 + p0 + q1 + 2) >> 2) as u8;
2626            plane[idx(1)] = clip1((2 * q1 + q0 + p1 + 2) >> 2) as u8;
2627        }
2628    }
2629}
2630
2631/// フレーム全体にデブロッキングを適用(I スライス: MB 境界 bS=4、内部 bS=3)。
2632/// 2 つの 4x4 ブロック間の境界強度(8.7.2.1)。I/P 両対応。
2633/// p_li/q_li: 輝度 4x4 ブロックの線形 index。is_mb_edge: MB 境界エッジか。
2634fn boundary_strength(st: &DecState, p_li: usize, q_li: usize, is_mb_edge: bool) -> u8 {
2635    let p_intra = !st.is_inter[p_li];
2636    let q_intra = !st.is_inter[q_li];
2637    if p_intra || q_intra {
2638        return if is_mb_edge { 4 } else { 3 };
2639    }
2640    if st.nnz_luma[p_li] > 0 || st.nnz_luma[q_li] > 0 {
2641        return 2;
2642    }
2643    // 単一参照前提: 参照差・MV 本数差は発生せず、MV 差のみ。
2644    let m = st.mv_l0[p_li];
2645    let n = st.mv_l0[q_li];
2646    if (m[0] - n[0]).abs() >= 4 || (m[1] - n[1]).abs() >= 4 {
2647        return 1;
2648    }
2649    0
2650}
2651
2652fn deblock_frame(
2653    st: &mut DecState,
2654    mb_w: usize,
2655    mb_h: usize,
2656    alpha_off: i32,
2657    beta_off: i32,
2658    chroma_qp_off: i32,
2659) {
2660    let w = st.yuv.width as usize;
2661    let cw = (st.yuv.width / 2) as usize;
2662    let blk_w = st.blk_w;
2663    for mby in 0..mb_h {
2664        for mbx in 0..mb_w {
2665            let qp = st.mb_qp[mby * mb_w + mbx];
2666            let qp_left = if mbx > 0 {
2667                st.mb_qp[mby * mb_w + mbx - 1]
2668            } else {
2669                qp
2670            };
2671            let qp_top = if mby > 0 {
2672                st.mb_qp[(mby - 1) * mb_w + mbx]
2673            } else {
2674                qp
2675            };
2676            let qbx = mbx * 4; // この MB の左上 4x4 ブロック座標
2677            let qby = mby * 4;
2678
2679            // 輝度 垂直エッジ(xe=0,4,8,12): 4 行セグメントごとに bS。
2680            for k in 0..4usize {
2681                let xe = k * 4;
2682                if xe == 0 && mbx == 0 {
2683                    continue;
2684                }
2685                let is_mb_edge = xe == 0;
2686                let qp2 = if is_mb_edge { qp_left } else { qp };
2687                let qpav = (qp + qp2 + 1) >> 1;
2688                for seg in 0..4usize {
2689                    let qx = qbx + k;
2690                    let qy = qby + seg;
2691                    let q_li = qy * blk_w + qx;
2692                    let p_li = qy * blk_w + qx - 1;
2693                    let bs = boundary_strength(st, p_li, q_li, is_mb_edge);
2694                    if bs == 0 {
2695                        continue;
2696                    }
2697                    let (a, b, t) = deblock_thresh(qpav, alpha_off, beta_off, bs);
2698                    deblock_luma_edge(
2699                        &mut st.yuv.y,
2700                        w,
2701                        true,
2702                        mbx * 16 + xe,
2703                        mby * 16 + seg * 4,
2704                        4,
2705                        bs,
2706                        a,
2707                        b,
2708                        t,
2709                    );
2710                }
2711            }
2712            // 輝度 水平エッジ(ye=0,4,8,12)
2713            for k in 0..4usize {
2714                let ye = k * 4;
2715                if ye == 0 && mby == 0 {
2716                    continue;
2717                }
2718                let is_mb_edge = ye == 0;
2719                let qp2 = if is_mb_edge { qp_top } else { qp };
2720                let qpav = (qp + qp2 + 1) >> 1;
2721                for seg in 0..4usize {
2722                    let qx = qbx + seg;
2723                    let qy = qby + k;
2724                    let q_li = qy * blk_w + qx;
2725                    let p_li = (qy - 1) * blk_w + qx;
2726                    let bs = boundary_strength(st, p_li, q_li, is_mb_edge);
2727                    if bs == 0 {
2728                        continue;
2729                    }
2730                    let (a, b, t) = deblock_thresh(qpav, alpha_off, beta_off, bs);
2731                    deblock_luma_edge(
2732                        &mut st.yuv.y,
2733                        w,
2734                        false,
2735                        mbx * 16 + seg * 4,
2736                        mby * 16 + ye,
2737                        4,
2738                        bs,
2739                        a,
2740                        b,
2741                        t,
2742                    );
2743                }
2744            }
2745            // クロマ: 輝度 x=0,8(垂直)/ y=0,8(水平)の bS を流用。クロマ 2 行/列セグメント。
2746            let qpc = chroma_qp(qp, chroma_qp_off);
2747            let qpc_left = chroma_qp(qp_left, chroma_qp_off);
2748            let qpc_top = chroma_qp(qp_top, chroma_qp_off);
2749            for comp in 0..2usize {
2750                // 垂直エッジ cxe=0,4(輝度 luma_xe=0,8)
2751                for ck in 0..2usize {
2752                    let cxe = ck * 4;
2753                    let luma_k = ck * 2; // 輝度ブロック列オフセット(0 or 2)
2754                    if cxe == 0 && mbx == 0 {
2755                        continue;
2756                    }
2757                    let is_mb_edge = cxe == 0;
2758                    let qpav = (qpc + if is_mb_edge { qpc_left } else { qpc } + 1) >> 1;
2759                    for seg in 0..4usize {
2760                        let qx = qbx + luma_k;
2761                        let qy = qby + seg;
2762                        let q_li = qy * blk_w + qx;
2763                        let p_li = qy * blk_w + qx - 1;
2764                        let bs = boundary_strength(st, p_li, q_li, is_mb_edge);
2765                        if bs == 0 {
2766                            continue;
2767                        }
2768                        let (a, b, t) = deblock_thresh(qpav, alpha_off, beta_off, bs);
2769                        let plane = if comp == 0 {
2770                            &mut st.yuv.u
2771                        } else {
2772                            &mut st.yuv.v
2773                        };
2774                        deblock_chroma_edge(
2775                            plane,
2776                            cw,
2777                            true,
2778                            mbx * 8 + cxe,
2779                            mby * 8 + seg * 2,
2780                            2,
2781                            bs,
2782                            a,
2783                            b,
2784                            t,
2785                        );
2786                    }
2787                }
2788                // 水平エッジ cye=0,4(輝度 luma_ye=0,8)
2789                for ck in 0..2usize {
2790                    let cye = ck * 4;
2791                    let luma_k = ck * 2;
2792                    if cye == 0 && mby == 0 {
2793                        continue;
2794                    }
2795                    let is_mb_edge = cye == 0;
2796                    let qpav = ((qpc + if is_mb_edge { qpc_top } else { qpc }) + 1) >> 1;
2797                    for seg in 0..4usize {
2798                        let qx = qbx + seg;
2799                        let qy = qby + luma_k;
2800                        let q_li = qy * blk_w + qx;
2801                        let p_li = (qy - 1) * blk_w + qx;
2802                        let bs = boundary_strength(st, p_li, q_li, is_mb_edge);
2803                        if bs == 0 {
2804                            continue;
2805                        }
2806                        let (a, b, t) = deblock_thresh(qpav, alpha_off, beta_off, bs);
2807                        let plane = if comp == 0 {
2808                            &mut st.yuv.u
2809                        } else {
2810                            &mut st.yuv.v
2811                        };
2812                        deblock_chroma_edge(
2813                            plane,
2814                            cw,
2815                            false,
2816                            mbx * 8 + seg * 2,
2817                            mby * 8 + cye,
2818                            2,
2819                            bs,
2820                            a,
2821                            b,
2822                            t,
2823                        );
2824                    }
2825                }
2826            }
2827        }
2828    }
2829}
2830
2831// ===================== テーブル健全性チェック =====================
2832
2833/// (len, code) 群が prefix-free か検査。衝突する組を見つけたら false。
2834fn is_prefix_free(entries: &[(u8, u16)], label: &str) -> bool {
2835    let mut ok = true;
2836    for i in 0..entries.len() {
2837        for j in 0..entries.len() {
2838            if i == j {
2839                continue;
2840            }
2841            let (la, ca) = entries[i];
2842            let (lb, cb) = entries[j];
2843            if la <= lb && (cb >> (lb - la)) == ca {
2844                crate::println!(
2845                    "  PREFIX-CONFLICT in {}: ({},{:b}) is prefix of ({},{:b})",
2846                    label,
2847                    la,
2848                    ca,
2849                    lb,
2850                    cb
2851                );
2852                ok = false;
2853            }
2854        }
2855    }
2856    ok
2857}
2858
2859/// coeff_token テーブルに有効な (tc, t1) 組が漏れなく 1 回ずつあるか検査。
2860/// max_tc までの全組(tc=0:(0,0)、tc>=1: t1=0..min(tc,3))を期待。
2861fn check_token_complete(t: &[TokEntry], max_tc: u8, label: &str) -> bool {
2862    let mut ok = true;
2863    for tc in 0..=max_tc {
2864        let max_t1 = if tc == 0 { 0 } else { tc.min(3) };
2865        for t1 in 0..=max_t1 {
2866            let count = t.iter().filter(|&&(_, _, c, o)| c == tc && o == t1).count();
2867            if count != 1 {
2868                crate::println!(
2869                    "  TOKEN {} ({},{}) appears {} times (want 1)",
2870                    label,
2871                    tc,
2872                    t1,
2873                    count
2874                );
2875                ok = false;
2876            }
2877        }
2878    }
2879    ok
2880}
2881
2882/// 全 CAVLC VLC テーブルの prefix-free 性 + coeff_token の完全性を検査。
2883pub fn tables_selftest() {
2884    let mut ok = true;
2885    let toks: [(&str, &[TokEntry]); 4] = [
2886        ("coeff_token_0", COEFF_TOKEN_0),
2887        ("coeff_token_2", COEFF_TOKEN_2),
2888        ("coeff_token_4", COEFF_TOKEN_4),
2889        ("coeff_token_cdc", COEFF_TOKEN_CHROMA_DC),
2890    ];
2891    for (name, t) in toks {
2892        let e: Vec<(u8, u16)> = t.iter().map(|&(l, c, _, _)| (l, c)).collect();
2893        if !is_prefix_free(&e, name) {
2894            ok = false;
2895        }
2896    }
2897    if !check_token_complete(COEFF_TOKEN_0, 16, "tok0") {
2898        ok = false;
2899    }
2900    if !check_token_complete(COEFF_TOKEN_2, 16, "tok2") {
2901        ok = false;
2902    }
2903    if !check_token_complete(COEFF_TOKEN_4, 16, "tok4") {
2904        ok = false;
2905    }
2906    if !check_token_complete(COEFF_TOKEN_CHROMA_DC, 4, "tokCDC") {
2907        ok = false;
2908    }
2909    // total_zeros の行 tc は total_zeros=0..(16-tc) の 17-tc エントリを持つはず。
2910    for (i, row) in TOTAL_ZEROS_16.iter().enumerate() {
2911        let tc = i + 1;
2912        if row.len() != 17 - tc {
2913            crate::println!(
2914                "  TZ16 row tc={} has {} entries (want {})",
2915                tc,
2916                row.len(),
2917                17 - tc
2918            );
2919            ok = false;
2920        }
2921    }
2922    for (i, row) in TOTAL_ZEROS_16.iter().enumerate() {
2923        if !is_prefix_free(row, "tz16") {
2924            crate::println!("   (tz16 row tc={})", i + 1);
2925            ok = false;
2926        }
2927    }
2928    for row in TOTAL_ZEROS_CHROMA.iter() {
2929        if !is_prefix_free(row, "tzC") {
2930            ok = false;
2931        }
2932    }
2933    for row in RUN_BEFORE.iter() {
2934        if !is_prefix_free(row, "run_before") {
2935            ok = false;
2936        }
2937    }
2938    // 正常時は無音(バッファ節約)。コンフリクト時のみ出す。
2939    if !ok {
2940        crate::println!("H264_TABLES: CONFLICTS FOUND");
2941    }
2942}
2943
2944// ===================== ゴールデンテスト =====================
2945
2946/// ゴールデン素材 1 本をデコードし、Y 面の一致画素数を返す。
2947fn run_golden(
2948    clip: &[u8],
2949    ref_yuv: &[u8],
2950) -> Result<(usize, usize, usize, usize, usize), &'static str> {
2951    let nals = split_annexb(clip);
2952    let mut sps: Option<Sps> = None;
2953    let mut pps: Option<Pps> = None;
2954    let mut reference: Option<DecodedYuv> = None;
2955    for nu in &nals {
2956        match nu.unit_type {
2957            7 => {
2958                sps = Some(parse_sps(&nu.rbsp)?);
2959            }
2960            8 => {
2961                pps = Some(parse_pps(&nu.rbsp)?);
2962            }
2963            1 | 5 => {
2964                let s = sps.as_ref().ok_or("no SPS")?;
2965                let p = pps.as_ref().ok_or("no PPS")?;
2966                let frame = decode_slice(s, p, &nu.rbsp, nu.unit_type == 5, reference.as_ref())?;
2967                reference = Some(frame);
2968            }
2969            _ => {}
2970        }
2971    }
2972    let yuv = reference.ok_or("no slice decoded")?;
2973    let yn = (yuv.width * yuv.height) as usize;
2974    let cn = yuv.u.len();
2975    // ref_yuv が複数フレームのとき、デコード結果は最後のフレーム。最後のフレームと比較。
2976    let fsize = yn + 2 * cn;
2977    let nframes = ref_yuv.len() / fsize;
2978    let off = (nframes.saturating_sub(1)) * fsize;
2979    let mut ym = 0;
2980    for i in 0..yn {
2981        if yuv.y[i] == ref_yuv[off + i] {
2982            ym += 1;
2983        }
2984    }
2985    let mut um = 0;
2986    for i in 0..cn {
2987        if yuv.u[i] == ref_yuv[off + yn + i] {
2988            um += 1;
2989        }
2990    }
2991    let mut vm = 0;
2992    for i in 0..cn {
2993        if yuv.v[i] == ref_yuv[off + yn + cn + i] {
2994            vm += 1;
2995        }
2996    }
2997    Ok((ym, um, vm, yn, cn))
2998}
2999
3000/// 不一致を 16x16 MB 単位で集計してデバッグ出力(複数フレーム対応、最後のフレームを比較)。
3001fn run_golden_debug(clip: &[u8], ref_yuv: &[u8]) {
3002    let nals = split_annexb(clip);
3003    let mut sps: Option<Sps> = None;
3004    let mut pps: Option<Pps> = None;
3005    let mut reference: Option<DecodedYuv> = None;
3006    for nu in &nals {
3007        match nu.unit_type {
3008            7 => sps = parse_sps(&nu.rbsp).ok(),
3009            8 => pps = parse_pps(&nu.rbsp).ok(),
3010            1 | 5 => {
3011                if let (Some(s), Some(p)) = (sps.as_ref(), pps.as_ref()) {
3012                    if let Ok(f) =
3013                        decode_slice(s, p, &nu.rbsp, nu.unit_type == 5, reference.as_ref())
3014                    {
3015                        reference = Some(f);
3016                    }
3017                }
3018            }
3019            _ => {}
3020        }
3021    }
3022    if let Some(yuv) = reference {
3023        let w = yuv.width as usize;
3024        let h = yuv.height as usize;
3025        let fsize = w * h + 2 * yuv.u.len();
3026        let off = (ref_yuv.len() / fsize).saturating_sub(1) * fsize;
3027        let mut shown = 0;
3028        'outer: for mby in (0..h).step_by(16) {
3029            for mbx in (0..w).step_by(16) {
3030                let mut bad = 0;
3031                let mut first = (0u8, 0u8, 0usize, 0usize);
3032                for yy in 0..16 {
3033                    for xx in 0..16 {
3034                        let i = (mby + yy) * w + mbx + xx;
3035                        if yuv.y[i] != ref_yuv[off + i] {
3036                            if bad == 0 {
3037                                first = (yuv.y[i], ref_yuv[off + i], mbx + xx, mby + yy);
3038                            }
3039                            bad += 1;
3040                        }
3041                    }
3042                }
3043                if bad > 0 {
3044                    crate::println!(
3045                        "  MB({},{}) bad={}/256 first@({},{}) got={} exp={}",
3046                        mbx,
3047                        mby,
3048                        bad,
3049                        first.2,
3050                        first.3,
3051                        first.0,
3052                        first.1
3053                    );
3054                    shown += 1;
3055                    if shown >= 8 {
3056                        break 'outer;
3057                    }
3058                }
3059            }
3060        }
3061    }
3062}
3063
3064/// 起動時に呼ぶゴールデン検証。結果をログ出力する。
3065pub fn golden_test() -> bool {
3066    use super::h264_golden::*;
3067    tables_selftest();
3068    let mut pass = 0usize;
3069    let mut total = 0usize;
3070    for (name, clip, refy) in [
3071        ("flat", FLAT_H264.as_slice(), FLAT_YUV.as_slice()),
3072        ("clip", CLIP_H264.as_slice(), CLIP_YUV.as_slice()),
3073        ("big", BIG_H264.as_slice(), BIG_YUV.as_slice()),
3074        ("dbclip", DBCLIP_H264.as_slice(), DBCLIP_YUV.as_slice()),
3075        ("dbbig", DBBIG_H264.as_slice(), DBBIG_YUV.as_slice()),
3076        ("pskip", PSKIP_H264.as_slice(), PSKIP_YUV.as_slice()),
3077        ("pint16", PINT16_H264.as_slice(), PINT16_YUV.as_slice()),
3078        ("psub", PSUB_H264.as_slice(), PSUB_YUV.as_slice()),
3079        ("ppart", PPART_H264.as_slice(), PPART_YUV.as_slice()),
3080        ("dbp", DBP_H264.as_slice(), DBP_YUV.as_slice()),
3081    ] {
3082        match run_golden(clip, refy) {
3083            Ok((ym, um, vm, yn, cn)) => {
3084                total += 1;
3085                if ym == yn && um == cn && vm == cn {
3086                    pass += 1;
3087                } else {
3088                    // 不一致のケースだけ詳細を出す(バッファを食わないため正常時は無音)。
3089                    crate::println!(
3090                        "H264_GOLDEN {}: MISMATCH Y {}/{} U {}/{} V {}/{}",
3091                        name,
3092                        ym,
3093                        yn,
3094                        um,
3095                        cn,
3096                        vm,
3097                        cn
3098                    );
3099                    if ym != yn {
3100                        run_golden_debug(clip, refy);
3101                    }
3102                }
3103            }
3104            Err(e) => {
3105                total += 1;
3106                crate::println!("H264_GOLDEN {}: ERROR {}", name, e);
3107            }
3108        }
3109    }
3110    // テスト詳細はサイレントモードで実行されるため、mismatch時以外は出力されない
3111    crate::info!("[H264] Golden test pass: {}/{}", pass, total);
3112    pass == total
3113}