use cranpose_services::{ImageLoadError, ImageLoadState, ImageRequest, rememberAsyncImage};
use cranpose_ui::{
Alignment, BitmapPainter, BoxWithConstraints, BoxWithConstraintsScope, ContentScale, Image,
Modifier, composable, density,
};
fn device_pixels(logical: f32, scale: f32) -> u32 {
if logical.is_finite() {
(logical * scale).ceil().clamp(1.0, u32::MAX as f32) as u32
} else {
u32::MAX
}
}
#[composable]
pub fn AsyncImage(
request: ImageRequest,
content_description: Option<String>,
modifier: Modifier,
content_scale: ContentScale,
placeholder: impl Fn() + 'static,
error: impl Fn(&ImageLoadError) + 'static,
) {
BoxWithConstraints(modifier, move |scope| {
let constraints = scope.constraints();
let scale = density().density();
let request = request.clone().or_max_size(
device_pixels(constraints.max_width, scale),
device_pixels(constraints.max_height, scale),
);
match rememberAsyncImage(request).value() {
ImageLoadState::Ready(bitmap) => {
let bounded =
constraints.max_width.is_finite() && constraints.max_height.is_finite();
let fill = if bounded {
Modifier::empty().fill_max_size()
} else {
Modifier::empty()
};
Image(
BitmapPainter(bitmap),
content_description.clone(),
fill,
Alignment::CENTER,
content_scale,
1.0,
None,
);
}
ImageLoadState::Loading => placeholder(),
ImageLoadState::Failed(failure) => error(&failure),
}
});
}