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