]>
Commit | Line | Data |
---|---|---|
1 | use nihav_core::codecs::*; | |
2 | use nihav_core::io::bitreader::*; | |
3 | ||
4 | const DICT_SIZE: usize = 4096; | |
5 | const START_BITS: u8 = 9; | |
6 | const MAX_BITS: u8 = 12; | |
7 | const START_POS: usize = 258; | |
8 | const INVALID_POS: usize = 65536; | |
9 | ||
10 | struct LZWState { | |
11 | dict_sym: [u8; DICT_SIZE], | |
12 | dict_prev: [u16; DICT_SIZE], | |
13 | dict_pos: usize, | |
14 | dict_lim: usize, | |
15 | idx_bits: u8, | |
16 | } | |
17 | ||
18 | impl LZWState { | |
19 | fn new() -> Self { | |
20 | Self { | |
21 | dict_sym: [0; DICT_SIZE], | |
22 | dict_prev: [0; DICT_SIZE], | |
23 | dict_pos: START_POS, | |
24 | dict_lim: 1 << START_BITS, | |
25 | idx_bits: START_BITS, | |
26 | } | |
27 | } | |
28 | fn reset(&mut self) { | |
29 | self.dict_pos = START_POS; | |
30 | self.dict_lim = 1 << START_BITS; | |
31 | self.idx_bits = START_BITS; | |
32 | } | |
33 | fn add(&mut self, prev: usize, sym: u8) { | |
34 | if self.dict_pos < self.dict_lim { | |
35 | self.dict_sym [self.dict_pos] = sym; | |
36 | self.dict_prev[self.dict_pos] = prev as u16; | |
37 | self.dict_pos += 1; | |
38 | } | |
39 | } | |
40 | fn decode_idx(&self, dst: &mut [u8], pos: usize, idx: usize) -> DecoderResult<usize> { | |
41 | let mut tot_len = 1; | |
42 | let mut tidx = idx; | |
43 | while tidx > 256 { | |
44 | tidx = self.dict_prev[tidx] as usize; | |
45 | tot_len += 1; | |
46 | } | |
47 | validate!(pos + tot_len <= dst.len()); | |
48 | ||
49 | let mut end = pos + tot_len - 1; | |
50 | let mut tidx = idx; | |
51 | while tidx > 256 { | |
52 | dst[end] = self.dict_sym[tidx]; | |
53 | end -= 1; | |
54 | tidx = self.dict_prev[tidx] as usize; | |
55 | } | |
56 | dst[end] = tidx as u8; | |
57 | ||
58 | Ok(tot_len) | |
59 | } | |
60 | #[allow(clippy::comparison_chain)] | |
61 | fn decode(&mut self, br: &mut BitReader, dst: &mut [u8]) -> DecoderResult<()> { | |
62 | self.reset(); | |
63 | ||
64 | let mut pos = 0; | |
65 | let mut lastidx = INVALID_POS; | |
66 | loop { | |
67 | let idx = br.read(self.idx_bits)? as usize; | |
68 | if idx == 256 { | |
69 | self.reset(); | |
70 | lastidx = INVALID_POS; | |
71 | continue; | |
72 | } | |
73 | if idx == 257 { | |
74 | break; | |
75 | } | |
76 | if idx < self.dict_pos { | |
77 | let len = self.decode_idx(dst, pos, idx)?; | |
78 | if lastidx != INVALID_POS { | |
79 | self.add(lastidx, dst[pos]); | |
80 | } | |
81 | pos += len; | |
82 | } else if idx == self.dict_pos { | |
83 | validate!(lastidx != INVALID_POS); | |
84 | let len = self.decode_idx(dst, pos, lastidx)?; | |
85 | let lastsym = dst[pos]; | |
86 | pos += len; | |
87 | validate!(pos < dst.len()); | |
88 | dst[pos] = lastsym; | |
89 | pos += 1; | |
90 | self.add(lastidx, lastsym); | |
91 | } else { | |
92 | return Err(DecoderError::InvalidData); | |
93 | } | |
94 | lastidx = idx; | |
95 | if self.dict_pos == self.dict_lim && self.idx_bits < MAX_BITS { | |
96 | self.dict_lim <<= 1; | |
97 | self.idx_bits += 1; | |
98 | } | |
99 | } | |
100 | validate!(pos == dst.len()); | |
101 | Ok(()) | |
102 | } | |
103 | } | |
104 | ||
105 | struct IPMADecoder { | |
106 | info: NACodecInfoRef, | |
107 | v1: bool, | |
108 | pal: [u8; 768], | |
109 | frame: Vec<u8>, | |
110 | fframe: Vec<u8>, | |
111 | dbuf: Vec<u8>, | |
112 | width: usize, | |
113 | height: usize, | |
114 | first: bool, | |
115 | lzw: LZWState, | |
116 | } | |
117 | ||
118 | impl IPMADecoder { | |
119 | fn new(v1: bool) -> Self { | |
120 | Self { | |
121 | info: NACodecInfoRef::default(), | |
122 | v1, | |
123 | pal: [0; 768], | |
124 | frame: Vec::new(), | |
125 | fframe: Vec::new(), | |
126 | dbuf: Vec::new(), | |
127 | width: 0, | |
128 | height: 0, | |
129 | first: false, | |
130 | lzw: LZWState::new(), | |
131 | } | |
132 | } | |
133 | } | |
134 | ||
135 | impl NADecoder for IPMADecoder { | |
136 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
137 | if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { | |
138 | self.width = vinfo.width; | |
139 | self.height = vinfo.height; | |
140 | self.frame = vec![0; self.width * self.height]; | |
141 | self.fframe = vec![0; self.width * self.height]; | |
142 | self.dbuf = vec![0; self.width * self.height]; | |
143 | self.pal = [0; 768]; | |
144 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.width, self.height, true, PAL8_FORMAT)); | |
145 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); | |
146 | ||
147 | self.first = self.v1; | |
148 | ||
149 | Ok(()) | |
150 | } else { | |
151 | Err(DecoderError::InvalidData) | |
152 | } | |
153 | } | |
154 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
155 | let src = pkt.get_buffer(); | |
156 | validate!(src.len() > 0); | |
157 | ||
158 | for sd in pkt.side_data.iter() { | |
159 | if let NASideData::Palette(true, ref pal) = sd { | |
160 | for (dst, src) in self.pal.chunks_mut(3).zip(pal.chunks(4)) { | |
161 | dst[0] = src[0]; | |
162 | dst[1] = src[1]; | |
163 | dst[2] = src[2]; | |
164 | } | |
165 | break; | |
166 | } | |
167 | } | |
168 | ||
169 | let mut br = BitReader::new(&src, BitReaderMode::LE); | |
170 | self.lzw.decode(&mut br, &mut self.dbuf)?; | |
171 | if self.first { | |
172 | self.fframe.copy_from_slice(&self.dbuf); | |
173 | self.first = false; | |
174 | } | |
175 | for (dp, (&sp, &rp)) in self.frame.iter_mut().zip(self.dbuf.iter().zip(self.fframe.iter())) { | |
176 | match sp { | |
177 | 0 => {}, | |
178 | 1 => *dp = rp, | |
179 | _ => *dp = sp, | |
180 | }; | |
181 | } | |
182 | ||
183 | let buf = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; | |
184 | let mut vbuf = buf.get_vbuf().unwrap(); | |
185 | let paloff = vbuf.get_offset(1); | |
186 | let stride = vbuf.get_stride(0); | |
187 | let data = vbuf.get_data_mut().unwrap(); | |
188 | ||
189 | for (drow, srow) in data.chunks_mut(stride).zip(self.frame.chunks(self.width)) { | |
190 | drow[..self.width].copy_from_slice(srow); | |
191 | } | |
192 | data[paloff..][..768].copy_from_slice(&self.pal); | |
193 | ||
194 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buf); | |
195 | let ftype = if pkt.keyframe { FrameType::I } else { FrameType::P }; | |
196 | frm.set_frame_type(ftype); | |
197 | Ok(frm.into_ref()) | |
198 | } | |
199 | fn flush(&mut self) { | |
200 | } | |
201 | } | |
202 | ||
203 | impl NAOptionHandler for IPMADecoder { | |
204 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
205 | fn set_options(&mut self, _options: &[NAOption]) { } | |
206 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
207 | } | |
208 | ||
209 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { | |
210 | Box::new(IPMADecoder::new(true)) | |
211 | } | |
212 | ||
213 | pub fn get_decoder_v2() -> Box<dyn NADecoder + Send> { | |
214 | Box::new(IPMADecoder::new(false)) | |
215 | } | |
216 | ||
217 | #[cfg(test)] | |
218 | mod test { | |
219 | use nihav_core::codecs::RegisteredDecoders; | |
220 | use nihav_core::demuxers::RegisteredDemuxers; | |
221 | use nihav_codec_support::test::dec_video::*; | |
222 | use crate::game_register_all_decoders; | |
223 | use nihav_commonfmt::generic_register_all_demuxers; | |
224 | #[test] | |
225 | fn test_ipma_video() { | |
226 | let mut dmx_reg = RegisteredDemuxers::new(); | |
227 | generic_register_all_demuxers(&mut dmx_reg); | |
228 | let mut dec_reg = RegisteredDecoders::new(); | |
229 | game_register_all_decoders(&mut dec_reg); | |
230 | ||
231 | test_decoding("avi", "ipma", "assets/Game/lripa07.avi", | |
232 | Some(2), &dmx_reg, &dec_reg, | |
233 | ExpectedTestResult::MD5Frames(vec![ | |
234 | [0x13a4e2e5, 0xeac494b1, 0xef107b9f, 0x1213bbc6], | |
235 | [0x09e2684a, 0xe453ec76, 0xff1c3cee, 0xf38701c9], | |
236 | [0xd39258b1, 0x2491b8d9, 0x37dd4415, 0x8ddb4669]])); | |
237 | } | |
238 | #[test] | |
239 | fn test_ip20_video() { | |
240 | let mut dmx_reg = RegisteredDemuxers::new(); | |
241 | generic_register_all_demuxers(&mut dmx_reg); | |
242 | let mut dec_reg = RegisteredDecoders::new(); | |
243 | game_register_all_decoders(&mut dec_reg); | |
244 | ||
245 | test_decoding("avi", "ipma2", "assets/Game/27mwza00.avi", | |
246 | Some(2), &dmx_reg, &dec_reg, | |
247 | ExpectedTestResult::MD5Frames(vec![ | |
248 | [0x8b000e62, 0x2e4e0344, 0x471e40ca, 0x7b9f83d4], | |
249 | [0xc1429aa9, 0x3f2e856b, 0x80b27114, 0x25b5d453], | |
250 | [0xfa24d5fa, 0x29bf2e17, 0x3cf5f550, 0x296e29b0]])); | |
251 | } | |
252 | } |