diff --git a/externs/olx.js b/externs/olx.js index da432b6c26..467d2eb6ea 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -4500,7 +4500,8 @@ olx.source.ImageVectorOptions.prototype.style; /** * @typedef {{sources: Array., - * operations: (Array.|undefined)}} + * operations: (Array.|undefined), + * operationType: (ol.raster.OperationType|undefined)}} * @api */ olx.source.RasterOptions; @@ -4515,7 +4516,7 @@ olx.source.RasterOptions.prototype.sources; /** - * Pixel operations. Operations will be called with pixels from input sources + * Pixel operations. Operations will be called with data from input sources * and the final output will be assigned to the raster source. * @type {Array.|undefined} * @api @@ -4523,6 +4524,17 @@ olx.source.RasterOptions.prototype.sources; olx.source.RasterOptions.prototype.operations; +/** + * Operation type. Supported values are `'pixel'` and `'image'`. By default, + * `'pixel'` operations are assumed, and operations will be called with an + * array of pixels from input sources. If set to `'image'`, operations will + * be called with an array of ImageData objects from input sources. + * @type {ol.raster.OperationType|undefined} + * @api + */ +olx.source.RasterOptions.prototype.operationType; + + /** * @typedef {{attributions: (Array.|undefined), * crossOrigin: (null|string|undefined), diff --git a/src/ol/raster/operation.js b/src/ol/raster/operation.js index b0430fba6b..b88d99c8af 100644 --- a/src/ol/raster/operation.js +++ b/src/ol/raster/operation.js @@ -1,23 +1,39 @@ goog.provide('ol.raster.IdentityOp'); goog.provide('ol.raster.Operation'); +goog.provide('ol.raster.OperationType'); /** - * A function that takes an array of {@link ol.raster.Pixel} as inputs, performs - * some operation on them, and returns an array of {@link ol.raster.Pixel} as - * outputs. + * Raster operation type. Supported values are `'pixel'` and `'image'`. + * @enum {string} + * @api + */ +ol.raster.OperationType = { + PIXEL: 'pixel', + IMAGE: 'image' +}; + + +/** + * A function that takes an array of input data, performs some operation, and + * returns an array of ouput data. For `'pixel'` type operations, functions + * will be called with an array of {@link ol.raster.Pixel} data and should + * return an array of the same. For `'image'` type operations, functions will + * be called with an array of {@link ImageData + * https://developer.mozilla.org/en-US/docs/Web/API/ImageData} and should return + * an array of the same. * - * @typedef {function(Array.): Array.} + * @typedef {function((Array.|Array.)): + * (Array.|Array.)} * @api */ ol.raster.Operation; /** - * The identity operation for pixels. Returns the supplied input pixels as - * outputs. - * @param {Array.} inputs Input pixels. - * @return {Array.} The input pixels as output. + * The identity operation. Returns the supplied input data as output. + * @param {(Array.|Array.)} inputs Input data. + * @return {(Array.|Array.)} The output data. */ ol.raster.IdentityOp = function(inputs) { return inputs; diff --git a/src/ol/source/rastersource.js b/src/ol/source/rastersource.js index 596e67ee1c..e51e013743 100644 --- a/src/ol/source/rastersource.js +++ b/src/ol/source/rastersource.js @@ -13,6 +13,7 @@ goog.require('ol.extent'); goog.require('ol.layer.Image'); goog.require('ol.layer.Tile'); goog.require('ol.raster.IdentityOp'); +goog.require('ol.raster.OperationType'); goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.source.Image'); @@ -41,6 +42,13 @@ ol.source.Raster = function(options) { this.operations_ = goog.isDef(options.operations) ? options.operations : [ol.raster.IdentityOp]; + /** + * @private + * @type {ol.raster.OperationType} + */ + this.operationType_ = goog.isDef(options.operationType) ? + options.operationType : ol.raster.OperationType.PIXEL; + /** * @private * @type {Array.} @@ -215,29 +223,36 @@ ol.source.Raster.prototype.composeFrame_ = function(frameState) { this.renderers_[i], frameState, frameState.layerStatesArray[i]); } - var targetImageData = context.getImageData(0, 0, canvas.width, canvas.height); - var target = targetImageData.data; - - var resolution = frameState.viewState.resolution / frameState.pixelRatio; this.dispatchEvent(new ol.source.RasterEvent( ol.source.RasterEventType.BEFOREOPERATIONS, resolution)); - var source, pixel; - for (var j = 0, jj = target.length; j < jj; j += 4) { - for (var k = 0; k < len; ++k) { - source = imageDatas[k].data; - pixel = pixels[k]; - pixel[0] = source[j]; - pixel[1] = source[j + 1]; - pixel[2] = source[j + 2]; - pixel[3] = source[j + 3]; + var targetImageData = null; + if (this.operationType_ === ol.raster.OperationType.PIXEL) { + targetImageData = context.getImageData(0, 0, canvas.width, + canvas.height); + var target = targetImageData.data; + + var source, pixel; + for (var j = 0, jj = target.length; j < jj; j += 4) { + for (var k = 0; k < len; ++k) { + source = imageDatas[k].data; + pixel = pixels[k]; + pixel[0] = source[j]; + pixel[1] = source[j + 1]; + pixel[2] = source[j + 2]; + pixel[3] = source[j + 3]; + } + pixel = this.runPixelOperations_(pixels)[0]; + target[j] = pixel[0]; + target[j + 1] = pixel[1]; + target[j + 2] = pixel[2]; + target[j + 3] = pixel[3]; } - pixel = this.runOperations_(pixels)[0]; - target[j] = pixel[0]; - target[j + 1] = pixel[1]; - target[j + 2] = pixel[2]; - target[j + 3] = pixel[3]; + } else if (this.operationType_ === ol.raster.OperationType.IMAGE) { + targetImageData = this.runImageOperations_(imageDatas)[0]; + } else { + goog.asserts.fail('unsupported operation type: ' + this.operationType_); } this.dispatchEvent(new ol.source.RasterEvent( @@ -255,7 +270,7 @@ ol.source.Raster.prototype.composeFrame_ = function(frameState) { * @return {Array.} The modified pixels. * @private */ -ol.source.Raster.prototype.runOperations_ = function(pixels) { +ol.source.Raster.prototype.runPixelOperations_ = function(pixels) { for (var i = 0, ii = this.operations_.length; i < ii; ++i) { pixels = this.operations_[i](pixels); } @@ -263,6 +278,20 @@ ol.source.Raster.prototype.runOperations_ = function(pixels) { }; +/** + * Run image operations. + * @param {Array.} imageDatas The input image data. + * @return {Array.} The output image data. + * @private + */ +ol.source.Raster.prototype.runImageOperations_ = function(imageDatas) { + for (var i = 0, ii = this.operations_.length; i < ii; ++i) { + imageDatas = this.operations_[i](imageDatas); + } + return imageDatas; +}; + + /** * Get image data from a renderer. * @param {ol.renderer.canvas.Layer} renderer Layer renderer.