tessera_components/
image.rs1use tessera_ui::{
7 Color, ComputedData, LayoutResult, MeasurementError, Modifier, Px,
8 layout::{MeasureScope, layout},
9 tessera,
10};
11
12use crate::{
13 image_vector::TintMode,
14 painter::Painter,
15 pipelines::{image::command::ImageCommand, image_vector::command::ImageVectorCommand},
16};
17
18pub use crate::pipelines::image::command::ImageData;
19
20fn placeholder_image_data() -> ImageData {
21 ImageData::from_rgba(vec![0, 0, 0, 0], 1, 1)
22}
23
24pub(crate) fn decode_raster(bytes: &[u8]) -> Result<ImageData, image::ImageError> {
25 let decoded = image::load_from_memory(bytes)?.into_rgba8();
26 let (width, height) = decoded.dimensions();
27 Ok(ImageData::from_rgba(decoded.into_raw(), width, height))
28}
29
30impl ImageBuilder {
31 pub fn painter(&self, painter: impl Into<Painter>) -> Self {
33 let value = Some(painter.into());
34 self.props
35 .configure("painter", move |props| props.painter = value);
36 Self { props: self.props }
37 }
38}
39
40#[derive(Clone, PartialEq)]
41struct ImageLayout {
42 painter: Painter,
43}
44
45impl tessera_ui::LayoutModifierNode for ImageLayout {
46 fn measure(&self, input: &mut MeasureScope<'_>) -> Result<LayoutResult, MeasurementError> {
47 let (intrinsic_width, intrinsic_height) = match &self.painter {
48 Painter::Source(_) => (Px::ZERO, Px::ZERO),
49 Painter::Vector(data) => (
50 clamp_f32_to_px(data.viewport_width),
51 clamp_f32_to_px(data.viewport_height),
52 ),
53 Painter::Raster(data) => (Px(data.width as i32), Px(data.height as i32)),
54 };
55
56 let width = input.parent_constraint().width().clamp(intrinsic_width);
57 let height = input.parent_constraint().height().clamp(intrinsic_height);
58
59 Ok(LayoutResult::new(ComputedData { width, height }))
60 }
61}
62
63impl tessera_ui::DrawModifierNode for ImageLayout {
64 fn draw(
65 &self,
66 ctx: &mut tessera_ui::DrawModifierContext<'_, '_>,
67 content: &mut dyn tessera_ui::DrawModifierContent,
68 ) {
69 let input = &mut *ctx.render_input;
70 match &self.painter {
71 Painter::Source(_) => {}
72 Painter::Raster(data) => {
73 let image_command = ImageCommand {
74 data: data.clone(),
75 opacity: 1.0,
76 };
77 input
78 .metadata_mut()
79 .fragment_mut()
80 .push_draw_command(image_command);
81 }
82 Painter::Vector(data) => {
83 let vector_command = ImageVectorCommand {
84 data: data.clone(),
85 tint: Color::WHITE,
86 tint_mode: TintMode::Multiply,
87 rotation: 0.0,
88 };
89 input
90 .metadata_mut()
91 .fragment_mut()
92 .push_draw_command(vector_command);
93 }
94 }
95 content.draw(input);
96 }
97}
98
99#[tessera]
127pub fn image(#[prop(skip_setter)] painter: Option<Painter>, modifier: Option<Modifier>) {
128 let modifier = modifier.unwrap_or_default();
129 let painter = painter
130 .and_then(Painter::resolve)
131 .unwrap_or_else(|| Painter::Raster(placeholder_image_data()));
132 let policy = ImageLayout {
133 painter: painter.clone(),
134 };
135 layout()
136 .modifier(modifier)
137 .policy(policy.clone())
138 .draw(policy);
139}
140
141pub(crate) fn clamp_f32_to_px(value: f32) -> Px {
142 let clamped = value.max(0.0).min(i32::MAX as f32);
143 Px(clamped.round() as i32)
144}