moved compression into TiffWriter

This commit is contained in:
Pretorer committed 2022-01-10 11:55:08 +01:00
1 parent de95ac35a2
commit 9f1e3d7a89
7 files changed
+179 -193

No files matched your search

+21 -46
View File
@@ -1,23 +1,11 @@
use std::{
convert::TryInto,
io::{Seek, Write},
};
use crate::{encoder::compression::*, tags::CompressionMethod};
use flate2::{write::ZlibEncoder, Compression as FlateCompression};
use crate::{
encoder::{
colortype::ColorType, compression::Compression, DirectoryEncoder, TiffKind, TiffValue,
},
tags::CompressionMethod,
TiffResult,
};
use std::io::Write;
/// The Deflate algorithm used to compress image data in TIFF files.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct Deflate {
level: FlateCompression,
buffer: Vec<u8>,
}
/// The level of compression used by the Deflate algorithm.
@@ -40,13 +28,9 @@ impl Default for DeflateLevel {
}
impl Deflate {
/// Lets be greedy and allocate more bytes in advance. We will likely encode longer image strips.
const DEFAULT_BUFFER_SIZE: usize = 256;
/// Create a new deflate compr+essor with a specific level of compression.
/// Create a new deflate compressor with a specific level of compression.
pub fn with_level(level: DeflateLevel) -> Self {
Self {
buffer: Vec::with_capacity(Self::DEFAULT_BUFFER_SIZE),
level: FlateCompression::new(level as u32),
}
}
@@ -61,36 +45,25 @@ impl Default for Deflate {
impl Compression for Deflate {
const COMPRESSION_METHOD: CompressionMethod = CompressionMethod::Deflate;
fn write_to<'a, T: ColorType, K: TiffKind, W: 'a + Write + Seek>(
&mut self,
encoder: &mut DirectoryEncoder<'a, W, K>,
value: &[T::Inner],
) -> TiffResult<(K::OffsetType, K::OffsetType)>
where
[T::Inner]: TiffValue,
{
let data = value.data();
{
let mut encoder = ZlibEncoder::new(&mut self.buffer, self.level);
encoder.write_all(&data)?;
encoder.finish()?;
}
fn get_algorithm(&self) -> Compressor {
Compressor::Deflate(self.clone())
}
}
let compressed_byte_count = self.buffer.len().try_into()?;
let offset = encoder
.write_data(self.buffer.as_slice())
.and_then(K::convert_offset)?;
// Clear the buffer for the next compression.
self.buffer.clear();
Ok((offset, compressed_byte_count))
impl CompressionAlgorithm for Deflate {
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> {
let mut encoder = ZlibEncoder::new(writer, self.level);
encoder.write(&bytes)?;
encoder.try_finish()?;
Ok(encoder.total_out())
}
}
#[cfg(test)]
mod tests {
use crate::encoder::compression::tests::{compress, TEST_DATA};
use super::*;
use crate::encoder::compression::tests::TEST_DATA;
use std::io::Cursor;
#[test]
fn test_deflate() {
@@ -102,7 +75,9 @@ mod tests {
0x3E, 0xAC, 0xF1, 0x98, 0xB9, 0x70, 0x17, 0x13,
];
let compressed_data = compress(TEST_DATA, super::Deflate::default());
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
let mut compressed_data = Vec::<u8>::new();
let mut writer = Cursor::new(&mut compressed_data);
Deflate::default().write_to(&mut writer, TEST_DATA).unwrap();
assert_eq!(EXPECTED_COMPRESSED_DATA, compressed_data.as_slice());
}
}
+21 -53
View File
@@ -1,67 +1,33 @@
use std::{
convert::TryInto,
io::{Seek, Write},
};
use crate::{encoder::compression::*, tags::CompressionMethod};
use std::io::Write;
use weezl::encode::Encoder as LZWEncoder;
use crate::{
encoder::{
colortype::ColorType, compression::Compression, DirectoryEncoder, TiffKind, TiffValue,
},
tags::CompressionMethod,
TiffResult,
};
/// The LZW algorithm used to compress image data in TIFF files.
#[derive(Debug, Clone)]
pub struct Lzw {
buffer: Vec<u8>,
}
impl Default for Lzw {
fn default() -> Self {
// Lets be greedy and allocate more bytes in advance. We will likely encode longer image strips.
const DEFAULT_BUFFER_SIZE: usize = 256;
Self {
buffer: Vec::with_capacity(DEFAULT_BUFFER_SIZE),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Lzw;
impl Compression for Lzw {
const COMPRESSION_METHOD: CompressionMethod = CompressionMethod::LZW;
fn write_to<'a, T: ColorType, K: TiffKind, W: 'a + Write + Seek>(
&mut self,
encoder: &mut DirectoryEncoder<'a, W, K>,
value: &[T::Inner],
) -> TiffResult<(K::OffsetType, K::OffsetType)>
where
[T::Inner]: TiffValue,
{
let bytes = value.data();
let compressed_byte_count = {
let mut encoder = LZWEncoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
let result = encoder.into_vec(&mut self.buffer).encode_all(&bytes);
result.status.map(|_| result.consumed_out)
}?
.try_into()?;
fn get_algorithm(&self) -> Compressor {
Compressor::Lzw(*self)
}
}
let offset = encoder
.write_data(self.buffer.as_slice())
.and_then(K::convert_offset)?;
// Clear the buffer for the next compression.
self.buffer.clear();
Ok((offset, compressed_byte_count))
impl CompressionAlgorithm for Lzw {
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> {
let mut encoder = LZWEncoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
let result = encoder.into_stream(writer).encode_all(bytes);
let byte_count = result.bytes_written as u64;
result.status.map(|_| byte_count)
}
}
#[cfg(test)]
mod tests {
use crate::encoder::compression::tests::{compress, TEST_DATA};
use super::*;
use crate::encoder::compression::tests::TEST_DATA;
use std::io::Cursor;
#[test]
fn test_lzw() {
@@ -73,7 +39,9 @@ mod tests {
0x4E, 0x86, 0x83, 0x69, 0xCC, 0x5D, 0x01,
];
let compressed_data = compress(TEST_DATA, super::Lzw::default());
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
let mut compressed_data = Vec::<u8>::new();
let mut writer = Cursor::new(&mut compressed_data);
Lzw::default().write_to(&mut writer, TEST_DATA).unwrap();
assert_eq!(EXPECTED_COMPRESSED_DATA, compressed_data.as_slice());
}
}
+37 -39
View File
@@ -1,10 +1,5 @@
use std::io::prelude::*;
use crate::{
encoder::{ColorType, DirectoryEncoder, TiffKind, TiffValue},
error::TiffResult,
tags::CompressionMethod,
};
use crate::tags::CompressionMethod;
use std::io::{self, Write};
mod deflate;
mod lzw;
@@ -14,45 +9,48 @@ pub use self::deflate::{Deflate, DeflateLevel};
pub use self::lzw::Lzw;
pub use self::uncompressed::Uncompressed;
/// An algorithm used for compression with associated optional buffers and/or configurations.
pub trait Compression: Default {
/// An algorithm used for compression
pub trait CompressionAlgorithm {
/// The algorithm writes data directly into the writer.
/// It returns the total number of bytes written.
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error>;
}
/// An algorithm used for compression with associated enums and optional configurations.
pub trait Compression: CompressionAlgorithm {
/// The corresponding tag to the algorithm.
const COMPRESSION_METHOD: CompressionMethod;
/// Write the data of a specific color type to the given encoder and return the offset and byte count, respectively.
fn write_to<'a, T: ColorType, K: TiffKind, W: 'a + Write + Seek>(
&mut self,
encoder: &mut DirectoryEncoder<'a, W, K>,
value: &[T::Inner],
) -> TiffResult<(K::OffsetType, K::OffsetType)>
where
[T::Inner]: TiffValue;
/// Method to optain a type that can store each variant of comression algorithm.
fn get_algorithm(&self) -> Compressor;
}
/// An enum to store each compression algorithm.
pub enum Compressor {
Uncompressed(Uncompressed),
Lzw(Lzw),
Deflate(Deflate),
}
impl Default for Compressor {
/// The default compression strategy does not apply any compression.
fn default() -> Self {
Compressor::Uncompressed(Uncompressed::default())
}
}
impl CompressionAlgorithm for Compressor {
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> {
match self {
Compressor::Uncompressed(algorithm) => algorithm.write_to(writer, bytes),
Compressor::Lzw(algorithm) => algorithm.write_to(writer, bytes),
Compressor::Deflate(algorithm) => algorithm.write_to(writer, bytes),
}
}
}
#[cfg(test)]
mod tests {
use crate::encoder::{colortype::Gray8, compression::Compression, TiffEncoder};
use std::io::Cursor;
pub const TEST_DATA: &'static [u8] =
b"This is a string for checking various compression algorithms.";
pub fn compress<C: Compression>(data: &[u8], compressor: C) -> Vec<u8> {
let mut buffer = Vec::new();
// Compress the data
{
let mut encoder = TiffEncoder::new(Cursor::new(&mut buffer)).unwrap();
let encoder = encoder
.new_image_with_compression::<Gray8, _>(data.len() as u32, 1, compressor)
.unwrap();
encoder.write_data(&data).unwrap();
}
// Remove the meta data written by the encoder
buffer.drain(..8);
buffer.drain(buffer.len() - 178..);
buffer
}
}
+18 -25
View File
@@ -1,15 +1,5 @@
use std::{
convert::TryInto,
io::{Seek, Write},
};
use crate::{
encoder::{
colortype::ColorType, compression::Compression, DirectoryEncoder, TiffKind, TiffValue,
},
tags::CompressionMethod,
TiffResult,
};
use crate::{encoder::compression::*, tags::CompressionMethod};
use std::io::Write;
/// The default algorithm which does not compress at all.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
@@ -18,27 +8,30 @@ pub struct Uncompressed;
impl Compression for Uncompressed {
const COMPRESSION_METHOD: CompressionMethod = CompressionMethod::None;
fn write_to<'a, T: ColorType, K: TiffKind, W: 'a + Write + Seek>(
&mut self,
encoder: &mut DirectoryEncoder<'a, W, K>,
value: &[T::Inner],
) -> TiffResult<(K::OffsetType, K::OffsetType)>
where
[T::Inner]: TiffValue,
{
let byte_count = value.len().try_into()?;
let offset = encoder.write_data(value).and_then(K::convert_offset)?;
Ok((offset, byte_count))
fn get_algorithm(&self) -> Compressor {
Compressor::Uncompressed(*self)
}
}
impl CompressionAlgorithm for Uncompressed {
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> {
writer.write(bytes).map(|byte_count| byte_count as u64)
}
}
#[cfg(test)]
mod tests {
use crate::encoder::compression::tests::{compress, TEST_DATA};
use super::*;
use crate::encoder::compression::tests::TEST_DATA;
use std::io::Cursor;
#[test]
fn test_no_compression() {
let compressed_data = compress(TEST_DATA, super::Uncompressed);
let mut compressed_data = Vec::<u8>::new();
let mut writer = Cursor::new(&mut compressed_data);
Uncompressed::default()
.write_to(&mut writer, TEST_DATA)
.unwrap();
assert_eq!(TEST_DATA, compressed_data);
}
}