2929const BitmapWriter = require ( "./bitmapWriter" ) ;
3030const LayerManager = require ( "./layerManager" ) ;
3131const SuperSampleBuffer = require ( "./superSampleBuffer" ) ;
32+ const colorUtils = require ( "../colorUtils" ) ;
3233
3334/**
3435 * A higher number of samples per pixel horizontally does not affect the
@@ -66,7 +67,13 @@ function rasterize(edgeTable, backColor) {
6667
6768 var layers = [ ] ;
6869 var color = 0 ;
70+
71+ // Keeps track of how many of the subpixellayers that are used for
72+ // the currently rendered scanline. Until a range requiring supersampling
73+ // is encountered only a single layer is needed.
74+ var usedLayers = 0 ;
6975
76+ // Create a layer manager for every subpixel scanline
7077 for ( var i = 0 ; i < SAMPLES_PER_PIXEL_Y ; i ++ ) {
7178 layers [ i ] = new LayerManager ( ) ;
7279 }
@@ -78,45 +85,124 @@ function rasterize(edgeTable, backColor) {
7885 continue ;
7986 }
8087
81- for ( var i = 0 ; i < layers . length ; i ++ ) {
88+ for ( var i = 0 ; i < usedLayers ; i ++ ) {
8289 layers [ i ] . clear ( ) ;
8390 }
91+ usedLayers = 1 ;
8492
8593 var superSampleRanges = getSuperSampleRanges ( scanline , edgeTable . width ) ;
8694
8795 writer . skip ( superSampleRanges [ 0 ] . fromX ) ;
8896
8997 for ( var rangeIndex = 0 ; rangeIndex < superSampleRanges . length ; rangeIndex ++ ) {
9098 var superSampleRange = superSampleRanges [ rangeIndex ] ;
99+
100+ // If there is exactly one edge in the supersample range, and it is crossing
101+ // the entire scanline, we can perform the antialiasing by integrating the
102+ // edge function.
103+ if ( superSampleRange . edges . length == 1 && (
104+ superSampleRange . edges [ 0 ] . y0 <= ey && superSampleRange . edges [ 0 ] . y1 >= ey + 1 ||
105+ superSampleRange . edges [ 0 ] . y0 >= ey + 1 && superSampleRange . edges [ 0 ] . y1 <= ey
106+ ) ) {
107+ var edge = superSampleRange . edges [ 0 ] ;
108+
109+ // Determine the lower and upper x value where the edge
110+ // intersects the scanline.
111+ var xey = edge . intersection ( ey ) ;
112+ var xey1 = edge . intersection ( ey + 1 ) ;
113+ var x0 = Math . min ( xey , xey1 ) ;
114+ var x1 = Math . max ( xey , xey1 ) ;
115+ var width = x1 - x0 ;
116+
117+ // Compute the average color of all subpixel layers before
118+ // and after the edge intersection.
119+ for ( var sy = 0 ; sy < usedLayers ; sy ++ ) {
120+ var subScanlineLayers = layers [ sy ] ;
121+ superSampleBuffer . add ( subScanlineLayers . color , 1 ) ;
122+ subScanlineLayers . add ( edge ) ;
123+ superSampleBuffer . add ( subScanlineLayers . color , 2 ) ;
124+ superSampleBuffer . rewind ( ) ;
125+ }
91126
92- var y = ey + SAMPLE_HEIGHT / 2 ;
93-
94- for ( var sy = 0 ; sy < SAMPLES_PER_PIXEL_Y ; sy ++ , y += SAMPLE_HEIGHT ) {
95- var subScanlineLayers = layers [ sy ] ;
96- color = subScanlineLayers . color ;
127+ var fromColor = superSampleBuffer . getColorAt ( 0 ) ;
128+ color = superSampleBuffer . getColorAt ( 1 ) ;
129+
130+ superSampleBuffer . clear ( ) ;
131+
132+ // Render pixels
133+ for ( var x = superSampleRange . fromX ; x < superSampleRange . toXExcl ; x ++ ) {
134+ if ( x0 >= x + 1 ) {
135+ // Pixel not covered
136+ writer . write ( fromColor , 1 ) ;
137+ continue ;
138+ }
139+
140+ if ( x1 <= x ) {
141+ // Pixel fully covered
142+ writer . write ( color , 1 ) ;
143+ continue ;
144+ }
145+
146+ // toColor coverage in the range [0.0, 1.0]
147+ // Initialize to the fully covered range of the pixel.
148+ var coverage = x1 < x + 1 ? x + 1 - x1 : 0 ;
149+
150+ // Compute integral for non-vertical edges
151+ if ( width > 0.001 ) {
152+ // Range to integrate
153+ var integralFrom = Math . max ( x0 , x ) ;
154+ var integralTo = Math . min ( x1 , x + 1 ) ;
155+
156+ coverage +=
157+ (
158+ ( integralTo * integralTo - integralFrom * integralFrom ) / 2 +
159+ x0 * ( integralFrom - integralTo )
160+ ) / width ;
161+ }
162+
163+ writer . write ( colorUtils . mix ( fromColor , color , coverage ) , 1 ) ;
164+ }
97165
98- var intersections = getIntersections ( superSampleRange . edges , y ) ;
166+ } // /simplified antialiasing
167+ else {
168+ // There are more than a single intersecting edge in this range.
169+ // Use super sampling to render the pixels.
170+ var y = ey + SAMPLE_HEIGHT / 2 ;
171+
172+ // Ensure all subpixel layers are initialized
173+ while ( usedLayers < SAMPLES_PER_PIXEL_Y ) {
174+ layers [ 0 ] . copyTo ( layers [ usedLayers ] ) ;
175+ usedLayers ++ ;
176+ }
99177
100- for ( var i = 0 ; i < intersections . length ; i ++ ) {
101- var intersection = intersections [ i ] ;
102- superSampleBuffer . add ( color , intersection . x - superSampleRange . fromX ) ;
103- subScanlineLayers . add ( intersection . edge ) ;
178+ // Average color of the pixels following the current supersample range.
179+ for ( var sy = 0 ; sy < SAMPLES_PER_PIXEL_Y ; sy ++ , y += SAMPLE_HEIGHT ) {
180+ var subScanlineLayers = layers [ sy ] ;
104181 color = subScanlineLayers . color ;
105- }
106182
107- // Write an extra pixel that will contain the color that
108- // will be forwarded until the next supersample range.
109- superSampleBuffer . add ( color , superSampleRange . width + 1 ) ;
110- superSampleBuffer . rewind ( ) ;
111- } // /subpixel
183+ var intersections = getIntersections ( superSampleRange . edges , y ) ;
184+
185+ for ( var i = 0 ; i < intersections . length ; i ++ ) {
186+ var intersection = intersections [ i ] ;
187+ superSampleBuffer . add ( color , intersection . x - superSampleRange . fromX ) ;
188+ subScanlineLayers . add ( intersection . edge ) ;
189+ color = subScanlineLayers . color ;
190+ }
191+
192+ // Write an extra pixel that will contain the color that
193+ // will be forwarded until the next supersample range.
194+ superSampleBuffer . add ( color , superSampleRange . width + 1 ) ;
195+ superSampleBuffer . rewind ( ) ;
196+ } // /subpixel
197+
198+ // Get color to be forwarded
199+ color = superSampleBuffer . getColorAt ( superSampleRange . width ) ;
200+
201+ // Blend subpixels
202+ superSampleBuffer . writeTo ( writer , superSampleRange . width ) ;
203+ superSampleBuffer . clear ( ) ;
204+ }
112205
113- // Get color to be forwarded
114- color = superSampleBuffer . getColorAt ( superSampleRange . width ) ;
115-
116- // Blend subpixels
117- superSampleBuffer . writeTo ( writer , superSampleRange . width ) ;
118- superSampleBuffer . clear ( ) ;
119-
120206 // Forward last color
121207 if ( rangeIndex + 1 < superSampleRanges . length ) {
122208 var nextRangeX = superSampleRanges [ rangeIndex + 1 ] . fromX ;
@@ -172,7 +258,6 @@ function getIntersections(edges, y) {
172258function getSuperSampleRanges ( scanline , width ) {
173259 var superSampleRanges = [ ] ;
174260
175- const ALLOWED_RANGE_DISTANCE = 1 ;
176261 var rangeIndex = 0 ;
177262 var superSampleRangeIndex = 0 ;
178263
@@ -192,7 +277,7 @@ function getSuperSampleRanges(scanline, width) {
192277 rangeIndex ++ ;
193278
194279 for ( var i = rangeIndex ; i < scanline . length ; i ++ ) {
195- if ( scanline [ i ] . fromX <= superSampleRange . toXExcl + ALLOWED_RANGE_DISTANCE ) {
280+ if ( scanline [ i ] . fromX < superSampleRange . toXExcl ) {
196281 superSampleRange . toXExcl = Math . max ( superSampleRange . toXExcl , scanline [ i ] . fromX + scanline [ i ] . width ) ;
197282 superSampleRange . edges . push ( scanline [ i ] . edge ) ;
198283 rangeIndex ++ ;
0 commit comments