Browse Source

Merge pull request #617 from SixLabors/af/memory-bridge2

Get rid of PixelAccessor<T>
pull/625/head
Anton Firsov 8 years ago
committed by GitHub
parent
commit
097c150440
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 87
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 26
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  3. 12
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  4. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  5. 31
      src/ImageSharp/ImageExtensions.Internal.cs
  6. 21
      src/ImageSharp/ImageFrame{TPixel}.cs
  7. 28
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  8. 10
      src/ImageSharp/Memory/Buffer2D{T}.cs
  9. 8
      src/ImageSharp/Memory/BufferArea{T}.cs
  10. 31
      src/ImageSharp/Memory/IBuffer2D{T}.cs
  11. 48
      src/ImageSharp/PixelAccessorExtensions.cs
  12. 151
      src/ImageSharp/PixelAccessor{TPixel}.cs
  13. 35
      src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorCompassProcessor.cs
  14. 46
      tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs
  15. 27
      tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs
  16. 4
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  17. 86
      tests/ImageSharp.Tests/Drawing/BeziersTests.cs
  18. 50
      tests/ImageSharp.Tests/Drawing/DrawPathTests.cs
  19. 41
      tests/ImageSharp.Tests/Drawing/FillPatternTests.cs
  20. 129
      tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs
  21. 106
      tests/ImageSharp.Tests/Drawing/LineTests.cs
  22. 69
      tests/ImageSharp.Tests/Drawing/PolygonTests.cs
  23. 47
      tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs
  24. 88
      tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
  25. 20
      tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs
  26. 23
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
  27. 47
      tests/ImageSharp.Tests/TestUtilities/TestUtils.cs
  28. 22
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
  29. 19
      tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs

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

@ -103,36 +103,42 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor<TPixel> pixels = image.Lock())
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
switch (this.infoHeader.Compression)
{
switch (this.infoHeader.Compression)
{
case BmpCompression.RGB:
if (this.infoHeader.BitsPerPixel == 32)
{
this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 24)
{
this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel <= 8)
{
this.ReadRgbPalette(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, inverted);
}
case BmpCompression.RGB:
if (this.infoHeader.BitsPerPixel == 32)
{
this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 24)
{
this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel <= 8)
{
this.ReadRgbPalette(
pixels,
palette,
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel,
inverted);
}
break;
case BmpCompression.RLE8:
this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
case BmpCompression.RLE8:
this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
default:
throw new NotSupportedException("Does not support this kind of bitmap files.");
}
break;
default:
throw new NotSupportedException("Does not support this kind of bitmap files.");
}
return image;
@ -202,12 +208,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Compresssed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRle8<TPixel>(PixelAccessor<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
private void ReadRle8<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
@ -313,13 +319,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="bits">The number of bits per pixel.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgbPalette<TPixel>(PixelAccessor<TPixel> pixels, byte[] colors, int width, int height, int bits, bool inverted)
private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bits, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
// Pixels per byte (bits per pixel)
@ -377,11 +383,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the 16 bit color palette from the stream
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb16<TPixel>(PixelAccessor<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb16<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 2);
@ -418,11 +424,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the 24 bit color palette from the stream
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb24<TPixel>(PixelAccessor<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 3);
@ -443,11 +449,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the 32 bit color palette from the stream
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32<TPixel>(PixelAccessor<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb32<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
@ -574,12 +580,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
$"The input bmp '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
this.infoHeader.VerifyDimensions();
}
}
}

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

@ -92,18 +92,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void WriteImage<TPixel>(Stream stream, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
Buffer2D<TPixel> pixels = image.PixelBuffer;
switch (this.bitsPerPixel)
{
switch (this.bitsPerPixel)
{
case BmpBitsPerPixel.Pixel32:
this.Write32Bit(stream, pixels);
break;
case BmpBitsPerPixel.Pixel32:
this.Write32Bit(stream, pixels);
break;
case BmpBitsPerPixel.Pixel24:
this.Write24Bit(stream, pixels);
break;
}
case BmpBitsPerPixel.Pixel24:
this.Write24Bit(stream, pixels);
break;
}
}
@ -117,8 +115,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(Stream stream, PixelAccessor<TPixel> pixels)
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
@ -137,8 +135,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(Stream stream, PixelAccessor<TPixel> pixels)
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))

12
src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs

