Merge pull request #343 from eerhardt/OptimizeColorParsing

Optimize Color Parsing
This commit is contained in:
jonlipsky 2022-03-10 12:54:00 -08:00 коммит произвёл GitHub
Родитель 8c22f8e8d1 4728bc41ed
Коммит 41f98ab2bf
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 654 добавлений и 328 удалений

Просмотреть файл

@ -36,6 +36,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphicsTester.MacCatalyst-
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphicsTester.Skia.Mac-net6", "samples\GraphicsTester.Skia.Mac\GraphicsTester.Skia.Mac-net6.csproj", "{883FB622-1BCB-437A-B20E-B5DBD3C53531}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Maui.Graphics.Benchmarks", "tests\Microsoft.Maui.Graphics.Benchmarks\Microsoft.Maui.Graphics.Benchmarks.csproj", "{52ABADFB-61CE-4124-BDFE-39A6F7465486}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -88,6 +90,10 @@ Global
{883FB622-1BCB-437A-B20E-B5DBD3C53531}.Debug|Any CPU.Build.0 = Debug|Any CPU
{883FB622-1BCB-437A-B20E-B5DBD3C53531}.Release|Any CPU.ActiveCfg = Release|Any CPU
{883FB622-1BCB-437A-B20E-B5DBD3C53531}.Release|Any CPU.Build.0 = Release|Any CPU
{52ABADFB-61CE-4124-BDFE-39A6F7465486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52ABADFB-61CE-4124-BDFE-39A6F7465486}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52ABADFB-61CE-4124-BDFE-39A6F7465486}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52ABADFB-61CE-4124-BDFE-39A6F7465486}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -104,6 +110,7 @@ Global
{093D3638-F133-46B0-AE23-D35D3FBFB6DD} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
{0B63725C-677E-43EE-ADD2-717CCD96DC23} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
{883FB622-1BCB-437A-B20E-B5DBD3C53531} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
{52ABADFB-61CE-4124-BDFE-39A6F7465486} = {D6F0522F-3BB5-4A10-953F-CA047F3D881A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0B07E734-6AE2-4A12-AD60-C072F3695854}

Просмотреть файл

@ -1,8 +1,8 @@
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
@ -337,91 +337,115 @@ namespace Microsoft.Maui.Graphics
return new Color((float)r, (float)g, (float)b, (float)a);
}
public static Color FromRgba(string colorAsHex)
{
//Remove # if present
if (colorAsHex.IndexOf('#') != -1)
colorAsHex = colorAsHex.Replace("#", "");
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.Length == 6)
if (!colorAsHex.IsEmpty)
{
//#RRGGBB
red = int.Parse(colorAsHex.Substring(0, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse(colorAsHex.Substring(2, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse(colorAsHex.Substring(4, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
}
else if (colorAsHex.Length == 3)
{
//#RGB
red = int.Parse($"{colorAsHex[0]}{colorAsHex[0]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse($"{colorAsHex[1]}{colorAsHex[1]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse($"{colorAsHex[2]}{colorAsHex[2]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
}
else if (colorAsHex.Length == 4)
{
//#RGBA
red = int.Parse($"{colorAsHex[0]}{colorAsHex[0]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse($"{colorAsHex[1]}{colorAsHex[1]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse($"{colorAsHex[2]}{colorAsHex[2]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
alpha = int.Parse($"{colorAsHex[3]}{colorAsHex[3]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
}
else if (colorAsHex.Length == 8)
{
//#RRGGBBAA
red = int.Parse(colorAsHex.Substring(0, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse(colorAsHex.Substring(2, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse(colorAsHex.Substring(4, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
alpha = int.Parse(colorAsHex.Substring(6, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
//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)
{
//Remove # if present
if (colorAsHex.IndexOf('#') != -1)
colorAsHex = colorAsHex.Replace("#", "");
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.Length == 6)
if (!colorAsHex.IsEmpty)
{
//#RRGGBB
red = int.Parse(colorAsHex.Substring(0, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse(colorAsHex.Substring(2, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse(colorAsHex.Substring(4, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
}
else if (colorAsHex.Length == 3)
{
//#RGB
red = int.Parse($"{colorAsHex[0]}{colorAsHex[0]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse($"{colorAsHex[1]}{colorAsHex[1]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse($"{colorAsHex[2]}{colorAsHex[2]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
}
else if (colorAsHex.Length == 4)
{
//#ARGB
alpha = int.Parse($"{colorAsHex[0]}{colorAsHex[0]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
red = int.Parse($"{colorAsHex[1]}{colorAsHex[1]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse($"{colorAsHex[2]}{colorAsHex[2]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse($"{colorAsHex[3]}{colorAsHex[3]}", NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
}
else if (colorAsHex.Length == 8)
{
//#AARRGGBB
alpha = int.Parse(colorAsHex.Substring(0, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
red = int.Parse(colorAsHex.Substring(2, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
green = int.Parse(colorAsHex.Substring(4, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
blue = int.Parse(colorAsHex.Substring(6, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
//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);
@ -565,12 +589,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<char> 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
{
@ -579,313 +605,143 @@ namespace Microsoft.Maui.Graphics
}
catch
{
color = default;
return false;
goto ReturnFalse;
}
}
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<char> quad0,
out ReadOnlySpan<char> quad1,
out ReadOnlySpan<char> quad2,
out ReadOnlySpan<char> quad3))
{
color = default;
return false;
goto ReturnFalse;
}
var quad = value.Substring(op + 1, cp - op - 1).Split(',');
if (quad.Length != 4)
{
color = default;
return false;
}
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);
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]);
if (!valid)
goto ReturnFalse;
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<char> triplet0,
out ReadOnlySpan<char> triplet1,
out ReadOnlySpan<char> triplet2))
{
color = default;
return false;
goto ReturnFalse;
}
var triplet = value.Substring(op + 1, cp - op - 1).Split(',');
if (triplet.Length != 3)
{
color = default;
return false;
}
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);
var r = ParseColorValue(triplet[0], 255, acceptPercent: true);
var g = ParseColorValue(triplet[1], 255, acceptPercent: true);
var b = ParseColorValue(triplet[2], 255, acceptPercent: true);
if (!valid)
goto ReturnFalse;
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<char> quad0,
out ReadOnlySpan<char> quad1,
out ReadOnlySpan<char> quad2,
out ReadOnlySpan<char> quad3))
{
color = default;
return false;
goto ReturnFalse;
}
var quad = value.Substring(op + 1, cp - op - 1).Split(',');
if (quad.Length != 4)
{
color = default;
return false;
}
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);
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]);
if (!valid)
goto ReturnFalse;
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<char> triplet0,
out ReadOnlySpan<char> triplet1,
out ReadOnlySpan<char> triplet2))
{
color = default;
return false;
goto ReturnFalse;
}
var triplet = value.Substring(op + 1, cp - op - 1).Split(',');
if (triplet.Length != 3)
{
color = default;
return false;
}
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);
var h = ParseColorValue(triplet[0], 360, acceptPercent: false);
var s = ParseColorValue(triplet[1], 100, acceptPercent: true);
var l = ParseColorValue(triplet[2], 100, acceptPercent: true);
if (!valid)
goto ReturnFalse;
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<char> quad0,
out ReadOnlySpan<char> quad1,
out ReadOnlySpan<char> quad2,
out ReadOnlySpan<char> quad3))
{
color = default;
return false;
goto ReturnFalse;
}
var quad = value.Substring(op + 1, cp - op - 1).Split(',');
if (quad.Length != 4)
{
color = default;
return false;
}
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);
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]);
if (!valid)
goto ReturnFalse;
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<char> triplet0,
out ReadOnlySpan<char> triplet1,
out ReadOnlySpan<char> triplet2))
{
color = default;
return false;
goto ReturnFalse;
}
var triplet = value.Substring(op + 1, cp - op - 1).Split(',');
if (triplet.Length != 3)
{
color = default;
return false;
}
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);
var h = ParseColorValue(triplet[0], 360, acceptPercent: false);
var s = ParseColorValue(triplet[1], 100, acceptPercent: true);
var v = ParseColorValue(triplet[2], 100, acceptPercent: true);
if (!valid)
goto ReturnFalse;
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;
@ -893,25 +749,327 @@ namespace Microsoft.Maui.Graphics
}
}
ReturnFalse:
color = default;
return false;
}
static double ParseColorValue(string elem, int maxValue, bool acceptPercent)
static Color GetNamedColor(ReadOnlySpan<char> 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<uint, (string, Color)> s_namedColors = BuildNamedColorDictionary();
static Dictionary<uint, (string, Color)> BuildNamedColorDictionary()
{
Dictionary<uint, (string, Color)> namedColors = new Dictionary<uint, (string, Color)>(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("darkgrey", Colors.DarkGrey);
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("darkslategrey", Colors.DarkSlateGrey);
AddColor("darkturquoise", Colors.DarkTurquoise);
AddColor("darkviolet", Colors.DarkViolet);
AddColor("deeppink", Colors.DeepPink);
AddColor("deepskyblue", Colors.DeepSkyBlue);
AddColor("dimgray", Colors.DimGray);
AddColor("dimgrey", Colors.DimGrey);
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("grey", Colors.Grey);
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("lightslategrey", Colors.LightSlateGrey);
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("slategrey", Colors.SlateGrey);
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<char> 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<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);
}
}

Просмотреть файл

@ -20,12 +20,10 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.4" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" Condition="$(TargetFramework.StartsWith('netstandard'))" />
<PackageReference Include="System.Drawing.Common" Version="6.0.0" Condition="$(TargetFramework.Contains('-windows'))" />
</ItemGroup>
<ItemGroup>
<Folder Include="Windows\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="iOS\GraphicsiOS.resx">
<Generator>ResXFileCodeGenerator</Generator>

Просмотреть файл

@ -18,6 +18,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
<PackageReference Include="System.Memory" Version="4.5.4" Condition="'$(TargetFramework)' != 'Xamarin.iOS10' AND '$(TargetFramework)' != 'Xamarin.Mac20'" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" Condition="$(TargetFramework.StartsWith('netstandard'))" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -52,7 +52,6 @@
<None Include="**\*.Standard.cs"/>
<Compile Remove="**\Standard\*.cs"/>
<None Include="**\Standard\*.cs"/>
<Compile Remove="**\Standard\*.cs"/>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,14 @@
using BenchmarkDotNet.Attributes;
namespace Microsoft.Maui.Graphics.Benchmarks
{
[MemoryDiagnoser]
public class ColorBenchmarker
{
[Benchmark]
public Color Parse() => Color.Parse("#979797");
[Benchmark]
public Color ParseBlack() => Color.Parse("Black");
}
}

Просмотреть файл

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.Maui.Graphics\Microsoft.Maui.Graphics-net6.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,12 @@
using BenchmarkDotNet.Running;
namespace Microsoft.Maui.Graphics.Benchmarks
{
class Program
{
static void Main(string[] args)
{
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
}
}
}

Просмотреть файл

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Maui.Graphics;
using Xunit;
@ -344,5 +345,125 @@ namespace Microsoft.Maui.Graphics.Tests
Assert.Equal(expectedComplement.Green, comp.Green, 3);
Assert.Equal(expectedComplement.Blue, comp.Blue, 3);
}
public static IEnumerable<object[]> TestFromRgbaValues()
{
yield return new object[] { "#111", Color.FromRgb(0x11, 0x11, 0x11) };
yield return new object[] { "#a222", Color.FromRgba(0xaa, 0x22, 0x22, 0x22) };
yield return new object[] { "#F2E2D2", Color.FromRgb(0xF2, 0xE2, 0xD2) };
yield return new object[] { "#C2F2E2D2", Color.FromRgba(0xC2, 0xF2, 0xE2, 0xD2) };
yield return new object[] { "111", Color.FromRgb(0x11, 0x11, 0x11) };
yield return new object[] { "a222", Color.FromRgba(0xaa, 0x22, 0x22, 0x22) };
yield return new object[] { "F2E2D2", Color.FromRgb(0xF2, 0xE2, 0xD2) };
yield return new object[] { "C2F2E2D2", Color.FromRgba(0xC2, 0xF2, 0xE2, 0xD2) };
}
[Theory]
[MemberData(nameof(TestFromRgbaValues))]
public void TestFromRgba(string value, Color expected)
{
Color actual = Color.FromRgba(value);
Assert.Equal(expected, actual);
}
public static IEnumerable<object[]> TestFromArgbValuesHash()
{
yield return new object[] { "#111", Color.FromRgb(0x11, 0x11, 0x11) };
yield return new object[] { "#a222", Color.FromRgba(0x22, 0x22, 0x22, 0xaa) };
yield return new object[] { "#F2E2D2", Color.FromRgb(0xF2, 0xE2, 0xD2) };
yield return new object[] { "#C2F2E2D2", Color.FromRgba(0xF2, 0xE2, 0xD2, 0xC2) };
}
public static IEnumerable<object[]> TestFromArgbValuesNoHash()
{
yield return new object[] { "111", Color.FromRgb(0x11, 0x11, 0x11) };
yield return new object[] { "a222", Color.FromRgba(0x22, 0x22, 0x22, 0xaa) };
yield return new object[] { "F2E2D2", Color.FromRgb(0xF2, 0xE2, 0xD2) };
yield return new object[] { "C2F2E2D2", Color.FromRgba(0xF2, 0xE2, 0xD2, 0xC2) };
}
[Theory]
[MemberData(nameof(TestFromArgbValuesHash))]
[MemberData(nameof(TestFromArgbValuesNoHash))]
public void TestFromArgb(string value, Color expected)
{
Color actual = Color.FromArgb(value);
Assert.Equal(expected, actual);
}
public static IEnumerable<object[]> TestParseValidValues()
{
foreach (object[] argb in TestFromArgbValuesHash())
{
yield return argb;
}
yield return new object[] { "rgb(255,0,0)", Color.FromRgb(255, 0, 0) };
yield return new object[] { "rgb(100%, 0%, 0%)", Color.FromRgb(255, 0, 0) };
yield return new object[] { "rgba(0, 255, 0, 0.7)", Color.FromRgba(0, 255, 0, 0.7f) };
yield return new object[] { "rgba(0%, 100%, 0%, 0.7)", Color.FromRgba(0, 255, 0, 0.7f) };
yield return new object[] { "hsl(120, 100%, 50%)", Color.FromHsla(120f / 360f, 1.0f, .5f) };
yield return new object[] { "hsl(120, 75, 20%)", Color.FromHsla(120f / 360f, .75f, .2f) };
yield return new object[] { "hsla(160, 100%, 50%, .4)", Color.FromHsla(160f / 360f, 1.0f, .5f, .4f) };
yield return new object[] { "hsla(160,100%,50%,.6)", Color.FromHsla(160f / 360f, 1.0f, .5f, .6f) };
yield return new object[] { "hsv(120, 85%, 35%)", Color.FromHsv(120f / 360f, .85f, .35f) };
yield return new object[] { "hsv(120, 85, 35)", Color.FromHsv(120f / 360f, .85f, .35f) };
yield return new object[] { "hsva(120, 100%, 50%, .8)", Color.FromHsva(120f / 360f, 1.0f, .5f, .8f) };
yield return new object[] { "hsva(120, 100, 50, .8)", Color.FromHsva(120f / 360f, 1.0f, .5f, .8f) };
}
[Theory]
[MemberData(nameof(TestParseValidValues))]
public void TestParseValid(string value, Color expected)
{
Assert.True(Color.TryParse(value, out Color actual));
Assert.Equal(expected, actual);
actual = Color.Parse(value);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("default")]
[InlineData("notAColor")]
[InlineData("#ZZZ")]
[InlineData("#12g")]
[InlineData("#1g3")]
[InlineData("#zyxv")]
[InlineData("222")]
[InlineData("rgb)255,0,0(")]
[InlineData("rgb255,0,0")]
[InlineData("rgba(255, 0, 0, 0.8")]
[InlineData("hsv(120, 100#, 50#)")]
[InlineData("hsv(120%, 100%, 50%)")]
[InlineData("hsva(120, 120%, 50%, a)")]
public void TestParseBad(string badValue)
{
Assert.False(Color.TryParse(badValue, out Color actual));
Assert.Throws<InvalidOperationException>(() => Color.Parse(badValue));
}
[Fact]
public void TestParseAllBuiltInColors()
{
var fields = typeof(Colors).GetFields(BindingFlags.Public | BindingFlags.Static);
Assert.True(fields.Length > 100, "we should have some Color fields");
foreach (FieldInfo field in fields)
{
string colorName = field.Name;
Color actual = Color.Parse(colorName);
Color expected = (Color)field.GetValue(null);
Assert.Equal(expected, actual);
}
}
}
}