@@ -110,37 +110,16 @@ private bool TryCreateEdgeBuffer<TPixel>(
110110
111111 int bandCount = ( int ) DivideRoundUp ( interest . Height , TileHeight ) ;
112112
113- IMemoryOwner < GpuEdge > ? defEdgeOwner ;
114- int edgeCount ;
115- IMemoryOwner < uint > ? defBandOffsets ;
116- bool edgeSuccess ;
117- if ( definition . IsStroke )
118- {
119- edgeSuccess = TryBuildStrokeEdges (
120- flushContext . MemoryAllocator ,
121- definition . Path ,
122- in interest ,
123- definition . RasterizerOptions . SamplingOrigin ,
124- definition . StrokeWidth ,
125- ( float ) ( definition . StrokeOptions ? . MiterLimit ?? 4.0 ) ,
126- bandCount ,
127- out defEdgeOwner ,
128- out edgeCount ,
129- out defBandOffsets ,
130- out error ) ;
131- }
132- else
133- {
134- edgeSuccess = TryBuildFixedPointEdges (
135- flushContext . MemoryAllocator ,
136- definition . Path ,
137- in interest ,
138- definition . RasterizerOptions . SamplingOrigin ,
139- out defEdgeOwner ,
140- out edgeCount ,
141- out defBandOffsets ,
142- out error ) ;
143- }
113+ // All commands are fills by this point — strokes were expanded by the batcher.
114+ bool edgeSuccess = TryBuildFixedPointEdges (
115+ flushContext . MemoryAllocator ,
116+ definition . Geometry ,
117+ in interest ,
118+ definition . RasterizerOptions . SamplingOrigin ,
119+ out IMemoryOwner < GpuEdge > ? defEdgeOwner ,
120+ out int edgeCount ,
121+ out IMemoryOwner < uint > ? defBandOffsets ,
122+ out error ) ;
144123
145124 if ( ! edgeSuccess )
146125 {
@@ -154,70 +133,16 @@ private bool TryCreateEdgeBuffer<TPixel>(
154133
155134 geometries [ i ] = new DefinitionGeometry ( defEdgeOwner , edgeCount , bandCount , defBandOffsets ) ;
156135
157- if ( definition . IsStroke && edgeCount > 0 )
158- {
159- // Centerline edges are band-sorted. Create one StrokeExpandCommand per band
160- // so the GPU expand shader writes outline edges into per-band output slots.
161- // This produces band-sorted outline edges compatible with the fill rasterizer.
162- Span < uint > clBandOffsets = defBandOffsets ! . Memory . Span ;
163- LineCap defLineCap = definition . StrokeOptions ? . LineCap ?? LineCap . Butt ;
164- LineJoin defLineJoin = definition . StrokeOptions ? . LineJoin ?? LineJoin . Bevel ;
165- int outlineEdgesPerCenterline = ComputeOutlineEdgesPerCenterline (
166- definition . StrokeWidth * 0.5f , defLineJoin , defLineCap ) ;
167- strokeCommands ??= [ ] ;
168- for ( int b = 0 ; b < bandCount ; b ++ )
169- {
170- uint bandStart = clBandOffsets [ b ] ;
171- uint bandEnd = b + 1 < clBandOffsets . Length ? clBandOffsets [ b + 1 ] : ( uint ) edgeCount ;
172- uint bandEdgeCount = bandEnd - bandStart ;
173- if ( bandEdgeCount == 0 )
174- {
175- continue ;
176- }
177-
178- int bandOutlineMax = ( int ) bandEdgeCount * outlineEdgesPerCenterline ;
179- strokeCommands . Add ( new StrokeExpandCommand (
180- i ,
181- ( uint ) runningEdgeStart + bandStart ,
182- bandEdgeCount ,
183- definition . StrokeWidth * 0.5f ,
184- ( uint ) defLineCap ,
185- ( uint ) defLineJoin ,
186- ( float ) ( definition . StrokeOptions ? . MiterLimit ?? 4.0 ) ,
187- bandOutlineMax ,
188- Band : b ) ) ;
189-
190- totalOutlineSlots += bandOutlineMax ;
191- }
192-
193- totalStrokeCenterlineEdges += edgeCount ;
194-
195- // Placeholder EdgePlacement — will be updated after outline space is allocated.
196- int bandOffsetEntriesForDef = bandCount + 1 ;
197- edgePlacementsSpan [ i ] = new EdgePlacement (
198- ( uint ) runningEdgeStart ,
199- ( uint ) edgeCount ,
200- fillRule ,
201- ( uint ) runningBandOffset ,
202- ( uint ) bandCount ) ;
203-
204- runningEdgeStart += edgeCount ;
205- runningBandOffset += bandOffsetEntriesForDef ;
206- }
207- else
208- {
209- int bandOffsetEntriesForDef = bandCount + 1 ;
210-
211- edgePlacementsSpan [ i ] = new EdgePlacement (
212- ( uint ) runningEdgeStart ,
213- ( uint ) edgeCount ,
214- fillRule ,
215- ( uint ) runningBandOffset ,
216- ( uint ) bandCount ) ;
136+ int bandOffsetEntriesForDef = bandCount + 1 ;
137+ edgePlacementsSpan [ i ] = new EdgePlacement (
138+ ( uint ) runningEdgeStart ,
139+ ( uint ) edgeCount ,
140+ fillRule ,
141+ ( uint ) runningBandOffset ,
142+ ( uint ) bandCount ) ;
217143
218- runningEdgeStart += edgeCount ;
219- runningBandOffset += bandOffsetEntriesForDef ;
220- }
144+ runningEdgeStart += edgeCount ;
145+ runningBandOffset += bandOffsetEntriesForDef ;
221146 }
222147
223148 totalEdgeCount = runningEdgeStart ;
@@ -597,7 +522,7 @@ private void DisposeCoverageResources()
597522 /// </remarks>
598523 private static bool TryBuildFixedPointEdges (
599524 MemoryAllocator allocator ,
600- IPath path ,
525+ PreparedGeometry geometry ,
601526 in Rectangle interest ,
602527 RasterizerSamplingOrigin samplingOrigin ,
603528 out IMemoryOwner < GpuEdge > ? edgeOwner ,
@@ -616,55 +541,44 @@ private static bool TryBuildFixedPointEdges(
616541 int interestX = interest . X ;
617542 int interestY = interest . Y ;
618543 int bandCount = ( int ) DivideRoundUp ( height , TileHeight ) ;
619-
620- // Flatten once and reuse the list for both passes.
621- IEnumerable < ISimplePath > flattened = path . Flatten ( ) ;
622- IReadOnlyList < ISimplePath > contours = flattened is IReadOnlyList < ISimplePath > list
623- ? list
624- : [ .. flattened ] ;
544+ ReadOnlySpan < PreparedLineSegment > segments = geometry . Segments ;
625545
626546 // Pass 1: Count edges per band.
627547 int totalSubEdges = 0 ;
628548 using IMemoryOwner < int > bandCountsOwner = allocator . Allocate < int > ( bandCount , AllocationOptions . Clean ) ;
629549 Span < int > bandCounts = bandCountsOwner . Memory . Span ;
630550
631- for ( int c = 0 ; c < contours . Count ; c ++ )
551+ for ( int i = 0 ; i < segments . Length ; i ++ )
632552 {
633- ISimplePath simplePath = contours [ c ] ;
634- ReadOnlySpan < PointF > points = simplePath . Points . Span ;
635- int segmentCount = simplePath . IsClosed ? points . Length : points . Length - 1 ;
553+ PreparedLineSegment segment = segments [ i ] ;
554+ PointF p0 = segment . P0 ;
555+ PointF p1 = segment . P1 ;
636556
637- for ( int j = 0 ; j < segmentCount ; j ++ )
557+ int y0 = ( int ) MathF . Round ( ( ( p0 . Y - interestY ) + samplingOffsetY ) * FixedOne ) ;
558+ int y1 = ( int ) MathF . Round ( ( ( p1 . Y - interestY ) + samplingOffsetY ) * FixedOne ) ;
559+ if ( y0 == y1 )
638560 {
639- PointF p0 = points [ j ] ;
640- PointF p1 = points [ j + 1 == points . Length ? 0 : j + 1 ] ;
641-
642- int y0 = ( int ) MathF . Round ( ( ( p0 . Y - interestY ) + samplingOffsetY ) * FixedOne ) ;
643- int y1 = ( int ) MathF . Round ( ( ( p1 . Y - interestY ) + samplingOffsetY ) * FixedOne ) ;
644- if ( y0 == y1 )
645- {
646- continue ;
647- }
561+ continue ;
562+ }
648563
649- int yMinFixed = Math . Min ( y0 , y1 ) ;
650- int yMaxFixed = Math . Max ( y0 , y1 ) ;
651- int minRow = Math . Max ( 0 , yMinFixed >> FixedShift ) ;
652- int maxRow = Math . Min ( height - 1 , ( yMaxFixed - 1 ) >> FixedShift ) ;
564+ int yMinFixed = Math . Min ( y0 , y1 ) ;
565+ int yMaxFixed = Math . Max ( y0 , y1 ) ;
566+ int minRow = Math . Max ( 0 , yMinFixed >> FixedShift ) ;
567+ int maxRow = Math . Min ( height - 1 , ( yMaxFixed - 1 ) >> FixedShift ) ;
653568
654- if ( minRow > maxRow )
655- {
656- continue ;
657- }
658-
659- int minBand = minRow / TileHeight ;
660- int maxBand = maxRow / TileHeight ;
661- for ( int b = minBand ; b <= maxBand ; b ++ )
662- {
663- bandCounts [ b ] ++ ;
664- }
569+ if ( minRow > maxRow )
570+ {
571+ continue ;
572+ }
665573
666- totalSubEdges += maxBand - minBand + 1 ;
574+ int minBand = minRow / TileHeight ;
575+ int maxBand = maxRow / TileHeight ;
576+ for ( int b = minBand ; b <= maxBand ; b ++ )
577+ {
578+ bandCounts [ b ] ++ ;
667579 }
580+
581+ totalSubEdges += maxBand - minBand + 1 ;
668582 }
669583
670584 if ( totalSubEdges == 0 )
@@ -690,47 +604,42 @@ private static bool TryBuildFixedPointEdges(
690604 using IMemoryOwner < uint > writeCursorsOwner = allocator . Allocate < uint > ( bandCount , AllocationOptions . Clean ) ;
691605 Span < uint > writeCursors = writeCursorsOwner . Memory . Span ;
692606
693- for ( int c = 0 ; c < contours . Count ; c ++ )
607+ for ( int i = 0 ; i < segments . Length ; i ++ )
694608 {
695- ISimplePath simplePath = contours [ c ] ;
696- ReadOnlySpan < PointF > points = simplePath . Points . Span ;
697- int segmentCount = simplePath . IsClosed ? points . Length : points . Length - 1 ;
698- for ( int j = 0 ; j < segmentCount ; j ++ )
609+ PreparedLineSegment segment = segments [ i ] ;
610+ PointF p0 = segment . P0 ;
611+ PointF p1 = segment . P1 ;
612+ float fx0 = ( p0 . X - interestX ) + samplingOffsetX ;
613+ float fy0 = ( p0 . Y - interestY ) + samplingOffsetY ;
614+ float fx1 = ( p1 . X - interestX ) + samplingOffsetX ;
615+ float fy1 = ( p1 . Y - interestY ) + samplingOffsetY ;
616+
617+ int x0 = ( int ) MathF . Round ( fx0 * FixedOne ) ;
618+ int y0 = ( int ) MathF . Round ( fy0 * FixedOne ) ;
619+ int x1 = ( int ) MathF . Round ( fx1 * FixedOne ) ;
620+ int y1 = ( int ) MathF . Round ( fy1 * FixedOne ) ;
621+ if ( y0 == y1 )
699622 {
700- PointF p0 = points [ j ] ;
701- PointF p1 = points [ j + 1 == points . Length ? 0 : j + 1 ] ;
702- float fx0 = ( p0 . X - interestX ) + samplingOffsetX ;
703- float fy0 = ( p0 . Y - interestY ) + samplingOffsetY ;
704- float fx1 = ( p1 . X - interestX ) + samplingOffsetX ;
705- float fy1 = ( p1 . Y - interestY ) + samplingOffsetY ;
706-
707- int x0 = ( int ) MathF . Round ( fx0 * FixedOne ) ;
708- int y0 = ( int ) MathF . Round ( fy0 * FixedOne ) ;
709- int x1 = ( int ) MathF . Round ( fx1 * FixedOne ) ;
710- int y1 = ( int ) MathF . Round ( fy1 * FixedOne ) ;
711- if ( y0 == y1 )
712- {
713- continue ;
714- }
623+ continue ;
624+ }
715625
716- int yMinFixed = Math . Min ( y0 , y1 ) ;
717- int yMaxFixed = Math . Max ( y0 , y1 ) ;
718- int minRow = Math . Max ( 0 , yMinFixed >> FixedShift ) ;
719- int maxRow = Math . Min ( height - 1 , ( yMaxFixed - 1 ) >> FixedShift ) ;
626+ int yMinFixed = Math . Min ( y0 , y1 ) ;
627+ int yMaxFixed = Math . Max ( y0 , y1 ) ;
628+ int minRow = Math . Max ( 0 , yMinFixed >> FixedShift ) ;
629+ int maxRow = Math . Min ( height - 1 , ( yMaxFixed - 1 ) >> FixedShift ) ;
720630
721- if ( minRow > maxRow )
722- {
723- continue ;
724- }
631+ if ( minRow > maxRow )
632+ {
633+ continue ;
634+ }
725635
726- GpuEdge edge = new ( ) { X0 = x0 , Y0 = y0 , X1 = x1 , Y1 = y1 } ;
727- int minBand = minRow / TileHeight ;
728- int maxBand = maxRow / TileHeight ;
729- for ( int band = minBand ; band <= maxBand ; band ++ )
730- {
731- finalSpan [ ( int ) ( offsets [ band ] + writeCursors [ band ] ) ] = edge ;
732- writeCursors [ band ] ++ ;
733- }
636+ GpuEdge edge = new ( ) { X0 = x0 , Y0 = y0 , X1 = x1 , Y1 = y1 } ;
637+ int minBand = minRow / TileHeight ;
638+ int maxBand = maxRow / TileHeight ;
639+ for ( int band = minBand ; band <= maxBand ; band ++ )
640+ {
641+ finalSpan [ ( int ) ( offsets [ band ] + writeCursors [ band ] ) ] = edge ;
642+ writeCursors [ band ] ++ ;
734643 }
735644 }
736645
0 commit comments