| 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 462 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 473 * @param {number=} opt_delay Load delay in milliseconds, useful to let the | 473 * @param {number=} opt_delay Load delay in milliseconds, useful to let the |
| 474 * animations play out before the computation heavy image loading starts. | 474 * animations play out before the computation heavy image loading starts. |
| 475 */ | 475 */ |
| 476 ImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) { | 476 ImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) { |
| 477 var entry = item.getEntry(); | 477 var entry = item.getEntry(); |
| 478 | 478 |
| 479 this.cancel(); | 479 this.cancel(); |
| 480 this.entry_ = entry; | 480 this.entry_ = entry; |
| 481 this.callback_ = callback; | 481 this.callback_ = callback; |
| 482 | 482 |
| 483 var targetImage = this.image_; |
| 483 // 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. |
| 484 var generation = ++this.generation_; | 485 var generation = ++this.generation_; |
| 485 | 486 |
| 486 /** | 487 /** |
| 487 * @param {!HTMLImageElement} image Image to be transformed. | 488 * @param {!HTMLImageElement} image Image to be transformed. |
| 488 * @param {Object=} opt_transform Transformation. | 489 * @param {Object=} opt_transform Transformation. |
| 489 */ | 490 */ |
| 490 var onTransform = function(image, opt_transform) { | 491 var onTransform = function(image, opt_transform) { |
| 491 if (generation === this.generation_) { | 492 if (generation === this.generation_) { |
| 492 this.convertImage_( | 493 this.convertImage_( |
| 493 image, opt_transform || { scaleX: 1, scaleY: 1, rotate90: 0}); | 494 image, opt_transform || { scaleX: 1, scaleY: 1, rotate90: 0}); |
| 494 } | 495 } |
| 495 }; | 496 }; |
| 496 onTransform = onTransform.bind(this); | 497 onTransform = onTransform.bind(this); |
| 497 | 498 |
| 498 /** | 499 /** |
| 499 * @param {string=} opt_error Error. | 500 * @param {string=} opt_error Error. |
| 500 */ | 501 */ |
| 501 var onError = function(opt_error) { | 502 var onError = function(opt_error) { |
| 502 this.image_.onerror = null; | 503 targetImage.onerror = null; |
| 503 this.image_.onload = null; | 504 targetImage.onload = null; |
| 504 var tmpCallback = this.callback_; | 505 var tmpCallback = this.callback_; |
| 505 this.callback_ = null; | 506 this.callback_ = null; |
| 506 var emptyCanvas = assertInstanceof(this.document_.createElement('canvas'), | 507 var emptyCanvas = assertInstanceof(this.document_.createElement('canvas'), |
| 507 HTMLCanvasElement); | 508 HTMLCanvasElement); |
| 508 emptyCanvas.width = 0; | 509 emptyCanvas.width = 0; |
| 509 emptyCanvas.height = 0; | 510 emptyCanvas.height = 0; |
| 510 tmpCallback(emptyCanvas, opt_error); | 511 tmpCallback(emptyCanvas, opt_error); |
| 511 }; | 512 }; |
| 512 onError = onError.bind(this); | 513 onError = onError.bind(this); |
| 513 | 514 |
| 514 var loadImage = function() { | 515 var loadImage = function(url) { |
| 516 if (generation !== this.generation_) |
| 517 return; |
| 518 |
| 515 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime')); | 519 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime')); |
| 516 this.timeout_ = 0; | 520 this.timeout_ = 0; |
| 517 | 521 |
| 518 this.image_.onload = function() { | 522 targetImage.onload = function() { |
| 519 this.image_.onerror = null; | 523 targetImage.onerror = null; |
| 520 this.image_.onload = null; | 524 targetImage.onload = null; |
| 521 this.metadataModel_.get([entry], ['contentImageTransform']).then( | 525 this.metadataModel_.get([entry], ['contentImageTransform']).then( |
| 522 function(metadataItems) { | 526 function(metadataItems) { |
| 523 onTransform(this.image_, metadataItems[0].contentImageTransform); | 527 onTransform(targetImage, metadataItems[0].contentImageTransform); |
| 524 }.bind(this)); | 528 }.bind(this)); |
| 525 }.bind(this); | 529 }.bind(this); |
| 526 | 530 |
| 527 // The error callback has an optional error argument, which in case of a | 531 // The error callback has an optional error argument, which in case of a |
| 528 // general error should not be specified | 532 // general error should not be specified |
| 529 this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR'); | 533 targetImage.onerror = onError.bind(null, 'GALLERY_IMAGE_ERROR'); |
| 534 |
| 535 targetImage.src = url; |
| 536 }.bind(this); |
| 537 |
| 538 // Loads the image. If already loaded, then forces a reload. |
| 539 var startLoad = function() { |
| 540 if (generation !== this.generation_) |
| 541 return; |
| 542 |
| 543 // Target current image. |
| 544 targetImage = this.image_; |
| 545 |
| 546 // Obtain target URL. |
| 547 if (FileType.isRaw(entry)) { |
| 548 ImageLoaderClient.getInstance().load(entry.toURL(), function(result) { |
| 549 if (result.status === 'success') |
| 550 loadImage(result.data); |
| 551 else |
| 552 onError('GALLERY_IMAGE_ERROR'); |
| 553 }, { |
| 554 cache: true, |
| 555 timestamp: item.getMetadataItem().modificationTime && |
| 556 item.getMetadataItem().modificationTime.getTime(), |
| 557 priority: 0 // Use highest priority to show main image. |
| 558 }); |
| 559 return; |
| 560 } |
| 530 | 561 |
| 531 // Load the image directly. The query parameter is workaround for | 562 // Load the image directly. The query parameter is workaround for |
| 532 // crbug.com/379678, which force to update the contents of the image. | 563 // crbug.com/379678, which force to update the contents of the image. |
| 533 this.image_.src = entry.toURL() + '?nocache=' + Date.now(); | 564 loadImage(entry.toURL() + '?nocache=' + Date.now()); |
| 534 }.bind(this); | 565 }.bind(this); |
| 535 | 566 |
| 536 // Loads the image. If already loaded, then forces a reload. | |
| 537 var startLoad = this.resetImage_.bind(this, function() { | |
| 538 loadImage(); | |
| 539 }.bind(this), onError); | |
| 540 | |
| 541 if (opt_delay) { | 567 if (opt_delay) { |
| 542 this.timeout_ = setTimeout(startLoad, opt_delay); | 568 this.timeout_ = setTimeout(startLoad, opt_delay); |
| 543 } else { | 569 } else { |
| 544 startLoad(); | 570 startLoad(); |
| 545 } | 571 } |
| 546 }; | 572 }; |
| 547 | 573 |
| 548 /** | 574 /** |
| 549 * Resets the image by forcing the garbage collection and clearing the src | |
| 550 * attribute. | |
| 551 * | |
| 552 * @param {function()} onSuccess Success callback. | |
| 553 * @param {function(string=)} onError Failure callback with an optional error | |
| 554 * identifier. | |
| 555 * @private | |
| 556 */ | |
| 557 ImageUtil.ImageLoader.prototype.resetImage_ = function(onSuccess, onError) { | |
| 558 var clearSrc = function() { | |
| 559 this.image_.onload = onSuccess; | |
| 560 this.image_.onerror = onSuccess; | |
| 561 this.image_.src = ''; | |
| 562 }.bind(this); | |
| 563 | |
| 564 var emptyImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' + | |
| 565 'AAABAAEAAAICTAEAOw=='; | |
| 566 | |
| 567 if (this.image_.src !== emptyImage) { | |
| 568 // Load an empty image, then clear src. | |
| 569 this.image_.onload = clearSrc; | |
| 570 this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR'); | |
| 571 this.image_.src = emptyImage; | |
| 572 } else { | |
| 573 // Empty image already loaded, so clear src immediately. | |
| 574 clearSrc(); | |
| 575 } | |
| 576 }; | |
| 577 | |
| 578 /** | |
| 579 * @return {boolean} True if an image is loading. | 575 * @return {boolean} True if an image is loading. |
| 580 */ | 576 */ |
| 581 ImageUtil.ImageLoader.prototype.isBusy = function() { | 577 ImageUtil.ImageLoader.prototype.isBusy = function() { |
| 582 return !!this.callback_; | 578 return !!this.callback_; |
| 583 }; | 579 }; |
| 584 | 580 |
| 585 /** | 581 /** |
| 586 * @param {Entry} entry Image entry. | 582 * @param {Entry} entry Image entry. |
| 587 * @return {boolean} True if loader loads this image. | 583 * @return {boolean} True if loader loads this image. |
| 588 */ | 584 */ |
| 589 ImageUtil.ImageLoader.prototype.isLoading = function(entry) { | 585 ImageUtil.ImageLoader.prototype.isLoading = function(entry) { |
| 590 return this.isBusy() && util.isSameEntry(this.entry_, entry); | 586 return this.isBusy() && util.isSameEntry(this.entry_, entry); |
| 591 }; | 587 }; |
| 592 | 588 |
| 593 /** | 589 /** |
| 594 * @param {function(!HTMLCanvasElement, string=)} callback To be called when the | 590 * @param {function(!HTMLCanvasElement, string=)} callback To be called when the |
| 595 * image loaded. | 591 * image loaded. |
| 596 */ | 592 */ |
| 597 ImageUtil.ImageLoader.prototype.setCallback = function(callback) { | 593 ImageUtil.ImageLoader.prototype.setCallback = function(callback) { |
| 598 this.callback_ = callback; | 594 this.callback_ = callback; |
| 599 }; | 595 }; |
| 600 | 596 |
| 601 /** | 597 /** |
| 602 * Stops loading image. | 598 * Stops loading image. |
| 603 */ | 599 */ |
| 604 ImageUtil.ImageLoader.prototype.cancel = function() { | 600 ImageUtil.ImageLoader.prototype.cancel = function() { |
| 605 if (!this.callback_) return; | 601 if (!this.callback_) |
| 602 return; |
| 606 this.callback_ = null; | 603 this.callback_ = null; |
| 607 if (this.timeout_) { | 604 if (this.timeout_) { |
| 608 clearTimeout(this.timeout_); | 605 clearTimeout(this.timeout_); |
| 609 this.timeout_ = 0; | 606 this.timeout_ = 0; |
| 610 } | 607 } |
| 611 if (this.image_) { | 608 if (this.image_) { |
| 612 this.image_.onload = function() {}; | 609 this.image_.onload = function() {}; |
| 613 this.image_.onerror = function() {}; | 610 this.image_.onerror = function() {}; |
| 614 this.image_.src = ''; | 611 // Force to free internal image by assigning empty image. |
| 612 this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' + |
| 613 'AAABAAEAAAICTAEAOw=='; |
| 614 this.image_ = document.createElement('img'); |
| 615 } | 615 } |
| 616 this.generation_++; // Silence the transform fetcher if it is in progress. | 616 this.generation_++; // Silence the transform fetcher if it is in progress. |
| 617 }; | 617 }; |
| 618 | 618 |
| 619 /** | 619 /** |
| 620 * @param {!HTMLImageElement} image Image to be transformed. | 620 * @param {!HTMLImageElement} image Image to be transformed. |
| 621 * @param {!Object} transform transformation description to apply to the image. | 621 * @param {!Object} transform transformation description to apply to the image. |
| 622 * @private | 622 * @private |
| 623 */ | 623 */ |
| 624 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { | 624 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 725 ImageUtil.getMetricName = function(name) { | 725 ImageUtil.getMetricName = function(name) { |
| 726 return 'PhotoEditor.' + name; | 726 return 'PhotoEditor.' + name; |
| 727 }; | 727 }; |
| 728 | 728 |
| 729 /** | 729 /** |
| 730 * Used for metrics reporting, keep in sync with the histogram description. | 730 * Used for metrics reporting, keep in sync with the histogram description. |
| 731 * @type {Array.<string>} | 731 * @type {Array.<string>} |
| 732 * @const | 732 * @const |
| 733 */ | 733 */ |
| 734 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; | 734 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; |
| OLD | NEW |