Basic TIFF support

This commit is contained in:
Matteo Paonessa 2024-02-15 14:00:54 +01:00
parent ea9452ddf2
commit 77ba5b7bd0
11 changed files with 312 additions and 73 deletions

View File

@ -1,8 +1,9 @@
use crate::jpeg::ChromaSubsampling;
use crate::{compress, compress_to_size, error, initialize_parameters, CSParameters};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use crate::jpeg::ChromaSubsampling;
use crate::{compress, compress_to_size, error, initialize_parameters, CSParameters};
#[repr(C)]
pub struct CCSParameters {
pub keep_metadata: bool,

View File

@ -1,15 +1,19 @@
extern crate alloc;
use crate::jpeg::ChromaSubsampling;
use crate::tiff::TiffCompression;
use crate::tiff::TiffCompression::Deflate;
use crate::utils::{get_filetype_from_memory, get_filetype_from_path, SupportedFileTypes};
use ::tiff::encoder::compression::DeflateLevel;
use error::CaesiumError;
use std::fs::File;
use std::io::Write;
use std::{cmp, fs};
use ::tiff::encoder::compression::DeflateLevel;
use ::tiff::encoder::compression::DeflateLevel::Best;
use error::CaesiumError;
use crate::jpeg::ChromaSubsampling;
use crate::tiff::TiffCompression;
use crate::tiff::TiffCompression::{Deflate, Lzw, Packbits};
use crate::utils::{get_filetype_from_memory, get_filetype_from_path, SupportedFileTypes};
mod error;
#[cfg(feature = "gif")]
mod gif;
@ -20,7 +24,7 @@ pub mod jpeg;
mod png;
mod resize;
#[cfg(feature = "tiff")]
mod tiff;
pub mod tiff;
mod utils;
#[cfg(feature = "webp")]
mod webp;
@ -128,7 +132,7 @@ pub fn compress(
}
_ => {
return Err(CaesiumError {
message: "Unknown file type".into(),
message: "Unknown file type or file not found".into(),
code: 10000,
});
}
@ -178,70 +182,89 @@ pub fn compress_to_size_in_memory(
let max_tries: u32 = 10;
let mut tries: u32 = 0;
let compressed_file = loop {
if tries >= max_tries {
return Err(CaesiumError {
message: "Max tries reached".into(),
code: 10201,
});
let compressed_file = match file_type {
#[cfg(feature = "tiff")]
SupportedFileTypes::Tiff => {
let algorithms = [
Lzw,
Packbits
];
parameters.tiff.deflate_level = Best;
parameters.tiff.algorithm = Deflate;
let mut smallest_result = tiff::compress_to_memory(in_file.clone(), parameters)?; //TODO clone
for tc in algorithms {
parameters.tiff.algorithm = tc;
let result = tiff::compress_to_memory(in_file.clone(), parameters)?; //TODO clone
if result.len() < smallest_result.len() {
smallest_result = result;
}
}
smallest_result
}
let compressed_file = match file_type {
#[cfg(feature = "jpg")]
SupportedFileTypes::Jpeg => {
parameters.jpeg.quality = quality;
jpeg::compress_to_memory(in_file.clone(), parameters)? //TODO clone
}
#[cfg(feature = "png")]
SupportedFileTypes::Png => {
parameters.png.quality = quality;
png::compress_to_memory(in_file.clone(), parameters)? //TODO clone
}
#[cfg(feature = "webp")]
SupportedFileTypes::WebP => {
parameters.webp.quality = quality;
webp::compress_to_memory(in_file.clone(), parameters)? //TODO clone
}
//TODO Tiff
_ => {
_ => loop {
if tries >= max_tries {
return Err(CaesiumError {
message: "Format not supported for compression to size".into(),
code: 10200,
message: "Max tries reached".into(),
code: 10201,
});
}
};
let compressed_file_size = compressed_file.len();
let compressed_file = match file_type {
#[cfg(feature = "jpg")]
SupportedFileTypes::Jpeg => {
parameters.jpeg.quality = quality;
jpeg::compress_to_memory(in_file.clone(), parameters)? //TODO clone
}
#[cfg(feature = "png")]
SupportedFileTypes::Png => {
parameters.png.quality = quality;
png::compress_to_memory(in_file.clone(), parameters)? //TODO clone
}
#[cfg(feature = "webp")]
SupportedFileTypes::WebP => {
parameters.webp.quality = quality;
webp::compress_to_memory(in_file.clone(), parameters)? //TODO clone
}
_ => {
return Err(CaesiumError {
message: "Format not supported for compression to size".into(),
code: 10200,
});
}
};
if compressed_file_size <= max_output_size
&& max_output_size - compressed_file_size < tolerance
{
break compressed_file;
}
let compressed_file_size = compressed_file.len();
if compressed_file_size <= max_output_size {
last_less = quality;
} else {
last_high = quality;
}
let last_quality = quality;
quality = cmp::max(1, cmp::min(100, (last_high + last_less) / 2));
if last_quality == quality {
if quality == 1 && last_high == 1 {
return if return_smallest {
Ok(compressed_file)
} else {
Err(CaesiumError {
message: "Cannot compress to desired quality".into(),
code: 10202,
})
};
if compressed_file_size <= max_output_size
&& max_output_size - compressed_file_size < tolerance
{
break compressed_file;
}
break compressed_file;
}
if compressed_file_size <= max_output_size {
last_less = quality;
} else {
last_high = quality;
}
let last_quality = quality;
quality = cmp::max(1, cmp::min(100, (last_high + last_less) / 2));
if last_quality == quality {
if quality == 1 && last_high == 1 {
return if return_smallest {
Ok(compressed_file)
} else {
Err(CaesiumError {
message: "Cannot compress to desired quality".into(),
code: 10202,
})
};
}
tries += 1;
break compressed_file;
}
tries += 1;
},
};
Ok(compressed_file)

View File

@ -15,6 +15,6 @@ fn main() -> ExitCode {
Err(e) => {
eprintln!("{}", e);
ExitCode::FAILURE
},
}
}
}

View File

@ -1,10 +1,11 @@
use std::io::Cursor;
use crate::error::CaesiumError;
use image::imageops::FilterType;
use image::io::Reader as ImageReader;
use image::DynamicImage;
use crate::error::CaesiumError;
pub fn resize(
image_buffer: Vec<u8>,
width: u32,

View File

@ -1,12 +1,14 @@
use crate::error::CaesiumError;
use crate::CSParameters;
use image::ImageFormat::Tiff;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use image::ImageFormat::Tiff;
use tiff::encoder::colortype::{RGB8, RGBA8};
use tiff::encoder::compression::{Deflate, Lzw, Packbits, Uncompressed};
use tiff::encoder::TiffEncoder;
use crate::error::CaesiumError;
use crate::resize::resize_image;
use crate::CSParameters;
#[derive(Copy, Clone, PartialEq)]
pub enum TiffCompression {
@ -72,6 +74,7 @@ pub fn compress_to_memory(
message: e.to_string(),
code: 20505,
})?;
let compression_result = match parameters.tiff.algorithm {
TiffCompression::Deflate => match color_type {
image::ColorType::Rgb8 => encoder.write_image_with_compression::<RGB8, Deflate>(

BIN
tests/samples/rgb8.tif Normal file

Binary file not shown.

BIN
tests/samples/rgba8.tif Normal file

Binary file not shown.

Binary file not shown.

211
tests/tiff.rs Normal file
View File

@ -0,0 +1,211 @@
use std::sync::Once;
use tiff::encoder::compression::DeflateLevel::Balanced;
use crate::cleanup::remove_compressed_test_file;
mod cleanup;
static INIT: Once = Once::new();
pub fn initialize(file: &str) {
INIT.call_once(|| {
remove_compressed_test_file(file);
});
}
#[test]
fn rgb8_uncompressed() {
let output = "tests/samples/output/uncompressed_rgb8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Uncompressed;
caesium::compress(
String::from("tests/samples/rgb8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgba8_uncompressed() {
let output = "tests/samples/output/uncompressed_rgba8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Uncompressed;
caesium::compress(
String::from("tests/samples/rgba8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgb8_deflate() {
let output = "tests/samples/output/deflate_rgb8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Deflate;
params.tiff.deflate_level = Balanced;
caesium::compress(
String::from("tests/samples/rgb8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgba8_deflate() {
let output = "tests/samples/output/deflate_rgba8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Deflate;
params.tiff.deflate_level = Balanced;
caesium::compress(
String::from("tests/samples/rgba8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgb8_lzw() {
let output = "tests/samples/output/lzw_rgb8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Lzw;
caesium::compress(
String::from("tests/samples/rgb8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgba8_lzw() {
let output = "tests/samples/output/lzw_rgba8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Lzw;
caesium::compress(
String::from("tests/samples/rgba8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgb8_packbits() {
let output = "tests/samples/output/packbits_rgb8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Packbits;
caesium::compress(
String::from("tests/samples/rgb8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgba8_packbits() {
let output = "tests/samples/output/packbits_rgba8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Packbits;
caesium::compress(
String::from("tests/samples/rgba8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
remove_compressed_test_file(output)
}
#[test]
fn rgb8_downscale() {
let output = "tests/samples/output/downscale_rgb8.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Lzw;
params.width = 50;
params.height = 20;
caesium::compress(
String::from("tests/samples/rgb8.tif"),
String::from(output),
&params,
)
.unwrap();
assert!(std::path::Path::new(output).exists());
assert_eq!(
infer::get_from_path(output).unwrap().unwrap().mime_type(),
"image/tiff"
);
assert_eq!(image::image_dimensions(output).unwrap(), (50, 20));
remove_compressed_test_file(output)
}
#[test]
fn unsupported() {
let output = "tests/samples/output/unsupported.tif";
initialize(output);
let mut params = caesium::initialize_parameters();
params.tiff.algorithm = caesium::tiff::TiffCompression::Lzw;
assert!(caesium::compress(
String::from("tests/samples/unsupported.tif"),
String::from(output),
&params,
).is_err());
}