Browse Source

Add transforms plus additional tests

af/merge-core
James Jackson-South 9 years ago
parent
commit
13455a0bb1
  1. 253
      src/ImageSharp/Colors/ColorVector.Transforms.cs
  2. 132
      tests/ImageSharp.Tests/Colors/ColorVectorTests.cs
  3. 118
      tests/ImageSharp.Tests/Colors/ColorVectorTransformTests.cs

253
src/ImageSharp/Colors/ColorVector.Transforms.cs

@ -0,0 +1,253 @@
// <copyright file="ColorVector.Transforms.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Numerics;
/// <summary>
/// Unpacked pixel type containing four 16-bit unsigned normalized values typically ranging from 0 to 1.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public partial struct ColorVector
{
/// <summary>
/// Adds the second color to the first.
/// </summary>
/// <param name="left">The first source color.</param>
/// <param name="right">The second source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector operator +(ColorVector left, ColorVector right)
{
return new ColorVector(left.backingVector + right.backingVector);
}
/// <summary>
/// Subtracts the second color from the first.
/// </summary>
/// <param name="left">The first source color.</param>
/// <param name="right">The second source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector operator -(ColorVector left, ColorVector right)
{
return new ColorVector(left.backingVector - right.backingVector);
}
/// <summary>
/// The blending formula simply selects the source color.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Normal(ColorVector backdrop, ColorVector source)
{
Vector4 normal = Vector4BlendTransforms.Normal(backdrop.backingVector, source.backingVector);
return new ColorVector(normal);
}
/// <summary>
/// Blends two colors by multiplication.
/// <remarks>
/// The source color is multiplied by the destination color and replaces the destination.
/// The resultant color is always at least as dark as either the source or destination color.
/// Multiplying any color with black results in black. Multiplying any color with white preserves the
/// original color.
/// </remarks>
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Multiply(ColorVector backdrop, ColorVector source)
{
Vector4 multiply = Vector4BlendTransforms.Multiply(backdrop.backingVector, source.backingVector);
return new ColorVector(multiply);
}
/// <summary>
/// Multiplies the complements of the backdrop and source color values, then complements the result.
/// <remarks>
/// The result color is always at least as light as either of the two constituent colors. Screening any
/// color with white produces white; screening with black leaves the original color unchanged.
/// The effect is similar to projecting multiple photographic slides simultaneously onto a single screen.
/// </remarks>
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Screen(ColorVector backdrop, ColorVector source)
{
Vector4 subtract = Vector4BlendTransforms.Screen(backdrop.backingVector, source.backingVector);
return new ColorVector(subtract);
}
/// <summary>
/// Multiplies or screens the colors, depending on the source color value. The effect is similar to
/// shining a harsh spotlight on the backdrop.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector HardLight(ColorVector backdrop, ColorVector source)
{
Vector4 hardlight = Vector4BlendTransforms.HardLight(backdrop.backingVector, source.backingVector);
return new ColorVector(hardlight);
}
/// <summary>
/// Multiplies or screens the colors, depending on the backdrop color value.
/// <remarks>
/// Source colors overlay the backdrop while preserving its highlights and shadows.
/// The backdrop color is not replaced but is mixed with the source color to reflect the lightness or darkness
/// of the backdrop.
/// </remarks>
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Overlay(ColorVector backdrop, ColorVector source)
{
Vector4 overlay = Vector4BlendTransforms.Overlay(backdrop.backingVector, source.backingVector);
return new ColorVector(overlay);
}
/// <summary>
/// Selects the darker of the backdrop and source colors.
/// The backdrop is replaced with the source where the source is darker; otherwise, it is left unchanged.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Darken(ColorVector backdrop, ColorVector source)
{
Vector4 darken = Vector4BlendTransforms.Darken(backdrop.backingVector, source.backingVector);
return new ColorVector(darken);
}
/// <summary>
/// Selects the lighter of the backdrop and source colors.
/// The backdrop is replaced with the source where the source is lighter; otherwise, it is left unchanged.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Lighten(ColorVector backdrop, ColorVector source)
{
Vector4 lighten = Vector4BlendTransforms.Lighten(backdrop.backingVector, source.backingVector);
return new ColorVector(lighten);
}
/// <summary>
/// Darkens or lightens the colors, depending on the source color value. The effect is similar to shining
/// a diffused spotlight on the backdrop.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector SoftLight(ColorVector backdrop, ColorVector source)
{
Vector4 softlight = Vector4BlendTransforms.SoftLight(backdrop.backingVector, source.backingVector);
return new ColorVector(softlight);
}
/// <summary>
/// Brightens the backdrop color to reflect the source color. Painting with black produces no changes.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector ColorDodge(ColorVector backdrop, ColorVector source)
{
Vector4 dodge = Vector4BlendTransforms.Dodge(backdrop.backingVector, source.backingVector);
return new ColorVector(dodge);
}
/// <summary>
/// Darkens the backdrop color to reflect the source color. Painting with white produces no change.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector ColorBurn(ColorVector backdrop, ColorVector source)
{
Vector4 burn = Vector4BlendTransforms.Burn(backdrop.backingVector, source.backingVector);
return new ColorVector(burn);
}
/// <summary>
/// Subtracts the darker of the two constituent colors from the lighter color.
/// Painting with white inverts the backdrop color; painting with black produces no change.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Difference(ColorVector backdrop, ColorVector source)
{
Vector4 difference = Vector4BlendTransforms.Difference(backdrop.backingVector, source.backingVector);
return new ColorVector(difference);
}
/// <summary>
/// Produces an effect similar to that of the <see cref="Difference"/> mode but lower in contrast. Painting with white
/// inverts the backdrop color; painting with black produces no change
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="ColorVector"/>.
/// </returns>
public static ColorVector Exclusion(ColorVector backdrop, ColorVector source)
{
Vector4 exclusion = Vector4BlendTransforms.Exclusion(backdrop.backingVector, source.backingVector);
return new ColorVector(exclusion);
}
/// <summary>
/// Linearly interpolates from one color to another based on the given weighting.
/// </summary>
/// <param name="from">The first color value.</param>
/// <param name="to">The second color value.</param>
/// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns>
/// The <see cref="ColorVector"/>
/// </returns>
public static ColorVector Lerp(ColorVector from, ColorVector to, float amount)
{
return new ColorVector(Vector4.Lerp(from.backingVector, to.backingVector, amount));
}
}
}