@ -157,5 +157,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp
dest = this;
}
internal void VerifyDimensions()
{
const int MaximumBmpDimension = 65535;
if (this.Width > MaximumBmpDimension || this.Height > MaximumBmpDimension)
{
throw new InvalidOperationException(
$"The input bmp '{this.Width}x{this.Height}' is "
+ $"bigger then the max allowed size '{MaximumBmpDimension}x{MaximumBmpDimension}'");
}
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
/// <param name="componentBuffers">The 1-4 sized list of component buffers.</param>
/// <param name="row">The row to convert</param>
public ComponentValues(IReadOnlyList<IBuffer2D<float>> componentBuffers, int row)
public ComponentValues(IReadOnlyList<Buffer2D<float>> componentBuffers, int row)
{
this.ComponentCount = componentBuffers.Count;

31
src/ImageSharp/ImageExtensions.Internal.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
/// <content>
/// Contains internal extensions for <see cref="Image{TPixel}"/>
/// </content>
public static partial class ImageExtensions
{
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="image">The image.</param>
/// <returns>
/// The <see cref="Buffer2D{TPixel}" />
/// </returns>
internal static Buffer2D<TPixel> GetRootFramePixelBuffer<TPixel>(this Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
return image.Frames.RootFrame.PixelBuffer;
}
}
}

21
src/ImageSharp/ImageFrame{TPixel}.cs

@ -210,27 +210,6 @@ namespace SixLabors.ImageSharp
return ref this.PixelBuffer[x, y];
}
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <returns>The <see cref="PixelAccessor{TPixel}"/></returns>
internal PixelAccessor<TPixel> Lock()
{
return new PixelAccessor<TPixel>(this);
}
/// <summary>
/// Copies the pixels to a <see cref="PixelAccessor{TPixel}"/> of the same size.
/// </summary>
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TPixel> target)
{
this.CopyTo(target.PixelBuffer);
}
/// <summary>
/// Copies the pixels to a <see cref="Buffer2D{TPixel}"/> of the same size.
/// </summary>

28
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -8,14 +8,14 @@ using SixLabors.Primitives;
namespace SixLabors.Memory
{
/// <summary>
/// Defines extension methods for <see cref="IBuffer2D{T}"/>.
/// Defines extension methods for <see cref="Buffer2D{T}"/>.
/// </summary>
internal static class Buffer2DExtensions
{
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing buffer of <paramref name="buffer"/>.
/// </summary>
internal static Span<T> GetSpan<T>(this IBuffer2D<T> buffer)
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
return buffer.Buffer.GetSpan();
@ -30,7 +30,7 @@ namespace SixLabors.Memory
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this IBuffer2D<T> buffer, int x, int y)
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int x, int y)
where T : struct
{
return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x);
@ -44,7 +44,7 @@ namespace SixLabors.Memory
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this IBuffer2D<T> buffer, int y)
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width);
@ -58,7 +58,7 @@ namespace SixLabors.Memory
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory<T> GetRowMemory<T>(this IBuffer2D<T> buffer, int y)
public static Memory<T> GetRowMemory<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return buffer.Buffer.Memory.Slice(y * buffer.Width, buffer.Width);
@ -68,9 +68,9 @@ namespace SixLabors.Memory
/// Returns the size of the buffer.
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="IBuffer2D{T}"/></param>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Size{T}"/> of the buffer</returns>
public static Size Size<T>(this IBuffer2D<T> buffer)
public static Size Size<T>(this Buffer2D<T> buffer)
where T : struct
{
return new Size(buffer.Width, buffer.Height);
@ -80,9 +80,9 @@ namespace SixLabors.Memory
/// Returns a <see cref="Rectangle"/> representing the full area of the buffer.
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="IBuffer2D{T}"/></param>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Rectangle"/></returns>
public static Rectangle FullRectangle<T>(this IBuffer2D<T> buffer)
public static Rectangle FullRectangle<T>(this Buffer2D<T> buffer)
where T : struct
{
return new Rectangle(0, 0, buffer.Width, buffer.Height);
@ -92,13 +92,13 @@ namespace SixLabors.Memory
/// Return a <see cref="BufferArea{T}"/> to the subarea represented by 'rectangle'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="IBuffer2D{T}"/></param>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this IBuffer2D<T> buffer, Rectangle rectangle)
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, Rectangle rectangle)
where T : struct => new BufferArea<T>(buffer, rectangle);
public static BufferArea<T> GetArea<T>(this IBuffer2D<T> buffer, int x, int y, int width, int height)
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
where T : struct
{
var rectangle = new Rectangle(x, y, width, height);
@ -109,9 +109,9 @@ namespace SixLabors.Memory
/// Return a <see cref="BufferArea{T}"/> to the whole area of 'buffer'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="IBuffer2D{T}"/></param>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this IBuffer2D<T> buffer)
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer)
where T : struct => new BufferArea<T>(buffer);
}
}

10
src/ImageSharp/Memory/Buffer2D{T}.cs

@ -12,7 +12,7 @@ namespace SixLabors.Memory
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal class Buffer2D<T> : IBuffer2D<T>, IDisposable
internal class Buffer2D<T> : IDisposable
where T : struct
{
/// <summary>
@ -28,10 +28,14 @@ namespace SixLabors.Memory
this.Height = height;
}
/// <inheritdoc />
/// <summary>
/// Gets the width.
/// </summary>
public int Width { get; private set; }
/// <inheritdoc />
/// <summary>
/// Gets the height.
/// </summary>
public int Height { get; private set; }
/// <summary>

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

@ -18,7 +18,7 @@ namespace SixLabors.Memory
public readonly Rectangle Rectangle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferArea(IBuffer2D<T> destinationBuffer, Rectangle rectangle)
public BufferArea(Buffer2D<T> destinationBuffer, Rectangle rectangle)
{
ImageSharp.DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle));
ImageSharp.DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle));
@ -30,15 +30,15 @@ namespace SixLabors.Memory
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferArea(IBuffer2D<T> destinationBuffer)
public BufferArea(Buffer2D<T> destinationBuffer)
: this(destinationBuffer, destinationBuffer.FullRectangle())
{
}
/// <summary>
/// Gets the <see cref="IBuffer2D{T}"/> being pointed by this instance.
/// Gets the <see cref="Buffer2D{T}"/> being pointed by this instance.
/// </summary>
public IBuffer2D<T> DestinationBuffer { get; }
public Buffer2D<T> DestinationBuffer { get; }
/// <summary>
/// Gets the size of the area.

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

