Optimize Color Parsing
This method was showing up in a profile of the startup of the .NET Podcasts app. Optimize it to not allocate intermediate strings on .NET Core, and instead use Span operations.
This commit is contained in:
Родитель
cc1e48974d
Коммит
9f2e493f4c
|
@ -0,0 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Microsoft.Maui.Graphics
|
||||||
|
{
|
||||||
|
public partial class Color
|
||||||
|
{
|
||||||
|
private static Color FromArgbImplementation(string colorAsHex)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<char> 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<char> 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<char> 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<char> s) =>
|
||||||
|
int.Parse(s, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ namespace Microsoft.Maui.Graphics
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("Red={Red}, Green={Green}, Blue={Blue}, Alpha={Alpha}")]
|
[DebuggerDisplay("Red={Red}, Green={Green}, Blue={Blue}, Alpha={Alpha}")]
|
||||||
[TypeConverter(typeof(Converters.ColorTypeConverter))]
|
[TypeConverter(typeof(Converters.ColorTypeConverter))]
|
||||||
public class Color
|
public partial class Color
|
||||||
{
|
{
|
||||||
public readonly float Red;
|
public readonly float Red;
|
||||||
public readonly float Green;
|
public readonly float Green;
|
||||||
|
@ -382,50 +382,7 @@ namespace Microsoft.Maui.Graphics
|
||||||
return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f);
|
return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Color FromArgb(string colorAsHex)
|
public static Color FromArgb(string colorAsHex) => FromArgbImplementation(colorAsHex);
|
||||||
{
|
|
||||||
//Remove # if present
|
|
||||||
if (colorAsHex.IndexOf('#') != -1)
|
|
||||||
colorAsHex = colorAsHex.Replace("#", "");
|
|
||||||
|
|
||||||
int red = 0;
|
|
||||||
int green = 0;
|
|
||||||
int blue = 0;
|
|
||||||
int alpha = 255;
|
|
||||||
|
|
||||||
if (colorAsHex.Length == 6)
|
|
||||||
{
|
|
||||||
//#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FromRgba(red / 255f, green / 255f, blue / 255f, alpha / 255f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Color FromHsla(float h, float s, float l, float a = 1)
|
public static Color FromHsla(float h, float s, float l, float a = 1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0-ios;net6.0-android;net6.0-maccatalyst;net6.0-macos;net6.0-tizen6.5</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0;net6.0-ios;net6.0-android;net6.0-maccatalyst;net6.0-macos;net6.0-tizen6.5</TargetFrameworks>
|
||||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows')) and '$(MSBuildRuntimeType)' == 'Full'">$(TargetFrameworks);net6.0-windows10.0.18362</TargetFrameworks>
|
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows')) and '$(MSBuildRuntimeType)' == 'Full'">$(TargetFrameworks);net6.0-windows10.0.18362</TargetFrameworks>
|
||||||
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
|
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
@ -23,9 +23,6 @@
|
||||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" Condition="$(TargetFramework.StartsWith('netstandard'))" />
|
<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'))" />
|
<PackageReference Include="System.Drawing.Common" Version="6.0.0" Condition="$(TargetFramework.Contains('-windows'))" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Windows\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Update="iOS\GraphicsiOS.resx">
|
<EmbeddedResource Update="iOS\GraphicsiOS.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
|
|
@ -52,10 +52,15 @@
|
||||||
<None Include="**\*.Standard.cs"/>
|
<None Include="**\*.Standard.cs"/>
|
||||||
<Compile Remove="**\Standard\*.cs"/>
|
<Compile Remove="**\Standard\*.cs"/>
|
||||||
<None Include="**\Standard\*.cs"/>
|
<None Include="**\Standard\*.cs"/>
|
||||||
<Compile Remove="**\Standard\*.cs"/>
|
|
||||||
<Reference Include="System.Numerics" />
|
<Reference Include="System.Numerics" />
|
||||||
<Reference Include="System.Numerics.Vectors" />
|
<Reference Include="System.Numerics.Vectors" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
|
||||||
|
<Compile Remove="**\*.NETCoreApp.cs"/>
|
||||||
|
<None Include="**\*.NETCoreApp.cs"/>
|
||||||
|
<Compile Remove="**\NETCoreApp\*.cs"/>
|
||||||
|
<None Include="**\NETCoreApp\*.cs"/>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup Condition="$(TargetFramework.StartsWith('netcoreapp')) != true ">
|
<ItemGroup Condition="$(TargetFramework.StartsWith('netcoreapp')) != true ">
|
||||||
<Compile Remove="**\*.Win32.cs"/>
|
<Compile Remove="**\*.Win32.cs"/>
|
||||||
<None Include="**\*.Win32.cs"/>
|
<None Include="**\*.Win32.cs"/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче