[8966] | 1 | /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
|
---|
| 2 | * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
|
---|
| 3 | * full text of the license. */
|
---|
| 4 |
|
---|
| 5 | /**
|
---|
| 6 | * CanvasBarrier is a helper class, to execute an operation
|
---|
| 7 | * on a canvas' pixel data in several web workers. The canvas
|
---|
| 8 | * is cut into rows and each web worker independently processes
|
---|
| 9 | * its part. When all web worker are finished, the method
|
---|
| 10 | * 'callbackDone' is triggered.
|
---|
| 11 | *
|
---|
| 12 | * @param {Object} numberOfWorkers - How many web worker should be used?
|
---|
| 13 | * @param {Object} canvasContext - The canvas' drawing context.
|
---|
| 14 | * @param {Object} workerScript - Path to the script which contains the web worker code.
|
---|
| 15 | * @param {Object} callbackDone - Triggered when all web workers are finished
|
---|
| 16 | * @param {Object} callbackStatus - Optional, triggered on a status update.
|
---|
| 17 | * @param {Object} parameters - Optional, additional parameters that are passed to each worker.
|
---|
| 18 | */
|
---|
| 19 | var CanvasBarrier = function(numberOfWorkers, canvasContext, workerScript, callbackDone,
|
---|
| 20 | callbackStatus, parameters) {
|
---|
| 21 |
|
---|
| 22 | this.numberOfWorkers = numberOfWorkers;
|
---|
| 23 | this.callbackDone = callbackDone;
|
---|
| 24 | this.callbackStatus = callbackStatus;
|
---|
| 25 | this.canvasContext = canvasContext;
|
---|
| 26 | this.parameters = parameters;
|
---|
| 27 |
|
---|
| 28 | this.runningWorkers = 0;
|
---|
| 29 | this.error = null;
|
---|
| 30 |
|
---|
| 31 | /**
|
---|
| 32 | * Start the web workers.
|
---|
| 33 | */
|
---|
| 34 | this.start = function() {
|
---|
| 35 | var canvasWidth = this.canvasContext.canvas.width;
|
---|
| 36 | var canvasHeight = this.canvasContext.canvas.height;
|
---|
| 37 |
|
---|
| 38 | this.runningWorkers = this.numberOfWorkers;
|
---|
| 39 | this.progress = [];
|
---|
| 40 | this.lastProgress = 0;
|
---|
| 41 |
|
---|
| 42 | // create a web worker for each row
|
---|
| 43 | for (var i = 0; i < this.numberOfWorkers; i++) {
|
---|
| 44 | this.progress.push(0);
|
---|
| 45 |
|
---|
| 46 | // get the position of this row
|
---|
| 47 | var x = 0;
|
---|
| 48 | var y = this.getRowPositionY(i, canvasHeight);
|
---|
| 49 | var width = canvasWidth;
|
---|
| 50 | var height = this.getRowPositionY(i+1, canvasHeight) - y;
|
---|
| 51 |
|
---|
| 52 | // get the pixel data for this row
|
---|
| 53 | var imageData = this.canvasContext.getImageData(x, y, width, height);
|
---|
| 54 |
|
---|
| 55 | // now we could use the keyword 'let' for not having to do this cascaded closures:
|
---|
| 56 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Closures#Creating_closures_in_loops.3a_A_common_mistake
|
---|
| 57 | var onMessage = this.getOnMessageCallback(i, this.canvasContext, x, y, this);
|
---|
| 58 | var onError = this.getOnErrorCallback(this);
|
---|
| 59 |
|
---|
| 60 | // start a web worker
|
---|
| 61 | var worker = new Worker(workerScript);
|
---|
| 62 | worker.onmessage = onMessage;
|
---|
| 63 | worker.onerror = onError;
|
---|
| 64 |
|
---|
| 65 | var task = {
|
---|
| 66 | parameters: this.parameters,
|
---|
| 67 | imageData: imageData
|
---|
| 68 | }
|
---|
| 69 |
|
---|
| 70 | worker.postMessage(task);
|
---|
| 71 | }
|
---|
| 72 | };
|
---|
| 73 |
|
---|
| 74 | /**
|
---|
| 75 | * Returns the y-position for the given row.
|
---|
| 76 | *
|
---|
| 77 | * @param {Object} i - Row index
|
---|
| 78 | * @param {Object} height - The canvas' height
|
---|
| 79 | * @return {Integer} - Y-Position
|
---|
| 80 | */
|
---|
| 81 | this.getRowPositionY = function(i,height) {
|
---|
| 82 | return Math.floor(height / this.numberOfWorkers * i);
|
---|
| 83 | };
|
---|
| 84 |
|
---|
| 85 | /**
|
---|
| 86 | * Returns a callback function that is used as
|
---|
| 87 | * onmessage handler for the web worker.
|
---|
| 88 | *
|
---|
| 89 | * @param {Object} index - Row index
|
---|
| 90 | * @param {Object} canvasContext - Canvas context
|
---|
| 91 | * @param {Object} x - X-Position of the row
|
---|
| 92 | * @param {Object} y - Y-Position of the row
|
---|
| 93 | * @param {Object} barrier
|
---|
| 94 | */
|
---|
| 95 | this.getOnMessageCallback = function(index, canvasContext, x, y, barrier) {
|
---|
| 96 | var context = {
|
---|
| 97 | index: index,
|
---|
| 98 | canvasContext: canvasContext,
|
---|
| 99 | x: x,
|
---|
| 100 | y: y,
|
---|
| 101 | barrier: barrier
|
---|
| 102 | };
|
---|
| 103 |
|
---|
| 104 | return function(event) {
|
---|
| 105 | if (event.data.status === "progress" && context.barrier.callbackStatus) {
|
---|
| 106 | context.barrier.reportProgress(context.index, event.data.progress);
|
---|
| 107 | } else if (event.data.status === "done") {
|
---|
| 108 | var imageData = event.data.imageData;
|
---|
| 109 | // directly write the row on the canvas
|
---|
| 110 | context.canvasContext.putImageData(imageData, context.x, context.y);
|
---|
| 111 | context.barrier.checkRunningWorkers();
|
---|
| 112 | }
|
---|
| 113 | };
|
---|
| 114 | };
|
---|
| 115 |
|
---|
| 116 | /**
|
---|
| 117 | * Returns a callback function that is used as
|
---|
| 118 | * onerror handler for the web worker.
|
---|
| 119 | *
|
---|
| 120 | * @param {Object} barrier
|
---|
| 121 | */
|
---|
| 122 | this.getOnErrorCallback = function(barrier) {
|
---|
| 123 | return function(error) {
|
---|
| 124 | barrier.error = error;
|
---|
| 125 | barrier.checkRunningWorkers();
|
---|
| 126 | };
|
---|
| 127 | };
|
---|
| 128 |
|
---|
| 129 | /**
|
---|
| 130 | * Called from the onmessage and onerror callback. When
|
---|
| 131 | * all web workers are finished, 'callbackDone' is triggered.
|
---|
| 132 | */
|
---|
| 133 | this.checkRunningWorkers = function() {
|
---|
| 134 | this.runningWorkers--;
|
---|
| 135 |
|
---|
| 136 | if (this.runningWorkers === 0) {
|
---|
| 137 | // all workers are finished
|
---|
| 138 | this.callbackDone({
|
---|
| 139 | canvas: this.canvas,
|
---|
| 140 | error: this.error
|
---|
| 141 | });
|
---|
| 142 | }
|
---|
| 143 | };
|
---|
| 144 |
|
---|
| 145 | /**
|
---|
| 146 | * Each web worker individually reports its progress. This method
|
---|
| 147 | * calculates the overall progress and calls 'callbackStatus'.
|
---|
| 148 | *
|
---|
| 149 | * @param {Object} index - The row index.
|
---|
| 150 | * @param {Object} progress - The progress for this row.
|
---|
| 151 | */
|
---|
| 152 | this.reportProgress = function(index, progress) {
|
---|
| 153 | // update the progress for this worker
|
---|
| 154 | this.progress[index] = progress;
|
---|
| 155 |
|
---|
| 156 | // then calculate the overall progress of all workers
|
---|
| 157 | var sum = 0;
|
---|
| 158 | for (var i = 0; i < this.progress.length; i++) {
|
---|
| 159 | sum += this.progress[i];
|
---|
| 160 | }
|
---|
| 161 | var overallProgress = Math.round(sum / (this.numberOfWorkers * 100) * 100);
|
---|
| 162 |
|
---|
| 163 | if (overallProgress > this.lastProgress) {
|
---|
| 164 | this.lastProgress = overallProgress;
|
---|
| 165 | this.callbackStatus(overallProgress);
|
---|
| 166 | }
|
---|
| 167 | };
|
---|
| 168 | };
|
---|