From cfe78f27666d5fc8c5e92c0a5726f27473f5723f Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 28 Oct 2016 09:16:30 +0200 Subject: [PATCH] Merge pull request #6034 from ahocevar/gradient-pattern-origin Use geometry extent's top left corner as pattern/gradient origin --- changelog/upgrade-notes.md | 5 ++++ examples/canvas-gradient-pattern.js | 11 +++---- src/ol/render/canvas/polygonreplay.js | 19 +++++++----- src/ol/render/canvas/replay.js | 27 +++++++++--------- src/ol/typedefs.js | 4 ++- .../polygon-pattern-gradient-canvas.png | Bin 1325 -> 1304 bytes 6 files changed, 39 insertions(+), 27 deletions(-) diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index b1ce5f6d12..4cd45e5919 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -1,5 +1,10 @@ ## Upgrade notes +#### `ol.style.Fill` with `CanvasGradient` or `CanvasPattern` + +The origin for gradients and patterns has changed from `[0, 0]` to the top-left +corner of the extent of the geometry being filled. + ### v3.19.0 #### `ol.style.Fill` with `CanvasGradient` or `CanvasPattern` diff --git a/examples/canvas-gradient-pattern.js b/examples/canvas-gradient-pattern.js index c3dde464b3..58ffb8ad75 100644 --- a/examples/canvas-gradient-pattern.js +++ b/examples/canvas-gradient-pattern.js @@ -1,5 +1,6 @@ goog.require('ol.Map'); goog.require('ol.View'); +goog.require('ol.extent'); goog.require('ol.format.GeoJSON'); goog.require('ol.has'); goog.require('ol.layer.Vector'); @@ -20,11 +21,11 @@ var pixelRatio = ol.has.DEVICE_PIXEL_RATIO; function gradient(feature, resolution) { var extent = feature.getGeometry().getExtent(); // Gradient starts on the left edge of each feature, and ends on the right. - // Coordinate origin is [0, 0], so we just divide by resolution and multiply - // with pixelRatio to match the renderer's pixel coordinate system. - var grad = context.createLinearGradient( - extent[0] / resolution * pixelRatio, 0, - extent[2] / resolution * pixelRatio, 0); + // Coordinate origin is the top-left corner of the extent of the geometry, so + // we just divide the geometry's extent width by resolution and multiply with + // pixelRatio to match the renderer's pixel coordinate system. + var grad = context.createLinearGradient(0, 0, + ol.extent.getWidth(extent) / resolution * pixelRatio, 0); grad.addColorStop(0, 'red'); grad.addColorStop(1 / 6, 'orange'); grad.addColorStop(2 / 6, 'yellow'); diff --git a/src/ol/render/canvas/polygonreplay.js b/src/ol/render/canvas/polygonreplay.js index d12243f3d7..b845e5aa88 100644 --- a/src/ol/render/canvas/polygonreplay.js +++ b/src/ol/render/canvas/polygonreplay.js @@ -132,7 +132,7 @@ ol.render.canvas.PolygonReplay.prototype.drawCircle = function(circleGeometry, f ol.DEBUG && console.assert(state.lineWidth !== undefined, 'state.lineWidth should be defined'); } - this.setFillStrokeStyles_(); + this.setFillStrokeStyles_(circleGeometry); this.beginGeometry(circleGeometry, feature); // always fill the circle for hit detection this.hitDetectionInstructions.push( @@ -182,7 +182,7 @@ ol.render.canvas.PolygonReplay.prototype.drawPolygon = function(polygonGeometry, ol.DEBUG && console.assert(state.lineWidth !== undefined, 'state.lineWidth should be defined'); } - this.setFillStrokeStyles_(); + this.setFillStrokeStyles_(polygonGeometry); this.beginGeometry(polygonGeometry, feature); // always fill the polygon for hit detection this.hitDetectionInstructions.push( @@ -217,7 +217,7 @@ ol.render.canvas.PolygonReplay.prototype.drawMultiPolygon = function(multiPolygo ol.DEBUG && console.assert(state.lineWidth !== undefined, 'state.lineWidth should be defined'); } - this.setFillStrokeStyles_(); + this.setFillStrokeStyles_(multiPolygonGeometry); this.beginGeometry(multiPolygonGeometry, feature); // always fill the multi-polygon for hit detection this.hitDetectionInstructions.push( @@ -332,8 +332,9 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle = function(fillStyle /** * @private + * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. */ -ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() { +ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function(geometry) { var state = this.state_; var fillStyle = state.fillStyle; var strokeStyle = state.strokeStyle; @@ -342,9 +343,13 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() { var lineJoin = state.lineJoin; var lineWidth = state.lineWidth; var miterLimit = state.miterLimit; - if (fillStyle !== undefined && state.currentFillStyle != fillStyle) { - this.instructions.push( - [ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle, typeof fillStyle != 'string']); + if (typeof fillStyle !== 'string' || fillStyle !== undefined && state.currentFillStyle != fillStyle) { + var fillInstruction = [ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle]; + if (typeof fillStyle !== 'string') { + var fillExtent = geometry.getExtent(); + fillInstruction.push([fillExtent[0], fillExtent[3]]); + } + this.instructions.push(fillInstruction); state.currentFillStyle = state.fillStyle; } if (strokeStyle !== undefined) { diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index f5993bb782..cb9fce4c17 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -60,9 +60,9 @@ ol.render.canvas.Replay = function(tolerance, maxExtent, resolution, overlaps) { /** * @private - * @type {boolean} + * @type {ol.Coordinate} */ - this.alignFill_ = false; + this.fillOrigin_; /** * @private @@ -194,18 +194,17 @@ ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, feature) { /** * @private * @param {CanvasRenderingContext2D} context Context. - * @param {ol.Transform} transform Transform. * @param {number} rotation Rotation. */ -ol.render.canvas.Replay.prototype.fill_ = function(context, transform, rotation) { - if (this.alignFill_) { - context.translate(transform[4], transform[5]); +ol.render.canvas.Replay.prototype.fill_ = function(context, rotation) { + if (this.fillOrigin_) { + var origin = ol.transform.apply(this.renderedTransform_, this.fillOrigin_.slice()); + context.translate(origin[0], origin[1]); context.rotate(rotation); } context.fill(); - if (this.alignFill_) { - context.rotate(-rotation); - context.translate(-transform[4], -transform[5]); + if (this.fillOrigin_) { + context.setTransform.apply(context, this.resetTransform_); } }; @@ -275,7 +274,7 @@ ol.render.canvas.Replay.prototype.replay_ = function( break; case ol.render.canvas.Instruction.BEGIN_PATH: if (pendingFill > batchSize) { - this.fill_(context, transform, viewRotation); + this.fill_(context, viewRotation); pendingFill = 0; } if (pendingStroke > batchSize) { @@ -452,7 +451,7 @@ ol.render.canvas.Replay.prototype.replay_ = function( if (batchSize) { pendingFill++; } else { - this.fill_(context, transform, viewRotation); + this.fill_(context, viewRotation); } ++i; break; @@ -490,10 +489,10 @@ ol.render.canvas.Replay.prototype.replay_ = function( ol.colorlike.isColorLike(instruction[1]), '2nd instruction should be a string, ' + 'CanvasPattern, or CanvasGradient'); - this.alignFill_ = instruction[2]; + this.fillOrigin_ = instruction[2]; if (pendingFill) { - this.fill_(context, transform, viewRotation); + this.fill_(context, viewRotation); pendingFill = 0; } @@ -559,7 +558,7 @@ ol.render.canvas.Replay.prototype.replay_ = function( } } if (pendingFill) { - this.fill_(context, transform, viewRotation); + this.fill_(context, viewRotation); } if (pendingStroke) { context.stroke(); diff --git a/src/ol/typedefs.js b/src/ol/typedefs.js index 74d3f29fef..7901c923f0 100644 --- a/src/ol/typedefs.js +++ b/src/ol/typedefs.js @@ -121,7 +121,9 @@ ol.Color; /** * A type accepted by CanvasRenderingContext2D.fillStyle * or CanvasRenderingContext2D.strokeStyle. - * Represents a color, pattern, or gradient. + * Represents a color, pattern, or gradient. The origin for patterns and + * gradients as fill style is the top-left corner of the extent of the geometry + * being filled. * * @typedef {string|CanvasPattern|CanvasGradient} */ diff --git a/test_rendering/spec/ol/style/expected/polygon-pattern-gradient-canvas.png b/test_rendering/spec/ol/style/expected/polygon-pattern-gradient-canvas.png index bb4302a78f6b9da5d2b4891919fba1e7a1be6a31..4a2fb3412acafaed1be8f9fba9a5a28d3960aba3 100644 GIT binary patch delta 1284 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 Q0RR9107*qoM6N<$f;u{Y@c;k-