Browse Source

removed PixelArea<T>!

pull/475/head
Anton Firszov 8 years ago
parent
commit
d1872c5d0c
  1. 20
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 24
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  3. 47
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  4. 42
      src/ImageSharp/Image/ImageFrame{TPixel}.cs
  5. 303
      src/ImageSharp/Image/PixelAccessor{TPixel}.cs
  6. 212
      src/ImageSharp/Image/PixelArea{TPixel}.cs
  7. 31
      src/ImageSharp/Memory/BufferArea{T}.cs
  8. 246
      tests/ImageSharp.Tests/Image/PixelAccessorTests.cs
  9. 33
      tests/ImageSharp.Tests/Memory/BufferAreaTests.cs

20
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -446,17 +446,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
PixelOperations<TPixel>.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width);
}
}
//using (var row = new PixelArea<TPixel>(width, ComponentOrder.Zyx, padding))
//{
// for (int y = 0; y < height; y++)
// {
// row.Read(this.currentStream);
// int newY = Invert(y, height, inverted);
// pixels.CopyFrom(row, newY);
// }
//}
}
/// <summary>
@ -471,14 +460,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
using (var row = new PixelArea<TPixel>(width, ComponentOrder.Zyxw, padding))
using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 4, padding))
{
for (int y = 0; y < height; y++)
{
row.Read(this.currentStream);
this.currentStream.Read(row);
int newY = Invert(y, height, inverted);
pixels.CopyFrom(row, newY);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgra32Bytes(row.Span, pixelSpan, width);
}
}
}

24
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -151,6 +151,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel)
{
return this.memoryManager.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding);
}
/// <summary>
/// Writes the 32bit color palette to the stream.
/// </summary>
@ -160,12 +165,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write32Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (PixelArea<TPixel> row = new PixelArea<TPixel>(pixels.Width, ComponentOrder.Zyxw, this.padding))
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
pixels.CopyTo(row, y);
writer.Write(row.Bytes, 0, row.Length);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(pixelSpan, row.Span, pixelSpan.Length);
writer.Write(row.Array, 0, row.Length());
}
}
}
@ -179,8 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write24Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row =
this.memoryManager.AllocatePaddedPixelRowBuffer(pixels.Width, 3, this.padding))
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
@ -189,15 +194,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
writer.Write(row.Array, 0, row.Length());
}
}
//using (PixelArea<TPixel> row = new PixelArea<TPixel>(pixels.Width, ComponentOrder.Zyx, this.padding))
//{
// for (int y = pixels.Height - 1; y >= 0; y--)
// {
// pixels.CopyTo(row, y);
// writer.Write(row.Bytes, 0, row.Length);
// }
//}
}
}
}

47
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -542,28 +542,31 @@ namespace SixLabors.ImageSharp.Formats.Gif
return;
}
// Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == imageWidth &&
this.restoreArea.Value.Height == imageHeight)
{
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{
pixelAccessor.Reset();
}
}
else
{
using (var emptyRow = new PixelArea<TPixel>(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
{
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{
for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++)
{
pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left);
}
}
}
}
BufferArea<TPixel> pixelArea = frame.PixelBuffer.GetArea(this.restoreArea.Value);
pixelArea.Clear();
//if (this.restoreArea.Value.Width == imageWidth &&
// this.restoreArea.Value.Height == imageHeight)
//{
// using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
// {
// pixelAccessor.Reset();
// }
//}
//else
//{
// using (var emptyRow = new PixelArea<TPixel>(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
// {
// using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
// {
// for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++)
// {
// pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left);
// }
// }
// }
//}
this.restoreArea = null;
}

42
src/ImageSharp/Image/ImageFrame{TPixel}.cs

