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
+1 -9
View File
@@ -55,27 +55,19 @@ const p4 = [];
class Executor extends Disposable { class Executor extends Disposable {
/** /**
* @param {import("../../extent.js").Extent} maxExtent Maximum extent.
* @param {number} resolution Resolution. * @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio. * @param {number} pixelRatio Pixel ratio.
* @param {boolean} overlaps The replay can have overlapping geometries. * @param {boolean} overlaps The replay can have overlapping geometries.
* @param {?} declutterTree Declutter tree. * @param {?} declutterTree Declutter tree.
* @param {SerializableInstructions} instructions The serializable instructions * @param {SerializableInstructions} instructions The serializable instructions
*/ */
constructor(maxExtent, resolution, pixelRatio, overlaps, declutterTree, instructions) { constructor(resolution, pixelRatio, overlaps, declutterTree, instructions) {
super(); super();
/** /**
* @type {?} * @type {?}
*/ */
this.declutterTree = declutterTree; this.declutterTree = declutterTree;
/**
* @protected
* @const
* @type {import("../../extent.js").Extent}
*/
this.maxExtent = maxExtent;
/** /**
* @protected * @protected
* @type {boolean} * @type {boolean}
+16 -6
View File
@@ -28,7 +28,10 @@ const ORDER = [
class ExecutorGroup extends Disposable { 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} resolution Resolution.
* @param {number} pixelRatio Pixel ratio. * @param {number} pixelRatio Pixel ratio.
* @param {boolean} overlaps The executor group can have overlapping geometries. * @param {boolean} overlaps The executor group can have overlapping geometries.
@@ -124,7 +127,7 @@ class ExecutorGroup extends Disposable {
const instructionByZindex = allInstructions[zIndex]; const instructionByZindex = allInstructions[zIndex];
for (const builderType in instructionByZindex) { for (const builderType in instructionByZindex) {
const instructions = instructionByZindex[builderType]; const instructions = instructionByZindex[builderType];
executors[builderType] = new Executor(this.maxExtent_, executors[builderType] = new Executor(
this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_, instructions); this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_, instructions);
} }
} }
@@ -283,6 +286,9 @@ class ExecutorGroup extends Disposable {
*/ */
getClipCoords(transform) { getClipCoords(transform) {
const maxExtent = this.maxExtent_; const maxExtent = this.maxExtent_;
if (!maxExtent) {
return null;
}
const minX = maxExtent[0]; const minX = maxExtent[0];
const minY = maxExtent[1]; const minY = maxExtent[1];
const maxX = maxExtent[2]; const maxX = maxExtent[2];
@@ -308,7 +314,7 @@ class ExecutorGroup extends Disposable {
let executor = executors[builderType]; let executor = executors[builderType];
if (executor === undefined) { if (executor === undefined) {
// FIXME: it should not be possible to ask for an executor that does not exist // 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_, { this.resolution_, this.pixelRatio_, this.overlaps_, {
instructions: [], instructions: [],
hitDetectionInstructions: [], hitDetectionInstructions: [],
@@ -353,8 +359,10 @@ class ExecutorGroup extends Disposable {
// setup clipping so that the parts of over-simplified geometries are not // setup clipping so that the parts of over-simplified geometries are not
// visible outside the current extent when panning // visible outside the current extent when panning
context.save(); if (this.maxExtent_) {
this.clip(context, transform); context.save();
this.clip(context, transform);
}
const builderTypes = opt_builderTypes ? opt_builderTypes : ORDER; const builderTypes = opt_builderTypes ? opt_builderTypes : ORDER;
let i, ii, j, jj, replays, replay; let i, ii, j, jj, replays, replay;
@@ -380,7 +388,9 @@ class ExecutorGroup extends Disposable {
} }
} }
context.restore(); if (this.maxExtent_) {
context.restore();
}
} }
} }
+33 -24
View File
@@ -313,7 +313,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
} }
} }
const executorGroupInstructions = builderGroup.finish(); 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()); pixelRatio, source.getOverlaps(), this.declutterTree_, executorGroupInstructions, layer.getRenderBuffer());
tile.executorGroups[layerUid].push(renderingReplayGroup); tile.executorGroups[layerUid].push(renderingReplayGroup);
} }
@@ -462,7 +466,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const tiles = this.renderedTiles; const tiles = this.renderedTiles;
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection); const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
const clips = []; const clips = [];
const zs = [];
for (let i = tiles.length - 1; i >= 0; --i) { for (let i = tiles.length - 1; i >= 0; --i) {
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]); const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]);
if (tile.getState() == TileState.ABORT) { if (tile.getState() == TileState.ABORT) {
@@ -481,32 +484,38 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
continue; continue;
} }
const currentZ = tile.tileCoord[0]; const currentZ = tile.tileCoord[0];
const currentClip = executorGroup.getClipCoords(transform); let zs, currentClip;
context.save(); if (!declutterReplays) {
zs = [];
currentClip = executorGroup.getClipCoords(transform);
context.save();
// Create a clip mask for regions in this low resolution tile that are // Create a clip mask for regions in this low resolution tile that are
// already filled by a higher resolution tile // already filled by a higher resolution tile
for (let j = 0, jj = clips.length; j < jj; ++j) { for (let j = 0, jj = clips.length; j < jj; ++j) {
const clip = clips[j]; const clip = clips[j];
if (currentZ < zs[j]) { if (currentZ < zs[j]) {
context.beginPath(); context.beginPath();
// counter-clockwise (outer ring) for current tile // counter-clockwise (outer ring) for current tile
context.moveTo(currentClip[0], currentClip[1]); context.moveTo(currentClip[0], currentClip[1]);
context.lineTo(currentClip[2], currentClip[3]); context.lineTo(currentClip[2], currentClip[3]);
context.lineTo(currentClip[4], currentClip[5]); context.lineTo(currentClip[4], currentClip[5]);
context.lineTo(currentClip[6], currentClip[7]); context.lineTo(currentClip[6], currentClip[7]);
// clockwise (inner ring) for higher resolution tile // clockwise (inner ring) for higher resolution tile
context.moveTo(clip[6], clip[7]); context.moveTo(clip[6], clip[7]);
context.lineTo(clip[4], clip[5]); context.lineTo(clip[4], clip[5]);
context.lineTo(clip[2], clip[3]); context.lineTo(clip[2], clip[3]);
context.lineTo(clip[0], clip[1]); context.lineTo(clip[0], clip[1]);
context.clip(); context.clip();
}
} }
} }
executorGroup.execute(context, transform, rotation, {}, hifi, replayTypes, declutterReplays); executorGroup.execute(context, transform, rotation, {}, hifi, replayTypes, declutterReplays);
context.restore(); if (!declutterReplays) {
clips.push(currentClip); context.restore();
zs.push(currentZ); clips.push(currentClip);
zs.push(currentZ);
}
} }
} }
if (declutterReplays) { if (declutterReplays) {
@@ -28,7 +28,7 @@ function createContext() {
function executeInstructions(builder, expectedDrawTextImageCalls, expectedBuilderImageCalls) { function executeInstructions(builder, expectedDrawTextImageCalls, expectedBuilderImageCalls) {
const transform = createTransform(); const transform = createTransform();
const context = createContext(); 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_'); sinon.spy(executor, 'drawTextImageWithPointPlacement_');
const replayImageStub = sinon.stub(executor, 'replayImage_'); const replayImageStub = sinon.stub(executor, 'replayImage_');
executor.execute(context, transform); executor.execute(context, transform);