Imagination Pilots Matte Animation decoder
[nihav.git] / nihav-game / src / codecs / ipma.rs
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 }