Added support for converting to/from HSV and HSL

This commit is contained in:
Matthew Leibowitz 2016-09-01 23:38:48 +02:00
Родитель a76f50b6cb
Коммит 555a626395
5 изменённых файлов: 355 добавлений и 0 удалений

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

@ -82,6 +82,8 @@ namespace SkiaSharp
}
public partial struct SKColor {
private const float EPSILON = 0.001f;
public static readonly SKColor Empty;
private uint color;
@ -101,6 +103,21 @@ namespace SkiaSharp
color = (uint)(0xff000000u | (red << 16) | (green << 8) | blue);
}
public SKColor WithRed (byte red)
{
return new SKColor (red, Green, Blue, Alpha);
}
public SKColor WithGreen (byte green)
{
return new SKColor (Red, green, Blue, Alpha);
}
public SKColor WithBlue (byte blue)
{
return new SKColor (Red, Green, blue, Alpha);
}
public SKColor WithAlpha (byte alpha)
{
return new SKColor (Red, Green, Blue, alpha);
@ -111,6 +128,214 @@ namespace SkiaSharp
public byte Green => (byte)((color >> 8) & 0xff);
public byte Blue => (byte)((color) & 0xff);
public static SKColor FromHsl (float h, float s, float l, byte a = 255)
{
// convert from percentages
h = h / 360f;
s = s / 100f;
l = l / 100f;
// RGB results from 0 to 255
var r = l * 255f;
var g = l * 255f;
var b = l * 255f;
// HSL from 0 to 1
if (Math.Abs (s) > EPSILON)
{
float v2;
if (l < 0.5f)
v2 = l * (1f + s);
else
v2 = (l + s) - (s * l);
var v1 = 2f * l - v2;
r = 255 * HueToRgb (v1, v2, h + (1f / 3f));
g = 255 * HueToRgb (v1, v2, h);
b = 255 * HueToRgb (v1, v2, h - (1f / 3f));
}
return new SKColor ((byte)r, (byte)g, (byte)b, a);
}
private static float HueToRgb (float v1, float v2, float vH)
{
if (vH < 0f)
vH += 1f;
if (vH > 1f)
vH -= 1f;
if ((6f * vH) < 1f)
return (v1 + (v2 - v1) * 6f * vH);
if ((2f * vH) < 1f)
return (v2);
if ((3f * vH) < 2f)
return (v1 + (v2 - v1) * ((2f / 3f) - vH) * 6f);
return (v1);
}
public static SKColor FromHsv(float h, float s, float v, byte a = 255)
{
// convert from percentages
h = h / 360f;
s = s / 100f;
v = v / 100f;
// RGB results from 0 to 255
var r = v;
var g = v;
var b = v;
// HSL from 0 to 1
if (Math.Abs (s) > EPSILON)
{
h = h * 6f;
if (Math.Abs (h - 6f) < EPSILON)
h = 0f; // H must be < 1
var hInt = (int)h;
var v1 = v * (1f - s);
var v2 = v * (1f - s * (h - hInt));
var v3 = v * (1f - s * (1f - (h - hInt)));
if (hInt == 0)
{
r = v;
g = v3;
b = v1;
}
else if (hInt == 1)
{
r = v2;
g = v;
b = v1;
}
else if (hInt == 2)
{
r = v1;
g = v;
b = v3;
}
else if (hInt == 3)
{
r = v1;
g = v2;
b = v;
}
else if (hInt == 4)
{
r = v3;
g = v1;
b = v;
}
else
{
r = v;
g = v1;
b = v2;
}
}
// RGB results from 0 to 255
r = r * 255f;
g = g * 255f;
b = b * 255f;
return new SKColor ((byte)r, (byte)g, (byte)b, a);
}
public void ToHsl (out float h, out float s, out float l)
{
// RGB from 0 to 255
var r = (Red / 255f);
var g = (Green / 255f);
var b = (Blue / 255f);
var min = Math.Min (Math.Min (r, g), b); // min value of RGB
var max = Math.Max (Math.Max (r, g), b); // max value of RGB
var delta = max - min; // delta RGB value
// default to a gray, no chroma...
h = 0f;
s = 0f;
l = (max + min) / 2f;
// chromatic data...
if (Math.Abs (delta) > EPSILON)
{
if (l < 0.5f)
s = delta / (max + min);
else
s = delta / (2f - max - min);
var deltaR = (((max - r) / 6f) + (delta / 2f)) / delta;
var deltaG = (((max - g) / 6f) + (delta / 2f)) / delta;
var deltaB = (((max - b) / 6f) + (delta / 2f)) / delta;
if (Math.Abs (r - max) < EPSILON) // r == max
h = deltaB - deltaG;
else if (Math.Abs (g - max) < EPSILON) // g == max
h = (1f / 3f) + deltaR - deltaB;
else // b == max
h = (2f / 3f) + deltaG - deltaR;
if (h < 0f)
h += 1f;
if (h > 1f)
h -= 1f;
}
// convert to percentages
h = h * 360f;
s = s * 100f;
l = l * 100f;
}
public void ToHsv (out float h, out float s, out float v)
{
// RGB from 0 to 255
var r = (Red / 255f);
var g = (Green / 255f);
var b = (Blue / 255f);
var min = Math.Min (Math.Min (r, g), b); // min value of RGB
var max = Math.Max (Math.Max (r, g), b); // max value of RGB
var delta = max - min; // delta RGB value
// default to a gray, no chroma...
h = 0;
s = 0;
v = max;
// chromatic data...
if (Math.Abs (delta) > EPSILON)
{
s = delta / max;
var deltaR = (((max - r) / 6f) + (delta / 2f)) / delta;
var deltaG = (((max - g) / 6f) + (delta / 2f)) / delta;
var deltaB = (((max - b) / 6f) + (delta / 2f)) / delta;
if (Math.Abs (r - max) < EPSILON) // r == max
h = deltaB - deltaG;
else if (Math.Abs (g - max) < EPSILON) // g == max
h = (1f / 3f) + deltaR - deltaB;
else // b == max
h = (2f / 3f) + deltaG - deltaR;
if (h < 0f)
h += 1f;
if (h > 1f)
h -= 1f;
}
// convert to percentages
h = h * 360f;
s = s * 100f;
v = v * 100f;
}
public override string ToString ()
{
return string.Format (CultureInfo.InvariantCulture, "#{0:x2}{1:x2}{2:x2}{3:x2}", Alpha, Red, Green, Blue);

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

@ -35,6 +35,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="Xamarin.iOS" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />

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

@ -35,6 +35,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="Xamarin.TVOS" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />

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

@ -106,6 +106,9 @@
<Compile Include="..\Tests\SKPathTest.cs" >
<Link>SKPathTest.cs</Link>
</Compile>
<Compile Include="..\Tests\SKColorTest.cs" >
<Link>SKColorTest.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Content Include="..\..\samples\SharedDemo\content-font.ttf">

125
tests/Tests/SKColorTest.cs Normal file
Просмотреть файл

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using SKOtherColor = System.Tuple<float, float, float>;
using ToOtherColor = System.Tuple<SkiaSharp.SKColor, System.Tuple<float, float, float>, string>;
namespace SkiaSharp.Tests
{
[TestFixture]
public class SKColorTest : SKTest
{
private const float EPSILON = 0.01f;
[Test]
public void ColorWithComponent()
{
var color = new SKColor();
Assert.AreEqual(0, color.Red);
Assert.AreEqual(0, color.Green);
Assert.AreEqual(0, color.Blue);
Assert.AreEqual(0, color.Alpha);
var red = color.WithRed(255);
Assert.AreEqual(255, red.Red);
Assert.AreEqual(0, red.Green);
Assert.AreEqual(0, red.Blue);
Assert.AreEqual(0, red.Alpha);
var green = color.WithGreen(255);
Assert.AreEqual(0, green.Red);
Assert.AreEqual(255, green.Green);
Assert.AreEqual(0, green.Blue);
Assert.AreEqual(0, green.Alpha);
var blue = color.WithBlue(255);
Assert.AreEqual(0, blue.Red);
Assert.AreEqual(0, blue.Green);
Assert.AreEqual(255, blue.Blue);
Assert.AreEqual(0, blue.Alpha);
var alpha = color.WithAlpha(255);
Assert.AreEqual(0, alpha.Red);
Assert.AreEqual(0, alpha.Green);
Assert.AreEqual(0, alpha.Blue);
Assert.AreEqual(255, alpha.Alpha);
}
[Test]
public void ColorRgbToHsl()
{
var tuples = new List<ToOtherColor> {
new ToOtherColor(new SKColor(000, 000, 000), new SKOtherColor(000f, 000.0f, 000.0f), "Black"),
new ToOtherColor(new SKColor(255, 000, 000), new SKOtherColor(000f, 100.0f, 050.0f), "Red"),
new ToOtherColor(new SKColor(255, 255, 000), new SKOtherColor(060f, 100.0f, 050.0f), "Yellow"),
new ToOtherColor(new SKColor(255, 255, 255), new SKOtherColor(000f, 000.0f, 100.0f), "White"),
new ToOtherColor(new SKColor(128, 128, 128), new SKOtherColor(000f, 000.0f, 050.2f), "Gray"),
new ToOtherColor(new SKColor(128, 128, 000), new SKOtherColor(060f, 100.0f, 025.1f), "Olive"),
new ToOtherColor(new SKColor(000, 128, 000), new SKOtherColor(120f, 100.0f, 025.1f), "Green"),
new ToOtherColor(new SKColor(000, 000, 128), new SKOtherColor(240f, 100.0f, 025.1f), "Navy"),
};
foreach (var item in tuples)
{
// values
SKColor rgb = item.Item1;
SKOtherColor other = item.Item2;
// to HSL
float h, s, l;
rgb.ToHsl(out h, out s, out l);
Assert.AreEqual(other.Item1, h, EPSILON, item.Item3 + " H");
Assert.AreEqual(other.Item2, s, EPSILON, item.Item3 + " S");
Assert.AreEqual(other.Item3, l, EPSILON, item.Item3 + " L");
// to RGB
SKColor back = SKColor.FromHsl(other.Item1, other.Item2, other.Item3);
Assert.AreEqual(rgb.Red, back.Red, item.Item3 + " R");
Assert.AreEqual(rgb.Green, back.Green, item.Item3 + " G");
Assert.AreEqual(rgb.Blue, back.Blue, item.Item3 + " B");
Assert.AreEqual(rgb.Alpha, back.Alpha, item.Item3 + " A");
}
}
[Test]
public void ColorRgbToHsv()
{
var tuples = new List<ToOtherColor> {
new ToOtherColor(new SKColor(000, 000, 000), new SKOtherColor(000f, 000.0f, 000.0f), "Black"),
new ToOtherColor(new SKColor(255, 000, 000), new SKOtherColor(000f, 100.0f, 100.0f), "Red"),
new ToOtherColor(new SKColor(255, 255, 000), new SKOtherColor(060f, 100.0f, 100.0f), "Yellow"),
new ToOtherColor(new SKColor(255, 255, 255), new SKOtherColor(000f, 000.0f, 100.0f), "White"),
new ToOtherColor(new SKColor(128, 128, 128), new SKOtherColor(000f, 000.0f, 050.2f), "Gray"),
new ToOtherColor(new SKColor(128, 128, 000), new SKOtherColor(060f, 100.0f, 050.2f), "Olive"),
new ToOtherColor(new SKColor(000, 128, 000), new SKOtherColor(120f, 100.0f, 050.2f), "Green"),
new ToOtherColor(new SKColor(000, 000, 128), new SKOtherColor(240f, 100.0f, 050.2f), "Navy"),
};
foreach (var item in tuples)
{
// values
SKColor rgb = item.Item1;
SKOtherColor other = item.Item2;
// to HSV
float h, s, v;
rgb.ToHsv(out h, out s, out v);
Assert.AreEqual(other.Item1, h, EPSILON, item.Item3 + " H");
Assert.AreEqual(other.Item2, s, EPSILON, item.Item3 + " S");
Assert.AreEqual(other.Item3, v, EPSILON, item.Item3 + " V");
// to RGB
SKColor back = SKColor.FromHsv(other.Item1, other.Item2, other.Item3);
Assert.AreEqual(rgb.Red, back.Red, item.Item3 + " R");
Assert.AreEqual(rgb.Green, back.Green, item.Item3 + " G");
Assert.AreEqual(rgb.Blue, back.Blue, item.Item3 + " B");
Assert.AreEqual(rgb.Alpha, back.Alpha, item.Item3 + " A");
}
}
}
}