tessera_ui_basic_components/
image.rs1use std::sync::Arc;
2
3use derive_builder::Builder;
4use image::GenericImageView;
5use tessera_ui::{ComputedData, Constraint, DimensionValue, Px};
6use tessera_ui_macros::tessera;
7
8use crate::pipelines::image::{ImageCommand, ImageData};
9
10#[derive(Clone, Debug)]
12pub enum ImageSource {
13 Path(String),
15 Bytes(Arc<[u8]>),
17}
18
19pub fn load_image_from_source(source: &ImageSource) -> Result<ImageData, image::ImageError> {
24 let decoded = match source {
25 ImageSource::Path(path) => image::open(path)?,
26 ImageSource::Bytes(bytes) => image::load_from_memory(bytes)?,
27 };
28 let (width, height) = decoded.dimensions();
29 Ok(ImageData {
30 data: Arc::new(decoded.to_rgba8().into_raw()),
31 width,
32 height,
33 })
34}
35
36#[derive(Debug, Builder, Clone)]
38#[builder(pattern = "owned")]
39pub struct ImageArgs {
40 pub data: ImageData,
42
43 #[builder(default, setter(strip_option))]
45 pub width: Option<DimensionValue>,
46
47 #[builder(default, setter(strip_option))]
49 pub height: Option<DimensionValue>,
50}
51
52impl From<ImageData> for ImageArgs {
53 fn from(data: ImageData) -> Self {
54 ImageArgsBuilder::default().data(data).build().unwrap()
55 }
56}
57
58#[tessera]
59pub fn image(args: impl Into<ImageArgs>) {
60 let image_args: ImageArgs = args.into();
61
62 measure(Box::new(move |input| {
63 let intrinsic_width = Px(image_args.data.width as i32);
64 let intrinsic_height = Px(image_args.data.height as i32);
65
66 let image_intrinsic_width = image_args.width.unwrap_or(DimensionValue::Wrap {
67 min: Some(intrinsic_width),
68 max: Some(intrinsic_width),
69 });
70 let image_intrinsic_height = image_args.height.unwrap_or(DimensionValue::Wrap {
71 min: Some(intrinsic_height),
72 max: Some(intrinsic_height),
73 });
74
75 let image_intrinsic_constraint =
76 Constraint::new(image_intrinsic_width, image_intrinsic_height);
77 let effective_image_constraint = image_intrinsic_constraint.merge(input.parent_constraint);
78
79 let width = match effective_image_constraint.width {
80 DimensionValue::Fixed(value) => value,
81 DimensionValue::Wrap { min, max } => min
82 .unwrap_or(Px(0))
83 .max(intrinsic_width)
84 .min(max.unwrap_or(Px::MAX)),
85 DimensionValue::Fill { min, max } => {
86 let parent_max = input.parent_constraint.width.to_max_px(Px::MAX);
87 max.unwrap_or(parent_max)
88 .max(min.unwrap_or(Px(0)))
89 .max(intrinsic_width)
90 }
91 };
92
93 let height = match effective_image_constraint.height {
94 DimensionValue::Fixed(value) => value,
95 DimensionValue::Wrap { min, max } => min
96 .unwrap_or(Px(0))
97 .max(intrinsic_height)
98 .min(max.unwrap_or(Px::MAX)),
99 DimensionValue::Fill { min, max } => {
100 let parent_max = input.parent_constraint.height.to_max_px(Px::MAX);
101 max.unwrap_or(parent_max)
102 .max(min.unwrap_or(Px(0)))
103 .max(intrinsic_height)
104 }
105 };
106
107 let image_command = ImageCommand {
108 data: image_args.data.clone(),
109 };
110
111 input
112 .metadatas
113 .entry(input.current_node_id)
114 .or_default()
115 .push_draw_command(image_command);
116
117 Ok(ComputedData { width, height })
118 }));
119}