use imgref::ImgVec;
use std::ops::{Index, IndexMut};
use std::sync::Mutex;
pub struct BufferPool {
buffers: Mutex<Vec<Vec<f32>>>,
}
impl core::fmt::Debug for BufferPool {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let count = self.buffers.lock().unwrap().len();
f.debug_struct("BufferPool")
.field("cached_buffers", &count)
.finish()
}
}
impl Default for BufferPool {
fn default() -> Self {
Self {
buffers: Mutex::new(Vec::new()),
}
}
}
impl BufferPool {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub(crate) fn take(&self, needed: usize) -> Vec<f32> {
let mut pool = self.buffers.lock().unwrap();
let mut best_idx = None;
let mut best_excess = usize::MAX;
for (i, buf) in pool.iter().enumerate() {
let cap = buf.len();
if cap >= needed && cap - needed < best_excess {
best_idx = Some(i);
best_excess = cap - needed;
}
}
if let Some(idx) = best_idx {
let mut buf = pool.swap_remove(idx);
drop(pool); buf.truncate(needed);
if buf.len() < needed {
#[cfg(feature = "unsafe-performance")]
#[allow(clippy::uninit_vec)]
{
buf.reserve(needed - buf.len());
unsafe { buf.set_len(needed) };
}
#[cfg(not(feature = "unsafe-performance"))]
buf.resize(needed, 0.0);
}
buf
} else {
drop(pool); #[cfg(feature = "unsafe-performance")]
#[allow(clippy::uninit_vec)]
{
let mut buf = Vec::with_capacity(needed);
unsafe { buf.set_len(needed) };
buf
}
#[cfg(not(feature = "unsafe-performance"))]
{
vec![0.0; needed]
}
}
}
pub(crate) fn put(&self, buf: Vec<f32>) {
let mut pool = self.buffers.lock().unwrap();
if pool.len() < 48 {
pool.push(buf);
}
}
}
impl Clone for BufferPool {
fn clone(&self) -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ImageF {
data: Vec<f32>,
width: usize,
height: usize,
stride: usize, }
impl ImageF {
#[must_use]
pub fn new(width: usize, height: usize) -> Self {
let stride = (width + 15) & !15;
Self {
data: vec![0.0; stride * height],
width,
height,
stride,
}
}
#[must_use]
pub(crate) fn new_uninit(width: usize, height: usize) -> Self {
let stride = (width + 15) & !15;
let needed = stride * height;
#[cfg(feature = "unsafe-performance")]
let data = {
let mut buf = Vec::with_capacity(needed);
#[allow(clippy::uninit_vec)]
unsafe {
buf.set_len(needed);
}
buf
};
#[cfg(not(feature = "unsafe-performance"))]
let data = vec![0.0; needed];
Self {
data,
width,
height,
stride,
}
}
#[must_use]
pub fn from_vec(data: Vec<f32>, width: usize, height: usize) -> Self {
assert_eq!(data.len(), width * height);
Self {
data,
width,
height,
stride: width,
}
}
#[must_use]
pub fn from_vec_padded(data: Vec<f32>, width: usize, height: usize, stride: usize) -> Self {
assert!(stride >= width);
assert_eq!(data.len(), stride * height);
Self {
data,
width,
height,
stride,
}
}
#[must_use]
pub fn filled(width: usize, height: usize, value: f32) -> Self {
let stride = (width + 15) & !15;
Self {
data: vec![value; stride * height],
width,
height,
stride,
}
}
#[inline]
#[must_use]
pub fn width(&self) -> usize {
self.width
}
#[inline]
#[must_use]
pub fn height(&self) -> usize {
self.height
}
#[inline]
#[must_use]
pub fn stride(&self) -> usize {
self.stride
}
#[inline]
#[must_use]
pub fn row(&self, y: usize) -> &[f32] {
let start = y * self.stride;
&self.data[start..start + self.width]
}
#[inline]
pub fn row_mut(&mut self, y: usize) -> &mut [f32] {
let start = y * self.stride;
&mut self.data[start..start + self.width]
}
#[inline]
#[must_use]
pub fn row_full(&self, y: usize) -> &[f32] {
let start = y * self.stride;
&self.data[start..start + self.stride]
}
#[inline]
pub fn row_full_mut(&mut self, y: usize) -> &mut [f32] {
let start = y * self.stride;
&mut self.data[start..start + self.stride]
}
#[inline]
#[must_use]
pub fn get(&self, x: usize, y: usize) -> f32 {
self.data[y * self.stride + x]
}
#[inline]
pub fn set(&mut self, x: usize, y: usize, value: f32) {
self.data[y * self.stride + x] = value;
}
#[cfg(feature = "unsafe-performance")]
#[allow(clippy::inline_always)]
#[inline(always)]
#[must_use]
pub(crate) unsafe fn get_unchecked(&self, x: usize, y: usize) -> f32 {
unsafe { *self.data.get_unchecked(y * self.stride + x) }
}
#[cfg(feature = "unsafe-performance")]
#[allow(clippy::inline_always)]
#[inline(always)]
pub(crate) unsafe fn set_unchecked(&mut self, x: usize, y: usize, value: f32) {
unsafe { *self.data.get_unchecked_mut(y * self.stride + x) = value };
}
#[cfg(feature = "unsafe-performance")]
#[allow(clippy::inline_always)]
#[inline(always)]
#[must_use]
pub(crate) unsafe fn row_unchecked(&self, y: usize) -> &[f32] {
let start = y * self.stride;
unsafe { self.data.get_unchecked(start..start + self.width) }
}
#[cfg(feature = "unsafe-performance")]
#[allow(clippy::inline_always)]
#[inline(always)]
pub(crate) unsafe fn row_mut_unchecked(&mut self, y: usize) -> &mut [f32] {
let start = y * self.stride;
unsafe { self.data.get_unchecked_mut(start..start + self.width) }
}
#[inline]
#[must_use]
pub fn as_ptr(&self) -> *const f32 {
self.data.as_ptr()
}
#[inline]
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut f32 {
self.data.as_mut_ptr()
}
#[inline]
#[must_use]
pub fn data(&self) -> &[f32] {
&self.data
}
#[inline]
pub fn data_mut(&mut self) -> &mut [f32] {
&mut self.data
}
#[must_use]
pub fn from_pool_dirty(width: usize, height: usize, pool: &BufferPool) -> Self {
let stride = (width + 15) & !15;
let needed = stride * height;
let data = pool.take(needed);
Self {
data,
width,
height,
stride,
}
}
#[must_use]
pub fn from_pool_zeroed(width: usize, height: usize, pool: &BufferPool) -> Self {
let mut img = Self::from_pool_dirty(width, height, pool);
img.data.fill(0.0);
img
}
pub fn recycle(self, pool: &BufferPool) {
pool.put(self.data);
}
#[must_use]
pub fn same_size(&self, other: &Self) -> bool {
self.width == other.width && self.height == other.height
}
pub fn copy_from(&mut self, other: &Self) {
assert!(self.same_size(other));
for y in 0..self.height {
self.row_mut(y).copy_from_slice(other.row(y));
}
}
pub fn fill(&mut self, value: f32) {
self.data.fill(value);
}
#[must_use]
pub(crate) fn into_imgvec(self) -> ImgVec<f32> {
if self.stride == self.width {
ImgVec::new(self.data, self.width, self.height)
} else {
let mut out = Vec::with_capacity(self.width * self.height);
for y in 0..self.height {
let start = y * self.stride;
out.extend_from_slice(&self.data[start..start + self.width]);
}
ImgVec::new(out, self.width, self.height)
}
}
}
impl Index<(usize, usize)> for ImageF {
type Output = f32;
#[inline]
fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
&self.data[y * self.stride + x]
}
}
impl IndexMut<(usize, usize)> for ImageF {
#[inline]
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output {
&mut self.data[y * self.stride + x]
}
}
#[derive(Debug, Clone)]
pub struct Image3F {
planes: [ImageF; 3],
}
impl Image3F {
#[must_use]
pub fn new(width: usize, height: usize) -> Self {
Self {
planes: [
ImageF::new(width, height),
ImageF::new(width, height),
ImageF::new(width, height),
],
}
}
#[must_use]
pub(crate) fn new_uninit(width: usize, height: usize) -> Self {
Self {
planes: [
ImageF::new_uninit(width, height),
ImageF::new_uninit(width, height),
ImageF::new_uninit(width, height),
],
}
}
#[must_use]
pub(crate) fn from_pool_dirty(width: usize, height: usize, pool: &BufferPool) -> Self {
Self {
planes: [
ImageF::from_pool_dirty(width, height, pool),
ImageF::from_pool_dirty(width, height, pool),
ImageF::from_pool_dirty(width, height, pool),
],
}
}
pub(crate) fn recycle(self, pool: &BufferPool) {
let [p0, p1, p2] = self.planes;
p0.recycle(pool);
p1.recycle(pool);
p2.recycle(pool);
}
#[must_use]
pub fn from_planes(plane0: ImageF, plane1: ImageF, plane2: ImageF) -> Self {
assert!(plane0.same_size(&plane1));
assert!(plane0.same_size(&plane2));
Self {
planes: [plane0, plane1, plane2],
}
}
#[inline]
#[must_use]
pub fn width(&self) -> usize {
self.planes[0].width()
}
#[inline]
#[must_use]
pub fn height(&self) -> usize {
self.planes[0].height()
}
#[inline]
#[must_use]
pub fn plane(&self, index: usize) -> &ImageF {
&self.planes[index]
}
#[inline]
pub fn plane_mut(&mut self, index: usize) -> &mut ImageF {
&mut self.planes[index]
}
#[inline]
#[must_use]
pub fn plane_row(&self, plane: usize, y: usize) -> &[f32] {
self.planes[plane].row(y)
}
#[inline]
pub fn plane_row_mut(&mut self, plane: usize, y: usize) -> &mut [f32] {
self.planes[plane].row_mut(y)
}
#[inline]
pub fn planes_mut(&mut self) -> (&mut ImageF, &mut ImageF, &mut ImageF) {
let [p0, p1, p2] = &mut self.planes;
(p0, p1, p2)
}
}
impl Index<usize> for Image3F {
type Output = ImageF;
fn index(&self, index: usize) -> &Self::Output {
&self.planes[index]
}
}
impl IndexMut<usize> for Image3F {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.planes[index]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_image_creation() {
let img = ImageF::new(100, 50);
assert_eq!(img.width(), 100);
assert_eq!(img.height(), 50);
assert!(img.stride() >= 100);
assert_eq!(img.stride() % 16, 0); }
#[test]
fn test_pixel_access() {
let mut img = ImageF::new(10, 10);
img.set(5, 3, 42.0);
assert!((img.get(5, 3) - 42.0).abs() < 0.001);
assert!((img[(5, 3)] - 42.0).abs() < 0.001);
}
#[test]
fn test_row_access() {
let mut img = ImageF::new(10, 10);
img.row_mut(5)[3] = 99.0;
assert!((img.row(5)[3] - 99.0).abs() < 0.001);
}
#[test]
fn test_image3f() {
let img = Image3F::new(100, 50);
assert_eq!(img.width(), 100);
assert_eq!(img.height(), 50);
}
}