diff --git a/src/ol/render/canvas/Executor.js b/src/ol/render/canvas/Executor.js index 4cae87c8e2..af7e326ff0 100644 --- a/src/ol/render/canvas/Executor.js +++ b/src/ol/render/canvas/Executor.js @@ -55,27 +55,19 @@ const p4 = []; class Executor extends Disposable { /** - * @param {import("../../extent.js").Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. * @param {SerializableInstructions} instructions The serializable instructions */ - constructor(maxExtent, resolution, pixelRatio, overlaps, declutterTree, instructions) { + constructor(resolution, pixelRatio, overlaps, declutterTree, instructions) { super(); /** * @type {?} */ this.declutterTree = declutterTree; - /** - * @protected - * @const - * @type {import("../../extent.js").Extent} - */ - this.maxExtent = maxExtent; - /** * @protected * @type {boolean} diff --git a/src/ol/render/canvas/ExecutorGroup.js b/src/ol/render/canvas/ExecutorGroup.js index a50558eb3c..60c03c08df 100644 --- a/src/ol/render/canvas/ExecutorGroup.js +++ b/src/ol/render/canvas/ExecutorGroup.js @@ -28,7 +28,10 @@ const ORDER = [ class ExecutorGroup extends Disposable { /** - * @param {import("../../extent.js").Extent} maxExtent Max extent. + * @param {import("../../extent.js").Extent} maxExtent Max extent for clipping. When a + * `maxExtent` was set on the Buillder for this executor group, the same `maxExtent` + * should be set here, unless the target context does not exceet that extent (which + * can be the case when rendering to tiles). * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The executor group can have overlapping geometries. @@ -124,7 +127,7 @@ class ExecutorGroup extends Disposable { const instructionByZindex = allInstructions[zIndex]; for (const builderType in instructionByZindex) { const instructions = instructionByZindex[builderType]; - executors[builderType] = new Executor(this.maxExtent_, + executors[builderType] = new Executor( this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_, instructions); } } @@ -283,6 +286,9 @@ class ExecutorGroup extends Disposable { */ getClipCoords(transform) { const maxExtent = this.maxExtent_; + if (!maxExtent) { + return null; + } const minX = maxExtent[0]; const minY = maxExtent[1]; const maxX = maxExtent[2]; @@ -308,7 +314,7 @@ class ExecutorGroup extends Disposable { let executor = executors[builderType]; if (executor === undefined) { // FIXME: it should not be possible to ask for an executor that does not exist - executor = new Executor(this.maxExtent_, + executor = new Executor( this.resolution_, this.pixelRatio_, this.overlaps_, { instructions: [], hitDetectionInstructions: [], @@ -353,8 +359,10 @@ class ExecutorGroup extends Disposable { // setup clipping so that the parts of over-simplified geometries are not // visible outside the current extent when panning - context.save(); - this.clip(context, transform); + if (this.maxExtent_) { + context.save(); + this.clip(context, transform); + } const builderTypes = opt_builderTypes ? opt_builderTypes : ORDER; let i, ii, j, jj, replays, replay; @@ -380,7 +388,9 @@ class ExecutorGroup extends Disposable { } } - context.restore(); + if (this.maxExtent_) { + context.restore(); + } } } diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index 34fe99637f..e38fc9384d 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -313,7 +313,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { } } const executorGroupInstructions = builderGroup.finish(); - const renderingReplayGroup = new CanvasExecutorGroup(sharedExtent, resolution, + // no need to clip when the render tile is covered by a single source tile + const replayExtent = layer.getDeclutter() && sourceTiles.length === 1 ? + null : + sharedExtent; + const renderingReplayGroup = new CanvasExecutorGroup(replayExtent, resolution, pixelRatio, source.getOverlaps(), this.declutterTree_, executorGroupInstructions, layer.getRenderBuffer()); tile.executorGroups[layerUid].push(renderingReplayGroup); } @@ -462,7 +466,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const tiles = this.renderedTiles; const tileGrid = source.getTileGridForProjection(frameState.viewState.projection); const clips = []; - const zs = []; for (let i = tiles.length - 1; i >= 0; --i) { const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]); if (tile.getState() == TileState.ABORT) { @@ -481,32 +484,38 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { continue; } const currentZ = tile.tileCoord[0]; - const currentClip = executorGroup.getClipCoords(transform); - context.save(); + let zs, currentClip; + if (!declutterReplays) { + zs = []; + currentClip = executorGroup.getClipCoords(transform); + context.save(); - // Create a clip mask for regions in this low resolution tile that are - // already filled by a higher resolution tile - for (let j = 0, jj = clips.length; j < jj; ++j) { - const 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(); + // Create a clip mask for regions in this low resolution tile that are + // already filled by a higher resolution tile + for (let j = 0, jj = clips.length; j < jj; ++j) { + const 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(); + } } } executorGroup.execute(context, transform, rotation, {}, hifi, replayTypes, declutterReplays); - context.restore(); - clips.push(currentClip); - zs.push(currentZ); + if (!declutterReplays) { + context.restore(); + clips.push(currentClip); + zs.push(currentZ); + } } } if (declutterReplays) { diff --git a/test/spec/ol/render/canvas/textbuilder.test.js b/test/spec/ol/render/canvas/textbuilder.test.js index b1c82f2546..18f61732f3 100644 --- a/test/spec/ol/render/canvas/textbuilder.test.js +++ b/test/spec/ol/render/canvas/textbuilder.test.js @@ -28,7 +28,7 @@ function createContext() { function executeInstructions(builder, expectedDrawTextImageCalls, expectedBuilderImageCalls) { const transform = createTransform(); const context = createContext(); - const executor = new Executor([-180, -90, 180, 90], 0.02, 1, false, null, builder.finish()); + const executor = new Executor(0.02, 1, false, null, builder.finish()); sinon.spy(executor, 'drawTextImageWithPointPlacement_'); const replayImageStub = sinon.stub(executor, 'replayImage_'); executor.execute(context, transform);