1pub mod viewer;
20pub use viewer::Viewer;
21
22use crate::core::border;
23use crate::core::image;
24use crate::core::layout;
25use crate::core::mouse;
26use crate::core::renderer;
27use crate::core::widget::{Meta, Tree};
28use crate::core::{ContentFit, Layout, Length, Point, Rectangle, Rotation, Size, Vector, Widget};
29
30pub use image::{FilterMethod, Handle};
31
32pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
34 Viewer::new(handle)
35}
36
37pub struct Image<Handle = image::Handle> {
56 handle: Handle,
57 width: Length,
58 height: Length,
59 crop: Option<Rectangle<u32>>,
60 border_radius: border::Radius,
61 content_fit: ContentFit,
62 filter_method: FilterMethod,
63 rotation: Rotation,
64 opacity: f32,
65 scale: f32,
66 expand: bool,
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 crop: None,
77 border_radius: border::Radius::default(),
78 content_fit: ContentFit::default(),
79 filter_method: FilterMethod::default(),
80 rotation: Rotation::default(),
81 opacity: 1.0,
82 scale: 1.0,
83 expand: false,
84 }
85 }
86
87 pub fn width(mut self, width: impl Into<Length>) -> Self {
89 self.width = width.into();
90 self
91 }
92
93 pub fn height(mut self, height: impl Into<Length>) -> Self {
95 self.height = height.into();
96 self
97 }
98
99 pub fn expand(mut self, expand: bool) -> Self {
108 self.expand = expand;
109 self
110 }
111
112 pub fn content_fit(mut self, content_fit: ContentFit) -> Self {
116 self.content_fit = content_fit;
117 self
118 }
119
120 pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
122 self.filter_method = filter_method;
123 self
124 }
125
126 pub fn rotation(mut self, rotation: impl Into<Rotation>) -> Self {
128 self.rotation = rotation.into();
129 self
130 }
131
132 pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
137 self.opacity = opacity.into();
138 self
139 }
140
141 pub fn scale(mut self, scale: impl Into<f32>) -> Self {
146 self.scale = scale.into();
147 self
148 }
149
150 pub fn crop(mut self, region: Rectangle<u32>) -> Self {
164 self.crop = Some(region);
165 self
166 }
167
168 pub fn border_radius(mut self, border_radius: impl Into<border::Radius>) -> Self {
173 self.border_radius = border_radius.into();
174 self
175 }
176}
177
178pub fn layout<Renderer, Handle>(
180 renderer: &Renderer,
181 limits: &layout::Limits,
182 handle: &Handle,
183 width: Length,
184 height: Length,
185 region: Option<Rectangle<u32>>,
186 content_fit: ContentFit,
187 rotation: Rotation,
188 expand: bool,
189) -> Size
190where
191 Renderer: image::Renderer<Handle = Handle>,
192{
193 let image_size = crop(renderer.measure_image(handle).unwrap_or_default(), region);
195
196 let rotated_size = rotation.apply(image_size);
198
199 let bounds = if expand {
201 limits.width(width).height(height).bounds()
202 } else {
203 limits.resolve(width, height, rotated_size)
204 };
205
206 let full_size = content_fit.fit(rotated_size, bounds);
208
209 Size {
211 width: match width {
212 Length::Shrink => f32::min(bounds.width, full_size.width),
213 _ => bounds.width,
214 },
215 height: match height {
216 Length::Shrink => f32::min(bounds.height, full_size.height),
217 _ => bounds.height,
218 },
219 }
220}
221
222fn drawing_bounds<Renderer, Handle>(
223 renderer: &Renderer,
224 bounds: Rectangle,
225 handle: &Handle,
226 region: Option<Rectangle<u32>>,
227 content_fit: ContentFit,
228 rotation: Rotation,
229 scale: f32,
230) -> Rectangle
231where
232 Renderer: image::Renderer<Handle = Handle>,
233{
234 let original_size = renderer.measure_image(handle).unwrap_or_default();
235 let image_size = crop(original_size, region);
236 let rotated_size = rotation.apply(image_size);
237 let adjusted_fit = content_fit.fit(rotated_size, bounds.size());
238
239 let fit_scale = Vector::new(
240 adjusted_fit.width / rotated_size.width,
241 adjusted_fit.height / rotated_size.height,
242 );
243
244 let final_size = image_size * fit_scale * scale;
245
246 let (crop_offset, final_size) = if let Some(region) = region {
247 let x = region.x.min(original_size.width) as f32;
248 let y = region.y.min(original_size.height) as f32;
249 let width = image_size.width;
250 let height = image_size.height;
251
252 let ratio = Vector::new(
253 original_size.width as f32 / width,
254 original_size.height as f32 / height,
255 );
256
257 let final_size = final_size * ratio;
258
259 let scale = Vector::new(
260 final_size.width / original_size.width as f32,
261 final_size.height / original_size.height as f32,
262 );
263
264 let offset = match content_fit {
265 ContentFit::None => Vector::new(x * scale.x, y * scale.y),
266 _ => Vector::new(
267 ((original_size.width as f32 - width) / 2.0 - x) * scale.x,
268 ((original_size.height as f32 - height) / 2.0 - y) * scale.y,
269 ),
270 };
271
272 (offset, final_size)
273 } else {
274 (Vector::ZERO, final_size)
275 };
276
277 let position = match content_fit {
278 ContentFit::None => Point::new(
279 bounds.x + (rotated_size.width - adjusted_fit.width) / 2.0,
280 bounds.y + (rotated_size.height - adjusted_fit.height) / 2.0,
281 ),
282 _ => Point::new(
283 bounds.center_x() - final_size.width / 2.0,
284 bounds.center_y() - final_size.height / 2.0,
285 ),
286 };
287
288 Rectangle::new(position + crop_offset, final_size)
289}
290
291fn crop(size: Size<u32>, region: Option<Rectangle<u32>>) -> Size<f32> {
292 if let Some(region) = region {
293 Size::new(
294 region.width.min(size.width) as f32,
295 region.height.min(size.height) as f32,
296 )
297 } else {
298 Size::new(size.width as f32, size.height as f32)
299 }
300}
301
302pub fn draw<Renderer, Handle>(
304 renderer: &mut Renderer,
305 layout: Layout,
306 handle: &Handle,
307 crop: Option<Rectangle<u32>>,
308 border_radius: border::Radius,
309 content_fit: ContentFit,
310 filter_method: FilterMethod,
311 rotation: Rotation,
312 opacity: f32,
313 scale: f32,
314) where
315 Renderer: image::Renderer<Handle = Handle>,
316 Handle: Clone,
317{
318 let bounds = layout.bounds();
319 let drawing_bounds =
320 drawing_bounds(renderer, bounds, handle, crop, content_fit, rotation, scale);
321
322 renderer.draw_image(
323 image::Image {
324 handle: handle.clone(),
325 border_radius,
326 filter_method,
327 rotation: rotation.radians(),
328 opacity,
329 },
330 drawing_bounds,
331 bounds,
332 );
333}
334
335impl<Handle> Meta for Image<Handle> {}
336
337impl<Message, Theme, Renderer, Handle> Widget<Message, Theme, Renderer> for Image<Handle>
338where
339 Renderer: image::Renderer<Handle = Handle>,
340 Handle: Clone,
341{
342 fn size(&self) -> Size<Length> {
343 Size {
344 width: self.width,
345 height: self.height,
346 }
347 }
348
349 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
350 tree.size = layout(
351 renderer,
352 limits,
353 &self.handle,
354 self.width,
355 self.height,
356 self.crop,
357 self.content_fit,
358 self.rotation,
359 self.expand,
360 );
361 }
362
363 fn draw(
364 &self,
365 _tree: &Tree,
366 renderer: &mut Renderer,
367 _theme: &Theme,
368 _style: &renderer::Style,
369 layout: Layout,
370 _cursor: mouse::Cursor,
371 _viewport: &Rectangle,
372 ) {
373 draw(
374 renderer,
375 layout,
376 &self.handle,
377 self.crop,
378 self.border_radius,
379 self.content_fit,
380 self.filter_method,
381 self.rotation,
382 self.opacity,
383 self.scale,
384 );
385 }
386}