Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 /** | 5 /** |
| 6 * Loads a thumbnail using provided url. In CANVAS mode, loaded images | 6 * Loads a thumbnail using provided url. In CANVAS mode, loaded images |
| 7 * are attached as <canvas> element, while in IMAGE mode as <img>. | 7 * are attached as <canvas> element, while in IMAGE mode as <img>. |
| 8 * <canvas> renders faster than <img>, however has bigger memory overhead. | 8 * <canvas> renders faster than <img>, however has bigger memory overhead. |
| 9 * | 9 * |
| 10 * @param {Entry} entry File entry. | 10 * @param {Entry} entry File entry. |
| (...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 317 }.bind(this)).then(function(result) { | 317 }.bind(this)).then(function(result) { |
| 318 if (!this.transform_) | 318 if (!this.transform_) |
| 319 return result; | 319 return result; |
| 320 else | 320 else |
| 321 return this.applyTransformToDataUrl_( | 321 return this.applyTransformToDataUrl_( |
| 322 this.transform_, result.data, result.width, result.height); | 322 this.transform_, result.data, result.width, result.height); |
| 323 }.bind(this)); | 323 }.bind(this)); |
| 324 }; | 324 }; |
| 325 | 325 |
| 326 /** | 326 /** |
| 327 * Renders an Image to canvas applying an image transformation. | |
| 328 * @param {Image} image Original image. Can be an unattached one to an elment. | |
| 329 * @param {number} width width of the original image | |
|
oka
2017/01/16 12:30:54
nit: Width of the ...? (instead of width).
| |
| 330 * @param {number} height height of the original image | |
|
oka
2017/01/16 12:30:54
nit: Height of the ...? (instead of height).
| |
| 331 * @param {{scaleX:number, scaleY:number, rotate90: number}} transform | |
| 332 * Transform. | |
| 333 * @param {!Object} canvas | |
| 334 */ | |
| 335 ThumbnailLoader.prototype.renderImageToCanvasWithTransform_ = function( | |
| 336 image, width, height, transform, canvas) { | |
| 337 var scaleX = transform.scaleX; | |
| 338 var scaleY = transform.scaleY; | |
| 339 var rotate90 = transform.rotate90; | |
| 340 | |
| 341 assert(scaleX === 1 || scaleX === -1); | |
| 342 assert(scaleY === 1 || scaleY === -1); | |
| 343 assert(rotate90 === 0 || rotate90 === 1); | |
| 344 | |
| 345 var context = canvas.getContext('2d'); | |
| 346 | |
| 347 canvas.width = rotate90 === 1 ? height : width; | |
| 348 canvas.height = rotate90 === 1 ? width : height; | |
| 349 | |
| 350 // Scale transformation should be applied before rotate transformation. | |
| 351 // i.e. When matrices for scale and rotate are A and B, transformation matrix | |
| 352 // should be BA. | |
| 353 | |
| 354 // Rotate 90 degree at center. | |
| 355 if (rotate90 === 1) { | |
| 356 context.translate(height, 0); | |
| 357 context.rotate(Math.PI / 2); | |
| 358 } | |
| 359 | |
| 360 // Flip X and Y. | |
| 361 context.translate(scaleX === -1 ? width : 0, scaleY === -1 ? height : 0); | |
| 362 context.scale(scaleX, scaleY); | |
| 363 context.drawImage(image, 0, 0); | |
| 364 }; | |
| 365 | |
| 366 /** | |
| 327 * Applies transform to data url. | 367 * Applies transform to data url. |
| 328 * | 368 * |
| 329 * @param {{scaleX:number, scaleY:number, rotate90: number}} transform | 369 * @param {{scaleX:number, scaleY:number, rotate90: number}} transform |
| 330 * Transform. | 370 * Transform. |
| 331 * @param {string} dataUrl Data url. | 371 * @param {string} dataUrl Data url. |
| 332 * @param {number} width Width. | 372 * @param {number} width Width. |
| 333 * @param {number} height Height. | 373 * @param {number} height Height. |
| 334 * @return {!Promise<{data:string, width:number, height:number}>} A promise | 374 * @return {!Promise<{data:string, width:number, height:number}>} A promise |
| 335 * which is resolved with dataUrl and its width and height. | 375 * which is resolved with dataUrl and its width and height. |
| 336 * @private | 376 * @private |
| 337 */ | 377 */ |
| 338 ThumbnailLoader.prototype.applyTransformToDataUrl_ = function( | 378 ThumbnailLoader.prototype.applyTransformToDataUrl_ = function( |
| 339 transform, dataUrl, width, height) { | 379 transform, dataUrl, width, height) { |
| 340 var image = new Image(); | 380 var image = new Image(); |
| 341 var scaleX = this.transform_.scaleX; | |
| 342 var scaleY = this.transform_.scaleY; | |
| 343 var rotate90 = this.transform_.rotate90; | |
| 344 | |
| 345 assert(scaleX === 1 || scaleX === -1); | |
| 346 assert(scaleY === 1 || scaleY === -1); | |
| 347 assert(rotate90 === 0 || rotate90 === 1); | |
| 348 | 381 |
| 349 return new Promise(function(resolve, reject) { | 382 return new Promise(function(resolve, reject) { |
| 350 // Decode image for transformation. | 383 // Decode image for transformation. |
| 351 image.onload = resolve; | 384 image.onload = resolve; |
| 352 image.onerror = reject; | 385 image.onerror = reject; |
| 353 image.src = dataUrl; | 386 image.src = dataUrl; |
| 354 }).then(function() { | 387 }).then(function() { |
| 355 // Apply transform. Scale transformation should be applied before rotate | |
| 356 // transformation. i.e. When matrices for scale and rotate are A and B, | |
| 357 // transformation matrix should be BA. | |
| 358 var canvas = document.createElement('canvas'); | 388 var canvas = document.createElement('canvas'); |
| 359 var context = canvas.getContext('2d'); | 389 this.renderImageToCanvasWithTransform_( |
| 360 | 390 image, width, height, this.transform_, canvas); |
| 361 canvas.width = rotate90 === 1 ? height : width; | |
| 362 canvas.height = rotate90 === 1 ? width : height; | |
| 363 | |
| 364 // Rotate 90 degree at center. | |
| 365 if (rotate90 === 1) { | |
| 366 context.translate(height, 0); | |
| 367 context.rotate(Math.PI / 2); | |
| 368 } | |
| 369 | |
| 370 // Flip X and Y. | |
| 371 context.translate(scaleX === -1 ? width : 0, scaleY === -1 ? height : 0); | |
| 372 context.scale(scaleX, scaleY); | |
| 373 | |
| 374 context.drawImage(image, 0, 0); | |
| 375 | |
| 376 return { | 391 return { |
| 377 data: canvas.toDataURL('image/png'), | 392 data: canvas.toDataURL('image/png'), |
| 378 width: canvas.width, | 393 width: canvas.width, |
| 379 height: canvas.height | 394 height: canvas.height |
| 380 }; | 395 }; |
| 381 }.bind(this)); | 396 }.bind(this)); |
| 382 } | 397 } |
| 383 | 398 |
| 384 /** | 399 /** |
| 385 * Cancels loading the current image. | 400 * Cancels loading the current image. |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 466 * @private | 481 * @private |
| 467 */ | 482 */ |
| 468 ThumbnailLoader.prototype.renderMedia_ = function() { | 483 ThumbnailLoader.prototype.renderMedia_ = function() { |
| 469 if (this.loaderType_ !== ThumbnailLoader.LoaderType.CANVAS) | 484 if (this.loaderType_ !== ThumbnailLoader.LoaderType.CANVAS) |
| 470 return; | 485 return; |
| 471 | 486 |
| 472 if (!this.canvas_) | 487 if (!this.canvas_) |
| 473 this.canvas_ = document.createElement('canvas'); | 488 this.canvas_ = document.createElement('canvas'); |
| 474 | 489 |
| 475 // Copy the image to a canvas if the canvas is outdated. | 490 // Copy the image to a canvas if the canvas is outdated. |
| 491 // At this point, image transformation is not applied because we attach style | |
| 492 // attribute to an img element in attachImage() instead. | |
| 476 if (!this.canvasUpToDate_) { | 493 if (!this.canvasUpToDate_) { |
| 477 this.canvas_.width = this.image_.width; | 494 this.canvas_.width = this.image_.width; |
| 478 this.canvas_.height = this.image_.height; | 495 this.canvas_.height = this.image_.height; |
| 479 var context = this.canvas_.getContext('2d'); | 496 var context = this.canvas_.getContext('2d'); |
| 480 context.drawImage(this.image_, 0, 0); | 497 context.drawImage(this.image_, 0, 0); |
| 481 this.canvasUpToDate_ = true; | 498 this.canvasUpToDate_ = true; |
| 482 } | 499 } |
| 483 }; | 500 }; |
| 484 | 501 |
| 485 /** | 502 /** |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 514 box.textContent = ''; | 531 box.textContent = ''; |
| 515 box.appendChild(attachableMedia); | 532 box.appendChild(attachableMedia); |
| 516 } | 533 } |
| 517 | 534 |
| 518 if (!this.taskId_) | 535 if (!this.taskId_) |
| 519 attachableMedia.classList.add('cached'); | 536 attachableMedia.classList.add('cached'); |
| 520 }; | 537 }; |
| 521 | 538 |
| 522 /** | 539 /** |
| 523 * Gets the loaded image. | 540 * Gets the loaded image. |
| 524 * TODO(mtomasz): Apply transformations. | |
| 525 * | 541 * |
| 526 * @return {Image|HTMLCanvasElement} Either image or a canvas object. | 542 * @return {Image|HTMLCanvasElement} Either image or a canvas object. |
| 527 */ | 543 */ |
| 528 ThumbnailLoader.prototype.getImage = function() { | 544 ThumbnailLoader.prototype.getImage = function() { |
| 529 this.renderMedia_(); | 545 this.renderMedia_(); |
| 530 return this.loaderType_ === ThumbnailLoader.LoaderType.CANVAS ? this.canvas_ : | 546 if (this.loaderType_ === ThumbnailLoader.LoaderType.IMAGE) |
| 531 this.image_; | 547 // TODO(yamaguchi): Fix image orientation in case of detached image loaded |
| 548 // in IMAGE mode. Either apply transformation here or return | |
| 549 // this.transform_ as well. | |
| 550 return this.image_; | |
| 551 if (this.transform_) { | |
| 552 this.renderImageToCanvasWithTransform_( | |
| 553 this.image_, this.image_.width, this.image_.height, | |
| 554 this.transform_, this.canvas_); | |
| 555 } | |
| 556 return this.canvas_; | |
| 532 }; | 557 }; |
| 533 | 558 |
| 534 /** | 559 /** |
| 535 * Update the image style to fit/fill the container. | 560 * Update the image style to fit/fill the container. |
| 536 * | 561 * |
| 537 * Using webkit center packing does not align the image properly, so we need | 562 * Using webkit center packing does not align the image properly, so we need |
| 538 * to wait until the image loads and its dimensions are known, then manually | 563 * to wait until the image loads and its dimensions are known, then manually |
| 539 * position it at the center. | 564 * position it at the center. |
| 540 * | 565 * |
| 541 * @param {Element} box Containing element. | 566 * @param {Element} box Containing element. |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 612 | 637 |
| 613 function percent(fraction) { | 638 function percent(fraction) { |
| 614 return (fraction * 100).toFixed(2) + '%'; | 639 return (fraction * 100).toFixed(2) + '%'; |
| 615 } | 640 } |
| 616 | 641 |
| 617 img.style.width = percent(fractionX); | 642 img.style.width = percent(fractionX); |
| 618 img.style.height = percent(fractionY); | 643 img.style.height = percent(fractionY); |
| 619 img.style.left = percent((1 - fractionX) / 2); | 644 img.style.left = percent((1 - fractionX) / 2); |
| 620 img.style.top = percent((1 - fractionY) / 2); | 645 img.style.top = percent((1 - fractionY) / 2); |
| 621 }; | 646 }; |
| OLD | NEW |