//! Shapes file bitmap type. use crate::{err::*, image::Image}; use super::clut; use bitflags::bitflags; /// Reads a `Bitmap`. pub fn read(b: &[u8]) -> ResultS { read_data! { endian: BIG, buf: b, size: 26, start: 0, data { let width = u16[0] usize; let height = u16[2] usize; let compr = u16[4]; let flags = u16[6] flag BmpFlags; let depth = u16[8]; } } let compr = compr == u16::max_value(); let alpha = flags.contains(BmpFlags::TRANSPARENT); let cmajr = flags.contains(BmpFlags::COLUMN_MAJOR); if depth != 8 { bail!("invalid bit depth (should always be 8)"); } let mut bmp = Bitmap::new(width, height, alpha, cmajr); let mut p = 30 + if cmajr {4 * width} else {4 * height}; let scanlines = if cmajr {width} else {height}; let pitch = if cmajr {height} else {width}; if compr { // compressed scanlines (transparency RLE) for _ in 0..scanlines { read_data! { endian: BIG, buf: b, size: 4, start: p, data { let fst = u16[0] usize; let lst = u16[2] usize; } } let end = lst - fst; p += 4; if lst < fst || fst > pitch || lst > pitch { bail!("invalid compressed scanline"); } for _ in 0..fst { bmp.cr.push(0); } bmp.cr.extend(ok!(b.get(p..p + end), "not enough data")?); for _ in lst..pitch { bmp.cr.push(0); } p += end; } } else { // simple copy bmp.cr.extend(ok!(b.get(p..p + width * height), "not enough data")?); } Ok(bmp) } impl Bitmap { /// Creates an empty bitmap. pub fn new(w: usize, h: usize, alpha: bool, cmajr: bool) -> Self { Self{w, h, alpha, cmajr, cr: Vec::with_capacity(w * h)} } } impl<'a, 'b> ImageShp<'a, 'b> { /// Creates an `ImageShp` with the given bitmap. pub fn new(bmp: &'a Bitmap, clut: &'b [clut::ColorShp]) -> Self { Self{bmp, clut} } } impl Image for ImageShp<'_, '_> { type Output = clut::ColorShp; fn w(&self) -> usize {self.bmp.w} fn h(&self) -> usize {self.bmp.h} fn index(&self, x: usize, y: usize) -> &Self::Output { static TRANSLUCENT_COLOR: clut::ColorShp = clut::ColorShp::Translucent; let cr = usize::from(if self.bmp.cmajr { self.bmp.cr[y + x * self.bmp.h] } else { self.bmp.cr[x + y * self.bmp.w] }); if self.bmp.alpha && cr == 0 { &TRANSLUCENT_COLOR } else { self.clut.get(cr).unwrap_or(&TRANSLUCENT_COLOR) } } } /// An unpacked Shape bitmap. #[derive(Debug, Eq, PartialEq)] pub struct Bitmap { w: usize, h: usize, cr: Vec, alpha: bool, cmajr: bool, } /// An image from a Shape. This mainly just exists so that `Bitmap` can use the /// `Image` trait. #[derive(Debug, Eq, PartialEq)] pub struct ImageShp<'a, 'b> { bmp: &'a Bitmap, clut: &'b [clut::ColorShp], } bitflags! { struct BmpFlags: u16 { const TRANSPARENT = 1 << 14; const COLUMN_MAJOR = 1 << 15; } } // EOF