use core::ops::{Index, IndexMut};
use core::{cmp, fmt};
use crate::buf::Buffer;
use crate::image::{Image, RawImage};
use crate::layout::Matrix as Layout;
use crate::{layout, AsTexel, BufferReuseError, Texel, TexelBuffer};
#[derive(Clone, PartialEq, Eq)]
pub struct Matrix<P> {
inner: RawImage<Buffer, Layout<P>>,
}
#[derive(PartialEq, Eq)]
pub struct MatrixReuseError<P> {
buffer: TexelBuffer<P>,
layout: Layout<P>,
}
#[derive(PartialEq, Eq)]
pub struct MapReuseError<P, Q> {
buffer: Matrix<P>,
layout: Option<Layout<Q>>,
}
impl<P> Matrix<P> {
pub fn with_layout(layout: Layout<P>) -> Self {
let rec = TexelBuffer::bytes_for_texel(layout.pixel, layout.byte_len());
Self::new_raw(rec, layout)
}
pub fn with_width_and_height(width: usize, height: usize) -> Self
where
P: AsTexel,
{
let layout =
Layout::width_and_height(width, height).expect("Texel layout can not fit into memory");
Self::with_layout(layout)
}
pub fn from_buffer(mut buffer: TexelBuffer<P>, layout: Layout<P>) -> Self {
buffer.resize_bytes(layout.byte_len());
Self::new_raw(buffer, layout)
}
pub fn from_reused_buffer(
mut buffer: TexelBuffer<P>,
layout: Layout<P>,
) -> Result<Self, MatrixReuseError<P>> {
match buffer.reuse_bytes(layout.byte_len()) {
Ok(_) => (),
Err(_) => return Err(MatrixReuseError { buffer, layout }),
}
Ok(Self::new_raw(buffer, layout))
}
fn new_raw(inner: TexelBuffer<P>, layout: Layout<P>) -> Self {
assert_eq!(inner.len(), layout.len(), "Texel count agrees with buffer");
Matrix {
inner: RawImage::from_buffer(inner, layout),
}
}
pub fn as_slice(&self) -> &[P] {
self.inner.as_slice()
}
pub fn as_mut_slice(&mut self) -> &mut [P] {
self.inner.as_mut_slice()
}
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
self.inner.as_bytes_mut()
}
pub fn resize(&mut self, layout: Layout<P>) {
self.inner.grow(&layout);
*self.inner.layout_mut_unguarded() = layout;
}
pub fn reuse(&mut self, layout: Layout<P>) -> Result<(), BufferReuseError> {
self.inner.try_reuse(layout)
}
pub fn transmute<Q: AsTexel>(self) -> Matrix<Q> {
self.transmute_to(Q::texel())
}
pub fn transmute_to<Q: AsTexel>(self, pixel: Texel<Q>) -> Matrix<Q> {
let layout = self.layout().transmute_to(pixel);
let inner = self.inner.reinterpret_unguarded(|_| layout);
Matrix { inner }
}
fn layout(&self) -> Layout<P> {
*self.inner.layout()
}
pub fn into_buffer(self) -> TexelBuffer<P> {
self.inner.into_buffer()
}
fn index_of(&self, x: usize, y: usize) -> usize {
self.layout().index_of(x, y)
}
pub fn map<F, Q>(self, map: F) -> Matrix<Q>
where
F: Fn(P) -> Q,
Q: AsTexel,
{
self.map_to(map, Q::texel())
}
pub fn map_to<F, Q>(self, map: F, pixel: Texel<Q>) -> Matrix<Q>
where
F: Fn(P) -> Q,
{
let layout = self
.layout()
.map_to(pixel)
.expect("Texel layout can not fit into memory");
let inner = self.into_buffer().map_to(map, pixel);
Matrix::from_buffer(inner, layout)
}
pub fn map_reuse<F, Q>(self, map: F) -> Result<Matrix<Q>, MapReuseError<P, Q>>
where
F: Fn(P) -> Q,
Q: AsTexel,
{
self.map_reuse_to(map, Q::texel())
}
pub fn map_reuse_to<F, Q>(
self,
map: F,
pixel: Texel<Q>,
) -> Result<Matrix<Q>, MapReuseError<P, Q>>
where
F: Fn(P) -> Q,
{
let layout = match self.layout().map_to(pixel) {
Some(layout) => layout,
None => {
return Err(MapReuseError {
buffer: self,
layout: None,
})
}
};
if self.inner.as_bytes().len() < layout.byte_len() {
return Err(MapReuseError {
buffer: self,
layout: Some(layout),
});
}
let inner = self.into_buffer().map_to(map, pixel);
Ok(Matrix::from_buffer(inner, layout))
}
}
impl<P> Layout<P> {
pub fn width_and_height_for_texel(
pixel: Texel<P>,
width: usize,
height: usize,
) -> Option<Self> {
let max_index = Self::max_index(width, height)?;
let _ = max_index.checked_mul(pixel.size())?;
Some(Layout {
width,
height,
pixel,
})
}
pub fn width_and_height(width: usize, height: usize) -> Option<Self>
where
P: AsTexel,
{
Self::width_and_height_for_texel(P::texel(), width, height)
}
pub const fn empty(pixel: Texel<P>) -> Self {
Layout {
pixel,
width: 0,
height: 0,
}
}
pub fn with_matrix(pixel: Texel<P>, matrix: layout::MatrixBytes) -> Option<Self> {
if pixel.size() == matrix.element.size() {
Some(Layout {
pixel,
width: matrix.first_dim,
height: matrix.second_dim,
})
} else {
None
}
}
pub fn into_matrix_bytes(self) -> layout::MatrixBytes {
layout::MatrixBytes {
element: self.pixel.into(),
first_dim: self.width,
second_dim: self.height,
}
}
pub fn byte_len(self) -> usize {
self.pixel.size() * self.width * self.height
}
pub fn len(self) -> usize {
self.width * self.height
}
pub fn width(self) -> usize {
self.width
}
pub fn height(self) -> usize {
self.height
}
pub fn pixel(self) -> Texel<P> {
self.pixel
}
pub fn transmute<Q: AsTexel>(self) -> Layout<Q> {
self.transmute_to(Q::texel())
}
pub fn transmute_to<Q>(self, pixel: Texel<Q>) -> Layout<Q> {
assert!(
self.pixel.size() == pixel.size(),
"{} vs {}",
self.pixel.size(),
pixel.size()
);
Layout {
width: self.width,
height: self.height,
pixel,
}
}
pub fn map<Q: AsTexel>(self) -> Option<Layout<Q>> {
self.map_to(Q::texel())
}
pub fn map_to<Q>(self, pixel: Texel<Q>) -> Option<Layout<Q>> {
Layout::width_and_height_for_texel(pixel, self.width, self.height)
}
pub(crate) fn index_of(self, x: usize, y: usize) -> usize {
assert!(self.in_bounds(x, y));
y * self.width() + x
}
pub(crate) fn in_bounds(self, x: usize, y: usize) -> bool {
x < self.width && y < self.height
}
fn max_index(width: usize, height: usize) -> Option<usize> {
width.checked_mul(height)
}
}
impl<P> MatrixReuseError<P> {
pub fn into_buffer(self) -> TexelBuffer<P> {
self.buffer
}
}
impl<P, Q> MapReuseError<P, Q> {
pub fn into_image(self) -> Matrix<P> {
self.buffer
}
pub fn layout(&self) -> Option<Layout<Q>> {
self.layout
}
}
impl<P> From<Image<Layout<P>>> for Matrix<P> {
fn from(image: Image<Layout<P>>) -> Self {
let layout = *image.layout();
let rec = image.into_buffer();
Self::new_raw(rec, layout)
}
}
impl<P> From<Matrix<P>> for Image<Layout<P>> {
fn from(matrix: Matrix<P>) -> Self {
let layout = matrix.layout();
let rec = matrix.into_buffer();
Image::from_buffer(rec, layout)
}
}
impl<P> layout::Layout for Layout<P> {
fn byte_len(&self) -> usize {
Layout::byte_len(*self)
}
}
impl<P> layout::SliceLayout for Layout<P> {
type Sample = P;
fn sample(&self) -> Texel<P> {
self.pixel
}
}
impl<P: AsTexel> Default for Layout<P> {
fn default() -> Self {
Self::empty(P::texel())
}
}
impl<P> layout::Take for Layout<P> {
fn take(&mut self) -> Self {
core::mem::replace(self, Self::empty(self.pixel))
}
}
impl<P> fmt::Debug for Layout<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Layout")
.field("width", &self.width)
.field("height", &self.height)
.field("pixel", &self.pixel)
.finish()
}
}
impl<P> Clone for Layout<P> {
fn clone(&self) -> Self {
Layout { ..*self }
}
}
impl<P> Copy for Layout<P> {}
impl<P> cmp::PartialEq for Layout<P> {
fn eq(&self, other: &Self) -> bool {
(self.width, self.height) == (other.width, other.height)
}
}
impl<P> cmp::Eq for Layout<P> {}
impl<P> cmp::PartialOrd for Layout<P> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
if self.width < other.width && self.height < other.height {
Some(cmp::Ordering::Less)
} else if self.width > other.width && self.height > other.height {
Some(cmp::Ordering::Greater)
} else if self.width == other.width && self.height == other.height {
Some(cmp::Ordering::Equal)
} else {
None
}
}
}
impl<P: AsTexel> Default for Matrix<P> {
fn default() -> Self {
Matrix::from_buffer(TexelBuffer::default(), Layout::default())
}
}
impl<P> Index<(usize, usize)> for Matrix<P> {
type Output = P;
fn index(&self, (x, y): (usize, usize)) -> &P {
&self.as_slice()[self.index_of(x, y)]
}
}
impl<P> IndexMut<(usize, usize)> for Matrix<P> {
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut P {
let index = self.index_of(x, y);
&mut self.as_mut_slice()[index]
}
}
impl<P: fmt::Debug> fmt::Debug for Matrix<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Matrix")
.field("layout", self.inner.layout())
.field("content", &self.inner.as_slice())
.finish()
}
}
impl<P> fmt::Debug for MatrixReuseError<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Matrix requires {} elements but buffer has capacity for only {}",
self.layout.len(),
self.buffer.capacity()
)
}
}
impl<P, Q> fmt::Debug for MapReuseError<P, Q> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.layout {
Some(layout) => write!(
f,
"Mapping image requires {} bytes but current buffer has a capacity of {}",
layout.byte_len(),
self.buffer.inner.as_capacity_bytes().len(),
),
None => write!(f, "Mapped image can not be allocated"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn buffer_reuse() {
let rec = TexelBuffer::<u8>::new(4);
assert!(rec.capacity() >= 4);
let layout = Layout::width_and_height(2, 2).unwrap();
let mut image =
Matrix::from_reused_buffer(rec, layout).expect("Rec is surely large enough");
image
.reuse(Layout::width_and_height(1, 1).unwrap())
.expect("Can scale down the image");
image.resize(Layout::width_and_height(0, 0).unwrap());
image
.reuse(layout)
.expect("Can still reuse original allocation");
}
}