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