Maraiah/maraiah/shp/bmap.rs

144 lines
3.1 KiB
Rust

//! Shapes file bitmap type.
use crate::{err::*, image::Image};
use super::clut;
use bitflags::bitflags;
/// Reads a `Bitmap`.
pub fn read(b: &[u8]) -> ResultS<Bitmap>
{
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<u8>,
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