changes to package name. added help command.
This commit is contained in:
parent
222b0e2c7b
commit
d4d87fffe3
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -82,8 +82,8 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "qoi-test"
|
name = "qoi"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"array-init",
|
"array-init",
|
||||||
"colors-transform",
|
"colors-transform",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "qoi-test"
|
name = "qoi"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
555
src/lib.rs
555
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).
|
//! 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)]
|
#![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::fmt;
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use log::{debug,info, Level, Record, SetLoggerError, LevelFilter};
|
use std::io::prelude::*;
|
||||||
|
|
||||||
use array_init;
|
use array_init;
|
||||||
|
|
||||||
@ -23,20 +23,22 @@ pub mod qoi_img {
|
|||||||
//inherit from base Error
|
//inherit from base Error
|
||||||
impl std::error::Error for ImgError {}
|
impl std::error::Error for ImgError {}
|
||||||
|
|
||||||
|
|
||||||
//Output for error handling
|
//Output for error handling
|
||||||
impl fmt::Display for ImgError {
|
impl fmt::Display for ImgError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ImgError::DataError => write!(f, "invalid number of bytes (must be devisible by 4)"),
|
ImgError::DataError => {
|
||||||
ImgError::PixelNumberError => write!(f, "number of pixels does not match height and width params"),
|
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::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
|
//boilerplate implementation of the log crate
|
||||||
struct SimpleLogger;
|
struct SimpleLogger;
|
||||||
|
|
||||||
@ -49,9 +51,7 @@ pub mod qoi_img {
|
|||||||
eprintln!("{} - {}", record.level(), record.args());
|
eprintln!("{} - {}", record.level(), record.args());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn flush(&self) {
|
fn flush(&self) {}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//logging boilerplate
|
//logging boilerplate
|
||||||
static LOGGER: SimpleLogger = SimpleLogger;
|
static LOGGER: SimpleLogger = SimpleLogger;
|
||||||
@ -84,11 +84,10 @@ pub mod qoi_img {
|
|||||||
/// #
|
/// #
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn init() -> Result<(), SetLoggerError>{
|
pub fn init() -> Result<(), SetLoggerError> {
|
||||||
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Debug))
|
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<Pixel>`,
|
/// 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<Pixel>`,
|
||||||
/// which can be generated by [`self::new`] if given byte data. Otherwise, [self.pixels] must be given filled vector.
|
/// 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.)
|
/// `height` and `width` are given as u32 (note that qoi encoding does not guarantee functionality for images containing over 4000000 pixels.)
|
||||||
@ -123,13 +122,19 @@ pub mod qoi_img {
|
|||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
//Image constructor, expects an array of u8 pixels values in order, left to right, top to bottom.
|
//Image constructor, expects an array of u8 pixels values in order, left to right, top to bottom.
|
||||||
pub fn new(data: Vec<u8>, height: u32, width: u32, channels: u8, colorspace: u8,) -> Result<Image, ImgError> {
|
pub fn new(
|
||||||
|
data: Vec<u8>,
|
||||||
|
height: u32,
|
||||||
|
width: u32,
|
||||||
|
channels: u8,
|
||||||
|
colorspace: u8,
|
||||||
|
) -> Result<Image, ImgError> {
|
||||||
let pixels: Vec<Pixel> = match Image::pixels_from_bytes(data) {
|
let pixels: Vec<Pixel> = match Image::pixels_from_bytes(data) {
|
||||||
Ok(out) => out,
|
Ok(out) => out,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
if pixels.len() == (height * width) as usize {
|
if pixels.len() == (height * width) as usize {
|
||||||
let out: Image = Image {
|
let out: Image = Image {
|
||||||
pixels,
|
pixels,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
@ -140,29 +145,34 @@ pub mod qoi_img {
|
|||||||
} else {
|
} else {
|
||||||
Err(ImgError::PixelNumberError)
|
Err(ImgError::PixelNumberError)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_pixels(pixels: Vec<Pixel>, height: u32, width: u32, channels: u8, colorspace: u8) -> Image {
|
pub fn from_pixels(
|
||||||
|
pixels: Vec<Pixel>,
|
||||||
|
height: u32,
|
||||||
|
width: u32,
|
||||||
|
channels: u8,
|
||||||
|
colorspace: u8,
|
||||||
|
) -> Image {
|
||||||
let img = Image {
|
let img = Image {
|
||||||
pixels,
|
pixels,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
channels,
|
channels,
|
||||||
colorspace
|
colorspace,
|
||||||
};
|
};
|
||||||
img
|
img
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixels_from_bytes(data: Vec<u8>) -> Result<Vec<Pixel>, ImgError> {
|
fn pixels_from_bytes(data: Vec<u8>) -> Result<Vec<Pixel>, ImgError> {
|
||||||
let mut pixels: Vec<Pixel> = Vec::with_capacity(data.len()/4);
|
let mut pixels: Vec<Pixel> = Vec::with_capacity(data.len() / 4);
|
||||||
if data.len() % 4 == 0 {
|
if data.len() % 4 == 0 {
|
||||||
for i in 0..data.len()/4 {
|
for i in 0..data.len() / 4 {
|
||||||
pixels.push(Pixel {
|
pixels.push(Pixel {
|
||||||
r: data[i*4],
|
r: data[i * 4],
|
||||||
g: data[i*4+1],
|
g: data[i * 4 + 1],
|
||||||
b: data[i*4+2],
|
b: data[i * 4 + 2],
|
||||||
a: data[i*4+3],
|
a: data[i * 4 + 3],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(pixels)
|
Ok(pixels)
|
||||||
@ -172,7 +182,7 @@ pub mod qoi_img {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Pixel {
|
pub struct Pixel {
|
||||||
r: u8,
|
r: u8,
|
||||||
g: u8,
|
g: u8,
|
||||||
@ -187,22 +197,19 @@ pub mod qoi_img {
|
|||||||
Luma,
|
Luma,
|
||||||
Diff,
|
Diff,
|
||||||
RGB,
|
RGB,
|
||||||
RGBA
|
RGBA,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pixel {
|
impl Pixel {
|
||||||
|
|
||||||
|
|
||||||
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Pixel {
|
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Pixel {
|
||||||
Pixel {
|
Pixel { r, g, b, a }
|
||||||
r,
|
|
||||||
g,
|
|
||||||
b,
|
|
||||||
a,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn equals(&self, other: &Pixel) -> bool {
|
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
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -217,23 +224,25 @@ pub mod qoi_img {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//self = curr pixel, other = prev pixel
|
//self = curr pixel, other = prev pixel
|
||||||
pub fn determine_chunk(&self, other: &Pixel, buffer: &Vec<Pixel>) -> (ChunkType, Option<(u8,u8,u8)>){
|
pub fn determine_chunk(
|
||||||
|
&self,
|
||||||
|
other: &Pixel,
|
||||||
|
buffer: &Vec<Pixel>,
|
||||||
|
) -> (ChunkType, Option<(u8, u8, u8)>) {
|
||||||
if self.equals(&other) {
|
if self.equals(&other) {
|
||||||
return (ChunkType::Run, None);
|
return (ChunkType::Run, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.equals(&buffer[color_hash(&self) as usize]) {
|
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 {
|
if self.a != other.a {
|
||||||
return (ChunkType::RGBA, None);
|
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 dr: i16 = diff_tuple.0;
|
||||||
let dg: i16 = diff_tuple.1;
|
let dg: i16 = diff_tuple.1;
|
||||||
let db: i16 = diff_tuple.2;
|
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 dg: u8 = (dg + DIFF_BIAS as i16) as u8;
|
||||||
let db: u8 = (db + DIFF_BIAS as i16) as u8;
|
let db: u8 = (db + DIFF_BIAS as i16) as u8;
|
||||||
return (ChunkType::Diff, Some((dr, dg, db)));
|
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 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 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;
|
let db_dg: u8 = (db - dg + LUMA_BIAS_RB as i16) as u8;
|
||||||
@ -251,7 +265,6 @@ pub mod qoi_img {
|
|||||||
} else {
|
} else {
|
||||||
return (ChunkType::RGB, None);
|
return (ChunkType::RGB, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
pub fn diff(&self, other: &Pixel) -> (i16, i16, i16) {
|
pub fn diff(&self, other: &Pixel) -> (i16, i16, i16) {
|
||||||
let mut dr: i16;
|
let mut dr: i16;
|
||||||
@ -286,39 +299,38 @@ pub mod qoi_img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(dr, dg, db)
|
(dr, dg, db)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Definition of header bytes
|
//Definition of header bytes
|
||||||
struct Header {
|
struct Header {
|
||||||
magic: [char; 4], //magic bytes "qoif"
|
magic: [char; 4], //magic bytes "qoif"
|
||||||
width: u32, //image width in pixels (BE)
|
width: u32, //image width in pixels (BE)
|
||||||
height: u32, //image height in pixels (BE)
|
height: u32, //image height in pixels (BE)
|
||||||
channels: u8, // 3 = RGB, 4 = RBGA
|
channels: u8, // 3 = RGB, 4 = RBGA
|
||||||
colorspace: u8, // 0 = sRGB with linear alpha, 1 = all channels linear
|
colorspace: u8, // 0 = sRGB with linear alpha, 1 = all channels linear
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
fn convert_to_bytestream(&self) -> [u8;14] {
|
fn convert_to_bytestream(&self) -> [u8; 14] {
|
||||||
let mut out: [u8; 14] = [0;14];
|
let mut out: [u8; 14] = [0; 14];
|
||||||
|
|
||||||
//First, set magic bytes
|
//First, set magic bytes
|
||||||
out[0] = self.magic[0] as u8;
|
out[0] = self.magic[0] as u8;
|
||||||
out[1] = self.magic[1] as u8;
|
out[1] = self.magic[1] as u8;
|
||||||
out[2] = self.magic[2] as u8;
|
out[2] = self.magic[2] as u8;
|
||||||
out[3] = self.magic[3] as u8;
|
out[3] = self.magic[3] as u8;
|
||||||
|
|
||||||
//split width and height into 8-bit chunks
|
//split width and height into 8-bit chunks
|
||||||
let width_bytes = self.width.to_be_bytes();
|
let width_bytes = self.width.to_be_bytes();
|
||||||
let height_bytes = self.height.to_be_bytes();
|
let height_bytes = self.height.to_be_bytes();
|
||||||
|
|
||||||
out[4] = width_bytes[0];
|
out[4] = width_bytes[0];
|
||||||
out[5] = width_bytes[1];
|
out[5] = width_bytes[1];
|
||||||
out[6] = width_bytes[2];
|
out[6] = width_bytes[2];
|
||||||
out[7] = width_bytes[3];
|
out[7] = width_bytes[3];
|
||||||
out[8] = height_bytes[0];
|
out[8] = height_bytes[0];
|
||||||
out[9] = height_bytes[1];
|
out[9] = height_bytes[1];
|
||||||
out[10] = height_bytes[2];
|
out[10] = height_bytes[2];
|
||||||
out[11] = height_bytes[3];
|
out[11] = height_bytes[3];
|
||||||
|
|
||||||
@ -333,65 +345,71 @@ pub mod qoi_img {
|
|||||||
//Definition of End of Stream bytes
|
//Definition of End of Stream bytes
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct End {
|
struct End {
|
||||||
bytes: [u8;8]
|
bytes: [u8; 8],
|
||||||
}
|
}
|
||||||
impl End {
|
impl End {
|
||||||
fn new() -> End {
|
fn new() -> End {
|
||||||
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
|
//chunks as defined in the QOI spec
|
||||||
const QOI_OP_RGB: u8 = 0b1111_1110;
|
const QOI_OP_RGB: u8 = 0b1111_1110;
|
||||||
const QOI_OP_RGBA: u8 = 0b1111_1111;
|
const QOI_OP_RGBA: u8 = 0b1111_1111;
|
||||||
const QOI_OP_RUN: u8 = 0b1100_0000;
|
const QOI_OP_RUN: u8 = 0b1100_0000;
|
||||||
const QOI_OP_INDEX: u8 = 0b0000_0000;
|
const QOI_OP_INDEX: u8 = 0b0000_0000;
|
||||||
const QOI_OP_DIFF: u8 = 0b0100_0000;
|
const QOI_OP_DIFF: u8 = 0b0100_0000;
|
||||||
const QOI_OP_LUMA: u8 = 0b1000_0000;
|
const QOI_OP_LUMA: u8 = 0b1000_0000;
|
||||||
|
|
||||||
//Biases as defined in the QOI spec
|
//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
|
//hash function for assigning buffer indices to stored pixels
|
||||||
fn color_hash(pixel: &Pixel) -> u8 {
|
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
|
(store % 64) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_from_image(img: Image) -> Vec<u8> {
|
pub fn encode_from_image(img: Image) -> Vec<u8> {
|
||||||
|
let mut prev_pixel: Pixel = Pixel {
|
||||||
let mut prev_pixel: Pixel = Pixel {r: 0u8, b: 0u8, g: 0u8, a: 255u8};
|
r: 0u8,
|
||||||
|
b: 0u8,
|
||||||
|
g: 0u8,
|
||||||
|
a: 255u8,
|
||||||
|
};
|
||||||
|
|
||||||
let mut prev_buffer: Vec<Pixel> = Vec::with_capacity(64);
|
let mut prev_buffer: Vec<Pixel> = Vec::with_capacity(64);
|
||||||
|
|
||||||
for i in 0..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);
|
prev_buffer.push(pix);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut encoded_bytes: Vec<u8> = Vec::new();
|
let mut encoded_bytes: Vec<u8> = Vec::new();
|
||||||
let mut run: u8 = 0;
|
let mut run: u8 = 0;
|
||||||
|
|
||||||
|
|
||||||
let head = Header {
|
let head = Header {
|
||||||
magic: ['q', 'o', 'i', 'f'],
|
magic: ['q', 'o', 'i', 'f'],
|
||||||
width: img.width,
|
width: img.width,
|
||||||
height: img.height,
|
height: img.height,
|
||||||
channels: img.channels,
|
channels: img.channels,
|
||||||
colorspace: img.colorspace
|
colorspace: img.colorspace,
|
||||||
};
|
};
|
||||||
let head_stream = head.convert_to_bytestream();
|
let head_stream = head.convert_to_bytestream();
|
||||||
|
|
||||||
|
|
||||||
for i in head_stream {
|
for i in head_stream {
|
||||||
encoded_bytes.push(i);
|
encoded_bytes.push(i);
|
||||||
}
|
}
|
||||||
@ -400,7 +418,8 @@ pub mod qoi_img {
|
|||||||
|
|
||||||
for pixel in img.pixels {
|
for pixel in img.pixels {
|
||||||
counter += 1;
|
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) {
|
if chunk == (ChunkType::Run, None) {
|
||||||
run += 1;
|
run += 1;
|
||||||
prev_pixel = pixel.clone();
|
prev_pixel = pixel.clone();
|
||||||
@ -409,18 +428,18 @@ pub mod qoi_img {
|
|||||||
if run > 0 {
|
if run > 0 {
|
||||||
if run > 62 {
|
if run > 62 {
|
||||||
while run > 0 {
|
while run > 0 {
|
||||||
if run/62 > 0 {
|
if run / 62 > 0 {
|
||||||
encoded_bytes.push(QOI_OP_RUN | (62-RUN_BIAS));
|
encoded_bytes.push(QOI_OP_RUN | (62 - RUN_BIAS));
|
||||||
run -= 62;
|
run -= 62;
|
||||||
} else if run%62 > 0 {
|
} else if run % 62 > 0 {
|
||||||
encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS));
|
encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS));
|
||||||
run = 0;
|
run = 0;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS));
|
encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS));
|
||||||
run = 0;
|
run = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,8 +448,8 @@ pub mod qoi_img {
|
|||||||
(ChunkType::Index, Some((index, irr1, irr2))) => {
|
(ChunkType::Index, Some((index, irr1, irr2))) => {
|
||||||
encoded_bytes.push(QOI_OP_INDEX | index);
|
encoded_bytes.push(QOI_OP_INDEX | index);
|
||||||
prev_pixel = pixel;
|
prev_pixel = pixel;
|
||||||
},
|
}
|
||||||
(ChunkType::Diff, Some((dr, dg,db))) => {
|
(ChunkType::Diff, Some((dr, dg, db))) => {
|
||||||
let mut out: u8 = 0b0000_0000;
|
let mut out: u8 = 0b0000_0000;
|
||||||
out = out | db;
|
out = out | db;
|
||||||
out = out | (dg << 2);
|
out = out | (dg << 2);
|
||||||
@ -438,9 +457,9 @@ pub mod qoi_img {
|
|||||||
encoded_bytes.push(QOI_OP_DIFF | out);
|
encoded_bytes.push(QOI_OP_DIFF | out);
|
||||||
prev_pixel = pixel.clone();
|
prev_pixel = pixel.clone();
|
||||||
prev_buffer[color_hash(&pixel) as usize] = pixel;
|
prev_buffer[color_hash(&pixel) as usize] = pixel;
|
||||||
},
|
}
|
||||||
(ChunkType::Luma, Some((dg, dr_dg, db_dg))) => {
|
(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] |= dg;
|
||||||
out[0] |= QOI_OP_LUMA;
|
out[0] |= QOI_OP_LUMA;
|
||||||
out[1] |= db_dg;
|
out[1] |= db_dg;
|
||||||
@ -449,7 +468,7 @@ pub mod qoi_img {
|
|||||||
encoded_bytes.push(out[1]);
|
encoded_bytes.push(out[1]);
|
||||||
prev_pixel = pixel.clone();
|
prev_pixel = pixel.clone();
|
||||||
prev_buffer[color_hash(&pixel) as usize] = pixel;
|
prev_buffer[color_hash(&pixel) as usize] = pixel;
|
||||||
},
|
}
|
||||||
(ChunkType::RGB, None) => {
|
(ChunkType::RGB, None) => {
|
||||||
encoded_bytes.push(QOI_OP_RGB);
|
encoded_bytes.push(QOI_OP_RGB);
|
||||||
encoded_bytes.push(pixel.r);
|
encoded_bytes.push(pixel.r);
|
||||||
@ -457,7 +476,7 @@ pub mod qoi_img {
|
|||||||
encoded_bytes.push(pixel.b);
|
encoded_bytes.push(pixel.b);
|
||||||
prev_pixel = pixel.clone();
|
prev_pixel = pixel.clone();
|
||||||
prev_buffer[color_hash(&pixel) as usize] = pixel;
|
prev_buffer[color_hash(&pixel) as usize] = pixel;
|
||||||
},
|
}
|
||||||
(ChunkType::RGBA, None) => {
|
(ChunkType::RGBA, None) => {
|
||||||
if (pixel.a as i16 - prev_pixel.a as i16) == 0i16 {
|
if (pixel.a as i16 - prev_pixel.a as i16) == 0i16 {
|
||||||
//this should never be reached, but it is
|
//this should never be reached, but it is
|
||||||
@ -476,27 +495,28 @@ pub mod qoi_img {
|
|||||||
prev_pixel = pixel.clone();
|
prev_pixel = pixel.clone();
|
||||||
prev_buffer[color_hash(&pixel) as usize] = pixel;
|
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 > 0 {
|
||||||
if run > 62 {
|
if run > 62 {
|
||||||
while run > 0 {
|
while run > 0 {
|
||||||
if run/62 > 0 {
|
if run / 62 > 0 {
|
||||||
encoded_bytes.push(QOI_OP_RUN | (62-RUN_BIAS));
|
encoded_bytes.push(QOI_OP_RUN | (62 - RUN_BIAS));
|
||||||
run -= 62;
|
run -= 62;
|
||||||
} else if run%62 > 0 {
|
} else if run % 62 > 0 {
|
||||||
encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS));
|
encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS));
|
||||||
run = 0;
|
run = 0;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
encoded_bytes.push(QOI_OP_RUN | (run-RUN_BIAS));
|
encoded_bytes.push(QOI_OP_RUN | (run - RUN_BIAS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,14 +526,19 @@ pub mod qoi_img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!("Number of pixels processed: {}.", counter);
|
info!("Number of pixels processed: {}.", counter);
|
||||||
info!("Number of bytes in encoding: {:?}.", encoded_bytes.len()-22);
|
info!(
|
||||||
info!("Compression rate: {:.2}%.", (1.0-(encoded_bytes.len()-22) as f64/(counter*4)as f64)*100.0);
|
"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
|
encoded_bytes
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_file(bytes: Vec<u8>, filename: &str) -> std::io::Result<()>{
|
pub fn write_to_file(bytes: Vec<u8>, filename: &str) -> std::io::Result<()> {
|
||||||
let mut file_path: String = String::from(filename);
|
let mut file_path: String = String::from(filename);
|
||||||
file_path.push_str(".qoi");
|
file_path.push_str(".qoi");
|
||||||
|
|
||||||
@ -527,32 +552,36 @@ pub mod qoi_img {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_header(bytes: &[u8]) -> Result<(u32,u32,u8,u8), ImgError> {
|
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 {
|
if bytes[0] == 'q' as u8
|
||||||
let mut width: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000;
|
&& 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;
|
let mut height: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000;
|
||||||
width |= ((bytes[4] as u32) << 24) as u32;
|
width |= ((bytes[4] as u32) << 24) as u32;
|
||||||
width |= ((bytes[5] as u32) << 16) as u32;
|
width |= ((bytes[5] as u32) << 16) as u32;
|
||||||
width |= ((bytes[6] as u32) << 8) as u32;
|
width |= ((bytes[6] as u32) << 8) as u32;
|
||||||
width |= (bytes[7]) as u32;
|
width |= (bytes[7]) as u32;
|
||||||
height |= ((bytes[8] as u32) << 24) as u32;
|
height |= ((bytes[8] as u32) << 24) as u32;
|
||||||
height |= ((bytes[9] as u32) << 16) as u32;
|
height |= ((bytes[9] as u32) << 16) as u32;
|
||||||
height |= ((bytes[10] as u32) << 8) as u32;
|
height |= ((bytes[10] as u32) << 8) as u32;
|
||||||
height |= (bytes[11]) as u32;
|
height |= (bytes[11]) as u32;
|
||||||
return Ok((width, height, bytes[12], bytes[13]));
|
return Ok((width, height, bytes[12], bytes[13]));
|
||||||
} else {
|
} else {
|
||||||
return Err(ImgError::HeaderError);
|
return Err(ImgError::HeaderError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_tag(tag: u8) -> Result<ChunkType,ImgError> {
|
fn read_tag(tag: u8) -> Result<ChunkType, ImgError> {
|
||||||
if tag == QOI_OP_RGB {
|
if tag == QOI_OP_RGB {
|
||||||
return Ok(ChunkType::RGB);
|
return Ok(ChunkType::RGB);
|
||||||
}
|
}
|
||||||
if tag == QOI_OP_RGBA {
|
if tag == QOI_OP_RGBA {
|
||||||
return Ok(ChunkType::RGBA);
|
return Ok(ChunkType::RGBA);
|
||||||
}
|
}
|
||||||
if (tag & 0b1100_0000) == QOI_OP_DIFF{
|
if (tag & 0b1100_0000) == QOI_OP_DIFF {
|
||||||
return Ok(ChunkType::Diff);
|
return Ok(ChunkType::Diff);
|
||||||
}
|
}
|
||||||
if (tag & 0b1100_0000) == QOI_OP_INDEX {
|
if (tag & 0b1100_0000) == QOI_OP_INDEX {
|
||||||
@ -584,7 +613,7 @@ pub mod qoi_img {
|
|||||||
|
|
||||||
dr = (byte & 0b00110000) >> 4;
|
dr = (byte & 0b00110000) >> 4;
|
||||||
dg = (byte & 0b00001100) >> 2;
|
dg = (byte & 0b00001100) >> 2;
|
||||||
db = byte & 0b00000011;
|
db = byte & 0b00000011;
|
||||||
|
|
||||||
let r: u8 = prev_pixel.r.wrapping_add(dr);
|
let r: u8 = prev_pixel.r.wrapping_add(dr);
|
||||||
let g: u8 = prev_pixel.g.wrapping_add(dg);
|
let g: u8 = prev_pixel.g.wrapping_add(dg);
|
||||||
@ -594,7 +623,7 @@ pub mod qoi_img {
|
|||||||
let b: u8 = b.wrapping_sub(DIFF_BIAS);
|
let b: u8 = b.wrapping_sub(DIFF_BIAS);
|
||||||
let g: u8 = g.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
|
pixel
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,9 +634,9 @@ pub mod qoi_img {
|
|||||||
let dg: u8;
|
let dg: u8;
|
||||||
let db: u8;
|
let db: u8;
|
||||||
|
|
||||||
dg = bytes[0] & 0b00111111;
|
dg = bytes[0] & 0b00111111;
|
||||||
dr_dg = (bytes[1] & 0b11110000) >> 4;
|
dr_dg = (bytes[1] & 0b11110000) >> 4;
|
||||||
db_dg = bytes[1] & 0b00001111;
|
db_dg = bytes[1] & 0b00001111;
|
||||||
dr = dr_dg + dg;
|
dr = dr_dg + dg;
|
||||||
db = db_dg + dg;
|
db = db_dg + dg;
|
||||||
|
|
||||||
@ -631,26 +660,31 @@ pub mod qoi_img {
|
|||||||
let channels: u8;
|
let channels: u8;
|
||||||
let colorspace: 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]) {
|
match read_header(&bytes[0..14]) {
|
||||||
Ok((w, h, ch, c))=> {
|
Ok((w, h, ch, c)) => {
|
||||||
width = w;
|
width = w;
|
||||||
height = h;
|
height = h;
|
||||||
channels = ch;
|
channels = ch;
|
||||||
colorspace = c;
|
colorspace = c;
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut pixels: Vec<Pixel> = Vec::with_capacity((width*height*4) as usize);
|
let mut pixels: Vec<Pixel> = 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 {
|
for i in 2..9 {
|
||||||
if bytes[bytes.len()-i] != 0 {
|
if bytes[bytes.len() - i] != 0 {
|
||||||
debug!("Ending bytes not present.");
|
debug!("Ending bytes not present.");
|
||||||
return Err(ImgError::DecodeError);
|
return Err(ImgError::DecodeError);
|
||||||
}
|
}
|
||||||
@ -667,48 +701,46 @@ pub mod qoi_img {
|
|||||||
|
|
||||||
while i < bytes.len() {
|
while i < bytes.len() {
|
||||||
match read_tag(bytes[i]) {
|
match read_tag(bytes[i]) {
|
||||||
Ok(tag) => {
|
Ok(tag) => match tag {
|
||||||
match tag {
|
ChunkType::RGB => {
|
||||||
ChunkType::RGB => {
|
let dec_pix: Pixel = dec_rgb(&bytes[i..i + 4], prev_pixel.a);
|
||||||
let dec_pix: Pixel = dec_rgb(&bytes[i..i+4], prev_pixel.a);
|
prev_pixel = dec_pix.clone();
|
||||||
prev_pixel = dec_pix.clone();
|
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
||||||
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
pixels.push(dec_pix);
|
||||||
pixels.push(dec_pix);
|
i += 3;
|
||||||
i += 3;
|
}
|
||||||
},
|
ChunkType::RGBA => {
|
||||||
ChunkType::RGBA => {
|
let dec_pix: Pixel = dec_rgba(&bytes[i..i + 5]);
|
||||||
let dec_pix: Pixel = dec_rgba(&bytes[i..i+5]);
|
prev_pixel = dec_pix.clone();
|
||||||
prev_pixel = dec_pix.clone();
|
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
||||||
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
pixels.push(dec_pix);
|
||||||
pixels.push(dec_pix);
|
i += 4;
|
||||||
i += 4;
|
}
|
||||||
},
|
ChunkType::Diff => {
|
||||||
ChunkType::Diff => {
|
let dec_pix: Pixel = dec_diff(bytes[i], &prev_pixel);
|
||||||
let dec_pix: Pixel = dec_diff(bytes[i], &prev_pixel);
|
prev_pixel = dec_pix.clone();
|
||||||
prev_pixel = dec_pix.clone();
|
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
||||||
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
pixels.push(dec_pix);
|
||||||
pixels.push(dec_pix);
|
}
|
||||||
},
|
ChunkType::Index => {
|
||||||
ChunkType::Index => {
|
let dec_pix: Pixel = prev_buffer[bytes[i] as usize];
|
||||||
let dec_pix: Pixel = prev_buffer[bytes[i] as usize];
|
prev_pixel = dec_pix.clone();
|
||||||
prev_pixel = dec_pix.clone();
|
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
||||||
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
pixels.push(dec_pix);
|
||||||
pixels.push(dec_pix);
|
}
|
||||||
},
|
ChunkType::Luma => {
|
||||||
ChunkType::Luma => {
|
let dec_pix: Pixel = dec_luma(&bytes[i..i + 2], &prev_pixel);
|
||||||
let dec_pix: Pixel = dec_luma(&bytes[i..i+2], &prev_pixel);
|
prev_pixel = dec_pix.clone();
|
||||||
prev_pixel = dec_pix.clone();
|
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
||||||
prev_buffer[color_hash(&dec_pix) as usize] = dec_pix.clone();
|
pixels.push(dec_pix);
|
||||||
pixels.push(dec_pix);
|
i += 1;
|
||||||
i += 1;
|
}
|
||||||
},
|
ChunkType::Run => {
|
||||||
ChunkType::Run => {
|
let length: u8 = (bytes[i] & 0b00111111) + RUN_BIAS;
|
||||||
let length: u8 = (bytes[i] & 0b00111111) + RUN_BIAS;
|
for j in 0..length {
|
||||||
for j in 0..length {
|
pixels.push(prev_pixel.clone());
|
||||||
pixels.push(prev_pixel.clone());
|
|
||||||
}
|
|
||||||
prev_buffer[color_hash(&prev_pixel) as usize] = prev_pixel.clone();
|
|
||||||
}
|
}
|
||||||
|
prev_buffer[color_hash(&prev_pixel) as usize] = prev_pixel.clone();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
@ -716,113 +748,128 @@ pub mod qoi_img {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if pixels.len() as u32 != height*width {
|
if pixels.len() as u32 != height * width {
|
||||||
debug!("h*w: {}", height*width);
|
debug!("h*w: {}", height * width);
|
||||||
debug!("n pixels: {}", pixels.len());
|
debug!("n pixels: {}", pixels.len());
|
||||||
return Err(ImgError::DecodeError);
|
return Err(ImgError::DecodeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let img = Image::from_pixels(pixels, height, width, channels, colorspace);
|
let img = Image::from_pixels(pixels, height, width, channels, colorspace);
|
||||||
Ok(img)
|
Ok(img)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::io;
|
use std::fs::File;
|
||||||
use std::io::{Read, BufReader};
|
use std::io;
|
||||||
use std::fs::File;
|
use std::io::{BufReader, Read};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn diff_test() {
|
fn diff_test() {
|
||||||
init().expect("Logger initialisation failed!");
|
init().expect("Logger initialisation failed!");
|
||||||
let pix1: Pixel = Pixel::new( 0, 0, 0,255);
|
let pix1: Pixel = Pixel::new(0, 0, 0, 255);
|
||||||
let pix2: Pixel = Pixel::new(255,255,255,255);
|
let pix2: Pixel = Pixel::new(255, 255, 255, 255);
|
||||||
|
|
||||||
let pix3: Pixel = Pixel::new(155,155,155,255);
|
let pix3: Pixel = Pixel::new(155, 155, 155, 255);
|
||||||
let pix4: Pixel = Pixel::new(160,160,160,255);
|
let pix4: Pixel = Pixel::new(160, 160, 160, 255);
|
||||||
|
|
||||||
assert_eq!(pix1.diff(&pix2), ( 1, 1, 1));
|
assert_eq!(pix1.diff(&pix2), (1, 1, 1));
|
||||||
assert_eq!(pix2.diff(&pix1), (-1,-1,-1));
|
assert_eq!(pix2.diff(&pix1), (-1, -1, -1));
|
||||||
assert_eq!(pix4.diff(&pix3), ( 5, 5, 5));
|
assert_eq!(pix4.diff(&pix3), (5, 5, 5));
|
||||||
assert_eq!(pix3.diff(&pix4), (-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<u8> = 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<u8> = 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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<u8> = Vec::new();
|
||||||
|
|
||||||
Ok(())
|
reader.read_to_end(&mut bytes)?;
|
||||||
|
|
||||||
}
|
let out_img: super::Image;
|
||||||
|
|
||||||
#[test]
|
match super::decode(bytes) {
|
||||||
fn tag_test() {
|
Ok(img) => out_img = img,
|
||||||
//init().expect("Logger initialisation failed!");
|
Err(err) => panic!("wallah geht nicht :/ {:?}", err),
|
||||||
let test_rgb: u8 = 0b1111_1110;
|
}
|
||||||
let test_rgba: u8 = 0b1111_1111;
|
write_to_file(encode_from_image(out_img), "test_dec").expect("wallahi!");
|
||||||
let test_luma: u8 = 0b1011_1010;
|
Ok(())
|
||||||
let test_run: u8 = 0b1110_1101;
|
}
|
||||||
let test_diff: u8 = 0b0110_1010;
|
|
||||||
let test_index: u8 = 0b0010_1010;
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decode_test() -> io::Result<()> {
|
||||||
|
let f: File = File::open("testcard_rgba.qoi")?;
|
||||||
|
let mut reader = BufReader::new(f);
|
||||||
|
let mut bytes: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
assert_eq!(Ok(ChunkType::RGB), super::read_tag(test_rgb));
|
reader.read_to_end(&mut bytes)?;
|
||||||
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]
|
let out_img: super::Image;
|
||||||
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));
|
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 };
|
write_to_file(encode_from_image(out_img), "testcard_new").expect("Boowomb!");
|
||||||
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));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use std::time::SystemTime;
|
|||||||
|
|
||||||
use colors_transform::{Color, Hsl, Rgb};
|
use colors_transform::{Color, Hsl, Rgb};
|
||||||
use png;
|
use png;
|
||||||
use qoi_test::qoi_img::*;
|
use qoi::qoi_lib::*;
|
||||||
|
|
||||||
fn encode_checkerboard() {
|
fn encode_checkerboard() {
|
||||||
let mut pixels: Vec<Pixel> = Vec::with_capacity(64 * 64);
|
let mut pixels: Vec<Pixel> = Vec::with_capacity(64 * 64);
|
||||||
@ -191,7 +191,7 @@ fn decode(args: &Vec<String>) -> io::Result<()> {
|
|||||||
|
|
||||||
reader.read_to_end(&mut bytes)?;
|
reader.read_to_end(&mut bytes)?;
|
||||||
|
|
||||||
match qoi_test::qoi_img::decode(bytes) {
|
match qoi::qoi_lib::decode(bytes) {
|
||||||
Ok(_img) => println!("Decoding successful!"),
|
Ok(_img) => println!("Decoding successful!"),
|
||||||
Err(err) => panic!("ERROR: {err:?}"),
|
Err(err) => panic!("ERROR: {err:?}"),
|
||||||
}
|
}
|
||||||
@ -249,6 +249,9 @@ fn main() {
|
|||||||
"bench" => {
|
"bench" => {
|
||||||
bench(&args);
|
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!")
|
panic!("Invalid arguments!")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user