Skip to content

Commit

Permalink
Use image::ImageDecoder::orientation() for image rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
chitoku-k committed Oct 19, 2024
1 parent c686f35 commit 3f06eda
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 51 deletions.
16 changes: 0 additions & 16 deletions api/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions api/crates/thumbnails/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ features = ["constructor"]
[dependencies.image]
version = "0.25.4"

[dependencies.kamadak-exif]
version = "0.5.5"

[dependencies.tokio]
version = "1.40.0"
features = ["rt-multi-thread"]
45 changes: 13 additions & 32 deletions api/crates/thumbnails/src/processor.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use std::io::{BufRead, Cursor, Seek, SeekFrom};
use std::io::{BufRead, Cursor, Seek};

use derive_more::Constructor;
use domain::{
entity::replicas::{OriginalImage, Size, ThumbnailImage},
error::{Error, ErrorKind, Result},
processor::media::MediumImageProcessor,
};
use exif::{In, Tag};
use image::{imageops::{flip_horizontal, flip_vertical, rotate180, rotate270, rotate90}, DynamicImage, ImageReader};
use image::{DynamicImage, ImageDecoder, ImageReader};
use tokio::task;

pub use image::{imageops::FilterType, ImageFormat};
Expand All @@ -19,24 +18,8 @@ pub struct InMemoryImageProcessor {
thumbnail_filter: FilterType,
}

fn rotate(image: DynamicImage, orientation: u32) -> Result<DynamicImage> {
let image = match orientation {
1 => image,
2 => DynamicImage::from(flip_horizontal(&image)),
3 => DynamicImage::from(rotate180(&image)),
4 => DynamicImage::from(flip_vertical(&image)),
5 => DynamicImage::from(flip_horizontal(&rotate90(&image))),
6 => DynamicImage::from(rotate90(&image)),
7 => DynamicImage::from(flip_horizontal(&rotate270(&image))),
8 => DynamicImage::from(rotate270(&image)),
_ => return Err(Error::from(ErrorKind::MediumReplicaDecodeFailed)),
};

Ok(image)
}

impl MediumImageProcessor for InMemoryImageProcessor {
async fn generate_thumbnail<R>(&self, mut read: R) -> Result<(OriginalImage, ThumbnailImage)>
async fn generate_thumbnail<R>(&self, read: R) -> Result<(OriginalImage, ThumbnailImage)>
where
R: BufRead + Seek + Send + 'static,
{
Expand All @@ -45,23 +28,21 @@ impl MediumImageProcessor for InMemoryImageProcessor {
let thumbnail_format = self.thumbnail_format;

task::spawn_blocking(move || {
let orientation = exif::Reader::new()
.read_from_container(&mut read)
.ok()
.and_then(|e| e.get_field(Tag::Orientation, In::PRIMARY).and_then(|f| f.value.get_uint(0)))
.unwrap_or(1);

read.seek(SeekFrom::Start(0))
.map_err(|e| Error::new(ErrorKind::MediumReplicaReadFailed, e))?;

let reader = ImageReader::new(read)
.with_guessed_format()
.map_err(|e| Error::new(ErrorKind::MediumReplicaReadFailed, e))?;

let format = reader.format().ok_or(ErrorKind::MediumReplicaUnsupported)?;
let image = reader.decode()
.map_err(|e| Error::new(ErrorKind::MediumReplicaDecodeFailed, e))
.and_then(|i| rotate(i, orientation))?;
let mut decoder = reader.into_decoder()
.map_err(|e| Error::new(ErrorKind::MediumReplicaDecodeFailed, e))?;

let orientation = decoder.orientation()
.map_err(|e| Error::new(ErrorKind::MediumReplicaDecodeFailed, e))?;

let mut image = DynamicImage::from_decoder(decoder)
.map_err(|e| Error::new(ErrorKind::MediumReplicaDecodeFailed, e))?;

image.apply_orientation(orientation);

let mut body = Vec::new();
let thumbnail = image.resize(thumbnail_size.width, thumbnail_size.height, thumbnail_filter);
Expand Down

0 comments on commit 3f06eda

Please sign in to comment.