| 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 | |
| 330 * @param {number} height height of the original image | |
| 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 /** | |
| 367 * Applies transform to data url. | 327 * Applies transform to data url. |
| 368 * | 328 * |
| 369 * @param {{scaleX:number, scaleY:number, rotate90: number}} transform | 329 * @param {{scaleX:number, scaleY:number, rotate90: number}} transform |
| 370 * Transform. | 330 * Transform. |
| 371 * @param {string} dataUrl Data url. | 331 * @param {string} dataUrl Data url. |
| 372 * @param {number} width Width. | 332 * @param {number} width Width. |
| 373 * @param {number} height Height. | 333 * @param {number} height Height. |
| 374 * @return {!Promise<{data:string, width:number, height:number}>} A promise | 334 * @return {!Promise<{data:string, width:number, height:number}>} A promise |
| 375 * which is resolved with dataUrl and its width and height. | 335 * which is resolved with dataUrl and its width and height. |
| 376 * @private | 336 * @private |
| 377 */ | 337 */ |
| 378 ThumbnailLoader.prototype.applyTransformToDataUrl_ = function( | 338 ThumbnailLoader.prototype.applyTransformToDataUrl_ = function( |
| 379 transform, dataUrl, width, height) { | 339 transform, dataUrl, width, height) { |
| 380 var image = new Image(); | 340 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); |
| 381 | 348 |
| 382 return new Promise(function(resolve, reject) { | 349 return new Promise(function(resolve, reject) { |
| 383 // Decode image for transformation. | 350 // Decode image for transformation. |
| 384 image.onload = resolve; | 351 image.onload = resolve; |
| 385 image.onerror = reject; | 352 image.onerror = reject; |
| 386 image.src = dataUrl; | 353 image.src = dataUrl; |
| 387 }).then(function() { | 354 }).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. |
| 388 var canvas = document.createElement('canvas'); | 358 var canvas = document.createElement('canvas'); |
| 389 this.renderImageToCanvasWithTransform_( | 359 var context = canvas.getContext('2d'); |
| 390 image, width, height, this.transform_, canvas); | 360 |
| 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 |
| 391 return { | 376 return { |
| 392 data: canvas.toDataURL('image/png'), | 377 data: canvas.toDataURL('image/png'), |
| 393 width: canvas.width, | 378 width: canvas.width, |
| 394 height: canvas.height | 379 height: canvas.height |
| 395 }; | 380 }; |
| 396 }.bind(this)); | 381 }.bind(this)); |
| 397 } | 382 } |
| 398 | 383 |
| 399 /** | 384 /** |
| 400 * Cancels loading the current image. | 385 * Cancels loading the current image. |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 481 * @private | 466 * @private |
| 482 */ | 467 */ |
| 483 ThumbnailLoader.prototype.renderMedia_ = function() { | 468 ThumbnailLoader.prototype.renderMedia_ = function() { |
| 484 if (this.loaderType_ !== ThumbnailLoader.LoaderType.CANVAS) | 469 if (this.loaderType_ !== ThumbnailLoader.LoaderType.CANVAS) |
| 485 return; | 470 return; |
| 486 | 471 |
| 487 if (!this.canvas_) | 472 if (!this.canvas_) |
| 488 this.canvas_ = document.createElement('canvas'); | 473 this.canvas_ = document.createElement('canvas'); |
| 489 | 474 |
| 490 // Copy the image to a canvas if the canvas is outdated. | 475 // 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. | |
| 493 if (!this.canvasUpToDate_) { | 476 if (!this.canvasUpToDate_) { |
| 494 this.canvas_.width = this.image_.width; | 477 this.canvas_.width = this.image_.width; |
| 495 this.canvas_.height = this.image_.height; | 478 this.canvas_.height = this.image_.height; |
| 496 var context = this.canvas_.getContext('2d'); | 479 var context = this.canvas_.getContext('2d'); |
| 497 context.drawImage(this.image_, 0, 0); | 480 context.drawImage(this.image_, 0, 0); |
| 498 this.canvasUpToDate_ = true; | 481 this.canvasUpToDate_ = true; |
| 499 } | 482 } |
| 500 }; | 483 }; |
| 501 | 484 |
| 502 /** | 485 /** |
| (...skipping 28 matching lines...) Expand all Loading... |
| 531 box.textContent = ''; | 514 box.textContent = ''; |
| 532 box.appendChild(attachableMedia); | 515 box.appendChild(attachableMedia); |
| 533 } | 516 } |
| 534 | 517 |
| 535 if (!this.taskId_) | 518 if (!this.taskId_) |
| 536 attachableMedia.classList.add('cached'); | 519 attachableMedia.classList.add('cached'); |
| 537 }; | 520 }; |
| 538 | 521 |
| 539 /** | 522 /** |
| 540 * Gets the loaded image. | 523 * Gets the loaded image. |
| 524 * TODO(mtomasz): Apply transformations. |
| 541 * | 525 * |
| 542 * @return {Image|HTMLCanvasElement} Either image or a canvas object. | 526 * @return {Image|HTMLCanvasElement} Either image or a canvas object. |
| 543 */ | 527 */ |
| 544 ThumbnailLoader.prototype.getImage = function() { | 528 ThumbnailLoader.prototype.getImage = function() { |
| 545 this.renderMedia_(); | 529 this.renderMedia_(); |
| 546 if (this.loaderType_ === ThumbnailLoader.LoaderType.IMAGE) | 530 return this.loaderType_ === ThumbnailLoader.LoaderType.CANVAS ? this.canvas_ : |
| 547 // TODO(yamaguchi): Fix image orientation in case of detached image loaded | 531 this.image_; |
| 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_; | |
| 557 }; | 532 }; |
| 558 | 533 |
| 559 /** | 534 /** |
| 560 * Update the image style to fit/fill the container. | 535 * Update the image style to fit/fill the container. |
| 561 * | 536 * |
| 562 * Using webkit center packing does not align the image properly, so we need | 537 * Using webkit center packing does not align the image properly, so we need |
| 563 * to wait until the image loads and its dimensions are known, then manually | 538 * to wait until the image loads and its dimensions are known, then manually |
| 564 * position it at the center. | 539 * position it at the center. |
| 565 * | 540 * |
| 566 * @param {Element} box Containing element. | 541 * @param {Element} box Containing element. |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 637 | 612 |
| 638 function percent(fraction) { | 613 function percent(fraction) { |
| 639 return (fraction * 100).toFixed(2) + '%'; | 614 return (fraction * 100).toFixed(2) + '%'; |
| 640 } | 615 } |
| 641 | 616 |
| 642 img.style.width = percent(fractionX); | 617 img.style.width = percent(fractionX); |
| 643 img.style.height = percent(fractionY); | 618 img.style.height = percent(fractionY); |
| 644 img.style.left = percent((1 - fractionX) / 2); | 619 img.style.left = percent((1 - fractionX) / 2); |
| 645 img.style.top = percent((1 - fractionY) / 2); | 620 img.style.top = percent((1 - fractionY) / 2); |
| 646 }; | 621 }; |
| OLD | NEW |