@@ -55,8 +55,8 @@ internal static partial class DefaultRasterizer
5555 private static readonly int WordBitCount = nint . Size * 8 ;
5656
5757 /// <summary>
58- /// Right -shift that converts an accumulated doubled cell area (max 2 * 256 * 256) down to the
59- /// 0..256 coverage step domain used by <see cref="Context.AreaToCoverage "/>.
58+ /// Left -shift that scales a 24.8 winding cover (256 per fully covered pixel) up to the doubled
59+ /// cell area domain accumulated by the cells ( <see cref="FullCoverageArea "/> per fully covered pixel) .
6060 /// </summary>
6161 private const int AreaToCoverageShift = 9 ;
6262
@@ -98,24 +98,24 @@ internal static partial class DefaultRasterizer
9898 private const int CrossingShift = ProfileIdBits + 1 ;
9999
100100 /// <summary>
101- /// Number of discrete coverage steps per fully covered pixel (one 24.8 unit of winding) .
101+ /// The doubled cell area of one fully covered pixel (2 * 256 * 256), one 24.8 unit of winding.
102102 /// </summary>
103- private const int CoverageStepCount = 256 ;
103+ private const int FullCoverageArea = ( FixedOne * FixedOne ) << 1 ;
104104
105105 /// <summary>
106- /// Bitmask implementing modulo 2 * <see cref="CoverageStepCount "/> for even-odd wrapping.
106+ /// Bitmask implementing modulo 2 * <see cref="FullCoverageArea "/> for even-odd wrapping.
107107 /// </summary>
108- private const int EvenOddMask = ( CoverageStepCount * 2 ) - 1 ;
108+ private const int EvenOddMask = ( FullCoverageArea * 2 ) - 1 ;
109109
110110 /// <summary>
111111 /// Length of one even-odd winding period; values past the midpoint mirror back down.
112112 /// </summary>
113- private const int EvenOddPeriod = CoverageStepCount * 2 ;
113+ private const int EvenOddPeriod = FullCoverageArea * 2 ;
114114
115115 /// <summary>
116- /// Multiplier converting integer coverage steps to normalized [0, 1] coverage.
116+ /// Multiplier converting doubled cell area to normalized [0, 1] coverage.
117117 /// </summary>
118- private const float CoverageScale = 1F / CoverageStepCount ;
118+ private const float CoverageScale = 1F / FullCoverageArea ;
119119
120120 /// <summary>
121121 /// Gets the preferred scene row height used by the CPU rasterizer.
@@ -1625,20 +1625,21 @@ private void CaptureCrossings(int x0, int y0, int x1, int y1, uint tag)
16251625 /// </summary>
16261626 /// <param name="area">
16271627 /// The accumulated doubled signed area in fixed-point cell units; a fully covered pixel
1628- /// corresponds to 2 * 256 * 256, which <see cref="AreaToCoverageShift"/> maps to <see cref="CoverageStepCount "/>.
1628+ /// corresponds to <see cref="FullCoverageArea "/>.
16291629 /// </param>
16301630 /// <returns>The normalized coverage value in [0, 1].</returns>
16311631 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
16321632 private readonly float AreaToCoverage ( int area )
16331633 {
1634- int signedArea = area >> AreaToCoverageShift ;
1635- int absoluteArea = signedArea < 0 ? - signedArea : signedArea ;
1634+ // The area keeps its full precision. Quantizing it to 1/256 steps before the blend
1635+ // moves edge pixels up to one output level away from the exact composite.
1636+ int absoluteArea = area < 0 ? - area : area ;
16361637 float coverage ;
16371638
16381639 if ( this . intersectionRule == IntersectionRule . NonZero )
16391640 {
16401641 // Non-zero winding clamps absolute winding accumulation to [0, 1].
1641- if ( absoluteArea >= CoverageStepCount )
1642+ if ( absoluteArea >= FullCoverageArea )
16421643 {
16431644 coverage = 1F ;
16441645 }
@@ -1649,14 +1650,14 @@ private readonly float AreaToCoverage(int area)
16491650 }
16501651 else
16511652 {
1652- // Even-odd wraps every 2*CoverageStepCount and mirrors second half.
1653+ // Even-odd wraps every 2 * FullCoverageArea and mirrors the second half.
16531654 int wrapped = absoluteArea & EvenOddMask ;
1654- if ( wrapped > CoverageStepCount )
1655+ if ( wrapped > FullCoverageArea )
16551656 {
16561657 wrapped = EvenOddPeriod - wrapped ;
16571658 }
16581659
1659- coverage = wrapped >= CoverageStepCount ? 1F : wrapped * CoverageScale ;
1660+ coverage = wrapped >= FullCoverageArea ? 1F : wrapped * CoverageScale ;
16601661 }
16611662
16621663 if ( this . coverageBoost != 0F )
@@ -1992,10 +1993,12 @@ private void RowDownR(int rowIndex, int p0x, int p0y, int p1x, int p1y)
19921993 }
19931994
19941995 // pp/mod/lift/rem implement an integer DDA that advances y at column boundaries
1995- // without accumulating rounding error; the remainder carries the exact fraction.
1996+ // without accumulating rounding error; the remainder carries the exact fraction. The
1997+ // half-divisor start term rounds every boundary to the nearest unit instead of
1998+ // flooring it, so the walked edge sits within half a unit of the true line.
19961999 int dx = p1x - p0x ;
19972000 int dy = p1y - p0y ;
1998- int pp = ( FixedOne - fx0 ) * dy ;
2001+ int pp = ( ( FixedOne - fx0 ) * dy ) + ( dx >> 1 ) ;
19992002 int cy = p0y + ( pp / dx ) ;
20002003
20012004 this . Cell ( rowIndex , columnIndex0 , fx0 , p0y , FixedOne , cy ) ;
@@ -2073,7 +2076,7 @@ private void RowUpR(int rowIndex, int p0x, int p0y, int p1x, int p1y)
20732076
20742077 int dx = p1x - p0x ;
20752078 int dy = p0y - p1y ;
2076- int pp = ( FixedOne - fx0 ) * dy ;
2079+ int pp = ( ( FixedOne - fx0 ) * dy ) + ( dx >> 1 ) ;
20772080 int cy = p0y - ( pp / dx ) ;
20782081
20792082 this . Cell ( rowIndex , columnIndex0 , fx0 , p0y , FixedOne , cy ) ;
@@ -2151,7 +2154,7 @@ private void RowDownL(int rowIndex, int p0x, int p0y, int p1x, int p1y)
21512154
21522155 int dx = p0x - p1x ;
21532156 int dy = p1y - p0y ;
2154- int pp = fx0 * dy ;
2157+ int pp = ( fx0 * dy ) + ( dx >> 1 ) ;
21552158 int cy = p0y + ( pp / dx ) ;
21562159
21572160 this . Cell ( rowIndex , columnIndex0 , fx0 , p0y , 0 , cy ) ;
@@ -2229,7 +2232,7 @@ private void RowUpL(int rowIndex, int p0x, int p0y, int p1x, int p1y)
22292232
22302233 int dx = p0x - p1x ;
22312234 int dy = p0y - p1y ;
2232- int pp = fx0 * dy ;
2235+ int pp = ( fx0 * dy ) + ( dx >> 1 ) ;
22332236 int cy = p0y - ( pp / dx ) ;
22342237
22352238 this . Cell ( rowIndex , columnIndex0 , fx0 , p0y , 0 , cy ) ;
@@ -2301,8 +2304,9 @@ private void LineDownR(int rowIndex0, int rowIndex1, int x0, int y0, int x1, int
23012304 int fy1 = y1 - ( rowIndex1 << FixedShift ) ;
23022305
23032306 // p/delta/mod/rem implement an integer DDA that advances x at row boundaries
2304- // without per-row floating-point math.
2305- int p = ( FixedOne - fy0 ) * dx ;
2307+ // without per-row floating-point math. The half-divisor start term rounds every
2308+ // boundary to the nearest unit instead of flooring it.
2309+ int p = ( ( FixedOne - fy0 ) * dx ) + ( dy >> 1 ) ;
23062310 int delta = p / dy ;
23072311 int cx = x0 + delta ;
23082312
@@ -2353,7 +2357,7 @@ private void LineUpR(int rowIndex0, int rowIndex1, int x0, int y0, int x1, int y
23532357 int fy1 = y1 - ( rowIndex1 << FixedShift ) ;
23542358
23552359 // Upward version of the same integer DDA stepping as LineDownR.
2356- int p = fy0 * dx ;
2360+ int p = ( fy0 * dx ) + ( dy >> 1 ) ;
23572361 int delta = p / dy ;
23582362 int cx = x0 + delta ;
23592363
@@ -2403,7 +2407,7 @@ private void LineDownL(int rowIndex0, int rowIndex1, int x0, int y0, int x1, int
24032407 int fy1 = y1 - ( rowIndex1 << FixedShift ) ;
24042408
24052409 // Right-to-left variant of the integer DDA.
2406- int p = ( FixedOne - fy0 ) * dx ;
2410+ int p = ( ( FixedOne - fy0 ) * dx ) + ( dy >> 1 ) ;
24072411 int delta = p / dy ;
24082412 int cx = x0 - delta ;
24092413
@@ -2453,7 +2457,7 @@ private void LineUpL(int rowIndex0, int rowIndex1, int x0, int y0, int x1, int y
24532457 int fy1 = y1 - ( rowIndex1 << FixedShift ) ;
24542458
24552459 // Upward + right-to-left variant of the integer DDA.
2456- int p = fy0 * dx ;
2460+ int p = ( fy0 * dx ) + ( dy >> 1 ) ;
24572461 int delta = p / dy ;
24582462 int cx = x0 - delta ;
24592463
0 commit comments