1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use of this source code is governed by a BSD-style license that can be
3ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// found in the LICENSE file.
4ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar exif = {
6ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  verbose: false,
7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  messageHandlers: {
9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    "init": function() {
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.log('thumbnailer initialized');
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    },
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    "get-exif": function(fileURL) {
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.processOneFile(fileURL, function callback(metadata) {
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          postMessage({verb: 'give-exif',
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                       arguments: [fileURL, metadata]});
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      });
18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    },
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  processOneFile: function(fileURL, callback) {
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var self = this;
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var currentStep = -1;
24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    function nextStep(var_args) {
26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      self.vlog('nextStep: ' + steps[currentStep + 1].name);
27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      steps[++currentStep].apply(self, arguments);
28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    function onError(err) {
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      self.vlog('Error processing: ' + fileURL + ': step: ' +
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                steps[currentStep].name + ": " + err);
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      postMessage({verb: 'give-exif-error',
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                   arguments: [fileURL, steps[currentStep].name, err]});
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var steps =
39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    [ // Step one, turn the url into an entry.
40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      function getEntry() {
41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        webkitResolveLocalFileSystemURL(fileURL,
42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                        function(entry) { nextStep(entry) },
43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                        onError);
44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      },
45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Step two, turn the entry into a file.
47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      function getFile(entry) {
48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        entry.file(function(file) { nextStep(file) }, onError);
49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      },
50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Step three, read the file header into a byte array.
52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      function readHeader(file) {
53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var reader = new FileReader(file.webkitSlice(0, 1024));
54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        reader.onerror = onError;
55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        reader.onload = function(event) { nextStep(file, reader.result) };
56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        reader.readAsArrayBuffer(file);
57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      },
58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
59ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Step four, find the exif marker and read all exif data.
60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      function findExif(file, buf) {
61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var br = new exif.BufferReader(buf);
62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var mark = br.readMark();
63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (mark != exif.MARK_SOI)
64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          return onError('Invalid file header: ' + mark.toString(16));
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        while (true) {
67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          if (mark == exif.MARK_SOS || br.eof()) {
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            return onError('Unable to find EXIF marker');
69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          }
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          mark = br.readMark();
72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          if (mark == exif.MARK_EXIF) {
73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            var length = br.readMarkLength();
74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            // Offsets inside the EXIF block are based after this bit of
76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            // magic, so we verify and discard it here, before exif parsing,
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            // to make offset calculations simpler.
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            var magic = br.readString(6);
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            if (magic != 'Exif\0\0')
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              return onError('Invalid EXIF magic: ' + magic.toString(16));
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            var pos = br.tell();
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            var reader = new FileReader();
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            reader.onerror = onError;
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            reader.onload = function(event) { nextStep(file, reader.result) };
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            reader.readAsArrayBuffer(file.webkitSlice(pos, pos + length - 6));
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            return;
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          }
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          br.skipMarkData();
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        }
92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      },
93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Step five, parse the exif data.
95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      function parseExif(file, buf) {
96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var br = new exif.BufferReader(buf);
97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var order = br.readScalar(2);
98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (order == exif.ALIGN_LITTLE) {
99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          br.setByteOrder(exif.BufferReader.LITTLE_ENDIAN);
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        } else if (order != exif.ALIGN_BIG) {
101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          return onError('Invalid alignment value: ' + order.toString(16));
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        }
103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var tag = br.readScalar(2);
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (tag != exif.TAG_TIFF)
106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          return onError('Invalid TIFF tag: ' + tag.toString(16));
107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var tags = {};
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var directoryOffset = br.readScalar(4);
110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        while (directoryOffset) {
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          br.seek(directoryOffset);
113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          var entryCount = br.readScalar(2);
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          for (var i = 0; i < entryCount; i++) {
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            var tag = tags[br.readScalar(2)] = {};
116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            tag.format = br.readScalar(2);
117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            tag.componentCount = br.readScalar(4);
118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            tag.value = br.readScalar(4);
119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          };
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          directoryOffset = br.readScalar(4);
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        }
123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        var metadata = { rawTags: tags };
125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (exif.TAG_JPG_THUMB_OFFSET in tags &&
127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            exif.TAG_JPG_THUMB_LENGTH in tags) {
128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          br.seek(tags[exif.TAG_JPG_THUMB_OFFSET].value);
129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          var b64 = br.readBase64(tags[exif.TAG_JPG_THUMB_LENGTH].value);
130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          metadata.thumbnailURL = 'data:image/jpeg;base64,' + b64;
131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        } else {
132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          self.vlog('Image has EXIF data, but no JPG thumbnail.');
133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        }
134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (exif.TAG_EXIF_IMAGE_WIDTH in tags)
136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          metadata.exifImageWidth = tags[exif.TAG_IMAGE_WIDTH];
137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (exif.TAG_EXIF_IMAGE_HEIGHT in tags)
139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          metadata.exifImageHeight = tags[exif.TAG_IMAGE_HEIGHT];
140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        nextStep(metadata);
142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      },
143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Step six, we're done.
145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      callback
146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ];
147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    nextStep();
149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  onMessage: function(event) {
152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var data = event.data;
153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (this.messageHandlers.hasOwnProperty(data.verb)) {
155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      //this.log('dispatching: ' + data.verb + ': ' + data.arguments);
156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.messageHandlers[data.verb].apply(this, data.arguments);
157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.log('Unknown message from client: ' + data.verb, data);
159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
162ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  log: function(var_args) {
163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var ary = Array.apply(null, arguments);
164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    postMessage({verb: 'log', arguments: ary});
165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  vlog: function(var_args) {
168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (this.verbose)
169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.log.apply(this, arguments);
170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.MARK_SOI = 0xffd8;  // Start of image data.
174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.MARK_SOS = 0xffda;  // Start of "stream" (the actual image data).
175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.MARK_EXIF = 0xffe1;  // Start of exif block.
176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.ALIGN_LITTLE = 0x4949;  // Indicates little endian alignment of exif data.
178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.ALIGN_BIG = 0x4d4d;  // Indicates big endian alignment of exif data.
179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.TAG_TIFF = 0x002a;  // First tag in the exif data.
181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.TAG_JPG_THUMB_OFFSET = 0x0201;
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.TAG_JPG_THUMB_LENGTH = 0x0202;
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.TAG_EXIF_IMAGE_WIDTH = 0xa002;
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.TAG_EXIF_IMAGE_HEIGHT = 0xa003;
185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.BufferReader = function(buf) {
187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.buf_ = buf;
188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.ary_ = new Uint8Array(buf);
189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.pos_ = 0;
190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.setByteOrder(exif.BufferReader.BIG_ENDIAN);
191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.BufferReader.LITTLE_ENDIAN = 0;  // Intel, 0x1234 is [0x34, 0x12]
194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.BufferReader.BIG_ENDIAN = 1;  // Motorola, 0x002a is [0x12, 0x34]
195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenexif.BufferReader.prototype = {
197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  setByteOrder: function(order) {
198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.order_ = order;
199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (order == exif.BufferReader.LITTLE_ENDIAN) {
200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.readScalar = this.readLittle;
201ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
202ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.readScalar = this.readBig;
203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
206ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  eof: function() {
207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.pos_ >= this.ary_.length;
208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readScalar: null,  // Either readLittle or readBig, according to byte order.
211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Big endian read.  Most significant bytes come first.
214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readBig: function(width) {
216ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var rv = 0;
217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    switch(width) {
218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 4:
219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv = this.ary_[this.pos_++] << 24;
220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 3:
221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv |= this.ary_[this.pos_++] << 16;
222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 2:
223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv |= this.ary_[this.pos_++] << 8;
224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 1:
225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv |= this.ary_[this.pos_++];
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return rv;
229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Little endian read.  Least significant bytes come first.
233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readLittle: function(width) {
235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var rv = 0;
236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    switch(width) {
237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 4:
238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv = this.ary_[this.pos_ + 3] << 24;
239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 3:
240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv |= this.ary_[this.pos_ + 2] << 16;
241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 2:
242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv |= this.ary_[this.pos_+ 1] << 8;
243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      case 1:
244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        rv |= this.ary_[this.pos_];
245ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.pos_ += width;
248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return rv;
249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readString: function(length) {
252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var chars = [];
253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < length; i++) {
254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chars[i] = String.fromCharCode(this.ary_[this.pos_++]);
255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return chars.join('');
258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base64Alphabet_: ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    'abcdefghijklmnopqrstuvwxyz' +
262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    '0123456789+/').split(''),
263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readBase64: function(length) {
265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var rv = [];
266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var chars = [];
267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var padding = 0;
268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < length; /* incremented inside */) {
270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      var bits = this.ary_[this.pos_ + i++] << 16;
271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (i < length) {
273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        bits |= this.ary_[this.pos_ + i++] << 8;
274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (i < length) {
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          bits |= this.ary_[this.pos_ + i++];
277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        } else {
278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          padding = 1;
279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        }
280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      } else {
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        padding = 2;
282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chars[3] = this.base64Alphabet_[bits & 63];
285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chars[2] = this.base64Alphabet_[(bits >> 6) & 63];
286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chars[1] = this.base64Alphabet_[(bits >> 12) & 63];
287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chars[0] = this.base64Alphabet_[(bits >> 18) & 63];
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      rv.push.apply(rv, chars);
290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.pos_ += i;
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (padding > 0)
295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chars[chars.length - 1] = '=';
296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (padding > 1)
297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chars[chars.length - 2] = '=';
298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return rv.join('');
300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readMark: function() {
303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.readScalar(2);
304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readMarkLength: function() {
307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Length includes the 2 bytes used to store the length.
308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.readScalar(2) - 2;
309ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
310ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  readMarkData: function(opt_arrayConstructor) {
312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var arrayConstructor = opt_arrayConstructor || Uint8Array;
313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var length = this.readMarkLength();
315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var slice = new arrayConstructor(this.buf_, this.pos_, length);
316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.pos_ += length;
317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return slice;
319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  skipMarkData: function() {
322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.skip(this.readMarkLength());
323ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
324ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  seek: function(pos) {
326ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.pos_ = pos;
327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  skip: function(count) {
330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.pos_ += count;
331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  tell: function() {
334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.pos_;
335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
337ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar onmessage = exif.onMessage.bind(exif);
339