OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 * Object representing an image item (a photo). | 6 * Object representing an image item (a photo). |
7 * | 7 * |
8 * @param {!FileEntry} entry Image entry. | 8 * @param {!FileEntry} entry Image entry. |
9 * @param {!EntryLocation} locationInfo Entry location information. | 9 * @param {!EntryLocation} locationInfo Entry location information. |
10 * @param {!MetadataItem} metadataItem | 10 * @param {!MetadataItem} metadataItem |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 * @type {!RegExp} | 148 * @type {!RegExp} |
149 * @const | 149 * @const |
150 */ | 150 */ |
151 Gallery.Item.REGEXP_COPY_N = | 151 Gallery.Item.REGEXP_COPY_N = |
152 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + ' \\((\\d+)\\)$'); | 152 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + ' \\((\\d+)\\)$'); |
153 | 153 |
154 /** | 154 /** |
155 * Creates a name for an edited copy of the file. | 155 * Creates a name for an edited copy of the file. |
156 * | 156 * |
157 * @param {!DirectoryEntry} dirEntry Entry. | 157 * @param {!DirectoryEntry} dirEntry Entry. |
| 158 * @param {string} newMimeType Mime type of new image. |
158 * @param {function(string)} callback Callback. | 159 * @param {function(string)} callback Callback. |
159 * @private | 160 * @private |
160 */ | 161 */ |
161 Gallery.Item.prototype.createCopyName_ = function(dirEntry, callback) { | 162 Gallery.Item.prototype.createCopyName_ = function( |
| 163 dirEntry, newMimeType, callback) { |
162 var name = this.getFileName(); | 164 var name = this.getFileName(); |
163 | 165 |
164 // If the item represents a file created during the current Gallery session | 166 // If the item represents a file created during the current Gallery session |
165 // we reuse it for subsequent saves instead of creating multiple copies. | 167 // we reuse it for subsequent saves instead of creating multiple copies. |
166 if (!this.original_) { | 168 if (!this.original_) { |
167 callback(name); | 169 callback(name); |
168 return; | 170 return; |
169 } | 171 } |
170 | 172 |
171 var ext = ''; | 173 var baseName = name.replace(/\.[^\.\/]+$/, ''); |
172 var index = name.lastIndexOf('.'); | 174 var ext = newMimeType === 'image/jpeg' ? '.jpg' : '.png'; |
173 if (index != -1) { | |
174 ext = name.substr(index); | |
175 name = name.substr(0, index); | |
176 } | |
177 | |
178 if (!ext.match(/jpe?g/i)) { | |
179 // Chrome can natively encode only two formats: JPEG and PNG. | |
180 // All non-JPEG images are saved in PNG, hence forcing the file extension. | |
181 ext = '.png'; | |
182 } | |
183 | 175 |
184 function tryNext(tries) { | 176 function tryNext(tries) { |
185 // All the names are used. Let's overwrite the last one. | 177 // All the names are used. Let's overwrite the last one. |
186 if (tries == 0) { | 178 if (tries == 0) { |
187 setTimeout(callback, 0, name + ext); | 179 setTimeout(callback, 0, baseName + ext); |
188 return; | 180 return; |
189 } | 181 } |
190 | 182 |
191 // If the file name contains the copy signature add/advance the sequential | 183 // If the file name contains the copy signature add/advance the sequential |
192 // number. | 184 // number. |
193 var matchN = Gallery.Item.REGEXP_COPY_N.exec(name); | 185 var matchN = Gallery.Item.REGEXP_COPY_N.exec(baseName); |
194 var match0 = Gallery.Item.REGEXP_COPY_0.exec(name); | 186 var match0 = Gallery.Item.REGEXP_COPY_0.exec(baseName); |
195 if (matchN && matchN[1] && matchN[2]) { | 187 if (matchN && matchN[1] && matchN[2]) { |
196 var copyNumber = parseInt(matchN[2], 10) + 1; | 188 var copyNumber = parseInt(matchN[2], 10) + 1; |
197 name = matchN[1] + Gallery.Item.COPY_SIGNATURE + ' (' + copyNumber + ')'; | 189 baseName = matchN[1] + Gallery.Item.COPY_SIGNATURE + |
| 190 ' (' + copyNumber + ')'; |
198 } else if (match0 && match0[1]) { | 191 } else if (match0 && match0[1]) { |
199 name = match0[1] + Gallery.Item.COPY_SIGNATURE + ' (1)'; | 192 baseName = match0[1] + Gallery.Item.COPY_SIGNATURE + ' (1)'; |
200 } else { | 193 } else { |
201 name += Gallery.Item.COPY_SIGNATURE; | 194 baseName += Gallery.Item.COPY_SIGNATURE; |
202 } | 195 } |
203 | 196 |
204 dirEntry.getFile(name + ext, {create: false, exclusive: false}, | 197 dirEntry.getFile(baseName + ext, {create: false, exclusive: false}, |
205 tryNext.bind(null, tries - 1), | 198 tryNext.bind(null, tries - 1), |
206 callback.bind(null, name + ext)); | 199 callback.bind(null, baseName + ext)); |
207 } | 200 } |
208 | 201 |
209 tryNext(10); | 202 tryNext(10); |
210 }; | 203 }; |
211 | 204 |
212 /** | 205 /** |
213 * Writes the new item content to either the existing or a new file. | 206 * Writes the new item content to either the existing or a new file. |
214 * | 207 * |
215 * @param {!VolumeManager} volumeManager Volume manager instance. | 208 * @param {!VolumeManager} volumeManager Volume manager instance. |
216 * @param {!MetadataModel} metadataModel | 209 * @param {!MetadataModel} metadataModel |
217 * @param {DirectoryEntry} fallbackDir Fallback directory in case the current | 210 * @param {DirectoryEntry} fallbackDir Fallback directory in case the current |
218 * directory is read only. | 211 * directory is read only. |
219 * @param {boolean} overwrite Whether to overwrite the image to the item or not. | 212 * @param {boolean} overwrite Whether to overwrite the image to the item or not. |
220 * @param {!HTMLCanvasElement} canvas Source canvas. | 213 * @param {!HTMLCanvasElement} canvas Source canvas. |
221 * @param {function(boolean)} callback Callback accepting true for success. | 214 * @param {function(boolean)} callback Callback accepting true for success. |
222 */ | 215 */ |
223 Gallery.Item.prototype.saveToFile = function( | 216 Gallery.Item.prototype.saveToFile = function( |
224 volumeManager, metadataModel, fallbackDir, overwrite, canvas, callback) { | 217 volumeManager, metadataModel, fallbackDir, overwrite, canvas, callback) { |
225 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); | 218 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); |
226 | 219 |
227 var name = this.getFileName(); | 220 var name = this.getFileName(); |
| 221 var newMimeType = name.match(/\.jpe?g$/i) || FileType.isRaw(this.entry_) ? |
| 222 'image/jpeg' : 'image/png'; |
228 | 223 |
229 var onSuccess = function(entry) { | 224 var onSuccess = function(entry) { |
230 var locationInfo = volumeManager.getLocationInfo(entry); | 225 var locationInfo = volumeManager.getLocationInfo(entry); |
231 if (!locationInfo) { | 226 if (!locationInfo) { |
232 // Reuse old location info if it fails to obtain location info. | 227 // Reuse old location info if it fails to obtain location info. |
233 locationInfo = this.locationInfo_; | 228 locationInfo = this.locationInfo_; |
234 } | 229 } |
235 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); | 230 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); |
236 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); | 231 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); |
237 | 232 |
(...skipping 25 matching lines...) Expand all Loading... |
263 var blob; | 258 var blob; |
264 var fileWriter; | 259 var fileWriter; |
265 | 260 |
266 metadataModel.get( | 261 metadataModel.get( |
267 [fileEntry], | 262 [fileEntry], |
268 ['mediaMimeType', 'contentMimeType', 'ifd', 'exifLittleEndian'] | 263 ['mediaMimeType', 'contentMimeType', 'ifd', 'exifLittleEndian'] |
269 ).then(function(metadataItems) { | 264 ).then(function(metadataItems) { |
270 // Create the blob of new image. | 265 // Create the blob of new image. |
271 var metadataItem = metadataItems[0]; | 266 var metadataItem = metadataItems[0]; |
272 metadataItem.modificationTime = new Date(); | 267 metadataItem.modificationTime = new Date(); |
| 268 metadataItem.mediaMimeType = newMimeType; |
273 var metadataEncoder = ImageEncoder.encodeMetadata( | 269 var metadataEncoder = ImageEncoder.encodeMetadata( |
274 metadataItem, canvas, /* quality for thumbnail*/ 0.8); | 270 metadataItem, canvas, /* quality for thumbnail*/ 0.8); |
275 // Contrary to what one might think 1.0 is not a good default. Opening | 271 // Contrary to what one might think 1.0 is not a good default. Opening |
276 // and saving an typical photo taken with consumer camera increases | 272 // and saving an typical photo taken with consumer camera increases |
277 // its file size by 50-100%. Experiments show that 0.9 is much better. | 273 // its file size by 50-100%. Experiments show that 0.9 is much better. |
278 // It shrinks some photos a bit, keeps others about the same size, but | 274 // It shrinks some photos a bit, keeps others about the same size, but |
279 // does not visibly lower the quality. | 275 // does not visibly lower the quality. |
280 blob = ImageEncoder.getBlob(canvas, metadataEncoder, 0.9); | 276 blob = ImageEncoder.getBlob(canvas, metadataEncoder, 0.9); |
281 }).then(function() { | 277 }.bind(this)).then(function() { |
282 // Create writer. | 278 // Create writer. |
283 return new Promise(function(fullfill, reject) { | 279 return new Promise(function(fullfill, reject) { |
284 fileEntry.createWriter(fullfill, reject); | 280 fileEntry.createWriter(fullfill, reject); |
285 }); | 281 }); |
286 }).then(function(writer) { | 282 }).then(function(writer) { |
287 fileWriter = writer; | 283 fileWriter = writer; |
288 | 284 |
289 // Truncates the file to 0 byte if it overwrites. | 285 // Truncates the file to 0 byte if it overwrites. |
290 return new Promise(function(fulfill, reject) { | 286 return new Promise(function(fulfill, reject) { |
291 if (!newFile) { | 287 if (!newFile) { |
(...skipping 29 matching lines...) Expand all Loading... |
321 }.bind(this), onError); | 317 }.bind(this), onError); |
322 }.bind(this); | 318 }.bind(this); |
323 | 319 |
324 var checkExistence = function(dir) { | 320 var checkExistence = function(dir) { |
325 dir.getFile(name, {create: false, exclusive: false}, | 321 dir.getFile(name, {create: false, exclusive: false}, |
326 getFile.bind(null, dir, false /* existing file */), | 322 getFile.bind(null, dir, false /* existing file */), |
327 getFile.bind(null, dir, true /* create new file */)); | 323 getFile.bind(null, dir, true /* create new file */)); |
328 }; | 324 }; |
329 | 325 |
330 var saveToDir = function(dir) { | 326 var saveToDir = function(dir) { |
331 if (overwrite && !this.locationInfo_.isReadOnly) { | 327 if (overwrite && |
| 328 !this.locationInfo_.isReadOnly && |
| 329 !FileType.isRaw(this.entry_)) { |
332 checkExistence(dir); | 330 checkExistence(dir); |
333 } else { | 331 } else { |
334 this.createCopyName_(dir, function(copyName) { | 332 this.createCopyName_(dir, newMimeType, function(copyName) { |
335 this.original_ = false; | 333 this.original_ = false; |
336 name = copyName; | 334 name = copyName; |
337 checkExistence(dir); | 335 checkExistence(dir); |
338 }.bind(this)); | 336 }.bind(this)); |
339 } | 337 } |
340 }.bind(this); | 338 }.bind(this); |
341 | 339 |
342 if (this.locationInfo_.isReadOnly) { | 340 if (this.locationInfo_.isReadOnly) { |
343 saveToDir(fallbackDir); | 341 saveToDir(fallbackDir); |
344 } else { | 342 } else { |
(...skipping 30 matching lines...) Expand all Loading... |
375 return Promise.reject(str('GALLERY_FILE_EXISTS')); | 373 return Promise.reject(str('GALLERY_FILE_EXISTS')); |
376 }, function() { | 374 }, function() { |
377 return new Promise( | 375 return new Promise( |
378 this.entry_.moveTo.bind(this.entry_, parentDirectory, newFileName)); | 376 this.entry_.moveTo.bind(this.entry_, parentDirectory, newFileName)); |
379 }.bind(this)); | 377 }.bind(this)); |
380 }.bind(this)); | 378 }.bind(this)); |
381 }.bind(this)).then(function(entry) { | 379 }.bind(this)).then(function(entry) { |
382 this.entry_ = entry; | 380 this.entry_ = entry; |
383 }.bind(this)); | 381 }.bind(this)); |
384 }; | 382 }; |
OLD | NEW |