diff --git a/src/ImageProcessorCore/Image/ImageBase.cs b/src/ImageProcessorCore/Image/ImageBase.cs index 4d17d9f01..d6333de10 100644 --- a/src/ImageProcessorCore/Image/ImageBase.cs +++ b/src/ImageProcessorCore/Image/ImageBase.cs @@ -3,6 +3,7 @@ // Licensed under the Apache License, Version 2.0. // + namespace ImageProcessorCore { using System; @@ -15,10 +16,15 @@ namespace ImageProcessorCore /// The pixel format. /// The packed format. uint, long, float. [DebuggerDisplay("Image: {Width}x{Height}")] - public abstract class ImageBase : IImageBase + public abstract unsafe class ImageBase : IImageBase where TColor : IPackedVector where TPacked : struct { + /// + /// The image pixels + /// + private TColor[] pixelBuffer; + /// /// Initializes a new instance of the class. /// @@ -41,7 +47,7 @@ namespace ImageProcessorCore this.Width = width; this.Height = height; - this.Pixels = new TColor[width * height]; + this.pixelBuffer = new TColor[width * height]; } /// @@ -61,9 +67,13 @@ namespace ImageProcessorCore this.Height = other.Height; this.CopyProperties(other); - // Copy the pixels. Don't use Unsafe.Copy as it is breaking edge detection. - this.Pixels = new TColor[this.Width * this.Height]; - Array.Copy(other.Pixels, this.Pixels, other.Pixels.Length); + // Copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here. + this.pixelBuffer = new TColor[this.Width * this.Height]; + using (PixelAccessor sourcePixels = other.Lock()) + using (PixelAccessor target = this.Lock()) + { + sourcePixels.CopyImage(target); + } } /// @@ -73,7 +83,8 @@ namespace ImageProcessorCore public int MaxHeight { get; set; } = int.MaxValue; /// - public TColor[] Pixels { get; private set; } + //public TColor[] Pixels { get; private set; } + public TColor[] Pixels => this.pixelBuffer; /// public int Width { get; private set; } @@ -106,7 +117,7 @@ namespace ImageProcessorCore this.Width = width; this.Height = height; - this.Pixels = pixels; + this.pixelBuffer = pixels; } /// @@ -123,9 +134,9 @@ namespace ImageProcessorCore this.Width = width; this.Height = height; - // Copy the pixels. Don't use Unsafe.Copy as it is breaking edge detection. - this.Pixels = new TColor[pixels.Length]; - Array.Copy(pixels, this.Pixels, pixels.Length); + // Copy the pixels. TODO: use Unsafe.Copy. + this.pixelBuffer = new TColor[pixels.Length]; + Array.Copy(pixels, this.pixelBuffer, pixels.Length); } /// diff --git a/src/ImageProcessorCore/Image/PixelAccessor.cs b/src/ImageProcessorCore/Image/PixelAccessor.cs index 8ebfb355e..7561150a5 100644 --- a/src/ImageProcessorCore/Image/PixelAccessor.cs +++ b/src/ImageProcessorCore/Image/PixelAccessor.cs @@ -77,7 +77,7 @@ namespace ImageProcessorCore public IntPtr DataPointer => this.dataPointer; /// - /// Gets the width of one row in the number of bytes. + /// Gets the size of a single pixel in the number of bytes. /// public int PixelSize { get; } @@ -106,19 +106,18 @@ namespace ImageProcessorCore { 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. + /// Copies a block of pixels at the specified position. /// - /// The x-coordinate of the source row. - /// The y-coordinate of the source row. + /// The x-coordinate of the source image. + /// The y-coordinate of the source image. /// The target pixel buffer accessor. - /// The x-coordinate of the target row. - /// The y-coordinate of the target row. + /// The x-coordinate of the target image. + /// The y-coordinate of the target image. /// The number of pixels to copy - public void CopyRow(int sourceX, int sourceY, PixelAccessor target, int targetX, int targetY, int pixelCount) + public void CopyBlock(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; @@ -128,6 +127,15 @@ namespace ImageProcessorCore Unsafe.CopyBlock(targetPtr, sourcePtr, byteCount); } + /// + /// Copies an entire image. + /// + /// The target pixel buffer accessor. + public void CopyImage(PixelAccessor target) + { + this.CopyBlock(0, 0, target, 0, 0, target.Width * target.Height); + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs b/tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs index df0706bc6..3e426f747 100644 --- a/tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs +++ b/tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs @@ -47,7 +47,7 @@ Bootstrapper.Instance.ParallelOptions, y => { - sourcePixels.CopyRow(0, y, targetPixels, 0, y, source.Width); + sourcePixels.CopyBlock(0, y, targetPixels, 0, y, source.Width); }); return targetPixels[0, 0];