mirror of https://github.com/SixLabors/ImageSharp
6 changed files with 587 additions and 26 deletions
@ -0,0 +1,253 @@ |
|||
// <copyright file="Vector4BlendTransforms.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; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Transform algorithms that match the equations defined in the W3C Compositing and Blending Level 1 specification.
|
|||
/// <see href="https://www.w3.org/TR/compositing-1/"/>
|
|||
/// </summary>
|
|||
public class Vector4BlendTransforms |
|||
{ |
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.001F; |
|||
|
|||
/// <summary>
|
|||
/// The blending formula simply selects the source vector.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Normal(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
return new Vector4(source.X, source.Y, source.Z, source.W); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Blends two vectors by multiplication.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Multiply(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 multiply = backdrop * source; |
|||
multiply.W = backdrop.W; |
|||
return multiply; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies the complements of the backdrop and source vector values, then complements the result.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Screen(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 subtract = backdrop + source - (backdrop * source); |
|||
subtract.W = backdrop.W; |
|||
return subtract; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies or screens the colors, depending on the source vector value.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 HardLight(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
return new Vector4(BlendOverlay(source.X, backdrop.X), BlendOverlay(source.Y, backdrop.Y), BlendOverlay(source.Z, backdrop.Z), backdrop.W); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies or screens the vectors, depending on the backdrop vector value.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Overlay(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
return new Vector4(BlendOverlay(backdrop.X, source.X), BlendOverlay(backdrop.Y, source.Y), BlendOverlay(backdrop.Z, source.Z), backdrop.W); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Selects the minimum of the backdrop and source vectors.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Darken(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 result = Vector4.Min(backdrop, source); |
|||
result.W = backdrop.W; |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Selects the max of the backdrop and source vector.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Lighten(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 result = Vector4.Max(backdrop, source); |
|||
result.W = backdrop.W; |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Selects the maximum or minimum of the vectors, depending on the source vector value.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 SoftLight(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
return new Vector4(BlendSoftLight(backdrop.X, source.X), BlendSoftLight(backdrop.Y, source.Y), BlendSoftLight(backdrop.Z, source.Z), backdrop.W); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Increases the backdrop vector to reflect the source vector.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Dodge(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
return new Vector4(BlendDodge(backdrop.X, source.X), BlendDodge(backdrop.Y, source.Y), BlendDodge(backdrop.Z, source.Z), backdrop.W); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decreases the backdrop vector to reflect the source vector.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Burn(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
return new Vector4(BlendBurn(backdrop.X, source.X), BlendBurn(backdrop.Y, source.Y), BlendBurn(backdrop.Z, source.Z), backdrop.W); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Subtracts the minimum of the two constituent vectors from the maximum vector.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Difference(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 result = Vector4.Abs(backdrop - source); |
|||
result.W = backdrop.W; |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Produces an effect similar to that of the <see cref="Difference"/> mode but lower in magnitude.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The backdrop vector.</param>
|
|||
/// <param name="source">The source vector.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Vector4"/>.
|
|||
/// </returns>
|
|||
public static Vector4 Exclusion(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
return new Vector4(BlendExclusion(backdrop.X, source.X), BlendExclusion(backdrop.Y, source.Y), BlendExclusion(backdrop.Z, source.Z), backdrop.W); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiplies or screens the color component, depending on the component value.
|
|||
/// </summary>
|
|||
/// <param name="b">The backdrop component.</param>
|
|||
/// <param name="s">The source component.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
private static float BlendOverlay(float b, float s) |
|||
{ |
|||
return b <= .5F ? (2F * b * s) : (1F - (2F * (1F - b) * (1F - s))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Darkens or lightens the backdrop component, depending on the source component value.
|
|||
/// </summary>
|
|||
/// <param name="b">The backdrop component.</param>
|
|||
/// <param name="s">The source component.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
private static float BlendSoftLight(float b, float s) |
|||
{ |
|||
return s <= .5F ? (2F * b * s + b * b * (1F - 2F * s)) : (float)(Math.Sqrt(b) * (2F * s - 1F) + 2F * b * (1F - s)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Brightens the backdrop component to reflect the source component.
|
|||
/// </summary>
|
|||
/// <param name="b">The backdrop component.</param>
|
|||
/// <param name="s">The source component.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
private static float BlendDodge(float b, float s) |
|||
{ |
|||
return Math.Abs(s - 1F) < Epsilon ? s : Math.Min(b / (1F - s), 1F); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Darkens the backdrop component to reflect the source component.
|
|||
/// </summary>
|
|||
/// <param name="b">The backdrop component.</param>
|
|||
/// <param name="s">The source component.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
private static float BlendBurn(float b, float s) |
|||
{ |
|||
return Math.Abs(s) < Epsilon ? s : Math.Max(1F - ((1F - b) / s), 0F); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Darkens the backdrop component to reflect the source component.
|
|||
/// </summary>
|
|||
/// <param name="b">The backdrop component.</param>
|
|||
/// <param name="s">The source component.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
private static float BlendExclusion(float b, float s) |
|||
{ |
|||
return b + s - 2F * b * s; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
// <copyright file="ColorTransformTests.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 ColorTransformTests |
|||
{ |
|||
/// <summary>
|
|||
/// Orange backdrop
|
|||
/// </summary>
|
|||
private static readonly Color Backdrop = new Color(204, 102, 0); |
|||
|
|||
/// <summary>
|
|||
/// Blue source
|
|||
/// </summary>
|
|||
private static readonly Color Source = new Color(0, 102, 153); |
|||
|
|||
[Fact] |
|||
public void Normal() |
|||
{ |
|||
Color normal = Color.Normal(Backdrop, Source); |
|||
Assert.True(normal == Source); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Multiply() |
|||
{ |
|||
Assert.True(Color.Multiply(Backdrop, Color.Black) == Color.Black); |
|||
Assert.True(Color.Multiply(Backdrop, Color.White) == Backdrop); |
|||
|
|||
Color multiply = Color.Multiply(Backdrop, Source); |
|||
Assert.True(multiply == new Color(0, 41, 0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Screen() |
|||
{ |
|||
Assert.True(Color.Screen(Backdrop, Color.Black) == Backdrop); |
|||
Assert.True(Color.Screen(Backdrop, Color.White) == Color.White); |
|||
|
|||
Color screen = Color.Screen(Backdrop, Source); |
|||
Assert.True(screen == new Color(204, 163, 153)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void HardLight() |
|||
{ |
|||
Color hardLight = Color.HardLight(Backdrop, Source); |
|||
Assert.True(hardLight == new Color(0, 82, 51)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Overlay() |
|||
{ |
|||
Color overlay = Color.Overlay(Backdrop, Source); |
|||
Assert.True(overlay == new Color(153, 82, 0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Darken() |
|||
{ |
|||
Color darken = Color.Darken(Backdrop, Source); |
|||
Assert.True(darken == new Color(0, 102, 0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Lighten() |
|||
{ |
|||
Color lighten = Color.Lighten(Backdrop, Source); |
|||
Assert.True(lighten == new Color(204, 102, 153)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SoftLight() |
|||
{ |
|||
Color softLight = Color.SoftLight(Backdrop, Source); |
|||
Assert.True(softLight == new Color(163, 90, 0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ColorDodge() |
|||
{ |
|||
Color colorDodge = Color.ColorDodge(Backdrop, Source); |
|||
Assert.True(colorDodge == new Color(204, 170, 0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ColorBurn() |
|||
{ |
|||
Color colorBurn = Color.ColorBurn(Backdrop, Source); |
|||
Assert.True(colorBurn == new Color(0, 0, 0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Difference() |
|||
{ |
|||
Color difference = Color.Difference(Backdrop, Source); |
|||
Assert.True(difference == new Color(204, 0, 153)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Exclusion() |
|||
{ |
|||
Color exclusion = Color.Exclusion(Backdrop, Source); |
|||
Assert.True(exclusion == new Color(204, 122, 153)); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue