pub mod surface;
pub use surface::FrameBuffer;
use crate::core::PlatformFamily;
use crate::platform::stub::StubPlatform;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SurfaceGeometry {
pub width: u32,
pub height: u32,
pub stride: usize,
}
impl SurfaceGeometry {
pub const fn tight(width: u32, height: u32) -> Self {
Self { width, height, stride: width as usize * 4 }
}
}
pub fn copy_rows(src: &[u8], dst: &mut [u8], geometry: SurfaceGeometry) -> bool {
let (width, height, stride) =
(geometry.width as usize, geometry.height as usize, geometry.stride);
if width == 0 || height == 0 {
return true;
}
let row_bytes = width * 4;
if stride < row_bytes {
return false;
}
let Some(needed) = required_end(stride, height, row_bytes) else {
return false;
};
if src.len() < needed || dst.len() < needed {
return false;
}
for row in 0..height {
let start = row * stride;
dst[start..start + row_bytes].copy_from_slice(&src[start..start + row_bytes]);
}
true
}
pub(crate) fn required_end(stride: usize, height: usize, row_bytes: usize) -> Option<usize> {
if height == 0 {
return Some(0);
}
stride.checked_mul(height - 1)?.checked_add(row_bytes)
}
pub fn instance() -> &'static StubPlatform {
crate::platform::stub::stub_platform_singleton()
}
pub const FAMILY: PlatformFamily = PlatformFamily::Embedded;
pub const BACKEND_NAME: &str = "portable";
#[cfg(test)]
mod tests {
use super::*;
use crate::platform::types::Platform;
#[test]
fn tight_geometry_packs_rows() {
assert_eq!(SurfaceGeometry::tight(0, 0).stride, 0);
assert_eq!(SurfaceGeometry::tight(64, 48).stride, 64 * 4);
assert_eq!(SurfaceGeometry::tight(64, 48).width, 64);
assert_eq!(SurfaceGeometry::tight(64, 48).height, 48);
}
#[test]
fn instance_reports_the_portable_identity() {
let host = instance();
assert_eq!(host.backend_name(), BACKEND_NAME);
assert_eq!(host.family(), FAMILY);
}
#[test]
fn copy_rows_moves_a_tightly_packed_frame() {
let geometry = SurfaceGeometry::tight(2, 2);
let src: Vec<u8> = (0..16).collect();
let mut dst = vec![0u8; 16];
assert!(copy_rows(&src, &mut dst, geometry));
assert_eq!(dst, src);
}
#[test]
fn copy_rows_honours_a_padded_stride() {
let geometry = SurfaceGeometry { width: 2, height: 2, stride: 12 };
let src = vec![1u8; 12 * 2];
let mut dst = vec![0u8; 12 * 2];
assert!(copy_rows(&src, &mut dst, geometry));
assert_eq!(dst[..8], [1u8; 8]);
assert_eq!(dst[8..12], [0u8; 4], "padding must not be written");
assert_eq!(dst[16..20], [1u8; 4]);
}
#[test]
fn copy_rows_refuses_a_region_that_does_not_fit() {
let geometry = SurfaceGeometry::tight(4, 4);
let src = vec![0u8; 16];
let mut dst = vec![0u8; 64];
assert!(!copy_rows(&src, &mut dst, geometry), "short source must be refused");
let src = vec![0u8; 64];
let mut dst = vec![0u8; 16];
assert!(!copy_rows(&src, &mut dst, geometry), "short destination must be refused");
}
#[test]
fn copy_rows_refuses_a_stride_narrower_than_a_row() {
let geometry = SurfaceGeometry { width: 4, height: 1, stride: 8 };
let src = vec![0u8; 16];
let mut dst = vec![0u8; 16];
assert!(!copy_rows(&src, &mut dst, geometry), "overlapping rows must be refused");
}
#[test]
fn copy_rows_treats_a_zero_sized_surface_as_a_no_op() {
let geometry = SurfaceGeometry { width: 0, height: 8, stride: 0 };
assert!(copy_rows(&[], &mut [], geometry));
let geometry = SurfaceGeometry { width: 8, height: 0, stride: 32 };
assert!(copy_rows(&[], &mut [], geometry));
}
}