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; |
524 if (generation !== this.generation_) | |
525 return; | |
525 this.metadataModel_.get([entry], ['contentImageTransform']).then( | 526 this.metadataModel_.get([entry], ['contentImageTransform']).then( |
526 function(metadataItems) { | 527 function(metadataItems) { |
528 if (generation !== this.generation_) | |
yawano
2016/01/21 09:11:32
nit: I don't think we need this second check since
ryoh
2016/01/22 01:15:54
That's right!
| |
529 return; | |
527 onTransform(targetImage, metadataItems[0].contentImageTransform); | 530 onTransform(targetImage, metadataItems[0].contentImageTransform); |
528 }.bind(this)); | 531 }.bind(this)); |
529 }.bind(this); | 532 }.bind(this); |
530 | 533 |
531 // The error callback has an optional error argument, which in case of a | 534 // The error callback has an optional error argument, which in case of a |
532 // general error should not be specified | 535 // general error should not be specified |
533 targetImage.onerror = onError.bind(null, 'GALLERY_IMAGE_ERROR'); | 536 targetImage.onerror = onError.bind(null, 'GALLERY_IMAGE_ERROR'); |
534 | 537 |
535 targetImage.src = url; | 538 targetImage.src = url; |
536 }.bind(this); | 539 }.bind(this); |
537 | 540 |
538 // Loads the image. If already loaded, then forces a reload. | 541 // Loads the image. If already loaded, then forces a reload. |
539 var startLoad = function() { | 542 var startLoad = function() { |
540 if (generation !== this.generation_) | 543 if (generation !== this.generation_) |
541 return; | 544 return; |
542 | 545 |
543 // Target current image. | |
544 targetImage = this.image_; | |
545 | |
546 // Obtain target URL. | 546 // Obtain target URL. |
547 if (FileType.isRaw(entry)) { | 547 if (FileType.isRaw(entry)) { |
548 var timestamp = | 548 var timestamp = |
549 item.getMetadataItem() && | 549 item.getMetadataItem() && |
550 item.getMetadataItem().modificationTime && | 550 item.getMetadataItem().modificationTime && |
551 item.getMetadataItem().modificationTime.getTime(); | 551 item.getMetadataItem().modificationTime.getTime(); |
552 ImageLoaderClient.getInstance().load(entry.toURL(), function(result) { | 552 ImageLoaderClient.getInstance().load(entry.toURL(), function(result) { |
553 if (generation !== this.generation_) | 553 if (generation !== this.generation_) |
554 return; | 554 return; |
555 if (result.status === 'success') | 555 if (result.status === 'success') |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
603 * Stops loading image. | 603 * Stops loading image. |
604 */ | 604 */ |
605 ImageUtil.ImageLoader.prototype.cancel = function() { | 605 ImageUtil.ImageLoader.prototype.cancel = function() { |
606 if (!this.callback_) | 606 if (!this.callback_) |
607 return; | 607 return; |
608 this.callback_ = null; | 608 this.callback_ = null; |
609 if (this.timeout_) { | 609 if (this.timeout_) { |
610 clearTimeout(this.timeout_); | 610 clearTimeout(this.timeout_); |
611 this.timeout_ = 0; | 611 this.timeout_ = 0; |
612 } | 612 } |
613 if (this.image_) { | |
614 this.image_.onload = function() {}; | |
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. | 613 this.generation_++; // Silence the transform fetcher if it is in progress. |
622 }; | 614 }; |
623 | 615 |
624 /** | 616 /** |
625 * @param {!HTMLImageElement} image Image to be transformed. | 617 * @param {!HTMLImageElement} image Image to be transformed. |
626 * @param {!Object} transform transformation description to apply to the image. | 618 * @param {!Object} transform transformation description to apply to the image. |
627 * @private | 619 * @private |
628 */ | 620 */ |
629 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { | 621 ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { |
622 if (!transform || | |
623 (transform.rotate90 === 0 && | |
624 transform.scaleX === 1 && | |
625 transform.scaleY === 1)) { | |
626 setTimeout(this.callback_, 0, image); | |
627 this.callback_ = null; | |
628 return; | |
629 } | |
630 var canvas = this.document_.createElement('canvas'); | 630 var canvas = this.document_.createElement('canvas'); |
631 | 631 |
632 if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions. | 632 if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions. |
633 canvas.width = image.height; | 633 canvas.width = image.height; |
634 canvas.height = image.width; | 634 canvas.height = image.width; |
635 } else { | 635 } else { |
636 canvas.width = image.width; | 636 canvas.width = image.width; |
637 canvas.height = image.height; | 637 canvas.height = image.height; |
638 } | 638 } |
639 | 639 |
(...skipping 23 matching lines...) Expand all Loading... | |
663 context.drawImage( | 663 context.drawImage( |
664 image, 0, firstRow, image.width, lastRow - firstRow, | 664 image, 0, firstRow, image.width, lastRow - firstRow, |
665 -image.width / 2, firstRow - image.height / 2, | 665 -image.width / 2, firstRow - image.height / 2, |
666 image.width, lastRow - firstRow); | 666 image.width, lastRow - firstRow); |
667 | 667 |
668 if (lastRow === image.height) { | 668 if (lastRow === image.height) { |
669 context.restore(); | 669 context.restore(); |
670 if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls. | 670 if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls. |
671 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime')); | 671 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime')); |
672 } | 672 } |
673 try { | 673 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; | 674 this.callback_ = null; |
679 } else { | 675 } else { |
680 var self = this; | 676 var self = this; |
681 this.timeout_ = setTimeout( | 677 this.timeout_ = setTimeout( |
682 function() { | 678 function() { |
683 self.timeout_ = 0; | 679 self.timeout_ = 0; |
684 self.copyStrip_(context, image, lastRow, rowCount); | 680 self.copyStrip_(context, image, lastRow, rowCount); |
685 }, 0); | 681 }, 0); |
686 } | 682 } |
687 }; | 683 }; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
725 | 721 |
726 /** | 722 /** |
727 * @param {string} name Local name. | 723 * @param {string} name Local name. |
728 * @return {string} Full name. | 724 * @return {string} Full name. |
729 */ | 725 */ |
730 ImageUtil.getMetricName = function(name) { | 726 ImageUtil.getMetricName = function(name) { |
731 return 'PhotoEditor.' + name; | 727 return 'PhotoEditor.' + name; |
732 }; | 728 }; |
733 | 729 |
734 /** | 730 /** |
731 * Ensures argument is canvas. If it's not, creates new canvas and copy. | |
732 * | |
733 * @param {!HTMLCanvasElement|!HTMLImageElement} imgOrCanvas image or canvas | |
734 * element | |
735 * @return {!HTMLCanvasElement} canvas. | |
736 */ | |
737 ImageUtil.ensureCanvas = function(imgOrCanvas) { | |
738 if(imgOrCanvas.tagName === 'canvas') { | |
739 return assertInstanceof(imgOrCanvas, HTMLCanvasElement); | |
740 } | |
741 var canvas = assertInstanceof(document.createElement('canvas'), | |
742 HTMLCanvasElement); | |
743 canvas.width = imgOrCanvas.width; | |
744 canvas.height = imgOrCanvas.height; | |
745 var context = canvas.getContext('2d'); | |
746 context.drawImage(imgOrCanvas, 0, 0); | |
747 return canvas; | |
748 }; | |
749 | |
750 /** | |
735 * Used for metrics reporting, keep in sync with the histogram description. | 751 * Used for metrics reporting, keep in sync with the histogram description. |
736 * @type {Array<string>} | 752 * @type {Array<string>} |
737 * @const | 753 * @const |
738 */ | 754 */ |
739 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; | 755 ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; |
OLD | NEW |