44using System . Drawing ;
55using System . Drawing . Drawing2D ;
66using System . Numerics ;
7+ using System . Runtime . InteropServices ;
78using BenchmarkDotNet . Attributes ;
89using GeoJSON . Net . Feature ;
910using Newtonsoft . Json ;
11+ using SharpBlaze ;
1012using SixLabors . ImageSharp . Drawing . Processing ;
13+ using SixLabors . ImageSharp . Drawing . Shapes . PolygonClipper ;
1114using SixLabors . ImageSharp . Drawing . Tests ;
1215using SixLabors . ImageSharp . PixelFormats ;
1316using SixLabors . ImageSharp . Processing ;
1417using SkiaSharp ;
18+ using BlazeMatrix = SharpBlaze . Matrix ;
1519using SDPointF = System . Drawing . PointF ;
1620
1721namespace SixLabors . ImageSharp . Drawing . Benchmarks . Drawing ;
@@ -21,13 +25,20 @@ public abstract class DrawPolygon
2125 private PointF [ ] [ ] points ;
2226
2327 private Image < Rgba32 > image ;
28+ private bool isImageSharp ;
2429
2530 private SDPointF [ ] [ ] sdPoints ;
2631 private Bitmap sdBitmap ;
2732 private Graphics sdGraphics ;
33+ private bool isSystem ;
2834
2935 private SKPath skPath ;
3036 private SKSurface skSurface ;
37+ private bool isSkia ;
38+
39+ private Executor executor ;
40+ private DestinationImage < TileDescriptor_8x16 > vecDst ;
41+ private bool isBlaze ;
3142
3243 protected abstract int Width { get ; }
3344
@@ -49,9 +60,11 @@ public void Setup()
4960 this . sdPoints = this . points . Select ( pts => pts . Select ( p => new SDPointF ( p . X , p . Y ) ) . ToArray ( ) ) . ToArray ( ) ;
5061
5162 this . skPath = new SKPath ( ) ;
63+
5264 foreach ( PointF [ ] ptArr in this . points . Where ( pts => pts . Length > 2 ) )
5365 {
5466 this . skPath . MoveTo ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ;
67+
5568 for ( int i = 1 ; i < ptArr . Length ; i ++ )
5669 {
5770 this . skPath . LineTo ( ptArr [ i ] . X , ptArr [ i ] . Y ) ;
@@ -60,6 +73,11 @@ public void Setup()
6073 this . skPath . LineTo ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ;
6174 }
6275
76+ this . executor = new SerialExecutor ( ) ;
77+ this . vecDst = new DestinationImage < TileDescriptor_8x16 > ( ) ;
78+ this . vecDst . UpdateSize ( new IntSize ( this . Width , this . Height ) ) ;
79+ this . vecDst . ClearImage ( ) ;
80+
6381 this . image = new Image < Rgba32 > ( this . Width , this . Height ) ;
6482 this . sdBitmap = new Bitmap ( this . Width , this . Height ) ;
6583 this . sdGraphics = Graphics . FromImage ( this . sdBitmap ) ;
@@ -69,8 +87,44 @@ public void Setup()
6987 }
7088
7189 [ GlobalCleanup ]
72- public void Cleanup ( )
90+ public unsafe void Cleanup ( )
7391 {
92+ string dir = "Images/DrawPolygon" ;
93+ Directory . CreateDirectory ( dir ) ;
94+
95+ if ( this . isImageSharp )
96+ {
97+ this . image . SaveAsPng ( System . IO . Path . Combine ( dir , "ImageSharp.png" ) ) ;
98+ this . isImageSharp = false ;
99+ }
100+
101+ if ( this . isBlaze )
102+ {
103+ using var blazeImage = Image . WrapMemory < Rgba32 > (
104+ this . vecDst . GetImageData ( ) ,
105+ this . vecDst . GetBytesPerRow ( ) * this . vecDst . GetImageHeight ( ) ,
106+ this . vecDst . GetImageWidth ( ) ,
107+ this . vecDst . GetImageHeight ( ) ) ;
108+
109+ blazeImage . SaveAsPng ( System . IO . Path . Combine ( dir , "Blaze.png" ) ) ;
110+ this . isBlaze = false ;
111+ }
112+
113+ if ( this . isSystem )
114+ {
115+ this . sdBitmap . Save ( System . IO . Path . Combine ( dir , "SystemDrawing.png" ) ) ;
116+ this . isSystem = false ;
117+ }
118+
119+ if ( this . isSkia )
120+ {
121+ using var skSnapshot = this . skSurface . Snapshot ( ) ;
122+ using var skEncoded = skSnapshot . Encode ( ) ;
123+ using var skFile = new FileStream ( System . IO . Path . Combine ( dir , "SkiaSharp.png" ) , FileMode . Create ) ;
124+ skEncoded . SaveTo ( skFile ) ;
125+ this . isSkia = false ;
126+ }
127+
74128 this . image . Dispose ( ) ;
75129 this . sdGraphics . Dispose ( ) ;
76130 this . sdBitmap . Dispose ( ) ;
@@ -87,18 +141,23 @@ public void SystemDrawing()
87141 {
88142 this . sdGraphics . DrawPolygon ( pen , loop ) ;
89143 }
144+
145+ this . isSystem = true ;
90146 }
91147
92148 [ Benchmark ]
93149 public void ImageSharp ( )
94- => this . image . Mutate (
150+ {
151+ this . image . Mutate (
95152 c =>
96153 {
97154 foreach ( PointF [ ] loop in this . points )
98155 {
99156 c . DrawPolygon ( Color . White , this . Thickness , loop ) ;
100157 }
101158 } ) ;
159+ this . isImageSharp = true ;
160+ }
102161
103162 [ Benchmark ( Baseline = true ) ]
104163 public void SkiaSharp ( )
@@ -112,6 +171,84 @@ public void SkiaSharp()
112171 } ;
113172
114173 this . skSurface . Canvas . DrawPath ( this . skPath , paint ) ;
174+ this . isSkia = true ;
175+ }
176+
177+ [ Benchmark ]
178+ public void Blaze ( )
179+ {
180+ VectorImageBuilder builder = new ( ) ;
181+
182+ foreach ( PointF [ ] loop in this . points )
183+ {
184+ var loopPolygon = new Polygon ( loop ) ;
185+ var brush = new Processing . SolidBrush ( Color . White ) ;
186+ var pen = new SolidPen ( brush , this . Thickness ) ;
187+ List < List < PointF > > outline = GenerateOutlineList ( loopPolygon , pen . StrokeWidth , pen . JointStyle , pen . EndCapStyle ) ;
188+
189+ foreach ( List < PointF > line in outline )
190+ {
191+ Span < PointF > ptArr = CollectionsMarshal . AsSpan ( line ) ;
192+
193+ builder . MoveTo ( new FloatPoint ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ) ;
194+ for ( int i = 1 ; i < ptArr . Length ; i ++ )
195+ {
196+ builder . LineTo ( new FloatPoint ( ptArr [ i ] . X , ptArr [ i ] . Y ) ) ;
197+ }
198+
199+ builder . LineTo ( new FloatPoint ( ptArr [ 0 ] . X , ptArr [ 1 ] . Y ) ) ;
200+
201+ builder . Close ( ) ;
202+ }
203+ }
204+
205+ VectorImage image = builder . ToVectorImage ( Color . White . ToPixel < Rgba32 > ( ) . PackedValue ) ;
206+
207+ this . vecDst . DrawImage ( image , BlazeMatrix . Identity , this . executor ) ;
208+
209+ this . isBlaze = true ;
210+ }
211+
212+ private static List < List < PointF > > GenerateOutlineList ( IPath path , float width , JointStyle jointStyle , EndCapStyle endCapStyle )
213+ {
214+ if ( width <= 0 )
215+ {
216+ return [ ] ;
217+ }
218+
219+ List < List < PointF > > stroked = [ ] ;
220+
221+ PolygonStroker stroker = new ( ) { Width = width , LineJoin = GetLineJoin ( jointStyle ) , LineCap = GetLineCap ( endCapStyle ) } ;
222+ foreach ( ISimplePath simplePath in path . Flatten ( ) )
223+ {
224+ bool isClosed = simplePath . IsClosed || endCapStyle is EndCapStyle . Polygon or EndCapStyle . Joined ;
225+ if ( simplePath is Path concretePath )
226+ {
227+ if ( concretePath . LineSegments . Count == 1 && concretePath . LineSegments [ 0 ] is LinearLineSegment lineSegment )
228+ {
229+ stroked . Add ( stroker . ProcessPath ( lineSegment . Flatten ( ) . Span , isClosed ) ) ;
230+ continue ;
231+ }
232+ }
233+
234+ stroked . Add ( stroker . ProcessPath ( simplePath . Points . Span , isClosed ) ) ;
235+ }
236+
237+ return stroked ;
238+
239+ static LineJoin GetLineJoin ( JointStyle value ) => value switch
240+ {
241+ JointStyle . Square => LineJoin . BevelJoin ,
242+ JointStyle . Round => LineJoin . RoundJoin ,
243+ _ => LineJoin . MiterJoin ,
244+ } ;
245+
246+ static LineCap GetLineCap ( EndCapStyle value ) => value switch
247+ {
248+ EndCapStyle . Round => LineCap . Round ,
249+ EndCapStyle . Square => LineCap . Square ,
250+ _ => LineCap . Butt ,
251+ } ;
115252 }
116253}
117254
0 commit comments