diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index 97bba2b460..74402ef8bf 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -266,6 +266,8 @@ ol.render.canvas.Replay.prototype.replay_ = function( var prevX, prevY, roundX, roundY; var pendingFill = 0; var pendingStroke = 0; + // When the batch size gets too big, performance decreases. 200 is a good + // balance between batch size and number of fill/stroke instructions. var batchSize = this.instructions != instructions || this.overlaps ? 0 : 200; while (i < ii) { @@ -1267,8 +1269,6 @@ ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCo closePathInstruction); offset = end; } - // FIXME is it quicker to fill and stroke each polygon individually, - // FIXME or all polygons together? var fillInstruction = [ol.render.canvas.Instruction.FILL]; this.hitDetectionInstructions.push(fillInstruction); if (state.fillStyle !== undefined) { diff --git a/test/spec/ol/renderer/canvas/canvasreplay.test.js b/test/spec/ol/renderer/canvas/canvasreplay.test.js index 496e8de250..72ad4b683f 100644 --- a/test/spec/ol/renderer/canvas/canvasreplay.test.js +++ b/test/spec/ol/renderer/canvas/canvasreplay.test.js @@ -46,9 +46,9 @@ describe('ol.render.canvas.ReplayGroup', function() { closePath: function() {}, setLineDash: function() {}, restore: function() {} - } + }; - }) + }); it('batches fill and stroke instructions for same style', function() { ol.renderer.vector.renderFeature(replay, feature1, style1, 1); @@ -126,7 +126,7 @@ describe('ol.render.canvas.ReplayGroup', function() { expect(fillCount).to.be(3); expect(strokeCount).to.be(3); expect(beginPathCount).to.be(3); - }) + }); }); }); diff --git a/test_rendering/spec/ol/layer/expected/vector-canvas-stroke.png b/test_rendering/spec/ol/layer/expected/vector-canvas-stroke.png new file mode 100644 index 0000000000..9627e9c34f Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/vector-canvas-stroke.png differ diff --git a/test_rendering/spec/ol/layer/vector.test.js b/test_rendering/spec/ol/layer/vector.test.js index 94738027d9..70feedc8a8 100644 --- a/test_rendering/spec/ol/layer/vector.test.js +++ b/test_rendering/spec/ol/layer/vector.test.js @@ -37,6 +37,16 @@ describe('ol.rendering.layer.Vector', function() { ]))); } + function addLineString(r) { + source.addFeature(new ol.Feature(new ol.geom.LineString([ + [center[0] - r, center[1] - r], + [center[0] + r, center[1] - r], + [center[0] + r, center[1] + r], + [center[0] - r, center[1] + r], + [center[0] - r, center[1] - r] + ]))); + } + describe('vector layer', function() { beforeEach(function() { @@ -96,9 +106,136 @@ describe('ol.rendering.layer.Vector', function() { map.once('postrender', function() { expectResemble(map, 'spec/ol/layer/expected/vector-canvas-opaque.png', 17, done); - }) + }); }); + it('renders stroke batches correctly with the canvas renderer', function(done) { + map = createMap('canvas'); + source = new ol.source.Vector({ + overlaps: false + }); + addLineString(100); + addLineString(250); + addLineString(600); + addLineString(720); + map.addLayer(new ol.layer.Vector({ + source: source, + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#3399CC', + width: 1.25 + }) + }) + })); + map.once('postrender', function() { + expectResemble(map, 'spec/ol/layer/expected/vector-canvas-stroke.png', + 7, done); + }); + }); + + it('interrupts fill/stroke batches correctly with the canvas renderer', function(done) { + map = createMap('canvas'); + var color; + function createSource(overlaps) { + color = '#3399CC'; + source = new ol.source.Vector({ + overlaps: overlaps + }); + addPolygon(720); + addPolygon(600); + addCircle(500); + addPolygon(250); + addCircle(200); + addPolygon(100); + return source; + } + function alternateColor() { + if (color == '#3399CC') { + color = '#CC9933'; + } else { + color = '#3399CC'; + } + return color; + } + var layer = new ol.layer.Vector({ + source: createSource(true), + style: function(feature) { + alternateColor(); + return new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: alternateColor(), + width: 1.25 + }), + fill: new ol.style.Fill({ + color: alternateColor() + }) + }); + } + }); + map.addLayer(layer); + map.once('postrender', function() { + var canvas = map.getRenderer().canvas_; + // take a snapshot of this `overlaps: true` image + var referenceImage = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height); + // now render the same with `overlaps: false` + layer.setSource(createSource(false)); + // result should be exactly the same as with `overlaps: true` + map.once('postrender', function() { + expectResemble(map, referenceImage, 0, done); + }); + }); + }); + + it('interrupts stroke batches correctly with the canvas renderer', function(done) { + map = createMap('canvas'); + var color; + function createSource(overlaps) { + color = '#3399CC'; + source = new ol.source.Vector({ + overlaps: overlaps + }); + addLineString(720); + addLineString(600); + addLineString(250); + addLineString(100); + return source; + } + function alternateColor() { + if (color == '#3399CC') { + color = '#CC9933'; + } else { + color = '#3399CC'; + } + return color; + } + var layer = new ol.layer.Vector({ + source: createSource(true), + style: function(feature) { + alternateColor(); + return new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: alternateColor(), + width: 1.25 + }), + fill: new ol.style.Fill({ + color: alternateColor() + }) + }); + } + }); + map.addLayer(layer); + map.once('postrender', function() { + var canvas = map.getRenderer().canvas_; + // take a snapshot of this `overlaps: true` image + var referenceImage = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height); + // now render the same with `overlaps: false` + layer.setSource(createSource(false)); + // result should be exactly the same as with `overlaps: true` + map.once('postrender', function() { + expectResemble(map, referenceImage, 0, done); + }); + }); + }); }); }); @@ -107,8 +244,10 @@ goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.Feature'); goog.require('ol.geom.Circle'); +goog.require('ol.geom.LineString'); goog.require('ol.geom.Polygon'); goog.require('ol.layer.Vector'); goog.require('ol.source.Vector'); +goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style');