use crate::{TILE_SIZE, TileFlags, err};
pub const PIXELS_PER_CLUSTER: u8 = 8;
#[derive(Clone, Copy)]
pub struct Cluster<const BITS_PER_PIXEL: usize> {
data: [u8; BITS_PER_PIXEL],
}
impl<const BITS_PER_PIXEL: usize> Default for Cluster<BITS_PER_PIXEL> {
fn default() -> Self {
assert!(
BITS_PER_PIXEL > 0 && BITS_PER_PIXEL < 9,
err!("Invalid BITS_PER_PIXEL")
);
Self {
data: [0; BITS_PER_PIXEL],
}
}
}
impl<const BITS_PER_PIXEL: usize> Cluster<BITS_PER_PIXEL> {
pub const PIXELS_PER_BYTE: usize = PIXELS_PER_CLUSTER as usize / BITS_PER_PIXEL;
pub const MASK: u8 = (1 << BITS_PER_PIXEL) - 1;
#[inline(always)]
pub fn get_subpixel(&self, index: u8) -> u8 {
debug_assert!(
index < PIXELS_PER_CLUSTER,
err!("Pixel index out of bounds")
);
let byte_idx = index as usize / Self::PIXELS_PER_BYTE;
let pos_in_byte = index as usize % Self::PIXELS_PER_BYTE;
let shift = (Self::PIXELS_PER_BYTE - 1 - pos_in_byte) * BITS_PER_PIXEL;
(self.data[byte_idx] >> shift) & Self::MASK
}
#[inline(always)]
pub fn set_subpixel(&mut self, value: u8, index: u8) {
debug_assert!(
index < PIXELS_PER_CLUSTER,
err!("Pixel index out of bounds")
);
let value = value & Self::MASK;
let byte_idx = index as usize / Self::PIXELS_PER_BYTE;
let pos_in_byte = index as usize % Self::PIXELS_PER_BYTE;
let shift = (Self::PIXELS_PER_BYTE - 1 - pos_in_byte) * BITS_PER_PIXEL;
let mask = Self::MASK << shift;
self.data[byte_idx] &= !mask;
self.data[byte_idx] |= value << shift;
}
#[inline]
pub fn flip(&self) -> Self {
let mut flipped = Self {
data: [0; BITS_PER_PIXEL],
};
let mut left_pixel = 0;
let mut right_pixel = PIXELS_PER_CLUSTER - 1;
while left_pixel < right_pixel {
let left_byte = left_pixel as usize / Self::PIXELS_PER_BYTE;
let left_pos = left_pixel as usize % Self::PIXELS_PER_BYTE;
let left_shift = (Self::PIXELS_PER_BYTE - 1 - left_pos) * BITS_PER_PIXEL;
let right_byte = right_pixel as usize / Self::PIXELS_PER_BYTE;
let right_pos = right_pixel as usize % Self::PIXELS_PER_BYTE;
let right_shift = (Self::PIXELS_PER_BYTE - 1 - right_pos) * BITS_PER_PIXEL;
let left_val = (self.data[left_byte] >> left_shift) & Self::MASK;
let right_val = (self.data[right_byte] >> right_shift) & Self::MASK;
let left_mask = Self::MASK << left_shift;
flipped.data[left_byte] &= !left_mask;
flipped.data[left_byte] |= right_val << left_shift;
let right_mask = Self::MASK << right_shift;
flipped.data[right_byte] &= !right_mask;
flipped.data[right_byte] |= left_val << right_shift;
left_pixel += 1;
right_pixel -= 1;
}
flipped
}
#[inline]
pub fn from_tile(tile_pixels: &[Cluster<BITS_PER_PIXEL>], flags: TileFlags, row: u8) -> Self {
debug_assert!(row < 8, "Row index out of bounds");
if flags.is_rotated() {
let mut rotated = Self::default();
const HIGH: u8 = TILE_SIZE - 1;
let col = if flags.is_flipped_y() {
row
} else {
HIGH - row
};
let start_row = if flags.is_flipped_x() {
HIGH
} else {
0
};
let row_step = if flags.is_flipped_x() { -1_i8 } else { 1_i8 };
for i in 0..TILE_SIZE {
let src_row = (start_row as i8 + (i as i8 * row_step)) as u8 & 7;
let src_cluster = tile_pixels[src_row as usize];
let pixel_value = src_cluster.get_subpixel(col);
rotated.set_subpixel(pixel_value, i);
}
rotated
} else {
if !flags.is_flipped_x() && !flags.is_flipped_y() {
return tile_pixels[row as usize];
}
let source_row = if flags.is_flipped_y() { 7 - row } else { row };
let source_cluster = tile_pixels[source_row as usize];
if flags.is_flipped_x() {
source_cluster.flip()
} else {
source_cluster
}
}
}
}
impl<const BYTES: usize> core::fmt::Debug for Cluster<BYTES> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self.data)
}
}
impl<const BYTES: usize> core::fmt::Display for Cluster<BYTES> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self.data)
}
}