From 4e0cbbe9738440f707e3d34d843101c5775432df Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 21 Apr 2023 12:37:59 +1200 Subject: [PATCH] Change BitDepth to u8 representation with TryFrom --- src/colors.rs | 70 +++++++++++++------------------------- src/headers.rs | 11 ++---- src/lib.rs | 2 +- src/png/mod.rs | 4 +-- src/reduction/bit_depth.rs | 4 +-- tests/filters.rs | 2 +- tests/regression.rs | 2 +- tests/strategies.rs | 2 +- 8 files changed, 34 insertions(+), 63 deletions(-) diff --git a/src/colors.rs b/src/colors.rs index 90c91266..b7a818c6 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -1,6 +1,8 @@ use rgb::{RGB16, RGBA8}; use std::fmt; +use crate::PngError; + #[derive(Debug, PartialEq, Eq, Clone)] /// The color type used to represent this image pub enum ColorType { @@ -67,64 +69,40 @@ impl ColorType { } } +#[repr(u8)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] /// The number of bits to be used per channel per pixel pub enum BitDepth { /// One bit per channel per pixel - One, + One = 1, /// Two bits per channel per pixel - Two, + Two = 2, /// Four bits per channel per pixel - Four, + Four = 4, /// Eight bits per channel per pixel - Eight, + Eight = 8, /// Sixteen bits per channel per pixel - Sixteen, + Sixteen = 16, +} + +impl TryFrom for BitDepth { + type Error = PngError; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(Self::One), + 2 => Ok(Self::Two), + 4 => Ok(Self::Four), + 8 => Ok(Self::Eight), + 16 => Ok(Self::Sixteen), + _ => Err(PngError::new("Unexpected bit depth")), + } + } } impl fmt::Display for BitDepth { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match *self { - BitDepth::One => "1", - BitDepth::Two => "2", - BitDepth::Four => "4", - BitDepth::Eight => "8", - BitDepth::Sixteen => "16", - } - ) - } -} - -impl BitDepth { - /// Retrieve the number of bits per channel per pixel as a `u8` - #[inline] - pub fn as_u8(self) -> u8 { - match self { - BitDepth::One => 1, - BitDepth::Two => 2, - BitDepth::Four => 4, - BitDepth::Eight => 8, - BitDepth::Sixteen => 16, - } - } - /// Parse a number of bits per channel per pixel into a `BitDepth` - /// - /// # Panics - /// - /// If depth is unsupported - #[inline] - pub fn from_u8(depth: u8) -> BitDepth { - match depth { - 1 => BitDepth::One, - 2 => BitDepth::Two, - 4 => BitDepth::Four, - 8 => BitDepth::Eight, - 16 => BitDepth::Sixteen, - _ => panic!("Unsupported bit depth"), - } + write!(f, "{}", *self as u8) } } diff --git a/src/headers.rs b/src/headers.rs index fa31101a..430ba731 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -32,7 +32,7 @@ impl IhdrData { #[must_use] #[inline] pub fn bpp(&self) -> usize { - (self.bit_depth.as_u8() * self.color_type.channels_per_pixel()) as usize + self.bit_depth as usize * self.color_type.channels_per_pixel() as usize } /// Byte length of IDAT that is correct for this IHDR @@ -173,14 +173,7 @@ pub fn parse_ihdr_header( 6 => ColorType::RGBA, _ => return Err(PngError::new("Unexpected color type in header")), }, - bit_depth: match byte_data[8] { - 1 => BitDepth::One, - 2 => BitDepth::Two, - 4 => BitDepth::Four, - 8 => BitDepth::Eight, - 16 => BitDepth::Sixteen, - _ => return Err(PngError::new("Unexpected bit depth in header")), - }, + bit_depth: byte_data[8].try_into()?, width: read_be_u32(&mut rdr).map_err(|_| PngError::TruncatedData)?, height: read_be_u32(&mut rdr).map_err(|_| PngError::TruncatedData)?, compression: byte_data[10], diff --git a/src/lib.rs b/src/lib.rs index fc6dda47..d88746b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -569,7 +569,7 @@ fn optimize_png( if filters.is_empty() { // Pick a filter automatically - if png.raw.ihdr.bit_depth.as_u8() >= 8 { + if png.raw.ihdr.bit_depth as u8 >= 8 { // Bigrams is the best all-rounder when there's at least one byte per pixel filters.insert(RowFilter::Bigrams); } else { diff --git a/src/png/mod.rs b/src/png/mod.rs index affff0e3..b3adb05b 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -144,7 +144,7 @@ impl PngData { ihdr_data .write_all(&self.raw.ihdr.height.to_be_bytes()) .ok(); - ihdr_data.write_all(&[self.raw.ihdr.bit_depth.as_u8()]).ok(); + ihdr_data.write_all(&[self.raw.ihdr.bit_depth as u8]).ok(); ihdr_data .write_all(&[self.raw.ihdr.color_type.png_header_code()]) .ok(); @@ -165,7 +165,7 @@ impl PngData { match &self.raw.ihdr.color_type { ColorType::Indexed { palette } => { let mut palette_data = Vec::with_capacity(palette.len() * 3); - let mut max_palette_size = 1 << (self.raw.ihdr.bit_depth.as_u8() as usize); + let mut max_palette_size = 1 << (self.raw.ihdr.bit_depth as u8); // Ensure bKGD color doesn't get truncated from palette if let Some(&idx) = self.raw.aux_headers.get(b"bKGD").and_then(|b| b.first()) { max_palette_size = max_palette_size.max(idx as usize + 1); diff --git a/src/reduction/bit_depth.rs b/src/reduction/bit_depth.rs index 55163099..ffa30ea1 100644 --- a/src/reduction/bit_depth.rs +++ b/src/reduction/bit_depth.rs @@ -32,7 +32,7 @@ pub fn reduce_bit_depth(png: &PngImage, minimum_bits: usize) -> Option #[must_use] pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Option { assert!((1..8).contains(&minimum_bits)); - let bit_depth: usize = png.ihdr.bit_depth.as_u8() as usize; + let bit_depth = png.ihdr.bit_depth as usize; if minimum_bits >= bit_depth || bit_depth > 8 { return None; } @@ -154,7 +154,7 @@ pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Op data: reduced, ihdr: IhdrData { color_type, - bit_depth: BitDepth::from_u8(minimum_bits as u8), + bit_depth: (minimum_bits as u8).try_into().unwrap(), ..png.ihdr }, aux_headers: png.aux_headers.clone(), diff --git a/tests/filters.rs b/tests/filters.rs index 57f0b4f8..065c1259 100644 --- a/tests/filters.rs +++ b/tests/filters.rs @@ -61,7 +61,7 @@ fn test_it_converts( assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out); assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out); if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type { - assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth.as_u8() as usize)); + assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth as u8)); } remove_file(output).ok(); diff --git a/tests/regression.rs b/tests/regression.rs index 8277ecaf..06833b25 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -70,7 +70,7 @@ fn test_it_converts( "optimized to wrong bit depth" ); if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type { - let mut max_palette_size = 1 << (png.raw.ihdr.bit_depth.as_u8() as usize); + let mut max_palette_size = 1 << (png.raw.ihdr.bit_depth as u8); // Ensure bKGD color is valid if let Some(&idx) = png.raw.aux_headers.get(b"bKGD").and_then(|b| b.first()) { assert!(palette.len() > idx as usize); diff --git a/tests/strategies.rs b/tests/strategies.rs index b7e16822..415014fe 100644 --- a/tests/strategies.rs +++ b/tests/strategies.rs @@ -60,7 +60,7 @@ fn test_it_converts( assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out); assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out); if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type { - assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth.as_u8() as usize)); + assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth as u8)); } remove_file(output).ok();