mirror of https://github.com/SixLabors/ImageSharp
3 changed files with 503 additions and 0 deletions
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
@ -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]); |
|||
} |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue