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 torin::prelude::Size2D;
28
29use crate::{
30 data::{
31 AccessibilityData,
32 EffectData,
33 LayoutData,
34 StyleState,
35 TextStyleData,
36 },
37 diff_key::DiffKey,
38 element::{
39 ClipContext,
40 Element,
41 ElementExt,
42 EventHandlers,
43 LayoutContext,
44 RenderContext,
45 },
46 layers::Layer,
47 prelude::{
48 AccessibilityExt,
49 ChildrenExt,
50 ContainerExt,
51 ContainerWithContentExt,
52 EffectExt,
53 EventHandlersExt,
54 ImageExt,
55 KeyExt,
56 LayerExt,
57 LayoutExt,
58 MaybeExt,
59 },
60 style::corner_radius::CornerRadius,
61 tree::DiffModifies,
62};
63
64pub fn image(image_handle: ImageHandle) -> Image {
69 let mut accessibility = AccessibilityData::default();
70 accessibility.builder.set_role(accesskit::Role::Image);
71 Image {
72 key: DiffKey::None,
73 element: ImageElement {
74 image_handle,
75 accessibility,
76 layout: LayoutData::default(),
77 event_handlers: HashMap::default(),
78 image_data: ImageData::default(),
79 relative_layer: Layer::default(),
80 effect: None,
81 corner_radius: None,
82 },
83 elements: Vec::new(),
84 }
85}
86
87#[derive(Default, Clone, Debug, PartialEq)]
89pub enum ImageCover {
90 #[default]
92 Fill,
93 Center,
95}
96
97#[derive(Default, Clone, Debug, PartialEq)]
99pub enum AspectRatio {
100 #[default]
102 Min,
103 Max,
105 Fit,
107 None,
109}
110
111#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
113pub enum SamplingMode {
114 Nearest,
116 Bilinear,
118 #[default]
120 Trilinear,
121 Mitchell,
123 CatmullRom,
125}
126
127impl SamplingMode {
128 pub fn sampling_options(&self) -> SamplingOptions {
130 match self {
131 Self::Nearest => SamplingOptions::new(FilterMode::Nearest, MipmapMode::None),
132 Self::Bilinear => SamplingOptions::new(FilterMode::Linear, MipmapMode::None),
133 Self::Trilinear => SamplingOptions::new(FilterMode::Linear, MipmapMode::Linear),
134 Self::Mitchell => SamplingOptions::from(CubicResampler::mitchell()),
135 Self::CatmullRom => SamplingOptions::from(CubicResampler::catmull_rom()),
136 }
137 }
138}
139
140#[derive(Clone)]
142pub struct ImageHandle {
143 pub image: SkImage,
144 pub bytes: Bytes,
146}
147
148impl ImageHandle {
149 pub fn new(image: SkImage, bytes: Bytes) -> Self {
150 Self { image, bytes }
151 }
152
153 pub fn from_rgba(width: u32, height: u32, bytes: Bytes, alpha_type: AlphaType) -> Option<Self> {
155 let row_bytes = (width as usize).checked_mul(4)?;
156 if bytes.len() < row_bytes.checked_mul(height as usize)? {
157 return None;
158 }
159 let info = ImageInfo::new(
160 ISize::new(width as i32, height as i32),
161 ColorType::RGBA8888,
162 alpha_type,
163 None,
164 );
165 let data = unsafe { Data::new_bytes(&bytes) };
167 let image = raster_from_data(&info, data, row_bytes)?;
168 Some(Self::new(image, bytes))
169 }
170}
171
172impl PartialEq for ImageHandle {
173 fn eq(&self, other: &Self) -> bool {
174 self.image.unique_id() == other.image.unique_id()
175 }
176}
177
178#[derive(Debug, Default, Clone, PartialEq)]
180pub struct ImageData {
181 pub sampling_mode: SamplingMode,
182 pub aspect_ratio: AspectRatio,
183 pub image_cover: ImageCover,
184 pub snap_to_grid: bool,
186}
187
188#[derive(PartialEq, Clone)]
189pub struct ImageElement {
190 pub accessibility: AccessibilityData,
191 pub layout: LayoutData,
192 pub event_handlers: EventHandlers,
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 if self.event_handlers != image.event_handlers {
244 diff.insert(DiffModifies::EVENT_HANDLERS);
245 }
246
247 diff
248 }
249
250 fn layout(&'_ self) -> Cow<'_, LayoutData> {
251 Cow::Borrowed(&self.layout)
252 }
253
254 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
255 self.effect.as_ref().map(Cow::Borrowed)
256 }
257
258 fn style(&'_ self) -> Cow<'_, StyleState> {
259 Cow::Owned(StyleState {
260 corner_radius: self.corner_radius.unwrap_or_default(),
261 ..StyleState::default()
262 })
263 }
264
265 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
266 Cow::Owned(TextStyleData::default())
267 }
268
269 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
270 Cow::Borrowed(&self.accessibility)
271 }
272
273 fn layer(&self) -> Layer {
274 self.relative_layer
275 }
276
277 fn events_handlers(&'_ self) -> Option<Cow<'_, EventHandlers>> {
278 Some(Cow::Borrowed(&self.event_handlers))
279 }
280
281 fn should_measure_inner_children(&self) -> bool {
282 true
283 }
284
285 fn should_hook_measurement(&self) -> bool {
286 true
287 }
288
289 fn measure(&self, context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
290 let image = &self.image_handle.image;
291
292 let image_width = image.width() as f32;
293 let image_height = image.height() as f32;
294
295 let area_size = (*context.area_size - context.torin_node.margin.into()).max(Size2D::zero());
296
297 let width_ratio = area_size.width / image_width;
298 let height_ratio = area_size.height / image_height;
299
300 let size = match self.image_data.aspect_ratio {
301 AspectRatio::Max => {
302 let ratio = width_ratio.max(height_ratio);
303
304 Size2D::new(image_width * ratio, image_height * ratio)
305 }
306 AspectRatio::Min => {
307 let ratio = width_ratio.min(height_ratio);
308
309 Size2D::new(image_width * ratio, image_height * ratio)
310 }
311 AspectRatio::Fit => Size2D::new(image_width, image_height),
312 AspectRatio::None => area_size,
313 };
314
315 Some((size, Rc::new(size)))
316 }
317
318 fn clip(&self, context: ClipContext) {
319 let rrect = self.render_rect(context.visible_area, context.scale_factor as f32);
320 context.canvas.clip_rrect(rrect, ClipOp::Intersect, true);
321 }
322
323 fn render(&self, context: RenderContext) {
324 let size = context
325 .layout_node
326 .data
327 .as_ref()
328 .unwrap()
329 .downcast_ref::<Size2D>()
330 .unwrap();
331
332 let mut area = context.layout_node.visible_area();
333
334 let mut rect = SkRect::new(
335 area.min_x(),
336 area.min_y(),
337 area.min_x() + size.width,
338 area.min_y() + size.height,
339 );
340 if self.image_data.image_cover == ImageCover::Center {
341 let width_offset = (size.width - area.width()) / 2.;
342 let height_offset = (size.height - area.height()) / 2.;
343
344 rect.left -= width_offset;
345 rect.right -= width_offset;
346 rect.top -= height_offset;
347 rect.bottom -= height_offset;
348 }
349
350 if self.image_data.snap_to_grid {
351 rect = SkRect::new(
352 rect.left.round(),
353 rect.top.round(),
354 rect.right.round(),
355 rect.bottom.round(),
356 );
357 area = area.round();
358 }
359
360 context.canvas.save();
361 let clip_rrect = self.render_rect(&area, context.scale_factor as f32);
362 context
363 .canvas
364 .clip_rrect(clip_rrect, ClipOp::Intersect, true);
365
366 let sampling = self.image_data.sampling_mode.sampling_options();
367
368 let mut paint = Paint::default();
369 paint.set_anti_alias(true);
370
371 context.canvas.draw_image_rect_with_sampling_options(
372 &self.image_handle.image,
373 None,
374 rect,
375 sampling,
376 &paint,
377 );
378
379 context.canvas.restore();
380 }
381}
382
383impl From<Image> for Element {
384 fn from(value: Image) -> Self {
385 Element::Element {
386 key: value.key,
387 element: Rc::new(value.element),
388 elements: value.elements,
389 }
390 }
391}
392
393impl KeyExt for Image {
394 fn write_key(&mut self) -> &mut DiffKey {
395 &mut self.key
396 }
397}
398
399impl EventHandlersExt for Image {
400 fn get_event_handlers(&mut self) -> &mut EventHandlers {
401 &mut self.element.event_handlers
402 }
403}
404
405impl AccessibilityExt for Image {
406 fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
407 &mut self.element.accessibility
408 }
409}
410impl MaybeExt for Image {}
411
412impl LayoutExt for Image {
413 fn get_layout(&mut self) -> &mut LayoutData {
414 &mut self.element.layout
415 }
416}
417
418impl ContainerExt for Image {}
419impl ContainerWithContentExt for Image {}
420
421impl ImageExt for Image {
422 fn get_image_data(&mut self) -> &mut ImageData {
423 &mut self.element.image_data
424 }
425}
426
427impl ChildrenExt for Image {
428 fn get_children(&mut self) -> &mut Vec<Element> {
429 &mut self.elements
430 }
431}
432
433impl LayerExt for Image {
434 fn get_layer(&mut self) -> &mut Layer {
435 &mut self.element.relative_layer
436 }
437}
438
439impl EffectExt for Image {
440 fn get_effect(&mut self) -> &mut EffectData {
441 self.element.effect.get_or_insert_with(EffectData::default)
442 }
443}
444
445#[derive(Clone)]
446pub struct Image {
447 key: DiffKey,
448 element: ImageElement,
449 elements: Vec<Element>,
450}
451
452impl Image {
453 pub fn try_downcast(element: &dyn ElementExt) -> Option<ImageElement> {
454 (element as &dyn Any)
455 .downcast_ref::<ImageElement>()
456 .cloned()
457 }
458
459 pub fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
461 self.element.corner_radius = Some(corner_radius.into());
462 self
463 }
464}