@ -21,11 +21,6 @@ namespace SixLabors.ImageSharp
public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The image pixels. Not private as Buffer2D requires an array in its constructor.
/// </summary>
private Buffer2D<TPixel> pixelBuffer;
private bool isDisposed;
/// <summary>
@ -54,7 +49,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(metaData, nameof(metaData));
this.MemoryManager = memoryManager;
this.pixelBuffer = memoryManager.AllocateClean2D<TPixel>(width, height);
this.PixelBuffer = memoryManager.AllocateClean2D<TPixel>(width, height);
this.MetaData = metaData;
}
@ -77,8 +72,8 @@ namespace SixLabors.ImageSharp
internal ImageFrame(MemoryManager memoryManager, ImageFrame<TPixel> source)
{
this.MemoryManager = memoryManager;
this.pixelBuffer = memoryManager.Allocate2D<TPixel>(source.pixelBuffer.Width, source.pixelBuffer.Height);
source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span);
this.PixelBuffer = memoryManager.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height);
source.PixelBuffer.Span.CopyTo(this.PixelBuffer.Span);
this.MetaData = source.MetaData.Clone();
}
@ -87,18 +82,23 @@ namespace SixLabors.ImageSharp
/// </summary>
public MemoryManager MemoryManager { get; }
/// <summary>
/// Gets the image pixels. Not private as Buffer2D requires an array in its constructor.
/// </summary>
internal Buffer2D<TPixel> PixelBuffer { get; private set; }
/// <inheritdoc/>
Buffer2D<TPixel> IPixelSource<TPixel>.PixelBuffer => this.pixelBuffer;
Buffer2D<TPixel> IPixelSource<TPixel>.PixelBuffer => this.PixelBuffer;
/// <summary>
/// Gets the width.
/// </summary>
public int Width => this.pixelBuffer.Width;
public int Width => this.PixelBuffer.Width;
/// <summary>
/// Gets the height.
/// </summary>
public int Height => this.pixelBuffer.Height;
public int Height => this.PixelBuffer.Height;
/// <summary>
/// Gets the meta data of the frame.
@ -116,13 +116,13 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.pixelBuffer[x, y];
return this.PixelBuffer[x, y];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.pixelBuffer[x, y] = value;
this.PixelBuffer[x, y] = value;
}
}
@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref TPixel GetPixelReference(int x, int y)
{
return ref this.pixelBuffer[x, y];
return ref this.PixelBuffer[x, y];
}
/// <summary>
@ -168,8 +168,8 @@ namespace SixLabors.ImageSharp
Guard.NotNull(pixelSource, nameof(pixelSource));
// Push my memory into the accessor (which in turn unpins the old buffer ready for the images use)
Buffer2D<TPixel> newPixels = pixelSource.SwapBufferOwnership(this.pixelBuffer);
this.pixelBuffer = newPixels;
Buffer2D<TPixel> newPixels = pixelSource.SwapBufferOwnership(this.PixelBuffer);
this.PixelBuffer = newPixels;
}
/// <summary>
@ -180,9 +180,9 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(pixelSource, nameof(pixelSource));
Buffer2D<TPixel> temp = this.pixelBuffer;
this.pixelBuffer = pixelSource.pixelBuffer;
pixelSource.pixelBuffer = temp;
Buffer2D<TPixel> temp = this.PixelBuffer;
this.PixelBuffer = pixelSource.PixelBuffer;
pixelSource.PixelBuffer = temp;
}
/// <summary>
@ -195,8 +195,8 @@ namespace SixLabors.ImageSharp
return;
}
this.pixelBuffer?.Dispose();
this.pixelBuffer = null;
this.PixelBuffer?.Dispose();
this.PixelBuffer = null;
// Note disposing is done.
this.isDisposed = true;

303
src/ImageSharp/Image/PixelAccessor{TPixel}.cs

