15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'use strict'; 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Command queue is the only way to modify images. 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Supports undo/redo. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Command execution is asynchronous (callback-based). 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Document} document Document to create canvases in. 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} canvas The canvas with the original image. 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(callback)} saveFunction Function to save the image. 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @constructor 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function CommandQueue(document, canvas, saveFunction) { 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.document_ = document; 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.undo_ = []; 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.redo_ = []; 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.subscribers_ = []; 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.currentImage_ = canvas; 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Current image may be null or not-null but with width = height = 0. 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Copying an image with zero dimensions causes js errors. 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (this.currentImage_) { 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.baselineImage_ = document.createElement('canvas'); 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.baselineImage_.width = this.currentImage_.width; 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.baselineImage_.height = this.currentImage_.height; 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (this.currentImage_.width > 0 && this.currentImage_.height > 0) { 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var context = this.baselineImage_.getContext('2d'); 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) context.drawImage(this.currentImage_, 0, 0); 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else { 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.baselineImage_ = null; 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImage_ = document.createElement('canvas'); 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImageAvailable_ = false; 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.saveFunction_ = saveFunction; 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.busy_ = false; 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.UIContext_ = {}; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Attach the UI elements to the command queue. 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Once the UI is attached the results of image manipulations are displayed. 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageView} imageView The ImageView object to display the results. 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageEditor.Prompt} prompt Prompt to use with this CommandQueue. 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(boolean)} lock Function to enable/disable buttons etc. 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.attachUI = function(imageView, prompt, lock) { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.UIContext_ = { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) imageView: imageView, 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prompt: prompt, 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lock: lock 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Execute the action when the queue is not busy. 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} callback Callback. 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.executeWhenReady = function(callback) { 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.isBusy()) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.subscribers_.push(callback); 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) setTimeout(callback, 0); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the command queue is busy. 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.isBusy = function() { return this.busy_ }; 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Set the queue state to busy. Lock the UI. 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.setBusy_ = function() { 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.busy_) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new Error('CommandQueue already busy'); 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.busy_ = true; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.UIContext_.lock) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.UIContext_.lock(true); 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ImageUtil.trace.resetTimer('command-busy'); 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Set the queue state to not busy. Unlock the UI and execute pending actions. 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.clearBusy_ = function() { 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.busy_) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new Error('Inconsistent CommandQueue already not busy'); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.busy_ = false; 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Execute the actions requested while the queue was busy. 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (this.subscribers_.length) 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.subscribers_.shift()(); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.UIContext_.lock) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.UIContext_.lock(false); 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ImageUtil.trace.reportTimer('command-busy'); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Commit the image change: save and unlock the UI. 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_delay Delay in ms (to avoid disrupting the animation). 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.commit_ = function(opt_delay) { 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) setTimeout(this.saveFunction_.bind(null, this.clearBusy_.bind(this)), 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opt_delay || 0); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Internal function to execute the command in a given context. 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Command} command The command to execute. 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} uiContext The UI context. 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} callback Completion callback. 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.doExecute_ = function(command, uiContext, callback) { 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.currentImage_) 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new Error('Cannot operate on null image'); 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Remember one previous image so that the first undo is as fast as possible. 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImage_.width = this.currentImage_.width; 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImage_.height = this.currentImage_.height; 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImageAvailable_ = true; 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var context = this.previousImage_.getContext('2d'); 1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) context.drawImage(this.currentImage_, 0, 0); 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) command.execute( 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.document_, 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.currentImage_, 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function(result, opt_delay) { 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.currentImage_ = result; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) callback(opt_delay); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }.bind(this), 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uiContext); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Executes the command. 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Command} command Command to execute. 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_keep_redo True if redo stack should not be cleared. 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.execute = function(command, opt_keep_redo) { 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.setBusy_(); 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!opt_keep_redo) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.redo_ = []; 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.undo_.push(command); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.doExecute_(command, this.UIContext_, this.commit_.bind(this)); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if Undo is applicable. 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.canUndo = function() { 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.undo_.length != 0; 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Undo the most recent command. 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.undo = function() { 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.canUndo()) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new Error('Cannot undo'); 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.setBusy_(); 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var command = this.undo_.pop(); 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.redo_.push(command); 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var self = this; 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function complete() { 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var delay = command.revertView( 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.currentImage_, self.UIContext_.imageView); 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.commit_(delay); 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (this.previousImageAvailable_) { 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // First undo after an execute call. 1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.currentImage_.width = this.previousImage_.width; 2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.currentImage_.height = this.previousImage_.height; 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var context = this.currentImage_.getContext('2d'); 2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) context.drawImage(this.previousImage_, 0, 0); 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Free memory. 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImage_.width = 0; 2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImage_.height = 0; 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImageAvailable_ = false; 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) complete(); 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(kaznacheev) Consider recalculating previousImage_ right here 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // by replaying the commands in the background. 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.currentImage_.width = this.baselineImage_.width; 2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.currentImage_.height = this.baselineImage_.height; 2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var context = this.currentImage_.getContext('2d'); 2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) context.drawImage(this.baselineImage_, 0, 0); 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var replay = function(index) { 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (index < self.undo_.length) 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.doExecute_(self.undo_[index], {}, replay.bind(null, index + 1)); 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else { 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) complete(); 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }; 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) replay(0); 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if Redo is applicable. 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.canRedo = function() { 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.redo_.length != 0; 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Repeat the command that was recently un-done. 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandQueue.prototype.redo = function() { 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.canRedo()) 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new Error('Cannot redo'); 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.execute(this.redo_.pop(), true); 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Closes internal buffers. Call to ensure, that internal buffers are freed 2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * as soon as possible. 2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)CommandQueue.prototype.close = function() { 2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Free memory used by the undo buffer. 2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImage_.width = 0; 2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImage_.height = 0; 2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.previousImageAvailable_ = false; 2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (this.baselineImage_) { 2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.baselineImage_.width = 0; 2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.baselineImage_.height = 0; 2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Command object encapsulates an operation on an image and a way to visualize 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * its result. 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Command name. 2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @constructor 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function Command(name) { 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.name_ = name; 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} String representation of the command. 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.prototype.toString = function() { 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 'Command ' + this.name_; 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Execute the command and visualize its results. 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The two actions are combined into one method because sometimes it is nice 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to be able to show partial results for slower operations. 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Document} document Document on which to execute command. 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} srcCanvas Canvas to execute on. 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(HTMLCanvasElement, number)} callback Callback to call on 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * completion. 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} uiContext Context to work in. 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.prototype.execute = function(document, srcCanvas, callback, uiContext) { 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) console.error('Command.prototype.execute not implemented'); 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Visualize reversion of the operation. 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} canvas Image data to use. 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageView} imageView ImageView to revert. 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Animation duration in ms. 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.prototype.revertView = function(canvas, imageView) { 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) imageView.replace(canvas); 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Creates canvas to render on. 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Document} document Document to create canvas in. 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} srcCanvas to copy optional dimensions from. 3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_width new canvas width. 3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_height new canvas height. 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLCanvasElement} Newly created canvas. 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.prototype.createCanvas_ = function( 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document, srcCanvas, opt_width, opt_height) { 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var result = document.createElement('canvas'); 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.width = opt_width || srcCanvas.width; 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.height = opt_height || srcCanvas.height; 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Rotate command 3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number} rotate90 Rotation angle in 90 degree increments (signed). 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @extends {Command} 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Rotate = function(rotate90) { 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Command.call(this, 'rotate(' + rotate90 * 90 + 'deg)'); 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.rotate90_ = rotate90; 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Rotate.prototype = { __proto__: Command.prototype }; 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @override */ 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Rotate.prototype.execute = function( 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document, srcCanvas, callback, uiContext) { 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var result = this.createCanvas_( 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document, 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) srcCanvas, 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (this.rotate90_ & 1) ? srcCanvas.height : srcCanvas.width, 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (this.rotate90_ & 1) ? srcCanvas.width : srcCanvas.height); 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ImageUtil.drawImageTransformed( 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result, srcCanvas, 1, 1, this.rotate90_ * Math.PI / 2); 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var delay; 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (uiContext.imageView) { 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delay = uiContext.imageView.replaceAndAnimate(result, null, this.rotate90_); 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) setTimeout(callback, 0, result, delay); 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @override */ 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Rotate.prototype.revertView = function(canvas, imageView) { 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return imageView.replaceAndAnimate(canvas, null, -this.rotate90_); 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Crop command. 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {Rect} imageRect Crop rectangle in image coordinates. 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @extends {Command} 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Crop = function(imageRect) { 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Command.call(this, 'crop' + imageRect.toString()); 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.imageRect_ = imageRect; 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Crop.prototype = { __proto__: Command.prototype }; 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @override */ 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Crop.prototype.execute = function( 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document, srcCanvas, callback, uiContext) { 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var result = this.createCanvas_( 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document, srcCanvas, this.imageRect_.width, this.imageRect_.height); 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Rect.drawImage(result.getContext('2d'), srcCanvas, null, this.imageRect_); 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var delay; 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (uiContext.imageView) { 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delay = uiContext.imageView.replaceAndAnimate(result, this.imageRect_, 0); 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) setTimeout(callback, 0, result, delay); 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @override */ 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Crop.prototype.revertView = function(canvas, imageView) { 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return imageView.animateAndReplace(canvas, this.imageRect_); 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Filter command. 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string} name Command name. 4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {function(ImageData,ImageData,number,number)} filter Filter function. 4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string} message Message to display when done. 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @extends {Command} 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Filter = function(name, filter, message) { 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Command.call(this, name); 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.filter_ = filter; 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.message_ = message; 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Filter.prototype = { __proto__: Command.prototype }; 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @override */ 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command.Filter.prototype.execute = function( 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document, srcCanvas, callback, uiContext) { 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var result = this.createCanvas_(document, srcCanvas); 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var self = this; 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var previousRow = 0; 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function onProgressVisible(updatedRow, rowCount) { 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (updatedRow == rowCount) { 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uiContext.imageView.replace(result); 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (self.message_) 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uiContext.prompt.show(self.message_, 2000); 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) callback(result); 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var viewport = uiContext.imageView.viewport_; 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var imageStrip = new Rect(viewport.getImageBounds()); 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) imageStrip.top = previousRow; 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) imageStrip.height = updatedRow - previousRow; 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var screenStrip = new Rect(viewport.getImageBoundsOnScreen()); 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) screenStrip.top = Math.round(viewport.imageToScreenY(previousRow)); 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) screenStrip.height = 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Math.round(viewport.imageToScreenY(updatedRow)) - screenStrip.top; 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uiContext.imageView.paintDeviceRect( 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) viewport.screenToDeviceRect(screenStrip), result, imageStrip); 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) previousRow = updatedRow; 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function onProgressInvisible(updatedRow, rowCount) { 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (updatedRow == rowCount) { 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) callback(result); 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filter.applyByStrips(result, srcCanvas, this.filter_, 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uiContext.imageView ? onProgressVisible : onProgressInvisible); 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 456