diff --git a/externs/olx.js b/externs/olx.js index 5d3c1fea97..d9ed973096 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -4418,7 +4418,7 @@ olx.source.VectorTileOptions.prototype.logo; /** * This source may have overlapping geometries. Default is `true`. Setting this - * to `false` (e.g. for sources with polygons that represent adminstrative + * to `false` (e.g. for sources with polygons that represent administrative * boundaries or TopoJSON sources) allows the renderer to optimise fill and * stroke operations. * @type {boolean|undefined} @@ -5864,7 +5864,7 @@ olx.source.VectorOptions.prototype.logo; /** * This source may have overlapping geometries. Default is `true`. Setting this - * to `false` (e.g. for sources with polygons that represent adminstrative + * to `false` (e.g. for sources with polygons that represent administrative * boundaries or TopoJSON sources) allows the renderer to optimise fill and * stroke operations. * @type {boolean|undefined} diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index 92b4a758b5..1d859128ed 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.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/src/ol/source/vectortile.js b/src/ol/source/vectortile.js index b9e003ddb6..a8f0b42590 100644 --- a/src/ol/source/vectortile.js +++ b/src/ol/source/vectortile.js @@ -56,7 +56,7 @@ ol.source.VectorTile = function(options) { * @private * @type {boolean} */ - this.overlaps_ = options.overlaps || true; + this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; /** * @protected diff --git a/test.html b/test.html deleted file mode 100644 index ab049dd51a..0000000000 --- a/test.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - OpenLayers 3 example - - - -

My Map

-
- - - - diff --git a/test/spec/ol/renderer/canvas/replay.test.js b/test/spec/ol/renderer/canvas/replay.test.js index 496e8de250..1585fed1d5 100644 --- a/test/spec/ol/renderer/canvas/replay.test.js +++ b/test/spec/ol/renderer/canvas/replay.test.js @@ -1,5 +1,17 @@ goog.provide('ol.test.renderer.canvas.Replay'); +goog.require('ol.transform'); +goog.require('ol.Feature'); +goog.require('ol.geom.Polygon'); +goog.require('ol.render.canvas.LineStringReplay'); +goog.require('ol.render.canvas.PolygonReplay'); +goog.require('ol.render.canvas.Replay'); +goog.require('ol.render.canvas.ReplayGroup'); +goog.require('ol.renderer.vector'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); + describe('ol.render.canvas.ReplayGroup', function() { describe('#replay', function() { @@ -8,7 +20,7 @@ describe('ol.render.canvas.ReplayGroup', function() { var feature1, feature2, feature3, style1, style2, transform; beforeEach(function() { - transform = goog.vec.Mat4.createNumber(); + transform = ol.transform.create(); replay = new ol.render.canvas.ReplayGroup(1, [-180, -90, 180, 90], 1, false); feature1 = new ol.Feature(new ol.geom.Polygon( [[[-90, -45], [-90, 0], [0, 0], [0, -45], [-90, -45]]])); @@ -46,9 +58,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); @@ -85,7 +97,7 @@ describe('ol.render.canvas.ReplayGroup', function() { ol.renderer.vector.renderFeature(replay, feature2, style2, 1); ol.renderer.vector.renderFeature(replay, feature3, style2, 1); var skippedUids = {}; - skippedUids[goog.getUid(feature1)] = true; + skippedUids[ol.getUid(feature1)] = true; replay.replay(context, 1, transform, 0, skippedUids); expect(fillCount).to.be(1); expect(strokeCount).to.be(1); @@ -97,7 +109,7 @@ describe('ol.render.canvas.ReplayGroup', function() { ol.renderer.vector.renderFeature(replay, feature2, style1, 1); ol.renderer.vector.renderFeature(replay, feature3, style2, 1); var skippedUids = {}; - skippedUids[goog.getUid(feature3)] = true; + skippedUids[ol.getUid(feature3)] = true; replay.replay(context, 1, transform, 0, skippedUids); expect(fillCount).to.be(1); expect(strokeCount).to.be(1); @@ -109,8 +121,8 @@ describe('ol.render.canvas.ReplayGroup', function() { ol.renderer.vector.renderFeature(replay, feature2, style1, 1); ol.renderer.vector.renderFeature(replay, feature3, style2, 1); var skippedUids = {}; - skippedUids[goog.getUid(feature1)] = true; - skippedUids[goog.getUid(feature2)] = true; + skippedUids[ol.getUid(feature1)] = true; + skippedUids[ol.getUid(feature2)] = true; replay.replay(context, 1, transform, 0, skippedUids); expect(fillCount).to.be(1); expect(strokeCount).to.be(1); @@ -126,7 +138,7 @@ describe('ol.render.canvas.ReplayGroup', function() { expect(fillCount).to.be(3); expect(strokeCount).to.be(3); expect(beginPathCount).to.be(3); - }) + }); }); }); @@ -252,15 +264,3 @@ describe('ol.render.canvas.PolygonReplay', function() { }); }); - -goog.require('goog.vec.Mat4'); -goog.require('ol.Feature'); -goog.require('ol.geom.Polygon'); -goog.require('ol.render.canvas.LineStringReplay'); -goog.require('ol.render.canvas.PolygonReplay'); -goog.require('ol.render.canvas.Replay'); -goog.require('ol.render.canvas.ReplayGroup'); -goog.require('ol.renderer.vector'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style'); 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 8a2fb7224d..a64903069b 100644 --- a/test_rendering/spec/ol/layer/vector.test.js +++ b/test_rendering/spec/ol/layer/vector.test.js @@ -8,6 +8,7 @@ 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'); @@ -49,6 +50,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() { @@ -108,19 +119,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); + }); + }); + }); }); }); - -goog.require('ol.Map'); -goog.require('ol.View'); -goog.require('ol.Feature'); -goog.require('ol.geom.Circle'); -goog.require('ol.geom.Polygon'); -goog.require('ol.layer.Vector'); -goog.require('ol.source.Vector'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style');