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 PathBuilder,
23 SamplingOptions,
24 SkImage,
25 SkRect,
26 TileMode,
27 blur,
28 raster_from_data,
29};
30use torin::{
31 prelude::Size2D,
32 scaled::Scaled,
33};
34
35use crate::{
36 data::{
37 AccessibilityData,
38 EffectData,
39 LayoutData,
40 StyleState,
41 TextStyleData,
42 },
43 diff_key::DiffKey,
44 element::{
45 ClipContext,
46 Element,
47 ElementExt,
48 EventHandlers,
49 LayoutContext,
50 RenderContext,
51 },
52 elements::rect::RectElement,
53 layers::Layer,
54 prelude::{
55 AccessibilityExt,
56 ChildrenExt,
57 ContainerExt,
58 ContainerWithContentExt,
59 DecorationExt,
60 EffectExt,
61 EventHandlersExt,
62 ImageExt,
63 KeyExt,
64 LayerExt,
65 LayoutExt,
66 MaybeExt,
67 StyleExt,
68 },
69 style::color::Color,
70 tree::DiffModifies,
71};
72
73pub fn image(image_handle: ImageHandle) -> Image {
78 let mut accessibility = AccessibilityData::default();
79 accessibility.builder.set_role(accesskit::Role::Image);
80 Image {
81 key: DiffKey::None,
82 element: ImageElement {
83 image_handle,
84 accessibility,
85 layout: LayoutData::default(),
86 event_handlers: HashMap::default(),
87 image_data: ImageData::default(),
88 relative_layer: Layer::default(),
89 effect: None,
90 style: StyleState::default(),
91 },
92 elements: Vec::new(),
93 }
94}
95
96#[derive(Default, Clone, Debug, PartialEq)]
98pub enum ImageCover {
99 #[default]
101 Fill,
102 Center,
104}
105
106#[derive(Default, Clone, Debug, PartialEq)]
108pub enum AspectRatio {
109 #[default]
111 Min,
112 Max,
114 Fit,
116 None,
118}
119
120#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
122pub enum SamplingMode {
123 Nearest,
125 Bilinear,
127 #[default]
129 Trilinear,
130 Mitchell,
132 CatmullRom,
134}
135
136impl SamplingMode {
137 pub fn sampling_options(&self) -> SamplingOptions {
139 match self {
140 Self::Nearest => SamplingOptions::new(FilterMode::Nearest, MipmapMode::None),
141 Self::Bilinear => SamplingOptions::new(FilterMode::Linear, MipmapMode::None),
142 Self::Trilinear => SamplingOptions::new(FilterMode::Linear, MipmapMode::Linear),
143 Self::Mitchell => SamplingOptions::from(CubicResampler::mitchell()),
144 Self::CatmullRom => SamplingOptions::from(CubicResampler::catmull_rom()),
145 }
146 }
147}
148
149#[derive(Clone)]
151pub struct ImageHandle {
152 pub image: SkImage,
153 pub bytes: Bytes,
155}
156
157impl ImageHandle {
158 pub fn new(image: SkImage, bytes: Bytes) -> Self {
159 Self { image, bytes }
160 }
161
162 pub fn from_rgba(width: u32, height: u32, bytes: Bytes, alpha_type: AlphaType) -> Option<Self> {
164 let row_bytes = (width as usize).checked_mul(4)?;
165 if bytes.len() < row_bytes.checked_mul(height as usize)? {
166 return None;
167 }
168 let info = ImageInfo::new(
169 ISize::new(width as i32, height as i32),
170 ColorType::RGBA8888,
171 alpha_type,
172 None,
173 );
174 let data = unsafe { Data::new_bytes(&bytes) };
176 let image = raster_from_data(&info, data, row_bytes)?;
177 Some(Self::new(image, bytes))
178 }
179}
180
181impl PartialEq for ImageHandle {
182 fn eq(&self, other: &Self) -> bool {
183 self.image.unique_id() == other.image.unique_id()
184 }
185}
186
187#[derive(Debug, Default, Clone, PartialEq)]
189pub struct ImageData {
190 pub sampling_mode: SamplingMode,
191 pub aspect_ratio: AspectRatio,
192 pub image_cover: ImageCover,
193 pub snap_to_grid: bool,
195 pub blur: f32,
197}
198
199#[derive(PartialEq, Clone)]
200pub struct ImageElement {
201 pub accessibility: AccessibilityData,
202 pub layout: LayoutData,
203 pub event_handlers: EventHandlers,
204 pub image_handle: ImageHandle,
205 pub image_data: ImageData,
206 pub relative_layer: Layer,
207 pub effect: Option<EffectData>,
208 pub style: StyleState,
209}
210
211impl ElementExt for ImageElement {
212 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
213 let Some(image) = (other.as_ref() as &dyn Any).downcast_ref::<ImageElement>() else {
214 return false;
215 };
216 self != image
217 }
218
219 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
220 let Some(image) = (other.as_ref() as &dyn Any).downcast_ref::<ImageElement>() else {
221 return DiffModifies::all();
222 };
223
224 let mut diff = DiffModifies::empty();
225
226 if self.accessibility != image.accessibility {
227 diff.insert(DiffModifies::ACCESSIBILITY);
228 }
229
230 if self.relative_layer != image.relative_layer {
231 diff.insert(DiffModifies::LAYER);
232 }
233
234 if self.layout != image.layout {
235 diff.insert(DiffModifies::LAYOUT);
236 }
237
238 if self.image_handle != image.image_handle {
239 diff.insert(DiffModifies::STYLE);
240
241 if self.image_handle.image.dimensions() != image.image_handle.image.dimensions() {
242 diff.insert(DiffModifies::LAYOUT);
243 }
244 }
245
246 if self.image_data != image.image_data {
247 diff.insert(DiffModifies::STYLE);
248 }
249
250 if self.effect != image.effect {
251 diff.insert(DiffModifies::EFFECT);
252 }
253
254 if self.style != image.style {
255 diff.insert(DiffModifies::STYLE);
256 }
257
258 if self.event_handlers != image.event_handlers {
259 diff.insert(DiffModifies::EVENT_HANDLERS);
260 }
261
262 diff
263 }
264
265 fn layout(&'_ self) -> Cow<'_, LayoutData> {
266 Cow::Borrowed(&self.layout)
267 }
268
269 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
270 self.effect.as_ref().map(Cow::Borrowed)
271 }
272
273 fn style(&'_ self) -> Cow<'_, StyleState> {
274 Cow::Borrowed(&self.style)
275 }
276
277 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
278 Cow::Owned(TextStyleData::default())
279 }
280
281 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
282 Cow::Borrowed(&self.accessibility)
283 }
284
285 fn layer(&self) -> Layer {
286 self.relative_layer
287 }
288
289 fn events_handlers(&'_ self) -> Option<Cow<'_, EventHandlers>> {
290 Some(Cow::Borrowed(&self.event_handlers))
291 }
292
293 fn should_measure_inner_children(&self) -> bool {
294 true
295 }
296
297 fn should_hook_measurement(&self) -> bool {
298 true
299 }
300
301 fn measure(&self, context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
302 let image = &self.image_handle.image;
303
304 let image_width = image.width() as f32;
305 let image_height = image.height() as f32;
306
307 let area_size = (*context.area_size - context.torin_node.margin.into()).max(Size2D::zero());
308
309 let width_ratio = area_size.width / image_width;
310 let height_ratio = area_size.height / image_height;
311
312 let size = match self.image_data.aspect_ratio {
313 AspectRatio::Max => {
314 let ratio = width_ratio.max(height_ratio);
315
316 Size2D::new(image_width * ratio, image_height * ratio)
317 }
318 AspectRatio::Min => {
319 let ratio = width_ratio.min(height_ratio);
320
321 Size2D::new(image_width * ratio, image_height * ratio)
322 }
323 AspectRatio::Fit => Size2D::new(image_width, image_height),
324 AspectRatio::None => area_size,
325 };
326
327 Some((size, Rc::new(size)))
328 }
329
330 fn clip(&self, context: ClipContext) {
331 let rrect = self.render_rect(context.visible_area, context.scale_factor as f32);
332 context.canvas.clip_rrect(rrect, ClipOp::Intersect, true);
333 }
334
335 fn render(&self, context: RenderContext) {
336 let size = context
337 .layout_node
338 .data
339 .as_ref()
340 .unwrap()
341 .downcast_ref::<Size2D>()
342 .unwrap();
343
344 let mut area = context.layout_node.visible_area();
345
346 let mut rect = SkRect::new(
347 area.min_x(),
348 area.min_y(),
349 area.min_x() + size.width,
350 area.min_y() + size.height,
351 );
352 if self.image_data.image_cover == ImageCover::Center {
353 let width_offset = (size.width - area.width()) / 2.;
354 let height_offset = (size.height - area.height()) / 2.;
355
356 rect.left -= width_offset;
357 rect.right -= width_offset;
358 rect.top -= height_offset;
359 rect.bottom -= height_offset;
360 }
361
362 if self.image_data.snap_to_grid {
363 rect = SkRect::new(
364 rect.left.round(),
365 rect.top.round(),
366 rect.right.round(),
367 rect.bottom.round(),
368 );
369 area = area.round();
370 }
371
372 context.canvas.save();
373 let clip_rrect = self.render_rect(&area, context.scale_factor as f32);
374 context
375 .canvas
376 .clip_rrect(clip_rrect, ClipOp::Intersect, true);
377
378 let sampling = self.image_data.sampling_mode.sampling_options();
379
380 let mut paint = Paint::default();
381 paint.set_anti_alias(true);
382 if self.image_data.blur > 0. {
383 paint.set_image_filter(blur(
384 (
385 self.image_data.blur * context.scale_factor as f32,
386 self.image_data.blur * context.scale_factor as f32,
387 ),
388 TileMode::Clamp,
389 None,
390 None,
391 ));
392 }
393
394 context.canvas.draw_image_rect_with_sampling_options(
395 &self.image_handle.image,
396 None,
397 rect,
398 sampling,
399 &paint,
400 );
401
402 context.canvas.restore();
403
404 let corner_radius = self
405 .style
406 .corner_radius
407 .with_scale(context.scale_factor as f32);
408 let mut path = PathBuilder::new();
409 if corner_radius.smoothing() > 0. {
410 path.add_path(&corner_radius.smoothed_path(clip_rrect), None);
411 } else {
412 path.add_rrect(clip_rrect, None, None);
413 }
414 let mut path = path.detach();
415
416 for shadow in &self.style.shadows {
417 if shadow.color != Color::TRANSPARENT {
418 let shadow = shadow.with_scale(context.scale_factor as f32);
419 RectElement::render_shadow(
420 context.canvas,
421 &mut path,
422 clip_rrect,
423 area,
424 &shadow,
425 &corner_radius,
426 );
427 }
428 }
429
430 for border in &self.style.borders {
431 if border.is_visible() {
432 let border = border.with_scale(context.scale_factor as f32);
433 RectElement::render_border(
434 context.canvas,
435 *clip_rrect.rect(),
436 &border,
437 &corner_radius,
438 );
439 }
440 }
441 }
442}
443
444impl From<Image> for Element {
445 fn from(value: Image) -> Self {
446 Element::Element {
447 key: value.key,
448 element: Rc::new(value.element),
449 elements: value.elements,
450 }
451 }
452}
453
454impl KeyExt for Image {
455 fn key_mut(&mut self) -> &mut DiffKey {
456 &mut self.key
457 }
458}
459
460impl EventHandlersExt for Image {
461 fn event_handlers_mut(&mut self) -> &mut EventHandlers {
462 &mut self.element.event_handlers
463 }
464}
465
466impl AccessibilityExt for Image {
467 fn accessibility_data_mut(&mut self) -> &mut AccessibilityData {
468 &mut self.element.accessibility
469 }
470}
471impl MaybeExt for Image {}
472
473impl LayoutExt for Image {
474 fn layout_mut(&mut self) -> &mut LayoutData {
475 &mut self.element.layout
476 }
477}
478
479impl DecorationExt for Image {}
480
481impl StyleExt for Image {
482 fn style_mut(&mut self) -> &mut StyleState {
483 &mut self.element.style
484 }
485}
486
487impl ContainerExt for Image {}
488impl ContainerWithContentExt for Image {}
489
490impl ImageExt for Image {
491 fn image_data_mut(&mut self) -> &mut ImageData {
492 &mut self.element.image_data
493 }
494}
495
496impl ChildrenExt for Image {
497 fn children_mut(&mut self) -> &mut Vec<Element> {
498 &mut self.elements
499 }
500}
501
502impl LayerExt for Image {
503 fn layer_mut(&mut self) -> &mut Layer {
504 &mut self.element.relative_layer
505 }
506}
507
508impl EffectExt for Image {
509 fn effect_mut(&mut self) -> &mut EffectData {
510 self.element.effect.get_or_insert_with(EffectData::default)
511 }
512}
513
514#[derive(Clone)]
515pub struct Image {
516 key: DiffKey,
517 element: ImageElement,
518 elements: Vec<Element>,
519}
520
521impl Image {
522 pub fn try_downcast(element: &dyn ElementExt) -> Option<ImageElement> {
523 (element as &dyn Any)
524 .downcast_ref::<ImageElement>()
525 .cloned()
526 }
527}