Create feature flags for more compression algorithms (#270)
This commit is contained in:
1 parent
dc2c67469b
commit
83e6d64751
8 files changed
+64
-15
No files matched your search
@@ -32,6 +32,19 @@ jobs:
|
|||||||
if: ${{ matrix.rust != '1.74.0' }}
|
if: ${{ matrix.rust != '1.74.0' }}
|
||||||
run: cargo test -v && cargo doc -v
|
run: cargo test -v && cargo doc -v
|
||||||
|
|
||||||
|
features:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
feature: ["", deflate, jpeg, lzw, zstd, default, "default,zstd"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: clippy
|
||||||
|
- name: Cargo check
|
||||||
|
run: cargo check --features "${{ matrix.feature }}" --no-default-features
|
||||||
|
|
||||||
rustfmt:
|
rustfmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
+11
-4
@@ -18,16 +18,23 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
half = { version = "2.4.1" }
|
half = { version = "2.4.1" }
|
||||||
weezl = "0.1.0"
|
|
||||||
zune-jpeg = "0.4.17"
|
|
||||||
flate2 = "1.0.20"
|
|
||||||
zstd = { version = "0.13", optional = true }
|
|
||||||
quick-error = "2.0.1"
|
quick-error = "2.0.1"
|
||||||
|
|
||||||
|
flate2 = { version = "1.0.20", optional = true }
|
||||||
|
weezl = { version = "0.1.0", optional = true }
|
||||||
|
zstd = { version = "0.13", optional = true }
|
||||||
|
zune-jpeg = { version = "0.4.17", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3.1"
|
criterion = "0.3.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["deflate", "jpeg", "lzw"]
|
||||||
|
|
||||||
|
# Compression algorithms
|
||||||
|
deflate = ["dep:flate2"]
|
||||||
|
jpeg = ["dep:zune-jpeg"]
|
||||||
|
lzw = ["dep:weezl"]
|
||||||
zstd = ["dep:zstd"]
|
zstd = ["dep:zstd"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
|
|||||||
+13
-8
@@ -1,5 +1,5 @@
|
|||||||
use super::ifd::{Directory, Value};
|
use super::ifd::{Directory, Value};
|
||||||
use super::stream::{ByteOrder, DeflateReader, LZWReader, PackBitsReader};
|
use super::stream::{ByteOrder, PackBitsReader};
|
||||||
use super::tag_reader::TagReader;
|
use super::tag_reader::TagReader;
|
||||||
use super::{predict_f16, predict_f32, predict_f64, Limits};
|
use super::{predict_f16, predict_f32, predict_f64, Limits};
|
||||||
use super::{stream::EndianReader, ChunkType};
|
use super::{stream::EndianReader, ChunkType};
|
||||||
@@ -9,7 +9,6 @@ use crate::tags::{
|
|||||||
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
|
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
|
||||||
use std::io::{self, Cursor, Read, Seek};
|
use std::io::{self, Cursor, Read, Seek};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use zune_jpeg::zune_core;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct StripDecodeState {
|
pub(crate) struct StripDecodeState {
|
||||||
@@ -370,23 +369,29 @@ impl Image {
|
|||||||
|
|
||||||
fn create_reader<'r, R: 'r + Read>(
|
fn create_reader<'r, R: 'r + Read>(
|
||||||
reader: R,
|
reader: R,
|
||||||
photometric_interpretation: PhotometricInterpretation,
|
#[allow(unused_variables)] photometric_interpretation: PhotometricInterpretation,
|
||||||
compression_method: CompressionMethod,
|
compression_method: CompressionMethod,
|
||||||
compressed_length: u64,
|
compressed_length: u64,
|
||||||
jpeg_tables: Option<&[u8]>,
|
#[allow(unused_variables)] jpeg_tables: Option<&[u8]>,
|
||||||
) -> TiffResult<Box<dyn Read + 'r>> {
|
) -> TiffResult<Box<dyn Read + 'r>> {
|
||||||
Ok(match compression_method {
|
Ok(match compression_method {
|
||||||
CompressionMethod::None => Box::new(reader),
|
CompressionMethod::None => Box::new(reader),
|
||||||
CompressionMethod::LZW => {
|
#[cfg(feature = "lzw")]
|
||||||
Box::new(LZWReader::new(reader, usize::try_from(compressed_length)?))
|
CompressionMethod::LZW => Box::new(super::stream::LZWReader::new(
|
||||||
}
|
reader,
|
||||||
|
usize::try_from(compressed_length)?,
|
||||||
|
)),
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
CompressionMethod::ZSTD => Box::new(zstd::Decoder::new(reader)?),
|
CompressionMethod::ZSTD => Box::new(zstd::Decoder::new(reader)?),
|
||||||
CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)),
|
CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)),
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
CompressionMethod::Deflate | CompressionMethod::OldDeflate => {
|
CompressionMethod::Deflate | CompressionMethod::OldDeflate => {
|
||||||
Box::new(DeflateReader::new(reader))
|
Box::new(super::stream::DeflateReader::new(reader))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "jpeg")]
|
||||||
CompressionMethod::ModernJPEG => {
|
CompressionMethod::ModernJPEG => {
|
||||||
|
use zune_jpeg::zune_core;
|
||||||
|
|
||||||
if jpeg_tables.is_some() && compressed_length < 2 {
|
if jpeg_tables.is_some() && compressed_length < 2 {
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
|
TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ impl<R: Read + Seek> EndianReader<R> {
|
|||||||
/// ## Deflate Reader
|
/// ## Deflate Reader
|
||||||
///
|
///
|
||||||
|
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
pub type DeflateReader<R> = flate2::read::ZlibDecoder<R>;
|
pub type DeflateReader<R> = flate2::read::ZlibDecoder<R>;
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -147,11 +148,13 @@ pub type DeflateReader<R> = flate2::read::ZlibDecoder<R>;
|
|||||||
///
|
///
|
||||||
|
|
||||||
/// Reader that decompresses LZW streams
|
/// Reader that decompresses LZW streams
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
pub struct LZWReader<R: Read> {
|
pub struct LZWReader<R: Read> {
|
||||||
reader: BufReader<Take<R>>,
|
reader: BufReader<Take<R>>,
|
||||||
decoder: weezl::decode::Decoder,
|
decoder: weezl::decode::Decoder,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
impl<R: Read> LZWReader<R> {
|
impl<R: Read> LZWReader<R> {
|
||||||
/// Wraps a reader
|
/// Wraps a reader
|
||||||
pub fn new(reader: R, compressed_length: usize) -> LZWReader<R> {
|
pub fn new(reader: R, compressed_length: usize) -> LZWReader<R> {
|
||||||
@@ -165,6 +168,7 @@ impl<R: Read> LZWReader<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
impl<R: Read> Read for LZWReader<R> {
|
impl<R: Read> Read for LZWReader<R> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
use crate::tags::CompressionMethod;
|
use crate::tags::CompressionMethod;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
mod deflate;
|
mod deflate;
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
mod lzw;
|
mod lzw;
|
||||||
mod packbits;
|
mod packbits;
|
||||||
mod uncompressed;
|
mod uncompressed;
|
||||||
|
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
pub use self::deflate::{Deflate, DeflateLevel};
|
pub use self::deflate::{Deflate, DeflateLevel};
|
||||||
|
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
pub use self::lzw::Lzw;
|
pub use self::lzw::Lzw;
|
||||||
|
|
||||||
pub use self::packbits::Packbits;
|
pub use self::packbits::Packbits;
|
||||||
pub use self::uncompressed::Uncompressed;
|
pub use self::uncompressed::Uncompressed;
|
||||||
|
|
||||||
@@ -30,7 +36,9 @@ pub trait Compression: CompressionAlgorithm {
|
|||||||
/// An enum to store each compression algorithm.
|
/// An enum to store each compression algorithm.
|
||||||
pub enum Compressor {
|
pub enum Compressor {
|
||||||
Uncompressed(Uncompressed),
|
Uncompressed(Uncompressed),
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
Lzw(Lzw),
|
Lzw(Lzw),
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
Deflate(Deflate),
|
Deflate(Deflate),
|
||||||
Packbits(Packbits),
|
Packbits(Packbits),
|
||||||
}
|
}
|
||||||
@@ -46,7 +54,9 @@ impl CompressionAlgorithm for Compressor {
|
|||||||
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> {
|
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> {
|
||||||
match self {
|
match self {
|
||||||
Compressor::Uncompressed(algorithm) => algorithm.write_to(writer, bytes),
|
Compressor::Uncompressed(algorithm) => algorithm.write_to(writer, bytes),
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
Compressor::Lzw(algorithm) => algorithm.write_to(writer, bytes),
|
Compressor::Lzw(algorithm) => algorithm.write_to(writer, bytes),
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
Compressor::Deflate(algorithm) => algorithm.write_to(writer, bytes),
|
Compressor::Deflate(algorithm) => algorithm.write_to(writer, bytes),
|
||||||
Compressor::Packbits(algorithm) => algorithm.write_to(writer, bytes),
|
Compressor::Packbits(algorithm) => algorithm.write_to(writer, bytes),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,15 @@ use self::writer::*;
|
|||||||
///
|
///
|
||||||
/// [Predictor::FloatingPoint] is currently not supported.
|
/// [Predictor::FloatingPoint] is currently not supported.
|
||||||
pub type Predictor = crate::tags::Predictor;
|
pub type Predictor = crate::tags::Predictor;
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
pub type DeflateLevel = compression::DeflateLevel;
|
pub type DeflateLevel = compression::DeflateLevel;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum Compression {
|
pub enum Compression {
|
||||||
Uncompressed,
|
Uncompressed,
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
Lzw,
|
Lzw,
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
Deflate(DeflateLevel),
|
Deflate(DeflateLevel),
|
||||||
Packbits,
|
Packbits,
|
||||||
}
|
}
|
||||||
@@ -54,7 +57,9 @@ impl Compression {
|
|||||||
fn tag(&self) -> CompressionMethod {
|
fn tag(&self) -> CompressionMethod {
|
||||||
match self {
|
match self {
|
||||||
Compression::Uncompressed => CompressionMethod::None,
|
Compression::Uncompressed => CompressionMethod::None,
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
Compression::Lzw => CompressionMethod::LZW,
|
Compression::Lzw => CompressionMethod::LZW,
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
Compression::Deflate(_) => CompressionMethod::Deflate,
|
Compression::Deflate(_) => CompressionMethod::Deflate,
|
||||||
Compression::Packbits => CompressionMethod::PackBits,
|
Compression::Packbits => CompressionMethod::PackBits,
|
||||||
}
|
}
|
||||||
@@ -63,7 +68,9 @@ impl Compression {
|
|||||||
fn get_algorithm(&self) -> Compressor {
|
fn get_algorithm(&self) -> Compressor {
|
||||||
match self {
|
match self {
|
||||||
Compression::Uncompressed => compression::Uncompressed {}.get_algorithm(),
|
Compression::Uncompressed => compression::Uncompressed {}.get_algorithm(),
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
Compression::Lzw => compression::Lzw {}.get_algorithm(),
|
Compression::Lzw => compression::Lzw {}.get_algorithm(),
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
Compression::Deflate(level) => compression::Deflate::with_level(*level).get_algorithm(),
|
Compression::Deflate(level) => compression::Deflate::with_level(*level).get_algorithm(),
|
||||||
Compression::Packbits => compression::Packbits {}.get_algorithm(),
|
Compression::Packbits => compression::Packbits {}.get_algorithm(),
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -3,7 +3,6 @@ use std::str;
|
|||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use quick_error::quick_error;
|
use quick_error::quick_error;
|
||||||
use weezl::LzwError;
|
|
||||||
|
|
||||||
use crate::decoder::ChunkType;
|
use crate::decoder::ChunkType;
|
||||||
use crate::tags::{
|
use crate::tags::{
|
||||||
@@ -206,12 +205,14 @@ impl From<std::num::TryFromIntError> for TiffError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LzwError> for TiffError {
|
#[cfg(feature = "lzw")]
|
||||||
fn from(err: LzwError) -> TiffError {
|
impl From<weezl::LzwError> for TiffError {
|
||||||
|
fn from(err: weezl::LzwError) -> TiffError {
|
||||||
TiffError::FormatError(TiffFormatError::CompressedDataCorrupt(err.to_string()))
|
TiffError::FormatError(TiffFormatError::CompressedDataCorrupt(err.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "jpeg")]
|
||||||
impl From<zune_jpeg::errors::DecodeErrors> for TiffError {
|
impl From<zune_jpeg::errors::DecodeErrors> for TiffError {
|
||||||
fn from(err: zune_jpeg::errors::DecodeErrors) -> Self {
|
fn from(err: zune_jpeg::errors::DecodeErrors) -> Self {
|
||||||
TiffError::FormatError(TiffFormatError::CompressedDataCorrupt(err.to_string()))
|
TiffError::FormatError(TiffFormatError::CompressedDataCorrupt(err.to_string()))
|
||||||
|
|||||||
@@ -139,11 +139,13 @@ fn encode_decode_without_compression() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "lzw")]
|
||||||
fn encode_decode_with_lzw() {
|
fn encode_decode_with_lzw() {
|
||||||
encode_decode_with_compression(Compression::Lzw);
|
encode_decode_with_compression(Compression::Lzw);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "deflate")]
|
||||||
fn encode_decode_with_deflate() {
|
fn encode_decode_with_deflate() {
|
||||||
encode_decode_with_compression(Compression::Deflate(DeflateLevel::Fast));
|
encode_decode_with_compression(Compression::Deflate(DeflateLevel::Fast));
|
||||||
encode_decode_with_compression(Compression::Deflate(DeflateLevel::Balanced));
|
encode_decode_with_compression(Compression::Deflate(DeflateLevel::Balanced));
|
||||||
|
|||||||
Reference in new issue
Block a user