Browse Source

C#: Synchronize Color with Core

- Add `Luminance` readonly property.
- Add `LinearToSrgb` and `SrgbToLinear` static methods.
- Add `FromOkHsl` static method.
- Add `FromRgbe9995` static method.
- Add `FromString` static method.
- Expose `FromHtml` static method.
- Expose `HtmlIsValid` static method.
- Add and update some Color documentation.
Raul Santos 2 years ago
parent
commit
d843a7ab97

+ 18 - 12
doc/classes/Color.xml

@@ -205,11 +205,18 @@
 			<description>
 				Returns the [Color] associated with the provided [param hex] integer in 32-bit ARGB format (8 bits per channel, alpha channel first).
 				In GDScript and C#, the [int] is best visualized with hexadecimal notation ([code]"0x"[/code] prefix).
-				[codeblock]
+				[codeblocks]
+				[gdscript]
 				var red = Color.hex(0xffff0000)
 				var dark_cyan = Color.hex(0xff008b8b)
 				var my_color = Color.hex(0xa4bbefd2)
-				[/codeblock]
+				[/gdscript]
+				[csharp]
+				var red = new Color(0xffff0000);
+				var dark_cyan = new Color(0xff008b8b);
+				var my_color = new Color(0xa4bbefd2);
+				[/csharp]
+				[/codeblocks]
 			</description>
 		</method>
 		<method name="hex64" qualifiers="static">
@@ -234,9 +241,9 @@
 				var col = Color.html("663399cc") # col is Color(0.4, 0.2, 0.6, 0.8)
 				[/gdscript]
 				[csharp]
-				var blue = new Color("#0000ff"); // blue is Color(0.0, 0.0, 1.0, 1.0)
-				var green = new Color("#0F0");   // green is Color(0.0, 1.0, 0.0, 1.0)
-				var col = new Color("663399cc"); // col is Color(0.4, 0.2, 0.6, 0.8)
+				var blue = Color.FromHtml("#0000ff"); // blue is Color(0.0, 0.0, 1.0, 1.0)
+				var green = Color.FromHtml("#0F0");   // green is Color(0.0, 1.0, 0.0, 1.0)
+				var col = Color.FromHtml("663399cc"); // col is Color(0.4, 0.2, 0.6, 0.8)
 				[/csharp]
 				[/codeblocks]
 			</description>
@@ -257,14 +264,13 @@
 				Color.html_is_valid("#55aaFF5")  # Returns false
 				[/gdscript]
 				[csharp]
-				// This method is not available in C#. Use `StringExtensions.IsValidHtmlColor()`, instead.
-				"#55AAFF".IsValidHtmlColor();   // Returns true
-				"#55AAFF20".IsValidHtmlColor(); // Returns true
-				"55AAFF".IsValidHtmlColor();    // Returns true
-				"#F2C".IsValidHtmlColor();      // Returns true
+				Color.IsHtmlValid("#55AAFF");   // Returns true
+				Color.IsHtmlValid("#55AAFF20"); // Returns true
+				Color.IsHtmlValid("55AAFF");    // Returns true
+				Color.IsHtmlValid("#F2C");      // Returns true
 
-				"#AABBC".IsValidHtmlColor();    // Returns false
-				"#55aaFF5".IsValidHtmlColor();  // Returns false
+				Color.IsHtmlValid("#AABBC");    // Returns false
+				Color.IsHtmlValid("#55aaFF5");  // Returns false
 				[/csharp]
 				[/codeblocks]
 			</description>

+ 155 - 12
modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Runtime.InteropServices;
+using Godot.NativeInterop;
 
 namespace Godot
 {
@@ -186,6 +187,19 @@ namespace Godot
             }
         }
 
+        /// <summary>
+        /// Returns the light intensity of the color, as a value between 0.0 and 1.0 (inclusive).
+        /// This is useful when determining light or dark color. Colors with a luminance smaller
+        /// than 0.5 can be generally considered dark.
+        /// Note: <see cref="Luminance"/> relies on the color being in the linear color space to
+        /// return an accurate relative luminance value. If the color is in the sRGB color space
+        /// use <see cref="SrgbToLinear"/> to convert it to the linear color space first.
+        /// </summary>
+        public readonly float Luminance
+        {
+            get { return 0.2126f * r + 0.7152f * g + 0.0722f * b; }
+        }
+
         /// <summary>
         /// Access color components using their index.
         /// </summary>
@@ -362,6 +376,35 @@ namespace Godot
             );
         }
 
