|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
namespace Microsoft.Maui.Graphics
{
[DebuggerDisplay("Red={Red}, Green={Green}, Blue={Blue}, Alpha={Alpha}")]
[TypeConverter(typeof(Converters.ColorTypeConverter))]
public class Color
{
public readonly float Red;
public readonly float Green;
public readonly float Blue;
public readonly float Alpha = 1;
public Color()
{
// Default Black
Red = Green = Blue = 0;
}
public Color(float gray)
{
Red = Green = Blue = gray.Clamp(0, 1);
}
public Color(float red, float green, float blue)
{
Red = red.Clamp(0, 1);
Green = green.Clamp(0, 1);
Blue = blue.Clamp(0, 1);
}
public Color(float red, float green, float blue, float alpha)
{
Red = red.Clamp(0, 1);
Green = green.Clamp(0, 1);
Blue = blue.Clamp(0, 1);
Alpha = alpha.Clamp(0, 1);
}
public Color(byte red, byte green, byte blue)
{
Red = (red / 255f).Clamp(0, 1);
Green = (green / 255f).Clamp(0, 1);
Blue = (blue / 255f).Clamp(0, 1);
Alpha = 1.0f;
}
public Color(byte red, byte green, byte blue, byte alpha)
{
Red = (red / 255f).Clamp(0, 1);
Green = (green / 255f).Clamp(0, 1);
Blue = (blue / 255f).Clamp(0, 1);
Alpha = (alpha / 255f).Clamp(0, 1);
}
public Color(int red, int green, int blue)
{
Red = (red / 255f).Clamp(0, 1);
Green = (green / 255f).Clamp(0, 1);
Blue = (blue / 255f).Clamp(0, 1);
Alpha = 1.0f;
}
public Color(int red, int green, int blue, int alpha)
{
Red = (red / 255f).Clamp(0, 1);
Green = (green / 255f).Clamp(0, 1);
Blue = (blue / 255f).Clamp(0, 1);
Alpha = (alpha / 255f).Clamp(0, 1);
}
public Color(Vector4 color)
{
Red = color.X.Clamp(0, 1);
Green = color.Y.Clamp(0, 1);
Blue = color.Z.Clamp(0, 1);
Alpha = color.W.Clamp(0, 1);
}
public override string ToString()
{
return $"[Color: Red={Red}, Green={Green}, Blue={Blue}, Alpha={Alpha}]";
}
public override int GetHashCode()
{
unchecked
{
int hashcode = Red.GetHashCode();
hashcode = (hashcode * 397) ^ Green.GetHashCode();
hashcode = (hashcode * 397) ^ Blue.GetHashCode();
hashcode = (hashcode * 397) ^ Alpha.GetHashCode();
return hashcode;
}
}
public override bool Equals(object obj)
{
if (obj is Color other)
return ToInt() == other.ToInt();
return base.Equals(obj);
}
[Obsolete("Use ToArgbHex instead.")]
public string ToHex(bool includeAlpha)
{
if (includeAlpha || Alpha < 1)
return "#" + ToHex(Alpha) + ToHex(Red) + ToHex(Green) + ToHex(Blue);
return "#" + ToHex(Red) + ToHex(Green) + ToHex(Blue);
}
public string ToHex()
{
return "#" + ToHex(Red) + ToHex(Green) + ToHex(Blue);
}
public string ToArgbHex(bool includeAlpha = false)
{
if (includeAlpha || Alpha < 1)
return "#" + ToHex(Alpha) + ToHex(Red) + ToHex(Green) + ToHex(Blue);
return "#" + ToHex(Red) + ToHex(Green) + ToHex(Blue);
}
public string ToRgbaHex(bool includeAlpha = false)
{
if (includeAlpha || Alpha < 1)
return "#" + ToHex(Red) + ToHex(Green) + ToHex(Blue) + ToHex(Alpha);
return "#" + ToHex(Red) + ToHex(Green) + ToHex(Blue);
}
[Obsolete("Use FromArgb instead.")]
public static Color FromHex(string colorAsArgbHex) => FromArgb(colorAsArgbHex);
public Paint AsPaint()
{
return new SolidPaint()
{
Color = this
};
}
public Color WithAlpha(float alpha)
{
if (Math.Abs(alpha - Alpha) < GeometryUtil.Epsilon)
return this;
return new Color(Red, Green, Blue, alpha);
}
public Color MultiplyAlpha(float multiplyBy)
{
return new Color(Red, Green, Blue, Alpha * multiplyBy);
}
private static string ToHex(float value)
{
var intValue = (int)(255f * value);
var stringValue = intValue.ToString("X");
if (stringValue.Length == 1)
return "0" + stringValue;
return stringValue;
}
public int ToInt()
{
ToRgba(out var r, out var g, out var b, out var a);
int argb = a << 24 | r << 16 | g << 8 | b;
return argb;
}
public uint ToUint() => (uint)ToInt();
public void ToRgb(out byte r, out byte g, out byte b) =>
ToRgba(out r, out g, out b, out _);
public void ToRgba(out byte r, out byte g, out byte b, out byte a)
{
a = (byte)(Alpha * 255f);
r = (byte)(Red * 255f);
g = (byte)(Green * 255f);
b = (byte)(Blue * 255f);
}
public float GetLuminosity()
{
float v = Math.Max(Red, Green);
v = Math.Max(v, Blue);
float m = Math.Min(Red, Green);
m = Math.Min(m, Blue);
var l = (m + v) / 2.0f;
if (l <= 0.0)
return 0;
return l;
}
public Color AddLuminosity(float delta)
{
ToHsl(out var h, out var s, out var l);
l += delta;
l = l.Clamp(0, 1);
return FromHsla(h, s, l, Alpha);
}
public Color WithLuminosity(float luminosity)
{
ToHsl(out var h, out var s, out var l);
return FromHsla(h, s, luminosity, Alpha);
}
public float GetSaturation()
{
ToHsl(out var h, out var s, out var l);
return s;
}
public Color WithSaturation(float saturation)
{
ToHsl(out var h, out var s, out var l);
return FromHsla(h, saturation, l, Alpha);
}
public float GetHue()
{
ToHsl(out var h, out var s, out var l);
return h;
}
public Color WithHue(float hue)
{
ToHsl(out var h, out var s, out var l);
return FromHsla(hue, s, l, Alpha);
}
public Color GetComplementary()
{
ToHsl(out var h, out var s, out var l);
// Add 180 (degrees) to get to the other side of the circle.
h += 0.5f;
// Ensure still within the bounds of a circle.
h %= 1.0f;
return Color.FromHsla(h, s, l);
}
public static Color FromHsva(float h, float s, float v, float a)
{
h = h.Clamp(0, 1);
s = s.Clamp(0, 1);
v = v.Clamp(0, 1);
var range = (int)(Math.Floor(h * 6)) % 6;
var f = h * 6 - Math.Floor(h * 6);
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (range)
{
case 0:
return FromRgba(v, t, p, a);
case 1:
return FromRgba(q, v, p, a);
case 2:
return FromRgba(p, v, t, a);
case 3:
return FromRgba(p, q, v, a);
case 4:
return FromRgba(t, p, v, a);
}
return FromRgba(v, p, q, a);
}
public static Color FromUint(uint argb)
{
return FromRgba((byte)((argb & 0x00ff0000) >> 0x10), (byte)((argb & 0x0000ff00) >> 0x8), (byte)(argb & 0x000000ff), (byte)((argb & 0xff000000) >> 0x18));
}
public static Color FromInt(int argb)
{
return FromRgba((byte)((argb & 0x00ff0000) >> 0x10), (byte)((argb & 0x0000ff00) >> 0x8), (byte)(argb & 0x000000ff), (byte)((argb & 0xff000000) >> 0x18));
}
public static Color FromRgb(byte red, byte green, byte blue)
{
return new Color(red / 255f, green / 255f, blue / 255f, 1f);
}
public static Color FromRgba(byte red, byte green, byte blue, byte alpha)
{
return new Color(red / 255f, green / 255f, blue / 255f, alpha / 255f);
}
public static Color FromRgb(int red, int green, int blue)
{
return new Color(red / 255f, green / 255f, blue / 255f, 1f);
}
public static Color FromRgba(int red, int green, int blue, int alpha)
{
return new Color(red / 255f, green / 255f, blue / 255f, alpha / 255f);
}
public static Color FromRgb(float red, float green, float blue)
{
return Color.FromRgba(red, green, blue, 1);
}
public static Color FromRgb(double red, double green, double blue)
{
return Color.FromRgba(red, green, blue, 1);
}
public static Color FromRgba(float r, float g, float b, float a)
{
return new Color(r, g, b, a);
}
public static Color FromRgba(double r, double g, double b, double a)
{
return new Color((float)r, (float)g, (float)b, (float)a);
}
public static Color FromRgba(string colorAsHex) => FromRgba(colorAsHex != null ? colorAsHex.AsSpan() : default);
static Color FromRgba(ReadOnlySpan<char> colorAsHex)
{
int red = 0;
int green = 0;
int blue = 0;
int alpha = 255;
if (!colorAsHex.IsEmpty)
{
//Skip # if present
if (colorAsHex[0] == '#')
colorAsHex = colorAsHex.Slice(1);
if (colorAsHex.Length == 6 || colorAsHex.Length == 3)
{
//#RRGGBB or #RGB - since there is no A, use FromArgb
return FromArgb(colorAsHex);
}
else if (colorAsHex.Length == 4)
{
//#RGBA
Span<char> temp = stackalloc char[2];
temp[0] = temp[1] = colorAsHex[0];
red = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[1];
green = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[2];
blue = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[3];
alpha = ParseInt(temp);
}
else if (colorAsHex.Length == 8)
{
//#RRGGBBAA
red = ParseInt(colorAsHex.Slice(0, 2));
green = ParseInt(colorAsHex.Slice(2, 2));
blue = ParseInt(colorAsHex.Slice(4, 2));
alpha = ParseInt(colorAsHex.Slice(6, 2));
}
}
return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f);
}
public static Color FromArgb(string colorAsHex) => FromArgb(colorAsHex != null ? colorAsHex.AsSpan() : default);
static Color FromArgb(ReadOnlySpan<char> colorAsHex)
{
int red = 0;
int green = 0;
int blue = 0;
int alpha = 255;
if (!colorAsHex.IsEmpty)
{
//Skip # if present
if (colorAsHex[0] == '#')
colorAsHex = colorAsHex.Slice(1);
if (colorAsHex.Length == 6)
{
//#RRGGBB
red = ParseInt(colorAsHex.Slice(0, 2));
green = ParseInt(colorAsHex.Slice(2, 2));
blue = ParseInt(colorAsHex.Slice(4, 2));
}
else if (colorAsHex.Length == 3)
{
//#RGB
Span<char> temp = stackalloc char[2];
temp[0] = temp[1] = colorAsHex[0];
red = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[1];
green = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[2];
blue = ParseInt(temp);
}
else if (colorAsHex.Length == 4)
{
//#ARGB
Span<char> temp = stackalloc char[2];
temp[0] = temp[1] = colorAsHex[0];
alpha = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[1];
red = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[2];
green = ParseInt(temp);
temp[0] = temp[1] = colorAsHex[3];
blue = ParseInt(temp);
}
else if (colorAsHex.Length == 8)
{
//#AARRGGBB
alpha = ParseInt(colorAsHex.Slice(0, 2));
red = ParseInt(colorAsHex.Slice(2, 2));
green = ParseInt(colorAsHex.Slice(4, 2));
blue = ParseInt(colorAsHex.Slice(6, 2));
}
}
return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f);
}
public static Color FromHsla(float h, float s, float l, float a = 1)
{
float red, green, blue;
ConvertToRgb(h, s, l, out red, out green, out blue);
return new Color(red, green, blue, a);
}
public static Color FromHsla(double h, double s, double l, double a = 1)
{
float red, green, blue;
ConvertToRgb((float)h, (float)s, (float)l, out red, out green, out blue);
return new Color(red, green, blue, (float)a);
}
public static Color FromHsv(float h, float s, float v)
{
return FromHsva(h, s, v, 1f);
}
public static Color FromHsva(int h, int s, int v, int a)
{
return FromHsva(h / 360f, s / 100f, v / 100f, a / 100f);
}
public static Color FromHsv(int h, int s, int v)
{
return FromHsva(h / 360f, s / 100f, v / 100f, 1f);
}
private static void ConvertToRgb(float hue, float saturation, float luminosity, out float r, out float g, out float b)
{
if (luminosity == 0)
{
r = g = b = 0;
return;
}
if (saturation == 0)
{
r = g = b = luminosity;
return;
}
float temp2 = luminosity <= 0.5f ? luminosity * (1.0f + saturation) : luminosity + saturation - luminosity * saturation;
float temp1 = 2.0f * luminosity - temp2;
var t3 = new[] { hue + 1.0f / 3.0f, hue, hue - 1.0f / 3.0f };
var clr = new float[] { 0, 0, 0 };
for (var i = 0; i < 3; i++)
{
if (t3[i] < 0)
t3[i] += 1.0f;
if (t3[i] > 1)
t3[i] -= 1.0f;
if (6.0 * t3[i] < 1.0)
clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0f;
else if (2.0 * t3[i] < 1.0)
clr[i] = temp2;
else if (3.0 * t3[i] < 2.0)
clr[i] = temp1 + (temp2 - temp1) * (2.0f / 3.0f - t3[i]) * 6.0f;
else
clr[i] = temp1;
}
r = clr[0];
g = clr[1];
b = clr[2];
}
public void ToHsl(out float h, out float s, out float l)
{
var r = Red;
var g = Green;
var b = Blue;
float v = Math.Max(r, g);
v = Math.Max(v, b);
float m = Math.Min(r, g);
m = Math.Min(m, b);
l = (m + v) / 2.0f;
if (l <= 0.0)
{
h = s = l = 0;
return;
}
float vm = v - m;
s = vm;
if (s > 0.0)
{
s /= l <= 0.5f ? v + m : 2.0f - v - m;
}
else
{
h = 0;
s = 0;
return;
}
float r2 = (v - r) / vm;
float g2 = (v - g) / vm;
float b2 = (v - b) / vm;
if (r == v)
{
h = g == m ? 5.0f + b2 : 1.0f - g2;
}
else if (g == v)
{
h = b == m ? 1.0f + r2 : 3.0f - b2;
}
else
{
h = r == m ? 3.0f + g2 : 5.0f - r2;
}
h /= 6.0f;
}
// Supported inputs
// HEX #rgb, #argb, #rrggbb, #aarrggbb
// RGB rgb(255,0,0), rgb(100%,0%,0%) values in range 0-255 or 0%-100%
// RGBA rgba(255, 0, 0, 0.8), rgba(100%, 0%, 0%, 0.8) opacity is 0.0-1.0
// HSL hsl(120, 100%, 50%) h is 0-360, s and l are 0%-100%
// HSLA hsla(120, 100%, 50%, .8) opacity is 0.0-1.0
// HSV hsv(120, 100%, 50%) h is 0-360, s and v are 0%-100%
// HSVA hsva(120, 100%, 50%, .8) opacity is 0.0-1.0
// Predefined color case insensitive
public static Color Parse(string value)
{
if (TryParse(value, out var c) && c != default)
return c;
throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(Color)}");
}
public static bool TryParse(string value, out Color color) => TryParse(value != null ? value.AsSpan() : default, out color);
static bool TryParse(ReadOnlySpan<char> value, out Color color)
{
value = value.Trim();
if (!value.IsEmpty)
{
if (value[0] == '#')
{
try
{
color = Color.FromArgb(value);
return true;
}
catch
{
goto ReturnFalse;
}
}
if (value.StartsWith("rgba".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (!TryParseFourColorRanges(value,
out ReadOnlySpan<char> quad0,
out ReadOnlySpan<char> quad1,
out ReadOnlySpan<char> quad2,
out ReadOnlySpan<char> quad3))
{
goto ReturnFalse;
}
bool valid = TryParseColorValue(quad0, 255, acceptPercent: true, out double r);
valid &= TryParseColorValue(quad1, 255, acceptPercent: true, out double g);
valid &= TryParseColorValue(quad2, 255, acceptPercent: true, out double b);
valid &= TryParseOpacity(quad3, out double a);
if (!valid)
goto ReturnFalse;
color = new Color((float)r, (float)g, (float)b, (float)a);
return true;
}
if (value.StartsWith("rgb".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (!TryParseThreeColorRanges(value,
out ReadOnlySpan<char> triplet0,
out ReadOnlySpan<char> triplet1,
out ReadOnlySpan<char> triplet2))
{
goto ReturnFalse;
}
bool valid = TryParseColorValue(triplet0, 255, acceptPercent: true, out double r);
valid &= TryParseColorValue(triplet1, 255, acceptPercent: true, out double g);
valid &= TryParseColorValue(triplet2, 255, acceptPercent: true, out double b);
if (!valid)
goto ReturnFalse;
color = new Color((float)r, (float)g, (float)b);
return true;
}
if (value.StartsWith("hsla".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (!TryParseFourColorRanges(value,
out ReadOnlySpan<char> quad0,
out ReadOnlySpan<char> quad1,
out ReadOnlySpan<char> quad2,
out ReadOnlySpan<char> quad3))
{
goto ReturnFalse;
}
bool valid = TryParseColorValue(quad0, 360, acceptPercent: false, out double h);
valid &= TryParseColorValue(quad1, 100, acceptPercent: true, out double s);
valid &= TryParseColorValue(quad2, 100, acceptPercent: true, out double l);
valid &= TryParseOpacity(quad3, out double a);
if (!valid)
goto ReturnFalse;
color = Color.FromHsla(h, s, l, a);
return true;
}
if (value.StartsWith("hsl".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (!TryParseThreeColorRanges(value,
out ReadOnlySpan<char> triplet0,
out ReadOnlySpan<char> triplet1,
out ReadOnlySpan<char> triplet2))
{
goto ReturnFalse;
}
bool valid = TryParseColorValue(triplet0, 360, acceptPercent: false, out double h);
valid &= TryParseColorValue(triplet1, 100, acceptPercent: true, out double s);
valid &= TryParseColorValue(triplet2, 100, acceptPercent: true, out double l);
if (!valid)
goto ReturnFalse;
color = Color.FromHsla(h, s, l);
return true;
}
if (value.StartsWith("hsva".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (!TryParseFourColorRanges(value,
out ReadOnlySpan<char> quad0,
out ReadOnlySpan<char> quad1,
out ReadOnlySpan<char> quad2,
out ReadOnlySpan<char> quad3))
{
goto ReturnFalse;
}
bool valid = TryParseColorValue(quad0, 360, acceptPercent: false, out double h);
valid &= TryParseColorValue(quad1, 100, acceptPercent: true, out double s);
valid &= TryParseColorValue(quad2, 100, acceptPercent: true, out double v);
valid &= TryParseOpacity(quad3, out double a);
if (!valid)
goto ReturnFalse;
color = Color.FromHsva((float)h, (float)s, (float)v, (float)a);
return true;
}
if (value.StartsWith("hsv".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (!TryParseThreeColorRanges(value,
out ReadOnlySpan<char> triplet0,
out ReadOnlySpan<char> triplet1,
out ReadOnlySpan<char> triplet2))
{
goto ReturnFalse;
}
bool valid = TryParseColorValue(triplet0, 360, acceptPercent: false, out double h);
valid &= TryParseColorValue(triplet1, 100, acceptPercent: true, out double s);
valid &= TryParseColorValue(triplet2, 100, acceptPercent: true, out double v);
if (!valid)
goto ReturnFalse;
color = Color.FromHsv((float)h, (float)s, (float)v);
return true;
}
var namedColor = GetNamedColor(value);
if (namedColor != null)
{
color = namedColor;
return true;
}
}
ReturnFalse:
color = default;
return false;
}
static Color GetNamedColor(ReadOnlySpan<char> value)
{
// the longest built-in Color's name is much lower than this check, so we should not allocate here in a typical usage
Span<char> loweredValue = value.Length <= 128 ? stackalloc char[value.Length] : new char[value.Length];
int charsWritten = value.ToLowerInvariant(loweredValue);
Debug.Assert(charsWritten == value.Length);
return loweredValue switch
{
"default" => default,
"aliceblue" => Colors.AliceBlue,
"antiquewhite" => Colors.AntiqueWhite,
"aqua" => Colors.Aqua,
"aquamarine" => Colors.Aquamarine,
"azure" => Colors.Azure,
"beige" => Colors.Beige,
"bisque" => Colors.Bisque,
"black" => Colors.Black,
"blanchedalmond" => Colors.BlanchedAlmond,
"blue" => Colors.Blue,
"blueviolet" => Colors.BlueViolet,
"brown" => Colors.Brown,
"burlywood" => Colors.BurlyWood,
"cadetblue" => Colors.CadetBlue,
"chartreuse" => Colors.Chartreuse,
"chocolate" => Colors.Chocolate,
"coral" => Colors.Coral,
"cornflowerblue" => Colors.CornflowerBlue,
"cornsilk" => Colors.Cornsilk,
"crimson" => Colors.Crimson,
"cyan" => Colors.Cyan,
"darkblue" => Colors.DarkBlue,
"darkcyan" => Colors.DarkCyan,
"darkgoldenrod" => Colors.DarkGoldenrod,
"darkgray" => Colors.DarkGray,
"darkgreen" => Colors.DarkGreen,
"darkgrey" => Colors.DarkGrey,
"darkkhaki" => Colors.DarkKhaki,
"darkmagenta" => Colors.DarkMagenta,
"darkolivegreen" => Colors.DarkOliveGreen,
"darkorange" => Colors.DarkOrange,
"darkorchid" => Colors.DarkOrchid,
"darkred" => Colors.DarkRed,
"darksalmon" => Colors.DarkSalmon,
"darkseagreen" => Colors.DarkSeaGreen,
"darkslateblue" => Colors.DarkSlateBlue,
"darkslategray" => Colors.DarkSlateGray,
"darkslategrey" => Colors.DarkSlateGrey,
"darkturquoise" => Colors.DarkTurquoise,
"darkviolet" => Colors.DarkViolet,
"deeppink" => Colors.DeepPink,
"deepskyblue" => Colors.DeepSkyBlue,
"dimgray" => Colors.DimGray,
"dimgrey" => Colors.DimGrey,
"dodgerblue" => Colors.DodgerBlue,
"firebrick" => Colors.Firebrick,
"floralwhite" => Colors.FloralWhite,
"forestgreen" => Colors.ForestGreen,
"fuchsia" => Colors.Fuchsia,
"gainsboro" => Colors.Gainsboro,
"ghostwhite" => Colors.GhostWhite,
"gold" => Colors.Gold,
"goldenrod" => Colors.Goldenrod,
"gray" => Colors.Gray,
"green" => Colors.Green,
"grey" => Colors.Grey,
"greenyellow" => Colors.GreenYellow,
"honeydew" => Colors.Honeydew,
"hotpink" => Colors.HotPink,
"indianred" => Colors.IndianRed,
"indigo" => Colors.Indigo,
"ivory" => Colors.Ivory,
"khaki" => Colors.Khaki,
"lavender" => Colors.Lavender,
"lavenderblush" => Colors.LavenderBlush,
"lawngreen" => Colors.LawnGreen,
"lemonchiffon" => Colors.LemonChiffon,
"lightblue" => Colors.LightBlue,
"lightcoral" => Colors.LightCoral,
"lightcyan" => Colors.LightCyan,
"lightgoldenrodyellow" => Colors.LightGoldenrodYellow,
"lightgrey" => Colors.LightGrey,
"lightgray" => Colors.LightGray,
"lightgreen" => Colors.LightGreen,
"lightpink" => Colors.LightPink,
"lightsalmon" => Colors.LightSalmon,
"lightseagreen" => Colors.LightSeaGreen,
"lightskyblue" => Colors.LightSkyBlue,
"lightslategray" => Colors.LightSlateGray,
"lightslategrey" => Colors.LightSlateGrey,
"lightsteelblue" => Colors.LightSteelBlue,
"lightyellow" => Colors.LightYellow,
"lime" => Colors.Lime,
"limegreen" => Colors.LimeGreen,
"linen" => Colors.Linen,
"magenta" => Colors.Magenta,
"maroon" => Colors.Maroon,
"mediumaquamarine" => Colors.MediumAquamarine,
"mediumblue" => Colors.MediumBlue,
"mediumorchid" => Colors.MediumOrchid,
"mediumpurple" => Colors.MediumPurple,
"mediumseagreen" => Colors.MediumSeaGreen,
"mediumslateblue" => Colors.MediumSlateBlue,
"mediumspringgreen" => Colors.MediumSpringGreen,
"mediumturquoise" => Colors.MediumTurquoise,
"mediumvioletred" => Colors.MediumVioletRed,
"midnightblue" => Colors.MidnightBlue,
"mintcream" => Colors.MintCream,
"mistyrose" => Colors.MistyRose,
"moccasin" => Colors.Moccasin,
"navajowhite" => Colors.NavajoWhite,
"navy" => Colors.Navy,
"oldlace" => Colors.OldLace,
"olive" => Colors.Olive,
"olivedrab" => Colors.OliveDrab,
"orange" => Colors.Orange,
"orangered" => Colors.OrangeRed,
"orchid" => Colors.Orchid,
"palegoldenrod" => Colors.PaleGoldenrod,
"palegreen" => Colors.PaleGreen,
"paleturquoise" => Colors.PaleTurquoise,
"palevioletred" => Colors.PaleVioletRed,
"papayawhip" => Colors.PapayaWhip,
"peachpuff" => Colors.PeachPuff,
"peru" => Colors.Peru,
"pink" => Colors.Pink,
"plum" => Colors.Plum,
"powderblue" => Colors.PowderBlue,
"purple" => Colors.Purple,
"red" => Colors.Red,
"rosybrown" => Colors.RosyBrown,
"royalblue" => Colors.RoyalBlue,
"saddlebrown" => Colors.SaddleBrown,
"salmon" => Colors.Salmon,
"sandybrown" => Colors.SandyBrown,
"seagreen" => Colors.SeaGreen,
"seashell" => Colors.SeaShell,
"sienna" => Colors.Sienna,
"silver" => Colors.Silver,
"skyblue" => Colors.SkyBlue,
"slateblue" => Colors.SlateBlue,
"slategray" => Colors.SlateGray,
"slategrey" => Colors.SlateGrey,
"snow" => Colors.Snow,
"springgreen" => Colors.SpringGreen,
"steelblue" => Colors.SteelBlue,
"tan" => Colors.Tan,
"teal" => Colors.Teal,
"thistle" => Colors.Thistle,
"tomato" => Colors.Tomato,
"transparent" => Colors.Transparent,
"turquoise" => Colors.Turquoise,
"violet" => Colors.Violet,
"wheat" => Colors.Wheat,
"white" => Colors.White,
"whitesmoke" => Colors.WhiteSmoke,
"yellow" => Colors.Yellow,
"yellowgreen" => Colors.YellowGreen,
_ => null
};
}
static bool TryParseFourColorRanges(
ReadOnlySpan<char> value,
out ReadOnlySpan<char> quad0,
out ReadOnlySpan<char> quad1,
out ReadOnlySpan<char> quad2,
out ReadOnlySpan<char> quad3)
{
var op = value.IndexOf('(');
var cp = value.LastIndexOf(')');
if (op < 0 || cp < 0 || cp < op)
goto ReturnFalse;
value = value.Slice(op + 1, cp - op - 1);
int index = value.IndexOf(',');
if (index == -1)
goto ReturnFalse;
quad0 = value.Slice(0, index);
value = value.Slice(index + 1);
index = value.IndexOf(',');
if (index == -1)
goto ReturnFalse;
quad1 = value.Slice(0, index);
value = value.Slice(index + 1);
index = value.IndexOf(',');
if (index == -1)
goto ReturnFalse;
quad2 = value.Slice(0, index);
quad3 = value.Slice(index + 1);
// if there are more commas, fail
if (quad3.IndexOf(',') != -1)
goto ReturnFalse;
return true;
ReturnFalse:
quad0 = quad1 = quad2 = quad3 = default;
return false;
}
static bool TryParseThreeColorRanges(
ReadOnlySpan<char> value,
out ReadOnlySpan<char> triplet0,
out ReadOnlySpan<char> triplet1,
out ReadOnlySpan<char> triplet2)
{
var op = value.IndexOf('(');
var cp = value.LastIndexOf(')');
if (op < 0 || cp < 0 || cp < op)
goto ReturnFalse;
value = value.Slice(op + 1, cp - op - 1);
int index = value.IndexOf(',');
if (index == -1)
goto ReturnFalse;
triplet0 = value.Slice(0, index);
value = value.Slice(index + 1);
index = value.IndexOf(',');
if (index == -1)
goto ReturnFalse;
triplet1 = value.Slice(0, index);
triplet2 = value.Slice(index + 1);
// if there are more commas, fail
if (triplet2.IndexOf(',') != -1)
goto ReturnFalse;
return true;
ReturnFalse:
triplet0 = triplet1 = triplet2 = default;
return false;
}
static bool TryParseColorValue(ReadOnlySpan<char> elem, int maxValue, bool acceptPercent, out double value)
{
elem = elem.Trim();
if (!elem.IsEmpty && elem[elem.Length - 1] == '%' && acceptPercent)
{
maxValue = 100;
elem = elem.Slice(0, elem.Length - 1);
}
if (TryParseDouble(elem, out value))
{
value = value.Clamp(0, maxValue) / maxValue;
return true;
}
return false;
}
static bool TryParseOpacity(ReadOnlySpan<char> elem, out double value)
{
if (TryParseDouble(elem, out value))
{
value = value.Clamp(0, 1);
return true;
}
return false;
}
static bool TryParseDouble(ReadOnlySpan<char> s, out double value) =>
double.TryParse(
#if NETSTANDARD2_0 || TIZEN
s.ToString(),
#else
s,
#endif
NumberStyles.Number, CultureInfo.InvariantCulture, out value);
static int ParseInt(ReadOnlySpan<char> s) =>
int.Parse(
#if NETSTANDARD2_0 || TIZEN
s.ToString(),
#else
s,
#endif
NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
public static implicit operator Color(Vector4 color) => new Color(color);
}
}
|