diff --git a/src/ol/render/canvas/canvas.js b/src/ol/render/canvas/canvas.js index 21e546d579..be3d39a949 100644 --- a/src/ol/render/canvas/canvas.js +++ b/src/ol/render/canvas/canvas.js @@ -94,3 +94,18 @@ ol.render.canvas.defaultTextBaseline = 'middle'; * @type {number} */ ol.render.canvas.defaultLineWidth = 1; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {number} rotation Rotation. + * @param {number} offsetX X offset. + * @param {number} offsetY Y offset. + */ +ol.render.canvas.rotateAtOffset = function(context, rotation, offsetX, offsetY) { + if (rotation !== 0) { + context.translate(offsetX, offsetY); + context.rotate(rotation); + context.translate(-offsetX, -offsetY); + } +}; diff --git a/src/ol/renderer/canvas/canvaslayerrenderer.js b/src/ol/renderer/canvas/canvaslayerrenderer.js index 6ccde19026..e34f430fcf 100644 --- a/src/ol/renderer/canvas/canvaslayerrenderer.js +++ b/src/ol/renderer/canvas/canvaslayerrenderer.js @@ -2,12 +2,11 @@ goog.provide('ol.renderer.canvas.Layer'); goog.require('goog.asserts'); goog.require('goog.vec.Mat4'); -goog.require('ol.array'); -goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.layer.Layer'); goog.require('ol.render.Event'); goog.require('ol.render.EventType'); +goog.require('ol.render.canvas'); goog.require('ol.render.canvas.Immediate'); goog.require('ol.renderer.Layer'); goog.require('ol.vec.Mat4'); @@ -53,6 +52,7 @@ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerStat var pixelRatio = frameState.pixelRatio; var width = frameState.size[0] * pixelRatio; var height = frameState.size[1] * pixelRatio; + var rotation = frameState.viewState.rotation; var topLeft = ol.extent.getTopLeft(extent); var topRight = ol.extent.getTopRight(extent); var bottomRight = ol.extent.getBottomRight(extent); @@ -68,18 +68,14 @@ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerStat bottomLeft, bottomLeft); context.save(); - context.translate(width / 2, height / 2); - context.rotate(-frameState.viewState.rotation); - context.translate(-width / 2, -height / 2); + ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); context.beginPath(); context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio); context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio); context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio); context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio); context.clip(); - context.translate(width / 2, height / 2); - context.rotate(frameState.viewState.rotation); - context.translate(-width / 2, -height / 2); + ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); } var imageTransform = this.getImageTransform(); @@ -121,9 +117,8 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, contex if (layer.hasListener(type)) { var width = frameState.size[0] * frameState.pixelRatio; var height = frameState.size[1] * frameState.pixelRatio; - context.translate(width / 2, height / 2); - context.rotate(-frameState.viewState.rotation); - context.translate(-width / 2, -height / 2); + var rotation = frameState.viewState.rotation; + ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); var transform = opt_transform !== undefined ? opt_transform : this.getTransform(frameState, 0); var render = new ol.render.canvas.Immediate( @@ -133,9 +128,7 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, contex context, null); layer.dispatchEvent(composeEvent); render.flush(); - context.translate(width / 2, height / 2); - context.rotate(frameState.viewState.rotation); - context.translate(-width / 2, -height / 2); + ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); } }; @@ -228,46 +221,3 @@ ol.renderer.canvas.Layer.prototype.getPixelOnCanvas = function(pixelOnMap, image ol.vec.Mat4.multVec2(imageTransformInv, pixelOnMap, pixelOnCanvas); return pixelOnCanvas; }; - - -/** - * @param {ol.Size} size Size. - * @return {boolean} True when the canvas with the current size does not exceed - * the maximum dimensions. - */ -ol.renderer.canvas.Layer.testCanvasSize = (function() { - - /** - * @type {CanvasRenderingContext2D} - */ - var context = null; - - /** - * @type {ImageData} - */ - var imageData = null; - - return function(size) { - if (!context) { - context = ol.dom.createCanvasContext2D(1, 1); - imageData = context.createImageData(1, 1); - var data = imageData.data; - data[0] = 42; - data[1] = 84; - data[2] = 126; - data[3] = 255; - } - var canvas = context.canvas; - var good = size[0] <= canvas.width && size[1] <= canvas.height; - if (!good) { - canvas.width = size[0]; - canvas.height = size[1]; - var x = size[0] - 1; - var y = size[1] - 1; - context.putImageData(imageData, x, y); - var result = context.getImageData(x, y, 1, 1); - good = ol.array.equals(imageData.data, result.data); - } - return good; - }; -})(); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index b3361be8b7..7599124d24 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -7,6 +7,7 @@ goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.layer.Vector'); goog.require('ol.render.EventType'); +goog.require('ol.render.canvas'); goog.require('ol.render.canvas.ReplayGroup'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.vector'); @@ -108,9 +109,8 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, lay var width = frameState.size[0] * pixelRatio; var height = frameState.size[1] * pixelRatio; - replayContext.translate(width / 2, height / 2); - replayContext.rotate(-rotation); - replayContext.translate(-width / 2, -height / 2); + ol.render.canvas.rotateAtOffset(replayContext, -rotation, + width / 2, height / 2); replayGroup.replay(replayContext, pixelRatio, transform, rotation, skippedFeatureUids); if (vectorSource.getWrapX() && projection.canWrapX() && @@ -140,9 +140,8 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, lay // restore original transform for render and compose events transform = this.getTransform(frameState, 0); } - replayContext.translate(width / 2, height / 2); - replayContext.rotate(rotation); - replayContext.translate(-width / 2, -height / 2); + ol.render.canvas.rotateAtOffset(replayContext, rotation, + width / 2, height / 2); if (replayContext != context) { this.dispatchRenderEvent(replayContext, frameState, transform); diff --git a/test/spec/ol/render/canvas.test.js b/test/spec/ol/render/canvas.test.js new file mode 100644 index 0000000000..f39f6a7e53 --- /dev/null +++ b/test/spec/ol/render/canvas.test.js @@ -0,0 +1,25 @@ +goog.provide('ol.test.render.canvas'); + + +describe('ol.render.canvas', function() { + + describe('rotateAtOffset', function() { + it('rotates a canvas at an offset point', function() { + var context = { + translate: sinon.spy(), + rotate: sinon.spy() + }; + ol.render.canvas.rotateAtOffset(context, Math.PI, 10, 10); + expect(context.translate.callCount).to.be(2); + expect(context.translate.firstCall.args).to.eql([10, 10]); + expect(context.translate.secondCall.args).to.eql([-10, -10]); + expect(context.rotate.callCount).to.be(1); + expect(context.rotate.firstCall.args).to.eql([Math.PI]); + }); + }); + +}); + + +goog.require('ol.render'); +goog.require('ol.render.canvas'); diff --git a/test/spec/ol/renderer/canvas/canvaslayerrenderer.test.js b/test/spec/ol/renderer/canvas/canvaslayerrenderer.test.js new file mode 100644 index 0000000000..f06d144002 --- /dev/null +++ b/test/spec/ol/renderer/canvas/canvaslayerrenderer.test.js @@ -0,0 +1,68 @@ +goog.provide('ol.test.renderer.canvas.Layer'); + + +describe('ol.renderer.canvas.Layer', function() { + + describe('#composeFrame()', function() { + it('clips to layer extent and draws image', function() { + var layer = new ol.layer.Image({ + extent: [1, 2, 3, 4] + }); + var renderer = new ol.renderer.canvas.Layer(layer); + var image = new Image(); + image.width = 3; + image.height = 3; + renderer.getImage = function() { + return image; + }; + var frameState = { + viewState: { + center: [2, 3], + resolution: 1, + rotation: 0 + }, + size: [10, 10], + pixelRatio: 1, + coordinateToPixelMatrix: goog.vec.Mat4.createNumber(), + pixelToCoordinateMatrix: goog.vec.Mat4.createNumber() + }; + renderer.getImageTransform = function() { + return goog.vec.Mat4.createNumberIdentity(); + } + ol.renderer.Map.prototype.calculateMatrices2D(frameState); + var layerState = layer.getLayerState(); + var context = { + save: sinon.spy(), + restore: sinon.spy(), + translate: sinon.spy(), + rotate: sinon.spy(), + beginPath: sinon.spy(), + moveTo: sinon.spy(), + lineTo: sinon.spy(), + clip: sinon.spy(), + drawImage: sinon.spy() + } + renderer.composeFrame(frameState, layerState, context); + expect(context.save.callCount).to.be(1); + expect(context.translate.callCount).to.be(0); + expect(context.rotate.callCount).to.be(0); + expect(context.beginPath.callCount).to.be(1); + expect(context.moveTo.firstCall.args).to.eql([4, 4]); + expect(context.lineTo.firstCall.args).to.eql([6, 4]); + expect(context.lineTo.secondCall.args).to.eql([6, 6]); + expect(context.lineTo.thirdCall.args).to.eql([4, 6]); + expect(context.clip.callCount).to.be(1); + expect(context.drawImage.firstCall.args).to.eql( + [renderer.getImage(), 0, 0, 3, 3, 0, 0, 3, 3]); + expect(context.restore.callCount).to.be(1); + }); + }); + +}); + + +goog.require('ol.render.canvas'); +goog.require('goog.vec.Mat4'); +goog.require('ol.layer.Image'); +goog.require('ol.renderer.Map'); +goog.require('ol.renderer.canvas.Layer');