@ -1,31 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.Memory
{
/// <summary>
/// An interface that represents a contigous buffer of value type objects
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal interface IBuffer2D<T>
where T : struct
{
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the contigous buffer being wrapped.
/// </summary>
IBuffer<T> Buffer { get; }
}
}

48
src/ImageSharp/PixelAccessorExtensions.cs

@ -1,48 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Helper methods fro acccess pixel accessors
/// </summary>
internal static class PixelAccessorExtensions
{
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frame">The frame.</param>
/// <returns>
/// The <see cref="PixelAccessor{TPixel}" />
/// </returns>
internal static PixelAccessor<TPixel> Lock<TPixel>(this IPixelSource<TPixel> frame)
where TPixel : struct, IPixel<TPixel>
{
return new PixelAccessor<TPixel>(frame);
}
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="image">The image.</param>
/// <returns>
/// The <see cref="PixelAccessor{TPixel}" />
/// </returns>
internal static PixelAccessor<TPixel> Lock<TPixel>(this Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
return image.Frames.RootFrame.Lock();
}
}
}

151
src/ImageSharp/PixelAccessor{TPixel}.cs

@ -1,151 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Provides per-pixel access to generic <see cref="Image{TPixel}"/> pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal sealed class PixelAccessor<TPixel> : IDisposable, IBuffer2D<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TPixel}"/> class.
/// </summary>
/// <param name="image">The image to provide pixel access for.</param>
public PixelAccessor(IPixelSource<TPixel> image)
{
Guard.NotNull(image, nameof(image));
Guard.MustBeGreaterThan(image.PixelBuffer.Width, 0, "image width");
Guard.MustBeGreaterThan(image.PixelBuffer.Height, 0, "image height");
this.SetPixelBufferUnsafe(image.PixelBuffer);
}
/// <summary>
/// Gets the <see cref="Buffer2D{T}"/> containing the pixel data.
/// </summary>
internal Buffer2D<TPixel> PixelBuffer { get; private set; }
/// <summary>
/// Gets the size of a single pixel in the number of bytes.
/// </summary>
public int PixelSize { get; private set; }
/// <summary>
/// Gets the width of one row in the number of bytes.
/// </summary>
public int RowStride { get; private set; }
/// <inheritdoc />
public int Width { get; private set; }
/// <inheritdoc />
public int Height { get; private set; }
public IBuffer<TPixel> Buffer => this.PixelBuffer.Buffer;
/// <summary>
/// Gets or sets the pixel at the specified position.
/// </summary>
/// <param name="x">The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image.</param>
/// <param name="y">The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.</param>
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
public TPixel this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
this.CheckCoordinates(x, y);
return this.GetSpan()[(y * this.Width) + x];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.CheckCoordinates(x, y);
Span<TPixel> span = this.GetSpan();
span[(y * this.Width) + x] = value;
}
}
/// <inheritdoc />
public void Dispose()
{
}
/// <summary>
/// Resets all the pixels to it's initial value.
/// </summary>
public void Reset()
{
this.PixelBuffer.Buffer.Clear();
}
/// <summary>
/// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!!
/// </summary>
/// <param name="pixels">The pixels.</param>
/// <returns>Returns the old pixel data thats has gust been replaced.</returns>
/// <remarks>If <see cref="M:PixelAccessor.PooledMemory"/> is true then caller is responsible for ensuring <see cref="M:PixelDataPool.Return()"/> is called.</remarks>
internal Buffer2D<TPixel> SwapBufferOwnership(Buffer2D<TPixel> pixels)
{
Buffer2D<TPixel> oldPixels = this.PixelBuffer;
this.SetPixelBufferUnsafe(pixels);
return oldPixels;
}
/// <summary>
/// Copies the pixels to another <see cref="PixelAccessor{TPixel}"/> of the same size.
/// </summary>
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TPixel> target)
{
this.PixelBuffer.GetSpan().CopyTo(target.PixelBuffer.GetSpan());
}
/// <summary>
/// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!!
/// </summary>
/// <param name="pixels">The pixel buffer</param>
private void SetPixelBufferUnsafe(Buffer2D<TPixel> pixels)
{
this.PixelBuffer = pixels;
this.Width = pixels.Width;
this.Height = pixels.Height;
this.PixelSize = Unsafe.SizeOf<TPixel>();
this.RowStride = this.Width * this.PixelSize;
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>
/// <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 coordinates are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckCoordinates(int x, int y)
{
if (x < 0 || x >= this.Width)
{
throw new ArgumentOutOfRangeException(nameof(x), x, $"{x} is outwith the image bounds.");
}
if (y < 0 || y >= this.Height)
{
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds.");
}
}
}
}

35
src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorCompassProcessor.cs

