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