From 461256767e567d387476b243c63dd07e44a5a61e Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 20 Mar 2019 22:25:25 +0100 Subject: [PATCH 1/4] Make pattern fills work on a scaled canvas --- src/ol/render/canvas/Executor.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ol/render/canvas/Executor.js b/src/ol/render/canvas/Executor.js index af7e326ff0..162eac03a9 100644 --- a/src/ol/render/canvas/Executor.js +++ b/src/ol/render/canvas/Executor.js @@ -9,7 +9,7 @@ import {lineStringLength} from '../../geom/flat/length.js'; import {drawTextOnPath} from '../../geom/flat/textpath.js'; import {transform2D} from '../../geom/flat/transform.js'; import {isEmpty} from '../../obj.js'; -import {drawImage, resetTransform, defaultPadding, defaultTextBaseline} from '../canvas.js'; +import {drawImage, defaultPadding, defaultTextBaseline} from '../canvas.js'; import CanvasInstruction from './Instruction.js'; import {TEXT_ALIGN} from './TextBuilder.js'; import { @@ -382,12 +382,13 @@ class Executor extends Disposable { if (this.alignFill_) { const origin = applyTransform(this.renderedTransform_, [0, 0]); const repeatSize = 512 * this.pixelRatio; + context.save(); context.translate(origin[0] % repeatSize, origin[1] % repeatSize); context.rotate(this.viewRotation_); } context.fill(); if (this.alignFill_) { - context.setTransform.apply(context, resetTransform); + context.restore(); } } From a25ca03040a897adf3bd1aea1df6890226aefa53 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 20 Mar 2019 22:26:56 +0100 Subject: [PATCH 2/4] Do not use tile.load() for getting source tiles --- src/ol/VectorRenderTile.js | 3 +-- src/ol/renderer/canvas/VectorTileLayer.js | 2 +- test/spec/ol/source/vectortile.test.js | 2 +- .../ol/{vectorimagetile.test.js => vectorrendertile.test.js} | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) rename test/spec/ol/{vectorimagetile.test.js => vectorrendertile.test.js} (96%) diff --git a/src/ol/VectorRenderTile.js b/src/ol/VectorRenderTile.js index 7c427316f4..47b3f47a1a 100644 --- a/src/ol/VectorRenderTile.js +++ b/src/ol/VectorRenderTile.js @@ -167,10 +167,9 @@ class VectorRenderTile extends Tile { /** * @inheritDoc - * @return {Array} Source tiles for this tile. */ load() { - return this.getSourceTiles_(this); + this.getSourceTiles_(this); } } diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index be8e05bc76..a0ab43bc57 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -243,7 +243,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const resolution = tileGrid.getResolution(zoom); const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); - const sourceTiles = tile.load(); + const sourceTiles = source.getSourceTiles(pixelRatio, projection, tile); const layerUid = getUid(layer); const executorGroups = tile.executorGroups[layerUid]; if (executorGroups) { diff --git a/test/spec/ol/source/vectortile.test.js b/test/spec/ol/source/vectortile.test.js index e849ff1d06..9a8c57a1c8 100644 --- a/test/spec/ol/source/vectortile.test.js +++ b/test/spec/ol/source/vectortile.test.js @@ -59,7 +59,7 @@ describe('ol.source.VectorTile', function() { tile.load(); const key = listen(tile, 'change', function(e) { if (tile.getState() === TileState.LOADED) { - const sourceTile = tile.load()[0]; + const sourceTile = source.getSourceTiles(1, source.getProjection(), tile)[0]; expect(sourceTile.getFeatures().length).to.be.greaterThan(0); unlistenByKey(key); done(); diff --git a/test/spec/ol/vectorimagetile.test.js b/test/spec/ol/vectorrendertile.test.js similarity index 96% rename from test/spec/ol/vectorimagetile.test.js rename to test/spec/ol/vectorrendertile.test.js index 56621a1e8f..6f49549482 100644 --- a/test/spec/ol/vectorimagetile.test.js +++ b/test/spec/ol/vectorrendertile.test.js @@ -85,7 +85,7 @@ describe('ol.VectorRenderTile', function() { const key = listen(tile, EventType.CHANGE, function() { if (tile.getState() === TileState.LOADED) { unlistenByKey(key); - const sourceTiles = tile.load(); + const sourceTiles = source.getSourceTiles(1, source.getProjection(), tile); expect(sourceTiles.length).to.be(1); expect(sourceTiles[0].tileCoord).to.eql([0, 16, 9]); done(); @@ -126,7 +126,7 @@ describe('ol.VectorRenderTile', function() { listenOnce(tile, 'change', function() { expect(tile.getState()).to.be(TileState.LOADED); expect(tile.loadingSourceTiles).to.be(0); - const sourceTiles = tile.load(); + const sourceTiles = source.getSourceTiles(1, source.getProjection(), tile); expect(sourceTiles.length).to.be(4); for (let i = 0, ii = sourceTiles.length; i < ii; ++i) { expect(sourceTiles[i].consumers).to.be(1); From d2f60a70d172b43931f95ec137c59e5b7784690b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 20 Mar 2019 22:28:09 +0100 Subject: [PATCH 3/4] Recreate instructions for the view resolution --- src/ol/VectorRenderTile.js | 9 +++++ src/ol/renderer/canvas/TileLayer.js | 9 ++--- src/ol/renderer/canvas/VectorTileLayer.js | 40 ++++++++++++++++++----- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/ol/VectorRenderTile.js b/src/ol/VectorRenderTile.js index 47b3f47a1a..d682cdb425 100644 --- a/src/ol/VectorRenderTile.js +++ b/src/ol/VectorRenderTile.js @@ -12,8 +12,10 @@ import {createCanvasContext2D} from './dom.js'; * @property {boolean} dirty * @property {null|import("./render.js").OrderFunction} renderedRenderOrder * @property {number} renderedTileRevision + * @property {number} renderedResolution * @property {number} renderedRevision * @property {number} renderedZ + * @property {number} renderedTileResolution * @property {number} renderedTileZ */ @@ -64,6 +66,11 @@ class VectorRenderTile extends Tile { */ this.replayState_ = {}; + /** + * @type {number} + */ + this.wantedResolution; + /** * @type {!function(import("./VectorRenderTile.js").default):Array} */ @@ -156,7 +163,9 @@ class VectorRenderTile extends Tile { this.replayState_[key] = { dirty: false, renderedRenderOrder: null, + renderedResolution: NaN, renderedRevision: -1, + renderedTileResolution: NaN, renderedTileRevision: -1, renderedZ: -1, renderedTileZ: -1 diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index cddca15d24..03b29a026f 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -82,11 +82,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { * @param {number} z Tile coordinate z. * @param {number} x Tile coordinate x. * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {import("../../proj/Projection.js").default} projection Projection. + * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. * @return {!import("../../Tile.js").default} Tile. */ - getTile(z, x, y, pixelRatio, projection) { + getTile(z, x, y, frameState) { + const pixelRatio = frameState.pixelRatio; + const projection = frameState.viewState.projection; const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer()); const tileSource = tileLayer.getSource(); let tile = tileSource.getTile(z, x, y, pixelRatio, projection); @@ -186,7 +187,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { this.newTiles_ = false; for (let x = tileRange.minX; x <= tileRange.maxX; ++x) { for (let y = tileRange.minY; y <= tileRange.maxY; ++y) { - const tile = this.getTile(z, x, y, pixelRatio, projection); + const tile = this.getTile(z, x, y, frameState); if (this.isDrawableTile(tile)) { const uid = getUid(this); if (tile.getState() == TileState.LOADED) { diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index a0ab43bc57..60ed514aad 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -179,15 +179,25 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { /** * @inheritDoc */ - getTile(z, x, y, pixelRatio, projection) { - const tile = /** @type {import("../../VectorRenderTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection)); + getTile(z, x, y, frameState) { + const tile = /** @type {import("../../VectorRenderTile.js").default} */ (super.getTile(z, x, y, frameState)); + const pixelRatio = frameState.pixelRatio; + const viewState = frameState.viewState; + const resolution = viewState.resolution; + const projection = viewState.projection; if (tile.getState() < TileState.LOADED) { + tile.wantedResolution = resolution; const tileUid = getUid(tile); if (!(tileUid in this.tileListenerKeys_)) { const listenerKey = listen(tile, EventType.CHANGE, this.prepareTile.bind(this, tile, pixelRatio, projection)); this.tileListenerKeys_[tileUid] = listenerKey; } } else { + const viewHints = frameState.viewHints; + const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]); + if (hifi || !tile.wantedResolution) { + tile.wantedResolution = resolution; + } this.prepareTile(tile, pixelRatio, projection); } return tile; @@ -230,8 +240,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const revision = layer.getRevision(); const renderOrder = layer.getRenderOrder() || null; + const resolution = tile.wantedResolution; const builderState = tile.getReplayState(layer); - if (!builderState.dirty && builderState.renderedRevision == revision && + if (!builderState.dirty && builderState.renderedResolution === resolution && + builderState.renderedRevision == revision && builderState.renderedRenderOrder == renderOrder && builderState.renderedZ === tile.sourceZ) { return; } @@ -239,8 +251,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const source = layer.getSource(); const sourceTileGrid = source.getTileGrid(); const tileGrid = source.getTileGridForProjection(projection); - const zoom = tile.tileCoord[0]; - const resolution = tileGrid.getResolution(zoom); const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); const sourceTiles = source.getSourceTiles(pixelRatio, projection, tile); @@ -306,6 +316,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { builderState.renderedRevision = revision; builderState.renderedZ = tile.sourceZ; builderState.renderedRenderOrder = renderOrder; + builderState.renderedResolution = resolution; } /** @@ -539,7 +550,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { if (this.declutterTree_ && layer.getRenderMode() === VectorTileRenderType.IMAGE) { this.declutterTree_.clear(); } - this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection); + const viewState = frameState.viewState; + const tileGrid = layer.getSource().getTileGridForProjection(viewState.projection); + const tileResolution = tileGrid.getResolution(tile.tileCoord[0]); + const renderPixelRatio = frameState.pixelRatio / tile.wantedResolution * tileResolution; + this.renderTileImage_(tile, frameState.pixelRatio, renderPixelRatio, viewState.projection); } clear(this.renderTileImageQueue_); } @@ -582,16 +597,18 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const replayState = tile.getReplayState(layer); const revision = layer.getRevision(); const sourceZ = tile.sourceZ; - return replayState.renderedTileRevision !== revision || replayState.renderedTileZ !== sourceZ; + const resolution = tile.wantedResolution; + return replayState.renderedTileResolution !== resolution || replayState.renderedTileRevision !== revision || replayState.renderedTileZ !== sourceZ; } /** * @param {import("../../VectorRenderTile.js").default} tile Tile. * @param {number} pixelRatio Pixel ratio. + * @param {number} renderPixelRatio Render pixel ratio. * @param {import("../../proj/Projection.js").default} projection Projection. * @private */ - renderTileImage_(tile, pixelRatio, projection) { + renderTileImage_(tile, pixelRatio, renderPixelRatio, projection) { const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const replayState = tile.getReplayState(layer); const revision = layer.getRevision(); @@ -607,15 +624,20 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const size = source.getTilePixelSize(z, pixelRatio, projection); context.canvas.width = size[0]; context.canvas.height = size[1]; + const canvasTransform = resetTransform(this.tmpTransform_); + const renderScale = pixelRatio / renderPixelRatio; + scaleTransform(canvasTransform, renderScale, renderScale); + context.setTransform.apply(context, canvasTransform); const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); for (let i = 0, ii = executorGroups.length; i < ii; ++i) { const executorGroup = executorGroups[i]; - const pixelScale = pixelRatio / resolution; + const pixelScale = renderPixelRatio / resolution; const transform = resetTransform(this.tmpTransform_); scaleTransform(transform, pixelScale, -pixelScale); translateTransform(transform, -tileExtent[0], -tileExtent[3]); executorGroup.execute(context, transform, 0, {}, true, IMAGE_REPLAYS[layer.getRenderMode()]); } + replayState.renderedTileResolution = tile.wantedResolution; } /** From fefcc9f0caea9f4fd72a717c853fdcc50e9c9a38 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 18 Mar 2019 21:32:55 +0100 Subject: [PATCH 4/4] Return directly when all we do is render queued images --- src/ol/renderer/canvas/TileLayer.js | 9 ++++++++- src/ol/renderer/canvas/VectorTileLayer.js | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index 03b29a026f..a2d601b6e6 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -4,7 +4,7 @@ import {getUid} from '../../util.js'; import TileRange from '../../TileRange.js'; import TileState from '../../TileState.js'; -import {createEmpty, getIntersection, getTopLeft} from '../../extent.js'; +import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js'; import CanvasLayerRenderer from './Layer.js'; import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js'; @@ -21,6 +21,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { constructor(tileLayer) { super(tileLayer); + /** + * Rendered extent has changed since the previous `renderFrame()` call + * @type {boolean} + */ + this.extentChanged = true; + /** * @private * @type {import("../../extent.js").Extent} @@ -301,6 +307,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { this.renderedRevision = sourceRevision; this.renderedResolution = tileResolution; + this.extentChanged = !this.renderedExtent_ || !equals(this.renderedExtent_, canvasExtent); this.renderedExtent_ = canvasExtent; this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index 60ed514aad..beaa29dcaf 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -27,7 +27,7 @@ import { makeInverse } from '../../transform.js'; import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js'; -import {clear} from '../../obj.js'; +import {clear, isEmpty} from '../../obj.js'; /** @@ -432,6 +432,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { return this.container_; } + if (!isEmpty(this.renderTileImageQueue_) && !this.extentChanged) { + this.renderTileImages_(hifi, frameState); + return this.container_; + } + const context = this.overlayContext_; const declutterReplays = layer.getDeclutter() ? {} : null; const source = layer.getSource();