//! QuickDraw PICT format loader. use crate::durandal::{bin::*, err::*, image::*}; use generic_array::*; /// Reads a `PixMap` header. fn read_pm_header<'a>(b: &'a [u8], pack: bool, clip: bool, im: &Image8) -> ResultS<(&'a [u8], Header)> { read_data! { 36, BE in b => pt_fl = u16[0]; top = u16[2] as usize; left = u16[4] as usize; bottom = u16[6] as usize; right = u16[8] as usize; pack_t = u16[12]; depth = u16[28]; } let pack_t = PackType::from_repr(pack_t)?; if pt_fl & 0x8000 == 0 { bail!("PICT1 not supported"); } if right - left != im.w() || bottom - top != im.h() { bail!("image bounds are incorrect"); } let mut p = 46; // get CLUT if packed let clut = if pack { let (clut, sz) = get_clut(&b[p..])?; p += sz; Some(clut) } else { None }; p += 18; // srcRect, dstRect, mode if clip { p += u16b(&b[p..]) as usize; // maskRgn } let rle = pack_t == PackType::Default || pack_t == PackType::Rle16 && depth == 16 || pack_t == PackType::Rle32 && depth == 32; let pitch = (pt_fl & 0x3FFF) as usize; Ok((&b[p..], Header{pitch, pack_t, depth, clut, rle})) } /// Reads an indexed `PixMap`. fn read_pm_ind(mut im: Image8, b: &[u8], hdr: Header) -> ResultS { let clut = ok!(hdr.clut, "no CLUT in indexed mode")?; let mut p = 0; if hdr.pitch < 8 && hdr.depth == 8 { // uncompressed 8-bit colormap indices for _ in 0..im.h() { for _ in 0..im.w() { let idx = b[p] as usize; im.cr.push(ok!(clut.get(idx), "invalid index")?.clone()); p += 1; } } Ok(im) } else if hdr.rle { // RLE compressed 1, 2, 4 or 8 bit colormap indices for _ in 0..im.h() { let (d, pp) = read_rle(&b[p..], hdr.pitch, false)?; let d = if hdr.depth < 8 { expand_data(d, hdr.depth)? } else { d }; p += pp; for &idx in &d { im.cr .push(ok!(clut.get(idx as usize), "invalid index")?.clone()); } } Ok(im) } else { bail!("invalid configuration") } } /// Reads a R5G5B5 `PixMap`. fn read_pm_16(mut im: Image8, b: &[u8], hdr: Header) -> ResultS { let mut p = 0; if hdr.pitch < 8 || hdr.pack_t == PackType::None { // uncompressed R5G5B5 for _ in 0..im.h() { for _ in 0..im.w() { let cr = u16b(&b[p..]); im.cr.push(r5g5b5_to_rgb8(cr)); p += 2; } } Ok(im) } else if hdr.rle { // RLE compressed R5G5B5 for _ in 0..im.h() { let (d, pp) = read_rle(&b[p..], hdr.pitch, true)?; p += pp; for x in 0..im.w() { let cr = u16b(&d[x * 2..]); im.cr.push(r5g5b5_to_rgb8(cr)); } } Ok(im) } else { bail!("invalid configuration") } } /// Reads a RGB8 `PixMap`. fn read_pm_32(mut im: Image8, b: &[u8], hdr: Header) -> ResultS { let mut p = 0; if hdr.pitch < 8 || hdr.pack_t == PackType::None || hdr.pack_t == PackType::NoPad { // uncompressed RGB8 or XRGB8 for _ in 0..im.h() { for _ in 0..im.w() { if hdr.pack_t != PackType::NoPad { p += 1; } read_data! { p + 3, BE in b => r = u8[p]; g = u8[p + 1]; b = u8[p + 2]; } im.cr.push(Color8::new(r, g, b)); p += 3; } } Ok(im) } else if hdr.rle { // RLE compressed RGB8 let pitch = hdr.pitch - im.w(); // remove padding byte from pitch for _ in 0..im.h() { let (d, pp) = read_rle(&b[p..], pitch, false)?; p += pp; for x in 0..im.w() { let r = d[x + im.w()]; let g = d[x + im.w() * 2]; let b = d[x + im.w() * 3]; im.cr.push(Color8::new(r, g, b)); } } Ok(im) } else { bail!("invalid configuration") } } /// Process a `CopyBits` operation. fn read_pm_area(im: Image8, b: &[u8], pack: bool, clip: bool) -> ResultS { let p = if pack {0} else {4}; let (b, hdr) = read_pm_header(&b[p..], pack, clip, &im)?; match hdr.depth { 1 | 2 | 4 | 8 => read_pm_ind(im, b, hdr), 16 => read_pm_16(im, b, hdr), 32 => read_pm_32(im, b, hdr), _ => bail!("invalid bit depth"), } } /// Load a `PICT` image. pub fn load_pict(b: &[u8]) -> ResultS { read_data! { 10, BE in b => h = u16[6] as usize; w = u16[8] as usize; } let im = Image8::new(w, h); let mut p = 10; // size of header while p < b.len() { read_data! { p + 2, BE in b => op = u16[p]; } p += 2; match op { 0x0098 => { // PackBitsRect return read_pm_area(im, &b[p..], true, false); } 0x0099 => { // PackBitsRgn return read_pm_area(im, &b[p..], true, true); } 0x009a => { // DirectBitsRect return read_pm_area(im, &b[p..], false, false); } 0x009b => { // DirectBitsRgn return read_pm_area(im, &b[p..], false, true); } 0x8200 => { // CompressedQuickTime unimplemented!(); } 0x00ff => { // OpEndPic break; } // help i'm trapped in an awful metafile format from the 80s 0x0000 | // NoOp 0x001c | // HiliteMode 0x001e | // DefHilite 0x0038 | // FrameSameRect 0x0039 | // PaintSameRect 0x003a | // EraseSameRect 0x003b | // InvertSameRect 0x003c | // FillSameRect 0x8000 | // Reserved 0x8100 => (), // Reserved 0x0003 | // TxFont 0x0004 | // TxFace 0x0005 | // TxMode 0x0008 | // PnMode 0x000d | // TxSize 0x0011 | // VersionOp 0x0015 | // PnLocHFrac 0x0016 | // ChExtra 0x0023 | // ShortLineFrom 0x00a0 => p += 2, // ShortComment 0x0006 | // SpExtra 0x0007 | // PnSize 0x000b | // OvSize 0x000c | // Origin 0x000e | // FgCol 0x000f | // BkCol 0x0021 => p += 4, // LineFrom 0x001a | // RGBFgCol 0x001b | // RGBBkCol 0x001d | // TxRatio 0x0022 => p += 6, // ShortLine 0x0002 | // BkPat 0x0009 | // PnPat 0x0010 | // TxRatio 0x0020 | // Line 0x002e | // GlyphState 0x0030 | // FrameRect 0x0031 | // PaintRect 0x0032 | // EraseRect 0x0033 | // InvertRect 0x0034 => p += 8, // FillRect 0x002d => p += 10, // LineJustify 0x0001 => p += (u16b(&b[p.. ]) & !1) as usize, // Clip 0x00a1 => p += (u16b(&b[p+2..]) & !1) as usize + 2, // LongComment 0x100..= 0x7fff => p += (op >> 8) as usize * 2, // Reserved _ => { bail!("invalid op in PICT"); } } } Err(err_msg("no image in data")) } /// Read a `ColorTable` structure. pub fn get_clut(b: &[u8]) -> ResultS<(Vec, usize)> { read_data! { 8, BE in b => dev = u16[4]; num = u16[6] as usize; } let dev = dev & 0x8000 != 0; let num = num + 1; let mut p = 8; let mut clut = vec![Color8::new(0, 0, 0); num]; for i in 0..num { read_data! { p + 8, BE in b => n = u16[p] as usize; r = u8[p + 2]; g = u8[p + 4]; b = u8[p + 6]; } // with device mapping, we ignore the index entirely let n = if dev {i} else {n}; *ok!(clut.get_mut(n), "invalid index")? = Color8::new(r, g, b); p += 8; } Ok((clut, p)) } /// Read run-length encoded data. pub fn read_rle(b: &[u8], pitch: usize, ln: bool) -> ResultS<(Vec, usize)> { let mut p = 0; let mut o = Vec::with_capacity(pitch); let sz = if pitch > 250 { (u16b(b) as usize + 2, p += 2).0 } else { (b[0] as usize + 1, p += 1).0 }; while p < sz { let szf = b[p]; let cmp = szf & 0x80 != 0; let len = if cmp {!szf + 2} else {szf + 1} as usize; p += 1; 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(err_msg("incorrect size for compressed scanline")) } } /// Read a sequence of packed RLE data. fn read_rle_data(cmp: bool, len: usize, out: &mut Vec, mut read: F) where F: FnMut() -> GenericArray, N: ArrayLength { 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. pub fn expand_data(b: Vec, depth: u16) -> ResultS> { let mut o = Vec::with_capacity(match depth { 4 => b.len() * 2, 2 => b.len() * 4, 1 => b.len() * 8, _ => bail!("invalid bit depth"), }); for ch in b { match depth { 4 => {for i in (0..=1).rev() {o.push(ch >> (i * 4) & 0xF_u8);}} 2 => {for i in (0..=3).rev() {o.push(ch >> (i * 2) & 0x3_u8);}} 1 => {for i in (0..=7).rev() {o.push(ch >> i & 0x1_u8);}} _ => bail!("invalid bit depth"), } } Ok(o) } struct Header { pitch: usize, pack_t: PackType, depth: u16, clut: Option>, rle: bool, } c_enum! { #[derive(PartialEq)] enum PackType: u16 { 0 => Default, 1 => None, 2 => NoPad, 3 => Rle16, 4 => Rle32, } } // EOF