1pub mod viewer;
20pub use viewer::Viewer;
21
22use crate::core::image;
23use crate::core::layout;
24use crate::core::mouse;
25use crate::core::renderer;
26use crate::core::widget::Tree;
27use crate::core::{
28 ContentFit, Element, Layout, Length, Point, Rectangle, Rotation, Size,
29 Vector, Widget,
30};
31
32pub use image::{FilterMethod, Handle};
33
34pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
36 Viewer::new(handle)
37}
38
39#[derive(Debug)]
58pub struct Image<Handle = image::Handle> {
59 handle: Handle,
60 width: Length,
61 height: Length,
62 content_fit: ContentFit,
63 filter_method: FilterMethod,
64 rotation: Rotation,
65 opacity: f32,
66 scale: f32,
67}
68
69impl<Handle> Image<Handle> {
70 pub fn new(handle: impl Into<Handle>) -> Self {
72 Image {
73 handle: handle.into(),
74 width: Length::Shrink,
75 height: Length::Shrink,
76 content_fit: ContentFit::default(),
77 filter_method: FilterMethod::default(),
78 rotation: Rotation::default(),
79 opacity: 1.0,
80 scale: 1.0,
81 }
82 }
83
84 pub fn width(mut self, width: impl Into<Length>) -> Self {
86 self.width = width.into();
87 self
88 }
89
90 pub fn height(mut self, height: impl Into<Length>) -> Self {
92 self.height = height.into();
93 self
94 }
95
96 pub fn content_fit(mut self, content_fit: ContentFit) -> Self {
100 self.content_fit = content_fit;
101 self
102 }
103
104 pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
106 self.filter_method = filter_method;
107 self
108 }
109
110 pub fn rotation(mut self, rotation: impl Into<Rotation>) -> Self {
112 self.rotation = rotation.into();
113 self
114 }
115
116 pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
121 self.opacity = opacity.into();
122 self
123 }
124
125 pub fn scale(mut self, scale: impl Into<f32>) -> Self {
130 self.scale = scale.into();
131 self
132 }
133}
134
135pub fn layout<Renderer, Handle>(
137 renderer: &Renderer,
138 limits: &layout::Limits,
139 handle: &Handle,
140 width: Length,
141 height: Length,
142 content_fit: ContentFit,
143 rotation: Rotation,
144) -> layout::Node
145where
146 Renderer: image::Renderer<Handle = Handle>,
147{
148 let image_size = renderer.measure_image(handle);
150 let image_size =
151 Size::new(image_size.width as f32, image_size.height as f32);
152
153 let rotated_size = rotation.apply(image_size);
155
156 let raw_size = limits.resolve(width, height, rotated_size);
158
159 let full_size = content_fit.fit(rotated_size, raw_size);
161
162 let final_size = Size {
164 width: match width {
165 Length::Shrink => f32::min(raw_size.width, full_size.width),
166 _ => raw_size.width,
167 },
168 height: match height {
169 Length::Shrink => f32::min(raw_size.height, full_size.height),
170 _ => raw_size.height,
171 },
172 };
173
174 layout::Node::new(final_size)
175}
176
177pub fn draw<Renderer, Handle>(
179 renderer: &mut Renderer,
180 layout: Layout<'_>,
181 viewport: &Rectangle,
182 handle: &Handle,
183 content_fit: ContentFit,
184 filter_method: FilterMethod,
185 rotation: Rotation,
186 opacity: f32,
187 scale: f32,
188) where
189 Renderer: image::Renderer<Handle = Handle>,
190 Handle: Clone,
191{
192 let Size { width, height } = renderer.measure_image(handle);
193 let image_size = Size::new(width as f32, height as f32);
194 let rotated_size = rotation.apply(image_size);
195
196 let bounds = layout.bounds();
197 let adjusted_fit = content_fit.fit(rotated_size, bounds.size());
198
199 let fit_scale = Vector::new(
200 adjusted_fit.width / rotated_size.width,
201 adjusted_fit.height / rotated_size.height,
202 );
203
204 let final_size = image_size * fit_scale * scale;
205
206 let position = match content_fit {
207 ContentFit::None => Point::new(
208 bounds.x + (rotated_size.width - adjusted_fit.width) / 2.0,
209 bounds.y + (rotated_size.height - adjusted_fit.height) / 2.0,
210 ),
211 _ => Point::new(
212 bounds.center_x() - final_size.width / 2.0,
213 bounds.center_y() - final_size.height / 2.0,
214 ),
215 };
216
217 let drawing_bounds = Rectangle::new(position, final_size);
218
219 let render = |renderer: &mut Renderer| {
220 renderer.draw_image(
221 image::Image {
222 handle: handle.clone(),
223 filter_method,
224 rotation: rotation.radians(),
225 opacity,
226 snap: true,
227 },
228 drawing_bounds,
229 );
230 };
231
232 if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height
233 {
234 if let Some(bounds) = bounds.intersection(viewport) {
235 renderer.with_layer(bounds, render);
236 }
237 } else {
238 render(renderer);
239 }
240}
241
242impl<Message, Theme, Renderer, Handle> Widget<Message, Theme, Renderer>
243 for Image<Handle>
244where
245 Renderer: image::Renderer<Handle = Handle>,
246 Handle: Clone,
247{
248 fn size(&self) -> Size<Length> {
249 Size {
250 width: self.width,
251 height: self.height,
252 }
253 }
254
255 fn layout(
256 &self,
257 _tree: &mut Tree,
258 renderer: &Renderer,
259 limits: &layout::Limits,
260 ) -> layout::Node {
261 layout(
262 renderer,
263 limits,
264 &self.handle,
265 self.width,
266 self.height,
267 self.content_fit,
268 self.rotation,
269 )
270 }
271
272 fn draw(
273 &self,
274 _state: &Tree,
275 renderer: &mut Renderer,
276 _theme: &Theme,
277 _style: &renderer::Style,
278 layout: Layout<'_>,
279 _cursor: mouse::Cursor,
280 viewport: &Rectangle,
281 ) {
282 draw(
283 renderer,
284 layout,
285 viewport,
286 &self.handle,
287 self.content_fit,
288 self.filter_method,
289 self.rotation,
290 self.opacity,
291 self.scale,
292 );
293 }
294}
295
296impl<'a, Message, Theme, Renderer, Handle> From<Image<Handle>>
297 for Element<'a, Message, Theme, Renderer>
298where
299 Renderer: image::Renderer<Handle = Handle>,
300 Handle: Clone + 'a,
301{
302 fn from(image: Image<Handle>) -> Element<'a, Message, Theme, Renderer> {
303 Element::new(image)
304 }
305}