pub mod palette;
#[cfg(feature = "dev-lcdc")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-lcdc")))]
pub mod lcd;
#[cfg(feature = "dev-nes-ppu")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-nes-ppu")))]
pub mod nes;
#[cfg(feature = "dev-pc-video")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-video")))]
pub mod pc;
#[cfg(feature = "dev-sms")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-sms")))]
pub mod sms;
#[cfg(feature = "display-png")]
#[cfg_attr(docsrs, doc(cfg(feature = "display-png")))]
pub mod png;
#[cfg(test)]
mod tests;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct PixelFormat(pub u16);
impl PixelFormat {
pub const RGBA8888: PixelFormat = PixelFormat(0);
pub const BGRA8888: PixelFormat = PixelFormat(1);
pub const RGB888: PixelFormat = PixelFormat(2);
#[inline]
#[must_use]
pub const fn bytes_per_pixel(self) -> u64 {
match self {
PixelFormat::RGB888 => 3,
_ => 4,
}
}
#[inline]
#[must_use]
pub const fn has_alpha(self) -> bool {
matches!(self, PixelFormat::RGBA8888 | PixelFormat::BGRA8888)
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
PixelFormat::RGBA8888 => "rgba8888",
PixelFormat::BGRA8888 => "bgra8888",
PixelFormat::RGB888 => "rgb888",
_ => "unknown",
}
}
#[inline]
const fn channel_offsets(self) -> [usize; 3] {
match self {
PixelFormat::BGRA8888 => [2, 1, 0],
_ => [0, 1, 2],
}
}
}
impl fmt::Display for PixelFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SurfaceInfo {
pub width: u32,
pub height: u32,
pub preferred_format: PixelFormat,
}
impl SurfaceInfo {
#[must_use]
pub const fn new(width: u32, height: u32, format: PixelFormat) -> SurfaceInfo {
SurfaceInfo {
width,
height,
preferred_format: format,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Surface {
format: PixelFormat,
width: u32,
height: u32,
stride: u64,
pixels: Vec<u8>,
serial: u64,
}
impl Surface {
#[must_use]
pub fn new(format: PixelFormat, width: u32, height: u32) -> Surface {
let stride = u64::from(width) * format.bytes_per_pixel();
let len = stride * u64::from(height);
Surface {
format,
width,
height,
stride,
pixels: vec![0u8; len as usize],
serial: 0,
}
}
#[must_use]
pub const fn empty() -> Surface {
Surface {
format: PixelFormat::RGBA8888,
width: 0,
height: 0,
stride: 0,
pixels: Vec::new(),
serial: 0,
}
}
#[must_use]
pub fn for_scanout(scanout: &dyn Scanout) -> Surface {
let info = scanout.info();
Surface::new(info.preferred_format, info.width, info.height)
}
#[inline]
#[must_use]
pub const fn format(&self) -> PixelFormat {
self.format
}
#[inline]
#[must_use]
pub const fn width(&self) -> u32 {
self.width
}
#[inline]
#[must_use]
pub const fn height(&self) -> u32 {
self.height
}
#[inline]
#[must_use]
pub const fn stride(&self) -> u64 {
self.stride
}
#[inline]
#[must_use]
pub fn len(&self) -> u64 {
self.pixels.len() as u64
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.pixels.is_empty()
}
#[must_use]
pub const fn info(&self) -> SurfaceInfo {
SurfaceInfo::new(self.width, self.height, self.format)
}
#[inline]
#[must_use]
pub const fn serial(&self) -> u64 {
self.serial
}
#[inline]
pub const fn set_serial(&mut self, serial: u64) {
self.serial = serial;
}
#[inline]
#[must_use]
pub fn pixels(&self) -> &[u8] {
&self.pixels
}
#[inline]
pub fn pixels_mut(&mut self) -> &mut [u8] {
&mut self.pixels
}
#[inline]
#[must_use]
pub fn as_ptr(&self) -> *const u8 {
self.pixels.as_ptr()
}
pub fn reshape(&mut self, format: PixelFormat, width: u32, height: u32) {
let stride = u64::from(width) * format.bytes_per_pixel();
let len = stride * u64::from(height);
if self.format == format && self.width == width && self.height == height {
return;
}
self.format = format;
self.width = width;
self.height = height;
self.stride = stride;
self.pixels.resize(len as usize, 0);
self.pixels.shrink_to_fit();
}
pub fn fill(&mut self, rgb: [u8; 3]) {
let bpp = self.format.bytes_per_pixel() as usize;
let offsets = self.format.channel_offsets();
let alpha = self.format.has_alpha();
for pixel in self.pixels.chunks_exact_mut(bpp) {
pixel[offsets[0]] = rgb[0];
pixel[offsets[1]] = rgb[1];
pixel[offsets[2]] = rgb[2];
if alpha {
pixel[3] = 0xff;
}
}
}
#[inline]
pub fn put(&mut self, x: u32, y: u32, rgb: [u8; 3]) {
let Some(offset) = self.offset_of(x, y) else {
return;
};
let offsets = self.format.channel_offsets();
let pixel = &mut self.pixels[offset..];
pixel[offsets[0]] = rgb[0];
pixel[offsets[1]] = rgb[1];
pixel[offsets[2]] = rgb[2];
if self.format.has_alpha() {
pixel[3] = 0xff;
}
}
#[inline]
#[must_use]
pub fn get(&self, x: u32, y: u32) -> Option<[u8; 3]> {
let offset = self.offset_of(x, y)?;
let offsets = self.format.channel_offsets();
let pixel = &self.pixels[offset..];
Some([pixel[offsets[0]], pixel[offsets[1]], pixel[offsets[2]]])
}
#[must_use]
pub fn row(&self, y: u32) -> Option<&[u8]> {
if y >= self.height {
return None;
}
let start = (u64::from(y) * self.stride) as usize;
let end = start + self.stride as usize;
Some(&self.pixels[start..end])
}
#[must_use]
pub fn hash(&self) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for b in &self.pixels {
h ^= u64::from(*b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
#[inline]
fn offset_of(&self, x: u32, y: u32) -> Option<usize> {
if x >= self.width || y >= self.height {
return None;
}
let bpp = self.format.bytes_per_pixel();
Some((u64::from(y) * self.stride + u64::from(x) * bpp) as usize)
}
}
pub trait Scanout: Send + Sync + fmt::Debug {
fn info(&self) -> SurfaceInfo;
fn frame_counter(&self) -> u64;
fn frame_period_ns(&self) -> u64 {
0
}
fn capture(&self, dst: &mut Surface) -> u64;
}