]>
Commit | Line | Data |
---|---|---|
8a350691 KS |
1 | use nihav_core::codecs::*; |
2 | use nihav_core::io::bitreader::*; | |
3 | use nihav_core::io::codebook::*; | |
4 | ||
5 | use super::RGB555_FORMAT; | |
6 | use super::yuvtab::YUV2RGB; | |
7 | ||
8 | struct DeltaCodebook { | |
9 | cb: Codebook<u16>, | |
10 | } | |
11 | ||
8a350691 KS |
12 | impl Default for DeltaCodebook { |
13 | fn default() -> Self { | |
635e4170 | 14 | let mut cr = TableCodebookDescReader::new(&LUMA_CODES, &LUMA_BITS, |idx| idx as u16); |
8a350691 KS |
15 | let cb = Codebook::new(&mut cr, CodebookMode::LSB).unwrap(); |
16 | Self { cb } | |
17 | } | |
18 | } | |
19 | ||
20 | fn get_mv(br: &mut BitReader, is_4x4: bool) -> DecoderResult<((i8, i8), bool)> { | |
21 | match br.read(2)? { | |
22 | 0b00 => Ok((MV_TAB1[br.read(3)? as usize], false)), | |
23 | 0b10 => Ok((MV_TAB2[br.read(4)? as usize], false)), | |
24 | 0b01 => { | |
25 | let idx = br.read(5)? as usize; | |
26 | let self_tab = if is_4x4 { &MV_TAB_SELF_4X4 } else { &MV_TAB_SELF_2X2 }; | |
27 | if idx < self_tab.len() { | |
28 | Ok((self_tab[idx], true)) | |
29 | } else { | |
30 | Ok((MV_TAB3[idx - self_tab.len()], false)) | |
31 | } | |
32 | }, | |
33 | 0b11 => { | |
34 | let idx = br.read(8)? as usize; | |
35 | validate!(idx < MV_TAB8.len()); | |
36 | Ok((MV_TAB8[idx], false)) | |
37 | }, | |
38 | _ => unreachable!(), | |
39 | } | |
40 | } | |
41 | ||
42 | #[derive(Default)] | |
43 | struct MBDecoder { | |
44 | info: NACodecInfoRef, | |
45 | cur_frm: Vec<u16>, | |
46 | prev_frm: Vec<u16>, | |
47 | width: usize, | |
48 | height: usize, | |
49 | is_yuv: bool, | |
50 | cb: DeltaCodebook, | |
51 | } | |
52 | ||
53 | impl MBDecoder { | |
54 | fn new() -> Self { Self::default() } | |
55 | } | |
56 | ||
57 | impl NADecoder for MBDecoder { | |
58 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
59 | if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { | |
60 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, RGB555_FORMAT)); | |
61 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); | |
62 | self.cur_frm = vec![0; vinfo.get_width() * vinfo.get_height()]; | |
63 | self.prev_frm = vec![0; vinfo.get_width() * vinfo.get_height()]; | |
64 | self.width = vinfo.get_width(); | |
65 | self.height = vinfo.get_height(); | |
66 | validate!((self.width & 3) == 0); | |
67 | validate!((self.height & 3) == 0); | |
68 | self.is_yuv = true; | |
69 | Ok(()) | |
70 | } else { | |
71 | Err(DecoderError::InvalidData) | |
72 | } | |
73 | } | |
74 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
75 | let src = pkt.get_buffer(); | |
76 | validate!(src.len() > 2); | |
77 | let mut br = BitReader::new(&src, BitReaderMode::LE); | |
78 | ||
79 | let mut is_intra = true; | |
80 | let mut dpos = 0; | |
81 | let mut avg_y = 0; | |
82 | for _y in (0..self.height).step_by(4) { | |
83 | for x in (0..self.width).step_by(4) { | |
84 | match br.read(2)? { | |
85 | 0b00 => { // skip | |
86 | for (dline, sline) in self.cur_frm[dpos + x..].chunks_mut(self.width) | |
87 | .zip(self.prev_frm[dpos + x..].chunks(self.width)).take(4) { | |
88 | dline[..4].copy_from_slice(&sline[..4]); | |
89 | } | |
90 | }, | |
91 | 0b10 => { // MV | |
92 | let ((dx, dy), copy_cur) = get_mv(&mut br, true)?; | |
93 | let src_pos = ((dpos + x) as isize) + (dx as isize) + (dy as isize) * (self.width as isize); | |
94 | validate!(src_pos >= 0); | |
95 | let src_pos = src_pos as usize; | |
96 | validate!(src_pos + 4 + self.width * 3 <= self.cur_frm.len()); | |
97 | if !copy_cur { | |
98 | let src = &self.prev_frm[src_pos..]; | |
99 | for (drow, srow) in self.cur_frm[dpos + x..].chunks_mut(self.width) | |
100 | .zip(src.chunks(self.width)).take(4) { | |
101 | drow[..4].copy_from_slice(&srow[..4]); | |
102 | } | |
103 | is_intra = false; | |
104 | } else { | |
105 | let mut ooff = dpos + x; | |
106 | let mut soff = src_pos; | |
107 | for _ in 0..4 { | |
108 | for i in 0..4 { | |
109 | self.cur_frm[ooff + i] = self.cur_frm[soff + i]; | |
110 | } | |
111 | ooff += self.width; | |
112 | soff += self.width; | |
113 | } | |
114 | } | |
115 | }, | |
116 | 0b01 => { // raw | |
117 | let uv = (br.read(10)? as u16) << 5; | |
118 | ||
119 | let mut luma = [0; 16]; | |
120 | for el in luma.iter_mut() { | |
121 | *el = br.read_cb(&self.cb.cb)?; | |
122 | } | |
123 | let mut luma_sum = 0; | |
124 | for row in (0..16).step_by(4) { | |
125 | for col in 0..4 { | |
126 | let pred_val = match (col, row) { | |
127 | (0, 0) => avg_y, | |
128 | (0, _) => luma[col + row - 4], | |
129 | (_, 0) => luma[col - 1], | |
130 | _ => (luma[col + row - 1] + luma[col + row - 4]) >> 1, | |
131 | }; | |
132 | luma[col + row] = (luma[col + row] + pred_val) & 0x1F; | |
133 | luma_sum += luma[col + row]; | |
134 | } | |
135 | } | |
136 | avg_y = luma_sum >> 4; | |
137 | ||
138 | for (drow, yrow) in self.cur_frm[dpos + x..].chunks_mut(self.width) | |
139 | .zip(luma.chunks_exact(4)) { | |
140 | for (dst, &src_y) in drow.iter_mut().zip(yrow.iter()) { | |
141 | *dst = src_y | uv; | |
142 | } | |
143 | } | |
144 | }, | |
145 | _ => { // subdivision | |
146 | let offsets = [dpos + x, dpos + x + 2, dpos + x + self.width * 2, dpos + x + 2 + self.width * 2]; | |
147 | for &offset in offsets.iter() { | |
148 | if br.read_bool()? { // MV | |
149 | let ((dx, dy), copy_cur) = get_mv(&mut br, false)?; | |
150 | let src_pos = (offset as isize) + (dx as isize) + (dy as isize) * (self.width as isize); | |
151 | validate!(src_pos >= 0); | |
152 | let src_pos = src_pos as usize; | |
153 | validate!(src_pos + 2 + self.width <= self.cur_frm.len()); | |
154 | if !copy_cur { | |
155 | let src = &self.prev_frm[src_pos..]; | |
156 | for (drow, srow) in self.cur_frm[offset..].chunks_mut(self.width) | |
157 | .zip(src.chunks(self.width)).take(2) { | |
158 | drow[..2].copy_from_slice(&srow[..2]); | |
159 | } | |
160 | is_intra = false; | |
161 | } else { | |
162 | let mut ooff = offset; | |
163 | let mut soff = src_pos; | |
164 | for _ in 0..2 { | |
165 | for i in 0..2 { | |
166 | self.cur_frm[ooff + i] = self.cur_frm[soff + i]; | |
167 | } | |
168 | ooff += self.width; | |
169 | soff += self.width; | |
170 | } | |
171 | } | |
172 | } else if br.read_bool()? { // raw | |
173 | let uv = (br.read(10)? as u16) << 5; | |
174 | ||
175 | let mut luma = [0; 4]; | |
176 | for el in luma.iter_mut() { | |
177 | *el = br.read_cb(&self.cb.cb)?; | |
178 | } | |
179 | luma[0] = (luma[0] + avg_y) & 0x1F; | |
180 | luma[1] = (luma[1] + luma[0]) & 0x1F; | |
181 | luma[2] = (luma[2] + luma[0]) & 0x1F; | |
182 | luma[3] = (luma[3] + ((luma[1] + luma[2]) >> 1)) & 0x1F; | |
183 | avg_y = luma.iter().sum::<u16>() >> 2; | |
184 | ||
185 | self.cur_frm[offset] = luma[0] | uv; | |
186 | self.cur_frm[offset + 1] = luma[1] | uv; | |
187 | self.cur_frm[offset + self.width] = luma[2] | uv; | |
188 | self.cur_frm[offset + self.width + 1] = luma[3] | uv; | |
189 | } else { // skip | |
190 | for (dline, sline) in self.cur_frm[offset..].chunks_mut(self.width) | |
191 | .zip(self.prev_frm[offset..].chunks(self.width)).take(2) { | |
192 | dline[..2].copy_from_slice(&sline[..2]); | |
193 | } | |
194 | } | |
195 | } | |
196 | }, | |
197 | }; | |
198 | } | |
199 | dpos += self.width * 4; | |
200 | } | |
201 | ||
202 | let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; | |
203 | let mut buf = bufinfo.get_vbuf16().unwrap(); | |
204 | let stride = buf.get_stride(0); | |
205 | let data = buf.get_data_mut().unwrap(); | |
206 | ||
207 | for (dline, sline) in data.chunks_exact_mut(stride) | |
208 | .zip(self.cur_frm.chunks_exact(self.width)) { | |
209 | dline[..self.width].copy_from_slice(sline); | |
210 | } | |
211 | if self.is_yuv { | |
212 | for el in data.iter_mut() { | |
213 | *el = YUV2RGB[(*el as usize) & 0x7FFF]; | |
214 | } | |
215 | } | |
216 | ||
217 | std::mem::swap(&mut self.cur_frm, &mut self.prev_frm); | |
218 | ||
219 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); | |
220 | frm.set_keyframe(is_intra); | |
221 | frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); | |
222 | Ok(frm.into_ref()) | |
223 | } | |
224 | fn flush(&mut self) { | |
225 | for el in self.cur_frm.iter_mut() { | |
226 | *el = 0; | |
227 | } | |
228 | for el in self.prev_frm.iter_mut() { | |
229 | *el = 0; | |
230 | } | |
231 | } | |
232 | } | |
233 | ||
234 | impl NAOptionHandler for MBDecoder { | |
235 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
236 | fn set_options(&mut self, _options: &[NAOption]) { } | |
237 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
238 | } | |
239 | ||
240 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { | |
241 | Box::new(MBDecoder::new()) | |
242 | } | |
243 | ||
244 | #[derive(Default,Debug,PartialEq)] | |
245 | enum ParseState { | |
246 | #[default] | |
247 | Start, | |
248 | BlockMode, | |
249 | Raw(u8), | |
250 | Subblock(u8), | |
251 | SubblockRaw(u8, u8), | |
252 | } | |
253 | ||
254 | #[derive(Default)] | |
255 | struct MBPacketiser { | |
256 | stream: Option<NAStreamRef>, | |
257 | buf: Vec<u8>, | |
258 | frameno: u32, | |
259 | intra: bool, | |
260 | bitpos: usize, | |
261 | x: usize, | |
262 | y: usize, | |
263 | state: ParseState, | |
264 | width: usize, | |
265 | height: usize, | |
266 | csizes: Vec<usize>, | |
267 | } | |
268 | ||
269 | impl MBPacketiser { | |
270 | fn new() -> Self { Self::default() } | |
271 | fn peek_bits(&mut self, nbits: u8) -> Option<u8> { | |
272 | if self.bitpos + usize::from(nbits) <= self.buf.len() * 8 { | |
273 | let tail = (self.bitpos as u8) & 7; | |
274 | let mask = 0xFF >> (8 - nbits); | |
275 | let cw = if tail + nbits <= 8 { | |
276 | u16::from(self.buf[self.bitpos >> 3]) | |
277 | } else { | |
278 | let b0 = self.buf[self.bitpos >> 3]; | |
279 | let b1 = self.buf[(self.bitpos >> 3) + 1]; | |
280 | u16::from(b0) + u16::from(b1) * 256 | |
281 | }; | |
282 | Some(((cw >> tail) as u8) & mask) | |
283 | } else { | |
284 | None | |
285 | } | |
286 | } | |
287 | fn peek_code(&mut self) -> Option<u8> { | |
288 | let mut cur_code = 0; | |
289 | let mut avail_bits = 0; | |
290 | let tail = self.bitpos & 7; | |
291 | while (avail_bits < (9 + tail)) && (self.bitpos + avail_bits + 8 <= self.buf.len() * 8) { | |
292 | cur_code |= u32::from(self.buf[(self.bitpos + avail_bits) >> 3]) << avail_bits; | |
293 | avail_bits += 8; | |
294 | } | |
295 | if avail_bits <= tail { | |
296 | return None; | |
297 | } | |
298 | let cur_code = (cur_code >> tail) as u16; | |
299 | let avail_bits = (avail_bits - tail) as u8; | |
300 | for (&code, &len) in LUMA_CODES.iter().zip(LUMA_BITS.iter()) { | |
301 | if len <= avail_bits && (cur_code & ((1 << len) - 1)) == code { | |
302 | return Some(len); | |
303 | } | |
304 | } | |
305 | None | |
306 | } | |
307 | fn skip_bits(&mut self, nbits: u8) { | |
308 | self.bitpos += usize::from(nbits); | |
309 | } | |
310 | fn advance_block(&mut self) { | |
311 | self.x += 4; | |
312 | if self.x == self.width { | |
313 | self.x = 0; | |
314 | self.y += 4; | |
315 | } | |
316 | } | |
317 | } | |
318 | ||
319 | impl NAPacketiser for MBPacketiser { | |
320 | fn attach_stream(&mut self, stream: NAStreamRef) { | |
321 | let vinfo = stream.get_info().get_properties().get_video_info().unwrap(); | |
322 | self.width = vinfo.width; | |
323 | self.height = vinfo.height; | |
324 | self.stream = Some(stream); | |
325 | } | |
326 | fn add_data(&mut self, src: &[u8]) -> bool { | |
327 | self.csizes.push(src.len()); | |
328 | self.buf.extend_from_slice(src); | |
329 | self.buf.len() < (1 << 10) | |
330 | } | |
331 | fn parse_stream(&mut self, id: u32) -> DecoderResult<NAStreamRef> { | |
332 | if let Some(ref stream) = self.stream { | |
333 | let mut stream = NAStream::clone(stream); | |
334 | stream.id = id; | |
335 | Ok(stream.into_ref()) | |
336 | } else { | |
337 | Err(DecoderError::MissingReference) | |
338 | } | |
339 | } | |
340 | fn skip_junk(&mut self) -> DecoderResult<usize> { | |
341 | Err(DecoderError::NotImplemented) | |
342 | } | |
343 | fn get_packet(&mut self, stream: NAStreamRef) -> DecoderResult<Option<NAPacket>> { | |
344 | if self.buf.len() * 8 < self.bitpos { | |
345 | return Ok(None); | |
346 | } | |
347 | ||
348 | if self.state == ParseState::Start { | |
349 | self.intra = true; | |
350 | self.x = 0; | |
351 | self.y = 0; | |
352 | self.state = ParseState::BlockMode; | |
353 | self.bitpos = 0; | |
354 | } | |
355 | ||
356 | while self.y < self.height { | |
357 | match self.state { | |
358 | ParseState::Start => unreachable!(), | |
359 | ParseState::BlockMode => { | |
360 | if let Some(mode) = self.peek_bits(2) { | |
361 | match mode { | |
362 | 0b00 => { // skip | |
363 | self.skip_bits(2); | |
364 | self.intra = false; | |
365 | self.advance_block(); | |
366 | }, | |
367 | 0b10 => { // MV block | |
368 | if let Some(ret) = self.peek_bits(4) { | |
369 | let mv_mode = ret >> 2; | |
370 | match mv_mode { | |
371 | 0b00 => self.skip_bits(3), | |
372 | 0b10 => self.skip_bits(4), | |
373 | 0b01 => self.skip_bits(5), | |
374 | _ => self.skip_bits(8), | |
375 | } | |
376 | if mv_mode != 0b01 { | |
377 | self.intra = false; | |
378 | } | |
379 | } else { | |
380 | return Ok(None); | |
381 | } | |
382 | self.skip_bits(4); // block mode + MV mode | |
383 | self.advance_block(); | |
384 | }, | |
385 | 0b11 => { // subblocks | |
386 | self.skip_bits(2); | |
387 | self.state = ParseState::Subblock(0); | |
388 | }, | |
389 | _ => { // raw block | |
390 | self.skip_bits(2); | |
391 | self.skip_bits(10); // UV | |
392 | self.state = ParseState::Raw(0); | |
393 | }, | |
394 | } | |
395 | } else { | |
396 | return Ok(None); | |
397 | } | |
398 | }, | |
399 | ParseState::Raw(coef) => { | |
400 | if let Some(bits) = self.peek_code() { | |
401 | self.skip_bits(bits); | |
402 | } else { | |
403 | return Ok(None); | |
404 | } | |
405 | self.state = if coef < 15 { | |
406 | ParseState::Raw(coef + 1) | |
407 | } else { | |
408 | self.advance_block(); | |
409 | ParseState::BlockMode | |
410 | }; | |
411 | }, | |
412 | ParseState::Subblock(sblk) => { | |
413 | if let Some(mode) = self.peek_bits(2) { | |
414 | match mode { | |
415 | 0b00 => { // skip | |
416 | self.intra = false; | |
417 | self.skip_bits(2); // subblock mode | |
418 | }, | |
419 | 0b10 => { // raw | |
420 | self.skip_bits(2); // subblock mode | |
421 | self.skip_bits(10); // UV | |
422 | self.state = ParseState::SubblockRaw(sblk, 0); | |
423 | continue; | |
424 | }, | |
425 | _ => { // MV | |
426 | if let Some(ret) = self.peek_bits(3) { | |
427 | let mv_mode = ret >> 1; | |
428 | match mv_mode { | |
429 | 0b00 => self.skip_bits(3), | |
430 | 0b10 => self.skip_bits(4), | |
431 | 0b01 => self.skip_bits(5), | |
432 | _ => self.skip_bits(8), | |
433 | } | |
434 | if mv_mode != 0b01 { | |
435 | self.intra = false; | |
436 | } | |
437 | self.skip_bits(3); // subblock mode + MV mode | |
438 | } else { | |
439 | return Ok(None); | |
440 | } | |
441 | }, | |
442 | }; | |
443 | self.state = if sblk < 3 { | |
444 | ParseState::Subblock(sblk + 1) | |
445 | } else { | |
446 | self.advance_block(); | |
447 | ParseState::BlockMode | |
448 | }; | |
449 | } else { | |
450 | return Ok(None); | |
451 | } | |
452 | }, | |
453 | ParseState::SubblockRaw(sblk, coef) => { | |
454 | if let Some(bits) = self.peek_code() { | |
455 | self.skip_bits(bits); | |
456 | } else { | |
457 | return Ok(None); | |
458 | } | |
459 | self.state = if coef < 3 { | |
460 | ParseState::SubblockRaw(sblk, coef + 1) | |
461 | } else if sblk < 3 { | |
462 | ParseState::Subblock(sblk + 1) | |
463 | } else { | |
464 | self.advance_block(); | |
465 | ParseState::BlockMode | |
466 | }; | |
467 | }, | |
468 | } | |
469 | } | |
470 | ||
471 | let size = (self.bitpos + 7) >> 3; | |
472 | ||
473 | let mut data = Vec::with_capacity(size); | |
474 | data.extend_from_slice(&self.buf[..size]); | |
475 | self.buf.drain(..size); | |
476 | ||
477 | if !self.csizes.is_empty() { | |
478 | if self.csizes[0] >= size { | |
479 | self.csizes[0] -= size; | |
480 | // skip possible padding at the end of chunk | |
481 | if self.csizes[0] == 1 { | |
482 | self.buf.remove(0); | |
483 | self.csizes[0] -= 1; | |
484 | } | |
485 | if self.csizes[0] == 0 { | |
486 | self.csizes.remove(0); | |
487 | } | |
488 | } else { | |
489 | println!("ran past input chunk end!"); | |
490 | self.csizes.clear(); | |
491 | self.buf.clear(); | |
492 | } | |
493 | } | |
494 | ||
495 | let ts = NATimeInfo::new(Some(u64::from(self.frameno)), None, None, stream.tb_num, stream.tb_den); | |
496 | self.frameno += 1; | |
497 | ||
498 | self.state = ParseState::Start; | |
499 | ||
500 | Ok(Some(NAPacket::new(stream, ts, self.intra, data))) | |
501 | } | |
502 | fn reset(&mut self) { | |
503 | self.buf.clear(); | |
504 | self.bitpos = 0; | |
505 | self.state = ParseState::Start; | |
506 | } | |
507 | fn bytes_left(&self) -> usize { self.buf.len() } | |
508 | } | |
509 | ||
510 | pub fn get_packetiser() -> Box<dyn NAPacketiser + Send> { | |
511 | Box::new(MBPacketiser::new()) | |
512 | } | |
513 | ||
514 | #[cfg(test)] | |
515 | mod test { | |
516 | use nihav_core::codecs::{RegisteredDecoders, RegisteredPacketisers}; | |
517 | use nihav_core::demuxers::RegisteredRawDemuxers; | |
518 | use nihav_codec_support::test::dec_video::*; | |
519 | use crate::*; | |
520 | #[test] | |
521 | fn test_movingblockshq() { | |
522 | let mut dmx_reg = RegisteredRawDemuxers::new(); | |
523 | acorn_register_all_raw_demuxers(&mut dmx_reg); | |
524 | let mut pkt_reg = RegisteredPacketisers::new(); | |
525 | acorn_register_all_packetisers(&mut pkt_reg); | |
526 | let mut dec_reg = RegisteredDecoders::new(); | |
527 | acorn_register_all_decoders(&mut dec_reg); | |
528 | ||
529 | // a sample from RISC DISC 3 | |
530 | test_decoding_raw("armovie", "movingblockshq", "assets/Acorn/EXPLODE", Some(3), | |
531 | &dmx_reg, &pkt_reg, &dec_reg, | |
532 | ExpectedTestResult::MD5Frames(vec![ | |
7f570da3 KS |
533 | [0xc684fd94, 0x9e9d8c0a, 0xa37d639a, 0x12312d08], |
534 | [0x33c29033, 0xe2ad4765, 0xde9d5e71, 0x5db43fda], | |
535 | [0xe3ab66e3, 0xd3303ce8, 0xe3df8b08, 0x52f6b83e], | |
536 | [0x2403a32a, 0x047f46bf, 0xd236fa90, 0xcc1939be]])); | |
8a350691 KS |
537 | } |
538 | } | |
539 | ||
b5c81786 | 540 | const LUMA_CODES: [u16; 32] = [ |
8a350691 KS |
541 | 0x002, 0x007, 0x004, 0x008, 0x01D, 0x03B, 0x035, 0x05B, |
542 | 0x065, 0x070, 0x050, 0x0ED, 0x0A5, 0x0C5, 0x090, 0x19B, | |
543 | 0x16D, 0x06D, 0x09B, 0x010, 0x045, 0x025, 0x01B, 0x030, | |
544 | 0x005, 0x02D, 0x015, 0x00D, 0x000, 0x00B, 0x003, 0x001 | |
545 | ]; | |
546 | const LUMA_BITS: [u8; 32] = [ | |
547 | 2, 3, 3, 4, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, | |
548 | 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 4, 3 | |
549 | ]; | |
550 | ||
551 | const MV_TAB1: [(i8, i8); 8] = [ | |
552 | (-1, -1), (0, -1), (1, -1), | |
553 | (-1, 0), (1, 0), | |
554 | (-1, 1), (0, 1), (1, 1) | |
555 | ]; | |
556 | ||
557 | const MV_TAB2: [(i8, i8); 16] = [ | |
558 | (-2, -2), (-1, -2), (0, -2), (1, -2), (2, -2), | |
559 | (-2, -1), (2, -1), | |
560 | (-2, 0), (2, 0), | |
561 | (-2, 1), (2, 1), | |
562 | (-2, 2), (-1, 2), (0, 2), (1, 2), (2, 2) | |
563 | ]; | |
564 | ||
565 | const MV_TAB3: [(i8, i8); 24] = [ | |
566 | (-3, -3), (-2, -3), (-1, -3), ( 0, -3), (1, -3), (2, -3), (3, -3), | |
567 | (-3, -2), (3, -2), | |
568 | (-3, -1), (3, -1), | |
569 | (-3, 0), (3, 0), | |
570 | (-3, 1), (3, 1), | |
571 | (-3, 2), (3, 2), | |
572 | (-3, 3), (-2, 3), (-1, 3), ( 0, 3), (1, 3), (2, 3), (3, 3) | |
573 | ]; | |
574 | ||
575 | const MV_TAB8: [(i8, i8); 240] = [ | |
576 | (-8,-8), (-7,-8), (-6,-8), (-5,-8), (-4,-8), (-3,-8), (-2,-8), (-1,-8), (0,-8), (1,-8), (2,-8), (3,-8), (4,-8), (5,-8), (6,-8), (7,-8), (8,-8), | |
577 | (-8,-7), (-7,-7), (-6,-7), (-5,-7), (-4,-7), (-3,-7), (-2,-7), (-1,-7), (0,-7), (1,-7), (2,-7), (3,-7), (4,-7), (5,-7), (6,-7), (7,-7), (8,-7), | |
578 | (-8,-6), (-7,-6), (-6,-6), (-5,-6), (-4,-6), (-3,-6), (-2,-6), (-1,-6), (0,-6), (1,-6), (2,-6), (3,-6), (4,-6), (5,-6), (6,-6), (7,-6), (8,-6), | |
579 | (-8,-5), (-7,-5), (-6,-5), (-5,-5), (-4,-5), (-3,-5), (-2,-5), (-1,-5), (0,-5), (1,-5), (2,-5), (3,-5), (4,-5), (5,-5), (6,-5), (7,-5), (8,-5), | |
580 | (-8,-4), (-7,-4), (-6,-4), (-5,-4), (-4,-4), (-3,-4), (-2,-4), (-1,-4), (0,-4), (1,-4), (2,-4), (3,-4), (4,-4), (5,-4), (6,-4), (7,-4), (8,-4), | |
581 | (-8,-3), (-7,-3), (-6,-3), (-5,-3), (-4,-3), (4,-3), (5,-3), (6,-3), (7,-3), (8,-3), | |
582 | (-8,-2), (-7,-2), (-6,-2), (-5,-2), (-4,-2), (4,-2), (5,-2), (6,-2), (7,-2), (8,-2), | |
583 | (-8,-1), (-7,-1), (-6,-1), (-5,-1), (-4,-1), (4,-1), (5,-1), (6,-1), (7,-1), (8,-1), | |
584 | (-8, 0), (-7, 0), (-6, 0), (-5, 0), (-4, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), | |
585 | (-8, 1), (-7, 1), (-6, 1), (-5, 1), (-4, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), | |
586 | (-8, 2), (-7, 2), (-6, 2), (-5, 2), (-4, 2), (4, 2), (5, 2), (6, 2), (7, 2), (8, 2), | |
587 | (-8, 3), (-7, 3), (-6, 3), (-5, 3), (-4, 3), (4, 3), (5, 3), (6, 3), (7, 3), (8, 3), | |
588 | (-8, 4), (-7, 4), (-6, 4), (-5, 4), (-4, 4), (-3, 4), (-2, 4), (-1, 4), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4), (5, 4), (6, 4), (7, 4), (8, 4), | |
589 | (-8, 5), (-7, 5), (-6, 5), (-5, 5), (-4, 5), (-3, 5), (-2, 5), (-1, 5), (0, 5), (1, 5), (2, 5), (3, 5), (4, 5), (5, 5), (6, 5), (7, 5), (8, 5), | |
590 | (-8, 6), (-7, 6), (-6, 6), (-5, 6), (-4, 6), (-3, 6), (-2, 6), (-1, 6), (0, 6), (1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 6), (7, 6), (8, 6), | |
591 | (-8, 7), (-7, 7), (-6, 7), (-5, 7), (-4, 7), (-3, 7), (-2, 7), (-1, 7), (0, 7), (1, 7), (2, 7), (3, 7), (4, 7), (5, 7), (6, 7), (7, 7), (8, 7), | |
592 | (-8, 8), (-7, 8), (-6, 8), (-5, 8), (-4, 8), (-3, 8), (-2, 8), (-1, 8), (0, 8), (1, 8), (2, 8), (3, 8), (4, 8), (5, 8), (6, 8), (7, 8), (8, 8) | |
593 | ]; | |
594 | ||
595 | const MV_TAB_SELF_4X4: [(i8, i8); 8] = [ | |
596 | (-2, -4), (-1, -4), ( 0, -4), (1, -4), (2, -4), | |
597 | (-4, 0), (-4, -1), (-4, -2), | |
598 | ]; | |
599 | const MV_TAB_SELF_2X2: [(i8, i8); 8] = [ | |
600 | (-2, -2), (-1, -2), ( 0, -2), (1, -2), (2, -2), | |
601 | (-2, -1), (-2, 0), (-3, 0), | |
602 | ]; |