44using System . Drawing ;
55using System . Drawing . Drawing2D ;
66using System . Numerics ;
7- using System . Runtime . InteropServices ;
8- using System . Runtime . Versioning ;
97using BenchmarkDotNet . Attributes ;
108using GeoJSON . Net . Feature ;
119using Newtonsoft . Json ;
12- using SharpBlaze ;
1310using SixLabors . ImageSharp . Drawing . Processing ;
14- using SixLabors . ImageSharp . Drawing . Shapes . PolygonClipper ;
1511using SixLabors . ImageSharp . Drawing . Tests ;
1612using SixLabors . ImageSharp . PixelFormats ;
1713using SixLabors . ImageSharp . Processing ;
1814using SkiaSharp ;
19- using BlazeMatrix = SharpBlaze . Matrix ;
2015using Pen = System . Drawing . Pen ;
2116using SDPointF = System . Drawing . PointF ;
2217
2318namespace SixLabors . ImageSharp . Drawing . Benchmarks . Drawing ;
2419
2520public abstract class DrawPolygon
2621{
27- private string artifactDir ;
28-
2922 private PointF [ ] [ ] points ;
3023
3124 private Image < Rgba32 > image ;
32- private bool savedImageSharp ;
3325
3426 private SDPointF [ ] [ ] sdPoints ;
3527 private Bitmap sdBitmap ;
3628 private Graphics sdGraphics ;
37- private bool savedSd ;
3829
3930 private SKPath skPath ;
4031 private SKSurface skSurface ;
41- private bool savedSkia ;
42-
43- private Executor executor ;
44- private DestinationImage < TileDescriptor_8x16 > vecDst ;
45- private bool savedBlaze ;
4632
4733 protected abstract int Width { get ; }
4834
4935 protected abstract int Height { get ; }
5036
5137 protected abstract float Thickness { get ; }
5238
53- protected abstract string BenchName { get ; }
54-
5539 protected virtual PointF [ ] [ ] GetPoints ( FeatureCollection features ) =>
5640 features . Features . SelectMany ( f => PolygonFactory . GetGeoJsonPoints ( f , Matrix3x2 . CreateScale ( 60 , 60 ) ) ) . ToArray ( ) ;
5741
58- private string GetArtifactPath ( string name ) => System . IO . Path . Combine ( this . artifactDir , name ) ;
59-
6042 [ GlobalSetup ]
6143 public void Setup ( )
6244 {
63- this . artifactDir = TestEnvironment . GetFullPath ( $ "artifacts\\ { BenchName } ") ;
64- Directory . CreateDirectory ( this . artifactDir ) ;
65-
6645 string jsonContent = File . ReadAllText ( TestFile . GetInputFileFullPath ( TestImages . GeoJson . States ) ) ;
6746
6847 FeatureCollection featureCollection = JsonConvert . DeserializeObject < FeatureCollection > ( jsonContent ) ;
@@ -71,11 +50,9 @@ public void Setup()
7150 this . sdPoints = this . points . Select ( pts => pts . Select ( p => new SDPointF ( p . X , p . Y ) ) . ToArray ( ) ) . ToArray ( ) ;
7251
7352 this . skPath = new SKPath ( ) ;
74-
7553 foreach ( PointF [ ] ptArr in this . points . Where ( pts => pts . Length > 2 ) )
7654 {
7755 this . skPath . MoveTo ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ;
78-
7956 for ( int i = 1 ; i < ptArr . Length ; i ++ )
8057 {
8158 this . skPath . LineTo ( ptArr [ i ] . X , ptArr [ i ] . Y ) ;
@@ -84,11 +61,6 @@ public void Setup()
8461 this . skPath . LineTo ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ;
8562 }
8663
87- this . executor = new SerialExecutor ( ) ;
88- this . vecDst = new DestinationImage < TileDescriptor_8x16 > ( ) ;
89- this . vecDst . UpdateSize ( new IntSize ( this . Width , this . Height ) ) ;
90- this . vecDst . ClearImage ( ) ;
91-
9264 this . image = new Image < Rgba32 > ( this . Width , this . Height ) ;
9365 this . sdBitmap = new Bitmap ( this . Width , this . Height ) ;
9466 this . sdGraphics = Graphics . FromImage ( this . sdBitmap ) ;
@@ -107,7 +79,6 @@ public void Cleanup()
10779 this . skPath . Dispose ( ) ;
10880 }
10981
110- [ SupportedOSPlatform ( "windows" ) ]
11182 [ Benchmark ]
11283 public void SystemDrawing ( )
11384 {
@@ -117,18 +88,11 @@ public void SystemDrawing()
11788 {
11889 this . sdGraphics . DrawPolygon ( pen , loop ) ;
11990 }
120-
121- if ( ! this . savedSd )
122- {
123- this . sdBitmap . Save ( this . GetArtifactPath ( "SystemDrawing.png" ) ) ;
124- this . savedSd = true ;
125- }
12691 }
12792
12893 [ Benchmark ]
12994 public void ImageSharp ( )
130- {
131- this . image . Mutate (
95+ => this . image . Mutate (
13296 c =>
13397 {
13498 foreach ( PointF [ ] loop in this . points )
@@ -137,13 +101,6 @@ public void ImageSharp()
137101 }
138102 } ) ;
139103
140- if ( ! this . savedImageSharp )
141- {
142- this . image . SaveAsPng ( this . GetArtifactPath ( "ImageSharp.png" ) ) ;
143- this . savedImageSharp = true ;
144- }
145- }
146-
147104 [ Benchmark ( Baseline = true ) ]
148105 public void SkiaSharp ( )
149106 {
@@ -156,134 +113,6 @@ public void SkiaSharp()
156113 } ;
157114
158115 this . skSurface . Canvas . DrawPath ( this . skPath , paint ) ;
159-
160- if ( ! this . savedSkia )
161- {
162- using var skSnapshot = this . skSurface . Snapshot ( ) ;
163- using var skEncoded = skSnapshot . Encode ( ) ;
164- using var skFile = new FileStream ( this . GetArtifactPath ( "SkiaSharp.png" ) , FileMode . Create ) ;
165- skEncoded . SaveTo ( skFile ) ;
166- this . savedSkia = true ;
167- }
168- }
169-
170- [ Benchmark ]
171- public unsafe void Blaze ( )
172- {
173- VectorImageBuilder builder = new ( ) ;
174-
175- foreach ( PointF [ ] loop in this . points )
176- {
177- var loopPolygon = new Polygon ( loop ) ;
178- var brush = new Processing . SolidBrush ( Color . White ) ;
179- var pen = new SolidPen ( brush , this . Thickness ) ;
180- List < List < PointF > > outline = GenerateOutlineList ( loopPolygon , pen . StrokeWidth , pen . JointStyle , pen . EndCapStyle ) ;
181-
182- foreach ( List < PointF > line in outline )
183- {
184- Span < PointF > ptArr = CollectionsMarshal . AsSpan ( line ) ;
185-
186- builder . MoveTo ( new FloatPoint ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ) ;
187- for ( int i = 1 ; i < ptArr . Length ; i ++ )
188- {
189- builder . LineTo ( new FloatPoint ( ptArr [ i ] . X , ptArr [ i ] . Y ) ) ;
190- }
191-
192- builder . LineTo ( new FloatPoint ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ) ;
193-
194- builder . Close ( ) ;
195- }
196- }
197-
198- VectorImage image = builder . ToVectorImage ( Color . White . ToPixel < Rgba32 > ( ) . PackedValue ) ;
199-
200- this . vecDst . DrawImage ( image , BlazeMatrix . Identity , this . executor ) ;
201-
202- if ( ! this . savedBlaze )
203- {
204- using var blazeImage = Image . WrapMemory < Rgba32 > (
205- this . vecDst . GetImageData ( ) ,
206- this . vecDst . GetBytesPerRow ( ) * this . vecDst . GetImageHeight ( ) ,
207- this . vecDst . GetImageWidth ( ) ,
208- this . vecDst . GetImageHeight ( ) ) ;
209-
210- blazeImage . SaveAsPng ( this . GetArtifactPath ( "Blaze.png" ) ) ;
211- this . savedBlaze = true ;
212- }
213- }
214-
215- public static List < List < PointF > > GenerateOutlineList ( IPath path , float width , JointStyle jointStyle , EndCapStyle endCapStyle )
216- {
217- List < List < PointF > > strokedLines = [ ] ;
218-
219- if ( width <= 0 )
220- {
221- return strokedLines ;
222- }
223-
224- PolygonStroker stroker = new ( )
225- {
226- Width = width ,
227- LineJoin = GetLineJoin ( jointStyle ) ,
228- LineCap = GetLineCap ( endCapStyle )
229- } ;
230- foreach ( ISimplePath simplePath in path . Flatten ( ) )
231- {
232- stroker . Reset ( ) ;
233-
234- int pointCount = 0 ;
235- if ( simplePath is Path concretePath )
236- {
237- foreach ( ILineSegment line in concretePath . LineSegments )
238- {
239- if ( line is CubicBezierLineSegment bezier )
240- {
241- // TODO: add bezier control points
242- ReadOnlySpan < PointF > points = line . Flatten ( ) . Span ;
243- stroker . AddLinePath ( points ) ;
244- pointCount += points . Length ;
245- }
246- else
247- {
248- ReadOnlySpan < PointF > points = line . Flatten ( ) . Span ;
249- stroker . AddLinePath ( points ) ;
250- pointCount += points . Length ;
251- }
252- }
253- }
254- else
255- {
256- ReadOnlySpan < PointF > points = simplePath . Points . Span ;
257- stroker . AddLinePath ( points ) ;
258- pointCount = points . Length ;
259- }
260-
261- bool isClosed = simplePath . IsClosed || endCapStyle is EndCapStyle . Polygon or EndCapStyle . Joined ;
262- if ( isClosed )
263- {
264- stroker . ClosePath ( ) ;
265- }
266-
267- List < PointF > lineBuilder = new ( pointCount * 4 ) ;
268- stroker . FinishPath ( lineBuilder ) ;
269- strokedLines . Add ( lineBuilder ) ;
270- }
271-
272- return strokedLines ;
273-
274- static LineJoin GetLineJoin ( JointStyle value ) => value switch
275- {
276- JointStyle . Square => LineJoin . BevelJoin ,
277- JointStyle . Round => LineJoin . RoundJoin ,
278- _ => LineJoin . MiterJoin ,
279- } ;
280-
281- static LineCap GetLineCap ( EndCapStyle value ) => value switch
282- {
283- EndCapStyle . Round => LineCap . Round ,
284- EndCapStyle . Square => LineCap . Square ,
285- _ => LineCap . Butt ,
286- } ;
287116 }
288117}
289118
@@ -294,8 +123,6 @@ public class DrawPolygonAll : DrawPolygon
294123 protected override int Height => 4800 ;
295124
296125 protected override float Thickness => 2f ;
297-
298- protected override string BenchName => nameof ( DrawPolygonAll ) ;
299126}
300127
301128public class DrawPolygonMediumThin : DrawPolygon
@@ -306,8 +133,6 @@ public class DrawPolygonMediumThin : DrawPolygon
306133
307134 protected override float Thickness => 1f ;
308135
309- protected override string BenchName => nameof ( DrawPolygonMediumThin ) ;
310-
311136 protected override PointF [ ] [ ] GetPoints ( FeatureCollection features )
312137 {
313138 Feature state = features . Features . Single ( f => ( string ) f . Properties [ "NAME" ] == "Mississippi" ) ;
@@ -321,6 +146,4 @@ protected override PointF[][] GetPoints(FeatureCollection features)
321146public class DrawPolygonMediumThick : DrawPolygonMediumThin
322147{
323148 protected override float Thickness => 10f ;
324-
325- protected override string BenchName => nameof ( DrawPolygonMediumThick ) ;
326149}
0 commit comments