1pub mod viewer;
3pub use viewer::Viewer;
4
5use crate::image;
6use crate::layout;
7use crate::renderer;
8use crate::widget::Tree;
9use crate::{
10 ContentFit, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
11};
12
13use std::hash::Hash;
14
15pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
17 Viewer::new(handle)
18}
19
20#[derive(Debug)]
33pub struct Image<Handle> {
34 handle: Handle,
35 width: Length,
36 height: Length,
37 content_fit: ContentFit,
38}
39
40impl<Handle> Image<Handle> {
41 pub fn new<T: Into<Handle>>(handle: T) -> Self {
43 Image {
44 handle: handle.into(),
45 width: Length::Shrink,
46 height: Length::Shrink,
47 content_fit: ContentFit::Contain,
48 }
49 }
50
51 pub fn width(mut self, width: impl Into<Length>) -> Self {
53 self.width = width.into();
54 self
55 }
56
57 pub fn height(mut self, height: impl Into<Length>) -> Self {
59 self.height = height.into();
60 self
61 }
62
63 pub fn content_fit(self, content_fit: ContentFit) -> Self {
67 Self {
68 content_fit,
69 ..self
70 }
71 }
72}
73
74pub fn layout<Renderer, Handle>(
76 renderer: &Renderer,
77 limits: &layout::Limits,
78 handle: &Handle,
79 width: Length,
80 height: Length,
81 content_fit: ContentFit,
82) -> layout::Node
83where
84 Renderer: image::Renderer<Handle = Handle>,
85{
86 let image_size = {
88 let Size { width, height } = renderer.dimensions(handle);
89
90 Size::new(width as f32, height as f32)
91 };
92
93 let raw_size = limits.width(width).height(height).resolve(image_size);
95
96 let full_size = content_fit.fit(image_size, raw_size);
98
99 let final_size = Size {
101 width: match width {
102 Length::Shrink => f32::min(raw_size.width, full_size.width),
103 _ => raw_size.width,
104 },
105 height: match height {
106 Length::Shrink => f32::min(raw_size.height, full_size.height),
107 _ => raw_size.height,
108 },
109 };
110
111 layout::Node::new(final_size)
112}
113
114pub fn draw<Renderer, Handle>(
116 renderer: &mut Renderer,
117 layout: Layout<'_>,
118 handle: &Handle,
119 content_fit: ContentFit,
120) where
121 Renderer: image::Renderer<Handle = Handle>,
122 Handle: Clone + Hash,
123{
124 let Size { width, height } = renderer.dimensions(handle);
125 let image_size = Size::new(width as f32, height as f32);
126
127 let bounds = layout.bounds();
128 let adjusted_fit = content_fit.fit(image_size, bounds.size());
129
130 let render = |renderer: &mut Renderer| {
131 let offset = Vector::new(
132 (bounds.width - adjusted_fit.width).max(0.0) / 2.0,
133 (bounds.height - adjusted_fit.height).max(0.0) / 2.0,
134 );
135
136 let drawing_bounds = Rectangle {
137 width: adjusted_fit.width,
138 height: adjusted_fit.height,
139 ..bounds
140 };
141
142 renderer.draw(handle.clone(), drawing_bounds + offset)
143 };
144
145 if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height
146 {
147 renderer.with_layer(bounds, render);
148 } else {
149 render(renderer)
150 }
151}
152
153impl<Message, Renderer, Handle> Widget<Message, Renderer> for Image<Handle>
154where
155 Renderer: image::Renderer<Handle = Handle>,
156 Handle: Clone + Hash,
157{
158 fn width(&self) -> Length {
159 self.width
160 }
161
162 fn height(&self) -> Length {
163 self.height
164 }
165
166 fn layout(
167 &self,
168 renderer: &Renderer,
169 limits: &layout::Limits,
170 ) -> layout::Node {
171 layout(
172 renderer,
173 limits,
174 &self.handle,
175 self.width,
176 self.height,
177 self.content_fit,
178 )
179 }
180
181 fn draw(
182 &self,
183 _state: &Tree,
184 renderer: &mut Renderer,
185 _theme: &Renderer::Theme,
186 _style: &renderer::Style,
187 layout: Layout<'_>,
188 _cursor_position: Point,
189 _viewport: &Rectangle,
190 ) {
191 draw(renderer, layout, &self.handle, self.content_fit)
192 }
193}
194
195impl<'a, Message, Renderer, Handle> From<Image<Handle>>
196 for Element<'a, Message, Renderer>
197where
198 Renderer: image::Renderer<Handle = Handle>,
199 Handle: Clone + Hash + 'a,
200{
201 fn from(image: Image<Handle>) -> Element<'a, Message, Renderer> {
202 Element::new(image)
203 }
204}