Allow UI thread to be used

Where workers are not available, or if operations are trivial to run, the main UI thread can be used instead.  This also adds tests that run in real browsers.
This commit is contained in:
Tim Schaub
2015-07-21 17:10:44 -06:00
parent af3c38052e
commit 0c486c522a
4 changed files with 404 additions and 14 deletions

View File

@@ -3,7 +3,9 @@ goog.provide('ol.source.RasterEvent');
goog.provide('ol.source.RasterEventType');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.functions');
goog.require('goog.object');
goog.require('goog.vec.Mat4');
@@ -47,11 +49,17 @@ ol.source.Raster = function(options) {
var operations = goog.isDef(options.operations) ?
options.operations : [ol.raster.IdentityOp];
/**
* @private
* @type {number}
*/
this.threads_ = goog.isDef(options.threads) ? options.threads : 1;
/**
* @private
* @type {*}
*/
this.worker_ = this.createWorker_(operations);
this.worker_ = this.createWorker_(operations, options.lib, this.threads_);
/**
* @private
@@ -59,6 +67,11 @@ ol.source.Raster = function(options) {
*/
this.renderers_ = ol.source.Raster.createRenderers_(options.sources);
for (var r = 0, rr = this.renderers_.length; r < rr; ++r) {
goog.events.listen(this.renderers_[r], goog.events.EventType.CHANGE,
this.changed, false, this);
}
/**
* @private
* @type {CanvasRenderingContext2D}
@@ -134,14 +147,19 @@ goog.inherits(ol.source.Raster, ol.source.Image);
/**
* Create a worker.
* @param {Array.<ol.raster.Operation>} operations The operations.
* @param {Object=} opt_lib Optional lib functions.
* @param {number=} opt_threads Number of threads.
* @return {*} The worker.
* @private
*/
ol.source.Raster.prototype.createWorker_ = function(operations) {
ol.source.Raster.prototype.createWorker_ =
function(operations, opt_lib, opt_threads) {
return new ol.ext.pixelworks.Processor({
operations: operations,
imageOps: this.operationType_ === ol.raster.OperationType.IMAGE,
queue: 1
queue: 1,
lib: opt_lib,
threads: opt_threads
});
};
@@ -149,10 +167,12 @@ ol.source.Raster.prototype.createWorker_ = function(operations) {
/**
* Reset the operations.
* @param {Array.<ol.raster.Operation>} operations New operations.
* @param {Object=} opt_lib Functions that will be available to operations run
* in a worker.
* @api
*/
ol.source.Raster.prototype.setOperations = function(operations) {
this.worker_ = this.createWorker_(operations);
ol.source.Raster.prototype.setOperations = function(operations, opt_lib) {
this.worker_ = this.createWorker_(operations, opt_lib, this.threads_);
this.changed();
};
@@ -281,10 +301,15 @@ ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) {
var len = this.renderers_.length;
var imageDatas = new Array(len);
for (var i = 0; i < len; ++i) {
imageDatas[i] = ol.source.Raster.getImageData_(
var imageData = ol.source.Raster.getImageData_(
this.renderers_[i], frameState, frameState.layerStatesArray[i]);
if (imageData) {
imageDatas[i] = imageData;
} else {
// image not yet ready
return;
}
}
frameState.tileQueue.loadMoreTiles(16, 16);
var data = {};
this.dispatchEvent(new ol.source.RasterEvent(
@@ -292,6 +317,8 @@ ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) {
this.worker_.process(imageDatas, data,
this.onWorkerComplete_.bind(this, frameState, callback));
frameState.tileQueue.loadMoreTiles(16, 16);
};
@@ -337,16 +364,50 @@ ol.source.Raster.prototype.onWorkerComplete_ =
*/
ol.source.Raster.getImageData_ = function(renderer, frameState, layerState) {
renderer.prepareFrame(frameState, layerState);
var canvas = renderer.getImage();
// We should be able to call renderer.composeFrame(), but this is inefficient
// for tiled sources (we've already rendered to an intermediate canvas in the
// prepareFrame call and we don't need to render again to the output canvas).
// TODO: make all canvas renderers render to a single canvas
var image = renderer.getImage();
if (!image) {
return null;
}
var imageTransform = renderer.getImageTransform();
var dx = goog.vec.Mat4.getElement(imageTransform, 0, 3);
var dy = goog.vec.Mat4.getElement(imageTransform, 1, 3);
return canvas.getContext('2d').getImageData(
Math.round(-dx), Math.round(-dy),
frameState.size[0], frameState.size[1]);
var dx = Math.round(goog.vec.Mat4.getElement(imageTransform, 0, 3));
var dy = Math.round(goog.vec.Mat4.getElement(imageTransform, 1, 3));
var width = frameState.size[0];
var height = frameState.size[1];
if (image instanceof Image) {
if (!ol.source.Raster.context_) {
ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height);
} else {
var canvas = ol.source.Raster.context_.canvas;
if (canvas.width !== width || canvas.height !== height) {
ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height);
} else {
ol.source.Raster.context_.clearRect(0, 0, width, height);
}
}
var dw = Math.round(
image.width * goog.vec.Mat4.getElement(imageTransform, 0, 0));
var dh = Math.round(
image.height * goog.vec.Mat4.getElement(imageTransform, 1, 1));
ol.source.Raster.context_.drawImage(image, dx, dy, dw, dh);
return ol.source.Raster.context_.getImageData(0, 0, width, height);
} else {
return image.getContext('2d').getImageData(-dx, -dy, width, height);
}
};
/**
* A reusable canvas context.
* @type {CanvasRenderingContext2D}
* @private
*/
ol.source.Raster.context_ = null;
/**
* Get a list of layer states from a list of renderers.
* @param {Array.<ol.renderer.canvas.Layer>} renderers Layer renderers.