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 |