marathon: split pict module into multiple files
parent
c5cd1aecf3
commit
651efd1f32
|
@ -1,212 +1,13 @@
|
||||||
//! QuickDraw PICT format loader.
|
//! QuickDraw PICT format loader.
|
||||||
|
|
||||||
|
pub mod clut;
|
||||||
|
pub mod pm;
|
||||||
|
pub mod rle;
|
||||||
|
|
||||||
use crate::durandal::{bin::*, err::*, image::*};
|
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<Image8>
|
|
||||||
{
|
|
||||||
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::<u8>(&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<Image8>
|
|
||||||
{
|
|
||||||
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::<u16>(&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<Image8>
|
|
||||||
{
|
|
||||||
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::<u8>(&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<Image8>
|
|
||||||
{
|
|
||||||
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.
|
/// Load a `PICT` image.
|
||||||
pub fn load_pict(b: &[u8]) -> ResultS<Image8>
|
pub fn read(b: &[u8]) -> ResultS<Image8>
|
||||||
{
|
{
|
||||||
read_data! {
|
read_data! {
|
||||||
endian: BIG, buf: b, size: 10, start: 0, data {
|
endian: BIG, buf: b, size: 10, start: 0, data {
|
||||||
|
@ -235,75 +36,75 @@ pub fn load_pict(b: &[u8]) -> ResultS<Image8>
|
||||||
match op {
|
match op {
|
||||||
0x0098 => {
|
0x0098 => {
|
||||||
// PackBitsRect
|
// PackBitsRect
|
||||||
return read_pm_area(im, &b[p..], true, false);
|
return pm::area::read(im, &b[p..], true, false);
|
||||||
}
|
}
|
||||||
0x0099 => {
|
0x0099 => {
|
||||||
// PackBitsRgn
|
// PackBitsRgn
|
||||||
return read_pm_area(im, &b[p..], true, true);
|
return pm::area::read(im, &b[p..], true, true);
|
||||||
}
|
}
|
||||||
0x009A => {
|
0x009a => {
|
||||||
// DirectBitsRect
|
// DirectBitsRect
|
||||||
return read_pm_area(im, &b[p..], false, false);
|
return pm::area::read(im, &b[p..], false, false);
|
||||||
}
|
}
|
||||||
0x009B => {
|
0x009b => {
|
||||||
// DirectBitsRgn
|
// DirectBitsRgn
|
||||||
return read_pm_area(im, &b[p..], false, true);
|
return pm::area::read(im, &b[p..], false, true);
|
||||||
}
|
}
|
||||||
0x8200 => {
|
0x8200 => {
|
||||||
// CompressedQuickTime
|
// CompressedQuickTime
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
0x00FF => {
|
0x00ff => {
|
||||||
// OpEndPic
|
// OpEndPic
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// help i'm trapped in an awful metafile format from the 80s
|
// help i'm trapped in an awful metafile format from the 80s
|
||||||
0x0000 | // NoOp
|
0x0000 | // NoOp
|
||||||
0x001C | // HiliteMode
|
0x001c | // HiliteMode
|
||||||
0x001E | // DefHilite
|
0x001e | // DefHilite
|
||||||
0x0038 | // FrameSameRect
|
0x0038 | // FrameSameRect
|
||||||
0x0039 | // PaintSameRect
|
0x0039 | // PaintSameRect
|
||||||
0x003A | // EraseSameRect
|
0x003a | // EraseSameRect
|
||||||
0x003B | // InvertSameRect
|
0x003b | // InvertSameRect
|
||||||
0x003C | // FillSameRect
|
0x003c | // FillSameRect
|
||||||
0x8000 | // Reserved
|
0x8000 | // Reserved
|
||||||
0x8100 => (), // Reserved
|
0x8100 => (), // Reserved
|
||||||
0x0003 | // TxFont
|
0x0003 | // TxFont
|
||||||
0x0004 | // TxFace
|
0x0004 | // TxFace
|
||||||
0x0005 | // TxMode
|
0x0005 | // TxMode
|
||||||
0x0008 | // PnMode
|
0x0008 | // PnMode
|
||||||
0x000D | // TxSize
|
0x000d | // TxSize
|
||||||
0x0011 | // VersionOp
|
0x0011 | // VersionOp
|
||||||
0x0015 | // PnLocHFrac
|
0x0015 | // PnLocHFrac
|
||||||
0x0016 | // ChExtra
|
0x0016 | // ChExtra
|
||||||
0x0023 | // ShortLineFrom
|
0x0023 | // ShortLineFrom
|
||||||
0x00A0 => p += 2, // ShortComment
|
0x00a0 => p += 2, // ShortComment
|
||||||
0x0006 | // SpExtra
|
0x0006 | // SpExtra
|
||||||
0x0007 | // PnSize
|
0x0007 | // PnSize
|
||||||
0x000B | // OvSize
|
0x000b | // OvSize
|
||||||
0x000C | // Origin
|
0x000c | // Origin
|
||||||
0x000E | // FgCol
|
0x000e | // FgCol
|
||||||
0x000F | // BkCol
|
0x000f | // BkCol
|
||||||
0x0021 => p += 4, // LineFrom
|
0x0021 => p += 4, // LineFrom
|
||||||
0x001A | // RGBFgCol
|
0x001a | // RGBFgCol
|
||||||
0x001B | // RGBBkCol
|
0x001b | // RGBBkCol
|
||||||
0x001D | // TxRatio
|
0x001d | // TxRatio
|
||||||
0x0022 => p += 6, // ShortLine
|
0x0022 => p += 6, // ShortLine
|
||||||
0x0002 | // BkPat
|
0x0002 | // BkPat
|
||||||
0x0009 | // PnPat
|
0x0009 | // PnPat
|
||||||
0x0010 | // TxRatio
|
0x0010 | // TxRatio
|
||||||
0x0020 | // Line
|
0x0020 | // Line
|
||||||
0x002E | // GlyphState
|
0x002e | // GlyphState
|
||||||
0x0030 | // FrameRect
|
0x0030 | // FrameRect
|
||||||
0x0031 | // PaintRect
|
0x0031 | // PaintRect
|
||||||
0x0032 | // EraseRect
|
0x0032 | // EraseRect
|
||||||
0x0033 | // InvertRect
|
0x0033 | // InvertRect
|
||||||
0x0034 => p += 8, // FillRect
|
0x0034 => p += 8, // FillRect
|
||||||
0x002D => p += 10, // LineJustify
|
0x002d => p += 10, // LineJustify
|
||||||
0x0001 => p += usize::from(u16b(&b[p.. ]) & !1), // Clip
|
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..=
|
0x100..=
|
||||||
0x7FFF => p += usize::from(op >> 8) * 2, // Reserved
|
0x7fff => p += usize::from(op >> 8) * 2, // Reserved
|
||||||
_ => {
|
_ => {
|
||||||
bail!("invalid op in PICT");
|
bail!("invalid op in PICT");
|
||||||
}
|
}
|
||||||
|
@ -313,196 +114,4 @@ pub fn load_pict(b: &[u8]) -> ResultS<Image8>
|
||||||
Err(err_msg("no image in data"))
|
Err(err_msg("no image in data"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a `ColorTable` structure.
|
|
||||||
pub fn get_clut(b: &[u8]) -> ResultS<(Vec<Color8>, 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<T>(b: &[u8], pitch: usize) -> ResultS<(Vec<T>, 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<Self>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReadRleData for u16
|
|
||||||
{
|
|
||||||
fn read_rle_data(b: &[u8],
|
|
||||||
p: &mut usize,
|
|
||||||
cmp: bool,
|
|
||||||
len: usize,
|
|
||||||
out: &mut Vec<Self>)
|
|
||||||
{
|
|
||||||
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<Self>)
|
|
||||||
{
|
|
||||||
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<u8>, depth: Depth) -> ResultS<Vec<u8>>
|
|
||||||
{
|
|
||||||
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<Vec<Color8>>,
|
|
||||||
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
|
// EOF
|
||||||
|
|
|
@ -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<Color8>, 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
|
|
@ -0,0 +1,9 @@
|
||||||
|
//! QuickDraw PICT `PixMap` data.
|
||||||
|
|
||||||
|
pub mod area;
|
||||||
|
pub mod head;
|
||||||
|
pub mod ind;
|
||||||
|
pub mod r5g5b5;
|
||||||
|
pub mod rgb8;
|
||||||
|
|
||||||
|
// EOF
|
|
@ -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<Image8>
|
||||||
|
{
|
||||||
|
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
|
|
@ -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<Vec<Color8>>,
|
||||||
|
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
|
|
@ -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<Image8>
|
||||||
|
{
|
||||||
|
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::<u8>(&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<u8>, depth: pm::head::Depth) -> ResultS<Vec<u8>>
|
||||||
|
{
|
||||||
|
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
|
|
@ -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<Image8>
|
||||||
|
{
|
||||||
|
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::<u16>(&b[p..], hdr.pitch)?;
|
||||||
|
|
||||||
|
p += pp;
|
||||||
|
|
||||||
|
for &cr in &d {
|
||||||
|
im.cr.push(r5g5b5_to_rgb8(cr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(im)
|
||||||
|
} else {
|
||||||
|
bail!("invalid configuration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -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<Image8>
|
||||||
|
{
|
||||||
|
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::<u8>(&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
|
|
@ -0,0 +1,94 @@
|
||||||
|
//! QuickDraw PICT format run length encoded data.
|
||||||
|
|
||||||
|
use crate::durandal::{bin::*, err::*};
|
||||||
|
|
||||||
|
/// Read run-length encoded data.
|
||||||
|
pub fn read<T>(b: &[u8], pitch: usize) -> ResultS<(Vec<T>, 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<Self>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadRleData for u16
|
||||||
|
{
|
||||||
|
fn read_rle_data(b: &[u8],
|
||||||
|
p: &mut usize,
|
||||||
|
cmp: bool,
|
||||||
|
len: usize,
|
||||||
|
out: &mut Vec<Self>)
|
||||||
|
{
|
||||||
|
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<Self>)
|
||||||
|
{
|
||||||
|
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
|
|
@ -8,15 +8,15 @@ fn get_clut_must_process()
|
||||||
const INPUT: &[u8] = include_bytes!("data/pict/clut.in");
|
const INPUT: &[u8] = include_bytes!("data/pict/clut.in");
|
||||||
const OUTPUT: [Color8; 256] = include!("data/pict/clut.out");
|
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]
|
#[test]
|
||||||
fn pict_must_not_process()
|
fn pict_must_not_process()
|
||||||
{
|
{
|
||||||
for inp in &RANDOM {
|
for inp in &RANDOM {
|
||||||
assert!(pict::get_clut(inp).is_err());
|
assert!(pict::clut::read(inp).is_err());
|
||||||
assert!(pict::load_pict(inp).is_err());
|
assert!(pict::read(inp).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue