mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 14be25789f2d8cbfc10b39557e63b60819d393ef Former-commit-id: 8445c082984b42411d1de44c0c36008bd77d036b Former-commit-id: a6d97b49cc8a1bc003b205b062e37abf45183f4eaf/merge-core
11 changed files with 508 additions and 1 deletions
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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> |
|||
@ -0,0 +1,14 @@ |
|||
namespace GenericImage |
|||
{ |
|||
public interface IImageBase<T> |
|||
where T : struct |
|||
{ |
|||
T[] Pixels { get; } |
|||
|
|||
int Width { get; } |
|||
|
|||
int Height { get; } |
|||
|
|||
IPixelAccessor Lock(); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace GenericImage |
|||
{ |
|||
using System; |
|||
|
|||
using GenericImage.PackedVectors; |
|||
|
|||
public interface IPixelAccessor : IDisposable |
|||
{ |
|||
IPackedVector this[int x, int y] |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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")] |
|||
@ -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": { } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue