Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(380)

Side by Side Diff: chrome/browser/resources/image_loader/image_loader.js

Issue 12304013: Introduce Image loader extension. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698