Run operations in a worker
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "0.9.0",
|
||||
"browserify": "9.0.3",
|
||||
"closure-util": "1.5.0",
|
||||
"fs-extra": "0.12.0",
|
||||
"glob": "5.0.3",
|
||||
@@ -37,11 +38,11 @@
|
||||
"metalsmith": "1.6.0",
|
||||
"metalsmith-templates": "0.7.0",
|
||||
"nomnom": "1.8.0",
|
||||
"pixelworks": "^0.8.0",
|
||||
"rbush": "1.3.5",
|
||||
"temp": "0.8.1",
|
||||
"walk": "2.3.4",
|
||||
"wrench": "1.5.8",
|
||||
"browserify": "9.0.3"
|
||||
"wrench": "1.5.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"clean-css": "2.2.16",
|
||||
@@ -61,6 +62,7 @@
|
||||
"slimerjs-edge": "0.10.0-pre-2"
|
||||
},
|
||||
"ext": [
|
||||
"rbush"
|
||||
"rbush",
|
||||
{"module": "pixelworks", "browserify": true}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
goog.provide('ol.ImageCanvas');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('ol.ImageBase');
|
||||
goog.require('ol.ImageState');
|
||||
|
||||
@@ -13,12 +14,23 @@ goog.require('ol.ImageState');
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {Array.<ol.Attribution>} attributions Attributions.
|
||||
* @param {HTMLCanvasElement} canvas Canvas.
|
||||
* @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to
|
||||
* support asynchronous canvas drawing.
|
||||
*/
|
||||
ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
|
||||
canvas) {
|
||||
canvas, opt_loader) {
|
||||
|
||||
goog.base(this, extent, resolution, pixelRatio, ol.ImageState.LOADED,
|
||||
attributions);
|
||||
/**
|
||||
* Optional canvas loader function.
|
||||
* @type {?ol.ImageCanvasLoader}
|
||||
* @private
|
||||
*/
|
||||
this.loader_ = goog.isDef(opt_loader) ? opt_loader : null;
|
||||
|
||||
var state = goog.isDef(opt_loader) ?
|
||||
ol.ImageState.IDLE : ol.ImageState.LOADED;
|
||||
|
||||
goog.base(this, extent, resolution, pixelRatio, state, attributions);
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -26,13 +38,68 @@ ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
|
||||
*/
|
||||
this.canvas_ = canvas;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Error}
|
||||
*/
|
||||
this.error_ = null;
|
||||
|
||||
};
|
||||
goog.inherits(ol.ImageCanvas, ol.ImageBase);
|
||||
|
||||
|
||||
/**
|
||||
* Get any error associated with asynchronous rendering.
|
||||
* @return {Error} Any error that occurred during rendering.
|
||||
*/
|
||||
ol.ImageCanvas.prototype.getError = function() {
|
||||
return this.error_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle async drawing complete.
|
||||
* @param {Error} err Any error during drawing.
|
||||
* @private
|
||||
*/
|
||||
ol.ImageCanvas.prototype.handleLoad_ = function(err) {
|
||||
if (err) {
|
||||
this.error_ = err;
|
||||
this.state = ol.ImageState.ERROR;
|
||||
} else {
|
||||
this.state = ol.ImageState.LOADED;
|
||||
}
|
||||
this.changed();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Trigger drawing on canvas.
|
||||
*/
|
||||
ol.ImageCanvas.prototype.load = function() {
|
||||
if (this.state == ol.ImageState.IDLE) {
|
||||
goog.asserts.assert(!goog.isNull(this.loader_));
|
||||
this.state = ol.ImageState.LOADING;
|
||||
this.changed();
|
||||
this.loader_(goog.bind(this.handleLoad_, this));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.ImageCanvas.prototype.getImage = function(opt_context) {
|
||||
return this.canvas_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A function that is called to trigger asynchronous canvas drawing. It is
|
||||
* called with a "done" callback that should be called when drawing is done.
|
||||
* If any error occurs during drawing, the "done" callback should be called with
|
||||
* that error.
|
||||
*
|
||||
* @typedef {function(function(Error))}
|
||||
*/
|
||||
ol.ImageCanvasLoader;
|
||||
|
||||
@@ -5,10 +5,12 @@ goog.provide('ol.source.RasterEventType');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.functions');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.vec.Mat4');
|
||||
goog.require('ol.ImageCanvas');
|
||||
goog.require('ol.TileQueue');
|
||||
goog.require('ol.dom');
|
||||
goog.require('ol.ext.pixelworks');
|
||||
goog.require('ol.extent');
|
||||
goog.require('ol.layer.Image');
|
||||
goog.require('ol.layer.Tile');
|
||||
@@ -35,19 +37,14 @@ goog.require('ol.source.Tile');
|
||||
*/
|
||||
ol.source.Raster = function(options) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<ol.raster.Operation>}
|
||||
*/
|
||||
this.operations_ = goog.isDef(options.operations) ?
|
||||
var 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;
|
||||
this.worker_ = new ol.ext.pixelworks.Processor({
|
||||
operations: operations,
|
||||
imageOps: options.operationType === ol.raster.OperationType.IMAGE,
|
||||
queue: 1
|
||||
});
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -75,6 +72,20 @@ ol.source.Raster = function(options) {
|
||||
layerStates[goog.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* The most recently rendered state.
|
||||
* @type {?ol.source.Raster.RenderedState}
|
||||
* @private
|
||||
*/
|
||||
this.renderedState_ = null;
|
||||
|
||||
/**
|
||||
* The most recently rendered image canvas.
|
||||
* @type {ol.ImageCanvas}
|
||||
* @private
|
||||
*/
|
||||
this.renderedImageCanvas_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {olx.FrameState}
|
||||
@@ -119,7 +130,7 @@ goog.inherits(ol.source.Raster, ol.source.Image);
|
||||
* @api
|
||||
*/
|
||||
ol.source.Raster.prototype.setOperations = function(operations) {
|
||||
this.operations_ = operations;
|
||||
this.worker_.setOperations(operations);
|
||||
this.changed();
|
||||
};
|
||||
|
||||
@@ -134,7 +145,12 @@ ol.source.Raster.prototype.setOperations = function(operations) {
|
||||
*/
|
||||
ol.source.Raster.prototype.updateFrameState_ =
|
||||
function(extent, resolution, projection) {
|
||||
var frameState = this.frameState_;
|
||||
|
||||
var frameState = /** @type {olx.FrameState} */ (
|
||||
goog.object.clone(this.frameState_));
|
||||
|
||||
frameState.viewState = /** @type {olx.ViewState} */ (
|
||||
goog.object.clone(frameState.viewState));
|
||||
|
||||
var center = ol.extent.getCenter(extent);
|
||||
var width = Math.round(ol.extent.getWidth(extent) / resolution);
|
||||
@@ -153,12 +169,36 @@ ol.source.Raster.prototype.updateFrameState_ =
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
if (!this.isDirty_(extent, resolution)) {
|
||||
return this.renderedImageCanvas_;
|
||||
}
|
||||
|
||||
var context = this.canvasContext_;
|
||||
var canvas = context.canvas;
|
||||
|
||||
@@ -172,10 +212,18 @@ ol.source.Raster.prototype.getImage =
|
||||
}
|
||||
|
||||
var frameState = this.updateFrameState_(extent, resolution, projection);
|
||||
this.composeFrame_(frameState);
|
||||
|
||||
var imageCanvas = new ol.ImageCanvas(extent, resolution, 1,
|
||||
this.getAttributions(), canvas);
|
||||
var imageCanvas = new ol.ImageCanvas(
|
||||
extent, resolution, 1, this.getAttributions(), canvas,
|
||||
this.composeFrame_.bind(this, frameState));
|
||||
|
||||
this.renderedImageCanvas_ = imageCanvas;
|
||||
|
||||
this.renderedState_ = {
|
||||
extent: extent,
|
||||
resolution: resolution,
|
||||
revision: this.getRevision()
|
||||
};
|
||||
|
||||
return imageCanvas;
|
||||
};
|
||||
@@ -204,93 +252,53 @@ 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.
|
||||
* @private
|
||||
*/
|
||||
ol.source.Raster.prototype.composeFrame_ = function(frameState) {
|
||||
if (!this.allSourcesReady_()) {
|
||||
return;
|
||||
}
|
||||
ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) {
|
||||
var len = this.renderers_.length;
|
||||
var imageDatas = new Array(len);
|
||||
var pixels = new Array(len);
|
||||
|
||||
var context = this.canvasContext_;
|
||||
var canvas = context.canvas;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
pixels[i] = [0, 0, 0, 0];
|
||||
imageDatas[i] = ol.source.Raster.getImageData_(
|
||||
this.renderers_[i], frameState, frameState.layerStatesArray[i]);
|
||||
}
|
||||
frameState.tileQueue.loadMoreTiles(16, 16);
|
||||
|
||||
var data = {};
|
||||
this.dispatchEvent(new ol.source.RasterEvent(
|
||||
ol.source.RasterEventType.BEFOREOPERATIONS, frameState, data));
|
||||
|
||||
var targetImageData = null;
|
||||
if (this.operationType_ === ol.raster.OperationType.PIXEL) {
|
||||
targetImageData = context.getImageData(0, 0, canvas.width,
|
||||
canvas.height);
|
||||
var target = targetImageData.data;
|
||||
this.worker_.process(imageDatas, data,
|
||||
this.onWorkerComplete_.bind(this, frameState, data, callback));
|
||||
};
|
||||
|
||||
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, data)[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, data)[0];
|
||||
} else {
|
||||
goog.asserts.fail('unsupported operation type: ' + this.operationType_);
|
||||
|
||||
/**
|
||||
* Called when pixel processing is complete.
|
||||
* @param {olx.FrameState} frameState The frame state.
|
||||
* @param {Object} data The user data.
|
||||
* @param {function(Error)} callback Called when rendering is complete.
|
||||
* @param {Error} err Any error during processing.
|
||||
* @param {ImageData} output The output image data.
|
||||
* @private
|
||||
*/
|
||||
ol.source.Raster.prototype.onWorkerComplete_ =
|
||||
function(frameState, data, callback, err, output) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
if (goog.isNull(output)) {
|
||||
// job aborted
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new ol.source.RasterEvent(
|
||||
ol.source.RasterEventType.AFTEROPERATIONS, frameState, data));
|
||||
|
||||
context.putImageData(targetImageData, 0, 0);
|
||||
this.canvasContext_.putImageData(output, 0, 0);
|
||||
|
||||
frameState.tileQueue.loadMoreTiles(16, 16);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Run pixel-wise operations to transform pixels.
|
||||
* @param {Array.<ol.raster.Pixel>} pixels The input pixels.
|
||||
* @param {Object} data User storage.
|
||||
* @return {Array.<ol.raster.Pixel>} The modified pixels.
|
||||
* @private
|
||||
*/
|
||||
ol.source.Raster.prototype.runPixelOperations_ = function(pixels, data) {
|
||||
for (var i = 0, ii = this.operations_.length; i < ii; ++i) {
|
||||
pixels = this.operations_[i](pixels, data);
|
||||
}
|
||||
return pixels;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Run image operations.
|
||||
* @param {Array.<ImageData>} imageDatas The input image data.
|
||||
* @param {Object} data User storage.
|
||||
* @return {Array.<ImageData>} The output image data.
|
||||
* @private
|
||||
*/
|
||||
ol.source.Raster.prototype.runImageOperations_ = function(imageDatas, data) {
|
||||
for (var i = 0, ii = this.operations_.length; i < ii; ++i) {
|
||||
imageDatas = this.operations_[i](imageDatas, data);
|
||||
}
|
||||
return imageDatas;
|
||||
callback(null);
|
||||
};
|
||||
|
||||
|
||||
@@ -388,6 +396,14 @@ ol.source.Raster.createTileRenderer_ = function(source) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{revision: number,
|
||||
* resolution: number,
|
||||
* extent: ol.Extent}}
|
||||
*/
|
||||
ol.source.Raster.RenderedState;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
|
||||
Reference in New Issue
Block a user