From 274593feefced226d821929679d265ddbe0f2662 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 11 Aug 2016 15:51:42 +0200 Subject: [PATCH 1/6] Add rendering test for closed paths --- .../expected/polygon-types-canvas-stroke.png | Bin 0 -> 628 bytes test_rendering/spec/ol/style/polygon.test.js | 67 +++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 test_rendering/spec/ol/style/expected/polygon-types-canvas-stroke.png diff --git a/test_rendering/spec/ol/style/expected/polygon-types-canvas-stroke.png b/test_rendering/spec/ol/style/expected/polygon-types-canvas-stroke.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd104c62b5c589559d30e70fc46a255adeaa85c GIT binary patch literal 628 zcmeAS@N?(olHy`uVBq!ia0vp^DImAQBQ#GGtZtfR=DoGWovtp zVBPXrtNlIe3oX)~-T39pUUq%Q>+hOUrGM>Q7q7d&JmbvQ-y(&7^Hw07hH&ktrqc=n z#vDg@m<*d8&H$M?%$A${E7RgHY+Qfx#}=L^H*3Ah;xFu7U-{&+-h@|nPuJHrp5FIt zp8swKk>69?e={wwJ*zvvtWjg%W%;+Ak!!an-Rsz(7&HS2ur=VE%spi?{a!xs$fvXJlY_;QNE|;h*Hfo|CN3z=Xlz M>FVdQ&MBb@0GCWDdjJ3c literal 0 HcmV?d00001 diff --git a/test_rendering/spec/ol/style/polygon.test.js b/test_rendering/spec/ol/style/polygon.test.js index 4fc152fe20..d9d6845bab 100644 --- a/test_rendering/spec/ol/style/polygon.test.js +++ b/test_rendering/spec/ol/style/polygon.test.js @@ -15,8 +15,9 @@ describe('ol.rendering.style.Polygon', function() { var target, map, vectorSource; - function createMap(renderer) { - target = createMapDiv(50, 50); + function createMap(renderer, opt_size) { + var size = opt_size || 50; + target = createMapDiv(size, size); vectorSource = new ol.source.Vector(); var vectorLayer = new ol.layer.Vector({ @@ -92,6 +93,68 @@ describe('ol.rendering.style.Polygon', function() { }); }); + describe('different types with stroke', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var stroke = new ol.style.Stroke({ + width: 10, + color: '#000', + lineJoin: 'round', + lineCap: 'butt' + }); + + var feature; + // rectangle + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-20, 10], [-20, 20], [-5, 20], [-5, 10], [-20, 10]] + ]) + }); + feature.setStyle(new ol.style.Style({ + stroke: stroke + })); + vectorSource.addFeature(feature); + + // rectangle with 1 hole + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[0, 10], [0, 20], [20, 20], [20, 10], [0, 10]], + [[5, 13], [10, 13], [10, 17], [5, 17], [5, 13]] + + ]) + }); + feature.setStyle(new ol.style.Style({ + stroke: stroke + })); + vectorSource.addFeature(feature); + + // rectangle with 2 holes + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-20, -20], [-20, 5], [20, 5], [20, -20], [-20, -20]], + [[-12, -12], [-8, -12], [-8, -3], [-12, -3], [-12, -3]], + [[0, -12], [13, -12], [13, -3], [0, -3], [0, -12]] + + ]) + }); + feature.setStyle(new ol.style.Style({ + stroke: stroke + })); + vectorSource.addFeature(feature); + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas', 100); + map.getView().setResolution(0.5); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/polygon-types-canvas-stroke.png', + IMAGE_TOLERANCE, done); + }); + }); + describe('z-index', function() { afterEach(function() { disposeMap(map); From a4f69f32d7bfae0a93396fa624878a6691484d1b Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 11 Aug 2016 13:17:05 +0200 Subject: [PATCH 2/6] Avoid expensive closePath() when it makes no visual difference --- src/ol/render/canvas/replay.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index 1de3481ca2..600d03242a 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -1205,6 +1205,7 @@ ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCo var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH]; this.instructions.push(beginPathInstruction); this.hitDetectionInstructions.push(beginPathInstruction); + var stroke = state.strokeStyle != undefined; var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; @@ -1213,10 +1214,15 @@ ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCo flatCoordinates, offset, end, stride, true); var moveToLineToInstruction = [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd]; - var closePathInstruction = [ol.render.canvas.Instruction.CLOSE_PATH]; - this.instructions.push(moveToLineToInstruction, closePathInstruction); - this.hitDetectionInstructions.push(moveToLineToInstruction, - closePathInstruction); + this.instructions.push(moveToLineToInstruction); + this.hitDetectionInstructions.push(moveToLineToInstruction); + if (stroke) { + // Performance optimization: only call closePath() when we have a stroke. + // Otherwise the ring is closed already (see appendFlatCoordinates above). + var closePathInstruction = [ol.render.canvas.Instruction.CLOSE_PATH]; + this.instructions.push(closePathInstruction); + this.hitDetectionInstructions.push(closePathInstruction); + } offset = end; } // FIXME is it quicker to fill and stroke each polygon individually, @@ -1226,7 +1232,7 @@ ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCo if (state.fillStyle !== undefined) { this.instructions.push(fillInstruction); } - if (state.strokeStyle !== undefined) { + if (stroke) { goog.DEBUG && console.assert(state.lineWidth !== undefined, 'state.lineWidth should be defined'); var strokeInstruction = [ol.render.canvas.Instruction.STROKE]; From 370c0c084673259d1c90c211b34217be0a2a0ba1 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 11 Aug 2016 13:17:42 +0200 Subject: [PATCH 3/6] Do not close the ring when we use closePath() --- src/ol/render/canvas/replay.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index 600d03242a..549d00d3fa 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -1210,8 +1210,10 @@ ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCo for (i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; var myBegin = this.coordinates.length; - var myEnd = this.appendFlatCoordinates( - flatCoordinates, offset, end, stride, true); + var myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, + // Performance optimization: only close the ring when we do not have a + // stroke. Otherwise closePath() will take care of that. + !stroke); var moveToLineToInstruction = [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd]; this.instructions.push(moveToLineToInstruction); From 9d80116bf085859c17d0626e7a7fb3a5b2c973b1 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 11 Aug 2016 13:27:28 +0200 Subject: [PATCH 4/6] Remove closePath() where clip() is used clip() does an implicit closePath(), see https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing#Clipping_paths --- src/ol/render/canvas/replay.js | 1 - src/ol/reproj/index.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index 549d00d3fa..cc28703652 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -2004,7 +2004,6 @@ ol.render.canvas.ReplayGroup.prototype.replay = function(context, pixelRatio, context.lineTo(flatClipCoords[2], flatClipCoords[3]); context.lineTo(flatClipCoords[4], flatClipCoords[5]); context.lineTo(flatClipCoords[6], flatClipCoords[7]); - context.closePath(); context.clip(); var replayTypes = opt_replayTypes ? opt_replayTypes : ol.render.replay.ORDER; diff --git a/src/ol/reproj/index.js b/src/ol/reproj/index.js index b033ed3ce0..21fc1afec4 100644 --- a/src/ol/reproj/index.js +++ b/src/ol/reproj/index.js @@ -224,7 +224,6 @@ ol.reproj.render = function(width, height, pixelRatio, context.lineTo(u1, v1); context.lineTo(u2, v2); } - context.closePath(); context.clip(); context.transform( From 30ed4a29c8e9fec857a42a1b57cbe98e32568671 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 11 Aug 2016 13:34:40 +0200 Subject: [PATCH 5/6] Fix winding order for raster reprojection triangles On Firefox/Linux, calling clip() without closePath() requires this. --- src/ol/reproj/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ol/reproj/index.js b/src/ol/reproj/index.js index 21fc1afec4..ded20b610f 100644 --- a/src/ol/reproj/index.js +++ b/src/ol/reproj/index.js @@ -216,12 +216,12 @@ ol.reproj.render = function(width, height, pixelRatio, var p1 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u1, v1); var p2 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u2, v2); - context.moveTo(p0[0], p0[1]); - context.lineTo(p1[0], p1[1]); + context.moveTo(p1[0], p1[1]); + context.lineTo(p0[0], p0[1]); context.lineTo(p2[0], p2[1]); } else { - context.moveTo(u0, v0); - context.lineTo(u1, v1); + context.moveTo(u1, v1); + context.lineTo(u0, v0); context.lineTo(u2, v2); } context.clip(); @@ -255,8 +255,8 @@ ol.reproj.render = function(width, height, pixelRatio, v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution; context.beginPath(); - context.moveTo(u0, v0); - context.lineTo(u1, v1); + context.moveTo(u1, v1); + context.lineTo(u0, v0); context.lineTo(u2, v2); context.closePath(); context.stroke(); From 65d03bfbfb79db4b1a777b000cf7a8a000cad849 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Wed, 24 Aug 2016 12:04:50 +0200 Subject: [PATCH 6/6] Bail out when there is no fill and stroke --- src/ol/render/canvas/replay.js | 12 +++++--- test/spec/ol/renderer/canvas/replay.test.js | 31 +++++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index cc28703652..255c59d4ec 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -1202,12 +1202,16 @@ ol.inherits(ol.render.canvas.PolygonReplay, ol.render.canvas.Replay); */ ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCoordinates, offset, ends, stride) { var state = this.state_; + var fill = state.fillStyle !== undefined; + var stroke = state.strokeStyle != undefined; + var numEnds = ends.length; + if (!fill && !stroke) { + return ends[numEnds - 1]; + } var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH]; this.instructions.push(beginPathInstruction); this.hitDetectionInstructions.push(beginPathInstruction); - var stroke = state.strokeStyle != undefined; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { + for (var i = 0; i < numEnds; ++i) { var end = ends[i]; var myBegin = this.coordinates.length; var myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, @@ -1231,7 +1235,7 @@ ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCo // FIXME or all polygons together? var fillInstruction = [ol.render.canvas.Instruction.FILL]; this.hitDetectionInstructions.push(fillInstruction); - if (state.fillStyle !== undefined) { + if (fill) { this.instructions.push(fillInstruction); } if (stroke) { diff --git a/test/spec/ol/renderer/canvas/replay.test.js b/test/spec/ol/renderer/canvas/replay.test.js index bace66747d..ed2feb861f 100644 --- a/test/spec/ol/renderer/canvas/replay.test.js +++ b/test/spec/ol/renderer/canvas/replay.test.js @@ -108,14 +108,35 @@ describe('ol.render.canvas.LineStringReplay', function() { describe('ol.render.canvas.PolygonReplay', function() { + var replay; + + beforeEach(function() { + var tolerance = 1; + var extent = [-180, -90, 180, 90]; + var resolution = 10; + replay = new ol.render.canvas.PolygonReplay(tolerance, extent, + resolution); + }); + + describe('#drawFlatCoordinatess_()', function() { + it('returns correct offset', function() { + var coords = [1, 2, 3, 4, 5, 6, 1, 2, 1, 2, 3, 4, 5, 6, 1, 2]; + var ends = [7, 14]; + var stroke = new ol.style.Stroke({ + width: 5 + }); + replay.setFillStrokeStyle(null, stroke); + var offset = replay.drawFlatCoordinatess_(coords, 0, ends, 2); + expect(offset).to.be(14); + replay.setFillStrokeStyle(null, null); + offset = replay.drawFlatCoordinatess_(coords, 0, ends, 2); + expect(offset).to.be(14); + }); + }); + describe('#getBufferedMaxExtent()', function() { it('buffers the max extent to accommodate stroke width', function() { - var tolerance = 1; - var extent = [-180, -90, 180, 90]; - var resolution = 10; - var replay = new ol.render.canvas.PolygonReplay(tolerance, extent, - resolution); var stroke = new ol.style.Stroke({ width: 5 });