use std::borrow::Cow;
use crate::pixel::{MAX_PIXEL_SIZE, PlainChannel, PlainPixel};
pub(crate) unsafe fn as_bytes<P: PlainChannel>(data: &[P]) -> &[u8] {
let byte_size = std::mem::size_of_val(data);
let ptr = data.as_ptr() as *const u8;
unsafe { std::slice::from_raw_parts(ptr, byte_size) }
}
pub(crate) unsafe fn as_mut_bytes<P: PlainPixel>(data: &mut [P]) -> &mut [u8] {
let byte_size = std::mem::size_of_val(data);
let ptr = data.as_mut_ptr() as *mut u8;
unsafe { std::slice::from_raw_parts_mut(ptr, byte_size) }
}
#[cfg(target_endian = "little")]
pub(crate) unsafe fn as_bytes_le<P: PlainPixel>(data: &[P]) -> Cow<'_, [u8]> {
Cow::Borrowed(unsafe { as_bytes(data) })
}
#[cfg(target_endian = "big")]
pub(crate) unsafe fn as_bytes_be<P: PlainPixel>(data: &[P]) -> Cow<'_, [u8]> {
Cow::Borrowed(unsafe { as_bytes(data) })
}
#[cfg(target_endian = "little")]
pub(crate) unsafe fn as_bytes_be<P: PlainPixel>(data: &[P]) -> Cow<'_, [u8]> {
Cow::Owned(as_bytes_changed_endian(data))
}
#[cfg(target_endian = "big")]
pub(crate) unsafe fn as_bytes_le<P: PlainPixel>(data: &[P]) -> Cow<'_, [u8]> {
Cow::Owned(as_bytes_changed_endian(data))
}
fn as_bytes_changed_endian<P: PlainPixel>(data: &[P]) -> Vec<u8> {
let bytes = unsafe { as_bytes(data) };
let pixel_size = <P as PlainChannel>::SIZE;
let mut be_bytes = Vec::with_capacity(bytes.len());
for i in 0..data.len() {
let mut offset = i * pixel_size;
for &size in P::CHANNELS {
let channel_bytes = &bytes[offset..offset + size];
be_bytes.extend(channel_bytes.iter().rev());
offset += size;
}
}
be_bytes
}
pub(crate) fn from_bytes<T: PlainChannel>(bytes: &[u8]) -> Option<T> {
if bytes.len() != std::mem::size_of::<T>() {
return None;
}
Some(unsafe { std::ptr::read_unaligned(bytes.as_ptr() as *const T) })
}
#[cfg(target_endian = "little")]
pub(crate) fn from_bytes_le<T: PlainPixel>(bytes: &[u8]) -> Option<T> {
from_bytes(bytes)
}
#[cfg(target_endian = "big")]
pub(crate) fn from_bytes_be<T: PlainPixel>(bytes: &[u8]) -> Option<T> {
from_bytes(bytes)
}
#[cfg(target_endian = "little")]
pub(crate) fn from_bytes_be<T: PlainPixel>(bytes: &[u8]) -> Option<T> {
from_bytes_changed_endian(bytes)
}
#[cfg(target_endian = "big")]
pub(crate) fn from_bytes_le<T: PlainPixel>(bytes: &[u8]) -> Option<T> {
from_bytes_changed_endian(bytes)
}
fn from_bytes_changed_endian<T: PlainPixel>(bytes: &[u8]) -> Option<T> {
if bytes.len() != <T as PlainChannel>::SIZE {
return None;
}
assert!(
<T as PlainChannel>::SIZE <= MAX_PIXEL_SIZE,
"pixel type is larger than the stack buffer ({} > {})",
<T as PlainChannel>::SIZE,
MAX_PIXEL_SIZE
);
let mut buf = [0u8; MAX_PIXEL_SIZE];
let swapped = &mut buf[..<T as PlainChannel>::SIZE];
let mut offset = 0;
for &size in T::CHANNELS {
let channel_bytes = &bytes[offset..offset + size];
swapped[offset..offset + size].copy_from_slice(channel_bytes);
swapped[offset..offset + size].reverse();
offset += size;
}
from_bytes(swapped)
}
#[inline]
pub(crate) fn in_bounds(size: &crate::Size, x: usize, y: usize) -> bool {
x < size.width && y < size.height
}
#[inline]
pub(crate) fn checked_index_or_panic(size: &crate::Size, x: usize, y: usize) -> usize {
assert!(
x < size.width && y < size.height,
"pixel index out of bounds: ({x}, {y}) is not inside {}x{}",
size.width,
size.height
);
y.checked_mul(size.width)
.and_then(|row_start| row_start.checked_add(x))
.unwrap_or_else(|| {
panic!(
"pixel index arithmetic overflowed usize: ({x}, {y}) on {}x{} image",
size.width, size.height
)
})
}
#[inline]
pub(crate) fn checked_row_range_or_panic(size: &crate::Size, y: usize) -> (usize, usize) {
assert!(
y < size.height,
"row index out of bounds: {y} is not less than height {}",
size.height
);
let start = y.checked_mul(size.width).unwrap_or_else(|| {
panic!(
"row index arithmetic overflowed usize: y={y} on width={}",
size.width
)
});
let end = start.checked_add(size.width).unwrap_or_else(|| {
panic!(
"row index arithmetic overflowed usize: y={y} on width={}",
size.width
)
});
(start, end)
}
#[inline]
pub(crate) fn index(size: &crate::Size, x: usize, y: usize) -> usize {
y * size.width + x
}