diff --git a/src/ol/render/EventType.js b/src/ol/render/EventType.js index cb33e22568..75ead01133 100644 --- a/src/ol/render/EventType.js +++ b/src/ol/render/EventType.js @@ -6,21 +6,41 @@ * @enum {string} */ export default { + /** - * @event module:ol/render/Event~RenderEvent#postcompose + * Triggered before a layer is rendered. + * @event module:ol/render/Event~RenderEvent#prerender * @api */ - POSTCOMPOSE: 'postcompose', - /** - * @event module:ol/render/Event~RenderEvent#precompose - * @api - */ - PRECOMPOSE: 'precompose', + PRERENDER: 'prerender', + /** * @event module:ol/render/Event~RenderEvent#render * @api */ RENDER: 'render', + + /** + * Triggered after a layer is rendered. + * @event module:ol/render/Event~RenderEvent#postrender + * @api + */ + POSTRENDER: 'postrender', + + /** + * Triggered before layers are rendered. + * @event module:ol/render/Event~RenderEvent#precompose + * @api + */ + PRECOMPOSE: 'precompose', + + /** + * Triggered after all layers are rendered. + * @event module:ol/render/Event~RenderEvent#postcompose + * @api + */ + POSTCOMPOSE: 'postcompose', + /** * Triggered when rendering is complete, i.e. all sources and tiles have * finished loading for the current viewport, and all tiles are faded in. @@ -28,4 +48,5 @@ export default { * @api */ RENDERCOMPLETE: 'rendercomplete' + }; diff --git a/src/ol/renderer/canvas/IntermediateCanvas.js b/src/ol/renderer/canvas/IntermediateCanvas.js index 3c27a015bf..5fddae6104 100644 --- a/src/ol/renderer/canvas/IntermediateCanvas.js +++ b/src/ol/renderer/canvas/IntermediateCanvas.js @@ -50,58 +50,12 @@ class IntermediateCanvasRenderer extends CanvasLayerRenderer { canvas.className = this.getLayer().getClassName(); } - /** - * @inheritDoc - */ - composeFrame(frameState, layerState, context) { - - this.preCompose(context, frameState); - - const image = this.getImage(); - if (image) { - - // clipped rendering if layer extent is set - const extent = layerState.extent; - const clipped = extent !== undefined && - !containsExtent(extent, frameState.extent) && - intersects(extent, frameState.extent); - if (clipped) { - this.clip(context, frameState, extent); - } - - const imageTransform = this.getImageTransform(); - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - const alpha = context.globalAlpha; - context.globalAlpha = layerState.opacity; - - // for performance reasons, context.setTransform is only used - // when the view is rotated. see http://jsperf.com/canvas-transform - const dx = imageTransform[4]; - const dy = imageTransform[5]; - const dw = image.width * imageTransform[0]; - const dh = image.height * imageTransform[3]; - if (dw >= 0.5 && dh >= 0.5) { - context.drawImage(image, 0, 0, +image.width, +image.height, - Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh)); - } - context.globalAlpha = alpha; - - if (clipped) { - context.restore(); - } - } - - this.postCompose(context, frameState, layerState); - } - /** * @inheritDoc */ renderFrame(frameState, layerState) { - this.preRender(frameState); + this.preRender(this.layerContext, frameState); const image = this.getImage(); if (image) { @@ -134,7 +88,7 @@ class IntermediateCanvasRenderer extends CanvasLayerRenderer { } } - this.postRender(frameState, layerState); + this.postRender(this.layerContext, frameState, layerState); return this.layerContext.canvas; } diff --git a/src/ol/renderer/canvas/Layer.js b/src/ol/renderer/canvas/Layer.js index 8677cde559..fc7f6bb333 100644 --- a/src/ol/renderer/canvas/Layer.js +++ b/src/ol/renderer/canvas/Layer.js @@ -1,7 +1,6 @@ /** * @module ol/renderer/canvas/Layer */ -import {abstract} from '../../util.js'; import {getBottomLeft, getBottomRight, getTopLeft, getTopRight} from '../../extent.js'; import {TRUE} from '../../functions.js'; import RenderEvent from '../../render/Event.js'; @@ -118,11 +117,10 @@ class CanvasLayerRenderer extends LayerRenderer { /** * @param {CanvasRenderingContext2D} context Context. * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../../layer/Layer.js").State} layerState Layer state. * @param {import("../../transform.js").Transform=} opt_transform Transform. * @protected */ - postCompose(context, frameState, layerState, opt_transform) { + preRender(context, frameState, opt_transform) { this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, context, frameState, opt_transform); } @@ -132,27 +130,8 @@ class CanvasLayerRenderer extends LayerRenderer { * @param {import("../../transform.js").Transform=} opt_transform Transform. * @protected */ - preCompose(context, frameState, opt_transform) { - this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, context, frameState, opt_transform); - } - - /** - * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../../transform.js").Transform=} opt_transform Transform. - * @protected - */ - preRender(frameState, opt_transform) { - // TODO: pre-render event - } - - /** - * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../../layer/Layer.js").State} layerState Layer state. - * @param {import("../../transform.js").Transform=} opt_transform Transform. - * @protected - */ - postRender(frameState, layerState, opt_transform) { - // TODO: pre-render event + postRender(context, frameState, opt_transform) { + this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, context, frameState, opt_transform); } /** @@ -205,16 +184,6 @@ class CanvasLayerRenderer extends LayerRenderer { return composeTransform(this.transform_, dx1, dy1, sx, sy, 0, dx2, dy2); } - /** - * @abstract - * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../../layer/Layer.js").State} layerState Layer state. - * @param {CanvasRenderingContext2D} context Context. - */ - composeFrame(frameState, layerState, context) { - abstract(); - } - } export default CanvasLayerRenderer; diff --git a/src/ol/renderer/canvas/Map.js b/src/ol/renderer/canvas/Map.js deleted file mode 100644 index d8aa1e3b7e..0000000000 --- a/src/ol/renderer/canvas/Map.js +++ /dev/null @@ -1,205 +0,0 @@ -/** - * @module ol/renderer/canvas/Map - */ -import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js'; -import {stableSort} from '../../array.js'; -import {CLASS_UNSELECTABLE} from '../../css.js'; -import {createCanvasContext2D} from '../../dom.js'; -import {visibleAtResolution} from '../../layer/Layer.js'; -import RenderEvent from '../../render/Event.js'; -import RenderEventType from '../../render/EventType.js'; -import {rotateAtOffset} from '../../render/canvas.js'; -import CanvasImmediateRenderer from '../../render/canvas/Immediate.js'; -import MapRenderer, {sortByZIndex} from '../Map.js'; -import SourceState from '../../source/State.js'; - - -/** - * @classdesc - * Canvas map renderer. - * @api - */ -class CanvasMapRenderer extends MapRenderer { - - /** - * @param {import("../../PluggableMap.js").default} map Map. - */ - constructor(map) { - super(map); - - const container = map.getViewport(); - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = createCanvasContext2D(); - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = this.context_.canvas; - - this.canvas_.style.width = '100%'; - this.canvas_.style.height = '100%'; - this.canvas_.style.display = 'block'; - this.canvas_.className = CLASS_UNSELECTABLE; - container.insertBefore(this.canvas_, container.childNodes[0] || null); - - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = true; - - /** - * @private - * @type {import("../../transform.js").Transform} - */ - this.transform_ = createTransform(); - - } - - /** - * @param {import("../../render/EventType.js").default} type Event type. - * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - */ - dispatchRenderEvent(type, frameState) { - const map = this.getMap(); - const context = this.context_; - if (map.hasListener(type)) { - const extent = frameState.extent; - const pixelRatio = frameState.pixelRatio; - const viewState = frameState.viewState; - const rotation = viewState.rotation; - - const transform = this.getTransform(frameState); - - const vectorContext = new CanvasImmediateRenderer(context, pixelRatio, - extent, transform, rotation); - const composeEvent = new RenderEvent(type, vectorContext, - frameState, context, null); - map.dispatchEvent(composeEvent); - } - } - - /** - * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @protected - * @return {!import("../../transform.js").Transform} Transform. - */ - getTransform(frameState) { - const viewState = frameState.viewState; - const dx1 = this.canvas_.width / 2; - const dy1 = this.canvas_.height / 2; - const sx = frameState.pixelRatio / viewState.resolution; - const sy = -sx; - const angle = -viewState.rotation; - const dx2 = -viewState.center[0]; - const dy2 = -viewState.center[1]; - return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2); - } - - /** - * @inheritDoc - */ - renderFrame(frameState) { - - if (!frameState) { - if (this.renderedVisible_) { - this.canvas_.style.display = 'none'; - this.renderedVisible_ = false; - } - return; - } - - const context = this.context_; - const pixelRatio = frameState.pixelRatio; - const width = Math.round(frameState.size[0] * pixelRatio); - const height = Math.round(frameState.size[1] * pixelRatio); - if (this.canvas_.width != width || this.canvas_.height != height) { - this.canvas_.width = width; - this.canvas_.height = height; - } else { - context.clearRect(0, 0, width, height); - } - - const rotation = frameState.viewState.rotation; - - this.calculateMatrices2D(frameState); - - this.dispatchRenderEvent(RenderEventType.PRECOMPOSE, frameState); - - const layerStatesArray = frameState.layerStatesArray; - stableSort(layerStatesArray, sortByZIndex); - - if (rotation) { - context.save(); - rotateAtOffset(context, rotation, width / 2, height / 2); - } - - const viewResolution = frameState.viewState.resolution; - let i, ii; - for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { - const layerState = layerStatesArray[i]; - const layer = layerState.layer; - const layerRenderer = /** @type {import("./Layer.js").default} */ (this.getLayerRenderer(layer)); - if (!visibleAtResolution(layerState, viewResolution) || - layerState.sourceState != SourceState.READY) { - continue; - } - if (layerRenderer.prepareFrame(frameState, layerState)) { - layerRenderer.composeFrame(frameState, layerState, context); - } - } - - if (rotation) { - context.restore(); - } - - this.dispatchRenderEvent(RenderEventType.POSTCOMPOSE, frameState); - - if (!this.renderedVisible_) { - this.canvas_.style.display = ''; - this.renderedVisible_ = true; - } - - this.scheduleRemoveUnusedLayerRenderers(frameState); - this.scheduleExpireIconCache(frameState); - } - - /** - * @inheritDoc - */ - forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, thisArg, layerFilter, thisArg2) { - let result; - const viewState = frameState.viewState; - const viewResolution = viewState.resolution; - - const layerStates = frameState.layerStatesArray; - const numLayers = layerStates.length; - - const coordinate = applyTransform( - frameState.pixelToCoordinateTransform, pixel.slice()); - - let i; - for (i = numLayers - 1; i >= 0; --i) { - const layerState = layerStates[i]; - const layer = layerState.layer; - if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { - const layerRenderer = /** @type {import("./Layer.js").default} */ (this.getLayerRenderer(layer)); - result = layerRenderer.forEachLayerAtCoordinate( - coordinate, frameState, hitTolerance, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; - } - -} - - -export default CanvasMapRenderer; diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 295dcd31c5..2d030f60a5 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -9,7 +9,7 @@ import EventType from '../../events/EventType.js'; import rbush from 'rbush'; import {buffer, createEmpty, containsExtent, getWidth} from '../../extent.js'; import RenderEventType from '../../render/EventType.js'; -import {labelCache, rotateAtOffset} from '../../render/canvas.js'; +import {labelCache} from '../../render/canvas.js'; import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js'; import InstructionsGroupExecutor from '../../render/canvas/ExecutorGroup.js'; import CanvasLayerRenderer from './Layer.js'; @@ -97,136 +97,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { super.disposeInternal(); } - /** - * @param {CanvasRenderingContext2D} context Context. - * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../../layer/Layer.js").State} layerState Layer state. - */ - compose(context, frameState, layerState) { - const extent = frameState.extent; - const pixelRatio = frameState.pixelRatio; - const skippedFeatureUids = layerState.managed ? - frameState.skippedFeatureUids : {}; - const viewState = frameState.viewState; - const projection = viewState.projection; - const rotation = viewState.rotation; - const projectionExtent = projection.getExtent(); - const vectorSource = /** @type {import("../../source/Vector.js").default} */ (this.getLayer().getSource()); - - let transform = this.getTransform(frameState, 0); - - // clipped rendering if layer extent is set - const clipExtent = layerState.extent; - const clipped = clipExtent !== undefined; - if (clipped) { - this.clip(context, frameState, clipExtent); - } - const replayGroup = this.replayGroup_; - if (replayGroup && !replayGroup.isEmpty()) { - if (this.declutterTree_) { - this.declutterTree_.clear(); - } - const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); - let drawOffsetX = 0; - let drawOffsetY = 0; - let replayContext; - const transparentLayer = layerState.opacity !== 1; - const hasRenderListeners = layer.hasListener(RenderEventType.RENDER); - if (transparentLayer || hasRenderListeners) { - let drawWidth = context.canvas.width; - let drawHeight = context.canvas.height; - if (rotation) { - const drawSize = Math.round(Math.sqrt(drawWidth * drawWidth + drawHeight * drawHeight)); - drawOffsetX = (drawSize - drawWidth) / 2; - drawOffsetY = (drawSize - drawHeight) / 2; - drawWidth = drawHeight = drawSize; - } - // resize and clear - this.context.canvas.width = drawWidth; - this.context.canvas.height = drawHeight; - replayContext = this.context; - } else { - replayContext = context; - } - - const alpha = replayContext.globalAlpha; - if (!transparentLayer) { - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - replayContext.globalAlpha = layerState.opacity; - } - - if (replayContext != context) { - replayContext.translate(drawOffsetX, drawOffsetY); - } - - const viewHints = frameState.viewHints; - const snapToPixel = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]); - const halfWidth = (frameState.size[0] * pixelRatio) / 2; - const halfHeight = (frameState.size[1] * pixelRatio) / 2; - rotateAtOffset(replayContext, -rotation, halfWidth, halfHeight); - replayGroup.execute(replayContext, transform, rotation, skippedFeatureUids, snapToPixel); - if (vectorSource.getWrapX() && projection.canWrapX() && - !containsExtent(projectionExtent, extent)) { - let startX = extent[0]; - const worldWidth = getWidth(projectionExtent); - let world = 0; - let offsetX; - while (startX < projectionExtent[0]) { - --world; - offsetX = worldWidth * world; - transform = this.getTransform(frameState, offsetX); - replayGroup.execute(replayContext, transform, rotation, skippedFeatureUids, snapToPixel); - startX += worldWidth; - } - world = 0; - startX = extent[2]; - while (startX > projectionExtent[2]) { - ++world; - offsetX = worldWidth * world; - transform = this.getTransform(frameState, offsetX); - replayGroup.execute(replayContext, transform, rotation, skippedFeatureUids, snapToPixel); - startX -= worldWidth; - } - } - rotateAtOffset(replayContext, rotation, halfWidth, halfHeight); - - if (hasRenderListeners) { - this.dispatchRenderEvent(replayContext, frameState, transform); - } - if (replayContext != context) { - if (transparentLayer) { - const mainContextAlpha = context.globalAlpha; - context.globalAlpha = layerState.opacity; - context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); - context.globalAlpha = mainContextAlpha; - } else { - context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); - } - replayContext.translate(-drawOffsetX, -drawOffsetY); - } - - if (!transparentLayer) { - replayContext.globalAlpha = alpha; - } - } - - if (clipped) { - context.restore(); - } - } - - /** - * @inheritDoc - */ - composeFrame(frameState, layerState, context) { - const transform = this.getTransform(frameState, 0); - this.preCompose(context, frameState, transform); - this.compose(context, frameState, layerState); - this.postCompose(context, frameState, layerState, transform); - } - /** * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. * @param {import("../../layer/Layer.js").State} layerState Layer state. @@ -318,10 +188,12 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { * @inheritDoc */ renderFrame(frameState, layerState) { - this.preRender(frameState); + const context = this.context; + this.preRender(context, frameState); this.render(frameState, layerState); - this.postRender(frameState, layerState); - const canvas = this.context.canvas; + this.postRender(context, frameState); + + const canvas = context.canvas; const opacity = layerState.opacity; if (opacity !== canvas.style.opacity) { diff --git a/test/spec/ol/renderer/canvas/intermediatecanvas.test.js b/test/spec/ol/renderer/canvas/intermediatecanvas.test.js deleted file mode 100644 index 37966bac88..0000000000 --- a/test/spec/ol/renderer/canvas/intermediatecanvas.test.js +++ /dev/null @@ -1,110 +0,0 @@ -import {create as createTransform, scale} from '../../../../../src/ol/transform.js'; -import ImageLayer from '../../../../../src/ol/layer/Image.js'; -import MapRenderer from '../../../../../src/ol/renderer/Map.js'; -import IntermediateCanvasRenderer from '../../../../../src/ol/renderer/canvas/IntermediateCanvas.js'; - - -describe('ol.renderer.canvas.IntermediateCanvas', function() { - - describe('#composeFrame()', function() { - let renderer, frameState, layerState, context; - beforeEach(function() { - const layer = new ImageLayer({ - extent: [1, 2, 3, 4] - }); - renderer = new IntermediateCanvasRenderer(layer); - const image = new Image(); - image.width = 3; - image.height = 3; - renderer.getImage = function() { - return image; - }; - frameState = { - viewState: { - center: [2, 3], - resolution: 1, - rotation: 0 - }, - size: [10, 10], - pixelRatio: 1, - coordinateToPixelTransform: createTransform(), - pixelToCoordinateTransform: createTransform() - }; - renderer.getImageTransform = function() { - return createTransform(); - }; - MapRenderer.prototype.calculateMatrices2D(frameState); - layerState = layer.getLayerState(); - context = { - save: sinon.spy(), - restore: sinon.spy(), - translate: sinon.spy(), - rotate: sinon.spy(), - beginPath: sinon.spy(), - moveTo: sinon.spy(), - lineTo: sinon.spy(), - clip: sinon.spy(), - drawImage: sinon.spy() - }; - }); - - it('clips to layer extent and draws image', function() { - frameState.extent = [0, 1, 4, 5]; - - renderer.composeFrame(frameState, layerState, context); - expect(context.save.callCount).to.be(1); - expect(context.translate.callCount).to.be(0); - expect(context.rotate.callCount).to.be(0); - expect(context.beginPath.callCount).to.be(1); - expect(context.moveTo.firstCall.args).to.eql([4, 4]); - expect(context.lineTo.firstCall.args).to.eql([6, 4]); - expect(context.lineTo.secondCall.args).to.eql([6, 6]); - expect(context.lineTo.thirdCall.args).to.eql([4, 6]); - expect(context.clip.callCount).to.be(1); - expect(context.drawImage.firstCall.args).to.eql( - [renderer.getImage(), 0, 0, 3, 3, 0, 0, 3, 3]); - expect(context.restore.callCount).to.be(1); - }); - - it('does not clip if frame extent does not intersect layer extent', function() { - frameState.extent = [1.1, 2.1, 2.9, 3.9]; - - renderer.composeFrame(frameState, layerState, context); - expect(context.save.callCount).to.be(0); - expect(context.translate.callCount).to.be(0); - expect(context.rotate.callCount).to.be(0); - expect(context.beginPath.callCount).to.be(0); - expect(context.clip.callCount).to.be(0); - expect(context.drawImage.firstCall.args).to.eql( - [renderer.getImage(), 0, 0, 3, 3, 0, 0, 3, 3]); - expect(context.restore.callCount).to.be(0); - }); - - it('does not clip if frame extent is outside of layer extent', function() { - frameState.extent = [10, 20, 30, 40]; - - renderer.composeFrame(frameState, layerState, context); - expect(context.save.callCount).to.be(0); - expect(context.translate.callCount).to.be(0); - expect(context.rotate.callCount).to.be(0); - expect(context.beginPath.callCount).to.be(0); - expect(context.clip.callCount).to.be(0); - expect(context.drawImage.firstCall.args).to.eql( - [renderer.getImage(), 0, 0, 3, 3, 0, 0, 3, 3]); - expect(context.restore.callCount).to.be(0); - }); - - it('does not draw image with width or height < 0.5', function() { - frameState.extent = [10, 20, 30, 40]; - renderer.getImageTransform = function() { - return scale(createTransform(), 0.1, 0.1); - }; - - renderer.composeFrame(frameState, layerState, context); - - expect(context.drawImage.notCalled).to.be(true); - }); - - }); - -}); diff --git a/test/spec/ol/renderer/canvas/map.test.js b/test/spec/ol/renderer/canvas/map.test.js deleted file mode 100644 index c3bdb637b8..0000000000 --- a/test/spec/ol/renderer/canvas/map.test.js +++ /dev/null @@ -1,271 +0,0 @@ -import {getUid} from '../../../../../src/ol/util.js'; -import Feature from '../../../../../src/ol/Feature.js'; -import Map from '../../../../../src/ol/Map.js'; -import View from '../../../../../src/ol/View.js'; -import Point from '../../../../../src/ol/geom/Point.js'; -import TileLayer from '../../../../../src/ol/layer/Tile.js'; -import VectorLayer from '../../../../../src/ol/layer/Vector.js'; -import CanvasLayerRenderer from '../../../../../src/ol/renderer/canvas/Layer.js'; -import CanvasMapRenderer from '../../../../../src/ol/renderer/canvas/Map.js'; -import VectorSource from '../../../../../src/ol/source/Vector.js'; -import Icon from '../../../../../src/ol/style/Icon.js'; -import Style from '../../../../../src/ol/style/Style.js'; - -describe('ol.renderer.canvas.Map', function() { - - describe('constructor', function() { - - it('creates a new instance', function() { - const map = new Map({ - target: document.createElement('div') - }); - const renderer = new CanvasMapRenderer(map); - expect(renderer).to.be.a(CanvasMapRenderer); - }); - - }); - - describe('#forEachFeatureAtCoordinate', function() { - - let layer, map, target; - - beforeEach(function(done) { - target = document.createElement('div'); - target.style.width = '100px'; - target.style.height = '100px'; - document.body.appendChild(target); - map = new Map({ - pixelRatio: 1, - target: target, - view: new View({ - center: [0, 0], - zoom: 0 - }) - }); - - // 1 x 1 pixel black icon - const img = document.createElement('img'); - img.onload = function() { - done(); - }; - img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=='; - - layer = new VectorLayer({ - source: new VectorSource({ - features: [ - new Feature({ - geometry: new Point([0, 0]) - }) - ] - }), - style: new Style({ - image: new Icon({ - img: img, - imgSize: [1, 1] - }) - }) - }); - }); - - afterEach(function() { - map.setTarget(null); - document.body.removeChild(target); - }); - - it('calls callback with layer for managed layers', function() { - map.addLayer(layer); - map.renderSync(); - const cb = sinon.spy(); - map.forEachFeatureAtPixel(map.getPixelFromCoordinate([0, 0]), cb); - expect(cb).to.be.called(); - expect(cb.firstCall.args[1]).to.be(layer); - }); - - it('calls callback with null for unmanaged layers', function() { - layer.setMap(map); - map.renderSync(); - const cb = sinon.spy(); - map.forEachFeatureAtPixel(map.getPixelFromCoordinate([0, 0]), cb); - expect(cb).to.be.called(); - expect(cb.firstCall.args[1]).to.be(null); - }); - - it('calls callback with main layer when skipped feature on unmanaged layer', function() { - const feature = layer.getSource().getFeatures()[0]; - const managedLayer = new VectorLayer({ - source: new VectorSource({ - features: [feature] - }) - }); - map.addLayer(managedLayer); - map.skipFeature(feature); - layer.setMap(map); - map.renderSync(); - const cb = sinon.spy(); - map.forEachFeatureAtPixel(map.getPixelFromCoordinate([0, 0]), cb); - expect(cb.callCount).to.be(1); - expect(cb.firstCall.args[1]).to.be(managedLayer); - }); - - it('filters managed layers', function() { - map.addLayer(layer); - map.renderSync(); - const cb = sinon.spy(); - map.forEachFeatureAtPixel(map.getPixelFromCoordinate([0, 0]), cb, { - layerFilter: function() { - return false; - } - }); - expect(cb).to.not.be.called(); - }); - - it('doesn\'t fail with layer with no source', function() { - map.addLayer(new TileLayer()); - map.renderSync(); - expect(function() { - map.forEachFeatureAtPixel(map.getPixelFromCoordinate([0, 0]), - function() {}); - }).to.not.throwException(); - }); - - it('calls callback for clicks inside of the hitTolerance', function() { - map.addLayer(layer); - map.renderSync(); - const cb1 = sinon.spy(); - const cb2 = sinon.spy(); - - const pixel = map.getPixelFromCoordinate([0, 0]); - - const pixelsInside = [ - [pixel[0] + 9, pixel[1]], - [pixel[0] - 9, pixel[1]], - [pixel[0], pixel[1] + 9], - [pixel[0], pixel[1] - 9] - ]; - - const pixelsOutside = [ - [pixel[0] + 9, pixel[1] + 9], - [pixel[0] - 9, pixel[1] + 9], - [pixel[0] + 9, pixel[1] - 9], - [pixel[0] - 9, pixel[1] - 9] - ]; - - for (let i = 0; i < 4; i++) { - map.forEachFeatureAtPixel(pixelsInside[i], cb1, {hitTolerance: 10}); - } - expect(cb1.callCount).to.be(4); - expect(cb1.firstCall.args[1]).to.be(layer); - - for (let j = 0; j < 4; j++) { - map.forEachFeatureAtPixel(pixelsOutside[j], cb2, {hitTolerance: 10}); - } - expect(cb2).not.to.be.called(); - }); - }); - - describe('#forEachLayerAtCoordinate', function() { - - let layer, map, target; - - beforeEach(function(done) { - target = document.createElement('div'); - target.style.width = '100px'; - target.style.height = '100px'; - document.body.appendChild(target); - map = new Map({ - pixelRatio: 1, - target: target, - view: new View({ - center: [0, 0], - zoom: 0 - }) - }); - - // 1 x 1 pixel black icon - const img = document.createElement('img'); - img.onload = function() { - done(); - }; - img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=='; - - layer = new VectorLayer({ - source: new VectorSource({ - features: [ - new Feature({ - geometry: new Point([0, 0]) - }) - ] - }), - style: new Style({ - image: new Icon({ - img: img, - imgSize: [1, 1] - }) - }) - }); - }); - - afterEach(function() { - map.setTarget(null); - document.body.removeChild(target); - }); - - it('calls callback for clicks inside of the hitTolerance', function() { - map.addLayer(layer); - map.renderSync(); - const cb1 = sinon.spy(); - const cb2 = sinon.spy(); - - const pixel = map.getPixelFromCoordinate([0, 0]); - - const pixelsInside = [ - [pixel[0] + 9, pixel[1]], - [pixel[0] - 9, pixel[1]], - [pixel[0], pixel[1] + 9], - [pixel[0], pixel[1] - 9] - ]; - - const pixelsOutside = [ - [pixel[0] + 9, pixel[1] + 9], - [pixel[0] - 9, pixel[1] + 9], - [pixel[0] + 9, pixel[1] - 9], - [pixel[0] - 9, pixel[1] - 9] - ]; - - for (let i = 0; i < 4; i++) { - map.forEachLayerAtPixel(pixelsInside[i], cb1, {hitTolerance: 10}); - } - expect(cb1.callCount).to.be(4); - expect(cb1.firstCall.args[0]).to.be(layer); - - for (let j = 0; j < 4; j++) { - map.forEachLayerAtPixel(pixelsOutside[j], cb2, {hitTolerance: 10}); - } - expect(cb2).not.to.be.called(); - }); - }); - - describe('#renderFrame()', function() { - let layer, map, renderer; - - beforeEach(function() { - map = new Map({}); - map.on('postcompose', function() {}); - layer = new VectorLayer({ - source: new VectorSource({wrapX: true}) - }); - renderer = map.getRenderer(); - renderer.layerRenderers_ = {}; - const layerRenderer = new CanvasLayerRenderer(layer); - layerRenderer.prepareFrame = function() { - return true; - }; - layerRenderer.getImage = function() { - return null; - }; - renderer.layerRenderers_[getUid(layer)] = layerRenderer; - }); - - }); - -}); diff --git a/test/spec/ol/renderer/canvas/tilelayer.test.js b/test/spec/ol/renderer/canvas/tilelayer.test.js index 21ba13e2f0..4120f086a8 100644 --- a/test/spec/ol/renderer/canvas/tilelayer.test.js +++ b/test/spec/ol/renderer/canvas/tilelayer.test.js @@ -1,12 +1,7 @@ import Map from '../../../../../src/ol/Map.js'; import View from '../../../../../src/ol/View.js'; import TileLayer from '../../../../../src/ol/layer/Tile.js'; -import {get as getProjection} from '../../../../../src/ol/proj.js'; -import MapRenderer from '../../../../../src/ol/renderer/Map.js'; -import CanvasTileLayerRenderer from '../../../../../src/ol/renderer/canvas/TileLayer.js'; import TileWMS from '../../../../../src/ol/source/TileWMS.js'; -import XYZ from '../../../../../src/ol/source/XYZ.js'; -import {create as createTransform} from '../../../../../src/ol/transform.js'; describe('ol.renderer.canvas.TileLayer', function() { @@ -54,70 +49,4 @@ describe('ol.renderer.canvas.TileLayer', function() { }); }); - describe('#composeFrame()', function() { - - let img = null; - beforeEach(function(done) { - img = new Image(1, 1); - img.onload = function() { - done(); - }; - img.src = 'data:image/gif;base64,' + - 'R0lGODlhAQABAPAAAP8AAP///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=='; - }); - afterEach(function() { - img = null; - }); - - it('uses correct draw scale when rotating (HiDPI)', function() { - const layer = new TileLayer({ - source: new XYZ({ - tileSize: 1 - }) - }); - const renderer = new CanvasTileLayerRenderer(layer); - renderer.renderedTiles = []; - const frameState = { - viewHints: [], - time: Date.now(), - viewState: { - center: [10, 5], - projection: getProjection('EPSG:3857'), - resolution: 1, - rotation: Math.PI - }, - extent: [0, 0, 20, 10], - size: [20, 10], - pixelRatio: 2, - coordinateToPixelTransform: createTransform(), - pixelToCoordinateTransform: createTransform(), - usedTiles: {}, - wantedTiles: {} - }; - renderer.getImageTransform = function() { - return createTransform(); - }; - MapRenderer.prototype.calculateMatrices2D(frameState); - const layerState = layer.getLayerState(); - const canvas = document.createElement('canvas'); - canvas.width = 200; - canvas.height = 100; - const context = { - canvas: canvas, - drawImage: sinon.spy() - }; - renderer.renderedTiles = [{ - getTileCoord: function() { - return [0, 0, 0]; - }, - getImage: function() { - return img; - } - }]; - renderer.prepareFrame(frameState, layerState); - renderer.composeFrame(frameState, layerState, context); - expect(context.drawImage.firstCall.args[0].width).to.be(17); - }); - }); - });