diff --git a/src/ImageSharp/Color.cs b/src/ImageSharp/Color.cs index 5b3de2eee..4f6cab97b 100644 --- a/src/ImageSharp/Color.cs +++ b/src/ImageSharp/Color.cs @@ -18,8 +18,7 @@ namespace SixLabors.ImageSharp public Color(Rgba32 pixel) { - this.data = default; - this.data.FromRgba32(pixel); + this.data = new Rgba64(pixel); } public Color(Vector4 vector) @@ -29,8 +28,7 @@ namespace SixLabors.ImageSharp } public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(new Rgba32(r, g, b, a)); - public static Color FromRgba(ushort r, ushort g, ushort b, ushort a) => new Color(new Rgba64(r, g, b, a)); - + public TPixel ToPixel() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 6d838a54d..cf9d4a552 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -57,20 +57,78 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Initializes a new instance of the struct. /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - public Rgba64(byte r, byte g, byte b, byte a) + /// A structure of 4 bytes in RGBA byte order. + public Rgba64(Rgba32 source) { - this.R = r; - this.G = g; - this.B = b; - this.A = a; + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); } /// - /// Gets or sets the RGB components of this struct as + /// Initializes a new instance of the struct. + /// + /// A structure of 4 bytes in BGRA byte order. + public Rgba64(Bgra32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + /// Initializes a new instance of the struct. + /// + /// A structure of 4 bytes in ARGB byte order. + public Rgba64(Argb32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + /// Initializes a new instance of the struct. + /// + /// A structure of 3 bytes in RGB byte order. + public Rgba64(Rgb24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ushort.MaxValue; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A structure of 3 bytes in BGR byte order. + public Rgba64(Bgr24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ushort.MaxValue; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The . + public Rgba64(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + this.R = (ushort)MathF.Round(vector.X); + this.G = (ushort)MathF.Round(vector.Y); + this.B = (ushort)MathF.Round(vector.Z); + this.A = (ushort)MathF.Round(vector.W); + } + + /// + /// Gets or sets the RGB components of this struct as . /// public Rgb48 Rgb { @@ -236,6 +294,74 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba64(Rgba64 source) => this = source; + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Rgba32 ToRgba32() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + byte a = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + return new Rgba32(r, g, b, a); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Bgra32 ToBgra32() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + byte a = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + return new Bgra32(r, g, b, a); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Argb32 ToArgb32() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + byte a = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + return new Argb32(r, g, b, a); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Rgb24 ToRgb24() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + return new Rgb24(r, g, b); + } + + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Bgr24 ToBgr24() + { + byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + byte b = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + return new Bgr24(r, g, b); + } + /// public override bool Equals(object obj) => obj is Rgba64 rgba64 && this.Equals(rgba64); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 51f80e0e1..55a2dda7a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -33,37 +33,50 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.One, new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue).ToVector4()); } - [Fact] - public void Rgba64_ToScaledVector4() + [Theory] + [InlineData(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)] + [InlineData(0, 0, 0, 0)] + [InlineData(ushort.MaxValue/2, 100, 2222, 33333)] + public void Rgba64_ToScaledVector4(ushort r, ushort g, ushort b, ushort a) { // arrange - var short2 = new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); + var short2 = new Rgba64(r, g, b, a); + + float max = ushort.MaxValue; + float rr = r / max; + float gg = g / max; + float bb = b / max; + float aa = a / max; // act Vector4 actual = short2.ToScaledVector4(); // assert - Assert.Equal(1, actual.X); - Assert.Equal(1, actual.Y); - Assert.Equal(1, actual.Z); - Assert.Equal(1, actual.W); + Assert.Equal(rr, actual.X); + Assert.Equal(gg, actual.Y); + Assert.Equal(bb, actual.Z); + Assert.Equal(aa, actual.W); } - [Fact] - public void Rgba64_FromScaledVector4() + [Theory] + [InlineData(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue)] + [InlineData(0, 0, 0, 0)] + [InlineData(ushort.MaxValue/2, 100, 2222, 33333)] + public void Rgba64_FromScaledVector4(ushort r, ushort g, ushort b, ushort a) { // arrange - var pixel = default(Rgba64); - var short4 = new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); - const ulong expected = 0xFFFFFFFFFFFFFFFF; - + + var source = new Rgba64(r, g, b, a); + // act - Vector4 scaled = short4.ToScaledVector4(); - pixel.FromScaledVector4(scaled); - ulong actual = pixel.PackedValue; + Vector4 scaled = source.ToScaledVector4(); + + Rgba64 actual = default; + actual.FromScaledVector4(scaled); + // assert - Assert.Equal(expected, actual); + Assert.Equal(source, actual); } [Fact] @@ -91,6 +104,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } + [Fact] public void Rgba64_FromBgra5551() @@ -108,5 +122,172 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, rgba.B); Assert.Equal(expected, rgba.A); } + + [Fact] + public void Equality_WhenTrue() + { + Rgba64 c1 = new Rgba64(100, 2000, 3000, 40000); + Rgba64 c2 = new Rgba64(100, 2000, 3000, 40000); + + Assert.True(c1.Equals(c2)); + Assert.True(c1.GetHashCode() == c2.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + Rgba64 c1 = new Rgba64(100, 2000, 3000, 40000); + Rgba64 c2 = new Rgba64(101, 2000, 3000, 40000); + Rgba64 c3 = new Rgba64(100, 2000, 3000, 40001); + + Assert.False(c1.Equals(c2)); + Assert.False(c2.Equals(c3)); + Assert.False(c3.Equals(c1)); + } + + [Fact] + public void Rgba64_FromRgba32() + { + var source = new Rgba32(20, 38, 76, 115); + var expected = new Rgba64(5140, 9766, 19532, 29555); + + Rgba64 actual = default; + actual.FromRgba32(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Rgba32() + { + var expected = new Rgba64(5140, 9766, 19532, 29555); + var source = new Rgba32(20, 38, 76, 115); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Bgra32() + { + var expected = new Rgba64(5140, 9766, 19532, 29555); + var source = new Bgra32(20, 38, 76, 115); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Argb32() + { + var expected = new Rgba64(5140, 9766, 19532, 29555); + var source = new Argb32(20, 38, 76, 115); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Rgb24() + { + var expected = new Rgba64(5140, 9766, 19532, ushort.MaxValue); + var source = new Rgb24(20, 38, 76); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Bgr24() + { + var expected = new Rgba64(5140, 9766, 19532, ushort.MaxValue); + var source = new Bgr24(20, 38, 76); + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ConstructFrom_Vector4() + { + Vector4 source = new Vector4(0f, 0.2f, 0.5f, 1f); + Rgba64 expected = default; + expected.FromScaledVector4(source); + + Rgba64 actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + + + [Fact] + public void ToRgba32_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Rgba32(20, 38, 76, 115); + + // act + var actual = source.ToRgba32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToBgra32_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Bgra32(20, 38, 76, 115); + + // act + var actual = source.ToBgra32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToArgb32_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Argb32(20, 38, 76, 115); + + // act + var actual = source.ToArgb32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToRgb24_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Rgb24(20, 38, 76); + + // act + var actual = source.ToRgb24(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ToBgr24_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Bgr24(20, 38, 76); + + // act + var actual = source.ToBgr24(); + + // assert + Assert.Equal(expected, actual); + } } }