11// Copyright (c) Six Labors.
22// Licensed under the Six Labors Split License.
33
4+ using SixLabors . Fonts . Rendering ;
5+ using SixLabors . ImageSharp . Drawing . Shapes . Text ;
6+
47namespace SixLabors . ImageSharp . Drawing . Processing ;
58
69/// <summary>
@@ -14,7 +17,7 @@ public static class FillPathCollectionExtensions
1417 /// <param name="source">The source image processing context.</param>
1518 /// <param name="options">The graphics options.</param>
1619 /// <param name="brush">The brush.</param>
17- /// <param name="paths">The shapes .</param>
20+ /// <param name="paths">The collection of paths .</param>
1821 /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
1922 public static IImageProcessingContext Fill (
2023 this IImageProcessingContext source ,
@@ -30,12 +33,120 @@ public static IImageProcessingContext Fill(
3033 return source ;
3134 }
3235
36+ /// <summary>
37+ /// Flood fills the image in the shape of the provided glyphs with the specified brush and pen.
38+ /// For multi-layer glyphs, a heuristic is used to decide whether to fill or stroke each layer.
39+ /// </summary>
40+ /// <param name="source">The source image processing context.</param>
41+ /// <param name="options">The graphics options.</param>
42+ /// <param name="brush">The brush.</param>
43+ /// <param name="pen">The pen.</param>
44+ /// <param name="paths">The collection of glyph paths.</param>
45+ /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
46+ public static IImageProcessingContext Fill (
47+ this IImageProcessingContext source ,
48+ DrawingOptions options ,
49+ Brush brush ,
50+ Pen pen ,
51+ IReadOnlyList < GlyphPathCollection > paths )
52+ => source . Fill ( options , brush , pen , paths , static ( gp , layer ) =>
53+ {
54+ if ( layer . Kind == GlyphLayerKind . Decoration )
55+ {
56+ // Decorations (underlines, strikethroughs, etc) are always filled.
57+ return true ;
58+ }
59+
60+ if ( layer . Kind == GlyphLayerKind . Glyph )
61+ {
62+ // Standard glyph layers are filled by default.
63+ return true ;
64+ }
65+
66+ // Default heuristic: stroke "background-like" layers (large coverage), fill others.
67+ // TODO: We should be using the area, not the bounds. Thin layers with large width/height
68+ // will be misclassified. e.g. shadows.
69+ RectangleF glyphBounds = gp . Bounds ;
70+ RectangleF layerBounds = layer . Bounds ;
71+
72+ if ( glyphBounds . Width <= 0 || glyphBounds . Height <= 0 )
73+ {
74+ return true ; // degenerate glyph, just fill
75+ }
76+
77+ // Use each dimension independently to avoid misclassifying thin layers.
78+ float rx = layerBounds . Width / glyphBounds . Width ;
79+ float ry = layerBounds . Height / glyphBounds . Height ;
80+
81+ // ≥50% coverage, stroke (don't fill). Otherwise, fill.
82+ return rx < 0.5F || ry < 0.5F ;
83+ } ) ;
84+
85+ /// <summary>
86+ /// Flood fills the image in the shape of the provided glyphs with the specified brush and pen.
87+ /// For multi-layer glyphs, a heuristic is used to decide whether to fill or stroke each layer.
88+ /// </summary>
89+ /// <param name="source">The source image processing context.</param>
90+ /// <param name="options">The graphics options.</param>
91+ /// <param name="brush">The brush.</param>
92+ /// <param name="pen">The pen.</param>
93+ /// <param name="paths">The collection of glyph paths.</param>
94+ /// <param name="shouldFillLayer">A function that decides whether to fill or stroke a given layer.</param>
95+ /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
96+ public static IImageProcessingContext Fill (
97+ this IImageProcessingContext source ,
98+ DrawingOptions options ,
99+ Brush brush ,
100+ Pen pen ,
101+ IReadOnlyList < GlyphPathCollection > paths ,
102+ Func < GlyphPathCollection , GlyphLayerInfo , bool > shouldFillLayer )
103+ {
104+ foreach ( GlyphPathCollection gp in paths )
105+ {
106+ if ( gp . LayerCount == 0 )
107+ {
108+ continue ;
109+ }
110+
111+ if ( gp . LayerCount == 1 )
112+ {
113+ // Single-layer glyph: just fill with the supplied brush.
114+ source . Fill ( options , brush , gp . Paths ) ;
115+ continue ;
116+ }
117+
118+ // Multi-layer: decide per layer whether to fill or stroke.
119+ for ( int i = 0 ; i < gp . Layers . Count ; i ++ )
120+ {
121+ GlyphLayerInfo layer = gp . Layers [ i ] ;
122+ IPath path = gp . PathList [ i ] ;
123+
124+ if ( shouldFillLayer ( gp , layer ) )
125+ {
126+ IntersectionRule fillRule = layer . FillRule == FillRule . EvenOdd
127+ ? IntersectionRule . EvenOdd
128+ : IntersectionRule . NonZero ;
129+
130+ // Respect the layer's fill rule if different to the drawing options.
131+ source . Fill ( options . CloneOrReturnForIntersectionRule ( fillRule ) , brush , path ) ;
132+ }
133+ else
134+ {
135+ // Outline only to preserve interior detail.
136+ source . Draw ( options , pen , path ) ;
137+ }
138+ }
139+ }
140+
141+ return source ;
142+ }
143+
33144 /// <summary>
34145 /// Flood fills the image in the shape of the provided polygon with the specified brush.
35146 /// </summary>
36147 /// <param name="source">The source image processing context.</param>
37148 /// <param name="brush">The brush.</param>
38- /// <param name="paths">The paths.</param>
149+ /// <param name="paths">The collection of paths.</param>
39150 /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
40151 public static IImageProcessingContext Fill (
41152 this IImageProcessingContext source ,
@@ -44,7 +155,23 @@ public static IImageProcessingContext Fill(
44155 source . Fill ( source . GetDrawingOptions ( ) , brush , paths ) ;
45156
46157 /// <summary>
47- /// Flood fills the image in the shape of the provided polygon with the specified brush.
158+ /// Flood fills the image in the shape of the provided glyphs with the specified brush and pen.
159+ /// For multi-layer glyphs, a heuristic is used to decide whether to fill or stroke each layer.
160+ /// </summary>
161+ /// <param name="source">The source image processing context.</param>
162+ /// <param name="brush">The brush.</param>
163+ /// <param name="pen">The pen.</param>
164+ /// <param name="paths">The collection of glyph paths.</param>
165+ /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
166+ public static IImageProcessingContext Fill (
167+ this IImageProcessingContext source ,
168+ Brush brush ,
169+ Pen pen ,
170+ IReadOnlyList < GlyphPathCollection > paths ) =>
171+ source . Fill ( source . GetDrawingOptions ( ) , brush , pen , paths ) ;
172+
173+ /// <summary>
174+ /// Flood fills the image in the shape of the provided polygon with the specified color.
48175 /// </summary>
49176 /// <param name="source">The source image processing context.</param>
50177 /// <param name="options">The options.</param>
@@ -59,15 +186,29 @@ public static IImageProcessingContext Fill(
59186 source . Fill ( options , new SolidBrush ( color ) , paths ) ;
60187
61188 /// <summary>
62- /// Flood fills the image in the shape of the provided polygon with the specified brush .
189+ /// Flood fills the image in the shape of the provided polygon with the specified color .
63190 /// </summary>
64191 /// <param name="source">The source image processing context.</param>
65192 /// <param name="color">The color.</param>
66- /// <param name="paths">The paths.</param>
193+ /// <param name="paths">The collection of paths.</param>
67194 /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
68195 public static IImageProcessingContext Fill (
69196 this IImageProcessingContext source ,
70197 Color color ,
71198 IPathCollection paths ) =>
72199 source . Fill ( new SolidBrush ( color ) , paths ) ;
200+
201+ /// <summary>
202+ /// Flood fills the image in the shape of the provided glyphs with the specified color.
203+ /// For multi-layer glyphs, a heuristic is used to decide whether to fill or stroke each layer.
204+ /// </summary>
205+ /// <param name="source">The source image processing context.</param>
206+ /// <param name="color">The color.</param>
207+ /// <param name="paths">The collection of glyph paths.</param>
208+ /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
209+ public static IImageProcessingContext Fill (
210+ this IImageProcessingContext source ,
211+ Color color ,
212+ IReadOnlyList < GlyphPathCollection > paths ) =>
213+ source . Fill ( new SolidBrush ( color ) , new SolidPen ( color ) , paths ) ;
73214}
0 commit comments