From 651efd1f32486264423f422449b553a18d4307e0 Mon Sep 17 00:00:00 2001 From: Alison Watson Date: Thu, 30 May 2019 21:28:35 -0400 Subject: [PATCH] marathon: split pict module into multiple files --- source/marathon/pict.rs | 451 ++---------------------------- source/marathon/pict/clut.rs | 42 +++ source/marathon/pict/pm.rs | 9 + source/marathon/pict/pm/area.rs | 21 ++ source/marathon/pict/pm/head.rs | 90 ++++++ source/marathon/pict/pm/ind.rs | 85 ++++++ source/marathon/pict/pm/r5g5b5.rs | 42 +++ source/marathon/pict/pm/rgb8.rs | 60 ++++ source/marathon/pict/rle.rs | 94 +++++++ tests/pict.rs | 6 +- 10 files changed, 476 insertions(+), 424 deletions(-) create mode 100644 source/marathon/pict/clut.rs create mode 100644 source/marathon/pict/pm.rs create mode 100644 source/marathon/pict/pm/area.rs create mode 100644 source/marathon/pict/pm/head.rs create mode 100644 source/marathon/pict/pm/ind.rs create mode 100644 source/marathon/pict/pm/r5g5b5.rs create mode 100644 source/marathon/pict/pm/rgb8.rs create mode 100644 source/marathon/pict/rle.rs diff --git a/source/marathon/pict.rs b/source/marathon/pict.rs index 68a4c31..523110a 100644 --- a/source/marathon/pict.rs +++ b/source/marathon/pict.rs @@ -1,212 +1,13 @@ //! QuickDraw PICT format loader. +pub mod clut; +pub mod pm; +pub mod rle; + use crate::durandal::{bin::*, err::*, image::*}; -use std::convert::TryFrom; - -// Reads a `PixMap` header. -fn read_pm_header<'a>(b: &'a [u8], - pack: bool, - clip: bool, - im: &Image8) - -> ResultS<(&'a [u8], Header)> -{ - read_data! { - endian: BIG, buf: b, size: 36, start: 0, data { - let pt_fl = u16[0]; - let top = u16[2] usize; - let left = u16[4] usize; - let bottom = u16[6] usize; - let right = u16[8] usize; - let pack_t = u16[12] enum PackType; - let depth = u16[28] enum Depth; - } - } - - 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 += usize::from(u16b(&b[p..])); // maskRgn - } - - let rle = pack_t == PackType::Default || - pack_t == PackType::Rle16 && depth == Depth::_16 || - pack_t == PackType::Rle32 && depth == Depth::_32; - - let pitch = usize::from(pt_fl & 0x3FFF); - - 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 == Depth::_8 { - // uncompressed 8-bit colormap indices - for _ in 0..im.h() { - for _ in 0..im.w() { - let idx = usize::from(b[p]); - - 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)?; - - let d = if hdr.depth < Depth::_8 { - expand_data(d, hdr.depth)? - } else { - d - }; - - p += pp; - - for &idx in &d { - im.cr - .push(ok!(clut.get(usize::from(idx)), "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)?; - - p += pp; - - for &cr in &d { - 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! { - endian: BIG, buf: b, size: 3, start: p, data { - let r = u8[0]; - let g = u8[1]; - let b = u8[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)?; - - 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 { - Depth::_1 | - Depth::_2 | - Depth::_4 | - Depth::_8 => read_pm_ind(im, b, hdr), - Depth::_16 => read_pm_16(im, b, hdr), - Depth::_32 => read_pm_32(im, b, hdr), - } -} /// Load a `PICT` image. -pub fn load_pict(b: &[u8]) -> ResultS +pub fn read(b: &[u8]) -> ResultS { read_data! { endian: BIG, buf: b, size: 10, start: 0, data { @@ -235,75 +36,75 @@ pub fn load_pict(b: &[u8]) -> ResultS match op { 0x0098 => { // PackBitsRect - return read_pm_area(im, &b[p..], true, false); + return pm::area::read(im, &b[p..], true, false); } 0x0099 => { // PackBitsRgn - return read_pm_area(im, &b[p..], true, true); + return pm::area::read(im, &b[p..], true, true); } - 0x009A => { + 0x009a => { // DirectBitsRect - return read_pm_area(im, &b[p..], false, false); + return pm::area::read(im, &b[p..], false, false); } - 0x009B => { + 0x009b => { // DirectBitsRgn - return read_pm_area(im, &b[p..], false, true); + return pm::area::read(im, &b[p..], false, true); } 0x8200 => { // CompressedQuickTime unimplemented!(); } - 0x00FF => { + 0x00ff => { // OpEndPic break; } // help i'm trapped in an awful metafile format from the 80s 0x0000 | // NoOp - 0x001C | // HiliteMode - 0x001E | // DefHilite + 0x001c | // HiliteMode + 0x001e | // DefHilite 0x0038 | // FrameSameRect 0x0039 | // PaintSameRect - 0x003A | // EraseSameRect - 0x003B | // InvertSameRect - 0x003C | // FillSameRect + 0x003a | // EraseSameRect + 0x003b | // InvertSameRect + 0x003c | // FillSameRect 0x8000 | // Reserved 0x8100 => (), // Reserved 0x0003 | // TxFont 0x0004 | // TxFace 0x0005 | // TxMode 0x0008 | // PnMode - 0x000D | // TxSize + 0x000d | // TxSize 0x0011 | // VersionOp 0x0015 | // PnLocHFrac 0x0016 | // ChExtra 0x0023 | // ShortLineFrom - 0x00A0 => p += 2, // ShortComment + 0x00a0 => p += 2, // ShortComment 0x0006 | // SpExtra 0x0007 | // PnSize - 0x000B | // OvSize - 0x000C | // Origin - 0x000E | // FgCol - 0x000F | // BkCol + 0x000b | // OvSize + 0x000c | // Origin + 0x000e | // FgCol + 0x000f | // BkCol 0x0021 => p += 4, // LineFrom - 0x001A | // RGBFgCol - 0x001B | // RGBBkCol - 0x001D | // TxRatio + 0x001a | // RGBFgCol + 0x001b | // RGBBkCol + 0x001d | // TxRatio 0x0022 => p += 6, // ShortLine 0x0002 | // BkPat 0x0009 | // PnPat 0x0010 | // TxRatio 0x0020 | // Line - 0x002E | // GlyphState + 0x002e | // GlyphState 0x0030 | // FrameRect 0x0031 | // PaintRect 0x0032 | // EraseRect 0x0033 | // InvertRect 0x0034 => p += 8, // FillRect - 0x002D => p += 10, // LineJustify + 0x002d => p += 10, // LineJustify 0x0001 => p += usize::from(u16b(&b[p.. ]) & !1), // Clip - 0x00A1 => p += usize::from(u16b(&b[p+2..]) & !1) + 2, // LongComment + 0x00a1 => p += usize::from(u16b(&b[p+2..]) & !1) + 2, // LongComment 0x100..= - 0x7FFF => p += usize::from(op >> 8) * 2, // Reserved + 0x7fff => p += usize::from(op >> 8) * 2, // Reserved _ => { bail!("invalid op in PICT"); } @@ -313,196 +114,4 @@ pub fn load_pict(b: &[u8]) -> ResultS Err(err_msg("no image in data")) } -/// Read a `ColorTable` structure. -pub fn get_clut(b: &[u8]) -> ResultS<(Vec, usize)> -{ - read_data! { - endian: BIG, buf: b, size: 8, start: 0, data { - let dev = u16[4]; - let num = u16[6] 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! { - endian: BIG, buf: b, size: 8, start: p, data { - let n = u16[0] usize; - let r = u8[2]; - let g = u8[4]; - let b = u8[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. -fn read_rle(b: &[u8], pitch: usize) -> ResultS<(Vec, usize)> - where T: ReadRleData -{ - let mut p = 0; - let mut o = Vec::with_capacity(pitch); - - let sz = if pitch > 250 { - (usize::from(u16b(b)) + 2, p += 2).0 - } else { - (usize::from(b[0]) + 1, p += 1).0 - }; - - while p < sz { - let szf = b[p]; - let cmp = szf & 0x80 != 0; - let len = usize::from(if cmp {!szf + 2} else {szf + 1}); - - p += 1; - o.reserve(len); - - T::read_rle_data(b, &mut p, cmp, len, &mut o); - } - - if o.len() == pitch { - Ok((o, p)) - } else { - Err(err_msg("incorrect size for compressed scanline")) - } -} - -trait ReadRleData: Sized -{ - // Read a sequence of packed RLE data. - fn read_rle_data(b: &[u8], - p: &mut usize, - cmp: bool, - len: usize, - out: &mut Vec); -} - -impl ReadRleData for u16 -{ - fn read_rle_data(b: &[u8], - p: &mut usize, - cmp: bool, - len: usize, - out: &mut Vec) - { - if cmp { - let d = u16b(&b[*p..*p + 2]); - *p += 2; - for _ in 0..len { - out.push(d); - } - } else { - for _ in 0..len { - let d = u16b(&b[*p..*p + 2]); - *p += 2; - out.push(d); - } - } - } -} - -impl ReadRleData for u8 -{ - fn read_rle_data(b: &[u8], - p: &mut usize, - cmp: bool, - len: usize, - out: &mut Vec) - { - if cmp { - let d = b[*p]; - *p += 1; - for _ in 0..len { - out.push(d); - } - } else { - for _ in 0..len { - let d = b[*p]; - *p += 1; - out.push(d); - } - } - } -} - -// Expand packed pixel data based on bit depth. -fn expand_data(b: Vec, depth: Depth) -> ResultS> -{ - let mut o = Vec::with_capacity(match depth { - Depth::_4 => b.len() * 2, - Depth::_2 => b.len() * 4, - Depth::_1 => b.len() * 8, - _ => bail!("invalid bit depth"), - }); - - for ch in b { - match depth { - Depth::_4 => { - for i in (0..=1).rev() { - o.push(ch >> (i * 4) & 0xF_u8); - } - } - Depth::_2 => { - for i in (0..=3).rev() { - o.push(ch >> (i * 2) & 0x3_u8); - } - } - Depth::_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: Depth, - clut: Option>, - rle: bool, -} - -c_enum! { - enum Depth: u16 - { - _1 = 1, - _2 = 2, - _4 = 4, - _8 = 8, - _16 = 16, - _32 = 32, - } -} - -c_enum! { - enum PackType: u16 - { - Default = 0, - None = 1, - NoPad = 2, - Rle16 = 3, - Rle32 = 4, - } -} - // EOF diff --git a/source/marathon/pict/clut.rs b/source/marathon/pict/clut.rs new file mode 100644 index 0000000..23d4dc1 --- /dev/null +++ b/source/marathon/pict/clut.rs @@ -0,0 +1,42 @@ +//! QuickDraw PICT color lookup tables. + +use crate::durandal::{err::*, image::*}; + +/// Read a `ColorTable` structure. +pub fn read(b: &[u8]) -> ResultS<(Vec, usize)> +{ + read_data! { + endian: BIG, buf: b, size: 8, start: 0, data { + let dev = u16[4]; + let num = u16[6] 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! { + endian: BIG, buf: b, size: 8, start: p, data { + let n = u16[0] usize; + let r = u8[2]; + let g = u8[4]; + let b = u8[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)) +} + +// EOF diff --git a/source/marathon/pict/pm.rs b/source/marathon/pict/pm.rs new file mode 100644 index 0000000..b380043 --- /dev/null +++ b/source/marathon/pict/pm.rs @@ -0,0 +1,9 @@ +//! QuickDraw PICT `PixMap` data. + +pub mod area; +pub mod head; +pub mod ind; +pub mod r5g5b5; +pub mod rgb8; + +// EOF diff --git a/source/marathon/pict/pm/area.rs b/source/marathon/pict/pm/area.rs new file mode 100644 index 0000000..3649850 --- /dev/null +++ b/source/marathon/pict/pm/area.rs @@ -0,0 +1,21 @@ +//! QuickDraw PICT `PixMap` areas. + +use crate::{durandal::{err::*, image::*}, marathon::pict::pm}; + +/// Process a `CopyBits` operation. +pub fn read(im: Image8, b: &[u8], pack: bool, clip: bool) -> ResultS +{ + let p = if pack {0} else {4}; + let (b, hdr) = pm::head::read(&b[p..], pack, clip, &im)?; + + match hdr.depth { + pm::head::Depth::_1 | + pm::head::Depth::_2 | + pm::head::Depth::_4 | + pm::head::Depth::_8 => pm::ind::read(im, b, hdr), + pm::head::Depth::_16 => pm::r5g5b5::read(im, b, hdr), + pm::head::Depth::_32 => pm::rgb8::read(im, b, hdr), + } +} + +// EOF diff --git a/source/marathon/pict/pm/head.rs b/source/marathon/pict/pm/head.rs new file mode 100644 index 0000000..3454ee1 --- /dev/null +++ b/source/marathon/pict/pm/head.rs @@ -0,0 +1,90 @@ +//! QuickDraw PICT `PixMap` headers. + +use crate::{durandal::{bin::*, err::*, image::*}, marathon::pict}; +use std::convert::TryFrom; + +/// Reads a `PixMap` header. +pub fn read<'a>(b: &'a [u8], + pack: bool, + clip: bool, + im: &Image8) -> ResultS<(&'a [u8], Header)> +{ + read_data! { + endian: BIG, buf: b, size: 36, start: 0, data { + let pt_fl = u16[0]; + let top = u16[2] usize; + let left = u16[4] usize; + let bottom = u16[6] usize; + let right = u16[8] usize; + let pack_t = u16[12] enum PackType; + let depth = u16[28] enum Depth; + } + } + + 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) = pict::clut::read(&b[p..])?; + p += sz; + Some(clut) + } else { + None + }; + + p += 18; // srcRect, dstRect, mode + + if clip { + p += usize::from(u16b(&b[p..])); // maskRgn + } + + let rle = pack_t == PackType::Default || + pack_t == PackType::Rle16 && depth == Depth::_16 || + pack_t == PackType::Rle32 && depth == Depth::_32; + + let pitch = usize::from(pt_fl & 0x3FFF); + + Ok((&b[p..], Header{pitch, pack_t, depth, clut, rle})) +} + +pub struct Header +{ + pub pitch: usize, + pub pack_t: PackType, + pub depth: Depth, + pub clut: Option>, + pub rle: bool, +} + +c_enum! { + pub enum Depth: u16 + { + _1 = 1, + _2 = 2, + _4 = 4, + _8 = 8, + _16 = 16, + _32 = 32, + } +} + +c_enum! { + pub enum PackType: u16 + { + Default = 0, + None = 1, + NoPad = 2, + Rle16 = 3, + Rle32 = 4, + } +} + +// EOF diff --git a/source/marathon/pict/pm/ind.rs b/source/marathon/pict/pm/ind.rs new file mode 100644 index 0000000..82d7493 --- /dev/null +++ b/source/marathon/pict/pm/ind.rs @@ -0,0 +1,85 @@ +//! QuickDraw PICT indexed `PixMap`s. + +use crate::{durandal::{err::*, image::*}, marathon::pict::{pm, rle}}; + +/// Reads an indexed `PixMap`. +pub fn read(mut im: Image8, + b: &[u8], + hdr: pm::head::Header) -> ResultS +{ + let clut = ok!(hdr.clut, "no CLUT in indexed mode")?; + let mut p = 0; + + if hdr.pitch < 8 && hdr.depth == pm::head::Depth::_8 { + // uncompressed 8-bit colormap indices + for _ in 0..im.h() { + for _ in 0..im.w() { + let idx = usize::from(b[p]); + + 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) = rle::read::(&b[p..], hdr.pitch)?; + + let d = if hdr.depth < pm::head::Depth::_8 { + expand_data(d, hdr.depth)? + } else { + d + }; + + p += pp; + + for &idx in &d { + im.cr + .push(ok!(clut.get(usize::from(idx)), "invalid index")?.clone()); + } + } + + Ok(im) + } else { + bail!("invalid configuration") + } +} + +/// Expand packed pixel data based on bit depth. +pub fn expand_data(b: Vec, depth: pm::head::Depth) -> ResultS> +{ + let mut o = Vec::with_capacity(match depth { + pm::head::Depth::_4 => b.len() * 2, + pm::head::Depth::_2 => b.len() * 4, + pm::head::Depth::_1 => b.len() * 8, + _ => bail!("invalid bit depth"), + }); + + for ch in b { + match depth { + pm::head::Depth::_4 => { + for i in (0..=1).rev() { + o.push(ch >> (i * 4) & 0xF_u8); + } + } + pm::head::Depth::_2 => { + for i in (0..=3).rev() { + o.push(ch >> (i * 2) & 0x3_u8); + } + } + pm::head::Depth::_1 => { + for i in (0..=7).rev() { + o.push(ch >> i & 0x1_u8); + } + } + _ => bail!("invalid bit depth"), + } + } + + Ok(o) +} + +// EOF diff --git a/source/marathon/pict/pm/r5g5b5.rs b/source/marathon/pict/pm/r5g5b5.rs new file mode 100644 index 0000000..6059420 --- /dev/null +++ b/source/marathon/pict/pm/r5g5b5.rs @@ -0,0 +1,42 @@ +//! QuickDraw PICT R5G5B5 `PixMap`s. + +use crate::{durandal::{bin::u16b, err::*, image::*}, marathon::pict::{pm, rle}}; + +/// Reads a R5G5B5 `PixMap`. +pub fn read(mut im: Image8, + b: &[u8], + hdr: pm::head::Header) -> ResultS +{ + let mut p = 0; + + if hdr.pitch < 8 || hdr.pack_t == pm::head::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) = rle::read::(&b[p..], hdr.pitch)?; + + p += pp; + + for &cr in &d { + im.cr.push(r5g5b5_to_rgb8(cr)); + } + } + + Ok(im) + } else { + bail!("invalid configuration") + } +} + +// EOF diff --git a/source/marathon/pict/pm/rgb8.rs b/source/marathon/pict/pm/rgb8.rs new file mode 100644 index 0000000..699bbd8 --- /dev/null +++ b/source/marathon/pict/pm/rgb8.rs @@ -0,0 +1,60 @@ +//! QuickDraw PICT RGB8 `PixMap`s. + +use crate::{durandal::{err::*, image::*}, marathon::pict::{pm, rle}}; + +/// Reads a RGB8 `PixMap`. +pub fn read(mut im: Image8, + b: &[u8], + hdr: pm::head::Header) -> ResultS +{ + let mut p = 0; + + if hdr.pitch < 8 || + hdr.pack_t == pm::head::PackType::None || + hdr.pack_t == pm::head::PackType::NoPad + { + // uncompressed RGB8 or XRGB8 + for _ in 0..im.h() { + for _ in 0..im.w() { + if hdr.pack_t != pm::head::PackType::NoPad { + p += 1; + } + + read_data! { + endian: BIG, buf: b, size: 3, start: p, data { + let r = u8[0]; + let g = u8[1]; + let b = u8[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) = rle::read::(&b[p..], pitch)?; + + 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") + } +} + +// EOF diff --git a/source/marathon/pict/rle.rs b/source/marathon/pict/rle.rs new file mode 100644 index 0000000..fbba299 --- /dev/null +++ b/source/marathon/pict/rle.rs @@ -0,0 +1,94 @@ +//! QuickDraw PICT format run length encoded data. + +use crate::durandal::{bin::*, err::*}; + +/// Read run-length encoded data. +pub fn read(b: &[u8], pitch: usize) -> ResultS<(Vec, usize)> + where T: ReadRleData +{ + let mut p = 0; + let mut o = Vec::with_capacity(pitch); + + let sz = if pitch > 250 { + (usize::from(u16b(b)) + 2, p += 2).0 + } else { + (usize::from(b[0]) + 1, p += 1).0 + }; + + while p < sz { + let szf = b[p]; + let cmp = szf & 0x80 != 0; + let len = usize::from(if cmp {!szf + 2} else {szf + 1}); + + p += 1; + o.reserve(len); + + T::read_rle_data(b, &mut p, cmp, len, &mut o); + } + + if o.len() == pitch { + Ok((o, p)) + } else { + Err(err_msg("incorrect size for compressed scanline")) + } +} + +pub trait ReadRleData: Sized +{ + // Read a sequence of packed RLE data. + fn read_rle_data(b: &[u8], + p: &mut usize, + cmp: bool, + len: usize, + out: &mut Vec); +} + +impl ReadRleData for u16 +{ + fn read_rle_data(b: &[u8], + p: &mut usize, + cmp: bool, + len: usize, + out: &mut Vec) + { + if cmp { + let d = u16b(&b[*p..*p + 2]); + *p += 2; + for _ in 0..len { + out.push(d); + } + } else { + for _ in 0..len { + let d = u16b(&b[*p..*p + 2]); + *p += 2; + out.push(d); + } + } + } +} + +impl ReadRleData for u8 +{ + fn read_rle_data(b: &[u8], + p: &mut usize, + cmp: bool, + len: usize, + out: &mut Vec) + { + if cmp { + let d = b[*p]; + *p += 1; + for _ in 0..len { + out.push(d); + } + } else { + for _ in 0..len { + let d = b[*p]; + *p += 1; + out.push(d); + } + } + } +} + +// EOF diff --git a/tests/pict.rs b/tests/pict.rs index 8fb4e7a..cd7076b 100644 --- a/tests/pict.rs +++ b/tests/pict.rs @@ -8,15 +8,15 @@ fn get_clut_must_process() const INPUT: &[u8] = include_bytes!("data/pict/clut.in"); const OUTPUT: [Color8; 256] = include!("data/pict/clut.out"); - assert_eq!(pict::get_clut(INPUT).unwrap(), (OUTPUT.to_vec(), 2056)); + assert_eq!(pict::clut::read(INPUT).unwrap(), (OUTPUT.to_vec(), 2056)); } #[test] fn pict_must_not_process() { for inp in &RANDOM { - assert!(pict::get_clut(inp).is_err()); - assert!(pict::load_pict(inp).is_err()); + assert!(pict::clut::read(inp).is_err()); + assert!(pict::read(inp).is_err()); } }