132
tests/ImageSharp.Tests/Colors/ColorVectorTests.cs

@ -0,0 +1,132 @@
// <copyright file="ColorVectorTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System.Numerics;
using System.Runtime.CompilerServices;
using Xunit;
/// <summary>
/// Tests the <see cref="ColorVector"/> struct.
/// </summary>
public class ColorVectorTests
{
/// <summary>
/// Tests the equality operators for equality.
/// </summary>
[Fact]
public void AreEqual()
{
ColorVector color1 = new ColorVector(0, 0, 0F);
ColorVector color2 = new ColorVector(0, 0, 0, 1F);
ColorVector color3 = ColorVector.FromHex("#000");
ColorVector color4 = ColorVector.FromHex("#000F");
ColorVector color5 = ColorVector.FromHex("#000000");
ColorVector color6 = ColorVector.FromHex("#000000FF");
Assert.Equal(color1, color2);
Assert.Equal(color1, color3);
Assert.Equal(color1, color4);
Assert.Equal(color1, color5);
Assert.Equal(color1, color6);
}
/// <summary>
/// Tests the equality operators for inequality.
/// </summary>
[Fact]
public void AreNotEqual()
{
ColorVector color1 = new ColorVector(1, 0, 0, 1);
ColorVector color2 = new ColorVector(0, 0, 0, 1);
ColorVector color3 = ColorVector.FromHex("#000");
ColorVector color4 = ColorVector.FromHex("#000000");
ColorVector color5 = ColorVector.FromHex("#FF000000");
Assert.NotEqual(color1, color2);
Assert.NotEqual(color1, color3);
Assert.NotEqual(color1, color4);
Assert.NotEqual(color1, color5);
}
/// <summary>
/// Tests whether the color constructor correctly assign properties.
/// </summary>
[Fact]
public void ConstructorAssignsProperties()
{
ColorVector color1 = new ColorVector(1, .1F, .133F, .864F);
Assert.Equal(1F, color1.R);
Assert.Equal(.1F, color1.G);
Assert.Equal(.133F, color1.B);
Assert.Equal(.864F, color1.A);
ColorVector color2 = new ColorVector(1, .1f, .133f);
Assert.Equal(1F, color2.R);
Assert.Equal(.1F, color2.G);
Assert.Equal(.133F, color2.B);
Assert.Equal(1F, color2.A);
ColorVector color4 = new ColorVector(new Vector3(1, .1f, .133f));
Assert.Equal(1F, color4.R);
Assert.Equal(.1F, color4.G);
Assert.Equal(.133F, color4.B);
Assert.Equal(1F, color4.A);
ColorVector color5 = new ColorVector(new Vector4(1, .1f, .133f, .5f));
Assert.Equal(1F, color5.R);
Assert.Equal(.1F, color5.G);
Assert.Equal(.133F, color5.B);
Assert.Equal(.5F, color5.A);
}
/// <summary>
/// Tests whether FromHex and ToHex work correctly.
/// </summary>
[Fact]
public void FromAndToHex()
{
ColorVector color = ColorVector.FromHex("#AABBCCDD");
Assert.Equal(170 / 255F, color.R);
Assert.Equal(187 / 255F, color.G);
Assert.Equal(204 / 255F, color.B);
Assert.Equal(221 / 255F, color.A);
color.A = 170 / 255F;
color.B = 187 / 255F;
color.G = 204 / 255F;
color.R = 221 / 255F;
Assert.Equal("DDCCBBAA", color.ToHex());
color.R = 0;
Assert.Equal("00CCBBAA", color.ToHex());
color.A = 255 / 255F;
Assert.Equal("00CCBBFF", color.ToHex());
}
/// <summary>
/// Tests that the individual float elements are layed out in RGBA order.
/// </summary>
[Fact]
public void FloatLayout()
{
ColorVector color = new ColorVector(1F, 2, 3, 4);
Vector4 colorBase = Unsafe.As<ColorVector, Vector4>(ref Unsafe.Add(ref color, 0));
float[] ordered = new float[4];
colorBase.CopyTo(ordered);
Assert.Equal(1, ordered[0]);
Assert.Equal(2, ordered[1]);
Assert.Equal(3, ordered[2]);
Assert.Equal(4, ordered[3]);
}
}
}

