@ -6,13 +6,39 @@
namespace ImageProcessorCore
{
using System ;
using System.Runtime.InteropServices ;
/// <summary>
/// The base class of all images. Encapsulates the basic properties and methods
/// required to manipulate images.
/// </summary>
public abstract class ImageBase : IImageBase
public abstract unsafe class ImageBase : IImageBase , IDisposable
{
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private float * pixelsBase ;
/// <summary>
/// The array of pixels.
/// </summary>
private float [ ] pixelsArray ;
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>
protected bool IsDisposed ;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase"/> class.
/// </summary>
@ -23,12 +49,8 @@ namespace ImageProcessorCore
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase"/> class.
/// </summary>
/// <param name="width">
/// The width of the image in pixels.
/// </param>
/// <param name="height">
/// The height of the image in pixels.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
@ -40,7 +62,10 @@ namespace ImageProcessorCore
this . Width = width ;
this . Height = height ;
this . Pixels = new float [ width * height * 4 ] ;
// Assign the pointer and pixels.
this . pixelsArray = new float [ width * height * 4 ] ;
this . pixelsHandle = GCHandle . Alloc ( this . pixelsArray , GCHandleType . Pinned ) ;
this . pixelsBase = ( float * ) this . pixelsHandle . AddrOfPinnedObject ( ) . ToPointer ( ) ;
}
/// <summary>
@ -56,14 +81,22 @@ namespace ImageProcessorCore
{
Guard . NotNull ( other , nameof ( other ) , "Other image cannot be null." ) ;
float [ ] pixels = other . Pixels ;
this . Width = other . Width ;
this . Height = other . Height ;
this . Quality = other . Quality ;
this . FrameDelay = other . FrameDelay ;
this . Pixels = new float [ pixels . Length ] ;
Array . Copy ( pixels , this . Pixels , pixels . Length ) ;
// Assign the pointer and copy the pixels.
this . pixelsArray = new float [ this . Width * this . Height * 4 ] ;
this . pixelsHandle = GCHandle . Alloc ( this . pixelsArray , GCHandleType . Pinned ) ;
this . pixelsBase = ( float * ) this . pixelsHandle . AddrOfPinnedObject ( ) . ToPointer ( ) ;
Array . Copy ( other . pixelsArray , this . pixelsArray , other . pixelsArray . Length ) ;
}
/// <inheritdoc/>
~ ImageBase ( )
{
this . Dispose ( false ) ;
}
/// <summary>
@ -76,45 +109,25 @@ namespace ImageProcessorCore
/// </summary>
public static int MaxHeight { get ; set ; } = int . MaxValue ;
/// <summary>
/// Gets the image pixels as byte array.
/// </summary>
/// <remarks>
/// The returned array has a length of Width * Height * 4 bytes
/// and stores the blue, the green, the red and the alpha value for
/// each pixel in this order.
/// </remarks>
public float [ ] Pixels { get ; private set ; }
/// <inheritdoc/>
public float [ ] Pixels = > this . pixelsArray ;
/// <summary>
/// Gets the width in pixels.
/// </summary>
/// <inheritdoc/>
public int Width { get ; private set ; }
/// <summary>
/// Gets the height in pixels.
/// </summary>
/// <inheritdoc/>
public int Height { get ; private set ; }
/// <summary>
/// Gets the pixel ratio made up of the width and height.
/// </summary>
/// <inheritdoc/>
public double PixelRatio = > ( double ) this . Width / this . Height ;
/// <summary>
/// Gets the <see cref="Rectangle"/> representing the bounds of the image.
/// </summary>
/// <inheritdoc/>
public Rectangle Bounds = > new Rectangle ( 0 , 0 , this . Width , this . Height ) ;
/// <inheritdoc/>
public int Quality { get ; set ; }
/// <summary>
/// Gets or sets the frame delay for animated images.
/// If not 0, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered.
/// </summary>
/// <inheritdoc/>
public int FrameDelay { get ; set ; }
/// <inheritdoc/>
@ -133,9 +146,7 @@ namespace ImageProcessorCore
throw new ArgumentOutOfRangeException ( nameof ( y ) , "Value cannot be less than zero or greater than the bitmap height." ) ;
}
#endif
int start = ( ( y * this . Width ) + x ) * 4 ;
return new Color ( this . Pixels [ start ] , this . Pixels [ start + 1 ] , this . Pixels [ start + 2 ] , this . Pixels [ start + 3 ] ) ;
return * ( ( Color * ) ( this . pixelsBase + ( ( y * this . Width ) + x ) * 4 ) ) ;
}
set
@ -151,12 +162,7 @@ namespace ImageProcessorCore
throw new ArgumentOutOfRangeException ( nameof ( y ) , "Value cannot be less than zero or greater than the bitmap height." ) ;
}
#endif
int start = ( ( y * this . Width ) + x ) * 4 ;
this . Pixels [ start + 0 ] = value . R ;
this . Pixels [ start + 1 ] = value . G ;
this . Pixels [ start + 2 ] = value . B ;
this . Pixels [ start + 3 ] = value . A ;
* ( Color * ) ( this . pixelsBase + ( ( ( y * this . Width ) + x ) * 4 ) ) = value ;
}
}
@ -181,7 +187,10 @@ namespace ImageProcessorCore
#endif
this . Width = width ;
this . Height = height ;
this . Pixels = pixels ;
this . pixelsArray = pixels ;
this . pixelsHandle = GCHandle . Alloc ( this . pixelsArray , GCHandleType . Pinned ) ;
this . pixelsBase = ( float * ) this . pixelsHandle . AddrOfPinnedObject ( ) . ToPointer ( ) ;
}
/// <inheritdoc/>
@ -205,9 +214,52 @@ namespace ImageProcessorCore
#endif
this . Width = width ;
this . Height = height ;
float [ ] clonedPixels = new float [ pixels . Length ] ;
Array . Copy ( pixels , clonedPixels , pixels . Length ) ;
this . Pixels = clonedPixels ;
// Assign the pointer and copy the pixels.
this . pixelsArray = new float [ pixels . Length ] ;
this . pixelsHandle = GCHandle . Alloc ( this . pixelsArray , GCHandleType . Pinned ) ;
this . pixelsBase = ( float * ) this . pixelsHandle . AddrOfPinnedObject ( ) . ToPointer ( ) ;
Array . Copy ( pixels , this . pixelsArray , pixels . Length ) ;
}
/// <inheritdoc/>
public void Dispose ( )
{
this . Dispose ( 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>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
protected virtual void Dispose ( bool disposing )
{
if ( this . IsDisposed )
{
return ;
}
if ( disposing )
{
// Dispose of any managed resources here.
this . pixelsArray = null ;
}
if ( this . pixelsHandle . IsAllocated )
{
this . pixelsHandle . Free ( ) ;
this . pixelsBase = null ;
}
// Note disposing is done.
this . IsDisposed = true ;
}
}
}