1f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// found in the LICENSE file.
4f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)'use strict';
6f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
8f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Command queue is the only way to modify images.
9f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Supports undo/redo.
10f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Command execution is asynchronous (callback-based).
11f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
12f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Document} document Document to create canvases in.
13f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLCanvasElement} canvas The canvas with the original image.
14f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function(callback)} saveFunction Function to save the image.
15f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
16f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
17f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)function CommandQueue(document, canvas, saveFunction) {
18f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.document_ = document;
19f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.undo_ = [];
20f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.redo_ = [];
21f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.subscribers_ = [];
22f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentImage_ = canvas;
23f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
24f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Current image may be null or not-null but with width = height = 0.
25f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Copying an image with zero dimensions causes js errors.
26f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.currentImage_) {
27f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.baselineImage_ = document.createElement('canvas');
28f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.baselineImage_.width = this.currentImage_.width;
29f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.baselineImage_.height = this.currentImage_.height;
30f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (this.currentImage_.width > 0 && this.currentImage_.height > 0) {
31f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      var context = this.baselineImage_.getContext('2d');
32f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      context.drawImage(this.currentImage_, 0, 0);
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
34f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.baselineImage_ = null;
36f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
37f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
38f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImage_ = document.createElement('canvas');
39f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImageAvailable_ = false;
40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.saveFunction_ = saveFunction;
42f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.busy_ = false;
43f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.UIContext_ = {};
44f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
45f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Attach the UI elements to the command queue.
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Once the UI is attached the results of image manipulations are displayed.
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageView} imageView The ImageView object to display the results.
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageEditor.Prompt} prompt Prompt to use with this CommandQueue.
52f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function(boolean)} lock Function to enable/disable buttons etc.
53f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
54f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.attachUI = function(imageView, prompt, lock) {
55f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.UIContext_ = {
56f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    imageView: imageView,
57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    prompt: prompt,
58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    lock: lock
59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  };
60f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
61f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
62f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
63f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Execute the action when the queue is not busy.
64f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} callback Callback.
65f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
66f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.executeWhenReady = function(callback) {
67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.isBusy())
68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.subscribers_.push(callback);
69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  else
70f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    setTimeout(callback, 0);
71f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
73f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if the command queue is busy.
75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.isBusy = function() { return this.busy_ };
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
79f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Set the queue state to busy. Lock the UI.
80f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
81f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
82f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.setBusy_ = function() {
83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.busy_)
84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    throw new Error('CommandQueue already busy');
85f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
86f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.busy_ = true;
87f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
88f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.UIContext_.lock)
89f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.UIContext_.lock(true);
90f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.trace.resetTimer('command-busy');
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
93f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
94f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
95f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Set the queue state to not busy. Unlock the UI and execute pending actions.
96f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
97f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
98f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.clearBusy_ = function() {
99f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.busy_)
100f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    throw new Error('Inconsistent CommandQueue already not busy');
101f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
102f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.busy_ = false;
103f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
104f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Execute the actions requested while the queue was busy.
105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  while (this.subscribers_.length)
106f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.subscribers_.shift()();
107f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
108f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.UIContext_.lock)
109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.UIContext_.lock(false);
110f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.trace.reportTimer('command-busy');
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Commit the image change: save and unlock the UI.
116f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number=} opt_delay Delay in ms (to avoid disrupting the animation).
117f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.commit_ = function(opt_delay) {
120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  setTimeout(this.saveFunction_.bind(null, this.clearBusy_.bind(this)),
121f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      opt_delay || 0);
122f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
123f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Internal function to execute the command in a given context.
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Command} command The command to execute.
128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} uiContext The UI context.
129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} callback Completion callback.
130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.doExecute_ = function(command, uiContext, callback) {
133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.currentImage_)
134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    throw new Error('Cannot operate on null image');
135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
136f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Remember one previous image so that the first undo is as fast as possible.
137f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImage_.width = this.currentImage_.width;
138f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImage_.height = this.currentImage_.height;
139f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImageAvailable_ = true;
140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var context = this.previousImage_.getContext('2d');
141f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  context.drawImage(this.currentImage_, 0, 0);
142f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  command.execute(
144f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.document_,
145f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.currentImage_,
146f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      function(result, opt_delay) {
147f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        this.currentImage_ = result;
148f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        callback(opt_delay);
149f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }.bind(this),
150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      uiContext);
151f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
152f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
153f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Executes the command.
155f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
156f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Command} command Command to execute.
157f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {boolean=} opt_keep_redo True if redo stack should not be cleared.
158f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
159f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.execute = function(command, opt_keep_redo) {
160f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.setBusy_();
161f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
162f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!opt_keep_redo)
163f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.redo_ = [];
164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
165f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.undo_.push(command);
166f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
167f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.doExecute_(command, this.UIContext_, this.commit_.bind(this));
168f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
169f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
171f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if Undo is applicable.
172f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
173f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.canUndo = function() {
174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.undo_.length != 0;
175f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Undo the most recent command.
179f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.undo = function() {
181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.canUndo())
182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    throw new Error('Cannot undo');
183f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.setBusy_();
185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
186f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var command = this.undo_.pop();
187f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.redo_.push(command);
188f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
189f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var self = this;
190f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
191f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  function complete() {
192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var delay = command.revertView(
193f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        self.currentImage_, self.UIContext_.imageView);
194f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    self.commit_(delay);
195f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
196f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
197f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.previousImageAvailable_) {
198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // First undo after an execute call.
199f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.currentImage_.width = this.previousImage_.width;
200f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.currentImage_.height = this.previousImage_.height;
201f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var context = this.currentImage_.getContext('2d');
202f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    context.drawImage(this.previousImage_, 0, 0);
203f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
204f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Free memory.
205f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.previousImage_.width = 0;
206f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.previousImage_.height = 0;
207f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.previousImageAvailable_ = false;
208f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
209f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    complete();
210f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // TODO(kaznacheev) Consider recalculating previousImage_ right here
211f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // by replaying the commands in the background.
212f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
213f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.currentImage_.width = this.baselineImage_.width;
214f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.currentImage_.height = this.baselineImage_.height;
215f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var context = this.currentImage_.getContext('2d');
216f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    context.drawImage(this.baselineImage_, 0, 0);
217f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
218f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var replay = function(index) {
219f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (index < self.undo_.length)
220f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        self.doExecute_(self.undo_[index], {}, replay.bind(null, index + 1));
221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      else {
222f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        complete();
223f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
224f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    };
225f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
226f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    replay(0);
227f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
228f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
229f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
230f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
231f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if Redo is applicable.
232f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
233f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.canRedo = function() {
234f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.redo_.length != 0;
235f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
236f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
237f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
238f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Repeat the command that was recently un-done.
239f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
240f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.redo = function() {
241f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.canRedo())
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    throw new Error('Cannot redo');
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.execute(this.redo_.pop(), true);
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Closes internal buffers. Call to ensure, that internal buffers are freed
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * as soon as possible.
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)CommandQueue.prototype.close = function() {
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Free memory used by the undo buffer.
253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImage_.width = 0;
254f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImage_.height = 0;
255f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.previousImageAvailable_ = false;
256f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
257f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.baselineImage_) {
258f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.baselineImage_.width = 0;
259f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.baselineImage_.height = 0;
260f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
261f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
262f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
263f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
264f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Command object encapsulates an operation on an image and a way to visualize
265f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * its result.
266f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
267f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name Command name.
268f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
269f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
270f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)function Command(name) {
271f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.name_ = name;
272f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
273f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
274f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
275f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {string} String representation of the command.
276f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
277f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.prototype.toString = function() {
278f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return 'Command ' + this.name_;
279f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
280f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
281f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
282f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Execute the command and visualize its results.
283f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
284f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * The two actions are combined into one method because sometimes it is nice
285f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * to be able to show partial results for slower operations.
286f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
287f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Document} document Document on which to execute command.
288f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLCanvasElement} srcCanvas Canvas to execute on.
289f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function(HTMLCanvasElement, number)} callback Callback to call on
290f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   completion.
291f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} uiContext Context to work in.
292f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
293f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.prototype.execute = function(document, srcCanvas, callback, uiContext) {
294f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  console.error('Command.prototype.execute not implemented');
295f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
296f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
297f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
298f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Visualize reversion of the operation.
299f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
300f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLCanvasElement} canvas Image data to use.
301f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageView} imageView ImageView to revert.
302f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {number} Animation duration in ms.
303f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
304f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.prototype.revertView = function(canvas, imageView) {
305f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  imageView.replace(canvas);
306f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return 0;
307f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
308f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
309f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
310f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Creates canvas to render on.
311f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
312f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Document} document Document to create canvas in.
313f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLCanvasElement} srcCanvas to copy optional dimensions from.
314f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number=} opt_width new canvas width.
315f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number=} opt_height new canvas height.
316f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {HTMLCanvasElement} Newly created canvas.
317f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
318f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
319f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.prototype.createCanvas_ = function(
320f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    document, srcCanvas, opt_width, opt_height) {
321f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var result = document.createElement('canvas');
322f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  result.width = opt_width || srcCanvas.width;
323f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  result.height = opt_height || srcCanvas.height;
324f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return result;
325f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
326f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
327f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
328f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
329f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Rotate command
330f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} rotate90 Rotation angle in 90 degree increments (signed).
331f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
332f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @extends {Command}
333f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
334f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Rotate = function(rotate90) {
335f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Command.call(this, 'rotate(' + rotate90 * 90 + 'deg)');
336f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.rotate90_ = rotate90;
337f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
338f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
339f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Rotate.prototype = { __proto__: Command.prototype };
340f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
341f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/** @override */
342f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Rotate.prototype.execute = function(
343f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    document, srcCanvas, callback, uiContext) {
344f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var result = this.createCanvas_(
345f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      document,
346f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      srcCanvas,
347f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      (this.rotate90_ & 1) ? srcCanvas.height : srcCanvas.width,
348f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      (this.rotate90_ & 1) ? srcCanvas.width : srcCanvas.height);
349f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.drawImageTransformed(
350f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      result, srcCanvas, 1, 1, this.rotate90_ * Math.PI / 2);
351f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var delay;
352f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (uiContext.imageView) {
353f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    delay = uiContext.imageView.replaceAndAnimate(result, null, this.rotate90_);
354f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
355f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  setTimeout(callback, 0, result, delay);
356f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
357f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
358f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/** @override */
359f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Rotate.prototype.revertView = function(canvas, imageView) {
360f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return imageView.replaceAndAnimate(canvas, null, -this.rotate90_);
361f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
362f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
363f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
364f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
365f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Crop command.
366f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
367f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Rect} imageRect Crop rectangle in image coordinates.
368f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
369f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @extends {Command}
370f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Crop = function(imageRect) {
372f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Command.call(this, 'crop' + imageRect.toString());
373f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.imageRect_ = imageRect;
374f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
375f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Crop.prototype = { __proto__: Command.prototype };
377f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
378f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/** @override */
379f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Crop.prototype.execute = function(
380f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    document, srcCanvas, callback, uiContext) {
381f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var result = this.createCanvas_(
382f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      document, srcCanvas, this.imageRect_.width, this.imageRect_.height);
383f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Rect.drawImage(result.getContext('2d'), srcCanvas, null, this.imageRect_);
384f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var delay;
385f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (uiContext.imageView) {
386f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    delay = uiContext.imageView.replaceAndAnimate(result, this.imageRect_, 0);
387f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
388f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  setTimeout(callback, 0, result, delay);
389f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
390f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
391f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/** @override */
392f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Crop.prototype.revertView = function(canvas, imageView) {
393f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return imageView.animateAndReplace(canvas, this.imageRect_);
394f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
395f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
396f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
397f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
398f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Filter command.
399f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
400f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name Command name.
401f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function(ImageData,ImageData,number,number)} filter Filter function.
402f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} message Message to display when done.
403f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
404f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @extends {Command}
405f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
406f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Filter = function(name, filter, message) {
407f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Command.call(this, name);
408f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.filter_ = filter;
409f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.message_ = message;
410f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
411f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
412f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Filter.prototype = { __proto__: Command.prototype };
413f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
414f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/** @override */
415f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)Command.Filter.prototype.execute = function(
416f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    document, srcCanvas, callback, uiContext) {
417f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var result = this.createCanvas_(document, srcCanvas);
418f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
419f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var self = this;
420f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
421f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var previousRow = 0;
422f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
423f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  function onProgressVisible(updatedRow, rowCount) {
424f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (updatedRow == rowCount) {
425f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      uiContext.imageView.replace(result);
426f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (self.message_)
427f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        uiContext.prompt.show(self.message_, 2000);
428f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      callback(result);
429f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    } else {
430f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      var viewport = uiContext.imageView.viewport_;
431f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
432f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      var imageStrip = new Rect(viewport.getImageBounds());
433f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      imageStrip.top = previousRow;
434f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      imageStrip.height = updatedRow - previousRow;
435f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
436f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      var screenStrip = new Rect(viewport.getImageBoundsOnScreen());
437f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      screenStrip.top = Math.round(viewport.imageToScreenY(previousRow));
438f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      screenStrip.height =
439f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          Math.round(viewport.imageToScreenY(updatedRow)) - screenStrip.top;
440f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
441f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      uiContext.imageView.paintDeviceRect(
442f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          viewport.screenToDeviceRect(screenStrip), result, imageStrip);
443f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      previousRow = updatedRow;
444f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
445f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
446f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
447f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  function onProgressInvisible(updatedRow, rowCount) {
448f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (updatedRow == rowCount) {
449f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      callback(result);
450f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
451f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
452f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
453f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  filter.applyByStrips(result, srcCanvas, this.filter_,
454f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      uiContext.imageView ? onProgressVisible : onProgressInvisible);
455f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
456