use crate::*;
pub struct PixelIter<'a> {
vid: &'a VideoChip,
x: u16, y: u16,
wrap_bg: bool,
subpixel_index: u8,
fg_palette: [Color9Bit; COLORS_PER_PALETTE as usize],
bg_palette: [Color9Bit; COLORS_PER_PALETTE as usize],
local_palettes: [[ColorID; COLORS_PER_TILE as usize]; LOCAL_PALETTE_COUNT as usize],
current_bg_flags: TileFlags, bg_color: Color9Bit, bg_cluster: Cluster<2>, scanline: &'a [Cluster<4>], force_bg_color: bool, }
pub struct ScreenCoords {
pub x: i32,
pub y: i32,
}
impl<'a> PixelIter<'a> {
pub fn new(vid: &'a VideoChip) -> Self {
let relative_y = vid.crop_y as usize;
let mut result = Self {
vid,
x: 0,
y: 0,
wrap_bg: vid.wrap_bg,
current_bg_flags: TileFlags::default(),
bg_cluster: Cluster::default(),
subpixel_index: 0,
bg_color: vid.bg_palette[vid.bg_color.id()],
fg_palette: vid.fg_palette.clone(),
bg_palette: vid.bg_palette.clone(),
local_palettes: vid.local_palettes.clone(),
force_bg_color: false,
scanline: &vid.scanlines[relative_y],
};
result.force_bg_color = !result.wrap_bg && result.is_outside();
if !result.force_bg_color {
result.update_bg_cluster();
let bg_x = (result.x as i16 + result.vid.scroll_x as i16 + vid.crop_x as i16)
.rem_euclid(BG_WIDTH as i16) as u16;
let tile_x = bg_x % TILE_SIZE as u16;
let local_idx = tile_x as usize % SUBPIXELS_TILE as usize;
result.subpixel_index = local_idx as u8;
}
result
}
#[inline]
fn update_bg_cluster(&mut self) {
let bg_x = (self.x as i16 + self.vid.scroll_x as i16 + self.vid.crop_x as i16)
.rem_euclid(BG_WIDTH as i16) as u16;
let bg_y = (self.y as i16 + self.vid.scroll_y as i16 + self.vid.crop_y as i16)
.rem_euclid(BG_HEIGHT as i16) as u16;
let bg_col = bg_x / TILE_SIZE as u16;
let bg_row = bg_y / TILE_SIZE as u16;
let bg_map_index = (bg_row as usize * BG_COLUMNS as usize) + bg_col as usize;
let current_bg_tile_id = self.vid.bg_map.tiles[bg_map_index].0;
self.current_bg_flags = self.vid.bg_map.flags[bg_map_index];
let tile_x = (bg_x % TILE_SIZE as u16) as u8;
let tile_y = (bg_y % TILE_SIZE as u16) as u8;
let tile_entry = self.vid.tiles[current_bg_tile_id as usize];
let tile_start = tile_entry.cluster_index as usize;
let tile_clusters = &self.vid.tile_pixels[tile_start..tile_start + 8];
self.bg_cluster = Cluster::from_tile(tile_clusters, self.current_bg_flags, tile_y);
self.subpixel_index = tile_x % PIXELS_PER_CLUSTER;
}
#[inline]
fn get_pixel_color(&self) -> Color9Bit {
if self.current_bg_flags.is_fg() && !self.force_bg_color {
let bg_palette = self.current_bg_flags.palette().0 as usize;
let color = self.bg_cluster.get_subpixel(self.subpixel_index);
if color > 0 {
let global_idx = self.local_palettes[bg_palette][color as usize].0 as usize;
return self.bg_palette[global_idx];
}
}
let relative_x = (self.x as usize).saturating_add(self.vid.crop_x as usize);
let x_cluster = relative_x / PIXELS_PER_CLUSTER as usize;
let sub_index = (relative_x % PIXELS_PER_CLUSTER as usize) as u8;
let fg_pixel = {
let fg_cluster = self.scanline[x_cluster];
fg_cluster.get_subpixel(sub_index)
};
if fg_pixel > 0 {
self.fg_palette[fg_pixel as usize]
} else if self.force_bg_color {
self.bg_color
} else {
let color = self.bg_cluster.get_subpixel(self.subpixel_index);
if color == 0 {
self.bg_color
} else {
let bg_palette = self.current_bg_flags.palette().0 as usize;
let global_idx = self.local_palettes[bg_palette][color as usize].0 as usize;
self.bg_palette[global_idx]
}
}
}
#[inline(always)]
fn is_outside(&self) -> bool {
let raw_x = self.x as i16 + self.vid.scroll_x + self.vid.crop_x as i16;
let raw_y = self.y as i16 + self.vid.scroll_y + self.vid.crop_y as i16;
let w = BG_WIDTH as i16;
let h = BG_HEIGHT as i16;
raw_x < 0 || raw_y < 0 || raw_x >= w || raw_y >= h
}
}
impl<'a> Iterator for PixelIter<'a> {
type Item = (ColorRGB24, ScreenCoords);
fn next(&mut self) -> Option<Self::Item> {
if self.y == self.vid.max_y as u16 {
return None;
}
let is_outside_viewport = self.x < self.vid.view_left as u16
|| self.x >= self.vid.view_right as u16
|| self.y < self.vid.view_top as u16
|| self.y >= self.vid.view_bottom as u16;
let color = if is_outside_viewport {
ColorRGB24::from(self.bg_color)
} else {
ColorRGB24::from(self.get_pixel_color())
};
let result_coords = ScreenCoords {
x: self.x as i32,
y: self.y as i32,
};
self.x += 1;
self.subpixel_index += 1;
let mut reload_cluster = false;
if self.subpixel_index >= PIXELS_PER_CLUSTER {
reload_cluster = true;
self.subpixel_index = 0;
}
if self.x == self.vid.max_x as u16 {
self.x = 0;
self.y += 1;
if self.y < self.vid.max_y as u16 {
let relative_y = (self.y as usize).saturating_add(self.vid.crop_y as usize);
self.scanline = &self.vid.scanlines[relative_y];
reload_cluster = true;
}
}
if reload_cluster {
let was_outside = self.force_bg_color || self.x == 0;
self.force_bg_color = !self.wrap_bg && self.is_outside();
if !self.force_bg_color || was_outside {
self.update_bg_cluster();
}
}
Some((color, result_coords))
}
}