Merge pull request #12442 from MoonE/fix-layer-extent
Fix layer render extent when it is not at all in view
This commit is contained in:
@@ -6,7 +6,7 @@ import ViewHint from '../../ViewHint.js';
|
||||
import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js';
|
||||
import {assign} from '../../obj.js';
|
||||
import {compose as composeTransform, makeInverse} from '../../transform.js';
|
||||
import {containsExtent, intersects} from '../../extent.js';
|
||||
import {containsExtent, intersects as intersectsExtent} from '../../extent.js';
|
||||
import {fromUserExtent} from '../../proj.js';
|
||||
import {getIntersection, isEmpty} from '../../extent.js';
|
||||
import {toString as toTransformString} from '../../transform.js';
|
||||
@@ -148,14 +148,14 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
// clipped rendering if layer extent is set
|
||||
let clipped = false;
|
||||
let render = true;
|
||||
if (layerState.extent) {
|
||||
const layerExtent = fromUserExtent(
|
||||
layerState.extent,
|
||||
viewState.projection
|
||||
);
|
||||
clipped =
|
||||
!containsExtent(layerExtent, frameState.extent) &&
|
||||
intersects(layerExtent, frameState.extent);
|
||||
render = intersectsExtent(layerExtent, frameState.extent);
|
||||
clipped = render && !containsExtent(layerExtent, frameState.extent);
|
||||
if (clipped) {
|
||||
this.clipUnrotated(context, frameState, layerExtent);
|
||||
}
|
||||
@@ -176,21 +176,21 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
this.renderedResolution = (imageResolution * pixelRatio) / imagePixelRatio;
|
||||
|
||||
const dx = transform[4];
|
||||
const dy = transform[5];
|
||||
const dw = img.width * transform[0];
|
||||
const dh = img.height * transform[3];
|
||||
|
||||
assign(context, this.getLayer().getSource().getContextOptions());
|
||||
this.preRender(context, frameState);
|
||||
if (dw >= 0.5 && dh >= 0.5) {
|
||||
if (render && dw >= 0.5 && dh >= 0.5) {
|
||||
const dx = transform[4];
|
||||
const dy = transform[5];
|
||||
const opacity = layerState.opacity;
|
||||
let previousAlpha;
|
||||
if (opacity !== 1) {
|
||||
previousAlpha = this.context.globalAlpha;
|
||||
this.context.globalAlpha = opacity;
|
||||
previousAlpha = context.globalAlpha;
|
||||
context.globalAlpha = opacity;
|
||||
}
|
||||
this.context.drawImage(
|
||||
context.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
@@ -202,7 +202,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
Math.round(dh)
|
||||
);
|
||||
if (opacity !== 1) {
|
||||
this.context.globalAlpha = previousAlpha;
|
||||
context.globalAlpha = previousAlpha;
|
||||
}
|
||||
}
|
||||
this.postRender(context, frameState);
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
getTopRight,
|
||||
} from '../../extent.js';
|
||||
import {createCanvasContext2D} from '../../dom.js';
|
||||
import {rotateAtOffset} from '../../render/canvas.js';
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
@@ -130,38 +129,6 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @param {import("../../extent.js").Extent} extent Clip extent.
|
||||
* @protected
|
||||
*/
|
||||
clip(context, frameState, extent) {
|
||||
const pixelRatio = frameState.pixelRatio;
|
||||
const halfWidth = (frameState.size[0] * pixelRatio) / 2;
|
||||
const halfHeight = (frameState.size[1] * pixelRatio) / 2;
|
||||
const rotation = frameState.viewState.rotation;
|
||||
const topLeft = getTopLeft(extent);
|
||||
const topRight = getTopRight(extent);
|
||||
const bottomRight = getBottomRight(extent);
|
||||
const bottomLeft = getBottomLeft(extent);
|
||||
|
||||
applyTransform(frameState.coordinateToPixelTransform, topLeft);
|
||||
applyTransform(frameState.coordinateToPixelTransform, topRight);
|
||||
applyTransform(frameState.coordinateToPixelTransform, bottomRight);
|
||||
applyTransform(frameState.coordinateToPixelTransform, bottomLeft);
|
||||
|
||||
context.save();
|
||||
rotateAtOffset(context, -rotation, halfWidth, halfHeight);
|
||||
context.beginPath();
|
||||
context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio);
|
||||
context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio);
|
||||
context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio);
|
||||
context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio);
|
||||
context.clip();
|
||||
rotateAtOffset(context, rotation, halfWidth, halfHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
|
||||
@@ -280,17 +280,19 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
// clipped rendering if layer extent is set
|
||||
let clipped = false;
|
||||
let render = true;
|
||||
if (layerState.extent && this.clipping) {
|
||||
const layerExtent = fromUserExtent(layerState.extent, projection);
|
||||
clipped =
|
||||
!containsExtent(layerExtent, frameState.extent) &&
|
||||
intersectsExtent(layerExtent, frameState.extent);
|
||||
render = intersectsExtent(layerExtent, frameState.extent);
|
||||
clipped = render && !containsExtent(layerExtent, frameState.extent);
|
||||
if (clipped) {
|
||||
this.clipUnrotated(context, frameState, layerExtent);
|
||||
}
|
||||
}
|
||||
|
||||
this.renderWorlds(replayGroup, frameState);
|
||||
if (render) {
|
||||
this.renderWorlds(replayGroup, frameState);
|
||||
}
|
||||
|
||||
if (clipped) {
|
||||
context.restore();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import CanvasImageLayerRenderer from '../../../../../../src/ol/renderer/canvas/ImageLayer.js';
|
||||
import Feature from '../../../../../../src/ol/Feature.js';
|
||||
import ImageLayer from '../../../../../../src/ol/layer/Image.js';
|
||||
import Map from '../../../../../../src/ol/Map.js';
|
||||
@@ -348,4 +349,90 @@ describe('ol.renderer.canvas.ImageLayer', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('renderFrame', function () {
|
||||
const projection = new Projection({
|
||||
code: 'custom-image',
|
||||
units: 'pixels',
|
||||
extent: [0, 0, 256, 256],
|
||||
});
|
||||
let renderer, layer;
|
||||
function createLayerFrameState(extent) {
|
||||
layer = new ImageLayer({
|
||||
source: new Static({
|
||||
url: 'spec/ol/data/osm-0-0-0.png',
|
||||
imageExtent: projection.getExtent(),
|
||||
projection: projection,
|
||||
}),
|
||||
extent: extent,
|
||||
});
|
||||
layer.getSource().image_.load();
|
||||
renderer = layer.getRenderer();
|
||||
renderer.renderWorlds = sinon.spy();
|
||||
renderer.clipUnrotated = sinon.spy();
|
||||
renderer.useContainer = function () {
|
||||
CanvasImageLayerRenderer.prototype.useContainer.apply(this, arguments);
|
||||
this.context = sinon.spy(this.context);
|
||||
};
|
||||
return {
|
||||
pixelRatio: 1,
|
||||
time: 1000000000000,
|
||||
viewState: {
|
||||
center: [0, 0],
|
||||
projection: projection,
|
||||
resolution: 1,
|
||||
rotation: 0,
|
||||
},
|
||||
animate: false,
|
||||
coordinateToPixelTransform: [1, 0, 0, 1, 0, 0],
|
||||
extent: [0, 0, 100, 100],
|
||||
index: 0,
|
||||
layerStatesArray: [layer.getLayerState()],
|
||||
layerIndex: 0,
|
||||
pixelToCoordinateTransform: [1, 0, 0, 1, 0, 0],
|
||||
size: [100, 100],
|
||||
viewHints: [],
|
||||
};
|
||||
}
|
||||
it('does not render if layer extent does not intersect view extent', function (done) {
|
||||
const frameState = createLayerFrameState([200, 200, 300, 300]);
|
||||
layer.getSource().on('imageloadend', function () {
|
||||
try {
|
||||
expect(renderer.prepareFrame(frameState)).to.be(false);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('renders if layer extent partially intersects view extent', function (done) {
|
||||
const frameState = createLayerFrameState([50, 50, 150, 150]);
|
||||
layer.getSource().on('imageloadend', function () {
|
||||
if (renderer.prepareFrame(frameState)) {
|
||||
renderer.renderFrame(frameState, null);
|
||||
}
|
||||
try {
|
||||
expect(renderer.clipUnrotated.callCount).to.be(1);
|
||||
expect(renderer.context.drawImage.callCount).to.be(1);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('renders without clipping when layer extent covers view', function (done) {
|
||||
const frameState = createLayerFrameState([0, 0, 100, 100]);
|
||||
layer.getSource().on('imageloadend', function () {
|
||||
if (renderer.prepareFrame(frameState)) {
|
||||
renderer.renderFrame(frameState, null);
|
||||
}
|
||||
try {
|
||||
expect(renderer.clipUnrotated.callCount).to.be(0);
|
||||
expect(renderer.context.drawImage.callCount).to.be(1);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -699,4 +699,62 @@ describe('ol.renderer.canvas.VectorLayer', function () {
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('renderFrame', function () {
|
||||
const projection = getProjection('EPSG:3857');
|
||||
let renderer;
|
||||
function createLayerFrameState(extent) {
|
||||
const layer = new VectorLayer({
|
||||
source: new VectorSource({
|
||||
features: [new Feature(new Point(getCenter(extent)))],
|
||||
}),
|
||||
extent: extent,
|
||||
});
|
||||
renderer = layer.getRenderer();
|
||||
renderer.renderWorlds = sinon.spy();
|
||||
renderer.clipUnrotated = sinon.spy();
|
||||
return {
|
||||
pixelRatio: 1,
|
||||
time: 1000000000000,
|
||||
viewState: {
|
||||
center: [0, 0],
|
||||
projection: projection,
|
||||
resolution: 1,
|
||||
rotation: 0,
|
||||
},
|
||||
animate: false,
|
||||
coordinateToPixelTransform: [1, 0, 0, 1, 0, 0],
|
||||
extent: [-50, -50, 50, 50],
|
||||
index: 0,
|
||||
layerStatesArray: [layer.getLayerState()],
|
||||
layerIndex: 0,
|
||||
pixelToCoordinateTransform: [1, 0, 0, 1, 0, 0],
|
||||
size: [100, 100],
|
||||
viewHints: [],
|
||||
};
|
||||
}
|
||||
it('does not render if layer extent does not intersect view extent', function () {
|
||||
const frameState = createLayerFrameState([100, 100, 200, 200]);
|
||||
if (renderer.prepareFrame(frameState)) {
|
||||
renderer.renderFrame(frameState, null);
|
||||
}
|
||||
expect(renderer.renderWorlds.callCount).to.be(0);
|
||||
expect(renderer.clipUnrotated.callCount).to.be(0);
|
||||
});
|
||||
it('renders if layer extent partially intersects view extent', function () {
|
||||
const frameState = createLayerFrameState([0, 0, 100, 100]);
|
||||
if (renderer.prepareFrame(frameState)) {
|
||||
renderer.renderFrame(frameState, null);
|
||||
}
|
||||
expect(renderer.renderWorlds.callCount).to.be(1);
|
||||
expect(renderer.clipUnrotated.callCount).to.be(1);
|
||||
});
|
||||
it('renders withoutt clipping when layer extent covers view', function () {
|
||||
const frameState = createLayerFrameState([-200, -200, 200, 200]);
|
||||
if (renderer.prepareFrame(frameState)) {
|
||||
renderer.renderFrame(frameState, null);
|
||||
}
|
||||
expect(renderer.renderWorlds.callCount).to.be(1);
|
||||
expect(renderer.clipUnrotated.callCount).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user