diff --git a/src/ol/render/canvas/replaygroup.js b/src/ol/render/canvas/replaygroup.js index 40a7722be2..fae6101bd2 100644 --- a/src/ol/render/canvas/replaygroup.js +++ b/src/ol/render/canvas/replaygroup.js @@ -149,6 +149,23 @@ ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function( }; +/** + * @param {ol.Transform} transform Transform. + * @return {Array.} Clip coordinates. + */ +ol.render.canvas.ReplayGroup.prototype.getClipCoords = function(transform) { + var maxExtent = this.maxExtent_; + var minX = maxExtent[0]; + var minY = maxExtent[1]; + var maxX = maxExtent[2]; + var maxY = maxExtent[3]; + var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY]; + ol.geom.flat.transform.transform2D( + flatClipCoords, 0, 8, 2, transform, flatClipCoords); + return flatClipCoords; +}; + + /** * @inheritDoc */ @@ -200,14 +217,7 @@ ol.render.canvas.ReplayGroup.prototype.replay = function(context, pixelRatio, // setup clipping so that the parts of over-simplified geometries are not // visible outside the current extent when panning - var maxExtent = this.maxExtent_; - var minX = maxExtent[0]; - var minY = maxExtent[1]; - var maxX = maxExtent[2]; - var maxY = maxExtent[3]; - var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY]; - ol.geom.flat.transform.transform2D( - flatClipCoords, 0, 8, 2, transform, flatClipCoords); + var flatClipCoords = this.getClipCoords(transform); context.save(); context.beginPath(); context.moveTo(flatClipCoords[0], flatClipCoords[1]); diff --git a/src/ol/renderer/canvas/imagelayer.js b/src/ol/renderer/canvas/imagelayer.js index 828b902e80..6dbf8d4abd 100644 --- a/src/ol/renderer/canvas/imagelayer.js +++ b/src/ol/renderer/canvas/imagelayer.js @@ -34,9 +34,9 @@ ol.renderer.canvas.ImageLayer = function(imageLayer) { /** * @private - * @type {?ol.Transform} + * @type {ol.Transform} */ - this.imageTransformInv_ = null; + this.coordinateToCanvasPixelTransform_ = ol.transform.create(); /** * @private @@ -70,7 +70,7 @@ ol.renderer.canvas.ImageLayer.prototype.forEachFeatureAtCoordinate = function(co /** - * @param {ol.Pixel} pixel Pixel. + * @param {ol.Coordinate} coordinate Coordinate. * @param {olx.FrameState} frameState FrameState. * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer * callback. @@ -78,7 +78,7 @@ ol.renderer.canvas.ImageLayer.prototype.forEachFeatureAtCoordinate = function(co * @return {T|undefined} Callback result. * @template S,T,U */ -ol.renderer.canvas.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { +ol.renderer.canvas.ImageLayer.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, callback, thisArg) { if (!this.getImage()) { return undefined; } @@ -86,8 +86,6 @@ ol.renderer.canvas.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, fr if (this.getLayer().getSource() instanceof ol.source.ImageVector) { // for ImageVector sources use the original hit-detection logic, // so that for example also transparent polygons are detected - var coordinate = ol.transform.apply( - frameState.pixelToCoordinateTransform, pixel.slice()); var hasFeature = this.forEachFeatureAtCoordinate( coordinate, frameState, ol.functions.TRUE, this); @@ -97,13 +95,8 @@ ol.renderer.canvas.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, fr return undefined; } } else { - // for all other image sources directly check the image - if (!this.imageTransformInv_) { - this.imageTransformInv_ = ol.transform.invert(this.imageTransform_.slice()); - } - - var pixelOnCanvas = - this.getPixelOnCanvas(pixel, this.imageTransformInv_); + var pixelOnCanvas = ol.transform.apply( + this.coordinateToCanvasPixelTransform_, coordinate.slice()); if (!this.hitCanvasContext_) { this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1); @@ -145,6 +138,7 @@ ol.renderer.canvas.ImageLayer.prototype.getImageTransform = function() { ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, layerState) { var pixelRatio = frameState.pixelRatio; + var size = frameState.size; var viewState = frameState.viewState; var viewCenter = viewState.center; var viewResolution = viewState.resolution; @@ -197,7 +191,11 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, laye ol.transform.translate(transform, imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); - this.imageTransformInv_ = null; + ol.transform.compose(ol.transform.reset(this.coordinateToCanvasPixelTransform_), + pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], + pixelRatio / viewResolution, -pixelRatio / viewResolution, + 0, + -viewCenter[0], -viewCenter[1]); this.updateAttributions(frameState.attributions, image.getAttributions()); this.updateLogos(frameState, imageSource); } diff --git a/src/ol/renderer/canvas/layer.js b/src/ol/renderer/canvas/layer.js index a9df0e7dea..39699128a3 100644 --- a/src/ol/renderer/canvas/layer.js +++ b/src/ol/renderer/canvas/layer.js @@ -2,6 +2,7 @@ goog.provide('ol.renderer.canvas.Layer'); goog.require('ol'); goog.require('ol.extent'); +goog.require('ol.functions'); goog.require('ol.render.Event'); goog.require('ol.render.canvas'); goog.require('ol.render.canvas.Immediate'); @@ -68,7 +69,7 @@ ol.renderer.canvas.Layer.prototype.clip = function(context, frameState, extent) */ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerState, context) { - this.dispatchPreComposeEvent(context, frameState); + this.preCompose(context, frameState); var image = this.getImage(); if (image) { @@ -102,8 +103,7 @@ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerStat } } - this.dispatchPostComposeEvent(context, frameState); - + this.postCompose(context, frameState, layerState); }; @@ -137,10 +137,11 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, contex /** * @param {CanvasRenderingContext2D} context Context. * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. * @param {ol.Transform=} opt_transform Transform. * @protected */ -ol.renderer.canvas.Layer.prototype.dispatchPostComposeEvent = function(context, frameState, opt_transform) { +ol.renderer.canvas.Layer.prototype.postCompose = function(context, frameState, layerState, opt_transform) { this.dispatchComposeEvent_(ol.render.Event.Type.POSTCOMPOSE, context, frameState, opt_transform); }; @@ -152,7 +153,7 @@ ol.renderer.canvas.Layer.prototype.dispatchPostComposeEvent = function(context, * @param {ol.Transform=} opt_transform Transform. * @protected */ -ol.renderer.canvas.Layer.prototype.dispatchPreComposeEvent = function(context, frameState, opt_transform) { +ol.renderer.canvas.Layer.prototype.preCompose = function(context, frameState, opt_transform) { this.dispatchComposeEvent_(ol.render.Event.Type.PRECOMPOSE, context, frameState, opt_transform); }; @@ -214,12 +215,21 @@ ol.renderer.canvas.Layer.prototype.prepareFrame = function(frameState, layerStat /** - * @param {ol.Pixel} pixelOnMap Pixel. - * @param {ol.Transform} imageTransformInv The transformation matrix - * to convert from a map pixel to a canvas pixel. - * @return {ol.Pixel} The pixel. - * @protected + * @param {ol.Coordinate} coordinate Coordinate. + * @param {olx.FrameState} frameState Frame state. + * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T */ -ol.renderer.canvas.Layer.prototype.getPixelOnCanvas = function(pixelOnMap, imageTransformInv) { - return ol.transform.apply(imageTransformInv, pixelOnMap.slice()); +ol.renderer.canvas.Layer.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, callback, thisArg) { + + var hasFeature = this.forEachFeatureAtCoordinate( + coordinate, frameState, ol.functions.TRUE, this); + + if (hasFeature) { + return callback.call(thisArg, this.getLayer(), null); + } else { + return undefined; + } }; diff --git a/src/ol/renderer/canvas/map.js b/src/ol/renderer/canvas/map.js index 6dcb6a10a6..3e78ec20ab 100644 --- a/src/ol/renderer/canvas/map.js +++ b/src/ol/renderer/canvas/map.js @@ -200,3 +200,36 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { this.scheduleRemoveUnusedLayerRenderers(frameState); this.scheduleExpireIconCache(frameState); }; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg, + layerFilter, thisArg2) { + var result; + var viewState = frameState.viewState; + var viewResolution = viewState.resolution; + + var layerStates = frameState.layerStatesArray; + var numLayers = layerStates.length; + + var coordinate = ol.transform.apply( + frameState.pixelToCoordinateTransform, pixel.slice()); + + var i; + for (i = numLayers - 1; i >= 0; --i) { + var layerState = layerStates[i]; + var layer = layerState.layer; + if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && + layerFilter.call(thisArg2, layer)) { + var layerRenderer = /** @type {ol.renderer.canvas.Layer} */ (this.getLayerRenderer(layer)); + result = layerRenderer.forEachLayerAtCoordinate( + coordinate, frameState, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; +}; diff --git a/src/ol/renderer/canvas/tilelayer.js b/src/ol/renderer/canvas/tilelayer.js index a83f80ab38..5c4ee410b9 100644 --- a/src/ol/renderer/canvas/tilelayer.js +++ b/src/ol/renderer/canvas/tilelayer.js @@ -9,8 +9,6 @@ goog.require('ol.Tile'); goog.require('ol.array'); goog.require('ol.dom'); goog.require('ol.extent'); -goog.require('ol.render.canvas'); -goog.require('ol.render.Event'); goog.require('ol.renderer.canvas.Layer'); @@ -29,9 +27,27 @@ ol.renderer.canvas.TileLayer = function(tileLayer) { */ this.context = ol.dom.createCanvasContext2D(); + /** + * @private + * @type {ol.Extent} + */ + this.renderedExtent_ = null; + + /** + * @private + * @type {number} + */ + this.renderedResolution_; + + /** + * @private + * @type {number} + */ + this.renderedRevision_; + /** * @protected - * @type {!Array.} + * @type {!Array.} */ this.renderedTiles = []; @@ -47,12 +63,24 @@ ol.renderer.canvas.TileLayer = function(tileLayer) { */ this.tmpTileCoord_ = [0, 0, 0]; + /** + * @private + * @type {ol.TileRange} + */ + this.tmpTileRange_ = new ol.TileRange(0, 0, 0, 0); + /** * @private * @type {ol.Transform} */ this.imageTransform_ = ol.transform.create(); + /** + * @private + * @type {ol.Transform} + */ + this.coordinateToCanvasPixelTransform_ = ol.transform.create(); + /** * @protected * @type {number} @@ -66,29 +94,20 @@ ol.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer); /** * @inheritDoc */ -ol.renderer.canvas.TileLayer.prototype.composeFrame = function( - frameState, layerState, context) { - var transform = this.getTransform(frameState, 0); - this.dispatchPreComposeEvent(context, frameState, transform); - this.renderTileImages(context, frameState, layerState); - this.dispatchPostComposeEvent(context, frameState, transform); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.TileLayer.prototype.prepareFrame = function( - frameState, layerState) { +ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layerState) { var pixelRatio = frameState.pixelRatio; + var size = frameState.size; var viewState = frameState.viewState; var projection = viewState.projection; + var viewResolution = viewState.resolution; + var viewCenter = viewState.center; var tileLayer = this.getLayer(); var tileSource = /** @type {ol.source.Tile} */ (tileLayer.getSource()); + var sourceRevision = tileSource.getRevision(); var tileGrid = tileSource.getTileGridForProjection(projection); - var z = tileGrid.getZForResolution(viewState.resolution, this.zDirection); + var z = tileGrid.getZForResolution(viewResolution, this.zDirection); var tileResolution = tileGrid.getResolution(z); var extent = frameState.extent; @@ -102,6 +121,9 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function( var tileRange = tileGrid.getTileRangeForExtentAndResolution( extent, tileResolution); + var imageExtent = tileGrid.getTileRangeExtent(z, tileRange); + + var tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio); /** * @type {Object.>} @@ -113,35 +135,33 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function( tileSource, projection, tilesToDrawByZ); var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError(); - var tmpExtent = this.tmpExtent; - var tmpTileRange = new ol.TileRange(0, 0, 0, 0); - var childTileRange, fullyLoaded, tile, x, y; - var drawableTile = ( - /** - * @param {!ol.Tile} tile Tile. - * @return {boolean} Tile is selected. - */ - function(tile) { - var tileState = tile.getState(); - return tileState == ol.Tile.State.LOADED || - tileState == ol.Tile.State.EMPTY || - tileState == ol.Tile.State.ERROR && !useInterimTilesOnError; - }); + var tmpTileRange = this.tmpTileRange_; + var newTiles = false; + var tile, x, y; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tile = tileSource.getTile(z, x, y, pixelRatio, projection); - if (!drawableTile(tile)) { + var tileState = tile.getState(); + var drawable = tileState == ol.Tile.State.LOADED || + tileState == ol.Tile.State.EMPTY || + tileState == ol.Tile.State.ERROR && !useInterimTilesOnError; + if (!drawable) { tile = tile.getInterimTile(); - } - if (drawableTile(tile)) { - tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; + } else { + if (tileState == ol.Tile.State.LOADED) { + tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; + if (!newTiles && this.renderedTiles.indexOf(tile) == -1) { + newTiles = true; + } + } continue; } - fullyLoaded = tileGrid.forEachTileCoordParentTileRange( + + var fullyLoaded = tileGrid.forEachTileCoordParentTileRange( tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); if (!fullyLoaded) { - childTileRange = tileGrid.getTileCoordChildTileRange( + var childTileRange = tileGrid.getTileCoordChildTileRange( tile.tileCoord, tmpTileRange, tmpExtent); if (childTileRange) { findLoadedTiles(z + 1, childTileRange); @@ -151,35 +171,104 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function( } } - /** @type {Array.} */ - var zs = Object.keys(tilesToDrawByZ).map(Number); - zs.sort(ol.array.numberSafeCompareFunction); - var renderables = this.renderedTiles; - renderables.length = 0; - var i, ii, currentZ, tileCoordKey, tilesToDraw; - for (i = 0, ii = zs.length; i < ii; ++i) { - currentZ = zs[i]; - tilesToDraw = tilesToDrawByZ[currentZ]; - for (tileCoordKey in tilesToDraw) { - tile = tilesToDraw[tileCoordKey]; - if (tile.getState() == ol.Tile.State.LOADED) { - renderables.push(tile); + var hints = frameState.viewHints; + if (!(this.renderedResolution_ && Date.now() - frameState.time > 16 && + (hints[ol.View.Hint.ANIMATING] || hints[ol.View.Hint.INTERACTING])) && + (newTiles || !(this.renderedExtent_ && + ol.extent.equals(this.renderedExtent_, imageExtent)) || + this.renderedRevision_ != sourceRevision)) { + + var tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection); + var width = tileRange.getWidth() * tilePixelSize[0]; + var height = tileRange.getHeight() * tilePixelSize[0]; + var context = this.context; + var canvas = context.canvas; + var opaque = tileSource.getOpaque(projection); + if (canvas.width != width || canvas.height != height) { + canvas.width = width; + canvas.height = height; + } else { + context.clearRect(0, 0, width, height); + } + + this.renderedTiles.length = 0; + /** @type {Array.} */ + var zs = Object.keys(tilesToDrawByZ).map(Number); + zs.sort(ol.array.numberSafeCompareFunction); + var currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii; + var tileExtent, tileGutter, tilesToDraw, w, h; + for (i = 0, ii = zs.length; i < ii; ++i) { + currentZ = zs[i]; + currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection); + currentResolution = tileGrid.getResolution(currentZ); + currentScale = currentResolution / tileResolution; + tileGutter = tilePixelRatio * tileSource.getGutter(projection); + tilesToDraw = tilesToDrawByZ[currentZ]; + for (var tileCoordKey in tilesToDraw) { + tile = tilesToDraw[tileCoordKey]; + tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent); + x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio; + y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio; + w = currentTilePixelSize[0] * currentScale; + h = currentTilePixelSize[1] * currentScale; + if (!opaque) { + context.clearRect(x, y, w, h); + } + this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter); + this.renderedTiles.push(tile); } } + + this.renderedRevision_ = sourceRevision; + this.renderedResolution_ = tileResolution; + this.renderedExtent_ = imageExtent; } + var scale = pixelRatio / tilePixelRatio * this.renderedResolution_ / viewResolution; + var transform = ol.transform.compose(this.imageTransform_, + pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, + scale, scale, + 0, + tilePixelRatio * (this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution_, + tilePixelRatio * (viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution_); + ol.transform.compose(this.coordinateToCanvasPixelTransform_, + pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], + pixelRatio / viewResolution, -pixelRatio / viewResolution, + 0, + -viewCenter[0], -viewCenter[1]); + + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, tileLayer.getPreload()); this.scheduleExpireCache(frameState, tileSource); this.updateLogos(frameState, tileSource); - return true; + return this.renderedTiles.length > 0; }; /** - * @param {ol.Pixel} pixel Pixel. + * @param {ol.Tile} tile Tile. + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @param {number} x Left of the tile. + * @param {number} y Top of the tile. + * @param {number} w Width of the tile. + * @param {number} h Height of the tile. + * @param {number} gutter Tile gutter. + */ +ol.renderer.canvas.TileLayer.prototype.drawTileImage = function(tile, frameState, layerState, x, y, w, h, gutter) { + var image = tile.getImage(); + if (image) { + this.context.drawImage(image, gutter, gutter, + image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h); + } +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. * @param {olx.FrameState} frameState FrameState. * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer * callback. @@ -187,188 +276,25 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function( * @return {T|undefined} Callback result. * @template S,T,U */ -ol.renderer.canvas.TileLayer.prototype.forEachLayerAtPixel = function( - pixel, frameState, callback, thisArg) { - var canvas = this.context.canvas; - var size = frameState.size; - var pixelRatio = frameState.pixelRatio; - canvas.width = size[0] * pixelRatio; - canvas.height = size[1] * pixelRatio; - this.composeFrame(frameState, this.getLayer().getLayerState(), this.context); - - var imageData = this.context.getImageData( - pixel[0], pixel[1], 1, 1).data; +ol.renderer.canvas.TileLayer.prototype.forEachLayerAtCoordinate = function( + coordinate, frameState, callback, thisArg) { + var canvasPixel = ol.transform.apply(this.coordinateToCanvasPixelTransform_, coordinate); + var imageData = this.context.getImageData(canvasPixel[0], canvasPixel[1], 1, 1).data; if (imageData[3] > 0) { - return callback.call(thisArg, this.getLayer(), imageData); + return callback.call(thisArg, this.getLayer(), imageData); } else { return undefined; } + }; /** - * @param {CanvasRenderingContext2D} context Context. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @protected + * @inheritDoc */ -ol.renderer.canvas.TileLayer.prototype.renderTileImages = function(context, frameState, layerState) { - var tilesToDraw = this.renderedTiles; - if (tilesToDraw.length === 0) { - return; - } - - var pixelRatio = frameState.pixelRatio; - var viewState = frameState.viewState; - var center = viewState.center; - var projection = viewState.projection; - var resolution = viewState.resolution; - var rotation = viewState.rotation; - 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 layer = this.getLayer(); - var source = /** @type {ol.source.Tile} */ (layer.getSource()); - var tileGutter = source.getTilePixelRatio(pixelRatio) * source.getGutter(projection); - var tileGrid = source.getTileGridForProjection(projection); - - var hasRenderListeners = layer.hasListener(ol.render.Event.Type.RENDER); - var renderContext = context; - var drawScale = 1; - var drawOffsetX, drawOffsetY, drawSize; - if (rotation || hasRenderListeners) { - renderContext = this.context; - var renderCanvas = renderContext.canvas; - drawScale = source.getTilePixelRatio(pixelRatio) / 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)); - } - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - var alpha = renderContext.globalAlpha; - renderContext.globalAlpha = layerState.opacity; - - var pixelExtents; - var opaque = source.getOpaque(projection) && layerState.opacity == 1; - if (!opaque) { - tilesToDraw.reverse(); - pixelExtents = []; - } - - var extent = layerState.extent; - var clipped = extent !== undefined; - if (clipped) { - var topLeft = ol.extent.getTopLeft(/** @type {ol.Extent} */ (extent)); - var topRight = ol.extent.getTopRight(/** @type {ol.Extent} */ (extent)); - var bottomRight = ol.extent.getBottomRight(/** @type {ol.Extent} */ (extent)); - var bottomLeft = ol.extent.getBottomLeft(/** @type {ol.Extent} */ (extent)); - - ol.transform.apply(frameState.coordinateToPixelTransform, topLeft); - ol.transform.apply(frameState.coordinateToPixelTransform, topRight); - ol.transform.apply(frameState.coordinateToPixelTransform, bottomRight); - ol.transform.apply(frameState.coordinateToPixelTransform, bottomLeft); - - var ox = drawOffsetX || 0; - var oy = drawOffsetY || 0; - renderContext.save(); - var cx = (renderContext.canvas.width) / 2; - var cy = (renderContext.canvas.height) / 2; - ol.render.canvas.rotateAtOffset(renderContext, -rotation, cx, cy); - renderContext.beginPath(); - renderContext.moveTo(drawScale * (topLeft[0] * pixelRatio + ox), - drawScale * (topLeft[1] * pixelRatio + oy)); - renderContext.lineTo(drawScale * (topRight[0] * pixelRatio + ox), - drawScale * (topRight[1] * pixelRatio + oy)); - renderContext.lineTo(drawScale * (bottomRight[0] * pixelRatio + ox), - drawScale * (bottomRight[1] * pixelRatio + oy)); - renderContext.lineTo(drawScale * (bottomLeft[0] * pixelRatio + ox), - drawScale * (bottomLeft[1] * pixelRatio + oy)); - renderContext.clip(); - ol.render.canvas.rotateAtOffset(renderContext, rotation, cx, cy); - } - - for (var i = 0, ii = tilesToDraw.length; i < ii; ++i) { - var tile = tilesToDraw[i]; - var tileCoord = tile.getTileCoord(); - var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); - var currentZ = tileCoord[0]; - // Calculate all insert points by tile widths from a common origin to avoid - // gaps caused by rounding - var origin = ol.extent.getBottomLeft(tileGrid.getTileCoordExtent( - tileGrid.getTileCoordForCoordAndZ(center, currentZ, this.tmpTileCoord_))); - var w = Math.round(ol.extent.getWidth(tileExtent) * pixelScale); - var h = Math.round(ol.extent.getHeight(tileExtent) * pixelScale); - var left = Math.round((tileExtent[0] - origin[0]) * pixelScale / w) * w + - offsetX + Math.round((origin[0] - center[0]) * pixelScale); - var top = Math.round((origin[1] - tileExtent[3]) * pixelScale / h) * h + - offsetY + Math.round((center[1] - origin[1]) * pixelScale); - if (!opaque) { - var pixelExtent = [left, top, left + w, top + h]; - // Create a clip mask for regions in this low resolution tile that are - // already filled by a higher resolution tile - renderContext.save(); - for (var j = 0, jj = pixelExtents.length; j < jj; ++j) { - var clipExtent = pixelExtents[j]; - if (ol.extent.intersects(pixelExtent, clipExtent)) { - renderContext.beginPath(); - // counter-clockwise (outer ring) for current tile - renderContext.moveTo(pixelExtent[0], pixelExtent[1]); - renderContext.lineTo(pixelExtent[0], pixelExtent[3]); - renderContext.lineTo(pixelExtent[2], pixelExtent[3]); - renderContext.lineTo(pixelExtent[2], pixelExtent[1]); - // clockwise (inner ring) for higher resolution tile - renderContext.moveTo(clipExtent[0], clipExtent[1]); - renderContext.lineTo(clipExtent[2], clipExtent[1]); - renderContext.lineTo(clipExtent[2], clipExtent[3]); - renderContext.lineTo(clipExtent[0], clipExtent[3]); - renderContext.closePath(); - renderContext.clip(); - } - } - pixelExtents.push(pixelExtent); - } - var tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection); - renderContext.drawImage(tile.getImage(), tileGutter, tileGutter, - tilePixelSize[0], tilePixelSize[1], left, top, w, h); - if (!opaque) { - renderContext.restore(); - } - } - - if (clipped) { - renderContext.restore(); - } - - if (hasRenderListeners) { - var dX = drawOffsetX - offsetX / drawScale + offsetX; - var dY = drawOffsetY - offsetY / drawScale + offsetY; - var imageTransform = ol.transform.compose(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; +ol.renderer.canvas.TileLayer.prototype.getImage = function() { + return this.context.canvas; }; @@ -377,3 +303,11 @@ ol.renderer.canvas.TileLayer.prototype.renderTileImages = function(context, fram * @return {ol.layer.Tile|ol.layer.VectorTile} */ ol.renderer.canvas.TileLayer.prototype.getLayer; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.TileLayer.prototype.getImageTransform = function() { + return this.imageTransform_; +}; diff --git a/src/ol/renderer/canvas/vectorlayer.js b/src/ol/renderer/canvas/vectorlayer.js index 8addea4e39..cd375e0f94 100644 --- a/src/ol/renderer/canvas/vectorlayer.js +++ b/src/ol/renderer/canvas/vectorlayer.js @@ -83,7 +83,7 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, lay var transform = this.getTransform(frameState, 0); - this.dispatchPreComposeEvent(context, frameState, transform); + this.preCompose(context, frameState, transform); // clipped rendering if layer extent is set var clipExtent = layerState.extent; @@ -169,7 +169,7 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, lay if (clipped) { context.restore(); } - this.dispatchPostComposeEvent(context, frameState, transform); + this.postCompose(context, frameState, layerState, transform); }; diff --git a/src/ol/renderer/canvas/vectortilelayer.js b/src/ol/renderer/canvas/vectortilelayer.js index 1e08c8e00c..ee4e14a14c 100644 --- a/src/ol/renderer/canvas/vectortilelayer.js +++ b/src/ol/renderer/canvas/vectortilelayer.js @@ -1,12 +1,10 @@ goog.provide('ol.renderer.canvas.VectorTileLayer'); goog.require('ol'); -goog.require('ol.array'); goog.require('ol.extent'); goog.require('ol.proj'); goog.require('ol.proj.Units'); goog.require('ol.layer.VectorTile'); -goog.require('ol.render.Event'); goog.require('ol.render.ReplayType'); goog.require('ol.render.canvas'); goog.require('ol.render.canvas.ReplayGroup'); @@ -66,126 +64,12 @@ ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS = { }; -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function( - frameState, layerState, context) { - var transform = this.getTransform(frameState, 0); - this.dispatchPreComposeEvent(context, frameState, transform); - - // clipped rendering if layer extent is set - var extent = layerState.extent; - var clipped = extent !== undefined; - if (clipped) { - this.clip(context, frameState, /** @type {ol.Extent} */ (extent)); - } - - var renderMode = this.getLayer().getRenderMode(); - if (renderMode !== ol.layer.VectorTile.RenderType.VECTOR) { - this.renderTileImages(context, frameState, layerState); - } - if (renderMode !== ol.layer.VectorTile.RenderType.IMAGE) { - this.renderTileReplays_(context, frameState, layerState); - } - - if (clipped) { - context.restore(); - } - - this.dispatchPostComposeEvent(context, frameState, transform); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @private - */ -ol.renderer.canvas.VectorTileLayer.prototype.renderTileReplays_ = function( - context, frameState, layerState) { - - var layer = this.getLayer(); - var replays = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[layer.getRenderMode()]; - var pixelRatio = frameState.pixelRatio; - var skippedFeatureUids = layerState.managed ? - frameState.skippedFeatureUids : {}; - var viewState = frameState.viewState; - var center = viewState.center; - var resolution = viewState.resolution; - var rotation = viewState.rotation; - var size = frameState.size; - var pixelScale = pixelRatio / resolution; - var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); - var tilePixelRatio = source.getTilePixelRatio(); - - var transform = this.getTransform(frameState, 0); - - var replayContext; - if (layer.hasListener(ol.render.Event.Type.RENDER)) { - // resize and clear - this.context.canvas.width = context.canvas.width; - this.context.canvas.height = context.canvas.height; - replayContext = this.context; - } else { - replayContext = context; - } - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - var alpha = replayContext.globalAlpha; - replayContext.globalAlpha = layerState.opacity; - - /** @type {Array.} */ - var tilesToDraw = this.renderedTiles; - - var tileGrid = source.getTileGrid(); - - var currentZ, i, ii, offsetX, offsetY, origin, pixelSpace, replayState; - var tile, tileExtent, tilePixelResolution, tileResolution, tileTransform; - for (i = 0, ii = tilesToDraw.length; i < ii; ++i) { - tile = tilesToDraw[i]; - replayState = tile.getReplayState(); - tileExtent = tileGrid.getTileCoordExtent( - tile.getTileCoord(), this.tmpExtent); - currentZ = tile.getTileCoord()[0]; - pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS; - tileResolution = tileGrid.getResolution(currentZ); - tilePixelResolution = tileResolution / tilePixelRatio; - offsetX = Math.round(pixelRatio * size[0] / 2); - offsetY = Math.round(pixelRatio * size[1] / 2); - - if (pixelSpace) { - origin = ol.extent.getTopLeft(tileExtent); - tileTransform = ol.transform.reset(this.tmpTransform_); - tileTransform = ol.transform.compose(this.tmpTransform_, - offsetX, offsetY, - pixelScale * tilePixelResolution, pixelScale * tilePixelResolution, - rotation, - (origin[0] - center[0]) / tilePixelResolution, (center[1] - origin[1]) / tilePixelResolution); - } else { - tileTransform = transform; - } - ol.render.canvas.rotateAtOffset(replayContext, -rotation, offsetX, offsetY); - replayState.replayGroup.replay(replayContext, pixelRatio, - tileTransform, rotation, skippedFeatureUids, replays); - ol.render.canvas.rotateAtOffset(replayContext, rotation, offsetX, offsetY); - } - - if (replayContext != context) { - this.dispatchRenderEvent(replayContext, frameState, transform); - context.drawImage(replayContext.canvas, 0, 0); - } - replayContext.globalAlpha = alpha; -}; - - /** * @param {ol.VectorTile} tile Tile. * @param {olx.FrameState} frameState Frame state. + * @private */ -ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile, +ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function(tile, frameState) { var layer = this.getLayer(); var pixelRatio = frameState.pixelRatio; @@ -204,12 +88,11 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile, var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var tileGrid = source.getTileGrid(); - var tileCoord = tile.getTileCoord(); + var tileCoord = tile.tileCoord; var tileProjection = tile.getProjection(); - var pixelSpace = tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS; var resolution = tileGrid.getResolution(tileCoord[0]); var extent, reproject, tileResolution; - if (pixelSpace) { + if (tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS) { var tilePixelRatio = tileResolution = source.getTilePixelRatio(); var tileSize = ol.size.toSize(tileGrid.getTileSize(tileCoord[0])); extent = [0, 0, tileSize[0] * tilePixelRatio, tileSize[1] * tilePixelRatio]; @@ -275,6 +158,21 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile, }; +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorTileLayer.prototype.drawTileImage = function( + tile, frameState, layerState, x, y, w, h, gutter) { + var vectorTile = /** @type {ol.VectorTile} */ (tile); + this.createReplayGroup_(vectorTile, frameState); + var layer = this.getLayer(); + if (layer.getRenderMode() != ol.layer.VectorTile.RenderType.VECTOR) { + this.renderTileImage_(vectorTile, frameState, layerState); + } + ol.renderer.canvas.TileLayer.prototype.drawTileImage.apply(this, arguments); +}; + + /** * @inheritDoc */ @@ -295,9 +193,8 @@ ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = functi var tile, tileCoord, tileExtent, tilePixelRatio, tileResolution; for (i = 0, ii = replayables.length; i < ii; ++i) { tile = replayables[i]; - tileCoord = tile.getTileCoord(); - tileExtent = source.getTileGrid().getTileCoordExtent(tileCoord, - this.tmpExtent); + tileCoord = tile.tileCoord; + tileExtent = source.getTileGrid().getTileCoordExtent(tileCoord, this.tmpExtent); if (!ol.extent.containsCoordinate(tileExtent, coordinate)) { continue; } @@ -332,6 +229,41 @@ ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = functi }; +/** + * @param {ol.Tile} tile Tile. + * @param {olx.FrameState} frameState Frame state. + * @return {ol.Transform} transform Transform. + * @private + */ +ol.renderer.canvas.VectorTileLayer.prototype.getReplayTransform_ = function(tile, frameState) { + if (tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) { + var layer = this.getLayer(); + var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); + var tileGrid = source.getTileGrid(); + var tileCoord = tile.tileCoord; + var tileResolution = + tileGrid.getResolution(tileCoord[0]) / source.getTilePixelRatio(); + var viewState = frameState.viewState; + var pixelRatio = frameState.pixelRatio; + var renderResolution = viewState.resolution / pixelRatio; + var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); + var center = viewState.center; + var origin = ol.extent.getTopLeft(tileExtent); + var size = frameState.size; + var offsetX = Math.round(pixelRatio * size[0] / 2); + var offsetY = Math.round(pixelRatio * size[1] / 2); + return ol.transform.compose(this.tmpTransform_, + offsetX, offsetY, + tileResolution / renderResolution, tileResolution / renderResolution, + viewState.rotation, + (origin[0] - center[0]) / tileResolution, + (center[1] - origin[1]) / tileResolution); + } else { + return this.getTransform(frameState, 0); + } +}; + + /** * Handle changes in image style state. * @param {ol.events.Event} event Image style change event. @@ -345,17 +277,53 @@ ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ = function( /** * @inheritDoc */ -ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, layerState) { - var prepared = ol.renderer.canvas.TileLayer.prototype.prepareFrame.call(this, frameState, layerState); - if (prepared) { - var skippedFeatures = Object.keys(frameState.skippedFeatureUids_ || {}); - for (var i = 0, ii = this.renderedTiles.length; i < ii; ++i) { - var tile = /** @type {ol.VectorTile} */ (this.renderedTiles[i]); - this.createReplayGroup(tile, frameState); - this.renderTileImage_(tile, frameState, layerState, skippedFeatures); +ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, frameState, layerState) { + var renderMode = this.getLayer().getRenderMode(); + var replays = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode]; + if (replays) { + var pixelRatio = frameState.pixelRatio; + var rotation = frameState.viewState.rotation; + var size = frameState.size; + var offsetX = Math.round(pixelRatio * size[0] / 2); + var offsetY = Math.round(pixelRatio * size[1] / 2); + var tiles = this.renderedTiles; + var clips = []; + var zs = []; + for (var i = tiles.length - 1; i >= 0; --i) { + var tile = /** @type {ol.VectorTile} */ (tiles[i]); + // Create a clip mask for regions in this low resolution tile that are + // already filled by a higher resolution tile + var transform = this.getReplayTransform_(tile, frameState); + var currentClip = tile.getReplayState().replayGroup.getClipCoords(transform); + var currentZ = tile.tileCoord[0]; + context.save(); + context.globalAlpha = layerState.opacity; + ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY); + for (var j = 0, jj = clips.length; j < jj; ++j) { + var clip = clips[j]; + if (currentZ < zs[j]) { + context.beginPath(); + // counter-clockwise (outer ring) for current tile + context.moveTo(currentClip[0], currentClip[1]); + context.lineTo(currentClip[2], currentClip[3]); + context.lineTo(currentClip[4], currentClip[5]); + context.lineTo(currentClip[6], currentClip[7]); + // clockwise (inner ring) for higher resolution tile + context.moveTo(clip[6], clip[7]); + context.lineTo(clip[4], clip[5]); + context.lineTo(clip[2], clip[3]); + context.lineTo(clip[0], clip[1]); + context.clip(); + } + } + var replayGroup = tile.getReplayState().replayGroup; + replayGroup.replay(context, pixelRatio, transform, rotation, {}, replays); + context.restore(); + clips.push(currentClip); + zs.push(currentZ); } } - return prepared; + ol.renderer.canvas.TileLayer.prototype.postCompose.apply(this, arguments); }; @@ -391,57 +359,38 @@ ol.renderer.canvas.VectorTileLayer.prototype.renderFeature = function(feature, s * @param {ol.VectorTile} tile Tile. * @param {olx.FrameState} frameState Frame state. * @param {ol.LayerState} layerState Layer state. - * @param {Array.} skippedFeatures Skipped features. * @private */ ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function( - tile, frameState, layerState, skippedFeatures) { + tile, frameState, layerState) { var layer = this.getLayer(); - var replays = ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS[layer.getRenderMode()]; - if (!replays) { - // do not create an image in 'vector' mode - return; - } - var pixelRatio = frameState.pixelRatio; var replayState = tile.getReplayState(); var revision = layer.getRevision(); - if (!ol.array.equals(replayState.skippedFeatures, skippedFeatures) || - replayState.renderedTileRevision !== revision) { - replayState.skippedFeatures = skippedFeatures; + var replays = ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS[layer.getRenderMode()]; + if (replays && replayState.renderedTileRevision !== revision) { replayState.renderedTileRevision = revision; - var tileContext = tile.getContext(); + var tileCoord = tile.tileCoord; + var z = tile.tileCoord[0]; + var pixelRatio = frameState.pixelRatio; var source = layer.getSource(); var tileGrid = source.getTileGrid(); - var currentZ = tile.getTileCoord()[0]; - var resolution = tileGrid.getResolution(currentZ); - var tileSize = ol.size.toSize(tileGrid.getTileSize(currentZ)); - var tileResolution = tileGrid.getResolution(currentZ); - var resolutionRatio = tileResolution / resolution; - var width = tileSize[0] * pixelRatio * resolutionRatio; - var height = tileSize[1] * pixelRatio * resolutionRatio; - tileContext.canvas.width = width / resolutionRatio + 0.5; - tileContext.canvas.height = height / resolutionRatio + 0.5; - tileContext.scale(1 / resolutionRatio, 1 / resolutionRatio); - tileContext.translate(width / 2, height / 2); - var pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS; - var pixelScale = pixelRatio / resolution; var tilePixelRatio = source.getTilePixelRatio(); - var tilePixelResolution = tileResolution / tilePixelRatio; - var tileExtent = tileGrid.getTileCoordExtent( - tile.getTileCoord(), this.tmpExtent); - var tileTransform = ol.transform.reset(this.tmpTransform_); - if (pixelSpace) { - ol.transform.scale(tileTransform, - pixelScale * tilePixelResolution, pixelScale * tilePixelResolution); - ol.transform.translate(tileTransform, - -tileSize[0] * tilePixelRatio / 2, -tileSize[1] * tilePixelRatio / 2); + var transform = ol.transform.reset(this.tmpTransform_); + if (tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) { + var renderPixelRatio = pixelRatio / tilePixelRatio; + ol.transform.scale(transform, renderPixelRatio, renderPixelRatio); } else { - var tileCenter = ol.extent.getCenter(tileExtent); - ol.transform.scale(tileTransform, pixelScale, -pixelScale); - ol.transform.translate(tileTransform, -tileCenter[0], -tileCenter[1]); + var resolution = tileGrid.getResolution(z); + var pixelScale = pixelRatio / resolution; + var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); + ol.transform.scale(transform, pixelScale, -pixelScale); + ol.transform.translate(transform, -tileExtent[0], -tileExtent[3]); } - replayState.replayGroup.replay(tileContext, pixelRatio, - tileTransform, 0, frameState.skippedFeatureUids || {}, replays); + var context = tile.getContext(); + var size = source.getTilePixelSize(z, pixelRatio, frameState.viewState.projection); + context.canvas.width = size[0]; + context.canvas.height = size[1]; + replayState.replayGroup.replay(context, pixelRatio, transform, 0, {}, replays); } }; diff --git a/src/ol/renderer/layer.js b/src/ol/renderer/layer.js index f6c603b8a4..2e96ea86d3 100644 --- a/src/ol/renderer/layer.js +++ b/src/ol/renderer/layer.js @@ -9,7 +9,6 @@ goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.functions'); goog.require('ol.source.State'); -goog.require('ol.transform'); /** @@ -45,29 +44,6 @@ ol.inherits(ol.renderer.Layer, ol.Observable); ol.renderer.Layer.prototype.forEachFeatureAtCoordinate = ol.nullFunction; -/** - * @param {ol.Pixel} pixel Pixel. - * @param {olx.FrameState} frameState Frame state. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T - */ -ol.renderer.Layer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { - var coordinate = ol.transform.apply( - frameState.pixelToCoordinateTransform, pixel.slice()); - - var hasFeature = this.forEachFeatureAtCoordinate( - coordinate, frameState, ol.functions.TRUE, this); - - if (hasFeature) { - return callback.call(thisArg, this.layer_, null); - } else { - return undefined; - } -}; - - /** * @param {ol.Coordinate} coordinate Coordinate. * @param {olx.FrameState} frameState Frame state. diff --git a/src/ol/renderer/map.js b/src/ol/renderer/map.js index a8e88f804e..e9f0d8bae0 100644 --- a/src/ol/renderer/map.js +++ b/src/ol/renderer/map.js @@ -167,6 +167,7 @@ ol.renderer.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, fram /** + * @abstract * @param {ol.Pixel} pixel Pixel. * @param {olx.FrameState} frameState FrameState. * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer @@ -181,29 +182,7 @@ ol.renderer.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, fram * @template S,T,U */ ol.renderer.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg, - layerFilter, thisArg2) { - var result; - var viewState = frameState.viewState; - var viewResolution = viewState.resolution; - - var layerStates = frameState.layerStatesArray; - var numLayers = layerStates.length; - var i; - for (i = numLayers - 1; i >= 0; --i) { - var layerState = layerStates[i]; - var layer = layerState.layer; - if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && - layerFilter.call(thisArg2, layer)) { - var layerRenderer = this.getLayerRenderer(layer); - result = layerRenderer.forEachLayerAtPixel( - pixel, frameState, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; -}; + layerFilter, thisArg2) {}; /** diff --git a/src/ol/renderer/webgl/imagelayer.js b/src/ol/renderer/webgl/imagelayer.js index 8e7507ef9d..8df0c036df 100644 --- a/src/ol/renderer/webgl/imagelayer.js +++ b/src/ol/renderer/webgl/imagelayer.js @@ -219,13 +219,7 @@ ol.renderer.webgl.ImageLayer.prototype.hasFeatureAtCoordinate = function(coordin /** - * @param {ol.Pixel} pixel Pixel. - * @param {olx.FrameState} frameState FrameState. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T,U + * @inheritDoc */ ol.renderer.webgl.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { if (!this.image_ || !this.image_.getImage()) { diff --git a/src/ol/renderer/webgl/layer.js b/src/ol/renderer/webgl/layer.js index e6f7da508d..1dd5ae61dd 100644 --- a/src/ol/renderer/webgl/layer.js +++ b/src/ol/renderer/webgl/layer.js @@ -251,3 +251,16 @@ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = function() { * @return {boolean} whether composeFrame should be called. */ ol.renderer.webgl.Layer.prototype.prepareFrame = function(frameState, layerState, context) {}; + + +/** + * @abstract + * @param {ol.Pixel} pixel Pixel. + * @param {olx.FrameState} frameState FrameState. + * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer + * callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ +ol.renderer.webgl.Layer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {}; diff --git a/src/ol/renderer/webgl/map.js b/src/ol/renderer/webgl/map.js index e055609ff6..5844610adf 100644 --- a/src/ol/renderer/webgl/map.js +++ b/src/ol/renderer/webgl/map.js @@ -584,7 +584,7 @@ ol.renderer.webgl.Map.prototype.forEachLayerAtPixel = function(pixel, frameState var layer = layerState.layer; if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) && layerFilter.call(thisArg, layer)) { - var layerRenderer = this.getLayerRenderer(layer); + var layerRenderer = /** @type {ol.renderer.webgl.Layer} */ (this.getLayerRenderer(layer)); result = layerRenderer.forEachLayerAtPixel( pixel, frameState, callback, thisArg); if (result) { diff --git a/src/ol/renderer/webgl/tilelayer.js b/src/ol/renderer/webgl/tilelayer.js index b7e958a1b2..824f7d3303 100644 --- a/src/ol/renderer/webgl/tilelayer.js +++ b/src/ol/renderer/webgl/tilelayer.js @@ -358,13 +358,7 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = function(frameState, layerS /** - * @param {ol.Pixel} pixel Pixel. - * @param {olx.FrameState} frameState FrameState. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T,U + * @inheritDoc */ ol.renderer.webgl.TileLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { if (!this.framebuffer) { diff --git a/src/ol/renderer/webgl/vectorlayer.js b/src/ol/renderer/webgl/vectorlayer.js index 494d5eb149..d94975f8a6 100644 --- a/src/ol/renderer/webgl/vectorlayer.js +++ b/src/ol/renderer/webgl/vectorlayer.js @@ -154,13 +154,7 @@ ol.renderer.webgl.VectorLayer.prototype.hasFeatureAtCoordinate = function(coordi /** - * @param {ol.Pixel} pixel Pixel. - * @param {olx.FrameState} frameState FrameState. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T,U + * @inheritDoc */ ol.renderer.webgl.VectorLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { var coordinate = ol.transform.apply( diff --git a/src/ol/typedefs.js b/src/ol/typedefs.js index 68782fbf3c..10b6cc70e8 100644 --- a/src/ol/typedefs.js +++ b/src/ol/typedefs.js @@ -633,8 +633,7 @@ ol.TilePriorityFunction; * renderedRenderOrder: (null|function(ol.Feature, ol.Feature):number), * renderedTileRevision: number, * renderedRevision: number, - * replayGroup: ol.render.ReplayGroup, - * skippedFeatures: Array.}} + * replayGroup: ol.render.ReplayGroup}} */ ol.TileReplayState; diff --git a/src/ol/vectortile.js b/src/ol/vectortile.js index 04efabc99a..66ce5df006 100644 --- a/src/ol/vectortile.js +++ b/src/ol/vectortile.js @@ -59,8 +59,7 @@ ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) { renderedRenderOrder: null, renderedRevision: -1, renderedTileRevision: -1, - replayGroup: null, - skippedFeatures: [] + replayGroup: null }; /** diff --git a/test/spec/ol/renderer/canvas/tilelayer.test.js b/test/spec/ol/renderer/canvas/tilelayer.test.js index c7d483f19f..1ed14bee54 100644 --- a/test/spec/ol/renderer/canvas/tilelayer.test.js +++ b/test/spec/ol/renderer/canvas/tilelayer.test.js @@ -32,15 +32,18 @@ describe('ol.renderer.canvas.TileLayer', function() { renderer.renderedTiles = []; var frameState = { viewState: { - center: [2, 3], + center: [10, 5], projection: ol.proj.get('EPSG:3857'), resolution: 1, rotation: Math.PI }, - size: [10, 10], + extent: [0, 0, 20, 10], + size: [20, 10], pixelRatio: 2, coordinateToPixelTransform: ol.transform.create(), - pixelToCoordinateTransform: ol.transform.create() + pixelToCoordinateTransform: ol.transform.create(), + usedTiles: {}, + wantedTiles: {} }; renderer.getImageTransform = function() { return ol.transform.create(); @@ -62,8 +65,9 @@ describe('ol.renderer.canvas.TileLayer', function() { return img; } }]; + renderer.prepareFrame(frameState, layerState); renderer.composeFrame(frameState, layerState, context); - expect(context.drawImage.firstCall.args[0].width).to.be(112); + expect(context.drawImage.firstCall.args[0].width).to.be(17); }); }); diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 4f54f8c8e3..7979fa3d19 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -89,7 +89,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { it('does not render images for pure vector rendering', function() { layer.renderMode_ = 'vector'; var spy = sinon.spy(ol.renderer.canvas.VectorTileLayer.prototype, - 'renderTileImages'); + 'renderTileImage_'); map.renderSync(); expect(spy.callCount).to.be(0); spy.restore(); @@ -98,7 +98,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { it('does not render replays for pure image rendering', function() { layer.renderMode_ = 'image'; var spy = sinon.spy(ol.renderer.canvas.VectorTileLayer.prototype, - 'renderTileReplays_'); + 'getReplayTransform_'); map.renderSync(); expect(spy.callCount).to.be(0); spy.restore(); @@ -106,9 +106,9 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { it('renders both replays and images for hybrid rendering', function() { var spy1 = sinon.spy(ol.renderer.canvas.VectorTileLayer.prototype, - 'renderTileReplays_'); + 'getReplayTransform_'); var spy2 = sinon.spy(ol.renderer.canvas.VectorTileLayer.prototype, - 'renderTileImages'); + 'renderTileImage_'); map.renderSync(); expect(spy1.callCount).to.be(1); expect(spy2.callCount).to.be(1); diff --git a/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent-rotate-hidpi.png b/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent-rotate-hidpi.png index 164e80b392..d0b115cca4 100644 Binary files a/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent-rotate-hidpi.png and b/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent-rotate-hidpi.png differ