use core::cell::RefCell;
use avr_oxide::io::Read;
use avr_oxide::oserror::OxideTryFrom;
use avr_oxide::OxideResult;
use avrox_display::gfx::img::tbff::{CachedBlock, ImageBlock, LayerType};
use avrox_display::gfx::pixels::Monochromatic;
use avrox_display::gfx::{Point, Renderable, RenderPlane, XCoord, YCoord};
use avrox_display::{GfxError, GfxResult};
use avrox_storage::Seek;
use avr_oxide::OxideResult::{Err,Ok};
use avrox_display::gfx::img::ImageFile;
#[derive(Clone)]
struct MonochromeBlock {
data: [u8; 8]
}
#[derive(Clone)]
pub struct MonochromeImage<FILE>
where
FILE: Read + Seek
{
source: RefCell<FILE>,
width: u16,
height: u16,
layer_offset: u32,
cached_block: RefCell<CachedBlock<MonochromeBlock,FILE>>
}
impl ImageBlock for MonochromeBlock {
const SIZE: usize = 8;
fn empty() -> Self {
MonochromeBlock {
data: [0x00; 8]
}
}
fn load_from<FILE: Read>(&mut self, source: &mut FILE) -> GfxResult<()>{
source.read_exact(&mut self.data)?;
Ok(())
}
}
impl MonochromeBlock {
fn is_pixel_set(&self, location: Point) -> bool {
let x = (location.0 % 8) as u8;
let y = (location.1 % 8) as usize;
if (self.data[y] & (0b1000_0000u8 >> x)) > 0 {
true
} else {
false
}
}
fn pixel_at(&self, location: Point) -> Monochromatic {
match self.is_pixel_set(location){
true => Monochromatic::WHITE,
false => Monochromatic::BLACK
}
}
}
impl<FILE> From<ImageFile<FILE>> for MonochromeImage<FILE>
where
FILE: Read + Seek
{
fn from(file: ImageFile<FILE>) -> Self {
Self::oxide_try_from(file).unwrap()
}
}
impl<FILE> OxideTryFrom<ImageFile<FILE>> for MonochromeImage<FILE>
where
FILE: Read + Seek
{
type Error = GfxError;
fn oxide_try_from(file: ImageFile<FILE>) -> OxideResult<Self, Self::Error> where Self: Sized {
if file.header.version >= 2 {
let layer_offset = file.header.layer_offsets[LayerType::Monochrome.to_layer_index()];
if layer_offset == 0 {
Err(GfxError::MissingLayer)
} else {
Ok(MonochromeImage {
source: file.source,
width: file.header.width,
height: file.header.height,
layer_offset: layer_offset,
cached_block: RefCell::new(CachedBlock::empty())
})
}
} else {
Err(GfxError::NotSupported)
}
}
}
impl<FILE> MonochromeImage<FILE>
where
FILE: Read + Seek
{
fn block_number_for_point(&self, location: Point) -> GfxResult<usize> {
let row = location.1 / 8;
let col = location.0 / 8;
let row_width = ((self.width-1) / 8) + 1;
Ok(((row_width * row) + col) as usize)
}
fn cache_block(&self, block_num: usize) -> GfxResult<()> {
let mut cached_block = self.cached_block.borrow_mut();
cached_block.set_and_load(block_num,
&mut self.source.borrow_mut(),
self.layer_offset)?;
Ok(())
}
fn get_pixel(&self, location: Point) -> GfxResult<Monochromatic> {
let block_num = self.block_number_for_point(location)?;
self.cache_block(block_num)?;
Ok(self.cached_block.borrow().data.pixel_at(location))
}
}
impl<FILE> Renderable for MonochromeImage<FILE>
where
FILE: Read + Seek {
type PIXEL = Monochromatic;
fn get_pixel_at<P: RenderPlane>(&self, coord: Point) -> GfxResult<Self::PIXEL> {
if (coord.0 < self.width) && (coord.1 < self.height) {
self.get_pixel(coord)
} else {
Err(GfxError::OutOfBounds)
}
}
fn get_dimensions<P: RenderPlane>(&self) -> GfxResult<(XCoord, YCoord)> {
Ok((self.width, self.height))
}
fn has_changes<P: RenderPlane>(&self) -> bool {
false
}
}
#[cfg(test)]
mod test {
use avrox_display::gfx::{fills, Point};
use avrox_display::gfx::fills::SolidFill;
use avrox_display::gfx::img::{ImageFile, MonochromeImage};
use avrox_display::gfx::img::tbff::test::TestFile;
use avrox_display::gfx::pixels::Monochromatic;
use avrox_display::gfx::primitives::{ConstScaleUp, HorizontalPair, Overlay, position};
use avrox_display::gfx::sevenseg::SevenSegmentDisplay;
use avrox_display::gfx::test::MonochromeTestRenderer;
#[test]
fn test_open_monochrome_file() {
let mut test_file = TestFile {
offset: 0,
data: &[ b'T', b'B', b'F', b'G',
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();
let monochrome = MonochromeImage::from(image);
}
#[test]
#[should_panic]
fn test_open_monochrome_file_fail() {
let mut test_file = TestFile {
offset: 0,
data: &[ b'T', b'B', b'F', b'G',
0x02,
0x00,0x0A, 0x00,0x0A, 0x00,0x00,0x00,0x00, 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,
]
};
let image = ImageFile::with_file(test_file).unwrap();
let monochrome = MonochromeImage::from(image);
}
#[test]
fn test_monochrome_pixel_routines() {
let mut test_file = TestFile {
offset: 0,
data: &[ b'T', b'B', b'F', b'G',
0x02,
0x00,0x0A, 0x00,0x0A, 0x00,0x00,0x00,0x19, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
};
let image : MonochromeImage<TestFile> = ImageFile::with_file(test_file).unwrap().into();
println!("Addr (0,0) == {:?}", image.get_pixel(Point(0,0)));
println!("Addr (8,0) == {:?}", image.get_pixel(Point(8,0)));
println!("Addr (0,8) == {:?}", image.get_pixel(Point(0,8)));
println!("Addr (8,8) == {:?}", image.get_pixel(Point(8,8)));
assert_eq!(image.get_pixel(Point(0,0)).unwrap(), Monochromatic::WHITE);
assert_eq!(image.get_pixel(Point(8,0)).unwrap(), Monochromatic::BLACK);
assert_eq!(image.get_pixel(Point(0,8)).unwrap(), Monochromatic::WHITE);
assert_eq!(image.get_pixel(Point(8,8)).unwrap(), Monochromatic::BLACK);
}
#[test]
fn test_monochrome_rendering1() {
let mut test_file = TestFile {
offset: 0,
data: &[ b'T', b'B', b'F', b'G',
0x02,
0x00,0x0A, 0x00,0x0A, 0x00,0x00,0x00,0x19, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
};
let image = MonochromeImage::from(ImageFile::with_file(test_file).unwrap());
let renderer = MonochromeTestRenderer::new();
let scene = Overlay::new(
image,
fills::crosshatch_fill());
renderer.render_scene(&scene);
}
#[test]
fn test_monochrome_rendering2() {
let mut test_file = TestFile {
offset: 0,
data: &[
0x54u8, 0x42u8, 0x46u8, 0x47u8,
0x02,
0x0u8, 0x1fu8,
0x0u8, 0x10u8,
0x00,0x00,0x00,0x19, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x2u8, 0x7u8, 0x3du8, 0x30u8, 0x3fu8, 0x7fu8, 0xdeu8, 0x4fu8, 0x40u8, 0xe8u8, 0xbcu8, 0xeu8, 0xe4u8, 0xf6u8, 0x7bu8, 0xe2u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x4du8, 0x4du8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x8u8, 0xdeu8, 0x8u8, 0x4fu8, 0xceu8, 0x7fu8, 0x20u8, 0x70u8, 0x3cu8, 0x7u8, 0x2u8, 0xf6u8, 0x77u8, 0x7eu8, 0x4u8, 0xeu8, 0x3cu8, 0xe8u8, 0x40u8, 0x4du8, 0x7cu8, 0x21u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0xe8u8, 0x6eu8, 0xc0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, ]
};
let image = MonochromeImage::from(ImageFile::with_file(test_file).unwrap());
let renderer = MonochromeTestRenderer::new();
let scene = Overlay::new(
image,
fills::crosshatch_fill());
renderer.render_scene(&scene);
}
#[test]
fn test_monochrome_rendering3() {
let mut test_file = TestFile {
offset: 0,
data: &[
0x54u8, 0x42u8, 0x46u8, 0x47u8,
0x02,
0x0u8, 0x21u8,
0x0u8, 0x10u8,
0x00,0x00,0x00,0x19, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x0u8, 0x0u8, 0x0u8, 0x7u8, 0xfu8, 0xfu8, 0xdu8, 0x1du8, 0x0u8, 0x0u8, 0x0u8, 0x38u8, 0xb8u8, 0x9cu8, 0xdcu8, 0xceu8, 0x0u8, 0x0u8, 0x0u8, 0x73u8, 0x77u8, 0xe7u8, 0xeeu8, 0xcfu8, 0x4u8, 0x1u8, 0x1u8, 0xf4u8, 0xf8u8, 0x38u8, 0x78u8, 0xf0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x18u8, 0x3fu8, 0x3fu8, 0x30u8, 0x70u8, 0x0u8, 0x0u8, 0x0u8, 0xefu8, 0xe7u8, 0xf7u8, 0x77u8, 0x33u8, 0x3u8, 0x0u8, 0x0u8, 0xdfu8, 0xddu8, 0x9du8, 0xb8u8, 0x38u8, 0x0u8, 0x0u8, 0x0u8, 0xc0u8, 0xc0u8, 0xc0u8, 0xc0u8, 0xe0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, 0x0u8, ]
};
let image = MonochromeImage::from(ImageFile::with_file(test_file).unwrap());
let renderer = MonochromeTestRenderer::new();
let counter = ConstScaleUp::<2,2,_>::new(SevenSegmentDisplay::<5,16,_,_>::new(Monochromatic::WHITE, Monochromatic::BLACK, Some(0xf00du32)));
let scene = Overlay::new(
HorizontalPair::<_,_,position::Beginning>::new(image, counter),
SolidFill::new(Monochromatic::BLACK)
);
renderer.render_scene(&scene);
}
}