//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
namespace ImageProcessorCore
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
///
/// Provides per-pixel access to generic pixels.
///
/// The pixel format.
/// The packed format. uint, long, float.
public unsafe class PixelAccessor : IDisposable
where TColor : IPackedVector
where TPacked : struct
{
///
/// The pointer to the pixel buffer.
///
private IntPtr dataPointer;
///
/// The position of the first pixel in the bitmap.
///
private byte* 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 PixelAccessor(ImageBase image)
{
Guard.NotNull(image, nameof(image));
Guard.MustBeGreaterThan(image.Width, 0, "image width");
Guard.MustBeGreaterThan(image.Height, 0, "image height");
this.Width = image.Width;
this.Height = image.Height;
this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned);
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.pixelsBase = (byte*)this.dataPointer.ToPointer();
this.PixelSize = Unsafe.SizeOf();
this.RowStride = this.Width * this.PixelSize;
}
///
/// Finalizes an instance of the class.
///
~PixelAccessor()
{
this.Dispose();
}
///
/// Gets the pointer to the pixel buffer.
///
public IntPtr DataPointer => this.dataPointer;
///
/// Gets the width of one row in the number of bytes.
///
public int PixelSize { get; }
///
/// Gets the width of one row in the number of bytes.
///
public int RowStride { get; }
///
/// Gets the width of the image.
///
public int Width { get; }
///
/// Gets the height of the image.
///
public int Height { get; }
///
/// Gets or sets the 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 TColor this[int x, int y]
{
get { return Unsafe.Read(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf()); }
set { Unsafe.Write(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf(), value); }
}
///
/// Copies an entire row of pixels.
///
/// The x-coordinate of the source row.
/// The y-coordinate of the source row.
/// The target pixel buffer accessor.
/// The x-coordinate of the target row.
/// The y-coordinate of the target row.
/// The number of pixels to copy
public void CopyRow(int sourceX, int sourceY, PixelAccessor target, int targetX, int targetY, int pixelCount)
{
int size = Unsafe.SizeOf();
byte* sourcePtr = this.pixelsBase + (sourceY * this.Width + sourceX) * size;
byte* targetPtr = target.pixelsBase + (targetY * target.Width + targetX) * size;
uint byteCount = (uint)(pixelCount * size);
Unsafe.CopyBlock(targetPtr, sourcePtr, byteCount);
}
///
/// 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.dataPointer = IntPtr.Zero;
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);
}
}
}