1 // Copyright 2013 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.
8 * Loads and resizes an image.
11 function ImageLoader() {
13 * Persistent cache object.
17 this.cache_ = new Cache();
20 * Manages pending requests and runs them in order of priorities.
24 this.worker_ = new Worker();
26 // Grant permissions to all volumes, initialize the cache and then start the
28 chrome.fileBrowserPrivate.getVolumeMetadataList(function(volumeMetadataList) {
29 var initPromises = volumeMetadataList.map(function(volumeMetadata) {
30 var requestPromise = new Promise(function(callback) {
31 chrome.fileBrowserPrivate.requestFileSystem(
32 volumeMetadata.volumeId,
35 return requestPromise;
37 initPromises.push(new Promise(this.cache_.initialize.bind(this.cache_)));
39 // After all initializatino promises are done, start the worker.
40 Promise.all(initPromises).then(this.worker_.start.bind(this.worker_));
42 // Listen for mount events, and grant permissions to volumes being mounted.
43 chrome.fileBrowserPrivate.onMountCompleted.addListener(
45 if (event.eventType == 'mount' && event.status == 'success') {
46 chrome.fileBrowserPrivate.requestFileSystem(
47 event.volumeMetadata.volumeId, function() {});
52 // Listen for incoming requests.
53 chrome.extension.onMessageExternal.addListener(function(request,
56 if (ImageLoader.ALLOWED_CLIENTS.indexOf(sender.id) !== -1) {
57 // Sending a response may fail if the receiver already went offline.
58 // This is not an error, but a normal and quite common situation.
59 var failSafeSendResponse = function(response) {
61 sendResponse(response);
67 return this.onMessage_(sender.id, request, failSafeSendResponse);
73 * List of extensions allowed to perform image requests.
76 * @type {Array.<string>}
78 ImageLoader.ALLOWED_CLIENTS =
79 ['hhaomjibdihmijegdhdafkllkbggdgoj']; // File Manager's extension id.
82 * Handles a request. Depending on type of the request, starts or stops
85 * @param {string} senderId Sender's extension id.
86 * @param {Object} request Request message as a hash array.
87 * @param {function} callback Callback to be called to return response.
88 * @return {boolean} True if the message channel should stay alive until the
92 ImageLoader.prototype.onMessage_ = function(senderId, request, callback) {
93 var requestId = senderId + ':' + request.taskId;
96 this.worker_.remove(requestId);
97 return false; // No callback calls.
99 // Create a request task and add it to the worker (queue).
100 var requestTask = new Request(requestId, this.cache_, request, callback);
101 this.worker_.add(requestTask);
102 return true; // Request will call the callback.
107 * Returns the singleton instance.
108 * @return {ImageLoader} ImageLoader object.
110 ImageLoader.getInstance = function() {
111 if (!ImageLoader.instance_)
112 ImageLoader.instance_ = new ImageLoader();
113 return ImageLoader.instance_;
117 * Checks if the options contain any image processing.
119 * @param {number} width Source width.
120 * @param {number} height Source height.
121 * @param {Object} options Resizing options as a hash array.
122 * @return {boolean} True if yes, false if not.
124 ImageLoader.shouldProcess = function(width, height, options) {
125 var targetDimensions = ImageLoader.resizeDimensions(width, height, options);
127 // Dimensions has to be adjusted.
128 if (targetDimensions.width != width || targetDimensions.height != height)
131 // Orientation has to be adjusted.
132 if (options.orientation)
135 // No changes required.
140 * Calculates dimensions taking into account resize options, such as:
141 * - scale: for scaling,
142 * - maxWidth, maxHeight: for maximum dimensions,
143 * - width, height: for exact requested size.
144 * Returns the target size as hash array with width, height properties.
146 * @param {number} width Source width.
147 * @param {number} height Source height.
148 * @param {Object} options Resizing options as a hash array.
149 * @return {Object} Dimensions, eg. {width: 100, height: 50}.
151 ImageLoader.resizeDimensions = function(width, height, options) {
152 var sourceWidth = width;
153 var sourceHeight = height;
155 // Flip dimensions for odd orientation values: 1 (90deg) and 3 (270deg).
156 if (options.orientation && options.orientation % 2) {
157 sourceWidth = height;
158 sourceHeight = width;
161 var targetWidth = sourceWidth;
162 var targetHeight = sourceHeight;
164 if ('scale' in options) {
165 targetWidth = sourceWidth * options.scale;
166 targetHeight = sourceHeight * options.scale;
169 if (options.maxWidth &&
170 targetWidth > options.maxWidth) {
171 var scale = options.maxWidth / targetWidth;
172 targetWidth *= scale;
173 targetHeight *= scale;
176 if (options.maxHeight &&
177 targetHeight > options.maxHeight) {
178 var scale = options.maxHeight / targetHeight;
179 targetWidth *= scale;
180 targetHeight *= scale;
184 targetWidth = options.width;
187 targetHeight = options.height;
189 targetWidth = Math.round(targetWidth);
190 targetHeight = Math.round(targetHeight);
192 return {width: targetWidth, height: targetHeight};
196 * Performs resizing of the source image into the target canvas.
198 * @param {HTMLCanvasElement|Image} source Source image or canvas.
199 * @param {HTMLCanvasElement} target Target canvas.
200 * @param {Object} options Resizing options as a hash array.
202 ImageLoader.resize = function(source, target, options) {
203 var targetDimensions = ImageLoader.resizeDimensions(
204 source.width, source.height, options);
206 target.width = targetDimensions.width;
207 target.height = targetDimensions.height;
209 // Default orientation is 0deg.
210 var orientation = options.orientation || 0;
212 // For odd orientation values: 1 (90deg) and 3 (270deg) flip dimensions.
215 if (orientation % 2) {
216 drawImageWidth = target.height;
217 drawImageHeight = target.width;
219 drawImageWidth = target.width;
220 drawImageHeight = target.height;
223 var targetContext = target.getContext('2d');
224 targetContext.save();
225 targetContext.translate(target.width / 2, target.height / 2);
226 targetContext.rotate(orientation * Math.PI / 2);
227 targetContext.drawImage(
230 source.width, source.height,
231 -drawImageWidth / 2, -drawImageHeight / 2,
232 drawImageWidth, drawImageHeight);
233 targetContext.restore();