use kas::draw::{DrawShared, ImageHandle};
use kas::layout::PixmapScaling;
use kas::prelude::*;
#[cfg(feature = "image")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "image")))]
#[derive(thiserror::Error, Debug)]
pub enum ImageError {
#[error("IO error")]
IOError(#[from] std::io::Error),
#[error(transparent)]
Image(#[from] image::ImageError),
#[error("failed to allocate texture space for image")]
Allocation,
}
#[cfg(feature = "image")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "image")))]
impl From<kas::draw::AllocError> for ImageError {
fn from(_: kas::draw::AllocError) -> ImageError {
ImageError::Allocation
}
}
#[cfg(feature = "image")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "image")))]
pub type Result<T> = std::result::Result<T, ImageError>;
impl_scope! {
#[derive(Clone, Debug, Default)]
#[widget {
Data = ();
}]
pub struct Image {
core: widget_core!(),
scaling: PixmapScaling,
handle: Option<ImageHandle>,
}
impl Self {
#[inline]
pub fn new(handle: ImageHandle, draw: &mut dyn DrawShared) -> Option<Self> {
let mut sprite = Self::default();
sprite.set(handle, draw).map(|_| sprite)
}
#[cfg(feature = "image")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "image")))]
#[inline]
pub fn new_path<P: AsRef<std::path::Path>>(
path: P,
draw: &mut dyn DrawShared,
) -> Result<Self> {
let mut sprite = Self::default();
let _ = sprite.load_path(path, draw)?;
Ok(sprite)
}
pub fn set(&mut self, handle: ImageHandle, draw: &mut dyn DrawShared) -> Option<Action> {
if let Some(size) = draw.image_size(&handle) {
self.scaling.size = size.cast();
self.handle = Some(handle);
Some(Action::RESIZE)
} else {
None
}
}
#[cfg(feature = "image")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "image")))]
pub fn load_path<P: AsRef<std::path::Path>>(
&mut self,
path: P,
draw: &mut dyn DrawShared,
) -> Result<Action> {
let image = image::io::Reader::open(path)?
.with_guessed_format()?
.decode()?;
let image = image.into_rgba8();
let size = image.dimensions();
let handle = draw.image_alloc(size)?;
draw.image_upload(&handle, &image, kas::draw::ImageFormat::Rgba8);
if let Some(old_handle) = self.handle.take() {
draw.image_free(old_handle);
}
self.scaling.size = size.cast();
self.handle = Some(handle);
Ok(Action::RESIZE)
}
pub fn clear(&mut self, draw: &mut dyn DrawShared) -> Action {
if let Some(handle) = self.handle.take() {
draw.image_free(handle);
Action::RESIZE
} else {
Action::empty()
}
}
#[inline]
#[must_use]
pub fn with_scaling(mut self, f: impl FnOnce(&mut PixmapScaling)) -> Self {
f(&mut self.scaling);
self
}
#[inline]
pub fn set_scaling(&mut self, f: impl FnOnce(&mut PixmapScaling)) -> Action {
f(&mut self.scaling);
Action::RESIZE
}
}
impl Layout for Image {
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.scaling.size_rules(sizer, axis)
}
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
let scale_factor = cx.size_cx().scale_factor();
self.core.rect = self.scaling.align_rect(rect, scale_factor);
}
fn draw(&mut self, mut draw: DrawCx) {
if let Some(id) = self.handle.as_ref().map(|h| h.id()) {
draw.image(self.rect(), id);
}
}
}
}