#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum PixelMode {
#[default]
Mono1 = 0,
Mono8 = 1,
Rgb8 = 2,
Bgr8 = 3,
Xbgr8 = 4,
Cmyk8 = 5,
DeviceN8 = 6,
}
pub const NCOMPS: [usize; 7] = [
0, 1, 3, 3, 4, 4, 8, ];
impl PixelMode {
#[inline]
#[must_use]
pub const fn bytes_per_pixel(self) -> usize {
match self {
Self::Mono1 => 0,
Self::Mono8 => 1,
Self::Rgb8 | Self::Bgr8 => 3,
Self::Xbgr8 | Self::Cmyk8 => 4,
Self::DeviceN8 => 8,
}
}
#[inline]
#[must_use]
pub const fn bits_per_pixel(self) -> usize {
match self {
Self::Mono1 => 1,
Self::Mono8 => 8,
Self::Rgb8 | Self::Bgr8 => 24,
Self::Xbgr8 | Self::Cmyk8 => 32,
Self::DeviceN8 => 64,
}
}
#[inline]
#[must_use]
pub const fn is_packed_bits(self) -> bool {
matches!(self, Self::Mono1)
}
#[inline]
#[must_use]
pub const fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::Mono1),
1 => Some(Self::Mono8),
2 => Some(Self::Rgb8),
3 => Some(Self::Bgr8),
4 => Some(Self::Xbgr8),
5 => Some(Self::Cmyk8),
6 => Some(Self::DeviceN8),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn pixel_count_to_bytes(self, count: usize) -> Option<usize> {
if self.is_packed_bits() {
return None;
}
count.checked_mul(self.bytes_per_pixel())
}
}
#[cfg(test)]
mod tests {
use super::*;
const ALL: &[PixelMode] = &[
PixelMode::Mono1,
PixelMode::Mono8,
PixelMode::Rgb8,
PixelMode::Bgr8,
PixelMode::Xbgr8,
PixelMode::Cmyk8,
PixelMode::DeviceN8,
];
#[cfg(debug_assertions)]
#[test]
fn ncomps_matches_bytes_per_pixel() {
for &mode in ALL {
let i = mode as usize;
assert_eq!(
NCOMPS[i],
mode.bytes_per_pixel(),
"NCOMPS[{i}] disagrees with bytes_per_pixel for {mode:?}",
);
}
}
#[test]
fn from_u8_roundtrip() {
for &mode in ALL {
assert_eq!(PixelMode::from_u8(mode as u8), Some(mode));
}
}
#[test]
fn from_u8_invalid() {
for v in 7u8..=255 {
assert_eq!(PixelMode::from_u8(v), None);
}
}
#[test]
fn mono1_is_packed() {
assert!(PixelMode::Mono1.is_packed_bits());
for &mode in ALL.iter().skip(1) {
assert!(!mode.is_packed_bits());
}
}
#[test]
fn bits_per_pixel_nonzero() {
for &mode in ALL {
assert!(
mode.bits_per_pixel() > 0,
"{mode:?} has zero bits_per_pixel"
);
}
}
#[test]
fn bits_consistent_with_bytes() {
for &mode in ALL {
if !mode.is_packed_bits() {
assert_eq!(
mode.bits_per_pixel(),
mode.bytes_per_pixel() * 8,
"{mode:?}: bits_per_pixel != bytes_per_pixel * 8",
);
}
}
}
#[test]
fn pixel_count_to_bytes_mono1_is_none() {
assert_eq!(PixelMode::Mono1.pixel_count_to_bytes(0), None);
assert_eq!(PixelMode::Mono1.pixel_count_to_bytes(100), None);
}
#[test]
fn pixel_count_to_bytes_overflow_is_none() {
assert_eq!(PixelMode::Rgb8.pixel_count_to_bytes(usize::MAX), None);
}
#[test]
fn pixel_count_to_bytes_correct() {
assert_eq!(PixelMode::Mono8.pixel_count_to_bytes(5), Some(5));
assert_eq!(PixelMode::Rgb8.pixel_count_to_bytes(10), Some(30));
assert_eq!(PixelMode::Xbgr8.pixel_count_to_bytes(4), Some(16));
assert_eq!(PixelMode::Cmyk8.pixel_count_to_bytes(3), Some(12));
assert_eq!(PixelMode::DeviceN8.pixel_count_to_bytes(2), Some(16));
}
#[test]
fn pixel_count_to_bytes_zero_count() {
for &mode in ALL.iter().skip(1) {
assert_eq!(mode.pixel_count_to_bytes(0), Some(0));
}
}
}