Avoid clipping when rendering to tiles that don't exceed the clip extent

This commit is contained in:
ahocevar
2019-01-23 23:04:53 +01:00
parent f74e56c939
commit 09a1c1ef1b
4 changed files with 51 additions and 40 deletions

View File

@@ -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}

View File

@@ -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();
}
}
}

View File

@@ -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) {