1 use nihav_core::codecs::*;
2 use nihav_core::io::bitreader::*;
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;
11 dict_sym: [u8; DICT_SIZE],
12 dict_prev: [u16; DICT_SIZE],
21 dict_sym: [0; DICT_SIZE],
22 dict_prev: [0; DICT_SIZE],
24 dict_lim: 1 << START_BITS,
29 self.dict_pos = START_POS;
30 self.dict_lim = 1 << START_BITS;
31 self.idx_bits = START_BITS;
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;
40 fn decode_idx(&self, dst: &mut [u8], pos: usize, idx: usize) -> DecoderResult<usize> {
44 tidx = self.dict_prev[tidx] as usize;
47 validate!(pos + tot_len <= dst.len());
49 let mut end = pos + tot_len - 1;
52 dst[end] = self.dict_sym[tidx];
54 tidx = self.dict_prev[tidx] as usize;
56 dst[end] = tidx as u8;
60 #[allow(clippy::comparison_chain)]
61 fn decode(&mut self, br: &mut BitReader, dst: &mut [u8]) -> DecoderResult<()> {
65 let mut lastidx = INVALID_POS;
67 let idx = br.read(self.idx_bits)? as usize;
70 lastidx = INVALID_POS;
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]);
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];
87 validate!(pos < dst.len());
90 self.add(lastidx, lastsym);
92 return Err(DecoderError::InvalidData);
95 if self.dict_pos == self.dict_lim && self.idx_bits < MAX_BITS {
100 validate!(pos == dst.len());
106 info: NACodecInfoRef,
119 fn new(v1: bool) -> Self {
121 info: NACodecInfoRef::default(),
130 lzw: LZWState::new(),
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];
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();
147 self.first = self.v1;
151 Err(DecoderError::InvalidData)
154 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
155 let src = pkt.get_buffer();
156 validate!(src.len() > 0);
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)) {
169 let mut br = BitReader::new(&src, BitReaderMode::LE);
170 self.lzw.decode(&mut br, &mut self.dbuf)?;
172 self.fframe.copy_from_slice(&self.dbuf);
175 for (dp, (&sp, &rp)) in self.frame.iter_mut().zip(self.dbuf.iter().zip(self.fframe.iter())) {
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();
189 for (drow, srow) in data.chunks_mut(stride).zip(self.frame.chunks(self.width)) {
190 drow[..self.width].copy_from_slice(srow);
192 data[paloff..][..768].copy_from_slice(&self.pal);
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);
199 fn flush(&mut self) {
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 }
209 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
210 Box::new(IPMADecoder::new(true))
213 pub fn get_decoder_v2() -> Box<dyn NADecoder + Send> {
214 Box::new(IPMADecoder::new(false))
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 // samples from https://misc.daniel-marschall.de/spiele/blown_away/ipma_codec/
226 fn test_ipma_video() {
227 let mut dmx_reg = RegisteredDemuxers::new();
228 generic_register_all_demuxers(&mut dmx_reg);
229 let mut dec_reg = RegisteredDecoders::new();
230 game_register_all_decoders(&mut dec_reg);
232 test_decoding("avi", "ipma", "assets/Game/lripa07.avi",
233 Some(2), &dmx_reg, &dec_reg,
234 ExpectedTestResult::MD5Frames(vec![
235 [0x13a4e2e5, 0xeac494b1, 0xef107b9f, 0x1213bbc6],
236 [0x09e2684a, 0xe453ec76, 0xff1c3cee, 0xf38701c9],
237 [0xd39258b1, 0x2491b8d9, 0x37dd4415, 0x8ddb4669]]));
240 fn test_ip20_video() {
241 let mut dmx_reg = RegisteredDemuxers::new();
242 generic_register_all_demuxers(&mut dmx_reg);
243 let mut dec_reg = RegisteredDecoders::new();
244 game_register_all_decoders(&mut dec_reg);
246 test_decoding("avi", "ipma2", "assets/Game/27mwza00.avi",
247 Some(2), &dmx_reg, &dec_reg,
248 ExpectedTestResult::MD5Frames(vec![
249 [0x8b000e62, 0x2e4e0344, 0x471e40ca, 0x7b9f83d4],
250 [0xc1429aa9, 0x3f2e856b, 0x80b27114, 0x25b5d453],
251 [0xfa24d5fa, 0x29bf2e17, 0x3cf5f550, 0x296e29b0]]));