diff --git a/GenericImage/IImageBase.cs b/GenericImage/IImageBase.cs index 4de7d3968..4e614a628 100644 --- a/GenericImage/IImageBase.cs +++ b/GenericImage/IImageBase.cs @@ -1,9 +1,11 @@ namespace GenericImage { - public interface IImageBase - where T : struct + using GenericImage.PackedVectors; + + public interface IImageBase + where TPacked : IPackedVector { - T[] Pixels { get; } + TPacked[] Pixels { get; } int Width { get; } diff --git a/GenericImage/ImageRgba64.cs b/GenericImage/ImageRgba64.cs index 1b673079d..9e58bf781 100644 --- a/GenericImage/ImageRgba64.cs +++ b/GenericImage/ImageRgba64.cs @@ -1,15 +1,17 @@ namespace GenericImage { - public class ImageRgba64 : IImageBase + using GenericImage.PackedVectors; + + public class ImageRgba64 : IImageBase { public ImageRgba64(int width, int height) { this.Width = width; this.Height = height; - this.Pixels = new ulong[width * height * 4]; + this.Pixels = new Rgba64[width * height]; } - public ulong[] Pixels { get; } + public Rgba64[] Pixels { get; } public int Width { get; } diff --git a/GenericImage/PackedVectors/IPackedVector.cs b/GenericImage/PackedVectors/IPackedVector.cs index be87f6d83..7bd809094 100644 --- a/GenericImage/PackedVectors/IPackedVector.cs +++ b/GenericImage/PackedVectors/IPackedVector.cs @@ -34,5 +34,11 @@ /// /// The . Vector4 ToVector4(); + + /// + /// Expands the packed representation into a . + /// + /// The . + byte[] ToBytes(); } } diff --git a/GenericImage/PackedVectors/Rgba32.cs b/GenericImage/PackedVectors/Rgba32.cs new file mode 100644 index 000000000..40bf1a894 --- /dev/null +++ b/GenericImage/PackedVectors/Rgba32.cs @@ -0,0 +1,155 @@ +namespace GenericImage.PackedVectors +{ + using System; + using System.Numerics; + + /// + /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. + /// + public struct Rgba32 : IPackedVector, IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + public Rgba32(float r, float g, float b, float a) + { + this.PackedValue = Pack(r, g, b, a); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// Vector containing the components for the packed vector. + /// + public Rgba32(Vector4 vector) + { + this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); + } + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(Rgba32 left, Rgba32 right) + { + return left.PackedValue == right.PackedValue; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator !=(Rgba32 left, Rgba32 right) + { + return left.PackedValue != right.PackedValue; + } + + /// + public void PackVector(Vector4 vector) + { + Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + this.PackedValue = Pack(clamped.X, clamped.Y, clamped.Z, clamped.W); + } + + /// + public Vector4 ToVector4() + { + return new Vector4( + ((this.PackedValue >> 16) & 0xFF) / 255f, + ((this.PackedValue >> 8) & 0xFF) / 255f, + (this.PackedValue & 0xFF) / 255f, + ((this.PackedValue >> 24) & 0xFF) / 255f); + } + + /// + public byte[] ToBytes() + { + return new[] + { + (byte)((this.PackedValue >> 16) & 0xFF), + (byte)((this.PackedValue >> 8) & 0xFF), + (byte)(this.PackedValue & 0xFF), + (byte)((this.PackedValue >> 24) & 0xFF) + }; + } + + /// + public override bool Equals(object obj) + { + return (obj is Rgba32) && this.Equals((Rgba32)obj); + } + + /// + public bool Equals(Rgba32 other) + { + return this.PackedValue == other.PackedValue; + } + + /// + /// 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.GetHashCode(this); + } + + /// + /// Sets the packed representation from the given component values. + /// + /// The x component. + /// The y component. + /// The z component. + /// The w component. + /// + /// The . + /// + private static uint Pack(float x, float y, float z, float w) + { + return ((uint)Math.Round(x * 255f) << 16) | + ((uint)Math.Round(y * 255f) << 8) | + (uint)Math.Round(z * 255f) | + ((uint)Math.Round(w * 255f) << 24); + } + + /// + /// Returns the hash code for this instance. + /// + /// + /// The instance of to return the hash code for. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + private int GetHashCode(Rgba32 packed) + { + return packed.PackedValue.GetHashCode(); + } + } +} diff --git a/GenericImage/PackedVectors/Rgba64.cs b/GenericImage/PackedVectors/Rgba64.cs index 832366ba1..ff529d15e 100644 --- a/GenericImage/PackedVectors/Rgba64.cs +++ b/GenericImage/PackedVectors/Rgba64.cs @@ -80,6 +80,12 @@ ((this.PackedValue >> 48) & 0xFFFF) / 65535f); } + /// + public byte[] ToBytes() + { + throw new NotImplementedException(); + } + /// public override bool Equals(object obj) { diff --git a/GenericImage/PixelAccessorRgba64.cs b/GenericImage/PixelAccessorRgba64.cs index 46dbd8dea..2dae8a0f1 100644 --- a/GenericImage/PixelAccessorRgba64.cs +++ b/GenericImage/PixelAccessorRgba64.cs @@ -13,7 +13,7 @@ /// /// The position of the first pixel in the bitmap. /// - private ulong* pixelsBase; + private Rgba64* pixelsBase; /// /// Provides a way to access the pixels from unmanaged memory. @@ -39,30 +39,17 @@ /// /// The image to provide pixel access for. /// - public PixelAccessorRgba64(IImageBase image) + public PixelAccessorRgba64(IImageBase image) { //Guard.NotNull(image, nameof(image)); //Guard.MustBeGreaterThan(image.Width, 0, "image width"); //Guard.MustBeGreaterThan(image.Height, 0, "image height"); - int size = image.Pixels.Length; this.Width = image.Width; this.Height = image.Height; - // Assign the pointer. - // If buffer is allocated on Large Object Heap i.e > 85Kb, then we are going to pin it instead of making a copy. - if (size > 87040) - { - this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned); - this.pixelsBase = (ulong*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); - } - else - { - fixed (ulong* pbuffer = image.Pixels) - { - this.pixelsBase = pbuffer; - } - } + this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned); + this.pixelsBase = (Rgba64*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); } /// @@ -110,7 +97,7 @@ throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - return *((Rgba64*)(this.pixelsBase + (((y * this.Width) + x) * 4))); + return *(this.pixelsBase + ((y * this.Width) + x)); } set @@ -126,7 +113,7 @@ throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - *(Rgba64*)(this.pixelsBase + (((y * this.Width) + x) * 4)) = (Rgba64)value; + *(this.pixelsBase + ((y * this.Width) + x)) = (Rgba64)value; } }