Skip to content

Commit d95b8fa

Browse files
bors[bot]jacobsky
andauthored
Merge #729
729: Implemented some additional API functions related to crate::core_type… r=toasteater a=jacobsky …s::Color I needed some additional functionality from the Color class in godot and noticed that there were a lot of APIs that weren't accessible from rust. This PR plumbs in the functions from godot and adds some tests that can be used to sanity check them. This commit adds the API functionality and some basic sanity tests that I verified are passing in the engine. If additional tests are required, please let me know. Signed-off-by: Jacobsky <[email protected]> Co-authored-by: Jacobsky <[email protected]>
2 parents cb0f4fd + c608e1a commit d95b8fa

File tree

4 files changed

+229
-17
lines changed

4 files changed

+229
-17
lines changed

examples/spinning_cube/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl RustTest {
6464
if let Some(mat) = owner.get_surface_material(0) {
6565
let mat = unsafe { mat.assume_safe() };
6666
let mat = mat.cast::<SpatialMaterial>().expect("Incorrect material");
67-
mat.set_albedo(Color::rgba(self.time.cos().abs(), 0.0, 0.0, 1.0));
67+
mat.set_albedo(Color::from_rgba(self.time.cos().abs(), 0.0, 0.0, 1.0));
6868
}
6969
}
7070
}

gdnative-core/src/core_types/color.rs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::private::get_api;
22
use crate::sys;
33
use std::mem::transmute;
44

5+
use crate::core_types::GodotString;
56
/// RGBA color with 32 bits floating point components.
67
#[repr(C)]
78
#[derive(Copy, Clone, Debug, PartialEq)]
@@ -13,16 +14,44 @@ pub struct Color {
1314
}
1415

1516
impl Color {
17+
#[deprecated]
1618
#[inline]
1719
pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
1820
Color { r, g, b, a }
1921
}
2022

23+
#[deprecated]
2124
#[inline]
2225
pub fn rgb(r: f32, g: f32, b: f32) -> Color {
2326
Color { r, g, b, a: 1.0 }
2427
}
2528

29+
#[inline]
30+
pub fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
31+
Color { r, g, b, a }
32+
}
33+
34+
#[inline]
35+
pub fn from_rgb(r: f32, g: f32, b: f32) -> Color {
36+
Color { r, g, b, a: 1.0 }
37+
}
38+
39+
#[inline]
40+
pub fn from_hsv(h: f32, s: f32, v: f32) -> Color {
41+
Color::from_hsva(h, s, v, 1.0)
42+
}
43+
44+
#[inline]
45+
pub fn from_hsva(h: f32, s: f32, v: f32, a: f32) -> Color {
46+
let color = Color {
47+
r: 0.0,
48+
g: 0.0,
49+
b: 0.0,
50+
a: 0.0,
51+
};
52+
Color::from_sys(unsafe { (get_api().godot_color_from_hsv)(color.sys(), h, s, v, a) })
53+
}
54+
2655
#[inline]
2756
pub fn h(&self) -> f32 {
2857
unsafe { (get_api().godot_color_get_h)(self.sys()) }
@@ -48,6 +77,132 @@ impl Color {
4877
}
4978
}
5079

