iced_graphics/
image.rs

1//! Load and operate on images.
2#[cfg(feature = "image")]
3pub use ::image as image_rs;
4
5use crate::core::image;
6use crate::core::svg;
7use crate::core::Rectangle;
8
9/// A raster or vector image.
10#[derive(Debug, Clone, PartialEq)]
11pub enum Image {
12    /// A raster image.
13    Raster {
14        /// The image handle
15        handle: image::Image,
16
17        /// The bounds of the image.
18        bounds: Rectangle,
19    },
20    /// A vector image.
21    Vector {
22        /// The handle of a vector image.
23        handle: svg::Svg,
24
25        /// The bounds of the image.
26        bounds: Rectangle,
27    },
28}
29
30impl Image {
31    /// Returns the bounds of the [`Image`].
32    pub fn bounds(&self) -> Rectangle {
33        match self {
34            Image::Raster { handle, bounds } => bounds.rotate(handle.rotation),
35            Image::Vector { handle, bounds, .. } => {
36                bounds.rotate(handle.rotation)
37            }
38        }
39    }
40}
41
42#[cfg(feature = "image")]
43/// Tries to load an image by its [`Handle`].
44///
45/// [`Handle`]: image::Handle
46pub fn load(
47    handle: &image::Handle,
48) -> ::image::ImageResult<::image::ImageBuffer<::image::Rgba<u8>, image::Bytes>>
49{
50    use bitflags::bitflags;
51
52    bitflags! {
53        struct Operation: u8 {
54            const FLIP_HORIZONTALLY = 0b001;
55            const ROTATE_180 = 0b010;
56            const FLIP_DIAGONALLY = 0b100;
57        }
58    }
59
60    impl Operation {
61        // Meaning of the returned value is described e.g. at:
62        // https://magnushoff.com/articles/jpeg-orientation/
63        fn from_exif<R>(reader: &mut R) -> Result<Self, exif::Error>
64        where
65            R: std::io::BufRead + std::io::Seek,
66        {
67            let exif = exif::Reader::new().read_from_container(reader)?;
68
69            Ok(exif
70                .get_field(exif::Tag::Orientation, exif::In::PRIMARY)
71                .and_then(|field| field.value.get_uint(0))
72                .and_then(|value| u8::try_from(value).ok())
73                .and_then(|value| Self::from_bits(value.saturating_sub(1)))
74                .unwrap_or_else(Self::empty))
75        }
76
77        fn perform(
78            self,
79            mut image: ::image::DynamicImage,
80        ) -> ::image::DynamicImage {
81            use ::image::imageops;
82
83            if self.contains(Self::FLIP_DIAGONALLY) {
84                imageops::flip_vertical_in_place(&mut image);
85            }
86
87            if self.contains(Self::ROTATE_180) {
88                imageops::rotate180_in_place(&mut image);
89            }
90
91            if self.contains(Self::FLIP_HORIZONTALLY) {
92                imageops::flip_horizontal_in_place(&mut image);
93            }
94
95            image
96        }
97    }
98
99    let (width, height, pixels) = match handle {
100        image::Handle::Path(_, path) => {
101            let image = ::image::io::Reader::open(&path)?
102                .with_guessed_format()?
103                .decode()?;
104
105            let operation = std::fs::File::open(path)
106                .ok()
107                .map(std::io::BufReader::new)
108                .and_then(|mut reader| Operation::from_exif(&mut reader).ok())
109                .unwrap_or_else(Operation::empty);
110
111            let rgba = operation.perform(image).into_rgba8();
112
113            (
114                rgba.width(),
115                rgba.height(),
116                image::Bytes::from(rgba.into_raw()),
117            )
118        }
119        image::Handle::Bytes(_, bytes) => {
120            let image = ::image::load_from_memory(bytes)?;
121            let operation =
122                Operation::from_exif(&mut std::io::Cursor::new(bytes))
123                    .ok()
124                    .unwrap_or_else(Operation::empty);
125
126            let rgba = operation.perform(image).into_rgba8();
127
128            (
129                rgba.width(),
130                rgba.height(),
131                image::Bytes::from(rgba.into_raw()),
132            )
133        }
134        image::Handle::Rgba {
135            width,
136            height,
137            pixels,
138            ..
139        } => (*width, *height, pixels.clone()),
140    };
141
142    if let Some(image) = ::image::ImageBuffer::from_raw(width, height, pixels) {
143        Ok(image)
144    } else {
145        Err(::image::error::ImageError::Limits(
146            ::image::error::LimitError::from_kind(
147                ::image::error::LimitErrorKind::DimensionError,
148            ),
149        ))
150    }
151}