Do not rotate map canvas after composition

This requires to draw tile layers to an intermediate canvas again, but
only when the view is rotated.
This commit is contained in:
Andreas Hocevar
2016-03-18 20:32:14 +01:00
parent 9c573a5b3c
commit 577e45cd97
5 changed files with 87 additions and 41 deletions
+6 -29
View File
@@ -19,6 +19,7 @@ goog.require('ol.layer.Vector');
goog.require('ol.layer.VectorTile'); goog.require('ol.layer.VectorTile');
goog.require('ol.render.Event'); goog.require('ol.render.Event');
goog.require('ol.render.EventType'); goog.require('ol.render.EventType');
goog.require('ol.render.canvas');
goog.require('ol.render.canvas.Immediate'); goog.require('ol.render.canvas.Immediate');
goog.require('ol.renderer.Map'); goog.require('ol.renderer.Map');
goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.ImageLayer');
@@ -181,7 +182,7 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
return; return;
} }
var context; var context = this.context_;
var pixelRatio = frameState.pixelRatio; var pixelRatio = frameState.pixelRatio;
var width = Math.round(frameState.size[0] * pixelRatio); var width = Math.round(frameState.size[0] * pixelRatio);
var height = Math.round(frameState.size[1] * pixelRatio); var height = Math.round(frameState.size[1] * pixelRatio);
@@ -189,29 +190,10 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
this.canvas_.width = width; this.canvas_.width = width;
this.canvas_.height = height; this.canvas_.height = height;
} else { } else {
this.context_.clearRect(0, 0, width, height); context.clearRect(0, 0, width, height);
} }
var rotation = frameState.viewState.rotation; var rotation = frameState.viewState.rotation;
var pixelExtent;
if (rotation) {
context = this.renderContext_;
pixelExtent = ol.extent.getForViewAndSize(this.pixelCenter_, pixelRatio,
rotation, frameState.size, this.pixelExtent_);
var renderWidth = Math.round(ol.extent.getWidth(pixelExtent));
var renderHeight = Math.round(ol.extent.getHeight(pixelExtent));
var renderCanvas = this.renderCanvas_;
if (renderCanvas.width != renderWidth || renderCanvas.height != renderHeight) {
renderCanvas.width = renderWidth;
renderCanvas.height = renderHeight;
this.renderContext_.translate(Math.round((renderWidth - width) / 2),
Math.round((renderHeight - height) / 2));
} else {
this.renderContext_.clearRect(0, 0, renderWidth, renderHeight);
}
} else {
context = this.context_;
}
this.calculateMatrices2D(frameState); this.calculateMatrices2D(frameState);
@@ -220,6 +202,8 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
var layerStatesArray = frameState.layerStatesArray; var layerStatesArray = frameState.layerStatesArray;
ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex); ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex);
ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2);
var viewResolution = frameState.viewState.resolution; var viewResolution = frameState.viewState.resolution;
var i, ii, layer, layerRenderer, layerState; var i, ii, layer, layerRenderer, layerState;
for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
@@ -237,14 +221,7 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
} }
} }
if (rotation) { ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2);
this.context_.translate(width / 2, height / 2);
this.context_.rotate(rotation);
this.context_.drawImage(this.renderCanvas_,
Math.round(pixelExtent[0]), Math.round(pixelExtent[1]));
this.context_.rotate(-rotation);
this.context_.translate(-width / 2, -height / 2);
}
this.dispatchComposeEvent_( this.dispatchComposeEvent_(
ol.render.EventType.POSTCOMPOSE, frameState); ol.render.EventType.POSTCOMPOSE, frameState);
@@ -3,6 +3,7 @@
goog.provide('ol.renderer.canvas.TileLayer'); goog.provide('ol.renderer.canvas.TileLayer');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.vec.Mat4');
goog.require('ol.TileRange'); goog.require('ol.TileRange');
goog.require('ol.TileState'); goog.require('ol.TileState');
goog.require('ol.array'); goog.require('ol.array');
@@ -12,6 +13,7 @@ goog.require('ol.layer.Tile');
goog.require('ol.render.EventType'); goog.require('ol.render.EventType');
goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.canvas.Layer');
goog.require('ol.source.Tile'); goog.require('ol.source.Tile');
goog.require('ol.vec.Mat4');
/** /**
@@ -41,6 +43,12 @@ ol.renderer.canvas.TileLayer = function(tileLayer) {
*/ */
this.tmpExtent_ = ol.extent.createEmpty(); this.tmpExtent_ = ol.extent.createEmpty();
/**
* @private
* @type {!goog.vec.Mat4.Number}
*/
this.imageTransform_ = goog.vec.Mat4.createNumber();
}; };
goog.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer); goog.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer);
@@ -55,7 +63,10 @@ ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
var center = viewState.center; var center = viewState.center;
var projection = viewState.projection; var projection = viewState.projection;
var resolution = viewState.resolution; var resolution = viewState.resolution;
var rotation = viewState.rotation;
var size = frameState.size; var size = frameState.size;
var offsetX = Math.round(pixelRatio * size[0] / 2);
var offsetY = Math.round(pixelRatio * size[1] / 2);
var pixelScale = pixelRatio / resolution; var pixelScale = pixelRatio / resolution;
var layer = this.getLayer(); var layer = this.getLayer();
var source = layer.getSource(); var source = layer.getSource();
@@ -67,17 +78,29 @@ ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
this.dispatchPreComposeEvent(context, frameState, transform); this.dispatchPreComposeEvent(context, frameState, transform);
var renderContext; var renderContext = context;
if (layer.hasListener(ol.render.EventType.RENDER)) { var hasRenderListeners = layer.hasListener(ol.render.EventType.RENDER);
// resize and clear var drawOffsetX, drawOffsetY, drawScale, drawSize;
this.context_.canvas.width = context.canvas.width; if (rotation || hasRenderListeners) {
this.context_.canvas.height = context.canvas.height;
renderContext = this.context_; renderContext = this.context_;
} else { var renderCanvas = renderContext.canvas;
renderContext = context; var tilePixelRatio = source.getTilePixelRatio(pixelRatio);
drawScale = tilePixelRatio / pixelRatio;
var width = context.canvas.width * drawScale;
var height = context.canvas.height * drawScale;
// Make sure the canvas is big enough for all possible rotation angles
drawSize = Math.round(Math.sqrt(width * width + height * height));
if (renderCanvas.width != drawSize) {
renderCanvas.width = renderCanvas.height = drawSize;
} else {
renderContext.clearRect(0, 0, drawSize, drawSize);
}
drawOffsetX = (drawSize - width) / 2 / drawScale;
drawOffsetY = (drawSize - height) / 2 / drawScale;
pixelScale *= drawScale;
offsetX = Math.round(drawScale * (offsetX + drawOffsetX))
offsetY = Math.round(drawScale * (offsetY + drawOffsetY));
} }
var offsetX = Math.round(pixelRatio * size[0] / 2);
var offsetY = Math.round(pixelRatio * size[1] / 2);
// for performance reasons, context.save / context.restore is not used // for performance reasons, context.save / context.restore is not used
// to save and restore the transformation matrix and the opacity. // to save and restore the transformation matrix and the opacity.
@@ -142,9 +165,17 @@ ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
} }
} }
if (renderContext != context) { if (hasRenderListeners) {
this.dispatchRenderEvent(renderContext, frameState, transform); var dX = drawOffsetX - offsetX / drawScale + offsetX;
context.drawImage(renderContext.canvas, 0, 0); var dY = drawOffsetY - offsetY / drawScale + offsetY;
var imageTransform = ol.vec.Mat4.makeTransform2D(this.imageTransform_,
drawSize / 2 - dX, drawSize / 2 - dY, pixelScale, -pixelScale,
-rotation, -center[0] + dX / pixelScale, -center[1] - dY / pixelScale);
this.dispatchRenderEvent(renderContext, frameState, imageTransform);
}
if (rotation || hasRenderListeners) {
context.drawImage(renderContext.canvas, -Math.round(drawOffsetX),
-Math.round(drawOffsetY), drawSize / drawScale, drawSize / drawScale);
} }
renderContext.globalAlpha = alpha; renderContext.globalAlpha = alpha;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 753 B

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+38
View File
@@ -188,13 +188,51 @@ describe('ol.rendering.layer.Tile', function() {
}); });
describe('tile layer with render listener', function() {
var source, onAddLayer;
beforeEach(function() {
source = new ol.source.XYZ({
url: 'spec/ol/data/tiles/osm/{z}/{x}/{y}.png'
});
onAddLayer = function(evt) {
evt.element.on('render', function(e) {
e.vectorContext.setImageStyle(new ol.style.Circle({
radius: 5,
snapToPixel: false,
fill: new ol.style.Fill({color: 'yellow'}),
stroke: new ol.style.Stroke({color: 'red', width: 1})
}));
e.vectorContext.drawPointGeometry(new ol.geom.Point(
ol.proj.transform([-123, 38], 'EPSG:4326', 'EPSG:3857')));
});
}
});
afterEach(function() {
disposeMap(map);
});
it('works with the canvas renderer', function(done) {
map = createMap('canvas');
map.getLayers().on('add', onAddLayer);
waitForTiles([source], {}, function() {
expectResemble(map, 'spec/ol/layer/expected/render-canvas.png',
2.6, done);
});
});
});
}); });
goog.require('ol.Map'); goog.require('ol.Map');
goog.require('ol.View'); goog.require('ol.View');
goog.require('ol.geom.Point');
goog.require('ol.layer.Tile'); goog.require('ol.layer.Tile');
goog.require('ol.object'); goog.require('ol.object');
goog.require('ol.proj'); goog.require('ol.proj');
goog.require('ol.source.TileImage'); goog.require('ol.source.TileImage');
goog.require('ol.source.XYZ'); goog.require('ol.source.XYZ');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.tilegrid.TileGrid'); goog.require('ol.tilegrid.TileGrid');