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#[allow(missing_debug_implementations)]
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 expand: bool,
68}
69
70impl<Handle> Image<Handle> {
71 pub fn new(handle: impl Into<Handle>) -> Self {
73 Image {
74 handle: handle.into(),
75 width: Length::Shrink,
76 height: Length::Shrink,
77 content_fit: ContentFit::default(),
78 filter_method: FilterMethod::default(),
79 rotation: Rotation::default(),
80 opacity: 1.0,
81 scale: 1.0,
82 expand: false,
83 }
84 }
85
86 pub fn width(mut self, width: impl Into<Length>) -> Self {
88 self.width = width.into();
89 self
90 }
91
92 pub fn height(mut self, height: impl Into<Length>) -> Self {
94 self.height = height.into();
95 self
96 }
97
98 pub fn expand(mut self, expand: bool) -> Self {
107 self.expand = expand;
108 self
109 }
110
111 pub fn content_fit(mut self, content_fit: ContentFit) -> Self {
115 self.content_fit = content_fit;
116 self
117 }
118
119 pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
121 self.filter_method = filter_method;
122 self
123 }
124
125 pub fn rotation(mut self, rotation: impl Into<Rotation>) -> Self {
127 self.rotation = rotation.into();
128 self
129 }
130
131 pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
136 self.opacity = opacity.into();
137 self
138 }
139
140 pub fn scale(mut self, scale: impl Into<f32>) -> Self {
145 self.scale = scale.into();
146 self
147 }
148}
149
150pub fn layout<Renderer, Handle>(
152 renderer: &Renderer,
153 limits: &layout::Limits,
154 handle: &Handle,
155 width: Length,
156 height: Length,
157 content_fit: ContentFit,
158 rotation: Rotation,
159 expand: bool,
160) -> layout::Node
161where
162 Renderer: image::Renderer<Handle = Handle>,
163{
164 let image_size = renderer.measure_image(handle);
166 let image_size =
167 Size::new(image_size.width as f32, image_size.height as f32);
168
169 let rotated_size = rotation.apply(image_size);
171
172 let bounds = if expand {
174 limits.max()
175 } else {
176 limits.resolve(width, height, rotated_size)
177 };
178
179 let full_size = content_fit.fit(rotated_size, bounds);
181
182 let final_size = Size {
184 width: match width {
185 Length::Shrink => f32::min(bounds.width, full_size.width),
186 _ => bounds.width,
187 },
188 height: match height {
189 Length::Shrink => f32::min(bounds.height, full_size.height),
190 _ => bounds.height,
191 },
192 };
193
194 layout::Node::new(final_size)
195}
196
197fn drawing_bounds<Renderer, Handle>(
198 renderer: &Renderer,
199 bounds: Rectangle,
200 handle: &Handle,
201 content_fit: ContentFit,
202 rotation: Rotation,
203 scale: f32,
204) -> Rectangle
205where
206 Renderer: image::Renderer<Handle = Handle>,
207{
208 let Size { width, height } = renderer.measure_image(handle);
209 let image_size = Size::new(width as f32, height as f32);
210 let rotated_size = rotation.apply(image_size);
211 let adjusted_fit = content_fit.fit(rotated_size, bounds.size());
212
213 let fit_scale = Vector::new(
214 adjusted_fit.width / rotated_size.width,
215 adjusted_fit.height / rotated_size.height,
216 );
217
218 let final_size = image_size * fit_scale * scale;
219
220 let position = match content_fit {
221 ContentFit::None => Point::new(
222 bounds.x + (rotated_size.width - adjusted_fit.width) / 2.0,
223 bounds.y + (rotated_size.height - adjusted_fit.height) / 2.0,
224 ),
225 _ => Point::new(
226 bounds.center_x() - final_size.width / 2.0,
227 bounds.center_y() - final_size.height / 2.0,
228 ),
229 };
230
231 Rectangle::new(position, final_size)
232}
233
234fn must_clip(bounds: Rectangle, drawing_bounds: Rectangle) -> bool {
235 drawing_bounds.width > bounds.width || drawing_bounds.height > bounds.height
236}
237
238pub fn draw<Renderer, Handle>(
240 renderer: &mut Renderer,
241 layout: Layout<'_>,
242 viewport: &Rectangle,
243 handle: &Handle,
244 content_fit: ContentFit,
245 filter_method: FilterMethod,
246 rotation: Rotation,
247 opacity: f32,
248 scale: f32,
249) where
250 Renderer: image::Renderer<Handle = Handle>,
251 Handle: Clone,
252{
253 let bounds = layout.bounds();
254 let drawing_bounds =
255 drawing_bounds(renderer, bounds, handle, content_fit, rotation, scale);
256
257 if must_clip(bounds, drawing_bounds) {
258 if let Some(bounds) = bounds.intersection(viewport) {
259 renderer.with_layer(bounds, |renderer| {
260 render(
261 renderer,
262 handle,
263 filter_method,
264 rotation,
265 opacity,
266 drawing_bounds,
267 );
268 });
269 }
270 } else {
271 render(
272 renderer,
273 handle,
274 filter_method,
275 rotation,
276 opacity,
277 drawing_bounds,
278 );
279 }
280}
281
282fn render<Renderer, Handle>(
283 renderer: &mut Renderer,
284 handle: &Handle,
285 filter_method: FilterMethod,
286 rotation: Rotation,
287 opacity: f32,
288 drawing_bounds: Rectangle,
289) where
290 Renderer: image::Renderer<Handle = Handle>,
291 Handle: Clone,
292{
293 renderer.draw_image(
294 image::Image {
295 handle: handle.clone(),
296 filter_method,
297 rotation: rotation.radians(),
298 opacity,
299 snap: true,
300 },
301 drawing_bounds,
302 );
303}
304
305impl<Message, Theme, Renderer, Handle> Widget<Message, Theme, Renderer>
306 for Image<Handle>
307where
308 Renderer: image::Renderer<Handle = Handle>,
309 Handle: Clone,
310{
311 fn size(&self) -> Size<Length> {
312 Size {
313 width: self.width,
314 height: self.height,
315 }
316 }
317
318 fn layout(
319 &self,
320 _tree: &mut Tree,
321 renderer: &Renderer,
322 limits: &layout::Limits,
323 ) -> layout::Node {
324 layout(
325 renderer,
326 limits,
327 &self.handle,
328 self.width,
329 self.height,
330 self.content_fit,
331 self.rotation,
332 self.expand,
333 )
334 }
335
336 fn draw(
337 &self,
338 _state: &Tree,
339 renderer: &mut Renderer,
340 _theme: &Theme,
341 _style: &renderer::Style,
342 layout: Layout<'_>,
343 _cursor: mouse::Cursor,
344 viewport: &Rectangle,
345 ) {
346 draw(
347 renderer,
348 layout,
349 viewport,
350 &self.handle,
351 self.content_fit,
352 self.filter_method,
353 self.rotation,
354 self.opacity,
355 self.scale,
356 );
357 }
358}
359
360impl<'a, Message, Theme, Renderer, Handle> From<Image<Handle>>
361 for Element<'a, Message, Theme, Renderer>
362where
363 Renderer: image::Renderer<Handle = Handle>,
364 Handle: Clone + 'a,
365{
366 fn from(image: Image<Handle>) -> Element<'a, Message, Theme, Renderer> {
367 Element::new(image)
368 }
369}