use crate::texel::MaxAligned;
use crate::{AsTexel, Texel};
use ::alloc::boxed::Box;
use core::{alloc, cmp};
mod matrix;
use crate::image::{Coord, ImageMut, ImageRef};
pub use crate::stride::{BadStrideError, StrideSpec, StridedBytes, StridedLayout, Strides};
pub struct Bytes(pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, Hash)]
pub struct TexelLayout {
size: usize,
align: usize,
}
pub trait Layout {
fn byte_len(&self) -> usize;
}
pub trait Decay<T>: Layout {
fn decay(from: T) -> Self;
}
impl<T: Layout> Decay<T> for Bytes {
fn decay(from: T) -> Bytes {
Bytes(from.byte_len())
}
}
pub trait Mend<From> {
type Into: Layout;
fn mend(self, from: &From) -> Self::Into;
}
pub trait TryMend<From> {
type Into: Layout;
type Err;
fn try_mend(self, from: &From) -> Result<Self::Into, Self::Err>;
}
pub trait Take: Layout {
fn take(&mut self) -> Self;
}
pub trait SliceLayout: Layout {
type Sample;
fn sample(&self) -> Texel<Self::Sample>;
fn len(&self) -> usize {
self.byte_len() / self.sample().size()
}
}
pub trait Raster<Pixel>: Layout + Sized {
fn dimensions(&self) -> Coord;
fn get(from: ImageRef<&Self>, at: Coord) -> Option<Pixel>;
}
pub trait RasterMut<Pixel>: Raster<Pixel> {
fn put(into: ImageMut<&mut Self>, at: Coord, val: Pixel);
fn shade(mut image: ImageMut<&mut Self>, mut f: impl FnMut(u32, u32, &mut Pixel)) {
let Coord(bx, by) = image.layout().dimensions();
for y in 0..by {
for x in 0..bx {
let mut pixel = Self::get(image.as_ref().as_deref(), Coord(x, y)).unwrap();
f(x, y, &mut pixel);
Self::put(image.as_mut().as_deref_mut(), Coord(x, y), pixel);
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct DynLayout {
pub(crate) repr: LayoutRepr,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum LayoutRepr {
Matrix(MatrixBytes),
Yuv420p(Yuv420p),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MatrixBytes {
pub(crate) element: TexelLayout,
pub(crate) first_dim: usize,
pub(crate) second_dim: usize,
}
pub struct Matrix<P> {
pub(crate) width: usize,
pub(crate) height: usize,
pub(crate) pixel: Texel<P>,
}
pub trait MatrixLayout: Layout {
fn matrix(&self) -> MatrixBytes;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct Yuv420p {
channel: TexelLayout,
width: u32,
height: u32,
}
#[derive(Debug, Default, PartialEq, Eq, Hash)]
pub struct MismatchedPixelError {
_private: (),
}
impl Bytes {
pub fn from_layout(layout: impl Layout) -> Self {
Bytes(layout.byte_len())
}
}
impl TexelLayout {
pub fn from_pixel<P: AsTexel>() -> Self {
let pix = P::texel();
TexelLayout {
size: pix.size(),
align: pix.align(),
}
}
pub const MAX_SIZE: Self = {
TexelLayout {
size: isize::MAX as usize,
align: 1,
}
};
pub fn with_layout(layout: alloc::Layout) -> Option<Self> {
if layout.align() > MaxAligned::texel().align() {
return None;
}
if layout.size() % layout.align() != 0 {
return None;
}
Some(TexelLayout {
size: layout.size(),
align: layout.align(),
})
}
pub fn layout(self) -> alloc::Layout {
alloc::Layout::from_size_align(self.size, self.align).expect("Valid layout")
}
#[must_use = "This does not modify `self`."]
pub fn packed(self, align: usize) -> TexelLayout {
assert!(align.is_power_of_two());
let align = self.align.min(align);
TexelLayout { align, ..self }
}
#[must_use = "This does not modify `self`."]
pub fn infimum(self, other: Self) -> TexelLayout {
TexelLayout {
size: self.size.min(other.size),
align: self.align.min(other.align),
}
}
pub const fn size(self) -> usize {
self.size
}
pub const fn align(self) -> usize {
self.size
}
pub const fn superset_of(&self, other: TexelLayout) -> bool {
self.size >= other.size && self.align >= other.align
}
}
impl DynLayout {
pub fn byte_len(&self) -> usize {
match self.repr {
LayoutRepr::Matrix(matrix) => matrix.byte_len(),
LayoutRepr::Yuv420p(matrix) => matrix.byte_len(),
}
}
}
impl MatrixBytes {
pub fn empty(element: TexelLayout) -> Self {
MatrixBytes {
element,
first_dim: 0,
second_dim: 0,
}
}
pub fn from_width_height(
element: TexelLayout,
first_dim: usize,
second_dim: usize,
) -> Option<Self> {
let max_index = first_dim.checked_mul(second_dim)?;
let _ = max_index.checked_mul(element.size)?;
Some(MatrixBytes {
element,
first_dim,
second_dim,
})
}
pub const fn element(&self) -> TexelLayout {
self.element
}
pub const fn width(&self) -> usize {
self.first_dim
}
pub const fn height(&self) -> usize {
self.second_dim
}
pub const fn byte_len(self) -> usize {
self.element.size * self.len()
}
pub const fn len(self) -> usize {
self.first_dim * self.second_dim
}
}
impl Yuv420p {
pub fn from_width_height(channel: TexelLayout, width: u32, height: u32) -> Option<Self> {
use core::convert::TryFrom;
if width % 2 != 0 || height % 2 != 0 {
return None;
}
let mwidth = usize::try_from(width).ok()?;
let mheight = usize::try_from(height).ok()?;
let y_count = mwidth.checked_mul(mheight)?;
let uv_count = y_count / 2;
let count = y_count.checked_add(uv_count)?;
let _ = count.checked_mul(channel.size)?;
Some(Yuv420p {
channel,
width,
height,
})
}
pub const fn byte_len(self) -> usize {
let ylen = (self.width as usize) * (self.height as usize) * self.channel.size;
ylen + ylen / 2
}
}
impl Layout for Bytes {
fn byte_len(&self) -> usize {
self.0
}
}
impl<'lt, T: Layout + ?Sized> Layout for &'lt T {
fn byte_len(&self) -> usize {
(**self).byte_len()
}
}
impl<'lt, T: Layout + ?Sized> Layout for &'lt mut T {
fn byte_len(&self) -> usize {
(**self).byte_len()
}
}
impl Take for Bytes {
fn take(&mut self) -> Self {
Bytes(core::mem::take(&mut self.0))
}
}
impl Layout for DynLayout {
fn byte_len(&self) -> usize {
DynLayout::byte_len(self)
}
}
impl Layout for MatrixBytes {
fn byte_len(&self) -> usize {
MatrixBytes::byte_len(*self)
}
}
impl Take for MatrixBytes {
fn take(&mut self) -> Self {
core::mem::replace(self, MatrixBytes::empty(self.element))
}
}
impl<P> MatrixLayout for Matrix<P> {
fn matrix(&self) -> MatrixBytes {
self.into_matrix_bytes()
}
}
impl<L: MatrixLayout> Decay<L> for MatrixBytes {
fn decay(from: L) -> MatrixBytes {
from.matrix()
}
}
impl<P> TryMend<MatrixBytes> for Texel<P> {
type Into = Matrix<P>;
type Err = MismatchedPixelError;
fn try_mend(self, matrix: &MatrixBytes) -> Result<Matrix<P>, Self::Err> {
Matrix::with_matrix(self, *matrix).ok_or_else(MismatchedPixelError::default)
}
}
impl<T> SliceLayout for &'_ T
where
T: SliceLayout,
{
type Sample = T::Sample;
fn sample(&self) -> Texel<Self::Sample> {
(**self).sample()
}
}
impl<T> SliceLayout for &'_ mut T
where
T: SliceLayout,
{
type Sample = T::Sample;
fn sample(&self) -> Texel<Self::Sample> {
(**self).sample()
}
}
impl<P> From<Texel<P>> for TexelLayout {
fn from(pix: Texel<P>) -> Self {
TexelLayout {
size: pix.size(),
align: pix.align(),
}
}
}
impl<L: Layout + ?Sized> Layout for Box<L> {
fn byte_len(&self) -> usize {
(**self).byte_len()
}
}
impl<L: Layout> Decay<L> for Box<L> {
fn decay(from: L) -> Box<L> {
Box::new(from)
}
}
impl cmp::PartialOrd for TexelLayout {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
if self.size == other.size && self.align == other.align {
Some(cmp::Ordering::Equal)
} else if self.size <= other.size && self.align <= other.align {
Some(cmp::Ordering::Less)
} else if self.size >= other.size && self.align >= other.align {
Some(cmp::Ordering::Greater)
} else {
None
}
}
}
macro_rules! bytes_from_layout {
($layout:path) => {
impl From<$layout> for Bytes {
fn from(layout: $layout) -> Self {
Bytes::from_layout(layout)
}
}
};
(<$($bound:ident),*> $layout:ident) => {
impl<$($bound),*> From<$layout <$($bound),*>> for Bytes {
fn from(layout: $layout <$($bound),*>) -> Self {
Bytes::from_layout(layout)
}
}
};
}
bytes_from_layout!(DynLayout);
bytes_from_layout!(MatrixBytes);
bytes_from_layout!(<P> Matrix);
impl From<MatrixBytes> for DynLayout {
fn from(matrix: MatrixBytes) -> Self {
DynLayout {
repr: LayoutRepr::Matrix(matrix),
}
}
}
impl From<Yuv420p> for DynLayout {
fn from(matrix: Yuv420p) -> Self {
DynLayout {
repr: LayoutRepr::Yuv420p(matrix),
}
}
}
impl<P> From<Matrix<P>> for MatrixBytes {
fn from(mat: Matrix<P>) -> Self {
MatrixBytes {
element: mat.pixel().into(),
first_dim: mat.width(),
second_dim: mat.height(),
}
}
}
impl<P> Raster<P> for Matrix<P> {
fn dimensions(&self) -> Coord {
use core::convert::TryFrom;
let width = u32::try_from(self.width()).unwrap_or(u32::MAX);
let height = u32::try_from(self.height()).unwrap_or(u32::MAX);
Coord(width, height)
}
fn get(from: ImageRef<&Self>, Coord(x, y): Coord) -> Option<P> {
if from.layout().in_bounds(x as usize, y as usize) {
let index = from.layout().index_of(x as usize, y as usize);
let texel = from.layout().sample();
from.as_slice().get(index).map(|v| texel.copy_val(v))
} else {
None
}
}
}
impl<P> RasterMut<P> for Matrix<P> {
fn put(into: ImageMut<&mut Self>, Coord(x, y): Coord, val: P) {
if into.layout().in_bounds(x as usize, y as usize) {
let index = into.layout().index_of(x as usize, y as usize);
if let Some(dst) = into.into_mut_slice().get_mut(index) {
*dst = val;
}
}
}
}