From f57413fc042d8f0c56af4560993771d59252ab2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Sun, 11 Jul 2021 16:59:26 +0200 Subject: [PATCH 1/2] add tests for layer extent config --- src/ol/renderer/canvas/ImageLayer.js | 8 +- .../ol/renderer/canvas/imagelayer.test.js | 87 +++++++++++++++++++ .../ol/renderer/canvas/vectorlayer.test.js | 58 +++++++++++++ 3 files changed, 149 insertions(+), 4 deletions(-) diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index 266d36bed5..435d55a6a0 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -187,10 +187,10 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer { 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); diff --git a/test/browser/spec/ol/renderer/canvas/imagelayer.test.js b/test/browser/spec/ol/renderer/canvas/imagelayer.test.js index 4b2c092c0b..71b34aa9f3 100644 --- a/test/browser/spec/ol/renderer/canvas/imagelayer.test.js +++ b/test/browser/spec/ol/renderer/canvas/imagelayer.test.js @@ -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); + } + }); + }); + }); }); diff --git a/test/browser/spec/ol/renderer/canvas/vectorlayer.test.js b/test/browser/spec/ol/renderer/canvas/vectorlayer.test.js index 37a548cc9e..3100001f82 100644 --- a/test/browser/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/browser/spec/ol/renderer/canvas/vectorlayer.test.js @@ -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); + }); + }); }); From 83226cc79a6056400ddaef2aa7034421a8adb5a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Mon, 28 Jun 2021 00:22:53 +0200 Subject: [PATCH 2/2] Fix layer render extent when it is not at all in view --- src/ol/renderer/canvas/ImageLayer.js | 14 ++++++------ src/ol/renderer/canvas/Layer.js | 33 --------------------------- src/ol/renderer/canvas/VectorLayer.js | 10 ++++---- 3 files changed, 13 insertions(+), 44 deletions(-) diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index 435d55a6a0..be4c241811 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -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,14 +176,14 @@ 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) { diff --git a/src/ol/renderer/canvas/Layer.js b/src/ol/renderer/canvas/Layer.js index d349baea1c..a0557b2f9f 100644 --- a/src/ol/renderer/canvas/Layer.js +++ b/src/ol/renderer/canvas/Layer.js @@ -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. diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index c3aa878ac2..da0de9da8b 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -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();