diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index 47b9e4e2be..257324cab1 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -50,10 +50,11 @@ ol.render.canvas.Instruction = { * @implements {ol.render.IVectorContext} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. * @protected * @struct */ -ol.render.canvas.Replay = function(tolerance, maxExtent) { +ol.render.canvas.Replay = function(tolerance, maxExtent, resolution) { /** * @protected @@ -67,6 +68,18 @@ ol.render.canvas.Replay = function(tolerance, maxExtent) { */ this.maxExtent = maxExtent; + /** + * @protected + * @type {number} + */ + this.maxLineWidth = 0; + + /** + * @protected + * @type {number} + */ + this.resolution = resolution; + /** * @private * @type {Array.<*>} @@ -137,7 +150,7 @@ ol.render.canvas.Replay.prototype.appendFlatCoordinates = function(flatCoordinates, offset, end, stride, close) { var myEnd = this.coordinates.length; - var extent = this.maxExtent; + var extent = this.getBufferedMaxExtent(); var lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]]; var nextCoord = [NaN, NaN]; var skipped = true; @@ -597,6 +610,18 @@ ol.render.canvas.Replay.prototype.endGeometry = ol.render.canvas.Replay.prototype.finish = goog.nullFunction; +/** + * Get the buffered rendering extent. Rendering will be clipped to the extent + * provided to the constructor. To account for symbolizers that may intersect + * this extent, we calculate a buffered extent (e.g. based on stroke width). + * @return {ol.Extent} The buffered rendering extent. + * @protected + */ +ol.render.canvas.Replay.prototype.getBufferedMaxExtent = function() { + return this.maxExtent; +}; + + /** * @return {ol.Extent} Extent. */ @@ -629,12 +654,13 @@ ol.render.canvas.Replay.prototype.setTextStyle = goog.abstractMethod; * @extends {ol.render.canvas.Replay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. * @protected * @struct */ -ol.render.canvas.ImageReplay = function(tolerance, maxExtent) { +ol.render.canvas.ImageReplay = function(tolerance, maxExtent, resolution) { - goog.base(this, tolerance, maxExtent); + goog.base(this, tolerance, maxExtent, resolution); /** * @private @@ -858,12 +884,13 @@ ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) { * @extends {ol.render.canvas.Replay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. * @protected * @struct */ -ol.render.canvas.LineStringReplay = function(tolerance, maxExtent) { +ol.render.canvas.LineStringReplay = function(tolerance, maxExtent, resolution) { - goog.base(this, tolerance, maxExtent); + goog.base(this, tolerance, maxExtent, resolution); /** * @private @@ -922,6 +949,19 @@ ol.render.canvas.LineStringReplay.prototype.drawFlatCoordinates_ = }; +/** + * @inheritDoc + */ +ol.render.canvas.LineStringReplay.prototype.getBufferedMaxExtent = function() { + var extent = this.maxExtent; + if (this.maxLineWidth) { + extent = ol.extent.buffer( + extent, this.resolution * (this.maxLineWidth + 1) / 2); + } + return extent; +}; + + /** * @private */ @@ -1067,6 +1107,7 @@ ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle = var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); this.state_.miterLimit = goog.isDef(strokeStyleMiterLimit) ? strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; + this.maxLineWidth = Math.max(this.maxLineWidth, this.state_.lineWidth); }; @@ -1076,12 +1117,13 @@ ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle = * @extends {ol.render.canvas.Replay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. * @protected * @struct */ -ol.render.canvas.PolygonReplay = function(tolerance, maxExtent) { +ol.render.canvas.PolygonReplay = function(tolerance, maxExtent, resolution) { - goog.base(this, tolerance, maxExtent); + goog.base(this, tolerance, maxExtent, resolution); /** * @private @@ -1316,6 +1358,19 @@ ol.render.canvas.PolygonReplay.prototype.finish = function() { }; +/** + * @inheritDoc + */ +ol.render.canvas.PolygonReplay.prototype.getBufferedMaxExtent = function() { + var extent = this.maxExtent; + if (this.maxLineWidth) { + extent = ol.extent.buffer( + extent, this.resolution * (this.maxLineWidth + 1) / 2); + } + return extent; +}; + + /** * @inheritDoc */ @@ -1350,6 +1405,7 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle = var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); state.miterLimit = goog.isDef(strokeStyleMiterLimit) ? strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; + this.maxLineWidth = Math.max(this.maxLineWidth, state.lineWidth); } else { state.strokeStyle = undefined; state.lineCap = undefined; @@ -1410,12 +1466,13 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() { * @extends {ol.render.canvas.Replay} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. * @protected * @struct */ -ol.render.canvas.TextReplay = function(tolerance, maxExtent) { +ol.render.canvas.TextReplay = function(tolerance, maxExtent, resolution) { - goog.base(this, tolerance, maxExtent); + goog.base(this, tolerance, maxExtent, resolution); /** * @private @@ -1727,9 +1784,10 @@ ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) { * @implements {ol.render.IReplayGroup} * @param {number} tolerance Tolerance. * @param {ol.Extent} maxExtent Max extent. + * @param {number} resolution Resolution. * @struct */ -ol.render.canvas.ReplayGroup = function(tolerance, maxExtent) { +ol.render.canvas.ReplayGroup = function(tolerance, maxExtent, resolution) { /** * @private @@ -1743,6 +1801,12 @@ ol.render.canvas.ReplayGroup = function(tolerance, maxExtent) { */ this.maxExtent_ = maxExtent; + /** + * @private + * @type {number} + */ + this.resolution_ = resolution; + /** * @private * @type {Object.} + * function(new: ol.render.canvas.Replay, number, ol.Extent, + * number)>} */ ol.render.canvas.BATCH_CONSTRUCTORS_ = { 'Image': ol.render.canvas.ImageReplay, diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index cf6715b075..5b0f97c226 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -243,7 +243,8 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = styleFunction = ol.feature.defaultStyleFunction; } var tolerance = frameStateResolution / (2 * pixelRatio); - var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent); + var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent, + frameStateResolution); vectorSource.forEachFeatureInExtent(extent, /** * @param {ol.Feature} feature Feature. diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index 16861f4bfc..07c8196ff8 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -111,7 +111,8 @@ ol.source.ImageVector.prototype.canvasFunctionInternal_ = function(extent, resolution, pixelRatio, size, projection) { var tolerance = resolution / (2 * pixelRatio); - var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent); + var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent, + resolution); var loading = false; this.source_.forEachFeatureInExtent(extent, diff --git a/test/spec/ol/renderer/canvas/canvasreplay.test.js b/test/spec/ol/renderer/canvas/canvasreplay.test.js index c9bf316fe7..a537079767 100644 --- a/test/spec/ol/renderer/canvas/canvasreplay.test.js +++ b/test/spec/ol/renderer/canvas/canvasreplay.test.js @@ -7,7 +7,7 @@ describe('ol.render.canvas.Replay', function() { it('creates a new replay batch', function() { var tolerance = 10; var extent = [-180, -90, 180, 90]; - var replay = new ol.render.canvas.Replay(tolerance, extent); + var replay = new ol.render.canvas.Replay(tolerance, extent, 1); expect(replay).to.be.a(ol.render.canvas.Replay); }); @@ -17,7 +17,7 @@ describe('ol.render.canvas.Replay', function() { var replay; beforeEach(function() { - replay = new ol.render.canvas.Replay(1, [-180, -90, 180, 90]); + replay = new ol.render.canvas.Replay(1, [-180, -90, 180, 90], 1); }); it('appends coordinates that are within the max extent', function() { @@ -78,4 +78,49 @@ describe('ol.render.canvas.Replay', function() { }); +describe('ol.render.canvas.LineStringReplay', function() { + + describe('#getBufferedMaxExtent()', function() { + + it('buffers the max extent to accomodate stroke width', function() { + var tolerance = 1; + var extent = [-180, -90, 180, 90]; + var resolution = 10; + var replay = new ol.render.canvas.LineStringReplay(tolerance, extent, + resolution); + var stroke = new ol.style.Stroke({ + width: 2 + }); + replay.setFillStrokeStyle(null, stroke); + var buffered = replay.getBufferedMaxExtent(); + expect(buffered).to.eql([-195, -105, 195, 105]); + }); + + }); + +}); + +describe('ol.render.canvas.PolygonReplay', function() { + + describe('#getBufferedMaxExtent()', function() { + + it('buffers the max extent to accomodate 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 + }); + replay.setFillStrokeStyle(null, stroke); + var buffered = replay.getBufferedMaxExtent(); + expect(buffered).to.eql([-210, -120, 210, 120]); + }); + + }); + +}); + goog.require('ol.render.canvas.Replay'); +goog.require('ol.style.Stroke');