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.
This commit is contained in:
Raul Santos 2022-12-06 04:58:16 +01:00
parent bc5d67c613
commit d843a7ab97
No known key found for this signature in database
GPG key ID: B532473AE3A803E4
4 changed files with 183 additions and 24 deletions

View file

@ -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>

View file

@ -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();
name = name.ToUpperInvariant();
if (!Colors.namedColors.ContainsKey(name))
{
throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
}
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)
{

View file

@ -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

View file

@ -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,