Skip to content

Commit a98cef7

Browse files
committed
Improved performance.
1 parent 0f8b633 commit a98cef7

File tree

4 files changed

+133
-29
lines changed

4 files changed

+133
-29
lines changed

lib/matrix.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Matrix.prototype.multiply = function Matrix_multiply(a, b, c, d, e, f) {
8484
* @param {number} y Y coordinate.
8585
* @returns {{x:number, y:number}}
8686
*/
87-
Matrix.prototype.multiplyPoint = function Matrix_multiply(x, y) {
87+
Matrix.prototype.multiplyPoint = function Matrix_multiplyPoint(x, y) {
8888
return {
8989
x: this._a * x + this._c * y + this._e,
9090
y: this._b * x + this._d * y + this._f

lib/rasterization/edge.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ function Edge(polygonId, x0, y0, x1, y1, color, windingRule) {
3636
this.windingRule = windingRule;
3737
}
3838

39-
Edge.prototype.intersection = function intersection(y)
40-
{
39+
Edge.prototype.intersection = function intersection(y) {
4140
var dx = (this.x1 - this.x0) * (this.y0 - y) / (this.y0 - this.y1);
4241
return this.x0 + dx;
4342
};

lib/rasterization/layerManager.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,26 @@ LayerManager.prototype.clear = function LayerManager_clear() {
5252
this.color = colorUtils.TRANSPARENT;
5353
};
5454

55+
/**
56+
* Copies all layers in this LayerManager to another LayerManager.
57+
* @param {LayerManager} other
58+
* @public
59+
*/
60+
LayerManager.prototype.copyTo = function LayerManager_copyTo(other) {
61+
other.color = this.color;
62+
other._layers = [];
63+
64+
for (var i = 0; i < this._layers.length; i++) {
65+
var layer = this._layers[i];
66+
other._layers[i] = {
67+
color: layer.color,
68+
inPath: layer.inPath,
69+
polygonId: layer.polygonId,
70+
winding: layer.winding
71+
};
72+
}
73+
};
74+
5575
/**
5676
* Adds a layer for the specified edge. The z-order is defined by its id.
5777
* @param {Edge} edge

lib/rasterization/rasterizer.js

Lines changed: 111 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
const BitmapWriter = require("./bitmapWriter");
3030
const LayerManager = require("./layerManager");
3131
const 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) {
172258
function 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

Comments
 (0)