Basic TIFF support
This commit is contained in:
parent
ea9452ddf2
commit
77ba5b7bd0
|
@ -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::ffi::{CStr, CString};
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
use crate::jpeg::ChromaSubsampling;
|
||||||
|
use crate::{compress, compress_to_size, error, initialize_parameters, CSParameters};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct CCSParameters {
|
pub struct CCSParameters {
|
||||||
pub keep_metadata: bool,
|
pub keep_metadata: bool,
|
||||||
|
|
147
src/lib.rs
147
src/lib.rs
|
@ -1,15 +1,19 @@
|
||||||
extern crate alloc;
|
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::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{cmp, fs};
|
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;
|
mod error;
|
||||||
#[cfg(feature = "gif")]
|
#[cfg(feature = "gif")]
|
||||||
mod gif;
|
mod gif;
|
||||||
|
@ -20,7 +24,7 @@ pub mod jpeg;
|
||||||
mod png;
|
mod png;
|
||||||
mod resize;
|
mod resize;
|
||||||
#[cfg(feature = "tiff")]
|
#[cfg(feature = "tiff")]
|
||||||
mod tiff;
|
pub mod tiff;
|
||||||
mod utils;
|
mod utils;
|
||||||
#[cfg(feature = "webp")]
|
#[cfg(feature = "webp")]
|
||||||
mod webp;
|
mod webp;
|
||||||
|
@ -128,7 +132,7 @@ pub fn compress(
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CaesiumError {
|
return Err(CaesiumError {
|
||||||
message: "Unknown file type".into(),
|
message: "Unknown file type or file not found".into(),
|
||||||
code: 10000,
|
code: 10000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -178,70 +182,89 @@ pub fn compress_to_size_in_memory(
|
||||||
let max_tries: u32 = 10;
|
let max_tries: u32 = 10;
|
||||||
let mut tries: u32 = 0;
|
let mut tries: u32 = 0;
|
||||||
|
|
||||||
let compressed_file = loop {
|
let compressed_file = match file_type {
|
||||||
if tries >= max_tries {
|
#[cfg(feature = "tiff")]
|
||||||
return Err(CaesiumError {
|
SupportedFileTypes::Tiff => {
|
||||||
message: "Max tries reached".into(),
|
let algorithms = [
|
||||||
code: 10201,
|
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
|
||||||
}
|
}
|
||||||
|
_ => loop {
|
||||||
let compressed_file = match file_type {
|
if tries >= max_tries {
|
||||||
#[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
|
|
||||||
_ => {
|
|
||||||
return Err(CaesiumError {
|
return Err(CaesiumError {
|
||||||
message: "Format not supported for compression to size".into(),
|
message: "Max tries reached".into(),
|
||||||
code: 10200,
|
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
|
let compressed_file_size = compressed_file.len();
|
||||||
&& max_output_size - compressed_file_size < tolerance
|
|
||||||
{
|
|
||||||
break compressed_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
if compressed_file_size <= max_output_size {
|
if compressed_file_size <= max_output_size
|
||||||
last_less = quality;
|
&& max_output_size - compressed_file_size < tolerance
|
||||||
} else {
|
{
|
||||||
last_high = quality;
|
break compressed_file;
|
||||||
}
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
Ok(compressed_file)
|
||||||
|
|
|
@ -15,6 +15,6 @@ fn main() -> ExitCode {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
ExitCode::FAILURE
|
ExitCode::FAILURE
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use crate::error::CaesiumError;
|
|
||||||
use image::imageops::FilterType;
|
use image::imageops::FilterType;
|
||||||
use image::io::Reader as ImageReader;
|
use image::io::Reader as ImageReader;
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
|
|
||||||
|
use crate::error::CaesiumError;
|
||||||
|
|
||||||
pub fn resize(
|
pub fn resize(
|
||||||
image_buffer: Vec<u8>,
|
image_buffer: Vec<u8>,
|
||||||
width: u32,
|
width: u32,
|
||||||
|
|
13
src/tiff.rs
13
src/tiff.rs
|
@ -1,12 +1,14 @@
|
||||||
use crate::error::CaesiumError;
|
|
||||||
use crate::CSParameters;
|
|
||||||
use image::ImageFormat::Tiff;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Cursor, Read, Write};
|
use std::io::{Cursor, Read, Write};
|
||||||
|
|
||||||
|
use image::ImageFormat::Tiff;
|
||||||
use tiff::encoder::colortype::{RGB8, RGBA8};
|
use tiff::encoder::colortype::{RGB8, RGBA8};
|
||||||
use tiff::encoder::compression::{Deflate, Lzw, Packbits, Uncompressed};
|
use tiff::encoder::compression::{Deflate, Lzw, Packbits, Uncompressed};
|
||||||
use tiff::encoder::TiffEncoder;
|
use tiff::encoder::TiffEncoder;
|
||||||
|
|
||||||
|
use crate::error::CaesiumError;
|
||||||
use crate::resize::resize_image;
|
use crate::resize::resize_image;
|
||||||
|
use crate::CSParameters;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum TiffCompression {
|
pub enum TiffCompression {
|
||||||
|
@ -40,7 +42,7 @@ pub fn compress(
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
code: 20502,
|
code: 20502,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
output_file
|
output_file
|
||||||
.write_all(&compressed_image)
|
.write_all(&compressed_image)
|
||||||
.map_err(|e| CaesiumError {
|
.map_err(|e| CaesiumError {
|
||||||
|
@ -72,6 +74,7 @@ pub fn compress_to_memory(
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
code: 20505,
|
code: 20505,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let compression_result = match parameters.tiff.algorithm {
|
let compression_result = match parameters.tiff.algorithm {
|
||||||
TiffCompression::Deflate => match color_type {
|
TiffCompression::Deflate => match color_type {
|
||||||
image::ColorType::Rgb8 => encoder.write_image_with_compression::<RGB8, Deflate>(
|
image::ColorType::Rgb8 => encoder.write_image_with_compression::<RGB8, Deflate>(
|
||||||
|
@ -163,4 +166,4 @@ pub fn compress_to_memory(
|
||||||
code: 20507,
|
code: 20507,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,12 +25,12 @@ pub fn compress(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let compressed_image = compress_to_memory(input_data, parameters)?;
|
let compressed_image = compress_to_memory(input_data, parameters)?;
|
||||||
|
|
||||||
let mut output_file = File::create(output_path).map_err(|e| CaesiumError {
|
let mut output_file = File::create(output_path).map_err(|e| CaesiumError {
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
code: 20302,
|
code: 20302,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
output_file
|
output_file
|
||||||
.write_all(&compressed_image)
|
.write_all(&compressed_image)
|
||||||
.map_err(|e| CaesiumError {
|
.map_err(|e| CaesiumError {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
¶ms,
|
||||||
|
).is_err());
|
||||||
|
}
|
Loading…
Reference in New Issue