read lzw .EGA files
This commit is contained in:
@@ -13,6 +13,7 @@ gl = "*"
|
||||
gfx = "*"
|
||||
gfx_device_gl = "*"
|
||||
image = "*"
|
||||
lzw = "*"
|
||||
nalgebra = "*"
|
||||
num-traits = "*"
|
||||
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],
|
||||
[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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user