1use kas::draw::{DrawShared, ImageHandle};
9use kas::layout::PixmapScaling;
10use kas::prelude::*;
11
12#[cfg(feature = "image")]
14#[derive(thiserror::Error, Debug)]
15pub enum ImageError {
16 #[error("IO error")]
17 IOError(#[from] std::io::Error),
18 #[error(transparent)]
19 Image(#[from] image::ImageError),
20 #[error("failed to allocate texture space for image")]
21 Allocation,
22}
23
24#[cfg(feature = "image")]
25impl From<kas::draw::AllocError> for ImageError {
26 fn from(_: kas::draw::AllocError) -> ImageError {
27 ImageError::Allocation
28 }
29}
30
31#[cfg(feature = "image")]
33pub type Result<T> = std::result::Result<T, ImageError>;
34
35#[impl_self]
36mod Image {
37 #[derive(Clone, Debug, Default)]
41 #[widget]
42 pub struct Image {
43 core: widget_core!(),
44 scaling: PixmapScaling,
45 handle: Option<ImageHandle>,
46 }
47
48 impl Self {
49 #[inline]
53 pub fn new(handle: ImageHandle, draw: &mut dyn DrawShared) -> Option<Self> {
54 draw.image_size(&handle).map(|size| {
55 let mut sprite = Self::default();
56 sprite.scaling.size = size.cast();
57 sprite.handle = Some(handle);
58 sprite
59 })
60 }
61
62 #[cfg(feature = "image")]
64 #[inline]
65 pub fn new_path<P: AsRef<std::path::Path>>(
66 path: P,
67 draw: &mut dyn DrawShared,
68 ) -> Result<Self> {
69 let mut sprite = Self::default();
70 sprite._load_path(path, draw)?;
71 Ok(sprite)
72 }
73
74 pub fn set(
78 &mut self,
79 cx: &mut EventState,
80 handle: ImageHandle,
81 draw: &mut dyn DrawShared,
82 ) -> bool {
83 if let Some(size) = draw.image_size(&handle) {
84 self.scaling.size = size.cast();
85 self.handle = Some(handle);
86 cx.resize(self);
87 true
88 } else {
89 false
90 }
91 }
92
93 #[cfg(feature = "image")]
97 pub fn load_path<P: AsRef<std::path::Path>>(
98 &mut self,
99 cx: &mut EventState,
100 path: P,
101 draw: &mut dyn DrawShared,
102 ) -> Result<()> {
103 self._load_path(path, draw).map(|_| {
104 cx.resize(self);
105 })
106 }
107
108 #[cfg(feature = "image")]
109 fn _load_path<P: AsRef<std::path::Path>>(
110 &mut self,
111 path: P,
112 draw: &mut dyn DrawShared,
113 ) -> Result<()> {
114 let image = image::ImageReader::open(path)?
115 .with_guessed_format()?
116 .decode()?;
117
118 let image = image.into_rgba8();
122 let size = image.dimensions();
123
124 let handle = draw.image_alloc(size)?;
125 draw.image_upload(&handle, &image, kas::draw::ImageFormat::Rgba8);
126
127 if let Some(old_handle) = self.handle.take() {
128 draw.image_free(old_handle);
129 }
130
131 self.scaling.size = size.cast();
132 self.handle = Some(handle);
133
134 Ok(())
135 }
136
137 pub fn clear(&mut self, cx: &mut EventState, draw: &mut dyn DrawShared) {
139 if let Some(handle) = self.handle.take() {
140 draw.image_free(handle);
141 cx.resize(self);
142 }
143 }
144
145 #[inline]
150 #[must_use]
151 pub fn with_scaling(mut self, f: impl FnOnce(&mut PixmapScaling)) -> Self {
152 f(&mut self.scaling);
153 self
154 }
155
156 #[inline]
161 pub fn set_scaling(&mut self, cx: &mut EventState, f: impl FnOnce(&mut PixmapScaling)) {
162 f(&mut self.scaling);
163 cx.resize(self);
165 }
166 }
167
168 impl Layout for Image {
169 fn rect(&self) -> Rect {
170 self.scaling.rect
171 }
172
173 fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
174 self.scaling.size_rules(sizer, axis)
175 }
176
177 fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
178 let align = hints.complete_default();
179 let scale_factor = cx.size_cx().scale_factor();
180 self.scaling.set_rect(rect, align, scale_factor);
181 }
182
183 fn draw(&self, mut draw: DrawCx) {
184 if let Some(id) = self.handle.as_ref().map(|h| h.id()) {
185 draw.image(self.rect(), id);
186 }
187 }
188 }
189
190 impl Tile for Self {
191 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
192 Role::Image
193 }
194 }
195}