use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy)]
pub struct GrayFrame<'a> {
data: &'a [u8],
width: usize,
height: usize,
stride: usize,
}
impl<'a> GrayFrame<'a> {
pub fn new(data: &'a [u8], width: usize, height: usize) -> Result<Self> {
Self::with_stride(data, width, height, width)
}
pub fn with_stride(data: &'a [u8], width: usize, height: usize, stride: usize) -> Result<Self> {
if width == 0 || height == 0 {
return Err(Error::InvalidFrame {
detail: "zero-sized frame".into(),
});
}
if stride < width {
return Err(Error::InvalidFrame {
detail: "stride smaller than width".into(),
});
}
let required = stride
.checked_mul(height - 1)
.and_then(|r| r.checked_add(width));
match required {
Some(req) if data.len() >= req => Ok(GrayFrame {
data,
width,
height,
stride,
}),
_ => Err(Error::InvalidFrame {
detail: "buffer too small for dimensions".into(),
}),
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn get(&self, x: usize, y: usize) -> Option<u8> {
if x >= self.width || y >= self.height {
return None;
}
Some(self.data[y * self.stride + x])
}
pub fn get_unchecked(&self, x: usize, y: usize) -> u8 {
debug_assert!(x < self.width && y < self.height);
self.data[y * self.stride + x]
}
pub fn row(&self, y: usize) -> Option<&'a [u8]> {
if y >= self.height {
return None;
}
let start = y * self.stride;
Some(&self.data[start..start + self.width])
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GrayImage {
data: Vec<u8>,
width: usize,
height: usize,
}
impl GrayImage {
pub fn filled(width: usize, height: usize, fill: u8) -> Self {
assert!(
width > 0 && height > 0,
"GrayImage dimensions must be nonzero"
);
GrayImage {
data: vec![fill; width * height],
width,
height,
}
}
pub fn new(width: usize, height: usize) -> Self {
Self::filled(width, height, 0)
}
pub fn from_raw(width: usize, height: usize, data: Vec<u8>) -> Self {
assert!(
width > 0 && height > 0,
"GrayImage dimensions must be nonzero"
);
assert_eq!(data.len(), width * height, "buffer size mismatch");
GrayImage {
data,
width,
height,
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn pixels(&self) -> &[u8] {
&self.data
}
pub fn get(&self, x: usize, y: usize) -> u8 {
if x >= self.width || y >= self.height {
return 0;
}
self.data[y * self.width + x]
}
pub fn set(&mut self, x: usize, y: usize, value: u8) {
assert!(
x < self.width && y < self.height,
"GrayImage::set out of bounds"
);
self.data[y * self.width + x] = value;
}
pub fn as_frame(&self) -> GrayFrame<'_> {
GrayFrame::new(&self.data, self.width, self.height).expect("valid GrayImage dimensions")
}
pub fn sample_bilinear(&self, x: f32, y: f32) -> Option<f32> {
if x < 0.0 || y < 0.0 || !x.is_finite() || !y.is_finite() {
return None;
}
let x0 = x.floor();
let y0 = y.floor();
let ix0 = x0 as usize;
let iy0 = y0 as usize;
if ix0 >= self.width || iy0 >= self.height {
return None;
}
let ix1 = (ix0 + 1).min(self.width - 1);
let iy1 = (iy0 + 1).min(self.height - 1);
let fx = x - x0;
let fy = y - y0;
let p00 = self.data[iy0 * self.width + ix0] as f32;
let p10 = self.data[iy0 * self.width + ix1] as f32;
let p01 = self.data[iy1 * self.width + ix0] as f32;
let p11 = self.data[iy1 * self.width + ix1] as f32;
let top = p00 + (p10 - p00) * fx;
let bot = p01 + (p11 - p01) * fx;
Some(top + (bot - top) * fy)
}
}