From 482cba3768d667263b1461716124cae7679d0014 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 24 Aug 2016 00:57:48 +1000 Subject: [PATCH] Begin fitting packed vector with XNA etc Former-commit-id: 929527d603ef214c2975e64fb43b6f9867ab2a4a Former-commit-id: 476e47a8f3c03a02420b57fda0f7b0b70eab4bba Former-commit-id: 0a9e0b6e26bfc736835bcc4cb9f02a2da246fa59 --- src/ImageProcessorCore/Colors/Color.cs | 150 +++++++++++------- .../Colors/PackedVector/IPackedVector.cs | 30 ++-- .../Common/Extensions/ComparableExtensions.cs | 24 +++ src/ImageProcessorCore/Image/PixelAccessor.cs | 2 +- .../Quantizers/Octree/OctreeQuantizer.cs | 4 +- .../Image/GetSetPixel.cs | 4 +- 6 files changed, 141 insertions(+), 73 deletions(-) diff --git a/src/ImageProcessorCore/Colors/Color.cs b/src/ImageProcessorCore/Colors/Color.cs index d5570d6470..fc5c0a210d 100644 --- a/src/ImageProcessorCore/Colors/Color.cs +++ b/src/ImageProcessorCore/Colors/Color.cs @@ -8,7 +8,6 @@ namespace ImageProcessorCore using System; using System.Numerics; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; /// /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. @@ -18,38 +17,77 @@ namespace ImageProcessorCore /// 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 : IPackedVector, IEquatable { + private const float Max = 255F; + private const float Min = 0F; + private uint packedValue; + /// - /// Gets or sets the blue component. + /// Gets or sets the red component. /// - [FieldOffset(0)] - public byte R; + public byte R + { + get + { + return (byte)this.packedValue; + } + set + { + this.packedValue = (uint)(this.packedValue & -0x100 | value); + } + } + /// /// Gets or sets the green component. /// - [FieldOffset(1)] - public byte G; + public byte G + { + get + { + return (byte)(this.packedValue >> 8); + } + set + { + this.packedValue = (uint)(this.packedValue & -0xff01 | (uint)value << 8); + } + } /// - /// Gets or sets the red component. + /// Gets or sets the blue component. /// - [FieldOffset(2)] - public byte B; + public byte B + { + get + { + return (byte)(this.packedValue >> 16); + } + set + { + this.packedValue = (uint)(this.packedValue & -0xff0001 | (uint)(value << 16)); + } + } /// /// Gets or sets the alpha component. /// - [FieldOffset(3)] - public byte A; + public byte A + { + get + { + return (byte)(this.packedValue >> 24); + } + set + { + this.packedValue = this.packedValue & 0xffffff | (uint)value << 24; + } + } /// /// The packed value. /// - [FieldOffset(0)] - private uint packedValue; + public uint PackedValue { get { return this.packedValue; } set { this.packedValue = value; } } /// /// Initializes a new instance of the struct. @@ -61,10 +99,7 @@ namespace ImageProcessorCore public Color(byte r, byte g, byte b, byte a = 255) : this() { - this.R = r; - this.G = g; - this.B = b; - this.A = a; + this.packedValue = (uint)(r | g << 8 | b << 16 | a << 24); } /// @@ -87,17 +122,19 @@ namespace ImageProcessorCore if (hex.Length == 8) { - this.R = Convert.ToByte(hex.Substring(2, 2), 16); - this.G = Convert.ToByte(hex.Substring(4, 2), 16); - this.B = Convert.ToByte(hex.Substring(6, 2), 16); - this.A = Convert.ToByte(hex.Substring(0, 2), 16); + this.packedValue = + (uint)(Convert.ToByte(hex.Substring(2, 2), 16) + | Convert.ToByte(hex.Substring(4, 2), 16) << 8 + | Convert.ToByte(hex.Substring(6, 2), 16) << 16 + | Convert.ToByte(hex.Substring(0, 2), 16) << 24); } else if (hex.Length == 6) { - this.R = Convert.ToByte(hex.Substring(0, 2), 16); - this.G = Convert.ToByte(hex.Substring(2, 2), 16); - this.B = Convert.ToByte(hex.Substring(4, 2), 16); - this.A = 255; + this.packedValue = + (uint)(Convert.ToByte(hex.Substring(0, 2), 16) + | Convert.ToByte(hex.Substring(2, 2), 16) << 8 + | Convert.ToByte(hex.Substring(4, 2), 16) << 16 + | 255 << 24); } else { @@ -105,10 +142,11 @@ namespace ImageProcessorCore string gh = char.ToString(hex[1]); string bh = char.ToString(hex[2]); - this.R = Convert.ToByte(rh + rh, 16); - this.G = Convert.ToByte(gh + gh, 16); - this.B = Convert.ToByte(bh + bh, 16); - this.A = 255; + this.packedValue = + (uint)(Convert.ToByte(rh + rh, 16) + | Convert.ToByte(gh + gh, 16) << 8 + | Convert.ToByte(bh + bh, 16) << 16 + | 255 << 24); } } @@ -122,11 +160,7 @@ namespace ImageProcessorCore public Color(float r, float g, float b, float a = 1) : this() { - Vector4 clamped = Vector4.Clamp(new Vector4(r, g, b, a), Vector4.Zero, Vector4.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = (byte)Math.Round(clamped.W); + Pack(ref r, ref g, ref b, ref a); } /// @@ -138,11 +172,8 @@ namespace ImageProcessorCore public Color(Vector3 vector) : this() { - Vector3 clamped = Vector3.Clamp(vector, Vector3.Zero, Vector3.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = 255; + float a = 1; + Pack(ref vector.X, ref vector.Y, ref vector.Z, ref a); } /// @@ -154,11 +185,7 @@ namespace ImageProcessorCore public Color(Vector4 vector) : this() { - Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = (byte)Math.Round(clamped.W); + this.packedValue = Pack(ref vector); } /// @@ -206,20 +233,13 @@ namespace ImageProcessorCore /// public void PackFromVector4(Vector4 vector) { - Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F; - this.R = (byte)Math.Round(clamped.X); - this.G = (byte)Math.Round(clamped.Y); - this.B = (byte)Math.Round(clamped.Z); - this.A = (byte)Math.Round(clamped.W); + this.packedValue = Pack(ref vector); } /// public void PackFromBytes(byte x, byte y, byte z, byte w) { - this.R = x; - this.G = y; - this.B = z; - this.A = w; + this.packedValue = (uint)(x | y << 8 | z << 16 | w << 24); } /// @@ -261,6 +281,28 @@ namespace ImageProcessorCore return this.GetHashCode(this); } + /// + /// Packs a vector into a uint. + /// + /// The vector containing the values to pack. + /// The ulong containing the packed values. + private static uint Pack(ref Vector4 vector) + { + // TODO: Maybe use Vector4.Clamp() instead. + return (uint)((byte)Math.Round(vector.X * Max).Clamp(Min, Max) + | ((byte)Math.Round(vector.Y * Max).Clamp(Min, Max) << 8) + | (byte)Math.Round(vector.Z * Max).Clamp(Min, Max) << 16 + | (byte)Math.Round(vector.W * Max).Clamp(Min, Max) << 24); + } + + private static uint Pack(ref float x, ref float y, ref float z, ref float w) + { + return (uint)((byte)Math.Round(x * Max).Clamp(Min, Max) + | ((byte)Math.Round(y * Max).Clamp(Min, Max) << 8) + | (byte)Math.Round(z * Max).Clamp(Min, Max) << 16 + | (byte)Math.Round(w * Max).Clamp(Min, Max) << 24); + } + /// /// Returns the hash code for this instance. /// diff --git a/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs b/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs index 114a173eb0..3ccf473f5c 100644 --- a/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs +++ b/src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs @@ -15,21 +15,23 @@ namespace ImageProcessorCore public interface IPackedVector : IPackedVector where TPacked : struct { - /// - /// Directly gets the packed representation of the packed vector. - /// Typically packed in least to greatest significance order. - /// - /// - /// The . - /// - TPacked GetPackedValue(); + TPacked PackedValue { get; set; } - /// - /// Directly sets the packed representation of the packed vector. - /// Typically packed in least to greatest significance order. - /// - /// The packed value. - void SetPackedValue(TPacked value); + ///// + ///// Directly gets the packed representation of the packed vector. + ///// Typically packed in least to greatest significance order. + ///// + ///// + ///// The . + ///// + //TPacked GetPackedValue(); + + ///// + ///// Directly sets the packed representation of the packed vector. + ///// Typically packed in least to greatest significance order. + ///// + ///// The packed value. + //void SetPackedValue(TPacked value); } /// diff --git a/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs b/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs index cb0288fb7b..a2704ae65a 100644 --- a/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs +++ b/src/ImageProcessorCore/Common/Extensions/ComparableExtensions.cs @@ -37,6 +37,30 @@ namespace ImageProcessorCore return value; } + /// + /// Restricts a to be within a specified range. + /// + /// The The value to clamp. + /// The minimum value. If value is less than min, min will be returned. + /// The maximum value. If value is greater than max, max will be returned. + /// + /// The representing the clamped value. + /// + public static uint Clamp(this uint value, uint min, uint max) + { + if (value > max) + { + return max; + } + + if (value < min) + { + return min; + } + + return value; + } + /// /// Restricts a to be within a specified range. /// diff --git a/src/ImageProcessorCore/Image/PixelAccessor.cs b/src/ImageProcessorCore/Image/PixelAccessor.cs index 833a4c6789..343b60dd14 100644 --- a/src/ImageProcessorCore/Image/PixelAccessor.cs +++ b/src/ImageProcessorCore/Image/PixelAccessor.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore using System.Runtime.InteropServices; /// - /// Encapsulates properties to provides per-pixel access to an images pixels. + /// Provides per-pixel access to generic pixels. /// /// The pixel format. /// The packed format. uint, long, float. diff --git a/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs index 522e07151b..42de023b03 100644 --- a/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs @@ -193,11 +193,11 @@ namespace ImageProcessorCore.Quantizers /// Add a given color value to the Octree /// /// - /// The containing color information to add. + /// The containing color information to add. /// public void AddColor(TColor pixel) { - TPacked packed = pixel.GetPackedValue(); + TPacked packed = pixel.PackedValue; //.GetPackedValue(); // Check if this request is for the same color as the last if (this.previousColor.Equals(packed)) { diff --git a/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs index eb742404ef..09d8c862f9 100644 --- a/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs +++ b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs @@ -10,7 +10,7 @@ public class GetSetPixel { - [Benchmark(Baseline = true, Description = "System.Drawing GetSeTColor pixel")] + [Benchmark(Baseline = true, Description = "System.Drawing GetSet pixel")] public SystemColor ResizeSystemDrawing() { using (Bitmap source = new Bitmap(400, 400)) @@ -20,7 +20,7 @@ } } - [Benchmark(Description = "ImageProcessorCore GetSeTColor pixel")] + [Benchmark(Description = "ImageProcessorCore GetSet pixel")] public CoreColor ResizeCore() { CoreImage image = new CoreImage(400, 400);