diff --git a/src/Microsoft.Maui.Graphics/Color.NETCoreApp.cs b/src/Microsoft.Maui.Graphics/Color.NETCoreApp.cs deleted file mode 100644 index 5a8a8ed..0000000 --- a/src/Microsoft.Maui.Graphics/Color.NETCoreApp.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Globalization; - -namespace Microsoft.Maui.Graphics -{ - public partial class Color - { - private static Color FromArgbImplementation(string colorAsHex) - { - ReadOnlySpan colorAsHexSpan = colorAsHex; - - //Skip # if present - if (colorAsHexSpan[0] == '#') - colorAsHexSpan = colorAsHexSpan.Slice(1); - - int red = 0; - int green = 0; - int blue = 0; - int alpha = 255; - - if (colorAsHexSpan.Length == 6) - { - //#RRGGBB - red = ParseInt(colorAsHexSpan.Slice(0, 2)); - green = ParseInt(colorAsHexSpan.Slice(2, 2)); - blue = ParseInt(colorAsHexSpan.Slice(4, 2)); - } - else if (colorAsHexSpan.Length == 3) - { - //#RGB - Span temp = stackalloc char[2]; - temp[0] = temp[1] = colorAsHexSpan[0]; - red = ParseInt(temp); - - temp[0] = temp[1] = colorAsHexSpan[1]; - green = ParseInt(temp); - - temp[0] = temp[1] = colorAsHexSpan[2]; - blue = ParseInt(temp); - } - else if (colorAsHexSpan.Length == 4) - { - //#ARGB - Span temp = stackalloc char[2]; - temp[0] = temp[1] = colorAsHexSpan[0]; - alpha = ParseInt(temp); - - temp[0] = temp[1] = colorAsHexSpan[1]; - red = ParseInt(temp); - - temp[0] = temp[1] = colorAsHexSpan[2]; - green = ParseInt(temp); - - temp[0] = temp[1] = colorAsHexSpan[3]; - blue = ParseInt(temp); - } - else if (colorAsHexSpan.Length == 8) - { - //#AARRGGBB - alpha = ParseInt(colorAsHexSpan.Slice(0, 2)); - red = ParseInt(colorAsHexSpan.Slice(2, 2)); - green = ParseInt(colorAsHexSpan.Slice(4, 2)); - blue = ParseInt(colorAsHexSpan.Slice(6, 2)); - } - - return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f); - } - - private static int ParseInt(ReadOnlySpan s) => - int.Parse(s, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture); - } -} diff --git a/src/Microsoft.Maui.Graphics/Color.Standard.cs b/src/Microsoft.Maui.Graphics/Color.Standard.cs deleted file mode 100644 index 9316cd6..0000000 --- a/src/Microsoft.Maui.Graphics/Color.Standard.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Globalization; - -namespace Microsoft.Maui.Graphics -{ - public partial class Color - { - private static Color FromArgbImplementation(string colorAsHex) - { - //Skip # if present - if (colorAsHex[0] == '#') - colorAsHex = colorAsHex.Substring(1); - - int red = 0; - int green = 0; - int blue = 0; - int alpha = 255; - - if (colorAsHex.Length == 6) - { - //#RRGGBB - red = ParseInt(colorAsHex.Substring(0, 2)); - green = ParseInt(colorAsHex.Substring(2, 2)); - blue = ParseInt(colorAsHex.Substring(4, 2)); - } - else if (colorAsHex.Length == 3) - { - //#RGB - red = ParseInt($"{colorAsHex[0]}{colorAsHex[0]}"); - green = ParseInt($"{colorAsHex[1]}{colorAsHex[1]}"); - blue = ParseInt($"{colorAsHex[2]}{colorAsHex[2]}"); - } - else if (colorAsHex.Length == 4) - { - //#ARGB - alpha = ParseInt($"{colorAsHex[0]}{colorAsHex[0]}"); - red = ParseInt($"{colorAsHex[1]}{colorAsHex[1]}"); - green = ParseInt($"{colorAsHex[2]}{colorAsHex[2]}"); - blue = ParseInt($"{colorAsHex[3]}{colorAsHex[3]}"); - } - else if (colorAsHex.Length == 8) - { - //#AARRGGBB - alpha = ParseInt(colorAsHex.Substring(0, 2)); - red = ParseInt(colorAsHex.Substring(2, 2)); - green = ParseInt(colorAsHex.Substring(4, 2)); - blue = ParseInt(colorAsHex.Substring(6, 2)); - } - - return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f); - } - - private static int ParseInt(string s) => - int.Parse(s, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture); - - } -} diff --git a/src/Microsoft.Maui.Graphics/Color.cs b/src/Microsoft.Maui.Graphics/Color.cs index 2636569..ba3d2c5 100644 --- a/src/Microsoft.Maui.Graphics/Color.cs +++ b/src/Microsoft.Maui.Graphics/Color.cs @@ -1,15 +1,15 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; -using System.Linq; using System.Numerics; namespace Microsoft.Maui.Graphics { [DebuggerDisplay("Red={Red}, Green={Green}, Blue={Blue}, Alpha={Alpha}")] [TypeConverter(typeof(Converters.ColorTypeConverter))] - public partial class Color + public class Color { public readonly float Red; public readonly float Green; @@ -382,7 +382,69 @@ namespace Microsoft.Maui.Graphics return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f); } - public static Color FromArgb(string colorAsHex) => FromArgbImplementation(colorAsHex); + public static Color FromArgb(string colorAsHex) => FromArgb(colorAsHex != null ? colorAsHex.AsSpan() : default); + + static Color FromArgb(ReadOnlySpan 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 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 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) { @@ -522,12 +584,14 @@ namespace Microsoft.Maui.Graphics throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(Color)}"); } - public static bool TryParse(string value, out Color color) + public static bool TryParse(string value, out Color color) => TryParse(value != null ? value.AsSpan() : default, out color); + + static bool TryParse(ReadOnlySpan value, out Color color) { - if (value != null) + value = value.Trim(); + if (!value.IsEmpty) { - value = value.Trim(); - if (value.StartsWith("#", StringComparison.Ordinal)) + if (value[0] == '#') { try { @@ -541,308 +605,127 @@ namespace Microsoft.Maui.Graphics } } - if (value.StartsWith("rgba", StringComparison.OrdinalIgnoreCase)) + if (value.StartsWith("rgba".AsSpan(), StringComparison.OrdinalIgnoreCase)) { - var op = value.IndexOf('('); - var cp = value.LastIndexOf(')'); - if (op < 0 || cp < 0 || cp < op) + if (!TryParseFourColorRanges(value, + out ReadOnlySpan quad0, + out ReadOnlySpan quad1, + out ReadOnlySpan quad2, + out ReadOnlySpan quad3)) { color = default; return false; } - var quad = value.Substring(op + 1, cp - op - 1).Split(','); - if (quad.Length != 4) - { - color = default; - return false; - } - - var r = ParseColorValue(quad[0], 255, acceptPercent: true); - var g = ParseColorValue(quad[1], 255, acceptPercent: true); - var b = ParseColorValue(quad[2], 255, acceptPercent: true); - var a = ParseOpacity(quad[3]); + var r = ParseColorValue(quad0, 255, acceptPercent: true); + var g = ParseColorValue(quad1, 255, acceptPercent: true); + var b = ParseColorValue(quad2, 255, acceptPercent: true); + var a = ParseOpacity(quad3); color = new Color((float)r, (float)g, (float)b, (float)a); return true; } - if (value.StartsWith("rgb", StringComparison.OrdinalIgnoreCase)) + if (value.StartsWith("rgb".AsSpan(), StringComparison.OrdinalIgnoreCase)) { - var op = value.IndexOf('('); - var cp = value.LastIndexOf(')'); - - if (op < 0 || cp < 0 || cp < op) + if (!TryParseThreeColorRanges(value, + out ReadOnlySpan triplet0, + out ReadOnlySpan triplet1, + out ReadOnlySpan triplet2)) { color = default; return false; } - var triplet = value.Substring(op + 1, cp - op - 1).Split(','); - if (triplet.Length != 3) - { - color = default; - return false; - } - - var r = ParseColorValue(triplet[0], 255, acceptPercent: true); - var g = ParseColorValue(triplet[1], 255, acceptPercent: true); - var b = ParseColorValue(triplet[2], 255, acceptPercent: true); + var r = ParseColorValue(triplet0, 255, acceptPercent: true); + var g = ParseColorValue(triplet1, 255, acceptPercent: true); + var b = ParseColorValue(triplet2, 255, acceptPercent: true); color = new Color((float)r, (float)g, (float)b); return true; } - if (value.StartsWith("hsla", StringComparison.OrdinalIgnoreCase)) + if (value.StartsWith("hsla".AsSpan(), StringComparison.OrdinalIgnoreCase)) { - var op = value.IndexOf('('); - var cp = value.LastIndexOf(')'); - if (op < 0 || cp < 0 || cp < op) + if (!TryParseFourColorRanges(value, + out ReadOnlySpan quad0, + out ReadOnlySpan quad1, + out ReadOnlySpan quad2, + out ReadOnlySpan quad3)) { color = default; return false; } - var quad = value.Substring(op + 1, cp - op - 1).Split(','); - if (quad.Length != 4) - { - color = default; - return false; - } - - var h = ParseColorValue(quad[0], 360, acceptPercent: false); - var s = ParseColorValue(quad[1], 100, acceptPercent: true); - var l = ParseColorValue(quad[2], 100, acceptPercent: true); - var a = ParseOpacity(quad[3]); + var h = ParseColorValue(quad0, 360, acceptPercent: false); + var s = ParseColorValue(quad1, 100, acceptPercent: true); + var l = ParseColorValue(quad2, 100, acceptPercent: true); + var a = ParseOpacity(quad3); color = Color.FromHsla(h, s, l, a); return true; } - if (value.StartsWith("hsl", StringComparison.OrdinalIgnoreCase)) + if (value.StartsWith("hsl".AsSpan(), StringComparison.OrdinalIgnoreCase)) { - var op = value.IndexOf('('); - var cp = value.LastIndexOf(')'); - if (op < 0 || cp < 0 || cp < op) + if (!TryParseThreeColorRanges(value, + out ReadOnlySpan triplet0, + out ReadOnlySpan triplet1, + out ReadOnlySpan triplet2)) { color = default; return false; } - var triplet = value.Substring(op + 1, cp - op - 1).Split(','); - if (triplet.Length != 3) - { - color = default; - return false; - } - - var h = ParseColorValue(triplet[0], 360, acceptPercent: false); - var s = ParseColorValue(triplet[1], 100, acceptPercent: true); - var l = ParseColorValue(triplet[2], 100, acceptPercent: true); + var h = ParseColorValue(triplet0, 360, acceptPercent: false); + var s = ParseColorValue(triplet1, 100, acceptPercent: true); + var l = ParseColorValue(triplet2, 100, acceptPercent: true); color = Color.FromHsla(h, s, l); return true; } - if (value.StartsWith("hsva", StringComparison.OrdinalIgnoreCase)) + if (value.StartsWith("hsva".AsSpan(), StringComparison.OrdinalIgnoreCase)) { - var op = value.IndexOf('('); - var cp = value.LastIndexOf(')'); - if (op < 0 || cp < 0 || cp < op) + if (!TryParseFourColorRanges(value, + out ReadOnlySpan quad0, + out ReadOnlySpan quad1, + out ReadOnlySpan quad2, + out ReadOnlySpan quad3)) { color = default; return false; } - var quad = value.Substring(op + 1, cp - op - 1).Split(','); - if (quad.Length != 4) - { - color = default; - return false; - } - - var h = ParseColorValue(quad[0], 360, acceptPercent: false); - var s = ParseColorValue(quad[1], 100, acceptPercent: true); - var v = ParseColorValue(quad[2], 100, acceptPercent: true); - var a = ParseOpacity(quad[3]); + var h = ParseColorValue(quad0, 360, acceptPercent: false); + var s = ParseColorValue(quad1, 100, acceptPercent: true); + var v = ParseColorValue(quad2, 100, acceptPercent: true); + var a = ParseOpacity(quad3); color = Color.FromHsva((float)h, (float)s, (float)v, (float)a); return true; } - if (value.StartsWith("hsv", StringComparison.OrdinalIgnoreCase)) + if (value.StartsWith("hsv".AsSpan(), StringComparison.OrdinalIgnoreCase)) { - var op = value.IndexOf('('); - var cp = value.LastIndexOf(')'); - if (op < 0 || cp < 0 || cp < op) + if (!TryParseThreeColorRanges(value, + out ReadOnlySpan triplet0, + out ReadOnlySpan triplet1, + out ReadOnlySpan triplet2)) { color = default; return false; } - var triplet = value.Substring(op + 1, cp - op - 1).Split(','); - if (triplet.Length != 3) - { - color = default; - return false; - } - - var h = ParseColorValue(triplet[0], 360, acceptPercent: false); - var s = ParseColorValue(triplet[1], 100, acceptPercent: true); - var v = ParseColorValue(triplet[2], 100, acceptPercent: true); + var h = ParseColorValue(triplet0, 360, acceptPercent: false); + var s = ParseColorValue(triplet1, 100, acceptPercent: true); + var v = ParseColorValue(triplet2, 100, acceptPercent: true); color = Color.FromHsv((float)h, (float)s, (float)v); return true; } - var namedColor = value?.ToLowerInvariant() 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, - "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, - "darkturquoise" => Colors.DarkTurquoise, - "darkviolet" => Colors.DarkViolet, - "deeppink" => Colors.DeepPink, - "deepskyblue" => Colors.DeepSkyBlue, - "dimgray" => Colors.DimGray, - "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, - "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, - "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, - "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 - }; - + var namedColor = GetNamedColor(value); if (namedColor != null) { color = namedColor; @@ -854,21 +737,303 @@ namespace Microsoft.Maui.Graphics return false; } - static double ParseColorValue(string elem, int maxValue, bool acceptPercent) + static Color GetNamedColor(ReadOnlySpan value) { - elem = elem.Trim(); - if (elem.EndsWith("%", StringComparison.Ordinal) && acceptPercent) + if (s_namedColors.TryGetValue(ComputeIgnoreCaseStringHash(value), out (string, Color) entry)) { - maxValue = 100; - elem = elem.Substring(0, elem.Length - 1); + if (value.Equals(entry.Item1.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return entry.Item2; + } } - return double.Parse(elem, NumberStyles.Number, CultureInfo.InvariantCulture).Clamp(0, maxValue) / maxValue; + + return null; } - static double ParseOpacity(string elem) - => double.Parse(elem, NumberStyles.Number, CultureInfo.InvariantCulture).Clamp(0, 1); + static readonly Dictionary s_namedColors = BuildNamedColorDictionary(); + + static Dictionary BuildNamedColorDictionary() + { + Dictionary namedColors = new Dictionary(capacity: 149); + void AddColor(string name, Color color) + { + uint hash = ComputeIgnoreCaseStringHash(name.AsSpan()); + + // If we encounter a hash-collison on the known color names, we will need to update our hashing algorithm + Debug.Assert(!namedColors.ContainsKey(hash)); + namedColors.Add(hash, (name, color)); + } + + AddColor("default", default); + AddColor("aliceblue", Colors.AliceBlue); + AddColor("antiquewhite", Colors.AntiqueWhite); + AddColor("aqua", Colors.Aqua); + AddColor("aquamarine", Colors.Aquamarine); + AddColor("azure", Colors.Azure); + AddColor("beige", Colors.Beige); + AddColor("bisque", Colors.Bisque); + AddColor("black", Colors.Black); + AddColor("blanchedalmond", Colors.BlanchedAlmond); + AddColor("blue", Colors.Blue); + AddColor("blueviolet", Colors.BlueViolet); + AddColor("brown", Colors.Brown); + AddColor("burlywood", Colors.BurlyWood); + AddColor("cadetblue", Colors.CadetBlue); + AddColor("chartreuse", Colors.Chartreuse); + AddColor("chocolate", Colors.Chocolate); + AddColor("coral", Colors.Coral); + AddColor("cornflowerblue", Colors.CornflowerBlue); + AddColor("cornsilk", Colors.Cornsilk); + AddColor("crimson", Colors.Crimson); + AddColor("cyan", Colors.Cyan); + AddColor("darkblue", Colors.DarkBlue); + AddColor("darkcyan", Colors.DarkCyan); + AddColor("darkgoldenrod", Colors.DarkGoldenrod); + AddColor("darkgray", Colors.DarkGray); + AddColor("darkgreen", Colors.DarkGreen); + AddColor("darkkhaki", Colors.DarkKhaki); + AddColor("darkmagenta", Colors.DarkMagenta); + AddColor("darkolivegreen", Colors.DarkOliveGreen); + AddColor("darkorange", Colors.DarkOrange); + AddColor("darkorchid", Colors.DarkOrchid); + AddColor("darkred", Colors.DarkRed); + AddColor("darksalmon", Colors.DarkSalmon); + AddColor("darkseagreen", Colors.DarkSeaGreen); + AddColor("darkslateblue", Colors.DarkSlateBlue); + AddColor("darkslategray", Colors.DarkSlateGray); + AddColor("darkturquoise", Colors.DarkTurquoise); + AddColor("darkviolet", Colors.DarkViolet); + AddColor("deeppink", Colors.DeepPink); + AddColor("deepskyblue", Colors.DeepSkyBlue); + AddColor("dimgray", Colors.DimGray); + AddColor("dodgerblue", Colors.DodgerBlue); + AddColor("firebrick", Colors.Firebrick); + AddColor("floralwhite", Colors.FloralWhite); + AddColor("forestgreen", Colors.ForestGreen); + AddColor("fuchsia", Colors.Fuchsia); + AddColor("gainsboro", Colors.Gainsboro); + AddColor("ghostwhite", Colors.GhostWhite); + AddColor("gold", Colors.Gold); + AddColor("goldenrod", Colors.Goldenrod); + AddColor("gray", Colors.Gray); + AddColor("green", Colors.Green); + AddColor("greenyellow", Colors.GreenYellow); + AddColor("honeydew", Colors.Honeydew); + AddColor("hotpink", Colors.HotPink); + AddColor("indianred", Colors.IndianRed); + AddColor("indigo", Colors.Indigo); + AddColor("ivory", Colors.Ivory); + AddColor("khaki", Colors.Khaki); + AddColor("lavender", Colors.Lavender); + AddColor("lavenderblush", Colors.LavenderBlush); + AddColor("lawngreen", Colors.LawnGreen); + AddColor("lemonchiffon", Colors.LemonChiffon); + AddColor("lightblue", Colors.LightBlue); + AddColor("lightcoral", Colors.LightCoral); + AddColor("lightcyan", Colors.LightCyan); + AddColor("lightgoldenrodyellow", Colors.LightGoldenrodYellow); + AddColor("lightgrey", Colors.LightGrey); + AddColor("lightgray", Colors.LightGray); + AddColor("lightgreen", Colors.LightGreen); + AddColor("lightpink", Colors.LightPink); + AddColor("lightsalmon", Colors.LightSalmon); + AddColor("lightseagreen", Colors.LightSeaGreen); + AddColor("lightskyblue", Colors.LightSkyBlue); + AddColor("lightslategray", Colors.LightSlateGray); + AddColor("lightsteelblue", Colors.LightSteelBlue); + AddColor("lightyellow", Colors.LightYellow); + AddColor("lime", Colors.Lime); + AddColor("limegreen", Colors.LimeGreen); + AddColor("linen", Colors.Linen); + AddColor("magenta", Colors.Magenta); + AddColor("maroon", Colors.Maroon); + AddColor("mediumaquamarine", Colors.MediumAquamarine); + AddColor("mediumblue", Colors.MediumBlue); + AddColor("mediumorchid", Colors.MediumOrchid); + AddColor("mediumpurple", Colors.MediumPurple); + AddColor("mediumseagreen", Colors.MediumSeaGreen); + AddColor("mediumslateblue", Colors.MediumSlateBlue); + AddColor("mediumspringgreen", Colors.MediumSpringGreen); + AddColor("mediumturquoise", Colors.MediumTurquoise); + AddColor("mediumvioletred", Colors.MediumVioletRed); + AddColor("midnightblue", Colors.MidnightBlue); + AddColor("mintcream", Colors.MintCream); + AddColor("mistyrose", Colors.MistyRose); + AddColor("moccasin", Colors.Moccasin); + AddColor("navajowhite", Colors.NavajoWhite); + AddColor("navy", Colors.Navy); + AddColor("oldlace", Colors.OldLace); + AddColor("olive", Colors.Olive); + AddColor("olivedrab", Colors.OliveDrab); + AddColor("orange", Colors.Orange); + AddColor("orangered", Colors.OrangeRed); + AddColor("orchid", Colors.Orchid); + AddColor("palegoldenrod", Colors.PaleGoldenrod); + AddColor("palegreen", Colors.PaleGreen); + AddColor("paleturquoise", Colors.PaleTurquoise); + AddColor("palevioletred", Colors.PaleVioletRed); + AddColor("papayawhip", Colors.PapayaWhip); + AddColor("peachpuff", Colors.PeachPuff); + AddColor("peru", Colors.Peru); + AddColor("pink", Colors.Pink); + AddColor("plum", Colors.Plum); + AddColor("powderblue", Colors.PowderBlue); + AddColor("purple", Colors.Purple); + AddColor("red", Colors.Red); + AddColor("rosybrown", Colors.RosyBrown); + AddColor("royalblue", Colors.RoyalBlue); + AddColor("saddlebrown", Colors.SaddleBrown); + AddColor("salmon", Colors.Salmon); + AddColor("sandybrown", Colors.SandyBrown); + AddColor("seagreen", Colors.SeaGreen); + AddColor("seashell", Colors.SeaShell); + AddColor("sienna", Colors.Sienna); + AddColor("silver", Colors.Silver); + AddColor("skyblue", Colors.SkyBlue); + AddColor("slateblue", Colors.SlateBlue); + AddColor("slategray", Colors.SlateGray); + AddColor("snow", Colors.Snow); + AddColor("springgreen", Colors.SpringGreen); + AddColor("steelblue", Colors.SteelBlue); + AddColor("tan", Colors.Tan); + AddColor("teal", Colors.Teal); + AddColor("thistle", Colors.Thistle); + AddColor("tomato", Colors.Tomato); + AddColor("transparent", Colors.Transparent); + AddColor("turquoise", Colors.Turquoise); + AddColor("violet", Colors.Violet); + AddColor("wheat", Colors.Wheat); + AddColor("white", Colors.White); + AddColor("whitesmoke", Colors.WhiteSmoke); + AddColor("yellow", Colors.Yellow); + AddColor("yellowgreen", Colors.YellowGreen); + + return namedColors; + } + + // this algorithm was taken from Roslyn when it creates a string jump table, modified to use ToLowerInvariant + // https://github.com/dotnet/roslyn/blob/6920e1e044dcadbc53e0af7d1582190fdcb88ec6/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs#L23 + static uint ComputeIgnoreCaseStringHash(ReadOnlySpan s) + { + uint hashCode = 2166136261u; + for (int i = 0; i < s.Length; i++) + { + hashCode = unchecked((char.ToLowerInvariant(s[i]) ^ hashCode) * 16777619); + } + return hashCode; + } + + static bool TryParseFourColorRanges( + ReadOnlySpan value, + out ReadOnlySpan quad0, + out ReadOnlySpan quad1, + out ReadOnlySpan quad2, + out ReadOnlySpan 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 value, + out ReadOnlySpan triplet0, + out ReadOnlySpan triplet1, + out ReadOnlySpan 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 double ParseColorValue(ReadOnlySpan elem, int maxValue, bool acceptPercent) + { + elem = elem.Trim(); + if (!elem.IsEmpty && elem[elem.Length - 1] == '%' && acceptPercent) + { + maxValue = 100; + elem = elem.Slice(0, elem.Length - 1); + } + return ParseDouble(elem).Clamp(0, maxValue) / maxValue; + } + + static double ParseOpacity(ReadOnlySpan elem) + => ParseDouble(elem).Clamp(0, 1); + + static double ParseDouble(ReadOnlySpan s) => + double.Parse( +#if NETSTANDARD2_0 + s.ToString(), +#else + s, +#endif + NumberStyles.Number, CultureInfo.InvariantCulture); + + static int ParseInt(ReadOnlySpan s) => + int.Parse( +#if NETSTANDARD2_0 + s.ToString(), +#else + s, +#endif + NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture); public static implicit operator Color(Vector4 color) => new Color(color); - } } diff --git a/src/Microsoft.Maui.Graphics/Microsoft.Maui.Graphics-net6.csproj b/src/Microsoft.Maui.Graphics/Microsoft.Maui.Graphics-net6.csproj index ae80157..d49003b 100644 --- a/src/Microsoft.Maui.Graphics/Microsoft.Maui.Graphics-net6.csproj +++ b/src/Microsoft.Maui.Graphics/Microsoft.Maui.Graphics-net6.csproj @@ -1,6 +1,6 @@  - netstandard2.0;netstandard2.1;net6.0;net6.0-ios;net6.0-android;net6.0-maccatalyst;net6.0-macos;net6.0-tizen6.5 + netstandard2.0;netstandard2.1;net6.0-ios;net6.0-android;net6.0-maccatalyst;net6.0-macos;net6.0-tizen6.5 $(TargetFrameworks);net6.0-windows10.0.18362 true @@ -20,6 +20,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/src/targets/MultiTargeting.targets b/src/targets/MultiTargeting.targets index 923b566..3e04ca7 100644 --- a/src/targets/MultiTargeting.targets +++ b/src/targets/MultiTargeting.targets @@ -55,12 +55,6 @@ - - - - - - diff --git a/tests/Microsoft.Maui.Graphics.Tests/Microsoft.Maui.Graphics.Tests-net6.csproj b/tests/Microsoft.Maui.Graphics.Tests/Microsoft.Maui.Graphics.Tests-net6.csproj index 8b1516d..b964241 100644 --- a/tests/Microsoft.Maui.Graphics.Tests/Microsoft.Maui.Graphics.Tests-net6.csproj +++ b/tests/Microsoft.Maui.Graphics.Tests/Microsoft.Maui.Graphics.Tests-net6.csproj @@ -1,7 +1,7 @@ - net6.0 + net5.0 false