| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 {string} url File URL. | 10 * @param {string} url File URL. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 if (apps[i].docIcon && apps[i].isPrimary) { | 31 if (apps[i].docIcon && apps[i].isPrimary) { |
| 32 this.fallbackUrl_ = apps[i].docIcon; | 32 this.fallbackUrl_ = apps[i].docIcon; |
| 33 break; | 33 break; |
| 34 } | 34 } |
| 35 } | 35 } |
| 36 } | 36 } |
| 37 | 37 |
| 38 if (opt_metadata.thumbnail && opt_metadata.thumbnail.url) { | 38 if (opt_metadata.thumbnail && opt_metadata.thumbnail.url) { |
| 39 this.thumbnailUrl_ = opt_metadata.thumbnail.url; | 39 this.thumbnailUrl_ = opt_metadata.thumbnail.url; |
| 40 this.transform_ = opt_metadata.thumbnail.transform; | 40 this.transform_ = opt_metadata.thumbnail.transform; |
| 41 } else if (FileType.isImage(url) && | 41 } else if (FileType.isImage(url)) { |
| 42 ThumbnailLoader.canUseImageUrl_(opt_metadata)) { | |
| 43 this.thumbnailUrl_ = url; | 42 this.thumbnailUrl_ = url; |
| 44 this.transform_ = opt_metadata.media && opt_metadata.media.imageTransform; | 43 this.transform_ = opt_metadata.media && opt_metadata.media.imageTransform; |
| 45 } else if (this.fallbackUrl_) { | 44 } else if (this.fallbackUrl_) { |
| 46 // Use fallback as the primary thumbnail. | 45 // Use fallback as the primary thumbnail. |
| 47 this.thumbnailUrl_ = this.fallbackUrl_; | 46 this.thumbnailUrl_ = this.fallbackUrl_; |
| 48 this.fallbackUrl_ = null; | 47 this.fallbackUrl_ = null; |
| 49 } // else the generic thumbnail based on the media type will be used. | 48 } // else the generic thumbnail based on the media type will be used. |
| 50 } | 49 } |
| 51 | 50 |
| 52 /** | 51 /** |
| 53 * Files with more pixels won't have thumbnails. | |
| 54 */ | |
| 55 ThumbnailLoader.MAX_PIXEL_COUNT = 1 << 21; // 2 MPix | |
| 56 | |
| 57 /** | |
| 58 * Files of bigger size won't have thumbnails. | |
| 59 */ | |
| 60 ThumbnailLoader.MAX_FILE_SIZE = 1 << 20; // 1 Mb | |
| 61 | |
| 62 /** | |
| 63 * In percents (0.0 - 1.0), how much area can be cropped to fill an image | 52 * In percents (0.0 - 1.0), how much area can be cropped to fill an image |
| 64 * in a container, when loading a thumbnail in FillMode.AUTO mode. | 53 * in a container, when loading a thumbnail in FillMode.AUTO mode. |
| 65 * The specified 30% value allows to fill 16:9, 3:2 pictures in 4:3 element. | 54 * The specified 30% value allows to fill 16:9, 3:2 pictures in 4:3 element. |
| 66 * @type {number} | 55 * @type {number} |
| 67 */ | 56 */ |
| 68 ThumbnailLoader.AUTO_FILL_THRESHOLD = 0.3; | 57 ThumbnailLoader.AUTO_FILL_THRESHOLD = 0.3; |
| 69 | 58 |
| 70 /** | 59 /** |
| 71 * Type of displaying a thumbnail within a box. | 60 * Type of displaying a thumbnail within a box. |
| 72 * @enum | 61 * @enum |
| 73 */ | 62 */ |
| 74 ThumbnailLoader.FillMode = { | 63 ThumbnailLoader.FillMode = { |
| 75 FILL: 0, // Fill whole box. Image may be cropped. | 64 FILL: 0, // Fill whole box. Image may be cropped. |
| 76 FIT: 1, // Keep aspect ratio, do not crop. | 65 FIT: 1, // Keep aspect ratio, do not crop. |
| 77 AUTO: 2 // Try to fill, but if incompatible aspect ratio, then fit. | 66 AUTO: 2 // Try to fill, but if incompatible aspect ratio, then fit. |
| 78 }; | 67 }; |
| 79 | 68 |
| 80 /** | 69 /** |
| 81 * Type of element to store the image. | 70 * Type of element to store the image. |
| 82 * @enum | 71 * @enum |
| 83 */ | 72 */ |
| 84 ThumbnailLoader.LoaderType = { | 73 ThumbnailLoader.LoaderType = { |
| 85 IMAGE: 0, | 74 IMAGE: 0, |
| 86 CANVAS: 1 | 75 CANVAS: 1 |
| 87 }; | 76 }; |
| 88 | 77 |
| 89 /** | 78 /** |
| 90 * If an image file does not have an embedded thumbnail we might want to use | 79 * Maximum thumbnail's width when generating from the full resolution image. |
| 91 * the image itself as a thumbnail. If the image is too large it hurts | 80 * @const |
| 92 * the performance a lot so we allow it only for moderately sized files. | 81 * @type {number} |
| 93 * | |
| 94 * @param {Object} metadata Metadata object. | |
| 95 * @return {boolean} Whether it is OK to use the image url for a preview. | |
| 96 * @private | |
| 97 */ | 82 */ |
| 98 ThumbnailLoader.canUseImageUrl_ = function(metadata) { | 83 ThumbnailLoader.THUMBNAIL_MAX_WIDTH = 500; |
| 99 return (metadata.filesystem && metadata.filesystem.size && | 84 |
| 100 metadata.filesystem.size <= ThumbnailLoader.MAX_FILE_SIZE) || | 85 /** |
| 101 (metadata.media && metadata.media.width && metadata.media.height && | 86 * Maximum thumbnail's height when generating from the full resolution image. |
| 102 metadata.media.width * metadata.media.height <= | 87 * @const |
| 103 ThumbnailLoader.MAX_PIXEL_COUNT); | 88 * @type {number} |
| 104 }; | 89 */ |
| 90 ThumbnailLoader.THUMBNAIL_MAX_HEIGHT = 500; |
| 105 | 91 |
| 106 /** | 92 /** |
| 107 * | 93 * |
| 108 * @param {HTMLElement} box Container element. | 94 * @param {HTMLElement} box Container element. |
| 109 * @param {ThumbnailLoader.FillMode} fillMode Fill mode. | 95 * @param {ThumbnailLoader.FillMode} fillMode Fill mode. |
| 110 * @param {function(Image, object} opt_onSuccess Success callback, | 96 * @param {function(Image, object} opt_onSuccess Success callback, |
| 111 * accepts the image and the transform. | 97 * accepts the image and the transform. |
| 112 * @param {function} opt_onError Error callback. | 98 * @param {function} opt_onError Error callback. |
| 113 * @param {function} opt_onGeneric Callback for generic image used. | 99 * @param {function} opt_onGeneric Callback for generic image used. |
| 114 */ | 100 */ |
| (...skipping 20 matching lines...) Expand all Loading... |
| 135 new ThumbnailLoader(this.fallbackUrl_, | 121 new ThumbnailLoader(this.fallbackUrl_, |
| 136 this.loaderType_, | 122 this.loaderType_, |
| 137 null, | 123 null, |
| 138 this.mediaType_). | 124 this.mediaType_). |
| 139 load(box, fillMode, opt_onSuccess); | 125 load(box, fillMode, opt_onSuccess); |
| 140 } else { | 126 } else { |
| 141 box.setAttribute('generic-thumbnail', this.mediaType_); | 127 box.setAttribute('generic-thumbnail', this.mediaType_); |
| 142 } | 128 } |
| 143 }.bind(this); | 129 }.bind(this); |
| 144 | 130 |
| 145 if (this.image_.src == this.thumbnailUrl_) { | 131 if (this.image_.src) { |
| 146 console.warn('Thumnbnail already loaded: ' + this.thumbnailUrl_); | 132 console.warn('Thumbnail already loaded: ' + this.thumbnailUrl_); |
| 147 return; | 133 return; |
| 148 } | 134 } |
| 149 | 135 |
| 150 util.loadImage(this.image_, this.thumbnailUrl_); | 136 // TODO(mtomasz): Smarter calculation of the requested size. |
| 137 var wasAttached = box.ownerDocument.contains(box); |
| 138 var taskId = util.loadImage( |
| 139 this.image_, |
| 140 this.thumbnailUrl_, |
| 141 { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH, |
| 142 maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT, |
| 143 cache: true }, |
| 144 function() { |
| 145 // If was attached, and it is not anymore, then cancel downloading. |
| 146 var isAttached = box.ownerDocument.contains(box); |
| 147 if (wasAttached && !isAttached) |
| 148 return false; |
| 149 return true; |
| 150 }); |
| 151 |
| 152 if (!taskId) |
| 153 this.image_.classList.add('cached'); |
| 151 }; | 154 }; |
| 152 | 155 |
| 153 /** | 156 /** |
| 154 * @return {boolean} True if a valid image is loaded. | 157 * @return {boolean} True if a valid image is loaded. |
| 155 */ | 158 */ |
| 156 ThumbnailLoader.prototype.hasValidImage = function() { | 159 ThumbnailLoader.prototype.hasValidImage = function() { |
| 157 return !!(this.image_ && this.image_.width && this.image_.height); | 160 return !!(this.image_ && this.image_.width && this.image_.height); |
| 158 }; | 161 }; |
| 159 | 162 |
| 160 /** | 163 /** |
| (...skipping 27 matching lines...) Expand all Loading... |
| 188 ThumbnailLoader.prototype.loadDetachedImage = function(callback) { | 191 ThumbnailLoader.prototype.loadDetachedImage = function(callback) { |
| 189 if (!this.thumbnailUrl_) { | 192 if (!this.thumbnailUrl_) { |
| 190 callback(true); | 193 callback(true); |
| 191 return; | 194 return; |
| 192 } | 195 } |
| 193 | 196 |
| 194 this.canvasUpToDate_ = false; | 197 this.canvasUpToDate_ = false; |
| 195 this.image_ = new Image(); | 198 this.image_ = new Image(); |
| 196 this.image_.onload = callback.bind(null, true); | 199 this.image_.onload = callback.bind(null, true); |
| 197 this.image_.onerror = callback.bind(null, false); | 200 this.image_.onerror = callback.bind(null, false); |
| 198 util.loadImage(this.image_, this.thumbnailUrl_); | 201 |
| 202 // TODO(mtomasz): Smarter calculation of the requested size. |
| 203 var taskId = util.loadImage( |
| 204 this.image_, |
| 205 this.thumbnailUrl_, |
| 206 { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH, |
| 207 maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT, |
| 208 cache: true }); |
| 209 |
| 210 if (!taskId) |
| 211 this.image_.classList.add('cached'); |
| 199 }; | 212 }; |
| 200 | 213 |
| 201 /** | 214 /** |
| 202 * Attach the image to a given element. | 215 * Attach the image to a given element. |
| 203 * @param {Element} container Parent element. | 216 * @param {Element} container Parent element. |
| 204 * @param {ThumbnailLoader.FillMode} fillMode Fill mode. | 217 * @param {ThumbnailLoader.FillMode} fillMode Fill mode. |
| 205 */ | 218 */ |
| 206 ThumbnailLoader.prototype.attachImage = function(container, fillMode) { | 219 ThumbnailLoader.prototype.attachImage = function(container, fillMode) { |
| 207 if (!this.hasValidImage()) { | 220 if (!this.hasValidImage()) { |
| 208 container.setAttribute('generic-thumbnail', this.mediaType_); | 221 container.setAttribute('generic-thumbnail', this.mediaType_); |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 310 | 323 |
| 311 function percent(fraction) { | 324 function percent(fraction) { |
| 312 return (fraction * 100).toFixed(2) + '%'; | 325 return (fraction * 100).toFixed(2) + '%'; |
| 313 } | 326 } |
| 314 | 327 |
| 315 img.style.width = percent(fractionX); | 328 img.style.width = percent(fractionX); |
| 316 img.style.height = percent(fractionY); | 329 img.style.height = percent(fractionY); |
| 317 img.style.left = percent((1 - fractionX) / 2); | 330 img.style.left = percent((1 - fractionX) / 2); |
| 318 img.style.top = percent((1 - fractionY) / 2); | 331 img.style.top = percent((1 - fractionY) / 2); |
| 319 }; | 332 }; |
| OLD | NEW |