OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * Loads and resizes an image. | |
7 * @constructor | |
8 */ | |
9 var ImageLoader = function() { | |
10 this.requests_ = {}; | |
11 | |
12 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { | |
13 // TODO(mtomasz): Handle. | |
14 }); | |
15 | |
16 chrome.extension.onConnectExternal.addListener(function(port) { | |
17 if (ImageLoader.ALLOWED_CLIENTS.indexOf(port.sender.id) !== -1) | |
18 port.onMessage.addListener(function(request) { | |
19 this.onMessage_(port, request, port.postMessage.bind(port)); | |
20 }.bind(this)); | |
21 }.bind(this)); | |
22 }; | |
23 | |
24 /** | |
25 * List of extensions allowed to perform image requests. | |
26 * | |
27 * @const | |
28 * @type {Array.<string>} | |
29 */ | |
30 ImageLoader.ALLOWED_CLIENTS = | |
31 ['hhaomjibdihmijegdhdafkllkbggdgoj']; // File Manager's extension id. | |
32 | |
33 /** | |
34 * Handles a request. Depending on type of the request, starts or stops | |
35 * an image task. | |
36 * | |
37 * @param {Port} port Connection port. | |
38 * @param {Object} request Request message as a hash array. | |
39 * @param {function} callback Callback to be called to return response. | |
40 * @private | |
41 */ | |
42 ImageLoader.prototype.onMessage_ = function(port, request, callback) { | |
43 var requestId = port.sender.id + ':' + request.taskId; | |
44 if (request.cancel) { | |
45 // Cancel a task. | |
46 if (requestId in this.requests_) { | |
47 this.requests_[requestId].cancel(); | |
48 delete this.requests_[requestId]; | |
49 } | |
50 } else { | |
51 // Start a task. | |
52 this.requests_[requestId] = | |
53 new ImageLoader.Request(request, callback); | |
54 } | |
55 }; | |
56 | |
57 /** | |
58 * Loads the image loader. Static. | |
59 */ | |
60 ImageLoader.load = function() { | |
James Hawkins
2013/02/20 04:31:06
Why is there a load method? Just have the client
mtomasz
2013/02/21 05:19:24
Client doesn't call it. Client communicates with I
James Hawkins
2013/02/21 23:20:36
Keep the code (more) simple and get rid of it.
mtomasz
2013/02/22 04:08:34
Done.
| |
61 ImageLoader.getInstance(); | |
62 }; | |
63 | |
64 /** | |
65 * Returns the singleton instance. Static. | |
James Hawkins
2013/02/20 04:31:06
nit: Remove 'static' here and elsewhere.
mtomasz
2013/02/21 05:19:24
Done.
| |
66 * @return {ImageLoader} ImageLoader object. | |
67 */ | |
68 ImageLoader.getInstance = function() { | |
69 if (!this.instance_) | |
70 this.instance_ = new ImageLoader(); | |
71 return this.instance_; | |
72 }; | |
73 | |
74 /** | |
75 * Calculates dimensions taking into account resize options, such as: | |
76 * - scale: for scaling, | |
77 * - maxWidth, maxHeight: for maximum dimensions, | |
78 * - width, height: for exact requested size. | |
79 * Returns the target size as hash array with width, height properties. | |
80 * Static. | |
81 * | |
82 * @param {number} width Source width. | |
83 * @param {number} height Source height. | |
84 * @param {Object} options Resizing options as a hash array. | |
85 * @return {Object} Dimensions, eg. { width: 100, height: 50 }. | |
86 */ | |
87 ImageLoader.resizeDimensions = function(width, height, options) { | |
88 var sourceWidth = width; | |
89 var sourceHeight = height; | |
90 | |
91 var targetWidth = sourceWidth; | |
92 var targetHeight = sourceHeight; | |
93 | |
94 if ('scale' in options) { | |
95 targetWidth = sourceWidth * options.scale; | |
96 targetHeight = sourceHeight * options.scale; | |
97 } | |
98 | |
99 if (options.maxWidth && | |
100 targetWidth > options.maxWidth) { | |
101 var scale = options.maxWidth / targetWidth; | |
102 targetWidth *= scale; | |
103 targetHeight *= scale; | |
104 } | |
105 | |
106 if (options.maxHeight && | |
107 targetHeight > options.maxHeight) { | |
108 var scale = options.maxHeight / targetHeight; | |
109 targetWidth *= scale; | |
110 targetHeight *= scale; | |
111 } | |
112 | |
113 if (options.width) | |
114 targetWidth = options.width; | |
115 | |
116 if (options.height) | |
117 targetHeight = options.height; | |
118 | |
119 targetWidth = Math.round(targetWidth); | |
120 targetHeight = Math.round(targetHeight); | |
121 | |
122 return { width: targetWidth, height: targetHeight }; | |
123 }; | |
124 | |
125 /** | |
126 * Performs resizing of the source image into the target canvas. Static. | |
127 * | |
128 * @param {HTMLCanvasElement|Image} source Source image or canvas. | |
129 * @param {HTMLCanvasElement} target Target canvas. | |
130 * @param {Object} options Resizing options as a hash array. | |
131 */ | |
132 ImageLoader.resize = function(source, target, options) { | |
133 var targetDimensions = ImageLoader.resizeDimensions( | |
134 source.width, source.height, options); | |
135 | |
136 target.width = targetDimensions.width; | |
137 target.height = targetDimensions.height; | |
138 | |
139 var targetContext = target.getContext('2d'); | |
140 targetContext.drawImage(source, | |
141 0, 0, source.width, source.height, | |
142 0, 0, target.width, target.height); | |
143 }; | |
144 | |
145 /** | |
146 * Creates and starts downloading and then resizing of the image. Finally, | |
147 * returns the image using the callback. | |
148 * | |
149 * @param {Object} request Request message as a hash array. | |
150 * @param {function} callback Callback used to send the response. | |
151 * @constructor | |
152 */ | |
153 ImageLoader.Request = function(request, callback) { | |
154 this.request_ = request; | |
155 this.sendResponse_ = callback; | |
156 | |
157 this.image_ = new Image(); | |
158 this.xhr_ = new XMLHttpRequest(); | |
159 this.canvas_ = document.createElement('canvas'); | |
160 this.context_ = this.canvas_.getContext('2d'); | |
161 | |
162 this.downloadOriginal_(); | |
163 }; | |
164 | |
165 /** | |
166 * Downloads an image directly or for remote resources using the XmlHttpRequest. | |
167 * @private | |
168 */ | |
169 ImageLoader.Request.prototype.downloadOriginal_ = function() { | |
170 this.image_.onload = this.onImageLoad_.bind(this); | |
171 this.image_.onerror = this.onImageError_.bind(this); | |
172 | |
173 if (window.harness || !this.request_.url.match(/^https?:/)) { | |
174 // Download directly. | |
175 this.image_.src = this.request_.url; | |
176 return; | |
177 } | |
178 | |
179 // Download using an xhr request. | |
180 this.xhr_.responseType = 'blob'; | |
181 | |
182 this.xhr_.onerror = this.image_.onerror; | |
183 this.xhr_.onload = function() { | |
184 if (this.xhr_.status != 200) { | |
185 this.image_.onerror(); | |
186 return; | |
187 } | |
188 | |
189 // Process returnes data. | |
190 var reader = new FileReader(); | |
191 reader.onerror = this.image_.onerror; | |
192 reader.onload = function(e) { | |
193 this.image_.src = e.target.result; | |
194 }.bind(this); | |
195 | |
196 // Load the data to the image as a data url. | |
197 reader.readAsDataURL(this.xhr_.response); | |
198 }.bind(this); | |
199 | |
200 // Perform a xhr request. | |
201 try { | |
202 this.xhr_.open('GET', this.request_.url, true); | |
203 this.xhr_.send(); | |
204 } catch (e) { | |
205 this.image_.onerror(); | |
206 } | |
207 }; | |
208 | |
209 /** | |
210 * Sends the resized image via the callback. | |
211 * @private | |
212 */ | |
213 ImageLoader.Request.prototype.sendImage_ = function() { | |
214 // TODO(mtomasz): Keep format. Never compress using jpeg codec for lossless | |
215 // images such as png, gif. | |
216 var pngData = this.canvas_.toDataURL('image/png'); | |
217 var jpegData = this.canvas_.toDataURL('image/jpeg', 0.9); | |
218 var imageData = pngData.length < jpegData.length * 2 ? pngData : jpegData; | |
219 this.sendResponse_({ status: 'success', | |
220 data: imageData, | |
221 taskId: this.request_.taskId }); | |
222 }; | |
223 | |
224 /** | |
225 * Handler, when contents are loaded into the image element. Performs resizing | |
226 * and finalizes the request process. | |
227 * | |
228 * @private | |
229 */ | |
230 ImageLoader.Request.prototype.onImageLoad_ = function() { | |
231 ImageLoader.resize(this.image_, this.canvas_, this.request_); | |
232 this.sendImage_(); | |
233 this.cleanup_(); | |
234 }; | |
235 | |
236 /** | |
237 * Handler, when loading of the image fails. Sends a failure response and | |
238 * finalizes the request process. | |
239 * | |
240 * @private | |
241 */ | |
242 ImageLoader.Request.prototype.onImageError_ = function() { | |
243 this.sendResponse_({ status: 'error', | |
244 taskId: this.request_.taskId }); | |
245 this.cleanup_(); | |
246 }; | |
247 | |
248 /** | |
249 * Cancels the request. | |
250 */ | |
251 ImageLoader.Request.prototype.cancel = function() { | |
252 this.cleanup_(); | |
253 }; | |
254 | |
255 /** | |
256 * Cleans up memory used by this request. | |
257 * @private | |
258 */ | |
259 ImageLoader.Request.prototype.cleanup_ = function() { | |
260 this.image_.onerror = function() {}; | |
261 this.image_.onload = function() {}; | |
262 | |
263 // Transparent 1x1 pixel gif, to force garbage collecting. | |
264 this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' + | |
265 'ABAAEAAAICTAEAOw=='; | |
266 | |
267 this.xhr_.onerror = function() {}; | |
268 this.xhr_.onload = function() {}; | |
269 this.xhr_.abort(); | |
270 | |
271 // Dispose memory allocated by Canvas. | |
272 this.canvas_.width = 0; | |
273 this.canvas_.height = 0; | |
274 }; | |
275 | |
276 // Load the extension. | |
277 ImageLoader.load(); | |
OLD | NEW |