Simplify and fix the PICT loader
parent
cba430393a
commit
11141c2e00
|
@ -5,4 +5,5 @@ authors = ["marrub"]
|
|||
|
||||
[dependencies]
|
||||
memmap = "0.6"
|
||||
generic-array = "0.12"
|
||||
#gtk = "0.4"
|
||||
|
|
|
@ -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<Color>,
|
||||
w: usize,
|
||||
h: usize,
|
||||
pub cr: Vec<Color>,
|
||||
}
|
||||
|
||||
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}
|
||||
|
|
|
@ -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<Color>
|
||||
{
|
||||
pack: u16,
|
||||
dept: u16,
|
||||
rle : bool,
|
||||
cmap: Vec<Color>,
|
||||
// = 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<F, N>(cmp: bool, len: usize, o: &mut Vec<u8>, mut rd: F)
|
||||
where F: FnMut() -> GenericArray<u8, N>,
|
||||
N: ArrayLength<u8>
|
||||
{
|
||||
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<T, F>(b: &[u8], long: bool, rd: F) -> Vec<T>
|
||||
where T: Copy,
|
||||
F: Fn(&mut usize) -> T
|
||||
fn read_rle(b: &[u8], pt: usize, ln: bool) -> Result<(Vec<u8>, 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<u8>
|
||||
{read_rle(b, long, |p| (b[*p], *p += 1).0)}
|
||||
|
||||
fn read_rle16(b: &[u8], long: bool) -> Vec<u16>
|
||||
{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<u8>, depth: u16) -> Result<Vec<u8>, &'static str>
|
||||
{
|
||||
|
@ -135,50 +100,67 @@ fn expand_data(b: Vec<u8>, depth: u16) -> Result<Vec<u8>, &'static str>
|
|||
/// Process a CopyBits operation.
|
||||
fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result<Image, &str>
|
||||
{
|
||||
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<Image, &str>
|
|||
// 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<Image, &str>
|
|||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue