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 |