@@ -20,14 +20,14 @@ public sealed class PathGradientBrush : IBrush
2020 private readonly IList < Edge > edges ;
2121
2222 private readonly Color centerColor ;
23+ private readonly bool hasSpecialCenterColor ;
2324
2425 /// <summary>
2526 /// Initializes a new instance of the <see cref="PathGradientBrush"/> class.
2627 /// </summary>
2728 /// <param name="points">Points that constitute a polygon that represents the gradient area.</param>
2829 /// <param name="colors">Array of colors that correspond to each point in the polygon.</param>
29- /// <param name="centerColor">Color at the center of the gradient area to which the other colors converge.</param>
30- public PathGradientBrush ( PointF [ ] points , Color [ ] colors , Color centerColor )
30+ public PathGradientBrush ( PointF [ ] points , Color [ ] colors )
3131 {
3232 if ( points == null )
3333 {
@@ -62,7 +62,7 @@ public PathGradientBrush(PointF[] points, Color[] colors, Color centerColor)
6262 lines [ i ] = new LinearLineSegment ( points [ i % size ] , points [ ( i + 1 ) % size ] ) ;
6363 }
6464
65- this . centerColor = centerColor ;
65+ this . centerColor = CalculateCenterColor ( colors ) ;
6666
6767 Color ColorAt ( int index ) => colors [ index % colors . Length ] ;
6868
@@ -75,9 +75,12 @@ public PathGradientBrush(PointF[] points, Color[] colors, Color centerColor)
7575 /// </summary>
7676 /// <param name="points">Points that constitute a polygon that represents the gradient area.</param>
7777 /// <param name="colors">Array of colors that correspond to each point in the polygon.</param>
78- public PathGradientBrush ( PointF [ ] points , Color [ ] colors )
79- : this ( points , colors , CalculateCenterColor ( colors ) )
78+ /// <param name="centerColor">Color at the center of the gradient area to which the other colors converge.</param>
79+ public PathGradientBrush ( PointF [ ] points , Color [ ] colors , Color centerColor )
80+ : this ( points , colors )
8081 {
82+ this . centerColor = centerColor ;
83+ this . hasSpecialCenterColor = true ;
8184 }
8285
8386 /// <inheritdoc />
@@ -88,7 +91,7 @@ public BrushApplicator<TPixel> CreateApplicator<TPixel>(
8891 RectangleF region )
8992 where TPixel : unmanaged, IPixel < TPixel >
9093 {
91- return new PathGradientBrushApplicator < TPixel > ( configuration , options , source , this . edges , this . centerColor ) ;
94+ return new PathGradientBrushApplicator < TPixel > ( configuration , options , source , this . edges , this . centerColor , this . hasSpecialCenterColor ) ;
9295 }
9396
9497 private static Color CalculateCenterColor ( Color [ ] colors )
@@ -209,6 +212,8 @@ private class PathGradientBrushApplicator<TPixel> : BrushApplicator<TPixel>
209212
210213 private readonly Vector4 centerColor ;
211214
215+ private readonly bool hasSpecialCenterColor ;
216+
212217 private readonly float maxDistance ;
213218
214219 private readonly IList < Edge > edges ;
@@ -225,19 +230,22 @@ private class PathGradientBrushApplicator<TPixel> : BrushApplicator<TPixel>
225230 /// <param name="source">The source image.</param>
226231 /// <param name="edges">Edges of the polygon.</param>
227232 /// <param name="centerColor">Color at the center of the gradient area to which the other colors converge.</param>
233+ /// <param name="hasSpecialCenterColor">Whether the center color is different from a smooth gradient between the edges.</param>
228234 public PathGradientBrushApplicator (
229235 Configuration configuration ,
230236 GraphicsOptions options ,
231237 ImageFrame < TPixel > source ,
232238 IList < Edge > edges ,
233- Color centerColor )
239+ Color centerColor ,
240+ bool hasSpecialCenterColor )
234241 : base ( configuration , options , source )
235242 {
236243 this . edges = edges ;
237244 PointF [ ] points = edges . Select ( s => s . Start ) . ToArray ( ) ;
238245
239246 this . center = points . Aggregate ( ( p1 , p2 ) => p1 + p2 ) / edges . Count ;
240247 this . centerColor = ( Vector4 ) centerColor ;
248+ this . hasSpecialCenterColor = hasSpecialCenterColor ;
241249 this . centerPixel = centerColor . ToPixel < TPixel > ( ) ;
242250
243251 this . maxDistance = points . Select ( p => ( Vector2 ) ( p - this . center ) ) . Max ( d => d . Length ( ) ) ;
@@ -257,6 +265,27 @@ public PathGradientBrushApplicator(
257265 return this . centerPixel ;
258266 }
259267
268+ if ( this . edges . Count == 3 && ! this . hasSpecialCenterColor )
269+ {
270+ if ( ! FindPointOnTriangle (
271+ this . edges [ 0 ] . Start ,
272+ this . edges [ 1 ] . Start ,
273+ this . edges [ 2 ] . Start ,
274+ point ,
275+ out float u ,
276+ out float v ) )
277+ {
278+ return this . transparentPixel ;
279+ }
280+
281+ Vector4 pointColor = ( ( 1 - u - v ) * this . edges [ 0 ] . StartColor ) + ( u * this . edges [ 0 ] . EndColor ) +
282+ ( v * this . edges [ 2 ] . StartColor ) ;
283+
284+ TPixel px = default ;
285+ px . FromScaledVector4 ( pointColor ) ;
286+ return px ;
287+ }
288+
260289 var direction = Vector2 . Normalize ( point - this . center ) ;
261290 PointF end = point + ( PointF ) ( direction * this . maxDistance ) ;
262291
@@ -275,7 +304,9 @@ public PathGradientBrushApplicator(
275304
276305 var color = Vector4 . Lerp ( edgeColor , this . centerColor , ratio ) ;
277306
278- return new Color ( color ) . ToPixel < TPixel > ( ) ;
307+ TPixel pixel = default ;
308+ pixel . FromScaledVector4 ( color ) ;
309+ return pixel ;
279310 }
280311 }
281312
@@ -301,6 +332,40 @@ public PathGradientBrushApplicator(
301332
302333 return closest ;
303334 }
335+
336+ private static bool FindPointOnTriangle ( PointF v1 , PointF v2 , PointF v3 , PointF point , out float u , out float v )
337+ {
338+ Vector2 e1 = v2 - v1 ;
339+ Vector2 e2 = v3 - v2 ;
340+ Vector2 e3 = v1 - v3 ;
341+
342+ Vector2 pv1 = point - v1 ;
343+ Vector2 pv2 = point - v2 ;
344+ Vector2 pv3 = point - v3 ;
345+
346+ var d1 = Vector3 . Cross ( new Vector3 ( e1 . X , e1 . Y , 0 ) , new Vector3 ( pv1 . X , pv1 . Y , 0 ) ) ;
347+ var d2 = Vector3 . Cross ( new Vector3 ( e2 . X , e2 . Y , 0 ) , new Vector3 ( pv2 . X , pv2 . Y , 0 ) ) ;
348+ var d3 = Vector3 . Cross ( new Vector3 ( e3 . X , e3 . Y , 0 ) , new Vector3 ( pv3 . X , pv3 . Y , 0 ) ) ;
349+
350+ if ( Math . Sign ( Vector3 . Dot ( d1 , d2 ) ) * Math . Sign ( Vector3 . Dot ( d1 , d3 ) ) == - 1 || Math . Sign ( Vector3 . Dot ( d1 , d2 ) ) * Math . Sign ( Vector3 . Dot ( d2 , d3 ) ) == - 1 )
351+ {
352+ u = 0 ;
353+ v = 0 ;
354+ return false ;
355+ }
356+
357+ // From Real-Time Collision Detection
358+ // https://gamedev.stackexchange.com/questions/23743/whats-the-most-efficient-way-to-find-barycentric-coordinates
359+ float d00 = Vector2 . Dot ( e1 , e1 ) ;
360+ float d01 = - Vector2 . Dot ( e1 , e3 ) ;
361+ float d11 = Vector2 . Dot ( e3 , e3 ) ;
362+ float d20 = Vector2 . Dot ( pv1 , e1 ) ;
363+ float d21 = - Vector2 . Dot ( pv1 , e3 ) ;
364+ float denominator = ( d00 * d11 ) - ( d01 * d01 ) ;
365+ u = ( ( d11 * d20 ) - ( d01 * d21 ) ) / denominator ;
366+ v = ( ( d00 * d21 ) - ( d01 * d20 ) ) / denominator ;
367+ return true ;
368+ }
304369 }
305370 }
306371}
0 commit comments