Browse Source

refactor FillRegionProcessor, drop MemoryManager.AllocateFake

af/merge-core
Anton Firszov 8 years ago
parent
commit
259f9736f3
  1. 63
      src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
  2. 10
      src/ImageSharp/Memory/MemoryManager.cs
  3. 18
      tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs
  4. 2
      tests/ImageSharp.Tests/TestUtilities/TestUtils.cs
  5. 2
      tests/Images/External

63
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs

@ -96,36 +96,35 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options)) using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{ {
int scanlineWidth = maxX - minX; int scanlineWidth = maxX - minX;
using (BasicArrayBuffer<float> buffer = source.MemoryManager.AllocateFake<float>(maxIntersections)) using (IBuffer<float> bBuffer = source.MemoryManager.Allocate<float>(maxIntersections))
using (BasicArrayBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(scanlineWidth)) using (IBuffer<float> bScanline = source.MemoryManager.Allocate<float>(scanlineWidth))
{ {
bool scanlineDirty = true; bool scanlineDirty = true;
float subpixelFraction = 1f / subpixelCount; float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan();
for (int y = minY; y < maxY; y++) for (int y = minY; y < maxY; y++)
{ {
if (scanlineDirty) if (scanlineDirty)
{ {
// clear the buffer scanline.Clear();
for (int x = 0; x < scanlineWidth; x++)
{
scanline[x] = 0;
}
scanlineDirty = false; scanlineDirty = false;
} }
float yPlusOne = y + 1; float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction) for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{ {
int pointsFound = region.Scan(subPixel + offset, buffer.GetSpan(), configuration); int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0) if (pointsFound == 0)
{ {
// nothing on this line skip // nothing on this line skip
continue; continue;
} }
QuickSort(new Span<float>(buffer.Array, 0, pointsFound)); QuickSort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2) for (int point = 0; point < pointsFound; point += 2)
{ {
@ -181,7 +180,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
} }
} }
applicator.Apply(scanline.GetSpan(), minX, y); applicator.Apply(scanline, minX, y);
} }
} }
} }
@ -189,31 +188,45 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(Span<float> data, int left, int right) private static void Swap(ref float left, ref float right)
{ {
float tmp = data[left]; float tmp = left;
data[left] = data[right]; left = right;
data[right] = tmp; right = tmp;
} }
private static void QuickSort(Span<float> data) private static void QuickSort(Span<float> data)
{ {
QuickSort(data, 0, data.Length - 1); if (data.Length < 2)
{
return;
}
else if (data.Length == 2)
{
if (data[0] > data[1])
{
Swap(ref data[0], ref data[1]);
}
return;
}
QuickSort(ref data[0], 0, data.Length - 1);
} }
private static void QuickSort(Span<float> data, int lo, int hi) private static void QuickSort(ref float data0, int lo, int hi)
{ {
if (lo < hi) if (lo < hi)
{ {
int p = Partition(data, lo, hi); int p = Partition(ref data0, lo, hi);
QuickSort(data, lo, p); QuickSort(ref data0, lo, p);
QuickSort(data, p + 1, hi); QuickSort(ref data0, p + 1, hi);
} }
} }
private static int Partition(Span<float> data, int lo, int hi) private static int Partition(ref float data0, int lo, int hi)
{ {
float pivot = data[lo]; float pivot = Unsafe.Add(ref data0, lo);
int i = lo - 1; int i = lo - 1;
int j = hi + 1; int j = hi + 1;
while (true) while (true)
@ -222,20 +235,20 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
{ {
i = i + 1; i = i + 1;
} }
while (data[i] < pivot && i < hi); while (Unsafe.Add(ref data0, i) < pivot && i < hi);
do do
{ {
j = j - 1; j = j - 1;
} }
while (data[j] > pivot && j > lo); while (Unsafe.Add(ref data0, j) > pivot && j > lo);
if (i >= j) if (i >= j)
{ {
return j; return j;
} }
Swap(data, i, j); Swap(ref Unsafe.Add(ref data0, i), ref Unsafe.Add(ref data0, j));
} }
} }
} }

10
src/ImageSharp/Memory/MemoryManager.cs

@ -27,16 +27,6 @@ namespace SixLabors.ImageSharp.Memory
/// <returns>The <see cref="IManagedByteBuffer"/></returns> /// <returns>The <see cref="IManagedByteBuffer"/></returns>
internal abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear); internal abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear);
/// <summary>
/// Temporal workaround. A method providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery.
/// Should be replaced with 'Allocate()' as soon as SixLabors.Shapes has Span-based API-s!
/// </summary>
internal BasicArrayBuffer<T> AllocateFake<T>(int length, bool dummy = false)
where T : struct
{
return new BasicArrayBuffer<T>(new T[length]);
}
/// <summary> /// <summary>
/// Releases all retained resources not being in use. /// Releases all retained resources not being in use.
/// Eg: by resetting array pools and letting GC to free the arrays. /// Eg: by resetting array pools and letting GC to free the arrays.

18
tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs

@ -12,7 +12,10 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing namespace SixLabors.ImageSharp.Tests.Drawing
{ {
using System;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
[GroupOutput("Drawing")] [GroupOutput("Drawing")]
public class FillSolidBrushTests public class FillSolidBrushTests
@ -67,6 +70,19 @@ namespace SixLabors.ImageSharp.Tests.Drawing
} }
} }
[Theory]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)]
public void FillRegion<TPixel>(TestImageProvider<TPixel> provider, int x0, int y0, int w, int h)
where TPixel : struct, IPixel<TPixel>
{
FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})";
var region = new RectangleF(x0, y0, w, h);
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>("Blue");
provider.RunValidatingProcessorTest(c => c.Fill(color, region), testDetails, ImageComparer.Exact);
}
public static readonly TheoryData<bool, string, float, PixelBlenderMode, float> BlendData = public static readonly TheoryData<bool, string, float, PixelBlenderMode, float> BlendData =
new TheoryData<bool, string, float, PixelBlenderMode, float>() new TheoryData<bool, string, float, PixelBlenderMode, float>()
{ {

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

@ -113,8 +113,6 @@ namespace SixLabors.ImageSharp.Tests
/// <returns></returns> /// <returns></returns>
public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType]; public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType];
public static IEnumerable<KeyValuePair<PixelTypes, Type>> ExpandAllTypes(this PixelTypes pixelTypes) public static IEnumerable<KeyValuePair<PixelTypes, Type>> ExpandAllTypes(this PixelTypes pixelTypes)
{ {
if (pixelTypes == PixelTypes.Undefined) if (pixelTypes == PixelTypes.Undefined)

2
tests/Images/External

@ -1 +1 @@
Subproject commit eb40b3c039dd8c8ca448cb8073a59ca178901e9f Subproject commit b1f057df33b7bfa6cabe714cf7090ac6017ea5d8
Loading…
Cancel
Save