diff options
-rw-r--r-- | wgpu/Cargo.toml | 2 | ||||
-rw-r--r-- | wgpu/src/image/raster.rs | 100 |
2 files changed, 97 insertions, 5 deletions
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 4c2a3e42..71da889f 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -34,6 +34,8 @@ raw-window-handle = "0.3" log = "0.4" guillotiere = "0.6" futures = "0.3" +kamadak-exif = "0.5" +bitflags = "1.2" [dependencies.bytemuck] version = "1.4" diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index d5c62545..e13987b4 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<u8>, Vec<u8>>), @@ -42,15 +44,28 @@ impl Cache { let memory = match handle.data() { image::Data::Path(path) => { - if let Ok(image) = ::image_rs::open(path) { - Memory::Host(image.to_bgra8()) + if let Ok(image) = image_rs::open(path) { + let operation = std::fs::File::open(path) + .ok() + .map(std::io::BufReader::new) + .and_then(|mut reader| { + Operation::from_exif(&mut reader).ok() + }) + .unwrap_or_else(Operation::empty); + + Memory::Host(operation.perform(image.to_bgra8())) } else { Memory::NotFound } } image::Data::Bytes(bytes) => { - if let Ok(image) = ::image_rs::load_from_memory(&bytes) { - Memory::Host(image.to_bgra8()) + if let Ok(image) = image_rs::load_from_memory(&bytes) { + let operation = + Operation::from_exif(&mut std::io::Cursor::new(bytes)) + .ok() + .unwrap_or_else(Operation::empty); + + Memory::Host(operation.perform(image.to_bgra8())) } else { Memory::Invalid } @@ -60,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(), @@ -132,3 +147,78 @@ impl Cache { self.map.contains_key(&handle.id()) } } + +bitflags! { + struct Operation: u8 { + const FLIP_HORIZONTALLY = 0b001; + const ROTATE_180 = 0b010; + const FLIP_DIAGONALLY = 0b100; + } +} + +impl Operation { + // Meaning of the returned value is described e.g. at: + // https://magnushoff.com/articles/jpeg-orientation/ + fn from_exif<R>(reader: &mut R) -> Result<Self, exif::Error> + 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)) + .and_then(|value| u8::try_from(value).ok()) + .and_then(|value| Self::from_bits(value.saturating_sub(1))) + .unwrap_or_else(Self::empty)) + } + + fn perform<P>( + self, + image: image_rs::ImageBuffer<P, Vec<P::Subpixel>>, + ) -> image_rs::ImageBuffer<P, Vec<P::Subpixel>> + where + P: image_rs::Pixel + 'static, + { + use image_rs::imageops; + + 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); + } + + if self.contains(Self::FLIP_HORIZONTALLY) { + imageops::flip_horizontal_in_place(&mut image); + } + + image + } +} + +fn flip_diagonally<I>( + image: I, +) -> image_rs::ImageBuffer<I::Pixel, Vec<<I::Pixel as image_rs::Pixel>::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 +} |