Browse Source

Add initial transforms.

Hardlight is broken.
pull/30/head
James Jackson-South 9 years ago
parent
commit
70ea546da7
  1. 11
      src/ImageSharp/Colors/Color.cs
  2. 2
      src/ImageSharp/Colors/ColorDefinitions.cs
  3. 133
      src/ImageSharp/Colors/ColorTransforms.cs
  4. 4
      tests/ImageSharp.Tests/Colors/ColorTests.cs
  5. 60
      tests/ImageSharp.Tests/Colors/ColorTransformTests.cs

11
src/ImageSharp/Colors/Color.cs

@ -107,6 +107,17 @@ namespace ImageSharp
this.packedValue = Pack(ref vector);
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="packed">
/// The packed value.
/// </param>
public Color(uint packed)
{
this.packedValue = packed;
}
/// <summary>
/// Gets or sets the red component.
/// </summary>

2
src/ImageSharp/Colors/ColorDefinitions.cs

@ -581,7 +581,7 @@ namespace ImageSharp
public static readonly Color Purple = new Color(128, 0, 128, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #0.
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #663399.
/// </summary>
public static readonly Color RebeccaPurple = new Color(102, 51, 153, 255);

133
src/ImageSharp/Colors/ColorTransforms.cs

@ -2,10 +2,8 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
@ -18,6 +16,34 @@ namespace ImageSharp
/// </remarks>
public partial struct Color
{
/// <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="Color"/>.
/// </returns>
public static Color operator +(Color left, Color right)
{
Vector4 add = left.ToVector4() + right.ToVector4();
return new Color(Pack(ref add));
}
/// <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="Color"/>.
/// </returns>
public static Color operator -(Color left, Color right)
{
Vector4 sub = left.ToVector4() - right.ToVector4();
return new Color(Pack(ref sub));
}
/// <summary>
/// Blends two colors by multiplication.
/// <remarks>
@ -27,31 +53,108 @@ namespace ImageSharp
/// original color.
/// </remarks>
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
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));
}
/// <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>
/// <param name="destination">The destination color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
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);
/// <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="Color"/>.
/// </returns>
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)));
}
/// <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="Color"/>.
/// </returns>
public static Color Overlay(Color backdrop, Color source)
{
return HardLight(source, backdrop);
}
/// <summary>

4
tests/ImageSharp.Tests/Colors/ColorTests.cs

@ -3,11 +3,11 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Numerics;
namespace ImageSharp.Tests
{
using System;
using System.Numerics;
using Xunit;
/// <summary>

60
tests/ImageSharp.Tests/Colors/ColorTransformTests.cs

@ -0,0 +1,60 @@
// <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.
/// <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 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));
}
}
}
Loading…
Cancel
Save