359 lines
10 KiB
Rust
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
|
|
}
|
|
}
|
|
} |