1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5'use strict';
6
7/**
8 * The base class for simple filters that only modify the image content
9 * but do not modify the image dimensions.
10 * @constructor
11 * @extends {ImageEditor.Mode}
12 */
13ImageEditor.Mode.Adjust = function() {
14  ImageEditor.Mode.apply(this, arguments);
15  this.implicitCommit = true;
16  this.doneMessage_ = null;
17  this.viewportGeneration_ = 0;
18};
19
20ImageEditor.Mode.Adjust.prototype = {__proto__: ImageEditor.Mode.prototype};
21
22/** @override */
23ImageEditor.Mode.Adjust.prototype.getCommand = function() {
24  if (!this.filter_) return null;
25
26  return new Command.Filter(this.name, this.filter_, this.doneMessage_);
27};
28
29/** @override */
30ImageEditor.Mode.Adjust.prototype.cleanUpUI = function() {
31  ImageEditor.Mode.prototype.cleanUpUI.apply(this, arguments);
32  this.hidePreview();
33};
34
35/**
36 * TODO(JSDOC)
37 */
38ImageEditor.Mode.Adjust.prototype.hidePreview = function() {
39  if (this.canvas_) {
40    this.canvas_.parentNode.removeChild(this.canvas_);
41    this.canvas_ = null;
42  }
43};
44
45/**
46 * TODO(JSDOC)
47 */
48ImageEditor.Mode.Adjust.prototype.cleanUpCaches = function() {
49  this.filter_ = null;
50  this.previewImageData_ = null;
51};
52
53/**
54 * TODO(JSDOC)
55 */
56ImageEditor.Mode.Adjust.prototype.reset = function() {
57  ImageEditor.Mode.prototype.reset.call(this);
58  this.hidePreview();
59  this.cleanUpCaches();
60};
61
62/**
63 * TODO(JSDOC)
64 * @param {Object} options  // TODO(JSDOC).
65 */
66ImageEditor.Mode.Adjust.prototype.update = function(options) {
67  ImageEditor.Mode.prototype.update.apply(this, arguments);
68
69  // We assume filter names are used in the UI directly.
70  // This will have to change with i18n.
71  this.filter_ = this.createFilter(options);
72  this.updatePreviewImage();
73  ImageUtil.trace.resetTimer('preview');
74  this.filter_(this.previewImageData_, this.originalImageData, 0, 0);
75  ImageUtil.trace.reportTimer('preview');
76  this.canvas_.getContext('2d').putImageData(
77      this.previewImageData_, 0, 0);
78};
79
80/**
81 * Copy the source image data for the preview.
82 * Use the cached copy if the viewport has not changed.
83 */
84ImageEditor.Mode.Adjust.prototype.updatePreviewImage = function() {
85  if (!this.previewImageData_ ||
86      this.viewportGeneration_ != this.getViewport().getCacheGeneration()) {
87    this.viewportGeneration_ = this.getViewport().getCacheGeneration();
88
89    if (!this.canvas_) {
90      this.canvas_ = this.getImageView().createOverlayCanvas();
91    }
92
93    this.getImageView().setupDeviceBuffer(this.canvas_);
94
95    this.originalImageData = this.getImageView().copyScreenImageData();
96    this.previewImageData_ = this.getImageView().copyScreenImageData();
97  }
98};
99
100/*
101 * Own methods
102 */
103
104/**
105 * TODO(JSDOC)
106 * @param {Object} options  // TODO(JSDOC).
107 * @return {function(ImageData,ImageData,number,number)} Created function.
108 */
109ImageEditor.Mode.Adjust.prototype.createFilter = function(options) {
110  return filter.create(this.name, options);
111};
112
113/**
114 * A base class for color filters that are scale independent.
115 * @constructor
116 */
117ImageEditor.Mode.ColorFilter = function() {
118  ImageEditor.Mode.Adjust.apply(this, arguments);
119};
120
121ImageEditor.Mode.ColorFilter.prototype =
122    {__proto__: ImageEditor.Mode.Adjust.prototype};
123
124/**
125 * TODO(JSDOC)
126 * @return {{r: Array.<number>, g: Array.<number>, b: Array.<number>}}
127 *    histogram.
128 */
129ImageEditor.Mode.ColorFilter.prototype.getHistogram = function() {
130  return filter.getHistogram(this.getImageView().getThumbnail());
131};
132
133/**
134 * Exposure/contrast filter.
135 * @constructor
136 */
137ImageEditor.Mode.Exposure = function() {
138  ImageEditor.Mode.ColorFilter.call(this, 'exposure', 'GALLERY_EXPOSURE');
139};
140
141ImageEditor.Mode.Exposure.prototype =
142    {__proto__: ImageEditor.Mode.ColorFilter.prototype};
143
144/**
145 * TODO(JSDOC)
146 * @param {ImageEditor.Toolbar} toolbar The toolbar to populate.
147 */
148ImageEditor.Mode.Exposure.prototype.createTools = function(toolbar) {
149  toolbar.addRange('brightness', 'GALLERY_BRIGHTNESS', -1, 0, 1, 100);
150  toolbar.addRange('contrast', 'GALLERY_CONTRAST', -1, 0, 1, 100);
151};
152
153/**
154 * Autofix.
155 * @constructor
156 */
157ImageEditor.Mode.Autofix = function() {
158  ImageEditor.Mode.ColorFilter.call(this, 'autofix', 'GALLERY_AUTOFIX');
159  this.doneMessage_ = 'GALLERY_FIXED';
160};
161
162ImageEditor.Mode.Autofix.prototype =
163    {__proto__: ImageEditor.Mode.ColorFilter.prototype};
164
165/**
166 * TODO(JSDOC)
167 * @param {ImageEditor.Toolbar} toolbar The toolbar to populate.
168 */
169ImageEditor.Mode.Autofix.prototype.createTools = function(toolbar) {
170  var self = this;
171  toolbar.addButton('Apply', this.apply.bind(this));
172};
173
174/**
175 * TODO(JSDOC)
176 * @return {boolean}  // TODO(JSDOC).
177 */
178ImageEditor.Mode.Autofix.prototype.isApplicable = function() {
179  return this.getImageView().hasValidImage() &&
180      filter.autofix.isApplicable(this.getHistogram());
181};
182
183/**
184 * TODO(JSDOC)
185 */
186ImageEditor.Mode.Autofix.prototype.apply = function() {
187  this.update({histogram: this.getHistogram()});
188};
189
190/**
191 * Instant Autofix.
192 * @constructor
193 */
194ImageEditor.Mode.InstantAutofix = function() {
195  ImageEditor.Mode.Autofix.apply(this, arguments);
196  this.instant = true;
197};
198
199ImageEditor.Mode.InstantAutofix.prototype =
200    {__proto__: ImageEditor.Mode.Autofix.prototype};
201
202/**
203 * TODO(JSDOC)
204 */
205ImageEditor.Mode.InstantAutofix.prototype.setUp = function() {
206  ImageEditor.Mode.Autofix.prototype.setUp.apply(this, arguments);
207  this.apply();
208};
209
210/**
211 * Blur filter.
212 * @constructor
213 */
214ImageEditor.Mode.Blur = function() {
215  ImageEditor.Mode.Adjust.call(this, 'blur');
216};
217
218ImageEditor.Mode.Blur.prototype =
219    {__proto__: ImageEditor.Mode.Adjust.prototype};
220
221/**
222 * TODO(JSDOC)
223 * @param {ImageEditor.Toolbar} toolbar The toolbar to populate.
224 */
225ImageEditor.Mode.Blur.prototype.createTools = function(toolbar) {
226  toolbar.addRange('strength', 'GALLERY_STRENGTH', 0, 0, 1, 100);
227  toolbar.addRange('radius', 'GALLERY_RADIUS', 1, 1, 3);
228};
229
230/**
231 * Sharpen filter.
232 * @constructor
233 */
234ImageEditor.Mode.Sharpen = function() {
235  ImageEditor.Mode.Adjust.call(this, 'sharpen');
236};
237
238ImageEditor.Mode.Sharpen.prototype =
239    {__proto__: ImageEditor.Mode.Adjust.prototype};
240
241/**
242 * TODO(JSDOC)
243 * @param {ImageEditor.Toolbar} toolbar The toolbar to populate.
244 */
245ImageEditor.Mode.Sharpen.prototype.createTools = function(toolbar) {
246  toolbar.addRange('strength', 'GALLERY_STRENGTH', 0, 0, 1, 100);
247  toolbar.addRange('radius', 'GALLERY_RADIUS', 1, 1, 3);
248};
249