]> git.nihav.org Git - nihav.git/blame - nihav-acorn/src/codecs/movingblockshq.rs
avimux: do not record palette change chunks in OpenDML index
[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
8a350691
KS
12impl 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
20fn 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)]
43struct 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
53impl MBDecoder {
54 fn new() -> Self { Self::default() }
55}
56
57impl 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
234impl 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
240pub fn get_decoder() -> Box<dyn NADecoder + Send> {
241 Box::new(MBDecoder::new())
242}
243
244#[derive(Default,Debug,PartialEq)]
245enum ParseState {
246 #[default]
247 Start,
248 BlockMode,
249 Raw(u8),
250 Subblock(u8),
251 SubblockRaw(u8, u8),
252}
253
254#[derive(Default)]
255struct 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
269impl 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
319impl 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
510pub fn get_packetiser() -> Box<dyn NAPacketiser + Send> {
511 Box::new(MBPacketiser::new())
512}
513
514#[cfg(test)]
515mod 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 540const 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];
546const 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
551const 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
557const 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
565const 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
575const 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
595const 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];
599const 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];