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.
6 * Loads and resizes an image.
9 function ImageLoader() {
11 * Persistent cache object.
15 this.cache_ = new Cache();
18 * Manages pending requests and runs them in order of priorities.
22 this.scheduler_ = new Scheduler();
24 // Grant permissions to all volumes, initialize the cache and then start the
26 chrome.fileManagerPrivate.getVolumeMetadataList(function(volumeMetadataList) {
27 var initPromises = volumeMetadataList.map(function(volumeMetadata) {
28 var requestPromise = new Promise(function(callback) {
29 chrome.fileManagerPrivate.requestFileSystem(
30 volumeMetadata.volumeId,
33 return requestPromise;
35 initPromises.push(new Promise(function(resolve, reject) {
36 this.cache_.initialize(resolve);
39 // After all initialization promises are done, start the scheduler.
40 Promise.all(initPromises).then(this.scheduler_.start.bind(this.scheduler_));
42 // Listen for mount events, and grant permissions to volumes being mounted.
43 chrome.fileManagerPrivate.onMountCompleted.addListener(
45 if (event.eventType == 'mount' && event.status == 'success') {
46 chrome.fileManagerPrivate.requestFileSystem(
47 event.volumeMetadata.volumeId, function() {});
52 // Listen for incoming requests.
53 chrome.runtime.onMessageExternal.addListener(
54 function(request, sender, sendResponse) {
55 if (ImageLoader.ALLOWED_CLIENTS.indexOf(sender.id) !== -1) {
56 // Sending a response may fail if the receiver already went offline.
57 // This is not an error, but a normal and quite common situation.
58 var failSafeSendResponse = function(response) {
60 sendResponse(response);
66 return this.onMessage_(sender.id,
67 /** @type {LoadImageRequest} */ (request),
68 failSafeSendResponse);
74 * List of extensions allowed to perform image requests.
77 * @type {Array.<string>}
79 ImageLoader.ALLOWED_CLIENTS = [
80 'hhaomjibdihmijegdhdafkllkbggdgoj', // File Manager's extension id.
81 'nlkncpkkdoccmpiclbokaimcnedabhhm' // Gallery extension id.
85 * Handles a request. Depending on type of the request, starts or stops
88 * @param {string} senderId Sender's extension id.
89 * @param {LoadImageRequest} request Request message as a hash array.
90 * @param {function(Object)} callback Callback to be called to return response.
91 * @return {boolean} True if the message channel should stay alive until the
95 ImageLoader.prototype.onMessage_ = function(senderId, request, callback) {
96 var requestId = senderId + ':' + request.taskId;
99 this.scheduler_.remove(requestId);
100 return false; // No callback calls.
102 // Create a request task and add it to the scheduler (queue).
103 var requestTask = new Request(requestId, this.cache_, request, callback);
104 this.scheduler_.add(requestTask);
105 return true; // Request will call the callback.
110 * Returns the singleton instance.
111 * @return {ImageLoader} ImageLoader object.
113 ImageLoader.getInstance = function() {
114 if (!ImageLoader.instance_)
115 ImageLoader.instance_ = new ImageLoader();
116 return ImageLoader.instance_;
120 * Checks if the options contain any image processing.
122 * @param {number} width Source width.
123 * @param {number} height Source height.
124 * @param {Object} options Resizing options as a hash array.
125 * @return {boolean} True if yes, false if not.
127 ImageLoader.shouldProcess = function(width, height, options) {
128 var targetDimensions = ImageLoader.resizeDimensions(width, height, options);
130 // Dimensions has to be adjusted.
131 if (targetDimensions.width != width || targetDimensions.height != height)
134 // Orientation has to be adjusted.
135 if (options.orientation)
138 // No changes required.
143 * Calculates dimensions taking into account resize options, such as:
144 * - scale: for scaling,
145 * - maxWidth, maxHeight: for maximum dimensions,
146 * - width, height: for exact requested size.
147 * Returns the target size as hash array with width, height properties.
149 * @param {number} width Source width.
150 * @param {number} height Source height.
151 * @param {Object} options Resizing options as a hash array.
152 * @return {Object} Dimensions, eg. {width: 100, height: 50}.
154 ImageLoader.resizeDimensions = function(width, height, options) {
155 var sourceWidth = width;
156 var sourceHeight = height;
158 // Flip dimensions for odd orientation values: 1 (90deg) and 3 (270deg).
159 if (options.orientation && options.orientation % 2) {
160 sourceWidth = height;
161 sourceHeight = width;
164 var targetWidth = sourceWidth;
165 var targetHeight = sourceHeight;
167 if ('scale' in options) {
168 targetWidth = sourceWidth * options.scale;
169 targetHeight = sourceHeight * options.scale;
172 if (options.maxWidth && targetWidth > options.maxWidth) {
173 var scale = options.maxWidth / targetWidth;
174 targetWidth *= scale;
175 targetHeight *= scale;
178 if (options.maxHeight && targetHeight > options.maxHeight) {
179 var scale = options.maxHeight / targetHeight;
180 targetWidth *= scale;
181 targetHeight *= scale;
185 targetWidth = options.width;
188 targetHeight = options.height;
190 targetWidth = Math.round(targetWidth);
191 targetHeight = Math.round(targetHeight);
193 return {width: targetWidth, height: targetHeight};
197 * Performs resizing of the source image into the target canvas.
199 * @param {HTMLCanvasElement|Image} source Source image or canvas.
200 * @param {HTMLCanvasElement} target Target canvas.
201 * @param {Object} options Resizing options as a hash array.
203 ImageLoader.resize = function(source, target, options) {
204 var targetDimensions = ImageLoader.resizeDimensions(
205 source.width, source.height, options);
207 target.width = targetDimensions.width;
208 target.height = targetDimensions.height;
210 // Default orientation is 0deg.
211 var orientation = options.orientation || 0;
213 // For odd orientation values: 1 (90deg) and 3 (270deg) flip dimensions.
216 if (orientation % 2) {
217 drawImageWidth = target.height;
218 drawImageHeight = target.width;
220 drawImageWidth = target.width;
221 drawImageHeight = target.height;
224 var targetContext = target.getContext('2d');
225 targetContext.save();
226 targetContext.translate(target.width / 2, target.height / 2);
227 targetContext.rotate(orientation * Math.PI / 2);
228 targetContext.drawImage(
231 source.width, source.height,
232 -drawImageWidth / 2, -drawImageHeight / 2,
233 drawImageWidth, drawImageHeight);
234 targetContext.restore();