1use std::{
2 any::Any,
3 borrow::Cow,
4 cell::RefCell,
5 collections::HashMap,
6 rc::Rc,
7};
8
9use bytes::Bytes;
10use freya_engine::prelude::{
11 ClipOp,
12 CubicResampler,
13 FilterMode,
14 MipmapMode,
15 Paint,
16 SamplingOptions,
17 SkImage,
18 SkRect,
19};
20use rustc_hash::FxHashMap;
21use torin::prelude::Size2D;
22
23use crate::{
24 data::{
25 AccessibilityData,
26 EffectData,
27 LayoutData,
28 StyleState,
29 TextStyleData,
30 },
31 diff_key::DiffKey,
32 element::{
33 ClipContext,
34 Element,
35 ElementExt,
36 EventHandlerType,
37 LayoutContext,
38 RenderContext,
39 },
40 events::name::EventName,
41 layers::Layer,
42 prelude::{
43 AccessibilityExt,
44 ChildrenExt,
45 ContainerExt,
46 ContainerWithContentExt,
47 EventHandlersExt,
48 ImageExt,
49 KeyExt,
50 LayerExt,
51 LayoutExt,
52 MaybeExt,
53 },
54 tree::DiffModifies,
55};
56
57#[derive(Default, Clone, Debug, PartialEq)]
58pub enum ImageCover {
59 #[default]
60 Fill,
61 Center,
62}
63
64#[derive(Default, Clone, Debug, PartialEq)]
65pub enum AspectRatio {
66 #[default]
67 Min,
68 Max,
69 Fit,
70 None,
71}
72
73#[derive(Clone, Debug, PartialEq, Default)]
74pub enum SamplingMode {
75 #[default]
76 Nearest,
77 Bilinear,
78 Trilinear,
79 Mitchell,
80 CatmullRom,
81}
82
83#[derive(Clone)]
84pub struct ImageHolder {
85 pub image: Rc<RefCell<SkImage>>,
86 pub bytes: Bytes,
87}
88
89impl PartialEq for ImageHolder {
90 fn eq(&self, other: &Self) -> bool {
91 Rc::ptr_eq(&self.image, &other.image)
92 }
93}
94
95#[derive(Debug, Default, Clone, PartialEq)]
96pub struct ImageData {
97 pub sampling_mode: SamplingMode,
98 pub aspect_ratio: AspectRatio,
99 pub image_cover: ImageCover,
100}
101
102#[derive(PartialEq, Clone)]
103pub struct ImageElement {
104 pub accessibility: AccessibilityData,
105 pub layout: LayoutData,
106 pub event_handlers: FxHashMap<EventName, EventHandlerType>,
107 pub image_holder: ImageHolder,
108 pub image_data: ImageData,
109 pub relative_layer: Layer,
110}
111
112impl ElementExt for ImageElement {
113 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
114 let Some(image) = (other.as_ref() as &dyn Any).downcast_ref::<ImageElement>() else {
115 return false;
116 };
117 self != image
118 }
119
120 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
121 let Some(image) = (other.as_ref() as &dyn Any).downcast_ref::<ImageElement>() else {
122 return DiffModifies::all();
123 };
124
125 let mut diff = DiffModifies::empty();
126
127 if self.accessibility != image.accessibility {
128 diff.insert(DiffModifies::ACCESSIBILITY);
129 }
130
131 if self.relative_layer != image.relative_layer {
132 diff.insert(DiffModifies::LAYER);
133 }
134
135 if self.layout != image.layout {
136 diff.insert(DiffModifies::LAYOUT);
137 }
138
139 if self.image_holder != image.image_holder {
140 diff.insert(DiffModifies::LAYOUT);
141 diff.insert(DiffModifies::STYLE);
142 }
143
144 diff
145 }
146
147 fn layout(&'_ self) -> Cow<'_, LayoutData> {
148 Cow::Borrowed(&self.layout)
149 }
150
151 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
152 None
153 }
154
155 fn style(&'_ self) -> Cow<'_, StyleState> {
156 Cow::Owned(StyleState::default())
157 }
158
159 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
160 Cow::Owned(TextStyleData::default())
161 }
162
163 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
164 Cow::Borrowed(&self.accessibility)
165 }
166
167 fn layer(&self) -> Layer {
168 self.relative_layer
169 }
170
171 fn should_measure_inner_children(&self) -> bool {
172 true
173 }
174
175 fn should_hook_measurement(&self) -> bool {
176 true
177 }
178
179 fn measure(&self, context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
180 let image = self.image_holder.image.borrow();
181
182 let image_width = image.width() as f32;
183 let image_height = image.height() as f32;
184
185 let width_ratio = context.area_size.width / image.width() as f32;
186 let height_ratio = context.area_size.height / image.height() as f32;
187
188 let size = match self.image_data.aspect_ratio {
189 AspectRatio::Max => {
190 let ratio = width_ratio.max(height_ratio);
191
192 Size2D::new(image_width * ratio, image_height * ratio)
193 }
194 AspectRatio::Min => {
195 let ratio = width_ratio.min(height_ratio);
196
197 Size2D::new(image_width * ratio, image_height * ratio)
198 }
199 AspectRatio::Fit => Size2D::new(image_width, image_height),
200 AspectRatio::None => *context.area_size,
201 };
202
203 Some((size, Rc::new(size)))
204 }
205
206 fn clip(&self, context: ClipContext) {
207 let area = context.visible_area;
208 context.canvas.clip_rect(
209 SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
210 ClipOp::Intersect,
211 true,
212 );
213 }
214
215 fn render(&self, context: RenderContext) {
216 let size = context
217 .layout_node
218 .data
219 .as_ref()
220 .unwrap()
221 .downcast_ref::<Size2D>()
222 .unwrap();
223
224 let area = context.layout_node.visible_area();
225 let image = self.image_holder.image.borrow();
226
227 let mut rect = SkRect::new(
228 area.min_x(),
229 area.min_y(),
230 area.min_x() + size.width,
231 area.min_y() + size.height,
232 );
233 let clip_rect = SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y());
234
235 if self.image_data.image_cover == ImageCover::Center {
236 let width_offset = (size.width - area.width()) / 2.;
237 let height_offset = (size.height - area.height()) / 2.;
238
239 rect.left -= width_offset;
240 rect.right -= width_offset;
241 rect.top -= height_offset;
242 rect.bottom -= height_offset;
243 }
244
245 context.canvas.save();
246 context.canvas.clip_rect(clip_rect, ClipOp::Intersect, true);
247
248 let sampling = match self.image_data.sampling_mode {
249 SamplingMode::Nearest => SamplingOptions::new(FilterMode::Nearest, MipmapMode::None),
250 SamplingMode::Bilinear => SamplingOptions::new(FilterMode::Linear, MipmapMode::None),
251 SamplingMode::Trilinear => SamplingOptions::new(FilterMode::Linear, MipmapMode::Linear),
252 SamplingMode::Mitchell => SamplingOptions::from(CubicResampler::mitchell()),
253 SamplingMode::CatmullRom => SamplingOptions::from(CubicResampler::catmull_rom()),
254 };
255
256 let mut paint = Paint::default();
257 paint.set_anti_alias(true);
258
259 context
260 .canvas
261 .draw_image_rect_with_sampling_options(&*image, None, rect, sampling, &paint);
262
263 context.canvas.restore();
264 }
265}
266
267impl From<Image> for Element {
268 fn from(value: Image) -> Self {
269 Element::Element {
270 key: value.key,
271 element: Rc::new(value.element),
272 elements: value.elements,
273 }
274 }
275}
276
277impl KeyExt for Image {
278 fn write_key(&mut self) -> &mut DiffKey {
279 &mut self.key
280 }
281}
282
283impl EventHandlersExt for Image {
284 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
285 &mut self.element.event_handlers
286 }
287}
288
289impl AccessibilityExt for Image {
290 fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
291 &mut self.element.accessibility
292 }
293}
294impl MaybeExt for Image {}
295
296impl LayoutExt for Image {
297 fn get_layout(&mut self) -> &mut LayoutData {
298 &mut self.element.layout
299 }
300}
301
302impl ContainerExt for Image {}
303impl ContainerWithContentExt for Image {}
304
305impl ImageExt for Image {
306 fn get_image_data(&mut self) -> &mut ImageData {
307 &mut self.element.image_data
308 }
309}
310
311impl ChildrenExt for Image {
312 fn get_children(&mut self) -> &mut Vec<Element> {
313 &mut self.elements
314 }
315}
316
317impl LayerExt for Image {
318 fn get_layer(&mut self) -> &mut Layer {
319 &mut self.element.relative_layer
320 }
321}
322
323pub struct Image {
324 key: DiffKey,
325 element: ImageElement,
326 elements: Vec<Element>,
327}
328
329pub fn image(image_holder: ImageHolder) -> Image {
334 let mut accessibility = AccessibilityData::default();
335 accessibility.builder.set_role(accesskit::Role::Image);
336 Image {
337 key: DiffKey::None,
338 element: ImageElement {
339 image_holder,
340 accessibility,
341 layout: LayoutData::default(),
342 event_handlers: HashMap::default(),
343 image_data: ImageData::default(),
344 relative_layer: Layer::default(),
345 },
346 elements: Vec::new(),
347 }
348}
349
350impl Image {
351 pub fn try_downcast(element: &dyn ElementExt) -> Option<ImageElement> {
352 (element as &dyn Any)
353 .downcast_ref::<ImageElement>()
354 .cloned()
355 }
356}