@ -3,11 +3,14 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Filters.Processors;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Convolution.Processors
@ -128,27 +131,35 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors
{
new ConvolutionProcessor<TPixel>(kernels[i]).Apply(pass, sourceRectangle, configuration);
using (PixelAccessor<TPixel> passPixels = pass.Lock())
using (PixelAccessor<TPixel> targetPixels = source.Lock())
{
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
Buffer2D<TPixel> passPixels = pass.PixelBuffer;
Buffer2D<TPixel> targetPixels = source.PixelBuffer;
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
int offsetY = y - shiftY;
ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY));
ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY));
for (int x = minX; x < maxX; x++)
{
int offsetX = x - shiftX;
// Grab the max components of the two pixels
TPixel packed = default(TPixel);
packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4()));
targetPixels[offsetX, offsetY] = packed;
ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX);
ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX);
var pixelValue = Vector4.Max(
currentPassPixel.ToVector4(),
currentTargetPixel.ToVector4());
currentTargetPixel.PackFromVector4(pixelValue);
}
});
}
}
}
}

46
tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs

@ -22,23 +22,21 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
using (var source = new Image<Rgba32>(1024, 768))
using (var target = new Image<Rgba32>(1024, 768))
{
using (PixelAccessor<Rgba32> sourcePixels = source.Lock())
using (PixelAccessor<Rgba32> targetPixels = target.Lock())
{
Parallel.For(
0,
source.Height,
Configuration.Default.ParallelOptions,
y =>
Buffer2D<Rgba32> sourcePixels = source.GetRootFramePixelBuffer();
Buffer2D<Rgba32> targetPixels = target.GetRootFramePixelBuffer();
Parallel.For(
0,
source.Height,
Configuration.Default.ParallelOptions,
y =>
{
for (int x = 0; x < source.Width; x++)
{
for (int x = 0; x < source.Width; x++)
{
targetPixels[x, y] = sourcePixels[x, y];
}
});
targetPixels[x, y] = sourcePixels[x, y];
}
});
return targetPixels[0, 0];
}
return targetPixels[0, 0];
}
}
@ -48,14 +46,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
using (var source = new Image<Rgba32>(1024, 768))
using (var target = new Image<Rgba32>(1024, 768))
{
using (PixelAccessor<Rgba32> sourcePixels = source.Lock())
using (PixelAccessor<Rgba32> targetPixels = target.Lock())
{
Parallel.For(
0,
source.Height,
Configuration.Default.ParallelOptions,
y =>
Buffer2D<Rgba32> sourcePixels = source.GetRootFramePixelBuffer();
Buffer2D<Rgba32> targetPixels = target.GetRootFramePixelBuffer();
Parallel.For(
0,
source.Height,
Configuration.Default.ParallelOptions,
y =>
{
Span<Rgba32> sourceRow = sourcePixels.GetRowSpan(y);
Span<Rgba32> targetRow = targetPixels.GetRowSpan(y);
@ -66,8 +63,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
}
});
return targetPixels[0, 0];
}
return targetPixels[0, 0];
}
}

27
tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs

