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