use std::{
marker::PhantomData,
ops::{Deref, DerefMut},
};
use crate::{
color,
iter::pixel::{Iter as Pixels, IterMut as PixelsMut},
orientation::Orientation,
pixel::{self, Pixel},
region::{self, Region},
view::{self, View},
};
#[derive(Clone, PartialEq, Debug)]
pub struct Buffer<P, C, D>
where
P: Pixel<C>,
C: pixel::Channel,
{
region: Region,
data: D,
stride: usize,
pixel: PhantomData<P>,
channel: PhantomData<C>,
}
impl<P, C> Buffer<P, C, Vec<C>>
where
P: Pixel<C>,
C: pixel::Channel,
{
#[inline]
pub fn new(width: u32, height: u32) -> Self {
Buffer {
region: Region::from(0, 0, width, height),
data: vec![zero!(); width as usize * height as usize * P::channels()],
stride: width as usize * P::channels(),
channel: PhantomData,
pixel: PhantomData,
}
}
}
impl<P, C> Buffer<P, C, Vec<C>>
where
P: pixel::Write<C>,
C: pixel::Channel,
{
#[inline]
pub fn from_pixel(width: u32, height: u32, pixel: &P) -> Self {
let mut buffer = Self::new(width, height);
buffer.fill(pixel);
buffer
}
#[inline]
pub fn from_fn<T, F>(width: u32, height: u32, mut func: F) -> Self
where
T: Into<P>,
F: FnMut(u32, u32) -> T,
{
let mut buffer = Self::new(width, height);
for (x, y) in buffer.region().absolute() {
buffer.set(x, y, &func(x, y).into());
}
buffer
}
}
impl<P, C> Buffer<P, C, Vec<C>>
where
P: pixel::Write<C> + color::Mix + Clone,
C: pixel::Channel,
{
#[inline]
pub fn from_gradient(width: u32, height: u32, mode: Orientation, gradient: color::Gradient<P>) -> Self {
let mut buffer = Buffer::new(width, height);
match mode {
Orientation::Vertical => {
for (y, px) in (0..height).zip(gradient.take(height as usize)) {
for x in 0..width {
buffer.set(x, y, &px);
}
}
}
Orientation::Horizontal => {
for (x, px) in (0..width).zip(gradient.take(width as usize)) {
for y in 0..height {
buffer.set(x, y, &px);
}
}
}
}
buffer
}
}
impl<P, C, D> Buffer<P, C, D>
where
P: Pixel<C>,
C: pixel::Channel,
D: Deref<Target = [C]>,
{
#[inline]
pub fn from_raw(width: u32, height: u32, data: D) -> Result<Self, ()> {
if data.len() < width as usize * height as usize * P::channels() {
return Err(());
}
Ok(Buffer {
region: Region::from(0, 0, width, height),
data,
stride: width as usize * P::channels(),
pixel: PhantomData,
channel: PhantomData,
})
}
}
impl<P, C, D> Buffer<P, C, D>
where
P: Pixel<C>,
C: pixel::Channel,
{
#[inline]
pub fn into_raw(self) -> D {
self.data
}
#[inline]
pub fn stride(&self) -> usize {
self.stride
}
#[inline]
pub fn region(&self) -> Region {
self.region
}
#[inline]
pub fn dimensions(&self) -> (u32, u32) {
(self.region.width, self.region.height)
}
#[inline]
pub fn width(&self) -> u32 {
self.region.width
}
#[inline]
pub fn height(&self) -> u32 {
self.region.height
}
}
impl<P, C, D> Buffer<P, C, D>
where
P: pixel::Read<C>,
C: pixel::Channel,
D: Deref<Target = [C]>,
{
#[inline]
pub fn get(&self, x: u32, y: u32) -> P {
view::Read::new(&self.data, self.stride, self.region, self.region).get(x, y)
}
#[inline]
pub fn readable(&self, region: region::Builder) -> view::Read<P, C> {
let region = region.complete(self.region);
if region.x + region.width > self.region.width || region.y + region.height > self.region.height {
panic!("out of bounds");
}
view::Read::new(&self.data, self.stride, self.region, region)
}
#[inline]
pub fn pixels(&self) -> Pixels<P, C> {
Pixels::new(&self.data, self.stride, self.region, self.region)
}
#[inline]
pub fn convert<PO, CO>(&self) -> Buffer<PO, CO, Vec<CO>>
where
P: Into<PO>,
PO: pixel::Write<CO>,
CO: pixel::Channel,
{
let mut result = Buffer::<PO, CO, Vec<CO>>::new(self.region.width, self.region.height);
for (input, output) in self.chunks(P::channels()).zip(result.chunks_mut(PO::channels())) {
P::read(input).into().write(output)
}
result
}
#[inline]
pub fn convert_with<PO, CO, F>(&self, mut func: F) -> Buffer<PO, CO, Vec<CO>>
where
F: FnMut(P) -> PO,
PO: pixel::Write<CO>,
CO: pixel::Channel,
{
let mut result = Buffer::<PO, CO, Vec<CO>>::new(self.region.width, self.region.height);
for (input, output) in self.chunks(P::channels()).zip(result.chunks_mut(PO::channels())) {
func(P::read(input)).write(output)
}
result
}
}
impl<P, C, D> Buffer<P, C, D>
where
P: pixel::Write<C>,
C: pixel::Channel,
D: DerefMut<Target = [C]>,
{
#[inline]
pub fn set(&mut self, x: u32, y: u32, pixel: &P) {
view::Write::new(&mut self.data, self.stride, self.region, self.region).set(x, y, pixel)
}
#[inline]
pub fn writable(&mut self, region: region::Builder) -> view::Write<P, C> {
let region = region.complete(self.region);
if region.x + region.width > self.region.width || region.y + region.height > self.region.height {
panic!("out of bounds");
}
view::Write::new(&mut self.data, self.stride, self.region, region)
}
#[inline]
pub fn fill(&mut self, pixel: &P) {
for chunk in self.chunks_mut(P::channels()) {
pixel.write(chunk);
}
}
}
impl<P, C, D> Buffer<P, C, D>
where
P: pixel::Write<C> + pixel::Read<C>,
C: pixel::Channel,
D: DerefMut<Target = [C]>,
{
#[inline]
pub fn view(&mut self, region: region::Builder) -> View<P, C> {
let region = region.complete(self.region);
if region.x + region.width > self.region.width || region.y + region.height > self.region.height {
panic!("out of bounds");
}
View::new(&mut self.data, self.stride, self.region, region)
}
#[inline]
pub fn pixels_mut(&mut self) -> PixelsMut<P, C> {
PixelsMut::new(&mut self.data, self.stride, self.region, self.region)
}
}
impl<'a, P, C, D> From<&'a Buffer<P, C, D>> for view::Read<'a, P, C>
where
P: pixel::Read<C>,
C: pixel::Channel,
D: Deref<Target = [C]>,
{
#[inline]
fn from(value: &'a Buffer<P, C, D>) -> view::Read<'a, P, C> {
value.readable(Default::default())
}
}
impl<'a, P, C, D> From<&'a mut Buffer<P, C, D>> for view::Write<'a, P, C>
where
P: pixel::Write<C>,
C: pixel::Channel,
D: DerefMut<Target = [C]>,
{
#[inline]
fn from(value: &'a mut Buffer<P, C, D>) -> view::Write<'a, P, C> {
value.writable(Default::default())
}
}
impl<'a, P, C, D> From<&'a mut Buffer<P, C, D>> for View<'a, P, C>
where
P: pixel::Write<C> + pixel::Read<C>,
C: pixel::Channel,
D: DerefMut<Target = [C]>,
{
#[inline]
fn from(value: &'a mut Buffer<P, C, D>) -> View<'a, P, C> {
value.view(Default::default())
}
}
impl<P, C, D> Deref for Buffer<P, C, D>
where
P: Pixel<C>,
C: pixel::Channel,
D: Deref<Target = [C]>,
{
type Target = D::Target;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<P, C, D> DerefMut for Buffer<P, C, D>
where
P: Pixel<C>,
C: pixel::Channel,
D: DerefMut<Target = [C]>,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::color::*;
#[test]
fn new() {
assert_eq!(3, Buffer::<Rgb, u8, Vec<_>>::new(1, 1).into_raw().len());
assert_eq!(6, Buffer::<Rgb, u8, Vec<_>>::new(1, 2).into_raw().len());
assert_eq!(6, Buffer::<Rgb, u8, Vec<_>>::new(2, 1).into_raw().len());
assert_eq!(12, Buffer::<Rgb, u8, Vec<_>>::new(2, 2).into_raw().len());
}
#[test]
fn from_raw() {
assert!(Buffer::<Rgb, u8, _>::from_raw(1, 1, vec![0, 0, 0]).is_ok());
assert!(Buffer::<Rgb, u8, _>::from_raw(1, 2, vec![0, 0, 0, 0, 0, 0]).is_ok());
assert!(Buffer::<Rgb, u8, _>::from_raw(2, 1, vec![0, 0, 0, 0, 0, 0]).is_ok());
assert!(Buffer::<Rgb, u8, _>::from_raw(2, 2, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).is_ok());
assert!(Buffer::<Rgb, u8, _>::from_raw(1, 1, vec![0, 0]).is_err());
}
#[test]
fn into_raw() {
assert_eq!(
vec![1, 2, 3],
Buffer::<Rgb, u8, _>::from_raw(1, 1, vec![1, 2, 3]).unwrap().into_raw()
);
assert_eq!(vec![0, 0, 0], Buffer::<Rgb, u8, Vec<_>>::new(1, 1).into_raw());
}
#[test]
fn deref() {
assert!(Buffer::<Rgb, u8, _>::from_raw(1, 1, vec![0, 0, 0]).unwrap().len() == 3);
}
#[test]
fn clone() {
let a = Buffer::<Rgb, u8, _>::from_raw(1, 1, vec![0, 0, 0]).unwrap();
let b = a.clone();
assert_eq!(a.get(0, 0), b.get(0, 0));
}
#[test]
fn eq() {
let a = Buffer::<Rgb, u8, _>::from_raw(1, 1, vec![0, 0, 0]).unwrap();
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn convert() {
let a = Buffer::<Rgb, u8, _>::from_raw(1, 1, vec![255, 0, 255]).unwrap();
let b = a.convert::<Rgba, u8>();
assert_eq!(Rgba::new(1.0, 0.0, 1.0, 1.0), b.get(0, 0));
assert_eq!(vec![255, 0, 255, 255], b.into_raw());
}
}