use std::sync::Arc;
use derive_setters::Setters;
use image::GenericImageView;
use tessera_ui::{
ComputedData, DimensionValue, MeasurementError, Modifier, Px,
layout::{LayoutInput, LayoutOutput, LayoutSpec, RenderInput},
tessera,
};
use crate::pipelines::image::command::ImageCommand;
pub use crate::pipelines::image::command::ImageData;
#[derive(Clone, Debug)]
pub enum ImageSource {
Path(String),
Bytes(Arc<[u8]>),
}
pub fn load_image_from_source(source: &ImageSource) -> Result<ImageData, image::ImageError> {
let decoded = match source {
ImageSource::Path(path) => image::open(path)?,
ImageSource::Bytes(bytes) => image::load_from_memory(bytes)?,
};
let (width, height) = decoded.dimensions();
Ok(ImageData {
data: Arc::new(decoded.to_rgba8().into_raw()),
width,
height,
})
}
#[derive(Debug, Setters, Clone)]
pub struct ImageArgs {
#[setters(into)]
pub data: Arc<ImageData>,
pub modifier: Modifier,
}
impl From<ImageData> for ImageArgs {
fn from(data: ImageData) -> Self {
Self {
data: Arc::new(data),
modifier: Modifier::new(),
}
}
}
#[derive(Clone, PartialEq)]
struct ImageLayout {
data: Arc<ImageData>,
}
impl LayoutSpec for ImageLayout {
fn measure(
&self,
input: &LayoutInput<'_>,
_output: &mut LayoutOutput<'_>,
) -> Result<ComputedData, MeasurementError> {
let intrinsic_width = Px(self.data.width as i32);
let intrinsic_height = Px(self.data.height as i32);
let width = match input.parent_constraint().width() {
DimensionValue::Fixed(value) => value,
DimensionValue::Wrap { min, max } => min
.unwrap_or(Px(0))
.max(intrinsic_width)
.min(max.unwrap_or(Px::MAX)),
DimensionValue::Fill { min, max } => max
.expect("Seems that you are trying to fill an infinite width, which is not allowed")
.max(min.unwrap_or(Px(0)))
.max(intrinsic_width),
};
let height = match input.parent_constraint().height() {
DimensionValue::Fixed(value) => value,
DimensionValue::Wrap { min, max } => min
.unwrap_or(Px(0))
.max(intrinsic_height)
.min(max.unwrap_or(Px::MAX)),
DimensionValue::Fill { min, max } => max
.expect(
"Seems that you are trying to fill an infinite height, which is not allowed",
)
.max(min.unwrap_or(Px(0)))
.max(intrinsic_height),
};
Ok(ComputedData { width, height })
}
fn record(&self, input: &RenderInput<'_>) {
let image_command = ImageCommand {
data: self.data.clone(),
opacity: 1.0,
};
input.metadata_mut().push_draw_command(image_command);
}
}
#[tessera]
pub fn image(args: impl Into<ImageArgs>) {
let image_args: ImageArgs = args.into();
let modifier = image_args.modifier;
modifier.run(move || image_inner(image_args));
}
#[tessera]
fn image_inner(args: ImageArgs) {
let data = args.data;
layout(ImageLayout { data });
}