//! 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_NOPAD : u16 = 2; const PACK_RLE16 : u16 = 3; const PACK_RLE32 : u16 = 4; /// Read a colorTable structure. fn get_clut(b: &[u8]) -> 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 } /// 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 { if cmp { 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], pt: usize, ln: bool) -> Result<(Vec, usize), &str> { 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 { 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() == pt {Ok((o, p))} else {Err("incorrect size for compressed scanline")} } /// Expand packed pixel data based on bit depth. fn expand_data(b: Vec, depth: u16) -> Result, &'static str> { let mut o = Vec::with_capacity(match depth { 4 => b.len() * 2, 2 => b.len() * 4, 1 => b.len() * 8, _ => return Err("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("invalid bit depth") } } Ok(o) } /// Process a CopyBits operation. fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result { let mut p = if !packed {4} else {0}; // baseAddr let (w, h) = (im.w(), im.h()); 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 p += 46; if pf & 0x8000 == 0 {return Err("PICT1 not supported")} if xe - xb != w || ye - yb != h {return Err("image bounds are incorrect")} 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); 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 && dept == 8 { 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 rle { for _ in 0..h { let (d, pp) = read_rle(&b[p..], pt, false)?; let d = if dept < 8 {expand_data(d, dept)?} else {d}; p += pp; for x in 0..w {im.cr.push(map[d[x] as usize].clone())} } Ok(im) } // invalid else {Err("invalid configuration")}, 16 => // uncompressed R5G5B5 if pt < 8 || pack == PACK_NONE { 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 rle { for _ in 0..h { 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) } // invalid else {Err("invalid configuration")}, 32 => // uncompressed RGB8 or XRGB8 if pt < 8 || pack == PACK_NONE || pack == PACK_NOPAD { for _ in 0..h { for _ in 0..w { if pack != 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) } // RLE compressed RGB8 else if rle { let pt = pt - w; // remove padding byte from pitch for _ in 0..h { let (d, pp) = read_rle(&b[p..], pt, 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) } // invalid else {Err("invalid configuration")}, _ => Err("invalid bit depth") } } /// Process a CompressedQuickTime operation. fn read_quicktime_c(_im: Image, _b: &[u8]) -> Result {Err("compressed quicktime format not implemented")} /// Load a PICT image. 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 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).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 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 0x0000 => (), // NoOp 0x001c => (), // HiliteMode 0x001e => (), // DefHilite 0x0038 => (), // FrameSameRect 0x0039 => (), // PaintSameRect 0x003a => (), // EraseSameRect 0x003b => (), // InvertSameRect 0x003c => (), // FillSameRect 0x8000 => (), // Reserved 0x8100 => (), // Reserved 0x0003 => p += 2, // TxFont 0x0004 => p += 2, // TxFace 0x0005 => p += 2, // TxMode 0x0008 => p += 2, // PnMode 0x000d => p += 2, // TxSize 0x0011 => p += 2, // VersionOp 0x0015 => p += 2, // PnLocHFrac 0x0016 => p += 2, // ChExtra 0x0023 => p += 2, // ShortLineFrom 0x00a0 => p += 2, // ShortComment 0x02ff => p += 2, // Version 0x0006 => p += 4, // SpExtra 0x0007 => p += 4, // PnSize 0x000b => p += 4, // OvSize 0x000c => p += 4, // Origin 0x000e => p += 4, // FgCol 0x000f => p += 4, // BkCol 0x0021 => p += 4, // LineFrom 0x001a => p += 6, // RGBFgCol 0x001b => p += 6, // RGBBkCol 0x001d => p += 6, // TxRatio 0x0022 => p += 6, // ShortLine 0x0002 => p += 8, // BkPat 0x0009 => p += 8, // PnPat 0x0010 => p += 8, // TxRatio 0x0020 => p += 8, // Line 0x002e => p += 8, // GlyphState 0x0030 => p += 8, // FrameRect 0x0031 => p += 8, // PaintRect 0x0032 => p += 8, // EraseRect 0x0033 => p += 8, // InvertRect 0x0034 => p += 8, // FillRect 0x002d => p += 10, // LineJustify 0x0c00 => p += 24, // HeaderOp 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") } } Err("no image in data") }