diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs
index c4ea2d762..090a35d9e 100644
--- a/src/ImageSharp/Colors/Color.cs
+++ b/src/ImageSharp/Colors/Color.cs
@@ -107,6 +107,17 @@ namespace ImageSharp
this.packedValue = Pack(ref vector);
}
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The packed value.
+ ///
+ public Color(uint packed)
+ {
+ this.packedValue = packed;
+ }
+
///
/// Gets or sets the red component.
///
diff --git a/src/ImageSharp/Colors/ColorDefinitions.cs b/src/ImageSharp/Colors/ColorDefinitions.cs
index 46e0f8709..5c1c30fbd 100644
--- a/src/ImageSharp/Colors/ColorDefinitions.cs
+++ b/src/ImageSharp/Colors/ColorDefinitions.cs
@@ -581,7 +581,7 @@ namespace ImageSharp
public static readonly Color Purple = new Color(128, 0, 128, 255);
///
- /// Represents a matching the W3C definition that has an hex value of #0.
+ /// Represents a matching the W3C definition that has an hex value of #663399.
///
public static readonly Color RebeccaPurple = new Color(102, 51, 153, 255);
diff --git a/src/ImageSharp/Colors/ColorTransforms.cs b/src/ImageSharp/Colors/ColorTransforms.cs
index e710b69e9..42a0ebc50 100644
--- a/src/ImageSharp/Colors/ColorTransforms.cs
+++ b/src/ImageSharp/Colors/ColorTransforms.cs
@@ -2,10 +2,8 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
-
namespace ImageSharp
{
- using System;
using System.Numerics;
///
@@ -18,6 +16,34 @@ namespace ImageSharp
///
public partial struct Color
{
+ ///
+ /// Adds the second color to the first.
+ ///
+ /// The first source color.
+ /// The second source color.
+ ///
+ /// The .
+ ///
+ public static Color operator +(Color left, Color right)
+ {
+ Vector4 add = left.ToVector4() + right.ToVector4();
+ return new Color(Pack(ref add));
+ }
+
+ ///
+ /// Subtracts the second color from the first.
+ ///
+ /// The first source color.
+ /// The second source color.
+ ///
+ /// The .
+ ///
+ public static Color operator -(Color left, Color right)
+ {
+ Vector4 sub = left.ToVector4() - right.ToVector4();
+ return new Color(Pack(ref sub));
+ }
+
///
/// Blends two colors by multiplication.
///
@@ -27,31 +53,108 @@ namespace ImageSharp
/// original color.
///
///
+ /// The backdrop color.
+ /// The source color.
+ ///
+ /// The .
+ ///
+ public static Color Multiply(Color backdrop, Color source)
+ {
+ if (source == Black)
+ {
+ return Black;
+ }
+
+ if (source == White)
+ {
+ return backdrop;
+ }
+
+ Vector4 vb = backdrop.ToVector4();
+ Vector4 vs = source.ToVector4();
+
+ Vector4 multiply = vb * vs;
+ multiply.W = vb.W;
+ return new Color(Pack(ref multiply));
+ }
+
+ ///
+ /// Multiplies the complements of the backdrop and source color values, then complements the result.
+ ///
+ /// 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.
+ ///
+ ///
+ /// The backdrop color.
/// The source color.
- /// The destination color.
///
/// The .
///
- public static Color Multiply(Color source, Color destination)
+ public static Color Screen(Color backdrop, Color source)
{
- if (destination == Color.Black)
+ if (source == Black)
{
- return Color.Black;
+ return backdrop;
}
- if (destination == Color.White)
+ if (source == White)
{
- return source;
+ return White;
}
- // TODO: This will use less memory than using Vector4
- // but we should test speed vs memory to see which is best balance.
- byte r = (byte)(source.R * destination.R).Clamp(0, 255);
- byte g = (byte)(source.G * destination.G).Clamp(0, 255);
- byte b = (byte)(source.B * destination.B).Clamp(0, 255);
- byte a = (byte)(source.A * destination.A).Clamp(0, 255);
+ Vector4 vb = backdrop.ToVector4();
+ Vector4 vs = source.ToVector4();
+
+ Vector4 subtract = Vector4.Clamp(vb + vs - (vb * vs), Vector4.Zero, Vector4.One);
+ subtract.W = vb.W;
+ return new Color(Pack(ref subtract));
+ }
- return new Color(r, g, b, a);
+ ///
+ /// Multiplies or screens the colors, depending on the source color value. The effect is similar to
+ /// shining a harsh spotlight on the backdrop.
+ ///
+ /// The backdrop color.
+ /// The source color.
+ ///
+ /// The .
+ ///
+ public static Color HardLight(Color backdrop, Color source)
+ {
+ // TODO: Why is this giving me nonsense?
+ // https://www.w3.org/TR/compositing-1/#blendinghardlight
+ // if(Cs <= 0.5)
+ // B(Cb, Cs) = Multiply(Cb, 2 x Cs)
+ // else
+ // B(Cb, Cs) = Screen(Cb, 2 x Cs -1)
+ Vector4 vs = source.ToVector4();
+ Vector4 blend = 2F * vs;
+ if (vs.X <= 0.5F && vs.Y <= 0.5F && vs.Z <= 0.5F)
+ {
+ return Multiply(backdrop, new Color(Pack(ref blend)));
+ }
+
+ blend = (2F * vs) - Vector4.One;
+ return Screen(backdrop, new Color(Pack(ref blend)));
+ }
+
+ ///
+ /// Multiplies or screens the colors, depending on the backdrop color value.
+ ///
+ /// 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.
+ ///
+ ///
+ /// The backdrop color.
+ /// The source color.
+ ///
+ /// The .
+ ///
+ public static Color Overlay(Color backdrop, Color source)
+ {
+ return HardLight(source, backdrop);
}
///
diff --git a/tests/ImageSharp.Tests/Colors/ColorTests.cs b/tests/ImageSharp.Tests/Colors/ColorTests.cs
index 23ebbec98..ec75ae6f7 100644
--- a/tests/ImageSharp.Tests/Colors/ColorTests.cs
+++ b/tests/ImageSharp.Tests/Colors/ColorTests.cs
@@ -3,11 +3,11 @@
// Licensed under the Apache License, Version 2.0.
//
-using System.Numerics;
-
namespace ImageSharp.Tests
{
using System;
+ using System.Numerics;
+
using Xunit;
///
diff --git a/tests/ImageSharp.Tests/Colors/ColorTransformTests.cs b/tests/ImageSharp.Tests/Colors/ColorTransformTests.cs
new file mode 100644
index 000000000..dc710549b
--- /dev/null
+++ b/tests/ImageSharp.Tests/Colors/ColorTransformTests.cs
@@ -0,0 +1,60 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests.Colors
+{
+ using Xunit;
+
+ ///
+ /// Tests the color transform algorithms.
+ ///
+ ///
+ public class ColorTransformTests
+ {
+ ///
+ /// Orange backdrop
+ ///
+ private static readonly Color Backdrop = new Color(204, 102, 0);
+
+ ///
+ /// Blue source
+ ///
+ private static readonly Color Source = new Color(0, 102, 153);
+
+ [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));
+ }
+ }
+}