diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f9f42a..1839202 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [stable, beta, nightly, 1.61.0] # MSRV=1.61 + rust: [stable, beta, nightly, 1.51.0] # MSRV=1.51 steps: - uses: actions/checkout@v2 with: {submodules: true} diff --git a/Cargo.toml b/Cargo.toml index 8417cc7..d1c8ec7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "qoi" -version = "0.4.1" +version = "0.4.0" description = "VERY fast encoder/decoder for QOI (Quite Okay Image) format" authors = ["Ivan Smirnov "] -edition = "2021" +edition = "2018" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/aldanor/qoi-rust" @@ -14,7 +14,7 @@ keywords = ["qoi", "graphics", "image", "encoding"] exclude = [ "assets/*", ] -rust-version = "1.61.0" +rust-version = "1.51.0" [features] default = ["std"] @@ -23,7 +23,7 @@ std = [] # std mode (enabled by default) - provides access to `std::io`, reference = [] # follows reference encoder implementation precisely, but may be slightly slower [dependencies] -bytemuck = "1.12" +bytemuck = "1.7" [workspace] members = ["libqoi", "bench"] diff --git a/README.md b/README.md index d0a9651..a7de677 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,6 @@ Fast encoder/decoder for [QOI image format](https://qoiformat.org/), implemented - `no_std` support. - Roundtrip-tested vs the reference C implementation; fuzz-tested. -### Note about this fork - -This fork implements a slight improvement to the original specs, which leaves unused the `QOI_OP_RGBA` chunk flag with RGB. - -Here, we use this flag for the new `QOI_OP_RUN2` chunk. It's like the `QOI_OP_RUN` chunk, but followed by two bytes representing `run` (BE). (only for RGB, as the flag is already used for RGBA) - -The decoder remains fully compatible with the original one (except when using `QOI_OP_RGBA` in a RGB image). The encoder is fully compatible for RGBA, not for RGB (except using the `reference` feature). - -Why this? Because it enables significant improvements for compressing images with large uniform areas (such as screen captures), or for encoding a diff-filtered video stream where successive frames often have identical regions. (see [syeve](https://framagit.org/ZettaScript/syeve) for the video encoding) - ### Examples ```rust @@ -61,7 +51,8 @@ this library proved to be the fastest one by a noticeable margin. ### Rust version -The minimum required Rust version for the latest crate version is 1.61.0. +The minimum required Rust version is 1.51.0 (any changes to this would be +considered to be a breaking change). ### `no_std` diff --git a/src/consts.rs b/src/consts.rs index 0e39c4b..2281c24 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -4,7 +4,6 @@ pub const QOI_OP_LUMA: u8 = 0x80; // 10xxxxxx pub const QOI_OP_RUN: u8 = 0xc0; // 11xxxxxx pub const QOI_OP_RGB: u8 = 0xfe; // 11111110 pub const QOI_OP_RGBA: u8 = 0xff; // 11111111 -pub const QOI_OP_RUN2: u8 = 0xff; // 11111111 pub const QOI_MASK_2: u8 = 0xc0; // (11)000000 diff --git a/src/decode.rs b/src/decode.rs index a4343d0..019dac2 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -49,21 +49,9 @@ where px.update_rgb(*r, *g, *b); data = dtail; } - [QOI_OP_RGBA, dtail @ ..] => { - if RGBA { - if let [r, g, b, a, dtail @ ..] = dtail { - px.update_rgba(*r, *g, *b, *a); - data = dtail; - } - } else if let [b1, b2, dtail @ ..] = dtail { - *px_out = px.into(); - let run = (u16::from_be_bytes([*b1, *b2]) as usize).min(pixels.len()); - let (phead, ptail) = pixels.split_at_mut(run); // can't panic - phead.fill(px.into()); - pixels = ptail; - data = dtail; - continue; - } + [QOI_OP_RGBA, r, g, b, a, dtail @ ..] if RGBA => { + px.update_rgba(*r, *g, *b, *a); + data = dtail; } [b1 @ QOI_OP_RUN..=QOI_OP_RUN_END, dtail @ ..] => { *px_out = px.into(); diff --git a/src/encode.rs b/src/encode.rs index ce2b36f..0ed8476 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -6,7 +6,7 @@ use std::io::Write; use bytemuck::Pod; -use crate::consts::{QOI_HEADER_SIZE, QOI_OP_INDEX, QOI_OP_RUN, QOI_PADDING, QOI_PADDING_SIZE, QOI_OP_RUN2}; +use crate::consts::{QOI_HEADER_SIZE, QOI_OP_INDEX, QOI_OP_RUN, QOI_PADDING, QOI_PADDING_SIZE}; use crate::error::{Error, Result}; use crate::header::Header; use crate::pixel::{Pixel, SupportedChannels}; @@ -26,7 +26,7 @@ where let mut index = [Pixel::new(); 256]; let mut px_prev = Pixel::new().with_a(0xff); let mut hash_prev = px_prev.hash_index(); - let mut run = 0_u16; + let mut run = 0_u8; let mut px = Pixel::::new().with_a(0xff); let mut index_allowed = false; @@ -36,20 +36,8 @@ where px.read(chunk); if px == px_prev { run += 1; - #[cfg(not(feature = "reference"))] - if N == 3 { - if run == 65535 || unlikely(i == n_pixels - 1) { - let [b1, b2] = (run-1).to_be_bytes(); - buf = buf.write_many(&[QOI_OP_RUN2, b1, b2])?; - run = 0; - } - } else if run == 62 || unlikely(i == n_pixels - 1) { - buf = buf.write_one(QOI_OP_RUN | (run as u8 - 1))?; - run = 0; - } - #[cfg(feature = "reference")] if run == 62 || unlikely(i == n_pixels - 1) { - buf = buf.write_one(QOI_OP_RUN | (run as u8 - 1))?; + buf = buf.write_one(QOI_OP_RUN | (run - 1))?; run = 0; } } else { @@ -57,18 +45,15 @@ where #[cfg(not(feature = "reference"))] { // credits for the original idea: @zakarumych (had to be fixed though) - buf = if run == 1 && index_allowed { - buf.write_one(QOI_OP_INDEX | hash_prev)? - } else if N == 4 || run < 63 { - buf.write_one(QOI_OP_RUN | (run as u8 - 1))? + buf = buf.write_one(if run == 1 && index_allowed { + QOI_OP_INDEX | hash_prev } else { - let [b1, b2] = (run-1).to_be_bytes(); - buf.write_many(&[QOI_OP_RUN2, b1, b2])? - }; + QOI_OP_RUN | (run - 1) + })?; } #[cfg(feature = "reference")] { - buf = buf.write_one(QOI_OP_RUN | (run as u8 - 1))?; + buf = buf.write_one(QOI_OP_RUN | (run - 1))?; } run = 0; }