diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
index 32b7500d3c..0def7e5aba 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
@@ -149,5 +149,18 @@ namespace ImageSharp.ColorSpaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLab ToCieLab(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToCieLab(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
index ec2bc5e1c2..5e5bc4ff3d 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
@@ -136,5 +136,18 @@ namespace ImageSharp.ColorSpaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToCieLch(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
index 684c142175..416193da90 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
@@ -137,5 +137,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieXyy ToCieXyy(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToCieXyy(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
index 21667b454d..c910b1cd0f 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
@@ -166,6 +166,21 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(linear);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieXyz ToCieXyz(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ // Conversion
+ Rgb rgb = this.ToRgb(color);
+
+ return this.ToCieXyz(rgb);
+ }
+
///
/// Gets the correct converter for the given rgb working space.
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
index 752b4b1bb6..45223719fd 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
@@ -138,5 +138,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(color);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Cmyk ToCmyk(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return CmykAndRgbConverter.Convert(rgb);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
index 8b58ebbf2f..a39b1cd33a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
@@ -138,5 +138,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(color);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsl ToHsl(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return HslAndRgbConverter.Convert(rgb);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
index 45acde225d..df62d8bc9b 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
@@ -133,5 +133,18 @@ namespace ImageSharp.ColorSpaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public HunterLab ToHunterLab(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToHunterLab(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
index e8a28740cf..cd0bad073a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
@@ -139,6 +139,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return RgbToLinearRgbConverter.Convert(color);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public LinearRgb ToLinearRgb(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+ return this.ToLinearRgb(rgb);
+ }
+
///
/// Gets the correct converter for the given rgb working space.
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
index 434c33d226..c888a9cda3 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
@@ -128,5 +128,18 @@ namespace ImageSharp.ColorSpaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Lms ToLms(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToLms(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
index e46af21d6a..1469888a1a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
@@ -133,5 +133,18 @@ namespace ImageSharp.ColorSpaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Rgb ToRgb(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ // Conversion
+ return YCbCrAndRgbConverter.Convert(color);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
new file mode 100644
index 0000000000..c670dde9d8
--- /dev/null
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
@@ -0,0 +1,156 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.ColorSpaces.Conversion
+{
+ using ImageSharp.ColorSpaces;
+ using ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr;
+
+ ///
+ /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
+ ///
+ public partial class ColorSpaceConverter
+ {
+ private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter();
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(CieLab color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToYCbCr(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToYCbCr(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(CieXyy color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToYCbCr(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(CieXyz color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return YCbCrAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(Cmyk color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return YCbCrAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(Hsl color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return YCbCrAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(HunterLab color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToYCbCr(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(LinearRgb color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return YCbCrAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(Lms color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToYCbCr(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(Rgb color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ return YCbCrAndRgbConverter.Convert(color);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs
new file mode 100644
index 0000000000..abd9fe6625
--- /dev/null
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs
@@ -0,0 +1,52 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr
+{
+ using System;
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
+ using ImageSharp.ColorSpaces;
+
+ ///
+ /// Color converter between YCbCr and Rgb
+ /// See for formulas.
+ ///
+ internal class YCbCrAndRgbConverter : IColorConversion, IColorConversion
+ {
+ private static readonly Vector3 MaxBytes = new Vector3(255F);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rgb Convert(YCbCr input)
+ {
+ DebugGuard.NotNull(input, nameof(input));
+
+ float y = input.Y;
+ float cb = input.Cb - 128F;
+ float cr = input.Cr - 128F;
+
+
+ return new Rgb(new Vector3(r, g, b) / MaxBytes);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public YCbCr Convert(Rgb input)
+ {
+ DebugGuard.NotNull(input, nameof(input));
+
+ Vector3 rgb = input.Vector * MaxBytes;
+ float r = rgb.X;
+ float g = rgb.Y;
+ float b = rgb.Z;
+
+ float y = (0.299F * r) + (0.587F * g) + (0.114F * b);
+
+ return new YCbCr(y, cb, cr);
+ }
+ }
+}
diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs
new file mode 100644
index 0000000000..19d0bcb8ad
--- /dev/null
+++ b/src/ImageSharp/ColorSpaces/YCbCr.cs
@@ -0,0 +1,183 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.ColorSpaces
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Represents an YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification for the JFIF use with Jpeg.
+ ///
+ ///
+ ///
+ public struct YCbCr : IColorVector, IEquatable, IAlmostEquatable
+ {
+ ///
+ /// Represents a that has Y, Cb, and Cr values set to zero.
+ ///
+ public static readonly YCbCr Empty = default(YCbCr);
+
+ ///
+ /// Vector which is used in clamping to the max value
+ ///
+ private static readonly Vector3 VectorMax = new Vector3(255F);
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private readonly Vector3 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The y luminance component.
+ /// The cb chroma component.
+ /// The cr chroma component.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public YCbCr(float y, float cb, float cr)
+ : this(new Vector3(y, cb, cr))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The vector representing the y, cb, cr components.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public YCbCr(Vector3 vector)
+ {
+ this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
+ }
+
+ ///
+ /// Gets the Y luminance component.
+ /// A value ranging between 0 and 255.
+ ///
+ public float Y
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector.X;
+ }
+
+ ///
+ /// Gets the Cb chroma component.
+ /// A value ranging between 0 and 255.
+ ///
+ public float Cb
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector.Y;
+ }
+
+ ///
+ /// Gets the Cr chroma component.
+ /// A value ranging between 0 and 255.
+ ///
+ public float Cr
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector.Z;
+ }
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ public Vector3 Vector
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector;
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(YCbCr left, YCbCr right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(YCbCr left, YCbCr right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.backingVector.GetHashCode();
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "YCbCr [ Empty ]";
+ }
+
+ return $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]";
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool Equals(object obj)
+ {
+ if (obj is YCbCr)
+ {
+ return this.Equals((YCbCr)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(YCbCr other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/Helpers/MathF.cs b/src/ImageSharp/Common/Helpers/MathF.cs
index c09862a3de..5ce5b5d5dd 100644
--- a/src/ImageSharp/Common/Helpers/MathF.cs
+++ b/src/ImageSharp/Common/Helpers/MathF.cs
@@ -201,6 +201,25 @@ namespace ImageSharp
return (float)Math.Round(f);
}
+ ///
+ /// Rounds a single-precision floating-point value to the nearest integer.
+ /// A parameter specifies how to round the value if it is midway between two numbers.
+ ///
+ /// A single-precision floating-point number to be rounded.
+ /// Specification for how to round if it is midway between two other numbers.
+ ///
+ /// The integer nearest . If is halfway between two integers, one of which is even
+ /// and the other odd, then determines which of the two is returned.
+ /// Note that this method returns a instead of an integral type.
+ ///
+ ///
+ /// is not a valid value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Round(float f, MidpointRounding mode)
+ {
+ return (float)Math.Round(f, mode);
+ }
+
///
/// Returns the sine of the specified angle.
///
diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs
index 1e1a5728fa..dd11a09b80 100644
--- a/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs
+++ b/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs
@@ -27,7 +27,8 @@ namespace ImageSharp.Tests.Colors
Lms.Empty,
LinearRgb.Empty,
Rgb.Empty,
- Hsl.Empty
+ Hsl.Empty,
+ YCbCr.Empty
};
public static readonly TheoryData