1use crate::sys;
19use crate::texture::TextureRef;
20use crate::ui::Ui;
21use crate::{StyleColor, StyleVar};
22use std::borrow::Cow;
23
24fn assert_non_negative_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
25 assert!(
26 value[0].is_finite() && value[1].is_finite(),
27 "{caller} {name} must contain finite values"
28 );
29 assert!(
30 value[0] >= 0.0 && value[1] >= 0.0,
31 "{caller} {name} must contain non-negative values"
32 );
33}
34
35fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
36 assert!(
37 value[0].is_finite() && value[1].is_finite(),
38 "{caller} {name} must contain finite values"
39 );
40}
41
42fn assert_finite_vec4(caller: &str, name: &str, value: [f32; 4]) {
43 assert!(
44 value.iter().all(|component| component.is_finite()),
45 "{caller} {name} must contain finite values"
46 );
47}
48
49fn is_default_tint_color(color: [f32; 4]) -> bool {
50 color == [1.0, 1.0, 1.0, 1.0]
51}
52
53fn is_transparent_color(color: [f32; 4]) -> bool {
54 color == [0.0, 0.0, 0.0, 0.0]
55}
56
57fn im_vec4(value: [f32; 4]) -> sys::ImVec4 {
58 sys::ImVec4 {
59 x: value[0],
60 y: value[1],
61 z: value[2],
62 w: value[3],
63 }
64}
65
66impl Ui {
89 #[doc(alias = "Image")]
91 pub fn image<'tex>(&self, texture: impl Into<TextureRef<'tex>>, size: [f32; 2]) {
92 self.image_config(texture, size).build()
93 }
94
95 #[doc(alias = "ImageButton")]
97 pub fn image_button<'tex>(
98 &self,
99 str_id: impl AsRef<str>,
100 texture: impl Into<TextureRef<'tex>>,
101 size: [f32; 2],
102 ) -> bool {
103 self.image_button_config(str_id.as_ref(), texture, size)
104 .build()
105 }
106
107 pub fn image_config<'ui, 'tex>(
109 &'ui self,
110 texture: impl Into<TextureRef<'tex>>,
111 size: [f32; 2],
112 ) -> Image<'ui, 'tex> {
113 Image::new(self, texture, size)
114 }
115
116 pub fn image_button_config<'ui, 'tex>(
118 &'ui self,
119 str_id: impl Into<Cow<'ui, str>>,
120 texture: impl Into<TextureRef<'tex>>,
121 size: [f32; 2],
122 ) -> ImageButton<'ui, 'tex> {
123 ImageButton::new(self, str_id, texture, size)
124 }
125}
126
127#[derive(Debug)]
129#[must_use]
130pub struct Image<'ui, 'tex> {
131 _ui: &'ui Ui,
132 texture: TextureRef<'tex>,
133 size: [f32; 2],
134 uv0: [f32; 2],
135 uv1: [f32; 2],
136 tint_color: [f32; 4],
137 border_color: [f32; 4],
138}
139
140impl<'ui, 'tex> Image<'ui, 'tex> {
141 pub fn new(ui: &'ui Ui, texture: impl Into<TextureRef<'tex>>, size: [f32; 2]) -> Self {
143 Self {
144 _ui: ui,
145 texture: texture.into(),
146 size,
147 uv0: [0.0, 0.0],
148 uv1: [1.0, 1.0],
149 tint_color: [1.0, 1.0, 1.0, 1.0],
150 border_color: [0.0, 0.0, 0.0, 0.0],
151 }
152 }
153
154 pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
156 self.uv0 = uv0;
157 self
158 }
159
160 pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
162 self.uv1 = uv1;
163 self
164 }
165
166 pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
172 self.tint_color = tint_color;
173 self
174 }
175
176 pub fn border_color(mut self, border_color: [f32; 4]) -> Self {
182 self.border_color = border_color;
183 self
184 }
185
186 pub fn build(self) {
188 assert_non_negative_finite_vec2("Image::build()", "size", self.size);
189 assert_finite_vec2("Image::build()", "uv0", self.uv0);
190 assert_finite_vec2("Image::build()", "uv1", self.uv1);
191 assert_finite_vec4("Image::build()", "tint_color", self.tint_color);
192 assert_finite_vec4("Image::build()", "border_color", self.border_color);
193
194 let size_vec: sys::ImVec2 = self.size.into();
195 let uv0_vec: sys::ImVec2 = self.uv0.into();
196 let uv1_vec: sys::ImVec2 = self.uv1.into();
197
198 let _border_size_token = (self.border_color[3] > 0.0).then(|| {
199 let current_size = self
200 ._ui
201 .run_with_bound_context(|| unsafe { self._ui.style().image_border_size() });
202 self._ui
203 .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
204 });
205 let _border_color_token = (self.border_color[3] > 0.0).then(|| {
206 self._ui
207 .push_style_color(StyleColor::Border, self.border_color)
208 });
209 let texture = self._ui.run_with_bound_context(|| {
210 self._ui
211 .resolve_texture_ref(self.texture)
212 .unwrap_or_else(|error| panic!("Image::build() rejected texture: {error}"))
213 });
214
215 if is_default_tint_color(self.tint_color) && is_transparent_color(self.border_color) {
216 self._ui.run_with_bound_context(|| unsafe {
217 sys::igImage(texture, size_vec, uv0_vec, uv1_vec)
218 })
219 } else {
220 self._ui.run_with_bound_context(|| unsafe {
221 sys::igImageWithBg(
222 texture,
223 size_vec,
224 uv0_vec,
225 uv1_vec,
226 im_vec4([0.0, 0.0, 0.0, 0.0]),
227 im_vec4(self.tint_color),
228 )
229 })
230 }
231 }
232
233 pub fn build_with_bg(self, bg_color: [f32; 4], tint_color: [f32; 4]) {
235 assert_non_negative_finite_vec2("Image::build_with_bg()", "size", self.size);
236 assert_finite_vec2("Image::build_with_bg()", "uv0", self.uv0);
237 assert_finite_vec2("Image::build_with_bg()", "uv1", self.uv1);
238 assert_finite_vec4("Image::build_with_bg()", "bg_color", bg_color);
239 assert_finite_vec4("Image::build_with_bg()", "tint_color", tint_color);
240 assert_finite_vec4("Image::build_with_bg()", "border_color", self.border_color);
241
242 let size_vec: sys::ImVec2 = self.size.into();
243 let uv0_vec: sys::ImVec2 = self.uv0.into();
244 let uv1_vec: sys::ImVec2 = self.uv1.into();
245
246 let _border_size_token = (self.border_color[3] > 0.0).then(|| {
247 let current_size = self
248 ._ui
249 .run_with_bound_context(|| unsafe { self._ui.style().image_border_size() });
250 self._ui
251 .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
252 });
253 let _border_color_token = (self.border_color[3] > 0.0).then(|| {
254 self._ui
255 .push_style_color(StyleColor::Border, self.border_color)
256 });
257 let texture = self._ui.run_with_bound_context(|| {
258 self._ui
259 .resolve_texture_ref(self.texture)
260 .unwrap_or_else(|error| panic!("Image::build_with_bg() rejected texture: {error}"))
261 });
262
263 self._ui.run_with_bound_context(|| unsafe {
264 sys::igImageWithBg(
265 texture,
266 size_vec,
267 uv0_vec,
268 uv1_vec,
269 im_vec4(bg_color),
270 im_vec4(tint_color),
271 )
272 });
273 }
274}
275
276#[derive(Debug)]
278#[must_use]
279pub struct ImageButton<'ui, 'tex> {
280 ui: &'ui Ui,
281 str_id: Cow<'ui, str>,
282 texture: TextureRef<'tex>,
283 size: [f32; 2],
284 uv0: [f32; 2],
285 uv1: [f32; 2],
286 bg_color: [f32; 4],
287 tint_color: [f32; 4],
288}
289
290impl<'ui, 'tex> ImageButton<'ui, 'tex> {
291 pub fn new(
293 ui: &'ui Ui,
294 str_id: impl Into<Cow<'ui, str>>,
295 texture: impl Into<TextureRef<'tex>>,
296 size: [f32; 2],
297 ) -> Self {
298 Self {
299 ui,
300 str_id: str_id.into(),
301 texture: texture.into(),
302 size,
303 uv0: [0.0, 0.0],
304 uv1: [1.0, 1.0],
305 bg_color: [0.0, 0.0, 0.0, 0.0],
306 tint_color: [1.0, 1.0, 1.0, 1.0],
307 }
308 }
309
310 pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
312 self.uv0 = uv0;
313 self
314 }
315
316 pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
318 self.uv1 = uv1;
319 self
320 }
321
322 pub fn bg_color(mut self, bg_color: [f32; 4]) -> Self {
324 self.bg_color = bg_color;
325 self
326 }
327
328 pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
330 self.tint_color = tint_color;
331 self
332 }
333
334 pub fn build(self) -> bool {
336 assert_non_negative_finite_vec2("ImageButton::build()", "size", self.size);
337 assert_finite_vec2("ImageButton::build()", "uv0", self.uv0);
338 assert_finite_vec2("ImageButton::build()", "uv1", self.uv1);
339 assert_finite_vec4("ImageButton::build()", "bg_color", self.bg_color);
340 assert_finite_vec4("ImageButton::build()", "tint_color", self.tint_color);
341
342 let str_id_ptr = self.ui.scratch_txt(self.str_id.as_ref());
343 let size_vec: sys::ImVec2 = self.size.into();
344 let uv0_vec: sys::ImVec2 = self.uv0.into();
345 let uv1_vec: sys::ImVec2 = self.uv1.into();
346
347 self.ui.run_with_bound_context(|| {
348 let texture = self
349 .ui
350 .resolve_texture_ref(self.texture)
351 .unwrap_or_else(|error| panic!("ImageButton::build() rejected texture: {error}"));
352 unsafe {
353 sys::igImageButton(
354 str_id_ptr,
355 texture,
356 size_vec,
357 uv0_vec,
358 uv1_vec,
359 im_vec4(self.bg_color),
360 im_vec4(self.tint_color),
361 )
362 }
363 })
364 }
365}