Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>
<AssemblyTitle>SixLabors.ImageSharp.Drawing</AssemblyTitle>
Expand All @@ -14,16 +13,12 @@
<Description>An extension to ImageSharp that allows the drawing of images, paths, and text.</Description>
<TargetFrameworks>netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;net472</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\shared-infrastructure\branding\icons\imagesharp.drawing\sixlabors.imagesharp.drawing.128.png" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta16" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1-alpha.0.1" />
</ItemGroup>

<Import Project="..\..\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems" Label="Shared" />

</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public static PolygonScanner Create(
IntersectionRule intersectionRule,
MemoryAllocator allocator)
{
var multipolygon = TessellatedMultipolygon.Create(polygon, allocator);
using var multipolygon = TessellatedMultipolygon.Create(polygon, allocator);
var edges = ScanEdgeCollection.Create(multipolygon, allocator, subsampling);
var scanner = new PolygonScanner(edges, multipolygon.TotalVertexCount * 2, minY, maxY, subsampling, intersectionRule, allocator);
scanner.Init();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static ScanEdgeCollection Create(
MemoryAllocator allocator,
int subsampling)
{
TessellatedMultipolygon multipolygon = TessellatedMultipolygon.Create(polygon, allocator);
using TessellatedMultipolygon multipolygon = TessellatedMultipolygon.Create(polygon, allocator);
return Create(multipolygon, allocator, subsampling);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
namespace SixLabors.ImageSharp.Drawing.Tests.Drawing.Text
{
[GroupOutput("Drawing/Text")]
[ValidateDisposedMemoryAllocations]
public class DrawTextOnImageTests
{
private const string AB = "AB\nAB";
Expand Down
77 changes: 77 additions & 0 deletions tests/ImageSharp.Drawing.Tests/MemoryAllocatorValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Threading;
using SixLabors.ImageSharp.Diagnostics;
using Xunit;

namespace SixLabors.ImageSharp.Drawing.Tests
{
public static class MemoryAllocatorValidator
{
private static readonly AsyncLocal<TestMemoryDiagnostics> LocalInstance = new();

public static bool MonitoringAllocations => LocalInstance.Value != null;

static MemoryAllocatorValidator()
{
MemoryDiagnostics.MemoryAllocated += MemoryDiagnostics_MemoryAllocated;
MemoryDiagnostics.MemoryReleased += MemoryDiagnostics_MemoryReleased;
}

private static void MemoryDiagnostics_MemoryReleased()
{
TestMemoryDiagnostics backing = LocalInstance.Value;
if (backing != null)
{
backing.TotalRemainingAllocated--;
}
}

private static void MemoryDiagnostics_MemoryAllocated()
{
TestMemoryDiagnostics backing = LocalInstance.Value;
if (backing != null)
{
backing.TotalAllocated++;
backing.TotalRemainingAllocated++;
}
}

public static TestMemoryDiagnostics MonitorAllocations()
{
var diag = new TestMemoryDiagnostics();
LocalInstance.Value = diag;
return diag;
}

public static void StopMonitoringAllocations() => LocalInstance.Value = null;

public static void ValidateAllocations(int expectedAllocationCount = 0)
=> LocalInstance.Value?.Validate(expectedAllocationCount);

public class TestMemoryDiagnostics : IDisposable
{
public int TotalAllocated { get; set; }

public int TotalRemainingAllocated { get; set; }

public void Validate(int expectedAllocationCount)
{
var count = this.TotalRemainingAllocated;
var pass = expectedAllocationCount == count;
Assert.True(pass, $"Expected a {expectedAllocationCount} undisposed buffers but found {count}");
}

public void Dispose()
{
this.Validate(0);
if (LocalInstance.Value == this)
{
StopMonitoringAllocations();
}
}
}
}
}
100 changes: 50 additions & 50 deletions tests/ImageSharp.Drawing.Tests/Shapes/Scan/ScanEdgeCollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,20 @@ namespace SixLabors.ImageSharp.Drawing.Tests.Shapes.Scan
{
public class ScanEdgeCollectionTests
{
private ScanEdgeCollection edges;

private static MemoryAllocator MemoryAllocator => Configuration.Default.MemoryAllocator;

private static readonly DebugDraw DebugDraw = new DebugDraw(nameof(ScanEdgeCollectionTests));

private void VerifyEdge(
ScanEdgeCollection edges,
float y0,
float y1,
(FuzzyFloat X, FuzzyFloat Y) arbitraryPoint,
int emit0,
int emit1,
bool edgeUp)
{
foreach (ScanEdge e in this.edges.Edges)
foreach (ScanEdge e in edges.Edges)
{
if (y0 == e.Y0 && y1 == e.Y1)
{
Expand All @@ -45,6 +44,7 @@ private void VerifyEdge(
}

[Fact]
[ValidateDisposedMemoryAllocations]
public void SimplePolygon_AllEmitCases()
{
// see: SimplePolygon_AllEmitCases.png
Expand Down Expand Up @@ -78,29 +78,29 @@ public void SimplePolygon_AllEmitCases()

DebugDraw.Polygon(polygon, 1, 100);

this.edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16);

Assert.Equal(19, this.edges.Edges.Length);

this.VerifyEdge(1f, 2f, (2.5f, 1.5f), 1, 2, true);
this.VerifyEdge(1f, 3f, (3.5f, 2f), 1, 1, false);
this.VerifyEdge(1f, 3f, (5f, 2f), 1, 1, true);
this.VerifyEdge(1f, 2f, (6.5f, 1.5f), 1, 2, false);
this.VerifyEdge(2f, 3f, (8.5f, 2.5f), 1, 0, false);
this.VerifyEdge(3f, 4f, (9f, 3.5f), 1, 0, false);
this.VerifyEdge(4f, 5f, (9.5f, 4.5f), 1, 0, false);
this.VerifyEdge(5f, 6f, (9.5f, 5.5f), 1, 1, false);
this.VerifyEdge(6f, 7f, (8f, 6.5f), 2, 2, false);
this.VerifyEdge(7f, 8f, (9f, 7.5f), 1, 1, false);
this.VerifyEdge(7f, 8f, (6.5f, 7.5f), 1, 1, true);
this.VerifyEdge(7f, 8f, (5.5f, 7.5f), 1, 1, false);
this.VerifyEdge(7f, 8f, (4.5f, 7.5f), 1, 1, true);
this.VerifyEdge(7f, 8f, (3.5f, 7.5f), 1, 1, false);
this.VerifyEdge(6f, 8f, (2f, 7f), 0, 1, true);
this.VerifyEdge(5f, 6f, (2.5f, 5.5f), 2, 1, true);
this.VerifyEdge(4f, 5f, (2f, 4.5f), 0, 1, true);
this.VerifyEdge(3f, 4f, (1.5f, 3.5f), 0, 1, true);
this.VerifyEdge(2f, 3f, (1f, 1.5f), 1, 1, true);
using var edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16);

Assert.Equal(19, edges.Edges.Length);

this.VerifyEdge(edges, 1f, 2f, (2.5f, 1.5f), 1, 2, true);
this.VerifyEdge(edges, 1f, 3f, (3.5f, 2f), 1, 1, false);
this.VerifyEdge(edges, 1f, 3f, (5f, 2f), 1, 1, true);
this.VerifyEdge(edges, 1f, 2f, (6.5f, 1.5f), 1, 2, false);
this.VerifyEdge(edges, 2f, 3f, (8.5f, 2.5f), 1, 0, false);
this.VerifyEdge(edges, 3f, 4f, (9f, 3.5f), 1, 0, false);
this.VerifyEdge(edges, 4f, 5f, (9.5f, 4.5f), 1, 0, false);
this.VerifyEdge(edges, 5f, 6f, (9.5f, 5.5f), 1, 1, false);
this.VerifyEdge(edges, 6f, 7f, (8f, 6.5f), 2, 2, false);
this.VerifyEdge(edges, 7f, 8f, (9f, 7.5f), 1, 1, false);
this.VerifyEdge(edges, 7f, 8f, (6.5f, 7.5f), 1, 1, true);
this.VerifyEdge(edges, 7f, 8f, (5.5f, 7.5f), 1, 1, false);
this.VerifyEdge(edges, 7f, 8f, (4.5f, 7.5f), 1, 1, true);
this.VerifyEdge(edges, 7f, 8f, (3.5f, 7.5f), 1, 1, false);
this.VerifyEdge(edges, 6f, 8f, (2f, 7f), 0, 1, true);
this.VerifyEdge(edges, 5f, 6f, (2.5f, 5.5f), 2, 1, true);
this.VerifyEdge(edges, 4f, 5f, (2f, 4.5f), 0, 1, true);
this.VerifyEdge(edges, 3f, 4f, (1.5f, 3.5f), 0, 1, true);
this.VerifyEdge(edges, 2f, 3f, (1f, 1.5f), 1, 1, true);
}

[Fact]
Expand All @@ -114,54 +114,54 @@ public void ComplexPolygon()
IPath polygon = contour.Clip(hole);
DebugDraw.Polygon(polygon, 1, 100);

this.edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16);
using var edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16);

Assert.Equal(8, this.edges.Count);
Assert.Equal(8, edges.Count);

this.VerifyEdge(1, 4, (1, 2), 1, 1, true);
this.VerifyEdge(1, 2, (4, 1.5f), 1, 2, false);
this.VerifyEdge(4, 5, (2, 4.5f), 2, 1, true);
this.VerifyEdge(2, 5, (5, 3f), 1, 1, false);
this.VerifyEdge(edges, 1, 4, (1, 2), 1, 1, true);
this.VerifyEdge(edges, 1, 2, (4, 1.5f), 1, 2, false);
this.VerifyEdge(edges, 4, 5, (2, 4.5f), 2, 1, true);
this.VerifyEdge(edges, 2, 5, (5, 3f), 1, 1, false);

this.VerifyEdge(2, 3, (2, 2.5f), 2, 2, false);
this.VerifyEdge(2, 3, (3.5f, 2.5f), 2, 1, true);
this.VerifyEdge(3, 4, (3, 3.5f), 1, 2, false);
this.VerifyEdge(3, 4, (4, 3.5f), 0, 2, true);
this.VerifyEdge(edges, 2, 3, (2, 2.5f), 2, 2, false);
this.VerifyEdge(edges, 2, 3, (3.5f, 2.5f), 2, 1, true);
this.VerifyEdge(edges, 3, 4, (3, 3.5f), 1, 2, false);
this.VerifyEdge(edges, 3, 4, (4, 3.5f), 0, 2, true);
}

[Fact]
public void NumericCornerCase_C()
{
this.edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.C, MemoryAllocator, 4);
Assert.Equal(2, this.edges.Count);
this.VerifyEdge(3.5f, 4f, (2f, 3.75f), 1, 1, true);
this.VerifyEdge(3.5f, 4f, (8f, 3.75f), 1, 1, false);
using var edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.C, MemoryAllocator, 4);
Assert.Equal(2, edges.Count);
this.VerifyEdge(edges, 3.5f, 4f, (2f, 3.75f), 1, 1, true);
this.VerifyEdge(edges, 3.5f, 4f, (8f, 3.75f), 1, 1, false);
}

[Fact]
public void NumericCornerCase_D()
{
this.edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.D, MemoryAllocator, 4);
Assert.Equal(5, this.edges.Count);
using var edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.D, MemoryAllocator, 4);
Assert.Equal(5, edges.Count);

this.VerifyEdge(3.25f, 4f, (12f, 3.75f), 1, 1, true);
this.VerifyEdge(3.25f, 3.5f, (15f, 3.375f), 1, 0, false);
this.VerifyEdge(3.5f, 4f, (18f, 3.75f), 1, 1, false);
this.VerifyEdge(edges, 3.25f, 4f, (12f, 3.75f), 1, 1, true);
this.VerifyEdge(edges, 3.25f, 3.5f, (15f, 3.375f), 1, 0, false);
this.VerifyEdge(edges, 3.5f, 4f, (18f, 3.75f), 1, 1, false);

// TODO: verify 2 more edges
}

