Files
renderbug/src/mappings.rs

359 lines
10 KiB
Rust

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<Virtual>, Coordinates<Self::Space>)>;
}
pub trait Select<'a> {
type Space: CoordinateSpace;
type View: CoordinateView<'a>;
fn select(&'a self, rect: &Rectangle<Virtual>) -> 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<LinearSpace>;
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<Virtual>) -> 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<const STRIDE_NUM: usize = 24> {
pub strides: [Stride; STRIDE_NUM],
pub pixel_count: usize,
pub size: Rectangle<StrideSpace>
}
impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
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<Rectangle<StrideSpace>> = 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<Virtual>) -> Self::View {
StrideView::new(self, rect)
}
}
#[derive(Debug, Clone, Copy)]
pub struct StrideSpace {}
impl CoordinateSpace for StrideSpace {
type Data = u8;
}
pub type StrideCoords = Coordinates<StrideSpace>;
pub struct StrideView<'a> {
pub map: &'a StrideMapping,
range: Rectangle<StrideSpace>,
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<Virtual>) -> Self {
// Zero-index shape of the pixel picking area
let range: Rectangle<StrideSpace> = 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<Virtual>, &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
}
}
}