diff --git a/core/math/color.cpp b/core/math/color.cpp
index f4b89031578c..c99801c0484b 100644
--- a/core/math/color.cpp
+++ b/core/math/color.cpp
@@ -321,6 +321,20 @@ void Color::set_hsl(float p_h, float p_s, float p_l, float p_alpha) {
}
}
+void Color::set_ok_hsv(float p_h, float p_s, float p_v, float p_alpha) {
+ ok_color::HSV hsv;
+ hsv.h = p_h;
+ hsv.s = p_s;
+ hsv.v = p_v;
+ ok_color new_ok_color;
+ ok_color::RGB rgb = new_ok_color.okhsv_to_srgb(hsv);
+ Color c = Color(rgb.r, rgb.g, rgb.b, p_alpha).clamp();
+ r = c.r;
+ g = c.g;
+ b = c.b;
+ a = c.a;
+}
+
void Color::set_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) {
ok_color::HSL hsl;
hsl.h = p_h;
@@ -669,13 +683,71 @@ Color Color::operator-() const {
1.0f - a);
}
+Color Color::from_ok_hsv(float p_h, float p_s, float p_v, float p_alpha) {
+ Color c;
+ c.set_ok_hsv(p_h, p_s, p_v, p_alpha);
+ return c;
+}
+
Color Color::from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) {
Color c;
c.set_ok_hsl(p_h, p_s, p_l, p_alpha);
return c;
}
+float Color::get_ok_hsv_h() const {
+ if (r == g && g == b) {
+ return 0.0f;
+ }
+ ok_color::RGB rgb;
+ rgb.r = r;
+ rgb.g = g;
+ rgb.b = b;
+ ok_color new_ok_color;
+ ok_color::HSV ok_hsv = new_ok_color.srgb_to_okhsv(rgb);
+ if (Math::is_nan(ok_hsv.h)) {
+ return 0.0f;
+ }
+ return CLAMP(ok_hsv.h, 0.0f, 1.0f);
+}
+
+float Color::get_ok_hsv_s() const {
+ if (r == g && g == b) {
+ return 0.0f;
+ }
+ ok_color::RGB rgb;
+ rgb.r = r;
+ rgb.g = g;
+ rgb.b = b;
+ ok_color new_ok_color;
+ ok_color::HSV ok_hsv = new_ok_color.srgb_to_okhsv(rgb);
+ if (Math::is_nan(ok_hsv.s)) {
+ return 0.0f;
+ }
+ return CLAMP(ok_hsv.s, 0.0f, 1.0f);
+}
+
+float Color::get_ok_hsv_v() const {
+ if (r == g && g == b) {
+ // Workaround for edge cases causing NaN.
+ return get_ok_hsl_l();
+ }
+ ok_color::RGB rgb;
+ rgb.r = r;
+ rgb.g = g;
+ rgb.b = b;
+ ok_color new_ok_color;
+ ok_color::HSV ok_hsv = new_ok_color.srgb_to_okhsv(rgb);
+ if (Math::is_nan(ok_hsv.v)) {
+ return 0.0f;
+ }
+ return CLAMP(ok_hsv.v, 0.0f, 1.0f);
+}
+
float Color::get_ok_hsl_h() const {
+ if (r == g && g == b) {
+ return 0.0f;
+ }
ok_color::RGB rgb;
rgb.r = r;
rgb.g = g;
@@ -689,6 +761,9 @@ float Color::get_ok_hsl_h() const {
}
float Color::get_ok_hsl_s() const {
+ if (r == g && g == b) {
+ return 0.0f;
+ }
ok_color::RGB rgb;
rgb.r = r;
rgb.g = g;
diff --git a/core/math/color.h b/core/math/color.h
index 4a056335c1c4..20bb624336f1 100644
--- a/core/math/color.h
+++ b/core/math/color.h
@@ -61,6 +61,10 @@ struct _NO_DISCARD_ Color {
float get_hsl_s() const;
float get_hsl_l() const;
void set_hsl(float p_h, float p_s, float p_l, float p_alpha = 1.0f);
+ float get_ok_hsv_h() const;
+ float get_ok_hsv_s() const;
+ float get_ok_hsv_v() const;
+ void set_ok_hsv(float p_h, float p_s, float p_v, float p_alpha = 1.0f);
float get_ok_hsl_h() const;
float get_ok_hsl_s() const;
float get_ok_hsl_l() const;
@@ -203,6 +207,7 @@ struct _NO_DISCARD_ Color {
static Color from_string(const String &p_string, const Color &p_default);
static Color from_hsv(float p_h, float p_s, float p_v, float p_alpha = 1.0f);
static Color from_hsl(float p_h, float p_s, float p_l, float p_alpha = 1.0f);
+ static Color from_ok_hsv(float p_h, float p_s, float p_v, float p_alpha = 1.0f);
static Color from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha = 1.0f);
static Color from_rgbe9995(uint32_t p_rgbe);
@@ -225,6 +230,9 @@ struct _NO_DISCARD_ Color {
_FORCE_INLINE_ void set_hsl_h(float p_h) { set_hsl(p_h, get_hsl_s(), get_hsl_l(), a); }
_FORCE_INLINE_ void set_hsl_s(float p_s) { set_hsl(get_hsl_h(), p_s, get_hsl_l(), a); }
_FORCE_INLINE_ void set_hsl_l(float p_l) { set_hsl(get_hsl_h(), get_hsl_s(), p_l, a); }
+ _FORCE_INLINE_ void set_ok_hsv_h(float p_h) { set_ok_hsv(p_h, get_ok_hsv_s(), get_ok_hsv_v(), a); }
+ _FORCE_INLINE_ void set_ok_hsv_s(float p_s) { set_ok_hsv(get_ok_hsv_h(), p_s, get_ok_hsv_v(), a); }
+ _FORCE_INLINE_ void set_ok_hsv_v(float p_v) { set_ok_hsv(get_ok_hsv_h(), get_ok_hsv_s(), p_v, a); }
_FORCE_INLINE_ void set_ok_hsl_h(float p_h) { set_ok_hsl(p_h, get_ok_hsl_s(), get_ok_hsl_l(), a); }
_FORCE_INLINE_ void set_ok_hsl_s(float p_s) { set_ok_hsl(get_ok_hsl_h(), p_s, get_ok_hsl_l(), a); }
_FORCE_INLINE_ void set_ok_hsl_l(float p_l) { set_ok_hsl(get_ok_hsl_h(), get_ok_hsl_s(), p_l, a); }
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index e22970ef5c9b..7825c7968216 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2003,6 +2003,7 @@ static void _register_variant_builtin_methods() {
bind_static_method(Color, from_string, sarray("str", "default"), varray());
bind_static_method(Color, from_hsv, sarray("h", "s", "v", "alpha"), varray(1.0));
bind_static_method(Color, from_hsl, sarray("h", "s", "l", "alpha"), varray(1.0));
+ bind_static_method(Color, from_ok_hsv, sarray("h", "s", "v", "alpha"), varray(1.0));
bind_static_method(Color, from_ok_hsl, sarray("h", "s", "l", "alpha"), varray(1.0));
bind_static_method(Color, from_rgbe9995, sarray("rgbe"), varray());
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index ce035f5f7ac2..2369921e3b2e 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -143,6 +143,14 @@ void register_named_setters_getters() {
REGISTER_MEMBER(Color, hsl_h);
REGISTER_MEMBER(Color, hsl_s);
REGISTER_MEMBER(Color, hsl_l);
+
+ REGISTER_MEMBER(Color, ok_hsv_h);
+ REGISTER_MEMBER(Color, ok_hsv_s);
+ REGISTER_MEMBER(Color, ok_hsv_v);
+
+ REGISTER_MEMBER(Color, ok_hsl_h);
+ REGISTER_MEMBER(Color, ok_hsl_s);
+ REGISTER_MEMBER(Color, ok_hsl_l);
}
void unregister_named_setters_getters() {
diff --git a/core/variant/variant_setget.h b/core/variant/variant_setget.h
index db6e2738172f..77c18ea11178 100644
--- a/core/variant/variant_setget.h
+++ b/core/variant/variant_setget.h
@@ -348,6 +348,10 @@ SETGET_NUMBER_STRUCT_FUNC(Color, double, hsl_h, set_hsl_h, get_hsl_h)
SETGET_NUMBER_STRUCT_FUNC(Color, double, hsl_s, set_hsl_s, get_hsl_s)
SETGET_NUMBER_STRUCT_FUNC(Color, double, hsl_l, set_hsl_l, get_hsl_l)
+SETGET_NUMBER_STRUCT_FUNC(Color, double, ok_hsv_h, set_ok_hsv_h, get_ok_hsv_h)
+SETGET_NUMBER_STRUCT_FUNC(Color, double, ok_hsv_s, set_ok_hsv_s, get_ok_hsv_s)
+SETGET_NUMBER_STRUCT_FUNC(Color, double, ok_hsv_v, set_ok_hsv_v, get_ok_hsv_v)
+
SETGET_NUMBER_STRUCT_FUNC(Color, double, ok_hsl_h, set_ok_hsl_h, get_ok_hsl_h)
SETGET_NUMBER_STRUCT_FUNC(Color, double, ok_hsl_s, set_ok_hsl_s, get_ok_hsl_s)
SETGET_NUMBER_STRUCT_FUNC(Color, double, ok_hsl_l, set_ok_hsl_l, get_ok_hsl_l)
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index 0592f5287ba2..98eee5d9af42 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -195,6 +195,24 @@
[/codeblocks]
+
+
+
+
+
+
+
+ Constructs a color from an [url=https://bottosson.github.io/posts/colorpicker/]OK HSV profile[/url]. The hue ([param h]), saturation ([param s]), and value ([param v]) are typically between 0.0 and 1.0.
+ [codeblocks]
+ [gdscript]
+ var color = Color.from_ok_hsv(0.58, 0.5, 0.79, 0.8)
+ [/gdscript]
+ [csharp]
+ var color = Color.FromOkHsv(0.58f, 0.5f, 0.79f, 0.8f);
+ [/csharp]
+ [/codeblocks]
+
+
@@ -520,6 +538,24 @@
The HSL saturation of this color, on the range 0 to 1.
+
+ The OK HSL hue of this color, on the range 0 to 1.
+
+
+ The OK HSL lightness of this color, on the range 0 to 1.
+
+
+ The OK HSL saturation of this color, on the range 0 to 1.
+
+
+ The OK HSV hue of this color, on the range 0 to 1.
+
+
+ The OK HSV saturation of this color, on the range 0 to 1.
+
+
+ The OK HSV value (brightness) of this color, on the range 0 to 1.
+
The color's red component, typically on the range of 0 to 1.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index 98e2556dbbfc..3fce435ad32d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -1062,6 +1062,32 @@ private static int ParseCol8(ReadOnlySpan str, int index)
return ParseCol4(str, index) * 16 + ParseCol4(str, index + 1);
}
+ ///
+ /// Constructs a color from an OK HSV profile. The ,
+ /// , and are typically
+ /// between 0.0 and 1.0.
+ ///
+ /// The OK HSV hue, typically on the range of 0 to 1.
+ /// The OK HSV saturation, typically on the range of 0 to 1.
+ /// The OK HSV value, typically on the range of 0 to 1.
+ /// The alpha (transparency) value, typically on the range of 0 to 1.
+ /// The constructed color.
+ public static Color FromOkHsv(float hue, float saturation, float value, float alpha = 1.0f)
+ {
+ return NativeFuncs.godotsharp_color_from_ok_hsv(hue, saturation, value, alpha);
+ }
+
+ ///
+ /// Converts a color to OK HSV values.
+ ///
+ /// Output parameter for the OK HSV hue.
+ /// Output parameter for the OK HSV saturation.
+ /// Output parameter for the OK HSV value.
+ public readonly void ToOkHsv(out float hue, out float saturation, out float value)
+ {
+ NativeFuncs.godotsharp_color_to_ok_hsv(this, out hue, out saturation, out value);
+ }
+
///
/// Constructs a color from an OK HSL profile. The ,
/// , and are typically
@@ -1077,6 +1103,17 @@ public static Color FromOkHsl(float hue, float saturation, float lightness, floa
return NativeFuncs.godotsharp_color_from_ok_hsl(hue, saturation, lightness, alpha);
}
+ ///
+ /// Converts a color to OK HSL values.
+ ///
+ /// Output parameter for the OK HSL hue.
+ /// Output parameter for the OK HSL saturation.
+ /// Output parameter for the OK HSL lightness.
+ public readonly void ToOkHsl(out float hue, out float saturation, out float lightness)
+ {
+ NativeFuncs.godotsharp_color_to_ok_hsl(this, out hue, out saturation, out lightness);
+ }
+
///
/// Encodes a from a RGBE9995 format integer.
/// See .
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index 3d72ee003669..fba8474e364d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -153,8 +153,14 @@ internal static partial godot_variant godotsharp_callable_call(in godot_callable
internal static partial void godotsharp_callable_call_deferred(in godot_callable p_callable,
godot_variant** p_args, int p_arg_count);
+ internal static partial Color godotsharp_color_from_ok_hsv(float p_h, float p_s, float p_v, float p_alpha);
+
+ internal static partial void godotsharp_color_to_ok_hsv(in Color p_color, out float r_h, out float r_s, out float r_v);
+
internal static partial Color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha);
+ internal static partial void godotsharp_color_to_ok_hsl(in Color p_color, out float r_h, out float r_s, out float r_l);
+
// GDNative functions
// gdnative.h
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 306ac333eb46..1730a590385a 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -517,6 +517,20 @@ void godotsharp_callable_call_deferred(Callable *p_callable, const Variant **p_a
p_callable->call_deferredp(p_args, p_arg_count);
}
+godot_color godotsharp_color_from_ok_hsv(float p_h, float p_s, float p_v, float p_alpha) {
+ godot_color ret;
+ Color *dest = (Color *)&ret;
+ memnew_placement(dest, Color(Color::from_ok_hsv(p_h, p_s, p_v, p_alpha)));
+ return ret;
+}
+
+void godotsharp_color_to_ok_hsv(godot_color *p_color, float *r_h, float *r_s, float *r_v) {
+ Color *color_val = (Color *)p_color;
+ *r_h = color_val->get_ok_hsv_h();
+ *r_s = color_val->get_ok_hsv_s();
+ *r_v = color_val->get_ok_hsv_v();
+}
+
godot_color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) {
godot_color ret;
Color *dest = (Color *)&ret;
@@ -524,6 +538,13 @@ godot_color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float
return ret;
}
+void godotsharp_color_to_ok_hsl(godot_color *p_color, float *r_h, float *r_s, float *r_l) {
+ Color *color_val = (Color *)p_color;
+ *r_h = color_val->get_ok_hsl_h();
+ *r_s = color_val->get_ok_hsl_s();
+ *r_l = color_val->get_ok_hsl_l();
+}
+
// GDNative functions
// gdnative.h
@@ -1432,7 +1453,10 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_callable_get_data_for_marshalling,
(void *)godotsharp_callable_call,
(void *)godotsharp_callable_call_deferred,
+ (void *)godotsharp_color_from_ok_hsv,
+ (void *)godotsharp_color_to_ok_hsv,
(void *)godotsharp_color_from_ok_hsl,
+ (void *)godotsharp_color_to_ok_hsl,
(void *)godotsharp_method_bind_ptrcall,
(void *)godotsharp_method_bind_call,
(void *)godotsharp_variant_new_string_name,
diff --git a/tests/core/math/test_color.h b/tests/core/math/test_color.h
index 398c2da17598..616a97043a67 100644
--- a/tests/core/math/test_color.h
+++ b/tests/core/math/test_color.h
@@ -71,6 +71,17 @@ TEST_CASE("[Color] Constructor methods") {
CHECK_MESSAGE(
green_rgba.is_equal_approx(green_hsla),
"Creation with HSL notation should result in components approximately equal to the default constructor.");
+
+ const Color red_rgba = Color(1, 0, 0, 0.25);
+ const Color red_ok_hsva = Color(0, 0, 0).from_ok_hsv(0.081205, 1, 1, 0.25);
+ const Color red_ok_hsla = Color(0, 0, 0).from_ok_hsl(0.081205, 1, 0.568085, 0.25);
+
+ CHECK_MESSAGE(
+ red_rgba.is_equal_approx(red_ok_hsva),
+ "Creation with OK HSV notation should result in components approximately equal to the default constructor.");
+ CHECK_MESSAGE(
+ red_rgba.is_equal_approx(red_ok_hsla),
+ "Creation with OK HSL notation should result in components approximately equal to the default constructor.");
}
TEST_CASE("[Color] Operators") {
@@ -123,6 +134,26 @@ TEST_CASE("[Color] Reading methods") {
CHECK_MESSAGE(
dark_blue.get_hsl_l() == doctest::Approx(0.25f),
"The returned HSL lightness should match the expected value.");
+
+ CHECK_MESSAGE(
+ dark_blue.get_ok_hsv_h() == doctest::Approx(0.733478f),
+ "The returned OK HSV hue should match the expected value.");
+ CHECK_MESSAGE(
+ dark_blue.get_ok_hsv_s() == doctest::Approx(0.999991f),
+ "The returned OK HSV saturation should match the expected value.");
+ CHECK_MESSAGE(
+ dark_blue.get_ok_hsv_v() == doctest::Approx(0.474967f),
+ "The returned OK HSV value should match the expected value.");
+
+ CHECK_MESSAGE(
+ dark_blue.get_ok_hsl_h() == doctest::Approx(0.733478f),
+ "The returned OK HSL hue should match the expected value.");
+ CHECK_MESSAGE(
+ dark_blue.get_ok_hsl_s() == doctest::Approx(0.999998f),
+ "The returned OK HSL saturation should match the expected value.");
+ CHECK_MESSAGE(
+ dark_blue.get_ok_hsl_l() == doctest::Approx(0.167343f),
+ "The returned OK HSL lightness should match the expected value.");
}
TEST_CASE("[Color] Conversion methods") {