@ -57,20 +57,19 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp BulkVectorConvert")]
public CoreSize BulkVectorConvert()
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
using (var image = new Image<Rgba32>(800, 800))
{
using (IBuffer<float> amounts = Configuration.Default.MemoryAllocator.Allocate<float>(image.Width))
{
amounts.GetSpan().Fill(1);
using (PixelAccessor<Rgba32> pixels = image.Lock())
Buffer2D<Rgba32> pixels = image.GetRootFramePixelBuffer();
for (int y = 0; y < image.Height; y++)
{
for (int y = 0; y < image.Height; y++)
{
Span<Rgba32> span = pixels.GetRowSpan(y);
this.BulkVectorConvert(span, span, span, amounts.GetSpan());
}
Span<Rgba32> span = pixels.GetRowSpan(y);
this.BulkVectorConvert(span, span, span, amounts.GetSpan());
}
return new CoreSize(image.Width, image.Height);
}
}
@ -79,18 +78,16 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp BulkPixelConvert")]
public CoreSize BulkPixelConvert()
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
using (var image = new Image<Rgba32>(800, 800))
{
using (IBuffer<float> amounts = Configuration.Default.MemoryAllocator.Allocate<float>(image.Width))
{
amounts.GetSpan().Fill(1);
using (PixelAccessor<Rgba32> pixels = image.Lock())
Buffer2D<Rgba32> pixels = image.GetRootFramePixelBuffer();
for (int y = 0; y < image.Height; y++)
{
for (int y = 0; y < image.Height; y++)
{
Span<Rgba32> span = pixels.GetRowSpan(y);
this.BulkPixelConvert(span, span, span, amounts.GetSpan());
}
Span<Rgba32> span = pixels.GetRowSpan(y);
this.BulkPixelConvert(span, span, span, amounts.GetSpan());
}
return new CoreSize(image.Width, image.Height);

4
tests/ImageSharp.Benchmarks/Samplers/Glow.cs

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Benchmarks
}
internal class GlowProcessorParallel<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessorParallel{TPixel}" /> class.
@ -103,8 +103,8 @@ namespace SixLabors.ImageSharp.Benchmarks
int width = maxX - minX;
using (IBuffer<TPixel> rowColors = Configuration.Default.MemoryAllocator.Allocate<TPixel>(width))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
Buffer2D<TPixel> sourcePixels = source.PixelBuffer;
rowColors.GetSpan().Fill(glowColor);
Parallel.For(

86
tests/ImageSharp.Tests/Drawing/BeziersTests.cs

@ -11,6 +11,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class Beziers : FileTestBase
{
[Fact]
@ -19,32 +21,30 @@ namespace SixLabors.ImageSharp.Tests.Drawing
string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue)
.DrawBeziers(Rgba32.HotPink, 5,
new SixLabors.Primitives.PointF[] {
new Vector2(10, 400),
new Vector2(30, 10),
new Vector2(240, 30),
new Vector2(300, 400)
}));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).DrawBeziers(
Rgba32.HotPink,
5,
new SixLabors.Primitives.PointF[]
{
new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400)
}));
image.Save($"{path}/Simple.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
//top of curve
Assert.Equal(Rgba32.HotPink, sourcePixels[138, 115]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
//top of curve
Assert.Equal(Rgba32.HotPink, sourcePixels[138, 115]);
//start points
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 395]);
Assert.Equal(Rgba32.HotPink, sourcePixels[300, 395]);
//start points
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 395]);
Assert.Equal(Rgba32.HotPink, sourcePixels[300, 395]);
//curve points should not be never be set
Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]);
//curve points should not be never be set
Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]);
// inside shape should be empty
Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]);
}
// inside shape should be empty
Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]);
}
}
@ -58,36 +58,34 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue)
.DrawBeziers(color,
10,
new SixLabors.Primitives.PointF[]{
new Vector2(10, 400),
new Vector2(30, 10),
new Vector2(240, 30),
new Vector2(300, 400)
}));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).DrawBeziers(
color,
10,
new SixLabors.Primitives.PointF[]
{
new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400)
}));
image.Save($"{path}/Opacity.png");
//shift background color towards foreground color by the opacity amount
var mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
// top of curve
Assert.Equal(mergedColor, sourcePixels[138, 115]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
// top of curve
Assert.Equal(mergedColor, sourcePixels[138, 115]);
// start points
Assert.Equal(mergedColor, sourcePixels[10, 395]);
Assert.Equal(mergedColor, sourcePixels[300, 395]);
// start points
Assert.Equal(mergedColor, sourcePixels[10, 395]);
Assert.Equal(mergedColor, sourcePixels[300, 395]);
// curve points should not be never be set
Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]);
// curve points should not be never be set
Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]);
// inside shape should be empty
Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]);
}
// inside shape should be empty
Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]);
}
}
}

50
tests/ImageSharp.Tests/Drawing/DrawPathTests.cs

@ -12,6 +12,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class DrawPathTests : FileTestBase
{
[Fact]
@ -24,26 +26,23 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300));
var bazierSegment = new CubicBezierLineSegment(new Vector2(50, 300),
var bazierSegment = new CubicBezierLineSegment(
new Vector2(50, 300),
new Vector2(500, 500),
new Vector2(60, 10),
new Vector2(10, 400));
var p = new Path(linerSegemnt, bazierSegment);
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Draw(Rgba32.HotPink, 5, p));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, p));
image.Save($"{path}/Simple.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
@ -71,22 +70,19 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Draw(color, 10, p));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(color, 10, p));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(mergedColor, sourcePixels[11, 11]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[11, 11]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
@ -102,15 +98,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing
for (int i = 0; i < 300; i += 20)
{
image.Mutate(x => x.DrawLines(pen, new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) }));
image.Mutate(
x => x.DrawLines(
pen,
new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) }));
}
image
.Save($"{path}/ClippedLines.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.White, sourcePixels[0, 90]);
}
image.Save($"{path}/ClippedLines.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.White, sourcePixels[0, 90]);
}
}
}

41
tests/ImageSharp.Tests/Drawing/FillPatternTests.cs

@ -12,6 +12,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class FillPatternBrushTests : FileTestBase
{
private void Test(string name, Rgba32 background, IBrush<Rgba32> brush, Rgba32[,] expectedPattern)
@ -19,36 +21,33 @@ namespace SixLabors.ImageSharp.Tests.Drawing
string path = TestEnvironment.CreateOutputDirectory("Fill", "PatternBrush");
using (var image = new Image<Rgba32>(20, 20))
{
image.Mutate(x => x
.Fill(background)
.Fill(brush));
image.Mutate(x => x.Fill(background).Fill(brush));
image.Save($"{path}/{name}.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
// lets pick random spots to start checking
var r = new Random();
var expectedPatternFast = new DenseMatrix<Rgba32>(expectedPattern);
int xStride = expectedPatternFast.Columns;
int yStride = expectedPatternFast.Rows;
int offsetX = r.Next(image.Width / xStride) * xStride;
int offsetY = r.Next(image.Height / yStride) * yStride;
for (int x = 0; x < xStride; x++)
{
// lets pick random spots to start checking
var r = new Random();
var expectedPatternFast = new DenseMatrix<Rgba32>(expectedPattern);
int xStride = expectedPatternFast.Columns;
int yStride = expectedPatternFast.Rows;
int offsetX = r.Next(image.Width / xStride) * xStride;
int offsetY = r.Next(image.Height / yStride) * yStride;
for (int x = 0; x < xStride; x++)
for (int y = 0; y < yStride; y++)
{
for (int y = 0; y < yStride; y++)
int actualX = x + offsetX;
int actualY = y + offsetY;
Rgba32 expected = expectedPatternFast[y, x]; // inverted pattern
Rgba32 actual = sourcePixels[actualX, actualY];
if (expected != actual)
{
int actualX = x + offsetX;
int actualY = y + offsetY;
Rgba32 expected = expectedPatternFast[y, x]; // inverted pattern
Rgba32 actual = sourcePixels[actualX, actualY];
if (expected != actual)
{
Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})");
}
Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})");
}
}
}
image.Mutate(x => x.Resize(80, 80));
image.Save($"{path}/{name}x4.png");
}

