1#[cfg(feature = "image")]
3use crate::core::Bytes;
4
5use crate::core::Rectangle;
6use crate::core::image;
7use crate::core::svg;
8
9#[allow(missing_docs)]
11#[derive(Debug, Clone, PartialEq)]
12pub enum Image {
13 Raster {
15 image: image::Image,
16 bounds: Rectangle,
17 clip_bounds: Rectangle,
18 },
19
20 Vector {
22 svg: svg::Svg,
23 bounds: Rectangle,
24 clip_bounds: Rectangle,
25 },
26}
27
28impl Image {
29 pub fn bounds(&self) -> Rectangle {
31 match self {
32 Image::Raster { image, bounds, .. } => bounds.rotate(image.rotation),
33 Image::Vector { svg, bounds, .. } => bounds.rotate(svg.rotation),
34 }
35 }
36}
37
38#[cfg(feature = "image")]
40pub type Buffer = ::image::ImageBuffer<::image::Rgba<u8>, Bytes>;
41
42#[cfg(feature = "image")]
43pub fn load(handle: &image::Handle) -> Result<Buffer, image::Error> {
47 use bitflags::bitflags;
48
49 bitflags! {
50 struct Operation: u8 {
51 const FLIP_HORIZONTALLY = 0b1;
52 const ROTATE_180 = 0b10;
53 const FLIP_VERTICALLY= 0b100;
54 const ROTATE_90 = 0b1000;
55 const ROTATE_270 = 0b10000;
56 }
57 }
58
59 impl Operation {
60 fn from_exif<R>(reader: &mut R) -> Result<Self, exif::Error>
63 where
64 R: std::io::BufRead + std::io::Seek,
65 {
66 let exif = exif::Reader::new().read_from_container(reader)?;
67
68 Ok(exif
69 .get_field(exif::Tag::Orientation, exif::In::PRIMARY)
70 .and_then(|field| field.value.get_uint(0))
71 .and_then(|value| u8::try_from(value).ok())
72 .map(|value| match value {
73 1 => Operation::empty(),
74 2 => Operation::FLIP_HORIZONTALLY,
75 3 => Operation::ROTATE_180,
76 4 => Operation::FLIP_VERTICALLY,
77 5 => Operation::ROTATE_90 | Operation::FLIP_HORIZONTALLY,
78 6 => Operation::ROTATE_90,
79 7 => Operation::ROTATE_90 | Operation::FLIP_VERTICALLY,
80 8 => Operation::ROTATE_270,
81 _ => Operation::empty(),
82 })
83 .unwrap_or_else(Self::empty))
84 }
85
86 fn perform(self, mut image: ::image::DynamicImage) -> ::image::DynamicImage {
87 use ::image::imageops;
88
89 if self.contains(Operation::ROTATE_90) {
90 image = imageops::rotate90(&image).into();
91 }
92
93 if self.contains(Self::ROTATE_180) {
94 imageops::rotate180_in_place(&mut image);
95 }
96
97 if self.contains(Operation::ROTATE_270) {
98 image = imageops::rotate270(&image).into();
99 }
100
101 if self.contains(Self::FLIP_VERTICALLY) {
102 imageops::flip_vertical_in_place(&mut image);
103 }
104
105 if self.contains(Self::FLIP_HORIZONTALLY) {
106 imageops::flip_horizontal_in_place(&mut image);
107 }
108
109 image
110 }
111 }
112
113 let (width, height, pixels) = match handle {
114 image::Handle::Path(_, path) => {
115 let image = ::image::open(path).map_err(to_error)?;
116
117 let operation = std::fs::File::open(path)
118 .ok()
119 .map(std::io::BufReader::new)
120 .and_then(|mut reader| Operation::from_exif(&mut reader).ok())
121 .unwrap_or_else(Operation::empty);
122
123 let rgba = operation.perform(image).into_rgba8();
124
125 (rgba.width(), rgba.height(), Bytes::from(rgba.into_raw()))
126 }
127 image::Handle::Bytes(_, bytes) => {
128 let image = ::image::load_from_memory(bytes).map_err(to_error)?;
129
130 let operation = Operation::from_exif(&mut std::io::Cursor::new(bytes))
131 .ok()
132 .unwrap_or_else(Operation::empty);
133
134 let rgba = operation.perform(image).into_rgba8();
135
136 (rgba.width(), rgba.height(), Bytes::from(rgba.into_raw()))
137 }
138 image::Handle::Rgba {
139 width,
140 height,
141 pixels,
142 ..
143 } => (*width, *height, pixels.clone()),
144 };
145
146 if let Some(image) = ::image::ImageBuffer::from_raw(width, height, pixels) {
147 Ok(image)
148 } else {
149 Err(to_error(::image::error::ImageError::Limits(
150 ::image::error::LimitError::from_kind(::image::error::LimitErrorKind::DimensionError),
151 )))
152 }
153}
154
155#[cfg(feature = "image")]
156fn to_error(error: ::image::ImageError) -> image::Error {
157 use std::sync::Arc;
158
159 match error {
160 ::image::ImageError::IoError(error) => image::Error::Inaccessible(Arc::new(error)),
161 error => image::Error::Invalid(Arc::new(error)),
162 }
163}