Add ability for NetLogLogger to gather data from more than just NetLog
[chromium-blink-merge.git] / ui / file_manager / gallery / js / image_editor / image_encoder.js
blob5d783fb761449f50e55cdbdf8c4dbf8ece236c1b
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
3 // found in the LICENSE file.
5 /**
6 * A namespace class for image encoding functions. All methods are static.
7 */
8 function ImageEncoder() {}
10 /**
11 * The value 360 px is enough in Files.app grid view for HiDPI devices.
12 * @const {number}
14 ImageEncoder.MAX_THUMBNAIL_DIMENSION = 360;
16 /**
17 * Tries to create thumbnail if the image width or height longer than the size.
18 * @const {number}
20 ImageEncoder.MIN_IMAGE_DIMENSION_FOR_THUMBNAIL =
21 ImageEncoder.MAX_THUMBNAIL_DIMENSION * 4;
23 /**
24 * Metadata encoders.
25 * @type {!Object.<string,function(
26 * new:ImageEncoder.MetadataEncoder,!MetadataItem)>}
27 * @const
29 ImageEncoder.metadataEncoders = {};
31 /**
32 * Registers metadata encoder.
33 * @param {function(new:ImageEncoder.MetadataEncoder,!MetadataItem)} constructor
34 * Constructor of a metadata encoder.
35 * @param {string} mimeType Mime type of the metadata encoder.
37 ImageEncoder.registerMetadataEncoder = function(constructor, mimeType) {
38 ImageEncoder.metadataEncoders[mimeType] = constructor;
41 /**
42 * Create a metadata encoder.
44 * The encoder will own and modify a copy of the original metadata.
46 * @param {!MetadataItem} metadata Original metadata.
47 * @return {!ImageEncoder.MetadataEncoder} Created metadata encoder.
49 ImageEncoder.createMetadataEncoder = function(metadata) {
50 var constructor =
51 ImageEncoder.metadataEncoders[metadata.mediaMimeType || ""] ||
52 ImageEncoder.MetadataEncoder;
53 return new constructor(metadata);
56 /**
57 * Create a metadata encoder object holding a copy of metadata
58 * modified according to the properties of the supplied image.
60 * @param {!MetadataItem} metadata Original metadata.
61 * @param {!HTMLCanvasElement} canvas Canvas to use for metadata.
62 * @param {number} thumbnailQuality Encoding quality of a thumbnail.
63 * @return {!ImageEncoder.MetadataEncoder} Encoder with encoded metadata.
65 ImageEncoder.encodeMetadata = function(metadata, canvas, thumbnailQuality) {
66 var encoder = ImageEncoder.createMetadataEncoder(metadata);
67 encoder.setImageData(canvas);
68 encoder.setThumbnailData(ImageEncoder.createThumbnail(canvas),
69 thumbnailQuality);
70 return encoder;
73 /**
74 * Return a blob with the encoded image with metadata inserted.
75 * @param {!HTMLCanvasElement} canvas The canvas with the image to be encoded.
76 * @param {!ImageEncoder.MetadataEncoder} metadataEncoder Encoder to use.
77 * @param {number} imageQuality (0..1], Encoding quality of an image.
78 * @return {!Blob} encoded data.
80 ImageEncoder.getBlob = function(canvas, metadataEncoder, imageQuality) {
81 ImageUtil.trace.resetTimer('dataurl');
82 // WebKit does not support canvas.toBlob yet so canvas.toDataURL is
83 // the only way to use the Chrome built-in image encoder.
84 var dataURL = canvas.toDataURL(metadataEncoder.mimeType, imageQuality);
85 ImageUtil.trace.reportTimer('dataurl');
87 var encodedImage = ImageEncoder.decodeDataURL(dataURL);
89 var encodedMetadata = metadataEncoder.encode();
91 var slices = [];
93 // TODO(kaznacheev): refactor |stringToArrayBuffer| and |encode| to return
94 // arrays instead of array buffers.
95 function appendSlice(arrayBuffer) {
96 slices.push(new DataView(arrayBuffer));
99 ImageUtil.trace.resetTimer('blob');
100 if (encodedMetadata.byteLength != 0) {
101 var metadataRange = metadataEncoder.findInsertionRange(encodedImage);
102 appendSlice(ImageEncoder.stringToArrayBuffer(
103 encodedImage, 0, metadataRange.from));
105 appendSlice(metadataEncoder.encode());
107 appendSlice(ImageEncoder.stringToArrayBuffer(
108 encodedImage, metadataRange.to, encodedImage.length));
109 } else {
110 appendSlice(ImageEncoder.stringToArrayBuffer(
111 encodedImage, 0, encodedImage.length));
113 var blob = new Blob(slices, {type: metadataEncoder.mimeType});
114 ImageUtil.trace.reportTimer('blob');
115 return blob;
119 * Decode a dataURL into a binary string containing the encoded image.
121 * Why return a string? Calling atob and having the rest of the code deal
122 * with a string is several times faster than decoding base64 in Javascript.
124 * @param {string} dataURL Data URL to decode.
125 * @return {string} A binary string (char codes are the actual byte values).
127 ImageEncoder.decodeDataURL = function(dataURL) {
128 // Skip the prefix ('data:image/<type>;base64,')
129 var base64string = dataURL.substring(dataURL.indexOf(',') + 1);
130 return window.atob(base64string);
134 * Return a thumbnail for an image.
135 * @param {!HTMLCanvasElement} canvas Original image.
136 * @return {HTMLCanvasElement} Thumbnail canvas.
138 ImageEncoder.createThumbnail = function(canvas) {
139 if (canvas.width < ImageEncoder.MIN_IMAGE_DIMENSION_FOR_THUMBNAIL &&
140 canvas.height < ImageEncoder.MIN_IMAGE_DIMENSION_FOR_THUMBNAIL) {
141 return null;
144 var ratio = Math.min(ImageEncoder.MAX_THUMBNAIL_DIMENSION / canvas.width,
145 ImageEncoder.MAX_THUMBNAIL_DIMENSION / canvas.height);
146 var thumbnailCanvas = assertInstanceof(
147 canvas.ownerDocument.createElement('canvas'), HTMLCanvasElement);
148 thumbnailCanvas.width = Math.round(canvas.width * ratio);
149 thumbnailCanvas.height = Math.round(canvas.height * ratio);
151 var context = thumbnailCanvas.getContext('2d');
152 context.drawImage(canvas,
153 0, 0, canvas.width, canvas.height,
154 0, 0, thumbnailCanvas.width, thumbnailCanvas.height);
156 return thumbnailCanvas;
160 * Converts string to an array buffer.
161 * @param {string} string A string.
162 * @param {number} from Start index.
163 * @param {number} to End index.
164 * @return {!ArrayBuffer} A created array buffer is returned.
166 ImageEncoder.stringToArrayBuffer = function(string, from, to) {
167 var size = to - from;
168 var array = new Uint8Array(size);
169 for (var i = 0; i != size; i++) {
170 array[i] = string.charCodeAt(from + i);
172 return array.buffer;
176 * A base class for a metadata encoder.
178 * Serves as a default metadata encoder for images that none of the metadata
179 * parsers recognized.
181 * @param {!MetadataItem} originalMetadata Starting metadata.
182 * @constructor
183 * @struct
185 ImageEncoder.MetadataEncoder = function(originalMetadata) {
186 var mimeType = ImageEncoder.MetadataEncoder.getMimeType_(originalMetadata);
189 * Chrome can only encode JPEG and PNG. Force PNG mime type so that we
190 * can save to file and generate a thumbnail.
191 * @public {string}
193 this.mimeType = mimeType === 'image/jpeg' ? 'image/jpeg' : 'image/png';
196 * @protected {string}
198 this.thumbnailDataUrl = '';
201 * @protected {number}
203 this.imageWidth = 0;
206 * @protected {number}
208 this.imageHeight = 0;
212 * Gets mime type from metadata. It reads media.mimeType at first, and if it
213 * fails, it falls back to external.contentMimeType. If both fields are
214 * undefined, it means that metadata is broken. Then it throws an exception.
216 * @param {!MetadataItem} metadata Metadata.
217 * @return {string} Mime type.
218 * @private
220 ImageEncoder.MetadataEncoder.getMimeType_ = function(metadata) {
221 if (metadata.mediaMimeType)
222 return metadata.mediaMimeType;
223 else if (metadata.contentMimeType)
224 return metadata.contentMimeType;
226 assertNotReached();
230 * Sets an image data.
231 * @param {!HTMLCanvasElement} canvas Canvas or anything with width and height
232 * properties.
234 ImageEncoder.MetadataEncoder.prototype.setImageData = function(canvas) {
235 this.imageWidth = canvas.width;
236 this.imageHeight = canvas.height;
240 * @param {HTMLCanvasElement} canvas Canvas to use as thumbnail. Note that it
241 * can be null.
242 * @param {number} quality Thumbnail quality.
244 ImageEncoder.MetadataEncoder.prototype.setThumbnailData =
245 function(canvas, quality) {
246 this.thumbnailDataUrl =
247 canvas ? canvas.toDataURL(this.mimeType, quality) : '';
251 * Returns a range where the metadata is (or should be) located.
252 * @param {string} encodedImage An encoded image.
253 * @return {{from:number, to:number}} An object with from and to properties.
255 ImageEncoder.MetadataEncoder.prototype.
256 findInsertionRange = function(encodedImage) { return {from: 0, to: 0}; };
259 * Returns serialized metadata ready to write to an image file.
260 * The return type is optimized for passing to Blob.append.
261 * @return {!ArrayBuffer} Serialized metadata.
263 ImageEncoder.MetadataEncoder.prototype.encode = function() {
264 return new Uint8Array(0).buffer;