From d4d87fffe3539eb0aa2afdf4c01f3dbfe78cbf0d Mon Sep 17 00:00:00 2001 From: Nihil Carcosa Date: Wed, 13 Nov 2024 23:55:53 +0100 Subject: [PATCH] changes to package name. added help command. --- Cargo.lock | 4 +- Cargo.toml | 4 +- src/lib.rs | 605 ++++++++++++++++++++++++++++------------------------ src/main.rs | 7 +- 4 files changed, 335 insertions(+), 285 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97439b3..6fb00cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,8 +82,8 @@ dependencies = [ ] [[package]] -name = "qoi-test" -version = "0.1.0" +name = "qoi" +version = "0.1.1" dependencies = [ "array-init", "colors-transform", diff --git a/Cargo.toml b/Cargo.toml index 3c03564..3751b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "qoi-test" -version = "0.1.0" +name = "qoi" +version = "0.1.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/lib.rs b/src/lib.rs index 36214f0..1a85fb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,12 @@ //! This crate should not be published as better crates are available, e.g. [rapid-qoi](https://crates.io/crates/rapid-qoi). #![allow(dead_code, unused_variables)] -pub mod qoi_img { +pub mod qoi_lib { + use log::{debug, info, Level, LevelFilter, Record, SetLoggerError}; use std::fmt; - use std::io::prelude::*; use std::fs::File; - use log::{debug,info, Level, Record, SetLoggerError, LevelFilter}; + use std::io::prelude::*; use array_init; @@ -23,20 +23,22 @@ pub mod qoi_img { //inherit from base Error impl std::error::Error for ImgError {} - //Output for error handling impl fmt::Display for ImgError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - ImgError::DataError => write!(f, "invalid number of bytes (must be devisible by 4)"), - ImgError::PixelNumberError => write!(f, "number of pixels does not match height and width params"), + ImgError::DataError => { + write!(f, "invalid number of bytes (must be devisible by 4)") + } + ImgError::PixelNumberError => { + write!(f, "number of pixels does not match height and width params") + } ImgError::DecodeError => write!(f, "decoder failed to construct valid image"), - ImgError::HeaderError => write!(f, "not a valid QOI file header") - } + ImgError::HeaderError => write!(f, "not a valid QOI file header"), + } } } - //boilerplate implementation of the log crate struct SimpleLogger; @@ -49,15 +51,13 @@ pub mod qoi_img { eprintln!("{} - {}", record.level(), record.args()); } } - fn flush(&self) { - - } + fn flush(&self) {} } //logging boilerplate static LOGGER: SimpleLogger = SimpleLogger; /// Initialises the logger provided by [log](https://crates.io/crates/log) /// # Example - /// + /// /// ``` /// # use std::error::Error; /// # use crate::qoi_test::qoi_img::*; @@ -65,12 +65,12 @@ pub mod qoi_img { /// init().expect("Failed to initialize logger"); /// # /// # Ok(()) - /// # + /// # /// # } /// ``` - /// + /// /// If you want to pass the error on replace the `println!`: - /// + /// /// ``` /// # use std::error::Error; /// # use crate::qoi_test::qoi_img::*; @@ -81,14 +81,13 @@ pub mod qoi_img { /// } /// # /// # Ok(()) - /// # + /// # /// # } /// ``` - pub fn init() -> Result<(), SetLoggerError>{ + pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Debug)) } - /// Custom image struct, which is used to store decoded data. Used by [encode_from_image] to encode the necessary data in bytes. Requires a Vector over [Pixel] values, `Vec`, /// which can be generated by [`self::new`] if given byte data. Otherwise, [self.pixels] must be given filled vector. /// `height` and `width` are given as u32 (note that qoi encoding does not guarantee functionality for images containing over 4000000 pixels.) @@ -100,7 +99,7 @@ pub mod qoi_img { /// # use std::error::Error; /// # use crate::qoi_test::qoi_img::*; /// # fn main() -> Result<(), Box> { - /// + /// /// let pixels: Vec = vec![0;1024*1024*4]; /// let height: u32 = 1024; /// let width: u32 = 1024; @@ -111,7 +110,7 @@ pub mod qoi_img { /// # Ok(()) /// # } /// ``` - /// + /// /// Alternatively, [`Image::from_pixels()`] can be used to create an image from pixel values. pub struct Image { pixels: Vec, @@ -123,13 +122,19 @@ pub mod qoi_img { impl Image { //Image constructor, expects an array of u8 pixels values in order, left to right, top to bottom. - pub fn new(data: Vec, height: u32, width: u32, channels: u8, colorspace: u8,) -> Result { + pub fn new( + data: Vec, + height: u32, + width: u32, + channels: u8, + colorspace: u8, + ) -> Result { let pixels: Vec = match Image::pixels_from_bytes(data) { Ok(out) => out, Err(error) => return Err(error), }; if pixels.len() == (height * width) as usize { - let out: Image = Image { + let out: Image = Image { pixels, height, width, @@ -140,29 +145,34 @@ pub mod qoi_img { } else { Err(ImgError::PixelNumberError) } - } - pub fn from_pixels(pixels: Vec, height: u32, width: u32, channels: u8, colorspace: u8) -> Image { + pub fn from_pixels( + pixels: Vec, + height: u32, + width: u32, + channels: u8, + colorspace: u8, + ) -> Image { let img = Image { pixels, height, width, channels, - colorspace + colorspace, }; img } fn pixels_from_bytes(data: Vec) -> Result, ImgError> { - let mut pixels: Vec = Vec::with_capacity(data.len()/4); + let mut pixels: Vec = Vec::with_capacity(data.len() / 4); if data.len() % 4 == 0 { - for i in 0..data.len()/4 { + for i in 0..data.len() / 4 { pixels.push(Pixel { - r: data[i*4], - g: data[i*4+1], - b: data[i*4+2], - a: data[i*4+3], + r: data[i * 4], + g: data[i * 4 + 1], + b: data[i * 4 + 2], + a: data[i * 4 + 3], }); } Ok(pixels) @@ -172,12 +182,12 @@ pub mod qoi_img { } } - #[derive(Clone,Copy,Debug, PartialEq)] + #[derive(Clone, Copy, Debug, PartialEq)] pub struct Pixel { - r: u8, + r: u8, g: u8, b: u8, - a: u8, + a: u8, } #[derive(Debug, PartialEq)] @@ -187,22 +197,19 @@ pub mod qoi_img { Luma, Diff, RGB, - RGBA + RGBA, } impl Pixel { - - pub fn new(r: u8, g: u8, b: u8, a: u8) -> Pixel { - Pixel { - r, - g, - b, - a, - } + Pixel { r, g, b, a } } fn equals(&self, other: &Pixel) -> bool { - if (self.r == other.r) && (self.g == other.g) && (self.b == other.b) && (self.a == other.a) { + if (self.r == other.r) + && (self.g == other.g) + && (self.b == other.b) + && (self.a == other.a) + { true } else { false @@ -217,23 +224,25 @@ pub mod qoi_img { } } - //self = curr pixel, other = prev pixel - pub fn determine_chunk(&self, other: &Pixel, buffer: &Vec) -> (ChunkType, Option<(u8,u8,u8)>){ - + pub fn determine_chunk( + &self, + other: &Pixel, + buffer: &Vec, + ) -> (ChunkType, Option<(u8, u8, u8)>) { if self.equals(&other) { return (ChunkType::Run, None); } - + if self.equals(&buffer[color_hash(&self) as usize]) { - return (ChunkType::Index,Some((color_hash(&self), 0,0 ))); + return (ChunkType::Index, Some((color_hash(&self), 0, 0))); } if self.a != other.a { return (ChunkType::RGBA, None); - } + } - let diff_tuple: (i16,i16,i16) = self.diff(other); + let diff_tuple: (i16, i16, i16) = self.diff(other); let dr: i16 = diff_tuple.0; let dg: i16 = diff_tuple.1; let db: i16 = diff_tuple.2; @@ -243,7 +252,12 @@ pub mod qoi_img { let dg: u8 = (dg + DIFF_BIAS as i16) as u8; let db: u8 = (db + DIFF_BIAS as i16) as u8; return (ChunkType::Diff, Some((dr, dg, db))); - } else if (dg > -33 && dg < 32) && ((dr - dg) > -9) && ((dr - dg) < 8) && ((db - dg) > -9) && ((db - dg) < 8) { + } else if (dg > -33 && dg < 32) + && ((dr - dg) > -9) + && ((dr - dg) < 8) + && ((db - dg) > -9) + && ((db - dg) < 8) + { let dg_out: u8 = (dg + LUMA_BIAS_G as i16) as u8; let dr_dg: u8 = (dr - dg + LUMA_BIAS_RB as i16) as u8; let db_dg: u8 = (db - dg + LUMA_BIAS_RB as i16) as u8; @@ -251,7 +265,6 @@ pub mod qoi_img { } else { return (ChunkType::RGB, None); } - } pub fn diff(&self, other: &Pixel) -> (i16, i16, i16) { let mut dr: i16; @@ -276,7 +289,7 @@ pub mod qoi_img { dg = dg_inv; dg = -dg; } - + db = self.b.wrapping_sub(other.b) as i16; db_inv = other.b.wrapping_sub(self.b) as i16; @@ -286,39 +299,38 @@ pub mod qoi_img { } (dr, dg, db) - } } //Definition of header bytes struct Header { - magic: [char; 4], //magic bytes "qoif" - width: u32, //image width in pixels (BE) - height: u32, //image height in pixels (BE) - channels: u8, // 3 = RGB, 4 = RBGA - colorspace: u8, // 0 = sRGB with linear alpha, 1 = all channels linear + magic: [char; 4], //magic bytes "qoif" + width: u32, //image width in pixels (BE) + height: u32, //image height in pixels (BE) + channels: u8, // 3 = RGB, 4 = RBGA + colorspace: u8, // 0 = sRGB with linear alpha, 1 = all channels linear } impl Header { - fn convert_to_bytestream(&self) -> [u8;14] { - let mut out: [u8; 14] = [0;14]; + fn convert_to_bytestream(&self) -> [u8; 14] { + let mut out: [u8; 14] = [0; 14]; //First, set magic bytes - out[0] = self.magic[0] as u8; - out[1] = self.magic[1] as u8; - out[2] = self.magic[2] as u8; - out[3] = self.magic[3] as u8; + out[0] = self.magic[0] as u8; + out[1] = self.magic[1] as u8; + out[2] = self.magic[2] as u8; + out[3] = self.magic[3] as u8; //split width and height into 8-bit chunks let width_bytes = self.width.to_be_bytes(); let height_bytes = self.height.to_be_bytes(); - - out[4] = width_bytes[0]; - out[5] = width_bytes[1]; - out[6] = width_bytes[2]; - out[7] = width_bytes[3]; - out[8] = height_bytes[0]; - out[9] = height_bytes[1]; + + out[4] = width_bytes[0]; + out[5] = width_bytes[1]; + out[6] = width_bytes[2]; + out[7] = width_bytes[3]; + out[8] = height_bytes[0]; + out[9] = height_bytes[1]; out[10] = height_bytes[2]; out[11] = height_bytes[3]; @@ -333,64 +345,70 @@ pub mod qoi_img { //Definition of End of Stream bytes #[derive(Debug)] struct End { - bytes: [u8;8] + bytes: [u8; 8], } impl End { fn new() -> End { End { - bytes: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01] + bytes: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], } } } //chunks as defined in the QOI spec - const QOI_OP_RGB: u8 = 0b1111_1110; - const QOI_OP_RGBA: u8 = 0b1111_1111; - const QOI_OP_RUN: u8 = 0b1100_0000; + const QOI_OP_RGB: u8 = 0b1111_1110; + const QOI_OP_RGBA: u8 = 0b1111_1111; + const QOI_OP_RUN: u8 = 0b1100_0000; const QOI_OP_INDEX: u8 = 0b0000_0000; - const QOI_OP_DIFF: u8 = 0b0100_0000; - const QOI_OP_LUMA: u8 = 0b1000_0000; + const QOI_OP_DIFF: u8 = 0b0100_0000; + const QOI_OP_LUMA: u8 = 0b1000_0000; //Biases as defined in the QOI spec - const RUN_BIAS: u8 = 1; + const RUN_BIAS: u8 = 1; - const DIFF_BIAS: u8 = 2; - - const LUMA_BIAS_G: u8 = 32; - const LUMA_BIAS_RB: u8 = 8; + const DIFF_BIAS: u8 = 2; + const LUMA_BIAS_G: u8 = 32; + const LUMA_BIAS_RB: u8 = 8; - //hash function for assigning buffer indices to stored pixels fn color_hash(pixel: &Pixel) -> u8 { - let store: u32 = pixel.r as u32 * 3 + pixel.g as u32 * 5 + pixel.b as u32 * 7 + pixel.a as u32 * 11; + let store: u32 = + pixel.r as u32 * 3 + pixel.g as u32 * 5 + pixel.b as u32 * 7 + pixel.a as u32 * 11; (store % 64) as u8 } pub fn encode_from_image(img: Image) -> Vec { - - let mut prev_pixel: Pixel = Pixel {r: 0u8, b: 0u8, g: 0u8, a: 255u8}; + let mut prev_pixel: Pixel = Pixel { + r: 0u8, + b: 0u8, + g: 0u8, + a: 255u8, + }; let mut prev_buffer: Vec = Vec::with_capacity(64); for i in 0..64 { - let pix: Pixel = Pixel {r:0,g:0,b:0,a:0}; + let pix: Pixel = Pixel { + r: 0, + g: 0, + b: 0, + a: 0, + }; prev_buffer.push(pix); } let mut encoded_bytes: Vec = Vec::new(); let mut run: u8 = 0; - let head = Header { magic: ['q', 'o', 'i', 'f'], width: img.width, height: img.height, channels: img.channels, - colorspace: img.colorspace + colorspace: img.colorspace, }; let head_stream = head.convert_to_bytestream(); - for i in head_stream { encoded_bytes.push(i); @@ -400,7 +418,8 @@ pub mod qoi_img { for pixel in img.pixels { counter += 1; - let chunk: (ChunkType, Option<(u8,u8,u8)>) = pixel.determine_chunk(&prev_pixel, &prev_buffer); + let chunk: (ChunkType, Option<(u8, u8, u8)>) = + pixel.determine_chunk(&prev_pixel, &prev_buffer); if chunk == (ChunkType::Run, None) { run += 1; prev_pixel = pixel.clone(); @@ -409,28 +428,28 @@ pub mod qoi_img { if run > 0 { if run > 62 { while run > 0 { - if run/62 > 0 { - encoded_bytes.push(QOI_OP_RUN | (62-RUN_BIAS)); + if run / 62 > 0 { + encoded_bytes.push(QOI_OP_RUN | (62 - RUN_BIAS)); run -= 62; - } else if run%62 > 0 { - encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS)); + } else if run % 62 > 0 { + encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS)); run = 0; } else { break; } } } else { - encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS)); + encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS)); run = 0; } } - + match chunk { (ChunkType::Index, Some((index, irr1, irr2))) => { encoded_bytes.push(QOI_OP_INDEX | index); prev_pixel = pixel; - }, - (ChunkType::Diff, Some((dr, dg,db))) => { + } + (ChunkType::Diff, Some((dr, dg, db))) => { let mut out: u8 = 0b0000_0000; out = out | db; out = out | (dg << 2); @@ -438,9 +457,9 @@ pub mod qoi_img { encoded_bytes.push(QOI_OP_DIFF | out); prev_pixel = pixel.clone(); prev_buffer[color_hash(&pixel) as usize] = pixel; - }, + } (ChunkType::Luma, Some((dg, dr_dg, db_dg))) => { - let mut out: [u8; 2] = [0b0000_0000;2]; + let mut out: [u8; 2] = [0b0000_0000; 2]; out[0] |= dg; out[0] |= QOI_OP_LUMA; out[1] |= db_dg; @@ -449,15 +468,15 @@ pub mod qoi_img { encoded_bytes.push(out[1]); prev_pixel = pixel.clone(); prev_buffer[color_hash(&pixel) as usize] = pixel; - }, + } (ChunkType::RGB, None) => { encoded_bytes.push(QOI_OP_RGB); encoded_bytes.push(pixel.r); encoded_bytes.push(pixel.g); encoded_bytes.push(pixel.b); prev_pixel = pixel.clone(); - prev_buffer[color_hash(&pixel) as usize] = pixel; - }, + prev_buffer[color_hash(&pixel) as usize] = pixel; + } (ChunkType::RGBA, None) => { if (pixel.a as i16 - prev_pixel.a as i16) == 0i16 { //this should never be reached, but it is @@ -466,7 +485,7 @@ pub mod qoi_img { encoded_bytes.push(pixel.g); encoded_bytes.push(pixel.b); prev_pixel = pixel.clone(); - prev_buffer[color_hash(&pixel) as usize] = pixel; + prev_buffer[color_hash(&pixel) as usize] = pixel; } else { encoded_bytes.push(QOI_OP_RGBA); encoded_bytes.push(pixel.r); @@ -476,27 +495,28 @@ pub mod qoi_img { prev_pixel = pixel.clone(); prev_buffer[color_hash(&pixel) as usize] = pixel; } - }, - _ => panic!("Critical error at encoding stage: Illegal output from difference function.") + } + _ => panic!( + "Critical error at encoding stage: Illegal output from difference function." + ), } - } if run > 0 { if run > 62 { while run > 0 { - if run/62 > 0 { - encoded_bytes.push(QOI_OP_RUN | (62-RUN_BIAS)); + if run / 62 > 0 { + encoded_bytes.push(QOI_OP_RUN | (62 - RUN_BIAS)); run -= 62; - } else if run%62 > 0 { - encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS)); + } else if run % 62 > 0 { + encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS)); run = 0; } else { break; } } } else { - encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS)); + encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS)); } } @@ -506,17 +526,22 @@ pub mod qoi_img { } info!("Number of pixels processed: {}.", counter); - info!("Number of bytes in encoding: {:?}.", encoded_bytes.len()-22); - info!("Compression rate: {:.2}%.", (1.0-(encoded_bytes.len()-22) as f64/(counter*4)as f64)*100.0); - + info!( + "Number of bytes in encoding: {:?}.", + encoded_bytes.len() - 22 + ); + info!( + "Compression rate: {:.2}%.", + (1.0 - (encoded_bytes.len() - 22) as f64 / (counter * 4) as f64) * 100.0 + ); + encoded_bytes - } - - pub fn write_to_file(bytes: Vec, filename: &str) -> std::io::Result<()>{ + + pub fn write_to_file(bytes: Vec, filename: &str) -> std::io::Result<()> { let mut file_path: String = String::from(filename); file_path.push_str(".qoi"); - + let mut buffer = File::create(file_path)?; let mut pos = 0; @@ -527,32 +552,36 @@ pub mod qoi_img { Ok(()) } - fn read_header(bytes: &[u8]) -> Result<(u32,u32,u8,u8), ImgError> { - if bytes[0] == 'q' as u8 && bytes[1] == 'o' as u8 && bytes[2] == 'i' as u8 && bytes[3] == 'f' as u8 { - let mut width: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000; + fn read_header(bytes: &[u8]) -> Result<(u32, u32, u8, u8), ImgError> { + if bytes[0] == 'q' as u8 + && bytes[1] == 'o' as u8 + && bytes[2] == 'i' as u8 + && bytes[3] == 'f' as u8 + { + let mut width: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000; let mut height: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000; - width |= ((bytes[4] as u32) << 24) as u32; - width |= ((bytes[5] as u32) << 16) as u32; - width |= ((bytes[6] as u32) << 8) as u32; - width |= (bytes[7]) as u32; - height |= ((bytes[8] as u32) << 24) as u32; - height |= ((bytes[9] as u32) << 16) as u32; - height |= ((bytes[10] as u32) << 8) as u32; - height |= (bytes[11]) as u32; + width |= ((bytes[4] as u32) << 24) as u32; + width |= ((bytes[5] as u32) << 16) as u32; + width |= ((bytes[6] as u32) << 8) as u32; + width |= (bytes[7]) as u32; + height |= ((bytes[8] as u32) << 24) as u32; + height |= ((bytes[9] as u32) << 16) as u32; + height |= ((bytes[10] as u32) << 8) as u32; + height |= (bytes[11]) as u32; return Ok((width, height, bytes[12], bytes[13])); } else { return Err(ImgError::HeaderError); } } - fn read_tag(tag: u8) -> Result { + fn read_tag(tag: u8) -> Result { if tag == QOI_OP_RGB { return Ok(ChunkType::RGB); - } + } if tag == QOI_OP_RGBA { return Ok(ChunkType::RGBA); } - if (tag & 0b1100_0000) == QOI_OP_DIFF{ + if (tag & 0b1100_0000) == QOI_OP_DIFF { return Ok(ChunkType::Diff); } if (tag & 0b1100_0000) == QOI_OP_INDEX { @@ -584,8 +613,8 @@ pub mod qoi_img { dr = (byte & 0b00110000) >> 4; dg = (byte & 0b00001100) >> 2; - db = byte & 0b00000011; - + db = byte & 0b00000011; + let r: u8 = prev_pixel.r.wrapping_add(dr); let g: u8 = prev_pixel.g.wrapping_add(dg); let b: u8 = prev_pixel.b.wrapping_add(db); @@ -594,7 +623,7 @@ pub mod qoi_img { let b: u8 = b.wrapping_sub(DIFF_BIAS); let g: u8 = g.wrapping_sub(DIFF_BIAS); - let pixel: Pixel = Pixel::new(r,g,b, prev_pixel.a); + let pixel: Pixel = Pixel::new(r, g, b, prev_pixel.a); pixel } @@ -605,9 +634,9 @@ pub mod qoi_img { let dg: u8; let db: u8; - dg = bytes[0] & 0b00111111; + dg = bytes[0] & 0b00111111; dr_dg = (bytes[1] & 0b11110000) >> 4; - db_dg = bytes[1] & 0b00001111; + db_dg = bytes[1] & 0b00001111; dr = dr_dg + dg; db = db_dg + dg; @@ -624,33 +653,38 @@ pub mod qoi_img { } fn dec_run() {} - + pub fn decode(mut bytes: Vec) -> Result { let width: u32; let height: u32; let channels: u8; let colorspace: u8; - let mut prev_pixel: Pixel = Pixel {r: 0u8, g: 0u8, b: 0u8, a: 255u8}; + let mut prev_pixel: Pixel = Pixel { + r: 0u8, + g: 0u8, + b: 0u8, + a: 255u8, + }; + + let mut prev_buffer: [Pixel; 64] = array_init::array_init(|_| Pixel::new(0, 0, 0, 0)); - let mut prev_buffer: [Pixel; 64] = array_init::array_init(|_| Pixel::new(0,0,0,0)); - match read_header(&bytes[0..14]) { - Ok((w, h, ch, c))=> { + Ok((w, h, ch, c)) => { width = w; height = h; channels = ch; colorspace = c; - }, + } Err(err) => { return Err(err); } } - let mut pixels: Vec = Vec::with_capacity((width*height*4) as usize); + let mut pixels: Vec = Vec::with_capacity((width * height * 4) as usize); - if bytes[bytes.len()-1] == 1 { + if bytes[bytes.len() - 1] == 1 { for i in 2..9 { - if bytes[bytes.len()-i] != 0 { + if bytes[bytes.len() - i] != 0 { debug!("Ending bytes not present."); return Err(ImgError::DecodeError); } @@ -662,53 +696,51 @@ pub mod qoi_img { debug!("Ending bytes not present."); return Err(ImgError::DecodeError); } - + let mut i: usize = 14; while i < bytes.len() { match read_tag(bytes[i]) { - Ok(tag) => { - match tag { - ChunkType::RGB => { - let dec_pix: Pixel = dec_rgb(&bytes[i..i+4], prev_pixel.a); - prev_pixel = dec_pix.clone(); - prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); - pixels.push(dec_pix); - i += 3; - }, - ChunkType::RGBA => { - let dec_pix: Pixel = dec_rgba(&bytes[i..i+5]); - prev_pixel = dec_pix.clone(); - prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); - pixels.push(dec_pix); - i += 4; - }, - ChunkType::Diff => { - let dec_pix: Pixel = dec_diff(bytes[i], &prev_pixel); - prev_pixel = dec_pix.clone(); - prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); - pixels.push(dec_pix); - }, - ChunkType::Index => { - let dec_pix: Pixel = prev_buffer[bytes[i] as usize]; - prev_pixel = dec_pix.clone(); - prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); - pixels.push(dec_pix); - }, - ChunkType::Luma => { - let dec_pix: Pixel = dec_luma(&bytes[i..i+2], &prev_pixel); - prev_pixel = dec_pix.clone(); - prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); - pixels.push(dec_pix); - i += 1; - }, - ChunkType::Run => { - let length: u8 = (bytes[i] & 0b00111111) + RUN_BIAS; - for j in 0..length { - pixels.push(prev_pixel.clone()); - } - prev_buffer[color_hash(&prev_pixel) as usize] = prev_pixel.clone(); + Ok(tag) => match tag { + ChunkType::RGB => { + let dec_pix: Pixel = dec_rgb(&bytes[i..i + 4], prev_pixel.a); + prev_pixel = dec_pix.clone(); + prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); + pixels.push(dec_pix); + i += 3; + } + ChunkType::RGBA => { + let dec_pix: Pixel = dec_rgba(&bytes[i..i + 5]); + prev_pixel = dec_pix.clone(); + prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); + pixels.push(dec_pix); + i += 4; + } + ChunkType::Diff => { + let dec_pix: Pixel = dec_diff(bytes[i], &prev_pixel); + prev_pixel = dec_pix.clone(); + prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); + pixels.push(dec_pix); + } + ChunkType::Index => { + let dec_pix: Pixel = prev_buffer[bytes[i] as usize]; + prev_pixel = dec_pix.clone(); + prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); + pixels.push(dec_pix); + } + ChunkType::Luma => { + let dec_pix: Pixel = dec_luma(&bytes[i..i + 2], &prev_pixel); + prev_pixel = dec_pix.clone(); + prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone(); + pixels.push(dec_pix); + i += 1; + } + ChunkType::Run => { + let length: u8 = (bytes[i] & 0b00111111) + RUN_BIAS; + for j in 0..length { + pixels.push(prev_pixel.clone()); } + prev_buffer[color_hash(&prev_pixel) as usize] = prev_pixel.clone(); } }, Err(err) => return Err(err), @@ -716,113 +748,128 @@ pub mod qoi_img { i += 1; } - if pixels.len() as u32 != height*width { - debug!("h*w: {}", height*width); + if pixels.len() as u32 != height * width { + debug!("h*w: {}", height * width); debug!("n pixels: {}", pixels.len()); return Err(ImgError::DecodeError); } let img = Image::from_pixels(pixels, height, width, channels, colorspace); Ok(img) - } #[cfg(test)] -mod tests { - use super::*; - use std::io; - use std::io::{Read, BufReader}; - use std::fs::File; + mod tests { + use super::*; + use std::fs::File; + use std::io; + use std::io::{BufReader, Read}; - #[test] - fn diff_test() { - init().expect("Logger initialisation failed!"); - let pix1: Pixel = Pixel::new( 0, 0, 0,255); - let pix2: Pixel = Pixel::new(255,255,255,255); + #[test] + fn diff_test() { + init().expect("Logger initialisation failed!"); + let pix1: Pixel = Pixel::new(0, 0, 0, 255); + let pix2: Pixel = Pixel::new(255, 255, 255, 255); - let pix3: Pixel = Pixel::new(155,155,155,255); - let pix4: Pixel = Pixel::new(160,160,160,255); + let pix3: Pixel = Pixel::new(155, 155, 155, 255); + let pix4: Pixel = Pixel::new(160, 160, 160, 255); - assert_eq!(pix1.diff(&pix2), ( 1, 1, 1)); - assert_eq!(pix2.diff(&pix1), (-1,-1,-1)); - assert_eq!(pix4.diff(&pix3), ( 5, 5, 5)); - assert_eq!(pix3.diff(&pix4), (-5,-5,-5)); - } - - #[test] - fn encode_test() -> io::Result<()> { - let f: File = File::open("test.qoi")?; - let mut reader = BufReader::new(f); - let mut bytes: Vec = Vec::new(); - - reader.read_to_end(&mut bytes)?; - - let out_img: super::Image; - - match super::decode(bytes) { - Ok(img) => out_img = img, - Err(err) => panic!("wallah geht nicht :/ {:?}", err) - } - write_to_file(encode_from_image(out_img), "test_dec").expect("wallahi!"); - Ok(()) - } - - #[test] - fn decode_test() -> io::Result<()> { - let f: File = File::open("testcard_rgba.qoi")?; - let mut reader = BufReader::new(f); - let mut bytes: Vec = Vec::new(); - - reader.read_to_end(&mut bytes)?; - - let out_img: super::Image; - - match super::decode(bytes) { - Ok(img) => out_img = img, - Err(err) => panic!("Ja bruder war nicht so erfolgreich ahahahahahha {:?}", err) + assert_eq!(pix1.diff(&pix2), (1, 1, 1)); + assert_eq!(pix2.diff(&pix1), (-1, -1, -1)); + assert_eq!(pix4.diff(&pix3), (5, 5, 5)); + assert_eq!(pix3.diff(&pix4), (-5, -5, -5)); } - write_to_file(encode_from_image(out_img), "testcard_new").expect("Boowomb!"); + #[test] + fn encode_test() -> io::Result<()> { + let f: File = File::open("test.qoi")?; + let mut reader = BufReader::new(f); + let mut bytes: Vec = Vec::new(); - Ok(()) + reader.read_to_end(&mut bytes)?; - } + let out_img: super::Image; - #[test] - fn tag_test() { - //init().expect("Logger initialisation failed!"); - let test_rgb: u8 = 0b1111_1110; - let test_rgba: u8 = 0b1111_1111; - let test_luma: u8 = 0b1011_1010; - let test_run: u8 = 0b1110_1101; - let test_diff: u8 = 0b0110_1010; - let test_index: u8 = 0b0010_1010; + match super::decode(bytes) { + Ok(img) => out_img = img, + Err(err) => panic!("wallah geht nicht :/ {:?}", err), + } + write_to_file(encode_from_image(out_img), "test_dec").expect("wallahi!"); + Ok(()) + } + #[test] + fn decode_test() -> io::Result<()> { + let f: File = File::open("testcard_rgba.qoi")?; + let mut reader = BufReader::new(f); + let mut bytes: Vec = Vec::new(); - assert_eq!(Ok(ChunkType::RGB), super::read_tag(test_rgb)); - assert_eq!(Ok(ChunkType::RGBA), super::read_tag(test_rgba)); - assert_eq!(Ok(ChunkType::Luma), super::read_tag(test_luma)); - assert_eq!(Ok(ChunkType::Diff), super::read_tag(test_diff)); - assert_eq!(Ok(ChunkType::Index), super::read_tag(test_index)); - assert_eq!(Ok(ChunkType::Run), super::read_tag(test_run)); - } + reader.read_to_end(&mut bytes)?; - #[test] - fn sub_decoders_test() { - //init().expect("Logger initialisation failed!"); - let pix: Pixel = Pixel { r: 255, g: 255, b: 255, a: 255 }; - let prev: Pixel = Pixel { r: 1, g: 1, b: 1, a: 255 }; - let byte: u8 = 0b01000000; + let out_img: super::Image; - assert_eq!(pix,dec_diff(byte, &prev)); + match super::decode(bytes) { + Ok(img) => out_img = img, + Err(err) => panic!("Ja bruder war nicht so erfolgreich ahahahahahha {:?}", err), + } - let pix: Pixel = Pixel { r: 17, g: 22, b: 28, a: 100 }; - let prev: Pixel = Pixel { r: 10, g: 10, b: 10, a: 100 }; - let byte: [u8;2] = [ 0b10101100, 0b00111110 ]; + write_to_file(encode_from_image(out_img), "testcard_new").expect("Boowomb!"); - assert_eq!(pix, dec_luma(&byte[0..2], &prev)); + Ok(()) + } + + #[test] + fn tag_test() { + //init().expect("Logger initialisation failed!"); + let test_rgb: u8 = 0b1111_1110; + let test_rgba: u8 = 0b1111_1111; + let test_luma: u8 = 0b1011_1010; + let test_run: u8 = 0b1110_1101; + let test_diff: u8 = 0b0110_1010; + let test_index: u8 = 0b0010_1010; + + assert_eq!(Ok(ChunkType::RGB), super::read_tag(test_rgb)); + assert_eq!(Ok(ChunkType::RGBA), super::read_tag(test_rgba)); + assert_eq!(Ok(ChunkType::Luma), super::read_tag(test_luma)); + assert_eq!(Ok(ChunkType::Diff), super::read_tag(test_diff)); + assert_eq!(Ok(ChunkType::Index), super::read_tag(test_index)); + assert_eq!(Ok(ChunkType::Run), super::read_tag(test_run)); + } + + #[test] + fn sub_decoders_test() { + //init().expect("Logger initialisation failed!"); + let pix: Pixel = Pixel { + r: 255, + g: 255, + b: 255, + a: 255, + }; + let prev: Pixel = Pixel { + r: 1, + g: 1, + b: 1, + a: 255, + }; + let byte: u8 = 0b01000000; + + assert_eq!(pix, dec_diff(byte, &prev)); + + let pix: Pixel = Pixel { + r: 17, + g: 22, + b: 28, + a: 100, + }; + let prev: Pixel = Pixel { + r: 10, + g: 10, + b: 10, + a: 100, + }; + let byte: [u8; 2] = [0b10101100, 0b00111110]; + + assert_eq!(pix, dec_luma(&byte[0..2], &prev)); + } } } - -} - diff --git a/src/main.rs b/src/main.rs index 849331e..c91051c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use std::time::SystemTime; use colors_transform::{Color, Hsl, Rgb}; use png; -use qoi_test::qoi_img::*; +use qoi::qoi_lib::*; fn encode_checkerboard() { let mut pixels: Vec = Vec::with_capacity(64 * 64); @@ -191,7 +191,7 @@ fn decode(args: &Vec) -> io::Result<()> { reader.read_to_end(&mut bytes)?; - match qoi_test::qoi_img::decode(bytes) { + match qoi::qoi_lib::decode(bytes) { Ok(_img) => println!("Decoding successful!"), Err(err) => panic!("ERROR: {err:?}"), } @@ -249,6 +249,9 @@ fn main() { "bench" => { bench(&args); } + "help" => { + println!("qoi supports the following commands: \n encode [IMAGE] (encodes given png-encoded into .qoi) \n decode [IMAGE] decodes given .qoi to .png \n bench [INPUT] [OUTPUT] encodes input .png into .qoi with encoding speed measured in microseconds.") + } _ => { panic!("Invalid arguments!") }