[Fact]
public void NumericCornerCase_H_ShouldCollapseNearZeroEdge()
{
this.edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.H, MemoryAllocator, 4);
using var edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.H, MemoryAllocator, 4);

Assert.Equal(3, this.edges.Count);
this.VerifyEdge(1.75f, 2f, (15f, 1.875f), 1, 1, true);
this.VerifyEdge(1.75f, 2.25f, (16f, 2f), 1, 1, false);
Assert.Equal(3, edges.Count);
this.VerifyEdge(edges, 1.75f, 2f, (15f, 1.875f), 1, 1, true);
this.VerifyEdge(edges, 1.75f, 2.25f, (16f, 2f), 1, 1, false);

// this places two dummy points:
this.VerifyEdge(2f, 2.25f, (15f, 2.125f), 2, 1, true);
this.VerifyEdge(edges, 2f, 2.25f, (15f, 2.125f), 2, 1, true);
}

private static FuzzyFloat F(float value, float eps) => new FuzzyFloat(value, eps);
Expand Down
4 changes: 2 additions & 2 deletions tests/ImageSharp.Drawing.Tests/TestFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public class TestDecoder : IImageDecoder

public int HeaderSize => this.testFormat.HeaderSize;

public Image<TPixel> Decode<TPixel>(Configuration config, Stream stream)
public Image<TPixel> Decode<TPixel>(Configuration config, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
var ms = new MemoryStream();
Expand All @@ -218,7 +218,7 @@ public Image<TPixel> Decode<TPixel>(Configuration config, Stream stream)

public bool IsSupportedFileFormat(Span<byte> header) => this.testFormat.IsSupportedFileFormat(header);

public Image Decode(Configuration configuration, Stream stream) => this.Decode<TestPixelForAgnosticDecode>(configuration, stream);
public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode<TestPixelForAgnosticDecode>(configuration, stream, cancellationToken);

public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using SixLabors.ImageSharp.Diagnostics;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ private static void FromRgba64Bytes<TPixel>(Configuration configuration, Span<by

public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, PixelFormats.IPixel<TPixel>
=> Task.FromResult(this.Decode<TPixel>(configuration, stream));
=> Task.FromResult(this.Decode<TPixel>(configuration, stream, cancellationToken));

public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, PixelFormats.IPixel<TPixel>
{
var bmpReadDefines = new BmpReadDefines
Expand Down Expand Up @@ -105,7 +105,7 @@ public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
return new Image<TPixel>(configuration, new ImageMetadata(), framesList);
}

public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode<Rgba32>(configuration, stream, cancellationToken);

public async Task<Image> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
=> await this.DecodeAsync<Rgba32>(configuration, stream, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class SystemDrawingReferenceDecoder : IImageDecoder, IImageInfoDetector
{
public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder();

public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var sourceBitmap = new System.Drawing.Bitmap(stream))
Expand Down Expand Up @@ -44,7 +44,7 @@ public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
}
}

public IImageInfo Identify(Configuration configuration, Stream stream)
public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
using (var sourceBitmap = new System.Drawing.Bitmap(stream))
{
Expand All @@ -56,8 +56,8 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
=> throw new System.NotImplementedException();

public Image Decode(Configuration configuration, Stream stream)
=> this.Decode<Rgba32>(configuration, stream);
public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(configuration, stream, cancellationToken);

public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
Expand Down
Loading