Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a function to apply Exif rotation #2299

Merged
merged 20 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d9b4517
Add in-place 180 degree rotation to DynamicImage
Shnatsel Jul 27, 2024
da93b9d
Doc comment: when rotating 180 degrees, direction doesn't matter
Shnatsel Jul 27, 2024
f8cea83
Add in-place flips to DynamicImage
Shnatsel Jul 27, 2024
a799736
Add a function to apply Exif rotation
Shnatsel Jul 30, 2024
fb1ccaf
Do not promise to implement complex algorithms that aren't currently …
Shnatsel Jul 30, 2024
d3a8acf
Create a type to encode image orientation, accept it in the function …
Shnatsel Sep 1, 2024
91f15b1
Make newly added in-place transformation functions private
Shnatsel Sep 9, 2024
92d2ec2
Change doc comment following review comment
Shnatsel Sep 9, 2024
ecdf8f5
Rename flip orientations to make them more explicit
Shnatsel Sep 9, 2024
fad5aaa
Document apply_orientation as an alternative to non-in-place functions
Shnatsel Sep 9, 2024
c3cec8c
Suppress Clippy false positive
Shnatsel Sep 9, 2024
873c410
Link to Clippy lint issue instead of just handwaving
Shnatsel Sep 9, 2024
644b68e
Merge branch 'image-rs:main' into exif-rotation
Shnatsel Sep 9, 2024
9d1a694
Make apply_orientation infallible now that it accepts an enum
Shnatsel Sep 9, 2024
25bd5eb
Fix incorrect doc comment
Shnatsel Sep 12, 2024
b4cb2d6
Fix erroneous exif conversion for value 7
Shnatsel Sep 13, 2024
9c0d517
Add a function to convert from Orientation to Exif values
Shnatsel Sep 13, 2024
005844d
Rename the module with `Orientation` struct to `metadata`
Shnatsel Sep 13, 2024
c461e99
Fix doclinks
Shnatsel Sep 13, 2024
6dad395
Link from Orientation struct to the function that applies it
Shnatsel Sep 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions src/dynimage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ use crate::image::{GenericImage, GenericImageView, ImageDecoder, ImageEncoder, I
use crate::image_reader::free_functions;
use crate::math::resize_dimensions;
use crate::traits::Pixel;
use crate::ImageReader;
use crate::{image, Luma, LumaA};
use crate::{imageops, ExtendedColorType};
use crate::{ImageReader, Orientation};
use crate::{Rgb32FImage, Rgba32FImage};

/// A Dynamic Image
Expand Down Expand Up @@ -878,30 +878,76 @@ impl DynamicImage {
dynamic_map!(*self, ref p => imageops::flip_vertical(p))
}

/// Flip this image vertically in place
pub fn flipv_in_place(&mut self) {
Shnatsel marked this conversation as resolved.
Show resolved Hide resolved
dynamic_map!(*self, ref mut p, imageops::flip_vertical_in_place(p))
}

/// Flip this image horizontally
Shnatsel marked this conversation as resolved.
Show resolved Hide resolved
#[must_use]
pub fn fliph(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::flip_horizontal(p))
}

/// Flip this image horizontally in place
pub fn fliph_in_place(&mut self) {
Shnatsel marked this conversation as resolved.
Show resolved Hide resolved
dynamic_map!(*self, ref mut p, imageops::flip_horizontal_in_place(p))
}

/// Rotate this image 90 degrees clockwise.
#[must_use]
pub fn rotate90(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::rotate90(p))
}

/// Rotate this image 180 degrees clockwise.
/// Rotate this image 180 degrees.
#[must_use]
pub fn rotate180(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::rotate180(p))
}

/// Rotate this image 180 degrees in place.
pub fn rotate180_in_place(&mut self) {
Shnatsel marked this conversation as resolved.
Show resolved Hide resolved
dynamic_map!(*self, ref mut p, imageops::rotate180_in_place(p))
}

