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 {
94 #[doc(alias = "Image")]
96 pub fn image<'tex>(&self, texture: impl Into<TextureRef<'tex>>, size: [f32; 2]) {
97 self.image_config(texture, size).build()
98 }
99
100 #[doc(alias = "ImageButton")]
102 pub fn image_button<'tex>(
103 &self,
104 str_id: impl AsRef<str>,
105 texture: impl Into<TextureRef<'tex>>,
106 size: [f32; 2],
107 ) -> bool {
108 self.image_button_config(str_id.as_ref(), texture, size)
109 .build()
110 }
111
112 pub fn image_config<'ui, 'tex>(
114 &'ui self,
115 texture: impl Into<TextureRef<'tex>>,
116 size: [f32; 2],
117 ) -> Image<'ui, 'tex> {
118 Image::new(self, texture, size)
119 }
120
121 pub fn image_button_config<'ui, 'tex>(
123 &'ui self,
124 str_id: impl Into<Cow<'ui, str>>,
125 texture: impl Into<TextureRef<'tex>>,
126 size: [f32; 2],
127 ) -> ImageButton<'ui, 'tex> {
128 ImageButton::new(self, str_id, texture, size)
129 }
130}
131
132#[derive(Debug)]
134#[must_use]
135pub struct Image<'ui, 'tex> {
136 _ui: &'ui Ui,
137 texture: TextureRef<'tex>,
138 size: [f32; 2],
139 uv0: [f32; 2],
140 uv1: [f32; 2],
141 tint_color: [f32; 4],
142 border_color: [f32; 4],
143}
144
145impl<'ui, 'tex> Image<'ui, 'tex> {
146 pub fn new(ui: &'ui Ui, texture: impl Into<TextureRef<'tex>>, size: [f32; 2]) -> Self {
148 Self {
149 _ui: ui,
150 texture: texture.into(),
151 size,
152 uv0: [0.0, 0.0],
153 uv1: [1.0, 1.0],
154 tint_color: [1.0, 1.0, 1.0, 1.0],
155 border_color: [0.0, 0.0, 0.0, 0.0],
156 }
157 }
158
159 pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
161 self.uv0 = uv0;
162 self
163 }
164
165 pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
167 self.uv1 = uv1;
168 self
169 }
170
171 pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
177 self.tint_color = tint_color;
178 self
179 }
180
181 pub fn border_color(mut self, border_color: [f32; 4]) -> Self {
187 self.border_color = border_color;
188 self
189 }
190
191 pub fn build(self) {
193 assert_non_negative_finite_vec2("Image::build()", "size", self.size);
194 assert_finite_vec2("Image::build()", "uv0", self.uv0);
195 assert_finite_vec2("Image::build()", "uv1", self.uv1);
196 assert_finite_vec4("Image::build()", "tint_color", self.tint_color);
197 assert_finite_vec4("Image::build()", "border_color", self.border_color);
198
199 let size_vec: sys::ImVec2 = self.size.into();
200 let uv0_vec: sys::ImVec2 = self.uv0.into();
201 let uv1_vec: sys::ImVec2 = self.uv1.into();
202
203 let _border_size_token = (self.border_color[3] > 0.0).then(|| {
204 let current_size = self
205 ._ui
206 .run_with_bound_context(|| unsafe { self._ui.style().image_border_size() });
207 self._ui
208 .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
209 });
210 let _border_color_token = (self.border_color[3] > 0.0).then(|| {
211 self._ui
212 .push_style_color(StyleColor::Border, self.border_color)
213 });
214 let texture = self._ui.run_with_bound_context(|| {
215 self._ui
216 .resolve_texture_ref(self.texture)
217 .unwrap_or_else(|error| panic!("Image::build() rejected texture: {error}"))
218 });
219
220 if is_default_tint_color(self.tint_color) && is_transparent_color(self.border_color) {
221 self._ui.run_with_bound_context(|| unsafe {
222 sys::igImage(texture, size_vec, uv0_vec, uv1_vec)
223 })
224 } else {
225 self._ui.run_with_bound_context(|| unsafe {
226 sys::igImageWithBg(
227 texture,
228 size_vec,
229 uv0_vec,
230 uv1_vec,
231 im_vec4([0.0, 0.0, 0.0, 0.0]),
232 im_vec4(self.tint_color),
233 )
234 })
235 }
236 }
237
238 pub fn build_with_bg(self, bg_color: [f32; 4], tint_color: [f32; 4]) {
240 assert_non_negative_finite_vec2("Image::build_with_bg()", "size", self.size);
241 assert_finite_vec2("Image::build_with_bg()", "uv0", self.uv0);
242 assert_finite_vec2("Image::build_with_bg()", "uv1", self.uv1);
243 assert_finite_vec4("Image::build_with_bg()", "bg_color", bg_color);
244 assert_finite_vec4("Image::build_with_bg()", "tint_color", tint_color);
245 assert_finite_vec4("Image::build_with_bg()", "border_color", self.border_color);
246
247 let size_vec: sys::ImVec2 = self.size.into();
248 let uv0_vec: sys::ImVec2 = self.uv0.into();
249 let uv1_vec: sys::ImVec2 = self.uv1.into();
250
251 let _border_size_token = (self.border_color[3] > 0.0).then(|| {
252 let current_size = self
253 ._ui
254 .run_with_bound_context(|| unsafe { self._ui.style().image_border_size() });
255 self._ui
256 .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
257 });
258 let _border_color_token = (self.border_color[3] > 0.0).then(|| {
259 self._ui
260 .push_style_color(StyleColor::Border, self.border_color)
261 });
262 let texture = self._ui.run_with_bound_context(|| {
263 self._ui
264 .resolve_texture_ref(self.texture)
265 .unwrap_or_else(|error| panic!("Image::build_with_bg() rejected texture: {error}"))
266 });
267
268 self._ui.run_with_bound_context(|| unsafe {
269 sys::igImageWithBg(
270 texture,
271 size_vec,
272 uv0_vec,
273 uv1_vec,
274 im_vec4(bg_color),
275 im_vec4(tint_color),
276 )
277 });
278 }
279}
280
281#[derive(Debug)]
283#[must_use]
284pub struct ImageButton<'ui, 'tex> {
285 ui: &'ui Ui,
286 str_id: Cow<'ui, str>,
287 texture: TextureRef<'tex>,
288 size: [f32; 2],
289 uv0: [f32; 2],
290 uv1: [f32; 2],
291 bg_color: [f32; 4],
292 tint_color: [f32; 4],
293}
294
295impl<'ui, 'tex> ImageButton<'ui, 'tex> {
296 pub fn new(
298 ui: &'ui Ui,
299 str_id: impl Into<Cow<'ui, str>>,
300 texture: impl Into<TextureRef<'tex>>,
301 size: [f32; 2],
302 ) -> Self {
303 Self {
304 ui,
305 str_id: str_id.into(),
306 texture: texture.into(),
307 size,
308 uv0: [0.0, 0.0],
309 uv1: [1.0, 1.0],
310 bg_color: [0.0, 0.0, 0.0, 0.0],
311 tint_color: [1.0, 1.0, 1.0, 1.0],
312 }
313 }
314
315 pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
317 self.uv0 = uv0;
318 self
319 }
320
321 pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
323 self.uv1 = uv1;
324 self
325 }
326
327 pub fn bg_color(mut self, bg_color: [f32; 4]) -> Self {
329 self.bg_color = bg_color;
330 self
331 }
332
333 pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
335 self.tint_color = tint_color;
336 self
337 }
338
339 pub fn build(self) -> bool {
341 assert_non_negative_finite_vec2("ImageButton::build()", "size", self.size);
342 assert_finite_vec2("ImageButton::build()", "uv0", self.uv0);
343 assert_finite_vec2("ImageButton::build()", "uv1", self.uv1);
344 assert_finite_vec4("ImageButton::build()", "bg_color", self.bg_color);
345 assert_finite_vec4("ImageButton::build()", "tint_color", self.tint_color);
346
347 let str_id_ptr = self.ui.scratch_txt(self.str_id.as_ref());
348 let size_vec: sys::ImVec2 = self.size.into();
349 let uv0_vec: sys::ImVec2 = self.uv0.into();
350 let uv1_vec: sys::ImVec2 = self.uv1.into();
351
352 self.ui.run_with_bound_context(|| {
353 let texture = self
354 .ui
355 .resolve_texture_ref(self.texture)
356 .unwrap_or_else(|error| panic!("ImageButton::build() rejected texture: {error}"));
357 unsafe {
358 sys::igImageButton(
359 str_id_ptr,
360 texture,
361 size_vec,
362 uv0_vec,
363 uv1_vec,
364 im_vec4(self.bg_color),
365 im_vec4(self.tint_color),
366 )
367 }
368 })
369 }
370}