Browse Source

Simplify YCbCr

pull/20/head
James Jackson-South 10 years ago
parent
commit
f63e9fe681
  1. 75
      src/ImageSharp/Colors/Colorspaces/YCbCr.cs
  2. 36
      tests/ImageSharp.Tests/Colors/ColorConversionTests.cs

75
src/ImageSharp/Colors/Colorspaces/YCbCr.cs

@ -7,59 +7,49 @@ namespace ImageSharp
{ {
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Numerics;
/// <summary> /// <summary>
/// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the /// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the full range standard used in digital imaging systems.
/// Full range standard used in digital imaging systems.
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/> /// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
/// </summary> /// </summary>
public struct YCbCr : IEquatable<YCbCr>, IAlmostEquatable<YCbCr, float> public struct YCbCr : IEquatable<YCbCr>
{ {
/// <summary> /// <summary>
/// Represents a <see cref="YCbCr"/> that has Y, Cb, and Cr values set to zero. /// Represents a <see cref="YCbCr"/> that has Y, Cb, and Cr values set to zero.
/// </summary> /// </summary>
public static readonly YCbCr Empty = default(YCbCr); public static readonly YCbCr Empty = default(YCbCr);
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001F;
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector3 backingVector;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="YCbCr"/> struct. /// Initializes a new instance of the <see cref="YCbCr"/> struct.
/// </summary> /// </summary>
/// <param name="y">The y luminance component.</param> /// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param> /// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param> /// <param name="cr">The cr chroma component.</param>
public YCbCr(float y, float cb, float cr) public YCbCr(byte y, byte cb, byte cr)
: this() : this()
{ {
this.backingVector = Vector3.Clamp(new Vector3(y, cb, cr), Vector3.Zero, new Vector3(255)); this.Y = y;
this.Cb = cb;
this.Cr = cr;
} }
/// <summary> /// <summary>
/// Gets the Y luminance component. /// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks> /// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary> /// </summary>
public float Y => this.backingVector.X; public byte Y { get; }
/// <summary> /// <summary>
/// Gets the Cb chroma component. /// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks> /// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary> /// </summary>
public float Cb => this.backingVector.Y; public byte Cb { get; }
/// <summary> /// <summary>
/// Gets the Cr chroma component. /// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks> /// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary> /// </summary>
public float Cr => this.backingVector.Z; public byte Cr { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="YCbCr"/> is empty. /// Gets a value indicating whether this <see cref="YCbCr"/> is empty.
@ -79,13 +69,13 @@ namespace ImageSharp
/// </returns> /// </returns>
public static implicit operator YCbCr(Color color) public static implicit operator YCbCr(Color color)
{ {
float r = color.R; byte r = color.R;
float g = color.G; byte g = color.G;
float b = color.B; byte b = color.B;
float y = (float)((0.299 * r) + (0.587 * g) + (0.114 * b)); byte y = (byte)((0.299F * r) + (0.587F * g) + (0.114F * b));
float cb = 128 + (float)((-0.168736 * r) - (0.331264 * g) + (0.5 * b)); byte cb = (byte)(128 + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)));
float cr = 128 + (float)((0.5 * r) - (0.418688 * g) - (0.081312 * b)); byte cr = (byte)(128 + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)));
return new YCbCr(y, cb, cr); return new YCbCr(y, cb, cr);
} }
@ -127,7 +117,13 @@ namespace ImageSharp
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return GetHashCode(this); unchecked
{
int hashCode = this.Y.GetHashCode();
hashCode = (hashCode * 397) ^ this.Cb.GetHashCode();
hashCode = (hashCode * 397) ^ this.Cr.GetHashCode();
return hashCode;
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -138,7 +134,7 @@ namespace ImageSharp
return "YCbCr [ Empty ]"; return "YCbCr [ Empty ]";
} }
return $"YCbCr [ Y={this.Y:#0.##}, Cb={this.Cb:#0.##}, Cr={this.Cr:#0.##} ]"; return $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]";
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -155,28 +151,7 @@ namespace ImageSharp
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(YCbCr other) public bool Equals(YCbCr other)
{ {
return this.AlmostEquals(other, Epsilon); return this.Y == other.Y && this.Cb == other.Cb && this.Cr == other.Cr;
}
/// <inheritdoc/>
public bool AlmostEquals(YCbCr other, float precision)
{
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision;
} }
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="YCbCr"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(YCbCr color) => color.backingVector.GetHashCode();
} }
} }

36
tests/ImageSharp.Tests/Colors/ColorConversionTests.cs

@ -31,23 +31,41 @@ namespace ImageSharp.Tests
Color color = Color.White; Color color = Color.White;
YCbCr yCbCr = color; YCbCr yCbCr = color;
Assert.Equal(255, yCbCr.Y, 0); Assert.Equal(255, yCbCr.Y);
Assert.Equal(128, yCbCr.Cb, 0); Assert.Equal(128, yCbCr.Cb);
Assert.Equal(128, yCbCr.Cr, 0); Assert.Equal(128, yCbCr.Cr);
// Black // Black
Color color2 = Color.Black; Color color2 = Color.Black;
YCbCr yCbCr2 = color2; YCbCr yCbCr2 = color2;
Assert.Equal(0, yCbCr2.Y, 0); Assert.Equal(0, yCbCr2.Y);
Assert.Equal(128, yCbCr2.Cb, 0); Assert.Equal(128, yCbCr2.Cb);
Assert.Equal(128, yCbCr2.Cr, 0); Assert.Equal(128, yCbCr2.Cr);
// Gray // Gray
Color color3 = Color.Gray; Color color3 = Color.Gray;
YCbCr yCbCr3 = color3; YCbCr yCbCr3 = color3;
Assert.Equal(128, yCbCr3.Y, 0); Assert.Equal(128, yCbCr3.Y);
Assert.Equal(128, yCbCr3.Cb, 0); Assert.Equal(128, yCbCr3.Cb);
Assert.Equal(128, yCbCr3.Cr, 0); Assert.Equal(128, yCbCr3.Cr);
//Assert.Equal(255, yCbCr.Y, 0);
//Assert.Equal(128, yCbCr.Cb, 0);
//Assert.Equal(128, yCbCr.Cr, 0);
//// Black
//Color color2 = Color.Black;
//YCbCr yCbCr2 = color2;
//Assert.Equal(0, yCbCr2.Y, 0);
//Assert.Equal(128, yCbCr2.Cb, 0);
//Assert.Equal(128, yCbCr2.Cr, 0);
//// Gray
//Color color3 = Color.Gray;
//YCbCr yCbCr3 = color3;
//Assert.Equal(128, yCbCr3.Y, 0);
//Assert.Equal(128, yCbCr3.Cb, 0);
//Assert.Equal(128, yCbCr3.Cr, 0);
} }
/// <summary> /// <summary>

Loading…
Cancel
Save