//! QuickDraw PICT format loader. use crate::durandal::{bin::*, err::*, image::*}; use generic_array::*; const PACK_DEFAULT: u16 = 0; const PACK_NONE: u16 = 1; const PACK_NOPAD: u16 = 2; const PACK_RLE16: u16 = 3; const PACK_RLE32: u16 = 4; /// Process a CopyBits operation. pub fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> ResultS { let mut p = if !packed {4} else {0}; let (w, h) = (im.w(), im.h()); 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(err_msg("PICT1 not supported")); } if right - left != w || bottom - top != h { return Err(err_msg("image bounds are incorrect")); } p += 46; // size of header // get CLUT if packed let clut = if packed { let (clut, sz) = get_clut(&b[p..])?; p += sz; Some(clut) } else { None }; p += 18; // srcRect, dstRect, mode if clip { p += b.c_u16b(p)? as usize; // maskRgn } let rle = pack_typ == PACK_DEFAULT || (pack_typ == PACK_RLE16 && depth == 16) || (pack_typ == PACK_RLE32 && depth == 32); let pitch = (pitch_fl & 0x3fff) as usize; match depth { 1 | 2 | 4 | 8 => { let clut = clut.ok_or_else(|| err_msg("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(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..], pitch, false)?; let d = if depth < 8 {expand_data(d, depth)?} else {d}; p += pp; for x in 0..w { im.cr.push(clut[d[x] as usize].clone()); } } Ok(im) } else { Err(err_msg("invalid configuration")) } } 16 => { 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)?)); } } Ok(im) } else if rle { // RLE compressed R5G5B5 for _ in 0..h { 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)?)); } } Ok(im) } else { Err(err_msg("invalid configuration")) } } 32 => { 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_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}); } } Ok(im) } else if rle { // RLE compressed RGB8 let pitch = pitch - w; // remove padding byte from pitch for _ in 0..h { let (d, pp) = read_rle(&b[p..], pitch, false)?; p += pp; for x in 0..w { 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}); } } Ok(im) } else { Err(err_msg("invalid configuration")) } } _ => Err(err_msg("invalid bit depth")), } } /// Process a CompressedQuickTime operation. pub fn read_quicktime_c(_im: Image, _b: &[u8]) -> ResultS { unimplemented!() } /// Load a PICT image. pub fn load_pict(b: &[u8]) -> ResultS { // 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() { let op = b.c_u16b((p, p += 2).0)?; match op { 0x0098 => { // PackBitsRect return read_bitmap_area(im, &b[p..], true, false); } 0x0099 => { // PackBitsRgn return read_bitmap_area(im, &b[p..], true, true); } 0x009a => { // DirectBitsRect return read_bitmap_area(im, &b[p..], false, false); } 0x009b => { // DirectBitsRgn return read_bitmap_area(im, &b[p..], false, true); } 0x8200 => { // CompressedQuickTime return read_quicktime_c(im, &b[p..]); } 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 | // ShortComment 0x02ff => p += 2, // Version 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 0x0c00 => p += 24, // HeaderOp 0x0001 => p += (b.c_u16b(p )? & !1) as usize, // Clip 0x00a1 => p += (b.c_u16b(p+2)? & !1) as usize + 2, // LongComment 0x100..= 0x7fff => p += (op >> 8) as usize * 2, // Reserved _ => { return Err(err_msg("invalid op in PICT")); } } } Err(err_msg("no image in data")) } /// Read a colorTable structure. pub fn get_clut(b: &[u8]) -> ResultS<(Vec, 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![Color{r: 0, g: 0, b: 0, a: 0}; num]; for i in 0..num { // with device mapping, we ignore the index entirely 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]; if n >= clut.len() { return Err(err_msg("bad clut index")); } clut[n] = Color{r, g, b, a: 255}; 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 { (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(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, _ => return Err(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(err_msg("invalid bit depth")), } } Ok(o) } // EOF