]> git.nihav.org Git - nihav.git/blob - nihav-misc/src/codecs/pgvv.rs
Radius Studio Video decoder
[nihav.git] / nihav-misc / src / codecs / pgvv.rs
1 // Somewhat simplified JPEG decoder
2 // TODO: move common JPEG decoding into codec-support
3
4 use nihav_core::io::byteio::{ByteReader,MemoryReader};
5 use nihav_core::io::bitreader::*;
6 use nihav_core::io::codebook::*;
7 use nihav_core::codecs::*;
8 use nihav_codec_support::codecs::ZIGZAG;
9
10 const W1: i32 = 2841;
11 const W2: i32 = 2676;
12 const W3: i32 = 2408;
13 const W5: i32 = 1609;
14 const W6: i32 = 1108;
15 const W7: i32 = 565;
16 const W8: i32 = 181;
17
18 const ROW_SHIFT: u8 = 8;
19 const COL_SHIFT: u8 = 14;
20
21 #[allow(clippy::erasing_op)]
22 fn idct_row(row: &mut [i16]) {
23 let in0 = ((i32::from(row[0])) << 11) + (1 << (ROW_SHIFT - 1));
24 let in1 = (i32::from(row[4])) << 11;
25 let in2 = i32::from(row[6]);
26 let in3 = i32::from(row[2]);
27 let in4 = i32::from(row[1]);
28 let in5 = i32::from(row[7]);
29 let in6 = i32::from(row[5]);
30 let in7 = i32::from(row[3]);
31
32 let tmp = W7 * (in4 + in5);
33 let a4 = tmp + (W1 - W7) * in4;
34 let a5 = tmp - (W1 + W7) * in5;
35
36 let tmp = W3 * (in6 + in7);
37 let a6 = tmp - (W3 - W5) * in6;
38 let a7 = tmp - (W3 + W5) * in7;
39
40 let tmp = in0 + in1;
41
42 let a0 = in0 - in1;
43 let t1 = W6 * (in2 + in3);
44 let a2 = t1 - (W2 + W6) * in2;
45 let a3 = t1 + (W2 - W6) * in3;
46 let b1 = a4 + a6;
47
48 let b4 = a4 - a6;
49 let t2 = a5 - a7;
50 let b6 = a5 + a7;
51 let b7 = tmp + a3;
52 let b5 = tmp - a3;
53 let b3 = a0 + a2;
54 let b0 = a0 - a2;
55 let b2 = (W8 * (b4 + t2) + 128) >> 8;
56 let b4 = (W8 * (b4 - t2) + 128) >> 8;
57
58 row[0] = ((b7 + b1) >> ROW_SHIFT) as i16;
59 row[7] = ((b7 - b1) >> ROW_SHIFT) as i16;
60 row[1] = ((b3 + b2) >> ROW_SHIFT) as i16;
61 row[6] = ((b3 - b2) >> ROW_SHIFT) as i16;
62 row[2] = ((b0 + b4) >> ROW_SHIFT) as i16;
63 row[5] = ((b0 - b4) >> ROW_SHIFT) as i16;
64 row[3] = ((b5 + b6) >> ROW_SHIFT) as i16;
65 row[4] = ((b5 - b6) >> ROW_SHIFT) as i16;
66 }
67
68 #[allow(clippy::erasing_op)]
69 #[allow(clippy::identity_op)]
70 fn idct_col(blk: &mut [i16; 64], off: usize) {
71 let in0 = ((i32::from(blk[off + 0*8])) << 8) + (1 << (COL_SHIFT - 1));
72 let in1 = (i32::from(blk[off + 4*8])) << 8;
73 let in2 = i32::from(blk[off + 6*8]);
74 let in3 = i32::from(blk[off + 2*8]);
75 let in4 = i32::from(blk[off + 1*8]);
76 let in5 = i32::from(blk[off + 7*8]);
77 let in6 = i32::from(blk[off + 5*8]);
78 let in7 = i32::from(blk[off + 3*8]);
79
80 let tmp = W7 * (in4 + in5);
81 let a4 = (tmp + (W1 - W7) * in4) >> 3;
82 let a5 = (tmp - (W1 + W7) * in5) >> 3;
83
84 let tmp = W3 * (in6 + in7);
85 let a6 = (tmp - (W3 - W5) * in6) >> 3;
86 let a7 = (tmp - (W3 + W5) * in7) >> 3;
87
88 let tmp = in0 + in1;
89
90 let a0 = in0 - in1;
91 let t1 = W6 * (in2 + in3);
92 let a2 = (t1 - (W2 + W6) * in2) >> 3;
93 let a3 = (t1 + (W2 - W6) * in3) >> 3;
94 let b1 = a4 + a6;
95
96 let b4 = a4 - a6;
97 let t2 = a5 - a7;
98 let b6 = a5 + a7;
99 let b7 = tmp + a3;
100 let b5 = tmp - a3;
101 let b3 = a0 + a2;
102 let b0 = a0 - a2;
103 let b2 = (W8 * (b4 + t2) + 128) >> 8;
104 let b4 = (W8 * (b4 - t2) + 128) >> 8;
105
106 blk[off + 0*8] = ((b7 + b1) >> COL_SHIFT) as i16;
107 blk[off + 7*8] = ((b7 - b1) >> COL_SHIFT) as i16;
108 blk[off + 1*8] = ((b3 + b2) >> COL_SHIFT) as i16;
109 blk[off + 6*8] = ((b3 - b2) >> COL_SHIFT) as i16;
110 blk[off + 2*8] = ((b0 + b4) >> COL_SHIFT) as i16;
111 blk[off + 5*8] = ((b0 - b4) >> COL_SHIFT) as i16;
112 blk[off + 3*8] = ((b5 + b6) >> COL_SHIFT) as i16;
113 blk[off + 4*8] = ((b5 - b6) >> COL_SHIFT) as i16;
114 }
115
116 fn idct(blk: &mut [i16; 64]) {
117 for i in 0..8 { idct_row(&mut blk[i*8..(i+1)*8]); }
118 for i in 0..8 { idct_col(blk, i); }
119 }
120
121 fn put_block(blk: &[i16; 64], dst: &mut [u8], stride: usize) {
122 for (drow, srow) in dst.chunks_mut(stride).zip(blk.chunks(8)) {
123 for (del, &pix) in drow.iter_mut().zip(srow.iter()) {
124 *del = pix.max(0).min(255) as u8;
125 }
126 }
127 }
128
129 #[derive(Clone,Copy,Default)]
130 struct ComponentInfo {
131 component_id: usize,
132 dc_table_id: usize,
133 ac_table_id: usize,
134 }
135
136 struct RadiusStudioDecoder {
137 info: NACodecInfoRef,
138 quant: [[i16; 64]; 4],
139 qselect: [u8; MAX_CHROMATONS],
140 subsamp: [u8; MAX_CHROMATONS],
141 codebook: [[Option<Codebook<u8>>; 4]; 2],
142 width: usize,
143 height: usize,
144 }
145
146 fn read_dc(br: &mut BitReader, cb: &Codebook<u8>) -> DecoderResult<i16> {
147 let cat = br.read_cb(cb)?;
148 if cat == 0 {
149 Ok(0)
150 } else {
151 validate!(cat < 12);
152 let add_bits = br.read(cat)? as i16;
153 let pivot = 1 << (cat - 1);
154 if add_bits < pivot {
155 Ok(add_bits + 1 - pivot * 2)
156 } else {
157 Ok(add_bits)
158 }
159 }
160 }
161
162 fn read_ac(br: &mut BitReader, cb: &Codebook<u8>) -> DecoderResult<(usize, i16)> {
163 let val = br.read_cb(cb)?;
164 let run = usize::from(val >> 4);
165 let cat = val & 0xF;
166 let level = if cat != 0 {
167 validate!(cat < 11);
168 let add_bits = br.read(cat)? as i16;
169 let pivot = 1 << (cat - 1);
170 if add_bits < pivot {
171 add_bits + 1 - pivot * 2
172 } else {
173 add_bits
174 }
175 } else {
176 validate!(run == 0 || run == 15);
177 0
178 };
179 Ok((run, level))
180 }
181
182 fn read_block(br: &mut BitReader, blk: &mut [i16; 64], dc_cb: &Codebook<u8>, ac_cb: &Codebook<u8>, ss: usize, se: usize, qmat: &[i16; 64]) -> DecoderResult<()> {
183 if ss == 0 {
184 blk[0] = read_dc(br, dc_cb)?;
185 blk[0] *= qmat[0];
186 }
187 let mut idx = 1;
188 while idx <= se {
189 let (run, level) = read_ac(br, ac_cb)?;
190 if run == 0 && level == 0 {
191 break;
192 }
193 idx += run;
194 validate!(idx < 64);
195 blk[ZIGZAG[idx]] = level * qmat[idx];
196 idx += 1;
197 }
198 Ok(())
199 }
200
201 impl RadiusStudioDecoder {
202 fn new() -> Self {
203 let dummy_info = NACodecInfo::new_dummy();
204
205 Self {
206 info: dummy_info,
207 quant: [[0; 64]; 4],
208 qselect: [0, 1, 1, 0, 0],
209 subsamp: [0x21, 0x11, 0x11, 0, 0],
210 codebook: [[None, None, None, None], [None, None, None, None]],
211 width: 0,
212 height: 0,
213 }
214 }
215
216 #[allow(clippy::needless_range_loop)]
217 fn decode_scan(&mut self, src: &[u8], mut buf: NAVideoBufferRef<u8>, ci: &[ComponentInfo], ss: usize, se: usize) -> DecoderResult<usize> {
218 let num_components = ci.len();
219 let mut last_dc = [1024; MAX_CHROMATONS];
220 let mut dc_cbs = Vec::with_capacity(num_components);
221 let mut ac_cbs = Vec::with_capacity(num_components);
222 let mut qmats = [&self.quant[0]; MAX_CHROMATONS];
223 for (i, cinfo) in ci.iter().enumerate() {
224 dc_cbs.push(if let Some(ref cb) = self.codebook[0][cinfo.dc_table_id] {
225 cb
226 } else { unreachable!(); });
227 ac_cbs.push(if let Some(ref cb) = self.codebook[1][cinfo.ac_table_id] {
228 cb
229 } else { unreachable!(); });
230 qmats[i] = &self.quant[self.qselect[cinfo.component_id] as usize];
231 }
232
233 let frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap();
234
235 let mut br = BitReader::new(src, BitReaderMode::BE);
236
237 let mut offs = frm.offset;
238 let mut nblks = [0; MAX_CHROMATONS];
239 let mut xstep = [0; MAX_CHROMATONS];
240 let mut ystep = [0; MAX_CHROMATONS];
241 let mut hstep = 8;
242 let mut vstep = 8;
243 for i in 0..num_components {
244 let hs = (self.subsamp[i] >> 4) as usize;
245 let vs = (self.subsamp[i] & 0xF) as usize;
246 hstep = hstep.max(hs * 8);
247 vstep = vstep.max(vs * 8);
248 nblks[i] = hs * vs;
249 xstep[i] = hs * 8;
250 ystep[i] = vs * 8;
251 }
252
253 let mut blocks;
254 for _y in (0..self.height).step_by(vstep) {
255 for x in 0..(self.width + hstep - 1) / hstep {
256 for i in 0..num_components {
257 blocks = [[0; 64]; 4];
258 for blk in blocks[..nblks[i]].iter_mut() {
259 read_block(&mut br, blk, dc_cbs[i], ac_cbs[i], ss, se, qmats[i])?;
260 if i == 0 {
261 blk[0] += last_dc[i];
262 last_dc[i] = blk[0];
263 } else {
264 blk[0] += last_dc[1];
265 last_dc[1] = blk[0];
266 }
267 idct(blk);
268 }
269 match self.subsamp[i] {
270 0x11 => {
271 put_block(&blocks[0], &mut frm.data[offs[i] + x * 8..], frm.stride[i]);
272 },
273 0x21 => {
274 put_block(&blocks[0], &mut frm.data[offs[i] + x * 16..], frm.stride[i]);
275 put_block(&blocks[1], &mut frm.data[offs[i] + x * 16 + 8..], frm.stride[i]);
276 },
277 0x12 => {
278 put_block(&blocks[0], &mut frm.data[offs[i] + x * 8..], frm.stride[i]);
279 put_block(&blocks[1], &mut frm.data[offs[i] + x * 8 + frm.stride[i] * 8..], frm.stride[i]);
280 },
281 0x22 => {
282 for j in 0..4 {
283 put_block(&blocks[j], &mut frm.data[offs[i] + x * 16 + (j & 1) * 8 + (j >> 1) * 8 * frm.stride[i]..], frm.stride[i]);
284 }
285 },
286 _ => unreachable!(),
287 };
288 }
289 }
290 for i in 0..num_components {
291 offs[i] += frm.stride[i] * ystep[i];
292 }
293 }
294
295 Ok((br.tell() + 7) / 8)
296 }
297 }
298
299 struct HuffDescReader<'a> {
300 codes: &'a [u16],
301 bits: &'a [u8],
302 syms: &'a [u8],
303 }
304
305 impl<'a> CodebookDescReader<u8> for HuffDescReader<'a> {
306 fn bits(&mut self, idx: usize) -> u8 { self.bits[idx] }
307 fn code(&mut self, idx: usize) -> u32 { u32::from(self.codes[idx]) }
308 fn sym (&mut self, idx: usize) -> u8 { self.syms[idx] }
309 fn len(&mut self) -> usize { self.syms.len() }
310 }
311
312 fn generate_cb(lens: &[u8; 16], syms: &[u8]) -> DecoderResult<Codebook<u8>> {
313 let mut codes = [0; 256];
314 let mut bits = [0; 256];
315
316 let mut iter = bits.iter_mut();
317 for (i, &len) in lens.iter().enumerate() {
318 for _ in 0..len {
319 *iter.next().unwrap() = (i + 1) as u8;
320 }
321 }
322 let mut code = 0;
323 let mut si = bits[0];
324 let mut idx = 0;
325 while idx < syms.len() {
326 while idx < syms.len() && bits[idx] == si {
327 codes[idx] = code;
328 code += 1;
329 idx += 1;
330 }
331 while idx < syms.len() && bits[idx] != si {
332 code <<= 1;
333 si += 1;
334 }
335 }
336
337 let mut cbr = HuffDescReader { codes: &codes, bits: &bits, syms };
338 Ok(Codebook::new(&mut cbr, CodebookMode::MSB)?)
339 }
340
341 fn build_default_cb(dc: bool, idx: usize) -> DecoderResult<Codebook<u8>> {
342 if dc {
343 generate_cb(&DC_LENS[idx], &DC_SYMS)
344 } else {
345 generate_cb(&AC_LENS[idx], AC_SYMS[idx])
346 }
347 }
348
349 impl NADecoder for RadiusStudioDecoder {
350 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
351 if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
352 let w = vinfo.get_width();
353 let h = vinfo.get_height();
354 self.width = w;
355 self.height = h;
356 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, false, YUV420_FORMAT));
357 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
358 Ok(())
359 } else {
360 Err(DecoderError::InvalidData)
361 }
362 }
363 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
364 let src = pkt.get_buffer();
365 if src.len() < 2048 { return Err(DecoderError::ShortData); }
366 let mut mr = MemoryReader::new_read(&src);
367 let mut br = ByteReader::new(&mut mr);
368 let _size_v0 = br.read_u32be()? as usize;
369 let field2_size = br.read_u32be()? as usize;
370 if field2_size != 0 {
371 return Err(DecoderError::NotImplemented);
372 }
373 let version = br.read_u16be()?;
374 validate!(version < 2);
375 let hdr_size = if version == 0 { 16 } else { 32 };
376 br.read_skip(1)?;
377 let quant = br.read_byte()?;
378
379 let formaton = NAPixelFormaton {
380 model: ColorModel::YUV(YUVSubmodel::YUVJ),
381 components: 3,
382 comp_info: [
383 Some(NAPixelChromaton {
384 h_ss: 0, v_ss: 0,
385 packed: false,
386 depth: 8,
387 shift: 0,
388 comp_offs: 0,
389 next_elem: 1,
390 }),
391 Some(NAPixelChromaton {
392 h_ss: 1, v_ss: 0,
393 packed: false,
394 depth: 8,
395 shift: 0,
396 comp_offs: 1,
397 next_elem: 1,
398 }),
399 Some(NAPixelChromaton {
400 h_ss: 1, v_ss: 0,
401 packed: false,
402 depth: 8,
403 shift: 0,
404 comp_offs: 2,
405 next_elem: 1,
406 }),
407 None,
408 None
409 ],
410 elem_size: 0,
411 be: false,
412 alpha: false,
413 palette: false,
414 };
415 let vinfo = NAVideoInfo::new(self.width, self.height, false, formaton);
416 let bufinfo = alloc_video_buffer(vinfo, 4)?;
417
418 for (dc_id, dc_cb) in self.codebook[0].iter_mut().enumerate().take(2) {
419 if dc_cb.is_none() {
420 validate!(dc_id < 2);
421 *dc_cb = Some(build_default_cb(true, dc_id)?);
422 }
423 }
424 for (ac_id, ac_cb) in self.codebook[1].iter_mut().enumerate().take(2) {
425 if ac_cb.is_none() {
426 validate!(ac_id < 2);
427 *ac_cb = Some(build_default_cb(false, ac_id)?);
428 }
429 }
430
431 const DEF_LUMA_QUANT: [u8; 64] = [
432 16, 11, 12, 14, 12, 10, 16, 14,
433 13, 14, 18, 17, 16, 19, 24, 40,
434 26, 24, 22, 22, 24, 49, 35, 37,
435 29, 40, 58, 51, 61, 60, 57, 51,
436 56, 55, 64, 72, 92, 78, 64, 68,
437 87, 69, 55, 56, 80, 109, 81, 87,
438 95, 98, 103, 104, 103, 62, 77, 113,
439 121, 112, 100, 120, 92, 101, 103, 99
440 ];
441 const DEF_CHROMA_QUANT: [u8; 64] = [
442 17, 18, 18, 24, 21, 24, 47, 26,
443 26, 47, 99, 66, 56, 66, 99, 99,
444 99, 99, 99, 99, 99, 99, 99, 99,
445 99, 99, 99, 99, 99, 99, 99, 99,
446 99, 99, 99, 99, 99, 99, 99, 99,
447 99, 99, 99, 99, 99, 99, 99, 99,
448 99, 99, 99, 99, 99, 99, 99, 99,
449 99, 99, 99, 99, 99, 99, 99, 99
450 ];
451
452 for (i, (&luma_q, &chroma_q)) in DEF_LUMA_QUANT.iter().zip(DEF_CHROMA_QUANT.iter()).enumerate() {
453 self.quant[0][ZIGZAG[i]] = ((i16::from(quant) * i16::from(luma_q) + 25) / 50).max(1).min(255);
454 self.quant[1][ZIGZAG[i]] = ((i16::from(quant) * i16::from(chroma_q) + 25) / 50).max(1).min(255);
455 }
456
457 if let Some(buf) = bufinfo.get_vbuf() {
458 let ci = [
459 ComponentInfo { component_id: 1, dc_table_id: 0, ac_table_id: 0 },
460 ComponentInfo { component_id: 2, dc_table_id: 1, ac_table_id: 1 },
461 ComponentInfo { component_id: 3, dc_table_id: 1, ac_table_id: 1 },
462 ];
463 self.decode_scan(&src[hdr_size..], buf, &ci, 0, 63)?;
464 } else { unreachable!(); }
465
466 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
467 frm.set_keyframe(true);
468 frm.set_frame_type(FrameType::I);
469 Ok(frm.into_ref())
470 }
471 fn flush(&mut self) {
472 }
473 }
474
475 impl NAOptionHandler for RadiusStudioDecoder {
476 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
477 fn set_options(&mut self, _options: &[NAOption]) { }
478 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
479 }
480
481 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
482 Box::new(RadiusStudioDecoder::new())
483 }
484
485 #[cfg(test)]
486 mod test {
487 use nihav_core::codecs::RegisteredDecoders;
488 use nihav_core::demuxers::RegisteredDemuxers;
489 use nihav_codec_support::test::dec_video::*;
490 use crate::misc_register_all_decoders;
491 use nihav_commonfmt::generic_register_all_demuxers;
492 #[test]
493 fn test_pgvv() {
494 let mut dmx_reg = RegisteredDemuxers::new();
495 generic_register_all_demuxers(&mut dmx_reg);
496 let mut dec_reg = RegisteredDecoders::new();
497 misc_register_all_decoders(&mut dec_reg);
498 // sample: https://samples.mplayerhq.hu/V-codecs/PGVV-RadiusStudio/airfone.mov
499 test_decoding("mov", "pgvv", "assets/Misc/airfone.mov", Some(6), &dmx_reg,
500 &dec_reg, ExpectedTestResult::MD5Frames(vec![
501 [0x84c27180, 0x1ae0ab09, 0x8cb668b5, 0xeba06638],
502 [0x803eae91, 0x56618078, 0x95b8a98f, 0x7e718ad9],
503 [0xd9543391, 0x339d9cc5, 0x100e868c, 0xa467eda1],
504 [0x5f50f6a4, 0x20e88428, 0xb1f3a573, 0xd8ac5acf]]));
505 }
506 }
507
508 const DC_LENS: [[u8; 16]; 2] = [
509 [ 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 ],
510 [ 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ]
511 ];
512 const DC_SYMS: [u8; 12] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
513 const AC_LENS: [[u8; 16]; 2] = [
514 [ 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125 ],
515 [ 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119 ]
516 ];
517 const AC_SYMS: [&[u8]; 2] = [
518 &[
519 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
520 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
521 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
522 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
523 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
524 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
525 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
526 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
527 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
528 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
529 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
530 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
531 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
532 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
533 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
534 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
535 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
536 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
537 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
538 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
539 0xf9, 0xfa
540 ],
541 &[
542 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
543 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
544 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
545 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
546 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
547 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
548 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
549 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
550 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
551 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
552 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
553 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
554 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
555 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
556 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
557 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
558 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
559 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
560 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
561 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
562 0xf9, 0xfa
563 ]
564 ];