129
tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs

@ -13,6 +13,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class LineComplexPolygonTests : FileTestBase
{
[Fact]
@ -32,34 +34,29 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Save($"{path}/Simple.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
Assert.Equal(Rgba32.HotPink, sourcePixels[37, 85]);
Assert.Equal(Rgba32.HotPink, sourcePixels[37, 85]);
Assert.Equal(Rgba32.HotPink, sourcePixels[93, 85]);
Assert.Equal(Rgba32.HotPink, sourcePixels[93, 85]);
Assert.Equal(Rgba32.HotPink, sourcePixels[65, 137]);
Assert.Equal(Rgba32.HotPink, sourcePixels[65, 137]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
@ -79,32 +76,27 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Save($"{path}/SimpleVanishHole.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
//Assert.Equal(Color.HotPink, sourcePixels[37, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[37, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[93, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[93, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[65, 137]);
//Assert.Equal(Color.HotPink, sourcePixels[65, 137]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
@ -124,25 +116,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Save($"{path}/SimpleOverlapping.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
Assert.Equal(Rgba32.Blue, sourcePixels[130, 41]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
Assert.Equal(Rgba32.Blue, sourcePixels[130, 41]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
@ -186,30 +174,27 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Draw(color, 5, simplePath.Clip(hole1)));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Draw(color, 5, simplePath.Clip(hole1)));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(mergedColor, sourcePixels[10, 10]);
Assert.Equal(mergedColor, sourcePixels[200, 150]);
Assert.Equal(mergedColor, sourcePixels[50, 300]);
Assert.Equal(mergedColor, sourcePixels[37, 85]);
Assert.Equal(mergedColor, sourcePixels[93, 85]);
Assert.Equal(mergedColor, sourcePixels[65, 137]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[10, 10]);
Assert.Equal(mergedColor, sourcePixels[200, 150]);
Assert.Equal(mergedColor, sourcePixels[50, 300]);
Assert.Equal(mergedColor, sourcePixels[37, 85]);
Assert.Equal(mergedColor, sourcePixels[93, 85]);
Assert.Equal(mergedColor, sourcePixels[65, 137]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
}

106
tests/ImageSharp.Tests/Drawing/LineTests.cs

@ -13,6 +13,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class LineTests : FileTestBase
{
[Fact]
@ -21,24 +23,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.DrawLines(Rgba32.HotPink, 5,
new SixLabors.Primitives.PointF[]{
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).DrawLines(
Rgba32.HotPink,
5,
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Simple.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
@ -48,27 +47,22 @@ namespace SixLabors.ImageSharp.Tests.Drawing
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.DrawLines(
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).DrawLines(
new GraphicsOptions(false),
Rgba32.HotPink,
5,
new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Simple_noantialias.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
@ -151,26 +145,25 @@ namespace SixLabors.ImageSharp.Tests.Drawing
var image = new Image<Rgba32>(500, 500);
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.DrawLines(color, 10, new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).DrawLines(
color,
10,
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
var mergedColor =
new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(mergedColor, sourcePixels[11, 11]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[11, 11]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
[Fact]
@ -180,27 +173,24 @@ namespace SixLabors.ImageSharp.Tests.Drawing
var image = new Image<Rgba32>(500, 500);
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.DrawLines(Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 10),
new Vector2(200, 150),
new Vector2(10, 150)
}));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).DrawLines(
Rgba32.HotPink,
10,
new Vector2(10, 10),
new Vector2(200, 10),
new Vector2(200, 150),
new Vector2(10, 150)));
image.Save($"{path}/Rectangle.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
}

69
tests/ImageSharp.Tests/Drawing/PolygonTests.cs

@ -12,6 +12,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class PolygonTests : FileTestBase
{
[Fact]
@ -21,26 +23,23 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (Image<Rgba32> image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.DrawPolygon(Rgba32.HotPink, 5,
new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).DrawPolygon(
Rgba32.HotPink,
5,
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Simple.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
@ -48,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
public void ImageShouldBeOverlayedPolygonOutlineWithOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons");
SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] {
PointF[] simplePath = {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
@ -58,24 +57,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (Image<Rgba32> image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.DrawPolygon(color, 10, simplePath));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).DrawPolygon(color, 10, simplePath));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Rgba32 mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(mergedColor, sourcePixels[9, 9]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[9, 9]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
@ -86,23 +82,20 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (Image<Rgba32> image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140)));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140)));
image.Save($"{path}/Rectangle.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[8, 8]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[8, 8]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]);
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
}

47
tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs

@ -13,6 +13,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class SolidComplexPolygonTests : FileTestBase
{
[Fact]
@ -32,18 +34,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing
// var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20));
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Fill(Rgba32.HotPink, clipped));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Fill(Rgba32.HotPink, clipped));
image.Save($"{path}/Simple.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
}
@ -64,18 +62,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Fill(Rgba32.HotPink, simplePath.Clip(hole1)));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Fill(Rgba32.HotPink, simplePath.Clip(hole1)));
image.Save($"{path}/SimpleOverlapping.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
}
@ -97,21 +91,18 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Fill(color, simplePath.Clip(hole1)));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).Fill(color, simplePath.Clip(hole1)));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(mergedColor, sourcePixels[20, 35]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[20, 35]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
}
}

