use crate::buffers::Pixbuf; use crate::geometry::*; use crate::lib8::interpolate::scale8; use crate::render::PixelView; use core::cmp::{max, min}; use core::fmt::{Formatter, Debug}; pub trait CoordinateView<'a>: Debug { type Space: CoordinateSpace; fn next(&mut self) -> Option<(Coordinates, Coordinates)>; } pub trait Select<'a> { type Space: CoordinateSpace; type View: CoordinateView<'a>; fn select(&'a self, rect: &Rectangle) -> Self::View; } #[derive(Debug)] pub struct LinearCoordView { max_x: u8, idx: usize, } pub struct LinearSpace {} impl CoordinateSpace for LinearSpace { type Data = usize; } pub type LinearCoords = Coordinates; impl<'a> CoordinateView<'a> for LinearCoordView { type Space = LinearSpace; fn next(&mut self) -> Option<(VirtualCoordinates, LinearCoords)> { if self.idx as u8 == self.max_x { None } else { let virt = VirtualCoordinates::new(self.idx as u8, 0); // FIXME: scale8 let phys = LinearCoords::new( self.idx as usize, 0 ); self.idx += 1; return Some((virt, phys)) } } } pub struct LinearPixelMapping { } impl LinearPixelMapping { pub fn new() -> Self { Self {} } } impl<'a> Select<'a> for LinearPixelMapping { type Space = LinearSpace; type View = LinearCoordView; fn select(&'a self, rect: &Rectangle) -> Self::View { LinearCoordView { max_x: rect.bottom_right.x, idx: 0, } } } #[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] pub struct Stride { pub length: u8, pub x: u8, pub y: u8, pub reverse: bool, pub physical_idx: usize } impl Stride { pub const fn pixel_idx_for_offset(&self, offset: u8) -> usize { if self.reverse { self.physical_idx + (self.length + self.y - 1 - offset) as usize } else { self.physical_idx + offset as usize } } } #[derive(Debug)] pub struct StrideMapping { pub strides: [Stride; STRIDE_NUM], pub pixel_count: usize, pub size: Rectangle } impl StrideMapping { pub fn new() -> Self { Self::from_json(&[ (0, 0, 255, false) ]) } pub fn new_fairylights() -> Self { Self::from_json(&[ (0, 0, 50, false) ]) } pub fn new_cyberplague() -> Self { Self::from_json(&[ (0, 6, 6, false), (1, 6, 6, true), (2, 6, 6, false), (3, 4, 9, true), (4, 4, 14, false), (5, 0, 17, true), (6, 2, 12, false), (7, 0, 18, true), (8, 4, 14, false), (9, 5, 9, true), (10, 4, 7, false), (11, 5, 6, true), (12, 5, 6, false) ]) } pub fn new_jar() -> Self { Self::from_json(&[ (0, 0, 17, false), (1, 0, 17, false), (2, 0, 17, false), (3, 0, 17, false), (4, 0, 16, false), (5, 0, 17, false), (6, 0, 17, false), (7, 0, 17, false), (8, 0, 17, false), (9, 0, 17, false), (10, 0, 17, false), (11, 0, 17, false), (12, 0, 18, false), (13, 0, 17, false), (14, 0, 18, false), (15, 0, 17, false), (16, 0, 17, false), (17, 0, 17, false) ]) } pub fn new_panel() -> Self { Self::from_json(&[ (0, 0, 16, false), (1, 0, 16, true), (2, 0, 16, false), (3, 0, 16, true), (4, 0, 16, false), (5, 0, 16, true), (6, 0, 16, false), (7, 0, 16, true), (8, 0, 16, false), (9, 0, 16, true), (10, 0, 16, false), (11, 0, 16, true), (12, 0, 16, false), (13, 0, 16, true), (14, 0, 16, false), (15, 0, 16, true), ]) } pub fn new_albus() -> Self { Self::from_json(&[ (0, 0, 50 * 3, false) ]) } pub fn from_json(stride_json: &[(u8, u8, u8, bool)]) -> Self { let mut strides = [Stride::default(); STRIDE_NUM]; let stride_count = stride_json.len(); let mut physical_idx = 0; let mut size: Option> = None; for stride_idx in 0..stride_count { let json_data = stride_json[stride_idx]; let x = json_data.0; let y = json_data.1; let length = json_data.2; let reverse = json_data.3; strides[stride_idx] = Stride { length, x, y, reverse, physical_idx }; physical_idx += length as usize; size = Some(match size.take() { None => Rectangle::new( Coordinates::new(x, y), Coordinates::new(x, y + length - 1), ), Some(s) => Rectangle::new( Coordinates::new( min(s.top_left.x, x), min(s.top_left.y, y) ), Coordinates::new( max(s.bottom_right.x, x), max(s.bottom_right.y, y + length - 1) ) ) }); log::info!("stride={:?} size={:?}", strides[stride_idx], size); } let s = size.take().unwrap(); log::info!("size={:?}", s); Self { strides, pixel_count: physical_idx, size: s } } } impl<'a> Select<'a> for StrideMapping { type Space = StrideSpace; type View = StrideView<'a>; fn select(&'a self, rect: &Rectangle) -> Self::View { StrideView::new(self, rect) } } #[derive(Debug, Clone, Copy)] pub struct StrideSpace {} impl CoordinateSpace for StrideSpace { type Data = u8; } pub type StrideCoords = Coordinates; pub struct StrideView<'a> { pub map: &'a StrideMapping, range: Rectangle, cur: StrideCoords, step_size: VirtualCoordinates } impl<'a> Debug for StrideView<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("StrideView") .field("range", &self.range) .field("step", &self.step_size) .field("cur", &self.cur).finish() } } impl<'a> StrideView<'a> { fn new(map: &'a StrideMapping, rect: &Rectangle) -> Self { // Zero-index shape of the pixel picking area let range: Rectangle = Rectangle::new( Coordinates::new( scale8(map.size.width() as u8, rect.top_left.x) + map.size.left() as u8, scale8(map.size.height() as u8, rect.top_left.y) + map.size.top() as u8 ), Coordinates::new( scale8(map.size.width() as u8, rect.bottom_right.x) + map.size.left() as u8, scale8(map.size.height() as u8, rect.bottom_right.y) + map.size.top() as u8 ) ); //log::info!("rect={:?} map.size={:?} range={:?}", rect, map.size, range); debug_assert!( range.bottom_right.x <= map.size.width() as u8 && range.bottom_right.y <= map.size.height() as u8, "the range for this view is out of bounds range={:?} rect={:?}, map_size={:?}", range, rect, (map.size.width(), map.size.height()) ); let step_size = VirtualCoordinates::new( u8::MAX / core::cmp::max(1, range.width()), u8::MAX / core::cmp::max(1, range.height()) //scale8(255, std::cmp::max(1, range.bottom_right.x - range.top_left.x)), //scale8(255, std::cmp::max(1, range.bottom_right.y - range.top_left.y)) ); debug_assert_ne!(step_size.x, 0); debug_assert_ne!(step_size.y, 0); return Self { map, range, step_size, cur: range.top_left }; } } impl<'a> CoordinateView<'a> for StrideView<'a> { type Space = StrideSpace; fn next(&mut self) -> Option<(VirtualCoordinates, StrideCoords)> { // Keep scanning until we reach the far right of the range while self.cur.x <= self.range.bottom_right.x { debug_assert!((self.cur.x as usize) < self.map.strides.len(), "stride out of bounds {:?}", self); let cur_stride: &Stride = &self.map.strides[self.cur.x as usize]; // Skip ahead to the top of the current stride if we are starting from higher above. if self.cur.y < cur_stride.y { self.cur.y = cur_stride.y; } // If we are at the bottom of our rectangle, or our stride, go to the next stride. if self.cur.y > self.range.bottom_right.y || self.cur.y > cur_stride.y + cur_stride.length - 1 { self.cur.x += 1; self.cur.y = self.range.top_left.y; continue; } // By now, we must be safely somewhere inside our current stride debug_assert!(self.cur.y <= cur_stride.y + cur_stride.length - 1, "coords={:?} out of bounds for stride={:?} view={:?}", self.cur, cur_stride, self); // Move to the next coord and return let physical_coords = self.cur; self.cur.y += 1; let virtual_coords = VirtualCoordinates::new( (physical_coords.x as u8).saturating_mul(self.step_size.x), (physical_coords.y as u8).saturating_mul(self.step_size.y) ); return Some((virtual_coords, physical_coords)); } None } } pub struct StrideSampler<'a, P: Pixbuf> { pixbuf: &'a mut P, selection: StrideView<'a> } impl<'a, P: Pixbuf> StrideSampler<'a, P> { pub fn new(pixbuf: &'a mut P, selection: StrideView<'a>) -> Self { StrideSampler { pixbuf, selection } } } impl<'a, P: Pixbuf> PixelView for StrideSampler<'a, P> { type Pixel = P::Pixel; fn next(&mut self) -> Option<(Coordinates, &mut Self::Pixel)> { if let Some((virt, coords)) = self.selection.next() { let idx = self.selection.map.strides[coords.x as usize].pixel_idx_for_offset(coords.y); Some((virt, &mut self.pixbuf[idx])) } else { None } } }