diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 6c390fa84b..7c3e842aa7 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -292,7 +292,7 @@ namespace ImageSharp.Formats int offset = 0; for (int x = 0; x < width; x++) { - short temp = BitConverter.ToInt16(row.Data, offset); + short temp = BitConverter.ToInt16(row.Bytes, offset); byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 8dab460d9d..7c3d18b93f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -160,7 +160,7 @@ namespace ImageSharp.Formats for (int y = pixels.Height - 1; y >= 0; y--) { pixels.CopyTo(row, y); - writer.Write(row.Data); + writer.Write(row.Bytes); } } } @@ -181,7 +181,7 @@ namespace ImageSharp.Formats for (int y = pixels.Height - 1; y >= 0; y--) { pixels.CopyTo(row, y); - writer.Write(row.Data); + writer.Write(row.Bytes); } } } diff --git a/src/ImageSharp/Image/PixelAccessor.cs b/src/ImageSharp/Image/PixelAccessor.cs index eec9b8cb27..fec50b9720 100644 --- a/src/ImageSharp/Image/PixelAccessor.cs +++ b/src/ImageSharp/Image/PixelAccessor.cs @@ -24,7 +24,7 @@ namespace ImageSharp private IntPtr dataPointer; /// - /// The position of the first pixel in the bitmap. + /// The position of the first pixel in the image. /// private byte* pixelsBase; @@ -231,7 +231,7 @@ namespace ImageSharp /// The width. protected virtual void CopyFromZYX(PixelRow row, int targetY, int width) { - byte* source = row.DataPointer; + byte* source = row.PixelBase; byte* destination = this.GetRowPointer(targetY); TColor packed = default(TColor); @@ -255,7 +255,7 @@ namespace ImageSharp /// The width. protected virtual void CopyFromZYXW(PixelRow row, int targetY, int width) { - byte* source = row.DataPointer; + byte* source = row.PixelBase; byte* destination = this.GetRowPointer(targetY); TColor packed = default(TColor); @@ -279,7 +279,7 @@ namespace ImageSharp /// The width. protected virtual void CopyFromXYZ(PixelRow row, int targetY, int width) { - byte* source = row.DataPointer; + byte* source = row.PixelBase; byte* destination = this.GetRowPointer(targetY); TColor packed = default(TColor); @@ -303,7 +303,7 @@ namespace ImageSharp /// The width. protected virtual void CopyFromXYZW(PixelRow row, int targetY, int width) { - byte* source = row.DataPointer; + byte* source = row.PixelBase; byte* destination = this.GetRowPointer(targetY); TColor packed = default(TColor); @@ -330,7 +330,7 @@ namespace ImageSharp int offset = 0; for (int x = 0; x < width; x++) { - this[x, sourceY].ToBytes(row.Data, offset, ComponentOrder.ZYX); + this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.ZYX); offset += 3; } } @@ -346,7 +346,7 @@ namespace ImageSharp int offset = 0; for (int x = 0; x < width; x++) { - this[x, sourceY].ToBytes(row.Data, offset, ComponentOrder.ZYXW); + this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.ZYXW); offset += 4; } } @@ -362,7 +362,7 @@ namespace ImageSharp int offset = 0; for (int x = 0; x < width; x++) { - this[x, sourceY].ToBytes(row.Data, offset, ComponentOrder.XYZ); + this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.XYZ); offset += 3; } } @@ -378,7 +378,7 @@ namespace ImageSharp int offset = 0; for (int x = 0; x < width; x++) { - this[x, sourceY].ToBytes(row.Data, offset, ComponentOrder.XYZW); + this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.XYZW); offset += 4; } } diff --git a/src/ImageSharp/Image/PixelRow.cs b/src/ImageSharp/Image/PixelRow.cs index fd5e14d4e5..6ad25dbed0 100644 --- a/src/ImageSharp/Image/PixelRow.cs +++ b/src/ImageSharp/Image/PixelRow.cs @@ -9,77 +9,171 @@ namespace ImageSharp using System.IO; using System.Runtime.InteropServices; - public unsafe sealed class PixelRow : IDisposable + /// + /// Represents a row of generic pixels. + /// + /// The pixel format. + /// The packed format. uint, long, float. + public sealed unsafe class PixelRow : IDisposable where TColor : struct, IPackedPixel where TPacked : struct { - private readonly GCHandle handle; - + /// + /// Provides a way to access the pixels from unmanaged memory. + /// + private readonly GCHandle pixelsHandle; + + /// + /// The pointer to the pixel buffer. + /// + private IntPtr dataPointer; + + /// + /// 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 width. + /// The component order. public PixelRow(int width, ComponentOrder componentOrder) : this(width, componentOrder, 0) { } + /// + /// Initializes a new instance of the class. + /// + /// The width. + /// The component order. + /// The number of bytes to pad each row. 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(); } + /// + /// Finalizes an instance of the class. + /// ~PixelRow() { this.Dispose(); } - public byte[] Data { get; } + /// + /// Gets the data in bytes. + /// + public byte[] Bytes { get; } + + /// + /// Gets the pointer to the pixel buffer. + /// + public IntPtr DataPointer => this.dataPointer; - public byte* DataPointer { get; private set; } + /// + /// Gets the data pointer. + /// + public byte* PixelBase { get; private set; } + /// + /// Gets the component order. + /// public ComponentOrder ComponentOrder { get; } + /// + /// Gets the width. + /// public int Width { get; } + /// + /// Reads the stream to the row. + /// + /// The stream. public void Read(Stream stream) { - stream.Read(this.Data, 0, this.Data.Length); + stream.Read(this.Bytes, 0, this.Bytes.Length); } + /// + /// Writes the row to the stream. + /// + /// The stream. public void Write(Stream stream) { - stream.Write(this.Data, 0, this.Data.Length); + stream.Write(this.Bytes, 0, this.Bytes.Length); } + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// 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); } + /// + /// Gets component count for the given order. + /// + /// The component order. + /// + /// The . + /// + /// + /// Thrown if an invalid order is given. + /// 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(); } } } diff --git a/src/ImageSharp/PixelAccessor.cs b/src/ImageSharp/PixelAccessor.cs index 88c781832a..ca49255d8c 100644 --- a/src/ImageSharp/PixelAccessor.cs +++ b/src/ImageSharp/PixelAccessor.cs @@ -25,7 +25,7 @@ namespace ImageSharp /// protected override void CopyFromZYX(PixelRow row, int targetY, int width) { - byte* source = row.DataPointer; + byte* source = row.PixelBase; byte* destination = this.GetRowPointer(targetY); for (int x = 0; x < width; x++) @@ -40,7 +40,7 @@ namespace ImageSharp /// protected override void CopyFromZYXW(PixelRow row, int targetY, int width) { - byte* source = row.DataPointer; + byte* source = row.PixelBase; byte* destination = this.GetRowPointer(targetY); for (int x = 0; x < width; x++) @@ -56,7 +56,7 @@ namespace ImageSharp protected override void CopyToZYX(PixelRow row, int sourceY, int width) { byte* source = this.GetRowPointer(sourceY); - byte* destination = row.DataPointer; + byte* destination = row.PixelBase; for (int x = 0; x < width; x++) { @@ -83,7 +83,7 @@ namespace ImageSharp protected override void CopyToZYXW(PixelRow row, int sourceY, int width) { byte* source = this.GetRowPointer(sourceY); - byte* destination = row.DataPointer; + byte* destination = row.PixelBase; for (int x = 0; x < width; x++) {