comparison WebApplication/jpeg-decoder.js @ 0:02f7a0400a91

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Feb 2015 13:45:35 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:02f7a0400a91
1 /**
2 * SOURCE: https://github.com/notmasteryet/jpgjs/blob/master/jpg.js
3 **/
4
5
6 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
7 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
8 /*
9 Copyright 2011 notmasteryet
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 // - The JPEG specification can be found in the ITU CCITT Recommendation T.81
25 // (www.w3.org/Graphics/JPEG/itu-t81.pdf)
26 // - The JFIF specification can be found in the JPEG File Interchange Format
27 // (www.w3.org/Graphics/JPEG/jfif3.pdf)
28 // - The Adobe Application-Specific JPEG markers in the Supporting the DCT Filters
29 // in PostScript Level 2, Technical Note #5116
30 // (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf)
31
32 var JpegImage = (function jpegImage() {
33 "use strict";
34 var dctZigZag = new Int32Array([
35 0,
36 1, 8,
37 16, 9, 2,
38 3, 10, 17, 24,
39 32, 25, 18, 11, 4,
40 5, 12, 19, 26, 33, 40,
41 48, 41, 34, 27, 20, 13, 6,
42 7, 14, 21, 28, 35, 42, 49, 56,
43 57, 50, 43, 36, 29, 22, 15,
44 23, 30, 37, 44, 51, 58,
45 59, 52, 45, 38, 31,
46 39, 46, 53, 60,
47 61, 54, 47,
48 55, 62,
49 63
50 ]);
51
52 var dctCos1 = 4017 // cos(pi/16)
53 var dctSin1 = 799 // sin(pi/16)
54 var dctCos3 = 3406 // cos(3*pi/16)
55 var dctSin3 = 2276 // sin(3*pi/16)
56 var dctCos6 = 1567 // cos(6*pi/16)
57 var dctSin6 = 3784 // sin(6*pi/16)
58 var dctSqrt2 = 5793 // sqrt(2)
59 var dctSqrt1d2 = 2896 // sqrt(2) / 2
60
61 function constructor() {
62 }
63
64 function buildHuffmanTable(codeLengths, values) {
65 var k = 0, code = [], i, j, length = 16;
66 while (length > 0 && !codeLengths[length - 1])
67 length--;
68 code.push({children: [], index: 0});
69 var p = code[0], q;
70 for (i = 0; i < length; i++) {
71 for (j = 0; j < codeLengths[i]; j++) {
72 p = code.pop();
73 p.children[p.index] = values[k];
74 while (p.index > 0) {
75 p = code.pop();
76 }
77 p.index++;
78 code.push(p);
79 while (code.length <= i) {
80 code.push(q = {children: [], index: 0});
81 p.children[p.index] = q.children;
82 p = q;
83 }
84 k++;
85 }
86 if (i + 1 < length) {
87 // p here points to last code
88 code.push(q = {children: [], index: 0});
89 p.children[p.index] = q.children;
90 p = q;
91 }
92 }
93 return code[0].children;
94 }
95
96 function getBlockBufferOffset(component, row, col) {
97 return 64 * ((component.blocksPerLine + 1) * row + col);
98 }
99
100 function decodeScan(data, offset,
101 frame, components, resetInterval,
102 spectralStart, spectralEnd,
103 successivePrev, successive) {
104 var precision = frame.precision;
105 var samplesPerLine = frame.samplesPerLine;
106 var scanLines = frame.scanLines;
107 var mcusPerLine = frame.mcusPerLine;
108 var progressive = frame.progressive;
109 var maxH = frame.maxH, maxV = frame.maxV;
110
111 var startOffset = offset, bitsData = 0, bitsCount = 0;
112
113 function readBit() {
114 if (bitsCount > 0) {
115 bitsCount--;
116 return (bitsData >> bitsCount) & 1;
117 }
118 bitsData = data[offset++];
119 if (bitsData == 0xFF) {
120 var nextByte = data[offset++];
121 if (nextByte) {
122 throw "unexpected marker: " + ((bitsData << 8) | nextByte).toString(16);
123 }
124 // unstuff 0
125 }
126 bitsCount = 7;
127 return bitsData >>> 7;
128 }
129
130 function decodeHuffman(tree) {
131 var node = tree;
132 var bit;
133 while ((bit = readBit()) !== null) {
134 node = node[bit];
135 if (typeof node === 'number')
136 return node;
137 if (typeof node !== 'object')
138 throw "invalid huffman sequence";
139 }
140 return null;
141 }
142
143 function receive(length) {
144 var n = 0;
145 while (length > 0) {
146 var bit = readBit();
147 if (bit === null) return;
148 n = (n << 1) | bit;
149 length--;
150 }
151 return n;
152 }
153
154 function receiveAndExtend(length) {
155 var n = receive(length);
156 if (n >= 1 << (length - 1))
157 return n;
158 return n + (-1 << length) + 1;
159 }
160
161 function decodeBaseline(component, offset) {
162 var t = decodeHuffman(component.huffmanTableDC);
163 var diff = t === 0 ? 0 : receiveAndExtend(t);
164 component.blockData[offset] = (component.pred += diff);
165 var k = 1;
166 while (k < 64) {
167 var rs = decodeHuffman(component.huffmanTableAC);
168 var s = rs & 15, r = rs >> 4;
169 if (s === 0) {
170 if (r < 15)
171 break;
172 k += 16;
173 continue;
174 }
175 k += r;
176 var z = dctZigZag[k];
177 component.blockData[offset + z] = receiveAndExtend(s);
178 k++;
179 }
180 }
181
182 function decodeDCFirst(component, offset) {
183 var t = decodeHuffman(component.huffmanTableDC);
184 var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive);
185 component.blockData[offset] = (component.pred += diff);
186 }
187
188 function decodeDCSuccessive(component, offset) {
189 component.blockData[offset] |= readBit() << successive;
190 }
191
192 var eobrun = 0;
193 function decodeACFirst(component, offset) {
194 if (eobrun > 0) {
195 eobrun--;
196 return;
197 }
198 var k = spectralStart, e = spectralEnd;
199 while (k <= e) {
200 var rs = decodeHuffman(component.huffmanTableAC);
201 var s = rs & 15, r = rs >> 4;
202 if (s === 0) {
203 if (r < 15) {
204 eobrun = receive(r) + (1 << r) - 1;
205 break;
206 }
207 k += 16;
208 continue;
209 }
210 k += r;
211 var z = dctZigZag[k];
212 component.blockData[offset + z] = receiveAndExtend(s) * (1 << successive);
213 k++;
214 }
215 }
216
217 var successiveACState = 0, successiveACNextValue;
218 function decodeACSuccessive(component, offset) {
219 var k = spectralStart, e = spectralEnd, r = 0;
220 while (k <= e) {
221 var z = dctZigZag[k];
222 switch (successiveACState) {
223 case 0: // initial state
224 var rs = decodeHuffman(component.huffmanTableAC);
225 var s = rs & 15, r = rs >> 4;
226 if (s === 0) {
227 if (r < 15) {
228 eobrun = receive(r) + (1 << r);
229 successiveACState = 4;
230 } else {
231 r = 16;
232 successiveACState = 1;
233 }
234 } else {
235 if (s !== 1)
236 throw "invalid ACn encoding";
237 successiveACNextValue = receiveAndExtend(s);
238 successiveACState = r ? 2 : 3;
239 }
240 continue;
241 case 1: // skipping r zero items
242 case 2:
243 if (component.blockData[offset + z]) {
244 component.blockData[offset + z] += (readBit() << successive);
245 } else {
246 r--;
247 if (r === 0)
248 successiveACState = successiveACState == 2 ? 3 : 0;
249 }
250 break;
251 case 3: // set value for a zero item
252 if (component.blockData[offset + z]) {
253 component.blockData[offset + z] += (readBit() << successive);
254 } else {
255 component.blockData[offset + z] = successiveACNextValue << successive;
256 successiveACState = 0;
257 }
258 break;
259 case 4: // eob
260 if (component.blockData[offset + z]) {
261 component.blockData[offset + z] += (readBit() << successive);
262 }
263 break;
264 }
265 k++;
266 }
267 if (successiveACState === 4) {
268 eobrun--;
269 if (eobrun === 0)
270 successiveACState = 0;
271 }
272 }
273
274 function decodeMcu(component, decode, mcu, row, col) {
275 var mcuRow = (mcu / mcusPerLine) | 0;
276 var mcuCol = mcu % mcusPerLine;
277 var blockRow = mcuRow * component.v + row;
278 var blockCol = mcuCol * component.h + col;
279 var offset = getBlockBufferOffset(component, blockRow, blockCol);
280 decode(component, offset);
281 }
282
283 function decodeBlock(component, decode, mcu) {
284 var blockRow = (mcu / component.blocksPerLine) | 0;
285 var blockCol = mcu % component.blocksPerLine;
286 var offset = getBlockBufferOffset(component, blockRow, blockCol);
287 decode(component, offset);
288 }
289
290 var componentsLength = components.length;
291 var component, i, j, k, n;
292 var decodeFn;
293 if (progressive) {
294 if (spectralStart === 0)
295 decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
296 else
297 decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
298 } else {
299 decodeFn = decodeBaseline;
300 }
301
302 var mcu = 0, marker;
303 var mcuExpected;
304 if (componentsLength == 1) {
305 mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
306 } else {
307 mcuExpected = mcusPerLine * frame.mcusPerColumn;
308 }
309 if (!resetInterval) {
310 resetInterval = mcuExpected;
311 }
312
313 var h, v;
314 while (mcu < mcuExpected) {
315 // reset interval stuff
316 for (i = 0; i < componentsLength; i++) {
317 components[i].pred = 0;
318 }
319 eobrun = 0;
320
321 if (componentsLength == 1) {
322 component = components[0];
323 for (n = 0; n < resetInterval; n++) {
324 decodeBlock(component, decodeFn, mcu);
325 mcu++;
326 }
327 } else {
328 for (n = 0; n < resetInterval; n++) {
329 for (i = 0; i < componentsLength; i++) {
330 component = components[i];
331 h = component.h;
332 v = component.v;
333 for (j = 0; j < v; j++) {
334 for (k = 0; k < h; k++) {
335 decodeMcu(component, decodeFn, mcu, j, k);
336 }
337 }
338 }
339 mcu++;
340 }
341 }
342
343 // find marker
344 bitsCount = 0;
345 marker = (data[offset] << 8) | data[offset + 1];
346 if (marker <= 0xFF00) {
347 throw "marker was not found";
348 }
349
350 if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
351 offset += 2;
352 } else {
353 break;
354 }
355 }
356
357 return offset - startOffset;
358 }
359
360 // A port of poppler's IDCT method which in turn is taken from:
361 // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
362 // "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
363 // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
364 // 988-991.
365 function quantizeAndInverse(component, blockBufferOffset, p) {
366 var qt = component.quantizationTable;
367 var v0, v1, v2, v3, v4, v5, v6, v7, t;
368 var i;
369
370 // dequant
371 for (i = 0; i < 64; i++) {
372 p[i] = component.blockData[blockBufferOffset + i] * qt[i];
373 }
374
375 // inverse DCT on rows
376 for (i = 0; i < 8; ++i) {
377 var row = 8 * i;
378
379 // check for all-zero AC coefficients
380 if (p[1 + row] == 0 && p[2 + row] == 0 && p[3 + row] == 0 &&
381 p[4 + row] == 0 && p[5 + row] == 0 && p[6 + row] == 0 &&
382 p[7 + row] == 0) {
383 t = (dctSqrt2 * p[0 + row] + 512) >> 10;
384 p[0 + row] = t;
385 p[1 + row] = t;
386 p[2 + row] = t;
387 p[3 + row] = t;
388 p[4 + row] = t;
389 p[5 + row] = t;
390 p[6 + row] = t;
391 p[7 + row] = t;
392 continue;
393 }
394
395 // stage 4
396 v0 = (dctSqrt2 * p[0 + row] + 128) >> 8;
397 v1 = (dctSqrt2 * p[4 + row] + 128) >> 8;
398 v2 = p[2 + row];
399 v3 = p[6 + row];
400 v4 = (dctSqrt1d2 * (p[1 + row] - p[7 + row]) + 128) >> 8;
401 v7 = (dctSqrt1d2 * (p[1 + row] + p[7 + row]) + 128) >> 8;
402 v5 = p[3 + row] << 4;
403 v6 = p[5 + row] << 4;
404
405 // stage 3
406 t = (v0 - v1+ 1) >> 1;
407 v0 = (v0 + v1 + 1) >> 1;
408 v1 = t;
409 t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
410 v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
411 v3 = t;
412 t = (v4 - v6 + 1) >> 1;
413 v4 = (v4 + v6 + 1) >> 1;
414 v6 = t;
415 t = (v7 + v5 + 1) >> 1;
416 v5 = (v7 - v5 + 1) >> 1;
417 v7 = t;
418
419 // stage 2
420 t = (v0 - v3 + 1) >> 1;
421 v0 = (v0 + v3 + 1) >> 1;
422 v3 = t;
423 t = (v1 - v2 + 1) >> 1;
424 v1 = (v1 + v2 + 1) >> 1;
425 v2 = t;
426 t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
427 v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
428 v7 = t;
429 t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
430 v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
431 v6 = t;
432
433 // stage 1
434 p[0 + row] = v0 + v7;
435 p[7 + row] = v0 - v7;
436 p[1 + row] = v1 + v6;
437 p[6 + row] = v1 - v6;
438 p[2 + row] = v2 + v5;
439 p[5 + row] = v2 - v5;
440 p[3 + row] = v3 + v4;
441 p[4 + row] = v3 - v4;
442 }
443
444 // inverse DCT on columns
445 for (i = 0; i < 8; ++i) {
446 var col = i;
447
448 // check for all-zero AC coefficients
449 if (p[1*8 + col] == 0 && p[2*8 + col] == 0 && p[3*8 + col] == 0 &&
450 p[4*8 + col] == 0 && p[5*8 + col] == 0 && p[6*8 + col] == 0 &&
451 p[7*8 + col] == 0) {
452 t = (dctSqrt2 * p[i+0] + 8192) >> 14;
453 p[0*8 + col] = t;
454 p[1*8 + col] = t;
455 p[2*8 + col] = t;
456 p[3*8 + col] = t;
457 p[4*8 + col] = t;
458 p[5*8 + col] = t;
459 p[6*8 + col] = t;
460 p[7*8 + col] = t;
461 continue;
462 }
463
464 // stage 4
465 v0 = (dctSqrt2 * p[0*8 + col] + 2048) >> 12;
466 v1 = (dctSqrt2 * p[4*8 + col] + 2048) >> 12;
467 v2 = p[2*8 + col];
468 v3 = p[6*8 + col];
469 v4 = (dctSqrt1d2 * (p[1*8 + col] - p[7*8 + col]) + 2048) >> 12;
470 v7 = (dctSqrt1d2 * (p[1*8 + col] + p[7*8 + col]) + 2048) >> 12;
471 v5 = p[3*8 + col];
472 v6 = p[5*8 + col];
473
474 // stage 3
475 t = (v0 - v1 + 1) >> 1;
476 v0 = (v0 + v1 + 1) >> 1;
477 v1 = t;
478 t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
479 v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
480 v3 = t;
481 t = (v4 - v6 + 1) >> 1;
482 v4 = (v4 + v6 + 1) >> 1;
483 v6 = t;
484 t = (v7 + v5 + 1) >> 1;
485 v5 = (v7 - v5 + 1) >> 1;
486 v7 = t;
487
488 // stage 2
489 t = (v0 - v3 + 1) >> 1;
490 v0 = (v0 + v3 + 1) >> 1;
491 v3 = t;
492 t = (v1 - v2 + 1) >> 1;
493 v1 = (v1 + v2 + 1) >> 1;
494 v2 = t;
495 t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
496 v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
497 v7 = t;
498 t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
499 v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
500 v6 = t;
501
502 // stage 1
503 p[0*8 + col] = v0 + v7;
504 p[7*8 + col] = v0 - v7;
505 p[1*8 + col] = v1 + v6;
506 p[6*8 + col] = v1 - v6;
507 p[2*8 + col] = v2 + v5;
508 p[5*8 + col] = v2 - v5;
509 p[3*8 + col] = v3 + v4;
510 p[4*8 + col] = v3 - v4;
511 }
512
513 // convert to 8-bit integers
514 for (i = 0; i < 64; ++i) {
515 var index = blockBufferOffset + i;
516 var q = p[i];
517 q = (q <= -2056) ? 0 : (q >= 2024) ? 255 : (q + 2056) >> 4;
518 component.blockData[index] = q;
519 }
520 }
521
522 function buildComponentData(frame, component) {
523 var lines = [];
524 var blocksPerLine = component.blocksPerLine;
525 var blocksPerColumn = component.blocksPerColumn;
526 var samplesPerLine = blocksPerLine << 3;
527 var computationBuffer = new Int32Array(64);
528
529 var i, j, ll = 0;
530 for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
531 for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
532 var offset = getBlockBufferOffset(component, blockRow, blockCol)
533 quantizeAndInverse(component, offset, computationBuffer);
534 }
535 }
536 return component.blockData;
537 }
538
539 function clampToUint8(a) {
540 return a <= 0 ? 0 : a >= 255 ? 255 : a | 0;
541 }
542
543 constructor.prototype = {
544 load: function load(path) {
545 var handleData = (function(data) {
546 this.parse(data);
547 if (this.onload)
548 this.onload();
549 }).bind(this);
550
551 if (path.indexOf("data:") > -1) {
552 var offset = path.indexOf("base64,")+7;
553 var data = atob(path.substring(offset));
554 var arr = new Uint8Array(data.length);
555 for (var i = data.length - 1; i >= 0; i--) {
556 arr[i] = data.charCodeAt(i);
557 }
558 handleData(data);
559 } else {
560 var xhr = new XMLHttpRequest();
561 xhr.open("GET", path, true);
562 xhr.responseType = "arraybuffer";
563 xhr.onload = (function() {
564 // TODO catch parse error
565 var data = new Uint8Array(xhr.response);
566 handleData(data);
567 }).bind(this);
568 xhr.send(null);
569 }
570 },
571
572 parse: function parse(data) {
573
574 function readUint16() {
575 var value = (data[offset] << 8) | data[offset + 1];
576 offset += 2;
577 return value;
578 }
579
580 function readDataBlock() {
581 var length = readUint16();
582 var array = data.subarray(offset, offset + length - 2);
583 offset += array.length;
584 return array;
585 }
586
587 function prepareComponents(frame) {
588 var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
589 var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
590 for (var i = 0; i < frame.components.length; i++) {
591 component = frame.components[i];
592 var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
593 var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
594 var blocksPerLineForMcu = mcusPerLine * component.h;
595 var blocksPerColumnForMcu = mcusPerColumn * component.v;
596
597 var blocksBufferSize = 64 * blocksPerColumnForMcu
598 * (blocksPerLineForMcu + 1);
599 component.blockData = new Int16Array(blocksBufferSize);
600 component.blocksPerLine = blocksPerLine;
601 component.blocksPerColumn = blocksPerColumn;
602 }
603 frame.mcusPerLine = mcusPerLine;
604 frame.mcusPerColumn = mcusPerColumn;
605 }
606
607 var offset = 0, length = data.length;
608 var jfif = null;
609 var adobe = null;
610 var pixels = null;
611 var frame, resetInterval;
612 var quantizationTables = [];
613 var huffmanTablesAC = [], huffmanTablesDC = [];
614 var fileMarker = readUint16();
615 if (fileMarker != 0xFFD8) { // SOI (Start of Image)
616 throw "SOI not found";
617 }
618
619 fileMarker = readUint16();
620 while (fileMarker != 0xFFD9) { // EOI (End of image)
621 var i, j, l;
622 switch(fileMarker) {
623 case 0xFFE0: // APP0 (Application Specific)
624 case 0xFFE1: // APP1
625 case 0xFFE2: // APP2
626 case 0xFFE3: // APP3
627 case 0xFFE4: // APP4
628 case 0xFFE5: // APP5
629 case 0xFFE6: // APP6
630 case 0xFFE7: // APP7
631 case 0xFFE8: // APP8
632 case 0xFFE9: // APP9
633 case 0xFFEA: // APP10
634 case 0xFFEB: // APP11
635 case 0xFFEC: // APP12
636 case 0xFFED: // APP13
637 case 0xFFEE: // APP14
638 case 0xFFEF: // APP15
639 case 0xFFFE: // COM (Comment)
640 var appData = readDataBlock();
641
642 if (fileMarker === 0xFFE0) {
643 if (appData[0] === 0x4A && appData[1] === 0x46 && appData[2] === 0x49 &&
644 appData[3] === 0x46 && appData[4] === 0) { // 'JFIF\x00'
645 jfif = {
646 version: { major: appData[5], minor: appData[6] },
647 densityUnits: appData[7],
648 xDensity: (appData[8] << 8) | appData[9],
649 yDensity: (appData[10] << 8) | appData[11],
650 thumbWidth: appData[12],
651 thumbHeight: appData[13],
652 thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
653 };
654 }
655 }
656 // TODO APP1 - Exif
657 if (fileMarker === 0xFFEE) {
658 if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6F &&
659 appData[3] === 0x62 && appData[4] === 0x65 && appData[5] === 0) { // 'Adobe\x00'
660 adobe = {
661 version: appData[6],
662 flags0: (appData[7] << 8) | appData[8],
663 flags1: (appData[9] << 8) | appData[10],
664 transformCode: appData[11]
665 };
666 }
667 }
668 break;
669
670 case 0xFFDB: // DQT (Define Quantization Tables)
671 var quantizationTablesLength = readUint16();
672 var quantizationTablesEnd = quantizationTablesLength + offset - 2;
673 while (offset < quantizationTablesEnd) {
674 var quantizationTableSpec = data[offset++];
675 var tableData = new Int32Array(64);
676 if ((quantizationTableSpec >> 4) === 0) { // 8 bit values
677 for (j = 0; j < 64; j++) {
678 var z = dctZigZag[j];
679 tableData[z] = data[offset++];
680 }
681 } else if ((quantizationTableSpec >> 4) === 1) { //16 bit
682 for (j = 0; j < 64; j++) {
683 var z = dctZigZag[j];
684 tableData[z] = readUint16();
685 }
686 } else
687 throw "DQT: invalid table spec";
688 quantizationTables[quantizationTableSpec & 15] = tableData;
689 }
690 break;
691
692 case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT)
693 case 0xFFC1: // SOF1 (Start of Frame, Extended DCT)
694 case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT)
695 if (frame) {
696 throw "Only single frame JPEGs supported";
697 }
698 readUint16(); // skip data length
699 frame = {};
700 frame.extended = (fileMarker === 0xFFC1);
701 frame.progressive = (fileMarker === 0xFFC2);
702 frame.precision = data[offset++];
703 frame.scanLines = readUint16();
704 frame.samplesPerLine = readUint16();
705 frame.components = [];
706 frame.componentIds = {};
707 var componentsCount = data[offset++], componentId;
708 var maxH = 0, maxV = 0;
709 for (i = 0; i < componentsCount; i++) {
710 componentId = data[offset];
711 var h = data[offset + 1] >> 4;
712 var v = data[offset + 1] & 15;
713 if (maxH < h) maxH = h;
714 if (maxV < v) maxV = v;
715 var qId = data[offset + 2];
716 var l = frame.components.push({
717 h: h,
718 v: v,
719 quantizationTable: quantizationTables[qId]
720 });
721 frame.componentIds[componentId] = l - 1;
722 offset += 3;
723 }
724 frame.maxH = maxH;
725 frame.maxV = maxV;
726 prepareComponents(frame);
727 break;
728
729 case 0xFFC4: // DHT (Define Huffman Tables)
730 var huffmanLength = readUint16();
731 for (i = 2; i < huffmanLength;) {
732 var huffmanTableSpec = data[offset++];
733 var codeLengths = new Uint8Array(16);
734 var codeLengthSum = 0;
735 for (j = 0; j < 16; j++, offset++)
736 codeLengthSum += (codeLengths[j] = data[offset]);
737 var huffmanValues = new Uint8Array(codeLengthSum);
738 for (j = 0; j < codeLengthSum; j++, offset++)
739 huffmanValues[j] = data[offset];
740 i += 17 + codeLengthSum;
741
742 ((huffmanTableSpec >> 4) === 0 ?
743 huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] =
744 buildHuffmanTable(codeLengths, huffmanValues);
745 }
746 break;
747
748 case 0xFFDD: // DRI (Define Restart Interval)
749 readUint16(); // skip data length
750 resetInterval = readUint16();
751 break;
752
753 case 0xFFDA: // SOS (Start of Scan)
754 var scanLength = readUint16();
755 var selectorsCount = data[offset++];
756 var components = [], component;
757 for (i = 0; i < selectorsCount; i++) {
758 var componentIndex = frame.componentIds[data[offset++]];
759 component = frame.components[componentIndex];
760 var tableSpec = data[offset++];
761 component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
762 component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
763 components.push(component);
764 }
765 var spectralStart = data[offset++];
766 var spectralEnd = data[offset++];
767 var successiveApproximation = data[offset++];
768 var processed = decodeScan(data, offset,
769 frame, components, resetInterval,
770 spectralStart, spectralEnd,
771 successiveApproximation >> 4, successiveApproximation & 15);
772 offset += processed;
773 break;
774 default:
775 if (data[offset - 3] == 0xFF &&
776 data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
777 // could be incorrect encoding -- last 0xFF byte of the previous
778 // block was eaten by the encoder
779 offset -= 3;
780 break;
781 }
782 throw "unknown JPEG marker " + fileMarker.toString(16);
783 }
784 fileMarker = readUint16();
785 }
786
787 this.width = frame.samplesPerLine;
788 this.height = frame.scanLines;
789 this.jfif = jfif;
790 this.adobe = adobe;
791 this.components = [];
792 for (var i = 0; i < frame.components.length; i++) {
793 var component = frame.components[i];
794 this.components.push({
795 output: buildComponentData(frame, component),
796 scaleX: component.h / frame.maxH,
797 scaleY: component.v / frame.maxV,
798 blocksPerLine: component.blocksPerLine,
799 blocksPerColumn: component.blocksPerColumn
800 });
801 }
802 },
803
804 getData: function getData(width, height) {
805 var scaleX = this.width / width, scaleY = this.height / height;
806
807 var component, componentScaleX, componentScaleY;
808 var x, y, i;
809 var offset = 0;
810 var Y, Cb, Cr, K, C, M, Ye, R, G, B;
811 var colorTransform;
812 var numComponents = this.components.length;
813 var dataLength = width * height * numComponents;
814 var data = new Uint8Array(dataLength);
815 var componentLine;
816
817 // lineData is reused for all components. Assume first component is
818 // the biggest
819 var lineData = new Uint8Array((this.components[0].blocksPerLine << 3) *
820 this.components[0].blocksPerColumn * 8);
821
822 // First construct image data ...
823 for (i = 0; i < numComponents; i++) {
824 component = this.components[i];
825 var blocksPerLine = component.blocksPerLine;
826 var blocksPerColumn = component.blocksPerColumn;
827 var samplesPerLine = blocksPerLine << 3;
828
829 var j, k, ll = 0;
830 var lineOffset = 0;
831 for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
832 var scanLine = blockRow << 3;
833 for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
834 var bufferOffset = getBlockBufferOffset(component, blockRow, blockCol);
835 var offset = 0, sample = blockCol << 3;
836 for (j = 0; j < 8; j++) {
837 var lineOffset = (scanLine + j) * samplesPerLine;
838 for (k = 0; k < 8; k++) {
839 lineData[lineOffset + sample + k] =
840 component.output[bufferOffset + offset++];
841 }
842 }
843 }
844 }
845
846 componentScaleX = component.scaleX * scaleX;
847 componentScaleY = component.scaleY * scaleY;
848 offset = i;
849
850 var cx, cy;
851 var index;
852 for (y = 0; y < height; y++) {
853 for (x = 0; x < width; x++) {
854 cy = 0 | (y * componentScaleY);
855 cx = 0 | (x * componentScaleX);
856 index = cy * samplesPerLine + cx;
857 data[offset] = lineData[index];
858 offset += numComponents;
859 }
860 }
861 }
862
863 // ... then transform colors, if necessary
864 switch (numComponents) {
865 case 1: case 2: break;
866 // no color conversion for one or two compoenents
867
868 case 3:
869 // The default transform for three components is true
870 colorTransform = true;
871 // The adobe transform marker overrides any previous setting
872 if (this.adobe && this.adobe.transformCode)
873 colorTransform = true;
874 else if (typeof this.colorTransform !== 'undefined')
875 colorTransform = !!this.colorTransform;
876
877 if (colorTransform) {
878 for (i = 0; i < dataLength; i += numComponents) {
879 Y = data[i ];
880 Cb = data[i + 1];
881 Cr = data[i + 2];
882
883 R = clampToUint8(Y - 179.456 + 1.402 * Cr);
884 G = clampToUint8(Y + 135.459 - 0.344 * Cb - 0.714 * Cr);
885 B = clampToUint8(Y - 226.816 + 1.772 * Cb);
886
887 data[i ] = R;
888 data[i + 1] = G;
889 data[i + 2] = B;
890 }
891 }
892 break;
893 case 4:
894 if (!this.adobe)
895 throw 'Unsupported color mode (4 components)';
896 // The default transform for four components is false
897 colorTransform = false;
898 // The adobe transform marker overrides any previous setting
899 if (this.adobe && this.adobe.transformCode)
900 colorTransform = true;
901 else if (typeof this.colorTransform !== 'undefined')
902 colorTransform = !!this.colorTransform;
903
904 if (colorTransform) {
905 for (i = 0; i < dataLength; i += numComponents) {
906 Y = data[i];
907 Cb = data[i + 1];
908 Cr = data[i + 2];
909
910 C = clampToUint8(434.456 - Y - 1.402 * Cr);
911 M = clampToUint8(119.541 - Y + 0.344 * Cb + 0.714 * Cr);
912 Y = clampToUint8(481.816 - Y - 1.772 * Cb);
913
914 data[i ] = C;
915 data[i + 1] = M;
916 data[i + 2] = Y;
917 // K is unchanged
918 }
919 }
920 break;
921 default:
922 throw 'Unsupported color mode';
923 }
924 return data;
925 },
926 copyToImageData: function copyToImageData(imageData) {
927 var width = imageData.width, height = imageData.height;
928 var imageDataBytes = width * height * 4;
929 var imageDataArray = imageData.data;
930 var data = this.getData(width, height);
931 var i = 0, j = 0, k0, k1;
932 var Y, K, C, M, R, G, B;
933 switch (this.components.length) {
934 case 1:
935 while (j < imageDataBytes) {
936 Y = data[i++];
937
938 imageDataArray[j++] = Y;
939 imageDataArray[j++] = Y;
940 imageDataArray[j++] = Y;
941 imageDataArray[j++] = 255;
942 }
943 break;
944 case 3:
945 while (j < imageDataBytes) {
946 R = data[i++];
947 G = data[i++];
948 B = data[i++];
949
950 imageDataArray[j++] = R;
951 imageDataArray[j++] = G;
952 imageDataArray[j++] = B;
953 imageDataArray[j++] = 255;
954 }
955 break;
956 case 4:
957 while (j < imageDataBytes) {
958 C = data[i++];
959 M = data[i++];
960 Y = data[i++];
961 K = data[i++];
962
963 k0 = 255 - K;
964 k1 = k0 / 255;
965
966
967 R = clampToUint8(k0 - C * k1);
968 G = clampToUint8(k0 - M * k1);
969 B = clampToUint8(k0 - Y * k1);
970
971 imageDataArray[j++] = R;
972 imageDataArray[j++] = G;
973 imageDataArray[j++] = B;
974 imageDataArray[j++] = 255;
975 }
976 break;
977 default:
978 throw 'Unsupported color mode';
979 }
980 }
981 };
982
983 return constructor;
984 })();