mod monochrome;
mod greyscale;
pub use monochrome::MonochromeImage;
pub use greyscale::GreyscaleImage;
use core::cell::RefCell;
use core::marker::PhantomData;
use ufmt::{Formatter, uWrite};
use avr_oxide::io::Read;
use avr_oxide::util::persist::derive::Persist;
use avr_oxide::util::persist::Persist;
use avrox_display::{GfxError, GfxResult};
use avrox_storage::{FileAddr, Seek, SeekFrom};
use avr_oxide::OxideResult::{Err,Ok};
#[allow(dead_code)]
pub enum LayerType {
Monochrome,
Greyscale8bit,
RGBA4bit,
RGBA8bit
}
#[derive(Clone)]
pub struct ImageFile<FILE>
where
FILE: Read + Seek
{
source: RefCell<FILE>,
header: Header
}
pub trait ImageBlock {
const SIZE: usize;
fn empty() -> Self;
fn load_from<FILE: Read>(&mut self, source: &mut FILE) -> GfxResult<()>;
}
#[derive(Clone)]
struct CachedBlock<BT,FILE>
where
BT: ImageBlock,
FILE: Read + Seek
{
number: usize,
data: BT,
phantom_file: PhantomData<FILE>
}
#[repr(C,align(1))]
#[derive(Persist,Clone)]
#[persist(magicnumber = 0x54)]
#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
pub struct Header {
tag: [u8; 3],
version: u8,
width: u16,
height: u16,
layer_offsets: [u32; LayerType::count()]
}
impl LayerType {
pub const fn to_layer_index(&self) -> usize {
match self {
LayerType::Monochrome => 0,
LayerType::Greyscale8bit => 1,
LayerType::RGBA4bit => 2,
LayerType::RGBA8bit => 3
}
}
pub const fn count() -> usize {
4
}
}
#[cfg(target_arch="avr")]
impl<FILE> ufmt::uDebug for ImageFile<FILE>
where
FILE: Read + Seek
{
fn fmt<W>(&self, out: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized {
out.write_str("ImageFile { header=")?;
self.header.fmt(out)?;
out.write_str("}")
}
}
impl<BT,FILE> CachedBlock<BT,FILE>
where
BT: ImageBlock,
FILE: Read + Seek
{
fn empty() -> Self {
CachedBlock {
number: usize::MAX,
data: BT::empty(),
phantom_file: PhantomData::default()
}
}
fn set_and_load(&mut self, number: usize, source: &mut FILE, base_addr: u32) -> GfxResult<()> {
if number != self.number {
source.seek(SeekFrom::Start((base_addr + ((number * BT::SIZE) as u32)) as FileAddr))?;
self.data.load_from::<FILE>(source)?;
self.number = number;
Ok(())
} else {
Ok(())
}
}
}
impl<FILE> ImageFile<FILE>
where
FILE: Read + Seek {
pub fn with_file(mut source: FILE) -> GfxResult<Self> {
source.rewind()?;
let header = Header::load_from(&mut source)?;
if header.tag == *b"BFG" {
Ok(ImageFile{
source: RefCell::new(source),
header
})
} else {
Err(GfxError::FileFormat)
}
}
pub fn get_header(&self) -> Header {
self.header.clone()
}
}
#[cfg(test)]
mod test {
use avr_oxide::io::Read;
use avrox_display::gfx::img::tbff::{ImageFile, MonochromeImage};
use avrox_display::gfx::pixels::Monochromatic;
use avrox_display::gfx::{fills, Point};
use avrox_display::gfx::primitives::{ConstScaleUp, HorizontalPair, Overlay, position};
use avrox_display::gfx::sevenseg::SevenSegmentDisplay;
use avrox_display::gfx::test::MonochromeTestRenderer;
use avrox_storage::{FileAddr, FileOffset, Seek, SeekFrom};
use avr_oxide::OxideResult::{Ok,Err};
use avrox_display::gfx::fills::SolidFill;
pub struct TestFile {
pub offset: usize,
pub data: &'static [u8]
}
impl Read for TestFile {
fn read(&mut self, buf: &mut [u8]) -> avr_oxide::io::Result<usize> {
buf[0] = self.data[self.offset];
self.offset += 1;
Ok(1)
}
}
impl Seek for TestFile {
fn seek(&mut self, pos: SeekFrom) -> avr_oxide::io::Result<FileAddr> {
let new_offset = match pos {
SeekFrom::Start(addr) => { addr as usize},
SeekFrom::End(delta) => { (((self.data.len()-1) as FileOffset) + delta) as usize },
SeekFrom::Current(delta) => { ((self.offset as FileOffset) + delta) as usize }
};
self.offset = new_offset;
Ok(self.offset as FileAddr)
}
fn stream_position(&mut self) -> avr_oxide::io::Result<FileAddr> {
Ok(self.offset as FileAddr)
}
}
#[should_panic]
#[test]
fn test_open_bad_header() {
let mut test_file = TestFile {
offset: 0,
data: &[ b'X', b'X', b'X', b'F',
0x02,
0x00,0x0A, 0x00,0x0A, 0x00,0x00,0x00,0x19, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
};
let image = ImageFile::with_file(test_file).unwrap();
}
}