Browse Source

Experiments with a generic image

Former-commit-id: 14be25789f2d8cbfc10b39557e63b60819d393ef
Former-commit-id: 8445c082984b42411d1de44c0c36008bd77d036b
Former-commit-id: a6d97b49cc8a1bc003b205b062e37abf45183f4e
af/merge-core
James Jackson-South 10 years ago
parent
commit
b837eba19d
  1. 40
      GenericImage/Common/Helpers/ImageMaths.cs
  2. 21
      GenericImage/GenericImage.xproj
  3. 14
      GenericImage/IImageBase.cs
  4. 15
      GenericImage/IPixelAccessor.cs
  5. 23
      GenericImage/ImageRgba64.cs
  6. 38
      GenericImage/PackedVectors/IPackedVector.cs
  7. 142
      GenericImage/PackedVectors/Rgba64.cs
  8. 161
      GenericImage/PixelAccessorRgba64.cs
  9. 19
      GenericImage/Properties/AssemblyInfo.cs
  10. 27
      GenericImage/project.json
  11. 9
      ImageProcessorCore.sln

40
GenericImage/Common/Helpers/ImageMaths.cs

@ -0,0 +1,40 @@
namespace GenericImage
{
public static class ImageMaths
{
/// <summary>
/// Restricts a value to be within a specified range.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c> will be returned.</param>
/// <param name="max">The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c> will be returned.</param>
/// <returns>The clamped value.</returns>
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;
}
/// <summary>
/// Restricts a value to be within a specified range.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c> will be returned.</param>
/// <param name="max">The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c> will be returned.</param>
/// <returns>The clamped value.</returns>
public static int Clamp(int value, int min, int max)
{
value = (value > max) ? max : value;
value = (value < min) ? min : value;
return value;
}
}
}

21
GenericImage/GenericImage.xproj

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>45d91211-9d4a-4382-a114-8786859e302a</ProjectGuid>
<RootNamespace>GenericImage</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

14
GenericImage/IImageBase.cs

@ -0,0 +1,14 @@
namespace GenericImage
{
public interface IImageBase<T>
where T : struct
{
T[] Pixels { get; }
int Width { get; }
int Height { get; }
IPixelAccessor Lock();
}
}

15
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;
}
}
}

23
GenericImage/ImageRgba64.cs

@ -0,0 +1,23 @@
namespace GenericImage
{
public class ImageRgba64 : IImageBase<ulong>
{
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);
}
}
}

38
GenericImage/PackedVectors/IPackedVector.cs

@ -0,0 +1,38 @@
namespace GenericImage.PackedVectors
{
using System.Numerics;
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values,
/// allowing multiple encodings to be manipulated in a generic way.
/// </summary>
/// <typeparam name="TPacked">
/// The type of object representing the packed value.
/// </typeparam>
public interface IPackedVector<TPacked> : IPackedVector
where TPacked : struct
{
/// <summary>
/// Gets or sets the packed representation of the value.
/// </summary>
TPacked PackedValue { get; set; }
}
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values.
/// </summary>
public interface IPackedVector
{
/// <summary>
/// Sets the packed representation from a <see cref="Vector4"/>.
/// </summary>
/// <param name="vector">The vector to pack.</param>
void PackVector(Vector4 vector);
/// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4();
}
}

142
GenericImage/PackedVectors/Rgba64.cs

@ -0,0 +1,142 @@
namespace GenericImage.PackedVectors
{
using System;
using System.Numerics;
/// <summary>
/// Packed vector type containing four 16-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Rgba64 : IPackedVector<ulong>, IEquatable<Rgba64>
{
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Rgba64(float r, float g, float b, float a)
{
this.PackedValue = Pack(r, g, b, a);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="vector">
/// Vector containing the components for the packed vector.
/// </param>
public Rgba64(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc/>
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba64"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba64"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rgba64 left, Rgba64 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Rgba64"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgba64"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rgba64 left, Rgba64 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc/>
public void PackVector(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return (obj is Rgba64) && this.Equals((Rgba64)obj);
}
/// <inheritdoc/>
public bool Equals(Rgba64 other)
{
return this.PackedValue == other.PackedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
/// <summary>
/// Sets the packed representation from the given component values.
/// </summary>
/// <param name="x">The x component.</param>
/// <param name="y">The y component.</param>
/// <param name="z">The z component.</param>
/// <param name="w">The w component.</param>
/// <returns>
/// The <see cref="ulong"/>.
/// </returns>
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);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="packed">
/// The instance of <see cref="Rgba64"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Rgba64 packed)
{
return packed.PackedValue.GetHashCode();
}
}
}

161
GenericImage/PixelAccessorRgba64.cs

@ -0,0 +1,161 @@
namespace GenericImage
{
using System;
using System.Runtime.InteropServices;
using GenericImage.PackedVectors;
/// <summary>
/// Provides per-pixel access to an images pixels.
/// </summary>
public sealed unsafe class PixelAccessorRgba64 : IPixelAccessor
{
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private ulong* pixelsBase;
/// <summary>
/// Provides a way to access the pixels from unmanaged memory.
/// </summary>
private GCHandle pixelsHandle;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// 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.
/// </remarks>
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessorRgba64"/> class.
/// </summary>
/// <param name="image">
/// The image to provide pixel access for.
/// </param>
public PixelAccessorRgba64(IImageBase<ulong> 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;
}
}
}
/// <summary>
/// Finalizes an instance of the <see cref="PixelAccessorRgba64"/> class.
/// </summary>
~PixelAccessorRgba64()
{
this.Dispose();
}
/// <summary>
/// Gets the width of the image.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height of the image.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets or sets the color of a pixel at the specified position.
/// </summary>
/// <param name="x">
/// The x-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <param name="y">
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="IPackedVector"/> at the specified position.</returns>
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;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
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);
}
}
}

19
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")]

27
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": { }
}
}

9
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

Loading…
Cancel
Save