From 824f234c60371d6a374e5e062d21bbc982b8d21b Mon Sep 17 00:00:00 2001 From: reshane Date: Mon, 3 Feb 2025 20:46:27 -0500 Subject: [PATCH] Decode use Read not BufRead + Seek --- CHANGELOG.md | 9 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- src/color.rs | 1 - src/decoder.rs | 35 ++++++++++------------- src/encoder.rs | 63 ++++++++++++++---------------------------- src/error.rs | 36 ++++++++++-------------- src/lib.rs | 6 ++-- tests/decode_images.rs | 54 +++++++++++++++--------------------- tests/encode_images.rs | 37 ++++++++++--------------- 10 files changed, 101 insertions(+), 144 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5aab36f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Wbmp Changelog + +## Versions + +### 0.1.1 + - Change traits on Decoder.reader from `BufRead + Seek` to `Read` + +### 0.1.0 + - Initial implementation, basic encoding and decoding functionality diff --git a/Cargo.lock b/Cargo.lock index 4b2c425..933ea8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,4 @@ version = 4 [[package]] name = "wbmp" -version = "0.1.0" +version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index f76b9ab..9e900cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "wbmp" license = "MIT OR Apache-2.0" -version = "0.1.0" +version = "0.1.1" description = "WBMP encoder and decoder" authors = ["shane kenny "] readme = "README.md" diff --git a/src/color.rs b/src/color.rs index b28b182..c9294a3 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,4 +1,3 @@ - /// Color type of the image. /// /// Wbmp does not support color images. This enum is used to indicate the diff --git a/src/decoder.rs b/src/decoder.rs index 81e4a30..ffbbd82 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -1,10 +1,6 @@ +use std::io::Read; -use std::io::BufRead; -use std::io::Seek; - -use crate::error::{ - WbmpResult, WbmpError, -}; +use crate::error::{WbmpError, WbmpResult}; const CONTINUATION_MASK: u8 = 0b10000000; const DATA_MASK: u8 = 0b01111111; @@ -17,18 +13,17 @@ pub struct Decoder { height: u32, } -impl Decoder { - +impl Decoder { /// Create a new decoder that decodes from the stream ```reader```. /// /// # Errors /// - `WbmpError::UnsupportedType` occurs if the TypeField in the image - /// headers is not set to 0. + /// headers is not set to 0. /// - `WbmpError::UnsupportedHeaders` occurs if extension headers are - /// specified in the image. Extension headers are not required for type - /// 0 WBMP images. + /// specified in the image. Extension headers are not required for type + /// 0 WBMP images. /// - `WbmpError::IoError` occurs if the image headers are malformed or - /// if another IoError occurs while reading from the provided `reader` + /// if another IoError occurs while reading from the provided `reader` /// /// ## Examples /// ``` @@ -52,14 +47,13 @@ impl Decoder { height: 0, } } - + /// Returns the `(width, height)` of the image. pub fn dimensions(&self) -> (u32, u32) { (self.width, self.height) } fn read_metadata(&mut self) -> WbmpResult<()> { - // TypeField let image_type_buf: &mut [u8; 1] = &mut [0; 1]; self.reader.read_exact(image_type_buf)?; @@ -92,7 +86,7 @@ impl Decoder { loop { self.reader.read_exact(width_buf)?; println!("{:x}", width_buf[0]); - self.width = (self.width<<7) | (width_buf[0] & DATA_MASK) as u32; + self.width = (self.width << 7) | (width_buf[0] & DATA_MASK) as u32; if width_buf[0] & CONTINUATION_MASK != CONTINUATION_MASK { break; } @@ -103,8 +97,7 @@ impl Decoder { // if the continuation bit is set, shift & read the next byte as well loop { self.reader.read_exact(height_buf)?; - self.height = (self.height << 7) | - (height_buf[0] & DATA_MASK) as u32; + self.height = (self.height << 7) | (height_buf[0] & DATA_MASK) as u32; if height_buf[0] & CONTINUATION_MASK != CONTINUATION_MASK { break; } @@ -119,9 +112,9 @@ impl Decoder { // convert each row, ignoring padding past self.width let data_len = (self.width * self.height) as usize; if buf.len() < data_len { - return Err(WbmpError::UsageError( - String::from("Provided buffer does not have enough capacity") - )); + return Err(WbmpError::UsageError(String::from( + "Provided buffer does not have enough capacity", + ))); } let row_bytes = if self.width % 8 == 0 { @@ -140,7 +133,7 @@ impl Decoder { 'bytes: for byte in bit_data.iter() { let mut s = 7; 'bits: while read_idx < data_len { - if byte & (1< { } impl<'a, W: Write> Encoder<'a, W> { - /// Create a new decoder that decodes from the stream ```reader```. /// /// ## Examples @@ -45,7 +41,7 @@ impl<'a, W: Write> Encoder<'a, W> { /// Set the greyscale threshold to use. /// - /// Greyscale values below this threshold (inclusive) will result in + /// Greyscale values below this threshold (inclusive) will result in /// black pixels while values above will be white. Default 127. pub fn with_threshold(mut self, threshold: u8) -> Self { self.threshold = threshold; @@ -60,18 +56,18 @@ impl<'a, W: Write> Encoder<'a, W> { let mut a = a >> 7; while a > 0 { result.insert(0, 0b10000000 | ((a & BIT_MASK) as u8)); - a = a >> 7; + a >>= 7; } result } - /// Encodes the image `image` with dimensions `width` by `height` and + /// Encodes the image `image` with dimensions `width` by `height` and /// `ColorType` `color_type`. pub fn encode( - &mut self, - image: &[u8], - width: u32, - height: u32, + &mut self, + image: &[u8], + width: u32, + height: u32, color_type: ColorType, ) -> WbmpResult<()> { // write headers @@ -79,9 +75,9 @@ impl<'a, W: Write> Encoder<'a, W> { let width_bytes = Self::bytes_from_u32(width); let height_bytes = Self::bytes_from_u32(height); - self.writer.write(type_fix_header_fields)?; - self.writer.write(&width_bytes)?; - self.writer.write(&height_bytes)?; + let _ = self.writer.write(type_fix_header_fields)?; + let _ = self.writer.write(&width_bytes)?; + let _ = self.writer.write(&height_bytes)?; // map and write image data match color_type { @@ -91,12 +87,7 @@ impl<'a, W: Write> Encoder<'a, W> { Ok(()) } - fn encode_luma8( - &mut self, - image: &[u8], - width: u32, - height: u32, - ) -> WbmpResult<()> { + fn encode_luma8(&mut self, image: &[u8], width: u32, height: u32) -> WbmpResult<()> { let data_len = (width * height) as usize; if data_len != image.len() { return Err(WbmpError::InvalidImageData); @@ -105,18 +96,15 @@ impl<'a, W: Write> Encoder<'a, W> { for row in image.chunks(width as usize) { let mut byte: u8 = 0; let mut bits: u8 = 0; - for i in 0..row.len() { - - let pixel = row[i]; - - if pixel >= self.threshold { - byte |= 1<<(7-bits); + for (i, pixel) in row.iter().enumerate() { + if *pixel >= self.threshold { + byte |= 1 << (7 - bits); } bits += 1; if bits == 8 || i == width as usize - 1 { - self.writer.write(&[byte; 1])?; + let _ = self.writer.write(&[byte; 1])?; byte = 0; bits = 0; } @@ -127,12 +115,7 @@ impl<'a, W: Write> Encoder<'a, W> { Ok(()) } - fn encode_rgba8( - &mut self, - image: &[u8], - width: u32, - height: u32, - ) -> WbmpResult<()> { + fn encode_rgba8(&mut self, image: &[u8], width: u32, height: u32) -> WbmpResult<()> { let data_len = (width * height * 4) as usize; if data_len != image.len() { return Err(WbmpError::InvalidImageData); @@ -143,19 +126,16 @@ impl<'a, W: Write> Encoder<'a, W> { let mut byte: u8 = 0; let mut bits: u8 = 0; for (i, pixel) in row.chunks(4).enumerate() { - - let pixel = pixel.iter() - .map(|c| *c as usize) - .sum::() / 4; + let pixel = pixel.iter().map(|c| *c as usize).sum::() / 4; if pixel >= self.threshold as usize { - byte |= 1<<(7-bits); + byte |= 1 << (7 - bits); } bits += 1; if bits == 8 || i == width as usize - 1 { - self.writer.write(&[byte; 1])?; + let _ = self.writer.write(&[byte; 1])?; byte = 0; bits = 0; } @@ -165,7 +145,4 @@ impl<'a, W: Write> Encoder<'a, W> { Ok(()) } - } - - diff --git a/src/error.rs b/src/error.rs index e6b200d..c268c17 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,15 +1,15 @@ use std::error::Error; -use std::{io, fmt}; +use std::{fmt, io}; /// Wbmp error kinds #[derive(Debug)] pub enum WbmpError { /// An I/O Error occurred while decoding the image. IoError(io::Error), - /// An Unsupported Image Type Identifier was encountered while decoding + /// An Unsupported Image Type Identifier was encountered while decoding /// the image. UnsupportedType(u8), - /// Unsupported Extension headers were encountered while decoding the + /// Unsupported Extension headers were encountered while decoding the /// image. UnsupportedHeaders, /// The image does not support the requested operation @@ -22,24 +22,18 @@ impl fmt::Display for WbmpError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { WbmpError::IoError(ref e) => e.fmt(fmt), - WbmpError::UsageError(ref f) => write!( - fmt, - "The requested operation could not be completed {}", - f, - ), - WbmpError::UnsupportedType(ref f) => write!( - fmt, - "The Decoder does not support the image type `{}`", - f - ), - WbmpError::UnsupportedHeaders => write!( - fmt, - "The Decoder does not support extension headers", - ), - WbmpError::InvalidImageData => write!( - fmt, - "The Image data does not match the ColorType", - ), + WbmpError::UsageError(ref f) => { + write!(fmt, "The requested operation could not be completed {}", f,) + } + WbmpError::UnsupportedType(ref f) => { + write!(fmt, "The Decoder does not support the image type `{}`", f) + } + WbmpError::UnsupportedHeaders => { + write!(fmt, "The Decoder does not support extension headers",) + } + WbmpError::InvalidImageData => { + write!(fmt, "The Image data does not match the ColorType",) + } } } } diff --git a/src/lib.rs b/src/lib.rs index f31caf0..b5952c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,11 +5,11 @@ //! # Related Links //! * +pub mod color; pub mod decoder; pub mod encoder; -mod error; -pub mod color; +pub mod error; +pub use crate::color::ColorType; pub use crate::decoder::Decoder; pub use crate::encoder::Encoder; -pub use crate::color::ColorType; diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 75e220c..2062a5a 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -1,4 +1,3 @@ - use std::fs::File; use std::io::{BufReader, Cursor}; use std::path::PathBuf; @@ -14,16 +13,17 @@ fn test_decode_image() { const EXPECTED_DATA_LEN: usize = 272640; let image_file_name = "sample_640x426.wbmp"; let path = PathBuf::from(TEST_IMAGE_PREFIX).join(image_file_name); - let file = BufReader::new( - File::open(path).expect("Where is the test file?") - ); - let mut decoder = Decoder::new(file) - .expect("Decoder should be instantiable"); + let file = BufReader::new(File::open(path).expect("Where is the test file?")); + let mut decoder = Decoder::new(file).expect("Decoder should be instantiable"); let (width, height) = decoder.dimensions(); assert!(width == EXPECTED_WIDTH, "Decoded image width should be 640"); - assert!(height == EXPECTED_HEIGHT, "Decoded image height should be 426"); + assert!( + height == EXPECTED_HEIGHT, + "Decoded image height should be 426" + ); let mut img_data = [0_u8; EXPECTED_DATA_LEN]; - decoder.read_image_data(&mut img_data) + decoder + .read_image_data(&mut img_data) .expect("Decoder should be able to read image bytes"); } @@ -32,19 +32,20 @@ fn test_decode_4x4() { const EXPECTED_WIDTH: u32 = 2; const EXPECTED_HEIGHT: u32 = 2; const EXPECTED_DATA_LEN: usize = 4; - let image_bytes = [ - 0x00, 0x00, 0x02, 0x02, 0xC0, 0xC0 - ]; + let image_bytes = [0x00, 0x00, 0x02, 0x02, 0xC0, 0xC0]; let reader = BufReader::new(Cursor::new(&image_bytes)); - let mut decoder = Decoder::new(reader) - .expect("Decoder should be instantiable"); + let mut decoder = Decoder::new(reader).expect("Decoder should be instantiable"); let (width, height) = decoder.dimensions(); assert!(width == EXPECTED_WIDTH, "Decoded image width should be 2"); - assert!(height == EXPECTED_HEIGHT, "Decoded image height should be 2"); + assert!( + height == EXPECTED_HEIGHT, + "Decoded image height should be 2" + ); let mut decoded_bytes = [0_u8; EXPECTED_DATA_LEN]; - decoder.read_image_data(&mut decoded_bytes) + decoder + .read_image_data(&mut decoded_bytes) .expect("Decoder should be able to read image bytes"); for byte in decoded_bytes.iter() { assert!(*byte == 0xFF); @@ -58,28 +59,19 @@ fn test_decode_9x9() { const EXPECTED_DATA_LEN: usize = 81; let image_bytes = [ // headers - 0x00, 0x00, - // dimensions - 0x09, 0x09, - // image data - 0xFF, 0x80, - 0xFF, 0x80, - 0xFF, 0x80, - 0xFF, 0x80, - 0xFF, 0x80, - 0xFF, 0x80, - 0xFF, 0x80, - 0xFF, 0x80, - 0xFF, 0x80, + 0x00, 0x00, // dimensions + 0x09, 0x09, // image data + 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0xFF, + 0x80, 0xFF, 0x80, ]; let reader = BufReader::new(Cursor::new(&image_bytes)); - let mut decoder = Decoder::new(reader) - .expect("Decoder should be instantiable"); + let mut decoder = Decoder::new(reader).expect("Decoder should be instantiable"); let (width, height) = decoder.dimensions(); assert!(width == EXPECTED_WIDTH); assert!(height == EXPECTED_HEIGHT); let mut decoded_bytes = [0_u8; EXPECTED_DATA_LEN]; - decoder.read_image_data(&mut decoded_bytes) + decoder + .read_image_data(&mut decoded_bytes) .expect("Decoder should be able to read image bytes"); for byte in decoded_bytes.iter() { assert!(*byte == 0xFF); diff --git a/tests/encode_images.rs b/tests/encode_images.rs index 0b681ec..4e53d75 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -1,11 +1,10 @@ - use std::fs::File; use std::io::BufReader; use std::path::PathBuf; +use wbmp::ColorType; use wbmp::Decoder; use wbmp::Encoder; -use wbmp::ColorType; const TEST_IMAGE_PREFIX: &str = "./tests/images/"; @@ -14,11 +13,8 @@ fn test_encode_image() { // decode from file let image_file_name = "sample_640x426.wbmp"; let path = PathBuf::from(TEST_IMAGE_PREFIX).join(image_file_name); - let file = BufReader::new( - File::open(path).expect("Where is the test file?") - ); - let mut decoder = Decoder::new(file) - .expect("Decoder should be instantiable"); + let file = BufReader::new(File::open(path).expect("Where is the test file?")); + let mut decoder = Decoder::new(file).expect("Decoder should be instantiable"); let (width, height) = decoder.dimensions(); assert!(width == 640, "Decoded image width should be 640"); assert!(height == 426, "Decoded image height should be 426"); @@ -29,9 +25,9 @@ fn test_encode_image() { // re-encode let mut out_bytes = Vec::new(); let mut encoder = Encoder::new(&mut out_bytes); - encoder.encode( - image_luma8.as_slice(), width, height, ColorType::Luma8 - ).unwrap(); + encoder + .encode(image_luma8.as_slice(), width, height, ColorType::Luma8) + .unwrap(); // length assert!(out_bytes.len() == 6 + ((640 * 426) / 8)); // headers @@ -47,18 +43,17 @@ fn test_encode_image() { #[test] fn test_encode_2x2_luma8() { - const WIDTH: u32 = 2; + const WIDTH: u32 = 2; const HEIGHT: u32 = 2; - let img_dat = vec![ - 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, - ]; + let img_dat = vec![0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8]; // rows are 2 long // we expect 2 octets, one per row // 0b1100_0000 let mut out_bytes = Vec::new(); let mut encoder = Encoder::new(&mut out_bytes); - encoder.encode(img_dat.as_slice(), WIDTH, HEIGHT, ColorType::Luma8) + encoder + .encode(img_dat.as_slice(), WIDTH, HEIGHT, ColorType::Luma8) .expect("Data should be encodeable"); // headers assert!(out_bytes[0] == 0x00); @@ -74,13 +69,11 @@ fn test_encode_2x2_luma8() { #[test] fn test_encode_2x2_rgba8() { - const WIDTH: u32 = 2; + const WIDTH: u32 = 2; const HEIGHT: u32 = 2; let img_dat = vec![ - 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, - 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, - 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, - 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, + 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, + 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, 0xFF_u8, ]; // rows are 2 long // we expect 2 octets, one per row @@ -88,7 +81,8 @@ fn test_encode_2x2_rgba8() { let mut out_bytes = Vec::new(); let mut encoder = Encoder::new(&mut out_bytes); - encoder.encode(img_dat.as_slice(), WIDTH, HEIGHT, ColorType::Rgba8) + encoder + .encode(img_dat.as_slice(), WIDTH, HEIGHT, ColorType::Rgba8) .expect("Data should be encodeable"); // headers assert!(out_bytes[0] == 0x00); @@ -101,4 +95,3 @@ fn test_encode_2x2_rgba8() { assert!(out_bytes[4] == 0xC0); assert!(out_bytes[5] == 0xC0); } -