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 {
87 #[doc(alias = "Image")]
89 pub fn image<'tex>(&self, texture: impl Into<TextureRef<'tex>>, size: [f32; 2]) {
90 self.image_config(texture, size).build()
91 }
92
93 #[doc(alias = "ImageButton")]
95 pub fn image_button<'tex>(
96 &self,
97 str_id: impl AsRef<str>,
98 texture: impl Into<TextureRef<'tex>>,
99 size: [f32; 2],
100 ) -> bool {
101 self.image_button_config(str_id.as_ref(), texture, size)
102 .build()
103 }
104
105 pub fn image_config<'ui, 'tex>(
107 &'ui self,
108 texture: impl Into<TextureRef<'tex>>,
109 size: [f32; 2],
110 ) -> Image<'ui, 'tex> {
111 Image::new(self, texture, size)
112 }
113
114 pub fn image_button_config<'ui, 'tex>(
116 &'ui self,
117 str_id: impl Into<Cow<'ui, str>>,
118 texture: impl Into<TextureRef<'tex>>,
119 size: [f32; 2],
120 ) -> ImageButton<'ui, 'tex> {
121 ImageButton::new(self, str_id, texture, size)
122 }
123}
124
125#[derive(Debug)]
127#[must_use]
128pub struct Image<'ui, 'tex> {
129 _ui: &'ui Ui,
130 texture: TextureRef<'tex>,
131 size: [f32; 2],
132 uv0: [f32; 2],
133 uv1: [f32; 2],
134 tint_color: [f32; 4],
135 border_color: [f32; 4],
136}
137
138impl<'ui, 'tex> Image<'ui, 'tex> {
139 pub fn new(ui: &'ui Ui, texture: impl Into<TextureRef<'tex>>, size: [f32; 2]) -> Self {
141 Self {
142 _ui: ui,
143 texture: texture.into(),
144 size,
145 uv0: [0.0, 0.0],
146 uv1: [1.0, 1.0],
147 tint_color: [1.0, 1.0, 1.0, 1.0],
148 border_color: [0.0, 0.0, 0.0, 0.0],
149 }
150 }
151
152 pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
154 self.uv0 = uv0;
155 self
156 }
157
158 pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
160 self.uv1 = uv1;
161 self
162 }
163
164 pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
170 self.tint_color = tint_color;
171 self
172 }
173
174 pub fn border_color(mut self, border_color: [f32; 4]) -> Self {
180 self.border_color = border_color;
181 self
182 }
183
184 pub fn build(self) {
186 assert_non_negative_finite_vec2("Image::build()", "size", self.size);
187 assert_finite_vec2("Image::build()", "uv0", self.uv0);
188 assert_finite_vec2("Image::build()", "uv1", self.uv1);
189 assert_finite_vec4("Image::build()", "tint_color", self.tint_color);
190 assert_finite_vec4("Image::build()", "border_color", self.border_color);
191
192 let size_vec: sys::ImVec2 = self.size.into();
193 let uv0_vec: sys::ImVec2 = self.uv0.into();
194 let uv1_vec: sys::ImVec2 = self.uv1.into();
195
196 let _border_size_token = (self.border_color[3] > 0.0).then(|| {
197 let current_size = self
198 ._ui
199 .run_with_bound_context(|| unsafe { self._ui.style().image_border_size() });
200 self._ui
201 .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
202 });
203 let _border_color_token = (self.border_color[3] > 0.0).then(|| {
204 self._ui
205 .push_style_color(StyleColor::Border, self.border_color)
206 });
207
208 if is_default_tint_color(self.tint_color) && is_transparent_color(self.border_color) {
209 self._ui.run_with_bound_context(|| unsafe {
210 sys::igImage(self.texture.raw(), size_vec, uv0_vec, uv1_vec)
211 })
212 } else {
213 self._ui.run_with_bound_context(|| unsafe {
214 sys::igImageWithBg(
215 self.texture.raw(),
216 size_vec,
217 uv0_vec,
218 uv1_vec,
219 im_vec4([0.0, 0.0, 0.0, 0.0]),
220 im_vec4(self.tint_color),
221 )
222 })
223 }
224 }
225
226 pub fn build_with_bg(self, bg_color: [f32; 4], tint_color: [f32; 4]) {
228 assert_non_negative_finite_vec2("Image::build_with_bg()", "size", self.size);
229 assert_finite_vec2("Image::build_with_bg()", "uv0", self.uv0);
230 assert_finite_vec2("Image::build_with_bg()", "uv1", self.uv1);
231 assert_finite_vec4("Image::build_with_bg()", "bg_color", bg_color);
232 assert_finite_vec4("Image::build_with_bg()", "tint_color", tint_color);
233 assert_finite_vec4("Image::build_with_bg()", "border_color", self.border_color);
234
235 let size_vec: sys::ImVec2 = self.size.into();
236 let uv0_vec: sys::ImVec2 = self.uv0.into();
237 let uv1_vec: sys::ImVec2 = self.uv1.into();
238
239 let _border_size_token = (self.border_color[3] > 0.0).then(|| {
240 let current_size = self
241 ._ui
242 .run_with_bound_context(|| unsafe { self._ui.style().image_border_size() });
243 self._ui
244 .push_style_var(StyleVar::ImageBorderSize(current_size.max(1.0)))
245 });
246 let _border_color_token = (self.border_color[3] > 0.0).then(|| {
247 self._ui
248 .push_style_color(StyleColor::Border, self.border_color)
249 });
250
251 self._ui.run_with_bound_context(|| unsafe {
252 sys::igImageWithBg(
253 self.texture.raw(),
254 size_vec,
255 uv0_vec,
256 uv1_vec,
257 im_vec4(bg_color),
258 im_vec4(tint_color),
259 )
260 });
261 }
262}
263
264#[derive(Debug)]
266#[must_use]
267pub struct ImageButton<'ui, 'tex> {
268 ui: &'ui Ui,
269 str_id: Cow<'ui, str>,
270 texture: TextureRef<'tex>,
271 size: [f32; 2],
272 uv0: [f32; 2],
273 uv1: [f32; 2],
274 bg_color: [f32; 4],
275 tint_color: [f32; 4],
276}
277
278impl<'ui, 'tex> ImageButton<'ui, 'tex> {
279 pub fn new(
281 ui: &'ui Ui,
282 str_id: impl Into<Cow<'ui, str>>,
283 texture: impl Into<TextureRef<'tex>>,
284 size: [f32; 2],
285 ) -> Self {
286 Self {
287 ui,
288 str_id: str_id.into(),
289 texture: texture.into(),
290 size,
291 uv0: [0.0, 0.0],
292 uv1: [1.0, 1.0],
293 bg_color: [0.0, 0.0, 0.0, 0.0],
294 tint_color: [1.0, 1.0, 1.0, 1.0],
295 }
296 }
297
298 pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
300 self.uv0 = uv0;
301 self
302 }
303
304 pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
306 self.uv1 = uv1;
307 self
308 }
309
310 pub fn bg_color(mut self, bg_color: [f32; 4]) -> Self {
312 self.bg_color = bg_color;
313 self
314 }
315
316 pub fn tint_color(mut self, tint_color: [f32; 4]) -> Self {
318 self.tint_color = tint_color;
319 self
320 }
321
322 pub fn build(self) -> bool {
324 assert_non_negative_finite_vec2("ImageButton::build()", "size", self.size);
325 assert_finite_vec2("ImageButton::build()", "uv0", self.uv0);
326 assert_finite_vec2("ImageButton::build()", "uv1", self.uv1);
327 assert_finite_vec4("ImageButton::build()", "bg_color", self.bg_color);
328 assert_finite_vec4("ImageButton::build()", "tint_color", self.tint_color);
329
330 let str_id_ptr = self.ui.scratch_txt(self.str_id.as_ref());
331 let size_vec: sys::ImVec2 = self.size.into();
332 let uv0_vec: sys::ImVec2 = self.uv0.into();
333 let uv1_vec: sys::ImVec2 = self.uv1.into();
334
335 self.ui.run_with_bound_context(|| unsafe {
336 sys::igImageButton(
337 str_id_ptr,
338 self.texture.raw(),
339 size_vec,
340 uv0_vec,
341 uv1_vec,
342 im_vec4(self.bg_color),
343 im_vec4(self.tint_color),
344 )
345 })
346 }
347}