Keep original orientation while converting

This commit is contained in:
Matteo Paonessa 2024-07-20 14:08:50 +02:00
parent ef402d1a1d
commit a67f2475a0
2 changed files with 59 additions and 24 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libcaesium" name = "libcaesium"
version = "0.16.1" version = "0.16.2"
authors = ["Matteo Paonessa <matteo.paonessa@gmail.com>"] authors = ["Matteo Paonessa <matteo.paonessa@gmail.com>"]
edition = "2021" edition = "2021"
categories = ["multimedia::images"] categories = ["multimedia::images"]
@ -42,10 +42,10 @@ bytes = "1.6"
lodepng = { version = "3.10", optional = true } lodepng = { version = "3.10", optional = true }
imagequant = { version = "4.3", optional = true, default-features = false } imagequant = { version = "4.3", optional = true, default-features = false }
tiff = { version = "0.9" } tiff = { version = "0.9" }
kamadak-exif = "0.5"
[dev-dependencies] [dev-dependencies]
dssim = { version = "3.3", default-features = false, features = ["no-macos-vimage"] } dssim = { version = "3.3", default-features = false, features = ["no-macos-vimage"] }
kamadak-exif = "0.5"
[[bin]] [[bin]]
name = "main" name = "main"

View File

@ -5,16 +5,18 @@ use image::{ColorType, DynamicImage, ImageFormat};
use image::io::Reader as ImageReader; use image::io::Reader as ImageReader;
use img_parts::{DynImage, ImageEXIF, ImageICC}; use img_parts::{DynImage, ImageEXIF, ImageICC};
use crate::error::CaesiumError;
use crate::{compress_in_memory, CSParameters, SupportedFileTypes}; use crate::{compress_in_memory, CSParameters, SupportedFileTypes};
use crate::error::CaesiumError;
use crate::utils::get_filetype_from_memory; use crate::utils::get_filetype_from_memory;
pub fn convert_in_memory(in_file: Vec<u8>, format: SupportedFileTypes, parameters: &CSParameters) -> Result<Vec<u8>, CaesiumError> { pub fn convert_in_memory(in_file: Vec<u8>, format: SupportedFileTypes, parameters: &CSParameters) -> Result<Vec<u8>, CaesiumError> {
let mut iccp = None;
let mut exif = None;
let output_format = map_image_format(format)?; let output_format = map_image_format(format)?;
let original_file_type = get_filetype_from_memory(&in_file);
let file_type = get_filetype_from_memory(&in_file); if original_file_type == format {
if file_type == format {
return Err(CaesiumError { return Err(CaesiumError {
message: "Cannot convert to the same format".into(), message: "Cannot convert to the same format".into(),
code: 10407, code: 10407,
@ -24,14 +26,37 @@ pub fn convert_in_memory(in_file: Vec<u8>, format: SupportedFileTypes, parameter
let i = in_file.as_slice(); let i = in_file.as_slice();
let mut original_image = ImageReader::new(Cursor::new(i)).with_guessed_format() let mut original_image = ImageReader::new(Cursor::new(i)).with_guessed_format()
.map_err(|e| CaesiumError { .map_err(|e| CaesiumError {
message: e.to_string(), message: e.to_string(),
code: 10402, code: 10402,
})?.decode() })?.decode()
.map_err(|e| CaesiumError { .map_err(|e| CaesiumError {
message: e.to_string(), message: e.to_string(),
code: 10403, code: 10403,
})?; })?;
if parameters.keep_metadata {
if original_file_type == SupportedFileTypes::Jpeg {
let orientation = get_jpeg_orientation(in_file.as_slice());
original_image = match orientation {
2 => original_image.fliph(),
3 => original_image.rotate180(),
4 => original_image.flipv(),
5 => original_image.rotate90().fliph(),
6 => original_image.rotate90(),
7 => original_image.fliph().rotate90(),
8 => original_image.rotate270(),
_ => original_image
}
}
(iccp, exif) = DynImage::from_bytes(Bytes::from(in_file))
.map_err(|e| CaesiumError {
message: e.to_string(),
code: 10401,
})?
.map_or((None, None), |dimg| (dimg.icc_profile(), dimg.exif()));
}
if format == SupportedFileTypes::Jpeg { if format == SupportedFileTypes::Jpeg {
original_image = match original_image.color() { original_image = match original_image.color() {
ColorType::Rgba8 => DynamicImage::from(original_image.to_rgb8()), ColorType::Rgba8 => DynamicImage::from(original_image.to_rgb8()),
@ -40,28 +65,21 @@ pub fn convert_in_memory(in_file: Vec<u8>, format: SupportedFileTypes, parameter
_ => original_image, _ => original_image,
}; };
} }
let mut output_image: Vec<u8> = Vec::new(); let mut output_image: Vec<u8> = Vec::new();
original_image.write_to(&mut Cursor::new(&mut output_image), output_format) original_image.write_to(&mut Cursor::new(&mut output_image), output_format)
.map_err(|e| CaesiumError { .map_err(|e| CaesiumError {
message: e.to_string(), message: e.to_string(),
code: 10404, code: 10404,
})?; })?;
let compressed_converted_image = compress_in_memory(output_image, parameters) let compressed_converted_image = compress_in_memory(output_image, parameters)
.map_err(|e| CaesiumError { .map_err(|e| CaesiumError {
message: e.to_string(), message: e.to_string(),
code: 10405, code: 10405,
})?; })?;
if parameters.keep_metadata { if parameters.keep_metadata {
let (iccp, exif) = DynImage::from_bytes(Bytes::from(in_file))
.map_err(|e| CaesiumError {
message: e.to_string(),
code: 10401,
})?
.map_or((None, None), |dimg| (dimg.icc_profile(), dimg.exif()));
let dyn_image = DynImage::from_bytes(Bytes::from(compressed_converted_image.clone())) let dyn_image = DynImage::from_bytes(Bytes::from(compressed_converted_image.clone()))
.map_err(|e| CaesiumError { .map_err(|e| CaesiumError {
message: e.to_string(), message: e.to_string(),
@ -109,4 +127,21 @@ fn map_image_format(format: SupportedFileTypes) -> Result<ImageFormat, CaesiumEr
}; };
Ok(image_format) Ok(image_format)
} }
fn get_jpeg_orientation(data: &[u8]) -> u32 {
let reader = exif::Reader::new();
let mut cursor = Cursor::new(data);
let exif_data = match reader.read_from_container(&mut cursor) {
Ok(v) => v,
Err(_) => return 1
};
let exif_field = match exif_data.get_field(exif::Tag::Orientation, exif::In::PRIMARY) {
Some(value) => value,
None => return 1,
};
exif_field.value.get_uint(0).unwrap_or(1)
}