/// Rotate this image 270 degrees clockwise.
#[must_use]
pub fn rotate270(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::rotate270(p))
}

/// Applies the specified [Orientation] to the image.
Shnatsel marked this conversation as resolved.
Show resolved Hide resolved
///
/// Note that for some orientations cannot be efficiently applied in-place.
/// In that case this function will make a copy of the image internally.
///
/// If this matters to you, please see the documentation on the variants of [Orientation]
/// to learn which orientations can and cannot be applied without copying.
pub fn apply_orientation(&mut self, orientation: Orientation) -> Result<(), ImageError> {
let image = self;
match orientation {
Orientation::NoTransforms => Ok(()),
Orientation::Rotate90 => Ok(*image = image.rotate90()),
Orientation::Rotate180 => Ok(image.rotate180_in_place()),
Orientation::Rotate270 => Ok(*image = image.rotate270()),
Orientation::FlipH => Ok(image.fliph_in_place()),
Orientation::FlipV => Ok(image.flipv_in_place()),
Orientation::Rotate90FlipH => {
let mut new_image = image.rotate90();
new_image.fliph_in_place();
*image = new_image;
Ok(())
}
Orientation::Rotate270FlipH => {
let mut new_image = image.rotate270();
new_image.fliph_in_place();
*image = new_image;
Ok(())
}
}
}

/// Encode this image and write it to ```w```.
///
/// Assumes the writer is buffered. In most cases,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ pub use crate::image_reader::{ImageReader, LimitSupport, Limits};
pub use crate::dynimage::DynamicImage;

pub use crate::animation::{Delay, Frame, Frames};
pub use crate::orientation::Orientation;

// More detailed error type
pub mod error;
Expand Down Expand Up @@ -289,6 +290,7 @@ mod color;
mod dynimage;
mod image;
mod image_reader;
mod orientation;
//TODO delete this module after a few releases
/// deprecated io module the original io module has been renamed to `image_reader`
pub mod io {
Expand Down
40 changes: 40 additions & 0 deletions src/orientation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/// Describes the transformations to be applied to the image.
/// Compatible with [Exif orientation](https://web.archive.org/web/20200412005226/https://www.impulseadventure.com/photo/exif-orientation.html).
///
/// Orientation is specified in the Exif metadata, and is often written by cameras.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Orientation {
/// Do not perform any transformations.
NoTransforms,
/// Rotate by 90 degrees clockwise.
Rotate90,
/// Rotate by 180 degrees. Can be performed in-place.
Rotate180,
/// Rotate by 90 degrees clockwise.
Rotate270,
/// Flip horizontally. Can be performed in-place.
FlipH,
Shnatsel marked this conversation as resolved.
Show resolved Hide resolved
/// Flip vertically. Can be performed in-place.
FlipV,
/// Rotate by 90 degrees clockwise and flip horizontally.
Rotate90FlipH,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bit long, but perhaps Rotate90FlipHorizontal for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that, and my eyes just glaze over when I try to read it. I genuinely believe this option is more readable.

And if its meaning is ever unclear, there is always the doc comment.

/// Rotate by 270 degrees clockwise and flip horizontally.
Rotate270FlipH,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rotate270FlipHorizontal?

}

impl Orientation {
/// Converts from [Exif orientation](https://web.archive.org/web/20200412005226/https://www.impulseadventure.com/photo/exif-orientation.html)
pub fn from_exif(exif_orientation: u8) -> Option<Self> {
match exif_orientation {
1 => Some(Self::NoTransforms),
2 => Some(Self::FlipH),
3 => Some(Self::Rotate180),
4 => Some(Self::FlipV),
5 => Some(Self::Rotate90FlipH),
6 => Some(Self::Rotate90),
7 => Some(Self::Rotate90FlipH),
8 => Some(Self::Rotate270),
0 | 9.. => None,
}
}
}
Loading