diff --git a/ImageSharp.sln b/ImageSharp.sln index 6e389421e..8ec2cf53b 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,18 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.9 +VisualStudioVersion = 15.0.26403.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .travis.yml = .travis.yml appveyor.yml = appveyor.yml + codecov.yml = codecov.yml CodeCoverage.runsettings = CodeCoverage.runsettings - contributing.md = contributing.md - dotnet-latest.ps1 = dotnet-latest.ps1 + .github\CONTRIBUTING.md = .github\CONTRIBUTING.md features.md = features.md - global.json = global.json ImageSharp.ruleset = ImageSharp.ruleset ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings NuGet.config = NuGet.config diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs index ace929bd6..b68b02eff 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs @@ -117,7 +117,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs index df492a764..1af58bc62 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs @@ -150,7 +150,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 46444e550..0ef11e161 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -55,7 +55,7 @@ namespace ImageSharp.Drawing.Processors { DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs index 257eeb3ae..4e3cd01ae 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs @@ -141,7 +141,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs index 125b07bca..74c7081b3 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs @@ -89,7 +89,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 8ba0cbdd0..c4515db37 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -2,7 +2,7 @@ An extension to ImageSharp that allows the drawing of images, paths, and text. ImageSharp.Drawing - 1.0.0-alpha5 + 1.0.0-alpha6 James Jackson-South and contributors netstandard1.1 true @@ -39,8 +39,8 @@ All - - + + ..\..\ImageSharp.ruleset diff --git a/src/ImageSharp.Drawing/Text/GlyphBuilder.cs b/src/ImageSharp.Drawing/Text/GlyphBuilder.cs index ac5d01de7..0033a608c 100644 --- a/src/ImageSharp.Drawing/Text/GlyphBuilder.cs +++ b/src/ImageSharp.Drawing/Text/GlyphBuilder.cs @@ -48,7 +48,8 @@ namespace ImageSharp.Drawing /// /// Begins the glyph. /// - void IGlyphRenderer.BeginGlyph() + /// The offset that the glyph will be rendered at. + void IGlyphRenderer.BeginGlyph(Vector2 location) { this.builder.Clear(); } diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs index 7d3b12ea0..2de8222d6 100644 --- a/src/ImageSharp/Colors/Color.BulkOperations.cs +++ b/src/ImageSharp/Colors/Color.BulkOperations.cs @@ -10,9 +10,14 @@ namespace ImageSharp using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - /// - /// Conains the definition of - /// + /// + /// Unpacked pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. + /// The color components are stored in red, green, blue, and alpha order. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// public partial struct Color { /// @@ -37,15 +42,12 @@ namespace ImageSharp /// https://github.com/dotnet/corefx/issues/15957 /// /// - internal static unsafe void ToVector4SimdAligned( - BufferSpan sourceColors, - BufferSpan destVectors, - int count) + internal static unsafe void ToVector4SimdAligned(BufferSpan sourceColors, BufferSpan destVectors, int count) { if (!Vector.IsHardwareAccelerated) { throw new InvalidOperationException( - "Color.BulkOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!"); + "Color32.BulkOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!"); } int vecSize = Vector.Count; @@ -62,21 +64,23 @@ namespace ImageSharp int unpackedRawCount = count * 4; - uint* src = (uint*)sourceColors.PointerAtOffset; - uint* srcEnd = src + count; + ref uint src = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference()); - using (PinnedBuffer tempBuf = new PinnedBuffer( - unpackedRawCount + Vector.Count)) + using (Buffer tempBuf = new Buffer( + unpackedRawCount + Vector.Count)) { - uint* tPtr = (uint*)tempBuf.Pointer; uint[] temp = tempBuf.Array; float[] fTemp = Unsafe.As(temp); - UnpackedRGBA* dst = (UnpackedRGBA*)tPtr; - for (; src < srcEnd; src++, dst++) + ref UnpackedRGBA tempBase = ref Unsafe.As(ref tempBuf[0]); + + for (int i = 0; i < count; i++) { + uint sVal = Unsafe.Add(ref src, i); + ref UnpackedRGBA dst = ref Unsafe.Add(ref tempBase, i); + // This call is the bottleneck now: - dst->Load(*src); + dst.Load(sVal); } for (int i = 0; i < unpackedRawCount; i += vecSize) @@ -91,7 +95,7 @@ namespace ImageSharp vf.CopyTo(fTemp, i); } - BufferSpan.Copy(tempBuf, (BufferSpan)destVectors, unpackedRawCount); + BufferSpan.Copy(tempBuf.Span.AsBytes(), destVectors.AsBytes(), unpackedRawCount * sizeof(uint)); } } @@ -123,131 +127,163 @@ namespace ImageSharp } /// - internal override unsafe void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; + ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference()); + ref Color destRef = ref destColors.DangerousGetPinnableReference(); - for (int x = 0; x < count; x++) + for (int i = 0; i < count; i++) { - Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24)); + ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Color dp = ref Unsafe.Add(ref destRef, i); - source += 3; - destination += 4; + Unsafe.As(ref dp) = sp; + dp.A = 255; } } /// - internal override unsafe void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) + internal override void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; + ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference()); - for (int x = 0; x < count; x++) + for (int i = 0; i < count; i++) { - *destination = *(source + 0); - *(destination + 1) = *(source + 1); - *(destination + 2) = *(source + 2); + ref Color sp = ref Unsafe.Add(ref sourceRef, i); + ref RGB24 dp = ref Unsafe.Add(ref destRef, i); - source += 4; - destination += 3; + dp = Unsafe.As(ref sp); } } /// - internal override void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override unsafe void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - BufferSpan.Copy(sourceBytes, destColors, count); + BufferSpan.Copy(sourceBytes, destColors.AsBytes(), count * sizeof(Color)); } /// - internal override void ToXyzwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) + internal override unsafe void ToXyzwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - BufferSpan.Copy(sourceColors, destBytes, count); + BufferSpan.Copy(sourceColors.AsBytes(), destBytes, count * sizeof(Color)); } /// - internal override unsafe void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; + ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference()); + ref Color destRef = ref destColors.DangerousGetPinnableReference(); - for (int x = 0; x < count; x++) + for (int i = 0; i < count; i++) { - Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24)); + ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Color dp = ref Unsafe.Add(ref destRef, i); - source += 3; - destination += 4; + Unsafe.As(ref dp) = sp.ToZyx(); + dp.A = 255; } } /// - internal override unsafe void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) + internal override void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; + ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference()); - for (int x = 0; x < count; x++) + for (int i = 0; i < count; i++) { - *destination = *(source + 2); - *(destination + 1) = *(source + 1); - *(destination + 2) = *(source + 0); + ref Color sp = ref Unsafe.Add(ref sourceRef, i); + ref RGB24 dp = ref Unsafe.Add(ref destRef, i); - source += 4; - destination += 3; + dp = Unsafe.As(ref sp).ToZyx(); } } /// - internal override unsafe void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; + ref RGBA32 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference()); + ref Color destRef = ref destColors.DangerousGetPinnableReference(); - for (int x = 0; x < count; x++) + for (int i = 0; i < count; i++) { - Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24)); - - source += 4; - destination += 4; + ref RGBA32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Color dp = ref Unsafe.Add(ref destRef, i); + RGBA32 zyxw = sp.ToZyxw(); + dp = Unsafe.As(ref zyxw); } } /// - internal override unsafe void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) + internal override void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; + ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGBA32 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference()); - for (int x = 0; x < count; x++) + for (int i = 0; i < count; i++) { - *destination = *(source + 2); - *(destination + 1) = *(source + 1); - *(destination + 2) = *(source + 0); - *(destination + 3) = *(source + 3); - - source += 4; - destination += 4; + ref RGBA32 sp = ref Unsafe.As(ref Unsafe.Add(ref sourceRef, i)); + ref RGBA32 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToZyxw(); } } + /// + /// Helper struct to manipulate 3-byte RGB data. + /// + [StructLayout(LayoutKind.Sequential)] + private struct RGB24 + { + private byte x; + + private byte y; + + private byte z; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RGB24 ToZyx() => new RGB24 { x = this.z, y = this.y, z = this.x }; + } + + /// + /// Helper struct to manipulate 4-byte RGBA data. + /// + [StructLayout(LayoutKind.Sequential)] + private struct RGBA32 + { + private byte x; + + private byte y; + + private byte z; + + private byte w; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RGBA32 ToZyxw() => new RGBA32 { x = this.z, y = this.y, z = this.x, w = this.w }; + } + /// /// Value type to store -s unpacked into multiple -s. /// + [StructLayout(LayoutKind.Sequential)] private struct UnpackedRGBA { private uint r; + private uint g; + private uint b; + private uint a; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Load(uint p) { this.r = p; - this.g = p >> Color.GreenShift; - this.b = p >> Color.BlueShift; - this.a = p >> Color.AlphaShift; + this.g = p >> GreenShift; + this.b = p >> BlueShift; + this.a = p >> AlphaShift; } } } diff --git a/src/ImageSharp/Colors/ColorDefinitions.cs b/src/ImageSharp/Colors/Color.Definitions.cs similarity index 99% rename from src/ImageSharp/Colors/ColorDefinitions.cs rename to src/ImageSharp/Colors/Color.Definitions.cs index 65165289d..4bc0d486a 100644 --- a/src/ImageSharp/Colors/ColorDefinitions.cs +++ b/src/ImageSharp/Colors/Color.Definitions.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/Colors/ColorTransforms.cs b/src/ImageSharp/Colors/Color.Transforms.cs similarity index 90% rename from src/ImageSharp/Colors/ColorTransforms.cs rename to src/ImageSharp/Colors/Color.Transforms.cs index f61afbf5b..15935afc4 100644 --- a/src/ImageSharp/Colors/ColorTransforms.cs +++ b/src/ImageSharp/Colors/Color.Transforms.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -6,9 +6,10 @@ namespace ImageSharp { using System.Numerics; + using System.Runtime.CompilerServices; /// - /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. + /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in red, green, blue, and alpha order. /// /// @@ -25,10 +26,11 @@ namespace ImageSharp /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color operator +(Color left, Color right) { Vector4 add = left.ToVector4() + right.ToVector4(); - return new Color(Pack(ref add)); + return PackNew(ref add); } /// @@ -39,10 +41,11 @@ namespace ImageSharp /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color operator -(Color left, Color right) { Vector4 sub = left.ToVector4() - right.ToVector4(); - return new Color(Pack(ref sub)); + return PackNew(ref sub); } /// @@ -56,7 +59,7 @@ namespace ImageSharp public static Color Normal(Color backdrop, Color source) { Vector4 normal = Vector4BlendTransforms.Normal(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref normal)); + return PackNew(ref normal); } /// @@ -76,7 +79,7 @@ namespace ImageSharp public static Color Multiply(Color backdrop, Color source) { Vector4 multiply = Vector4BlendTransforms.Multiply(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref multiply)); + return PackNew(ref multiply); } /// @@ -95,7 +98,7 @@ namespace ImageSharp public static Color Screen(Color backdrop, Color source) { Vector4 subtract = Vector4BlendTransforms.Screen(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref subtract)); + return PackNew(ref subtract); } /// @@ -110,7 +113,7 @@ namespace ImageSharp public static Color HardLight(Color backdrop, Color source) { Vector4 hardlight = Vector4BlendTransforms.HardLight(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref hardlight)); + return PackNew(ref hardlight); } /// @@ -129,7 +132,7 @@ namespace ImageSharp public static Color Overlay(Color backdrop, Color source) { Vector4 overlay = Vector4BlendTransforms.Overlay(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref overlay)); + return PackNew(ref overlay); } /// @@ -144,7 +147,7 @@ namespace ImageSharp public static Color Darken(Color backdrop, Color source) { Vector4 darken = Vector4BlendTransforms.Darken(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref darken)); + return PackNew(ref darken); } /// @@ -159,7 +162,7 @@ namespace ImageSharp public static Color Lighten(Color backdrop, Color source) { Vector4 lighten = Vector4BlendTransforms.Lighten(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref lighten)); + return PackNew(ref lighten); } /// @@ -174,7 +177,7 @@ namespace ImageSharp public static Color SoftLight(Color backdrop, Color source) { Vector4 softlight = Vector4BlendTransforms.SoftLight(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref softlight)); + return PackNew(ref softlight); } /// @@ -188,7 +191,7 @@ namespace ImageSharp public static Color ColorDodge(Color backdrop, Color source) { Vector4 dodge = Vector4BlendTransforms.Dodge(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref dodge)); + return PackNew(ref dodge); } /// @@ -202,7 +205,7 @@ namespace ImageSharp public static Color ColorBurn(Color backdrop, Color source) { Vector4 burn = Vector4BlendTransforms.Burn(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref burn)); + return PackNew(ref burn); } /// @@ -217,7 +220,7 @@ namespace ImageSharp public static Color Difference(Color backdrop, Color source) { Vector4 difference = Vector4BlendTransforms.Difference(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref difference)); + return PackNew(ref difference); } /// @@ -232,7 +235,7 @@ namespace ImageSharp public static Color Exclusion(Color backdrop, Color source) { Vector4 exclusion = Vector4BlendTransforms.Exclusion(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref exclusion)); + return PackNew(ref exclusion); } /// @@ -249,7 +252,8 @@ namespace ImageSharp /// public static Color Lerp(Color from, Color to, float amount) { - return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount)); + Vector4 lerp = Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount); + return PackNew(ref lerp); } } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 597730937..fa83429df 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -5,10 +5,9 @@ namespace ImageSharp { - using System; - using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. @@ -18,8 +17,39 @@ namespace ImageSharp /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// + [StructLayout(LayoutKind.Explicit)] public partial struct Color : IPixel, IPackedVector { + /// + /// Gets or sets the red component. + /// + [FieldOffset(0)] + public byte R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(1)] + public byte G; + + /// + /// Gets or sets the blue component. + /// + [FieldOffset(2)] + public byte B; + + /// + /// Gets or sets the alpha component. + /// + [FieldOffset(3)] + public byte A; + + /// + /// The packed representation of the value. + /// + [FieldOffset(0)] + public uint Rgba; + /// /// The shift count for the red component /// @@ -50,11 +80,6 @@ namespace ImageSharp /// private static readonly Vector4 Half = new Vector4(0.5F); - /// - /// The packed value. - /// - private uint packedValue; - /// /// Initializes a new instance of the struct. /// @@ -62,9 +87,14 @@ namespace ImageSharp /// The green component. /// The blue component. /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(byte r, byte g, byte b, byte a = 255) + : this() { - this.packedValue = Pack(r, g, b, a); + this.R = r; + this.G = g; + this.B = b; + this.A = a; } /// @@ -74,9 +104,11 @@ namespace ImageSharp /// The green component. /// The blue component. /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(float r, float g, float b, float a = 1) + : this() { - this.packedValue = Pack(r, g, b, a); + this.Pack(r, g, b, a); } /// @@ -85,9 +117,11 @@ namespace ImageSharp /// /// The vector containing the components for the packed vector. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Vector3 vector) + : this() { - this.packedValue = Pack(ref vector); + this.Pack(ref vector); } /// @@ -96,9 +130,11 @@ namespace ImageSharp /// /// The vector containing the components for the packed vector. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Vector4 vector) + : this() { - this.packedValue = Pack(ref vector); + this = PackNew(ref vector); } /// @@ -107,96 +143,15 @@ namespace ImageSharp /// /// The packed value. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(uint packed) + : this() { - this.packedValue = packed; - } - - /// - /// Gets or sets the red component. - /// - public byte R - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> RedShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0xFFFFFF00 | (uint)value << RedShift; - } - } - - /// - /// Gets or sets the green component. - /// - public byte G - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> GreenShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0xFFFF00FF | (uint)value << GreenShift; - } - } - - /// - /// Gets or sets the blue component. - /// - public byte B - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> BlueShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0xFF00FFFF | (uint)value << BlueShift; - } - } - - /// - /// Gets or sets the alpha component. - /// - public byte A - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> AlphaShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0x00FFFFFF | (uint)value << AlphaShift; - } + this.Rgba = packed; } /// - public uint PackedValue - { - get - { - return this.packedValue; - } - - set - { - this.packedValue = value; - } - } + public uint PackedValue { get => this.Rgba; set => this.Rgba = value; } /// /// Compares two objects for equality. @@ -213,7 +168,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Color left, Color right) { - return left.packedValue == right.packedValue; + return left.Rgba == right.Rgba; } /// @@ -227,7 +182,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Color left, Color right) { - return left.packedValue != right.packedValue; + return left.Rgba != right.Rgba; } /// @@ -246,13 +201,16 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new Color.BulkOperations(); + public BulkPixelOperations CreateBulkOperations() => new BulkOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { - this.packedValue = Pack(x, y, z, w); + this.R = x; + this.G = y; + this.B = z; + this.A = w; } /// @@ -307,7 +265,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { - this.packedValue = Pack(ref vector); + this.Pack(ref vector); } /// @@ -327,7 +285,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Color other) { - return this.packedValue == other.packedValue; + return this.Rgba == other.Rgba; } /// @@ -342,65 +300,85 @@ namespace ImageSharp /// public override int GetHashCode() { - return this.packedValue.GetHashCode(); + unchecked + { + int hashCode = this.R.GetHashCode(); + hashCode = (hashCode * 397) ^ this.G.GetHashCode(); + hashCode = (hashCode * 397) ^ this.B.GetHashCode(); + hashCode = (hashCode * 397) ^ this.A.GetHashCode(); + return hashCode; + } } /// - /// Packs a into a uint. + /// Packs the four floats into a . /// - /// The vector containing the values to pack. - /// The containing the packed values. + /// The x-component + /// The y-component + /// The z-component + /// The w-component + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector4 vector) + private static uint Pack(byte x, byte y, byte z, byte w) { - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - return (uint)(((byte)vector.X << RedShift) - | ((byte)vector.Y << GreenShift) - | ((byte)vector.Z << BlueShift) - | (byte)vector.W << AlphaShift); + return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); } /// - /// Packs a into a uint. + /// Packs a into a color returning a new instance as a result. /// /// The vector containing the values to pack. - /// The containing the packed values. + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector3 vector) + private static Color PackNew(ref Vector4 vector) { - Vector4 value = new Vector4(vector, 1); - return Pack(ref value); + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + + return new Color((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W); } /// - /// Packs the four floats into a . + /// Packs the four floats into a color. /// /// The x-component /// The y-component /// The z-component /// The w-component - /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y, float z, float w) + private void Pack(float x, float y, float z, float w) { Vector4 value = new Vector4(x, y, z, w); - return Pack(ref value); + this.Pack(ref value); } /// - /// Packs the four floats into a . + /// Packs a into a uint. /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The + /// The vector containing the values to pack. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(byte x, byte y, byte z, byte w) + private void Pack(ref Vector3 vector) { - return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); + Vector4 value = new Vector4(vector, 1); + this.Pack(ref value); + } + + /// + /// Packs a into a color. + /// + /// The vector containing the values to pack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Pack(ref Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + + this.R = (byte)vector.X; + this.G = (byte)vector.Y; + this.B = (byte)vector.Z; + this.A = (byte)vector.W; } } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorVector.BulkOperations.cs b/src/ImageSharp/Colors/ColorVector.BulkOperations.cs new file mode 100644 index 000000000..ab32313c0 --- /dev/null +++ b/src/ImageSharp/Colors/ColorVector.BulkOperations.cs @@ -0,0 +1,27 @@ +namespace ImageSharp +{ + using System.Numerics; + + /// + /// Unpacked pixel type containing four 16-bit unsigned normalized values typically ranging from 0 to 1. + /// The color components are stored in red, green, blue, and alpha order. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public partial struct ColorVector + { + /// + /// implementation optimized for . + /// + internal class BulkOperations : BulkPixelOperations + { + /// + internal override unsafe void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count) + { + BufferSpan.Copy(sourceColors.AsBytes(), destVectors.AsBytes(), count * sizeof(Vector4)); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorVector.Definitions.cs b/src/ImageSharp/Colors/ColorVector.Definitions.cs new file mode 100644 index 000000000..150b7209c --- /dev/null +++ b/src/ImageSharp/Colors/ColorVector.Definitions.cs @@ -0,0 +1,728 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. + /// The color components are stored in red, green, blue, and alpha order. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public partial struct ColorVector + { + /// + /// Represents a matching the W3C definition that has an hex value of #F0F8FF. + /// + public static readonly ColorVector AliceBlue = NamedColors.AliceBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAEBD7. + /// + public static readonly ColorVector AntiqueWhite = NamedColors.AntiqueWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly ColorVector Aqua = NamedColors.Aqua; + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFFD4. + /// + public static readonly ColorVector Aquamarine = NamedColors.Aquamarine; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFFF. + /// + public static readonly ColorVector Azure = NamedColors.Azure; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5DC. + /// + public static readonly ColorVector Beige = NamedColors.Beige; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4C4. + /// + public static readonly ColorVector Bisque = NamedColors.Bisque; + + /// + /// Represents a matching the W3C definition that has an hex value of #000000. + /// + public static readonly ColorVector Black = NamedColors.Black; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEBCD. + /// + public static readonly ColorVector BlanchedAlmond = NamedColors.BlanchedAlmond; + + /// + /// Represents a matching the W3C definition that has an hex value of #0000FF. + /// + public static readonly ColorVector Blue = NamedColors.Blue; + + /// + /// Represents a matching the W3C definition that has an hex value of #8A2BE2. + /// + public static readonly ColorVector BlueViolet = NamedColors.BlueViolet; + + /// + /// Represents a matching the W3C definition that has an hex value of #A52A2A. + /// + public static readonly ColorVector Brown = NamedColors.Brown; + + /// + /// Represents a matching the W3C definition that has an hex value of #DEB887. + /// + public static readonly ColorVector BurlyWood = NamedColors.BurlyWood; + + /// + /// Represents a matching the W3C definition that has an hex value of #5F9EA0. + /// + public static readonly ColorVector CadetBlue = NamedColors.CadetBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFF00. + /// + public static readonly ColorVector Chartreuse = NamedColors.Chartreuse; + + /// + /// Represents a matching the W3C definition that has an hex value of #D2691E. + /// + public static readonly ColorVector Chocolate = NamedColors.Chocolate; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF7F50. + /// + public static readonly ColorVector Coral = NamedColors.Coral; + + /// + /// Represents a matching the W3C definition that has an hex value of #6495ED. + /// + public static readonly ColorVector CornflowerBlue = NamedColors.CornflowerBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF8DC. + /// + public static readonly ColorVector Cornsilk = NamedColors.Cornsilk; + + /// + /// Represents a matching the W3C definition that has an hex value of #DC143C. + /// + public static readonly ColorVector Crimson = NamedColors.Crimson; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly ColorVector Cyan = NamedColors.Cyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #00008B. + /// + public static readonly ColorVector DarkBlue = NamedColors.DarkBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #008B8B. + /// + public static readonly ColorVector DarkCyan = NamedColors.DarkCyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #B8860B. + /// + public static readonly ColorVector DarkGoldenrod = NamedColors.DarkGoldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #A9A9A9. + /// + public static readonly ColorVector DarkGray = NamedColors.DarkGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #006400. + /// + public static readonly ColorVector DarkGreen = NamedColors.DarkGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #BDB76B. + /// + public static readonly ColorVector DarkKhaki = NamedColors.DarkKhaki; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B008B. + /// + public static readonly ColorVector DarkMagenta = NamedColors.DarkMagenta; + + /// + /// Represents a matching the W3C definition that has an hex value of #556B2F. + /// + public static readonly ColorVector DarkOliveGreen = NamedColors.DarkOliveGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF8C00. + /// + public static readonly ColorVector DarkOrange = NamedColors.DarkOrange; + + /// + /// Represents a matching the W3C definition that has an hex value of #9932CC. + /// + public static readonly ColorVector DarkOrchid = NamedColors.DarkOrchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B0000. + /// + public static readonly ColorVector DarkRed = NamedColors.DarkRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #E9967A. + /// + public static readonly ColorVector DarkSalmon = NamedColors.DarkSalmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// + public static readonly ColorVector DarkSeaGreen = NamedColors.DarkSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #483D8B. + /// + public static readonly ColorVector DarkSlateBlue = NamedColors.DarkSlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #2F4F4F. + /// + public static readonly ColorVector DarkSlateGray = NamedColors.DarkSlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #00CED1. + /// + public static readonly ColorVector DarkTurquoise = NamedColors.DarkTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #9400D3. + /// + public static readonly ColorVector DarkViolet = NamedColors.DarkViolet; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF1493. + /// + public static readonly ColorVector DeepPink = NamedColors.DeepPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #00BFFF. + /// + public static readonly ColorVector DeepSkyBlue = NamedColors.DeepSkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #696969. + /// + public static readonly ColorVector DimGray = NamedColors.DimGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #1E90FF. + /// + public static readonly ColorVector DodgerBlue = NamedColors.DodgerBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #B22222. + /// + public static readonly ColorVector Firebrick = NamedColors.Firebrick; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAF0. + /// + public static readonly ColorVector FloralWhite = NamedColors.FloralWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #228B22. + /// + public static readonly ColorVector ForestGreen = NamedColors.ForestGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly ColorVector Fuchsia = NamedColors.Fuchsia; + + /// + /// Represents a matching the W3C definition that has an hex value of #DCDCDC. + /// + public static readonly ColorVector Gainsboro = NamedColors.Gainsboro; + + /// + /// Represents a matching the W3C definition that has an hex value of #F8F8FF. + /// + public static readonly ColorVector GhostWhite = NamedColors.GhostWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFD700. + /// + public static readonly ColorVector Gold = NamedColors.Gold; + + /// + /// Represents a matching the W3C definition that has an hex value of #DAA520. + /// + public static readonly ColorVector Goldenrod = NamedColors.Goldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #808080. + /// + public static readonly ColorVector Gray = NamedColors.Gray; + + /// + /// Represents a matching the W3C definition that has an hex value of #008000. + /// + public static readonly ColorVector Green = NamedColors.Green; + + /// + /// Represents a matching the W3C definition that has an hex value of #ADFF2F. + /// + public static readonly ColorVector GreenYellow = NamedColors.GreenYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFF0. + /// + public static readonly ColorVector Honeydew = NamedColors.Honeydew; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF69B4. + /// + public static readonly ColorVector HotPink = NamedColors.HotPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #CD5C5C. + /// + public static readonly ColorVector IndianRed = NamedColors.IndianRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #4B0082. + /// + public static readonly ColorVector Indigo = NamedColors.Indigo; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFF0. + /// + public static readonly ColorVector Ivory = NamedColors.Ivory; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0E68C. + /// + public static readonly ColorVector Khaki = NamedColors.Khaki; + + /// + /// Represents a matching the W3C definition that has an hex value of #E6E6FA. + /// + public static readonly ColorVector Lavender = NamedColors.Lavender; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF0F5. + /// + public static readonly ColorVector LavenderBlush = NamedColors.LavenderBlush; + + /// + /// Represents a matching the W3C definition that has an hex value of #7CFC00. + /// + public static readonly ColorVector LawnGreen = NamedColors.LawnGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFACD. + /// + public static readonly ColorVector LemonChiffon = NamedColors.LemonChiffon; + + /// + /// Represents a matching the W3C definition that has an hex value of #ADD8E6. + /// + public static readonly ColorVector LightBlue = NamedColors.LightBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #F08080. + /// + public static readonly ColorVector LightCoral = NamedColors.LightCoral; + + /// + /// Represents a matching the W3C definition that has an hex value of #E0FFFF. + /// + public static readonly ColorVector LightCyan = NamedColors.LightCyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAFAD2. + /// + public static readonly ColorVector LightGoldenrodYellow = NamedColors.LightGoldenrodYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #D3D3D3. + /// + public static readonly ColorVector LightGray = NamedColors.LightGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #90EE90. + /// + public static readonly ColorVector LightGreen = NamedColors.LightGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFB6C1. + /// + public static readonly ColorVector LightPink = NamedColors.LightPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA07A. + /// + public static readonly ColorVector LightSalmon = NamedColors.LightSalmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #20B2AA. + /// + public static readonly ColorVector LightSeaGreen = NamedColors.LightSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEFA. + /// + public static readonly ColorVector LightSkyBlue = NamedColors.LightSkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #778899. + /// + public static readonly ColorVector LightSlateGray = NamedColors.LightSlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #B0C4DE. + /// + public static readonly ColorVector LightSteelBlue = NamedColors.LightSteelBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFE0. + /// + public static readonly ColorVector LightYellow = NamedColors.LightYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF00. + /// + public static readonly ColorVector Lime = NamedColors.Lime; + + /// + /// Represents a matching the W3C definition that has an hex value of #32CD32. + /// + public static readonly ColorVector LimeGreen = NamedColors.LimeGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAF0E6. + /// + public static readonly ColorVector Linen = NamedColors.Linen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly ColorVector Magenta = NamedColors.Magenta; + + /// + /// Represents a matching the W3C definition that has an hex value of #800000. + /// + public static readonly ColorVector Maroon = NamedColors.Maroon; + + /// + /// Represents a matching the W3C definition that has an hex value of #66CDAA. + /// + public static readonly ColorVector MediumAquamarine = NamedColors.MediumAquamarine; + + /// + /// Represents a matching the W3C definition that has an hex value of #0000CD. + /// + public static readonly ColorVector MediumBlue = NamedColors.MediumBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #BA55D3. + /// + public static readonly ColorVector MediumOrchid = NamedColors.MediumOrchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #9370DB. + /// + public static readonly ColorVector MediumPurple = NamedColors.MediumPurple; + + /// + /// Represents a matching the W3C definition that has an hex value of #3CB371. + /// + public static readonly ColorVector MediumSeaGreen = NamedColors.MediumSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #7B68EE. + /// + public static readonly ColorVector MediumSlateBlue = NamedColors.MediumSlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FA9A. + /// + public static readonly ColorVector MediumSpringGreen = NamedColors.MediumSpringGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #48D1CC. + /// + public static readonly ColorVector MediumTurquoise = NamedColors.MediumTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #C71585. + /// + public static readonly ColorVector MediumVioletRed = NamedColors.MediumVioletRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #191970. + /// + public static readonly ColorVector MidnightBlue = NamedColors.MidnightBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5FFFA. + /// + public static readonly ColorVector MintCream = NamedColors.MintCream; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4E1. + /// + public static readonly ColorVector MistyRose = NamedColors.MistyRose; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4B5. + /// + public static readonly ColorVector Moccasin = NamedColors.Moccasin; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDEAD. + /// + public static readonly ColorVector NavajoWhite = NamedColors.NavajoWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #000080. + /// + public static readonly ColorVector Navy = NamedColors.Navy; + + /// + /// Represents a matching the W3C definition that has an hex value of #FDF5E6. + /// + public static readonly ColorVector OldLace = NamedColors.OldLace; + + /// + /// Represents a matching the W3C definition that has an hex value of #808000. + /// + public static readonly ColorVector Olive = NamedColors.Olive; + + /// + /// Represents a matching the W3C definition that has an hex value of #6B8E23. + /// + public static readonly ColorVector OliveDrab = NamedColors.OliveDrab; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA500. + /// + public static readonly ColorVector Orange = NamedColors.Orange; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF4500. + /// + public static readonly ColorVector OrangeRed = NamedColors.OrangeRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #DA70D6. + /// + public static readonly ColorVector Orchid = NamedColors.Orchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #EEE8AA. + /// + public static readonly ColorVector PaleGoldenrod = NamedColors.PaleGoldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #98FB98. + /// + public static readonly ColorVector PaleGreen = NamedColors.PaleGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #AFEEEE. + /// + public static readonly ColorVector PaleTurquoise = NamedColors.PaleTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #DB7093. + /// + public static readonly ColorVector PaleVioletRed = NamedColors.PaleVioletRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEFD5. + /// + public static readonly ColorVector PapayaWhip = NamedColors.PapayaWhip; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDAB9. + /// + public static readonly ColorVector PeachPuff = NamedColors.PeachPuff; + + /// + /// Represents a matching the W3C definition that has an hex value of #CD853F. + /// + public static readonly ColorVector Peru = NamedColors.Peru; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFC0CB. + /// + public static readonly ColorVector Pink = NamedColors.Pink; + + /// + /// Represents a matching the W3C definition that has an hex value of #DDA0DD. + /// + public static readonly ColorVector Plum = NamedColors.Plum; + + /// + /// Represents a matching the W3C definition that has an hex value of #B0E0E6. + /// + public static readonly ColorVector PowderBlue = NamedColors.PowderBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #800080. + /// + public static readonly ColorVector Purple = NamedColors.Purple; + + /// + /// Represents a matching the W3C definition that has an hex value of #663399. + /// + public static readonly ColorVector RebeccaPurple = NamedColors.RebeccaPurple; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF0000. + /// + public static readonly ColorVector Red = NamedColors.Red; + + /// + /// Represents a matching the W3C definition that has an hex value of #BC8F8F. + /// + public static readonly ColorVector RosyBrown = NamedColors.RosyBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #4169E1. + /// + public static readonly ColorVector RoyalBlue = NamedColors.RoyalBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B4513. + /// + public static readonly ColorVector SaddleBrown = NamedColors.SaddleBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #FA8072. + /// + public static readonly ColorVector Salmon = NamedColors.Salmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #F4A460. + /// + public static readonly ColorVector SandyBrown = NamedColors.SandyBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #2E8B57. + /// + public static readonly ColorVector SeaGreen = NamedColors.SeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF5EE. + /// + public static readonly ColorVector SeaShell = NamedColors.SeaShell; + + /// + /// Represents a matching the W3C definition that has an hex value of #A0522D. + /// + public static readonly ColorVector Sienna = NamedColors.Sienna; + + /// + /// Represents a matching the W3C definition that has an hex value of #C0C0C0. + /// + public static readonly ColorVector Silver = NamedColors.Silver; + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEEB. + /// + public static readonly ColorVector SkyBlue = NamedColors.SkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #6A5ACD. + /// + public static readonly ColorVector SlateBlue = NamedColors.SlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #708090. + /// + public static readonly ColorVector SlateGray = NamedColors.SlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAFA. + /// + public static readonly ColorVector Snow = NamedColors.Snow; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF7F. + /// + public static readonly ColorVector SpringGreen = NamedColors.SpringGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #4682B4. + /// + public static readonly ColorVector SteelBlue = NamedColors.SteelBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #D2B48C. + /// + public static readonly ColorVector Tan = NamedColors.Tan; + + /// + /// Represents a matching the W3C definition that has an hex value of #008080. + /// + public static readonly ColorVector Teal = NamedColors.Teal; + + /// + /// Represents a matching the W3C definition that has an hex value of #D8BFD8. + /// + public static readonly ColorVector Thistle = NamedColors.Thistle; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF6347. + /// + public static readonly ColorVector Tomato = NamedColors.Tomato; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly ColorVector Transparent = NamedColors.Transparent; + + /// + /// Represents a matching the W3C definition that has an hex value of #40E0D0. + /// + public static readonly ColorVector Turquoise = NamedColors.Turquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #EE82EE. + /// + public static readonly ColorVector Violet = NamedColors.Violet; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5DEB3. + /// + public static readonly ColorVector Wheat = NamedColors.Wheat; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly ColorVector White = NamedColors.White; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5F5. + /// + public static readonly ColorVector WhiteSmoke = NamedColors.WhiteSmoke; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFF00. + /// + public static readonly ColorVector Yellow = NamedColors.Yellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #9ACD32. + /// + public static readonly ColorVector YellowGreen = NamedColors.YellowGreen; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorVector.Transforms.cs b/src/ImageSharp/Colors/ColorVector.Transforms.cs new file mode 100644 index 000000000..a884f2618 --- /dev/null +++ b/src/ImageSharp/Colors/ColorVector.Transforms.cs @@ -0,0 +1,256 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. + /// The color components are stored in red, green, blue, and alpha order. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public partial struct ColorVector + { + /// + /// Adds the second color to the first. + /// + /// The first source color. + /// The second source color. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ColorVector operator +(ColorVector left, ColorVector right) + { + return new ColorVector(left.backingVector + right.backingVector); + } + + /// + /// Subtracts the second color from the first. + /// + /// The first source color. + /// The second source color. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ColorVector operator -(ColorVector left, ColorVector right) + { + return new ColorVector(left.backingVector - right.backingVector); + } + + /// + /// The blending formula simply selects the source color. + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector Normal(ColorVector backdrop, ColorVector source) + { + Vector4 normal = Vector4BlendTransforms.Normal(backdrop.backingVector, source.backingVector); + return new ColorVector(normal); + } + + /// + /// Blends two colors by multiplication. + /// + /// The source color is multiplied by the destination color and replaces the destination. + /// The resultant color is always at least as dark as either the source or destination color. + /// Multiplying any color with black results in black. Multiplying any color with white preserves the + /// original color. + /// + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector Multiply(ColorVector backdrop, ColorVector source) + { + Vector4 multiply = Vector4BlendTransforms.Multiply(backdrop.backingVector, source.backingVector); + return new ColorVector(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 . + /// + public static ColorVector Screen(ColorVector backdrop, ColorVector source) + { + Vector4 subtract = Vector4BlendTransforms.Screen(backdrop.backingVector, source.backingVector); + return new ColorVector(subtract); + } + + /// + /// 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 ColorVector HardLight(ColorVector backdrop, ColorVector source) + { + Vector4 hardlight = Vector4BlendTransforms.HardLight(backdrop.backingVector, source.backingVector); + return new ColorVector(hardlight); + } + + /// + /// 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 ColorVector Overlay(ColorVector backdrop, ColorVector source) + { + Vector4 overlay = Vector4BlendTransforms.Overlay(backdrop.backingVector, source.backingVector); + return new ColorVector(overlay); + } + + /// + /// Selects the darker of the backdrop and source colors. + /// The backdrop is replaced with the source where the source is darker; otherwise, it is left unchanged. + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector Darken(ColorVector backdrop, ColorVector source) + { + Vector4 darken = Vector4BlendTransforms.Darken(backdrop.backingVector, source.backingVector); + return new ColorVector(darken); + } + + /// + /// Selects the lighter of the backdrop and source colors. + /// The backdrop is replaced with the source where the source is lighter; otherwise, it is left unchanged. + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector Lighten(ColorVector backdrop, ColorVector source) + { + Vector4 lighten = Vector4BlendTransforms.Lighten(backdrop.backingVector, source.backingVector); + return new ColorVector(lighten); + } + + /// + /// Darkens or lightens the colors, depending on the source color value. The effect is similar to shining + /// a diffused spotlight on the backdrop. + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector SoftLight(ColorVector backdrop, ColorVector source) + { + Vector4 softlight = Vector4BlendTransforms.SoftLight(backdrop.backingVector, source.backingVector); + return new ColorVector(softlight); + } + + /// + /// Brightens the backdrop color to reflect the source color. Painting with black produces no changes. + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector ColorDodge(ColorVector backdrop, ColorVector source) + { + Vector4 dodge = Vector4BlendTransforms.Dodge(backdrop.backingVector, source.backingVector); + return new ColorVector(dodge); + } + + /// + /// Darkens the backdrop color to reflect the source color. Painting with white produces no change. + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector ColorBurn(ColorVector backdrop, ColorVector source) + { + Vector4 burn = Vector4BlendTransforms.Burn(backdrop.backingVector, source.backingVector); + return new ColorVector(burn); + } + + /// + /// Subtracts the darker of the two constituent colors from the lighter color. + /// Painting with white inverts the backdrop color; painting with black produces no change. + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector Difference(ColorVector backdrop, ColorVector source) + { + Vector4 difference = Vector4BlendTransforms.Difference(backdrop.backingVector, source.backingVector); + return new ColorVector(difference); + } + + /// + /// Produces an effect similar to that of the mode but lower in contrast. Painting with white + /// inverts the backdrop color; painting with black produces no change + /// + /// The backdrop color. + /// The source color. + /// + /// The . + /// + public static ColorVector Exclusion(ColorVector backdrop, ColorVector source) + { + Vector4 exclusion = Vector4BlendTransforms.Exclusion(backdrop.backingVector, source.backingVector); + return new ColorVector(exclusion); + } + + /// + /// Linearly interpolates from one color to another based on the given weighting. + /// + /// The first color value. + /// The second color value. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// + /// The + /// + public static ColorVector Lerp(ColorVector from, ColorVector to, float amount) + { + return new ColorVector(Vector4.Lerp(from.backingVector, to.backingVector, amount)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorVector.cs b/src/ImageSharp/Colors/ColorVector.cs new file mode 100644 index 000000000..06ee5b805 --- /dev/null +++ b/src/ImageSharp/Colors/ColorVector.cs @@ -0,0 +1,320 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. + /// The color components are stored in red, green, blue, and alpha order. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public partial struct ColorVector : IPixel + { + /// + /// The maximum byte value. + /// + private static readonly Vector4 MaxBytes = new Vector4(255); + + /// + /// The half vector value. + /// + private static readonly Vector4 Half = new Vector4(0.5F); + + /// + /// The backing vector for SIMD support. + /// + private Vector4 backingVector; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ColorVector(byte r, byte g, byte b, byte a = 255) + : this() + { + this.backingVector = new Vector4(r, g, b, a) / MaxBytes; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ColorVector(float r, float g, float b, float a = 1) + : this() + { + this.backingVector = new Vector4(r, g, b, a); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ColorVector(Vector3 vector) + : this() + { + this.backingVector = new Vector4(vector, 1); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ColorVector(Vector4 vector) + : this() + { + this.backingVector = vector; + } + + /// + /// Gets or sets the red component. + /// + public float R + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.X; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.X = value; + } + } + + /// + /// Gets or sets the green component. + /// + public float G + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.Y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.Y = value; + } + } + + /// + /// Gets or sets the blue component. + /// + public float B + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.Z = value; + } + } + + /// + /// Gets or sets the alpha component. + /// + public float A + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.W; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.W = value; + } + } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(ColorVector left, ColorVector right) + { + return left.backingVector == right.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 parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(ColorVector left, ColorVector right) + { + return left.backingVector != right.backingVector; + } + + /// + /// Creates a new instance of the struct. + /// + /// + /// The hexadecimal representation of the combined color components arranged + /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. + /// + /// + /// The . + /// + public static ColorVector FromHex(string hex) + { + return ColorBuilder.FromHex(hex); + } + + /// + public BulkPixelOperations CreateBulkOperations() => new ColorVector.BulkOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromBytes(byte x, byte y, byte z, byte w) + { + this.backingVector = new Vector4(x, y, z, w) / MaxBytes; + } + + /// + /// Converts the value of this instance to a hexadecimal string. + /// + /// A hexadecimal string representation of the value. + public string ToHex() + { + // Hex is RRGGBBAA + Vector4 vector = this.backingVector * MaxBytes; + vector += Half; + uint hexOrder = (uint)((byte)vector.W | (byte)vector.Z << 8 | (byte)vector.Y << 16 | (byte)vector.X << 24); + return hexOrder.ToString("X8"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToXyzBytes(byte[] bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.X; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.Z; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToXyzwBytes(byte[] bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.X; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.Z; + bytes[startIndex + 3] = (byte)vector.W; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToZyxBytes(byte[] bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.Z; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.X; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToZyxwBytes(byte[] bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.Z; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.X; + bytes[startIndex + 3] = (byte)vector.W; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromVector4(Vector4 vector) + { + this.backingVector = vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return this.backingVector; + } + + /// + public override bool Equals(object obj) + { + return (obj is ColorVector) && this.Equals((ColorVector)obj); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(ColorVector other) + { + return this.backingVector == other.backingVector; + } + + /// + /// Gets a string representation of the packed vector. + /// + /// A string representation of the packed vector. + public override string ToString() + { + return this.ToVector4().ToString(); + } + + /// + public override int GetHashCode() + { + return this.backingVector.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/NamedColors{TColor}.cs b/src/ImageSharp/Colors/NamedColors{TColor}.cs index bcad4dd40..f8080195d 100644 --- a/src/ImageSharp/Colors/NamedColors{TColor}.cs +++ b/src/ImageSharp/Colors/NamedColors{TColor}.cs @@ -8,719 +8,719 @@ namespace ImageSharp using System; /// - /// A set of named colors mapped to the provided Color space. + /// A set of named colors mapped to the provided color space. /// /// The type of the color. public static class NamedColors where TColor : struct, IPixel { /// - /// Represents a matching the W3C definition that has an hex value of #F0F8FF. + /// Represents a matching the W3C definition that has an hex value of #F0F8FF. /// public static readonly TColor AliceBlue = ColorBuilder.FromRGBA(240, 248, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FAEBD7. + /// Represents a matching the W3C definition that has an hex value of #FAEBD7. /// public static readonly TColor AntiqueWhite = ColorBuilder.FromRGBA(250, 235, 215, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// public static readonly TColor Aqua = ColorBuilder.FromRGBA(0, 255, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #7FFFD4. + /// Represents a matching the W3C definition that has an hex value of #7FFFD4. /// public static readonly TColor Aquamarine = ColorBuilder.FromRGBA(127, 255, 212, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F0FFFF. + /// Represents a matching the W3C definition that has an hex value of #F0FFFF. /// public static readonly TColor Azure = ColorBuilder.FromRGBA(240, 255, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F5F5DC. + /// Represents a matching the W3C definition that has an hex value of #F5F5DC. /// public static readonly TColor Beige = ColorBuilder.FromRGBA(245, 245, 220, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFE4C4. + /// Represents a matching the W3C definition that has an hex value of #FFE4C4. /// public static readonly TColor Bisque = ColorBuilder.FromRGBA(255, 228, 196, 255); /// - /// Represents a matching the W3C definition that has an hex value of #000000. + /// Represents a matching the W3C definition that has an hex value of #000000. /// public static readonly TColor Black = ColorBuilder.FromRGBA(0, 0, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFEBCD. + /// Represents a matching the W3C definition that has an hex value of #FFEBCD. /// public static readonly TColor BlanchedAlmond = ColorBuilder.FromRGBA(255, 235, 205, 255); /// - /// Represents a matching the W3C definition that has an hex value of #0000FF. + /// Represents a matching the W3C definition that has an hex value of #0000FF. /// public static readonly TColor Blue = ColorBuilder.FromRGBA(0, 0, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #8A2BE2. + /// Represents a matching the W3C definition that has an hex value of #8A2BE2. /// public static readonly TColor BlueViolet = ColorBuilder.FromRGBA(138, 43, 226, 255); /// - /// Represents a matching the W3C definition that has an hex value of #A52A2A. + /// Represents a matching the W3C definition that has an hex value of #A52A2A. /// public static readonly TColor Brown = ColorBuilder.FromRGBA(165, 42, 42, 255); /// - /// Represents a matching the W3C definition that has an hex value of #DEB887. + /// Represents a matching the W3C definition that has an hex value of #DEB887. /// public static readonly TColor BurlyWood = ColorBuilder.FromRGBA(222, 184, 135, 255); /// - /// Represents a matching the W3C definition that has an hex value of #5F9EA0. + /// Represents a matching the W3C definition that has an hex value of #5F9EA0. /// public static readonly TColor CadetBlue = ColorBuilder.FromRGBA(95, 158, 160, 255); /// - /// Represents a matching the W3C definition that has an hex value of #7FFF00. + /// Represents a matching the W3C definition that has an hex value of #7FFF00. /// public static readonly TColor Chartreuse = ColorBuilder.FromRGBA(127, 255, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #D2691E. + /// Represents a matching the W3C definition that has an hex value of #D2691E. /// public static readonly TColor Chocolate = ColorBuilder.FromRGBA(210, 105, 30, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF7F50. + /// Represents a matching the W3C definition that has an hex value of #FF7F50. /// public static readonly TColor Coral = ColorBuilder.FromRGBA(255, 127, 80, 255); /// - /// Represents a matching the W3C definition that has an hex value of #6495ED. + /// Represents a matching the W3C definition that has an hex value of #6495ED. /// public static readonly TColor CornflowerBlue = ColorBuilder.FromRGBA(100, 149, 237, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFF8DC. + /// Represents a matching the W3C definition that has an hex value of #FFF8DC. /// public static readonly TColor Cornsilk = ColorBuilder.FromRGBA(255, 248, 220, 255); /// - /// Represents a matching the W3C definition that has an hex value of #DC143C. + /// Represents a matching the W3C definition that has an hex value of #DC143C. /// public static readonly TColor Crimson = ColorBuilder.FromRGBA(220, 20, 60, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// public static readonly TColor Cyan = ColorBuilder.FromRGBA(0, 255, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00008B. + /// Represents a matching the W3C definition that has an hex value of #00008B. /// public static readonly TColor DarkBlue = ColorBuilder.FromRGBA(0, 0, 139, 255); /// - /// Represents a matching the W3C definition that has an hex value of #008B8B. + /// Represents a matching the W3C definition that has an hex value of #008B8B. /// public static readonly TColor DarkCyan = ColorBuilder.FromRGBA(0, 139, 139, 255); /// - /// Represents a matching the W3C definition that has an hex value of #B8860B. + /// Represents a matching the W3C definition that has an hex value of #B8860B. /// public static readonly TColor DarkGoldenrod = ColorBuilder.FromRGBA(184, 134, 11, 255); /// - /// Represents a matching the W3C definition that has an hex value of #A9A9A9. + /// Represents a matching the W3C definition that has an hex value of #A9A9A9. /// public static readonly TColor DarkGray = ColorBuilder.FromRGBA(169, 169, 169, 255); /// - /// Represents a matching the W3C definition that has an hex value of #006400. + /// Represents a matching the W3C definition that has an hex value of #006400. /// public static readonly TColor DarkGreen = ColorBuilder.FromRGBA(0, 100, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #BDB76B. + /// Represents a matching the W3C definition that has an hex value of #BDB76B. /// public static readonly TColor DarkKhaki = ColorBuilder.FromRGBA(189, 183, 107, 255); /// - /// Represents a matching the W3C definition that has an hex value of #8B008B. + /// Represents a matching the W3C definition that has an hex value of #8B008B. /// public static readonly TColor DarkMagenta = ColorBuilder.FromRGBA(139, 0, 139, 255); /// - /// Represents a matching the W3C definition that has an hex value of #556B2F. + /// Represents a matching the W3C definition that has an hex value of #556B2F. /// public static readonly TColor DarkOliveGreen = ColorBuilder.FromRGBA(85, 107, 47, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF8C00. + /// Represents a matching the W3C definition that has an hex value of #FF8C00. /// public static readonly TColor DarkOrange = ColorBuilder.FromRGBA(255, 140, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #9932CC. + /// Represents a matching the W3C definition that has an hex value of #9932CC. /// public static readonly TColor DarkOrchid = ColorBuilder.FromRGBA(153, 50, 204, 255); /// - /// Represents a matching the W3C definition that has an hex value of #8B0000. + /// Represents a matching the W3C definition that has an hex value of #8B0000. /// public static readonly TColor DarkRed = ColorBuilder.FromRGBA(139, 0, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #E9967A. + /// Represents a matching the W3C definition that has an hex value of #E9967A. /// public static readonly TColor DarkSalmon = ColorBuilder.FromRGBA(233, 150, 122, 255); /// - /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// Represents a matching the W3C definition that has an hex value of #8FBC8B. /// public static readonly TColor DarkSeaGreen = ColorBuilder.FromRGBA(143, 188, 139, 255); /// - /// Represents a matching the W3C definition that has an hex value of #483D8B. + /// Represents a matching the W3C definition that has an hex value of #483D8B. /// public static readonly TColor DarkSlateBlue = ColorBuilder.FromRGBA(72, 61, 139, 255); /// - /// Represents a matching the W3C definition that has an hex value of #2F4F4F. + /// Represents a matching the W3C definition that has an hex value of #2F4F4F. /// public static readonly TColor DarkSlateGray = ColorBuilder.FromRGBA(47, 79, 79, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00CED1. + /// Represents a matching the W3C definition that has an hex value of #00CED1. /// public static readonly TColor DarkTurquoise = ColorBuilder.FromRGBA(0, 206, 209, 255); /// - /// Represents a matching the W3C definition that has an hex value of #9400D3. + /// Represents a matching the W3C definition that has an hex value of #9400D3. /// public static readonly TColor DarkViolet = ColorBuilder.FromRGBA(148, 0, 211, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF1493. + /// Represents a matching the W3C definition that has an hex value of #FF1493. /// public static readonly TColor DeepPink = ColorBuilder.FromRGBA(255, 20, 147, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00BFFF. + /// Represents a matching the W3C definition that has an hex value of #00BFFF. /// public static readonly TColor DeepSkyBlue = ColorBuilder.FromRGBA(0, 191, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #696969. + /// Represents a matching the W3C definition that has an hex value of #696969. /// public static readonly TColor DimGray = ColorBuilder.FromRGBA(105, 105, 105, 255); /// - /// Represents a matching the W3C definition that has an hex value of #1E90FF. + /// Represents a matching the W3C definition that has an hex value of #1E90FF. /// public static readonly TColor DodgerBlue = ColorBuilder.FromRGBA(30, 144, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #B22222. + /// Represents a matching the W3C definition that has an hex value of #B22222. /// public static readonly TColor Firebrick = ColorBuilder.FromRGBA(178, 34, 34, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFAF0. + /// Represents a matching the W3C definition that has an hex value of #FFFAF0. /// public static readonly TColor FloralWhite = ColorBuilder.FromRGBA(255, 250, 240, 255); /// - /// Represents a matching the W3C definition that has an hex value of #228B22. + /// Represents a matching the W3C definition that has an hex value of #228B22. /// public static readonly TColor ForestGreen = ColorBuilder.FromRGBA(34, 139, 34, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// public static readonly TColor Fuchsia = ColorBuilder.FromRGBA(255, 0, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #DCDCDC. + /// Represents a matching the W3C definition that has an hex value of #DCDCDC. /// public static readonly TColor Gainsboro = ColorBuilder.FromRGBA(220, 220, 220, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F8F8FF. + /// Represents a matching the W3C definition that has an hex value of #F8F8FF. /// public static readonly TColor GhostWhite = ColorBuilder.FromRGBA(248, 248, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFD700. + /// Represents a matching the W3C definition that has an hex value of #FFD700. /// public static readonly TColor Gold = ColorBuilder.FromRGBA(255, 215, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #DAA520. + /// Represents a matching the W3C definition that has an hex value of #DAA520. /// public static readonly TColor Goldenrod = ColorBuilder.FromRGBA(218, 165, 32, 255); /// - /// Represents a matching the W3C definition that has an hex value of #808080. + /// Represents a matching the W3C definition that has an hex value of #808080. /// public static readonly TColor Gray = ColorBuilder.FromRGBA(128, 128, 128, 255); /// - /// Represents a matching the W3C definition that has an hex value of #008000. + /// Represents a matching the W3C definition that has an hex value of #008000. /// public static readonly TColor Green = ColorBuilder.FromRGBA(0, 128, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #ADFF2F. + /// Represents a matching the W3C definition that has an hex value of #ADFF2F. /// public static readonly TColor GreenYellow = ColorBuilder.FromRGBA(173, 255, 47, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F0FFF0. + /// Represents a matching the W3C definition that has an hex value of #F0FFF0. /// public static readonly TColor Honeydew = ColorBuilder.FromRGBA(240, 255, 240, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF69B4. + /// Represents a matching the W3C definition that has an hex value of #FF69B4. /// public static readonly TColor HotPink = ColorBuilder.FromRGBA(255, 105, 180, 255); /// - /// Represents a matching the W3C definition that has an hex value of #CD5C5C. + /// Represents a matching the W3C definition that has an hex value of #CD5C5C. /// public static readonly TColor IndianRed = ColorBuilder.FromRGBA(205, 92, 92, 255); /// - /// Represents a matching the W3C definition that has an hex value of #4B0082. + /// Represents a matching the W3C definition that has an hex value of #4B0082. /// public static readonly TColor Indigo = ColorBuilder.FromRGBA(75, 0, 130, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFFF0. + /// Represents a matching the W3C definition that has an hex value of #FFFFF0. /// public static readonly TColor Ivory = ColorBuilder.FromRGBA(255, 255, 240, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F0E68C. + /// Represents a matching the W3C definition that has an hex value of #F0E68C. /// public static readonly TColor Khaki = ColorBuilder.FromRGBA(240, 230, 140, 255); /// - /// Represents a matching the W3C definition that has an hex value of #E6E6FA. + /// Represents a matching the W3C definition that has an hex value of #E6E6FA. /// public static readonly TColor Lavender = ColorBuilder.FromRGBA(230, 230, 250, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFF0F5. + /// Represents a matching the W3C definition that has an hex value of #FFF0F5. /// public static readonly TColor LavenderBlush = ColorBuilder.FromRGBA(255, 240, 245, 255); /// - /// Represents a matching the W3C definition that has an hex value of #7CFC00. + /// Represents a matching the W3C definition that has an hex value of #7CFC00. /// public static readonly TColor LawnGreen = ColorBuilder.FromRGBA(124, 252, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFACD. + /// Represents a matching the W3C definition that has an hex value of #FFFACD. /// public static readonly TColor LemonChiffon = ColorBuilder.FromRGBA(255, 250, 205, 255); /// - /// Represents a matching the W3C definition that has an hex value of #ADD8E6. + /// Represents a matching the W3C definition that has an hex value of #ADD8E6. /// public static readonly TColor LightBlue = ColorBuilder.FromRGBA(173, 216, 230, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F08080. + /// Represents a matching the W3C definition that has an hex value of #F08080. /// public static readonly TColor LightCoral = ColorBuilder.FromRGBA(240, 128, 128, 255); /// - /// Represents a matching the W3C definition that has an hex value of #E0FFFF. + /// Represents a matching the W3C definition that has an hex value of #E0FFFF. /// public static readonly TColor LightCyan = ColorBuilder.FromRGBA(224, 255, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FAFAD2. + /// Represents a matching the W3C definition that has an hex value of #FAFAD2. /// public static readonly TColor LightGoldenrodYellow = ColorBuilder.FromRGBA(250, 250, 210, 255); /// - /// Represents a matching the W3C definition that has an hex value of #D3D3D3. + /// Represents a matching the W3C definition that has an hex value of #D3D3D3. /// public static readonly TColor LightGray = ColorBuilder.FromRGBA(211, 211, 211, 255); /// - /// Represents a matching the W3C definition that has an hex value of #90EE90. + /// Represents a matching the W3C definition that has an hex value of #90EE90. /// public static readonly TColor LightGreen = ColorBuilder.FromRGBA(144, 238, 144, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFB6C1. + /// Represents a matching the W3C definition that has an hex value of #FFB6C1. /// public static readonly TColor LightPink = ColorBuilder.FromRGBA(255, 182, 193, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFA07A. + /// Represents a matching the W3C definition that has an hex value of #FFA07A. /// public static readonly TColor LightSalmon = ColorBuilder.FromRGBA(255, 160, 122, 255); /// - /// Represents a matching the W3C definition that has an hex value of #20B2AA. + /// Represents a matching the W3C definition that has an hex value of #20B2AA. /// public static readonly TColor LightSeaGreen = ColorBuilder.FromRGBA(32, 178, 170, 255); /// - /// Represents a matching the W3C definition that has an hex value of #87CEFA. + /// Represents a matching the W3C definition that has an hex value of #87CEFA. /// public static readonly TColor LightSkyBlue = ColorBuilder.FromRGBA(135, 206, 250, 255); /// - /// Represents a matching the W3C definition that has an hex value of #778899. + /// Represents a matching the W3C definition that has an hex value of #778899. /// public static readonly TColor LightSlateGray = ColorBuilder.FromRGBA(119, 136, 153, 255); /// - /// Represents a matching the W3C definition that has an hex value of #B0C4DE. + /// Represents a matching the W3C definition that has an hex value of #B0C4DE. /// public static readonly TColor LightSteelBlue = ColorBuilder.FromRGBA(176, 196, 222, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFFE0. + /// Represents a matching the W3C definition that has an hex value of #FFFFE0. /// public static readonly TColor LightYellow = ColorBuilder.FromRGBA(255, 255, 224, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00FF00. + /// Represents a matching the W3C definition that has an hex value of #00FF00. /// public static readonly TColor Lime = ColorBuilder.FromRGBA(0, 255, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #32CD32. + /// Represents a matching the W3C definition that has an hex value of #32CD32. /// public static readonly TColor LimeGreen = ColorBuilder.FromRGBA(50, 205, 50, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FAF0E6. + /// Represents a matching the W3C definition that has an hex value of #FAF0E6. /// public static readonly TColor Linen = ColorBuilder.FromRGBA(250, 240, 230, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// public static readonly TColor Magenta = ColorBuilder.FromRGBA(255, 0, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #800000. + /// Represents a matching the W3C definition that has an hex value of #800000. /// public static readonly TColor Maroon = ColorBuilder.FromRGBA(128, 0, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #66CDAA. + /// Represents a matching the W3C definition that has an hex value of #66CDAA. /// public static readonly TColor MediumAquamarine = ColorBuilder.FromRGBA(102, 205, 170, 255); /// - /// Represents a matching the W3C definition that has an hex value of #0000CD. + /// Represents a matching the W3C definition that has an hex value of #0000CD. /// public static readonly TColor MediumBlue = ColorBuilder.FromRGBA(0, 0, 205, 255); /// - /// Represents a matching the W3C definition that has an hex value of #BA55D3. + /// Represents a matching the W3C definition that has an hex value of #BA55D3. /// public static readonly TColor MediumOrchid = ColorBuilder.FromRGBA(186, 85, 211, 255); /// - /// Represents a matching the W3C definition that has an hex value of #9370DB. + /// Represents a matching the W3C definition that has an hex value of #9370DB. /// public static readonly TColor MediumPurple = ColorBuilder.FromRGBA(147, 112, 219, 255); /// - /// Represents a matching the W3C definition that has an hex value of #3CB371. + /// Represents a matching the W3C definition that has an hex value of #3CB371. /// public static readonly TColor MediumSeaGreen = ColorBuilder.FromRGBA(60, 179, 113, 255); /// - /// Represents a matching the W3C definition that has an hex value of #7B68EE. + /// Represents a matching the W3C definition that has an hex value of #7B68EE. /// public static readonly TColor MediumSlateBlue = ColorBuilder.FromRGBA(123, 104, 238, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00FA9A. + /// Represents a matching the W3C definition that has an hex value of #00FA9A. /// public static readonly TColor MediumSpringGreen = ColorBuilder.FromRGBA(0, 250, 154, 255); /// - /// Represents a matching the W3C definition that has an hex value of #48D1CC. + /// Represents a matching the W3C definition that has an hex value of #48D1CC. /// public static readonly TColor MediumTurquoise = ColorBuilder.FromRGBA(72, 209, 204, 255); /// - /// Represents a matching the W3C definition that has an hex value of #C71585. + /// Represents a matching the W3C definition that has an hex value of #C71585. /// public static readonly TColor MediumVioletRed = ColorBuilder.FromRGBA(199, 21, 133, 255); /// - /// Represents a matching the W3C definition that has an hex value of #191970. + /// Represents a matching the W3C definition that has an hex value of #191970. /// public static readonly TColor MidnightBlue = ColorBuilder.FromRGBA(25, 25, 112, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F5FFFA. + /// Represents a matching the W3C definition that has an hex value of #F5FFFA. /// public static readonly TColor MintCream = ColorBuilder.FromRGBA(245, 255, 250, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFE4E1. + /// Represents a matching the W3C definition that has an hex value of #FFE4E1. /// public static readonly TColor MistyRose = ColorBuilder.FromRGBA(255, 228, 225, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFE4B5. + /// Represents a matching the W3C definition that has an hex value of #FFE4B5. /// public static readonly TColor Moccasin = ColorBuilder.FromRGBA(255, 228, 181, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFDEAD. + /// Represents a matching the W3C definition that has an hex value of #FFDEAD. /// public static readonly TColor NavajoWhite = ColorBuilder.FromRGBA(255, 222, 173, 255); /// - /// Represents a matching the W3C definition that has an hex value of #000080. + /// Represents a matching the W3C definition that has an hex value of #000080. /// public static readonly TColor Navy = ColorBuilder.FromRGBA(0, 0, 128, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FDF5E6. + /// Represents a matching the W3C definition that has an hex value of #FDF5E6. /// public static readonly TColor OldLace = ColorBuilder.FromRGBA(253, 245, 230, 255); /// - /// Represents a matching the W3C definition that has an hex value of #808000. + /// Represents a matching the W3C definition that has an hex value of #808000. /// public static readonly TColor Olive = ColorBuilder.FromRGBA(128, 128, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #6B8E23. + /// Represents a matching the W3C definition that has an hex value of #6B8E23. /// public static readonly TColor OliveDrab = ColorBuilder.FromRGBA(107, 142, 35, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFA500. + /// Represents a matching the W3C definition that has an hex value of #FFA500. /// public static readonly TColor Orange = ColorBuilder.FromRGBA(255, 165, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF4500. + /// Represents a matching the W3C definition that has an hex value of #FF4500. /// public static readonly TColor OrangeRed = ColorBuilder.FromRGBA(255, 69, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #DA70D6. + /// Represents a matching the W3C definition that has an hex value of #DA70D6. /// public static readonly TColor Orchid = ColorBuilder.FromRGBA(218, 112, 214, 255); /// - /// Represents a matching the W3C definition that has an hex value of #EEE8AA. + /// Represents a matching the W3C definition that has an hex value of #EEE8AA. /// public static readonly TColor PaleGoldenrod = ColorBuilder.FromRGBA(238, 232, 170, 255); /// - /// Represents a matching the W3C definition that has an hex value of #98FB98. + /// Represents a matching the W3C definition that has an hex value of #98FB98. /// public static readonly TColor PaleGreen = ColorBuilder.FromRGBA(152, 251, 152, 255); /// - /// Represents a matching the W3C definition that has an hex value of #AFEEEE. + /// Represents a matching the W3C definition that has an hex value of #AFEEEE. /// public static readonly TColor PaleTurquoise = ColorBuilder.FromRGBA(175, 238, 238, 255); /// - /// Represents a matching the W3C definition that has an hex value of #DB7093. + /// Represents a matching the W3C definition that has an hex value of #DB7093. /// public static readonly TColor PaleVioletRed = ColorBuilder.FromRGBA(219, 112, 147, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFEFD5. + /// Represents a matching the W3C definition that has an hex value of #FFEFD5. /// public static readonly TColor PapayaWhip = ColorBuilder.FromRGBA(255, 239, 213, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFDAB9. + /// Represents a matching the W3C definition that has an hex value of #FFDAB9. /// public static readonly TColor PeachPuff = ColorBuilder.FromRGBA(255, 218, 185, 255); /// - /// Represents a matching the W3C definition that has an hex value of #CD853F. + /// Represents a matching the W3C definition that has an hex value of #CD853F. /// public static readonly TColor Peru = ColorBuilder.FromRGBA(205, 133, 63, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFC0CB. + /// Represents a matching the W3C definition that has an hex value of #FFC0CB. /// public static readonly TColor Pink = ColorBuilder.FromRGBA(255, 192, 203, 255); /// - /// Represents a matching the W3C definition that has an hex value of #DDA0DD. + /// Represents a matching the W3C definition that has an hex value of #DDA0DD. /// public static readonly TColor Plum = ColorBuilder.FromRGBA(221, 160, 221, 255); /// - /// Represents a matching the W3C definition that has an hex value of #B0E0E6. + /// Represents a matching the W3C definition that has an hex value of #B0E0E6. /// public static readonly TColor PowderBlue = ColorBuilder.FromRGBA(176, 224, 230, 255); /// - /// Represents a matching the W3C definition that has an hex value of #800080. + /// Represents a matching the W3C definition that has an hex value of #800080. /// public static readonly TColor Purple = ColorBuilder.FromRGBA(128, 0, 128, 255); /// - /// Represents a matching the W3C definition that has an hex value of #663399. + /// Represents a matching the W3C definition that has an hex value of #663399. /// public static readonly TColor RebeccaPurple = ColorBuilder.FromRGBA(102, 51, 153, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF0000. + /// Represents a matching the W3C definition that has an hex value of #FF0000. /// public static readonly TColor Red = ColorBuilder.FromRGBA(255, 0, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #BC8F8F. + /// Represents a matching the W3C definition that has an hex value of #BC8F8F. /// public static readonly TColor RosyBrown = ColorBuilder.FromRGBA(188, 143, 143, 255); /// - /// Represents a matching the W3C definition that has an hex value of #4169E1. + /// Represents a matching the W3C definition that has an hex value of #4169E1. /// public static readonly TColor RoyalBlue = ColorBuilder.FromRGBA(65, 105, 225, 255); /// - /// Represents a matching the W3C definition that has an hex value of #8B4513. + /// Represents a matching the W3C definition that has an hex value of #8B4513. /// public static readonly TColor SaddleBrown = ColorBuilder.FromRGBA(139, 69, 19, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FA8072. + /// Represents a matching the W3C definition that has an hex value of #FA8072. /// public static readonly TColor Salmon = ColorBuilder.FromRGBA(250, 128, 114, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F4A460. + /// Represents a matching the W3C definition that has an hex value of #F4A460. /// public static readonly TColor SandyBrown = ColorBuilder.FromRGBA(244, 164, 96, 255); /// - /// Represents a matching the W3C definition that has an hex value of #2E8B57. + /// Represents a matching the W3C definition that has an hex value of #2E8B57. /// public static readonly TColor SeaGreen = ColorBuilder.FromRGBA(46, 139, 87, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFF5EE. + /// Represents a matching the W3C definition that has an hex value of #FFF5EE. /// public static readonly TColor SeaShell = ColorBuilder.FromRGBA(255, 245, 238, 255); /// - /// Represents a matching the W3C definition that has an hex value of #A0522D. + /// Represents a matching the W3C definition that has an hex value of #A0522D. /// public static readonly TColor Sienna = ColorBuilder.FromRGBA(160, 82, 45, 255); /// - /// Represents a matching the W3C definition that has an hex value of #C0C0C0. + /// Represents a matching the W3C definition that has an hex value of #C0C0C0. /// public static readonly TColor Silver = ColorBuilder.FromRGBA(192, 192, 192, 255); /// - /// Represents a matching the W3C definition that has an hex value of #87CEEB. + /// Represents a matching the W3C definition that has an hex value of #87CEEB. /// public static readonly TColor SkyBlue = ColorBuilder.FromRGBA(135, 206, 235, 255); /// - /// Represents a matching the W3C definition that has an hex value of #6A5ACD. + /// Represents a matching the W3C definition that has an hex value of #6A5ACD. /// public static readonly TColor SlateBlue = ColorBuilder.FromRGBA(106, 90, 205, 255); /// - /// Represents a matching the W3C definition that has an hex value of #708090. + /// Represents a matching the W3C definition that has an hex value of #708090. /// public static readonly TColor SlateGray = ColorBuilder.FromRGBA(112, 128, 144, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFAFA. + /// Represents a matching the W3C definition that has an hex value of #FFFAFA. /// public static readonly TColor Snow = ColorBuilder.FromRGBA(255, 250, 250, 255); /// - /// Represents a matching the W3C definition that has an hex value of #00FF7F. + /// Represents a matching the W3C definition that has an hex value of #00FF7F. /// public static readonly TColor SpringGreen = ColorBuilder.FromRGBA(0, 255, 127, 255); /// - /// Represents a matching the W3C definition that has an hex value of #4682B4. + /// Represents a matching the W3C definition that has an hex value of #4682B4. /// public static readonly TColor SteelBlue = ColorBuilder.FromRGBA(70, 130, 180, 255); /// - /// Represents a matching the W3C definition that has an hex value of #D2B48C. + /// Represents a matching the W3C definition that has an hex value of #D2B48C. /// public static readonly TColor Tan = ColorBuilder.FromRGBA(210, 180, 140, 255); /// - /// Represents a matching the W3C definition that has an hex value of #008080. + /// Represents a matching the W3C definition that has an hex value of #008080. /// public static readonly TColor Teal = ColorBuilder.FromRGBA(0, 128, 128, 255); /// - /// Represents a matching the W3C definition that has an hex value of #D8BFD8. + /// Represents a matching the W3C definition that has an hex value of #D8BFD8. /// public static readonly TColor Thistle = ColorBuilder.FromRGBA(216, 191, 216, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FF6347. + /// Represents a matching the W3C definition that has an hex value of #FF6347. /// public static readonly TColor Tomato = ColorBuilder.FromRGBA(255, 99, 71, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// public static readonly TColor Transparent = ColorBuilder.FromRGBA(255, 255, 255, 0); /// - /// Represents a matching the W3C definition that has an hex value of #40E0D0. + /// Represents a matching the W3C definition that has an hex value of #40E0D0. /// public static readonly TColor Turquoise = ColorBuilder.FromRGBA(64, 224, 208, 255); /// - /// Represents a matching the W3C definition that has an hex value of #EE82EE. + /// Represents a matching the W3C definition that has an hex value of #EE82EE. /// public static readonly TColor Violet = ColorBuilder.FromRGBA(238, 130, 238, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F5DEB3. + /// Represents a matching the W3C definition that has an hex value of #F5DEB3. /// public static readonly TColor Wheat = ColorBuilder.FromRGBA(245, 222, 179, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// public static readonly TColor White = ColorBuilder.FromRGBA(255, 255, 255, 255); /// - /// Represents a matching the W3C definition that has an hex value of #F5F5F5. + /// Represents a matching the W3C definition that has an hex value of #F5F5F5. /// public static readonly TColor WhiteSmoke = ColorBuilder.FromRGBA(245, 245, 245, 255); /// - /// Represents a matching the W3C definition that has an hex value of #FFFF00. + /// Represents a matching the W3C definition that has an hex value of #FFFF00. /// public static readonly TColor Yellow = ColorBuilder.FromRGBA(255, 255, 0, 255); /// - /// Represents a matching the W3C definition that has an hex value of #9ACD32. + /// Represents a matching the W3C definition that has an hex value of #9ACD32. /// public static readonly TColor YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); } diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb32.cs similarity index 87% rename from src/ImageSharp/Colors/PackedPixel/Argb.cs rename to src/ImageSharp/Colors/PackedPixel/Argb32.cs index d03c098cd..64255a53b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb32.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -17,7 +17,7 @@ namespace ImageSharp /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - public struct Argb : IPixel, IPackedVector + public struct Argb32 : IPixel, IPackedVector { /// /// The shift count for the blue component @@ -50,58 +50,58 @@ namespace ImageSharp private static readonly Vector4 Half = new Vector4(0.5F); /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The red component. /// The green component. /// The blue component. /// The alpha component. - public Argb(byte r, byte g, byte b, byte a = 255) + public Argb32(byte r, byte g, byte b, byte a = 255) { this.PackedValue = Pack(r, g, b, a); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The red component. /// The green component. /// The blue component. /// The alpha component. - public Argb(float r, float g, float b, float a = 1) + public Argb32(float r, float g, float b, float a = 1) { this.PackedValue = Pack(r, g, b, a); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The vector containing the components for the packed vector. /// - public Argb(Vector3 vector) + public Argb32(Vector3 vector) { this.PackedValue = Pack(ref vector); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The vector containing the components for the packed vector. /// - public Argb(Vector4 vector) + public Argb32(Vector4 vector) { this.PackedValue = Pack(ref vector); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The packed value. /// - public Argb(uint packed = 0) + public Argb32(uint packed = 0) { this.PackedValue = packed; } @@ -182,33 +182,33 @@ namespace ImageSharp } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the parameter is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Argb left, Argb right) + public static bool operator ==(Argb32 left, Argb32 right) { return left.PackedValue == right.PackedValue; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is not equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Argb left, Argb right) + public static bool operator !=(Argb32 left, Argb32 right) { return left.PackedValue != right.PackedValue; } @@ -221,7 +221,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -278,12 +278,12 @@ namespace ImageSharp /// public override bool Equals(object obj) { - return obj is Argb && this.Equals((Argb)obj); + return obj is Argb32 && this.Equals((Argb32)obj); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Argb other) + public bool Equals(Argb32 other) { return this.PackedValue == other.PackedValue; } diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 7b6169f9c..db0251703 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -13,14 +13,9 @@ namespace ImageSharp /// for pixel buffers of type . /// /// The pixel format. - public unsafe class BulkPixelOperations + public class BulkPixelOperations where TColor : struct, IPixel { - /// - /// The size of in bytes - /// - private static readonly int ColorSize = Unsafe.SizeOf(); - /// /// Gets the global instance for the pixel type /// @@ -32,23 +27,16 @@ namespace ImageSharp /// The to the source vectors. /// The to the destination colors. /// The number of pixels to convert. - internal virtual void PackFromVector4( - BufferSpan sourceVectors, - BufferSpan destColors, - int count) + internal virtual void PackFromVector4(BufferSpan sourceVectors, BufferSpan destColors, int count) { - Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; - byte* dp = (byte*)destColors; + ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference(); + ref TColor destRef = ref destColors.DangerousGetPinnableReference(); for (int i = 0; i < count; i++) { - Vector4 v = Unsafe.Read(sp); - TColor c = default(TColor); - c.PackFromVector4(v); - Unsafe.Write(dp, c); - - sp++; - dp += ColorSize; + ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i); + ref TColor dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromVector4(sp); } } @@ -58,20 +46,16 @@ namespace ImageSharp /// The to the source colors. /// The to the destination vectors. /// The number of pixels to convert. - internal virtual void ToVector4( - BufferSpan sourceColors, - BufferSpan destVectors, - int count) + internal virtual void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count) { - byte* sp = (byte*)sourceColors; - Vector4* dp = (Vector4*)destVectors.PointerAtOffset; + ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference(); for (int i = 0; i < count; i++) { - TColor c = Unsafe.Read(sp); - *dp = c.ToVector4(); - sp += ColorSize; - dp++; + ref TColor sp = ref Unsafe.Add(ref sourceRef, i); + ref Vector4 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToVector4(); } } @@ -81,21 +65,20 @@ namespace ImageSharp /// The to the source bytes. /// The to the destination colors. /// The number of pixels to convert. - internal virtual void PackFromXyzBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) + internal virtual void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TColor destRef = ref destColors.DangerousGetPinnableReference(); for (int i = 0; i < count; i++) { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; + int i3 = i * 3; + ref TColor dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i3), + Unsafe.Add(ref sourceRef, i3 + 1), + Unsafe.Add(ref sourceRef, i3 + 2), + 255); } } @@ -107,14 +90,13 @@ namespace ImageSharp /// The number of pixels to convert. internal virtual void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* sp = (byte*)sourceColors; + ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference(); byte[] dest = destBytes.Array; - for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3) + for (int i = 0; i < count; i++) { - TColor c = Unsafe.Read(sp); - c.ToXyzBytes(dest, i); - sp += ColorSize; + ref TColor sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToXyzBytes(dest, destBytes.Start + (i * 3)); } } @@ -124,21 +106,20 @@ namespace ImageSharp /// The to the source bytes. /// The to the destination colors. /// The number of pixels to convert. - internal virtual void PackFromXyzwBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) + internal virtual void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TColor destRef = ref destColors.DangerousGetPinnableReference(); for (int i = 0; i < count; i++) { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; + int i4 = i * 4; + ref TColor dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i4), + Unsafe.Add(ref sourceRef, i4 + 1), + Unsafe.Add(ref sourceRef, i4 + 2), + Unsafe.Add(ref sourceRef, i4 + 3)); } } @@ -148,19 +129,15 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToXyzwBytes( - BufferSpan sourceColors, - BufferSpan destBytes, - int count) + internal virtual void ToXyzwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* sp = (byte*)sourceColors; + ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference(); byte[] dest = destBytes.Array; - for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4) + for (int i = 0; i < count; i++) { - TColor c = Unsafe.Read(sp); - c.ToXyzwBytes(dest, i); - sp += ColorSize; + ref TColor sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToXyzwBytes(dest, destBytes.Start + (i * 4)); } } @@ -170,21 +147,20 @@ namespace ImageSharp /// The to the source bytes. /// The to the destination colors. /// The number of pixels to convert. - internal virtual void PackFromZyxBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) + internal virtual void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TColor destRef = ref destColors.DangerousGetPinnableReference(); for (int i = 0; i < count; i++) { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; + int i3 = i * 3; + ref TColor dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i3 + 2), + Unsafe.Add(ref sourceRef, i3 + 1), + Unsafe.Add(ref sourceRef, i3), + 255); } } @@ -196,14 +172,13 @@ namespace ImageSharp /// The number of pixels to convert. internal virtual void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* sp = (byte*)sourceColors; + ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference(); byte[] dest = destBytes.Array; - for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3) + for (int i = 0; i < count; i++) { - TColor c = Unsafe.Read(sp); - c.ToZyxBytes(dest, i); - sp += ColorSize; + ref TColor sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToZyxBytes(dest, destBytes.Start + (i * 3)); } } @@ -213,21 +188,20 @@ namespace ImageSharp /// The to the source bytes. /// The to the destination colors. /// The number of pixels to convert. - internal virtual void PackFromZyxwBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) + internal virtual void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TColor destRef = ref destColors.DangerousGetPinnableReference(); for (int i = 0; i < count; i++) { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; + int i4 = i * 4; + ref TColor dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i4 + 2), + Unsafe.Add(ref sourceRef, i4 + 1), + Unsafe.Add(ref sourceRef, i4), + Unsafe.Add(ref sourceRef, i4 + 3)); } } @@ -237,19 +211,15 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToZyxwBytes( - BufferSpan sourceColors, - BufferSpan destBytes, - int count) + internal virtual void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* sp = (byte*)sourceColors; + ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference(); byte[] dest = destBytes.Array; - for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4) + for (int i = 0; i < count; i++) { - TColor c = Unsafe.Read(sp); - c.ToZyxwBytes(dest, i); - sp += ColorSize; + ref TColor sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToZyxwBytes(dest, destBytes.Start + (i * 4)); } } } diff --git a/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs b/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs index 949e44cc0..13727870c 100644 --- a/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs +++ b/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs @@ -300,7 +300,7 @@ namespace ImageSharp private static bool IsStandardNormalizedType(Type type) { return type == typeof(Color) - || type == typeof(Argb) + || type == typeof(Argb32) || type == typeof(Alpha8) || type == typeof(Bgr565) || type == typeof(Bgra4444) diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs b/src/ImageSharp/Common/Memory/Buffer.cs similarity index 68% rename from src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs rename to src/ImageSharp/Common/Memory/Buffer.cs index 611688c99..c26b8ea18 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/Buffer.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -11,13 +11,18 @@ namespace ImageSharp using System.Runtime.InteropServices; /// - /// Manages a pinned buffer of value type objects as a Disposable resource. + /// Manages a buffer of value type objects as a Disposable resource. /// The backing array is either pooled or comes from the outside. /// /// The value type. - internal class PinnedBuffer : IDisposable + internal class Buffer : IDisposable where T : struct { + /// + /// A pointer to the first element of when pinned. + /// + private IntPtr pointer; + /// /// A handle that allows to access the managed as an unmanaged memory by pinning. /// @@ -25,40 +30,38 @@ namespace ImageSharp /// /// A value indicating wheter should be returned to - /// when disposing this instance. + /// when disposing this instance. /// private bool isPoolingOwner; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The desired count of elements. (Minimum size for ) - public PinnedBuffer(int length) + public Buffer(int length) { this.Length = length; this.Array = PixelDataPool.Rent(length); this.isPoolingOwner = true; - this.Pin(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin. - public PinnedBuffer(T[] array) + public Buffer(T[] array) { this.Length = array.Length; this.Array = array; this.isPoolingOwner = false; - this.Pin(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin. /// The count of "relevant" elements in 'array'. - public PinnedBuffer(T[] array, int length) + public Buffer(T[] array, int length) { if (array.Length < length) { @@ -68,19 +71,18 @@ namespace ImageSharp this.Length = length; this.Array = array; this.isPoolingOwner = false; - this.Pin(); } /// - /// Finalizes an instance of the class. + /// Finalizes an instance of the class. /// - ~PinnedBuffer() + ~Buffer() { this.UnPin(); } /// - /// Gets a value indicating whether this instance is disposed, or has lost ownership of . + /// Gets a value indicating whether this instance is disposed, or has lost ownership of . /// public bool IsDisposedOrLostArrayOwnership { get; private set; } @@ -94,11 +96,6 @@ namespace ImageSharp /// public T[] Array { get; private set; } - /// - /// Gets a pointer to the pinned . - /// - public IntPtr Pointer { get; private set; } - /// /// Gets a to the backing buffer. /// @@ -109,37 +106,35 @@ namespace ImageSharp /// /// The index /// The reference to the specified element - public unsafe ref T this[int index] + public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - - byte* ptr = (byte*)this.Pointer + BufferSpan.SizeOf(index); - return ref Unsafe.AsRef(ptr); + return ref this.Array[index]; } } /// - /// Converts to an . + /// Converts to an . /// - /// The to convert. + /// The to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe implicit operator BufferSpan(PinnedBuffer buffer) + public static implicit operator BufferSpan(Buffer buffer) { - return new BufferSpan(buffer.Array, (void*)buffer.Pointer, 0, buffer.Length); + return new BufferSpan(buffer.Array, 0, buffer.Length); } /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// Creates a clean instance of initializing it's elements with 'default(T)'. /// /// The desired count of elements. (Minimum size for ) - /// The instance + /// The instance [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PinnedBuffer CreateClean(int count) + public static Buffer CreateClean(int count) { - PinnedBuffer buffer = new PinnedBuffer(count); + Buffer buffer = new Buffer(count); buffer.Clear(); return buffer; } @@ -150,9 +145,9 @@ namespace ImageSharp /// The start /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferSpan Slice(int start) + public BufferSpan Slice(int start) { - return new BufferSpan(this.Array, (void*)this.Pointer, start, this.Length - start); + return new BufferSpan(this.Array, start, this.Length - start); } /// @@ -162,13 +157,13 @@ namespace ImageSharp /// The length of the slice /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferSpan Slice(int start, int length) + public BufferSpan Slice(int start, int length) { - return new BufferSpan(this.Array, (void*)this.Pointer, start, length); + return new BufferSpan(this.Array, start, length); } /// - /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. + /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() @@ -199,11 +194,11 @@ namespace ImageSharp /// /// The unpinned [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] UnPinAndTakeArrayOwnership() + public T[] TakeArrayOwnership() { if (this.IsDisposedOrLostArrayOwnership) { - throw new InvalidOperationException("UnPinAndTakeArrayOwnership() is invalid: either PinnedBuffer is disposed or UnPinAndTakeArrayOwnership() has been called multiple times!"); + throw new InvalidOperationException("TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); } this.IsDisposedOrLostArrayOwnership = true; @@ -220,17 +215,29 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - ((BufferSpan)this).Clear(); + this.Span.Clear(); } /// /// Pins . /// + /// The pinned pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Pin() + public IntPtr Pin() { - this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); - this.Pointer = this.handle.AddrOfPinnedObject(); + if (this.IsDisposedOrLostArrayOwnership) + { + throw new InvalidOperationException( + "Pin() is invalid on a buffer with IsDisposedOrLostArrayOwnership == true!"); + } + + if (this.pointer == IntPtr.Zero) + { + this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); + this.pointer = this.handle.AddrOfPinnedObject(); + } + + return this.pointer; } /// @@ -239,13 +246,13 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UnPin() { - if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) + if (this.pointer == IntPtr.Zero || !this.handle.IsAllocated) { return; } this.handle.Free(); - this.Pointer = IntPtr.Zero; + this.pointer = IntPtr.Zero; } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs b/src/ImageSharp/Common/Memory/Buffer2D.cs similarity index 69% rename from src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs rename to src/ImageSharp/Common/Memory/Buffer2D.cs index 3ff174c5d..c4eb50700 100644 --- a/src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/Buffer2D.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -9,19 +9,19 @@ namespace ImageSharp using System.Runtime.CompilerServices; /// - /// Represents a pinned buffer of value type objects + /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. /// /// The value type. - internal class PinnedImageBuffer : PinnedBuffer, IPinnedImageBuffer + internal class Buffer2D : Buffer, IBuffer2D where T : struct { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of elements in a row /// The number of rows - public PinnedImageBuffer(int width, int height) + public Buffer2D(int width, int height) : base(width * height) { this.Width = width; @@ -29,12 +29,12 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin /// The number of elements in a row /// The number of rows - public PinnedImageBuffer(T[] array, int width, int height) + public Buffer2D(T[] array, int width, int height) : base(array, width * height) { this.Width = width; @@ -63,14 +63,14 @@ namespace ImageSharp } /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// Creates a clean instance of initializing it's elements with 'default(T)'. /// /// The number of elements in a row /// The number of rows - /// The instance - public static PinnedImageBuffer CreateClean(int width, int height) + /// The instance + public static Buffer2D CreateClean(int width, int height) { - PinnedImageBuffer buffer = new PinnedImageBuffer(width, height); + Buffer2D buffer = new Buffer2D(width, height); buffer.Clear(); return buffer; } diff --git a/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs b/src/ImageSharp/Common/Memory/Buffer2DExtensions.cs similarity index 78% rename from src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs rename to src/ImageSharp/Common/Memory/Buffer2DExtensions.cs index fcd5b3831..4c3cc4d40 100644 --- a/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs +++ b/src/ImageSharp/Common/Memory/Buffer2DExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -9,9 +9,9 @@ namespace ImageSharp using System.Runtime.CompilerServices; /// - /// Defines extension methods for . + /// Defines extension methods for . /// - internal static class PinnedImageBufferExtensions + internal static class Buffer2DExtensions { /// /// Gets a to the row 'y' beginning from the pixel at 'x'. @@ -22,7 +22,7 @@ namespace ImageSharp /// The element type /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferSpan GetRowSpan(this IPinnedImageBuffer buffer, int x, int y) + public static BufferSpan GetRowSpan(this IBuffer2D buffer, int x, int y) where T : struct { return buffer.Span.Slice((y * buffer.Width) + x, buffer.Width - x); @@ -36,7 +36,7 @@ namespace ImageSharp /// The element type /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferSpan GetRowSpan(this IPinnedImageBuffer buffer, int y) + public static BufferSpan GetRowSpan(this IBuffer2D buffer, int y) where T : struct { return buffer.Span.Slice(y * buffer.Width, buffer.Width); diff --git a/src/ImageSharp/Common/Memory/BufferSpan.cs b/src/ImageSharp/Common/Memory/BufferSpan.cs index 42a6fbc6b..c51c110be 100644 --- a/src/ImageSharp/Common/Memory/BufferSpan.cs +++ b/src/ImageSharp/Common/Memory/BufferSpan.cs @@ -6,7 +6,6 @@ namespace ImageSharp { using System; - using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -15,60 +14,49 @@ namespace ImageSharp /// internal static class BufferSpan { - /// - /// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size. - /// - private const int ByteCountThreshold = 1024; - /// /// Copy 'count' number of elements of the same type from 'source' to 'dest' /// /// The element type. - /// The input + /// The to copy elements from. /// The destination . /// The number of elements to copy [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(BufferSpan source, BufferSpan destination, int count) + public static unsafe void Copy(BufferSpan source, BufferSpan destination, int count) where T : struct { - CopyImpl(source, destination, count); - } + DebugGuard.MustBeLessThanOrEqualTo(count, source.Length, nameof(count)); + DebugGuard.MustBeLessThanOrEqualTo(count, destination.Length, nameof(count)); - /// - /// Copy 'countInSource' elements of from 'source' into the raw byte buffer 'destination'. - /// - /// The element type. - /// The source buffer of elements to copy from. - /// The destination buffer. - /// The number of elements to copy from 'source' - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(BufferSpan source, BufferSpan destination, int countInSource) - where T : struct - { - CopyImpl(source, destination, countInSource); + ref byte srcRef = ref Unsafe.As(ref source.DangerousGetPinnableReference()); + ref byte destRef = ref Unsafe.As(ref destination.DangerousGetPinnableReference()); + + int byteCount = Unsafe.SizeOf() * count; + + // TODO: Use unfixed Unsafe.CopyBlock(ref T, ref T, int) for small blocks, when it gets available! + fixed (byte* pSrc = &srcRef) + fixed (byte* pDest = &destRef) + { +#if NETSTANDARD1_1 + Unsafe.CopyBlock(pDest, pSrc, (uint)byteCount); +#else + int destLength = destination.Length * Unsafe.SizeOf(); + Buffer.MemoryCopy(pSrc, pDest, destLength, byteCount); +#endif + } } /// - /// Copy 'countInDest' number of elements into 'dest' from a raw byte buffer defined by 'source'. + /// Copy all elements of 'source' into 'destination'. /// /// The element type. - /// The raw source buffer to copy from"/> - /// The destination buffer"/> - /// The number of elements to copy. + /// The to copy elements from. + /// The destination . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(BufferSpan source, BufferSpan destination, int countInDest) + public static void Copy(BufferSpan source, BufferSpan destination) where T : struct { - int byteCount = SizeOf(countInDest); - - if (byteCount > (int)ByteCountThreshold) - { - Marshal.Copy(source.Array, source.Start, destination.PointerAtOffset, byteCount); - } - else - { - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount); - } + Copy(source, destination, source.Length); } /// @@ -91,39 +79,5 @@ namespace ImageSharp public static uint USizeOf(int count) where T : struct => (uint)SizeOf(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CopyImpl(BufferSpan source, BufferSpan destination, int count) - where T : struct - where TDest : struct - { - int byteCount = SizeOf(count); - - if (byteCount > ByteCountThreshold) - { - if (Unsafe.SizeOf() == sizeof(long)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(int)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(short)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(byte)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - } - - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs index 8ef88814c..1b0bef314 100644 --- a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Represents a contiguous region of a pinned managed array. - /// The array is usually owned by a instance. + /// The array is usually owned by a instance. /// /// /// is very similar to corefx System.Span<T>, and we try to maintain a compatible API. @@ -30,13 +30,12 @@ namespace ImageSharp /// Initializes a new instance of the struct from a pinned array and an start. /// /// The pinned array - /// Pointer to the beginning of the array /// The index at which to begin the span. /// The length [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan(T[] array, void* pointerToArray, int start, int length) + public BufferSpan(T[] array, int start, int length) { - GuardArrayAndPointer(array, pointerToArray); + GuardArray(array); DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start)); DebugGuard.MustBeLessThanOrEqualTo(length, array.Length - start, nameof(length)); @@ -44,45 +43,40 @@ namespace ImageSharp this.Array = array; this.Length = length; this.Start = start; - this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf() * start); } /// /// Initializes a new instance of the struct from a pinned array and an start. /// /// The pinned array - /// Pointer to the beginning of the array /// The index at which to begin the span. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan(T[] array, void* pointerToArray, int start) + public BufferSpan(T[] array, int start) { - GuardArrayAndPointer(array, pointerToArray); + GuardArray(array); DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start)); this.Array = array; this.Length = array.Length - start; this.Start = start; - this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf() * start); } /// /// Initializes a new instance of the struct from a pinned array. /// /// The pinned array - /// Pointer to the start of 'array' [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan(T[] array, void* pointerToArray) + public BufferSpan(T[] array) { - GuardArrayAndPointer(array, pointerToArray); + GuardArray(array); this.Array = array; this.Start = 0; this.Length = array.Length; - this.PointerAtOffset = (IntPtr)pointerToArray; } /// - /// Gets the backing array + /// Gets the backing array. /// public T[] Array { get; private set; } @@ -101,11 +95,6 @@ namespace ImageSharp /// public int ByteOffset => this.Start * Unsafe.SizeOf(); - /// - /// Gets the pointer to the offseted array position - /// - public IntPtr PointerAtOffset { get; private set; } - /// /// Returns a reference to specified element of the span. /// @@ -117,45 +106,36 @@ namespace ImageSharp get { DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - - byte* ptr = (byte*)this.PointerAtOffset + BufferSpan.SizeOf(index); - return ref Unsafe.AsRef(ptr); + ref T startRef = ref this.DangerousGetPinnableReference(); + return ref Unsafe.Add(ref startRef, index); } } /// - /// Convertes instance to a raw 'void*' pointer - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(BufferSpan bufferSpan) - { - return (void*)bufferSpan.PointerAtOffset; - } - - /// - /// Converts instance to a raw 'byte*' pointer + /// Converts generic to a of bytes + /// setting it's and to correct values. /// - /// The to convert + /// The span of bytes [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte*(BufferSpan bufferSpan) + public BufferSpan AsBytes() { - return (byte*)bufferSpan.PointerAtOffset; + BufferSpan result = default(BufferSpan); + result.Array = Unsafe.As(this.Array); + result.Start = this.Start * Unsafe.SizeOf(); + result.Length = this.Length * Unsafe.SizeOf(); + return result; } /// - /// Converts generic to a of bytes - /// setting it's and to correct values. + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// - /// The to convert + /// The reference to the 0th element [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator BufferSpan(BufferSpan source) + public ref T DangerousGetPinnableReference() { - BufferSpan result = default(BufferSpan); - result.Array = Unsafe.As(source.Array); - result.Start = source.Start * Unsafe.SizeOf(); - result.PointerAtOffset = source.PointerAtOffset; - return result; + ref T origin = ref this.Array[0]; + return ref Unsafe.Add(ref origin, this.Start); } /// @@ -171,7 +151,6 @@ namespace ImageSharp BufferSpan result = default(BufferSpan); result.Array = this.Array; result.Start = this.Start + start; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * start); result.Length = this.Length - start; return result; } @@ -191,7 +170,6 @@ namespace ImageSharp BufferSpan result = default(BufferSpan); result.Array = this.Array; result.Start = this.Start + start; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * start); result.Length = length; return result; } @@ -205,14 +183,8 @@ namespace ImageSharp { DebugGuard.MustBeLessThanOrEqualTo(count, this.Length, nameof(count)); - if (count < 256) - { - Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferSpan.USizeOf(count)); - } - else - { - System.Array.Clear(this.Array, this.Start, count); - } + // TODO: Use Unsafe.InitBlock(ref T) for small arrays, when it get's official + System.Array.Clear(this.Array, this.Start, count); } /// @@ -225,13 +197,9 @@ namespace ImageSharp } [Conditional("DEBUG")] - private static void GuardArrayAndPointer(T[] array, void* pointerToArray) + private static void GuardArray(T[] array) { DebugGuard.NotNull(array, nameof(array)); - DebugGuard.IsFalse( - pointerToArray == (void*)0, - nameof(pointerToArray), - "pointerToArray should not be null pointer!"); } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs b/src/ImageSharp/Common/Memory/IBuffer2D.cs similarity index 86% rename from src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs rename to src/ImageSharp/Common/Memory/IBuffer2D.cs index 374cbed99..1ba08e49f 100644 --- a/src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/IBuffer2D.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,7 +10,7 @@ namespace ImageSharp /// interpreted as a 2D region of x elements. /// /// The value type. - internal interface IPinnedImageBuffer + internal interface IBuffer2D where T : struct { /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 66f400c01..c3cf75a0f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -307,7 +307,8 @@ namespace ImageSharp.Formats rgbBytes.Reset(); pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); - byte* data = (byte*)rgbBytes.DataPointer; + ref byte data0 = ref rgbBytes.Bytes[0]; + int dataIdx = 0; for (int j = 0; j < 8; j++) { @@ -315,9 +316,9 @@ namespace ImageSharp.Formats for (int i = 0; i < 8; i++) { // Convert returned bytes into the YCbCr color space. Assume RGBA - int r = data[0]; - int g = data[1]; - int b = data[2]; + int r = Unsafe.Add(ref data0, dataIdx); + int g = Unsafe.Add(ref data0, dataIdx + 1); + int b = Unsafe.Add(ref data0, dataIdx + 2); // Speed up the algorithm by removing floating point calculation // Scale by 65536, add .5F and truncate value. We use bit shifting to divide the result @@ -343,7 +344,7 @@ namespace ImageSharp.Formats cbBlockRaw[index] = cb; crBlockRaw[index] = cr; - data += 3; + dataIdx += 3; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs b/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs index dd96985d9..ace309812 100644 --- a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Formats.Jpg { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; /// /// Jpeg specific utilities and extension methods @@ -33,19 +34,6 @@ namespace ImageSharp.Formats.Jpg StretchPixels(dest, stretchFromX, stretchFromY); } - /// - /// Copy an RGB value - /// - /// Source pointer - /// Destination pointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyRgb(byte* source, byte* dest) - { - *dest++ = *source++; // R - *dest++ = *source++; // G - *dest = *source; // B - } - // Nothing to stretch if (fromX, fromY) is outside the area, or is at (0,0) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsInvalidStretchStartingPosition(PixelArea area, int fromX, int fromY) @@ -64,31 +52,38 @@ namespace ImageSharp.Formats.Jpg for (int y = 0; y < fromY; y++) { - byte* ptrBase = (byte*)area.DataPointer + (y * area.RowStride); + ref RGB24 ptrBase = ref GetRowStart(area, y); for (int x = fromX; x < area.Width; x++) { - byte* prevPtr = ptrBase + ((x - 1) * 3); - byte* currPtr = ptrBase + (x * 3); - - CopyRgb(prevPtr, currPtr); + // Copy the left neighbour pixel to the current one + Unsafe.Add(ref ptrBase, x) = Unsafe.Add(ref ptrBase, x - 1); } } for (int y = fromY; y < area.Height; y++) { - byte* currBase = (byte*)area.DataPointer + (y * area.RowStride); - byte* prevBase = (byte*)area.DataPointer + ((y - 1) * area.RowStride); + ref RGB24 currBase = ref GetRowStart(area, y); + ref RGB24 prevBase = ref GetRowStart(area, y - 1); for (int x = 0; x < area.Width; x++) { - int x3 = 3 * x; - byte* currPtr = currBase + x3; - byte* prevPtr = prevBase + x3; - - CopyRgb(prevPtr, currPtr); + // Copy the top neighbour pixel to the current one + Unsafe.Add(ref currBase, x) = Unsafe.Add(ref prevBase, x); } } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ref RGB24 GetRowStart(PixelArea area, int y) + where TColor : struct, IPixel + { + return ref Unsafe.As(ref area.GetRowSpan(y).DangerousGetPinnableReference()); + } + + [StructLayout(LayoutKind.Sequential, Size = 3)] + private struct RGB24 + { + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index f5393cfb3..fb3613adf 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -15,14 +15,9 @@ namespace ImageSharp /// Provides per-pixel access to generic pixels. /// /// The pixel format. - public sealed unsafe class PixelAccessor : IDisposable, IPinnedImageBuffer + public sealed class PixelAccessor : IDisposable, IBuffer2D where TColor : struct, IPixel { - /// - /// The position of the first pixel in the image. - /// - private byte* pixelsBase; - /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -35,9 +30,9 @@ namespace ImageSharp private bool isDisposed; /// - /// The containing the pixel data. + /// The containing the pixel data. /// - private PinnedImageBuffer pixelBuffer; + private Buffer2D pixelBuffer; /// /// Initializes a new instance of the class. @@ -59,7 +54,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, PinnedImageBuffer.CreateClean(width, height)) + : this(width, height, Buffer2D.CreateClean(width, height)) { } @@ -69,7 +64,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - private PixelAccessor(int width, int height, PinnedImageBuffer pixels) + private PixelAccessor(int width, int height, Buffer2D pixels) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); @@ -93,11 +88,6 @@ namespace ImageSharp /// public TColor[] PixelArray => this.pixelBuffer.Array; - /// - /// Gets the pointer to the pixel buffer. - /// - public IntPtr DataPointer => this.pixelBuffer.Pointer; - /// /// Gets the size of a single pixel in the number of bytes. /// @@ -124,7 +114,7 @@ namespace ImageSharp public ParallelOptions ParallelOptions { get; } /// - BufferSpan IPinnedImageBuffer.Span => this.pixelBuffer; + BufferSpan IBuffer2D.Span => this.pixelBuffer; private static BulkPixelOperations Operations => BulkPixelOperations.Instance; @@ -139,15 +129,13 @@ namespace ImageSharp get { this.CheckCoordinates(x, y); - - return Unsafe.Read(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf())); + return this.PixelArray[(y * this.Width) + x]; } set { this.CheckCoordinates(x, y); - - Unsafe.Write(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf()), value); + this.PixelArray[(y * this.Width) + x] = value; } } @@ -179,7 +167,7 @@ namespace ImageSharp /// public void Reset() { - Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height)); + this.pixelBuffer.Clear(); } /// @@ -251,7 +239,7 @@ namespace ImageSharp /// If is true then caller is responsible for ensuring is called. internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels) { - TColor[] oldPixels = this.pixelBuffer.UnPinAndTakeArrayOwnership(); + TColor[] oldPixels = this.pixelBuffer.TakeArrayOwnership(); this.SetPixelBufferUnsafe(width, height, pixels); return oldPixels; } @@ -262,9 +250,7 @@ namespace ImageSharp /// The target pixel buffer accessor. internal void CopyTo(PixelAccessor target) { - uint byteCount = (uint)(this.Width * this.Height * Unsafe.SizeOf()); - - Unsafe.CopyBlock(target.pixelsBase, this.pixelsBase, byteCount); + BufferSpan.Copy(this.pixelBuffer.Span, target.pixelBuffer.Span); } /// @@ -424,7 +410,7 @@ namespace ImageSharp private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels) { - this.SetPixelBufferUnsafe(width, height, new PinnedImageBuffer(pixels, width, height)); + this.SetPixelBufferUnsafe(width, height, new Buffer2D(pixels, width, height)); } /// @@ -433,10 +419,9 @@ namespace ImageSharp /// The width. /// The height. /// The pixel buffer - private void SetPixelBufferUnsafe(int width, int height, PinnedImageBuffer pixels) + private void SetPixelBufferUnsafe(int width, int height, Buffer2D pixels) { this.pixelBuffer = pixels; - this.pixelsBase = (byte*)pixels.Pointer; this.Width = width; this.Height = height; diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index bd10c9b6b..176eb0a16 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -13,7 +13,7 @@ namespace ImageSharp /// Represents an area of generic pixels. /// /// The pixel format. - internal sealed unsafe class PixelArea : IDisposable + internal sealed class PixelArea : IDisposable where TColor : struct, IPixel { /// @@ -30,7 +30,7 @@ namespace ImageSharp /// /// The underlying buffer containing the raw pixel data. /// - private PinnedBuffer byteBuffer; + private Buffer byteBuffer; /// /// Initializes a new instance of the class. @@ -66,8 +66,7 @@ namespace ImageSharp this.RowStride = width * GetComponentCount(componentOrder); this.Length = bytes.Length; // TODO: Is this the right value for Length? - this.byteBuffer = new PinnedBuffer(bytes); - this.PixelBase = (byte*)this.byteBuffer.Pointer; + this.byteBuffer = new Buffer(bytes); } /// @@ -117,8 +116,7 @@ namespace ImageSharp this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.byteBuffer = new PinnedBuffer(this.Length); - this.PixelBase = (byte*)this.byteBuffer.Pointer; + this.byteBuffer = new Buffer(this.Length); } /// @@ -136,21 +134,11 @@ namespace ImageSharp /// public ComponentOrder ComponentOrder { get; } - /// - /// Gets the pointer to the pixel buffer. - /// - public IntPtr DataPointer => this.byteBuffer.Pointer; - /// /// Gets the height. /// public int Height { get; } - /// - /// Gets the data pointer. - /// - public byte* PixelBase { get; private set; } - /// /// Gets the width of one row in the number of bytes. /// @@ -198,7 +186,7 @@ namespace ImageSharp /// public void Reset() { - Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowStride * this.Height)); + this.byteBuffer.Clear(); } /// diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index c51c0833a..a19bed604 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -2,7 +2,7 @@ A cross-platform library for the processing of image files; written in C# ImageSharp - 1.0.0-alpha5 + 1.0.0-alpha6 James Jackson-South and contributors netstandard1.3;netstandard1.1 true diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index 58f32bdd8..6164bd228 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs @@ -73,6 +73,8 @@ namespace ImageSharp /// public Collection Read(byte[] data) { + DebugGuard.NotNull(data, nameof(data)); + Collection result = new Collection(); this.exifData = data; @@ -390,7 +392,13 @@ namespace ImageSharp private string GetString(uint length) { - return ToString(this.GetBytes(length)); + byte[] data = this.GetBytes(length); + if (data == null || data.Length == 0) + { + return null; + } + + return ToString(data); } private void GetThumbnail(uint offset) diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 99b143de6..4c43d654d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -41,7 +41,7 @@ namespace ImageSharp.Processing.Processors /// /// Gets an unsafe float* pointer to the beginning of . /// - public float* Ptr => (float*)this.Span.PointerAtOffset; + public ref float Ptr => ref this.Span.DangerousGetPinnableReference(); /// /// Gets the lenghth of the weights window @@ -56,19 +56,18 @@ namespace ImageSharp.Processing.Processors [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ComputeWeightedRowSum(BufferSpan rowSpan) { - float* horizontalValues = this.Ptr; + ref float horizontalValues = ref this.Ptr; int left = this.Left; - Vector4* vecPtr = (Vector4*)rowSpan.PointerAtOffset; - vecPtr += left; + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left); // Destination color components Vector4 result = Vector4.Zero; for (int i = 0; i < this.Length; i++) { - float weight = horizontalValues[i]; - result += (*vecPtr) * weight; - vecPtr++; + float weight = Unsafe.Add(ref horizontalValues, i); + Vector4 v = Unsafe.Add(ref vecPtr, i); + result += v * weight; } return result; @@ -83,19 +82,18 @@ namespace ImageSharp.Processing.Processors [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ComputeExpandedWeightedRowSum(BufferSpan rowSpan) { - float* horizontalValues = this.Ptr; + ref float horizontalValues = ref this.Ptr; int left = this.Left; - Vector4* vecPtr = (Vector4*)rowSpan.PointerAtOffset; - vecPtr += left; + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left); // Destination color components Vector4 result = Vector4.Zero; for (int i = 0; i < this.Length; i++) { - float weight = horizontalValues[i]; - result += (*vecPtr).Expand() * weight; - vecPtr++; + float weight = Unsafe.Add(ref horizontalValues, i); + Vector4 v = Unsafe.Add(ref vecPtr, i); + result += v.Expand() * weight; } return result; @@ -109,9 +107,9 @@ namespace ImageSharp.Processing.Processors /// The column position /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedColumnSum(PinnedImageBuffer firstPassPixels, int x) + public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x) { - float* verticalValues = this.Ptr; + ref float verticalValues = ref this.Ptr; int left = this.Left; // Destination color components @@ -119,7 +117,7 @@ namespace ImageSharp.Processing.Processors for (int i = 0; i < this.Length; i++) { - float yw = verticalValues[i]; + float yw = Unsafe.Add(ref verticalValues, i); int index = left + i; result += firstPassPixels[x, index] * yw; } @@ -133,7 +131,7 @@ namespace ImageSharp.Processing.Processors /// internal class WeightsBuffer : IDisposable { - private PinnedImageBuffer dataBuffer; + private Buffer2D dataBuffer; /// /// Initializes a new instance of the class. @@ -142,7 +140,7 @@ namespace ImageSharp.Processing.Processors /// The size of the destination window public WeightsBuffer(int sourceSize, int destinationSize) { - this.dataBuffer = PinnedImageBuffer.CreateClean(sourceSize, destinationSize); + this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize); this.Weights = new WeightsWindow[destinationSize]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 1374e5815..50c75a3fd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Processing.Processors { using System; using System.Buffers; + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; /// @@ -111,13 +112,15 @@ namespace ImageSharp.Processing.Processors WeightsWindow ws = result.GetWeightsWindow(i, left, right); result.Weights[i] = ws; - float* weights = ws.Ptr; + ref float weights = ref ws.Ptr; for (int j = left; j <= right; j++) { float weight = sampler.GetValue((j - center) / scale); sum += weight; - weights[j - left] = weight; + + // weights[j - left] = weight: + Unsafe.Add(ref weights, j - left) = weight; } // Normalise, best to do it here rather than in the pixel loop later on. @@ -125,7 +128,9 @@ namespace ImageSharp.Processing.Processors { for (int w = 0; w < ws.Length; w++) { - weights[w] = weights[w] / sum; + // weights[w] = weights[w] / sum: + ref float wRef = ref Unsafe.Add(ref weights, w); + wRef = wRef / sum; } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 944e245ac..08d96e283 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -106,7 +106,7 @@ namespace ImageSharp.Processing.Processors using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { using (PixelAccessor sourcePixels = source.Lock()) - using (PinnedImageBuffer firstPassPixels = new PinnedImageBuffer(width, source.Height)) + using (Buffer2D firstPassPixels = new Buffer2D(width, source.Height)) { firstPassPixels.Clear(); @@ -117,7 +117,7 @@ namespace ImageSharp.Processing.Processors y => { // TODO: Without Parallel.For() this buffer object could be reused: - using (PinnedBuffer tempRowBuffer = new PinnedBuffer(sourcePixels.Width)) + using (Buffer tempRowBuffer = new Buffer(sourcePixels.Width)) { BufferSpan sourceRow = sourcePixels.GetRowSpan(y); diff --git a/src/ImageSharp/Quantizers/Quantizer.cs b/src/ImageSharp/Quantizers/Quantizer.cs index bb856ccc5..492ec5f2b 100644 --- a/src/ImageSharp/Quantizers/Quantizer.cs +++ b/src/ImageSharp/Quantizers/Quantizer.cs @@ -68,7 +68,20 @@ namespace ImageSharp.Quantizers // Collect the palette. Required before the second pass runs. colorPalette = this.GetPalette(); - this.SecondPass(pixels, quantizedPixels, width, height); + + if (this.Dither) + { + // We clone the image as we don't want to alter the original. + using (Image clone = new Image(image)) + using (PixelAccessor clonedPixels = clone.Lock()) + { + this.SecondPass(clonedPixels, quantizedPixels, width, height); + } + } + else + { + this.SecondPass(pixels, quantizedPixels, width, height); + } } return new QuantizedImage(width, height, colorPalette, quantizedPixels); diff --git a/src/ImageSharp/Quantizers/WuQuantizer.cs b/src/ImageSharp/Quantizers/WuQuantizer.cs index c1c81d0ac..46af6fab9 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer.cs @@ -136,7 +136,7 @@ namespace ImageSharp.Quantizers { Guard.NotNull(image, nameof(image)); - this.colors = maxColors.Clamp(1, 256); + this.colors = maxColors.Clamp(1, 255); try { diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index e912ea29f..befff61d5 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -6,7 +6,7 @@ using BenchmarkDotNet.Attributes; using ImageSharp; - + /// /// Compares two implementation candidates for general BulkPixelOperations.ToVector4(): /// - One iterating with pointers @@ -14,9 +14,9 @@ /// public unsafe class PackFromVector4ReferenceVsPointer { - private PinnedBuffer destination; + private Buffer destination; - private PinnedBuffer source; + private Buffer source; [Params(16, 128, 1024)] public int Count { get; set; } @@ -24,8 +24,10 @@ [Setup] public void Setup() { - this.destination = new PinnedBuffer(this.Count); - this.source = new PinnedBuffer(this.Count * 4); + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count * 4); + this.source.Pin(); + this.destination.Pin(); } [Cleanup] @@ -38,8 +40,8 @@ [Benchmark(Baseline = true)] public void PackUsingPointers() { - Vector4* sp = (Vector4*)this.source.Pointer; - byte* dp = (byte*)this.destination.Pointer; + Vector4* sp = (Vector4*)this.source.Pin(); + byte* dp = (byte*)this.destination.Pin(); int count = this.Count; int size = sizeof(ImageSharp.Color); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 1c541d28b..33969b8fb 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class PackFromXyzw where TColor : struct, IPixel { - private PinnedBuffer destination; + private Buffer destination; - private PinnedBuffer source; + private Buffer source; [Params(16, 128, 1024)] public int Count { get; set; } @@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.destination = new PinnedBuffer(this.Count); - this.source = new PinnedBuffer(this.Count * 4); + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count * 4); } [Cleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index b48eaa35a..4cabfc01f 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class ToVector4 where TColor : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(64, 300, 1024)] public int Count { get; set; } @@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count); } [Cleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index bc59dba4e..f6ae4256d 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class ToXyz where TColor : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(16, 128, 1024)] public int Count { get; set; } @@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count * 3); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count * 3); } [Cleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index a4ec6f6dc..f23ca3e5c 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -13,9 +13,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class ToXyzw where TColor : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(16, 128, 1024)] public int Count { get; set; } @@ -23,8 +23,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count * 4); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count * 4); } [Cleanup] @@ -64,7 +64,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk { } - public class ToXyzw_Argb : ToXyzw + public class ToXyzw_Argb : ToXyzw { } } diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index 72fd6dc24..31e9cc0e3 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Benchmarks.General using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; - + [Config(typeof(Config.Short))] public class ArrayCopy { @@ -58,8 +58,7 @@ namespace ImageSharp.Benchmarks.General Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); } } - - + [Benchmark(Description = "Copy using Marshal.Copy")] public unsafe void CopyUsingMarshalCopy() { @@ -68,5 +67,37 @@ namespace ImageSharp.Benchmarks.General Marshal.Copy(this.source, 0, (IntPtr)pinnedDestination, this.Count); } } + + /***************************************************************************************************************** + *************** RESULTS on i7-4810MQ 2.80GHz + Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1085.0 ******************** + ***************************************************************************************************************** + * + * Method | Count | Mean | StdErr | StdDev | Scaled | Scaled-StdDev | + * ---------------------------------- |------ |------------ |----------- |----------- |------- |-------------- | + * 'Copy using Array.Copy()' | 10 | 20.3074 ns | 0.1194 ns | 0.2068 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 10 | 6.1002 ns | 0.1981 ns | 0.3432 ns | 0.30 | 0.01 | + * 'Copy using Buffer.BlockCopy()' | 10 | 10.7879 ns | 0.0984 ns | 0.1705 ns | 0.53 | 0.01 | + * 'Copy using Buffer.MemoryCopy' | 10 | 4.9625 ns | 0.0200 ns | 0.0347 ns | 0.24 | 0.00 | + * 'Copy using Marshal.Copy' | 10 | 16.1782 ns | 0.0919 ns | 0.1592 ns | 0.80 | 0.01 | + * + * 'Copy using Array.Copy()' | 100 | 31.5945 ns | 0.2908 ns | 0.5037 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 100 | 10.2722 ns | 0.5202 ns | 0.9010 ns | 0.33 | 0.02 | + * 'Copy using Buffer.BlockCopy()' | 100 | 22.0322 ns | 0.0284 ns | 0.0493 ns | 0.70 | 0.01 | + * 'Copy using Buffer.MemoryCopy' | 100 | 10.2472 ns | 0.0359 ns | 0.0622 ns | 0.32 | 0.00 | + * 'Copy using Marshal.Copy' | 100 | 34.3820 ns | 1.1868 ns | 2.0555 ns | 1.09 | 0.05 | + * + * 'Copy using Array.Copy()' | 1000 | 40.9743 ns | 0.0521 ns | 0.0902 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 1000 | 42.7840 ns | 2.0139 ns | 3.4882 ns | 1.04 | 0.07 | + * 'Copy using Buffer.BlockCopy()' | 1000 | 33.7361 ns | 0.0751 ns | 0.1300 ns | 0.82 | 0.00 | + * 'Copy using Buffer.MemoryCopy' | 1000 | 35.7541 ns | 0.0480 ns | 0.0832 ns | 0.87 | 0.00 | + * 'Copy using Marshal.Copy' | 1000 | 42.2028 ns | 0.2769 ns | 0.4795 ns | 1.03 | 0.01 | + * + * 'Copy using Array.Copy()' | 10000 | 200.0438 ns | 0.2251 ns | 0.3899 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 10000 | 389.6957 ns | 13.2770 ns | 22.9964 ns | 1.95 | 0.09 | + * 'Copy using Buffer.BlockCopy()' | 10000 | 191.3478 ns | 0.1557 ns | 0.2697 ns | 0.96 | 0.00 | + * 'Copy using Buffer.MemoryCopy' | 10000 | 196.4679 ns | 0.2731 ns | 0.4730 ns | 0.98 | 0.00 | + * 'Copy using Marshal.Copy' | 10000 | 202.5392 ns | 0.5561 ns | 0.9631 ns | 1.01 | 0.00 | + * + */ } } diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index 9aa836de5..c7a2021de 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -11,15 +11,15 @@ namespace ImageSharp.Benchmarks.General public unsafe class ClearBuffer { - private PinnedBuffer buffer; - + private Buffer buffer; + [Params(32, 128, 512)] public int Count { get; set; } [Setup] public void Setup() { - this.buffer = new PinnedBuffer(this.Count); + this.buffer = new Buffer(this.Count); } [Cleanup] @@ -37,7 +37,7 @@ namespace ImageSharp.Benchmarks.General [Benchmark] public void Unsafe_InitBlock() { - Unsafe.InitBlock((void*)this.buffer.Pointer, default(byte), (uint)this.Count*sizeof(uint)); + Unsafe.InitBlock((void*)this.buffer.Pin(), default(byte), (uint)this.Count * sizeof(uint)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index 48f2316a2..eeab6506a 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Benchmarks.General public class IterateArray { // Usual pinned stuff - private PinnedBuffer buffer; + private Buffer buffer; // An array that's not pinned by intent! private Vector4[] array; @@ -19,7 +19,8 @@ namespace ImageSharp.Benchmarks.General [Setup] public void Setup() { - this.buffer = new PinnedBuffer(this.Length); + this.buffer = new Buffer(this.Length); + this.buffer.Pin(); this.array = new Vector4[this.Length]; } @@ -41,7 +42,7 @@ namespace ImageSharp.Benchmarks.General { Vector4 sum = new Vector4(); - Vector4* ptr = (Vector4*) this.buffer.Pointer; + Vector4* ptr = (Vector4*) this.buffer.Pin(); Vector4* end = ptr + this.Length; for (; ptr < end; ptr++) diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs new file mode 100644 index 000000000..d9237b801 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -0,0 +1,360 @@ +namespace ImageSharp.Benchmarks.General +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + using BenchmarkDotNet.Attributes; + + // Pixel indexing benchmarks compare different methods for getting/setting all pixel values in a subsegment of a single pixel row. + public abstract unsafe class PixelIndexing + { + /// + /// https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Pinnable.cs + /// + protected class Pinnable + { + public T Data; + } + + /// + /// The indexer methods are encapsulated into a struct to make sure everything is inlined. + /// + internal struct Data + { + private Vector4* pointer; + + private Pinnable pinnable; + + private Vector4[] array; + + private int width; + + public Data(Buffer2D buffer) + { + this.pointer = (Vector4*)buffer.Pin(); + this.pinnable = Unsafe.As>(buffer.Array); + this.array = buffer.Array; + this.width = buffer.Width; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 GetPointersBasicImpl(int x, int y) + { + return this.pointer[y * this.width + x]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 GetPointersSrcsUnsafeImpl(int x, int y) + { + // This is the original solution in PixelAccessor: + return Unsafe.Read((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf())); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 GetReferencesImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + return Unsafe.Add(ref this.pinnable.Data, elementOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 GetReferencesRefReturnsImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithPointersBasicImpl(int x, int y, Vector4 v) + { + this.pointer[y * this.width + x] = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithPointersSrcsUnsafeImpl(int x, int y, Vector4 v) + { + Unsafe.Write((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf()), v); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithReferencesOnPinnableIncorrectImpl(int x, int y, Vector4 v) + { + int elementOffset = (y * this.width) + x; + // Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68 + Unsafe.Add(ref this.pinnable.Data, elementOffset) = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 IndexWithReferencesOnPinnableIncorrectRefReturnImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + // Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68 + return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithUnsafeReferenceArithmeticsOnArray0Impl(int x, int y, Vector4 v) + { + int elementOffset = (y * this.width) + x; + Unsafe.Add(ref this.array[0], elementOffset) = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + return ref Unsafe.Add(ref this.array[0], elementOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexSetArrayStraightforward(int x, int y, Vector4 v) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + this.array[(y * this.width) + x] = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 IndexWithReferencesOnArrayStraightforwardRefReturnImpl(int x, int y) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + return ref this.array[(y * this.width) + x]; + } + } + + internal Buffer2D buffer; + + protected int width; + + protected int startIndex; + + protected int endIndex; + + protected Vector4* pointer; + + protected Vector4[] array; + + protected Pinnable pinnable; + + // [Params(1024)] + public int Count { get; set; } = 1024; + + [Setup] + public void Setup() + { + this.width = 2048; + this.buffer = new Buffer2D(2048, 2048); + this.pointer = (Vector4*)this.buffer.Pin(); + this.array = this.buffer.Array; + this.pinnable = Unsafe.As>(this.array); + + this.startIndex = 2048 / 2 - (this.Count / 2); + this.endIndex = 2048 / 2 + (this.Count / 2); + } + + [Cleanup] + public void Cleanup() + { + this.buffer.Dispose(); + } + + } + + public class PixelIndexingGetter : PixelIndexing + { + [Benchmark(Description = "Index.Get: Pointers+arithmetics", Baseline = true)] + public Vector4 IndexWithPointersBasic() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetPointersBasicImpl(x, y); + } + + return sum; + } + + [Benchmark(Description = "Index.Get: Pointers+SRCS.Unsafe")] + public Vector4 IndexWithPointersSrcsUnsafe() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetPointersSrcsUnsafeImpl(x, y); + } + + return sum; + } + + [Benchmark(Description = "Index.Get: References")] + public Vector4 IndexWithReferences() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetReferencesImpl(x, y); + } + + return sum; + } + + [Benchmark(Description = "Index.Get: References|refreturns")] + public Vector4 IndexWithReferencesRefReturns() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetReferencesRefReturnsImpl(x, y); + } + + return sum; + } + } + + public class PixelIndexingSetter : PixelIndexing + { + [Benchmark(Description = "!!! Index.Set: Pointers|arithmetics", Baseline = true)] + public void IndexWithPointersBasic() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithPointersBasicImpl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: Pointers|SRCS.Unsafe")] + public void IndexWithPointersSrcsUnsafe() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithPointersSrcsUnsafeImpl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: References|IncorrectPinnable")] + public void IndexWithReferencesPinnableBasic() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithReferencesOnPinnableIncorrectImpl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: References|IncorrectPinnable|refreturn")] + public void IndexWithReferencesPinnableRefReturn() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithReferencesOnPinnableIncorrectRefReturnImpl(x, y) = v; + } + } + + [Benchmark(Description = "Index.Set: References|Array[0]Unsafe")] + public void IndexWithReferencesArrayBasic() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithUnsafeReferenceArithmeticsOnArray0Impl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: References|Array[0]Unsafe|refreturn")] + public void IndexWithReferencesArrayRefReturn() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(x, y) = v; + } + } + + [Benchmark(Description = "!!! Index.Set: References|Array+Straight")] + public void IndexWithReferencesArrayStraightforward() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + data.IndexSetArrayStraightforward(x, y, v); + } + } + + + [Benchmark(Description = "!!! Index.Set: References|Array+Straight|refreturn")] + public void IndexWithReferencesArrayStraightforwardRefReturn() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(x, y) = v; + } + } + + [Benchmark(Description = "!!! Index.Set: SmartUnsafe")] + public void SmartUnsafe() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + // This method is basically an unsafe variant of .GetRowSpan(y) + indexing individual pixels in the row. + // If a user seriously needs by-pixel manipulation to be performant, we should provide this option. + + ref Vector4 rowStart = ref data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(this.startIndex, this.startIndex); + + for (int i = 0; i < this.Count; i++) + { + // We don't have to add 'Width * y' here! + Unsafe.Add(ref rowStart, i) = v; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 04570ce8f..569070af2 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; using CoreSize = ImageSharp.Size; using CoreImage = ImageSharp.Image; + using CoreVectorImage = ImageSharp.Image; public class Resize : BenchmarkBase { @@ -44,6 +45,16 @@ namespace ImageSharp.Benchmarks } } + [Benchmark(Description = "ImageSharp Vector Resize")] + public CoreSize ResizeCoreVector() + { + using (CoreVectorImage image = new CoreVectorImage(2000, 2000)) + { + image.Resize(400, 400); + return new CoreSize(image.Width, image.Height); + } + } + [Benchmark(Description = "ImageSharp Compand Resize")] public CoreSize ResizeCoreCompand() { @@ -53,5 +64,15 @@ namespace ImageSharp.Benchmarks return new CoreSize(image.Width, image.Height); } } + + [Benchmark(Description = "ImageSharp Vector Compand Resize")] + public CoreSize ResizeCoreVectorCompand() + { + using (CoreVectorImage image = new CoreVectorImage(2000, 2000)) + { + image.Resize(400, 400, true); + return new CoreSize(image.Width, image.Height); + } + } } } diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index dad603523..1bd51d8f3 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -7,7 +7,7 @@ namespace ImageSharp.Sandbox46 { using System; using System.Runtime.DesignerServices; - + using ImageSharp.Tests; using ImageSharp.Tests.Colors; @@ -53,10 +53,10 @@ namespace ImageSharp.Sandbox46 private static void RunToVector4ProfilingTest() { - BulkPixelOperationsTests.Color tests = new BulkPixelOperationsTests.Color(new ConsoleOutput()); + BulkPixelOperationsTests.Color32 tests = new BulkPixelOperationsTests.Color32(new ConsoleOutput()); tests.Benchmark_ToVector4(); } - + private static void RunDecodeJpegProfilingTests() { Console.WriteLine("RunDecodeJpegProfilingTests..."); diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 60e25aa04..d4b9f6f3a 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -10,22 +10,22 @@ namespace ImageSharp.Tests.Colors public class BulkPixelOperationsTests { - public class Color : BulkPixelOperationsTests + public class Color32 : BulkPixelOperationsTests { - public Color(ITestOutputHelper output) + public Color32(ITestOutputHelper output) : base(output) { } // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; + //public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; [Fact] public void IsSpecialImplementation() { Assert.IsType(BulkPixelOperations.Instance); } - + [Fact] public void ToVector4SimdAligned() { @@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => ImageSharp.Color.BulkOperations.ToVector4SimdAligned(s, d, 64) - ); + ); } // [Fact] // Profiling benchmark - enable manually! @@ -45,8 +45,8 @@ namespace ImageSharp.Tests.Colors int times = 200000; int count = 1024; - using (PinnedBuffer source = new PinnedBuffer(count)) - using (PinnedBuffer dest = new PinnedBuffer(count)) + using (Buffer source = new Buffer(count)) + using (Buffer dest = new Buffer(count)) { this.Measure( times, @@ -58,7 +58,7 @@ namespace ImageSharp.Tests.Colors } } - public class Argb : BulkPixelOperationsTests + public class Argb : BulkPixelOperationsTests { // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: public Argb(ITestOutputHelper output) @@ -66,13 +66,13 @@ namespace ImageSharp.Tests.Colors { } - public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; + //public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; } [Theory] [WithBlankImages(1, 1, PixelTypes.All)] public void GetGlobalInstance(TestImageProvider dummy) - where TColor:struct, IPixel + where TColor : struct, IPixel { Assert.NotNull(BulkPixelOperations.Instance); } @@ -112,7 +112,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.PackFromVector4(s, d, count) - ); + ); } internal static Vector4[] CreateExpectedVector4Data(TColor[] source) @@ -137,7 +137,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToVector4(s, d, count) - ); + ); } @@ -156,10 +156,10 @@ namespace ImageSharp.Tests.Colors } TestOperation( - source, - expected, + source, + expected, (s, d) => Operations.PackFromXyzBytes(s, d, count) - ); + ); } [Theory] @@ -179,7 +179,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToXyzBytes(s, d, count) - ); + ); } [Theory] @@ -200,7 +200,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.PackFromXyzwBytes(s, d, count) - ); + ); } [Theory] @@ -220,7 +220,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToXyzwBytes(s, d, count) - ); + ); } [Theory] @@ -241,7 +241,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.PackFromZyxBytes(s, d, count) - ); + ); } [Theory] @@ -261,7 +261,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToZyxBytes(s, d, count) - ); + ); } [Theory] @@ -282,7 +282,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.PackFromZyxwBytes(s, d, count) - ); + ); } [Theory] @@ -302,26 +302,26 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToZyxwBytes(s, d, count) - ); + ); } - + private class TestBuffers : IDisposable where TSource : struct where TDest : struct { - public PinnedBuffer SourceBuffer { get; } - public PinnedBuffer ActualDestBuffer { get; } - public PinnedBuffer ExpectedDestBuffer { get; } + public Buffer SourceBuffer { get; } + public Buffer ActualDestBuffer { get; } + public Buffer ExpectedDestBuffer { get; } public BufferSpan Source => this.SourceBuffer; public BufferSpan ActualDest => this.ActualDestBuffer; - + public TestBuffers(TSource[] source, TDest[] expectedDest) { - this.SourceBuffer = new PinnedBuffer(source); - this.ExpectedDestBuffer = new PinnedBuffer(expectedDest); - this.ActualDestBuffer = new PinnedBuffer(expectedDest.Length); + this.SourceBuffer = new Buffer(source); + this.ExpectedDestBuffer = new Buffer(expectedDest); + this.ActualDestBuffer = new Buffer(expectedDest.Length); } public void Dispose() diff --git a/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs b/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs index 83c02635a..d2c5cf845 100644 --- a/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs @@ -30,7 +30,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector4), vector4Components }; + yield return new object[] { new Argb32(vector4), vector4Components }; yield return new object[] { new Bgra4444(vector4), vector4Components }; yield return new object[] { new Bgra5551(vector4), vector4Components }; yield return new object[] { new Byte4(vector4), vector4Components }; @@ -63,7 +63,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector3), vector4Components }; + yield return new object[] { new Argb32(vector3), vector4Components }; yield return new object[] { new Bgr565(vector3), vector4Components }; } } @@ -88,7 +88,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; + yield return new object[] { new Argb32(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; yield return new object[] { new Bgra4444(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; yield return new object[] { new Bgra5551(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; yield return new object[] { new Byte4(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; @@ -121,7 +121,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector3.X, vector3.Y, vector3.Z), vector4Components }; + yield return new object[] { new Argb32(vector3.X, vector3.Y, vector3.Z), vector4Components }; yield return new object[] { new Bgr565(vector3.X, vector3.Y, vector3.Z), vector4Components }; } } diff --git a/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs b/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs index 8bda0bc8c..e07d4e6f2 100644 --- a/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Tests.Colors new TheoryData() { { new Alpha8(.5F), new Alpha8(.5F), typeof(Alpha8) }, - { new Argb(Vector4.One), new Argb(Vector4.One), typeof(Argb) }, + { new Argb32(Vector4.One), new Argb32(Vector4.One), typeof(Argb32) }, { new Bgr565(Vector3.One), new Bgr565(Vector3.One), typeof(Bgr565) }, { new Bgra4444(Vector4.One), new Bgra4444(Vector4.One), typeof(Bgra4444) }, { new Bgra5551(Vector4.One), new Bgra5551(Vector4.One), typeof(Bgra5551) }, @@ -32,6 +32,7 @@ namespace ImageSharp.Tests.Colors { new NormalizedShort4(Vector4.One), new NormalizedShort4(Vector4.One), typeof(NormalizedShort4) }, { new Rg32(Vector2.One), new Rg32(Vector2.One), typeof(Rg32) }, { new Rgba1010102(Vector4.One), new Rgba1010102(Vector4.One), typeof(Rgba1010102) }, + { new Color(Vector4.One), new Color(Vector4.One), typeof(Color) }, { new Rgba64(Vector4.One), new Rgba64(Vector4.One), typeof(Rgba64) }, { new Short2(Vector2.One * 0x7FFF), new Short2(Vector2.One * 0x7FFF), typeof(Short2) }, { new Short4(Vector4.One * 0x7FFF), new Short4(Vector4.One * 0x7FFF), typeof(Short4) }, @@ -42,7 +43,7 @@ namespace ImageSharp.Tests.Colors { // Valid object against null { new Alpha8(.5F), null, typeof(Alpha8) }, - { new Argb(Vector4.One), null, typeof(Argb) }, + { new Argb32(Vector4.One), null, typeof(Argb32) }, { new Bgr565(Vector3.One), null, typeof(Bgr565) }, { new Bgra4444(Vector4.One), null, typeof(Bgra4444) }, { new Bgra5551(Vector4.One), null, typeof(Bgra5551) }, @@ -65,7 +66,7 @@ namespace ImageSharp.Tests.Colors new TheoryData() { // Valid objects of different types but not equal - { new Alpha8(.5F), new Argb(Vector4.Zero), null }, + { new Alpha8(.5F), new Argb32(Vector4.Zero), null }, { new HalfSingle(-1F), new NormalizedShort2(Vector2.Zero), null }, { new Rgba1010102(Vector4.One), new Bgra5551(Vector4.Zero), null }, }; @@ -75,7 +76,7 @@ namespace ImageSharp.Tests.Colors { // Valid objects of the same type but not equal { new Alpha8(.5F), new Alpha8(.8F), typeof(Alpha8) }, - { new Argb(Vector4.One), new Argb(Vector4.Zero), typeof(Argb) }, + { new Argb32(Vector4.One), new Argb32(Vector4.Zero), typeof(Argb32) }, { new Bgr565(Vector3.One), new Bgr565(Vector3.Zero), typeof(Bgr565) }, { new Bgra4444(Vector4.One), new Bgra4444(Vector4.Zero), typeof(Bgra4444) }, { new Bgra5551(Vector4.One), new Bgra5551(Vector4.Zero), typeof(Bgra5551) }, @@ -89,6 +90,7 @@ namespace ImageSharp.Tests.Colors { new NormalizedShort4(Vector4.One), new NormalizedShort4(Vector4.Zero), typeof(NormalizedShort4) }, { new Rg32(Vector2.One), new Rg32(Vector2.Zero), typeof(Rg32) }, { new Rgba1010102(Vector4.One), new Rgba1010102(Vector4.Zero), typeof(Rgba1010102) }, + { new Color(Vector4.One), new Color(Vector4.Zero), typeof(Color) }, { new Rgba64(Vector4.One), new Rgba64(Vector4.Zero), typeof(Rgba64) }, { new Short2(Vector2.One * 0x7FFF), new Short2(Vector2.Zero), typeof(Short2) }, { new Short4(Vector4.One * 0x7FFF), new Short4(Vector4.Zero), typeof(Short4) }, @@ -211,5 +213,41 @@ namespace ImageSharp.Tests.Colors // Assert Assert.True(notEqual); } + + [Theory] + [MemberData(nameof(AlmostEqualsData))] + public void AlmostEquals(object first, object second, Type type, float precision) + { + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) + dynamic firstObject = Convert.ChangeType(first, type); + dynamic secondObject = Convert.ChangeType(second, type); + + // Act + dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision); + + // Assert + Assert.True(almostEqual); + } + + [Theory] + [MemberData(nameof(AlmostNotEqualsData))] + public void AlmostNotEquals(object first, object second, Type type, float precision) + { + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) + dynamic firstObject = Convert.ChangeType(first, type); + dynamic secondObject = Convert.ChangeType(second, type); + + // Act + dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision); + + // Assert + Assert.False(almostEqual); + } } } diff --git a/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs b/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs index 7b743e53a..5fbb42b2a 100644 --- a/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs @@ -29,7 +29,7 @@ namespace ImageSharp.Tests.Colors { float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(), vector4Components }; + yield return new object[] { new Argb32(), vector4Components }; yield return new object[] { new Bgra4444(), vector4Components }; yield return new object[] { new Bgra5551(), vector4Components }; yield return new object[] { new Byte4(), vector4Components }; @@ -60,7 +60,7 @@ namespace ImageSharp.Tests.Colors { float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(), vector4Components }; + yield return new object[] { new Argb32(), vector4Components }; yield return new object[] { new Bgr565(), vector4Components }; } } diff --git a/tests/ImageSharp.Tests/Colors/ColorTests.cs b/tests/ImageSharp.Tests/Colors/ColorTests.cs index 312e66466..e2c62b507 100644 --- a/tests/ImageSharp.Tests/Colors/ColorTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorTests.cs @@ -124,6 +124,8 @@ namespace ImageSharp.Tests Assert.Equal(2, colorBase[1]); Assert.Equal(3, colorBase[2]); Assert.Equal(4, colorBase[3]); + + Assert.Equal(4, sizeof(Color)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/ColorVectorTests.cs b/tests/ImageSharp.Tests/Colors/ColorVectorTests.cs new file mode 100644 index 000000000..4300b1b38 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/ColorVectorTests.cs @@ -0,0 +1,132 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + using Xunit; + + /// + /// Tests the struct. + /// + public class ColorVectorTests + { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + ColorVector color1 = new ColorVector(0, 0, 0F); + ColorVector color2 = new ColorVector(0, 0, 0, 1F); + ColorVector color3 = ColorVector.FromHex("#000"); + ColorVector color4 = ColorVector.FromHex("#000F"); + ColorVector color5 = ColorVector.FromHex("#000000"); + ColorVector color6 = ColorVector.FromHex("#000000FF"); + + Assert.Equal(color1, color2); + Assert.Equal(color1, color3); + Assert.Equal(color1, color4); + Assert.Equal(color1, color5); + Assert.Equal(color1, color6); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + ColorVector color1 = new ColorVector(1, 0, 0, 1); + ColorVector color2 = new ColorVector(0, 0, 0, 1); + ColorVector color3 = ColorVector.FromHex("#000"); + ColorVector color4 = ColorVector.FromHex("#000000"); + ColorVector color5 = ColorVector.FromHex("#FF000000"); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color1, color3); + Assert.NotEqual(color1, color4); + Assert.NotEqual(color1, color5); + } + + /// + /// Tests whether the color constructor correctly assign properties. + /// + [Fact] + public void ConstructorAssignsProperties() + { + ColorVector color1 = new ColorVector(1, .1F, .133F, .864F); + Assert.Equal(1F, color1.R); + Assert.Equal(.1F, color1.G); + Assert.Equal(.133F, color1.B); + Assert.Equal(.864F, color1.A); + + ColorVector color2 = new ColorVector(1, .1f, .133f); + Assert.Equal(1F, color2.R); + Assert.Equal(.1F, color2.G); + Assert.Equal(.133F, color2.B); + Assert.Equal(1F, color2.A); + + ColorVector color4 = new ColorVector(new Vector3(1, .1f, .133f)); + Assert.Equal(1F, color4.R); + Assert.Equal(.1F, color4.G); + Assert.Equal(.133F, color4.B); + Assert.Equal(1F, color4.A); + + ColorVector color5 = new ColorVector(new Vector4(1, .1f, .133f, .5f)); + Assert.Equal(1F, color5.R); + Assert.Equal(.1F, color5.G); + Assert.Equal(.133F, color5.B); + Assert.Equal(.5F, color5.A); + } + + /// + /// Tests whether FromHex and ToHex work correctly. + /// + [Fact] + public void FromAndToHex() + { + ColorVector color = ColorVector.FromHex("#AABBCCDD"); + Assert.Equal(170 / 255F, color.R); + Assert.Equal(187 / 255F, color.G); + Assert.Equal(204 / 255F, color.B); + Assert.Equal(221 / 255F, color.A); + + color.A = 170 / 255F; + color.B = 187 / 255F; + color.G = 204 / 255F; + color.R = 221 / 255F; + + Assert.Equal("DDCCBBAA", color.ToHex()); + + color.R = 0; + + Assert.Equal("00CCBBAA", color.ToHex()); + + color.A = 255 / 255F; + + Assert.Equal("00CCBBFF", color.ToHex()); + } + + /// + /// Tests that the individual float elements are layed out in RGBA order. + /// + [Fact] + public void FloatLayout() + { + ColorVector color = new ColorVector(1F, 2, 3, 4); + Vector4 colorBase = Unsafe.As(ref Unsafe.Add(ref color, 0)); + float[] ordered = new float[4]; + colorBase.CopyTo(ordered); + + Assert.Equal(1, ordered[0]); + Assert.Equal(2, ordered[1]); + Assert.Equal(3, ordered[2]); + Assert.Equal(4, ordered[3]); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/ColorVectorTransformTests.cs b/tests/ImageSharp.Tests/Colors/ColorVectorTransformTests.cs new file mode 100644 index 000000000..c2e27d231 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/ColorVectorTransformTests.cs @@ -0,0 +1,118 @@ +// +// 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. Test results match the output of CSS equivalents. + /// + /// + public class ColorVectorTransformTests + { + private static readonly ApproximateFloatComparer FloatComparer = new ApproximateFloatComparer(0.01F); + + /// + /// Orange backdrop + /// + private static readonly ColorVector Backdrop = new ColorVector(204, 102, 0); + + /// + /// Blue source + /// + private static readonly ColorVector Source = new ColorVector(0, 102, 153); + + [Fact] + public void Normal() + { + ColorVector normal = ColorVector.Normal(Backdrop, Source); + Assert.True(normal == Source); + } + + [Fact] + public void Multiply() + { + Assert.Equal(ColorVector.Multiply(Backdrop, ColorVector.Black).ToVector4(), Color.Black.ToVector4(), FloatComparer); + Assert.Equal(ColorVector.Multiply(Backdrop, ColorVector.White).ToVector4(), Backdrop.ToVector4(), FloatComparer); + + ColorVector multiply = ColorVector.Multiply(Backdrop, Source); + Assert.Equal(multiply.ToVector4(), new ColorVector(0, 41, 0).ToVector4(), FloatComparer); + } + + [Fact] + public void Screen() + { + Assert.Equal(ColorVector.Screen(Backdrop, ColorVector.Black).ToVector4(), Backdrop.ToVector4(), FloatComparer); + Assert.Equal(ColorVector.Screen(Backdrop, ColorVector.White).ToVector4(), ColorVector.White.ToVector4(), FloatComparer); + + ColorVector screen = ColorVector.Screen(Backdrop, Source); + Assert.Equal(screen.ToVector4(), new ColorVector(204, 163, 153).ToVector4(), FloatComparer); + } + + [Fact] + public void HardLight() + { + ColorVector hardLight = ColorVector.HardLight(Backdrop, Source); + Assert.Equal(hardLight.ToVector4(), new ColorVector(0, 82, 51).ToVector4(), FloatComparer); + } + + [Fact] + public void Overlay() + { + ColorVector overlay = ColorVector.Overlay(Backdrop, Source); + Assert.Equal(overlay.ToVector4(), new ColorVector(153, 82, 0).ToVector4(), FloatComparer); + } + + [Fact] + public void Darken() + { + ColorVector darken = ColorVector.Darken(Backdrop, Source); + Assert.Equal(darken.ToVector4(), new ColorVector(0, 102, 0).ToVector4(), FloatComparer); + } + + [Fact] + public void Lighten() + { + ColorVector lighten = ColorVector.Lighten(Backdrop, Source); + Assert.Equal(lighten.ToVector4(), new ColorVector(204, 102, 153).ToVector4(), FloatComparer); + } + + [Fact] + public void SoftLight() + { + ColorVector softLight = ColorVector.SoftLight(Backdrop, Source); + Assert.Equal(softLight.ToVector4(), new ColorVector(163, 90, 0).ToVector4(), FloatComparer); + } + + [Fact] + public void ColorDodge() + { + ColorVector colorDodge = ColorVector.ColorDodge(Backdrop, Source); + Assert.Equal(colorDodge.ToVector4(), new ColorVector(204, 170, 0).ToVector4(), FloatComparer); + } + + [Fact] + public void ColorBurn() + { + ColorVector colorBurn = ColorVector.ColorBurn(Backdrop, Source); + Assert.Equal(colorBurn.ToVector4(), new ColorVector(0, 0, 0).ToVector4(), FloatComparer); + } + + [Fact] + public void Difference() + { + ColorVector difference = ColorVector.Difference(Backdrop, Source); + Assert.Equal(difference.ToVector4(), new ColorVector(204, 0, 153).ToVector4(), FloatComparer); + } + + [Fact] + public void Exclusion() + { + ColorVector exclusion = ColorVector.Exclusion(Backdrop, Source); + Assert.Equal(exclusion.ToVector4(), new ColorVector(204, 122, 153).ToVector4(), FloatComparer); + } + } +} diff --git a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs index 3e2b6fcd5..52ca86cae 100644 --- a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs +++ b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs @@ -62,29 +62,29 @@ namespace ImageSharp.Tests.Colors } [Fact] - public void Argb() + public void Argb32() { // Test the limits. - Assert.Equal((uint)0x0, new Argb(Vector4.Zero).PackedValue); - Assert.Equal(0xFFFFFFFF, new Argb(Vector4.One).PackedValue); + Assert.Equal((uint)0x0, new Argb32(Vector4.Zero).PackedValue); + Assert.Equal(0xFFFFFFFF, new Argb32(Vector4.One).PackedValue); // Test ToVector4. - Assert.True(Equal(Vector4.One, new Argb(Vector4.One).ToVector4())); - Assert.True(Equal(Vector4.Zero, new Argb(Vector4.Zero).ToVector4())); - Assert.True(Equal(Vector4.UnitX, new Argb(Vector4.UnitX).ToVector4())); - Assert.True(Equal(Vector4.UnitY, new Argb(Vector4.UnitY).ToVector4())); - Assert.True(Equal(Vector4.UnitZ, new Argb(Vector4.UnitZ).ToVector4())); - Assert.True(Equal(Vector4.UnitW, new Argb(Vector4.UnitW).ToVector4())); + Assert.True(Equal(Vector4.One, new Argb32(Vector4.One).ToVector4())); + Assert.True(Equal(Vector4.Zero, new Argb32(Vector4.Zero).ToVector4())); + Assert.True(Equal(Vector4.UnitX, new Argb32(Vector4.UnitX).ToVector4())); + Assert.True(Equal(Vector4.UnitY, new Argb32(Vector4.UnitY).ToVector4())); + Assert.True(Equal(Vector4.UnitZ, new Argb32(Vector4.UnitZ).ToVector4())); + Assert.True(Equal(Vector4.UnitW, new Argb32(Vector4.UnitW).ToVector4())); // Test clamping. - Assert.True(Equal(Vector4.Zero, new Argb(Vector4.One * -1234.0f).ToVector4())); - Assert.True(Equal(Vector4.One, new Argb(Vector4.One * +1234.0f).ToVector4())); + Assert.True(Equal(Vector4.Zero, new Argb32(Vector4.One * -1234.0f).ToVector4())); + Assert.True(Equal(Vector4.One, new Argb32(Vector4.One * +1234.0f).ToVector4())); float x = +0.1f; float y = -0.3f; float z = +0.5f; float w = -0.7f; - Argb argb = new Argb(x, y, z, w); + Argb32 argb = new Argb32(x, y, z, w); Assert.Equal(0x001a0080u, argb.PackedValue); // Test ordering @@ -711,6 +711,51 @@ namespace ImageSharp.Tests.Colors Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 }); } + [Fact] + public void Color() + { + // Test the limits. + Assert.Equal((uint)0x0, new Color(Vector4.Zero).PackedValue); + Assert.Equal(0xFFFFFFFF, new Color(Vector4.One).PackedValue); + + // Test ToVector4. + Assert.True(Equal(Vector4.One, new Color(Vector4.One).ToVector4())); + Assert.True(Equal(Vector4.Zero, new Color(Vector4.Zero).ToVector4())); + Assert.True(Equal(Vector4.UnitX, new Color(Vector4.UnitX).ToVector4())); + Assert.True(Equal(Vector4.UnitY, new Color(Vector4.UnitY).ToVector4())); + Assert.True(Equal(Vector4.UnitZ, new Color(Vector4.UnitZ).ToVector4())); + Assert.True(Equal(Vector4.UnitW, new Color(Vector4.UnitW).ToVector4())); + + // Test clamping. + Assert.True(Equal(Vector4.Zero, new Color(Vector4.One * -1234.0f).ToVector4())); + Assert.True(Equal(Vector4.One, new Color(Vector4.One * +1234.0f).ToVector4())); + + float x = +0.1f; + float y = -0.3f; + float z = +0.5f; + float w = -0.7f; + Color rgba32 = new Color(x, y, z, w); + Assert.Equal(0x80001Au, rgba32.PackedValue); + + // Test ordering + byte[] rgb = new byte[3]; + byte[] rgba = new byte[4]; + byte[] bgr = new byte[3]; + byte[] bgra = new byte[4]; + + rgba32.ToXyzBytes(rgb, 0); + Assert.Equal(rgb, new byte[] { 0x1a, 0, 0x80 }); + + rgba32.ToXyzwBytes(rgba, 0); + Assert.Equal(rgba, new byte[] { 0x1a, 0, 0x80, 0 }); + + rgba32.ToZyxBytes(bgr, 0); + Assert.Equal(bgr, new byte[] { 0x80, 0, 0x1a }); + + rgba32.ToZyxwBytes(bgra, 0); + Assert.Equal(bgra, new byte[] { 0x80, 0, 0x1a, 0 }); + } + [Fact] public void Rgba64() { diff --git a/tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs new file mode 100644 index 000000000..4fb189ca8 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs @@ -0,0 +1,148 @@ +namespace ImageSharp.Tests.Colors +{ + using System.Numerics; + + using Xunit; + + public class UnPackedPixelTests + { + [Fact] + public void Color_Types_From_Bytes_Produce_Equal_Scaled_Component_OutPut() + { + Color color = new Color(24, 48, 96, 192); + ColorVector colorVector = new ColorVector(24, 48, 96, 192); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Floats_Produce_Equal_Scaled_Component_OutPut() + { + Color color = new Color(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + ColorVector colorVector = new ColorVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Vector4_Produce_Equal_Scaled_Component_OutPut() + { + Color color = new Color(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + ColorVector colorVector = new ColorVector(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Vector3_Produce_Equal_Scaled_Component_OutPut() + { + Color color = new Color(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + ColorVector colorVector = new ColorVector(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Hex_Produce_Equal_Scaled_Component_OutPut() + { + Color color = Color.FromHex("183060C0"); + ColorVector colorVector = ColorVector.FromHex("183060C0"); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_To_Vector4_Produce_Equal_OutPut() + { + Color color = new Color(24, 48, 96, 192); + ColorVector colorVector = new ColorVector(24, 48, 96, 192); + + Assert.Equal(color.ToVector4(), colorVector.ToVector4()); + } + + [Fact] + public void Color_Types_To_RgbBytes_Produce_Equal_OutPut() + { + Color color = new Color(24, 48, 96, 192); + ColorVector colorVector = new ColorVector(24, 48, 96, 192); + + byte[] rgb = new byte[3]; + byte[] rgbVector = new byte[3]; + + color.ToXyzBytes(rgb, 0); + colorVector.ToXyzBytes(rgbVector, 0); + + Assert.Equal(rgb, rgbVector); + } + + [Fact] + public void Color_Types_To_RgbaBytes_Produce_Equal_OutPut() + { + Color color = new Color(24, 48, 96, 192); + ColorVector colorVector = new ColorVector(24, 48, 96, 192); + + byte[] rgba = new byte[4]; + byte[] rgbaVector = new byte[4]; + + color.ToXyzwBytes(rgba, 0); + colorVector.ToXyzwBytes(rgbaVector, 0); + + Assert.Equal(rgba, rgbaVector); + } + + [Fact] + public void Color_Types_To_BgrBytes_Produce_Equal_OutPut() + { + Color color = new Color(24, 48, 96, 192); + ColorVector colorVector = new ColorVector(24, 48, 96, 192); + + byte[] bgr = new byte[3]; + byte[] bgrVector = new byte[3]; + + color.ToZyxBytes(bgr, 0); + colorVector.ToZyxBytes(bgrVector, 0); + + Assert.Equal(bgr, bgrVector); + } + + [Fact] + public void Color_Types_To_BgraBytes_Produce_Equal_OutPut() + { + Color color = new Color(24, 48, 96, 192); + ColorVector colorVector = new ColorVector(24, 48, 96, 192); + + byte[] bgra = new byte[4]; + byte[] bgraVector = new byte[4]; + + color.ToZyxwBytes(bgra, 0); + colorVector.ToZyxwBytes(bgraVector, 0); + + Assert.Equal(bgra, bgraVector); + } + + [Fact] + public void Color_Types_To_Hex_Produce_Equal_OutPut() + { + Color color = new Color(24, 48, 96, 192); + ColorVector colorVector = new ColorVector(24, 48, 96, 192); + + // 183060C0 + Assert.Equal(color.ToHex(), colorVector.ToHex()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs b/tests/ImageSharp.Tests/Common/Buffer2DTests.cs similarity index 68% rename from tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs rename to tests/ImageSharp.Tests/Common/Buffer2DTests.cs index a23f93a70..ac92ab87b 100644 --- a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/Buffer2DTests.cs @@ -1,20 +1,34 @@ // ReSharper disable InconsistentNaming namespace ImageSharp.Tests.Common { + using System; using System.Runtime.CompilerServices; using Xunit; using static TestStructs; - public unsafe class PinnedImageBufferTests + public unsafe class Buffer2DTests { + // ReSharper disable once ClassNeverInstantiated.Local + private class Assert : Xunit.Assert + { + public static void SpanPointsTo(BufferSpan span, Buffer buffer, int bufferOffset = 0) + where T : struct + { + ref T actual = ref span.DangerousGetPinnableReference(); + ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); + + Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); + } + } + [Theory] [InlineData(7, 42)] [InlineData(1025, 17)] public void Construct(int width, int height) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -28,7 +42,7 @@ namespace ImageSharp.Tests.Common public void Construct_FromExternalArray(int width, int height) { Foo[] array = new Foo[width * height + 10]; - using (PinnedImageBuffer buffer = new PinnedImageBuffer(array, width, height)) + using (Buffer2D buffer = new Buffer2D(array, width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -42,7 +56,7 @@ namespace ImageSharp.Tests.Common { for (int i = 0; i < 100; i++) { - using (PinnedImageBuffer buffer = PinnedImageBuffer.CreateClean(42, 42)) + using (Buffer2D buffer = Buffer2D.CreateClean(42, 42)) { for (int j = 0; j < buffer.Length; j++) { @@ -59,13 +73,13 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { BufferSpan span = buffer.GetRowSpan(y); Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); - Assert.Equal(buffer.Pointer + sizeof(Foo) * width * y, span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, width * y); } } @@ -75,13 +89,13 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { BufferSpan span = buffer.GetRowSpan(x, y); Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); - Assert.Equal(buffer.Pointer + sizeof(Foo) * (width * y + x), span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, width * y + x); } } @@ -91,7 +105,7 @@ namespace ImageSharp.Tests.Common [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Foo[] array = buffer.Array; diff --git a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs index aee032acc..ebf3a866a 100644 --- a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs @@ -1,5 +1,6 @@ // ReSharper disable ObjectCreationAsStatement // ReSharper disable InconsistentNaming + namespace ImageSharp.Tests.Common { using System; @@ -11,18 +12,30 @@ namespace ImageSharp.Tests.Common public unsafe class BufferSpanTests { + // ReSharper disable once ClassNeverInstantiated.Local + private class Assert : Xunit.Assert + { + public static void SameRefs(ref T1 a, ref T2 b) + { + ref T1 bb = ref Unsafe.As(ref b); + + True(Unsafe.AreSame(ref a, ref bb), "References are not same!"); + } + } + [Fact] public void AsBytes() { Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) }; - using (PinnedBuffer colorBuf = new PinnedBuffer(fooz)) + using (Buffer colorBuf = new Buffer(fooz)) { BufferSpan orig = colorBuf.Slice(1); - BufferSpan asBytes = (BufferSpan < byte > )orig; + BufferSpan asBytes = orig.AsBytes(); Assert.Equal(asBytes.Start, sizeof(Foo)); - Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset); + Assert.Equal(orig.Length * Unsafe.SizeOf(), asBytes.Length); + Assert.SameRefs(ref orig.DangerousGetPinnableReference(), ref asBytes.DangerousGetPinnableReference()); } } @@ -32,16 +45,14 @@ namespace ImageSharp.Tests.Common public void Basic() { Foo[] array = Foo.CreateArray(3); - fixed (Foo* p = array) - { - // Act: - BufferSpan span = new BufferSpan(array, p); - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal((IntPtr)p, span.PointerAtOffset); - Assert.Equal(3, span.Length); - } + // Act: + BufferSpan span = new BufferSpan(array); + + // Assert: + Assert.Equal(array, span.Array); + Assert.Equal(3, span.Length); + Assert.SameRefs(ref array[0], ref span.DangerousGetPinnableReference()); } [Fact] @@ -49,17 +60,15 @@ namespace ImageSharp.Tests.Common { Foo[] array = Foo.CreateArray(4); int start = 2; - fixed (Foo* p = array) - { - // Act: - BufferSpan span = new BufferSpan(array, p, start); - - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(start, span.Start); - Assert.Equal((IntPtr)(p + start), span.PointerAtOffset); - Assert.Equal(array.Length - start, span.Length); - } + + // Act: + BufferSpan span = new BufferSpan(array, start); + + // Assert: + Assert.Equal(array, span.Array); + Assert.Equal(start, span.Start); + Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); + Assert.Equal(array.Length - start, span.Length); } [Fact] @@ -68,17 +77,14 @@ namespace ImageSharp.Tests.Common Foo[] array = Foo.CreateArray(10); int start = 2; int length = 3; - fixed (Foo* p = array) - { - // Act: - BufferSpan span = new BufferSpan(array, p, start, length); - - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(start, span.Start); - Assert.Equal((IntPtr)(p + start), span.PointerAtOffset); - Assert.Equal(length, span.Length); - } + // Act: + BufferSpan span = new BufferSpan(array, start, length); + + // Assert: + Assert.Equal(array, span.Array); + Assert.Equal(start, span.Start); + Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); + Assert.Equal(length, span.Length); } } @@ -92,19 +98,16 @@ namespace ImageSharp.Tests.Common int start1 = 2; int totalOffset = start0 + start1; - fixed (Foo* p = array) - { - BufferSpan span = new BufferSpan(array, p, start0); + BufferSpan span = new BufferSpan(array, start0); - // Act: - span = span.Slice(start1); + // Act: + span = span.Slice(start1); - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(totalOffset, span.Start); - Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset); - Assert.Equal(array.Length - totalOffset, span.Length); - } + // Assert: + Assert.Equal(array, span.Array); + Assert.Equal(totalOffset, span.Start); + Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference()); + Assert.Equal(array.Length - totalOffset, span.Length); } [Fact] @@ -116,24 +119,19 @@ namespace ImageSharp.Tests.Common int totalOffset = start0 + start1; int sliceLength = 3; - fixed (Foo* p = array) - { - BufferSpan span = new BufferSpan(array, p, start0); + BufferSpan span = new BufferSpan(array, start0); - // Act: - span = span.Slice(start1, sliceLength); + // Act: + span = span.Slice(start1, sliceLength); - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(totalOffset, span.Start); - Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset); - Assert.Equal(sliceLength, span.Length); - } + // Assert: + Assert.Equal(array, span.Array); + Assert.Equal(totalOffset, span.Start); + Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference()); + Assert.Equal(sliceLength, span.Length); } } - - [Theory] [InlineData(4)] [InlineData(1500)] @@ -142,21 +140,17 @@ namespace ImageSharp.Tests.Common Foo[] array = Foo.CreateArray(count + 42); int offset = 2; - fixed (Foo* p = array) - { - BufferSpan ap = new BufferSpan(array, p, offset); + BufferSpan ap = new BufferSpan(array, offset); - // Act: - ap.Clear(count); + // Act: + ap.Clear(count); - Assert.NotEqual(default(Foo), array[offset-1]); - Assert.Equal(default(Foo), array[offset]); - Assert.Equal(default(Foo), array[offset + count-1]); - Assert.NotEqual(default(Foo), array[offset + count]); - } + Assert.NotEqual(default(Foo), array[offset - 1]); + Assert.Equal(default(Foo), array[offset]); + Assert.Equal(default(Foo), array[offset + count - 1]); + Assert.NotEqual(default(Foo), array[offset + count]); } - public class Indexer { public static readonly TheoryData IndexerData = @@ -175,14 +169,11 @@ namespace ImageSharp.Tests.Common public void Read(int length, int start, int index) { Foo[] a = Foo.CreateArray(length); - fixed (Foo* p = a) - { - BufferSpan span = new BufferSpan(a, p, start); + BufferSpan span = new BufferSpan(a, start); - Foo element = span[index]; + Foo element = span[index]; - Assert.Equal(a[start + index], element); - } + Assert.Equal(a[start + index], element); } [Theory] @@ -190,17 +181,46 @@ namespace ImageSharp.Tests.Common public void Write(int length, int start, int index) { Foo[] a = Foo.CreateArray(length); - fixed (Foo* p = a) - { - BufferSpan span = new BufferSpan(a, p, start); + BufferSpan span = new BufferSpan(a, start); - span[index] = new Foo(666, 666); + span[index] = new Foo(666, 666); - Assert.Equal(new Foo(666, 666), a[start + index]); - } + Assert.Equal(new Foo(666, 666), a[start + index]); + } + + [Theory] + [InlineData(10, 0, 0, 5)] + [InlineData(10, 1, 1, 5)] + [InlineData(10, 1, 1, 6)] + [InlineData(10, 1, 1, 7)] + public void AsBytes_Read(int length, int start, int index, int byteOffset) + { + Foo[] a = Foo.CreateArray(length); + BufferSpan span = new BufferSpan(a, start); + + BufferSpan bytes = span.AsBytes(); + + byte actual = bytes[index * Unsafe.SizeOf() + byteOffset]; + + ref byte baseRef = ref Unsafe.As(ref a[0]); + byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf() + byteOffset); + + Assert.Equal(expected, actual); } } + [Theory] + [InlineData(0, 4)] + [InlineData(2, 4)] + [InlineData(3, 4)] + public void DangerousGetPinnableReference(int start, int length) + { + Foo[] a = Foo.CreateArray(length); + BufferSpan span = new BufferSpan(a, start); + ref Foo r = ref span.DangerousGetPinnableReference(); + + Assert.True(Unsafe.AreSame(ref a[start], ref r)); + } public class Copy { @@ -238,14 +258,10 @@ namespace ImageSharp.Tests.Common Foo[] source = Foo.CreateArray(count + 2); Foo[] dest = new Foo[count + 5]; - fixed (Foo* pSource = source) - fixed (Foo* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(dest, 1); - BufferSpan.Copy(apSource, apDest, count-1); - } + BufferSpan.Copy(apSource, apDest, count - 1); AssertNotDefault(source, 1); AssertNotDefault(dest, 1); @@ -253,7 +269,7 @@ namespace ImageSharp.Tests.Common Assert.NotEqual(source[0], dest[0]); Assert.Equal(source[1], dest[1]); Assert.Equal(source[2], dest[2]); - Assert.Equal(source[count-1], dest[count-1]); + Assert.Equal(source[count - 1], dest[count - 1]); Assert.NotEqual(source[count], dest[count]); } @@ -265,14 +281,10 @@ namespace ImageSharp.Tests.Common AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); AlignedFoo[] dest = new AlignedFoo[count + 5]; - fixed (AlignedFoo* pSource = source) - fixed (AlignedFoo* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(dest, 1); - BufferSpan.Copy(apSource, apDest, count - 1); - } + BufferSpan.Copy(apSource, apDest, count - 1); AssertNotDefault(source, 1); AssertNotDefault(dest, 1); @@ -289,17 +301,13 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void IntToInt(int count) { - int[] source = CreateTestInts(count+2); + int[] source = CreateTestInts(count + 2); int[] dest = new int[count + 5]; - fixed (int* pSource = source) - fixed (int* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(dest, 1); - BufferSpan.Copy(apSource, apDest, count -1); - } + BufferSpan.Copy(apSource, apDest, count - 1); AssertNotDefault(source, 1); AssertNotDefault(dest, 1); @@ -317,17 +325,13 @@ namespace ImageSharp.Tests.Common public void GenericToBytes(int count) { int destCount = count * sizeof(Foo); - Foo[] source = Foo.CreateArray(count+2); - byte[] dest = new byte[destCount + sizeof(Foo)*2]; + Foo[] source = Foo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(Foo) * 2]; - fixed (Foo* pSource = source) - fixed (byte* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(Foo)); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(dest, sizeof(Foo)); - BufferSpan.Copy(apSource, apDest, count - 1); - } + BufferSpan.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(Foo)); AssertNotDefault(source, 1); @@ -347,14 +351,10 @@ namespace ImageSharp.Tests.Common AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(AlignedFoo) * 2]; - fixed (AlignedFoo* pSource = source) - fixed (byte* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(AlignedFoo)); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(dest, sizeof(AlignedFoo)); - BufferSpan.Copy(apSource, apDest, count - 1); - } + BufferSpan.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(AlignedFoo)); AssertNotDefault(source, 1); @@ -371,17 +371,13 @@ namespace ImageSharp.Tests.Common public void IntToBytes(int count) { int destCount = count * sizeof(int); - int[] source = CreateTestInts(count+2); + int[] source = CreateTestInts(count + 2); byte[] dest = new byte[destCount + sizeof(int) + 1]; - fixed (int* pSource = source) - fixed (byte* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource); - BufferSpan apDest = new BufferSpan(dest, pDest); + BufferSpan apSource = new BufferSpan(source); + BufferSpan apDest = new BufferSpan(dest); - BufferSpan.Copy(apSource, apDest, count); - } + BufferSpan.Copy(apSource.AsBytes(), apDest, count * sizeof(int)); AssertNotDefault(source, 1); @@ -398,15 +394,11 @@ namespace ImageSharp.Tests.Common int srcCount = count * sizeof(Foo); byte[] source = CreateTestBytes(srcCount); Foo[] dest = new Foo[count + 2]; - - fixed(byte* pSource = source) - fixed (Foo* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource); - BufferSpan apDest = new BufferSpan(dest, pDest); - BufferSpan.Copy(apSource, apDest, count); - } + BufferSpan apSource = new BufferSpan(source); + BufferSpan apDest = new BufferSpan(dest); + + BufferSpan.Copy(apSource, apDest.AsBytes(), count * sizeof(Foo)); AssertNotDefault(source, sizeof(Foo) + 1); AssertNotDefault(dest, 1); @@ -418,14 +410,14 @@ namespace ImageSharp.Tests.Common } [Fact] - public void ColorToBytes() + public void Color32ToBytes() { Color[] colors = { new Color(0, 1, 2, 3), new Color(4, 5, 6, 7), new Color(8, 9, 10, 11), }; - using (PinnedBuffer colorBuf = new PinnedBuffer(colors)) - using (PinnedBuffer byteBuf = new PinnedBuffer(colors.Length*4)) + using (Buffer colorBuf = new Buffer(colors)) + using (Buffer byteBuf = new Buffer(colors.Length * 4)) { - BufferSpan.Copy(colorBuf, byteBuf, colorBuf.Length); + BufferSpan.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length * sizeof(Color)); byte[] a = byteBuf.Array; @@ -436,7 +428,6 @@ namespace ImageSharp.Tests.Common } } - internal static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) { fixed (Foo* pArray = array) diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/BufferTests.cs similarity index 62% rename from tests/ImageSharp.Tests/Common/PinnedBufferTests.cs rename to tests/ImageSharp.Tests/Common/BufferTests.cs index 5e812d5a0..23205d012 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferTests.cs @@ -1,4 +1,5 @@ -namespace ImageSharp.Tests.Common +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests.Common { using System; using System.Runtime.CompilerServices; @@ -9,21 +10,37 @@ using static TestStructs; - public unsafe class PinnedBufferTests + public unsafe class BufferTests { + // ReSharper disable once ClassNeverInstantiated.Local + private class Assert : Xunit.Assert + { + public static void SpanPointsTo(BufferSpan span, Buffer buffer, int bufferOffset = 0) + where T : struct + { + ref T actual = ref span.DangerousGetPinnableReference(); + ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); + + Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); + } + + public static void Equal(void* expected, void* actual) + { + Assert.Equal((IntPtr)expected, (IntPtr)actual); + } + } + [Theory] [InlineData(42)] [InlineData(1111)] public void ConstructWithOwnArray(int count) { - using (PinnedBuffer buffer = new PinnedBuffer(count)) + using (Buffer buffer = new Buffer(count)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); Assert.Equal(count, buffer.Length); Assert.True(buffer.Array.Length >= count); - - VerifyPointer(buffer); } } @@ -33,13 +50,11 @@ public void ConstructWithExistingArray(int count) { Foo[] array = new Foo[count]; - using (PinnedBuffer buffer = new PinnedBuffer(array)) + using (Buffer buffer = new Buffer(array)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); Assert.Equal(count, buffer.Length); - - VerifyPointer(buffer); } } @@ -49,7 +64,7 @@ public void Clear(int count) { Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { buffer.Clear(); @@ -63,7 +78,7 @@ { for (int i = 0; i < 100; i++) { - using (PinnedBuffer buffer = PinnedBuffer.CreateClean(42)) + using (Buffer buffer = Buffer.CreateClean(42)) { for (int j = 0; j < buffer.Length; j++) { @@ -90,7 +105,7 @@ { Foo[] a = Foo.CreateArray(length); - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { Foo element = buffer[index]; @@ -104,7 +119,7 @@ { Foo[] a = Foo.CreateArray(length); - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { buffer[index] = new Foo(666, 666); @@ -116,24 +131,24 @@ [Fact] public void Dispose() { - PinnedBuffer buffer = new PinnedBuffer(42); + Buffer buffer = new Buffer(42); buffer.Dispose(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); } - + [Theory] [InlineData(7)] [InlineData(123)] public void CastToSpan(int bufferLength) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { BufferSpan span = buffer; Assert.Equal(buffer.Array, span.Array); Assert.Equal(0, span.Start); - Assert.Equal(buffer.Pointer, span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer); Assert.Equal(span.Length, bufferLength); } } @@ -141,13 +156,13 @@ [Fact] public void Span() { - using (PinnedBuffer buffer = new PinnedBuffer(42)) + using (Buffer buffer = new Buffer(42)) { BufferSpan span = buffer.Span; Assert.Equal(buffer.Array, span.Array); Assert.Equal(0, span.Start); - Assert.Equal(buffer.Pointer, span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer); Assert.Equal(span.Length, 42); } } @@ -160,13 +175,13 @@ [InlineData(123, 17)] public void WithStartOnly(int bufferLength, int start) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { BufferSpan span = buffer.Slice(start); Assert.Equal(buffer.Array, span.Array); Assert.Equal(start, span.Start); - Assert.Equal(buffer.Pointer + start * Unsafe.SizeOf(), span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, bufferLength - start); } } @@ -176,13 +191,13 @@ [InlineData(123, 17, 42)] public void WithStartAndLength(int bufferLength, int start, int spanLength) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { BufferSpan span = buffer.Slice(start, spanLength); Assert.Equal(buffer.Array, span.Array); Assert.Equal(start, span.Start); - Assert.Equal(buffer.Pointer + start * Unsafe.SizeOf(), span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, spanLength); } } @@ -192,9 +207,9 @@ public void UnPinAndTakeArrayOwnership() { Foo[] data = null; - using (PinnedBuffer buffer = new PinnedBuffer(42)) + using (Buffer buffer = new Buffer(42)) { - data = buffer.UnPinAndTakeArrayOwnership(); + data = buffer.TakeArrayOwnership(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); } @@ -202,10 +217,41 @@ Assert.True(data.Length >= 42); } - private static void VerifyPointer(PinnedBuffer buffer) + public class Pin { - IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); - Assert.Equal(ptr, buffer.Pointer); + [Fact] + public void ReturnsPinnedPointerToTheBeginningOfArray() + { + using (Buffer buffer = new Buffer(42)) + { + Foo* actual = (Foo*)buffer.Pin(); + fixed (Foo* expected = buffer.Array) + { + Assert.Equal(expected, actual); + } + } + } + + [Fact] + public void SecondCallReturnsTheSamePointer() + { + using (Buffer buffer = new Buffer(42)) + { + IntPtr ptr1 = buffer.Pin(); + IntPtr ptr2 = buffer.Pin(); + + Assert.Equal(ptr1, ptr2); + } + } + + [Fact] + public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() + { + Buffer buffer = new Buffer(42); + buffer.Dispose(); + + Assert.Throws(() => buffer.Pin()); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/TestStructs.cs b/tests/ImageSharp.Tests/Common/TestStructs.cs index 9e762bbd1..f7f09bea2 100644 --- a/tests/ImageSharp.Tests/Common/TestStructs.cs +++ b/tests/ImageSharp.Tests/Common/TestStructs.cs @@ -25,6 +25,8 @@ namespace ImageSharp.Tests.Common } return result; } + + public override string ToString() => $"({this.A},{this.B})"; } diff --git a/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs b/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs index c7121695e..7f16f30bb 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs @@ -19,7 +19,7 @@ namespace ImageSharp.Tests.Drawing.Text GlyphBuilder fullBuilder = new GlyphBuilder(new System.Numerics.Vector2(10, 99)); IGlyphRenderer builder = fullBuilder; - builder.BeginGlyph(); + builder.BeginGlyph(Vector2.Zero); builder.BeginFigure(); builder.MoveTo(new Vector2(0, 0)); builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 @@ -52,7 +52,7 @@ namespace ImageSharp.Tests.Drawing.Text IGlyphRenderer builder = fullBuilder; for (int i = 0; i < 10; i++) { - builder.BeginGlyph(); + builder.BeginGlyph(Vector2.Zero); builder.BeginFigure(); builder.MoveTo(new Vector2(0, 0)); builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index cdd892dce..d83424b24 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Tests public static string[] ProgressiveTestJpegs = TestImages.Jpeg.Progressive.All; [Theory] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb32)] public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) where TColor : struct, IPixel { @@ -36,7 +36,7 @@ namespace ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb32)] public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) where TColor : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 0833cb868..f900fe782 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -48,8 +48,8 @@ namespace ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio420, 75)] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio444, 75)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb32, JpegSubsample.Ratio420, 75)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb32, JpegSubsample.Ratio444, 75)] public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) where TColor : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index d0a7fae33..25fe46aa2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -39,7 +39,7 @@ namespace ImageSharp.Tests } [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb32)] public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider) where TColor : struct, IPixel { @@ -61,7 +61,7 @@ namespace ImageSharp.Tests } [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb32)] public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider) where TColor : struct, IPixel { diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs new file mode 100644 index 000000000..dc62f1cbf --- /dev/null +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.ObjectModel; + using Xunit; + + public class ExifReaderTests + { + [Fact] + public void Read_DataIsEmpty_ReturnsEmptyCollection() + { + ExifReader reader = new ExifReader(); + byte[] data = new byte[] { }; + + Collection result = reader.Read(data); + + Assert.Equal(0, result.Count); + } + + [Fact] + public void Read_DataIsMinimal_ReturnsEmptyCollection() + { + ExifReader reader = new ExifReader(); + byte[] data = new byte[] { 69, 120, 105, 102, 0, 0 }; + + Collection result = reader.Read(data); + + Assert.Equal(0, result.Count); + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 39ce61495..c40abd934 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -61,7 +61,7 @@ namespace ImageSharp.Tests BlackWhiteChecker(pixels); // top left VirticalBars(pixels); // top right TransparentGradients(pixels); // bottom left - Rainbow(pixels); // bottom right + Rainbow(pixels); // bottom right } } /// @@ -70,7 +70,7 @@ namespace ImageSharp.Tests /// private static void VirticalBars(PixelAccessor pixels) { - // topLeft + // topLeft int left = pixels.Width / 2; int right = pixels.Width; int top = 0; @@ -101,7 +101,7 @@ namespace ImageSharp.Tests /// private static void BlackWhiteChecker(PixelAccessor pixels) { - // topLeft + // topLeft int left = 0; int right = pixels.Width / 2; int top = 0; @@ -140,7 +140,7 @@ namespace ImageSharp.Tests /// private static void TransparentGradients(PixelAccessor pixels) { - // topLeft + // topLeft int left = 0; int right = pixels.Width / 2; int top = pixels.Height / 2; diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index 489ef970a..92a16563a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Tests Alpha8 = 1 << 0, - Argb = 1 << 1, + Argb32 = 1 << 1, Bgr565 = 1 << 2, diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 6760735d1..cea9cfea0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } [Theory] - [WithBlankImages(42, 666, PixelTypes.Color | PixelTypes.Argb | PixelTypes.HalfSingle, "hello")] + [WithBlankImages(42, 666, PixelTypes.Color | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) where TColor : struct, IPixel { @@ -86,7 +86,7 @@ namespace ImageSharp.Tests public static string[] AllBmpFiles => TestImages.Bmp.All; [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.Argb)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.Argb32)] public void Use_WithFileCollection(TestImageProvider provider) where TColor : struct, IPixel { @@ -96,7 +96,7 @@ namespace ImageSharp.Tests } [Theory] - [WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Color | PixelTypes.Argb)] + [WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Color | PixelTypes.Argb32)] public void Use_WithSolidFilledImagesAttribute(TestImageProvider provider) where TColor : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 63c24a157..0d24410ac 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -85,7 +85,7 @@ namespace ImageSharp.Tests [Theory] [InlineData(PixelTypes.Color, typeof(Color))] - [InlineData(PixelTypes.Argb, typeof(Argb))] + [InlineData(PixelTypes.Argb32, typeof(Argb32))] [InlineData(PixelTypes.HalfVector4, typeof(HalfVector4))] [InlineData(PixelTypes.StandardImageClass, typeof(Color))] public void ToType(PixelTypes pt, Type expectedType) @@ -95,7 +95,7 @@ namespace ImageSharp.Tests [Theory] [InlineData(typeof(Color), PixelTypes.Color)] - [InlineData(typeof(Argb), PixelTypes.Argb)] + [InlineData(typeof(Argb32), PixelTypes.Argb32)] public void GetPixelType(Type clrType, PixelTypes expectedPixelType) { Assert.Equal(expectedPixelType, clrType.GetPixelType());