diff --git a/GenericImage/Common/Helpers/ImageMaths.cs b/GenericImage/Common/Helpers/ImageMaths.cs
new file mode 100644
index 000000000..2c5e4f884
--- /dev/null
+++ b/GenericImage/Common/Helpers/ImageMaths.cs
@@ -0,0 +1,40 @@
+namespace GenericImage
+{
+ public static class ImageMaths
+ {
+ ///
+ /// Restricts a value to be within a specified range.
+ ///
+ /// 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 clamped value.
+ public static float Clamp(float value, float min, float max)
+ {
+ // This compare order is very important!!!
+ // We must follow HLSL behavior in the case user specified min value is bigger than max value.
+ // First we check to see if we're greater than the max
+ value = (value > max) ? max : value;
+
+ // Then we check to see if we're less than the min.
+ value = (value < min) ? min : value;
+
+ // There's no check to see if min > max.
+ return value;
+ }
+
+ ///
+ /// Restricts a value to be within a specified range.
+ ///
+ /// 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 clamped value.
+ public static int Clamp(int value, int min, int max)
+ {
+ value = (value > max) ? max : value;
+ value = (value < min) ? min : value;
+ return value;
+ }
+ }
+}
diff --git a/GenericImage/GenericImage.xproj b/GenericImage/GenericImage.xproj
new file mode 100644
index 000000000..9ffcf7223
--- /dev/null
+++ b/GenericImage/GenericImage.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ 45d91211-9d4a-4382-a114-8786859e302a
+ GenericImage
+ .\obj
+ .\bin\
+ v4.5.2
+
+
+
+ 2.0
+
+
+
diff --git a/GenericImage/IImageBase.cs b/GenericImage/IImageBase.cs
new file mode 100644
index 000000000..4de7d3968
--- /dev/null
+++ b/GenericImage/IImageBase.cs
@@ -0,0 +1,14 @@
+namespace GenericImage
+{
+ public interface IImageBase
+ where T : struct
+ {
+ T[] Pixels { get; }
+
+ int Width { get; }
+
+ int Height { get; }
+
+ IPixelAccessor Lock();
+ }
+}
diff --git a/GenericImage/IPixelAccessor.cs b/GenericImage/IPixelAccessor.cs
new file mode 100644
index 000000000..7eb9b445b
--- /dev/null
+++ b/GenericImage/IPixelAccessor.cs
@@ -0,0 +1,15 @@
+namespace GenericImage
+{
+ using System;
+
+ using GenericImage.PackedVectors;
+
+ public interface IPixelAccessor : IDisposable
+ {
+ IPackedVector this[int x, int y]
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GenericImage/ImageRgba64.cs b/GenericImage/ImageRgba64.cs
new file mode 100644
index 000000000..1b673079d
--- /dev/null
+++ b/GenericImage/ImageRgba64.cs
@@ -0,0 +1,23 @@
+namespace GenericImage
+{
+ public class ImageRgba64 : IImageBase
+ {
+ public ImageRgba64(int width, int height)
+ {
+ this.Width = width;
+ this.Height = height;
+ this.Pixels = new ulong[width * height * 4];
+ }
+
+ public ulong[] Pixels { get; }
+
+ public int Width { get; }
+
+ public int Height { get; }
+
+ public IPixelAccessor Lock()
+ {
+ return new PixelAccessorRgba64(this);
+ }
+ }
+}
diff --git a/GenericImage/PackedVectors/IPackedVector.cs b/GenericImage/PackedVectors/IPackedVector.cs
new file mode 100644
index 000000000..be87f6d83
--- /dev/null
+++ b/GenericImage/PackedVectors/IPackedVector.cs
@@ -0,0 +1,38 @@
+namespace GenericImage.PackedVectors
+{
+ using System.Numerics;
+
+ ///
+ /// An interface that converts packed vector types to and from values,
+ /// allowing multiple encodings to be manipulated in a generic way.
+ ///
+ ///
+ /// The type of object representing the packed value.
+ ///
+ public interface IPackedVector : IPackedVector
+ where TPacked : struct
+ {
+ ///
+ /// Gets or sets the packed representation of the value.
+ ///
+ TPacked PackedValue { get; set; }
+ }
+
+ ///
+ /// An interface that converts packed vector types to and from values.
+ ///
+ public interface IPackedVector
+ {
+ ///
+ /// Sets the packed representation from a .
+ ///
+ /// The vector to pack.
+ void PackVector(Vector4 vector);
+
+ ///
+ /// Expands the packed representation into a .
+ ///
+ /// The .
+ Vector4 ToVector4();
+ }
+}
diff --git a/GenericImage/PackedVectors/Rgba64.cs b/GenericImage/PackedVectors/Rgba64.cs
new file mode 100644
index 000000000..832366ba1
--- /dev/null
+++ b/GenericImage/PackedVectors/Rgba64.cs
@@ -0,0 +1,142 @@
+namespace GenericImage.PackedVectors
+{
+ using System;
+ using System.Numerics;
+
+ ///
+ /// Packed vector type containing four 16-bit unsigned normalized values ranging from 0 to 1.
+ ///
+ public struct Rgba64 : IPackedVector, IEquatable
+ {
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The alpha component.
+ public Rgba64(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 Rgba64(Vector4 vector)
+ {
+ this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
+ }
+
+ ///
+ public ulong 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 ==(Rgba64 left, Rgba64 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 !=(Rgba64 left, Rgba64 right)
+ {
+ return left.PackedValue != right.PackedValue;
+ }
+
+ ///
+ public void PackVector(Vector4 vector)
+ {
+ this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
+ }
+
+ ///
+ public Vector4 ToVector4()
+ {
+ return new Vector4(
+ (this.PackedValue & 0xFFFF) / 65535f,
+ ((this.PackedValue >> 16) & 0xFFFF) / 65535f,
+ ((this.PackedValue >> 32) & 0xFFFF) / 65535f,
+ ((this.PackedValue >> 48) & 0xFFFF) / 65535f);
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is Rgba64) && this.Equals((Rgba64)obj);
+ }
+
+ ///
+ public bool Equals(Rgba64 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 ulong Pack(float x, float y, float z, float w)
+ {
+ return (ulong)Math.Round(ImageMaths.Clamp(x, 0, 1) * 65535f) |
+ ((ulong)Math.Round(ImageMaths.Clamp(y, 0, 1) * 65535f) << 16) |
+ ((ulong)Math.Round(ImageMaths.Clamp(z, 0, 1) * 65535f) << 32) |
+ ((ulong)Math.Round(ImageMaths.Clamp(w, 0, 1) * 65535f) << 48);
+ }
+
+ ///
+ /// 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(Rgba64 packed)
+ {
+ return packed.PackedValue.GetHashCode();
+ }
+ }
+}
diff --git a/GenericImage/PixelAccessorRgba64.cs b/GenericImage/PixelAccessorRgba64.cs
new file mode 100644
index 000000000..46dbd8dea
--- /dev/null
+++ b/GenericImage/PixelAccessorRgba64.cs
@@ -0,0 +1,161 @@
+namespace GenericImage
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+ using GenericImage.PackedVectors;
+
+ ///
+ /// Provides per-pixel access to an images pixels.
+ ///
+ public sealed unsafe class PixelAccessorRgba64 : IPixelAccessor
+ {
+ ///
+ /// The position of the first pixel in the bitmap.
+ ///
+ private ulong* pixelsBase;
+
+ ///
+ /// Provides a way to access the pixels from unmanaged memory.
+ ///
+ private GCHandle pixelsHandle;
+
+ ///
+ /// A value indicating whether this instance of the given entity has been disposed.
+ ///
+ /// if this instance has been disposed; otherwise, .
+ ///
+ /// If the entity is disposed, it must not be disposed a second
+ /// time. The isDisposed field is set the first time the entity
+ /// is disposed. If the isDisposed field is true, then the Dispose()
+ /// method will not dispose again. This help not to prolong the entity's
+ /// life in the Garbage Collector.
+ ///
+ private bool isDisposed;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The image to provide pixel access for.
+ ///
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~PixelAccessorRgba64()
+ {
+ this.Dispose();
+ }
+
+ ///
+ /// Gets the width of the image.
+ ///
+ public int Width { get; }
+
+ ///
+ /// Gets the height of the image.
+ ///
+ public int Height { get; }
+
+ ///
+ /// Gets or sets the color of a pixel at the specified position.
+ ///
+ ///
+ /// The x-coordinate of the pixel. Must be greater
+ /// than zero and smaller than the width of the pixel.
+ ///
+ ///
+ /// The y-coordinate of the pixel. Must be greater
+ /// than zero and smaller than the width of the pixel.
+ ///
+ /// The at the specified position.
+ public IPackedVector this[int x, int y]
+ {
+ get
+ {
+#if DEBUG
+ if ((x < 0) || (x >= this.Width))
+ {
+ throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width.");
+ }
+
+ if ((y < 0) || (y >= this.Height))
+ {
+ 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)));
+ }
+
+ set
+ {
+#if DEBUG
+ if ((x < 0) || (x >= this.Width))
+ {
+ throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width.");
+ }
+
+ if ((y < 0) || (y >= this.Height))
+ {
+ 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;
+ }
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ if (this.isDisposed)
+ {
+ return;
+ }
+
+ if (this.pixelsHandle.IsAllocated)
+ {
+ this.pixelsHandle.Free();
+ }
+
+ this.pixelsBase = null;
+
+ // Note disposing is done.
+ this.isDisposed = true;
+
+ // This object will be cleaned up by the Dispose method.
+ // Therefore, you should call GC.SuppressFinalize to
+ // take this object off the finalization queue
+ // and prevent finalization code for this object
+ // from executing a second time.
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/GenericImage/Properties/AssemblyInfo.cs b/GenericImage/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..8dd035951
--- /dev/null
+++ b/GenericImage/Properties/AssemblyInfo.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("GenericImage")]
+[assembly: AssemblyTrademark("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("45d91211-9d4a-4382-a114-8786859e302a")]
diff --git a/GenericImage/project.json b/GenericImage/project.json
new file mode 100644
index 000000000..617c22489
--- /dev/null
+++ b/GenericImage/project.json
@@ -0,0 +1,27 @@
+{
+ "version": "1.0.0-*",
+
+ "buildOptions": {
+ "allowUnsafe": true,
+ "debugType": "portable"
+ },
+ "dependencies": {
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Diagnostics.Tools": "4.0.1",
+ "System.IO": "4.1.0",
+ "System.IO.Compression": "4.1.0",
+ "System.Linq": "4.1.0",
+ "System.Numerics.Vectors": "4.1.1",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Runtime.InteropServices": "4.1.0",
+ "System.Text.Encoding.Extensions": "4.0.11",
+ "System.Threading": "4.0.11",
+ "System.Threading.Tasks": "4.0.11",
+ "System.Threading.Tasks.Parallel": "4.0.1"
+ },
+ "frameworks": {
+ "netstandard1.1": { }
+ }
+}
diff --git a/ImageProcessorCore.sln b/ImageProcessorCore.sln
index 9d828a74a..6e8766591 100644
--- a/ImageProcessorCore.sln
+++ b/ImageProcessorCore.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageProcessorCore", "src\ImageProcessorCore\ImageProcessorCore.xproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}"
EndProject
@@ -21,6 +21,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageProcessorCore.Benchmarks", "tests\ImageProcessorCore.Benchmarks\ImageProcessorCore.Benchmarks.xproj", "{299D8E18-102C-42DE-ADBF-79098EE706A8}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GenericImage", "GenericImage\GenericImage.xproj", "{45D91211-9D4A-4382-A114-8786859E302A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {45D91211-9D4A-4382-A114-8786859E302A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45D91211-9D4A-4382-A114-8786859E302A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45D91211-9D4A-4382-A114-8786859E302A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45D91211-9D4A-4382-A114-8786859E302A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -47,5 +53,6 @@ Global
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{299D8E18-102C-42DE-ADBF-79098EE706A8} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
+ {45D91211-9D4A-4382-A114-8786859E302A} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
EndGlobalSection
EndGlobal