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