use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct Channel {
data: Vec<i32>,
width: usize,
height: usize,
pub hshift: u32,
pub vshift: u32,
pub component: i32,
}
impl Channel {
pub fn new(width: usize, height: usize) -> Result<Self> {
if width == 0 || height == 0 {
return Err(Error::InvalidImageDimensions(width, height));
}
let size = width
.checked_mul(height)
.ok_or(Error::InvalidImageDimensions(width, height))?;
let mut data = Vec::new();
data.try_reserve_exact(size)?;
data.resize(size, 0);
Ok(Self {
data,
width,
height,
hshift: 0,
vshift: 0,
component: -1,
})
}
pub fn from_vec(data: Vec<i32>, width: usize, height: usize) -> Result<Self> {
if width == 0 || height == 0 {
return Err(Error::InvalidImageDimensions(width, height));
}
if data.len() != width * height {
return Err(Error::InvalidImageDimensions(width, height));
}
Ok(Self {
data,
width,
height,
hshift: 0,
vshift: 0,
component: -1,
})
}
#[inline]
pub fn width(&self) -> usize {
self.width
}
#[inline]
pub fn height(&self) -> usize {
self.height
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[inline]
pub fn get(&self, x: usize, y: usize) -> i32 {
debug_assert!(x < self.width && y < self.height);
self.data[y * self.width + x]
}
#[inline]
pub fn set(&mut self, x: usize, y: usize, value: i32) {
debug_assert!(x < self.width && y < self.height);
self.data[y * self.width + x] = value;
}
#[inline]
pub fn row(&self, y: usize) -> &[i32] {
debug_assert!(y < self.height);
let start = y * self.width;
&self.data[start..start + self.width]
}
#[inline]
pub fn row_mut(&mut self, y: usize) -> &mut [i32] {
debug_assert!(y < self.height);
let start = y * self.width;
&mut self.data[start..start + self.width]
}
#[inline]
pub fn data(&self) -> &[i32] {
&self.data
}
#[inline]
pub fn data_mut(&mut self) -> &mut [i32] {
&mut self.data
}
#[inline]
pub fn get_clamped(&self, x: isize, y: isize) -> i32 {
if x < 0 || y < 0 || x >= self.width as isize || y >= self.height as isize {
0
} else {
self.data[y as usize * self.width + x as usize]
}
}
pub fn extract_shifted_region(
&self,
rect_x0: usize,
rect_y0: usize,
rect_xsize: usize,
rect_ysize: usize,
) -> Option<Channel> {
let x0 = rect_x0 >> self.hshift;
let y0 = rect_y0 >> self.vshift;
let xsize = (rect_xsize >> self.hshift).min(self.width.saturating_sub(x0));
let ysize = (rect_ysize >> self.vshift).min(self.height.saturating_sub(y0));
if xsize == 0 || ysize == 0 {
return None;
}
let mut data = Vec::with_capacity(xsize * ysize);
for y in 0..ysize {
for x in 0..xsize {
data.push(self.get(x0 + x, y0 + y));
}
}
let mut ch = Channel::from_vec(data, xsize, ysize).ok()?;
ch.hshift = self.hshift;
ch.vshift = self.vshift;
ch.component = self.component;
Some(ch)
}
pub fn extract_grid_cell(&self, gx: usize, gy: usize, group_dim: usize) -> Option<Channel> {
let grid_w = group_dim >> self.hshift;
let grid_h = group_dim >> self.vshift;
if grid_w == 0 || grid_h == 0 {
return None;
}
let bx = gx * grid_w;
let by = gy * grid_h;
if bx >= self.width || by >= self.height {
return None;
}
let xsize = (self.width - bx).min(grid_w);
let ysize = (self.height - by).min(grid_h);
if xsize == 0 || ysize == 0 {
return None;
}
let mut data = Vec::with_capacity(xsize * ysize);
for y in 0..ysize {
for x in 0..xsize {
data.push(self.get(bx + x, by + y));
}
}
let mut ch = Channel::from_vec(data, xsize, ysize).ok()?;
ch.hshift = self.hshift;
ch.vshift = self.vshift;
ch.component = self.component;
Some(ch)
}
#[inline]
pub fn get_clamped_to_edge(&self, x: isize, y: isize) -> i32 {
let x = x.clamp(0, self.width as isize - 1) as usize;
let y = y.clamp(0, self.height as isize - 1) as usize;
self.data[y * self.width + x]
}
}
#[derive(Debug, Clone)]
pub struct ModularImage {
pub channels: Vec<Channel>,
pub bit_depth: u32,
pub is_grayscale: bool,
pub has_alpha: bool,
}
impl ModularImage {
pub fn from_rgb8(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(3))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 3,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let mut channels = Vec::with_capacity(3);
for c in 0..3 {
let mut channel = Channel::new(width, height)?;
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) * 3 + c;
channel.set(x, y, data[idx] as i32);
}
}
channels.push(channel);
}
Ok(Self {
channels,
bit_depth: 8,
is_grayscale: false,
has_alpha: false,
})
}
pub fn from_rgba8(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(4))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 4,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let mut channels = Vec::with_capacity(4);
for c in 0..4 {
let mut channel = Channel::new(width, height)?;
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) * 4 + c;
channel.set(x, y, data[idx] as i32);
}
}
channels.push(channel);
}
Ok(Self {
channels,
bit_depth: 8,
is_grayscale: false,
has_alpha: true,
})
}
pub fn from_gray8(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width.checked_mul(height).ok_or(Error::DimensionOverflow {
width,
height,
channels: 1,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let mut channel = Channel::new(width, height)?;
for (i, &val) in data.iter().enumerate() {
let x = i % width;
let y = i / width;
channel.set(x, y, val as i32);
}
Ok(Self {
channels: vec![channel],
bit_depth: 8,
is_grayscale: true,
has_alpha: false,
})
}
pub fn from_rgb16(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(6))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 3,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let mut channels = Vec::with_capacity(3);
for c in 0..3 {
let mut channel = Channel::new(width, height)?;
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) * 6 + c * 2;
let val = u16::from_be_bytes([data[idx], data[idx + 1]]);
channel.set(x, y, val as i32);
}
}
channels.push(channel);
}
Ok(Self {
channels,
bit_depth: 16,
is_grayscale: false,
has_alpha: false,
})
}
pub fn from_rgb16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(6))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 3,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let pixels: &[u16] = bytemuck::cast_slice(data);
let mut channels = Vec::with_capacity(3);
for c in 0..3 {
let mut channel = Channel::new(width, height)?;
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) * 3 + c;
channel.set(x, y, pixels[idx] as i32);
}
}
channels.push(channel);
}
Ok(Self {
channels,
bit_depth: 16,
is_grayscale: false,
has_alpha: false,
})
}
pub fn from_rgba16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(8))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 4,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let pixels: &[u16] = bytemuck::cast_slice(data);
let mut channels = Vec::with_capacity(4);
for c in 0..4 {
let mut channel = Channel::new(width, height)?;
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) * 4 + c;
channel.set(x, y, pixels[idx] as i32);
}
}
channels.push(channel);
}
Ok(Self {
channels,
bit_depth: 16,
is_grayscale: false,
has_alpha: true,
})
}
pub fn from_grayalpha8(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(2))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 2,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let mut gray = Channel::new(width, height)?;
let mut alpha = Channel::new(width, height)?;
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) * 2;
gray.set(x, y, data[idx] as i32);
alpha.set(x, y, data[idx + 1] as i32);
}
}
Ok(Self {
channels: vec![gray, alpha],
bit_depth: 8,
is_grayscale: true,
has_alpha: true,
})
}
pub fn from_gray16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(2))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 1,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let pixels: &[u16] = bytemuck::cast_slice(data);
let mut channel = Channel::new(width, height)?;
for (i, &val) in pixels.iter().enumerate() {
let x = i % width;
let y = i / width;
channel.set(x, y, val as i32);
}
Ok(Self {
channels: vec![channel],
bit_depth: 16,
is_grayscale: true,
has_alpha: false,
})
}
pub fn from_grayalpha16_native(data: &[u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|n| n.checked_mul(4))
.ok_or(Error::DimensionOverflow {
width,
height,
channels: 2,
})?;
if data.len() != expected {
return Err(Error::InvalidImageDimensions(width, height));
}
let pixels: &[u16] = bytemuck::cast_slice(data);
let mut gray = Channel::new(width, height)?;
let mut alpha = Channel::new(width, height)?;
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) * 2;
gray.set(x, y, pixels[idx] as i32);
alpha.set(x, y, pixels[idx + 1] as i32);
}
}
Ok(Self {
channels: vec![gray, alpha],
bit_depth: 16,
is_grayscale: true,
has_alpha: true,
})
}
pub fn width(&self) -> usize {
self.channels.first().map_or(0, |c| c.width())
}
pub fn height(&self) -> usize {
self.channels.first().map_or(0, |c| c.height())
}
pub fn num_channels(&self) -> usize {
self.channels.len()
}
pub fn channel(&self, idx: usize) -> &Channel {
&self.channels[idx]
}
pub fn channel_mut(&mut self, idx: usize) -> &mut Channel {
&mut self.channels[idx]
}
pub fn extract_region(
&self,
x_start: usize,
y_start: usize,
x_end: usize,
y_end: usize,
) -> Result<Self> {
let region_width = x_end.saturating_sub(x_start);
let region_height = y_end.saturating_sub(y_start);
if region_width == 0 || region_height == 0 {
return Err(Error::InvalidImageDimensions(region_width, region_height));
}
let mut channels = Vec::with_capacity(self.channels.len());
for src_channel in &self.channels {
let mut dst_channel = Channel::new(region_width, region_height)?;
for dy in 0..region_height {
let sy = y_start + dy;
if sy >= src_channel.height() {
continue;
}
for dx in 0..region_width {
let sx = x_start + dx;
if sx >= src_channel.width() {
continue;
}
dst_channel.set(dx, dy, src_channel.get(sx, sy));
}
}
channels.push(dst_channel);
}
Ok(Self {
channels,
bit_depth: self.bit_depth,
is_grayscale: self.is_grayscale,
has_alpha: self.has_alpha,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_channel_creation() {
let channel = Channel::new(100, 100).unwrap();
assert_eq!(channel.width(), 100);
assert_eq!(channel.height(), 100);
assert_eq!(channel.len(), 10000);
}
#[test]
fn test_channel_access() {
let mut channel = Channel::new(10, 10).unwrap();
channel.set(5, 5, 42);
assert_eq!(channel.get(5, 5), 42);
}
#[test]
fn test_channel_clamped() {
let mut channel = Channel::new(10, 10).unwrap();
channel.set(0, 0, 100);
assert_eq!(channel.get_clamped(-1, -1), 0);
assert_eq!(channel.get_clamped(0, 0), 100);
assert_eq!(channel.get_clamped(100, 100), 0);
}
#[test]
fn test_modular_image_rgb8() {
let data = vec![
255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0, ];
let img = ModularImage::from_rgb8(&data, 2, 2).unwrap();
assert_eq!(img.num_channels(), 3);
assert_eq!(img.width(), 2);
assert_eq!(img.height(), 2);
assert_eq!(img.channel(0).get(0, 0), 255);
assert_eq!(img.channel(0).get(1, 0), 0);
assert_eq!(img.channel(1).get(0, 0), 0);
assert_eq!(img.channel(1).get(1, 0), 255);
assert_eq!(img.channel(2).get(0, 0), 0);
assert_eq!(img.channel(2).get(0, 1), 255);
}
}