OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * Command queue is the only way to modify images. | 6 * Command queue is the only way to modify images. |
7 * Supports undo/redo. | 7 * Supports undo/redo. |
8 * Command execution is asynchronous (callback-based). | 8 * Command execution is asynchronous (callback-based). |
9 * | 9 * |
10 * @param {Document} document Document to create canvases in. | 10 * @param {Document} document Document to create canvases in. |
11 * @param {HTMLCanvasElement} canvas The canvas with the original image. | 11 * @param {HTMLCanvasElement} canvas The canvas with the original image. |
12 * @param {function(callback)} saveFunction Function to save the image. | 12 * @param {function(callback)} saveFunction Function to save the image. |
13 * @constructor | 13 * @constructor |
14 */ | 14 */ |
15 function CommandQueue(document, canvas, saveFunction) { | 15 function CommandQueue(document, canvas, saveFunction) { |
16 this.document_ = document; | 16 this.document_ = document; |
17 this.undo_ = []; | 17 this.undo_ = []; |
18 this.redo_ = []; | 18 this.redo_ = []; |
19 this.subscribers_ = []; | 19 this.subscribers_ = []; |
20 | 20 |
21 this.baselineImage_ = canvas; | |
22 this.currentImage_ = canvas; | 21 this.currentImage_ = canvas; |
| 22 |
| 23 this.baselineImage_ = document.createElement('canvas'); |
| 24 this.baselineImage_.width = this.currentImage_.width; |
| 25 this.baselineImage_.height = this.currentImage_.height; |
| 26 var context = this.baselineImage_.getContext('2d'); |
| 27 context.drawImage(this.currentImage_, 0, 0); |
| 28 |
23 this.previousImage_ = document.createElement('canvas'); | 29 this.previousImage_ = document.createElement('canvas'); |
| 30 this.previousImageAvailable_ = false; |
24 | 31 |
25 this.saveFunction_ = saveFunction; | 32 this.saveFunction_ = saveFunction; |
26 | 33 |
27 this.busy_ = false; | 34 this.busy_ = false; |
28 | 35 |
29 this.UIContext_ = {}; | 36 this.UIContext_ = {}; |
30 } | 37 } |
31 | 38 |
32 /** | 39 /** |
33 * Attach the UI elements to the command queue. | 40 * Attach the UI elements to the command queue. |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 * @param {function} callback Completion callback. | 122 * @param {function} callback Completion callback. |
116 * @private | 123 * @private |
117 */ | 124 */ |
118 CommandQueue.prototype.doExecute_ = function(command, uiContext, callback) { | 125 CommandQueue.prototype.doExecute_ = function(command, uiContext, callback) { |
119 if (!this.currentImage_) | 126 if (!this.currentImage_) |
120 throw new Error('Cannot operate on null image'); | 127 throw new Error('Cannot operate on null image'); |
121 | 128 |
122 // Remember one previous image so that the first undo is as fast as possible. | 129 // Remember one previous image so that the first undo is as fast as possible. |
123 this.previousImage_.width = this.currentImage_.width; | 130 this.previousImage_.width = this.currentImage_.width; |
124 this.previousImage_.height = this.currentImage_.height; | 131 this.previousImage_.height = this.currentImage_.height; |
| 132 this.previousImageAvailable_ = true; |
125 var context = this.previousImage_.getContext('2d'); | 133 var context = this.previousImage_.getContext('2d'); |
126 context.drawImage(this.currentImage_, 0, 0); | 134 context.drawImage(this.currentImage_, 0, 0); |
127 | 135 |
128 command.execute( | 136 command.execute( |
129 this.document_, | 137 this.document_, |
130 this.currentImage_, | 138 this.currentImage_, |
131 function(result, opt_delay) { | 139 function(result, opt_delay) { |
132 this.currentImage_ = result; | 140 this.currentImage_ = result; |
133 callback(opt_delay); | 141 callback(opt_delay); |
134 }.bind(this), | 142 }.bind(this), |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
172 this.redo_.push(command); | 180 this.redo_.push(command); |
173 | 181 |
174 var self = this; | 182 var self = this; |
175 | 183 |
176 function complete() { | 184 function complete() { |
177 var delay = command.revertView( | 185 var delay = command.revertView( |
178 self.currentImage_, self.UIContext_.imageView); | 186 self.currentImage_, self.UIContext_.imageView); |
179 self.commit_(delay); | 187 self.commit_(delay); |
180 } | 188 } |
181 | 189 |
182 if (this.previousImage_) { | 190 if (this.previousImageAvailable_) { |
183 // First undo after an execute call. | 191 // First undo after an execute call. |
184 this.currentImage_.width = this.previousImage_.width; | 192 this.currentImage_.width = this.previousImage_.width; |
185 this.currentImage_.height = this.previousImage_.height; | 193 this.currentImage_.height = this.previousImage_.height; |
186 var context = this.currentImage_.getContext('2d'); | 194 var context = this.currentImage_.getContext('2d'); |
187 context.drawImage(this.previousImage_, 0, 0); | 195 context.drawImage(this.previousImage_, 0, 0); |
188 | 196 |
189 // Free memory. | 197 // Free memory. |
190 this.previousImage_.width = 0; | 198 this.previousImage_.width = 0; |
191 this.previousImage_.height = 0; | 199 this.previousImage_.height = 0; |
| 200 this.previousImageAvailable_ = false; |
192 | 201 |
193 complete(); | 202 complete(); |
194 // TODO(kaznacheev) Consider recalculating previousImage_ right here | 203 // TODO(kaznacheev) Consider recalculating previousImage_ right here |
195 // by replaying the commands in the background. | 204 // by replaying the commands in the background. |
196 } else { | 205 } else { |
197 this.currentImage_ = this.baselineImage_; | 206 this.currentImage_.width = this.baselineImage_.width; |
| 207 this.currentImage_.height = this.baselineImage_.height; |
| 208 var context = this.currentImage_.getContext('2d'); |
| 209 context.drawImage(this.baselineImage_, 0, 0); |
198 | 210 |
199 var replay = function(index) { | 211 var replay = function(index) { |
200 if (index < self.undo_.length) | 212 if (index < self.undo_.length) |
201 self.doExecute_(self.undo_[index], {}, replay.bind(null, index + 1)); | 213 self.doExecute_(self.undo_[index], {}, replay.bind(null, index + 1)); |
202 else { | 214 else { |
203 complete(); | 215 complete(); |
204 } | 216 } |
205 }; | 217 }; |
206 | 218 |
207 replay(0); | 219 replay(0); |
(...skipping 18 matching lines...) Expand all Loading... |
226 }; | 238 }; |
227 | 239 |
228 /** | 240 /** |
229 * Closes internal buffers. Call to ensure, that internal buffers are freed | 241 * Closes internal buffers. Call to ensure, that internal buffers are freed |
230 * as soon as possible. | 242 * as soon as possible. |
231 */ | 243 */ |
232 CommandQueue.prototype.close = function() { | 244 CommandQueue.prototype.close = function() { |
233 // Free memory used by the undo buffer. | 245 // Free memory used by the undo buffer. |
234 this.previousImage_.width = 0; | 246 this.previousImage_.width = 0; |
235 this.previousImage_.height = 0; | 247 this.previousImage_.height = 0; |
| 248 this.previousImageAvailable_ = false; |
| 249 |
| 250 this.baselineImage_.width = 0; |
| 251 this.baselineImage_.height = 0; |
236 }; | 252 }; |
237 | 253 |
238 /** | 254 /** |
239 * Command object encapsulates an operation on an image and a way to visualize | 255 * Command object encapsulates an operation on an image and a way to visualize |
240 * its result. | 256 * its result. |
241 * | 257 * |
242 * @param {string} name Command name. | 258 * @param {string} name Command name. |
243 * @constructor | 259 * @constructor |
244 */ | 260 */ |
245 function Command(name) { | 261 function Command(name) { |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
421 | 437 |
422 function onProgressInvisible(updatedRow, rowCount) { | 438 function onProgressInvisible(updatedRow, rowCount) { |
423 if (updatedRow == rowCount) { | 439 if (updatedRow == rowCount) { |
424 callback(result); | 440 callback(result); |
425 } | 441 } |
426 } | 442 } |
427 | 443 |
428 filter.applyByStrips(result, srcCanvas, this.filter_, | 444 filter.applyByStrips(result, srcCanvas, this.filter_, |
429 uiContext.imageView ? onProgressVisible : onProgressInvisible); | 445 uiContext.imageView ? onProgressVisible : onProgressInvisible); |
430 }; | 446 }; |
OLD | NEW |