From 11141c2e00f959bcf8cd1bb7f00ef7fc1fd26b8a Mon Sep 17 00:00:00 2001 From: Marrub Date: Sun, 9 Sep 2018 18:17:36 -0400 Subject: [PATCH] Simplify and fix the PICT loader --- Cargo.toml | 1 + src/durandal/image.rs | 18 +-- src/durandal/pict.rs | 264 ++++++++++++++++++++---------------------- 3 files changed, 138 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7863731..8713dbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ authors = ["marrub"] [dependencies] memmap = "0.6" +generic-array = "0.12" #gtk = "0.4" diff --git a/src/durandal/image.rs b/src/durandal/image.rs index d3ca2b7..763271a 100644 --- a/src/durandal/image.rs +++ b/src/durandal/image.rs @@ -4,6 +4,7 @@ use std::ops::{Index, IndexMut}; /// RGBA8 color. #[derive(Clone)] +#[derive(Debug)] pub struct Color { pub r: u8, @@ -15,9 +16,9 @@ pub struct Color /// Image with width and height. pub struct Image { - w: usize, - h: usize, - cr: Vec, + w: usize, + h: usize, + pub cr: Vec, } impl Color @@ -25,9 +26,12 @@ impl Color /// Converts a R5G5B5 format color to RGBA8. pub fn from_r5g5b5(rgb: u16) -> Color { - Color{r: (rgb >> 10 ) as u8 * 8, - g: (rgb >> 5 & 31) as u8 * 8, - b: (rgb & 31) as u8 * 8, + let r = rgb >> 10 & 0x1f; + let g = rgb >> 5 & 0x1f; + let b = rgb & 0x1f; + Color{r: (r << 3 | r >> 2) as u8, + g: (g << 3 | g >> 2) as u8, + b: (b << 3 | b >> 2) as u8, a: 255} } } @@ -36,7 +40,7 @@ impl Image { /// Creates a new Image structure. pub fn new(w: usize, h: usize) -> Image - {Image{w, h, cr: Vec::with_capacity(w as usize * h as usize)}} + {Image{w, h, cr: Vec::with_capacity(w * h)}} pub fn w(&self) -> usize {self.w} pub fn h(&self) -> usize {self.h} diff --git a/src/durandal/pict.rs b/src/durandal/pict.rs index 6af6ed8..a967f2f 100644 --- a/src/durandal/pict.rs +++ b/src/durandal/pict.rs @@ -1,114 +1,79 @@ //! QuickDraw PICT format loader. +use generic_array::*; + use durandal::image::*; use durandal::bin::*; const PACK_DEFAULT: u16 = 0; const PACK_NONE : u16 = 1; -const PACK_NOALPHA: u16 = 2; +const PACK_NOPAD : u16 = 2; const PACK_RLE16 : u16 = 3; const PACK_RLE32 : u16 = 4; -struct PixMap +/// Read a colorTable structure. +fn get_clut(b: &[u8]) -> Vec { - pack: u16, - dept: u16, - rle : bool, - cmap: Vec, + // = b_u32b(&b[ ..4]); ctSeed + let dev = b_u16b(&b[4..6]) & 0x8000 != 0; // ctFlags + let num = b_u16b(&b[6..8]) 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 = (b_u16b(&b[p ..p+2]) & 0xff) as usize; + let r = (b_u16b(&b[p+2..p+4]) >> 8 ) as u8; + let g = (b_u16b(&b[p+4..p+6]) >> 8 ) as u8; + let b = (b_u16b(&b[p+6..p+8]) >> 8 ) as u8; + + // with device mapping, we ignore the index entirely + map[if dev {i} else {n}] = Color{r, g, b, a: 255}; + } + + map } -impl PixMap +/// Read a sequence of packed RLE data. +fn read_rle_data(cmp: bool, len: usize, o: &mut Vec, mut rd: F) + where F: FnMut() -> GenericArray, + N: ArrayLength { - fn empty() -> PixMap - {PixMap{pack: PACK_DEFAULT, dept: 1, rle: false, cmap: Vec::new()}} - - fn new(b: &[u8], packed: bool) -> (usize, PixMap) + if cmp { - // version = b_u16b(&b[ 0.. 2]); - let pack = b_u16b(&b[ 2.. 4]); - // packSize = b_u32b(&b[ 4.. 8]) as usize; - // horzDPI = b_u32b(&b[ 8..12]); - // vertDPI = b_u32b(&b[12..16]); - // pixelType = b_u16b(&b[16..18]); - let dept = b_u16b(&b[18..20]); - // components = b_u16b(&b[20..22]); - // compDepth = b_u16b(&b[22..24]); - // planeOffs = b_u32b(&b[24..28]); - // colorTable = b_u32b(&b[28..32]); - // reserved = b_u32b(&b[32..36]); - - let mut px = PixMap{ - pack, - dept, - rle: pack == PACK_DEFAULT || - (dept == 16 && pack == PACK_RLE16) || - (dept == 32 && pack == PACK_RLE32), - cmap: Vec::new(), - }; - - // if it's packed we need to grab the color map - if packed - { - // table = b_u32b(&b[36..40]); - let dev = b_u16b(&b[40..42]) & 0x8000 != 0; - let colo = b_u16b(&b[42..44]) as usize + 1; - - px.cmap.resize(colo as usize, Color{r: 0, g: 0, b: 0, a: 0}); - for i in 0..colo - { - let p = 44 + i * 8; - let n = (b_u16b(&b[p+0..p+ 2]) & 0xff) as usize; - let r = (b_u16b(&b[p+2..p+ 4]) / 257) as u8; - let g = (b_u16b(&b[p+6..p+ 8]) / 257) as u8; - let b = (b_u16b(&b[p+8..p+10]) / 257) as u8; - - // with device mapping, we ignore the index entirely - let n = if dev {i} else {n}; - - px.cmap[n] = Color{r, g, b, a: 255}; - } - - (44 + colo * 8, px) - } - else - {(36, px)} + 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], long: bool, rd: F) -> Vec - where T: Copy, - F: Fn(&mut usize) -> T +fn read_rle(b: &[u8], pt: usize, ln: bool) -> Result<(Vec, usize), &str> { - let (st, sz) = if long {(2usize, b_u16b(&b[0..2]) as usize)} - else {(1usize, b[0] as usize)}; - - let mut o = Vec::with_capacity(sz); - let mut p = st; + let mut p = 0; + let mut o = Vec::with_capacity(pt); + let sz = if pt > 250 {(b_u16b(&b[0..2]) as usize + 2, p += 2).0} + else {( b[0] as usize + 1, p += 1).0}; while p < sz { - // size and flags are in one byte, we interpret it as a signed integer - // because it's easier to handle - let szf = b[(p, p += 1).0] as i8; - let sz = if szf < 0 {-szf + 1} else {szf + 1}; + 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(sz as usize); + o.reserve(len); - // either repeated or unique data - if szf < 0 {let d = rd(&mut p); for _ in 0..sz { o.push(d)}} - else {for _ in 0..sz {let d = rd(&mut p); o.push(d)}} + 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)} } - o + if o.len() == pt {Ok((o, p))} + else {Err("incorrect size for compressed scanline")} } -fn read_rle8(b: &[u8], long: bool) -> Vec - {read_rle(b, long, |p| (b[*p], *p += 1).0)} - -fn read_rle16(b: &[u8], long: bool) -> Vec - {read_rle(b, long, |p| (b_u16b(&b[*p..*p+2]), *p += 2).0)} - /// Expand packed pixel data based on bit depth. fn expand_data(b: Vec, depth: u16) -> Result, &'static str> { @@ -135,50 +100,67 @@ fn expand_data(b: Vec, depth: u16) -> Result, &'static str> /// Process a CopyBits operation. fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result { - let mut p = if !packed {/*baseAddress = b_u32b(&b[0..4]);*/ 4} else {0}; - - // get pitch and flags, flags are packed into the upper 2 bits - let pf = b_u16b(&b[p..p+2]); - let pm = pf & 0x8000 != 0; - let pt = pf & 0x3fff; + let mut p = if !packed {4} else {0}; // baseAddr let (w, h) = (im.w(), im.h()); - let yb = b_u16b(&b[p+2..p+ 4]) as usize; - let xb = b_u16b(&b[p+4..p+ 6]) as usize; - let ye = b_u16b(&b[p+6..p+ 8]) as usize; - let xe = b_u16b(&b[p+8..p+10]) as usize; + let pf = b_u16b(&b[p ..p+2]); // rowBytes + let yb = b_u16b(&b[p+ 2..p+ 4]) as usize; // Bounds + let xb = b_u16b(&b[p+ 4..p+ 6]) as usize; // 〃 + let ye = b_u16b(&b[p+ 6..p+ 8]) as usize; // 〃 + let xe = b_u16b(&b[p+ 8..p+10]) as usize; // 〃 + // = b_u16b(&b[p+10..p+12]); pmVersion + let pack = b_u16b(&b[p+12..p+14]); // packType + // = b_u32b(&b[p+14..p+18]); packSize + // = b_u32b(&b[p+18..p+22]); hRes + // = b_u32b(&b[p+22..p+26]); vRes + // = b_u16b(&b[p+26..p+28]); pixelType + let dept = b_u16b(&b[p+28..p+30]); // pixelSize + // = b_u16b(&b[p+30..p+32]); cmpCount + // = b_u16b(&b[p+32..p+34]); cmpSize + // = b_u32b(&b[p+34..p+38]); planeBytes + // = b_u32b(&b[p+38..p+42]); pmTable + // = b_u32b(&b[p+42..p+46]); pmReserved - if xe - xb < w || ye - yb < h {return Err("image size is incorrect")} + p += 46; - let pxm = - if pm {let (pp, pxm) = PixMap::new(&b[p+10..], packed); p += pp; pxm} - else {PixMap::empty()}; + if pf & 0x8000 == 0 {return Err("PICT1 not supported")} + if xe - xb != w || ye - yb != h {return Err("image bounds are incorrect")} - if clip {let sz = b_u16b(&b[p..p+2]) as usize; p += sz} + let map = if packed {get_clut(&b[p..])} else {Vec::new()}; + let pt = (pf & 0x3fff) as usize; + let rle = pack == PACK_DEFAULT || + (pack == PACK_RLE16 && dept == 16) || + (pack == PACK_RLE32 && dept == 32); - match pxm.dept { + p += 18 + if packed {8 + map.len() * 8} else {0}; // srcRect, dstRect, mode + + if clip {let sz = b_u16b(&b[p..p+2]) as usize; p += sz} // maskRgn + + match dept { 1 | 2 | 4 | 8 => // uncompressed 8-bit colormap indices - if pt < 8 && pxm.dept == 8 + if pt < 8 && dept == 8 { - for y in 0..h { - for x in 0..w - {im[(x, y)] = pxm.cmap[b[(p, p += 1).0] as usize].clone()} + for _ in 0..h { + for _ in 0..w + {im.cr.push(map[b[(p, p += 1).0] as usize].clone())} } Ok(im) } // RLE compressed 1, 2, 4 or 8 bit colormap indices - else if pxm.rle + else if rle { - for y in 0..h + for _ in 0..h { - let d = read_rle8(&b[p..], pt > 250); - let d = if pxm.dept < 8 {expand_data(d, pxm.dept)?} else {d}; + let (d, pp) = read_rle(&b[p..], pt, false)?; + let d = if dept < 8 {expand_data(d, dept)?} else {d}; - for x in 0..w {im[(x, y)] = pxm.cmap[d[x] as usize].clone()} + p += pp; + + for x in 0..w {im.cr.push(map[d[x] as usize].clone())} } Ok(im) @@ -188,23 +170,27 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result else {Err("invalid configuration")}, 16 => // uncompressed R5G5B5 - if pt < 8 || pxm.pack == PACK_NONE + if pt < 8 || pack == PACK_NONE { - for y in 0..h { - for x in 0..w - {im[(x, y)] = Color::from_r5g5b5(b_u16b((&b[p..p+2], p += 2).0))} + for _ in 0..h { + for _ in 0..w + {im.cr.push(Color::from_r5g5b5(b_u16b((&b[p..p+2], p += 2).0)))} } Ok(im) } // RLE compressed R5G5B5 - else if pxm.rle + else if rle { - for y in 0..h + for _ in 0..h { - let d = read_rle16(&b[p..], pt > 250); - for x in 0..w {im[(x, y)] = Color::from_r5g5b5(d[x])} + let (d, pp) = read_rle(&b[p..], pt, true)?; + + p += pp; + + for x in 0..w + {im.cr.push(Color::from_r5g5b5(b_u16b(&d[x*2..x*2+2])))} } Ok(im) @@ -213,18 +199,16 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result // invalid else {Err("invalid configuration")}, 32 => - // uncompressed RGB8 or ARGB8 - if pt < 8 || pxm.pack == PACK_NONE || pxm.pack == PACK_NOALPHA + // uncompressed RGB8 or XRGB8 + if pt < 8 || pack == PACK_NONE || pack == PACK_NOPAD { - for y in 0..h { - for x in 0..w + for _ in 0..h { + for _ in 0..w { - let a = if pxm.pack != PACK_NOALPHA {(b[p], p += 1).0} else {255}; - let r = b[p+0]; - let g = b[p+1]; - let b = b[p+2]; + if pack != PACK_NOPAD {p += 1}; + let (r, g, b) = (b[p], b[p+1], b[p+2]); p += 3; - im[(x, y)] = Color{r, g, b, a}; + im.cr.push(Color{r, g, b, a: 255}); } } @@ -232,17 +216,19 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result } // RLE compressed RGB8 - else if pxm.rle + else if rle { - for y in 0..h + let pt = pt - w; // remove padding byte from pitch + for _ in 0..h { - let d = read_rle8(&b[p..], pt > 250); + let (d, pp) = read_rle(&b[p..], pt, false)?; + + p += pp; + for x in 0..w { - let r = d[x + w * 0]; - let g = d[x + w * 1]; - let b = d[x + w * 2]; - im[(x, y)] = Color{r, g, b, a: 255}; + let (r, g, b) = (d[x+w*0], d[x+w*1], d[x+w*2]); + im.cr.push(Color{r, g, b, a: 255}); } } @@ -265,15 +251,15 @@ pub fn load_pict(b: &[u8]) -> Result // size = b_u16b(&b[0.. 2]); // top = b_u16b(&b[2.. 4]); // left = b_u16b(&b[4.. 6]); - let w = b_u16b(&b[6.. 8]) as usize; - let h = b_u16b(&b[8..10]) as usize; + let h = b_u16b(&b[6.. 8]) as usize; + let w = b_u16b(&b[8..10]) as usize; let im = Image::new(w, h); let mut p = 10; while p < b.len() { - let op = b_u16b(&b[p..p+2]); - p += 2; + let op = b_u16b((&b[p..p+2], p += 2).0); + match op { 0x0098 => return read_bitmap_area(im, &b[p..], true, false), // PackBitsRect 0x0099 => return read_bitmap_area(im, &b[p..], true, true ), // PackBitsRgn @@ -326,9 +312,11 @@ pub fn load_pict(b: &[u8]) -> Result 0x0034 => p += 8, // FillRect 0x002d => p += 10, // LineJustify 0x0c00 => p += 24, // HeaderOp - _ => - if op >= 0x100 && op <= 0x7fff {p += ((op as usize & 0xff00) >> 8) * 2} - else {return Err("invalid opcode in PICT")} + 0x0001 => p += (b_u16b(&b[p ..p+2]) & !1) as usize, // Clip + 0x00a1 => p += (b_u16b(&b[p+2..p+4]) & !1) as usize + 2, // LongComment + 0x100..= + 0x7fff => p += (op >> 8) as usize * 2, // Reserved + _ => return Err("invalid op in PICT") } }