Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / gallery / js / image_editor / image_encoder.js
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.
4
5 /**
6  * A namespace class for image encoding functions. All methods are static.
7  */
8 function ImageEncoder() {}
9
10 /**
11  * @type {Array.<Object>}
12  */
13 ImageEncoder.metadataEncoders = {};
14
15 /**
16  * @param {function(new:ImageEncoder.MetadataEncoder)} constructor
17  *     // TODO(JSDOC).
18  * @param {string} mimeType  // TODO(JSDOC).
19  */
20 ImageEncoder.registerMetadataEncoder = function(constructor, mimeType) {
21   ImageEncoder.metadataEncoders[mimeType] = constructor;
22 };
23
24 /**
25  * Create a metadata encoder.
26  *
27  * The encoder will own and modify a copy of the original metadata.
28  *
29  * @param {Object} metadata Original metadata.
30  * @return {ImageEncoder.MetadataEncoder} Created metadata encoder.
31  */
32 ImageEncoder.createMetadataEncoder = function(metadata) {
33   var constructor =
34       (metadata && ImageEncoder.metadataEncoders[metadata.mimeType]) ||
35       ImageEncoder.MetadataEncoder;
36   return new constructor(metadata);
37 };
38
39
40 /**
41  * Create a metadata encoder object holding a copy of metadata
42  * modified according to the properties of the supplied image.
43  *
44  * @param {Object} metadata Original metadata.
45  * @param {HTMLCanvasElement} canvas Canvas to use for metadata.
46  * @param {number} quality Encoding quality (defaults to 1).
47  * @return {ImageEncoder.MetadataEncoder} Encoder with encoded metadata.
48  */
49 ImageEncoder.encodeMetadata = function(metadata, canvas, quality) {
50   var encoder = ImageEncoder.createMetadataEncoder(metadata);
51   encoder.setImageData(canvas);
52   encoder.setThumbnailData(ImageEncoder.createThumbnail(canvas), quality || 1);
53   return encoder;
54 };
55
56
57 /**
58  * Return a blob with the encoded image with metadata inserted.
59  * @param {HTMLCanvasElement} canvas The canvas with the image to be encoded.
60  * @param {ImageEncoder.MetadataEncoder} metadataEncoder Encoder to use.
61  * @param {number} quality (0..1], Encoding quality, defaults to 0.9.
62  * @return {Blob} encoded data.
63  */
64 ImageEncoder.getBlob = function(canvas, metadataEncoder, quality) {
65   // 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
67   // by 50-100%.
68   // 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.
70   quality = quality || 0.9;
71
72   ImageUtil.trace.resetTimer('dataurl');
73   // WebKit does not support canvas.toBlob yet so canvas.toDataURL is
74   // the only way to use the Chrome built-in image encoder.
75   var dataURL =
76       canvas.toDataURL(metadataEncoder.getMetadata().mimeType, quality);
77   ImageUtil.trace.reportTimer('dataurl');
78
79   var encodedImage = ImageEncoder.decodeDataURL(dataURL);
80
81   var encodedMetadata = metadataEncoder.encode();
82
83   var slices = [];
84
85   // TODO(kaznacheev): refactor |stringToArrayBuffer| and |encode| to return
86   // arrays instead of array buffers.
87   function appendSlice(arrayBuffer) {
88     slices.push(new DataView(arrayBuffer));
89   }
90
91   ImageUtil.trace.resetTimer('blob');
92   if (encodedMetadata.byteLength != 0) {
93     var metadataRange = metadataEncoder.findInsertionRange(encodedImage);
94     appendSlice(ImageEncoder.stringToArrayBuffer(
95         encodedImage, 0, metadataRange.from));
96
97     appendSlice(metadataEncoder.encode());
98
99     appendSlice(ImageEncoder.stringToArrayBuffer(
100         encodedImage, metadataRange.to, encodedImage.length));
101   } else {
102     appendSlice(ImageEncoder.stringToArrayBuffer(
103         encodedImage, 0, encodedImage.length));
104   }
105   var blob = new Blob(slices, {type: metadataEncoder.getMetadata().mimeType});
106   ImageUtil.trace.reportTimer('blob');
107   return blob;
108 };
109
110 /**
111  * Decode a dataURL into a binary string containing the encoded image.
112  *
113  * 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.
115  *
116  * @param {string} dataURL Data URL to decode.
117  * @return {string} A binary string (char codes are the actual byte values).
118  */
119 ImageEncoder.decodeDataURL = function(dataURL) {
120   // Skip the prefix ('data:image/<type>;base64,')
121   var base64string = dataURL.substring(dataURL.indexOf(',') + 1);
122   return atob(base64string);
123 };
124
125 /**
126  * Return a thumbnail for an image.
127  * @param {HTMLCanvasElement} canvas Original image.
128  * @param {number=} opt_shrinkage Thumbnail should be at least this much smaller
129  *     than the original image (in each dimension).
130  * @return {HTMLCanvasElement} Thumbnail canvas.
131  */
132 ImageEncoder.createThumbnail = function(canvas, opt_shrinkage) {
133   var MAX_THUMBNAIL_DIMENSION = 320;
134
135   opt_shrinkage = Math.max(opt_shrinkage || 4,
136                            canvas.width / MAX_THUMBNAIL_DIMENSION,
137                            canvas.height / MAX_THUMBNAIL_DIMENSION);
138
139   var thumbnailCanvas = canvas.ownerDocument.createElement('canvas');
140   thumbnailCanvas.width = Math.round(canvas.width / opt_shrinkage);
141   thumbnailCanvas.height = Math.round(canvas.height / opt_shrinkage);
142
143   var context = thumbnailCanvas.getContext('2d');
144   context.drawImage(canvas,
145       0, 0, canvas.width, canvas.height,
146       0, 0, thumbnailCanvas.width, thumbnailCanvas.height);
147
148   return thumbnailCanvas;
149 };
150
151 /**
152  * TODO(JSDOC)
153  * @param {string} string  // TODO(JSDOC).
154  * @param {number} from  // TODO(JSDOC).
155  * @param {number} to  // TODO(JSDOC).
156  * @return {ArrayBuffer}  // TODO(JSDOC).
157  */
158 ImageEncoder.stringToArrayBuffer = function(string, from, to) {
159   var size = to - from;
160   var array = new Uint8Array(size);
161   for (var i = 0; i != size; i++) {
162     array[i] = string.charCodeAt(from + i);
163   }
164   return array.buffer;
165 };
166
167 /**
168  * A base class for a metadata encoder.
169  *
170  * Serves as a default metadata encoder for images that none of the metadata
171  * parsers recognized.
172  *
173  * @param {Object} original_metadata Starting metadata.
174  * @constructor
175  */
176 ImageEncoder.MetadataEncoder = function(original_metadata) {
177   this.metadata_ = MetadataCache.cloneMetadata(original_metadata) || {};
178   if (this.metadata_.mimeType != 'image/jpeg') {
179     // Chrome can only encode JPEG and PNG. Force PNG mime type so that we
180     // can save to file and generate a thumbnail.
181     this.metadata_.mimeType = 'image/png';
182   }
183 };
184
185 /**
186  * TODO(JSDOC)
187  * @return {Object}   // TODO(JSDOC).
188  */
189 ImageEncoder.MetadataEncoder.prototype.getMetadata = function() {
190   return this.metadata_;
191 };
192
193 /**
194  * @param {HTMLCanvasElement|Object} canvas Canvas or or anything with
195  *                                          width and height properties.
196  */
197 ImageEncoder.MetadataEncoder.prototype.setImageData = function(canvas) {
198   this.metadata_.width = canvas.width;
199   this.metadata_.height = canvas.height;
200 };
201
202 /**
203  * @param {HTMLCanvasElement} canvas Canvas to use as thumbnail.
204  * @param {number} quality Thumbnail quality.
205  */
206 ImageEncoder.MetadataEncoder.prototype.setThumbnailData =
207     function(canvas, quality) {
208   this.metadata_.thumbnailURL =
209       canvas.toDataURL(this.metadata_.mimeType, quality);
210   delete this.metadata_.thumbnailTransform;
211 };
212
213 /**
214  * Return a range where the metadata is (or should be) located.
215  * @param {string} encodedImage // TODO(JSDOC).
216  * @return {Object} An object with from and to properties.
217  */
218 ImageEncoder.MetadataEncoder.prototype.
219     findInsertionRange = function(encodedImage) { return {from: 0, to: 0}; };
220
221 /**
222  * Return serialized metadata ready to write to an image file.
223  * The return type is optimized for passing to Blob.append.
224  * @return {ArrayBuffer} // TODO(JSDOC).
225  */
226 ImageEncoder.MetadataEncoder.prototype.encode = function() {
227   return new Uint8Array(0).buffer;
228 };