From 82e3e316dd49884b06fea3ad3129e23faaa7588a Mon Sep 17 00:00:00 2001 From: Mateusz Czapliński Date: Wed, 29 Sep 2021 19:26:46 +0200 Subject: Honor Exif orientation in `iced_wgpu::Image` --- wgpu/src/image/raster.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index d5c62545..2944cc41 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -43,14 +43,20 @@ impl Cache { let memory = match handle.data() { image::Data::Path(path) => { if let Ok(image) = ::image_rs::open(path) { - Memory::Host(image.to_bgra8()) + let orientation = std::fs::File::open(path) + .ok() + .map(std::io::BufReader::new) + .and_then(|mut reader| exif_orientation(&mut reader)); + Memory::Host(fix_orientation(image.to_bgra8(), orientation)) } else { Memory::NotFound } } image::Data::Bytes(bytes) => { if let Ok(image) = ::image_rs::load_from_memory(&bytes) { - Memory::Host(image.to_bgra8()) + let orientation = + exif_orientation(&mut std::io::Cursor::new(bytes)); + Memory::Host(fix_orientation(image.to_bgra8(), orientation)) } else { Memory::Invalid } @@ -132,3 +138,39 @@ impl Cache { self.map.contains_key(&handle.id()) } } + +fn fix_orientation( + mut img: ::image_rs::ImageBuffer<::image_rs::Bgra, Vec>, + orientation: Option, +) -> ::image_rs::ImageBuffer<::image_rs::Bgra, Vec> { + use ::image_rs::imageops::*; + match orientation.unwrap_or(1) { + 2 => flip_horizontal_in_place(&mut img), + 3 => rotate180_in_place(&mut img), + 4 => flip_vertical_in_place(&mut img), + 5 => { + img = rotate90(&img); + flip_horizontal_in_place(&mut img); + } + 6 => img = rotate90(&img), + 7 => { + img = rotate90(&img); + flip_vertical_in_place(&mut img); + } + 8 => img = rotate270(&img), + _ => {} + }; + img +} + +// Meaning of the returned value is described e.g. at: +// https://magnushoff.com/articles/jpeg-orientation/ +fn exif_orientation(reader: &mut R) -> Option +where + R: std::io::BufRead + std::io::Seek, +{ + let exif = ::exif::Reader::new().read_from_container(reader).ok()?; + exif.get_field(::exif::Tag::Orientation, ::exif::In::PRIMARY)? + .value + .get_uint(0) +} -- cgit From 03fee3106f0d32f0883bf3ee293213059902f304 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Sep 2021 14:24:23 +0700 Subject: Introduce `Orientation` enum in `image::raster` --- wgpu/src/image/raster.rs | 109 +++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 37 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 2944cc41..e94c07ed 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -46,17 +46,24 @@ impl Cache { let orientation = std::fs::File::open(path) .ok() .map(std::io::BufReader::new) - .and_then(|mut reader| exif_orientation(&mut reader)); - Memory::Host(fix_orientation(image.to_bgra8(), orientation)) + .and_then(|mut reader| { + Orientation::from_exif(&mut reader).ok() + }) + .unwrap_or(Orientation::Default); + + Memory::Host(orientation.apply(image.to_bgra8())) } else { Memory::NotFound } } image::Data::Bytes(bytes) => { if let Ok(image) = ::image_rs::load_from_memory(&bytes) { - let orientation = - exif_orientation(&mut std::io::Cursor::new(bytes)); - Memory::Host(fix_orientation(image.to_bgra8(), orientation)) + let orientation = Orientation::from_exif( + &mut std::io::Cursor::new(bytes), + ) + .unwrap_or(Orientation::Default); + + Memory::Host(orientation.apply(image.to_bgra8())) } else { Memory::Invalid } @@ -139,38 +146,66 @@ impl Cache { } } -fn fix_orientation( - mut img: ::image_rs::ImageBuffer<::image_rs::Bgra, Vec>, - orientation: Option, -) -> ::image_rs::ImageBuffer<::image_rs::Bgra, Vec> { - use ::image_rs::imageops::*; - match orientation.unwrap_or(1) { - 2 => flip_horizontal_in_place(&mut img), - 3 => rotate180_in_place(&mut img), - 4 => flip_vertical_in_place(&mut img), - 5 => { - img = rotate90(&img); - flip_horizontal_in_place(&mut img); - } - 6 => img = rotate90(&img), - 7 => { - img = rotate90(&img); - flip_vertical_in_place(&mut img); - } - 8 => img = rotate270(&img), - _ => {} - }; - img +#[derive(Debug, Clone, Copy)] +enum Orientation { + Default, + FlippedHorizontally, + FlippedVertically, + Rotated90, + Rotated180, + Rotated270, + Rotated90AndFlippedHorizontally, + Rotated90AndFlippedVertically, } -// Meaning of the returned value is described e.g. at: -// https://magnushoff.com/articles/jpeg-orientation/ -fn exif_orientation(reader: &mut R) -> Option -where - R: std::io::BufRead + std::io::Seek, -{ - let exif = ::exif::Reader::new().read_from_container(reader).ok()?; - exif.get_field(::exif::Tag::Orientation, ::exif::In::PRIMARY)? - .value - .get_uint(0) +impl Orientation { + // Meaning of the returned value is described e.g. at: + // https://magnushoff.com/articles/jpeg-orientation/ + fn from_exif(reader: &mut R) -> Result + where + R: std::io::BufRead + std::io::Seek, + { + let exif = ::exif::Reader::new().read_from_container(reader)?; + + Ok(exif + .get_field(::exif::Tag::Orientation, ::exif::In::PRIMARY) + .and_then(|field| field.value.get_uint(0)) + .map(|value| match value { + 2 => Orientation::FlippedHorizontally, + 3 => Orientation::Rotated180, + 4 => Orientation::FlippedVertically, + 5 => Orientation::Rotated90AndFlippedHorizontally, + 6 => Orientation::Rotated90, + 7 => Orientation::Rotated90AndFlippedVertically, + 8 => Orientation::Rotated270, + _ => Orientation::Default, + }) + .unwrap_or(Orientation::Default)) + } + + fn apply( + self, + mut img: ::image_rs::ImageBuffer<::image_rs::Bgra, Vec>, + ) -> ::image_rs::ImageBuffer<::image_rs::Bgra, Vec> { + use ::image_rs::imageops::*; + + match self { + Self::FlippedHorizontally => flip_horizontal_in_place(&mut img), + Self::Rotated180 => rotate180_in_place(&mut img), + Self::FlippedVertically => flip_vertical_in_place(&mut img), + Self::Rotated90AndFlippedHorizontally => { + img = rotate90(&img); + flip_horizontal_in_place(&mut img); + } + Self::Rotated90 => img = rotate90(&img), + Self::Rotated90AndFlippedVertically => { + img = rotate90(&img); + flip_vertical_in_place(&mut img); + } + Self::Rotated270 => img = rotate270(&img), + Self::Default => {} + }; + + img + } } -- cgit From 86d6d53cfa24d55e15a20b34c344f40b0ad5244b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Sep 2021 14:35:10 +0700 Subject: Remove unnecessary absolute module paths in `image::raster` --- wgpu/src/image/raster.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index e94c07ed..826b4539 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -165,7 +165,7 @@ impl Orientation { where R: std::io::BufRead + std::io::Seek, { - let exif = ::exif::Reader::new().read_from_container(reader)?; + let exif = exif::Reader::new().read_from_container(reader)?; Ok(exif .get_field(::exif::Tag::Orientation, ::exif::In::PRIMARY) @@ -186,8 +186,8 @@ impl Orientation { fn apply( self, mut img: ::image_rs::ImageBuffer<::image_rs::Bgra, Vec>, - ) -> ::image_rs::ImageBuffer<::image_rs::Bgra, Vec> { - use ::image_rs::imageops::*; + ) -> image_rs::ImageBuffer<::image_rs::Bgra, Vec> { + use image_rs::imageops::*; match self { Self::FlippedHorizontally => flip_horizontal_in_place(&mut img), -- cgit From 21d138aa28a518ca0893eec4abbda95b07ba56bc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Sep 2021 16:11:30 +0700 Subject: Refactor `Orientation` into `Operation` in `image::raster` --- wgpu/src/image/raster.rs | 94 +++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 53 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 826b4539..21659039 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -2,6 +2,8 @@ use crate::image::atlas::{self, Atlas}; use iced_native::image; use std::collections::{HashMap, HashSet}; +use bitflags::bitflags; + #[derive(Debug)] pub enum Memory { Host(::image_rs::ImageBuffer<::image_rs::Bgra, Vec>), @@ -43,27 +45,27 @@ impl Cache { let memory = match handle.data() { image::Data::Path(path) => { if let Ok(image) = ::image_rs::open(path) { - let orientation = std::fs::File::open(path) + let operation = std::fs::File::open(path) .ok() .map(std::io::BufReader::new) .and_then(|mut reader| { - Orientation::from_exif(&mut reader).ok() + Operation::from_exif(&mut reader).ok() }) - .unwrap_or(Orientation::Default); + .unwrap_or_else(Operation::empty); - Memory::Host(orientation.apply(image.to_bgra8())) + Memory::Host(operation.perform(image.to_bgra8())) } else { Memory::NotFound } } image::Data::Bytes(bytes) => { if let Ok(image) = ::image_rs::load_from_memory(&bytes) { - let orientation = Orientation::from_exif( - &mut std::io::Cursor::new(bytes), - ) - .unwrap_or(Orientation::Default); + let operation = + Operation::from_exif(&mut std::io::Cursor::new(bytes)) + .ok() + .unwrap_or_else(Operation::empty); - Memory::Host(orientation.apply(image.to_bgra8())) + Memory::Host(operation.perform(image.to_bgra8())) } else { Memory::Invalid } @@ -146,66 +148,52 @@ impl Cache { } } -#[derive(Debug, Clone, Copy)] -enum Orientation { - Default, - FlippedHorizontally, - FlippedVertically, - Rotated90, - Rotated180, - Rotated270, - Rotated90AndFlippedHorizontally, - Rotated90AndFlippedVertically, +bitflags! { + struct Operation: u8 { + const FLIP_HORIZONTALLY = 0b001; + const ROTATE_180 = 0b010; + const FLIP_DIAGONALLY = 0b100; + } } -impl Orientation { +impl Operation { // Meaning of the returned value is described e.g. at: // https://magnushoff.com/articles/jpeg-orientation/ fn from_exif(reader: &mut R) -> Result where R: std::io::BufRead + std::io::Seek, { + use std::convert::TryFrom; + let exif = exif::Reader::new().read_from_container(reader)?; Ok(exif .get_field(::exif::Tag::Orientation, ::exif::In::PRIMARY) .and_then(|field| field.value.get_uint(0)) - .map(|value| match value { - 2 => Orientation::FlippedHorizontally, - 3 => Orientation::Rotated180, - 4 => Orientation::FlippedVertically, - 5 => Orientation::Rotated90AndFlippedHorizontally, - 6 => Orientation::Rotated90, - 7 => Orientation::Rotated90AndFlippedVertically, - 8 => Orientation::Rotated270, - _ => Orientation::Default, - }) - .unwrap_or(Orientation::Default)) + .and_then(|value| u8::try_from(value).ok()) + .and_then(|value| Self::from_bits(value.saturating_sub(1))) + .unwrap_or_else(Self::empty)) } - fn apply( - self, - mut img: ::image_rs::ImageBuffer<::image_rs::Bgra, Vec>, - ) -> image_rs::ImageBuffer<::image_rs::Bgra, Vec> { - use image_rs::imageops::*; + fn perform(self, mut image: I) -> I + where + I: image_rs::GenericImage, + { + use image_rs::imageops; - match self { - Self::FlippedHorizontally => flip_horizontal_in_place(&mut img), - Self::Rotated180 => rotate180_in_place(&mut img), - Self::FlippedVertically => flip_vertical_in_place(&mut img), - Self::Rotated90AndFlippedHorizontally => { - img = rotate90(&img); - flip_horizontal_in_place(&mut img); - } - Self::Rotated90 => img = rotate90(&img), - Self::Rotated90AndFlippedVertically => { - img = rotate90(&img); - flip_vertical_in_place(&mut img); - } - Self::Rotated270 => img = rotate270(&img), - Self::Default => {} - }; + if self.contains(Self::FLIP_DIAGONALLY) { + imageops::flip_horizontal_in_place(&mut image); + imageops::flip_vertical_in_place(&mut image); + } + + if self.contains(Self::ROTATE_180) { + imageops::rotate180_in_place(&mut image); + } + + if self.contains(Self::FLIP_HORIZONTALLY) { + imageops::flip_horizontal_in_place(&mut image); + } - img + image } } -- cgit From 60070eef274828ee4b65553c17d7bd14d2e39183 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Sep 2021 16:12:28 +0700 Subject: Remove absolute module path leftovers in `image::raster` --- wgpu/src/image/raster.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 21659039..318cefb6 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -44,7 +44,7 @@ impl Cache { let memory = match handle.data() { image::Data::Path(path) => { - if let Ok(image) = ::image_rs::open(path) { + if let Ok(image) = image_rs::open(path) { let operation = std::fs::File::open(path) .ok() .map(std::io::BufReader::new) @@ -59,7 +59,7 @@ impl Cache { } } image::Data::Bytes(bytes) => { - if let Ok(image) = ::image_rs::load_from_memory(&bytes) { + if let Ok(image) = image_rs::load_from_memory(&bytes) { let operation = Operation::from_exif(&mut std::io::Cursor::new(bytes)) .ok() @@ -75,7 +75,7 @@ impl Cache { height, pixels, } => { - if let Some(image) = ::image_rs::ImageBuffer::from_vec( + if let Some(image) = image_rs::ImageBuffer::from_vec( *width, *height, pixels.to_vec(), @@ -168,7 +168,7 @@ impl Operation { let exif = exif::Reader::new().read_from_container(reader)?; Ok(exif - .get_field(::exif::Tag::Orientation, ::exif::In::PRIMARY) + .get_field(exif::Tag::Orientation, exif::In::PRIMARY) .and_then(|field| field.value.get_uint(0)) .and_then(|value| u8::try_from(value).ok()) .and_then(|value| Self::from_bits(value.saturating_sub(1))) -- cgit From 2cc7e0a44901a46887b73d9e34d9b3dcc1de8580 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Sep 2021 16:39:43 +0700 Subject: Fix `Operation::perform` in `image::raster` Flipping diagonally isn't the same as flipping each axis individually :sweat_smile: --- wgpu/src/image/raster.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 318cefb6..e13987b4 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -175,16 +175,20 @@ impl Operation { .unwrap_or_else(Self::empty)) } - fn perform(self, mut image: I) -> I + fn perform

( + self, + image: image_rs::ImageBuffer>, + ) -> image_rs::ImageBuffer> where - I: image_rs::GenericImage, + P: image_rs::Pixel + 'static, { use image_rs::imageops; - if self.contains(Self::FLIP_DIAGONALLY) { - imageops::flip_horizontal_in_place(&mut image); - imageops::flip_vertical_in_place(&mut image); - } + let mut image = if self.contains(Self::FLIP_DIAGONALLY) { + flip_diagonally(image) + } else { + image + }; if self.contains(Self::ROTATE_180) { imageops::rotate180_in_place(&mut image); @@ -197,3 +201,24 @@ impl Operation { image } } + +fn flip_diagonally( + image: I, +) -> image_rs::ImageBuffer::Subpixel>> +where + I: image_rs::GenericImage, + I::Pixel: 'static, +{ + let (width, height) = image.dimensions(); + let mut out = image_rs::ImageBuffer::new(height, width); + + for x in 0..width { + for y in 0..height { + let p = image.get_pixel(x, y); + + out.put_pixel(y, x, p); + } + } + + out +} -- cgit