80+
#[inline]
81+
pub fn blend(&self, other: &Color) -> Color {
82+
Color::from_sys(unsafe { (get_api().godot_color_blend)(self.sys(), other.sys()) })
83+
}
84+
85+
#[inline]
86+
pub fn contrasted(&self) -> Color {
87+
Color::from_sys(unsafe { (get_api().godot_color_contrasted)(self.sys()) })
88+
}
89+
90+
#[inline]
91+
pub fn darkened(&self, amount: f32) -> Color {
92+
Color::from_sys(unsafe { (get_api().godot_color_darkened)(self.sys(), amount) })
93+
}
94+
95+
#[inline]
96+
pub fn gray(&self) -> f32 {
97+
// Implemented as described in godot docs
98+
(self.r + self.b + self.g) / 3.0
99+
}
100+
101+
#[inline]
102+
pub fn inverted(&self) -> Color {
103+
// Implementation as described in godot docs.
104+
Color {
105+
r: 1.0 - self.r,
106+
g: 1.0 - self.g,
107+
b: 1.0 - self.b,
108+
a: self.a,
109+
}
110+
}
111+
112+
#[inline]
113+
pub fn to_html(&self, with_alpha: bool) -> GodotString {
114+
GodotString::from_sys(unsafe { (get_api().godot_color_to_html)(self.sys(), with_alpha) })
115+
}
116+
117+
/// Returns the reverse of the RGBA32 byte representation for this color where each byte represents a component of the ABGR profile.
118+
/// This is the byte information used when storing this color as a part of a texture.
119+
/// # Endianness
120+
/// On big endian architecture this is stored in ABGR byte order
121+
/// On little endian machines this is stored in RGBA byte order
122+
/// # Example
123+
/// `0x00FF7FFF` would be the equivalent to `Color::from_rgba(1.0, 0.5, 1.0, 0.0)`
124+
#[inline]
125+
pub fn to_abgr32(&self) -> u32 {
126+
((self.a * 255.0) as u32) << 24
127+
| ((self.b * 255.0) as u32) << 16
128+
| ((self.g * 255.0) as u32) << 8
129+
| (self.r * 255.0) as u32
130+
}
131+
132+
/// Returns the reverse of the RGBA64 byte representation for this color where each word represents represents a component of the ABGR profile.
133+
/// This is the byte information used when storing this color as a part of a texture.
134+
/// # Endianness
135+
/// On big endian architecture this is stored in ABGR word order
136+
/// On little endian machines this is stored in RGBA word order
137+
/// # Example
138+
/// `0x0000FFFF7FFFFFFF` would be the equivalent to `Color::from_rgba(0.0, 1.0, 0.5, 1.0)`
139+
#[inline]
140+
pub fn to_abgr64(&self) -> u64 {
141+
((self.a * 65535.0) as u64) << 48
142+
| ((self.b * 65535.0) as u64) << 32
143+
| ((self.g * 65535.0) as u64) << 16
144+
| ((self.r * 65535.0) as u64)
145+
}
146+
147+
/// Returns the ARGB32 format representation representation for this color where each byte represents a component of the ARGB profile.
148+
/// This is the byte information used when storing this color as a part of a texture.
149+
/// # Endianness
150+
/// On big endian architecture this is stored in the order ARGB byte order
151+
/// On little endian machines this is stored in the order BGRA byte order
152+
/// `0x0000FFFF7FFFFFFF` would be the equivalent to `Color::from_rgba(1.0, 0.5, 1.0, 0.0)`
153+
#[inline]
154+
pub fn to_argb32(&self) -> u32 {
155+
((self.a * 255.0) as u32) << 24
156+
| ((self.r * 255.0) as u32) << 16
157+
| ((self.g * 255.0) as u32) << 8
158+
| (self.b * 255.0) as u32
159+
}
160+
161+
/// Returns the ARGB64 format representation for this color where each word represents a component of the ARGB profile.
162+
/// This is the byte information used when storing this color as a part of a texture.
163+
/// # Endianness
164+
/// On big endian architecture this is stored in the order ARGB word order
165+
/// On little endian machines this is stored in the order BGRA word order
166+
/// # Example
167+
/// `0x0000FFFF7FFFFFFF` would be the equivalent to `Color::from_rgba(1.0, 0.5, 1.0, 0.0)`
168+
#[inline]
169+
pub fn to_argb64(&self) -> u64 {
170+
((self.a * 65535.0) as u64) << 48
171+
| ((self.r * 65535.0) as u64) << 32
172+
| ((self.g * 65535.0) as u64) << 16
173+
| ((self.b * 65535.0) as u64)
174+
}
175+
176+
/// Returns the OpenGL Texture format byte representation for this color where each byte represents a component of the RGBA profile.
177+
/// This is the byte information used when storing this color as a part of a texture.
178+
/// # Endianness
179+
/// On big endian architecture this is stored in RGBA byte order
180+
/// On little endian machines this is stored in ABGR byte order
181+
/// # Example
182+
/// `0x00FF7FFF` would be the equivalent to `Color::from_rgba(0.0, 1.0, 0.5, 1.0)`
183+
#[inline]
184+
pub fn to_rgba32(&self) -> u32 {
185+
((self.r * 255.0) as u32) << 24
186+
| ((self.g * 255.0) as u32) << 16
187+
| ((self.b * 255.0) as u32) << 8
188+
| (self.a * 255.0) as u32
189+
}
190+
191+
/// Returns the OpenGL Texture format byte representation for this color where each byte represents a component of the RGBA profile.
192+
/// This is the byte information used when storing this color as a part of a texture.
193+
/// # Endianness
194+
/// On big endian architecture this is stored in RGBA word order
195+
/// On little endian machines this is stored in ABGR word order
196+
/// # Example
197+
/// `0x0000FFFF7FFFFFFF` would be the equivalent to `Color::from_rgba(0.0, 1.0, 0.5, 1.0)`
198+
#[inline]
199+
pub fn to_rgba64(&self) -> u64 {
200+
((self.r * 65535.0) as u64) << 48
201+
| ((self.g * 65535.0) as u64) << 32
202+
| ((self.b * 65535.0) as u64) << 16
203+
| ((self.a * 65535.0) as u64)
204+
}
205+
51206
#[doc(hidden)]
52207
#[inline]
53208
pub fn sys(&self) -> &sys::godot_color {
@@ -72,3 +227,60 @@ fn color_repr() {
72227
use std::mem::size_of;
73228
assert_eq!(size_of::<Color>(), size_of::<sys::godot_color>());
74229
}
230+
231+
#[test]
232+
fn color_to_pixel_color_formats() {
233+
let color = Color::from_rgba(1.0, 0.5, 1.0, 0.0);
234+
assert_eq!(0xFF7FFF00, color.to_rgba32());
235+
assert_eq!(0xFFFF7FFFFFFF0000, color.to_rgba64());
236+
assert_eq!(0x00FF7FFF, color.to_abgr32());
237+
assert_eq!(0x0000FFFF7FFFFFFF, color.to_abgr64());
238+
assert_eq!(0x00FF7FFF, color.to_argb32());
239+
assert_eq!(0x0000FFFF7FFFFFFF, color.to_argb64());
240+
}
241+
242+
godot_test!(test_color {
243+
// Test to_html
244+
assert_eq!("ffffffff", Color::from_rgba(1.0, 1.0, 1.0, 1.0).to_html(true).to_string());
245+
assert_eq!("ffffff", Color::from_rgba(1.0, 1.0, 1.0, 1.0).to_html(false).to_string());
246+
assert_eq!("80ffffff", Color::from_rgba(1.0, 1.0, 1.0, 0.5).to_html(true).to_string());
247+
assert_eq!("ffffff", Color::from_rgba(1.0, 1.0, 1.0, 0.5).to_html(false).to_string());
248+
assert_eq!("ff8000", Color::from_rgb(1.0, 0.5, 0.0).to_html(false).to_string());
249+
assert_eq!("ff0080ff", Color::from_rgb(0.0, 0.5, 1.0).to_html(true).to_string());
250+
// Test Gray
251+
// String comparison due to non-trivial way to truncate floats
252+
use crate::core_types::IsEqualApprox;
253+
assert!(0.4f32.is_equal_approx(Color::from_rgb(0.2, 0.4, 0.6).gray()));
254+
assert!(0.5f32.is_equal_approx(Color::from_rgb(0.1, 0.5, 0.9).gray()));
255+
assert!(0.9f32.is_equal_approx(Color::from_rgb(1.0, 1.0, 0.7).gray()));
256+
assert!(0.42f32.is_equal_approx(Color::from_rgb(0.6, 0.6, 0.06).gray()));
257+
// Test invert
258+
let inverted = Color::from_rgb(1.0, 1.0,1.0).inverted();
259+
assert!(0f32.is_equal_approx(inverted.r));
260+
assert!(0f32.is_equal_approx(inverted.g));
261+
assert!(0f32.is_equal_approx(inverted.b));
262+
263+
let inverted = Color::from_rgb(0.95, 0.95,0.95).inverted();
264+
assert!(0.05f32.is_equal_approx(inverted.r));
265+
assert!(0.05f32.is_equal_approx(inverted.g));
266+
assert!(0.05f32.is_equal_approx(inverted.b));
267+
268+
let inverted = Color::from_rgb(0.05, 0.95,0.55).inverted();
269+
assert!(0.95f32.is_equal_approx(inverted.r));
270+
assert!(0.05f32.is_equal_approx(inverted.g));
271+
assert!(0.45f32.is_equal_approx(inverted.b));
272+
273+
// This is a series of sanity checks to test that the API bounds work properly.
274+
let hsv_color = Color::from_hsv(0.75, 0.5, 0.25);
275+
let color = Color::from_hsva(0.75, 0.5, 0.25, 1.0);
276+
assert_eq!(hsv_color, color);
277+
let color = Color::from_rgb(0.75, 0.5, 0.25);
278+
assert_eq!(Color::from_rgb(0.25, 0.5, 0.75), color.inverted());
279+
// Following results were derived from the godot engine code based on the RGB values of 0.75, 0.5, 0.25 respectively.
280+
assert_eq!(Color::from_rgb(0.25, 0.00, 0.75), color.contrasted());
281+
assert_eq!(Color::from_rgba(0.60, 0.40, 0.20, 1.0), color.darkened(0.20));
282+
// Check that the blend values are correct.
283+
let color = Color::from_rgba(0.0, 1.0, 0.5, 1.0);
284+
let other_color = Color::from_rgba(1.0, 0.0, 0.5, 1.0);
285+
assert_eq!(Color::from_rgba(1.0, 0.0, 0.5, 1.0), color.blend(&other_color));
286+
});

gdnative-core/src/core_types/color_array.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ godot_test!(
99
use crate::NewRef as _;
1010

1111
let arr = ColorArray::from_vec(vec![
12-
Color::rgb(1.0, 0.0, 0.0),
13-
Color::rgb(0.0, 1.0, 0.0),
14-
Color::rgb(0.0, 0.0, 1.0),
12+
Color::from_rgb(1.0, 0.0, 0.0),
13+
Color::from_rgb(0.0, 1.0, 0.0),
14+
Color::from_rgb(0.0, 0.0, 1.0),
1515
]);
1616

1717
let original_read = {
1818
let read = arr.read();
1919
assert_eq!(&[
20-
Color::rgb(1.0, 0.0, 0.0),
21-
Color::rgb(0.0, 1.0, 0.0),
22-
Color::rgb(0.0, 0.0, 1.0),
20+
Color::from_rgb(1.0, 0.0, 0.0),
21+
Color::from_rgb(0.0, 1.0, 0.0),
22+
Color::from_rgb(0.0, 0.0, 1.0),
2323
], read.as_slice());
2424
read.clone()
2525
};
@@ -34,25 +34,25 @@ godot_test!(
3434
}
3535
}
3636

37-
assert_eq!(Color::rgb(1.0, 0.0, 1.0), cow_arr.get(0));
38-
assert_eq!(Color::rgb(0.0, 1.0, 1.0), cow_arr.get(1));
39-
assert_eq!(Color::rgb(0.0, 0.0, 1.0), cow_arr.get(2));
37+
assert_eq!(Color::from_rgb(1.0, 0.0, 1.0), cow_arr.get(0));
38+
assert_eq!(Color::from_rgb(0.0, 1.0, 1.0), cow_arr.get(1));
39+
assert_eq!(Color::from_rgb(0.0, 0.0, 1.0), cow_arr.get(2));
4040

4141
// the write shouldn't have affected the original array
4242
assert_eq!(&[
43-
Color::rgb(1.0, 0.0, 0.0),
44-
Color::rgb(0.0, 1.0, 0.0),
45-
Color::rgb(0.0, 0.0, 1.0),
43+
Color::from_rgb(1.0, 0.0, 0.0),
44+
Color::from_rgb(0.0, 1.0, 0.0),
45+
Color::from_rgb(0.0, 0.0, 1.0),
4646
], original_read.as_slice());
4747
}
4848
);
4949

5050
godot_test!(
5151
test_color_array_debug {
5252
let arr = ColorArray::from_vec(vec![
53-
Color::rgb(1.0, 0.0, 0.0),
54-
Color::rgb(0.0, 1.0, 0.0),
55-
Color::rgb(0.0, 0.0, 1.0),
53+
Color::from_rgb(1.0, 0.0, 0.0),
54+
Color::from_rgb(0.0, 1.0, 0.0),
55+
Color::from_rgb(0.0, 0.0, 1.0),
5656
]);
5757

5858
assert_eq!(format!("{:?}", arr), "[Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }, Color { r: 0.0, g: 1.0, b: 0.0, a: 1.0 }, Color { r: 0.0, g: 0.0, b: 1.0, a: 1.0 }]");

test/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub extern "C" fn run_tests(
2222

2323
status &= gdnative::core_types::dictionary::test_dictionary();
2424
// status &= gdnative::test_dictionary_clone_clear();
25-
25+
status &= gdnative::core_types::test_color();
2626
status &= gdnative::core_types::test_array();
2727
status &= gdnative::core_types::test_array_debug();
2828
// status &= gdnative::test_array_clone_clear();

0 commit comments

Comments
 (0)