+        /// <summary>
+        /// Returns the color converted to the sRGB color space.
+        /// This method assumes the original color is in the linear color space.
+        /// See also <see cref="SrgbToLinear"/> which performs the opposite operation.
+        /// </summary>
+        /// <returns>The sRGB color.</returns>
+        public readonly Color LinearToSrgb()
+        {
+            return new Color(
+                r < 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * (float)Mathf.Pow(r, 1.0f / 2.4f) - 0.055f,
+                g < 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * (float)Mathf.Pow(g, 1.0f / 2.4f) - 0.055f,
+                b < 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * (float)Mathf.Pow(b, 1.0f / 2.4f) - 0.055f, a);
+        }
+
+        /// <summary>
+        /// Returns the color converted to linear color space.
+        /// This method assumes the original color already is in sRGB color space.
+        /// See also <see cref="LinearToSrgb"/> which performs the opposite operation.
+        /// </summary>
+        /// <returns>The color in linear color space.</returns>
+        public readonly Color SrgbToLinear()
+        {
+            return new Color(
+                r < 0.04045f ? r * (1.0f / 12.92f) : (float)Mathf.Pow((r + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f),
+                g < 0.04045f ? g * (1.0f / 12.92f) : (float)Mathf.Pow((g + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f),
+                b < 0.04045f ? b * (1.0f / 12.92f) : (float)Mathf.Pow((b + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f),
+                a);
+        }
+
         /// <summary>
         /// Returns the color converted to an unsigned 32-bit integer in ABGR
         /// format (each byte represents a color channel).
@@ -565,6 +608,10 @@ namespace Godot
         /// <see cref="Colors"/> constants.
         /// </summary>
         /// <param name="code">The HTML color code or color name to construct from.</param>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// A color cannot be inferred from the given <paramref name="code"/>.
+        /// It was invalid HTML and a color with that name was not found.
+        /// </exception>
         public Color(string code)
         {
             if (HtmlIsValid(code))
@@ -597,7 +644,7 @@ namespace Godot
         /// <exception name="ArgumentOutOfRangeException">
         /// <paramref name="rgba"/> color code is invalid.
         /// </exception>
-        private static Color FromHTML(ReadOnlySpan<char> rgba)
+        public static Color FromHTML(ReadOnlySpan<char> rgba)
         {
             Color c;
             if (rgba.Length == 0)
@@ -705,28 +752,59 @@ namespace Godot
         /// the constants defined in <see cref="Colors"/>.
         /// </summary>
         /// <param name="name">The name of the color.</param>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// A color with the given name is not found.
+        /// </exception>
         /// <returns>The constructed color.</returns>
         private static Color Named(string name)
+        {
+            if (!FindNamedColor(name, out Color color))
+            {
+                throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
+            }
+
+            return color;
+        }
+
+        /// <summary>
+        /// Returns a color according to the standardized name, with the
+        /// specified alpha value. Supported color names are the same as
+        /// the constants defined in <see cref="Colors"/>.
+        /// If a color with the given name is not found, it returns
+        /// <paramref name="default"/>.
+        /// </summary>
+        /// <param name="name">The name of the color.</param>
+        /// <param name="default">
+        /// The default color to return when a color with the given name
+        /// is not found.
+        /// </param>
+        /// <returns>The constructed color.</returns>
+        private static Color Named(string name, Color @default)
+        {
+            if (!FindNamedColor(name, out Color color))
+            {
+                return @default;
+            }
+
+            return color;
+        }
+
+        private static bool FindNamedColor(string name, out Color color)
         {
             name = name.Replace(" ", string.Empty);
             name = name.Replace("-", string.Empty);
             name = name.Replace("_", string.Empty);
             name = name.Replace("'", string.Empty);
             name = name.Replace(".", string.Empty);
-            name = name.ToUpper();
-
-            if (!Colors.namedColors.ContainsKey(name))
-            {
-                throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
-            }
+            name = name.ToUpperInvariant();
 
-            return Colors.namedColors[name];
+            return Colors.namedColors.TryGetValue(name, out color);
         }
 
         /// <summary>
-        /// Constructs a color from an HSV profile, with values on the
-        /// range of 0 to 1. This is equivalent to using each of
-        /// the <c>h</c>/<c>s</c>/<c>v</c> properties, but much more efficient.
+        /// Constructs a color from an HSV profile. The <paramref name="hue"/>,
+        /// <paramref name="saturation"/>, and <paramref name="value"/> are typically
+        /// between 0.0 and 1.0.
         /// </summary>
         /// <param name="hue">The HSV hue, typically on the range of 0 to 1.</param>
         /// <param name="saturation">The HSV saturation, typically on the range of 0 to 1.</param>
@@ -841,13 +919,78 @@ namespace Godot
             return ParseCol4(str, index) * 16 + ParseCol4(str, index + 1);
         }
 
+        /// <summary>
+        /// Constructs a color from an OK HSL profile. The <paramref name="hue"/>,
+        /// <paramref name="saturation"/>, and <paramref name="lightness"/> are typically
+        /// between 0.0 and 1.0.
+        /// </summary>
+        /// <param name="hue">The OK HSL hue, typically on the range of 0 to 1.</param>
+        /// <param name="saturation">The OK HSL saturation, typically on the range of 0 to 1.</param>
+        /// <param name="lightness">The OK HSL lightness, typically on the range of 0 to 1.</param>
+        /// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param>
+        /// <returns>The constructed color.</returns>
+        public static Color FromOkHsl(float hue, float saturation, float lightness, float alpha = 1.0f)
+        {
+            return NativeFuncs.godotsharp_color_from_ok_hsl(hue, saturation, lightness, alpha);
+        }
+
+        /// <summary>
+        /// Encodes a <see cref="Color"/> from a RGBE9995 format integer.
+        /// See <see cref="Image.Format.Rgbe9995"/>.
+        /// </summary>
+        /// <param name="rgbe">The RGBE9995 encoded color.</param>
+        /// <returns>The constructed color.</returns>
+        public static Color FromRgbe9995(uint rgbe)
+        {
+            float r = rgbe & 0x1ff;
+            float g = (rgbe >> 9) & 0x1ff;
+            float b = (rgbe >> 18) & 0x1ff;
+            float e = rgbe >> 27;
+            float m = (float)Mathf.Pow(2.0f, e - 15.0f - 9.0f);
+
+            float rd = r * m;
+            float gd = g * m;
+            float bd = b * m;
+
+            return new Color(rd, gd, bd, 1.0f);
+        }
+
+        /// <summary>
+        /// Constructs a color from the given string, which can be either an HTML color
+        /// code or a named color. Returns <paramref name="default"/> if the color cannot
+        /// be inferred from the string. Supported color names are the same as the
+        /// <see cref="Colors"/> constants.
+        /// </summary>
+        /// <param name="str">The HTML color code or color name.</param>
+        /// <param name="default">The fallback color to return if the color cannot be inferred.</param>
+        /// <returns>The constructed color.</returns>
+        public static Color FromString(string str, Color @default)
+        {
+            if (HtmlIsValid(str))
+            {
+                return FromHTML(str);
+            }
+            else
+            {
+                return Named(str, @default);
+            }
+        }
+
         private static string ToHex32(float val)
         {
             byte b = (byte)Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
             return b.HexEncode();
         }
 
-        internal static bool HtmlIsValid(ReadOnlySpan<char> color)
+        /// <summary>
+        /// Returns <see langword="true"/> if <paramref name="color"/> is a valid HTML hexadecimal
+        /// color string. The string must be a hexadecimal value (case-insensitive) of either 3,
+        /// 4, 6 or 8 digits, and may be prefixed by a hash sign (<c>#</c>). This method is
+        /// identical to <see cref="StringExtensions.IsValidHtmlColor(string)"/>.
+        /// </summary>
+        /// <param name="color">The HTML hexadecimal color string.</param>
+        /// <returns>Whether or not the string was a valid HTML hexadecimal color string.</returns>
+        public static bool HtmlIsValid(ReadOnlySpan<char> color)
         {
             if (color.IsEmpty)
             {

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs

@@ -153,6 +153,8 @@ namespace Godot.NativeInterop
         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_hsl(float p_h, float p_s, float p_l, float p_alpha);
+
         // GDNative functions
 
         // gdnative.h

+ 8 - 0
modules/mono/glue/runtime_interop.cpp

@@ -514,6 +514,13 @@ 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_hsl(float p_h, float p_s, float p_l, float p_alpha) {
+	godot_color ret;
+	Color *dest = (Color *)&ret;
+	memnew_placement(dest, Color(Color::from_ok_hsl(p_h, p_s, p_l, p_alpha)));
+	return ret;
+}
+
 // GDNative functions
 
 // gdnative.h
@@ -1345,6 +1352,7 @@ 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_hsl,
 	(void *)godotsharp_method_bind_ptrcall,
 	(void *)godotsharp_method_bind_call,
 	(void *)godotsharp_variant_new_string_name,