read lzw .EGA files

This commit is contained in:
2016-11-01 03:14:49 -07:00
parent da0d5a26a3
commit ddc135e09a
2 changed files with 152 additions and 26 deletions

View File

@@ -13,6 +13,7 @@ gl = "*"
gfx = "*"
gfx_device_gl = "*"
image = "*"
lzw = "*"
nalgebra = "*"
num-traits = "*"
openvr = { git = "https://github.com/rust-openvr/rust-openvr" }

View File

@@ -1,19 +1,25 @@
static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xAA, 0x00],
[0x00, 0xAA, 0x00, 0x00],
[0x00, 0xAA, 0xAA, 0x00],
[0xAA, 0x00, 0x00, 0x00],
[0xAA, 0x00, 0xAA, 0x00],
[0xAA, 0x55, 0x00, 0x00],
[0xAA, 0xAA, 0xAA, 0x00],
[0x55, 0x55, 0x55, 0x00],
[0x55, 0x55, 0xFF, 0x00],
[0x55, 0xFF, 0x55, 0x00],
[0x55, 0xFF, 0xFF, 0x00],
[0xFF, 0x55, 0x55, 0x00],
[0xFF, 0x55, 0xFF, 0x00],
[0xFF, 0xFF, 0x55, 0x00],
[0xFF, 0xFF, 0xFF, 0x00]];
extern crate lzw;
use std::cell::RefCell;
use self::lzw::BitReader;
static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0xFF],
[0x00, 0x00, 0xAA, 0xFF],
[0x00, 0xAA, 0x00, 0xFF],
[0x00, 0xAA, 0xAA, 0xFF],
[0xAA, 0x00, 0x00, 0xFF],
[0xAA, 0x00, 0xAA, 0xFF],
[0xAA, 0x55, 0x00, 0xFF],
[0xAA, 0xAA, 0xAA, 0xFF],
[0x55, 0x55, 0x55, 0xFF],
[0x55, 0x55, 0xFF, 0xFF],
[0x55, 0xFF, 0x55, 0xFF],
[0x55, 0xFF, 0xFF, 0xFF],
[0xFF, 0x55, 0x55, 0xFF],
[0xFF, 0x55, 0xFF, 0xFF],
[0xFF, 0xFF, 0x55, 0xFF],
[0xFF, 0xFF, 0xFF, 0xFF]];
pub enum Compression {
Uncompressed,
@@ -98,16 +104,12 @@ pub fn decode<'a>(buf: &[u8], compression: Compression, tiling: Tiling)
let out: Vec<u8>;
out = match compression {
Compression::Uncompressed => buf.iter()
.flat_map(|tile_byte| {
EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize]
.into_iter()
.chain(EGA_PALETTE[(tile_byte & 0xF) as usize]
.into_iter())
})
.map(|x| *x)
.collect(),
_ => unimplemented!()
Compression::Uncompressed => decode_uncompressed(buf),
Compression::Rle => unimplemented!(),
Compression::Lzw => {
let mut decoder = U4Lzw::new();
decode_uncompressed(&decoder.decode(buf))
},
};
let dim = match tiling {
Tiling::Tiled(tiledim) => tiledim as usize,
@@ -115,3 +117,126 @@ pub fn decode<'a>(buf: &[u8], compression: Compression, tiling: Tiling)
};
EgaPage { data: out, dim: dim}
}
fn decode_uncompressed(buf: &[u8]) -> Vec<u8> {
buf.iter()
.flat_map(|tile_byte| {
EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize]
.into_iter()
.chain(EGA_PALETTE[(tile_byte & 0xF) as usize]
.into_iter())
})
.map(|x| *x)
.collect()
}
struct U4Lzw {
table: Vec<RefCell<Vec<u8>>>,
load: u16,
}
const U4_CODE_SIZE: u8 = 12;
impl U4Lzw {
fn new() -> U4Lzw {
let mut out = U4Lzw {
table: vec![RefCell::new(Vec::new()); 1 << U4_CODE_SIZE],
load: 0
};
for (i, entry) in out.table.iter_mut().take(256).enumerate() {
entry.borrow_mut().push(i as u8)
}
out
}
fn reset(&mut self) {
for i in 256..(1 << U4_CODE_SIZE) {
self.table[i].borrow_mut().clear();
}
self.load = 0;
}
fn decode(&mut self, bytes: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
let mut iter = CodeIter::new(bytes);
let mut prev = iter.next().expect("empty lzw");
let mut ch = prev as u8;
out.push(ch);
for code in iter {
{
let mut new;
let mut seq = &*self.table[code as usize].borrow();
if seq.is_empty() {
new = self.table[prev as usize].borrow().clone();
new.push(ch);
seq = &new;
};
out.extend(seq.iter());
ch = seq[0];
}
let hash = self.hash(ch, prev);
{
let old = self.table[prev as usize].borrow();
let mut entry = self.table[hash].borrow_mut();
entry.extend(old.iter());
entry.push(ch);
}
prev = code;
self.load += 1;
if self.load > 0xccd { self.reset(); }
}
out
}
fn hash(&self, root: u8, code: u16) -> usize {
let hash = Self::hash_primary(root, code);
if self.table[hash].borrow().is_empty() { return hash; }
let mut hash = Self::hash_secondary(root, code);
while !self.table[hash].borrow().is_empty() {
hash = (hash + 509) & 0xfff
}
hash
}
fn hash_primary(root: u8, code: u16) -> usize {
((root as (u16) << 4) ^ code) as usize
}
fn hash_secondary(root: u8, code: u16) -> usize {
let base = (((root as (u16) << 1) + code) | 0x800) as u32;
let squared = base * base;
((squared & 0x0003ffc0) >> 6) as usize
}
}
struct CodeIter<'a> {
pos: usize,
bytes: &'a [u8],
reader: lzw::MsbReader,
}
impl<'a> CodeIter<'a> {
fn new(bytes: &[u8]) -> CodeIter {
CodeIter { pos: 0, bytes: bytes, reader: lzw::MsbReader::new() }
}
}
impl<'a> Iterator for CodeIter<'a> {
type Item = u16;
fn next(&mut self) -> Option<u16> {
match self.reader.read_bits(&self.bytes[self.pos..], U4_CODE_SIZE) {
lzw::Bits::Some(used, code) => {
self.pos += used;
Some(code)
},
lzw::Bits::None(used) => {
self.pos += used;
None
}
}
}
}