use anyhow::bail; use crate::{ pixel::Pixel, ser::{Ser, Sink, Source}, vec2::{Small, Vec2}, }; #[derive(Clone, Debug)] pub enum Block { Literal(Vec), Split(Box<[Block; 2]>), Reference { translation: Vec2 }, } impl Block { pub fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { match &self { Block::Literal(pixels) => { sink.put(0u8)?; pixels.write(sink)?; } Block::Split(box [a, b]) => { sink.put(1u8)?; a.write(sink)?; b.write(sink)?; } Block::Reference { translation } => { sink.put(2u8)?; sink.put(Small(*translation))?; } } Ok(()) } pub fn read(source: &mut impl std::io::Read, size: Vec2) -> anyhow::Result { Ok(match source.get::()? { 0 => Block::Literal(source.get()?), 1 => Block::Split(Box::new({ let vert = size.x > size.y; let asize = if vert { (size.x / 2, size.y).into() } else { (size.x, size.y / 2).into() }; let bsize = if vert { (size.x - size.x / 2, size.y).into() } else { (size.x, size.y - size.y / 2).into() }; let a = Block::read(source, asize)?; let b = Block::read(source, bsize)?; [a, b] })), 2 => Block::Reference { translation: source.get::>()?.0, }, x => bail!("corrupt block type ({})", x), }) } } impl Block { pub fn is_literal(&self) -> bool { matches!(self, Block::Literal(..)) } }