118
tests/ImageSharp.Tests/Colors/ColorVectorTransformTests.cs

@ -0,0 +1,118 @@
// <copyright file="ColorVectorTransformTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests.Colors
{
using Xunit;
/// <summary>
/// Tests the color transform algorithms. Test results match the output of CSS equivalents.
/// <see href="https://jsfiddle.net/jamessouth/L1v8r6kh/"/>
/// </summary>
public class ColorVectorTransformTests
{
private static readonly ApproximateFloatComparer FloatComparer = new ApproximateFloatComparer(0.01F);
/// <summary>
/// Orange backdrop
/// </summary>
private static readonly ColorVector Backdrop = new ColorVector(204, 102, 0);
/// <summary>
/// Blue source
/// </summary>
private static readonly ColorVector Source = new ColorVector(0, 102, 153);
[Fact]
public void Normal()
{
ColorVector normal = ColorVector.Normal(Backdrop, Source);
Assert.True(normal == Source);
}
[Fact]
public void Multiply()
{
Assert.Equal(ColorVector.Multiply(Backdrop, ColorVector.Black).ToVector4(), Backdrop.ToVector4(), FloatComparer);
Assert.Equal(ColorVector.Multiply(Backdrop, ColorVector.White).ToVector4(), ColorVector.White.ToVector4(), FloatComparer);
ColorVector multiply = ColorVector.Multiply(Backdrop, Source);
Assert.Equal(multiply.ToVector4(), new ColorVector(0, 41, 0).ToVector4(), FloatComparer);
}
[Fact]
public void Screen()
{
Assert.Equal(ColorVector.Screen(Backdrop, ColorVector.Black).ToVector4(), Backdrop.ToVector4(), FloatComparer);
Assert.Equal(ColorVector.Screen(Backdrop, ColorVector.White).ToVector4(), ColorVector.White.ToVector4(), FloatComparer);
ColorVector screen = ColorVector.Screen(Backdrop, Source);
Assert.Equal(screen.ToVector4(), new ColorVector(204, 163, 153).ToVector4(), FloatComparer);
}
[Fact]
public void HardLight()
{
ColorVector hardLight = ColorVector.HardLight(Backdrop, Source);
Assert.Equal(hardLight.ToVector4(), new ColorVector(0, 82, 51).ToVector4(), FloatComparer);
}
[Fact]
public void Overlay()
{
ColorVector overlay = ColorVector.Overlay(Backdrop, Source);
Assert.Equal(overlay.ToVector4(), new ColorVector(153, 82, 0).ToVector4(), FloatComparer);
}
[Fact]
public void Darken()
{
ColorVector darken = ColorVector.Darken(Backdrop, Source);
Assert.Equal(darken.ToVector4(), new ColorVector(0, 102, 0).ToVector4(), FloatComparer);
}
[Fact]
public void Lighten()
{
ColorVector lighten = ColorVector.Lighten(Backdrop, Source);
Assert.Equal(lighten.ToVector4(), new ColorVector(204, 102, 153).ToVector4(), FloatComparer);
}
[Fact]
public void SoftLight()
{
ColorVector softLight = ColorVector.SoftLight(Backdrop, Source);
Assert.Equal(softLight.ToVector4(), new ColorVector(163, 90, 0).ToVector4(), FloatComparer);
}
[Fact]
public void ColorDodge()
{
ColorVector colorDodge = ColorVector.ColorDodge(Backdrop, Source);
Assert.Equal(colorDodge.ToVector4(), new ColorVector(204, 170, 0).ToVector4(), FloatComparer);
}
[Fact]
public void ColorBurn()
{
ColorVector colorBurn = ColorVector.ColorBurn(Backdrop, Source);
Assert.Equal(colorBurn.ToVector4(), new ColorVector(0, 0, 0).ToVector4(), FloatComparer);
}
[Fact]
public void Difference()
{
ColorVector difference = ColorVector.Difference(Backdrop, Source);
Assert.Equal(difference.ToVector4(), new ColorVector(204, 0, 153).ToVector4(), FloatComparer);
}
[Fact]
public void Exclusion()
{
ColorVector exclusion = ColorVector.Exclusion(Backdrop, Source);
Assert.Equal(exclusion.ToVector4(), new ColorVector(204, 122, 153).ToVector4(), FloatComparer);
}
}
}
Loading…
Cancel
Save