Avoid clipping when rendering to tiles that don't exceed the clip extent
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user