use crate::{ format::ser::{ConstSizeSerExt, Sink, Small, Source}, helpers::vector::Vec2, helpers::{matrix::Mat2, pixel::Pixel}, }; use anyhow::bail; #[derive(Clone, Debug)] pub enum Block { Literal(Vec), Split(Box<[Block; 2]>), Reference { translation: Vec2 }, AdvancedReference(AdvancedReference), } #[derive(Clone, Debug)] pub struct AdvancedReference { pub translation: Vec2, pub transform: Mat2, pub value_scale: i8, } impl Block { pub const REFZERO: Block = Block::Reference { translation: Vec2::::ZERO, }; pub fn write(&self, sink: &mut impl std::io::Write, size: Vec2) -> anyhow::Result<()> { match &self { Block::Literal(pixels) => { sink.put(0u8)?; pixels.write_const_size(sink, size.area() as usize)?; } Block::Split(box [a, b]) => { sink.put(1u8)?; let [asize, bsize] = split_size(size); a.write(sink, asize)?; b.write(sink, bsize)?; } Block::Reference { translation } => { sink.put(2u8)?; sink.put(Small(*translation))?; } Block::AdvancedReference(AdvancedReference { translation, transform, value_scale, }) => { sink.put(3u8)?; sink.put((*translation, *transform, *value_scale))?; } } Ok(()) } pub fn read(source: &mut impl std::io::Read, size: Vec2) -> anyhow::Result { Ok(match source.get::()? { 0 => Block::Literal(Vec::read_const_size(source, size.area() as usize)?), 1 => Block::Split(Box::new({ let [asize, bsize] = split_size(size); let a = Block::read(source, asize)?; let b = Block::read(source, bsize)?; [a, b] })), 2 => Block::Reference { translation: source.get::>>()?.0, }, 3 => Block::AdvancedReference(AdvancedReference { translation: source.get()?, transform: source.get()?, value_scale: source.get()?, }), x => bail!("corrupt block type ({})", x), }) } } pub fn split_size(size: Vec2) -> [Vec2; 2] { let vert = size.x > size.y; [ if vert { (size.x / 2, size.y).into() } else { (size.x, size.y / 2).into() }, if vert { (size.x - size.x / 2, size.y).into() } else { (size.x, size.y - size.y / 2).into() }, ] } impl Block { pub fn is_literal(&self) -> bool { matches!(self, Block::Literal(..)) } pub fn is_ref_without_translation(&self) -> bool { matches!( self, Block::Reference { translation: Vec2::::ZERO } ) } } impl Default for AdvancedReference { fn default() -> Self { Self { translation: Vec2 { x: 0, y: 0 }, transform: Mat2 { a: 4, b: 0, c: 0, d: 4, }, value_scale: 4, // so 1 } } }