mirror of https://github.com/SixLabors/ImageSharp
9 changed files with 125 additions and 833 deletions
@ -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}."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue