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