Maraiah/src/marathon/pict.rs

317 lines
9.2 KiB
Rust
Raw Normal View History

2018-09-06 09:01:52 -07:00
//! QuickDraw PICT format loader.
2018-12-11 00:08:23 -08:00
use crate::durandal::{bin::*, err::*, image::*};
2018-09-09 15:17:36 -07:00
use generic_array::*;
2018-09-06 09:01:52 -07:00
const PACK_DEFAULT: u16 = 0;
const PACK_NONE : u16 = 1;
2018-09-09 15:17:36 -07:00
const PACK_NOPAD : u16 = 2;
2018-09-06 09:01:52 -07:00
const PACK_RLE16 : u16 = 3;
const PACK_RLE32 : u16 = 4;
/// Process a CopyBits operation.
2019-02-04 21:12:10 -08:00
pub fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> ResultS<Image>
2018-09-06 09:01:52 -07:00
{
2018-12-11 15:50:23 -08:00
let mut p = if !packed {4} else {0};
2018-09-06 09:01:52 -07:00
let (w, h) = (im.w(), im.h());
2018-12-11 15:50:23 -08:00
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)?;
2018-12-11 16:06:51 -08:00
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");
}
2018-09-09 15:17:36 -07:00
2018-12-10 23:32:59 -08:00
p += 46; // size of header
2018-09-09 15:17:36 -07:00
2018-12-11 15:50:23 -08:00
// get CLUT if packed
let clut = if packed {
let (clut, sz) = get_clut(&b[p..])?;
p += sz;
Some(clut)
} else {
None
};
2018-09-11 12:07:42 -07:00
2018-12-11 15:50:23 -08:00
p += 18; // srcRect, dstRect, mode
2018-09-09 15:17:36 -07:00
2018-12-11 16:06:51 -08:00
if clip {p += b.c_u16b(p)? as usize;} // maskRgn
2018-09-11 12:07:42 -07:00
2018-12-11 15:50:23 -08:00
let rle = pack_typ == PACK_DEFAULT ||
(pack_typ == PACK_RLE16 && depth == 16) ||
(pack_typ == PACK_RLE32 && depth == 32);
2018-09-09 15:17:36 -07:00
2018-12-11 15:50:23 -08:00
let pitch = (pitch_fl & 0x3fff) as usize;
2018-09-09 15:17:36 -07:00
2018-12-11 15:50:23 -08:00
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 {
2018-12-10 23:32:59 -08:00
// uncompressed 8-bit colormap indices
2018-09-09 15:17:36 -07:00
for _ in 0..h {
2018-12-11 15:50:23 -08:00
for _ in 0..w {
im.cr.push(clut[b[(p, p += 1).0] as usize].clone());
}
2018-09-06 09:01:52 -07:00
}
Ok(im)
2018-12-10 23:32:59 -08:00
} else if rle {
// RLE compressed 1, 2, 4 or 8 bit colormap indices
for _ in 0..h {
2018-12-11 15:50:23 -08:00
let (d, pp) = read_rle(&b[p..], pitch, false)?;
let d = if depth < 8 {expand_data(d, depth)?} else {d};
2018-09-06 09:01:52 -07:00
2018-09-09 15:17:36 -07:00
p += pp;
2018-12-11 16:06:51 -08:00
for x in 0..w {im.cr.push(clut[d[x] as usize].clone());}
2018-09-06 09:01:52 -07:00
}
Ok(im)
}
2018-12-11 15:50:23 -08:00
else {err_msg("invalid configuration")}
},
2018-09-06 09:01:52 -07:00
16 =>
2018-12-11 15:50:23 -08:00
if pitch < 8 || pack_typ == PACK_NONE {
2018-12-10 23:32:59 -08:00
// uncompressed R5G5B5
2018-09-09 15:17:36 -07:00
for _ in 0..h {
2018-12-11 15:50:23 -08:00
for _ in 0..w {
im.cr.push(Color::from_r5g5b5(b.c_u16b((p, p += 2).0)?));
}
2018-09-06 09:01:52 -07:00
}
Ok(im)
2018-12-10 23:32:59 -08:00
} else if rle {
// RLE compressed R5G5B5
for _ in 0..h {
2018-12-11 15:50:23 -08:00
let (d, pp) = read_rle(&b[p..], pitch, true)?;
2018-09-09 15:17:36 -07:00
p += pp;
2018-12-11 16:06:51 -08:00
for x in 0..w {im.cr.push(Color::from_r5g5b5(d.c_u16b(x*2)?));}
2018-09-06 09:01:52 -07:00
}
Ok(im)
}
2018-12-11 00:08:23 -08:00
else {err_msg("invalid configuration")},
2018-09-06 09:01:52 -07:00
32 =>
2018-12-11 15:50:23 -08:00
if pitch < 8 || pack_typ == PACK_NONE || pack_typ == PACK_NOPAD {
2018-12-10 23:32:59 -08:00
// uncompressed RGB8 or XRGB8
2018-09-09 15:17:36 -07:00
for _ in 0..h {
2018-12-10 23:32:59 -08:00
for _ in 0..w {
2018-12-11 16:06:51 -08:00
if pack_typ != PACK_NOPAD {p += 1;}
2018-09-09 15:17:36 -07:00
let (r, g, b) = (b[p], b[p+1], b[p+2]);
2018-09-06 09:01:52 -07:00
p += 3;
2018-09-09 15:17:36 -07:00
im.cr.push(Color{r, g, b, a: 255});
2018-09-06 09:01:52 -07:00
}
}
Ok(im)
2018-12-10 23:32:59 -08:00
} else if rle {
// RLE compressed RGB8
2018-12-11 15:50:23 -08:00
let pitch = pitch - w; // remove padding byte from pitch
2018-12-10 23:32:59 -08:00
for _ in 0..h {
2018-12-11 15:50:23 -08:00
let (d, pp) = read_rle(&b[p..], pitch, false)?;
2018-09-09 15:17:36 -07:00
p += pp;
2018-12-10 23:32:59 -08:00
for x in 0..w {
2018-09-09 15:17:36 -07:00
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});
2018-09-06 09:01:52 -07:00
}
}
Ok(im)
}
2018-12-11 00:08:23 -08:00
else {err_msg("invalid configuration")},
_ => err_msg("invalid bit depth")
2018-09-06 09:01:52 -07:00
}
}
/// Process a CompressedQuickTime operation.
2019-02-04 21:12:10 -08:00
pub fn read_quicktime_c(_im: Image, _b: &[u8]) -> ResultS<Image>
2018-12-11 16:06:51 -08:00
{
err_msg("compressed quicktime format not implemented")
}
2018-09-06 09:01:52 -07:00
/// Load a PICT image.
2018-09-11 12:07:42 -07:00
pub fn load_pict(b: &[u8]) -> ResultS<Image>
2018-09-06 09:01:52 -07:00
{
2018-12-10 23:32:59 -08:00
// size = b.c_u16b(0)?;
// top = b.c_u16b(2)?;
// left = b.c_u16b(4)?;
let h = b.c_u16b(6)? as usize;
let w = b.c_u16b(8)? as usize;
let im = Image::new(w, h);
let mut p = 10; // size of header
while p < b.len() {
2018-09-11 12:07:42 -07:00
let op = b.c_u16b((p, p += 2).0)?;
2018-09-09 15:17:36 -07:00
2018-09-06 09:01:52 -07:00
match op {
0x0098 => return read_bitmap_area(im, &b[p..], true, false), // PackBitsRect
0x0099 => return read_bitmap_area(im, &b[p..], true, true ), // PackBitsRgn
0x009a => return read_bitmap_area(im, &b[p..], false, false), // DirectBitsRect
0x009b => return read_bitmap_area(im, &b[p..], false, true ), // DirectBitsRgn
0x8200 => return read_quicktime_c(im, &b[p..]), // CompressedQuickTime
0x00ff => break, // OpEndPic
// help i'm trapped in an awful metafile format from the 80s
2018-12-10 23:32:59 -08:00
0x0000 | // NoOp
0x001c | // HiliteMode
0x001e | // DefHilite
0x0038 | // FrameSameRect
0x0039 | // PaintSameRect
0x003a | // EraseSameRect
0x003b | // InvertSameRect
0x003c | // FillSameRect
0x8000 | // Reserved
2018-09-06 09:01:52 -07:00
0x8100 => (), // Reserved
2018-12-10 23:32:59 -08:00
0x0003 | // TxFont
0x0004 | // TxFace
0x0005 | // TxMode
0x0008 | // PnMode
0x000d | // TxSize
0x0011 | // VersionOp
0x0015 | // PnLocHFrac
0x0016 | // ChExtra
0x0023 | // ShortLineFrom
0x00a0 | // ShortComment
2018-09-06 09:01:52 -07:00
0x02ff => p += 2, // Version
2018-12-10 23:32:59 -08:00
0x0006 | // SpExtra
0x0007 | // PnSize
0x000b | // OvSize
0x000c | // Origin
0x000e | // FgCol
0x000f | // BkCol
2018-09-06 09:01:52 -07:00
0x0021 => p += 4, // LineFrom
2018-12-10 23:32:59 -08:00
0x001a | // RGBFgCol
0x001b | // RGBBkCol
0x001d | // TxRatio
2018-09-06 09:01:52 -07:00
0x0022 => p += 6, // ShortLine
2018-12-10 23:32:59 -08:00
0x0002 | // BkPat
0x0009 | // PnPat
0x0010 | // TxRatio
0x0020 | // Line
0x002e | // GlyphState
0x0030 | // FrameRect
0x0031 | // PaintRect
0x0032 | // EraseRect
0x0033 | // InvertRect
2018-09-06 09:01:52 -07:00
0x0034 => p += 8, // FillRect
0x002d => p += 10, // LineJustify
0x0c00 => p += 24, // HeaderOp
2018-09-11 12:07:42 -07:00
0x0001 => p += (b.c_u16b(p )? & !1) as usize, // Clip
0x00a1 => p += (b.c_u16b(p+2)? & !1) as usize + 2, // LongComment
2018-09-09 15:17:36 -07:00
0x100..=
0x7fff => p += (op >> 8) as usize * 2, // Reserved
2018-12-11 00:08:23 -08:00
_ => return err_msg("invalid op in PICT")
2018-09-06 09:01:52 -07:00
}
}
2018-12-11 00:08:23 -08:00
err_msg("no image in data")
2018-09-06 09:01:52 -07:00
}
2018-09-11 01:20:54 -07:00
2018-12-11 15:50:23 -08:00
/// Read a colorTable structure.
2019-02-04 21:12:10 -08:00
pub fn get_clut(b: &[u8]) -> ResultS<(Vec<Color>, usize)>
2018-12-11 15:50:23 -08:00
{
// 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;
2019-02-04 21:12:10 -08:00
let mut clut = vec![Color{r: 0, g: 0, b: 0, a: 0}; num];
2018-12-11 15:50:23 -08:00
for i in 0..num {
// with device mapping, we ignore the index entirely
2019-02-04 21:12:10 -08:00
let n = if !dev {b[p + 1] as usize} else {i};
let r = b[p+2];
let g = b[p+4];
let b = b[p+6];
2018-12-11 15:50:23 -08:00
2019-02-04 21:12:10 -08:00
if n >= clut.len() {return err_msg("bad clut index");}
2018-12-11 15:50:23 -08:00
clut[n] = Color{r, g, b, a: 255};
2019-02-04 21:12:10 -08:00
2018-12-11 15:50:23 -08:00
p += 8;
}
Ok((clut, p))
}
/// Read run-length encoded data.
2019-02-04 21:12:10 -08:00
pub fn read_rle(b: &[u8], pitch: usize, ln: bool) -> ResultS<(Vec<u8>, usize)>
2018-12-11 15:50:23 -08:00
{
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();
2018-12-11 16:06:51 -08:00
for _ in 0..len {for v in d.iter() {out.push(*v);}}
2018-12-11 15:50:23 -08:00
} else {
2018-12-11 16:06:51 -08:00
for _ in 0..len {let d = read(); for v in d.iter() {out.push(*v);}}
2018-12-11 15:50:23 -08:00
}
}
/// Expand packed pixel data based on bit depth.
2019-02-04 21:12:10 -08:00
pub fn expand_data(b: Vec<u8>, depth: u16) -> ResultS<Vec<u8>>
2018-12-11 15:50:23 -08:00
{
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 {
2018-12-11 16:06:51 -08:00
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
2018-12-11 15:50:23 -08:00
_ => return err_msg("invalid bit depth")
}
}
Ok(o)
}
2018-09-11 01:20:54 -07:00
// EOF