@ -158,65 +158,6 @@ namespace SixLabors.ImageSharp
this.PixelBuffer.Buffer.Clear();
}
/// <summary>
/// Copy an area of pixels to the image.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
internal void CopyFrom(PixelArea<TPixel> area, int targetY, int targetX = 0)
{
this.CheckCoordinates(area, targetX, targetY);
this.CopyFrom(area, targetX, targetY, area.Width, area.Height);
}
/// <summary>
/// Copy pixels from the image to an area of pixels.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
internal void CopyTo(PixelArea<TPixel> area, int sourceY, int sourceX = 0)
{
this.CheckCoordinates(area, sourceX, sourceY);
this.CopyTo(area, sourceX, sourceY, area.Width, area.Height);
}
/// <summary>
/// Copy pixels from the image to an area of pixels. This method will make sure that the pixels
/// that are copied are within the bounds of the image.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
internal void SafeCopyTo(PixelArea<TPixel> area, int sourceY, int sourceX = 0)
{
int width = Math.Min(area.Width, this.Width - sourceX);
if (width < 1)
{
return;
}
int height = Math.Min(area.Height, this.Height - sourceY);
if (height < 1)
{
return;
}
this.CopyTo(area, sourceX, sourceY, width, height);
}
/// <summary>
/// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!!
/// </summary>
@ -239,161 +180,6 @@ namespace SixLabors.ImageSharp
SpanHelper.Copy(this.PixelBuffer.Span, target.PixelBuffer.Span);
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.Zyx"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyFromZyx(PixelArea<TPixel> area, int targetX, int targetY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromBgr24Bytes(source, destination, width);
}
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.Zyxw"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyFromZyxw(PixelArea<TPixel> area, int targetX, int targetY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromBgra32Bytes(source, destination, width);
}
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.Xyz"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyFromXyz(PixelArea<TPixel> area, int targetX, int targetY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromRgb24Bytes(source, destination, width);
}
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.Xyzw"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyFromXyzw(PixelArea<TPixel> area, int targetX, int targetY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromRgba32Bytes(source, destination, width);
}
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.Zyx"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyToZyx(PixelArea<TPixel> area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToBgr24Bytes(source, destination, width);
}
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.Zyxw"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyToZyxw(PixelArea<TPixel> area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToBgra32Bytes(source, destination, width);
}
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.Xyz"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyToXyz(PixelArea<TPixel> area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToRgb24Bytes(source, destination, width);
}
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.Xyzw"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CopyToXyzw(PixelArea<TPixel> area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToRgba32Bytes(source, destination, width);
}
}
/// <summary>
/// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!!
/// </summary>
@ -410,95 +196,6 @@ namespace SixLabors.ImageSharp
this.RowStride = this.Width * this.PixelSize;
}
/// <summary>
/// Copy an area of pixels to the image.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width of the area to copy.</param>
/// <param name="height">The height of the area to copy.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
private void CopyFrom(PixelArea<TPixel> area, int targetX, int targetY, int width, int height)
{
switch (area.ComponentOrder)
{
case ComponentOrder.Zyx:
this.CopyFromZyx(area, targetX, targetY, width, height);
break;
case ComponentOrder.Zyxw:
this.CopyFromZyxw(area, targetX, targetY, width, height);
break;
case ComponentOrder.Xyz:
this.CopyFromXyz(area, targetX, targetY, width, height);
break;
case ComponentOrder.Xyzw:
this.CopyFromXyzw(area, targetX, targetY, width, height);
break;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Copy pixels from the image to an area of pixels.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width of the area to copy.</param>
/// <param name="height">The height of the area to copy.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
private void CopyTo(PixelArea<TPixel> area, int sourceX, int sourceY, int width, int height)
{
switch (area.ComponentOrder)
{
case ComponentOrder.Zyx:
this.CopyToZyx(area, sourceX, sourceY, width, height);
break;
case ComponentOrder.Zyxw:
this.CopyToZyxw(area, sourceX, sourceY, width, height);
break;
case ComponentOrder.Xyz:
this.CopyToXyz(area, sourceX, sourceY, width, height);
break;
case ComponentOrder.Xyzw:
this.CopyToXyzw(area, sourceX, sourceY, width, height);
break;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Checks that the given area and offset are within the bounds of the image.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="x">The x-coordinate of the pixel. Must be greater than zero and less than the width of the image.</param>
/// <param name="y">The y-coordinate of the pixel. Must be greater than zero and less than the height of the image.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the dimensions are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckCoordinates(PixelArea<TPixel> area, int x, int y)
{
int width = Math.Min(area.Width, this.Width - x);
if (width < 1)
{
throw new ArgumentOutOfRangeException(nameof(width), width, "Invalid area size specified.");
}
int height = Math.Min(area.Height, this.Height - y);
if (height < 1)
{
throw new ArgumentOutOfRangeException(nameof(height), height, "Invalid area size specified.");
}
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>

212
src/ImageSharp/Image/PixelArea{TPixel}.cs

@ -1,212 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Represents an area of generic <see cref="Image{TPixel}"/> pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal sealed class PixelArea<TPixel> : IDisposable
where TPixel : struct, IPixel<TPixel>
{
/// <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>
/// The underlying buffer containing the raw pixel data.
/// </summary>
private readonly IManagedByteBuffer byteBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TPixel}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="componentOrder">The component order.</param>
public PixelArea(int width, ComponentOrder componentOrder)
: this(width, 1, componentOrder, 0)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TPixel}"/> 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 PixelArea(int width, ComponentOrder componentOrder, int padding)
: this(width, 1, componentOrder, padding)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TPixel}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param>
public PixelArea(int width, int height, ComponentOrder componentOrder)
: this(width, height, componentOrder, 0)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TPixel}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param>
/// <param name="padding">The number of bytes to pad each row.</param>
public PixelArea(int width, int height, ComponentOrder componentOrder, int padding)
{
this.Width = width;
this.Height = height;
this.ComponentOrder = componentOrder;
this.RowStride = (width * GetComponentCount(componentOrder)) + padding;
this.Length = this.RowStride * height;
this.byteBuffer = Configuration.Default.MemoryManager.AllocateCleanManagedByteBuffer(this.Length);
}
/// <summary>
/// Gets the data in bytes.
/// </summary>
public byte[] Bytes => this.byteBuffer.Array;
/// <summary>
/// Gets the length of the buffer.
/// </summary>
public int Length { get; }
/// <summary>
/// Gets the component order.
/// </summary>
public ComponentOrder ComponentOrder { get; }
/// <summary>
/// Gets the height.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets the width of one row in the number of bytes.
/// </summary>
public int RowStride { get; }
/// <summary>
/// Gets the width.
/// </summary>
public int Width { get; }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (this.isDisposed)
{
return;
}
this.byteBuffer.Dispose();
this.isDisposed = true;
}
/// <summary>
/// Reads the stream to the area.
/// </summary>
/// <param name="stream">The stream.</param>
public void Read(Stream stream)
{
stream.Read(this.Bytes, 0, this.Length);
}
/// <summary>
/// Writes the area to the stream.
/// </summary>
/// <param name="stream">The stream.</param>
public void Write(Stream stream)
{
stream.Write(this.Bytes, 0, this.Length);
}
/// <summary>
/// Resets the bytes of the array to it's initial value.
/// </summary>
public void Reset()
{
this.byteBuffer.Clear();
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row y.
/// </summary>
/// <param name="y">The y coordinate</param>
/// <returns>The <see cref="Span{T}"/></returns>
internal Span<byte> GetRowSpan(int y)
{
return this.byteBuffer.Slice(y * this.RowStride);
}
/// <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();
}
/// <summary>
/// Checks that the length of the byte array to ensure that it matches the given width and height.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="bytes">The byte array.</param>
/// <param name="componentOrder">The component order.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the byte array is th incorrect length.
/// </exception>
[Conditional("DEBUG")]
private void CheckBytesLength(int width, int height, byte[] bytes, ComponentOrder componentOrder)
{
int requiredLength = (width * GetComponentCount(componentOrder)) * height;
if (bytes.Length != requiredLength)
{
throw new ArgumentOutOfRangeException(
nameof(bytes),
$"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}.");
}
}
}
}

31
src/ImageSharp/Memory/BufferArea{T}.cs

@ -45,11 +45,26 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
public Size Size => this.Rectangle.Size;
/// <summary>
/// Gets the width
/// </summary>
public int Width => this.Rectangle.Width;
/// <summary>
/// Gets the height
/// </summary>
public int Height => this.Rectangle.Height;
/// <summary>
/// Gets the pixel stride which is equal to the width of <see cref="DestinationBuffer"/>.
/// </summary>
public int Stride => this.DestinationBuffer.Width;
/// <summary>
/// Gets a value indicating whether the area refers to the entire <see cref="DestinationBuffer"/>
/// </summary>
public bool IsFullBufferArea => this.Size == this.DestinationBuffer.Size();
/// <summary>
/// Gets or sets a value at the given index.
/// </summary>
@ -126,5 +141,21 @@ namespace SixLabors.ImageSharp.Memory
{
return (y + this.Rectangle.Y) * this.DestinationBuffer.Width;
}
public void Clear()
{
// Optimization for when the size of the area is the same as the buffer size.
if (this.IsFullBufferArea)
{
this.DestinationBuffer.Span.Clear();
return;
}
for (int y = 0; y < this.Rectangle.Height; y++)
{
Span<T> row = this.GetRowSpan(y);
row.Clear();
}
}
}
}

246
tests/ImageSharp.Tests/Image/PixelAccessorTests.cs

@ -1,246 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
/// <summary>
/// Tests the <see cref="PixelAccessor"/> class.
/// </summary>
public class PixelAccessorTests
{
public static Image<TPixel> CreateTestImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var image = new Image<TPixel>(10, 10);
using (PixelAccessor<TPixel> pixels = image.Lock())
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
var v = new Vector4(i, j, 0, 1);
v /= 10;
var color = default(TPixel);
color.PackFromVector4(v);
pixels[i, j] = color;
}
}
}
return image;
}
[Theory]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyz)]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyx)]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyzw)]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyxw)]
internal void CopyTo_Then_CopyFrom_OnFullImageRect<TPixel>(TestImageProvider<TPixel> provider, ComponentOrder order)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> src = provider.GetImage())
{
using (Image<TPixel> dest = new Image<TPixel>(src.Width, src.Height))
{
using (PixelArea<TPixel> area = new PixelArea<TPixel>(src.Width, src.Height, order))
{
using (PixelAccessor<TPixel> srcPixels = src.Lock())
{
srcPixels.CopyTo(area, 0, 0);
}
using (PixelAccessor<TPixel> destPixels = dest.Lock())
{
destPixels.CopyFrom(area, 0, 0);
}
}
Assert.True(src.IsEquivalentTo(dest, false));
}
}
}
[Theory]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyz)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)]
internal void CopyToThenCopyFromWithOffset<TPixel>(TestImageProvider<TPixel> provider, ComponentOrder order)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> destImage = new Image<TPixel>(8, 8))
{
using (Image<TPixel> srcImage = provider.GetImage())
{
srcImage.Mutate(x => x.Fill(NamedColors<TPixel>.Red, new Rectangle(4, 4, 8, 8)));
using (PixelAccessor<TPixel> srcPixels = srcImage.Lock())
{
using (PixelArea<TPixel> area = new PixelArea<TPixel>(8, 8, order))
{
srcPixels.CopyTo(area, 4, 4);
using (PixelAccessor<TPixel> destPixels = destImage.Lock())
{
destPixels.CopyFrom(area, 0, 0);
}
}
}
}
provider.Utility.SourceFileOrDescription = order.ToString();
provider.Utility.SaveTestOutputFile(destImage, "bmp");
using (Image<TPixel> expectedImage = new Image<TPixel>(8, 8))
{
expectedImage.Mutate(x => x.Fill(NamedColors<TPixel>.Red));
Assert.True(destImage.IsEquivalentTo(expectedImage));
}
}
}
[Fact]
public void CopyFromZYX()
{
using (Image<Rgba32> image = new Image<Rgba32>(1, 1))
{
CopyFromZYXImpl(image);
}
}
[Fact]
public void CopyFromZYXW()
{
using (Image<Rgba32> image = new Image<Rgba32>(1, 1))
{
CopyFromZYXWImpl(image);
}
}
[Fact]
public void CopyToZYX()
{
using (Image<Rgba32> image = new Image<Rgba32>(1, 1))
{
CopyToZYXImpl(image);
}
}
[Fact]
public void CopyToZYXW()
{
using (Image<Rgba32> image = new Image<Rgba32>(1, 1))
{
CopyToZYXWImpl(image);
}
}
private static void CopyFromZYXImpl<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
{
byte red = 1;
byte green = 2;
byte blue = 3;
byte alpha = 255;
using (PixelArea<TPixel> row = new PixelArea<TPixel>(1, ComponentOrder.Zyx))
{
row.Bytes[0] = blue;
row.Bytes[1] = green;
row.Bytes[2] = red;
pixels.CopyFrom(row, 0);
Rgba32 color = (Rgba32)(object)pixels[0, 0];
Assert.Equal(red, color.R);
Assert.Equal(green, color.G);
Assert.Equal(blue, color.B);
Assert.Equal(alpha, color.A);
}
}
}
private static void CopyFromZYXWImpl<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
{
byte red = 1;
byte green = 2;
byte blue = 3;
byte alpha = 4;
using (PixelArea<TPixel> row = new PixelArea<TPixel>(1, ComponentOrder.Zyxw))
{
row.Bytes[0] = blue;
row.Bytes[1] = green;
row.Bytes[2] = red;
row.Bytes[3] = alpha;
pixels.CopyFrom(row, 0);
Rgba32 color = (Rgba32)(object)pixels[0, 0];
Assert.Equal(red, color.R);
Assert.Equal(green, color.G);
Assert.Equal(blue, color.B);
Assert.Equal(alpha, color.A);
}
}
}
private static void CopyToZYXImpl<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
{
byte red = 1;
byte green = 2;
byte blue = 3;
using (PixelArea<TPixel> row = new PixelArea<TPixel>(1, ComponentOrder.Zyx))
{
pixels[0, 0] = (TPixel)(object)new Rgba32(red, green, blue);
pixels.CopyTo(row, 0);
Assert.Equal(blue, row.Bytes[0]);
Assert.Equal(green, row.Bytes[1]);
Assert.Equal(red, row.Bytes[2]);
}
}
}
private static void CopyToZYXWImpl<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
{
byte red = 1;
byte green = 2;
byte blue = 3;
byte alpha = 4;
using (PixelArea<TPixel> row = new PixelArea<TPixel>(1, ComponentOrder.Zyxw))
{
pixels[0, 0] = (TPixel)(object)new Rgba32(red, green, blue, alpha);
pixels.CopyTo(row, 0);
Assert.Equal(blue, row.Bytes[0]);
Assert.Equal(green, row.Bytes[1]);
Assert.Equal(red, row.Bytes[2]);
Assert.Equal(alpha, row.Bytes[3]);
}
}
}
}
}

33
tests/ImageSharp.Tests/Memory/BufferAreaTests.cs

@ -110,5 +110,38 @@ namespace SixLabors.ImageSharp.Tests.Memory
Assert.Equal(expected, r);
}
}
[Fact]
public void Clear_FullArea()
{
using (Buffer2D<int> buffer = CreateTestBuffer(22, 13))
{
buffer.GetArea().Clear();
Span<int> fullSpan = buffer.Span;
Assert.True(fullSpan.SequenceEqual(new int[fullSpan.Length]));
}
}
[Fact]
public void Clear_SubArea()
{
using (Buffer2D<int> buffer = CreateTestBuffer(20, 30))
{
BufferArea<int> area = buffer.GetArea(5, 5, 10, 10);
area.Clear();
Assert.NotEqual(0, buffer[4, 4]);
Assert.NotEqual(0, buffer[15, 15]);
Assert.Equal(0, buffer[5, 5]);
Assert.Equal(0, buffer[14, 14]);
for (int y = area.Rectangle.Y; y < area.Rectangle.Bottom; y++)
{
Span<int> span = buffer.GetRowSpan(y).Slice(area.Rectangle.X, area.Width);
Assert.True(span.SequenceEqual(new int[area.Width]));
}
}
}
}
}
Loading…
Cancel
Save