using System; namespace Prism.UnitTests { public class OklabTests : UnitTestSharp.TestFixture { private const double Tolerance = 1e-4; public static bool Near(double expected, double actual) { return Math.Abs(expected - actual) < Tolerance; } public static bool NearColor(Oklab expected, Oklab actual) { return Near(expected.Lightness, actual.Lightness) && Near(expected.A, actual.A) && Near(expected.B, actual.B) && Near(expected.Alpha, actual.Alpha); } public class CtorTests : UnitTestSharp.TestFixture { public void AssignsAllFourParts() { var color = new Oklab(0.25, -0.5, 0.75, 0.125); CheckEqual(0.25, color.Lightness); CheckEqual(-0.5, color.A); CheckEqual(0.75, color.B); CheckEqual(0.125, color.Alpha); } public void WithoutAlpha_IsOpaque() { CheckEqual(1.0, new Oklab(0.5, 0, 0).Alpha); } public void PartsOutsideWhatAColorShows_AreKept() { var color = new Oklab(1.5, -2, 3, 4); CheckEqual(1.5, color.Lightness); CheckEqual(-2.0, color.A); CheckEqual(3.0, color.B); CheckEqual(4.0, color.Alpha); } public void NaNLightness_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklab(double.NaN, 0, 0); } public void InfiniteA_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklab(0.5, double.PositiveInfinity, 0); } public void NaNB_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklab(0.5, 0, double.NaN); } public void InfiniteAlpha_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Oklab(0.5, 0, 0, double.NegativeInfinity); } } public class FromColorTests : UnitTestSharp.TestFixture { public void Black_IsAllZero() { CheckEqual(new Oklab(0, 0, 0), Oklab.FromColor(Color.FromArgb(0, 0, 0))); } public void White_IsFullLightnessWithNoColor() { CheckTrue(NearColor(new Oklab(1, 0, 0), Oklab.FromColor(Color.FromArgb(255, 255, 255)))); } public void Red_MatchesTheReferenceValues() { CheckTrue(NearColor( new Oklab(0.62796, 0.22486, 0.12585), Oklab.FromColor(Color.FromArgb(255, 0, 0)))); } public void Green_MatchesTheReferenceValues() { CheckTrue(NearColor( new Oklab(0.86644, -0.23389, 0.17950), Oklab.FromColor(Color.FromArgb(0, 255, 0)))); } public void Blue_MatchesTheReferenceValues() { CheckTrue(NearColor( new Oklab(0.45201, -0.03246, -0.31153), Oklab.FromColor(Color.FromArgb(0, 0, 255)))); } public void AlphaIsCarriedOverAsACoverageFraction() { CheckEqual(128 / 255.0, Oklab.FromColor(Color.FromArgb(128, 255, 0, 0)).Alpha); } } public class FromLinearTests : UnitTestSharp.TestFixture { public void LinearRed_MatchesTheReferenceValues() { CheckTrue(NearColor( new Oklab(0.62796, 0.22486, 0.12585), Oklab.FromLinear(new LinearSpaceColor(1, 0, 0)))); } public void ALightBrighterThanWhite_HasMoreThanFullLightness() { CheckTrue(Oklab.FromLinear(new LinearSpaceColor(4, 4, 4)).Lightness > 1); } public void ANegativeLight_HasNegativeLightness() { CheckTrue(Oklab.FromLinear(new LinearSpaceColor(-1, 0, 0)).Lightness < 0); } public void AlphaIsCarriedOverUnchanged() { CheckEqual(0.25, Oklab.FromLinear(new LinearSpaceColor(0, 0, 0, 0.25)).Alpha); } } public class ToLinearTests : UnitTestSharp.TestFixture { public void UndoesFromLinear() { var original = new LinearSpaceColor(0.2, 0.4, 0.6, 0.8); LinearSpaceColor roundTripped = Oklab.FromLinear(original).ToLinear(); CheckTrue(Near(original.Red, roundTripped.Red)); CheckTrue(Near(original.Green, roundTripped.Green)); CheckTrue(Near(original.Blue, roundTripped.Blue)); CheckEqual(0.8, roundTripped.Alpha); } public void ABrightRedBeyondTheGamut_GivesAChannelAboveOne() { LinearSpaceColor linear = new Oklab(1, 0.3, 0).ToLinear(); CheckTrue(Math.Abs(2.9202 - linear.Red) < 1e-2); } public void AGreenBeyondTheGamut_GivesANegativeRed() { CheckTrue(new Oklab(0.5, -0.3, 0).ToLinear().Red < 0); } } public class ToSrgbTests : UnitTestSharp.TestFixture { public void IsTheLinearLightColorEncoded() { var color = new Oklab(0.6, 0.05, -0.02, 0.5); CheckEqual(color.ToLinear().ToSrgb(), color.ToSrgb()); } } public class ToColorTests : UnitTestSharp.TestFixture { public void UndoesFromColor_ForBlackAndWhite() { CheckEqual(Color.FromArgb(0, 0, 0), Oklab.FromColor(Color.FromArgb(0, 0, 0)).ToColor()); CheckEqual(Color.FromArgb(255, 255, 255), Oklab.FromColor(Color.FromArgb(255, 255, 255)).ToColor()); } public void UndoesFromColor_ForAnOrange() { CheckEqual(Color.FromArgb(255, 165, 0), Oklab.FromColor(Color.FromArgb(255, 165, 0)).ToColor()); } public void UndoesFromColor_ForAColorWithAlpha() { Color original = Color.FromArgb(77, 12, 34, 56); CheckEqual(original, Oklab.FromColor(original).ToColor()); } public void UndoesFromColor_ForAColorWithBlueTheLargest() { Color original = Color.FromArgb(200, 40, 220); CheckEqual(original, Oklab.FromColor(original).ToColor()); } public void ABrightRedBeyondTheGamut_ClampsToFullRed() { CheckEqual((byte)255, new Oklab(1, 0.3, 0).ToColor().Red); } public void AGreenBeyondTheGamut_ClampsRedToZero() { CheckEqual((byte)0, new Oklab(0.5, -0.3, 0).ToColor().Red); } public void HalfLightness_IsDarkerThanMidGray() { CheckEqual(Color.FromArgb(99, 99, 99), new Oklab(0.5, 0, 0).ToColor()); } } public class LerpTests : UnitTestSharp.TestFixture { public void AtZero_IsTheStart() { var from = new Oklab(0.25, 0.5, -0.5, 1); CheckEqual(from, Oklab.Lerp(from, new Oklab(0.75, -0.5, 0.5, 0), 0)); } public void AtOne_IsTheEnd() { var to = new Oklab(0.75, -0.5, 0.25, 0.5); CheckEqual(to, Oklab.Lerp(new Oklab(0.25, 0.5, -0.5, 1), to, 1)); } public void BlendsEveryColorPartLinearly() { CheckEqual( new Oklab(0.5, 0, 0, 1), Oklab.Lerp(new Oklab(0.25, 0.5, -0.5, 1), new Oklab(0.75, -0.5, 0.5, 1), 0.5)); } public void AlphaBlendsLinearly_LeavingTheSameColorAlone() { Oklab blended = Oklab.Lerp( new Oklab(0.5, 0.25, 0.125, 0.25), new Oklab(0.5, 0.25, 0.125, 0.75), 0.25); CheckEqual(new Oklab(0.5, 0.25, 0.125, 0.375), blended); } public void ATransparentStart_AddsNoColor() { Oklab blended = Oklab.Lerp( new Oklab(0.25, 0.5, -0.5, 0), new Oklab(0.75, -0.5, 0.5, 1), 0.5); CheckEqual(new Oklab(0.75, -0.5, 0.5, 0.5), blended); } public void ATransparentEnd_AddsNoColor() { Oklab blended = Oklab.Lerp( new Oklab(0.25, 0.5, -0.5, 1), new Oklab(0.75, -0.5, 0.5, 0), 0.5); CheckEqual(new Oklab(0.25, 0.5, -0.5, 0.5), blended); } public void BothTransparent_BlendTheColorsLinearly() { Oklab blended = Oklab.Lerp( new Oklab(0.25, 0.5, -0.5, 0), new Oklab(0.75, -0.5, 0.5, 0), 0.5); CheckEqual(new Oklab(0.5, 0, 0, 0), blended); } public void UnequalAlphas_WeighEachColorByItsAlpha() { Oklab blended = Oklab.Lerp( new Oklab(0.25, 0.5, -0.5, 0.25), new Oklab(0.75, -0.5, 0.5, 0.75), 0.5); CheckEqual(new Oklab(0.625, -0.25, 0.25, 0.5), blended); } public void ANegativeAlpha_CountsAsTransparent() { Oklab blended = Oklab.Lerp( new Oklab(0.25, 0.5, -0.5, -1), new Oklab(0.75, -0.5, 0.5, 1), 0.5); CheckEqual(new Oklab(0.75, -0.5, 0.5, 0), blended); } public void BlackToWhiteHalfway_DisplaysAsADarkGray() { Color halfway = Oklab.Lerp( Oklab.FromColor(Colors.Black), Oklab.FromColor(Colors.White), 0.5).ToColor(); CheckEqual(Color.FromArgb(99, 99, 99), halfway); } public void AmountBelowZero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Oklab.Lerp(new Oklab(0, 0, 0), new Oklab(1, 0, 0), -0.1); } public void AmountAboveOne_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Oklab.Lerp(new Oklab(0, 0, 0), new Oklab(1, 0, 0), 1.1); } public void NaNAmount_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Oklab.Lerp(new Oklab(0, 0, 0), new Oklab(1, 0, 0), double.NaN); } } public class EqualityTests : UnitTestSharp.TestFixture { public void SameParts_AreEqual() { CheckTrue(new Oklab(0.1, 0.2, 0.3, 0.4).Equals(new Oklab(0.1, 0.2, 0.3, 0.4))); CheckTrue(new Oklab(0.1, 0.2, 0.3, 0.4) == new Oklab(0.1, 0.2, 0.3, 0.4)); CheckFalse(new Oklab(0.1, 0.2, 0.3, 0.4) != new Oklab(0.1, 0.2, 0.3, 0.4)); } public void AnyDifferentPart_IsNotEqual() { var baseColor = new Oklab(0.1, 0.2, 0.3, 0.4); CheckFalse(baseColor.Equals(new Oklab(0.9, 0.2, 0.3, 0.4))); CheckFalse(baseColor.Equals(new Oklab(0.1, 0.9, 0.3, 0.4))); CheckFalse(baseColor.Equals(new Oklab(0.1, 0.2, 0.9, 0.4))); CheckFalse(baseColor.Equals(new Oklab(0.1, 0.2, 0.3, 0.9))); CheckTrue(baseColor != new Oklab(0.1, 0.2, 0.3, 0.9)); } public void BoxedColor_IsEqualToTheSameColor() { CheckTrue(new Oklab(0.1, 0.2, 0.3).Equals((object)new Oklab(0.1, 0.2, 0.3))); } public void ANonOklab_IsNotEqual() { CheckFalse(new Oklab(0.1, 0.2, 0.3).Equals("color")); } public void EqualColors_HaveTheSameHashCode() { CheckEqual(new Oklab(0.1, 0.2, 0.3, 0.4).GetHashCode(), new Oklab(0.1, 0.2, 0.3, 0.4).GetHashCode()); } } } }