read lzw .EGA files
This commit is contained in:
@@ -13,6 +13,7 @@ gl = "*"
|
|||||||
gfx = "*"
|
gfx = "*"
|
||||||
gfx_device_gl = "*"
|
gfx_device_gl = "*"
|
||||||
image = "*"
|
image = "*"
|
||||||
|
lzw = "*"
|
||||||
nalgebra = "*"
|
nalgebra = "*"
|
||||||
num-traits = "*"
|
num-traits = "*"
|
||||||
openvr = { git = "https://github.com/rust-openvr/rust-openvr" }
|
openvr = { git = "https://github.com/rust-openvr/rust-openvr" }
|
||||||
|
|||||||
177
src/ega.rs
177
src/ega.rs
@@ -1,19 +1,25 @@
|
|||||||
static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0x00],
|
extern crate lzw;
|
||||||
[0x00, 0x00, 0xAA, 0x00],
|
|
||||||
[0x00, 0xAA, 0x00, 0x00],
|
use std::cell::RefCell;
|
||||||
[0x00, 0xAA, 0xAA, 0x00],
|
|
||||||
[0xAA, 0x00, 0x00, 0x00],
|
use self::lzw::BitReader;
|
||||||
[0xAA, 0x00, 0xAA, 0x00],
|
|
||||||
[0xAA, 0x55, 0x00, 0x00],
|
static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0xFF],
|
||||||
[0xAA, 0xAA, 0xAA, 0x00],
|
[0x00, 0x00, 0xAA, 0xFF],
|
||||||
[0x55, 0x55, 0x55, 0x00],
|
[0x00, 0xAA, 0x00, 0xFF],
|
||||||
[0x55, 0x55, 0xFF, 0x00],
|
[0x00, 0xAA, 0xAA, 0xFF],
|
||||||
[0x55, 0xFF, 0x55, 0x00],
|
[0xAA, 0x00, 0x00, 0xFF],
|
||||||
[0x55, 0xFF, 0xFF, 0x00],
|
[0xAA, 0x00, 0xAA, 0xFF],
|
||||||
[0xFF, 0x55, 0x55, 0x00],
|
[0xAA, 0x55, 0x00, 0xFF],
|
||||||
[0xFF, 0x55, 0xFF, 0x00],
|
[0xAA, 0xAA, 0xAA, 0xFF],
|
||||||
[0xFF, 0xFF, 0x55, 0x00],
|
[0x55, 0x55, 0x55, 0xFF],
|
||||||
[0xFF, 0xFF, 0xFF, 0x00]];
|
[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 {
|
pub enum Compression {
|
||||||
Uncompressed,
|
Uncompressed,
|
||||||
@@ -98,16 +104,12 @@ pub fn decode<'a>(buf: &[u8], compression: Compression, tiling: Tiling)
|
|||||||
let out: Vec<u8>;
|
let out: Vec<u8>;
|
||||||
|
|
||||||
out = match compression {
|
out = match compression {
|
||||||
Compression::Uncompressed => buf.iter()
|
Compression::Uncompressed => decode_uncompressed(buf),
|
||||||
.flat_map(|tile_byte| {
|
Compression::Rle => unimplemented!(),
|
||||||
EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize]
|
Compression::Lzw => {
|
||||||
.into_iter()
|
let mut decoder = U4Lzw::new();
|
||||||
.chain(EGA_PALETTE[(tile_byte & 0xF) as usize]
|
decode_uncompressed(&decoder.decode(buf))
|
||||||
.into_iter())
|
},
|
||||||
})
|
|
||||||
.map(|x| *x)
|
|
||||||
.collect(),
|
|
||||||
_ => unimplemented!()
|
|
||||||
};
|
};
|
||||||
let dim = match tiling {
|
let dim = match tiling {
|
||||||
Tiling::Tiled(tiledim) => tiledim as usize,
|
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}
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user