|
|
|
@ -9,77 +9,171 @@ namespace ImageSharp |
|
|
|
using System.IO; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
|
|
public unsafe sealed class PixelRow<TColor, TPacked> : IDisposable |
|
|
|
/// <summary>
|
|
|
|
/// Represents a row of generic <see cref="Image{TColor,TPacked}"/> pixels.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TColor">The pixel format.</typeparam>
|
|
|
|
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
|
|
|
|
public sealed unsafe class PixelRow<TColor, TPacked> : IDisposable |
|
|
|
where TColor : struct, IPackedPixel<TPacked> |
|
|
|
where TPacked : struct |
|
|
|
{ |
|
|
|
private readonly GCHandle handle; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Provides a way to access the pixels from unmanaged memory.
|
|
|
|
/// </summary>
|
|
|
|
private readonly GCHandle pixelsHandle; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The pointer to the pixel buffer.
|
|
|
|
/// </summary>
|
|
|
|
private IntPtr dataPointer; |
|
|
|
|
|
|
|
/// <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="PixelRow{TColor,TPacked}"/> class.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="width">The width. </param>
|
|
|
|
/// <param name="componentOrder">The component order.</param>
|
|
|
|
public PixelRow(int width, ComponentOrder componentOrder) |
|
|
|
: this(width, componentOrder, 0) |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="PixelRow{TColor,TPacked}"/> class.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="width">The width. </param>
|
|
|
|
/// <param name="componentOrder">The component order.</param>
|
|
|
|
/// <param name="padding">The number of bytes to pad each row.</param>
|
|
|
|
public PixelRow(int width, ComponentOrder componentOrder, int padding) |
|
|
|
{ |
|
|
|
this.Width = width; |
|
|
|
this.ComponentOrder = componentOrder; |
|
|
|
this.Data = new byte[(width * GetComponentCount(componentOrder)) + padding]; |
|
|
|
this.handle = GCHandle.Alloc(this.Data, GCHandleType.Pinned); |
|
|
|
this.DataPointer = (byte*)this.handle.AddrOfPinnedObject().ToPointer(); |
|
|
|
this.Bytes = new byte[(width * GetComponentCount(componentOrder)) + padding]; |
|
|
|
this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); |
|
|
|
|
|
|
|
// TODO: Why is Resharper warning us about an impure method call?
|
|
|
|
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); |
|
|
|
this.PixelBase = (byte*)this.dataPointer.ToPointer(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Finalizes an instance of the <see cref="PixelRow{TColor,TPacked}"/> class.
|
|
|
|
/// </summary>
|
|
|
|
~PixelRow() |
|
|
|
{ |
|
|
|
this.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
public byte[] Data { get; } |
|
|
|
/// <summary>
|
|
|
|
/// Gets the data in bytes.
|
|
|
|
/// </summary>
|
|
|
|
public byte[] Bytes { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the pointer to the pixel buffer.
|
|
|
|
/// </summary>
|
|
|
|
public IntPtr DataPointer => this.dataPointer; |
|
|
|
|
|
|
|
public byte* DataPointer { get; private set; } |
|
|
|
/// <summary>
|
|
|
|
/// Gets the data pointer.
|
|
|
|
/// </summary>
|
|
|
|
public byte* PixelBase { get; private set; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the component order.
|
|
|
|
/// </summary>
|
|
|
|
public ComponentOrder ComponentOrder { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the width.
|
|
|
|
/// </summary>
|
|
|
|
public int Width { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reads the stream to the row.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="stream">The stream.</param>
|
|
|
|
public void Read(Stream stream) |
|
|
|
{ |
|
|
|
stream.Read(this.Data, 0, this.Data.Length); |
|
|
|
stream.Read(this.Bytes, 0, this.Bytes.Length); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Writes the row to the stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="stream">The stream.</param>
|
|
|
|
public void Write(Stream stream) |
|
|
|
{ |
|
|
|
stream.Write(this.Data, 0, this.Data.Length); |
|
|
|
stream.Write(this.Bytes, 0, this.Bytes.Length); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
|
|
/// </summary>
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
if (this.DataPointer == null) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.handle.IsAllocated) |
|
|
|
{ |
|
|
|
this.handle.Free(); |
|
|
|
} |
|
|
|
|
|
|
|
this.DataPointer = null; |
|
|
|
if (this.isDisposed) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.PixelBase == null) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.pixelsHandle.IsAllocated) |
|
|
|
{ |
|
|
|
this.pixelsHandle.Free(); |
|
|
|
} |
|
|
|
|
|
|
|
this.dataPointer = IntPtr.Zero; |
|
|
|
this.PixelBase = null; |
|
|
|
|
|
|
|
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); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets component count for the given order.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="componentOrder">The component order.</param>
|
|
|
|
/// <returns>
|
|
|
|
/// The <see cref="int"/>.
|
|
|
|
/// </returns>
|
|
|
|
/// <exception cref="NotSupportedException">
|
|
|
|
/// Thrown if an invalid order is given.
|
|
|
|
/// </exception>
|
|
|
|
private static int GetComponentCount(ComponentOrder componentOrder) |
|
|
|
{ |
|
|
|
switch (componentOrder) |
|
|
|
{ |
|
|
|
case ComponentOrder.ZYX: |
|
|
|
case ComponentOrder.XYZ: |
|
|
|
return 3; |
|
|
|
case ComponentOrder.ZYXW: |
|
|
|
case ComponentOrder.XYZW: |
|
|
|
return 4; |
|
|
|
} |
|
|
|
|
|
|
|
throw new NotSupportedException(); |
|
|
|
switch (componentOrder) |
|
|
|
{ |
|
|
|
case ComponentOrder.ZYX: |
|
|
|
case ComponentOrder.XYZ: |
|
|
|
return 3; |
|
|
|
case ComponentOrder.ZYXW: |
|
|
|
case ComponentOrder.XYZW: |
|
|
|
return 4; |
|
|
|
} |
|
|
|
|
|
|
|
throw new NotSupportedException(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|