Chromium Code Reviews| 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 * A namespace class for image encoding functions. All methods are static. | 6 * A namespace class for image encoding functions. All methods are static. |
| 7 */ | 7 */ |
| 8 function ImageEncoder() {} | 8 function ImageEncoder() {} |
| 9 | 9 |
| 10 /** | 10 /** |
| 11 * @type {Array.<Object>} | 11 * Metadata encoders. |
| 12 * @type {!Object.<string,function(new:ImageEncoder.MetadataEncoder,!Object)>} | |
| 13 * @const | |
| 12 */ | 14 */ |
| 13 ImageEncoder.metadataEncoders = {}; | 15 ImageEncoder.metadataEncoders = {}; |
| 14 | 16 |
| 15 /** | 17 /** |
| 16 * @param {function(new:ImageEncoder.MetadataEncoder)} constructor | 18 * Registers metadata encoder. |
| 17 * // TODO(JSDOC). | 19 * @param {function(new:ImageEncoder.MetadataEncoder,!Object)} constructor |
| 18 * @param {string} mimeType // TODO(JSDOC). | 20 * Constructor of a metadata encoder. |
| 21 * @param {string} mimeType Mime type of the metadata encoder. | |
| 19 */ | 22 */ |
| 20 ImageEncoder.registerMetadataEncoder = function(constructor, mimeType) { | 23 ImageEncoder.registerMetadataEncoder = function(constructor, mimeType) { |
| 21 ImageEncoder.metadataEncoders[mimeType] = constructor; | 24 ImageEncoder.metadataEncoders[mimeType] = constructor; |
| 22 }; | 25 }; |
| 23 | 26 |
| 24 /** | 27 /** |
| 25 * Create a metadata encoder. | 28 * Create a metadata encoder. |
| 26 * | 29 * |
| 27 * The encoder will own and modify a copy of the original metadata. | 30 * The encoder will own and modify a copy of the original metadata. |
| 28 * | 31 * |
| 29 * @param {Object} metadata Original metadata. | 32 * @param {!Object} metadata Original metadata. |
| 30 * @return {ImageEncoder.MetadataEncoder} Created metadata encoder. | 33 * @return {!ImageEncoder.MetadataEncoder} Created metadata encoder. |
| 31 */ | 34 */ |
| 32 ImageEncoder.createMetadataEncoder = function(metadata) { | 35 ImageEncoder.createMetadataEncoder = function(metadata) { |
| 33 var constructor = | 36 var constructor = |
| 34 (metadata && ImageEncoder.metadataEncoders[metadata.mimeType]) || | 37 (metadata && ImageEncoder.metadataEncoders[metadata.mimeType]) || |
| 35 ImageEncoder.MetadataEncoder; | 38 ImageEncoder.MetadataEncoder; |
| 36 return new constructor(metadata); | 39 return new constructor(metadata); |
| 37 }; | 40 }; |
| 38 | 41 |
| 39 | 42 |
| 40 /** | 43 /** |
| 41 * Create a metadata encoder object holding a copy of metadata | 44 * Create a metadata encoder object holding a copy of metadata |
| 42 * modified according to the properties of the supplied image. | 45 * modified according to the properties of the supplied image. |
| 43 * | 46 * |
| 44 * @param {Object} metadata Original metadata. | 47 * @param {!Object} metadata Original metadata. |
| 45 * @param {HTMLCanvasElement} canvas Canvas to use for metadata. | 48 * @param {!HTMLCanvasElement} canvas Canvas to use for metadata. |
| 46 * @param {number} quality Encoding quality (defaults to 1). | 49 * @param {number} quality Encoding quality (defaults to 1). |
| 47 * @return {ImageEncoder.MetadataEncoder} Encoder with encoded metadata. | 50 * @return {!ImageEncoder.MetadataEncoder} Encoder with encoded metadata. |
| 48 */ | 51 */ |
| 49 ImageEncoder.encodeMetadata = function(metadata, canvas, quality) { | 52 ImageEncoder.encodeMetadata = function(metadata, canvas, quality) { |
| 50 var encoder = ImageEncoder.createMetadataEncoder(metadata); | 53 var encoder = ImageEncoder.createMetadataEncoder(metadata); |
| 51 encoder.setImageData(canvas); | 54 encoder.setImageData(canvas); |
| 52 encoder.setThumbnailData(ImageEncoder.createThumbnail(canvas), quality || 1); | 55 encoder.setThumbnailData(ImageEncoder.createThumbnail(canvas), quality || 1); |
| 53 return encoder; | 56 return encoder; |
| 54 }; | 57 }; |
| 55 | 58 |
| 56 | 59 |
| 57 /** | 60 /** |
| 58 * Return a blob with the encoded image with metadata inserted. | 61 * Return a blob with the encoded image with metadata inserted. |
| 59 * @param {HTMLCanvasElement} canvas The canvas with the image to be encoded. | 62 * @param {!HTMLCanvasElement} canvas The canvas with the image to be encoded. |
| 60 * @param {ImageEncoder.MetadataEncoder} metadataEncoder Encoder to use. | 63 * @param {!ImageEncoder.MetadataEncoder} metadataEncoder Encoder to use. |
| 61 * @param {number} quality (0..1], Encoding quality, defaults to 0.9. | 64 * @param {number} quality (0..1], Encoding quality, defaults to 0.9. |
| 62 * @return {Blob} encoded data. | 65 * @return {!Blob} encoded data. |
| 63 */ | 66 */ |
| 64 ImageEncoder.getBlob = function(canvas, metadataEncoder, quality) { | 67 ImageEncoder.getBlob = function(canvas, metadataEncoder, quality) { |
| 65 // Contrary to what one might think 1.0 is not a good default. Opening and | 68 // Contrary to what one might think 1.0 is not a good default. Opening and |
| 66 // saving an typical photo taken with consumer camera increases its file size | 69 // saving an typical photo taken with consumer camera increases its file size |
| 67 // by 50-100%. | 70 // by 50-100%. |
| 68 // Experiments show that 0.9 is much better. It shrinks some photos a bit, | 71 // Experiments show that 0.9 is much better. It shrinks some photos a bit, |
| 69 // keeps others about the same size, but does not visibly lower the quality. | 72 // keeps others about the same size, but does not visibly lower the quality. |
| 70 quality = quality || 0.9; | 73 quality = quality || 0.9; |
| 71 | 74 |
| 72 ImageUtil.trace.resetTimer('dataurl'); | 75 ImageUtil.trace.resetTimer('dataurl'); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 * | 115 * |
| 113 * Why return a string? Calling atob and having the rest of the code deal | 116 * Why return a string? Calling atob and having the rest of the code deal |
| 114 * with a string is several times faster than decoding base64 in Javascript. | 117 * with a string is several times faster than decoding base64 in Javascript. |
| 115 * | 118 * |
| 116 * @param {string} dataURL Data URL to decode. | 119 * @param {string} dataURL Data URL to decode. |
| 117 * @return {string} A binary string (char codes are the actual byte values). | 120 * @return {string} A binary string (char codes are the actual byte values). |
| 118 */ | 121 */ |
| 119 ImageEncoder.decodeDataURL = function(dataURL) { | 122 ImageEncoder.decodeDataURL = function(dataURL) { |
| 120 // Skip the prefix ('data:image/<type>;base64,') | 123 // Skip the prefix ('data:image/<type>;base64,') |
| 121 var base64string = dataURL.substring(dataURL.indexOf(',') + 1); | 124 var base64string = dataURL.substring(dataURL.indexOf(',') + 1); |
| 122 return atob(base64string); | 125 return window.atob(base64string); |
| 123 }; | 126 }; |
| 124 | 127 |
| 125 /** | 128 /** |
| 126 * Return a thumbnail for an image. | 129 * Return a thumbnail for an image. |
| 127 * @param {HTMLCanvasElement} canvas Original image. | 130 * @param {!HTMLCanvasElement} canvas Original image. |
| 128 * @param {number=} opt_shrinkage Thumbnail should be at least this much smaller | 131 * @param {number=} opt_shrinkage Thumbnail should be at least this much smaller |
| 129 * than the original image (in each dimension). | 132 * than the original image (in each dimension). |
| 130 * @return {HTMLCanvasElement} Thumbnail canvas. | 133 * @return {!HTMLCanvasElement} Thumbnail canvas. |
| 131 */ | 134 */ |
| 132 ImageEncoder.createThumbnail = function(canvas, opt_shrinkage) { | 135 ImageEncoder.createThumbnail = function(canvas, opt_shrinkage) { |
| 133 var MAX_THUMBNAIL_DIMENSION = 320; | 136 var MAX_THUMBNAIL_DIMENSION = 320; |
| 134 | 137 |
| 135 opt_shrinkage = Math.max(opt_shrinkage || 4, | 138 opt_shrinkage = Math.max(opt_shrinkage || 4, |
| 136 canvas.width / MAX_THUMBNAIL_DIMENSION, | 139 canvas.width / MAX_THUMBNAIL_DIMENSION, |
| 137 canvas.height / MAX_THUMBNAIL_DIMENSION); | 140 canvas.height / MAX_THUMBNAIL_DIMENSION); |
| 138 | 141 |
| 139 var thumbnailCanvas = canvas.ownerDocument.createElement('canvas'); | 142 var thumbnailCanvas = assertInstanceof( |
| 143 canvas.ownerDocument.createElement('canvas'), HTMLCanvasElement); | |
| 140 thumbnailCanvas.width = Math.round(canvas.width / opt_shrinkage); | 144 thumbnailCanvas.width = Math.round(canvas.width / opt_shrinkage); |
| 141 thumbnailCanvas.height = Math.round(canvas.height / opt_shrinkage); | 145 thumbnailCanvas.height = Math.round(canvas.height / opt_shrinkage); |
| 142 | 146 |
| 143 var context = thumbnailCanvas.getContext('2d'); | 147 var context = thumbnailCanvas.getContext('2d'); |
| 144 context.drawImage(canvas, | 148 context.drawImage(canvas, |
| 145 0, 0, canvas.width, canvas.height, | 149 0, 0, canvas.width, canvas.height, |
| 146 0, 0, thumbnailCanvas.width, thumbnailCanvas.height); | 150 0, 0, thumbnailCanvas.width, thumbnailCanvas.height); |
| 147 | 151 |
| 148 return thumbnailCanvas; | 152 return thumbnailCanvas; |
| 149 }; | 153 }; |
| 150 | 154 |
| 151 /** | 155 /** |
| 152 * TODO(JSDOC) | 156 * Convert string to an array buffer. |
|
hirono
2014/11/18 01:43:55
nit: Converts
yawano
2014/11/18 04:26:53
Done.
| |
| 153 * @param {string} string // TODO(JSDOC). | 157 * @param {string} string A string. |
| 154 * @param {number} from // TODO(JSDOC). | 158 * @param {number} from Strt index. |
| 155 * @param {number} to // TODO(JSDOC). | 159 * @param {number} to End index. |
| 156 * @return {ArrayBuffer} // TODO(JSDOC). | 160 * @return {!ArrayBuffer} A created array buffer is returned. |
| 157 */ | 161 */ |
| 158 ImageEncoder.stringToArrayBuffer = function(string, from, to) { | 162 ImageEncoder.stringToArrayBuffer = function(string, from, to) { |
| 159 var size = to - from; | 163 var size = to - from; |
| 160 var array = new Uint8Array(size); | 164 var array = new Uint8Array(size); |
| 161 for (var i = 0; i != size; i++) { | 165 for (var i = 0; i != size; i++) { |
| 162 array[i] = string.charCodeAt(from + i); | 166 array[i] = string.charCodeAt(from + i); |
| 163 } | 167 } |
| 164 return array.buffer; | 168 return array.buffer; |
| 165 }; | 169 }; |
| 166 | 170 |
| 167 /** | 171 /** |
| 168 * A base class for a metadata encoder. | 172 * A base class for a metadata encoder. |
| 169 * | 173 * |
| 170 * Serves as a default metadata encoder for images that none of the metadata | 174 * Serves as a default metadata encoder for images that none of the metadata |
| 171 * parsers recognized. | 175 * parsers recognized. |
| 172 * | 176 * |
| 173 * @param {Object} original_metadata Starting metadata. | 177 * @param {!Object} original_metadata Starting metadata. |
| 174 * @constructor | 178 * @constructor |
| 179 * @struct | |
| 175 */ | 180 */ |
| 176 ImageEncoder.MetadataEncoder = function(original_metadata) { | 181 ImageEncoder.MetadataEncoder = function(original_metadata) { |
| 177 this.metadata_ = MetadataCache.cloneMetadata(original_metadata) || {}; | 182 this.metadata_ = MetadataCache.cloneMetadata(original_metadata) || {}; |
| 178 if (this.metadata_.mimeType != 'image/jpeg') { | 183 if (this.metadata_.mimeType != 'image/jpeg') { |
| 179 // Chrome can only encode JPEG and PNG. Force PNG mime type so that we | 184 // Chrome can only encode JPEG and PNG. Force PNG mime type so that we |
| 180 // can save to file and generate a thumbnail. | 185 // can save to file and generate a thumbnail. |
| 181 this.metadata_.mimeType = 'image/png'; | 186 this.metadata_.mimeType = 'image/png'; |
| 182 } | 187 } |
| 183 }; | 188 }; |
| 184 | 189 |
| 185 /** | 190 /** |
| 186 * TODO(JSDOC) | 191 * Returns metadata. |
| 187 * @return {Object} // TODO(JSDOC). | 192 * @return {!Object} A metadata. |
| 188 */ | 193 */ |
| 189 ImageEncoder.MetadataEncoder.prototype.getMetadata = function() { | 194 ImageEncoder.MetadataEncoder.prototype.getMetadata = function() { |
| 190 return this.metadata_; | 195 return this.metadata_; |
| 191 }; | 196 }; |
| 192 | 197 |
| 193 /** | 198 /** |
| 194 * @param {HTMLCanvasElement|Object} canvas Canvas or or anything with | 199 * @param {!HTMLCanvasElement} canvas Canvas or or anything with |
| 195 * width and height properties. | 200 * width and height properties. |
| 196 */ | 201 */ |
| 197 ImageEncoder.MetadataEncoder.prototype.setImageData = function(canvas) { | 202 ImageEncoder.MetadataEncoder.prototype.setImageData = function(canvas) { |
| 198 this.metadata_.width = canvas.width; | 203 this.metadata_.width = canvas.width; |
| 199 this.metadata_.height = canvas.height; | 204 this.metadata_.height = canvas.height; |
| 200 }; | 205 }; |
| 201 | 206 |
| 202 /** | 207 /** |
| 203 * @param {HTMLCanvasElement} canvas Canvas to use as thumbnail. | 208 * @param {!HTMLCanvasElement} canvas Canvas to use as thumbnail. |
| 204 * @param {number} quality Thumbnail quality. | 209 * @param {number} quality Thumbnail quality. |
| 205 */ | 210 */ |
| 206 ImageEncoder.MetadataEncoder.prototype.setThumbnailData = | 211 ImageEncoder.MetadataEncoder.prototype.setThumbnailData = |
| 207 function(canvas, quality) { | 212 function(canvas, quality) { |
| 208 this.metadata_.thumbnailURL = | 213 this.metadata_.thumbnailURL = |
| 209 canvas.toDataURL(this.metadata_.mimeType, quality); | 214 canvas.toDataURL(this.metadata_.mimeType, quality); |
| 210 delete this.metadata_.thumbnailTransform; | 215 delete this.metadata_.thumbnailTransform; |
| 211 }; | 216 }; |
| 212 | 217 |
| 213 /** | 218 /** |
| 214 * Return a range where the metadata is (or should be) located. | 219 * Return a range where the metadata is (or should be) located. |
|
hirono
2014/11/18 01:43:55
Please fix it with 'Returns'.
yawano
2014/11/18 04:26:53
Done.
| |
| 215 * @param {string} encodedImage // TODO(JSDOC). | 220 * @param {string} encodedImage An encoded image. |
| 216 * @return {Object} An object with from and to properties. | 221 * @return {!{from:number, to:number}} An object with from and to properties. |
|
hirono
2014/11/18 01:43:55
I think we don't need to add '!'. Record type (suc
yawano
2014/11/18 04:26:53
Done.
| |
| 217 */ | 222 */ |
| 218 ImageEncoder.MetadataEncoder.prototype. | 223 ImageEncoder.MetadataEncoder.prototype. |
| 219 findInsertionRange = function(encodedImage) { return {from: 0, to: 0}; }; | 224 findInsertionRange = function(encodedImage) { return {from: 0, to: 0}; }; |
| 220 | 225 |
| 221 /** | 226 /** |
| 222 * Return serialized metadata ready to write to an image file. | 227 * Return serialized metadata ready to write to an image file. |
|
hirono
2014/11/18 01:43:55
ditto.
yawano
2014/11/18 04:26:53
Done.
| |
| 223 * The return type is optimized for passing to Blob.append. | 228 * The return type is optimized for passing to Blob.append. |
| 224 * @return {ArrayBuffer} // TODO(JSDOC). | 229 * @return {ArrayBuffer} Serialized metadata. |
|
hirono
2014/11/18 01:43:55
!ArrayBuffer ?
yawano
2014/11/18 04:26:53
Done.
| |
| 225 */ | 230 */ |
| 226 ImageEncoder.MetadataEncoder.prototype.encode = function() { | 231 ImageEncoder.MetadataEncoder.prototype.encode = function() { |
| 227 return new Uint8Array(0).buffer; | 232 return new Uint8Array(0).buffer; |
| 228 }; | 233 }; |
| OLD | NEW |