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