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 // Namespace object for the utilities. | 5 // Namespace object for the utilities. |
| 6 var ImageUtil = {}; | 6 var ImageUtil = {}; |
| 7 | 7 |
| 8 /** | 8 /** |
| 9 * Performance trace. | 9 * Performance trace. |
| 10 */ | 10 */ |
| (...skipping 422 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 433 */ | 433 */ |
| 434 ImageUtil.ImageLoader = function(document, metadataModel) { | 434 ImageUtil.ImageLoader = function(document, metadataModel) { |
| 435 this.document_ = document; | 435 this.document_ = document; |
| 436 | 436 |
| 437 /** | 437 /** |
| 438 * @private {!MetadataModel} | 438 * @private {!MetadataModel} |
| 439 * @const | 439 * @const |
| 440 */ | 440 */ |
| 441 this.metadataModel_ = metadataModel; | 441 this.metadataModel_ = metadataModel; |
| 442 | 442 |
| 443 this.image_ = new Image(); | |
| 444 this.generation_ = 0; | 443 this.generation_ = 0; |
| 445 | 444 |
| 446 /** | 445 /** |
| 447 * @type {number} | 446 * @type {number} |
| 448 * @private | 447 * @private |
| 449 */ | 448 */ |
| 450 this.timeout_ = 0; | 449 this.timeout_ = 0; |
| 451 | 450 |
| 452 /** | 451 /** |
| 453 * @type {?function(!HTMLCanvasElement, string=)} | 452 * @type {?function(!HTMLCanvasElement, string=)} |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 473 * @param {number=} opt_delay Load delay in milliseconds, useful to let the | 472 * @param {number=} opt_delay Load delay in milliseconds, useful to let the |
| 474 * animations play out before the computation heavy image loading starts. | 473 * animations play out before the computation heavy image loading starts. |
| 475 */ | 474 */ |
| 476 ImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) { | 475 ImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) { |
| 477 var entry = item.getEntry(); | 476 var entry = item.getEntry(); |
| 478 | 477 |
| 479 this.cancel(); | 478 this.cancel(); |
| 480 this.entry_ = entry; | 479 this.entry_ = entry; |
| 481 this.callback_ = callback; | 480 this.callback_ = callback; |
| 482 | 481 |
| 483 var targetImage = this.image_; | 482 var targetImage = assertInstanceof(this.document_.createElement('img'), |
| 483 HTMLImageElement); | |
| 484 // The transform fetcher is not cancellable so we need a generation counter. | 484 // The transform fetcher is not cancellable so we need a generation counter. |
| 485 var generation = ++this.generation_; | 485 var generation = ++this.generation_; |
| 486 | 486 |
| 487 /** | 487 /** |
| 488 * @param {!HTMLImageElement} image Image to be transformed. | 488 * @param {!HTMLImageElement} image Image to be transformed. |
| 489 * @param {Object=} opt_transform Transformation. | 489 * @param {Object=} opt_transform Transformation. |
| 490 */ | 490 */ |
| 491 var onTransform = function(image, opt_transform) { | 491 var onTransform = function(image, opt_transform) { |
| 492 if (generation === this.generation_) { | 492 if (generation === this.generation_) { |
| 493 this.convertImage_( | 493 this.convertImage_(image, opt_transform); |
| 494 image, opt_transform || { scaleX: 1, scaleY: 1, rotate90: 0}); | |
| 495 } | 494 } |
| 496 }; | 495 }; |
| 497 onTransform = onTransform.bind(this); | 496 onTransform = onTransform.bind(this); |
| 498 | 497 |
| 499 /** | 498 /** |
| 500 * @param {string=} opt_error Error. | 499 * @param {string=} opt_error Error. |
| 501 */ | 500 */ |
| 502 var onError = function(opt_error) { | 501 var onError = function(opt_error) { |
| 503 targetImage.onerror = null; | 502 targetImage.onerror = null; |
| 504 targetImage.onload = null; | 503 targetImage.onload = null; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 515 var loadImage = function(url) { | 514 var loadImage = function(url) { |
| 516 if (generation !== this.generation_) | 515 if (generation !== this.generation_) |
| 517 return; | 516 return; |
| 518 | 517 |
| 519 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime')); | 518 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime')); |
| 520 this.timeout_ = 0; | 519 this.timeout_ = 0; |
| 521 | 520 |
| 522 targetImage.onload = function() { | 521 targetImage.onload = function() { |
| 523 targetImage.onerror = null; | 522 targetImage.onerror = null; |
| 524 targetImage.onload = null; | 523 targetImage.onload = null; |
| 525 this.metadataModel_.get([entry], ['contentImageTransform']).then( | 524 this.metadataModel_.get([entry], ['contentImageTransform']).then( |
|
yawano
2016/01/21 08:08:14
nit: How about to add generation check here not to
ryoh
2016/01/21 08:42:31
It sounds nice. I will add checks.
| |
| 526 function(metadataItems) { | 525 function(metadataItems) { |
| 527 onTransform(targetImage, metadataItems[0].contentImageTransform); | 526 onTransform(targetImage, metadataItems[0].contentImageTransform); |
| 528 }.bind(this)); | 527 }.bind(this)); |
| 529 }.bind(this); | 528 }.bind(this); |
| 530 | 529 |
| 531 // The error callback has an optional error argument, which in case of a | 530 // The error callback has an optional error argument, which in case of a |
| 532 // general error should not be specified | 531 // general error should not be specified |
| 533 targetImage.onerror = onError.bind(null, 'GALLERY_IMAGE_ERROR'); | 532 targetImage.onerror = onError.bind(null, 'GALLERY_IMAGE_ERROR'); |
| 534 | 533 |
| 535 targetImage.src = url; | 534 targetImage.src = url; |
| 536 }.bind(this); | 535 }.bind(this); |
| 537 | 536 |
| 538 // Loads the image. If already loaded, then forces a reload. | 537 // Loads the image. If already loaded, then forces a reload. |
| 539 var startLoad = function() { | 538 var startLoad = function() { |
| 540 if (generation !== this.generation_) | 539 if (generation !== this.generation_) |
| 541 return; | 540 return; |
| 542 | 541 |
| 543 // Target current image. | |
| 544 targetImage = this.image_; | |
| 545 | |
| 546 // Obtain target URL. | 542 // Obtain target URL. |
| 547 if (FileType.isRaw(entry)) { | 543 if (FileType.isRaw(entry)) { |
| 548 var timestamp = | 544 var timestamp = |
| 549 item.getMetadataItem() && | 545 item.getMetadataItem() && |
| 550 item.getMetadataItem().modificationTime && | 546 item.getMetadataItem().modificationTime && |
| 551 item.getMetadataItem().modificationTime.getTime(); | 547 item.getMetadataItem().modificationTime.getTime(); |
| 552 ImageLoaderClient.getInstance().load(entry.toURL(), function(result) { | 548 ImageLoaderClient.getInstance().load(entry.toURL(), function(result) { |
| 553 if (generation !== this.generation_) | 549 if (generation !== this.generation_) |
| 554 return; | 550 return; |
| 555 if (result.status === 'success') | 551 if (result.status === 'success') |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 603 * Stops loading image. | 599 * Stops loading image. |
| 604 */ | 600 */ |
| 605 ImageUtil.ImageLoader.prototype.cancel = function() { | 601 ImageUtil.ImageLoader.prototype.cancel = function() { |
| 606 if (!this.callback_) | 602 if (!this.callback_) |
| 607 return; | 603 return; |
| 608 this.callback_ = null; | 604 this.callback_ = null; |
| 609 if (this.timeout_) { | 605 if (this.timeout_) { |
| 610 clearTimeout(this.timeout_); | 606 clearTimeout(this.timeout_); |
| 611 this.timeout_ = 0; | 607 this.timeout_ = 0; |
| 612 } | 608 } |
| 613 if (this.image_) { | |
| 614 this.image_.onload = function() {}; | |
|
yawano
2016/01/21 05:14:38
Sorry, I didn't pointed this out in the last revie
ryoh
2016/01/21 07:28:53
I think it's controlled by "this.generation_" to c
yawano
2016/01/21 08:08:14
Yes, this.generation_ works. I've missed the last
| |
| 615 this.image_.onerror = function() {}; | |
| 616 // Force to free internal image by assigning empty image. | |
| 617 this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' + | |
| 618 'AAABAAEAAAICTAEAOw=='; | |
| 619 this.image_ = document.createElement('img'); | |
| 620 } | |
| 621 this.generation_++; // Silence the transform fetcher if it is in progress. | 609 this.generation_++; // Silence the transform fetcher if it is in progress. |
| 622 }; | 610 }; |
| 623 | 611 |
| 624 /** | 612 /** |
| 625 * @param {!HTMLImageElement} image Image to be transformed. | 613 * @param {!HTMLImageElement} image Image to be transformed. |
| 626 * @param {!Object} transform transformation description to apply to the image. | 614 * @param {!Object} transform transformation description to apply to the image. |
| 627 * @private | 615 * @private |
| 628 */ | 616 */ |
| 629 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { | 617 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { |
| 618 if (!transform || | |
| 619 (transform.rotate90 === 0 && | |
| 620 transform.scaleX === 1 && | |
| 621 transform.scaleY === 1)) { | |
| 622 setTimeout(this.callback_, 0, image); | |
| 623 this.callback_ = null; | |
| 624 return; | |
| 625 } | |
| 630 var canvas = this.document_.createElement('canvas'); | 626 var canvas = this.document_.createElement('canvas'); |
| 631 | 627 |
| 632 if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions. | 628 if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions. |
| 633 canvas.width = image.height; | 629 canvas.width = image.height; |
| 634 canvas.height = image.width; | 630 canvas.height = image.width; |
| 635 } else { | 631 } else { |
| 636 canvas.width = image.width; | 632 canvas.width = image.width; |
| 637 canvas.height = image.height; | 633 canvas.height = image.height; |
| 638 } | 634 } |
| 639 | 635 |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 663 context.drawImage( | 659 context.drawImage( |
| 664 image, 0, firstRow, image.width, lastRow - firstRow, | 660 image, 0, firstRow, image.width, lastRow - firstRow, |
| 665 -image.width / 2, firstRow - image.height / 2, | 661 -image.width / 2, firstRow - image.height / 2, |
| 666 image.width, lastRow - firstRow); | 662 image.width, lastRow - firstRow); |
| 667 | 663 |
| 668 if (lastRow === image.height) { | 664 if (lastRow === image.height) { |
| 669 context.restore(); | 665 context.restore(); |
| 670 if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls. | 666 if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls. |
| 671 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime')); | 667 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime')); |
| 672 } | 668 } |
| 673 try { | 669 setTimeout(this.callback_, 0, context.canvas); |
| 674 setTimeout(this.callback_, 0, context.canvas); | |
| 675 } catch (e) { | |
| 676 console.error(e); | |
| 677 } | |
| 678 this.callback_ = null; | 670 this.callback_ = null; |
| 679 } else { | 671 } else { |
| 680 var self = this; | 672 var self = this; |
| 681 this.timeout_ = setTimeout( | 673 this.timeout_ = setTimeout( |
| 682 function() { | 674 function() { |
| 683 self.timeout_ = 0; | 675 self.timeout_ = 0; |
| 684 self.copyStrip_(context, image, lastRow, rowCount); | 676 self.copyStrip_(context, image, lastRow, rowCount); |
| 685 }, 0); | 677 }, 0); |
| 686 } | 678 } |
| 687 }; | 679 }; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 725 | 717 |
| 726 /** | 718 /** |
| 727 * @param {string} name Local name. | 719 * @param {string} name Local name. |
| 728 * @return {string} Full name. | 720 * @return {string} Full name. |
| 729 */ | 721 */ |
| 730 ImageUtil.getMetricName = function(name) { | 722 ImageUtil.getMetricName = function(name) { |
| 731 return 'PhotoEditor.' + name; | 723 return 'PhotoEditor.' + name; |
| 732 }; | 724 }; |
| 733 | 725 |
| 734 /** | 726 /** |
| 727 * Ensures argument is canvas. If it's not, creates new canvas and copy. | |
| 728 * | |
| 729 * @param {!HTMLCanvasElement|!HTMLImageElement} imgOrCanvas image or canvas | |
| 730 * element | |
| 731 * @return {!HTMLCanvasElement} canvas. | |
| 732 */ | |
| 733 ImageUtil.ensureCanvas = function(imgOrCanvas) { | |
| 734 if(imgOrCanvas.tagName === 'canvas') { | |
| 735 return assertInstanceof(imgOrCanvas, HTMLCanvasElement); | |
| 736 } | |
| 737 var canvas = assertInstanceof(document.createElement('canvas'), | |
| 738 HTMLCanvasElement); | |
| 739 canvas.width = imgOrCanvas.width; | |
| 740 canvas.height = imgOrCanvas.height; | |
| 741 var context = canvas.getContext('2d'); | |
| 742 context.drawImage(imgOrCanvas, 0, 0); | |
| 743 return canvas; | |
| 744 }; | |
| 745 | |
| 746 /** | |
| 735 * Used for metrics reporting, keep in sync with the histogram description. | 747 * Used for metrics reporting, keep in sync with the histogram description. |
| 736 * @type {Array<string>} | 748 * @type {Array<string>} |
| 737 * @const | 749 * @const |
| 738 */ | 750 */ |
| 739 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; | 751 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; |
| OLD | NEW |