Run operations in a worker
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "0.9.0",
|
"async": "0.9.0",
|
||||||
|
"browserify": "9.0.3",
|
||||||
"closure-util": "1.5.0",
|
"closure-util": "1.5.0",
|
||||||
"fs-extra": "0.12.0",
|
"fs-extra": "0.12.0",
|
||||||
"glob": "5.0.3",
|
"glob": "5.0.3",
|
||||||
@@ -37,11 +38,11 @@
|
|||||||
"metalsmith": "1.6.0",
|
"metalsmith": "1.6.0",
|
||||||
"metalsmith-templates": "0.7.0",
|
"metalsmith-templates": "0.7.0",
|
||||||
"nomnom": "1.8.0",
|
"nomnom": "1.8.0",
|
||||||
|
"pixelworks": "^0.8.0",
|
||||||
"rbush": "1.3.5",
|
"rbush": "1.3.5",
|
||||||
"temp": "0.8.1",
|
"temp": "0.8.1",
|
||||||
"walk": "2.3.4",
|
"walk": "2.3.4",
|
||||||
"wrench": "1.5.8",
|
"wrench": "1.5.8"
|
||||||
"browserify": "9.0.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"clean-css": "2.2.16",
|
"clean-css": "2.2.16",
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
"slimerjs-edge": "0.10.0-pre-2"
|
"slimerjs-edge": "0.10.0-pre-2"
|
||||||
},
|
},
|
||||||
"ext": [
|
"ext": [
|
||||||
"rbush"
|
"rbush",
|
||||||
|
{"module": "pixelworks", "browserify": true}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
goog.provide('ol.ImageCanvas');
|
goog.provide('ol.ImageCanvas');
|
||||||
|
|
||||||
|
goog.require('goog.asserts');
|
||||||
goog.require('ol.ImageBase');
|
goog.require('ol.ImageBase');
|
||||||
goog.require('ol.ImageState');
|
goog.require('ol.ImageState');
|
||||||
|
|
||||||
@@ -13,12 +14,23 @@ goog.require('ol.ImageState');
|
|||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
* @param {Array.<ol.Attribution>} attributions Attributions.
|
* @param {Array.<ol.Attribution>} attributions Attributions.
|
||||||
* @param {HTMLCanvasElement} canvas Canvas.
|
* @param {HTMLCanvasElement} canvas Canvas.
|
||||||
|
* @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to
|
||||||
|
* support asynchronous canvas drawing.
|
||||||
*/
|
*/
|
||||||
ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
|
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
|
* @private
|
||||||
@@ -26,13 +38,68 @@ ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
|
|||||||
*/
|
*/
|
||||||
this.canvas_ = canvas;
|
this.canvas_ = canvas;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Error}
|
||||||
|
*/
|
||||||
|
this.error_ = null;
|
||||||
|
|
||||||
};
|
};
|
||||||
goog.inherits(ol.ImageCanvas, ol.ImageBase);
|
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
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.ImageCanvas.prototype.getImage = function(opt_context) {
|
ol.ImageCanvas.prototype.getImage = function(opt_context) {
|
||||||
return this.canvas_;
|
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.asserts');
|
||||||
goog.require('goog.events.Event');
|
goog.require('goog.events.Event');
|
||||||
goog.require('goog.functions');
|
goog.require('goog.functions');
|
||||||
|
goog.require('goog.object');
|
||||||
goog.require('goog.vec.Mat4');
|
goog.require('goog.vec.Mat4');
|
||||||
goog.require('ol.ImageCanvas');
|
goog.require('ol.ImageCanvas');
|
||||||
goog.require('ol.TileQueue');
|
goog.require('ol.TileQueue');
|
||||||
goog.require('ol.dom');
|
goog.require('ol.dom');
|
||||||
|
goog.require('ol.ext.pixelworks');
|
||||||
goog.require('ol.extent');
|
goog.require('ol.extent');
|
||||||
goog.require('ol.layer.Image');
|
goog.require('ol.layer.Image');
|
||||||
goog.require('ol.layer.Tile');
|
goog.require('ol.layer.Tile');
|
||||||
@@ -35,19 +37,14 @@ goog.require('ol.source.Tile');
|
|||||||
*/
|
*/
|
||||||
ol.source.Raster = function(options) {
|
ol.source.Raster = function(options) {
|
||||||
|
|
||||||
/**
|
var operations = goog.isDef(options.operations) ?
|
||||||
* @private
|
|
||||||
* @type {Array.<ol.raster.Operation>}
|
|
||||||
*/
|
|
||||||
this.operations_ = goog.isDef(options.operations) ?
|
|
||||||
options.operations : [ol.raster.IdentityOp];
|
options.operations : [ol.raster.IdentityOp];
|
||||||
|
|
||||||
/**
|
this.worker_ = new ol.ext.pixelworks.Processor({
|
||||||
* @private
|
operations: operations,
|
||||||
* @type {ol.raster.OperationType}
|
imageOps: options.operationType === ol.raster.OperationType.IMAGE,
|
||||||
*/
|
queue: 1
|
||||||
this.operationType_ = goog.isDef(options.operationType) ?
|
});
|
||||||
options.operationType : ol.raster.OperationType.PIXEL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -75,6 +72,20 @@ ol.source.Raster = function(options) {
|
|||||||
layerStates[goog.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
|
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
|
* @private
|
||||||
* @type {olx.FrameState}
|
* @type {olx.FrameState}
|
||||||
@@ -119,7 +130,7 @@ goog.inherits(ol.source.Raster, ol.source.Image);
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
ol.source.Raster.prototype.setOperations = function(operations) {
|
ol.source.Raster.prototype.setOperations = function(operations) {
|
||||||
this.operations_ = operations;
|
this.worker_.setOperations(operations);
|
||||||
this.changed();
|
this.changed();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,7 +145,12 @@ ol.source.Raster.prototype.setOperations = function(operations) {
|
|||||||
*/
|
*/
|
||||||
ol.source.Raster.prototype.updateFrameState_ =
|
ol.source.Raster.prototype.updateFrameState_ =
|
||||||
function(extent, resolution, projection) {
|
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 center = ol.extent.getCenter(extent);
|
||||||
var width = Math.round(ol.extent.getWidth(extent) / resolution);
|
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
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.source.Raster.prototype.getImage =
|
ol.source.Raster.prototype.getImage =
|
||||||
function(extent, resolution, pixelRatio, projection) {
|
function(extent, resolution, pixelRatio, projection) {
|
||||||
|
|
||||||
|
if (!this.allSourcesReady_()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isDirty_(extent, resolution)) {
|
||||||
|
return this.renderedImageCanvas_;
|
||||||
|
}
|
||||||
|
|
||||||
var context = this.canvasContext_;
|
var context = this.canvasContext_;
|
||||||
var canvas = context.canvas;
|
var canvas = context.canvas;
|
||||||
|
|
||||||
@@ -172,10 +212,18 @@ ol.source.Raster.prototype.getImage =
|
|||||||
}
|
}
|
||||||
|
|
||||||
var frameState = this.updateFrameState_(extent, resolution, projection);
|
var frameState = this.updateFrameState_(extent, resolution, projection);
|
||||||
this.composeFrame_(frameState);
|
|
||||||
|
|
||||||
var imageCanvas = new ol.ImageCanvas(extent, resolution, 1,
|
var imageCanvas = new ol.ImageCanvas(
|
||||||
this.getAttributions(), canvas);
|
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;
|
return imageCanvas;
|
||||||
};
|
};
|
||||||
@@ -204,93 +252,53 @@ ol.source.Raster.prototype.allSourcesReady_ = function() {
|
|||||||
* Compose the frame. This renders data from all sources, runs pixel-wise
|
* Compose the frame. This renders data from all sources, runs pixel-wise
|
||||||
* operations, and renders the result to the stored canvas context.
|
* operations, and renders the result to the stored canvas context.
|
||||||
* @param {olx.FrameState} frameState The frame state.
|
* @param {olx.FrameState} frameState The frame state.
|
||||||
|
* @param {function(Error)} callback Called when composition is complete.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
ol.source.Raster.prototype.composeFrame_ = function(frameState) {
|
ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) {
|
||||||
if (!this.allSourcesReady_()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var len = this.renderers_.length;
|
var len = this.renderers_.length;
|
||||||
var imageDatas = new Array(len);
|
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) {
|
for (var i = 0; i < len; ++i) {
|
||||||
pixels[i] = [0, 0, 0, 0];
|
|
||||||
imageDatas[i] = ol.source.Raster.getImageData_(
|
imageDatas[i] = ol.source.Raster.getImageData_(
|
||||||
this.renderers_[i], frameState, frameState.layerStatesArray[i]);
|
this.renderers_[i], frameState, frameState.layerStatesArray[i]);
|
||||||
}
|
}
|
||||||
|
frameState.tileQueue.loadMoreTiles(16, 16);
|
||||||
|
|
||||||
var data = {};
|
var data = {};
|
||||||
this.dispatchEvent(new ol.source.RasterEvent(
|
this.dispatchEvent(new ol.source.RasterEvent(
|
||||||
ol.source.RasterEventType.BEFOREOPERATIONS, frameState, data));
|
ol.source.RasterEventType.BEFOREOPERATIONS, frameState, data));
|
||||||
|
|
||||||
var targetImageData = null;
|
this.worker_.process(imageDatas, data,
|
||||||
if (this.operationType_ === ol.raster.OperationType.PIXEL) {
|
this.onWorkerComplete_.bind(this, frameState, data, callback));
|
||||||
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) {
|
* Called when pixel processing is complete.
|
||||||
source = imageDatas[k].data;
|
* @param {olx.FrameState} frameState The frame state.
|
||||||
pixel = pixels[k];
|
* @param {Object} data The user data.
|
||||||
pixel[0] = source[j];
|
* @param {function(Error)} callback Called when rendering is complete.
|
||||||
pixel[1] = source[j + 1];
|
* @param {Error} err Any error during processing.
|
||||||
pixel[2] = source[j + 2];
|
* @param {ImageData} output The output image data.
|
||||||
pixel[3] = source[j + 3];
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.prototype.onWorkerComplete_ =
|
||||||
|
function(frameState, data, callback, err, output) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
pixel = this.runPixelOperations_(pixels, data)[0];
|
if (goog.isNull(output)) {
|
||||||
target[j] = pixel[0];
|
// job aborted
|
||||||
target[j + 1] = pixel[1];
|
return;
|
||||||
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_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchEvent(new ol.source.RasterEvent(
|
this.dispatchEvent(new ol.source.RasterEvent(
|
||||||
ol.source.RasterEventType.AFTEROPERATIONS, frameState, data));
|
ol.source.RasterEventType.AFTEROPERATIONS, frameState, data));
|
||||||
|
|
||||||
context.putImageData(targetImageData, 0, 0);
|
this.canvasContext_.putImageData(output, 0, 0);
|
||||||
|
|
||||||
frameState.tileQueue.loadMoreTiles(16, 16);
|
callback(null);
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -388,6 +396,14 @@ ol.source.Raster.createTileRenderer_ = function(source) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{revision: number,
|
||||||
|
* resolution: number,
|
||||||
|
* extent: ol.Extent}}
|
||||||
|
*/
|
||||||
|
ol.source.Raster.RenderedState;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
|
|||||||
Reference in New Issue
Block a user