88
tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs

@ -15,6 +15,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class SolidPolygonTests : FileTestBase
{
[Fact]
@ -29,14 +31,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.FillPolygon(new GraphicsOptions(true), Rgba32.HotPink, simplePath));
image.Mutate(x => x.FillPolygon(new GraphicsOptions(true), Rgba32.HotPink, simplePath));
image.Save($"{path}/Simple.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]);
}
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]);
}
}
@ -52,14 +51,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.FillPolygon(new GraphicsOptions(true), Brushes.Horizontal(Rgba32.HotPink), simplePath));
image.Mutate(
x => x.FillPolygon(new GraphicsOptions(true), Brushes.Horizontal(Rgba32.HotPink), simplePath));
image.Save($"{path}/Pattern.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]);
}
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]);
}
}
@ -75,21 +72,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.FillPolygon(new GraphicsOptions(false), Rgba32.HotPink, simplePath));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).FillPolygon(
new GraphicsOptions(false),
Rgba32.HotPink,
simplePath));
image.Save($"{path}/Simple_NoAntialias.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.True(Rgba32.HotPink == sourcePixels[11, 11], "[11, 11] wrong");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.True(Rgba32.HotPink == sourcePixels[11, 11], "[11, 11] wrong");
Assert.True(Rgba32.HotPink == sourcePixels[199, 149], "[199, 149] wrong");
Assert.True(Rgba32.HotPink == sourcePixels[199, 149], "[199, 149] wrong");
Assert.True(Rgba32.HotPink == sourcePixels[50, 50], "[50, 50] wrong");
Assert.True(Rgba32.HotPink == sourcePixels[50, 50], "[50, 50] wrong");
Assert.True(Rgba32.Blue == sourcePixels[2, 2], "[2, 2] wrong");
}
Assert.True(Rgba32.Blue == sourcePixels[2, 2], "[2, 2] wrong");
}
}
@ -128,18 +125,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.FillPolygon(color, simplePath));
image.Mutate(x => x.BackgroundColor(Rgba32.Blue).FillPolygon(color, simplePath));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
@ -150,23 +144,22 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Fill(Rgba32.HotPink, new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140)));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).Fill(
Rgba32.HotPink,
new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140)));
image.Save($"{path}/Rectangle.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]);
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
@ -177,17 +170,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var image = new Image<Rgba32>(100, 100))
{
image.Mutate(x => x
.BackgroundColor(Rgba32.Blue)
.Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30)));
image.Mutate(
x => x.BackgroundColor(Rgba32.Blue).Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30)));
image.Save($"{path}/Triangle.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
Assert.Equal(Rgba32.Blue, sourcePixels[30, 65]);
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.Blue, sourcePixels[30, 65]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]);
}
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]);
}
}

20
tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs

@ -10,24 +10,24 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
using SixLabors.Memory;
public class GenericBlock8x8Tests
{
public static Image<TPixel> CreateTestImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var image = new Image<TPixel>(10, 10);
using (PixelAccessor<TPixel> pixels = image.Lock())
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
for (int i = 0; i < 10; i++)
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
for (int j = 0; j < 10; j++)
{
var rgba = new Rgba32((byte)(i + 1), (byte)(j + 1), (byte)200, (byte)255);
var color = default(TPixel);
color.PackFromRgba32(rgba);
pixels[i, j] = color;
}
var rgba = new Rgba32((byte)(i + 1), (byte)(j + 1), (byte)200, (byte)255);
var color = default(TPixel);
color.PackFromRgba32(rgba);
pixels[i, j] = color;
}
}

23
tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs

