=a.length)throw"unexpected EOF";if(g=a[f++],h=a[f++],255==g){if(224==h&&(this.A=e()),(h>224&&h<240||254==h)&&this.B.push({app:h,data:e()}),219==h&&this.C.push(e()),h>=192&&h<=194){if(this.j)throw"Only single frame JPEGs supported";if(d(),this.j={E:193===h,r:194===h,F:a[f++],G:d(),H:d(),i:[],I:{},J:1,K:1},this.j.G*this.j.H>this.a)throw"Image is too big.";var o,p=a[f++],q=0,r=0;for(i=0;i>4,t=15&a[f+1];q>4===0?n:m)[15&D]=b(E,G)}}if(221==h&&(k=d()),218==h){d();var H=a[f++],I=[];for(i=0;i>4],l.q=m[15&K],I.push(l)}var L=a[f++],M=a[f++],N=a[f++],O=c(a,f,this.j,I,k,L,M,N>>4,15&N);f+=O}if(217==h)break}else{for(255==a[f-3]&&a[f-2]>=192&&a[f-2]<=254&&(f-=3);255!=a[f]&&fB&&(b=new Uint8Array(2*y.length),b.set(y),y=b,B=b.length-128)}function b(b){a(b>>8&255),a(255&b)}function q(a){var b;C+a.length>B&&(b=new Uint8Array(2*y.length+a.length),b.set(y),y=b,B=b.length-128),y.set(a,C),C+=a.length}function r(c){b(65504),c.A?(b(c.A.length+2),q(c.A)):(b(16),a(74),a(70),a(73),a(70),a(0),a(1),a(1),a(0),b(1),b(1),a(0),a(0))}function s(a){for(var c=0;c>6]-e;if(e=a.o[b>>6],0===k)for(i=f[0][1],z<<=i,z+=f[0][0],A+=i;A>7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8,z&=(1<7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8,z&=(1<0&&0===a.m[b+l];l--);if(0===l){for(i=g[0][1],z<<=i,z+=g[0][0],A+=i;A>7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8,z&=(1<=16){m=p>>4;for(var q=1;q<=m;++q)for(i=g[240][1],z<<=i,z+=g[240][0],A+=i;A>7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8,z&=(1<7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8,z&=(1<7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8,z&=(1<7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8,z&=(1<B&&(j=new Uint8Array(2*y.length),j.set(y),y=j,B=j.length-128),e}var y,z,A,B,C,D;y=new Uint8Array(65536),B=65408,C=0,z=0,A=0,b(65496),r(this),t(this),s(this),u(this),v(this),w(this),z=0,A=0;var E,F,G,H,I,J,K,L,M,N=[];for(K=0;K7;)D=255&z>>>A-8,y[C++]=D,255==D&&C++,A-=8;return A>0&&(z<<=8-A,z+=(1<<8-A)-1,y[C++]=255&z),b(65497),this.D&&q(this.D),y.slice(0,C)},f5stego.prototype.clearTail=function(){if(!this.D)return null;var a=this.D;return this.D=null,a},f5stego.prototype.N=function(a){this.D=a},f5stego.prototype.getTail=function(){return this.D},f5stego.prototype.clearAPPs=function(){var a=this.B;return this.B=[],a},f5stego.prototype.getAPPn=function(a,b){var c,d,e=new Uint8Array(0),f=[];if(a&=255,a<16&&(a+=224),224===a)return this.A;for(c=0;c= 8) {
189 | out[outPos++] = (extrByte & 0xFF) ^ gamma[gammaI++];
190 | bitsAvail -= 8;
191 | extrByte = extrByte >> 8;
192 | }
193 | }
194 | }
195 | }
196 |
197 | while (bitsAvail > 0) {
198 | out[outPos++] = (extrByte & 0xFF) ^ gamma[gammaI++];
199 | bitsAvail -= 8;
200 | extrByte = extrByte >> 8;
201 | }
202 | var s = 2,
203 | l = out[0];
204 |
205 | if (out[1] & 128) {
206 | s++;
207 | l += ((out[1] & 127) << 8) + (out[2] << 15);
208 | } else {
209 | l += out[1] << 8;
210 | }
211 |
212 | return out.subarray(s, s + l);
213 | }
214 |
215 | function _buildHuffmanTable(nrcodes, values) {
216 | var codevalue = 0,
217 | pos_in_table = 0,
218 | HT = new Uint16Array(65536);
219 | for (var k = 0; k < 16; k++) {
220 | for (var j = 0; j < nrcodes[k]; j++) {
221 | for (var i = codevalue << (15 - k), cntTo = ((codevalue + 1) << (15 - k)); i < cntTo; i++) {
222 | HT[i] = values[pos_in_table] + ((k + 1) << 8);
223 | }
224 | pos_in_table++;
225 | codevalue++;
226 | }
227 | codevalue *= 2;
228 | }
229 | return HT;
230 | }
231 |
232 | function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive) {
233 |
234 | var startOffset = offset,
235 | bitsData = 0,
236 | bitsCount = 0,
237 | eobrun = 0,
238 | p1 = 1 << successive, /* 1 in the bit position being coded */
239 | m1 = -1 << successive; /* -1 in the bit position being coded */
240 |
241 | function decodeHuffman(t) {
242 | while (bitsCount < 16) {
243 | bitsData = (bitsData << 8) + (data[offset] | 0);
244 | bitsCount += 8;
245 | if (data[offset] == 0xFF) offset++;
246 | offset++;
247 | }
248 | t = t[(bitsData >>> (bitsCount - 16)) & 0xFFFF];
249 | if (!t) throw "invalid huffman sequence";
250 | bitsCount -= t >>> 8;
251 | return t & 255;
252 | }
253 |
254 | function receiveAndExtend(r) {
255 | while (bitsCount < r) {
256 | bitsData = (bitsData << 8) + data[offset++];
257 | if ((bitsData & 0xff) == 0xFF) offset++;
258 | bitsCount += 8;
259 | }
260 | var n = (bitsData >> (bitsCount - r)) & ((1 << r) - 1);
261 | bitsCount -= r;
262 | if (n < 1 << (r - 1)) n += (-1 << r) + 1;
263 | return n;
264 | }
265 |
266 | function receive(t) {
267 | while (bitsCount < t) {
268 | bitsData = (bitsData << 8) + data[offset++];
269 | if ((bitsData & 0xff) == 0xFF) offset++;
270 | bitsCount += 8;
271 | }
272 | var n = (bitsData >>> (bitsCount - t)) & ((1 << t) - 1);
273 | bitsCount -= t;
274 | return n;
275 | }
276 |
277 | function getBit() {
278 | if (!bitsCount) {
279 | bitsData = data[offset++];
280 | if (bitsData == 0xFF) offset++;
281 | bitsCount = 8;
282 | }
283 | return (bitsData >>> --bitsCount) & 1;
284 | }
285 |
286 | function decodeBaseline(component, pos) {
287 | var t = decodeHuffman(component.huffmanTableDC);
288 | var diff = 0;
289 | if (t !== 0) {
290 | diff = receiveAndExtend(t);
291 | }
292 | component.pred += diff;
293 |
294 | var k = 1,
295 | s, r;
296 | while (k < 64) {
297 | s = decodeHuffman(component.huffmanTableAC);
298 |
299 | r = s >> 4;
300 | s &= 15;
301 |
302 | if (s === 0) {
303 | if (r < 15) {
304 | break;
305 | }
306 | k += 16;
307 | continue;
308 | }
309 | k += r;
310 | component.blocks[pos + k] = receiveAndExtend(s);
311 | k++;
312 | }
313 | }
314 |
315 | function decodeDCFirst(component) {
316 | var diff = 0,
317 | t = decodeHuffman(component.huffmanTableDC);
318 | if (t !== 0) {
319 | diff = receiveAndExtend(t);
320 | }
321 | component.pred += diff << successive;
322 | }
323 |
324 | function decodeACFirst(component, pos) {
325 | if (eobrun > 0) {
326 | eobrun--;
327 | return;
328 | }
329 |
330 | var k = spectralStart,
331 | s, r;
332 |
333 | while (k <= spectralEnd) {
334 | s = decodeHuffman(component.huffmanTableAC);
335 | r = s >> 4;
336 | s &= 15;
337 | if (s === 0) {
338 | if (r != 15) {
339 | eobrun = (1 << r) - 1;
340 | if (r) {
341 | eobrun += receive(r);
342 | }
343 | break;
344 | }
345 | k += 16;
346 | continue;
347 | }
348 |
349 | k += r;
350 | component.blocks[pos + k] = receiveAndExtend(s) * p1;
351 | k++;
352 | }
353 | }
354 |
355 | function decodeACSuccessive(component, pos) {
356 | var k = spectralStart,
357 | r, s;
358 |
359 | if (!eobrun) {
360 | while (k <= spectralEnd) {
361 | s = decodeHuffman(component.huffmanTableAC);
362 | r = s >> 4;
363 | s &= 15;
364 |
365 | if (s) {
366 | if (s != 1) throw "bad jpeg";
367 | if (getBit()) s = p1;
368 | else s = m1;
369 | } else {
370 | if (r != 15) {
371 | eobrun = (1 << r);
372 | if (r) {
373 | eobrun += receive(r);
374 | }
375 | break;
376 | }
377 | }
378 |
379 | while (k <= spectralEnd) {
380 | if (component.blocks[pos + k]) {
381 | component.blocks[pos + k] += getBit() * (component.blocks[pos + k] >= 0 ? p1 : m1);
382 | } else {
383 | if (--r < 0) break;
384 | }
385 | k++;
386 | }
387 |
388 | if (s) component.blocks[pos + k] = s;
389 | k++;
390 | }
391 | }
392 |
393 | if (eobrun) {
394 | while (k <= spectralEnd) {
395 | if (component.blocks[pos + k]) {
396 | component.blocks[pos + k] += getBit() * (component.blocks[pos + k] >= 0 ? p1 : m1);
397 | }
398 | k++;
399 | }
400 | eobrun--;
401 | }
402 | }
403 |
404 | var decodeFn;
405 |
406 | if (frame.progressive) {
407 | if (spectralStart === 0)
408 | decodeFn = successivePrev === 0 ? decodeDCFirst : getBit;
409 | else
410 | decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
411 | } else {
412 | decodeFn = decodeBaseline;
413 | }
414 |
415 | var marker, mcuExpected, i, j, k, n, mcusPerLine, mcusPerRow, x, y;
416 |
417 | if (components.length == 1) {
418 | mcusPerLine = components[0].blocksPerLine;
419 | mcusPerRow = components[0].blocksPerColumn;
420 | mcuExpected = mcusPerRow * mcusPerLine;
421 |
422 | if (!resetInterval) resetInterval = mcuExpected;
423 | n = resetInterval;
424 | components[0].pred = 0;
425 | eobrun = 0;
426 |
427 | for (y = 0; y < mcusPerRow; y++) {
428 | for (x = 0; x < mcusPerLine; x++) {
429 | if (!n) {
430 | n = resetInterval;
431 | components[0].pred = 0;
432 | eobrun = 0;
433 |
434 | // find marker
435 | offset -= (bitsCount / 8) | 0;
436 | if (data[offset - 1] == 0xFF) offset--;
437 | bitsCount = 0;
438 | marker = (data[offset] << 8) | data[offset + 1];
439 |
440 | if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
441 | offset += 2;
442 | } else {
443 | if (marker <= 0xFF00) {
444 | throw "bad jpeg";
445 | }
446 | break;
447 | }
448 | }
449 | n--;
450 | for (i = 0; i < components.length; i++) {
451 | decodeFn(components[i], (y * components[i].blocksPerLineForMcu + x) * 64);
452 | }
453 |
454 | }
455 | }
456 | } else {
457 | mcusPerLine = frame.mcusPerLine;
458 | mcusPerRow = frame.mcusPerColumn;
459 | mcuExpected = mcusPerRow * mcusPerLine;
460 |
461 | if (!resetInterval) resetInterval = mcuExpected;
462 | n = resetInterval;
463 | for (i = 0; i < components.length; i++) components[i].pred = 0;
464 | eobrun = 0;
465 |
466 | for (y = 0; y < mcusPerRow; y++) {
467 | for (x = 0; x < mcusPerLine; x++) {
468 | if (!n) {
469 | n = resetInterval;
470 | for (i = 0; i < components.length; i++) components[i].pred = 0;
471 | eobrun = 0;
472 |
473 | // find marker
474 | offset -= (bitsCount / 8) | 0;
475 | if (data[offset - 1] == 0xFF) offset--;
476 | bitsCount = 0;
477 | marker = (data[offset] << 8) | data[offset + 1];
478 |
479 | if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
480 | offset += 2;
481 | } else {
482 | if (marker <= 0xFF00) {
483 | throw "bad jpeg";
484 | }
485 | break;
486 | }
487 | }
488 | n--;
489 | for (i = 0; i < components.length; i++) {
490 | for (j = 0; j < components[i].v; j++) {
491 | for (k = 0; k < components[i].h; k++) {
492 | decodeFn(components[i], ((y * components[i].v + j) * components[i].blocksPerLineForMcu + x * components[i].h + k) * 64);
493 | }
494 | }
495 | }
496 | }
497 | }
498 | }
499 | offset -= (bitsCount / 8) | 0;
500 | if (data[offset - 1] == 0xFF) offset--;
501 | return offset - startOffset;
502 | }
503 |
504 | function extract(data) {
505 |
506 | function readUint16() {
507 | var value = (data[offset] << 8) | data[offset + 1];
508 | offset += 2;
509 | return value;
510 | }
511 |
512 | var frame, offset = 0;
513 |
514 | var markerHi, markerLo, resetInterval, component,
515 | huffmanTablesAC = [],
516 | huffmanTablesDC = [];
517 |
518 | while (1) {
519 | if (offset >= data.length) throw "unexpected EOF";
520 |
521 | markerHi = data[offset++];
522 | markerLo = data[offset++];
523 |
524 | if (markerHi == 0xFF) {
525 | if ((markerLo >= 0xE0 && markerLo < 0xF0) || markerLo == 0xFE || markerLo == 0xDB) { //APPn + COM
526 | offset += readUint16();
527 | }
528 |
529 | if (markerLo >= 0xC0 && markerLo <= 0xC2) {
530 | // SOF0 (Start of Frame, Baseline DCT)
531 | // SOF1 (Start of Frame, Extended DCT)
532 | // SOF2 (Start of Frame, Progressive DCT)
533 | if (frame) throw "Only single frame JPEGs supported";
534 | frame = {};
535 | readUint16(); // skip data length
536 | offset++;
537 | frame.progressive = markerLo === 0xC2;
538 | frame.scanLines = readUint16();
539 | frame.samplesPerLine = readUint16();
540 | frame.components = [];
541 | frame.componentIds = {};
542 |
543 | if (frame.scanLines * frame.samplesPerLine > maxPixels) throw "Image is too big.";
544 |
545 | var componentsCount = data[offset++],
546 | componentId;
547 | var maxH = 0,
548 | maxV = 0;
549 | for (i = 0; i < componentsCount; i++) {
550 | componentId = data[offset];
551 | var h = data[offset + 1] >> 4;
552 | var v = data[offset + 1] & 15;
553 | if (maxH < h) maxH = h;
554 | if (maxV < v) maxV = v;
555 | frame.componentIds[componentId] = frame.components.push({
556 | componentId: componentId,
557 | h: h,
558 | v: v
559 | }) - 1;
560 | offset += 3;
561 | }
562 | frame.maxH = maxH;
563 | frame.maxV = maxV;
564 |
565 | var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / maxH);
566 | var mcusPerColumn = Math.ceil(frame.scanLines / 8 / maxV);
567 | for (i = 0; i < frame.components.length; i++) {
568 | component = frame.components[i];
569 | var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / maxH);
570 | var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / maxV);
571 | var blocksPerLineForMcu = mcusPerLine * component.h;
572 | var blocksPerColumnForMcu = mcusPerColumn * component.v;
573 |
574 | component.blocks = new Int16Array(64 * blocksPerColumnForMcu * blocksPerLineForMcu);
575 | component.blocksPerLine = blocksPerLine;
576 | component.blocksPerColumn = blocksPerColumn;
577 | component.blocksPerLineForMcu = blocksPerLineForMcu;
578 | component.blocksPerColumnForMcu = blocksPerColumnForMcu;
579 | }
580 | frame.mcusPerLine = mcusPerLine;
581 | frame.mcusPerColumn = mcusPerColumn;
582 | }
583 |
584 | if (markerLo == 0xC4) { // DHT (Define Huffman Tables)
585 | var huffmanLength = readUint16();
586 | for (i = 2; i < huffmanLength;) {
587 | var huffmanTableSpec = data[offset++];
588 | var codeLengths = new Uint8Array(16);
589 | var codeLengthSum = 0;
590 | for (j = 0; j < 16; j++, offset++)
591 | codeLengthSum += (codeLengths[j] = data[offset]);
592 | var huffmanValues = new Uint8Array(codeLengthSum);
593 | for (j = 0; j < codeLengthSum; j++, offset++)
594 | huffmanValues[j] = data[offset];
595 | i += 17 + codeLengthSum;
596 | ((huffmanTableSpec >> 4) === 0 ?
597 | huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = _buildHuffmanTable(codeLengths, huffmanValues);
598 | }
599 | }
600 |
601 | if (markerLo == 0xDD) { // DRI (Define Restart Interval)
602 | resetInterval = readUint16();
603 | }
604 |
605 | if (markerLo == 0xDA) { // SOS (Start of Scan)
606 | if (!frame) throw "bad jpeg";
607 | readUint16();
608 | var selectorsCount = data[offset++];
609 | var components = [];
610 |
611 | for (i = 0; i < selectorsCount; i++) {
612 | var componentIndex = frame.componentIds[data[offset++]];
613 | component = frame.components[componentIndex];
614 | var tableSpec = data[offset++];
615 | component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
616 | component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
617 | components.push(component);
618 | }
619 |
620 | var spectralStart = data[offset++];
621 | var spectralEnd = data[offset++];
622 | var successiveApproximation = data[offset++];
623 | offset += decodeScan(data, offset,
624 | frame, components, resetInterval,
625 | spectralStart, spectralEnd,
626 | successiveApproximation >> 4, successiveApproximation & 15);
627 | }
628 |
629 | if (markerLo == 0xD9) { // EOI (End of image)
630 | break;
631 | }
632 | } else {
633 | if (data[offset - 3] == 0xFF &&
634 | data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
635 | // could be incorrect encoding -- last 0xFF byte of the previous
636 | // block was eaten by the encoder
637 | offset -= 3;
638 | }
639 | while (data[offset] != 0xFF && offset < data.length) {
640 | // file could be damaged and have some extra data between blocks
641 | offset++;
642 | }
643 |
644 | if (data[offset] != 0xFF) {
645 | throw "bad jpeg ";
646 | }
647 | }
648 | }
649 |
650 | if (!frame) throw 'bad jpeg';
651 |
652 | component = frame.components[0];
653 |
654 | if (component.componentId != 1) {
655 | for (i = 0; i < frame.components.length; i++) {
656 | if (frame.components[i].componentId == 1) {
657 | component = frame.components[i];
658 | break;
659 | }
660 | }
661 | }
662 |
663 | return _f5read(component.blocks);
664 | }
665 |
666 | return extract;
667 | }
668 |
--------------------------------------------------------------------------------
/f5stego.js:
--------------------------------------------------------------------------------
1 | /* This software is licensed under the MIT License.
2 |
3 | Copyright (c) 2016 desudesutalk
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 | https://github.com/desudesutalk/f5stegojs
23 |
24 | This library is based on https://github.com/owencm/js-steg by Owen Campbell-
25 | Moore. Decoder and encoder was optimized for speed, F5 algorithm and metadata
26 | manipulation utils was added to library.
27 |
28 | Original code was released under MIT and Apache licenses, so here follows
29 | original licenses of Owen code:
30 |
31 | jpeg decoder license:
32 |
33 | Modified JPEG decoder for Steganography by Owen Campbell-Moore, based on one
34 | released by Adobe.
35 |
36 | Copyright 2011 notmasteryet
37 |
38 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use
39 | this file except in compliance with the License. You may obtain a copy of the
40 | License at
41 |
42 | http://www.apache.org/licenses/LICENSE-2.0
43 |
44 | Unless required by applicable law or agreed to in writing, software distributed
45 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
46 | CONDITIONS OF ANY KIND, either express or implied. See the License for the
47 | specific language governing permissions and limitations under the License.
48 |
49 |
50 | jpeg encoder license:
51 |
52 | JPEG encoder ported to JavaScript, optimized by Andreas Ritter
53 | (www.bytestrom.eu, 11/2009) and made suitable for steganography by Owen
54 | Campbell-Moore (www.owencampbellmoore.com, 03/13)
55 |
56 | Based on v 0.9a
57 |
58 | Licensed under the MIT License
59 |
60 | Copyright (c) 2009 Andreas Ritter
61 |
62 | Permission is hereby granted, free of charge, to any person obtaining a copy of
63 | this software and associated documentation files (the "Software"), to deal in
64 | the Software without restriction, including without limitation the rights to
65 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
66 | the Software, and to permit persons to whom the Software is furnished to do so,
67 | subject to the following conditions:
68 |
69 | The above copyright notice and this permission notice shall be included in all
70 | copies or substantial portions of the Software.
71 |
72 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
73 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
74 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
75 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
76 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
77 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
78 |
79 | Copyright (c) 2008, Adobe Systems Incorporated All rights reserved.
80 |
81 | Redistribution and use in source and binary forms, with or without modification,
82 | are permitted provided that the following conditions are met:
83 |
84 | Redistributions of source code must retain the above copyright notice, this list
85 | of conditions and the following disclaimer.
86 |
87 | Redistributions in binary form must reproduce the above copyright notice, this
88 | list of conditions and the following disclaimer in the documentation and/or
89 | other materials provided with the distribution.
90 |
91 | Neither the name of Adobe Systems Incorporated nor the names of its contributors
92 | may be used to endorse or promote products derived from this software without
93 | specific prior written permission.
94 |
95 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
96 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
97 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
98 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
99 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
100 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
101 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
102 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
103 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
104 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
105 | */
106 |
107 | /* global define, module, exports */
108 | /* jshint sub:true */
109 | ;(function(root, factory) {
110 | 'use strict';
111 | if (typeof define === 'function' && define.amd) {
112 | define([], factory);
113 | } else if (typeof exports === 'object') {
114 | module.exports = factory();
115 | } else {
116 | root.f5stego = factory();
117 | }
118 | }(this, function() {
119 | 'use strict';
120 |
121 | var f5stego = function(key, maxPixels) {
122 | this.maxPixels = maxPixels || 4096 * 4096;
123 | this.shuffleInit(key);
124 | };
125 |
126 | // Shuffle used in f5 algo
127 | // ===========================================================================================================
128 | f5stego.prototype.shuffleInit = function(key) {
129 | this.randPool = new ArrayBuffer(this.maxPixels * 4.125);
130 |
131 | if (!key.length) throw 'key needed';
132 |
133 | var i = 0,
134 | j = 0,
135 | t = 0,
136 | k = 0,
137 | S = new Uint8Array(256),
138 | rnd = new Uint8Array(this.randPool);
139 |
140 | // init state from key
141 | for (i = 0; i < 256; ++i) S[i] = i;
142 |
143 | for (i = 0; i < 256; ++i) {
144 | j = (j + S[i] + key[i % key.length]) & 255;
145 | t = S[i];
146 | S[i] = S[j];
147 | S[j] = t;
148 | }
149 | i = 0;
150 | j = 0;
151 |
152 | // shuffle data
153 | for (k = 0; k < this.maxPixels * 4.125; ++k) {
154 | i = (i + 1) & 255;
155 | j = (j + S[i]) & 255;
156 | t = S[i];
157 | S[i] = S[j];
158 | S[j] = t;
159 | rnd[k] = S[(t + S[i]) & 255];
160 | }
161 | };
162 |
163 | f5stego.prototype.stegShuffle = function(pm) {
164 | var t, l, k, random_index,
165 | rand32Array = new Uint32Array(this.randPool);
166 |
167 | if (typeof pm == 'number') {
168 | l = pm;
169 | pm = new Uint32Array(l);
170 | for (k = 1; k < l; k++) {
171 | random_index = rand32Array[k] % (k + 1);
172 | if (random_index != k) pm[k] = pm[random_index];
173 | pm[random_index] = k;
174 | }
175 | } else {
176 | l = pm.length;
177 | for (k = 1; k < l; k++) {
178 | random_index = rand32Array[k] % (k + 1);
179 | // if (random_index != k) {
180 | t = pm[k];
181 | pm[k] = pm[random_index];
182 | pm[random_index] = t;
183 | // }
184 | }
185 | }
186 | return { pm: pm, gamma: new Uint8Array(this.randPool, l * 4) };
187 | };
188 |
189 | // Internal f5 algo functions
190 | // ===========================================================================================================
191 | f5stego.prototype._analyze = function(coeff) {
192 | var _one = 0,
193 | _zero = 0,
194 | _large, _ratio, usable, i, k, embedded, matched, changed;
195 |
196 | for (i = 0; i < coeff.length; i++) {
197 | if (i % 64 === 0) continue;
198 | if (coeff[i] === 0) _zero++;
199 | if (coeff[i] == 1 || coeff[i] == -1) _one++;
200 | }
201 |
202 | _large = coeff.length - _zero - _one - coeff.length / 64;
203 | _ratio = _one / (_large + _one);
204 |
205 | var res = {
206 | 'capacity': [0, ((_large + (0.49 * _one)) >> 3) - 1],
207 | 'coeff_total': coeff.length,
208 | 'coeff_large': _large,
209 | 'coeff_zero': _zero,
210 | 'coeff_one': _one,
211 | 'coeff_one_ratio': _one / (_large + _one)
212 | };
213 |
214 | for (i = 2; i < 17; i++) {
215 | k = (1 << i) - 1;
216 | usable = _large + _one;
217 | embedded = 0;
218 | while (usable > k) {
219 | matched = (usable / k / (1 << i) / (1 << i)) | 0;
220 | usable -= matched * k;
221 |
222 | changed = (usable * (1 - _ratio) / k * 0.96) | 0;
223 | usable -= changed * k;
224 | embedded += changed + matched;
225 |
226 | k++;
227 | }
228 | res.capacity[i] = ((i * embedded) >> 3) - 1;
229 | }
230 |
231 | return res;
232 | };
233 |
234 | f5stego.prototype._f5write = function(coeff, data, k) {
235 | var coeff_count = coeff.length;
236 |
237 | var _changed = 0,
238 | _embedded = 0,
239 | _examined = 0,
240 | _thrown = 0,
241 | shuffled_index = 0,
242 | i, n, ii;
243 |
244 | var pm = this.stegShuffle(coeff_count);
245 | var gamma = pm.gamma,
246 | gammaI = 0;
247 | pm = pm.pm;
248 |
249 | var next_bit_to_embed = 0,
250 | byte_to_embed = data.length,
251 | data_idx = 0,
252 | available_bits_to_embed = 0;
253 |
254 | n = (1 << k) - 1;
255 |
256 | byte_to_embed = k - 1;
257 | byte_to_embed ^= gamma[gammaI++];
258 | next_bit_to_embed = byte_to_embed & 1;
259 | byte_to_embed >>= 1;
260 | available_bits_to_embed = 3;
261 |
262 | for (ii = 0; ii < coeff_count; ii++) {
263 | shuffled_index = pm[ii];
264 |
265 | if (shuffled_index % 64 === 0 || coeff[shuffled_index] === 0) continue;
266 |
267 | var cc = coeff[shuffled_index];
268 | _examined++;
269 |
270 | if (cc > 0 && (cc & 1) != next_bit_to_embed) {
271 | coeff[shuffled_index]--;
272 | _changed++;
273 | } else if (cc < 0 && (cc & 1) == next_bit_to_embed) {
274 | coeff[shuffled_index]++;
275 | _changed++;
276 | }
277 |
278 | if (coeff[shuffled_index] !== 0) {
279 | _embedded++;
280 | if (available_bits_to_embed === 0) {
281 | if (k != 1 || data_idx >= data.length) break;
282 | byte_to_embed = data[data_idx++];
283 | byte_to_embed ^= gamma[gammaI++];
284 | available_bits_to_embed = 8;
285 | }
286 | next_bit_to_embed = byte_to_embed & 1;
287 | byte_to_embed >>= 1;
288 | available_bits_to_embed--;
289 | } else {
290 | _thrown++;
291 | }
292 | }
293 |
294 | if (k == 1 && _embedded < data.length * 8) throw 'capacity exceeded ' + (_embedded / 8) + ' ' + data.length;
295 |
296 | if (k != 1) {
297 | //ii--;
298 | var is_last_byte = false,
299 | k_bits_to_embed = 0;
300 |
301 | while (!is_last_byte || (available_bits_to_embed !== 0 && is_last_byte)) {
302 | k_bits_to_embed = 0;
303 |
304 | for (i = 0; i < k; i++) {
305 | if (available_bits_to_embed === 0) {
306 | if (data_idx >= data.length) {
307 | is_last_byte = true;
308 | break;
309 | }
310 | byte_to_embed = data[data_idx++];
311 | byte_to_embed ^= gamma[gammaI++];
312 | available_bits_to_embed = 8;
313 | }
314 | next_bit_to_embed = byte_to_embed & 1;
315 | byte_to_embed >>= 1;
316 | available_bits_to_embed--;
317 | k_bits_to_embed |= next_bit_to_embed << i;
318 |
319 | }
320 |
321 | var code_word = [];
322 | var ci = null;
323 |
324 | for (i = 0; i < n; i++) {
325 | while (true) {
326 | if (++ii >= coeff_count) {
327 | throw 'capacity exceeded ' + (_embedded / 8);
328 | }
329 | ci = pm[ii];
330 |
331 | if (ci % 64 !== 0 && coeff[ci] !== 0) break;
332 | }
333 | code_word.push(ci);
334 | }
335 | _examined += n;
336 |
337 | while (true) {
338 | var vhash = 0,
339 | extracted_bit;
340 |
341 | for (i = 0; i < code_word.length; i++) {
342 | if (coeff[code_word[i]] > 0) {
343 | extracted_bit = coeff[code_word[i]] & 1;
344 | } else {
345 | extracted_bit = 1 - (coeff[code_word[i]] & 1);
346 | }
347 |
348 | if (extracted_bit == 1)
349 | vhash ^= i + 1;
350 | }
351 |
352 | i = vhash ^ k_bits_to_embed;
353 | if (!i) {
354 | _embedded += k;
355 | break;
356 | }
357 |
358 | i--;
359 | coeff[code_word[i]] += coeff[code_word[i]] < 0 ? 1 : -1;
360 | _changed++;
361 |
362 | if (coeff[code_word[i]] === 0) {
363 | _thrown++;
364 | code_word.splice(i, 1);
365 |
366 | while (true) {
367 | if (++ii >= coeff_count) {
368 | throw 'capacity exceeded ' + (_embedded / 8);
369 | }
370 | ci = pm[ii];
371 | if (ci % 64 !== 0 && coeff[ci] !== 0) break;
372 |
373 | }
374 | _examined++;
375 | code_word.push(ci);
376 | } else {
377 | _embedded += k;
378 | break;
379 | }
380 | }
381 | }
382 | }
383 |
384 | return {
385 | 'k': k,
386 | 'embedded': _embedded / 8,
387 | 'examined': _examined,
388 | 'changed': _changed,
389 | 'thrown': _thrown,
390 | 'efficiency': (_embedded / _changed).toFixed(2)
391 | };
392 | };
393 |
394 | // Public f5 algo functions
395 | // ===========================================================================================================
396 | f5stego.prototype.analyze = function() {
397 | var i, comp = this.frame.components[0];
398 |
399 | if (comp.componentId != 1) {
400 | for (i = 0; i < this.frame.components.length; i++) {
401 | if (this.frame.components[i].componentId == 1) {
402 | comp = this.frame.components[i];
403 | break;
404 | }
405 | }
406 | }
407 |
408 | return this._analyze(comp.blocks);
409 | };
410 |
411 | f5stego.prototype.f5put = function(data, k) {
412 | var t, i, comp = this.frame.components[0];
413 |
414 | // Looks funny, but who knows?
415 | // From the other hand you need ~80MB jpeg to hide 8MB of data and this will be bigger than 4096x4096 pixels
416 | if (data.length > 8388607) throw 'Data too big. Max 8388607 bytes allowed.';
417 |
418 | if (data.length < 32768) {
419 | t = new Uint8Array(2 + data.length);
420 | t[0] = data.length & 255;
421 | t[1] = data.length >>> 8;
422 | t.set(data, 2);
423 | } else {
424 | t = new Uint8Array(3 + data.length);
425 | t[0] = data.length & 255;
426 | t[1] = ((data.length >>> 8) & 127) + 128;
427 | t[2] = data.length >>> 15;
428 | t.set(data, 3);
429 | }
430 |
431 | if (comp.componentId != 1) {
432 | for (i = 0; i < this.frame.components.length; i++) {
433 | if (this.frame.components[i].componentId == 1) {
434 | comp = this.frame.components[i];
435 | break;
436 | }
437 | }
438 | }
439 |
440 | if (k) {
441 | return this._f5write(comp.blocks, t, k);
442 | }
443 |
444 | var ret, prop = this._analyze(comp.blocks);
445 |
446 | k = 0;
447 |
448 | for (i = prop.capacity.length - 1; i >= 0; i--) {
449 | if (prop.capacity[i] >= t.length) {
450 | k = i;
451 | break;
452 | }
453 | }
454 |
455 | if (k === 0) throw 'capacity exceeded';
456 |
457 | try {
458 | ret = this._f5write(comp.blocks, t, k);
459 | } catch (e) {
460 | k--;
461 | if (k === 0) throw 'capacity exceeded';
462 | ret = this._f5write(comp.blocks, t, k);
463 | }
464 |
465 | ret['stats'] = prop;
466 |
467 | return ret;
468 | };
469 |
470 | f5stego.prototype.f5get = function() {
471 | var comp = this.frame.components[0];
472 |
473 | if (comp.componentId != 1) {
474 | for (var i = 0; i < this.frame.components.length; i++) {
475 | if (this.frame.components[i].componentId == 1) {
476 | comp = this.frame.components[i];
477 | break;
478 | }
479 | }
480 | }
481 |
482 | var coeff = new Int16Array(comp.blocks.length);
483 | coeff.set(comp.blocks);
484 |
485 | var pos = -1,
486 | extrBit = 0,
487 | cCount = coeff.length - 1;
488 |
489 | var pm = this.stegShuffle(coeff),
490 | gamma = pm.gamma,
491 | gammaI = 0;
492 |
493 | var n, k = 0;
494 |
495 | var out = new Uint8Array((coeff.length / 8) | 0),
496 | extrByte = 0,
497 | outPos = 0,
498 | bitsAvail = 0,
499 | code = 0,
500 | hash = 0;
501 |
502 | while (bitsAvail < 4) {
503 | pos++;
504 |
505 | if (coeff[pos] === 0) {
506 | continue;
507 | }
508 |
509 | extrBit = coeff[pos] & 1;
510 |
511 |
512 | if (coeff[pos] < 0) {
513 | extrBit = 1 - extrBit;
514 | }
515 |
516 | k |= extrBit << bitsAvail;
517 | bitsAvail++;
518 | }
519 |
520 | k = (k ^ gamma[gammaI++] & 15) + 1;
521 | n = (1 << k) - 1;
522 |
523 | bitsAvail = 0;
524 |
525 | if (k == 1) {
526 | while (pos < cCount) {
527 | pos++;
528 |
529 | if (coeff[pos] === 0) {
530 | continue;
531 | }
532 |
533 | extrBit = coeff[pos] & 1;
534 |
535 | if (coeff[pos] < 0) {
536 | extrBit = 1 - extrBit;
537 | }
538 |
539 | extrByte |= extrBit << bitsAvail;
540 | bitsAvail++;
541 |
542 | if (bitsAvail == 8) {
543 | out[outPos++] = extrByte ^ gamma[gammaI++];
544 | extrByte = 0;
545 | bitsAvail = 0;
546 | }
547 | }
548 | } else {
549 | while (pos < cCount) {
550 | pos++;
551 |
552 | if (coeff[pos] === 0) {
553 | continue;
554 | }
555 |
556 | extrBit = coeff[pos] & 1;
557 |
558 | if (coeff[pos] < 0) {
559 | extrBit = 1 - extrBit;
560 | }
561 |
562 | hash ^= extrBit * ++code;
563 |
564 | if (code == n) {
565 | extrByte |= hash << bitsAvail;
566 | bitsAvail += k;
567 | code = 0;
568 | hash = 0;
569 |
570 | while (bitsAvail >= 8) {
571 | out[outPos++] = (extrByte & 0xFF) ^ gamma[gammaI++];
572 | bitsAvail -= 8;
573 | extrByte = extrByte >> 8;
574 | }
575 | }
576 | }
577 | }
578 |
579 | while (bitsAvail > 0) {
580 | out[outPos++] = (extrByte & 0xFF) ^ gamma[gammaI++];
581 | bitsAvail -= 8;
582 | extrByte = extrByte >> 8;
583 | }
584 |
585 | var s = 2,
586 | l = out[0];
587 |
588 | if (out[1] & 128) {
589 | s++;
590 | l += ((out[1] & 127) << 8) + (out[2] << 15);
591 | } else {
592 | l += out[1] << 8;
593 | }
594 |
595 | return out.subarray(s, s + l);
596 | };
597 |
598 | // JPEG decoder
599 | // ===========================================================================================================
600 | f5stego.prototype.parse = function(data) {
601 | var offset = 0;
602 |
603 | function _buildHuffmanTable(nrcodes, values) {
604 | var codevalue = 0,
605 | pos_in_table = 0,
606 | HT = new Uint16Array(65536);
607 | for (var k = 0; k < 16; k++) {
608 | for (var j = 0; j < nrcodes[k]; j++) {
609 | for (var i = codevalue << (15 - k), cntTo = ((codevalue + 1) << (15 - k)); i < cntTo; i++) {
610 | HT[i] = values[pos_in_table] + ((k + 1) << 8);
611 | }
612 | pos_in_table++;
613 | codevalue++;
614 | }
615 | codevalue *= 2;
616 | }
617 | return HT;
618 | }
619 |
620 | function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive) {
621 |
622 | var startOffset = offset,
623 | bitsData = 0,
624 | bitsCount = 0,
625 | eobrun = 0,
626 | p1 = 1 << successive, /* 1 in the bit position being coded */
627 | m1 = -1 << successive; /* -1 in the bit position being coded */
628 |
629 | function decodeBaseline(component, pos) {
630 | while (bitsCount < 16) {
631 | bitsData = (bitsData << 8) + (data[offset] | 0);
632 | bitsCount += 8;
633 | if (data[offset] == 0xFF) offset++;
634 | offset++;
635 | }
636 | var t = component.huffmanTableDC[(bitsData >>> (bitsCount - 16)) & 0xFFFF];
637 | if (!t) throw "invalid huffman sequence";
638 | bitsCount -= t >>> 8;
639 | t &= 255;
640 |
641 | var diff = 0;
642 | if (t !== 0) {
643 | while (bitsCount < t) {
644 | bitsData = (bitsData << 8) + data[offset++];
645 | if ((bitsData & 0xff) == 0xFF) offset++;
646 | bitsCount += 8;
647 | }
648 | diff = (bitsData >>> (bitsCount - t)) & ((1 << t) - 1);
649 | bitsCount -= t;
650 | if (diff < 1 << (t - 1)) diff += (-1 << t) + 1;
651 | }
652 | component.blocksDC[pos >> 6] = (component.pred += diff);
653 |
654 | var k = 1,
655 | s, r;
656 | while (k < 64) {
657 |
658 | while (bitsCount < 16) {
659 | bitsData = (bitsData << 8) + (data[offset] | 0);
660 | bitsCount += 8;
661 | if (data[offset] == 0xFF) offset++;
662 | offset++;
663 | }
664 | s = component.huffmanTableAC[(bitsData >>> (bitsCount - 16)) & 0xFFFF];
665 | if (!s) throw "invalid huffman sequence";
666 | bitsCount -= s >>> 8;
667 | r = (s >> 4) & 15;
668 | s &= 15;
669 |
670 | if (s === 0) {
671 | if (r < 15) {
672 | break;
673 | }
674 | k += 16;
675 | continue;
676 | }
677 | k += r;
678 | while (bitsCount < s) {
679 | bitsData = (bitsData << 8) + data[offset++];
680 | if ((bitsData & 0xff) == 0xFF) offset++;
681 | bitsCount += 8;
682 | }
683 | component.blocks[pos + k] = (bitsData >>> (bitsCount - s)) & ((1 << s) - 1);
684 | bitsCount -= s;
685 | if (component.blocks[pos + k] < 1 << (s - 1)) component.blocks[pos + k] += (-1 << s) + 1;
686 | k++;
687 | }
688 | }
689 |
690 | function decodeDCFirst(component, pos) {
691 | var diff = 0;
692 | while (bitsCount < 16) {
693 | bitsData = (bitsData << 8) + (data[offset] | 0);
694 | bitsCount += 8;
695 | if (data[offset] == 0xFF) offset++;
696 | offset++;
697 | }
698 | var t = component.huffmanTableDC[(bitsData >>> (bitsCount - 16)) & 0xFFFF];
699 | if (!t) throw "invalid huffman sequence";
700 | bitsCount -= t >>> 8;
701 | t &= 255;
702 |
703 | if (t !== 0) {
704 | while (bitsCount < t) {
705 | bitsData = (bitsData << 8) + data[offset++];
706 | if ((bitsData & 0xff) == 0xFF) offset++;
707 | bitsCount += 8;
708 | }
709 | diff = (bitsData >>> (bitsCount - t)) & ((1 << t) - 1);
710 | bitsCount -= t;
711 | if (diff < 1 << (t - 1)) diff += (-1 << t) + 1;
712 | }
713 | component.blocksDC[pos >> 6] = (component.pred += diff << successive);
714 | }
715 |
716 | function decodeDCSuccessive(component, pos) {
717 | if (!bitsCount) {
718 | bitsData = data[offset++];
719 | if (bitsData == 0xFF) offset++;
720 | bitsCount = 8;
721 | }
722 | component.blocksDC[pos >> 6] |= ((bitsData >>> --bitsCount) & 1) << successive;
723 | }
724 |
725 | function decodeACFirst(component, pos) {
726 | if (eobrun > 0) {
727 | eobrun--;
728 | return;
729 | }
730 |
731 | var k = spectralStart,
732 | s, r;
733 |
734 | while (k <= spectralEnd) {
735 | while (bitsCount < 16) {
736 | bitsData = (bitsData << 8) + (data[offset] | 0);
737 | bitsCount += 8;
738 | if (data[offset] == 0xFF) offset++;
739 | offset++;
740 | }
741 | s = component.huffmanTableAC[(bitsData >>> (bitsCount - 16)) & 0xFFFF];
742 | if (!s) throw "invalid huffman sequence";
743 | bitsCount -= s >>> 8;
744 | r = (s >> 4) & 15;
745 | s &= 15;
746 |
747 | if (s === 0) {
748 | if (r != 15) {
749 | eobrun = (1 << r) - 1;
750 | if (r) {
751 | while (bitsCount < r) {
752 | bitsData = (bitsData << 8) + data[offset++];
753 | if ((bitsData & 0xff) == 0xFF) offset++;
754 | bitsCount += 8;
755 | }
756 | eobrun += (bitsData >>> (bitsCount - r)) & ((1 << r) - 1);
757 | bitsCount -= r;
758 | }
759 | break;
760 | }
761 | k += 16;
762 | continue;
763 | }
764 |
765 | k += r;
766 | while (bitsCount < s) {
767 | bitsData = (bitsData << 8) + data[offset++];
768 | if ((bitsData & 0xff) == 0xFF) offset++;
769 | bitsCount += 8;
770 | }
771 | component.blocks[pos + k] = (bitsData >>> (bitsCount - s)) & ((1 << s) - 1);
772 | bitsCount -= s;
773 | if (component.blocks[pos + k] < 1 << (s - 1)) component.blocks[pos + k] += (-1 << s) + 1;
774 | component.blocks[pos + k] *= p1;
775 | k++;
776 | }
777 | }
778 |
779 | function decodeACSuccessive(component, pos) {
780 | var k = spectralStart,
781 | r, s;
782 |
783 | if (!eobrun) {
784 | while (k <= spectralEnd) {
785 | while (bitsCount < 16) {
786 | bitsData = (bitsData << 8) + (data[offset] | 0);
787 | bitsCount += 8;
788 | if (data[offset] == 0xFF) offset++;
789 | offset++;
790 | }
791 | s = component.huffmanTableAC[(bitsData >>> (bitsCount - 16)) & 0xFFFF];
792 | if (!s) throw "invalid huffman sequence";
793 | bitsCount -= s >>> 8;
794 | r = (s >> 4) & 15;
795 | s &= 15;
796 |
797 | if (s) {
798 | if (s != 1) throw "bad jpeg";
799 | if (!bitsCount) {
800 | bitsData = data[offset++];
801 | if (bitsData == 0xFF) offset++;
802 | bitsCount = 8;
803 | }
804 | s = ((bitsData >>> --bitsCount) & 1) ? p1 : m1;
805 | } else {
806 | if (r != 15) {
807 | eobrun = (1 << r);
808 | if (r) {
809 | while (bitsCount < r) {
810 | bitsData = (bitsData << 8) + data[offset++];
811 | if ((bitsData & 0xff) == 0xFF) offset++;
812 | bitsCount += 8;
813 | }
814 | eobrun += (bitsData >>> (bitsCount - r)) & ((1 << r) - 1);
815 | bitsCount -= r;
816 | }
817 | break;
818 | }
819 | }
820 |
821 | while (k <= spectralEnd) {
822 | if (component.blocks[pos + k]) {
823 | if (!bitsCount) {
824 | bitsData = data[offset++];
825 | if (bitsData == 0xFF) offset++;
826 | bitsCount = 8;
827 | }
828 | component.blocks[pos + k] += ((bitsData >>> --bitsCount) & 1) * (component.blocks[pos + k] >= 0 ? p1 : m1);
829 | } else {
830 | if (--r < 0) break;
831 | }
832 | k++;
833 | }
834 |
835 | if (s) component.blocks[pos + k] = s;
836 | k++;
837 | }
838 | }
839 |
840 | if (eobrun) {
841 | while (k <= spectralEnd) {
842 | if (component.blocks[pos + k]) {
843 | if (!bitsCount) {
844 | bitsData = data[offset++];
845 | if (bitsData == 0xFF) offset++;
846 | bitsCount = 8;
847 | }
848 | component.blocks[pos + k] += ((bitsData >>> --bitsCount) & 1) * (component.blocks[pos + k] >= 0 ? p1 : m1);
849 | }
850 | k++;
851 | }
852 | eobrun--;
853 | }
854 | }
855 |
856 | var decodeFn;
857 |
858 | if (frame.progressive) {
859 | if (spectralStart === 0)
860 | decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
861 | else
862 | decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
863 | } else {
864 | decodeFn = decodeBaseline;
865 | }
866 |
867 | var marker, mcuExpected, i, j, k, n, mcusPerLine, mcusPerRow, x, y;
868 |
869 | if (components.length == 1) {
870 | mcusPerLine = components[0].blocksPerLine;
871 | mcusPerRow = components[0].blocksPerColumn;
872 | mcuExpected = mcusPerRow * mcusPerLine;
873 |
874 | if (!resetInterval) resetInterval = mcuExpected;
875 | n = resetInterval;
876 | components[0].pred = 0;
877 | eobrun = 0;
878 |
879 | for (y = 0; y < mcusPerRow; y++) {
880 | for (x = 0; x < mcusPerLine; x++) {
881 | if (!n) {
882 | n = resetInterval;
883 | components[0].pred = 0;
884 | eobrun = 0;
885 |
886 | // find marker
887 | offset -= (bitsCount / 8) | 0;
888 | if (data[offset - 1] == 0xFF) offset--;
889 | bitsCount = 0;
890 | marker = (data[offset] << 8) | data[offset + 1];
891 |
892 | if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
893 | offset += 2;
894 | } else {
895 | if (marker <= 0xFF00) {
896 | throw "bad jpeg";
897 | }
898 | break;
899 | }
900 | }
901 | n--;
902 | for (i = 0; i < components.length; i++) {
903 | decodeFn(components[i], (y * components[i].blocksPerLineForMcu + x) * 64);
904 | }
905 |
906 | }
907 | }
908 | } else {
909 | mcusPerLine = frame.mcusPerLine;
910 | mcusPerRow = frame.mcusPerColumn;
911 | mcuExpected = mcusPerRow * mcusPerLine;
912 |
913 | if (!resetInterval) resetInterval = mcuExpected;
914 | n = resetInterval;
915 | for (i = 0; i < components.length; i++) components[i].pred = 0;
916 | eobrun = 0;
917 |
918 | for (y = 0; y < mcusPerRow; y++) {
919 | for (x = 0; x < mcusPerLine; x++) {
920 | if (!n) {
921 | n = resetInterval;
922 | for (i = 0; i < components.length; i++) components[i].pred = 0;
923 | eobrun = 0;
924 |
925 | // find marker
926 | offset -= (bitsCount / 8) | 0;
927 | if (data[offset - 1] == 0xFF) offset--;
928 | bitsCount = 0;
929 | marker = (data[offset] << 8) | data[offset + 1];
930 |
931 | if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
932 | offset += 2;
933 | } else {
934 | if (marker <= 0xFF00) {
935 | throw "bad jpeg";
936 | }
937 | break;
938 | }
939 | }
940 | n--;
941 | for (i = 0; i < components.length; i++) {
942 | for (j = 0; j < components[i].v; j++) {
943 | for (k = 0; k < components[i].h; k++) {
944 | decodeFn(components[i], ((y * components[i].v + j) * components[i].blocksPerLineForMcu + x * components[i].h + k) * 64);
945 | }
946 | }
947 | }
948 | }
949 | }
950 | }
951 | offset -= (bitsCount / 8) | 0;
952 | if (data[offset - 1] == 0xFF) offset--;
953 | return offset - startOffset;
954 | }
955 |
956 | function readUint16() {
957 | var value = (data[offset] << 8) | data[offset + 1];
958 | offset += 2;
959 | return value;
960 | }
961 |
962 | function readDataBlock() {
963 | var length = readUint16();
964 | var array = data.subarray(offset, offset + length - 2);
965 | offset += array.length;
966 | return array;
967 | }
968 |
969 | this['_raw'] = data;
970 | this['jfif'] = null;
971 | this['APPn'] = [];
972 | this['qts'] = [];
973 | this['frame'] = null;
974 | this['tail'] = null;
975 |
976 | var markerHi, markerLo, i, j, resetInterval, component;
977 | var huffmanTablesAC = [],
978 | huffmanTablesDC = [];
979 |
980 | while (1) {
981 | if (offset >= data.length) throw "unexpected EOF";
982 |
983 | markerHi = data[offset++];
984 | markerLo = data[offset++];
985 |
986 | if (markerHi == 0xFF) {
987 | if (markerLo == 0xE0) { //APP0 - JFIF header
988 | this.jfif = readDataBlock();
989 | }
990 |
991 | if ((markerLo > 0xE0 && markerLo < 0xF0) || markerLo == 0xFE) { //APPn + COM
992 | this.APPn.push({
993 | 'app': markerLo,
994 | 'data': readDataBlock()
995 | });
996 | }
997 |
998 | if (markerLo == 0xDB) { // DQT (Define Quantization Tables)
999 | this.qts.push(readDataBlock());
1000 | }
1001 |
1002 | if (markerLo >= 0xC0 && markerLo <= 0xC2) {
1003 | // SOF0 (Start of Frame, Baseline DCT)
1004 | // SOF1 (Start of Frame, Extended DCT)
1005 | // SOF2 (Start of Frame, Progressive DCT)
1006 | if (this.frame) throw "Only single frame JPEGs supported";
1007 | readUint16(); // skip data length
1008 |
1009 | this.frame = {
1010 | 'extended': (markerLo === 0xC1),
1011 | 'progressive': (markerLo === 0xC2),
1012 | 'precision': data[offset++],
1013 | 'scanLines': readUint16(),
1014 | 'samplesPerLine': readUint16(),
1015 | 'components': [],
1016 | 'componentIds': {},
1017 | 'maxH': 1,
1018 | 'maxV': 1
1019 | };
1020 |
1021 | if (this.frame.scanLines * this.frame.samplesPerLine > this.maxPixels) throw "Image is too big.";
1022 |
1023 | var componentsCount = data[offset++],
1024 | componentId;
1025 | var maxH = 0,
1026 | maxV = 0;
1027 | for (i = 0; i < componentsCount; i++) {
1028 | componentId = data[offset];
1029 | var h = data[offset + 1] >> 4;
1030 | var v = data[offset + 1] & 15;
1031 | if (maxH < h) maxH = h;
1032 | if (maxV < v) maxV = v;
1033 | var qId = data[offset + 2];
1034 | var l = this.frame.components.push({
1035 | 'componentId': componentId,
1036 | 'h': h,
1037 | 'v': v,
1038 | 'quantizationTable': qId
1039 | });
1040 | this.frame.componentIds[componentId] = l - 1;
1041 | offset += 3;
1042 | }
1043 | this.frame.maxH = maxH;
1044 | this.frame.maxV = maxV;
1045 |
1046 | var mcusPerLine = Math.ceil(this.frame.samplesPerLine / 8 / maxH);
1047 | var mcusPerColumn = Math.ceil(this.frame.scanLines / 8 / maxV);
1048 | for (i = 0; i < this.frame.components.length; i++) {
1049 | component = this.frame.components[i];
1050 | var blocksPerLine = Math.ceil(Math.ceil(this.frame.samplesPerLine / 8) * component.h / maxH);
1051 | var blocksPerColumn = Math.ceil(Math.ceil(this.frame.scanLines / 8) * component.v / maxV);
1052 | var blocksPerLineForMcu = mcusPerLine * component.h;
1053 | var blocksPerColumnForMcu = mcusPerColumn * component.v;
1054 |
1055 | component['blocks'] = new Int16Array(blocksPerColumnForMcu * blocksPerLineForMcu * 64);
1056 | component['blocksDC'] = new Int16Array(blocksPerColumnForMcu * blocksPerLineForMcu);
1057 | component['blocksPerLine'] = blocksPerLine;
1058 | component['blocksPerColumn'] = blocksPerColumn;
1059 | component['blocksPerLineForMcu'] = blocksPerLineForMcu;
1060 | component['blocksPerColumnForMcu'] = blocksPerColumnForMcu;
1061 | }
1062 | this.frame['mcusPerLine'] = mcusPerLine;
1063 | this.frame['mcusPerColumn'] = mcusPerColumn;
1064 | }
1065 |
1066 | if (markerLo == 0xC4) { // DHT (Define Huffman Tables)
1067 | var huffmanLength = readUint16();
1068 | for (i = 2; i < huffmanLength;) {
1069 | var huffmanTableSpec = data[offset++];
1070 | var codeLengths = new Uint8Array(16);
1071 | var codeLengthSum = 0;
1072 | for (j = 0; j < 16; j++, offset++)
1073 | codeLengthSum += (codeLengths[j] = data[offset]);
1074 | var huffmanValues = new Uint8Array(codeLengthSum);
1075 | for (j = 0; j < codeLengthSum; j++, offset++)
1076 | huffmanValues[j] = data[offset];
1077 | i += 17 + codeLengthSum;
1078 | ((huffmanTableSpec >> 4) === 0 ?
1079 | huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = _buildHuffmanTable(codeLengths, huffmanValues);
1080 | }
1081 | }
1082 |
1083 | if (markerLo == 0xDD) { // DRI (Define Restart Interval)
1084 | resetInterval = readUint16();
1085 | }
1086 |
1087 | if (markerLo == 0xDA) { // SOS (Start of Scan)
1088 | readUint16();
1089 | var selectorsCount = data[offset++];
1090 | var components = [];
1091 |
1092 | for (i = 0; i < selectorsCount; i++) {
1093 | var componentIndex = this.frame.componentIds[data[offset++]];
1094 | component = this.frame.components[componentIndex];
1095 | var tableSpec = data[offset++];
1096 | component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
1097 | component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
1098 | components.push(component);
1099 | }
1100 |
1101 | var spectralStart = data[offset++];
1102 | var spectralEnd = data[offset++];
1103 | var successiveApproximation = data[offset++];
1104 | var processed = decodeScan(data, offset,
1105 | this.frame, components, resetInterval,
1106 | spectralStart, spectralEnd,
1107 | successiveApproximation >> 4, successiveApproximation & 15);
1108 | offset += processed;
1109 | }
1110 |
1111 | if (markerLo == 0xD9) { // EOI (End of image)
1112 | break;
1113 | }
1114 | } else {
1115 | if (data[offset - 3] == 0xFF &&
1116 | data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
1117 | // could be incorrect encoding -- last 0xFF byte of the previous
1118 | // block was eaten by the encoder
1119 | offset -= 3;
1120 | }
1121 | while (data[offset] != 0xFF && offset < data.length) {
1122 | // file could be damaged and have some extra data between blocks
1123 | offset++;
1124 | }
1125 |
1126 | if (data[offset] != 0xFF) {
1127 | throw "bad jpeg ";
1128 | }
1129 | }
1130 | }
1131 |
1132 | if (!this.frame) throw 'bad jpeg';
1133 |
1134 | if (offset < data.length) this.tail = data.subarray(offset);
1135 |
1136 | return this;
1137 | };
1138 |
1139 | // Standard Huffman tables for coder initialization
1140 | // ===========================================================================================================
1141 | var bitcode = new Array(65535),
1142 | category = new Array(65535),
1143 | std_dc_luminance_nrcodes = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
1144 | std_dc_luminance_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
1145 | std_ac_luminance_nrcodes = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d],
1146 | std_ac_luminance_values = [
1147 | 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
1148 | 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
1149 | 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
1150 | 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
1151 | 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
1152 | 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
1153 | 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
1154 | 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1155 | 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
1156 | 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1157 | 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
1158 | 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
1159 | 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
1160 | 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
1161 | 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
1162 | 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
1163 | 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
1164 | 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
1165 | 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
1166 | 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
1167 | 0xf9, 0xfa
1168 | ],
1169 | std_dc_chrominance_nrcodes = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
1170 | std_dc_chrominance_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
1171 | std_ac_chrominance_nrcodes = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77],
1172 | std_ac_chrominance_values = [
1173 | 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
1174 | 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
1175 | 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
1176 | 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
1177 | 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
1178 | 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
1179 | 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
1180 | 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
1181 | 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
1182 | 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
1183 | 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
1184 | 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
1185 | 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
1186 | 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
1187 | 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
1188 | 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
1189 | 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
1190 | 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
1191 | 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
1192 | 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
1193 | 0xf9, 0xfa
1194 | ];
1195 |
1196 | function _initCategoryNumber() {
1197 | var nrlower = 1;
1198 | var nrupper = 2;
1199 | for (var cat = 1; cat <= 15; cat++) {
1200 | //Positive numbers
1201 | for (var nr = nrlower; nr < nrupper; nr++) {
1202 | category[32767 + nr] = cat;
1203 | bitcode[32767 + nr] = [];
1204 | bitcode[32767 + nr][1] = cat;
1205 | bitcode[32767 + nr][0] = nr;
1206 | }
1207 | //Negative numbers
1208 | for (var nrneg = -(nrupper - 1); nrneg <= -nrlower; nrneg++) {
1209 | category[32767 + nrneg] = cat;
1210 | bitcode[32767 + nrneg] = [];
1211 | bitcode[32767 + nrneg][1] = cat;
1212 | bitcode[32767 + nrneg][0] = nrupper - 1 + nrneg;
1213 | }
1214 | nrlower <<= 1;
1215 | nrupper <<= 1;
1216 | }
1217 | }
1218 |
1219 | _initCategoryNumber();
1220 |
1221 | function _computeHuffmanTbl(nrcodes, std_table) {
1222 | var codevalue = 0;
1223 | var pos_in_table = 0;
1224 | var HT = [];
1225 | for (var k = 1; k <= 16; k++) {
1226 | for (var j = 1; j <= nrcodes[k]; j++) {
1227 | HT[std_table[pos_in_table]] = [];
1228 | HT[std_table[pos_in_table]][0] = codevalue;
1229 | HT[std_table[pos_in_table]][1] = k;
1230 | pos_in_table++;
1231 | codevalue++;
1232 | }
1233 | codevalue *= 2;
1234 | }
1235 | return HT;
1236 | }
1237 |
1238 | var YDC_HT = _computeHuffmanTbl(std_dc_luminance_nrcodes, std_dc_luminance_values),
1239 | UVDC_HT = _computeHuffmanTbl(std_dc_chrominance_nrcodes, std_dc_chrominance_values),
1240 | YAC_HT = _computeHuffmanTbl(std_ac_luminance_nrcodes, std_ac_luminance_values),
1241 | UVAC_HT = _computeHuffmanTbl(std_ac_chrominance_nrcodes, std_ac_chrominance_values);
1242 |
1243 | // JPEG encoder
1244 | // ===========================================================================================================
1245 | f5stego.prototype.pack = function() {
1246 | var byteout, bytenew, bytepos, poslast, outpos, byte;
1247 |
1248 | // IO functions
1249 | function writeByte(value) {
1250 | var t;
1251 |
1252 | byteout[outpos++] = value;
1253 | if (outpos > poslast) {
1254 | t = new Uint8Array(byteout.length * 2);
1255 | t.set(byteout);
1256 | byteout = t;
1257 | poslast = t.length - 128;
1258 | }
1259 | }
1260 |
1261 | function writeWord(value) {
1262 | writeByte((value >> 8) & 0xFF);
1263 | writeByte((value) & 0xFF);
1264 | }
1265 |
1266 | function writeBlock(block) {
1267 | var t;
1268 | if (outpos + block.length > poslast) {
1269 | t = new Uint8Array(byteout.length * 2 + block.length);
1270 | t.set(byteout);
1271 | byteout = t;
1272 | poslast = t.length - 128;
1273 | }
1274 |
1275 | byteout.set(block, outpos);
1276 | outpos += block.length;
1277 | }
1278 |
1279 | function writeAPP0(self) {
1280 | writeWord(0xFFE0); // marker
1281 | if (!self.jfif) {
1282 | writeWord(16); // length
1283 | writeByte(0x4A); // J
1284 | writeByte(0x46); // F
1285 | writeByte(0x49); // I
1286 | writeByte(0x46); // F
1287 | writeByte(0); // = "JFIF",'\0'
1288 | writeByte(1); // versionhi
1289 | writeByte(1); // versionlo
1290 | writeByte(0); // xyunits
1291 | writeWord(1); // xdensity
1292 | writeWord(1); // ydensity
1293 | writeByte(0); // thumbnwidth
1294 | writeByte(0); // thumbnheight
1295 | } else {
1296 | writeWord(self.jfif.length + 2); // length
1297 | writeBlock(self.jfif);
1298 | }
1299 | }
1300 |
1301 | function writeDQT(self) {
1302 | for (var i = 0; i < self.qts.length; i++) {
1303 | writeWord(0xFFDB); // marker
1304 | writeWord(self.qts[i].length + 2); // length
1305 | writeBlock(self.qts[i]);
1306 | }
1307 | }
1308 |
1309 | function writeAPPn(self) {
1310 | for (var i = 0; i < self.APPn.length; i++) {
1311 | writeWord(0xFF00 | self.APPn[i].app);
1312 | writeWord(self.APPn[i].data.length + 2);
1313 | writeBlock(self.APPn[i].data);
1314 | }
1315 | }
1316 |
1317 | function writeSOF0(self) {
1318 | writeWord(0xFFC0); // marker
1319 | writeWord(8 + self.frame.components.length * 3); // length
1320 | writeByte(self.frame.precision); // precision
1321 | writeWord(self.frame.scanLines);
1322 | writeWord(self.frame.samplesPerLine);
1323 | writeByte(self.frame.components.length); // nrofcomponents
1324 |
1325 | for (var i = 0; i < self.frame.components.length; i++) {
1326 | var c = self.frame.components[i];
1327 | writeByte(c.componentId);
1328 | writeByte(c.h << 4 | c.v);
1329 | writeByte(c.quantizationTable);
1330 | }
1331 | }
1332 |
1333 | function writeDHT(self) {
1334 | writeWord(0xFFC4); // marker
1335 | writeWord(31); // length
1336 | writeByte(0); // HTYDCinfo
1337 | for (var i = 0; i < 16; i++) {
1338 | writeByte(std_dc_luminance_nrcodes[i + 1]);
1339 | }
1340 | for (var j = 0; j <= 11; j++) {
1341 | writeByte(std_dc_luminance_values[j]);
1342 | }
1343 |
1344 | writeWord(0xFFC4); // marker
1345 | writeWord(181); // length
1346 | writeByte(0x10); // HTYACinfo
1347 | for (var k = 0; k < 16; k++) {
1348 | writeByte(std_ac_luminance_nrcodes[k + 1]);
1349 | }
1350 | for (var l = 0; l <= 161; l++) {
1351 | writeByte(std_ac_luminance_values[l]);
1352 | }
1353 |
1354 | if (self.frame.components.length != 1) {
1355 | writeWord(0xFFC4); // marker
1356 | writeWord(31); // length
1357 | writeByte(1); // HTUDCinfo
1358 | for (var m = 0; m < 16; m++) {
1359 | writeByte(std_dc_chrominance_nrcodes[m + 1]);
1360 | }
1361 | for (var n = 0; n <= 11; n++) {
1362 | writeByte(std_dc_chrominance_values[n]);
1363 | }
1364 |
1365 | writeWord(0xFFC4); // marker
1366 | writeWord(181); // length
1367 | writeByte(0x11); // HTUACinfo
1368 | for (var o = 0; o < 16; o++) {
1369 | writeByte(std_ac_chrominance_nrcodes[o + 1]);
1370 | }
1371 | for (var p = 0; p <= 161; p++) {
1372 | writeByte(std_ac_chrominance_values[p]);
1373 | }
1374 | }
1375 | }
1376 |
1377 | function writeSOS(self) {
1378 | writeWord(0xFFDA); // marker
1379 | writeWord(6 + self.frame.components.length * 2); // length
1380 | writeByte(self.frame.components.length); // nrofcomponents
1381 |
1382 | for (var i = 0; i < self.frame.components.length; i++) {
1383 | var c = self.frame.components[i];
1384 | writeByte(c.componentId);
1385 | if (i === 0) {
1386 | writeByte(0);
1387 | } else {
1388 | writeByte(0x11);
1389 | }
1390 | }
1391 |
1392 | writeByte(0); // Ss
1393 | writeByte(0x3f); // Se
1394 | writeByte(0); // Bf
1395 | }
1396 |
1397 | function processDU(comp, POS, DC, HTDC, HTAC) {
1398 | var pos, posval, t;
1399 |
1400 | if (bytepos === 0) bytenew = 0;
1401 |
1402 | var Diff = comp.blocksDC[POS >> 6] - DC;
1403 | DC = comp.blocksDC[POS >> 6];
1404 | //Encode DC
1405 | if (Diff === 0) {
1406 | posval = HTDC[0][1];
1407 |
1408 | bytenew <<= posval;
1409 | bytenew += HTDC[0][0];
1410 | bytepos += posval;
1411 |
1412 | while (bytepos > 7) {
1413 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1414 | byteout[outpos++] = byte;
1415 | if (byte == 0xFF) {
1416 | outpos++;
1417 | }
1418 | bytepos -= 8;
1419 | bytenew &= (1 << bytepos) - 1;
1420 | }
1421 |
1422 | } else {
1423 | pos = 32767 + Diff;
1424 |
1425 | posval = HTDC[category[pos]][1];
1426 | bytenew <<= posval;
1427 | bytenew += HTDC[category[pos]][0];
1428 | bytepos += posval;
1429 |
1430 | posval = bitcode[pos][1];
1431 | bytenew <<= posval;
1432 | bytenew += bitcode[pos][0];
1433 | bytepos += posval;
1434 |
1435 | while (bytepos > 7) {
1436 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1437 | byteout[outpos++] = byte;
1438 | if (byte == 0xFF) {
1439 | outpos++;
1440 | }
1441 | bytepos -= 8;
1442 | bytenew &= (1 << bytepos) - 1;
1443 | }
1444 | }
1445 | //Encode ACs
1446 | var end0pos = 63; // was const... which is crazy
1447 | for (;
1448 | (end0pos > 0) && (comp.blocks[POS + end0pos] === 0); end0pos--) {}
1449 | //end0pos = first element in reverse order !=0
1450 | if (end0pos === 0) {
1451 | posval = HTAC[0x00][1];
1452 | bytenew <<= posval;
1453 | bytenew += HTAC[0x00][0];
1454 | bytepos += posval;
1455 |
1456 | while (bytepos > 7) {
1457 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1458 | byteout[outpos++] = byte;
1459 | if (byte == 0xFF) {
1460 | outpos++;
1461 | }
1462 | bytepos -= 8;
1463 | bytenew &= (1 << bytepos) - 1;
1464 | }
1465 | return DC;
1466 | }
1467 | var i = 1;
1468 | var lng;
1469 | while (i <= end0pos) {
1470 | var startpos = i;
1471 | for (;
1472 | (comp.blocks[POS + i] === 0) && (i <= end0pos); ++i) {}
1473 | var nrzeroes = i - startpos;
1474 | if (nrzeroes >= 16) {
1475 | lng = nrzeroes >> 4;
1476 | for (var nrmarker = 1; nrmarker <= lng; ++nrmarker) {
1477 | posval = HTAC[0xF0][1];
1478 | bytenew <<= posval;
1479 | bytenew += HTAC[0xF0][0];
1480 | bytepos += posval;
1481 |
1482 | while (bytepos > 7) {
1483 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1484 | byteout[outpos++] = byte;
1485 | if (byte == 0xFF) {
1486 | outpos++;
1487 | }
1488 | bytepos -= 8;
1489 | bytenew &= (1 << bytepos) - 1;
1490 | }
1491 | }
1492 | nrzeroes = nrzeroes & 0xF;
1493 | }
1494 | pos = 32767 + comp.blocks[POS + i];
1495 |
1496 | posval = HTAC[(nrzeroes << 4) + category[pos]][1];
1497 | bytenew <<= posval;
1498 | bytenew += HTAC[(nrzeroes << 4) + category[pos]][0];
1499 | bytepos += posval;
1500 |
1501 | while (bytepos > 7) {
1502 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1503 | byteout[outpos++] = byte;
1504 | if (byte == 0xFF) {
1505 | outpos++;
1506 | }
1507 | bytepos -= 8;
1508 | bytenew &= (1 << bytepos) - 1;
1509 | }
1510 |
1511 | posval = bitcode[pos][1];
1512 | bytenew <<= posval;
1513 | bytenew += bitcode[pos][0];
1514 | bytepos += posval;
1515 |
1516 | while (bytepos > 7) {
1517 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1518 | byteout[outpos++] = byte;
1519 | if (byte == 0xFF) {
1520 | outpos++;
1521 | }
1522 | bytepos -= 8;
1523 | bytenew &= (1 << bytepos) - 1;
1524 | }
1525 | i++;
1526 | }
1527 | if (end0pos != 63) {
1528 | posval = HTAC[0x00][1];
1529 | bytenew <<= posval;
1530 | bytenew += HTAC[0x00][0];
1531 | bytepos += posval;
1532 |
1533 | while (bytepos > 7) {
1534 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1535 | byteout[outpos++] = byte;
1536 | if (byte == 0xFF) {
1537 | outpos++;
1538 | }
1539 | bytepos -= 8;
1540 | bytenew &= (1 << bytepos) - 1;
1541 | }
1542 | }
1543 |
1544 | if (outpos > poslast) {
1545 | t = new Uint8Array(byteout.length * 2);
1546 | t.set(byteout);
1547 | byteout = t;
1548 | poslast = t.length - 128;
1549 | }
1550 |
1551 | return DC;
1552 | }
1553 |
1554 | // Initialize bit writer
1555 | byteout = new Uint8Array(65536);
1556 | poslast = 65536 - 128;
1557 | outpos = 0;
1558 | bytenew = 0;
1559 | bytepos = 0;
1560 |
1561 | // Add JPEG headers
1562 | writeWord(0xFFD8); // SOI
1563 | writeAPP0(this);
1564 | writeAPPn(this);
1565 | writeDQT(this);
1566 | writeSOF0(this);
1567 | writeDHT(this);
1568 | writeSOS(this);
1569 |
1570 | bytenew = 0;
1571 | bytepos = 0;
1572 |
1573 | var c, mcuRow, mcuCol, blockRow, blockCol, mcu, i, v, h;
1574 |
1575 | var DCdiff = [];
1576 | for (i = 0; i < this.frame.components.length; i++) {
1577 | DCdiff.push(0);
1578 | }
1579 |
1580 | for (mcu = 0; mcu < this.frame.mcusPerLine * this.frame.mcusPerColumn; mcu++) {
1581 | mcuRow = (mcu / this.frame.mcusPerLine) | 0;
1582 | mcuCol = mcu % this.frame.mcusPerLine;
1583 | for (i = 0; i < this.frame.components.length; i++) {
1584 | c = this.frame.components[i];
1585 | for (v = 0; v < c.v; v++) {
1586 | blockRow = mcuRow * c.v + v;
1587 | for (h = 0; h < c.h; h++) {
1588 | blockCol = mcuCol * c.h + h;
1589 | if (i === 0) {
1590 | DCdiff[i] = processDU(c, (blockRow * this.frame.mcusPerLine * c.h + blockCol) * 64, DCdiff[i], YDC_HT, YAC_HT);
1591 | } else {
1592 | DCdiff[i] = processDU(c, (blockRow * this.frame.mcusPerLine * c.h + blockCol) * 64, DCdiff[i], UVDC_HT, UVAC_HT);
1593 | }
1594 | }
1595 | }
1596 | }
1597 | }
1598 |
1599 | // Write last bytes from coder
1600 | while (bytepos > 7) {
1601 | byte = 0xFF & (bytenew >>> (bytepos - 8));
1602 | byteout[outpos++] = byte;
1603 | if (byte == 0xFF) {
1604 | outpos++;
1605 | }
1606 | bytepos -= 8;
1607 | }
1608 | // And do the bit alignment of the EOI marker
1609 | if (bytepos > 0) {
1610 | bytenew <<= 8 - bytepos;
1611 | bytenew += (1 << (8 - bytepos)) - 1;
1612 | byteout[outpos++] = 0xFF & bytenew;
1613 | }
1614 |
1615 | writeWord(0xFFD9); //EOI
1616 | if (this.tail) writeBlock(this.tail);
1617 |
1618 | return byteout.slice(0, outpos);
1619 | };
1620 |
1621 | // Metadata manipulation
1622 | // ===========================================================================================================
1623 | f5stego.prototype.clearTail = function() {
1624 | if (!this.tail) return null;
1625 |
1626 | var t = this.tail;
1627 | this.tail = null;
1628 |
1629 | return t;
1630 | };
1631 |
1632 | f5stego.prototype.setTail = function(data) {
1633 | this.tail = data;
1634 | };
1635 |
1636 | f5stego.prototype.getTail = function() {
1637 | return this.tail;
1638 | };
1639 |
1640 | f5stego.prototype.clearAPPs = function() {
1641 | var t = this.APPn;
1642 | this.APPn = [];
1643 | return t;
1644 | };
1645 |
1646 | f5stego.prototype.getAPPn = function(id, remove) {
1647 | var i, t, ret = new Uint8Array(0),
1648 | n = [];
1649 |
1650 | id &= 0xFF;
1651 | if (id < 16) id += 0xE0;
1652 | if (id === 0xE0) return this.jfif;
1653 |
1654 | for (i = 0; i < this.APPn.length; i++) {
1655 | if (this.APPn[i].app == id) {
1656 | t = new Uint8Array(ret.length + this.APPn[i].data.length);
1657 | t.set(ret);
1658 | t.set(this.APPn[i].data, ret.length);
1659 | ret = t;
1660 | } else if (remove) n.push(this.APPn[i]);
1661 | }
1662 |
1663 | if (remove) this.APPn = n;
1664 |
1665 | if (ret.length === 0) return null;
1666 |
1667 | return ret;
1668 | };
1669 |
1670 | f5stego.prototype.setAPPn = function(id, data) {
1671 | var i, t, ret;
1672 |
1673 | id &= 0xFF;
1674 | if (id < 16) id += 0xE0;
1675 |
1676 | if (id === 0xE0) {
1677 | t = this.jfif;
1678 | this.jfif = data;
1679 | return t;
1680 | }
1681 |
1682 | ret = this.getAPPn(id, true);
1683 |
1684 | if (data.length < 65534) {
1685 | this.APPn.push({ 'app': id, 'data': data });
1686 | return ret;
1687 | }
1688 |
1689 | i = 0;
1690 |
1691 | while (i < data.length) {
1692 | this.APPn.push({ 'app': id, 'data': data.subarray(i, i + 65533) });
1693 | i += 65533;
1694 | }
1695 |
1696 | return ret;
1697 | };
1698 |
1699 | f5stego.prototype.strip = function() {
1700 | this.clearTail();
1701 | this.clearAPPs();
1702 | return true;
1703 | };
1704 |
1705 | // Shorthand functions to embed/extract f5 data
1706 | // ===========================================================================================================
1707 | f5stego.prototype.embed = function(image, data) {
1708 | this.parse(image).f5put(data);
1709 | return this.pack();
1710 | };
1711 |
1712 | f5stego.prototype.extract = function(image) {
1713 | return this.parse(image).f5get();
1714 | };
1715 |
1716 | return f5stego;
1717 | }));
1718 |
--------------------------------------------------------------------------------