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 | };
|
---|