@ -9,6 +9,8 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests
{
using SixLabors.Memory;
public abstract partial class TestImageProvider<TPixel>
where TPixel : struct, IPixel<TPixel>
{
@ -54,19 +56,18 @@ namespace SixLabors.ImageSharp.Tests
private static void DrawTestPattern(Image<TPixel> image)
{
// first lets split the image into 4 quadrants
using (PixelAccessor<TPixel> pixels = image.Lock())
{
BlackWhiteChecker(pixels); // top left
VerticalBars(pixels); // top right
TransparentGradients(pixels); // bottom left
Rainbow(pixels); // bottom right
}
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
BlackWhiteChecker(pixels); // top left
VerticalBars(pixels); // top right
TransparentGradients(pixels); // bottom left
Rainbow(pixels); // bottom right
}
/// <summary>
/// Fills the top right quadrant with alternating solid vertical bars.
/// </summary>
/// <param name="pixels"></param>
private static void VerticalBars(PixelAccessor<TPixel> pixels)
private static void VerticalBars(Buffer2D<TPixel> pixels)
{
// topLeft
int left = pixels.Width / 2;
@ -103,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests
/// fills the top left quadrant with a black and white checker board.
/// </summary>
/// <param name="pixels"></param>
private static void BlackWhiteChecker(PixelAccessor<TPixel> pixels)
private static void BlackWhiteChecker(Buffer2D<TPixel> pixels)
{
// topLeft
int left = 0;
@ -142,7 +143,7 @@ namespace SixLabors.ImageSharp.Tests
/// Fills the bottom left quadrent with 3 horizental bars in Red, Green and Blue with a alpha gradient from left (transparent) to right (solid).
/// </summary>
/// <param name="pixels"></param>
private static void TransparentGradients(PixelAccessor<TPixel> pixels)
private static void TransparentGradients(Buffer2D<TPixel> pixels)
{
// topLeft
int left = 0;
@ -187,7 +188,7 @@ namespace SixLabors.ImageSharp.Tests
/// A better algorithm could be used but it works
/// </summary>
/// <param name="pixels"></param>
private static void Rainbow(PixelAccessor<TPixel> pixels)
private static void Rainbow(Buffer2D<TPixel> pixels)
{
int left = pixels.Width / 2;
int right = pixels.Width;

47
tests/ImageSharp.Tests/TestUtilities/TestUtils.cs

@ -15,6 +15,7 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Tests
{
using SixLabors.ImageSharp.Advanced;
using SixLabors.Memory;
/// <summary>
/// Various utility and extension methods.
@ -63,36 +64,30 @@ namespace SixLabors.ImageSharp.Tests
var rgb1 = default(Rgb24);
var rgb2 = default(Rgb24);
using (PixelAccessor<TPixel> pixA = a.Lock())
Buffer2D<TPixel> pixA = a.GetRootFramePixelBuffer();
Buffer2D<TPixel> pixB = b.GetRootFramePixelBuffer();
for (int y = 0; y < a.Height; y++)
{
using (PixelAccessor<TPixel> pixB = b.Lock())
for (int x = 0; x < a.Width; x++)
{
for (int y = 0; y < a.Height; y++)
TPixel ca = pixA[x, y];
TPixel cb = pixB[x, y];
if (compareAlpha)
{
if (!ca.Equals(cb))
{
return false;
}
}
else
{
for (int x = 0; x < a.Width; x++)
ca.ToRgb24(ref rgb1);
cb.ToRgb24(ref rgb2);
if (rgb1.R != rgb2.R || rgb1.G != rgb2.G || rgb1.B != rgb2.B)
{
TPixel ca = pixA[x, y];
TPixel cb = pixB[x, y];
if (compareAlpha)
{
if (!ca.Equals(cb))
{
return false;
}
}
else
{
ca.ToRgb24(ref rgb1);
cb.ToRgb24(ref rgb2);
if (rgb1.R != rgb2.R ||
rgb1.G != rgb2.G ||
rgb1.B != rgb2.B)
{
return false;
}
}
return false;
}
}
}

22
tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

@ -13,6 +13,8 @@ using SixLabors.ImageSharp.Formats;
namespace SixLabors.ImageSharp.Tests
{
using SixLabors.Memory;
public class TestImageProviderTests
{
public TestImageProviderTests(ITestOutputHelper output)
@ -282,19 +284,17 @@ namespace SixLabors.ImageSharp.Tests
var rgba = default(Rgba32);
using (PixelAccessor<TPixel> pixels = img.Lock())
Buffer2D<TPixel> pixels = img.GetRootFramePixelBuffer();
for (int y = 0; y < pixels.Height; y++)
{
for (int y = 0; y < pixels.Height; y++)
for (int x = 0; x < pixels.Width; x++)
{
for (int x = 0; x < pixels.Width; x++)
{
pixels[x, y].ToRgba32(ref rgba);
Assert.Equal(255, rgba.R);
Assert.Equal(100, rgba.G);
Assert.Equal(50, rgba.B);
Assert.Equal(200, rgba.A);
}
pixels[x, y].ToRgba32(ref rgba);
Assert.Equal(255, rgba.R);
Assert.Equal(100, rgba.G);
Assert.Equal(50, rgba.B);
Assert.Equal(200, rgba.A);
}
}
}

19
tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs

@ -14,6 +14,7 @@ using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests
{
using SixLabors.ImageSharp.Processing.Effects;
using SixLabors.Memory;
public class TestUtilityExtensionsTests
{
@ -29,20 +30,18 @@ namespace SixLabors.ImageSharp.Tests
{
var image = new Image<TPixel>(10, 10);
using (PixelAccessor<TPixel> pixels = image.Lock())
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
for (int i = 0; i < 10; i++)
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
for (int j = 0; j < 10; j++)
{
var v = new Vector4(i, j, 0, 1);
v /= 10;
var v = new Vector4(i, j, 0, 1);
v /= 10;
var color = default(TPixel);
color.PackFromVector4(v);
var color = default(TPixel);
color.PackFromVector4(v);
pixels[i, j] = color;
}
pixels[i, j] = color;
}
}

Loading…
Cancel
Save