From a65e30fe41488cb85f3a1e38772b8e673079cbdf Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 30 Mar 2017 22:02:54 -0600 Subject: [PATCH] Reworked raster source rendering --- src/ol/source/raster.js | 181 +++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 106 deletions(-) diff --git a/src/ol/source/raster.js b/src/ol/source/raster.js index 2ebcc06937..0ef94c8625 100644 --- a/src/ol/source/raster.js +++ b/src/ol/source/raster.js @@ -65,12 +65,6 @@ ol.source.Raster = function(options) { this.changed, this); } - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.canvasContext_ = ol.dom.createCanvasContext2D(); - /** * @private * @type {ol.TileQueue} @@ -88,11 +82,11 @@ ol.source.Raster = function(options) { } /** - * The most recently rendered state. - * @type {?ol.SourceRasterRenderedState} + * The most recently requested frame state. + * @type {olx.FrameState} * @private */ - this.renderedState_ = null; + this.requestedFrameState_; /** * The most recently rendered image canvas. @@ -101,6 +95,12 @@ ol.source.Raster = function(options) { */ this.renderedImageCanvas_ = null; + /** + * The most recently rendered revision. + * @type {number} + */ + this.renderedRevision_; + /** * @private * @type {olx.FrameState} @@ -176,13 +176,11 @@ ol.source.Raster.prototype.updateFrameState_ = function(extent, resolution, proj ol.obj.assign({}, frameState.viewState)); var center = ol.extent.getCenter(extent); - var width = Math.round(ol.extent.getWidth(extent) / resolution); - var height = Math.round(ol.extent.getHeight(extent) / resolution); - frameState.extent = extent; - frameState.focus = ol.extent.getCenter(extent); - frameState.size[0] = width; - frameState.size[1] = height; + frameState.extent = extent.slice(); + frameState.focus = center; + frameState.size[0] = Math.round(ol.extent.getWidth(extent) / resolution); + frameState.size[1] = Math.round(ol.extent.getHeight(extent) / resolution); var viewState = frameState.viewState; viewState.center = center; @@ -192,66 +190,6 @@ ol.source.Raster.prototype.updateFrameState_ = function(extent, resolution, proj }; -/** - * Determine if the most recently rendered image canvas is dirty. - * @param {ol.Extent} extent The requested extent. - * @param {number} resolution The requested resolution. - * @return {boolean} The image is dirty. - * @private - */ -ol.source.Raster.prototype.isDirty_ = function(extent, resolution) { - var state = this.renderedState_; - return !state || - this.getRevision() !== state.revision || - resolution !== state.resolution || - !ol.extent.equals(extent, state.extent); -}; - - -/** - * @inheritDoc - */ -ol.source.Raster.prototype.getImage = function(extent, resolution, pixelRatio, projection) { - - if (!this.allSourcesReady_()) { - return null; - } - - var currentExtent = extent.slice(); - if (!this.isDirty_(currentExtent, resolution)) { - return this.renderedImageCanvas_; - } - - var context = this.canvasContext_; - var canvas = context.canvas; - - var width = Math.round(ol.extent.getWidth(currentExtent) / resolution); - var height = Math.round(ol.extent.getHeight(currentExtent) / resolution); - - if (width !== canvas.width || - height !== canvas.height) { - canvas.width = width; - canvas.height = height; - } - - var frameState = this.updateFrameState_(currentExtent, resolution, projection); - - var imageCanvas = new ol.ImageCanvas( - currentExtent, resolution, 1, this.getAttributions(), canvas, - this.composeFrame_.bind(this, frameState)); - - this.renderedImageCanvas_ = imageCanvas; - - this.renderedState_ = { - extent: currentExtent, - resolution: resolution, - revision: this.getRevision() - }; - - return imageCanvas; -}; - - /** * Determine if all sources are ready. * @return {boolean} All sources are ready. @@ -272,13 +210,41 @@ ol.source.Raster.prototype.allSourcesReady_ = function() { /** - * Compose the frame. This renders data from all sources, runs pixel-wise - * operations, and renders the result to the stored canvas context. - * @param {olx.FrameState} frameState The frame state. - * @param {function(Error)} callback Called when composition is complete. + * @inheritDoc + */ +ol.source.Raster.prototype.getImage = function(extent, resolution, pixelRatio, projection) { + if (!this.allSourcesReady_()) { + return null; + } + + var frameState = this.updateFrameState_(extent, resolution, projection); + this.requestedFrameState_ = frameState; + + frameState.tileQueue.loadMoreTiles(16, 16); + + // check if we can't reuse the existing ol.ImageCanvas + if (this.renderedImageCanvas_) { + var renderedResolution = this.renderedImageCanvas_.getResolution(); + var renderedExtent = this.renderedImageCanvas_.getExtent(); + if (resolution !== renderedResolution || !ol.extent.equals(extent, renderedExtent)) { + this.renderedImageCanvas_ = null; + } + } + + if (!this.renderedImageCanvas_ || this.getRevision() !== this.renderedRevision_) { + this.processSources_(); + } + + return this.renderedImageCanvas_; +}; + + +/** + * Start processing source data. * @private */ -ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) { +ol.source.Raster.prototype.processSources_ = function() { + var frameState = this.requestedFrameState_; var len = this.renderers_.length; var imageDatas = new Array(len); for (var i = 0; i < len; ++i) { @@ -287,53 +253,56 @@ ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) { if (imageData) { imageDatas[i] = imageData; } else { - // image not yet ready - imageDatas = null; - break; + return; } } - if (imageDatas) { - var data = {}; - this.dispatchEvent(new ol.source.Raster.Event( - ol.source.Raster.EventType_.BEFOREOPERATIONS, frameState, data)); - - this.worker_.process(imageDatas, data, - this.onWorkerComplete_.bind(this, frameState, callback)); - } - - frameState.tileQueue.loadMoreTiles(16, 16); + var data = {}; + this.dispatchEvent(new ol.source.Raster.Event( + ol.source.Raster.EventType_.BEFOREOPERATIONS, frameState, data)); + this.worker_.process(imageDatas, data, + this.onWorkerComplete_.bind(this, frameState)); }; /** * Called when pixel processing is complete. * @param {olx.FrameState} frameState The frame state. - * @param {function(Error)} callback Called when rendering is complete. * @param {Error} err Any error during processing. * @param {ImageData} output The output image data. * @param {Object} data The user data. * @private */ -ol.source.Raster.prototype.onWorkerComplete_ = function(frameState, callback, err, output, data) { - if (err) { - callback(err); +ol.source.Raster.prototype.onWorkerComplete_ = function(frameState, err, output, data) { + if (err || !output) { return; } - if (!output) { - // job aborted + + // do nothing if extent or resolution changed + var extent = frameState.extent; + var resolution = frameState.viewState.resolution; + if (resolution !== this.requestedFrameState_.viewState.resolution || + !ol.extent.equals(extent, this.requestedFrameState_.extent)) { return; } + var context; + if (this.renderedImageCanvas_) { + context = this.renderedImageCanvas_.getImage().getContext('2d'); + } else { + var width = Math.round(ol.extent.getWidth(extent) / resolution); + var height = Math.round(ol.extent.getHeight(extent) / resolution); + context = ol.dom.createCanvasContext2D(width, height); + this.renderedImageCanvas_ = new ol.ImageCanvas( + extent, resolution, 1, this.getAttributions(), context.canvas); + } + context.putImageData(output, 0, 0); + + this.changed(); + this.renderedRevision_ = this.getRevision(); + this.dispatchEvent(new ol.source.Raster.Event( ol.source.Raster.EventType_.AFTEROPERATIONS, frameState, data)); - - var resolution = frameState.viewState.resolution / frameState.pixelRatio; - if (!this.isDirty_(frameState.extent, resolution)) { - this.canvasContext_.putImageData(output, 0, 0); - } - - callback(null); };