further clean up and optimize code
parent
f0212fe162
commit
2cb4e49b49
|
@ -5,5 +5,6 @@ pub use failure::{Error, Fail};
|
|||
pub type ResultS<T> = Result<T, Error>;
|
||||
|
||||
pub fn err_msg<T>(s: &'static str) -> ResultS<T> {Err(failure::err_msg(s))}
|
||||
pub fn err_msg_v (s: &'static str) -> Error { failure::err_msg(s) }
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -9,183 +9,112 @@ const PACK_NOPAD : u16 = 2;
|
|||
const PACK_RLE16 : u16 = 3;
|
||||
const PACK_RLE32 : u16 = 4;
|
||||
|
||||
/// Read a colorTable structure.
|
||||
fn get_clut(b: &[u8]) -> ResultS<Vec<Color>>
|
||||
{
|
||||
// = b.c_u32b(0)?; ctSeed
|
||||
let dev = b.c_u16b(4)? & 0x8000 != 0; // ctFlags
|
||||
let num = b.c_u16b(6)? as usize + 1; // ctSize
|
||||
let mut map = Vec::new();
|
||||
|
||||
map.resize(num, Color{r: 0, g: 0, b: 0, a: 0});
|
||||
|
||||
for i in 0..num {
|
||||
let p = 8 + i * 8;
|
||||
let n = if !dev {(b.c_u16b(p )? & 0xff) as usize} else {i};
|
||||
let r = (b.c_u16b(p+2)? >> 8) as u8;
|
||||
let g = (b.c_u16b(p+4)? >> 8) as u8;
|
||||
let b = (b.c_u16b(p+6)? >> 8) as u8;
|
||||
|
||||
// with device mapping, we ignore the index entirely
|
||||
map[n] = Color{r, g, b, a: 255};
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
/// Read a sequence of packed RLE data.
|
||||
fn read_rle_data<F, N>(cmp: bool, len: usize, o: &mut Vec<u8>, mut rd: F)
|
||||
where F: FnMut() -> GenericArray<u8, N>,
|
||||
N: ArrayLength<u8>
|
||||
{
|
||||
if cmp {
|
||||
let d = rd();
|
||||
for _ in 0..len {for v in d.iter() {o.push(*v)}}
|
||||
} else {
|
||||
for _ in 0..len {let d = rd(); for v in d.iter() {o.push(*v)}}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read run-length encoded data.
|
||||
fn read_rle(b: &[u8], pt: usize, ln: bool) -> ResultS<(Vec<u8>, usize)>
|
||||
{
|
||||
let mut p = 0;
|
||||
let mut o = Vec::with_capacity(pt);
|
||||
let sz = if pt > 250 {(b.c_u16b(0)? as usize + 2, p += 2).0}
|
||||
else {(b[0] as usize + 1, p += 1).0};
|
||||
|
||||
while p < sz {
|
||||
let szf = b[(p, p += 1).0];
|
||||
let cmp = szf & 0x80 != 0;
|
||||
let len = if cmp {!szf + 2} else {szf + 1} as usize;
|
||||
|
||||
o.reserve(len);
|
||||
|
||||
if ln {read_rle_data(cmp, len, &mut o, || (arr![u8; b[p], b[p+1]], p += 2).0)}
|
||||
else {read_rle_data(cmp, len, &mut o, || (arr![u8; b[p] ], p += 1).0)}
|
||||
}
|
||||
|
||||
if o.len() == pt {Ok((o, p))}
|
||||
else {err_msg("incorrect size for compressed scanline")}
|
||||
}
|
||||
|
||||
/// Expand packed pixel data based on bit depth.
|
||||
fn expand_data(b: Vec<u8>, depth: u16) -> ResultS<Vec<u8>>
|
||||
{
|
||||
let mut o = Vec::with_capacity(match depth {
|
||||
4 => b.len() * 2,
|
||||
2 => b.len() * 4,
|
||||
1 => b.len() * 8,
|
||||
_ => return err_msg("invalid bit depth")
|
||||
});
|
||||
|
||||
for ch in b {
|
||||
match depth {
|
||||
4 => for i in 1..=0 {o.push(ch >> i * 4 & 0xfu8)}, // 2 nibbles
|
||||
2 => for i in 3..=0 {o.push(ch >> i * 2 & 0x3u8)}, // 4 dibits
|
||||
1 => for i in 7..=0 {o.push(ch >> i * 1 & 0x1u8)}, // 8 bits
|
||||
_ => return err_msg("invalid bit depth")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(o)
|
||||
}
|
||||
|
||||
/// Process a CopyBits operation.
|
||||
fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> ResultS<Image>
|
||||
{
|
||||
let mut p = if !packed {4} else {0}; // baseAddr
|
||||
let mut p = if !packed {4} else {0};
|
||||
|
||||
let (w, h) = (im.w(), im.h());
|
||||
|
||||
let pf = b.c_u16b(p )?; // rowBytes
|
||||
let yb = b.c_u16b(p+ 2)? as usize; // Bounds
|
||||
let xb = b.c_u16b(p+ 4)? as usize; // 〃
|
||||
let ye = b.c_u16b(p+ 6)? as usize; // 〃
|
||||
let xe = b.c_u16b(p+ 8)? as usize; // 〃
|
||||
// = b.c_u16b(p+10)?; pmVersion
|
||||
let pack = b.c_u16b(p+12)?; // packType
|
||||
// = b.c_u32b(p+14)?; packSize
|
||||
// = b.c_u32b(p+18)?; hRes
|
||||
// = b.c_u32b(p+22)?; vRes
|
||||
// = b.c_u16b(p+26)?; pixelType
|
||||
let dept = b.c_u16b(p+28)?; // pixelSize
|
||||
// = b.c_u16b(p+30)?; cmpCount
|
||||
// = b.c_u16b(p+32)?; cmpSize
|
||||
// = b.c_u32b(p+34)?; planeBytes
|
||||
// = b.c_u32b(p+38)?; pmTable
|
||||
// = b.c_u32b(p+42)?; pmReserved
|
||||
let pitch_fl = b.c_u16b(p )?;
|
||||
let top = b.c_u16b(p+ 2)? as usize;
|
||||
let left = b.c_u16b(p+ 4)? as usize;
|
||||
let bottom = b.c_u16b(p+ 6)? as usize;
|
||||
let right = b.c_u16b(p+ 8)? as usize;
|
||||
// version = b.c_u16b(p+10)?;
|
||||
let pack_typ = b.c_u16b(p+12)?;
|
||||
// pack_siz = b.c_u32b(p+14)?;
|
||||
// horz_dpi = b.c_u32b(p+18)?;
|
||||
// vert_dpi = b.c_u32b(p+22)?;
|
||||
// format = b.c_u16b(p+26)?;
|
||||
let depth = b.c_u16b(p+28)?;
|
||||
// comp_n = b.c_u16b(p+30)?;
|
||||
// comp_d = b.c_u16b(p+32)?;
|
||||
// planeofs = b.c_u32b(p+34)?;
|
||||
// clut_id = b.c_u32b(p+38)?;
|
||||
|
||||
if pitch_fl & 0x8000 == 0
|
||||
{return err_msg("PICT1 not supported")}
|
||||
if right - left != w || bottom - top != h
|
||||
{return err_msg("image bounds are incorrect")}
|
||||
|
||||
p += 46; // size of header
|
||||
|
||||
if pf & 0x8000 == 0 {return err_msg("PICT1 not supported")}
|
||||
if xe - xb != w || ye - yb != h {return err_msg("image bounds are incorrect")}
|
||||
// get CLUT if packed
|
||||
let clut = if packed {
|
||||
let (clut, sz) = get_clut(&b[p..])?;
|
||||
p += sz;
|
||||
Some(clut)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let map = if packed {get_clut(&b[p..])?} else {Vec::new()};
|
||||
p += 18; // srcRect, dstRect, mode
|
||||
|
||||
let rle = pack == PACK_DEFAULT ||
|
||||
(pack == PACK_RLE16 && dept == 16) ||
|
||||
(pack == PACK_RLE32 && dept == 32);
|
||||
if clip {p += b.c_u16b(p)? as usize} // maskRgn
|
||||
|
||||
let pt = (pf & 0x3fff) as usize;
|
||||
let rle = pack_typ == PACK_DEFAULT ||
|
||||
(pack_typ == PACK_RLE16 && depth == 16) ||
|
||||
(pack_typ == PACK_RLE32 && depth == 32);
|
||||
|
||||
p += 18 + if packed {8 + map.len() * 8} else {0}; // srcRect, dstRect, mode
|
||||
let pitch = (pitch_fl & 0x3fff) as usize;
|
||||
|
||||
if clip {let sz = b.c_u16b(p)? as usize; p += sz} // maskRgn
|
||||
|
||||
match dept {
|
||||
1 | 2 | 4 | 8 =>
|
||||
if pt < 8 && dept == 8 {
|
||||
match depth {
|
||||
1 | 2 | 4 | 8 => {
|
||||
let clut = clut.ok_or_else(|| err_msg_v("no clut in indexed mode"))?;
|
||||
if pitch < 8 && depth == 8 {
|
||||
// uncompressed 8-bit colormap indices
|
||||
for _ in 0..h {
|
||||
for _ in 0..w
|
||||
{im.cr.push(map[b[(p, p += 1).0] as usize].clone())}
|
||||
for _ in 0..w {
|
||||
im.cr.push(clut[b[(p, p += 1).0] as usize].clone());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(im)
|
||||
} else if rle {
|
||||
// RLE compressed 1, 2, 4 or 8 bit colormap indices
|
||||
for _ in 0..h {
|
||||
let (d, pp) = read_rle(&b[p..], pt, false)?;
|
||||
let d = if dept < 8 {expand_data(d, dept)?} else {d};
|
||||
let (d, pp) = read_rle(&b[p..], pitch, false)?;
|
||||
let d = if depth < 8 {expand_data(d, depth)?} else {d};
|
||||
|
||||
p += pp;
|
||||
|
||||
for x in 0..w {im.cr.push(map[d[x] as usize].clone())}
|
||||
for x in 0..w {im.cr.push(clut[d[x] as usize].clone())}
|
||||
}
|
||||
|
||||
Ok(im)
|
||||
}
|
||||
else {err_msg("invalid configuration")},
|
||||
else {err_msg("invalid configuration")}
|
||||
},
|
||||
16 =>
|
||||
if pt < 8 || pack == PACK_NONE {
|
||||
if pitch < 8 || pack_typ == PACK_NONE {
|
||||
// uncompressed R5G5B5
|
||||
for _ in 0..h {
|
||||
for _ in 0..w
|
||||
{im.cr.push(Color::from_r5g5b5(b.c_u16b((p, p += 2).0)?))}
|
||||
for _ in 0..w {
|
||||
im.cr.push(Color::from_r5g5b5(b.c_u16b((p, p += 2).0)?));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(im)
|
||||
} else if rle {
|
||||
// RLE compressed R5G5B5
|
||||
for _ in 0..h {
|
||||
let (d, pp) = read_rle(&b[p..], pt, true)?;
|
||||
let (d, pp) = read_rle(&b[p..], pitch, true)?;
|
||||
|
||||
p += pp;
|
||||
|
||||
for x in 0..w
|
||||
{im.cr.push(Color::from_r5g5b5(d.c_u16b(x*2)?))}
|
||||
for x in 0..w {im.cr.push(Color::from_r5g5b5(d.c_u16b(x*2)?))}
|
||||
}
|
||||
|
||||
Ok(im)
|
||||
}
|
||||
else {err_msg("invalid configuration")},
|
||||
32 =>
|
||||
if pt < 8 || pack == PACK_NONE || pack == PACK_NOPAD {
|
||||
if pitch < 8 || pack_typ == PACK_NONE || pack_typ == PACK_NOPAD {
|
||||
// uncompressed RGB8 or XRGB8
|
||||
for _ in 0..h {
|
||||
for _ in 0..w {
|
||||
if pack != PACK_NOPAD {p += 1};
|
||||
if pack_typ != PACK_NOPAD {p += 1}
|
||||
let (r, g, b) = (b[p], b[p+1], b[p+2]);
|
||||
p += 3;
|
||||
im.cr.push(Color{r, g, b, a: 255});
|
||||
|
@ -195,9 +124,9 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result
|
|||
Ok(im)
|
||||
} else if rle {
|
||||
// RLE compressed RGB8
|
||||
let pt = pt - w; // remove padding byte from pitch
|
||||
let pitch = pitch - w; // remove padding byte from pitch
|
||||
for _ in 0..h {
|
||||
let (d, pp) = read_rle(&b[p..], pt, false)?;
|
||||
let (d, pp) = read_rle(&b[p..], pitch, false)?;
|
||||
|
||||
p += pp;
|
||||
|
||||
|
@ -296,4 +225,88 @@ pub fn load_pict(b: &[u8]) -> ResultS<Image>
|
|||
err_msg("no image in data")
|
||||
}
|
||||
|
||||
/// Read a colorTable structure.
|
||||
fn get_clut(b: &[u8]) -> ResultS<(Vec<Color>, usize)>
|
||||
{
|
||||
// sed = b.c_u32b(0)?;
|
||||
let dev = b.c_u16b(4)? & 0x8000 != 0;
|
||||
let num = b.c_u16b(6)? as usize + 1;
|
||||
|
||||
let mut p = 8;
|
||||
let mut clut = Vec::with_capacity(num);
|
||||
|
||||
clut.resize(num, Color{r: 0, g: 0, b: 0, a: 0});
|
||||
|
||||
for i in 0..num {
|
||||
// with device mapping, we ignore the index entirely
|
||||
let n = if !dev {(b.c_u16b(p )? & 0xff) as usize} else {i};
|
||||
let r = (b.c_u16b(p+2)? >> 8) as u8;
|
||||
let g = (b.c_u16b(p+4)? >> 8) as u8;
|
||||
let b = (b.c_u16b(p+6)? >> 8) as u8;
|
||||
|
||||
clut[n] = Color{r, g, b, a: 255};
|
||||
p += 8;
|
||||
}
|
||||
|
||||
Ok((clut, p))
|
||||
}
|
||||
|
||||
/// Read run-length encoded data.
|
||||
fn read_rle(b: &[u8], pitch: usize, ln: bool) -> ResultS<(Vec<u8>, usize)>
|
||||
{
|
||||
let mut p = 0;
|
||||
let mut o = Vec::with_capacity(pitch);
|
||||
let sz = if pitch > 250 {(b.c_u16b(0)? as usize + 2, p += 2).0}
|
||||
else {(b[0] as usize + 1, p += 1).0};
|
||||
|
||||
while p < sz {
|
||||
let szf = b[(p, p += 1).0];
|
||||
let cmp = szf & 0x80 != 0;
|
||||
let len = if cmp {!szf + 2} else {szf + 1} as usize;
|
||||
|
||||
o.reserve(len);
|
||||
|
||||
if ln {read_rle_data(cmp, len, &mut o, || (arr![u8; b[p], b[p+1]], p += 2).0)}
|
||||
else {read_rle_data(cmp, len, &mut o, || (arr![u8; b[p] ], p += 1).0)}
|
||||
}
|
||||
|
||||
if o.len() == pitch {Ok((o, p))}
|
||||
else {err_msg("incorrect size for compressed scanline")}
|
||||
}
|
||||
|
||||
/// Read a sequence of packed RLE data.
|
||||
fn read_rle_data<F, N>(cmp: bool, len: usize, out: &mut Vec<u8>, mut read: F)
|
||||
where F: FnMut() -> GenericArray<u8, N>,
|
||||
N: ArrayLength<u8>
|
||||
{
|
||||
if cmp {
|
||||
let d = read();
|
||||
for _ in 0..len {for v in d.iter() {out.push(*v)}}
|
||||
} else {
|
||||
for _ in 0..len {let d = read(); for v in d.iter() {out.push(*v)}}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand packed pixel data based on bit depth.
|
||||
fn expand_data(b: Vec<u8>, depth: u16) -> ResultS<Vec<u8>>
|
||||
{
|
||||
let mut o = Vec::with_capacity(match depth {
|
||||
4 => b.len() * 2,
|
||||
2 => b.len() * 4,
|
||||
1 => b.len() * 8,
|
||||
_ => return err_msg("invalid bit depth")
|
||||
});
|
||||
|
||||
for ch in b {
|
||||
match depth {
|
||||
4 => for i in 1..=0 {o.push(ch >> i * 4 & 0xfu8)}, // 2 nibbles
|
||||
2 => for i in 3..=0 {o.push(ch >> i * 2 & 0x3u8)}, // 4 dibits
|
||||
1 => for i in 7..=0 {o.push(ch >> i * 1 & 0x1u8)}, // 8 bits
|
||||
_ => return err_msg("invalid bit depth")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(o)
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
22
src/main.rs
22
src/main.rs
|
@ -67,27 +67,19 @@ fn main() -> ResultS<()>
|
|||
|
||||
for (id, ent) in wad.entries {
|
||||
if let Some(b) = ent.chunks.get(b"PICT") {
|
||||
match load_pict(b) {
|
||||
Ok(im) => {
|
||||
println!("entry {} has PICT {}x{}", id, im.w(), im.h());
|
||||
write_ppm(&format!("out_{}.ppm", id), &im)?;
|
||||
},
|
||||
Err(e) => println!("entry {} has PICT (invalid: {:?})", id, e),
|
||||
}
|
||||
let im = load_pict(b)?;
|
||||
println!("entry {} has PICT {}x{}", id, im.w(), im.h());
|
||||
write_ppm(&format!("out_{}.ppm", id), &im)?;
|
||||
}
|
||||
|
||||
if let Some(b) = ent.chunks.get(b"Minf") {
|
||||
match Minf::chunk(b) {
|
||||
Ok (c) => println!("entry {} has Minf {:#?}", id, c),
|
||||
Err(e) => println!("entry {} has Minf (invalid: {:?})", id, e),
|
||||
}
|
||||
let minf = Minf::chunk(b)?;
|
||||
println!("entry {} has Minf {:#?}", id, minf);
|
||||
}
|
||||
|
||||
if let Some(b) = ent.chunks.get(b"term") {
|
||||
match term::Terminal::chunk(b) {
|
||||
Ok (c) => println!("entry {} has term {:#?}", id, c),
|
||||
Err(e) => println!("entry {} has term (invalid: {:?})", id, e),
|
||||
}
|
||||
let term = term::Terminal::chunk(b)?;
|
||||
println!("entry {} has term {:#?}", id, term);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue