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