use std::sync::Arc;
use derive_builder::Builder;
use image::GenericImageView;
use tessera_ui::{ComputedData, Constraint, DimensionValue, Px, tessera};
use crate::pipelines::image::ImageCommand;
pub use crate::pipelines::image::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, Builder, Clone)]
#[builder(pattern = "owned")]
pub struct ImageArgs {
#[builder(setter(into))]
pub data: Arc<ImageData>,
#[builder(default = "DimensionValue::WRAP", setter(into))]
pub width: DimensionValue,
#[builder(default = "DimensionValue::WRAP", setter(into))]
pub height: DimensionValue,
}
impl From<ImageData> for ImageArgs {
fn from(data: ImageData) -> Self {
ImageArgsBuilder::default()
.data(Arc::new(data))
.build()
.unwrap()
}
}
#[tessera]
pub fn image(args: impl Into<ImageArgs>) {
let image_args: ImageArgs = args.into();
measure(Box::new(move |input| {
let intrinsic_width = Px(image_args.data.width as i32);
let intrinsic_height = Px(image_args.data.height as i32);
let image_intrinsic_width = image_args.width;
let image_intrinsic_height = image_args.height;
let image_intrinsic_constraint =
Constraint::new(image_intrinsic_width, image_intrinsic_height);
let effective_image_constraint = image_intrinsic_constraint.merge(input.parent_constraint);
let width = match effective_image_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 } => {
let parent_max = input.parent_constraint.width.get_max().unwrap_or(Px::MAX);
max.unwrap_or(parent_max)
.max(min.unwrap_or(Px(0)))
.max(intrinsic_width)
}
};
let height = match effective_image_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 } => {
let parent_max = input.parent_constraint.height.get_max().unwrap_or(Px::MAX);
max.unwrap_or(parent_max)
.max(min.unwrap_or(Px(0)))
.max(intrinsic_height)
}
};
let image_command = ImageCommand {
data: image_args.data.clone(),
};
input
.metadatas
.entry(input.current_node_id)
.or_default()
.push_draw_command(image_command);
Ok(ComputedData { width, height })
}));
}