Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1238)

Side by Side Diff: ui/file_manager/gallery/js/image_editor/image_util.js

Issue 791363003: Add type annotations to gallery/js/image_editor/image_util.js. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed format. Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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 // Namespace object for the utilities. 5 // Namespace object for the utilities.
6 function ImageUtil() {} 6 var ImageUtil = {};
7 7
8 /** 8 /**
9 * Performance trace. 9 * Performance trace.
10 */ 10 */
11 ImageUtil.trace = (function() { 11 ImageUtil.trace = (function() {
12 /**
13 * Performance trace.
14 * @constructor
15 * @struct
16 */
12 function PerformanceTrace() { 17 function PerformanceTrace() {
13 this.lines_ = {}; 18 this.lines_ = {};
14 this.timers_ = {}; 19 this.timers_ = {};
15 this.container_ = null; 20 this.container_ = null;
16 } 21 }
17 22
18 PerformanceTrace.prototype.bindToDOM = function(container) { 23 PerformanceTrace.prototype.bindToDOM = function(container) {
19 this.container_ = container; 24 this.container_ = container;
20 }; 25 };
21 26
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
67 * @param {number} value Value to check. 72 * @param {number} value Value to check.
68 * @param {number} max Maximum value. 73 * @param {number} max Maximum value.
69 * @return {boolean} True if value is between. 74 * @return {boolean} True if value is between.
70 */ 75 */
71 ImageUtil.between = function(min, value, max) { 76 ImageUtil.between = function(min, value, max) {
72 return (value - min) * (value - max) <= 0; 77 return (value - min) * (value - max) <= 0;
73 }; 78 };
74 79
75 /** 80 /**
76 * Rectangle class. 81 * Rectangle class.
82 *
83 * @param {number} left Left.
84 * @param {number} top Top.
85 * @param {number} width Width.
86 * @param {number} height Height.
87 * @constructor
88 * @struct
77 */ 89 */
78 90 function ImageRect(left, top, width, height) {
79 /** 91 this.left = left;
80 * Rectangle constructor takes 0, 1, 2 or 4 arguments. 92 this.top = top;
81 * Supports following variants: 93 this.width = width;
82 * new ImageRect(left, top, width, height) 94 this.height = height;
83 * new ImageRect(width, height)
84 * new ImageRect(rect) // anything with left, top, width, height.
85 * new ImageRect(bounds) // anything with left, top, right, bottom.
86 * new ImageRect(canvas|image) // anything with width and height.
87 * new ImageRect() // empty rectangle.
88 * @constructor
89 */
90 function ImageRect() {
91 switch (arguments.length) {
92 case 4:
93 this.left = arguments[0];
94 this.top = arguments[1];
95 this.width = arguments[2];
96 this.height = arguments[3];
97 return;
98
99 case 2:
100 this.left = 0;
101 this.top = 0;
102 this.width = arguments[0];
103 this.height = arguments[1];
104 return;
105
106 case 1: {
107 var source = arguments[0];
108 if ('left' in source && 'top' in source) {
109 this.left = source.left;
110 this.top = source.top;
111 if ('right' in source && 'bottom' in source) {
112 this.width = source.right - source.left;
113 this.height = source.bottom - source.top;
114 return;
115 }
116 } else {
117 this.left = 0;
118 this.top = 0;
119 }
120 if ('width' in source && 'height' in source) {
121 this.width = source.width;
122 this.height = source.height;
123 return;
124 }
125 break; // Fall through to the error message.
126 }
127
128 case 0:
129 this.left = 0;
130 this.top = 0;
131 this.width = 0;
132 this.height = 0;
133 return;
134 }
135 console.error('Invalid ImageRect constructor arguments:',
136 Array.apply(null, arguments));
137 } 95 }
138 96
139 /** 97 /**
140 * Creates an image rect with a canvas. 98 * Creates an image rect with a canvas.
141 * @param {!HTMLCanvasElement} canvas A canvas. 99 * @param {!HTMLCanvasElement} canvas A canvas.
142 * @return {!ImageRect} 100 * @return {!ImageRect}
101 *
102 * TODO(yawano) Since createFromImage accepts HTMLCanvasElement, delete this
103 * method later.
143 */ 104 */
144 ImageRect.createFromCanvas = function(canvas) { 105 ImageRect.createFromCanvas = function(canvas) {
145 return new ImageRect(canvas); 106 return ImageRect.createFromImage(canvas);
146 }; 107 };
147 108
148 /** 109 /**
110 * Creates an image rect with an image or a canvas.
111 * @param {!(HTMLImageElement|HTMLCanvasElement)} image An image or a canvas.
112 * @return {!ImageRect}
113 */
114 ImageRect.createFromImage = function(image) {
115 return new ImageRect(0, 0, image.width, image.height);
116 };
117
118 /**
119 * Clone an image rect.
120 * @param {!ImageRect} imageRect An image rect.
121 * @return {!ImageRect}
122 */
123 ImageRect.clone = function(imageRect) {
124 return new ImageRect(imageRect.left, imageRect.top, imageRect.width,
125 imageRect.height);
126 };
127
128 /**
149 * Creates an image rect with a bound. 129 * Creates an image rect with a bound.
150 * @param {{left: number, top: number, right: number, bottom: number}} bound 130 * @param {{left: number, top: number, right: number, bottom: number}} bound
151 * A bound. 131 * A bound.
152 * @return {!ImageRect} 132 * @return {!ImageRect}
153 */ 133 */
154 ImageRect.createFromBounds = function(bound) { 134 ImageRect.createFromBounds = function(bound) {
155 return new ImageRect(bound); 135 return new ImageRect(bound.left, bound.top,
136 bound.right - bound.left, bound.bottom - bound.top);
156 }; 137 };
157 138
158 /** 139 /**
159 * Creates an image rect with width and height. 140 * Creates an image rect with width and height.
160 * @param {number} width Width. 141 * @param {number} width Width.
161 * @param {number} height Height. 142 * @param {number} height Height.
162 * @return {!ImageRect} 143 * @return {!ImageRect}
163 */ 144 */
164 ImageRect.createFromWidthAndHeight = function(width, height) { 145 ImageRect.createFromWidthAndHeight = function(width, height) {
165 return new ImageRect(width, height); 146 return new ImageRect(0, 0, width, height);
166 }; 147 };
167 148
168 /** 149 /**
169 * Creates an image rect with left, top, width and height. 150 * Creates an image rect with left, top, width and height.
170 * @param {number} left Left. 151 * @param {number} left Left.
171 * @param {number} top Top. 152 * @param {number} top Top.
172 * @param {number} width Width. 153 * @param {number} width Width.
173 * @param {number} height Height. 154 * @param {number} height Height.
174 * @return {!ImageRect} 155 * @return {!ImageRect}
156 *
157 * TODO(yawano) Remove createWith calls and call constructor directly.
fukino 2014/12/11 11:28:16 nit: We usually use semicolon after the name, like
yawano 2014/12/12 01:11:33 Done.
175 */ 158 */
176 ImageRect.createWith = function(left, top, width, height) { 159 ImageRect.createWith = function(left, top, width, height) {
177 return new ImageRect(left, top, width, height); 160 return new ImageRect(left, top, width, height);
178 }; 161 };
179 162
180 ImageRect.prototype = { 163 ImageRect.prototype = /** @struct */ ({
164 // TODO(yawano) Change getters to methods (e.g. getRight()).
fukino 2014/12/11 11:28:15 ditto
yawano 2014/12/12 01:11:33 Done.
165
181 /** 166 /**
182 * Obtains the x coordinate of right edge. The most right pixels in the 167 * Obtains the x coordinate of right edge. The most right pixels in the
183 * rectangle are (x = right - 1) and the pixels (x = right) are not included 168 * rectangle are (x = right - 1) and the pixels (x = right) are not included
184 * in the rectangle. 169 * in the rectangle.
185 * @return {number} 170 * @return {number}
186 */ 171 */
187 get right() { 172 get right() {
188 return this.left + this.width; 173 return this.left + this.width;
189 }, 174 },
190 175
191 /** 176 /**
192 * Obtains the y coordinate of bottom edge. The most bottom pixels in the 177 * Obtains the y coordinate of bottom edge. The most bottom pixels in the
193 * rectangle are (y = bottom - 1) and the pixels (y = bottom) are not included 178 * rectangle are (y = bottom - 1) and the pixels (y = bottom) are not included
194 * in the rectangle. 179 * in the rectangle.
195 * @return {number} 180 * @return {number}
196 */ 181 */
197 get bottom() { 182 get bottom() {
198 return this.top + this.height; 183 return this.top + this.height;
199 } 184 }
200 }; 185 });
201 186
202 /** 187 /**
203 * @param {number} factor Factor to scale. 188 * @param {number} factor Factor to scale.
204 * @return {ImageRect} A rectangle with every dimension scaled. 189 * @return {!ImageRect} A rectangle with every dimension scaled.
205 */ 190 */
206 ImageRect.prototype.scale = function(factor) { 191 ImageRect.prototype.scale = function(factor) {
207 return new ImageRect( 192 return new ImageRect(
208 this.left * factor, 193 this.left * factor,
209 this.top * factor, 194 this.top * factor,
210 this.width * factor, 195 this.width * factor,
211 this.height * factor); 196 this.height * factor);
212 }; 197 };
213 198
214 /** 199 /**
215 * @param {number} dx Difference in X. 200 * @param {number} dx Difference in X.
216 * @param {number} dy Difference in Y. 201 * @param {number} dy Difference in Y.
217 * @return {ImageRect} A rectangle shifted by (dx,dy), same size. 202 * @return {!ImageRect} A rectangle shifted by (dx,dy), same size.
218 */ 203 */
219 ImageRect.prototype.shift = function(dx, dy) { 204 ImageRect.prototype.shift = function(dx, dy) {
220 return new ImageRect(this.left + dx, this.top + dy, this.width, this.height); 205 return new ImageRect(this.left + dx, this.top + dy, this.width, this.height);
221 }; 206 };
222 207
223 /** 208 /**
224 * @param {number} x Coordinate of the left top corner. 209 * @param {number} x Coordinate of the left top corner.
225 * @param {number} y Coordinate of the left top corner. 210 * @param {number} y Coordinate of the left top corner.
226 * @return {ImageRect} A rectangle with left==x and top==y, same size. 211 * @return {!ImageRect} A rectangle with left==x and top==y, same size.
227 */ 212 */
228 ImageRect.prototype.moveTo = function(x, y) { 213 ImageRect.prototype.moveTo = function(x, y) {
229 return new ImageRect(x, y, this.width, this.height); 214 return new ImageRect(x, y, this.width, this.height);
230 }; 215 };
231 216
232 /** 217 /**
233 * @param {number} dx Difference in X. 218 * @param {number} dx Difference in X.
234 * @param {number} dy Difference in Y. 219 * @param {number} dy Difference in Y.
235 * @return {!ImageRect} A rectangle inflated by (dx, dy), same center. 220 * @return {!ImageRect} A rectangle inflated by (dx, dy), same center.
236 */ 221 */
237 ImageRect.prototype.inflate = function(dx, dy) { 222 ImageRect.prototype.inflate = function(dx, dy) {
238 return new ImageRect( 223 return new ImageRect(
239 this.left - dx, this.top - dy, this.width + 2 * dx, this.height + 2 * dy); 224 this.left - dx, this.top - dy, this.width + 2 * dx, this.height + 2 * dy);
240 }; 225 };
241 226
242 /** 227 /**
243 * @param {number} x Coordinate of the point. 228 * @param {number} x Coordinate of the point.
244 * @param {number} y Coordinate of the point. 229 * @param {number} y Coordinate of the point.
245 * @return {boolean} True if the point lies inside the rectangle. 230 * @return {boolean} True if the point lies inside the rectangle.
246 */ 231 */
247 ImageRect.prototype.inside = function(x, y) { 232 ImageRect.prototype.inside = function(x, y) {
248 return this.left <= x && x < this.left + this.width && 233 return this.left <= x && x < this.left + this.width &&
249 this.top <= y && y < this.top + this.height; 234 this.top <= y && y < this.top + this.height;
250 }; 235 };
251 236
252 /** 237 /**
253 * @param {ImageRect} rect Rectangle to check. 238 * @param {!ImageRect} rect Rectangle to check.
254 * @return {boolean} True if this rectangle intersects with the |rect|. 239 * @return {boolean} True if this rectangle intersects with the |rect|.
255 */ 240 */
256 ImageRect.prototype.intersects = function(rect) { 241 ImageRect.prototype.intersects = function(rect) {
257 return (this.left + this.width) > rect.left && 242 return (this.left + this.width) > rect.left &&
258 (rect.left + rect.width) > this.left && 243 (rect.left + rect.width) > this.left &&
259 (this.top + this.height) > rect.top && 244 (this.top + this.height) > rect.top &&
260 (rect.top + rect.height) > this.top; 245 (rect.top + rect.height) > this.top;
261 }; 246 };
262 247
263 /** 248 /**
264 * @param {ImageRect} rect Rectangle to check. 249 * @param {!ImageRect} rect Rectangle to check.
265 * @return {boolean} True if this rectangle containing the |rect|. 250 * @return {boolean} True if this rectangle containing the |rect|.
266 */ 251 */
267 ImageRect.prototype.contains = function(rect) { 252 ImageRect.prototype.contains = function(rect) {
268 return (this.left <= rect.left) && 253 return (this.left <= rect.left) &&
269 (rect.left + rect.width) <= (this.left + this.width) && 254 (rect.left + rect.width) <= (this.left + this.width) &&
270 (this.top <= rect.top) && 255 (this.top <= rect.top) &&
271 (rect.top + rect.height) <= (this.top + this.height); 256 (rect.top + rect.height) <= (this.top + this.height);
272 }; 257 };
273 258
274 /** 259 /**
275 * @return {boolean} True if rectangle is empty. 260 * @return {boolean} True if rectangle is empty.
276 */ 261 */
277 ImageRect.prototype.isEmpty = function() { 262 ImageRect.prototype.isEmpty = function() {
278 return this.width === 0 || this.height === 0; 263 return this.width === 0 || this.height === 0;
279 }; 264 };
280 265
281 /** 266 /**
282 * Clamp the rectangle to the bounds by moving it. 267 * Clamp the rectangle to the bounds by moving it.
283 * Decrease the size only if necessary. 268 * Decrease the size only if necessary.
284 * @param {ImageRect} bounds Bounds. 269 * @param {!ImageRect} bounds Bounds.
285 * @return {ImageRect} Calculated rectangle. 270 * @return {!ImageRect} Calculated rectangle.
286 */ 271 */
287 ImageRect.prototype.clamp = function(bounds) { 272 ImageRect.prototype.clamp = function(bounds) {
288 var rect = new ImageRect(this); 273 var rect = ImageRect.clone(this);
289 274
290 if (rect.width > bounds.width) { 275 if (rect.width > bounds.width) {
291 rect.left = bounds.left; 276 rect.left = bounds.left;
292 rect.width = bounds.width; 277 rect.width = bounds.width;
293 } else if (rect.left < bounds.left) { 278 } else if (rect.left < bounds.left) {
294 rect.left = bounds.left; 279 rect.left = bounds.left;
295 } else if (rect.left + rect.width > 280 } else if (rect.left + rect.width >
296 bounds.left + bounds.width) { 281 bounds.left + bounds.width) {
297 rect.left = bounds.left + bounds.width - rect.width; 282 rect.left = bounds.left + bounds.width - rect.width;
298 } 283 }
(...skipping 17 matching lines...) Expand all
316 ImageRect.prototype.toString = function() { 301 ImageRect.prototype.toString = function() {
317 return '(' + this.left + ',' + this.top + '):' + 302 return '(' + this.left + ',' + this.top + '):' +
318 '(' + (this.left + this.width) + ',' + (this.top + this.height) + ')'; 303 '(' + (this.left + this.width) + ',' + (this.top + this.height) + ')';
319 }; 304 };
320 /* 305 /*
321 * Useful shortcuts for drawing (static functions). 306 * Useful shortcuts for drawing (static functions).
322 */ 307 */
323 308
324 /** 309 /**
325 * Draw the image in context with appropriate scaling. 310 * Draw the image in context with appropriate scaling.
326 * @param {CanvasRenderingContext2D} context Context to draw. 311 * @param {!CanvasRenderingContext2D} context Context to draw.
327 * @param {!(HTMLCanvasElement|HTMLImageElement)} image Image to draw. 312 * @param {!(HTMLCanvasElement|HTMLImageElement)} image Image to draw.
328 * @param {ImageRect=} opt_dstRect Rectangle in the canvas (whole canvas by 313 * @param {ImageRect=} opt_dstRect Rectangle in the canvas (whole canvas by
329 * default). 314 * default).
330 * @param {ImageRect=} opt_srcRect Rectangle in the image (whole image by 315 * @param {ImageRect=} opt_srcRect Rectangle in the image (whole image by
331 * default). 316 * default).
332 */ 317 */
333 ImageRect.drawImage = function(context, image, opt_dstRect, opt_srcRect) { 318 ImageRect.drawImage = function(context, image, opt_dstRect, opt_srcRect) {
334 opt_dstRect = opt_dstRect || new ImageRect(context.canvas); 319 opt_dstRect = opt_dstRect ||
335 opt_srcRect = opt_srcRect || new ImageRect(image); 320 ImageRect.createFromImage(assert(context.canvas));
321 opt_srcRect = opt_srcRect || ImageRect.createFromImage(image);
336 if (opt_dstRect.isEmpty() || opt_srcRect.isEmpty()) 322 if (opt_dstRect.isEmpty() || opt_srcRect.isEmpty())
337 return; 323 return;
338 context.drawImage(image, 324 context.drawImage(image,
339 opt_srcRect.left, opt_srcRect.top, opt_srcRect.width, opt_srcRect.height, 325 opt_srcRect.left, opt_srcRect.top, opt_srcRect.width, opt_srcRect.height,
340 opt_dstRect.left, opt_dstRect.top, opt_dstRect.width, opt_dstRect.height); 326 opt_dstRect.left, opt_dstRect.top, opt_dstRect.width, opt_dstRect.height);
341 }; 327 };
342 328
343 /** 329 /**
344 * Draw a box around the rectangle. 330 * Draw a box around the rectangle.
345 * @param {CanvasRenderingContext2D} context Context to draw. 331 * @param {!CanvasRenderingContext2D} context Context to draw.
346 * @param {ImageRect} rect Rectangle. 332 * @param {!ImageRect} rect Rectangle.
347 */ 333 */
348 ImageRect.outline = function(context, rect) { 334 ImageRect.outline = function(context, rect) {
349 context.strokeRect( 335 context.strokeRect(
350 rect.left - 0.5, rect.top - 0.5, rect.width + 1, rect.height + 1); 336 rect.left - 0.5, rect.top - 0.5, rect.width + 1, rect.height + 1);
351 }; 337 };
352 338
353 /** 339 /**
354 * Fill the rectangle. 340 * Fill the rectangle.
355 * @param {CanvasRenderingContext2D} context Context to draw. 341 * @param {!CanvasRenderingContext2D} context Context to draw.
356 * @param {ImageRect} rect Rectangle. 342 * @param {!ImageRect} rect Rectangle.
357 */ 343 */
358 ImageRect.fill = function(context, rect) { 344 ImageRect.fill = function(context, rect) {
359 context.fillRect(rect.left, rect.top, rect.width, rect.height); 345 context.fillRect(rect.left, rect.top, rect.width, rect.height);
360 }; 346 };
361 347
362 /** 348 /**
363 * Fills the space between the two rectangles. 349 * Fills the space between the two rectangles.
364 * @param {CanvasRenderingContext2D} context Context to draw. 350 * @param {!CanvasRenderingContext2D} context Context to draw.
365 * @param {ImageRect} inner Inner rectangle. 351 * @param {!ImageRect} inner Inner rectangle.
366 * @param {ImageRect} outer Outer rectangle. 352 * @param {!ImageRect} outer Outer rectangle.
367 */ 353 */
368 ImageRect.fillBetween = function(context, inner, outer) { 354 ImageRect.fillBetween = function(context, inner, outer) {
369 var innerRight = inner.left + inner.width; 355 var innerRight = inner.left + inner.width;
370 var innerBottom = inner.top + inner.height; 356 var innerBottom = inner.top + inner.height;
371 var outerRight = outer.left + outer.width; 357 var outerRight = outer.left + outer.width;
372 var outerBottom = outer.top + outer.height; 358 var outerBottom = outer.top + outer.height;
373 if (inner.top > outer.top) { 359 if (inner.top > outer.top) {
374 context.fillRect( 360 context.fillRect(
375 outer.left, outer.top, outer.width, inner.top - outer.top); 361 outer.left, outer.top, outer.width, inner.top - outer.top);
376 } 362 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 */ 395 */
410 Circle.prototype.inside = function(x, y) { 396 Circle.prototype.inside = function(x, y) {
411 x -= this.x; 397 x -= this.x;
412 y -= this.y; 398 y -= this.y;
413 return x * x + y * y <= this.squaredR; 399 return x * x + y * y <= this.squaredR;
414 }; 400 };
415 401
416 /** 402 /**
417 * Copy an image applying scaling and rotation. 403 * Copy an image applying scaling and rotation.
418 * 404 *
419 * @param {HTMLCanvasElement} dst Destination. 405 * @param {!HTMLCanvasElement} dst Destination.
420 * @param {HTMLCanvasElement|HTMLImageElement} src Source. 406 * @param {!(HTMLCanvasElement|HTMLImageElement)} src Source.
421 * @param {number} scaleX Y scale transformation. 407 * @param {number} scaleX Y scale transformation.
422 * @param {number} scaleY X scale transformation. 408 * @param {number} scaleY X scale transformation.
423 * @param {number} angle (in radians). 409 * @param {number} angle (in radians).
424 */ 410 */
425 ImageUtil.drawImageTransformed = function(dst, src, scaleX, scaleY, angle) { 411 ImageUtil.drawImageTransformed = function(dst, src, scaleX, scaleY, angle) {
426 var context = dst.getContext('2d'); 412 var context = dst.getContext('2d');
427 context.save(); 413 context.save();
428 context.translate(context.canvas.width / 2, context.canvas.height / 2); 414 context.translate(context.canvas.width / 2, context.canvas.height / 2);
429 context.rotate(angle); 415 context.rotate(angle);
430 context.scale(scaleX, scaleY); 416 context.scale(scaleX, scaleY);
431 context.drawImage(src, -src.width / 2, -src.height / 2); 417 context.drawImage(src, -src.width / 2, -src.height / 2);
432 context.restore(); 418 context.restore();
433 }; 419 };
434 420
435 /** 421 /**
436 * Adds or removes an attribute to/from an HTML element. 422 * Adds or removes an attribute to/from an HTML element.
437 * @param {HTMLElement} element To be applied to. 423 * @param {!HTMLElement} element To be applied to.
438 * @param {string} attribute Name of attribute. 424 * @param {string} attribute Name of attribute.
439 * @param {boolean} on True if add, false if remove. 425 * @param {boolean} on True if add, false if remove.
440 */ 426 */
441 ImageUtil.setAttribute = function(element, attribute, on) { 427 ImageUtil.setAttribute = function(element, attribute, on) {
442 if (on) 428 if (on)
443 element.setAttribute(attribute, ''); 429 element.setAttribute(attribute, '');
444 else 430 else
445 element.removeAttribute(attribute); 431 element.removeAttribute(attribute);
446 }; 432 };
447 433
448 /** 434 /**
449 * Adds or removes CSS class to/from an HTML element. 435 * Adds or removes CSS class to/from an HTML element.
450 * @param {HTMLElement} element To be applied to. 436 * @param {!HTMLElement} element To be applied to.
451 * @param {string} className Name of CSS class. 437 * @param {string} className Name of CSS class.
452 * @param {boolean} on True if add, false if remove. 438 * @param {boolean} on True if add, false if remove.
453 */ 439 */
454 ImageUtil.setClass = function(element, className, on) { 440 ImageUtil.setClass = function(element, className, on) {
455 var cl = element.classList; 441 var cl = element.classList;
456 if (on) 442 if (on)
457 cl.add(className); 443 cl.add(className);
458 else 444 else
459 cl.remove(className); 445 cl.remove(className);
460 }; 446 };
461 447
462 /** 448 /**
463 * ImageLoader loads an image from a given Entry into a canvas in two steps: 449 * ImageLoader loads an image from a given Entry into a canvas in two steps:
464 * 1. Loads the image into an HTMLImageElement. 450 * 1. Loads the image into an HTMLImageElement.
465 * 2. Copies pixels from HTMLImageElement to HTMLCanvasElement. This is done 451 * 2. Copies pixels from HTMLImageElement to HTMLCanvasElement. This is done
466 * stripe-by-stripe to avoid freezing up the UI. The transform is taken into 452 * stripe-by-stripe to avoid freezing up the UI. The transform is taken into
467 * account. 453 * account.
468 * 454 *
469 * @param {HTMLDocument} document Owner document. 455 * @param {!HTMLDocument} document Owner document.
470 * @constructor 456 * @constructor
457 * @struct
471 */ 458 */
472 ImageUtil.ImageLoader = function(document) { 459 ImageUtil.ImageLoader = function(document) {
473 this.document_ = document; 460 this.document_ = document;
474 this.image_ = new Image(); 461 this.image_ = new Image();
475 this.generation_ = 0; 462 this.generation_ = 0;
463
464 /**
465 * @type {number}
466 * @private
467 */
468 this.timeout_ = 0;
469
470 /**
471 * @type {?function(!HTMLCanvasElement, string=)}
472 * @private
473 */
474 this.callback_ = null;
475
476 /**
477 * @type {FileEntry}
478 * @private
479 */
480 this.entry_ = null;
476 }; 481 };
477 482
478 /** 483 /**
479 * Loads an image. 484 * Loads an image.
480 * TODO(mtomasz): Simplify, or even get rid of this class and merge with the 485 * TODO(mtomasz): Simplify, or even get rid of this class and merge with the
481 * ThumbnaiLoader class. 486 * ThumbnaiLoader class.
482 * 487 *
483 * @param {Gallery.Item} item Item representing the image to be loaded. 488 * @param {!Gallery.Item} item Item representing the image to be loaded.
484 * @param {function(!HTMLCanvasElement, string=)} callback Callback to be 489 * @param {function(!HTMLCanvasElement, string=)} callback Callback to be
485 * called when loaded. The second optional argument is an error identifier. 490 * called when loaded. The second optional argument is an error identifier.
486 * @param {number=} opt_delay Load delay in milliseconds, useful to let the 491 * @param {number=} opt_delay Load delay in milliseconds, useful to let the
487 * animations play out before the computation heavy image loading starts. 492 * animations play out before the computation heavy image loading starts.
488 */ 493 */
489 ImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) { 494 ImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) {
490 var entry = item.getEntry(); 495 var entry = item.getEntry();
491 496
492 this.cancel(); 497 this.cancel();
493 this.entry_ = entry; 498 this.entry_ = entry;
494 this.callback_ = callback; 499 this.callback_ = callback;
495 500
496 // The transform fetcher is not cancellable so we need a generation counter. 501 // The transform fetcher is not cancellable so we need a generation counter.
497 var generation = ++this.generation_; 502 var generation = ++this.generation_;
498 var onTransform = function(image, transform) { 503
504 /**
505 * @param {!HTMLImageElement} image Image to be transformed.
506 * @param {Object=} opt_transform Transformation.
507 */
508 var onTransform = function(image, opt_transform) {
499 if (generation === this.generation_) { 509 if (generation === this.generation_) {
500 this.convertImage_( 510 this.convertImage_(
501 image, transform || { scaleX: 1, scaleY: 1, rotate90: 0}); 511 image, opt_transform || { scaleX: 1, scaleY: 1, rotate90: 0});
502 } 512 }
503 }.bind(this); 513 };
514 onTransform = onTransform.bind(this);
504 515
516 /**
517 * @param {string=} opt_error Error.
518 */
505 var onError = function(opt_error) { 519 var onError = function(opt_error) {
506 this.image_.onerror = null; 520 this.image_.onerror = null;
507 this.image_.onload = null; 521 this.image_.onload = null;
508 var tmpCallback = this.callback_; 522 var tmpCallback = this.callback_;
509 this.callback_ = null; 523 this.callback_ = null;
510 var emptyCanvas = this.document_.createElement('canvas'); 524 var emptyCanvas = assertInstanceof(this.document_.createElement('canvas'),
525 HTMLCanvasElement);
511 emptyCanvas.width = 0; 526 emptyCanvas.width = 0;
512 emptyCanvas.height = 0; 527 emptyCanvas.height = 0;
513 tmpCallback(emptyCanvas, opt_error); 528 tmpCallback(emptyCanvas, opt_error);
514 }.bind(this); 529 };
530 onError = onError.bind(this);
515 531
516 var loadImage = function() { 532 var loadImage = function() {
517 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime')); 533 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime'));
518 this.timeout_ = null; 534 this.timeout_ = null;
519 535
520 this.image_.onload = function() { 536 this.image_.onload = function() {
521 this.image_.onerror = null; 537 this.image_.onerror = null;
522 this.image_.onload = null; 538 this.image_.onload = null;
523 item.getFetchedMedia().then(function(fetchedMediaMetadata) { 539 item.getFetchedMedia().then(function(fetchedMediaMetadata) {
524 onTransform(this.image_, fetchedMediaMetadata.imageTransform); 540 if ('imageTransform' in fetchedMediaMetadata) {
fukino 2014/12/11 11:28:15 Does "if (fetchedMediaMetadata.imageTransform)" ha
yawano 2014/12/12 01:11:33 Done. I didn't know that we can write in this way
541 onTransform(this.image_,
542 (/** @type {{imageTransform:Object}} */ (fetchedMediaMetadata))
543 .imageTransform);
544 } else {
545 onTransform(this.image_);
546 }
525 }.bind(this)).catch(function(error) { 547 }.bind(this)).catch(function(error) {
526 console.error(error.stack || error); 548 console.error(error.stack || error);
527 }); 549 });
528 }.bind(this); 550 }.bind(this);
529 551
530 // The error callback has an optional error argument, which in case of a 552 // The error callback has an optional error argument, which in case of a
531 // general error should not be specified 553 // general error should not be specified
532 this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR'); 554 this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR');
533 555
534 // Load the image directly. The query parameter is workaround for 556 // Load the image directly. The query parameter is workaround for
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
602 }; 624 };
603 625
604 /** 626 /**
605 * Stops loading image. 627 * Stops loading image.
606 */ 628 */
607 ImageUtil.ImageLoader.prototype.cancel = function() { 629 ImageUtil.ImageLoader.prototype.cancel = function() {
608 if (!this.callback_) return; 630 if (!this.callback_) return;
609 this.callback_ = null; 631 this.callback_ = null;
610 if (this.timeout_) { 632 if (this.timeout_) {
611 clearTimeout(this.timeout_); 633 clearTimeout(this.timeout_);
612 this.timeout_ = null; 634 this.timeout_ = 0;
613 } 635 }
614 if (this.image_) { 636 if (this.image_) {
615 this.image_.onload = function() {}; 637 this.image_.onload = function() {};
616 this.image_.onerror = function() {}; 638 this.image_.onerror = function() {};
617 this.image_.src = ''; 639 this.image_.src = '';
618 } 640 }
619 this.generation_++; // Silence the transform fetcher if it is in progress. 641 this.generation_++; // Silence the transform fetcher if it is in progress.
620 }; 642 };
621 643
622 /** 644 /**
623 * @param {HTMLImageElement} image Image to be transformed. 645 * @param {!HTMLImageElement} image Image to be transformed.
624 * @param {Object} transform transformation description to apply to the image. 646 * @param {!Object} transform transformation description to apply to the image.
625 * @private 647 * @private
626 */ 648 */
627 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { 649 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) {
628 var canvas = this.document_.createElement('canvas'); 650 var canvas = this.document_.createElement('canvas');
629 651
630 if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions. 652 if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions.
631 canvas.width = image.height; 653 canvas.width = image.height;
632 canvas.height = image.width; 654 canvas.height = image.width;
633 } else { 655 } else {
634 canvas.width = image.width; 656 canvas.width = image.width;
635 canvas.height = image.height; 657 canvas.height = image.height;
636 } 658 }
637 659
638 var context = canvas.getContext('2d'); 660 var context = canvas.getContext('2d');
639 context.save(); 661 context.save();
640 context.translate(canvas.width / 2, canvas.height / 2); 662 context.translate(canvas.width / 2, canvas.height / 2);
641 context.rotate(transform.rotate90 * Math.PI / 2); 663 context.rotate(transform.rotate90 * Math.PI / 2);
642 context.scale(transform.scaleX, transform.scaleY); 664 context.scale(transform.scaleX, transform.scaleY);
643 665
644 var stripCount = Math.ceil(image.width * image.height / (1 << 21)); 666 var stripCount = Math.ceil(image.width * image.height / (1 << 21));
645 var step = Math.max(16, Math.ceil(image.height / stripCount)) & 0xFFFFF0; 667 var step = Math.max(16, Math.ceil(image.height / stripCount)) & 0xFFFFF0;
646 668
647 this.copyStrip_(context, image, 0, step); 669 this.copyStrip_(context, image, 0, step);
648 }; 670 };
649 671
650 /** 672 /**
651 * @param {CanvasRenderingContext2D} context Context to draw. 673 * @param {!CanvasRenderingContext2D} context Context to draw.
652 * @param {HTMLImageElement} image Image to draw. 674 * @param {!HTMLImageElement} image Image to draw.
653 * @param {number} firstRow Number of the first pixel row to draw. 675 * @param {number} firstRow Number of the first pixel row to draw.
654 * @param {number} rowCount Count of pixel rows to draw. 676 * @param {number} rowCount Count of pixel rows to draw.
655 * @private 677 * @private
656 */ 678 */
657 ImageUtil.ImageLoader.prototype.copyStrip_ = function( 679 ImageUtil.ImageLoader.prototype.copyStrip_ = function(
658 context, image, firstRow, rowCount) { 680 context, image, firstRow, rowCount) {
659 var lastRow = Math.min(firstRow + rowCount, image.height); 681 var lastRow = Math.min(firstRow + rowCount, image.height);
660 682
661 context.drawImage( 683 context.drawImage(
662 image, 0, firstRow, image.width, lastRow - firstRow, 684 image, 0, firstRow, image.width, lastRow - firstRow,
663 -image.width / 2, firstRow - image.height / 2, 685 -image.width / 2, firstRow - image.height / 2,
664 image.width, lastRow - firstRow); 686 image.width, lastRow - firstRow);
665 687
666 if (lastRow === image.height) { 688 if (lastRow === image.height) {
667 context.restore(); 689 context.restore();
668 if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls. 690 if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls.
669 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime')); 691 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime'));
670 } 692 }
671 try { 693 try {
672 setTimeout(this.callback_, 0, context.canvas); 694 setTimeout(this.callback_, 0, context.canvas);
673 } catch (e) { 695 } catch (e) {
674 console.error(e); 696 console.error(e);
675 } 697 }
676 this.callback_ = null; 698 this.callback_ = null;
677 } else { 699 } else {
678 var self = this; 700 var self = this;
679 this.timeout_ = setTimeout( 701 this.timeout_ = setTimeout(
680 function() { 702 function() {
681 self.timeout_ = null; 703 self.timeout_ = 0;
682 self.copyStrip_(context, image, lastRow, rowCount); 704 self.copyStrip_(context, image, lastRow, rowCount);
683 }, 0); 705 }, 0);
684 } 706 }
685 }; 707 };
686 708
687 /** 709 /**
688 * @param {HTMLElement} element To remove children from. 710 * @param {!HTMLElement} element To remove children from.
689 */ 711 */
690 ImageUtil.removeChildren = function(element) { 712 ImageUtil.removeChildren = function(element) {
691 element.textContent = ''; 713 element.textContent = '';
692 }; 714 };
693 715
694 /** 716 /**
695 * @param {string} name File name (with extension). 717 * @param {string} name File name (with extension).
696 * @return {string} File name without extension. 718 * @return {string} File name without extension.
697 */ 719 */
698 ImageUtil.getDisplayNameFromName = function(name) { 720 ImageUtil.getDisplayNameFromName = function(name) {
(...skipping 11 matching lines...) Expand all
710 ImageUtil.getExtensionFromFullName = function(name) { 732 ImageUtil.getExtensionFromFullName = function(name) {
711 var index = name.lastIndexOf('.'); 733 var index = name.lastIndexOf('.');
712 if (index !== -1) 734 if (index !== -1)
713 return name.substring(index); 735 return name.substring(index);
714 else 736 else
715 return ''; 737 return '';
716 }; 738 };
717 739
718 /** 740 /**
719 * Metrics (from metrics.js) itnitialized by the File Manager from owner frame. 741 * Metrics (from metrics.js) itnitialized by the File Manager from owner frame.
720 * @type {Object?} 742 * @type {Object}
721 */ 743 */
722 ImageUtil.metrics = null; 744 ImageUtil.metrics = null;
723 745
724 /** 746 /**
725 * @param {string} name Local name. 747 * @param {string} name Local name.
726 * @return {string} Full name. 748 * @return {string} Full name.
727 */ 749 */
728 ImageUtil.getMetricName = function(name) { 750 ImageUtil.getMetricName = function(name) {
729 return 'PhotoEditor.' + name; 751 return 'PhotoEditor.' + name;
730 }; 752 };
731 753
732 /** 754 /**
733 * Used for metrics reporting, keep in sync with the histogram description. 755 * Used for metrics reporting, keep in sync with the histogram description.
756 * @type {Array.<string>}
757 * @const
734 */ 758 */
735 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; 759 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp'];
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698