|
@@ -21,8 +21,8 @@ if (typeof PDFJS === 'undefined') {
|
|
|
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
|
|
}
|
|
|
|
|
|
-PDFJS.version = '1.0.96';
|
|
|
-PDFJS.build = '42ed902';
|
|
|
+PDFJS.version = '1.0.99';
|
|
|
+PDFJS.build = '58f697f';
|
|
|
|
|
|
(function pdfjsWrapper() {
|
|
|
// Use strict in our context only - users might not want it
|
|
@@ -30341,6 +30341,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
return Promise.resolve(image);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
/**
|
|
|
* Decode and clamp a value. The formula is different from the spec because we
|
|
|
* don't decode to float range [0,1], we decode it in the [0,max] range.
|
|
@@ -30350,6 +30351,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
// Clamp the value to the range
|
|
|
return (value < 0 ? 0 : (value > max ? max : value));
|
|
|
}
|
|
|
+
|
|
|
function PDFImage(xref, res, image, inline, smask, mask, isMask) {
|
|
|
this.image = image;
|
|
|
var dict = image.dict;
|
|
@@ -30576,11 +30578,13 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
this.smask && this.smask.width || 0,
|
|
|
this.mask && this.mask.width || 0);
|
|
|
},
|
|
|
+
|
|
|
get drawHeight() {
|
|
|
return Math.max(this.height,
|
|
|
this.smask && this.smask.height || 0,
|
|
|
this.mask && this.mask.height || 0);
|
|
|
},
|
|
|
+
|
|
|
decodeBuffer: function PDFImage_decodeBuffer(buffer) {
|
|
|
var bpc = this.bpc;
|
|
|
var numComps = this.numComps;
|
|
@@ -30606,6 +30610,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
+
|
|
|
getComponents: function PDFImage_getComponents(buffer) {
|
|
|
var bpc = this.bpc;
|
|
|
|
|
@@ -30682,6 +30687,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
}
|
|
|
return output;
|
|
|
},
|
|
|
+
|
|
|
fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height,
|
|
|
actualHeight, image) {
|
|
|
var smask = this.smask;
|
|
@@ -30748,6 +30754,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
+
|
|
|
undoPreblend: function PDFImage_undoPreblend(buffer, width, height) {
|
|
|
var matte = this.smask && this.smask.matte;
|
|
|
if (!matte) {
|
|
@@ -30764,7 +30771,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
var alpha = buffer[i + 3];
|
|
|
if (alpha === 0) {
|
|
|
// according formula we have to get Infinity in all components
|
|
|
- // making it as white (tipical paper color) should be okay
|
|
|
+ // making it white (tipical paper color) should be okay
|
|
|
buffer[i] = 255;
|
|
|
buffer[i + 1] = 255;
|
|
|
buffer[i + 2] = 255;
|
|
@@ -30776,6 +30783,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]);
|
|
|
}
|
|
|
},
|
|
|
+
|
|
|
createImageData: function PDFImage_createImageData(forceRGBA) {
|
|
|
var drawWidth = this.drawWidth;
|
|
|
var drawHeight = this.drawHeight;
|
|
@@ -30791,7 +30799,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
|
|
|
// Rows start at byte boundary.
|
|
|
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
|
|
|
- var imgArray = this.getImageBytes(originalHeight * rowBytes);
|
|
|
+ var imgArray;
|
|
|
|
|
|
if (!forceRGBA) {
|
|
|
// If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image
|
|
@@ -30811,6 +30819,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
drawWidth === originalWidth && drawHeight === originalHeight) {
|
|
|
imgData.kind = kind;
|
|
|
|
|
|
+ imgArray = this.getImageBytes(originalHeight * rowBytes);
|
|
|
// If imgArray came from a DecodeStream, we're safe to transfer it
|
|
|
// (and thus neuter it) because it will constitute the entire
|
|
|
// DecodeStream's data. But if it came from a Stream, we need to
|
|
@@ -30826,7 +30835,14 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
return imgData;
|
|
|
}
|
|
|
}
|
|
|
+ if (this.image instanceof JpegStream) {
|
|
|
+ imgData.kind = ImageKind.RGB_24BPP;
|
|
|
+ imgData.data = this.getImageBytes(originalHeight * rowBytes,
|
|
|
+ drawWidth, drawHeight);
|
|
|
+ return imgData;
|
|
|
+ }
|
|
|
|
|
|
+ imgArray = this.getImageBytes(originalHeight * rowBytes);
|
|
|
// imgArray can be incomplete (e.g. after CCITT fax encoding).
|
|
|
var actualHeight = 0 | (imgArray.length / rowBytes *
|
|
|
drawHeight / originalHeight);
|
|
@@ -30864,6 +30880,7 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
|
|
|
return imgData;
|
|
|
},
|
|
|
+
|
|
|
fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) {
|
|
|
var numComps = this.numComps;
|
|
|
if (numComps != 1) {
|
|
@@ -30908,8 +30925,12 @@ var PDFImage = (function PDFImageClosure() {
|
|
|
buffer[i] = (scale * comps[i]) | 0;
|
|
|
}
|
|
|
},
|
|
|
- getImageBytes: function PDFImage_getImageBytes(length) {
|
|
|
+
|
|
|
+ getImageBytes: function PDFImage_getImageBytes(length,
|
|
|
+ drawWidth, drawHeight) {
|
|
|
this.image.reset();
|
|
|
+ this.image.drawWidth = drawWidth;
|
|
|
+ this.image.drawHeight = drawHeight;
|
|
|
return this.image.getBytes(length);
|
|
|
}
|
|
|
};
|
|
@@ -35763,9 +35784,8 @@ var JpegStream = (function JpegStreamClosure() {
|
|
|
jpegImage.colorTransform = this.colorTransform;
|
|
|
}
|
|
|
jpegImage.parse(this.bytes);
|
|
|
- var width = jpegImage.width;
|
|
|
- var height = jpegImage.height;
|
|
|
- var data = jpegImage.getData(width, height);
|
|
|
+ var data = jpegImage.getData(this.drawWidth, this.drawHeight,
|
|
|
+ /* forceRGBoutput = */true);
|
|
|
this.buffer = data;
|
|
|
this.bufferLength = data.length;
|
|
|
this.eof = true;
|
|
@@ -35773,6 +35793,12 @@ var JpegStream = (function JpegStreamClosure() {
|
|
|
error('JPEG error: ' + e);
|
|
|
}
|
|
|
};
|
|
|
+
|
|
|
+ JpegStream.prototype.getBytes = function JpegStream_getBytes(length) {
|
|
|
+ this.ensureBuffer();
|
|
|
+ return this.buffer;
|
|
|
+ };
|
|
|
+
|
|
|
JpegStream.prototype.getIR = function JpegStream_getIR() {
|
|
|
return PDFJS.createObjectURL(this.bytes, 'image/jpeg');
|
|
|
};
|
|
@@ -37909,4477 +37935,4524 @@ var ArithmeticDecoder = (function ArithmeticDecoderClosure() {
|
|
|
})();
|
|
|
|
|
|
|
|
|
-var JpxImage = (function JpxImageClosure() {
|
|
|
- // Table E.1
|
|
|
- var SubbandsGainLog2 = {
|
|
|
- 'LL': 0,
|
|
|
- 'LH': 1,
|
|
|
- 'HL': 1,
|
|
|
- 'HH': 2
|
|
|
- };
|
|
|
- function JpxImage() {
|
|
|
- this.failOnCorruptedImage = false;
|
|
|
- }
|
|
|
- JpxImage.prototype = {
|
|
|
- load: function JpxImage_load(url) {
|
|
|
- var xhr = new XMLHttpRequest();
|
|
|
- xhr.open('GET', url, true);
|
|
|
- xhr.responseType = 'arraybuffer';
|
|
|
- xhr.onload = (function() {
|
|
|
- // TODO catch parse error
|
|
|
- var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);
|
|
|
- this.parse(data);
|
|
|
- if (this.onload) {
|
|
|
- this.onload();
|
|
|
- }
|
|
|
- }).bind(this);
|
|
|
- xhr.send(null);
|
|
|
- },
|
|
|
- parse: function JpxImage_parse(data) {
|
|
|
+var JpegImage = (function jpegImage() {
|
|
|
+ var dctZigZag = new Int32Array([
|
|
|
+ 0,
|
|
|
+ 1, 8,
|
|
|
+ 16, 9, 2,
|
|
|
+ 3, 10, 17, 24,
|
|
|
+ 32, 25, 18, 11, 4,
|
|
|
+ 5, 12, 19, 26, 33, 40,
|
|
|
+ 48, 41, 34, 27, 20, 13, 6,
|
|
|
+ 7, 14, 21, 28, 35, 42, 49, 56,
|
|
|
+ 57, 50, 43, 36, 29, 22, 15,
|
|
|
+ 23, 30, 37, 44, 51, 58,
|
|
|
+ 59, 52, 45, 38, 31,
|
|
|
+ 39, 46, 53, 60,
|
|
|
+ 61, 54, 47,
|
|
|
+ 55, 62,
|
|
|
+ 63
|
|
|
+ ]);
|
|
|
|
|
|
- var head = readUint16(data, 0);
|
|
|
- // No box header, immediate start of codestream (SOC)
|
|
|
- if (head === 0xFF4F) {
|
|
|
- this.parseCodestream(data, 0, data.length);
|
|
|
- return;
|
|
|
- }
|
|
|
+ var dctCos1 = 4017; // cos(pi/16)
|
|
|
+ var dctSin1 = 799; // sin(pi/16)
|
|
|
+ var dctCos3 = 3406; // cos(3*pi/16)
|
|
|
+ var dctSin3 = 2276; // sin(3*pi/16)
|
|
|
+ var dctCos6 = 1567; // cos(6*pi/16)
|
|
|
+ var dctSin6 = 3784; // sin(6*pi/16)
|
|
|
+ var dctSqrt2 = 5793; // sqrt(2)
|
|
|
+ var dctSqrt1d2 = 2896; // sqrt(2) / 2
|
|
|
|
|
|
- var position = 0, length = data.length;
|
|
|
- while (position < length) {
|
|
|
- var headerSize = 8;
|
|
|
- var lbox = readUint32(data, position);
|
|
|
- var tbox = readUint32(data, position + 4);
|
|
|
- position += headerSize;
|
|
|
- if (lbox === 1) {
|
|
|
- // XLBox: read UInt64 according to spec.
|
|
|
- // JavaScript's int precision of 53 bit should be sufficient here.
|
|
|
- lbox = readUint32(data, position) * 4294967296 +
|
|
|
- readUint32(data, position + 4);
|
|
|
- position += 8;
|
|
|
- headerSize += 8;
|
|
|
- }
|
|
|
- if (lbox === 0) {
|
|
|
- lbox = length - position + headerSize;
|
|
|
- }
|
|
|
- if (lbox < headerSize) {
|
|
|
- error('JPX error: Invalid box field size');
|
|
|
- }
|
|
|
- var dataLength = lbox - headerSize;
|
|
|
- var jumpDataLength = true;
|
|
|
- switch (tbox) {
|
|
|
- case 0x6A501A1A: // 'jP\032\032'
|
|
|
- // TODO
|
|
|
- break;
|
|
|
- case 0x6A703268: // 'jp2h'
|
|
|
- jumpDataLength = false; // parsing child boxes
|
|
|
- break;
|
|
|
- case 0x636F6C72: // 'colr'
|
|
|
- // TODO
|
|
|
- break;
|
|
|
- case 0x6A703263: // 'jp2c'
|
|
|
- this.parseCodestream(data, position, position + dataLength);
|
|
|
- break;
|
|
|
+ function constructor() {
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildHuffmanTable(codeLengths, values) {
|
|
|
+ var k = 0, code = [], i, j, length = 16;
|
|
|
+ while (length > 0 && !codeLengths[length - 1]) {
|
|
|
+ length--;
|
|
|
+ }
|
|
|
+ code.push({children: [], index: 0});
|
|
|
+ var p = code[0], q;
|
|
|
+ for (i = 0; i < length; i++) {
|
|
|
+ for (j = 0; j < codeLengths[i]; j++) {
|
|
|
+ p = code.pop();
|
|
|
+ p.children[p.index] = values[k];
|
|
|
+ while (p.index > 0) {
|
|
|
+ p = code.pop();
|
|
|
}
|
|
|
- if (jumpDataLength) {
|
|
|
- position += dataLength;
|
|
|
+ p.index++;
|
|
|
+ code.push(p);
|
|
|
+ while (code.length <= i) {
|
|
|
+ code.push(q = {children: [], index: 0});
|
|
|
+ p.children[p.index] = q.children;
|
|
|
+ p = q;
|
|
|
}
|
|
|
+ k++;
|
|
|
}
|
|
|
- },
|
|
|
- parseImageProperties: function JpxImage_parseImageProperties(stream) {
|
|
|
- try {
|
|
|
- var newByte = stream.getByte();
|
|
|
- while (newByte >= 0) {
|
|
|
- var oldByte = newByte;
|
|
|
- newByte = stream.getByte();
|
|
|
- var code = (oldByte << 8) | newByte;
|
|
|
- // Image and tile size (SIZ)
|
|
|
- if (code == 0xFF51) {
|
|
|
- stream.skip(4);
|
|
|
- var Xsiz = stream.getInt32() >>> 0; // Byte 4
|
|
|
- var Ysiz = stream.getInt32() >>> 0; // Byte 8
|
|
|
- var XOsiz = stream.getInt32() >>> 0; // Byte 12
|
|
|
- var YOsiz = stream.getInt32() >>> 0; // Byte 16
|
|
|
- stream.skip(16);
|
|
|
- var Csiz = stream.getUint16(); // Byte 36
|
|
|
- this.width = Xsiz - XOsiz;
|
|
|
- this.height = Ysiz - YOsiz;
|
|
|
- this.componentsCount = Csiz;
|
|
|
- // Results are always returned as Uint8Arrays
|
|
|
- this.bitsPerComponent = 8;
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- throw 'No size marker found in JPX stream';
|
|
|
- } catch (e) {
|
|
|
- if (this.failOnCorruptedImage) {
|
|
|
- error('JPX error: ' + e);
|
|
|
- } else {
|
|
|
- warn('JPX error: ' + e + '. Trying to recover');
|
|
|
- }
|
|
|
+ if (i + 1 < length) {
|
|
|
+ // p here points to last code
|
|
|
+ code.push(q = {children: [], index: 0});
|
|
|
+ p.children[p.index] = q.children;
|
|
|
+ p = q;
|
|
|
}
|
|
|
- },
|
|
|
- parseCodestream: function JpxImage_parseCodestream(data, start, end) {
|
|
|
- var context = {};
|
|
|
- try {
|
|
|
- var position = start;
|
|
|
- while (position + 1 < end) {
|
|
|
- var code = readUint16(data, position);
|
|
|
- position += 2;
|
|
|
-
|
|
|
- var length = 0, j, sqcd, spqcds, spqcdSize, scalarExpounded, tile;
|
|
|
- switch (code) {
|
|
|
- case 0xFF4F: // Start of codestream (SOC)
|
|
|
- context.mainHeader = true;
|
|
|
- break;
|
|
|
- case 0xFFD9: // End of codestream (EOC)
|
|
|
- break;
|
|
|
- case 0xFF51: // Image and tile size (SIZ)
|
|
|
- length = readUint16(data, position);
|
|
|
- var siz = {};
|
|
|
- siz.Xsiz = readUint32(data, position + 4);
|
|
|
- siz.Ysiz = readUint32(data, position + 8);
|
|
|
- siz.XOsiz = readUint32(data, position + 12);
|
|
|
- siz.YOsiz = readUint32(data, position + 16);
|
|
|
- siz.XTsiz = readUint32(data, position + 20);
|
|
|
- siz.YTsiz = readUint32(data, position + 24);
|
|
|
- siz.XTOsiz = readUint32(data, position + 28);
|
|
|
- siz.YTOsiz = readUint32(data, position + 32);
|
|
|
- var componentsCount = readUint16(data, position + 36);
|
|
|
- siz.Csiz = componentsCount;
|
|
|
- var components = [];
|
|
|
- j = position + 38;
|
|
|
- for (var i = 0; i < componentsCount; i++) {
|
|
|
- var component = {
|
|
|
- precision: (data[j] & 0x7F) + 1,
|
|
|
- isSigned: !!(data[j] & 0x80),
|
|
|
- XRsiz: data[j + 1],
|
|
|
- YRsiz: data[j + 1]
|
|
|
- };
|
|
|
- calculateComponentDimensions(component, siz);
|
|
|
- components.push(component);
|
|
|
- }
|
|
|
- context.SIZ = siz;
|
|
|
- context.components = components;
|
|
|
- calculateTileGrids(context, components);
|
|
|
- context.QCC = [];
|
|
|
- context.COC = [];
|
|
|
- break;
|
|
|
- case 0xFF5C: // Quantization default (QCD)
|
|
|
- length = readUint16(data, position);
|
|
|
- var qcd = {};
|
|
|
- j = position + 2;
|
|
|
- sqcd = data[j++];
|
|
|
- switch (sqcd & 0x1F) {
|
|
|
- case 0:
|
|
|
- spqcdSize = 8;
|
|
|
- scalarExpounded = true;
|
|
|
- break;
|
|
|
- case 1:
|
|
|
- spqcdSize = 16;
|
|
|
- scalarExpounded = false;
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- spqcdSize = 16;
|
|
|
- scalarExpounded = true;
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw 'Invalid SQcd value ' + sqcd;
|
|
|
- }
|
|
|
- qcd.noQuantization = (spqcdSize == 8);
|
|
|
- qcd.scalarExpounded = scalarExpounded;
|
|
|
- qcd.guardBits = sqcd >> 5;
|
|
|
- spqcds = [];
|
|
|
- while (j < length + position) {
|
|
|
- var spqcd = {};
|
|
|
- if (spqcdSize == 8) {
|
|
|
- spqcd.epsilon = data[j++] >> 3;
|
|
|
- spqcd.mu = 0;
|
|
|
- } else {
|
|
|
- spqcd.epsilon = data[j] >> 3;
|
|
|
- spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1];
|
|
|
- j += 2;
|
|
|
- }
|
|
|
- spqcds.push(spqcd);
|
|
|
- }
|
|
|
- qcd.SPqcds = spqcds;
|
|
|
- if (context.mainHeader) {
|
|
|
- context.QCD = qcd;
|
|
|
- } else {
|
|
|
- context.currentTile.QCD = qcd;
|
|
|
- context.currentTile.QCC = [];
|
|
|
- }
|
|
|
- break;
|
|
|
- case 0xFF5D: // Quantization component (QCC)
|
|
|
- length = readUint16(data, position);
|
|
|
- var qcc = {};
|
|
|
- j = position + 2;
|
|
|
- var cqcc;
|
|
|
- if (context.SIZ.Csiz < 257) {
|
|
|
- cqcc = data[j++];
|
|
|
- } else {
|
|
|
- cqcc = readUint16(data, j);
|
|
|
- j += 2;
|
|
|
- }
|
|
|
- sqcd = data[j++];
|
|
|
- switch (sqcd & 0x1F) {
|
|
|
- case 0:
|
|
|
- spqcdSize = 8;
|
|
|
- scalarExpounded = true;
|
|
|
- break;
|
|
|
- case 1:
|
|
|
- spqcdSize = 16;
|
|
|
- scalarExpounded = false;
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- spqcdSize = 16;
|
|
|
- scalarExpounded = true;
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw 'Invalid SQcd value ' + sqcd;
|
|
|
- }
|
|
|
- qcc.noQuantization = (spqcdSize == 8);
|
|
|
- qcc.scalarExpounded = scalarExpounded;
|
|
|
- qcc.guardBits = sqcd >> 5;
|
|
|
- spqcds = [];
|
|
|
- while (j < (length + position)) {
|
|
|
- spqcd = {};
|
|
|
- if (spqcdSize == 8) {
|
|
|
- spqcd.epsilon = data[j++] >> 3;
|
|
|
- spqcd.mu = 0;
|
|
|
- } else {
|
|
|
- spqcd.epsilon = data[j] >> 3;
|
|
|
- spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1];
|
|
|
- j += 2;
|
|
|
- }
|
|
|
- spqcds.push(spqcd);
|
|
|
- }
|
|
|
- qcc.SPqcds = spqcds;
|
|
|
- if (context.mainHeader) {
|
|
|
- context.QCC[cqcc] = qcc;
|
|
|
- } else {
|
|
|
- context.currentTile.QCC[cqcc] = qcc;
|
|
|
- }
|
|
|
- break;
|
|
|
- case 0xFF52: // Coding style default (COD)
|
|
|
- length = readUint16(data, position);
|
|
|
- var cod = {};
|
|
|
- j = position + 2;
|
|
|
- var scod = data[j++];
|
|
|
- cod.entropyCoderWithCustomPrecincts = !!(scod & 1);
|
|
|
- cod.sopMarkerUsed = !!(scod & 2);
|
|
|
- cod.ephMarkerUsed = !!(scod & 4);
|
|
|
- cod.progressionOrder = data[j++];
|
|
|
- cod.layersCount = readUint16(data, j);
|
|
|
- j += 2;
|
|
|
- cod.multipleComponentTransform = data[j++];
|
|
|
+ }
|
|
|
+ return code[0].children;
|
|
|
+ }
|
|
|
|
|
|
- cod.decompositionLevelsCount = data[j++];
|
|
|
- cod.xcb = (data[j++] & 0xF) + 2;
|
|
|
- cod.ycb = (data[j++] & 0xF) + 2;
|
|
|
- var blockStyle = data[j++];
|
|
|
- cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1);
|
|
|
- cod.resetContextProbabilities = !!(blockStyle & 2);
|
|
|
- cod.terminationOnEachCodingPass = !!(blockStyle & 4);
|
|
|
- cod.verticalyStripe = !!(blockStyle & 8);
|
|
|
- cod.predictableTermination = !!(blockStyle & 16);
|
|
|
- cod.segmentationSymbolUsed = !!(blockStyle & 32);
|
|
|
- cod.reversibleTransformation = data[j++];
|
|
|
- if (cod.entropyCoderWithCustomPrecincts) {
|
|
|
- var precinctsSizes = [];
|
|
|
- while (j < length + position) {
|
|
|
- var precinctsSize = data[j++];
|
|
|
- precinctsSizes.push({
|
|
|
- PPx: precinctsSize & 0xF,
|
|
|
- PPy: precinctsSize >> 4
|
|
|
- });
|
|
|
- }
|
|
|
- cod.precinctsSizes = precinctsSizes;
|
|
|
- }
|
|
|
+ function getBlockBufferOffset(component, row, col) {
|
|
|
+ return 64 * ((component.blocksPerLine + 1) * row + col);
|
|
|
+ }
|
|
|
|
|
|
- if (cod.sopMarkerUsed || cod.ephMarkerUsed ||
|
|
|
- cod.selectiveArithmeticCodingBypass ||
|
|
|
- cod.resetContextProbabilities ||
|
|
|
- cod.terminationOnEachCodingPass ||
|
|
|
- cod.verticalyStripe || cod.predictableTermination) {
|
|
|
- throw 'Unsupported COD options: ' +
|
|
|
- globalScope.JSON.stringify(cod);
|
|
|
- }
|
|
|
+ function decodeScan(data, offset,
|
|
|
+ frame, components, resetInterval,
|
|
|
+ spectralStart, spectralEnd,
|
|
|
+ successivePrev, successive) {
|
|
|
+ var precision = frame.precision;
|
|
|
+ var samplesPerLine = frame.samplesPerLine;
|
|
|
+ var scanLines = frame.scanLines;
|
|
|
+ var mcusPerLine = frame.mcusPerLine;
|
|
|
+ var progressive = frame.progressive;
|
|
|
+ var maxH = frame.maxH, maxV = frame.maxV;
|
|
|
|
|
|
- if (context.mainHeader) {
|
|
|
- context.COD = cod;
|
|
|
- } else {
|
|
|
- context.currentTile.COD = cod;
|
|
|
- context.currentTile.COC = [];
|
|
|
- }
|
|
|
- break;
|
|
|
- case 0xFF90: // Start of tile-part (SOT)
|
|
|
- length = readUint16(data, position);
|
|
|
- tile = {};
|
|
|
- tile.index = readUint16(data, position + 2);
|
|
|
- tile.length = readUint32(data, position + 4);
|
|
|
- tile.dataEnd = tile.length + position - 2;
|
|
|
- tile.partIndex = data[position + 8];
|
|
|
- tile.partsCount = data[position + 9];
|
|
|
+ var startOffset = offset, bitsData = 0, bitsCount = 0;
|
|
|
|
|
|
- context.mainHeader = false;
|
|
|
- if (tile.partIndex === 0) {
|
|
|
- // reset component specific settings
|
|
|
- tile.COD = context.COD;
|
|
|
- tile.COC = context.COC.slice(0); // clone of the global COC
|
|
|
- tile.QCD = context.QCD;
|
|
|
- tile.QCC = context.QCC.slice(0); // clone of the global COC
|
|
|
- }
|
|
|
- context.currentTile = tile;
|
|
|
- break;
|
|
|
- case 0xFF93: // Start of data (SOD)
|
|
|
- tile = context.currentTile;
|
|
|
- if (tile.partIndex === 0) {
|
|
|
- initializeTile(context, tile.index);
|
|
|
- buildPackets(context);
|
|
|
- }
|
|
|
+ function readBit() {
|
|
|
+ if (bitsCount > 0) {
|
|
|
+ bitsCount--;
|
|
|
+ return (bitsData >> bitsCount) & 1;
|
|
|
+ }
|
|
|
+ bitsData = data[offset++];
|
|
|
+ if (bitsData == 0xFF) {
|
|
|
+ var nextByte = data[offset++];
|
|
|
+ if (nextByte) {
|
|
|
+ throw 'unexpected marker: ' +
|
|
|
+ ((bitsData << 8) | nextByte).toString(16);
|
|
|
+ }
|
|
|
+ // unstuff 0
|
|
|
+ }
|
|
|
+ bitsCount = 7;
|
|
|
+ return bitsData >>> 7;
|
|
|
+ }
|
|
|
|
|
|
- // moving to the end of the data
|
|
|
- length = tile.dataEnd - position;
|
|
|
- parseTilePackets(context, data, position, length);
|
|
|
- break;
|
|
|
- case 0xFF64: // Comment (COM)
|
|
|
- length = readUint16(data, position);
|
|
|
- // skipping content
|
|
|
- break;
|
|
|
- case 0xFF53: // Coding style component (COC)
|
|
|
- throw 'Codestream code 0xFF53 (COC) is not implemented';
|
|
|
- default:
|
|
|
- throw 'Unknown codestream code: ' + code.toString(16);
|
|
|
- }
|
|
|
- position += length;
|
|
|
+ function decodeHuffman(tree) {
|
|
|
+ var node = tree;
|
|
|
+ var bit;
|
|
|
+ while ((bit = readBit()) !== null) {
|
|
|
+ node = node[bit];
|
|
|
+ if (typeof node === 'number') {
|
|
|
+ return node;
|
|
|
}
|
|
|
- } catch (e) {
|
|
|
- if (this.failOnCorruptedImage) {
|
|
|
- error('JPX error: ' + e);
|
|
|
- } else {
|
|
|
- warn('JPX error: ' + e + '. Trying to recover');
|
|
|
+ if (typeof node !== 'object') {
|
|
|
+ throw 'invalid huffman sequence';
|
|
|
}
|
|
|
}
|
|
|
- this.tiles = transformComponents(context);
|
|
|
- this.width = context.SIZ.Xsiz - context.SIZ.XOsiz;
|
|
|
- this.height = context.SIZ.Ysiz - context.SIZ.YOsiz;
|
|
|
- this.componentsCount = context.SIZ.Csiz;
|
|
|
+ return null;
|
|
|
}
|
|
|
- };
|
|
|
- function calculateComponentDimensions(component, siz) {
|
|
|
- // Section B.2 Component mapping
|
|
|
- component.x0 = Math.ceil(siz.XOsiz / component.XRsiz);
|
|
|
- component.x1 = Math.ceil(siz.Xsiz / component.XRsiz);
|
|
|
- component.y0 = Math.ceil(siz.YOsiz / component.YRsiz);
|
|
|
- component.y1 = Math.ceil(siz.Ysiz / component.YRsiz);
|
|
|
- component.width = component.x1 - component.x0;
|
|
|
- component.height = component.y1 - component.y0;
|
|
|
- }
|
|
|
- function calculateTileGrids(context, components) {
|
|
|
- var siz = context.SIZ;
|
|
|
- // Section B.3 Division into tile and tile-components
|
|
|
- var tile, tiles = [];
|
|
|
- var numXtiles = Math.ceil((siz.Xsiz - siz.XTOsiz) / siz.XTsiz);
|
|
|
- var numYtiles = Math.ceil((siz.Ysiz - siz.YTOsiz) / siz.YTsiz);
|
|
|
- for (var q = 0; q < numYtiles; q++) {
|
|
|
- for (var p = 0; p < numXtiles; p++) {
|
|
|
- tile = {};
|
|
|
- tile.tx0 = Math.max(siz.XTOsiz + p * siz.XTsiz, siz.XOsiz);
|
|
|
- tile.ty0 = Math.max(siz.YTOsiz + q * siz.YTsiz, siz.YOsiz);
|
|
|
- tile.tx1 = Math.min(siz.XTOsiz + (p + 1) * siz.XTsiz, siz.Xsiz);
|
|
|
- tile.ty1 = Math.min(siz.YTOsiz + (q + 1) * siz.YTsiz, siz.Ysiz);
|
|
|
- tile.width = tile.tx1 - tile.tx0;
|
|
|
- tile.height = tile.ty1 - tile.ty0;
|
|
|
- tile.components = [];
|
|
|
- tiles.push(tile);
|
|
|
+
|
|
|
+ function receive(length) {
|
|
|
+ var n = 0;
|
|
|
+ while (length > 0) {
|
|
|
+ var bit = readBit();
|
|
|
+ if (bit === null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ n = (n << 1) | bit;
|
|
|
+ length--;
|
|
|
}
|
|
|
+ return n;
|
|
|
}
|
|
|
- context.tiles = tiles;
|
|
|
|
|
|
- var componentsCount = siz.Csiz;
|
|
|
- for (var i = 0, ii = componentsCount; i < ii; i++) {
|
|
|
- var component = components[i];
|
|
|
- for (var j = 0, jj = tiles.length; j < jj; j++) {
|
|
|
- var tileComponent = {};
|
|
|
- tile = tiles[j];
|
|
|
- tileComponent.tcx0 = Math.ceil(tile.tx0 / component.XRsiz);
|
|
|
- tileComponent.tcy0 = Math.ceil(tile.ty0 / component.YRsiz);
|
|
|
- tileComponent.tcx1 = Math.ceil(tile.tx1 / component.XRsiz);
|
|
|
- tileComponent.tcy1 = Math.ceil(tile.ty1 / component.YRsiz);
|
|
|
- tileComponent.width = tileComponent.tcx1 - tileComponent.tcx0;
|
|
|
- tileComponent.height = tileComponent.tcy1 - tileComponent.tcy0;
|
|
|
- tile.components[i] = tileComponent;
|
|
|
+ function receiveAndExtend(length) {
|
|
|
+ var n = receive(length);
|
|
|
+ if (n >= 1 << (length - 1)) {
|
|
|
+ return n;
|
|
|
}
|
|
|
+ return n + (-1 << length) + 1;
|
|
|
}
|
|
|
- }
|
|
|
- function getBlocksDimensions(context, component, r) {
|
|
|
- var codOrCoc = component.codingStyleParameters;
|
|
|
- var result = {};
|
|
|
- if (!codOrCoc.entropyCoderWithCustomPrecincts) {
|
|
|
- result.PPx = 15;
|
|
|
- result.PPy = 15;
|
|
|
- } else {
|
|
|
- result.PPx = codOrCoc.precinctsSizes[r].PPx;
|
|
|
- result.PPy = codOrCoc.precinctsSizes[r].PPy;
|
|
|
- }
|
|
|
- // calculate codeblock size as described in section B.7
|
|
|
- result.xcb_ = (r > 0 ? Math.min(codOrCoc.xcb, result.PPx - 1) :
|
|
|
- Math.min(codOrCoc.xcb, result.PPx));
|
|
|
- result.ycb_ = (r > 0 ? Math.min(codOrCoc.ycb, result.PPy - 1) :
|
|
|
- Math.min(codOrCoc.ycb, result.PPy));
|
|
|
- return result;
|
|
|
- }
|
|
|
- function buildPrecincts(context, resolution, dimensions) {
|
|
|
- // Section B.6 Division resolution to precincts
|
|
|
- var precinctWidth = 1 << dimensions.PPx;
|
|
|
- var precinctHeight = 1 << dimensions.PPy;
|
|
|
- var numprecinctswide = (resolution.trx1 > resolution.trx0 ?
|
|
|
- Math.ceil(resolution.trx1 / precinctWidth) -
|
|
|
- Math.floor(resolution.trx0 / precinctWidth) : 0);
|
|
|
- var numprecinctshigh = (resolution.try1 > resolution.try0 ?
|
|
|
- Math.ceil(resolution.try1 / precinctHeight) -
|
|
|
- Math.floor(resolution.try0 / precinctHeight) : 0);
|
|
|
- var numprecincts = numprecinctswide * numprecinctshigh;
|
|
|
- var precinctXOffset = Math.floor(resolution.trx0 / precinctWidth) *
|
|
|
- precinctWidth;
|
|
|
- var precinctYOffset = Math.floor(resolution.try0 / precinctHeight) *
|
|
|
- precinctHeight;
|
|
|
- resolution.precinctParameters = {
|
|
|
- precinctXOffset: precinctXOffset,
|
|
|
- precinctYOffset: precinctYOffset,
|
|
|
- precinctWidth: precinctWidth,
|
|
|
- precinctHeight: precinctHeight,
|
|
|
- numprecinctswide: numprecinctswide,
|
|
|
- numprecinctshigh: numprecinctshigh,
|
|
|
- numprecincts: numprecincts
|
|
|
- };
|
|
|
- }
|
|
|
- function buildCodeblocks(context, subband, dimensions) {
|
|
|
- // Section B.7 Division sub-band into code-blocks
|
|
|
- var xcb_ = dimensions.xcb_;
|
|
|
- var ycb_ = dimensions.ycb_;
|
|
|
- var codeblockWidth = 1 << xcb_;
|
|
|
- var codeblockHeight = 1 << ycb_;
|
|
|
- var cbx0 = subband.tbx0 >> xcb_;
|
|
|
- var cby0 = subband.tby0 >> ycb_;
|
|
|
- var cbx1 = (subband.tbx1 + codeblockWidth - 1) >> xcb_;
|
|
|
- var cby1 = (subband.tby1 + codeblockHeight - 1) >> ycb_;
|
|
|
- var precinctParameters = subband.resolution.precinctParameters;
|
|
|
- var codeblocks = [];
|
|
|
- var precincts = [];
|
|
|
- var i, j, codeblock, precinctNumber;
|
|
|
- for (j = cby0; j < cby1; j++) {
|
|
|
- for (i = cbx0; i < cbx1; i++) {
|
|
|
- codeblock = {
|
|
|
- cbx: i,
|
|
|
- cby: j,
|
|
|
- tbx0: codeblockWidth * i,
|
|
|
- tby0: codeblockHeight * j,
|
|
|
- tbx1: codeblockWidth * (i + 1),
|
|
|
- tby1: codeblockHeight * (j + 1)
|
|
|
- };
|
|
|
- // calculate precinct number
|
|
|
- var pi = Math.floor((codeblock.tbx0 -
|
|
|
- precinctParameters.precinctXOffset) /
|
|
|
- precinctParameters.precinctWidth);
|
|
|
- var pj = Math.floor((codeblock.tby0 -
|
|
|
- precinctParameters.precinctYOffset) /
|
|
|
- precinctParameters.precinctHeight);
|
|
|
- precinctNumber = pj + pi * precinctParameters.numprecinctswide;
|
|
|
- codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0);
|
|
|
- codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0);
|
|
|
- codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1);
|
|
|
- codeblock.tby1_ = Math.min(subband.tby1, codeblock.tby1);
|
|
|
- codeblock.precinctNumber = precinctNumber;
|
|
|
- codeblock.subbandType = subband.type;
|
|
|
- codeblock.Lblock = 3;
|
|
|
- codeblocks.push(codeblock);
|
|
|
- // building precinct for the sub-band
|
|
|
- var precinct = precincts[precinctNumber];
|
|
|
- if (precinct !== undefined) {
|
|
|
- if (i < precinct.cbxMin) {
|
|
|
- precinct.cbxMin = i;
|
|
|
- } else if (i > precinct.cbxMax) {
|
|
|
- precinct.cbxMax = i;
|
|
|
- }
|
|
|
- if (j < precinct.cbyMin) {
|
|
|
- precinct.cbxMin = j;
|
|
|
- } else if (j > precinct.cbyMax) {
|
|
|
- precinct.cbyMax = j;
|
|
|
+
|
|
|
+ function decodeBaseline(component, offset) {
|
|
|
+ var t = decodeHuffman(component.huffmanTableDC);
|
|
|
+ var diff = t === 0 ? 0 : receiveAndExtend(t);
|
|
|
+ component.blockData[offset] = (component.pred += diff);
|
|
|
+ var k = 1;
|
|
|
+ while (k < 64) {
|
|
|
+ var rs = decodeHuffman(component.huffmanTableAC);
|
|
|
+ var s = rs & 15, r = rs >> 4;
|
|
|
+ if (s === 0) {
|
|
|
+ if (r < 15) {
|
|
|
+ break;
|
|
|
}
|
|
|
- } else {
|
|
|
- precincts[precinctNumber] = precinct = {
|
|
|
- cbxMin: i,
|
|
|
- cbyMin: j,
|
|
|
- cbxMax: i,
|
|
|
- cbyMax: j
|
|
|
- };
|
|
|
- }
|
|
|
- codeblock.precinct = precinct;
|
|
|
- }
|
|
|
- }
|
|
|
- subband.codeblockParameters = {
|
|
|
- codeblockWidth: xcb_,
|
|
|
- codeblockHeight: ycb_,
|
|
|
- numcodeblockwide: cbx1 - cbx0 + 1,
|
|
|
- numcodeblockhigh: cby1 - cby1 + 1
|
|
|
- };
|
|
|
- subband.codeblocks = codeblocks;
|
|
|
- subband.precincts = precincts;
|
|
|
- }
|
|
|
- function createPacket(resolution, precinctNumber, layerNumber) {
|
|
|
- var precinctCodeblocks = [];
|
|
|
- // Section B.10.8 Order of info in packet
|
|
|
- var subbands = resolution.subbands;
|
|
|
- // sub-bands already ordered in 'LL', 'HL', 'LH', and 'HH' sequence
|
|
|
- for (var i = 0, ii = subbands.length; i < ii; i++) {
|
|
|
- var subband = subbands[i];
|
|
|
- var codeblocks = subband.codeblocks;
|
|
|
- for (var j = 0, jj = codeblocks.length; j < jj; j++) {
|
|
|
- var codeblock = codeblocks[j];
|
|
|
- if (codeblock.precinctNumber != precinctNumber) {
|
|
|
+ k += 16;
|
|
|
continue;
|
|
|
}
|
|
|
- precinctCodeblocks.push(codeblock);
|
|
|
+ k += r;
|
|
|
+ var z = dctZigZag[k];
|
|
|
+ component.blockData[offset + z] = receiveAndExtend(s);
|
|
|
+ k++;
|
|
|
}
|
|
|
}
|
|
|
- return {
|
|
|
- layerNumber: layerNumber,
|
|
|
- codeblocks: precinctCodeblocks
|
|
|
- };
|
|
|
- }
|
|
|
- function LayerResolutionComponentPositionIterator(context) {
|
|
|
- var siz = context.SIZ;
|
|
|
- var tileIndex = context.currentTile.index;
|
|
|
- var tile = context.tiles[tileIndex];
|
|
|
- var layersCount = tile.codingStyleDefaultParameters.layersCount;
|
|
|
- var componentsCount = siz.Csiz;
|
|
|
- var maxDecompositionLevelsCount = 0;
|
|
|
- for (var q = 0; q < componentsCount; q++) {
|
|
|
- maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount,
|
|
|
- tile.components[q].codingStyleParameters.decompositionLevelsCount);
|
|
|
- }
|
|
|
|
|
|
- var l = 0, r = 0, i = 0, k = 0;
|
|
|
+ function decodeDCFirst(component, offset) {
|
|
|
+ var t = decodeHuffman(component.huffmanTableDC);
|
|
|
+ var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive);
|
|
|
+ component.blockData[offset] = (component.pred += diff);
|
|
|
+ }
|
|
|
|
|
|
- this.nextPacket = function JpxImage_nextPacket() {
|
|
|
- // Section B.12.1.1 Layer-resolution-component-position
|
|
|
- for (; l < layersCount; l++) {
|
|
|
- for (; r <= maxDecompositionLevelsCount; r++) {
|
|
|
- for (; i < componentsCount; i++) {
|
|
|
- var component = tile.components[i];
|
|
|
- if (r > component.codingStyleParameters.decompositionLevelsCount) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ function decodeDCSuccessive(component, offset) {
|
|
|
+ component.blockData[offset] |= readBit() << successive;
|
|
|
+ }
|
|
|
|
|
|
- var resolution = component.resolutions[r];
|
|
|
- var numprecincts = resolution.precinctParameters.numprecincts;
|
|
|
- for (; k < numprecincts;) {
|
|
|
- var packet = createPacket(resolution, k, l);
|
|
|
- k++;
|
|
|
- return packet;
|
|
|
- }
|
|
|
- k = 0;
|
|
|
+ var eobrun = 0;
|
|
|
+ function decodeACFirst(component, offset) {
|
|
|
+ if (eobrun > 0) {
|
|
|
+ eobrun--;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ var k = spectralStart, e = spectralEnd;
|
|
|
+ while (k <= e) {
|
|
|
+ var rs = decodeHuffman(component.huffmanTableAC);
|
|
|
+ var s = rs & 15, r = rs >> 4;
|
|
|
+ if (s === 0) {
|
|
|
+ if (r < 15) {
|
|
|
+ eobrun = receive(r) + (1 << r) - 1;
|
|
|
+ break;
|
|
|
}
|
|
|
- i = 0;
|
|
|
+ k += 16;
|
|
|
+ continue;
|
|
|
}
|
|
|
- r = 0;
|
|
|
+ k += r;
|
|
|
+ var z = dctZigZag[k];
|
|
|
+ component.blockData[offset + z] =
|
|
|
+ receiveAndExtend(s) * (1 << successive);
|
|
|
+ k++;
|
|
|
}
|
|
|
- throw 'Out of packets';
|
|
|
- };
|
|
|
- }
|
|
|
- function ResolutionLayerComponentPositionIterator(context) {
|
|
|
- var siz = context.SIZ;
|
|
|
- var tileIndex = context.currentTile.index;
|
|
|
- var tile = context.tiles[tileIndex];
|
|
|
- var layersCount = tile.codingStyleDefaultParameters.layersCount;
|
|
|
- var componentsCount = siz.Csiz;
|
|
|
- var maxDecompositionLevelsCount = 0;
|
|
|
- for (var q = 0; q < componentsCount; q++) {
|
|
|
- maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount,
|
|
|
- tile.components[q].codingStyleParameters.decompositionLevelsCount);
|
|
|
}
|
|
|
|
|
|
- var r = 0, l = 0, i = 0, k = 0;
|
|
|
-
|
|
|
- this.nextPacket = function JpxImage_nextPacket() {
|
|
|
- // Section B.12.1.2 Resolution-layer-component-position
|
|
|
- for (; r <= maxDecompositionLevelsCount; r++) {
|
|
|
- for (; l < layersCount; l++) {
|
|
|
- for (; i < componentsCount; i++) {
|
|
|
- var component = tile.components[i];
|
|
|
- if (r > component.codingStyleParameters.decompositionLevelsCount) {
|
|
|
- continue;
|
|
|
+ var successiveACState = 0, successiveACNextValue;
|
|
|
+ function decodeACSuccessive(component, offset) {
|
|
|
+ var k = spectralStart;
|
|
|
+ var e = spectralEnd;
|
|
|
+ var r = 0;
|
|
|
+ var s;
|
|
|
+ var rs;
|
|
|
+ while (k <= e) {
|
|
|
+ var z = dctZigZag[k];
|
|
|
+ switch (successiveACState) {
|
|
|
+ case 0: // initial state
|
|
|
+ rs = decodeHuffman(component.huffmanTableAC);
|
|
|
+ s = rs & 15;
|
|
|
+ r = rs >> 4;
|
|
|
+ if (s === 0) {
|
|
|
+ if (r < 15) {
|
|
|
+ eobrun = receive(r) + (1 << r);
|
|
|
+ successiveACState = 4;
|
|
|
+ } else {
|
|
|
+ r = 16;
|
|
|
+ successiveACState = 1;
|
|
|
}
|
|
|
-
|
|
|
- var resolution = component.resolutions[r];
|
|
|
- var numprecincts = resolution.precinctParameters.numprecincts;
|
|
|
- for (; k < numprecincts;) {
|
|
|
- var packet = createPacket(resolution, k, l);
|
|
|
- k++;
|
|
|
- return packet;
|
|
|
+ } else {
|
|
|
+ if (s !== 1) {
|
|
|
+ throw 'invalid ACn encoding';
|
|
|
}
|
|
|
- k = 0;
|
|
|
+ successiveACNextValue = receiveAndExtend(s);
|
|
|
+ successiveACState = r ? 2 : 3;
|
|
|
}
|
|
|
- i = 0;
|
|
|
+ continue;
|
|
|
+ case 1: // skipping r zero items
|
|
|
+ case 2:
|
|
|
+ if (component.blockData[offset + z]) {
|
|
|
+ component.blockData[offset + z] += (readBit() << successive);
|
|
|
+ } else {
|
|
|
+ r--;
|
|
|
+ if (r === 0) {
|
|
|
+ successiveACState = successiveACState == 2 ? 3 : 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 3: // set value for a zero item
|
|
|
+ if (component.blockData[offset + z]) {
|
|
|
+ component.blockData[offset + z] += (readBit() << successive);
|
|
|
+ } else {
|
|
|
+ component.blockData[offset + z] =
|
|
|
+ successiveACNextValue << successive;
|
|
|
+ successiveACState = 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 4: // eob
|
|
|
+ if (component.blockData[offset + z]) {
|
|
|
+ component.blockData[offset + z] += (readBit() << successive);
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
- l = 0;
|
|
|
+ k++;
|
|
|
}
|
|
|
- throw 'Out of packets';
|
|
|
- };
|
|
|
- }
|
|
|
- function buildPackets(context) {
|
|
|
- var siz = context.SIZ;
|
|
|
- var tileIndex = context.currentTile.index;
|
|
|
- var tile = context.tiles[tileIndex];
|
|
|
- var componentsCount = siz.Csiz;
|
|
|
- // Creating resolutions and sub-bands for each component
|
|
|
- for (var c = 0; c < componentsCount; c++) {
|
|
|
- var component = tile.components[c];
|
|
|
- var decompositionLevelsCount =
|
|
|
- component.codingStyleParameters.decompositionLevelsCount;
|
|
|
- // Section B.5 Resolution levels and sub-bands
|
|
|
- var resolutions = [];
|
|
|
- var subbands = [];
|
|
|
- for (var r = 0; r <= decompositionLevelsCount; r++) {
|
|
|
- var blocksDimensions = getBlocksDimensions(context, component, r);
|
|
|
- var resolution = {};
|
|
|
- var scale = 1 << (decompositionLevelsCount - r);
|
|
|
- resolution.trx0 = Math.ceil(component.tcx0 / scale);
|
|
|
- resolution.try0 = Math.ceil(component.tcy0 / scale);
|
|
|
- resolution.trx1 = Math.ceil(component.tcx1 / scale);
|
|
|
- resolution.try1 = Math.ceil(component.tcy1 / scale);
|
|
|
- buildPrecincts(context, resolution, blocksDimensions);
|
|
|
- resolutions.push(resolution);
|
|
|
-
|
|
|
- var subband;
|
|
|
- if (r === 0) {
|
|
|
- // one sub-band (LL) with last decomposition
|
|
|
- subband = {};
|
|
|
- subband.type = 'LL';
|
|
|
- subband.tbx0 = Math.ceil(component.tcx0 / scale);
|
|
|
- subband.tby0 = Math.ceil(component.tcy0 / scale);
|
|
|
- subband.tbx1 = Math.ceil(component.tcx1 / scale);
|
|
|
- subband.tby1 = Math.ceil(component.tcy1 / scale);
|
|
|
- subband.resolution = resolution;
|
|
|
- buildCodeblocks(context, subband, blocksDimensions);
|
|
|
- subbands.push(subband);
|
|
|
- resolution.subbands = [subband];
|
|
|
- } else {
|
|
|
- var bscale = 1 << (decompositionLevelsCount - r + 1);
|
|
|
- var resolutionSubbands = [];
|
|
|
- // three sub-bands (HL, LH and HH) with rest of decompositions
|
|
|
- subband = {};
|
|
|
- subband.type = 'HL';
|
|
|
- subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
|
|
|
- subband.tby0 = Math.ceil(component.tcy0 / bscale);
|
|
|
- subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
|
|
|
- subband.tby1 = Math.ceil(component.tcy1 / bscale);
|
|
|
- subband.resolution = resolution;
|
|
|
- buildCodeblocks(context, subband, blocksDimensions);
|
|
|
- subbands.push(subband);
|
|
|
- resolutionSubbands.push(subband);
|
|
|
-
|
|
|
- subband = {};
|
|
|
- subband.type = 'LH';
|
|
|
- subband.tbx0 = Math.ceil(component.tcx0 / bscale);
|
|
|
- subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
|
|
|
- subband.tbx1 = Math.ceil(component.tcx1 / bscale);
|
|
|
- subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
|
|
|
- subband.resolution = resolution;
|
|
|
- buildCodeblocks(context, subband, blocksDimensions);
|
|
|
- subbands.push(subband);
|
|
|
- resolutionSubbands.push(subband);
|
|
|
-
|
|
|
- subband = {};
|
|
|
- subband.type = 'HH';
|
|
|
- subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
|
|
|
- subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
|
|
|
- subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
|
|
|
- subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
|
|
|
- subband.resolution = resolution;
|
|
|
- buildCodeblocks(context, subband, blocksDimensions);
|
|
|
- subbands.push(subband);
|
|
|
- resolutionSubbands.push(subband);
|
|
|
-
|
|
|
- resolution.subbands = resolutionSubbands;
|
|
|
+ if (successiveACState === 4) {
|
|
|
+ eobrun--;
|
|
|
+ if (eobrun === 0) {
|
|
|
+ successiveACState = 0;
|
|
|
}
|
|
|
}
|
|
|
- component.resolutions = resolutions;
|
|
|
- component.subbands = subbands;
|
|
|
}
|
|
|
- // Generate the packets sequence
|
|
|
- var progressionOrder = tile.codingStyleDefaultParameters.progressionOrder;
|
|
|
- switch (progressionOrder) {
|
|
|
- case 0:
|
|
|
- tile.packetsIterator =
|
|
|
- new LayerResolutionComponentPositionIterator(context);
|
|
|
- break;
|
|
|
- case 1:
|
|
|
- tile.packetsIterator =
|
|
|
- new ResolutionLayerComponentPositionIterator(context);
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw 'Unsupported progression order ' + progressionOrder;
|
|
|
+
|
|
|
+ function decodeMcu(component, decode, mcu, row, col) {
|
|
|
+ var mcuRow = (mcu / mcusPerLine) | 0;
|
|
|
+ var mcuCol = mcu % mcusPerLine;
|
|
|
+ var blockRow = mcuRow * component.v + row;
|
|
|
+ var blockCol = mcuCol * component.h + col;
|
|
|
+ var offset = getBlockBufferOffset(component, blockRow, blockCol);
|
|
|
+ decode(component, offset);
|
|
|
}
|
|
|
- }
|
|
|
- function parseTilePackets(context, data, offset, dataLength) {
|
|
|
- var position = 0;
|
|
|
- var buffer, bufferSize = 0, skipNextBit = false;
|
|
|
- function readBits(count) {
|
|
|
- while (bufferSize < count) {
|
|
|
- var b = data[offset + position];
|
|
|
- position++;
|
|
|
- if (skipNextBit) {
|
|
|
- buffer = (buffer << 7) | b;
|
|
|
- bufferSize += 7;
|
|
|
- skipNextBit = false;
|
|
|
- } else {
|
|
|
- buffer = (buffer << 8) | b;
|
|
|
- bufferSize += 8;
|
|
|
- }
|
|
|
- if (b == 0xFF) {
|
|
|
- skipNextBit = true;
|
|
|
- }
|
|
|
- }
|
|
|
- bufferSize -= count;
|
|
|
- return (buffer >>> bufferSize) & ((1 << count) - 1);
|
|
|
+
|
|
|
+ function decodeBlock(component, decode, mcu) {
|
|
|
+ var blockRow = (mcu / component.blocksPerLine) | 0;
|
|
|
+ var blockCol = mcu % component.blocksPerLine;
|
|
|
+ var offset = getBlockBufferOffset(component, blockRow, blockCol);
|
|
|
+ decode(component, offset);
|
|
|
}
|
|
|
- function alignToByte() {
|
|
|
- bufferSize = 0;
|
|
|
- if (skipNextBit) {
|
|
|
- position++;
|
|
|
- skipNextBit = false;
|
|
|
+
|
|
|
+ var componentsLength = components.length;
|
|
|
+ var component, i, j, k, n;
|
|
|
+ var decodeFn;
|
|
|
+ if (progressive) {
|
|
|
+ if (spectralStart === 0) {
|
|
|
+ decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
|
|
|
+ } else {
|
|
|
+ decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
|
|
|
}
|
|
|
+ } else {
|
|
|
+ decodeFn = decodeBaseline;
|
|
|
}
|
|
|
- function readCodingpasses() {
|
|
|
- if (readBits(1) === 0) {
|
|
|
- return 1;
|
|
|
- }
|
|
|
- if (readBits(1) === 0) {
|
|
|
- return 2;
|
|
|
- }
|
|
|
- var value = readBits(2);
|
|
|
- if (value < 3) {
|
|
|
- return value + 3;
|
|
|
- }
|
|
|
- value = readBits(5);
|
|
|
- if (value < 31) {
|
|
|
- return value + 6;
|
|
|
- }
|
|
|
- value = readBits(7);
|
|
|
- return value + 37;
|
|
|
+
|
|
|
+ var mcu = 0, marker;
|
|
|
+ var mcuExpected;
|
|
|
+ if (componentsLength == 1) {
|
|
|
+ mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
|
|
|
+ } else {
|
|
|
+ mcuExpected = mcusPerLine * frame.mcusPerColumn;
|
|
|
}
|
|
|
- var tileIndex = context.currentTile.index;
|
|
|
- var tile = context.tiles[tileIndex];
|
|
|
- var packetsIterator = tile.packetsIterator;
|
|
|
- while (position < dataLength) {
|
|
|
- var packet = packetsIterator.nextPacket();
|
|
|
- if (!readBits(1)) {
|
|
|
- alignToByte();
|
|
|
- continue;
|
|
|
+ if (!resetInterval) {
|
|
|
+ resetInterval = mcuExpected;
|
|
|
+ }
|
|
|
+
|
|
|
+ var h, v;
|
|
|
+ while (mcu < mcuExpected) {
|
|
|
+ // reset interval stuff
|
|
|
+ for (i = 0; i < componentsLength; i++) {
|
|
|
+ components[i].pred = 0;
|
|
|
}
|
|
|
- var layerNumber = packet.layerNumber;
|
|
|
- var queue = [], codeblock;
|
|
|
- for (var i = 0, ii = packet.codeblocks.length; i < ii; i++) {
|
|
|
- codeblock = packet.codeblocks[i];
|
|
|
- var precinct = codeblock.precinct;
|
|
|
- var codeblockColumn = codeblock.cbx - precinct.cbxMin;
|
|
|
- var codeblockRow = codeblock.cby - precinct.cbyMin;
|
|
|
- var codeblockIncluded = false;
|
|
|
- var firstTimeInclusion = false;
|
|
|
- var valueReady;
|
|
|
- if ('included' in codeblock) {
|
|
|
- codeblockIncluded = !!readBits(1);
|
|
|
- } else {
|
|
|
- // reading inclusion tree
|
|
|
- precinct = codeblock.precinct;
|
|
|
- var inclusionTree, zeroBitPlanesTree;
|
|
|
- if ('inclusionTree' in precinct) {
|
|
|
- inclusionTree = precinct.inclusionTree;
|
|
|
- } else {
|
|
|
- // building inclusion and zero bit-planes trees
|
|
|
- var width = precinct.cbxMax - precinct.cbxMin + 1;
|
|
|
- var height = precinct.cbyMax - precinct.cbyMin + 1;
|
|
|
- inclusionTree = new InclusionTree(width, height, layerNumber);
|
|
|
- zeroBitPlanesTree = new TagTree(width, height);
|
|
|
- precinct.inclusionTree = inclusionTree;
|
|
|
- precinct.zeroBitPlanesTree = zeroBitPlanesTree;
|
|
|
- }
|
|
|
+ eobrun = 0;
|
|
|
|
|
|
- if (inclusionTree.reset(codeblockColumn, codeblockRow, layerNumber)) {
|
|
|
- while (true) {
|
|
|
- if (readBits(1)) {
|
|
|
- valueReady = !inclusionTree.nextLevel();
|
|
|
- if (valueReady) {
|
|
|
- codeblock.included = true;
|
|
|
- codeblockIncluded = firstTimeInclusion = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- } else {
|
|
|
- inclusionTree.incrementValue(layerNumber);
|
|
|
- break;
|
|
|
+ if (componentsLength == 1) {
|
|
|
+ component = components[0];
|
|
|
+ for (n = 0; n < resetInterval; n++) {
|
|
|
+ decodeBlock(component, decodeFn, mcu);
|
|
|
+ mcu++;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (n = 0; n < resetInterval; n++) {
|
|
|
+ for (i = 0; i < componentsLength; i++) {
|
|
|
+ component = components[i];
|
|
|
+ h = component.h;
|
|
|
+ v = component.v;
|
|
|
+ for (j = 0; j < v; j++) {
|
|
|
+ for (k = 0; k < h; k++) {
|
|
|
+ decodeMcu(component, decodeFn, mcu, j, k);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ mcu++;
|
|
|
}
|
|
|
- if (!codeblockIncluded) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (firstTimeInclusion) {
|
|
|
- zeroBitPlanesTree = precinct.zeroBitPlanesTree;
|
|
|
- zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);
|
|
|
- while (true) {
|
|
|
- if (readBits(1)) {
|
|
|
- valueReady = !zeroBitPlanesTree.nextLevel();
|
|
|
- if (valueReady) {
|
|
|
- break;
|
|
|
- }
|
|
|
- } else {
|
|
|
- zeroBitPlanesTree.incrementValue();
|
|
|
- }
|
|
|
- }
|
|
|
- codeblock.zeroBitPlanes = zeroBitPlanesTree.value;
|
|
|
- }
|
|
|
- var codingpasses = readCodingpasses();
|
|
|
- while (readBits(1)) {
|
|
|
- codeblock.Lblock++;
|
|
|
- }
|
|
|
- var codingpassesLog2 = log2(codingpasses);
|
|
|
- // rounding down log2
|
|
|
- var bits = ((codingpasses < (1 << codingpassesLog2)) ?
|
|
|
- codingpassesLog2 - 1 : codingpassesLog2) + codeblock.Lblock;
|
|
|
- var codedDataLength = readBits(bits);
|
|
|
- queue.push({
|
|
|
- codeblock: codeblock,
|
|
|
- codingpasses: codingpasses,
|
|
|
- dataLength: codedDataLength
|
|
|
- });
|
|
|
}
|
|
|
- alignToByte();
|
|
|
- while (queue.length > 0) {
|
|
|
- var packetItem = queue.shift();
|
|
|
- codeblock = packetItem.codeblock;
|
|
|
- if (!('data' in codeblock)) {
|
|
|
- codeblock.data = [];
|
|
|
- }
|
|
|
- codeblock.data.push({
|
|
|
- data: data,
|
|
|
- start: offset + position,
|
|
|
- end: offset + position + packetItem.dataLength,
|
|
|
- codingpasses: packetItem.codingpasses
|
|
|
- });
|
|
|
- position += packetItem.dataLength;
|
|
|
+
|
|
|
+ // find marker
|
|
|
+ bitsCount = 0;
|
|
|
+ marker = (data[offset] << 8) | data[offset + 1];
|
|
|
+ if (marker <= 0xFF00) {
|
|
|
+ throw 'marker was not found';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
|
|
|
+ offset += 2;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
- return position;
|
|
|
+
|
|
|
+ return offset - startOffset;
|
|
|
}
|
|
|
- function copyCoefficients(coefficients, levelWidth, levelHeight, subband,
|
|
|
- delta, mb, reversible, segmentationSymbolUsed) {
|
|
|
- var x0 = subband.tbx0;
|
|
|
- var y0 = subband.tby0;
|
|
|
- var width = subband.tbx1 - subband.tbx0;
|
|
|
- var codeblocks = subband.codeblocks;
|
|
|
- var right = subband.type.charAt(0) === 'H' ? 1 : 0;
|
|
|
- var bottom = subband.type.charAt(1) === 'H' ? levelWidth : 0;
|
|
|
|
|
|
- for (var i = 0, ii = codeblocks.length; i < ii; ++i) {
|
|
|
- var codeblock = codeblocks[i];
|
|
|
- var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
|
|
|
- var blockHeight = codeblock.tby1_ - codeblock.tby0_;
|
|
|
- if (blockWidth === 0 || blockHeight === 0) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (!('data' in codeblock)) {
|
|
|
+ // A port of poppler's IDCT method which in turn is taken from:
|
|
|
+ // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
|
|
|
+ // 'Practical Fast 1-D DCT Algorithms with 11 Multiplications',
|
|
|
+ // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
|
|
|
+ // 988-991.
|
|
|
+ function quantizeAndInverse(component, blockBufferOffset, p) {
|
|
|
+ var qt = component.quantizationTable;
|
|
|
+ var v0, v1, v2, v3, v4, v5, v6, v7, t;
|
|
|
+ var i;
|
|
|
+
|
|
|
+ // dequant
|
|
|
+ for (i = 0; i < 64; i++) {
|
|
|
+ p[i] = component.blockData[blockBufferOffset + i] * qt[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ // inverse DCT on rows
|
|
|
+ for (i = 0; i < 8; ++i) {
|
|
|
+ var row = 8 * i;
|
|
|
+
|
|
|
+ // check for all-zero AC coefficients
|
|
|
+ if (p[1 + row] === 0 && p[2 + row] === 0 && p[3 + row] === 0 &&
|
|
|
+ p[4 + row] === 0 && p[5 + row] === 0 && p[6 + row] === 0 &&
|
|
|
+ p[7 + row] === 0) {
|
|
|
+ t = (dctSqrt2 * p[0 + row] + 512) >> 10;
|
|
|
+ p[0 + row] = t;
|
|
|
+ p[1 + row] = t;
|
|
|
+ p[2 + row] = t;
|
|
|
+ p[3 + row] = t;
|
|
|
+ p[4 + row] = t;
|
|
|
+ p[5 + row] = t;
|
|
|
+ p[6 + row] = t;
|
|
|
+ p[7 + row] = t;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- var bitModel, currentCodingpassType;
|
|
|
- bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
|
|
|
- codeblock.zeroBitPlanes, mb);
|
|
|
- currentCodingpassType = 2; // first bit plane starts from cleanup
|
|
|
+ // stage 4
|
|
|
+ v0 = (dctSqrt2 * p[0 + row] + 128) >> 8;
|
|
|
+ v1 = (dctSqrt2 * p[4 + row] + 128) >> 8;
|
|
|
+ v2 = p[2 + row];
|
|
|
+ v3 = p[6 + row];
|
|
|
+ v4 = (dctSqrt1d2 * (p[1 + row] - p[7 + row]) + 128) >> 8;
|
|
|
+ v7 = (dctSqrt1d2 * (p[1 + row] + p[7 + row]) + 128) >> 8;
|
|
|
+ v5 = p[3 + row] << 4;
|
|
|
+ v6 = p[5 + row] << 4;
|
|
|
|
|
|
- // collect data
|
|
|
- var data = codeblock.data, totalLength = 0, codingpasses = 0;
|
|
|
- var j, jj, dataItem;
|
|
|
- for (j = 0, jj = data.length; j < jj; j++) {
|
|
|
- dataItem = data[j];
|
|
|
- totalLength += dataItem.end - dataItem.start;
|
|
|
- codingpasses += dataItem.codingpasses;
|
|
|
- }
|
|
|
- var encodedData = new Uint8Array(totalLength);
|
|
|
- var position = 0;
|
|
|
- for (j = 0, jj = data.length; j < jj; j++) {
|
|
|
- dataItem = data[j];
|
|
|
- var chunk = dataItem.data.subarray(dataItem.start, dataItem.end);
|
|
|
- encodedData.set(chunk, position);
|
|
|
- position += chunk.length;
|
|
|
- }
|
|
|
- // decoding the item
|
|
|
- var decoder = new ArithmeticDecoder(encodedData, 0, totalLength);
|
|
|
- bitModel.setDecoder(decoder);
|
|
|
+ // stage 3
|
|
|
+ t = (v0 - v1+ 1) >> 1;
|
|
|
+ v0 = (v0 + v1 + 1) >> 1;
|
|
|
+ v1 = t;
|
|
|
+ t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
|
|
|
+ v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
|
|
|
+ v3 = t;
|
|
|
+ t = (v4 - v6 + 1) >> 1;
|
|
|
+ v4 = (v4 + v6 + 1) >> 1;
|
|
|
+ v6 = t;
|
|
|
+ t = (v7 + v5 + 1) >> 1;
|
|
|
+ v5 = (v7 - v5 + 1) >> 1;
|
|
|
+ v7 = t;
|
|
|
|
|
|
- for (j = 0; j < codingpasses; j++) {
|
|
|
- switch (currentCodingpassType) {
|
|
|
- case 0:
|
|
|
- bitModel.runSignificancePropogationPass();
|
|
|
- break;
|
|
|
- case 1:
|
|
|
- bitModel.runMagnitudeRefinementPass();
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- bitModel.runCleanupPass();
|
|
|
- if (segmentationSymbolUsed) {
|
|
|
- bitModel.checkSegmentationSymbol();
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- currentCodingpassType = (currentCodingpassType + 1) % 3;
|
|
|
- }
|
|
|
+ // stage 2
|
|
|
+ t = (v0 - v3 + 1) >> 1;
|
|
|
+ v0 = (v0 + v3 + 1) >> 1;
|
|
|
+ v3 = t;
|
|
|
+ t = (v1 - v2 + 1) >> 1;
|
|
|
+ v1 = (v1 + v2 + 1) >> 1;
|
|
|
+ v2 = t;
|
|
|
+ t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
|
|
|
+ v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
|
|
|
+ v7 = t;
|
|
|
+ t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
|
|
|
+ v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
|
|
|
+ v6 = t;
|
|
|
|
|
|
- var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width;
|
|
|
- var sign = bitModel.coefficentsSign;
|
|
|
- var magnitude = bitModel.coefficentsMagnitude;
|
|
|
- var bitsDecoded = bitModel.bitsDecoded;
|
|
|
- var magnitudeCorrection = reversible ? 0 : 0.5;
|
|
|
- var k, n, nb;
|
|
|
- position = 0;
|
|
|
- // Do the interleaving of Section F.3.3 here, so we do not need
|
|
|
- // to copy later. LL level is not interleaved, just copied.
|
|
|
- var interleave = (subband.type !== 'LL');
|
|
|
- for (j = 0; j < blockHeight; j++) {
|
|
|
- var row = (offset / width) | 0; // row in the non-interleaved subband
|
|
|
- var levelOffset = 2 * row * (levelWidth - width) + right + bottom;
|
|
|
- for (k = 0; k < blockWidth; k++) {
|
|
|
- n = magnitude[position];
|
|
|
- if (n !== 0) {
|
|
|
- n = (n + magnitudeCorrection) * delta;
|
|
|
- if (sign[position] !== 0) {
|
|
|
- n = -n;
|
|
|
- }
|
|
|
- nb = bitsDecoded[position];
|
|
|
- var pos = interleave ? (levelOffset + (offset << 1)) : offset;
|
|
|
- if (reversible && (nb >= mb)) {
|
|
|
- coefficients[pos] = n;
|
|
|
- } else {
|
|
|
- coefficients[pos] = n * (1 << (mb - nb));
|
|
|
- }
|
|
|
- }
|
|
|
- offset++;
|
|
|
- position++;
|
|
|
- }
|
|
|
- offset += width - blockWidth;
|
|
|
- }
|
|
|
+ // stage 1
|
|
|
+ p[0 + row] = v0 + v7;
|
|
|
+ p[7 + row] = v0 - v7;
|
|
|
+ p[1 + row] = v1 + v6;
|
|
|
+ p[6 + row] = v1 - v6;
|
|
|
+ p[2 + row] = v2 + v5;
|
|
|
+ p[5 + row] = v2 - v5;
|
|
|
+ p[3 + row] = v3 + v4;
|
|
|
+ p[4 + row] = v3 - v4;
|
|
|
}
|
|
|
- }
|
|
|
- function transformTile(context, tile, c) {
|
|
|
- var component = tile.components[c];
|
|
|
- var codingStyleParameters = component.codingStyleParameters;
|
|
|
- var quantizationParameters = component.quantizationParameters;
|
|
|
- var decompositionLevelsCount =
|
|
|
- codingStyleParameters.decompositionLevelsCount;
|
|
|
- var spqcds = quantizationParameters.SPqcds;
|
|
|
- var scalarExpounded = quantizationParameters.scalarExpounded;
|
|
|
- var guardBits = quantizationParameters.guardBits;
|
|
|
- var segmentationSymbolUsed = codingStyleParameters.segmentationSymbolUsed;
|
|
|
- var precision = context.components[c].precision;
|
|
|
|
|
|
- var reversible = codingStyleParameters.reversibleTransformation;
|
|
|
- var transform = (reversible ? new ReversibleTransform() :
|
|
|
- new IrreversibleTransform());
|
|
|
+ // inverse DCT on columns
|
|
|
+ for (i = 0; i < 8; ++i) {
|
|
|
+ var col = i;
|
|
|
|
|
|
- var subbandCoefficients = [];
|
|
|
- var b = 0;
|
|
|
- for (var i = 0; i <= decompositionLevelsCount; i++) {
|
|
|
- var resolution = component.resolutions[i];
|
|
|
+ // check for all-zero AC coefficients
|
|
|
+ if (p[1*8 + col] === 0 && p[2*8 + col] === 0 && p[3*8 + col] === 0 &&
|
|
|
+ p[4*8 + col] === 0 && p[5*8 + col] === 0 && p[6*8 + col] === 0 &&
|
|
|
+ p[7*8 + col] === 0) {
|
|
|
+ t = (dctSqrt2 * p[i+0] + 8192) >> 14;
|
|
|
+ p[0*8 + col] = t;
|
|
|
+ p[1*8 + col] = t;
|
|
|
+ p[2*8 + col] = t;
|
|
|
+ p[3*8 + col] = t;
|
|
|
+ p[4*8 + col] = t;
|
|
|
+ p[5*8 + col] = t;
|
|
|
+ p[6*8 + col] = t;
|
|
|
+ p[7*8 + col] = t;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- var width = resolution.trx1 - resolution.trx0;
|
|
|
- var height = resolution.try1 - resolution.try0;
|
|
|
- // Allocate space for the whole sublevel.
|
|
|
- var coefficients = new Float32Array(width * height);
|
|
|
+ // stage 4
|
|
|
+ v0 = (dctSqrt2 * p[0*8 + col] + 2048) >> 12;
|
|
|
+ v1 = (dctSqrt2 * p[4*8 + col] + 2048) >> 12;
|
|
|
+ v2 = p[2*8 + col];
|
|
|
+ v3 = p[6*8 + col];
|
|
|
+ v4 = (dctSqrt1d2 * (p[1*8 + col] - p[7*8 + col]) + 2048) >> 12;
|
|
|
+ v7 = (dctSqrt1d2 * (p[1*8 + col] + p[7*8 + col]) + 2048) >> 12;
|
|
|
+ v5 = p[3*8 + col];
|
|
|
+ v6 = p[5*8 + col];
|
|
|
|
|
|
- for (var j = 0, jj = resolution.subbands.length; j < jj; j++) {
|
|
|
- var mu, epsilon;
|
|
|
- if (!scalarExpounded) {
|
|
|
- // formula E-5
|
|
|
- mu = spqcds[0].mu;
|
|
|
- epsilon = spqcds[0].epsilon + (i > 0 ? 1 - i : 0);
|
|
|
- } else {
|
|
|
- mu = spqcds[b].mu;
|
|
|
- epsilon = spqcds[b].epsilon;
|
|
|
- b++;
|
|
|
- }
|
|
|
+ // stage 3
|
|
|
+ t = (v0 - v1 + 1) >> 1;
|
|
|
+ v0 = (v0 + v1 + 1) >> 1;
|
|
|
+ v1 = t;
|
|
|
+ t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
|
|
|
+ v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
|
|
|
+ v3 = t;
|
|
|
+ t = (v4 - v6 + 1) >> 1;
|
|
|
+ v4 = (v4 + v6 + 1) >> 1;
|
|
|
+ v6 = t;
|
|
|
+ t = (v7 + v5 + 1) >> 1;
|
|
|
+ v5 = (v7 - v5 + 1) >> 1;
|
|
|
+ v7 = t;
|
|
|
|
|
|
- var subband = resolution.subbands[j];
|
|
|
- var gainLog2 = SubbandsGainLog2[subband.type];
|
|
|
+ // stage 2
|
|
|
+ t = (v0 - v3 + 1) >> 1;
|
|
|
+ v0 = (v0 + v3 + 1) >> 1;
|
|
|
+ v3 = t;
|
|
|
+ t = (v1 - v2 + 1) >> 1;
|
|
|
+ v1 = (v1 + v2 + 1) >> 1;
|
|
|
+ v2 = t;
|
|
|
+ t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
|
|
|
+ v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
|
|
|
+ v7 = t;
|
|
|
+ t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
|
|
|
+ v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
|
|
|
+ v6 = t;
|
|
|
|
|
|
- // calulate quantization coefficient (Section E.1.1.1)
|
|
|
- var delta = (reversible ? 1 :
|
|
|
- Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048));
|
|
|
- var mb = (guardBits + epsilon - 1);
|
|
|
+ // stage 1
|
|
|
+ p[0*8 + col] = v0 + v7;
|
|
|
+ p[7*8 + col] = v0 - v7;
|
|
|
+ p[1*8 + col] = v1 + v6;
|
|
|
+ p[6*8 + col] = v1 - v6;
|
|
|
+ p[2*8 + col] = v2 + v5;
|
|
|
+ p[5*8 + col] = v2 - v5;
|
|
|
+ p[3*8 + col] = v3 + v4;
|
|
|
+ p[4*8 + col] = v3 - v4;
|
|
|
+ }
|
|
|
|
|
|
- // In the first resolution level, copyCoefficients will fill the
|
|
|
- // whole array with coefficients. In the succeding passes,
|
|
|
- // copyCoefficients will consecutively fill in the values that belong
|
|
|
- // to the interleaved positions of the HL, LH, and HH coefficients.
|
|
|
- // The LL coefficients will then be interleaved in Transform.iterate().
|
|
|
- copyCoefficients(coefficients, width, height, subband, delta, mb,
|
|
|
- reversible, segmentationSymbolUsed);
|
|
|
+ // convert to 8-bit integers
|
|
|
+ for (i = 0; i < 64; ++i) {
|
|
|
+ var index = blockBufferOffset + i;
|
|
|
+ var q = p[i];
|
|
|
+ q = (q <= -2056) ? 0 : (q >= 2024) ? 255 : (q + 2056) >> 4;
|
|
|
+ component.blockData[index] = q;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildComponentData(frame, component) {
|
|
|
+ var lines = [];
|
|
|
+ var blocksPerLine = component.blocksPerLine;
|
|
|
+ var blocksPerColumn = component.blocksPerColumn;
|
|
|
+ var samplesPerLine = blocksPerLine << 3;
|
|
|
+ var computationBuffer = new Int32Array(64);
|
|
|
+
|
|
|
+ var i, j, ll = 0;
|
|
|
+ for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
|
|
|
+ for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
|
|
|
+ var offset = getBlockBufferOffset(component, blockRow, blockCol);
|
|
|
+ quantizeAndInverse(component, offset, computationBuffer);
|
|
|
}
|
|
|
- subbandCoefficients.push({
|
|
|
- width: width,
|
|
|
- height: height,
|
|
|
- items: coefficients
|
|
|
- });
|
|
|
}
|
|
|
+ return component.blockData;
|
|
|
+ }
|
|
|
|
|
|
- var result = transform.calculate(subbandCoefficients,
|
|
|
- component.tcx0, component.tcy0);
|
|
|
- return {
|
|
|
- left: component.tcx0,
|
|
|
- top: component.tcy0,
|
|
|
- width: result.width,
|
|
|
- height: result.height,
|
|
|
- items: result.items
|
|
|
- };
|
|
|
+ function clamp0to255(a) {
|
|
|
+ return a <= 0 ? 0 : a >= 255 ? 255 : a;
|
|
|
}
|
|
|
- function transformComponents(context) {
|
|
|
- var siz = context.SIZ;
|
|
|
- var components = context.components;
|
|
|
- var componentsCount = siz.Csiz;
|
|
|
- var resultImages = [];
|
|
|
- for (var i = 0, ii = context.tiles.length; i < ii; i++) {
|
|
|
- var tile = context.tiles[i];
|
|
|
- var transformedTiles = [];
|
|
|
- var c;
|
|
|
- for (c = 0; c < componentsCount; c++) {
|
|
|
- transformedTiles[c] = transformTile(context, tile, c);
|
|
|
+
|
|
|
+ constructor.prototype = {
|
|
|
+ parse: function parse(data) {
|
|
|
+
|
|
|
+ function readUint16() {
|
|
|
+ var value = (data[offset] << 8) | data[offset + 1];
|
|
|
+ offset += 2;
|
|
|
+ return value;
|
|
|
}
|
|
|
- var tile0 = transformedTiles[0];
|
|
|
- var out = new Uint8Array(tile0.items.length * componentsCount);
|
|
|
- var result = {
|
|
|
- left: tile0.left,
|
|
|
- top: tile0.top,
|
|
|
- width: tile0.width,
|
|
|
- height: tile0.height,
|
|
|
- items: out
|
|
|
- };
|
|
|
|
|
|
- // Section G.2.2 Inverse multi component transform
|
|
|
- var shift, offset, max, min;
|
|
|
- var pos = 0, j, jj, y0, y1, y2, r, g, b, k, val;
|
|
|
- if (tile.codingStyleDefaultParameters.multipleComponentTransform) {
|
|
|
- var fourComponents = componentsCount === 4;
|
|
|
- var y0items = transformedTiles[0].items;
|
|
|
- var y1items = transformedTiles[1].items;
|
|
|
- var y2items = transformedTiles[2].items;
|
|
|
- var y3items = fourComponents ? transformedTiles[3].items : null;
|
|
|
+ function readDataBlock() {
|
|
|
+ var length = readUint16();
|
|
|
+ var array = data.subarray(offset, offset + length - 2);
|
|
|
+ offset += array.length;
|
|
|
+ return array;
|
|
|
+ }
|
|
|
|
|
|
- // HACK: The multiple component transform formulas below assume that
|
|
|
- // all components have the same precision. With this in mind, we
|
|
|
- // compute shift and offset only once.
|
|
|
- shift = components[0].precision - 8;
|
|
|
- offset = (128 << shift) + 0.5;
|
|
|
- max = (127.5 * (1 << shift));
|
|
|
- min = -max;
|
|
|
+ function prepareComponents(frame) {
|
|
|
+ var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
|
|
|
+ var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
|
|
|
+ for (var i = 0; i < frame.components.length; i++) {
|
|
|
+ component = frame.components[i];
|
|
|
+ var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) *
|
|
|
+ component.h / frame.maxH);
|
|
|
+ var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) *
|
|
|
+ component.v / frame.maxV);
|
|
|
+ var blocksPerLineForMcu = mcusPerLine * component.h;
|
|
|
+ var blocksPerColumnForMcu = mcusPerColumn * component.v;
|
|
|
|
|
|
- var component0 = tile.components[0];
|
|
|
- if (!component0.codingStyleParameters.reversibleTransformation) {
|
|
|
- // inverse irreversible multiple component transform
|
|
|
- for (j = 0, jj = y0items.length; j < jj; ++j) {
|
|
|
- y0 = y0items[j];
|
|
|
- y1 = y1items[j];
|
|
|
- y2 = y2items[j];
|
|
|
- r = y0 + 1.402 * y2;
|
|
|
- g = y0 - 0.34413 * y1 - 0.71414 * y2;
|
|
|
- b = y0 + 1.772 * y1;
|
|
|
- out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift;
|
|
|
- out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift;
|
|
|
- out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift;
|
|
|
- if (fourComponents) {
|
|
|
- k = y3items[j];
|
|
|
- out[pos++] =
|
|
|
- k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift;
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // inverse reversible multiple component transform
|
|
|
- for (j = 0, jj = y0items.length; j < jj; ++j) {
|
|
|
- y0 = y0items[j];
|
|
|
- y1 = y1items[j];
|
|
|
- y2 = y2items[j];
|
|
|
- g = y0 - ((y2 + y1) >> 2);
|
|
|
- r = g + y2;
|
|
|
- b = g + y1;
|
|
|
- out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift;
|
|
|
- out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift;
|
|
|
- out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift;
|
|
|
- if (fourComponents) {
|
|
|
- k = y3items[j];
|
|
|
- out[pos++] =
|
|
|
- k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else { // no multi-component transform
|
|
|
- for (c = 0; c < componentsCount; c++) {
|
|
|
- var items = transformedTiles[c].items;
|
|
|
- shift = components[c].precision - 8;
|
|
|
- offset = (128 << shift) + 0.5;
|
|
|
- max = (127.5 * (1 << shift));
|
|
|
- min = -max;
|
|
|
- for (pos = c, j = 0, jj = items.length; j < jj; j++) {
|
|
|
- val = items[j];
|
|
|
- out[pos] = val <= min ? 0 :
|
|
|
- val >= max ? 255 : (val + offset) >> shift;
|
|
|
- pos += componentsCount;
|
|
|
- }
|
|
|
+ var blocksBufferSize = 64 * blocksPerColumnForMcu *
|
|
|
+ (blocksPerLineForMcu + 1);
|
|
|
+ component.blockData = new Int16Array(blocksBufferSize);
|
|
|
+ component.blocksPerLine = blocksPerLine;
|
|
|
+ component.blocksPerColumn = blocksPerColumn;
|
|
|
}
|
|
|
+ frame.mcusPerLine = mcusPerLine;
|
|
|
+ frame.mcusPerColumn = mcusPerColumn;
|
|
|
}
|
|
|
- resultImages.push(result);
|
|
|
- }
|
|
|
- return resultImages;
|
|
|
- }
|
|
|
- function initializeTile(context, tileIndex) {
|
|
|
- var siz = context.SIZ;
|
|
|
- var componentsCount = siz.Csiz;
|
|
|
- var tile = context.tiles[tileIndex];
|
|
|
- for (var c = 0; c < componentsCount; c++) {
|
|
|
- var component = tile.components[c];
|
|
|
- var qcdOrQcc = (c in context.currentTile.QCC ?
|
|
|
- context.currentTile.QCC[c] : context.currentTile.QCD);
|
|
|
- component.quantizationParameters = qcdOrQcc;
|
|
|
- var codOrCoc = (c in context.currentTile.COC ?
|
|
|
- context.currentTile.COC[c] : context.currentTile.COD);
|
|
|
- component.codingStyleParameters = codOrCoc;
|
|
|
- }
|
|
|
- tile.codingStyleDefaultParameters = context.currentTile.COD;
|
|
|
- }
|
|
|
|
|
|
- // Section B.10.2 Tag trees
|
|
|
- var TagTree = (function TagTreeClosure() {
|
|
|
- function TagTree(width, height) {
|
|
|
- var levelsLength = log2(Math.max(width, height)) + 1;
|
|
|
- this.levels = [];
|
|
|
- for (var i = 0; i < levelsLength; i++) {
|
|
|
- var level = {
|
|
|
- width: width,
|
|
|
- height: height,
|
|
|
- items: []
|
|
|
- };
|
|
|
- this.levels.push(level);
|
|
|
- width = Math.ceil(width / 2);
|
|
|
- height = Math.ceil(height / 2);
|
|
|
- }
|
|
|
- }
|
|
|
- TagTree.prototype = {
|
|
|
- reset: function TagTree_reset(i, j) {
|
|
|
- var currentLevel = 0, value = 0, level;
|
|
|
- while (currentLevel < this.levels.length) {
|
|
|
- level = this.levels[currentLevel];
|
|
|
- var index = i + j * level.width;
|
|
|
- if (index in level.items) {
|
|
|
- value = level.items[index];
|
|
|
- break;
|
|
|
- }
|
|
|
- level.index = index;
|
|
|
- i >>= 1;
|
|
|
- j >>= 1;
|
|
|
- currentLevel++;
|
|
|
- }
|
|
|
- currentLevel--;
|
|
|
- level = this.levels[currentLevel];
|
|
|
- level.items[level.index] = value;
|
|
|
- this.currentLevel = currentLevel;
|
|
|
- delete this.value;
|
|
|
- },
|
|
|
- incrementValue: function TagTree_incrementValue() {
|
|
|
- var level = this.levels[this.currentLevel];
|
|
|
- level.items[level.index]++;
|
|
|
- },
|
|
|
- nextLevel: function TagTree_nextLevel() {
|
|
|
- var currentLevel = this.currentLevel;
|
|
|
- var level = this.levels[currentLevel];
|
|
|
- var value = level.items[level.index];
|
|
|
- currentLevel--;
|
|
|
- if (currentLevel < 0) {
|
|
|
- this.value = value;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- this.currentLevel = currentLevel;
|
|
|
- level = this.levels[currentLevel];
|
|
|
- level.items[level.index] = value;
|
|
|
- return true;
|
|
|
+ var offset = 0, length = data.length;
|
|
|
+ var jfif = null;
|
|
|
+ var adobe = null;
|
|
|
+ var pixels = null;
|
|
|
+ var frame, resetInterval;
|
|
|
+ var quantizationTables = [];
|
|
|
+ var huffmanTablesAC = [], huffmanTablesDC = [];
|
|
|
+ var fileMarker = readUint16();
|
|
|
+ if (fileMarker != 0xFFD8) { // SOI (Start of Image)
|
|
|
+ throw 'SOI not found';
|
|
|
}
|
|
|
- };
|
|
|
- return TagTree;
|
|
|
- })();
|
|
|
|
|
|
- var InclusionTree = (function InclusionTreeClosure() {
|
|
|
- function InclusionTree(width, height, defaultValue) {
|
|
|
- var levelsLength = log2(Math.max(width, height)) + 1;
|
|
|
- this.levels = [];
|
|
|
- for (var i = 0; i < levelsLength; i++) {
|
|
|
- var items = new Uint8Array(width * height);
|
|
|
- for (var j = 0, jj = items.length; j < jj; j++) {
|
|
|
- items[j] = defaultValue;
|
|
|
- }
|
|
|
+ fileMarker = readUint16();
|
|
|
+ while (fileMarker != 0xFFD9) { // EOI (End of image)
|
|
|
+ var i, j, l;
|
|
|
+ switch(fileMarker) {
|
|
|
+ case 0xFFE0: // APP0 (Application Specific)
|
|
|
+ case 0xFFE1: // APP1
|
|
|
+ case 0xFFE2: // APP2
|
|
|
+ case 0xFFE3: // APP3
|
|
|
+ case 0xFFE4: // APP4
|
|
|
+ case 0xFFE5: // APP5
|
|
|
+ case 0xFFE6: // APP6
|
|
|
+ case 0xFFE7: // APP7
|
|
|
+ case 0xFFE8: // APP8
|
|
|
+ case 0xFFE9: // APP9
|
|
|
+ case 0xFFEA: // APP10
|
|
|
+ case 0xFFEB: // APP11
|
|
|
+ case 0xFFEC: // APP12
|
|
|
+ case 0xFFED: // APP13
|
|
|
+ case 0xFFEE: // APP14
|
|
|
+ case 0xFFEF: // APP15
|
|
|
+ case 0xFFFE: // COM (Comment)
|
|
|
+ var appData = readDataBlock();
|
|
|
|
|
|
- var level = {
|
|
|
- width: width,
|
|
|
- height: height,
|
|
|
- items: items
|
|
|
- };
|
|
|
- this.levels.push(level);
|
|
|
+ if (fileMarker === 0xFFE0) {
|
|
|
+ if (appData[0] === 0x4A && appData[1] === 0x46 &&
|
|
|
+ appData[2] === 0x49 && appData[3] === 0x46 &&
|
|
|
+ appData[4] === 0) { // 'JFIF\x00'
|
|
|
+ jfif = {
|
|
|
+ version: { major: appData[5], minor: appData[6] },
|
|
|
+ densityUnits: appData[7],
|
|
|
+ xDensity: (appData[8] << 8) | appData[9],
|
|
|
+ yDensity: (appData[10] << 8) | appData[11],
|
|
|
+ thumbWidth: appData[12],
|
|
|
+ thumbHeight: appData[13],
|
|
|
+ thumbData: appData.subarray(14, 14 +
|
|
|
+ 3 * appData[12] * appData[13])
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // TODO APP1 - Exif
|
|
|
+ if (fileMarker === 0xFFEE) {
|
|
|
+ if (appData[0] === 0x41 && appData[1] === 0x64 &&
|
|
|
+ appData[2] === 0x6F && appData[3] === 0x62 &&
|
|
|
+ appData[4] === 0x65 && appData[5] === 0) { // 'Adobe\x00'
|
|
|
+ adobe = {
|
|
|
+ version: appData[6],
|
|
|
+ flags0: (appData[7] << 8) | appData[8],
|
|
|
+ flags1: (appData[9] << 8) | appData[10],
|
|
|
+ transformCode: appData[11]
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
|
|
|
- width = Math.ceil(width / 2);
|
|
|
- height = Math.ceil(height / 2);
|
|
|
- }
|
|
|
- }
|
|
|
- InclusionTree.prototype = {
|
|
|
- reset: function InclusionTree_reset(i, j, stopValue) {
|
|
|
- var currentLevel = 0;
|
|
|
- while (currentLevel < this.levels.length) {
|
|
|
- var level = this.levels[currentLevel];
|
|
|
- var index = i + j * level.width;
|
|
|
- level.index = index;
|
|
|
- var value = level.items[index];
|
|
|
+ case 0xFFDB: // DQT (Define Quantization Tables)
|
|
|
+ var quantizationTablesLength = readUint16();
|
|
|
+ var quantizationTablesEnd = quantizationTablesLength + offset - 2;
|
|
|
+ var z;
|
|
|
+ while (offset < quantizationTablesEnd) {
|
|
|
+ var quantizationTableSpec = data[offset++];
|
|
|
+ var tableData = new Int32Array(64);
|
|
|
+ if ((quantizationTableSpec >> 4) === 0) { // 8 bit values
|
|
|
+ for (j = 0; j < 64; j++) {
|
|
|
+ z = dctZigZag[j];
|
|
|
+ tableData[z] = data[offset++];
|
|
|
+ }
|
|
|
+ } else if ((quantizationTableSpec >> 4) === 1) { //16 bit
|
|
|
+ for (j = 0; j < 64; j++) {
|
|
|
+ z = dctZigZag[j];
|
|
|
+ tableData[z] = readUint16();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw 'DQT: invalid table spec';
|
|
|
+ }
|
|
|
+ quantizationTables[quantizationTableSpec & 15] = tableData;
|
|
|
+ }
|
|
|
+ break;
|
|
|
|
|
|
- if (value == 0xFF) {
|
|
|
+ case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT)
|
|
|
+ case 0xFFC1: // SOF1 (Start of Frame, Extended DCT)
|
|
|
+ case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT)
|
|
|
+ if (frame) {
|
|
|
+ throw 'Only single frame JPEGs supported';
|
|
|
+ }
|
|
|
+ readUint16(); // skip data length
|
|
|
+ frame = {};
|
|
|
+ frame.extended = (fileMarker === 0xFFC1);
|
|
|
+ frame.progressive = (fileMarker === 0xFFC2);
|
|
|
+ frame.precision = data[offset++];
|
|
|
+ frame.scanLines = readUint16();
|
|
|
+ frame.samplesPerLine = readUint16();
|
|
|
+ frame.components = [];
|
|
|
+ frame.componentIds = {};
|
|
|
+ var componentsCount = data[offset++], componentId;
|
|
|
+ var maxH = 0, maxV = 0;
|
|
|
+ for (i = 0; i < componentsCount; i++) {
|
|
|
+ componentId = data[offset];
|
|
|
+ var h = data[offset + 1] >> 4;
|
|
|
+ var v = data[offset + 1] & 15;
|
|
|
+ if (maxH < h) {
|
|
|
+ maxH = h;
|
|
|
+ }
|
|
|
+ if (maxV < v) {
|
|
|
+ maxV = v;
|
|
|
+ }
|
|
|
+ var qId = data[offset + 2];
|
|
|
+ l = frame.components.push({
|
|
|
+ h: h,
|
|
|
+ v: v,
|
|
|
+ quantizationTable: quantizationTables[qId]
|
|
|
+ });
|
|
|
+ frame.componentIds[componentId] = l - 1;
|
|
|
+ offset += 3;
|
|
|
+ }
|
|
|
+ frame.maxH = maxH;
|
|
|
+ frame.maxV = maxV;
|
|
|
+ prepareComponents(frame);
|
|
|
break;
|
|
|
- }
|
|
|
|
|
|
- if (value > stopValue) {
|
|
|
- this.currentLevel = currentLevel;
|
|
|
- // already know about this one, propagating the value to top levels
|
|
|
- this.propagateValues();
|
|
|
- return false;
|
|
|
- }
|
|
|
+ case 0xFFC4: // DHT (Define Huffman Tables)
|
|
|
+ var huffmanLength = readUint16();
|
|
|
+ for (i = 2; i < huffmanLength;) {
|
|
|
+ var huffmanTableSpec = data[offset++];
|
|
|
+ var codeLengths = new Uint8Array(16);
|
|
|
+ var codeLengthSum = 0;
|
|
|
+ for (j = 0; j < 16; j++, offset++) {
|
|
|
+ codeLengthSum += (codeLengths[j] = data[offset]);
|
|
|
+ }
|
|
|
+ var huffmanValues = new Uint8Array(codeLengthSum);
|
|
|
+ for (j = 0; j < codeLengthSum; j++, offset++) {
|
|
|
+ huffmanValues[j] = data[offset];
|
|
|
+ }
|
|
|
+ i += 17 + codeLengthSum;
|
|
|
|
|
|
- i >>= 1;
|
|
|
- j >>= 1;
|
|
|
- currentLevel++;
|
|
|
- }
|
|
|
- this.currentLevel = currentLevel - 1;
|
|
|
- return true;
|
|
|
- },
|
|
|
- incrementValue: function InclusionTree_incrementValue(stopValue) {
|
|
|
- var level = this.levels[this.currentLevel];
|
|
|
- level.items[level.index] = stopValue + 1;
|
|
|
- this.propagateValues();
|
|
|
- },
|
|
|
- propagateValues: function InclusionTree_propagateValues() {
|
|
|
- var levelIndex = this.currentLevel;
|
|
|
- var level = this.levels[levelIndex];
|
|
|
- var currentValue = level.items[level.index];
|
|
|
- while (--levelIndex >= 0) {
|
|
|
- level = this.levels[levelIndex];
|
|
|
- level.items[level.index] = currentValue;
|
|
|
- }
|
|
|
- },
|
|
|
- nextLevel: function InclusionTree_nextLevel() {
|
|
|
- var currentLevel = this.currentLevel;
|
|
|
- var level = this.levels[currentLevel];
|
|
|
- var value = level.items[level.index];
|
|
|
- level.items[level.index] = 0xFF;
|
|
|
- currentLevel--;
|
|
|
- if (currentLevel < 0) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ ((huffmanTableSpec >> 4) === 0 ?
|
|
|
+ huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] =
|
|
|
+ buildHuffmanTable(codeLengths, huffmanValues);
|
|
|
+ }
|
|
|
+ break;
|
|
|
|
|
|
- this.currentLevel = currentLevel;
|
|
|
- level = this.levels[currentLevel];
|
|
|
- level.items[level.index] = value;
|
|
|
- return true;
|
|
|
+ case 0xFFDD: // DRI (Define Restart Interval)
|
|
|
+ readUint16(); // skip data length
|
|
|
+ resetInterval = readUint16();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 0xFFDA: // SOS (Start of Scan)
|
|
|
+ var scanLength = readUint16();
|
|
|
+ var selectorsCount = data[offset++];
|
|
|
+ var components = [], component;
|
|
|
+ for (i = 0; i < selectorsCount; i++) {
|
|
|
+ var componentIndex = frame.componentIds[data[offset++]];
|
|
|
+ component = frame.components[componentIndex];
|
|
|
+ var tableSpec = data[offset++];
|
|
|
+ component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
|
|
|
+ component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
|
|
|
+ components.push(component);
|
|
|
+ }
|
|
|
+ var spectralStart = data[offset++];
|
|
|
+ var spectralEnd = data[offset++];
|
|
|
+ var successiveApproximation = data[offset++];
|
|
|
+ var processed = decodeScan(data, offset,
|
|
|
+ frame, components, resetInterval,
|
|
|
+ spectralStart, spectralEnd,
|
|
|
+ successiveApproximation >> 4, successiveApproximation & 15);
|
|
|
+ offset += processed;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (data[offset - 3] == 0xFF &&
|
|
|
+ data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
|
|
|
+ // could be incorrect encoding -- last 0xFF byte of the previous
|
|
|
+ // block was eaten by the encoder
|
|
|
+ offset -= 3;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ throw 'unknown JPEG marker ' + fileMarker.toString(16);
|
|
|
+ }
|
|
|
+ fileMarker = readUint16();
|
|
|
}
|
|
|
- };
|
|
|
- return InclusionTree;
|
|
|
- })();
|
|
|
|
|
|
- // Section D. Coefficient bit modeling
|
|
|
- var BitModel = (function BitModelClosure() {
|
|
|
- var UNIFORM_CONTEXT = 17;
|
|
|
- var RUNLENGTH_CONTEXT = 18;
|
|
|
- // Table D-1
|
|
|
- // The index is binary presentation: 0dddvvhh, ddd - sum of Di (0..4),
|
|
|
- // vv - sum of Vi (0..2), and hh - sum of Hi (0..2)
|
|
|
- var LLAndLHContextsLabel = new Uint8Array([
|
|
|
- 0, 5, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 1, 6, 8, 0, 3, 7, 8, 0, 4,
|
|
|
- 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6,
|
|
|
- 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8
|
|
|
- ]);
|
|
|
- var HLContextLabel = new Uint8Array([
|
|
|
- 0, 3, 4, 0, 5, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 1, 3, 4, 0, 6, 7, 7, 0, 8,
|
|
|
- 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3,
|
|
|
- 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8
|
|
|
- ]);
|
|
|
- var HHContextLabel = new Uint8Array([
|
|
|
- 0, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 4, 5, 0, 4, 5, 5, 0, 5,
|
|
|
- 5, 5, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 8, 8,
|
|
|
- 8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8
|
|
|
- ]);
|
|
|
+ this.width = frame.samplesPerLine;
|
|
|
+ this.height = frame.scanLines;
|
|
|
+ this.jfif = jfif;
|
|
|
+ this.adobe = adobe;
|
|
|
+ this.components = [];
|
|
|
+ for (i = 0; i < frame.components.length; i++) {
|
|
|
+ component = frame.components[i];
|
|
|
+ this.components.push({
|
|
|
+ output: buildComponentData(frame, component),
|
|
|
+ scaleX: component.h / frame.maxH,
|
|
|
+ scaleY: component.v / frame.maxV,
|
|
|
+ blocksPerLine: component.blocksPerLine,
|
|
|
+ blocksPerColumn: component.blocksPerColumn
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.numComponents = this.components.length;
|
|
|
+ },
|
|
|
|
|
|
- function BitModel(width, height, subband, zeroBitPlanes, mb) {
|
|
|
- this.width = width;
|
|
|
- this.height = height;
|
|
|
+ _getLinearizedBlockData: function getLinearizedBlockData(width, height) {
|
|
|
+ var scaleX = this.width / width, scaleY = this.height / height;
|
|
|
|
|
|
- this.contextLabelTable = (subband == 'HH' ? HHContextLabel :
|
|
|
- (subband == 'HL' ? HLContextLabel : LLAndLHContextsLabel));
|
|
|
+ var component, componentScaleX, componentScaleY;
|
|
|
+ var x, y, i;
|
|
|
+ var offset = 0;
|
|
|
+ var Y, Cb, Cr, K, C, M, Ye, R, G, B;
|
|
|
+ var colorTransform;
|
|
|
+ var numComponents = this.components.length;
|
|
|
+ var dataLength = width * height * numComponents;
|
|
|
+ var data = new Uint8Array(dataLength);
|
|
|
+ var componentLine;
|
|
|
|
|
|
- var coefficientCount = width * height;
|
|
|
+ // lineData is reused for all components. Assume first component is
|
|
|
+ // the biggest
|
|
|
+ var lineData = new Uint8Array((this.components[0].blocksPerLine << 3) *
|
|
|
+ this.components[0].blocksPerColumn * 8);
|
|
|
|
|
|
- // coefficients outside the encoding region treated as insignificant
|
|
|
- // add border state cells for significanceState
|
|
|
- this.neighborsSignificance = new Uint8Array(coefficientCount);
|
|
|
- this.coefficentsSign = new Uint8Array(coefficientCount);
|
|
|
- this.coefficentsMagnitude = mb > 14 ? new Uint32Array(coefficientCount) :
|
|
|
- mb > 6 ? new Uint16Array(coefficientCount) :
|
|
|
- new Uint8Array(coefficientCount);
|
|
|
- this.processingFlags = new Uint8Array(coefficientCount);
|
|
|
+ // First construct image data ...
|
|
|
+ for (i = 0; i < numComponents; i++) {
|
|
|
+ component = this.components[i];
|
|
|
+ var blocksPerLine = component.blocksPerLine;
|
|
|
+ var blocksPerColumn = component.blocksPerColumn;
|
|
|
+ var samplesPerLine = blocksPerLine << 3;
|
|
|
|
|
|
- var bitsDecoded = new Uint8Array(coefficientCount);
|
|
|
- if (zeroBitPlanes !== 0) {
|
|
|
- for (var i = 0; i < coefficientCount; i++) {
|
|
|
- bitsDecoded[i] = zeroBitPlanes;
|
|
|
+ var j, k, ll = 0;
|
|
|
+ var sample;
|
|
|
+ var lineOffset = 0;
|
|
|
+ for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
|
|
|
+ var scanLine = blockRow << 3;
|
|
|
+ for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
|
|
|
+ var bufferOffset = getBlockBufferOffset(component,
|
|
|
+ blockRow, blockCol);
|
|
|
+ offset = 0;
|
|
|
+ sample = blockCol << 3;
|
|
|
+ for (j = 0; j < 8; j++) {
|
|
|
+ lineOffset = (scanLine + j) * samplesPerLine;
|
|
|
+ for (k = 0; k < 8; k++) {
|
|
|
+ lineData[lineOffset + sample + k] =
|
|
|
+ component.output[bufferOffset + offset++];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- this.bitsDecoded = bitsDecoded;
|
|
|
-
|
|
|
- this.reset();
|
|
|
- }
|
|
|
-
|
|
|
- BitModel.prototype = {
|
|
|
- setDecoder: function BitModel_setDecoder(decoder) {
|
|
|
- this.decoder = decoder;
|
|
|
- },
|
|
|
- reset: function BitModel_reset() {
|
|
|
- // We have 17 contexts that are accessed via context labels,
|
|
|
- // plus the uniform and runlength context.
|
|
|
- this.contexts = new Int8Array(19);
|
|
|
|
|
|
- // Contexts are packed into 1 byte:
|
|
|
- // highest 7 bits carry the index, lowest bit carries mps
|
|
|
- this.contexts[0] = (4 << 1) | 0;
|
|
|
- this.contexts[UNIFORM_CONTEXT] = (46 << 1) | 0;
|
|
|
- this.contexts[RUNLENGTH_CONTEXT] = (3 << 1) | 0;
|
|
|
- },
|
|
|
- setNeighborsSignificance:
|
|
|
- function BitModel_setNeighborsSignificance(row, column, index) {
|
|
|
- var neighborsSignificance = this.neighborsSignificance;
|
|
|
- var width = this.width, height = this.height;
|
|
|
- var left = (column > 0);
|
|
|
- var right = (column + 1 < width);
|
|
|
- var i;
|
|
|
+ componentScaleX = component.scaleX * scaleX;
|
|
|
+ componentScaleY = component.scaleY * scaleY;
|
|
|
+ offset = i;
|
|
|
|
|
|
- if (row > 0) {
|
|
|
- i = index - width;
|
|
|
- if (left) {
|
|
|
- neighborsSignificance[i - 1] += 0x10;
|
|
|
- }
|
|
|
- if (right) {
|
|
|
- neighborsSignificance[i + 1] += 0x10;
|
|
|
+ var cx, cy;
|
|
|
+ var index;
|
|
|
+ for (y = 0; y < height; y++) {
|
|
|
+ for (x = 0; x < width; x++) {
|
|
|
+ cy = 0 | (y * componentScaleY);
|
|
|
+ cx = 0 | (x * componentScaleX);
|
|
|
+ index = cy * samplesPerLine + cx;
|
|
|
+ data[offset] = lineData[index];
|
|
|
+ offset += numComponents;
|
|
|
}
|
|
|
- neighborsSignificance[i] += 0x04;
|
|
|
}
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ },
|
|
|
|
|
|
- if (row + 1 < height) {
|
|
|
- i = index + width;
|
|
|
- if (left) {
|
|
|
- neighborsSignificance[i - 1] += 0x10;
|
|
|
- }
|
|
|
- if (right) {
|
|
|
- neighborsSignificance[i + 1] += 0x10;
|
|
|
- }
|
|
|
- neighborsSignificance[i] += 0x04;
|
|
|
- }
|
|
|
+ _isColorConversionNeeded: function isColorConversionNeeded() {
|
|
|
+ if (this.adobe && this.adobe.transformCode) {
|
|
|
+ // The adobe transform marker overrides any previous setting
|
|
|
+ return true;
|
|
|
+ } else if (this.numComponents == 3) {
|
|
|
+ return true;
|
|
|
+ } else if (typeof this.colorTransform !== 'undefined') {
|
|
|
+ return !!this.colorTransform;
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- if (left) {
|
|
|
- neighborsSignificance[index - 1] += 0x01;
|
|
|
- }
|
|
|
- if (right) {
|
|
|
- neighborsSignificance[index + 1] += 0x01;
|
|
|
- }
|
|
|
- neighborsSignificance[index] |= 0x80;
|
|
|
- },
|
|
|
- runSignificancePropogationPass:
|
|
|
- function BitModel_runSignificancePropogationPass() {
|
|
|
- var decoder = this.decoder;
|
|
|
- var width = this.width, height = this.height;
|
|
|
- var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
- var coefficentsSign = this.coefficentsSign;
|
|
|
- var neighborsSignificance = this.neighborsSignificance;
|
|
|
- var processingFlags = this.processingFlags;
|
|
|
- var contexts = this.contexts;
|
|
|
- var labels = this.contextLabelTable;
|
|
|
- var bitsDecoded = this.bitsDecoded;
|
|
|
- var processedInverseMask = ~1;
|
|
|
- var processedMask = 1;
|
|
|
- var firstMagnitudeBitMask = 2;
|
|
|
+ _convertYccToRgb: function convertYccToRgb(data) {
|
|
|
+ var Y, Cb, Cr;
|
|
|
+ for (var i = 0; i < data.length; i += this.numComponents) {
|
|
|
+ Y = data[i ];
|
|
|
+ Cb = data[i + 1];
|
|
|
+ Cr = data[i + 2];
|
|
|
+ data[i ] = clamp0to255(Y - 179.456 + 1.402 * Cr);
|
|
|
+ data[i + 1] = clamp0to255(Y + 135.459 - 0.344 * Cb - 0.714 * Cr);
|
|
|
+ data[i + 2] = clamp0to255(Y - 226.816 + 1.772 * Cb);
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ },
|
|
|
|
|
|
- for (var i0 = 0; i0 < height; i0 += 4) {
|
|
|
- for (var j = 0; j < width; j++) {
|
|
|
- var index = i0 * width + j;
|
|
|
- for (var i1 = 0; i1 < 4; i1++, index += width) {
|
|
|
- var i = i0 + i1;
|
|
|
- if (i >= height) {
|
|
|
- break;
|
|
|
- }
|
|
|
- // clear processed flag first
|
|
|
- processingFlags[index] &= processedInverseMask;
|
|
|
+ _convertYcckToRgb: function convertYcckToRgb(data) {
|
|
|
+ var Y, Cb, Cr, k, CbCb, CbCr, CbY, Cbk, CrCr, Crk, CrY, YY, Yk, kk;
|
|
|
+ var offset = 0;
|
|
|
+ for (var i = 0; i < data.length; i += this.numComponents) {
|
|
|
+ Y = data[i];
|
|
|
+ Cb = data[i + 1];
|
|
|
+ Cr = data[i + 2];
|
|
|
+ k = data[i + 3];
|
|
|
+
|
|
|
+ CbCb = Cb * Cb;
|
|
|
+ CbCr = Cb * Cr;
|
|
|
+ CbY = Cb * Y;
|
|
|
+ Cbk = Cb * k;
|
|
|
+ CrCr = Cr * Cr;
|
|
|
+ Crk = Cr * k;
|
|
|
+ CrY = Cr * Y;
|
|
|
+ YY = Y * Y;
|
|
|
+ Yk = Y * k;
|
|
|
+ kk = k * k;
|
|
|
+
|
|
|
+ var r = - 122.67195406894 -
|
|
|
+ 6.60635669420364e-5 * CbCb + 0.000437130475926232 * CbCr -
|
|
|
+ 5.4080610064599e-5* CbY + 0.00048449797120281* Cbk -
|
|
|
+ 0.154362151871126 * Cb - 0.000957964378445773 * CrCr +
|
|
|
+ 0.000817076911346625 * CrY - 0.00477271405408747 * Crk +
|
|
|
+ 1.53380253221734 * Cr + 0.000961250184130688 * YY -
|
|
|
+ 0.00266257332283933 * Yk + 0.48357088451265 * Y -
|
|
|
+ 0.000336197177618394 * kk + 0.484791561490776 * k;
|
|
|
+
|
|
|
+ var g = 107.268039397724 +
|
|
|
+ 2.19927104525741e-5 * CbCb - 0.000640992018297945 * CbCr +
|
|
|
+ 0.000659397001245577* CbY + 0.000426105652938837* Cbk -
|
|
|
+ 0.176491792462875 * Cb - 0.000778269941513683 * CrCr +
|
|
|
+ 0.00130872261408275 * CrY + 0.000770482631801132 * Crk -
|
|
|
+ 0.151051492775562 * Cr + 0.00126935368114843 * YY -
|
|
|
+ 0.00265090189010898 * Yk + 0.25802910206845 * Y -
|
|
|
+ 0.000318913117588328 * kk - 0.213742400323665 * k;
|
|
|
+
|
|
|
+ var b = - 20.810012546947 -
|
|
|
+ 0.000570115196973677 * CbCb - 2.63409051004589e-5 * CbCr +
|
|
|
+ 0.0020741088115012* CbY - 0.00288260236853442* Cbk +
|
|
|
+ 0.814272968359295 * Cb - 1.53496057440975e-5 * CrCr -
|
|
|
+ 0.000132689043961446 * CrY + 0.000560833691242812 * Crk -
|
|
|
+ 0.195152027534049 * Cr + 0.00174418132927582 * YY -
|
|
|
+ 0.00255243321439347 * Yk + 0.116935020465145 * Y -
|
|
|
+ 0.000343531996510555 * kk + 0.24165260232407 * k;
|
|
|
+
|
|
|
+ data[offset++] = clamp0to255(r);
|
|
|
+ data[offset++] = clamp0to255(g);
|
|
|
+ data[offset++] = clamp0to255(b);
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ },
|
|
|
|
|
|
- if (coefficentsMagnitude[index] ||
|
|
|
- !neighborsSignificance[index]) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ _convertYcckToCmyk: function convertYcckToCmyk(data) {
|
|
|
+ var Y, Cb, Cr;
|
|
|
+ for (var i = 0; i < data.length; i += this.numComponents) {
|
|
|
+ Y = data[i];
|
|
|
+ Cb = data[i + 1];
|
|
|
+ Cr = data[i + 2];
|
|
|
+ data[i ] = clamp0to255(434.456 - Y - 1.402 * Cr);
|
|
|
+ data[i + 1] = clamp0to255(119.541 - Y + 0.344 * Cb + 0.714 * Cr);
|
|
|
+ data[i + 2] = clamp0to255(481.816 - Y - 1.772 * Cb);
|
|
|
+ // K in data[i + 3] is unchanged
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ },
|
|
|
|
|
|
- var contextLabel = labels[neighborsSignificance[index]];
|
|
|
- var decision = decoder.readBit(contexts, contextLabel);
|
|
|
- if (decision) {
|
|
|
- var sign = this.decodeSignBit(i, j, index);
|
|
|
- coefficentsSign[index] = sign;
|
|
|
- coefficentsMagnitude[index] = 1;
|
|
|
- this.setNeighborsSignificance(i, j, index);
|
|
|
- processingFlags[index] |= firstMagnitudeBitMask;
|
|
|
- }
|
|
|
- bitsDecoded[index]++;
|
|
|
- processingFlags[index] |= processedMask;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- decodeSignBit: function BitModel_decodeSignBit(row, column, index) {
|
|
|
- var width = this.width, height = this.height;
|
|
|
- var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
- var coefficentsSign = this.coefficentsSign;
|
|
|
- var contribution, sign0, sign1, significance1;
|
|
|
- var contextLabel, decoded;
|
|
|
+ _convertCmykToRgb: function convertCmykToRgb(data) {
|
|
|
+ var c, m, y, k;
|
|
|
+ var offset = 0;
|
|
|
+ for (var i = 0; i < data.length; i += this.numComponents) {
|
|
|
+ c = data[i ] * 0.00392156862745098;
|
|
|
+ m = data[i + 1] * 0.00392156862745098;
|
|
|
+ y = data[i + 2] * 0.00392156862745098;
|
|
|
+ k = data[i + 3] * 0.00392156862745098;
|
|
|
+
|
|
|
+ var r =
|
|
|
+ c * (-4.387332384609988 * c + 54.48615194189176 * m +
|
|
|
+ 18.82290502165302 * y + 212.25662451639585 * k +
|
|
|
+ -285.2331026137004) +
|
|
|
+ m * (1.7149763477362134 * m - 5.6096736904047315 * y +
|
|
|
+ -17.873870861415444 * k - 5.497006427196366) +
|
|
|
+ y * (-2.5217340131683033 * y - 21.248923337353073 * k +
|
|
|
+ 17.5119270841813) +
|
|
|
+ k * (-21.86122147463605 * k - 189.48180835922747) + 255;
|
|
|
+ var g =
|
|
|
+ c * (8.841041422036149 * c + 60.118027045597366 * m +
|
|
|
+ 6.871425592049007 * y + 31.159100130055922 * k +
|
|
|
+ -79.2970844816548) +
|
|
|
+ m * (-15.310361306967817 * m + 17.575251261109482 * y +
|
|
|
+ 131.35250912493976 * k - 190.9453302588951) +
|
|
|
+ y * (4.444339102852739 * y + 9.8632861493405 * k - 24.8674158255588) +
|
|
|
+ k * (-20.737325471181034 * k - 187.80453709719578) + 255;
|
|
|
+ var b =
|
|
|
+ c * (0.8842522430003296 * c + 8.078677503112928 * m +
|
|
|
+ 30.89978309703729 * y - 0.23883238689178934 * k +
|
|
|
+ -14.183576799673286) +
|
|
|
+ m * (10.49593273432072 * m + 63.02378494754052 * y +
|
|
|
+ 50.606957656360734 * k - 112.23884253719248) +
|
|
|
+ y * (0.03296041114873217 * y + 115.60384449646641 * k +
|
|
|
+ -193.58209356861505) +
|
|
|
+ k * (-22.33816807309886 * k - 180.12613974708367) + 255;
|
|
|
+
|
|
|
+ data[offset++] = clamp0to255(r);
|
|
|
+ data[offset++] = clamp0to255(g);
|
|
|
+ data[offset++] = clamp0to255(b);
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ },
|
|
|
|
|
|
- // calculate horizontal contribution
|
|
|
- significance1 = (column > 0 && coefficentsMagnitude[index - 1] !== 0);
|
|
|
- if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) {
|
|
|
- sign1 = coefficentsSign[index + 1];
|
|
|
- if (significance1) {
|
|
|
- sign0 = coefficentsSign[index - 1];
|
|
|
- contribution = 1 - sign1 - sign0;
|
|
|
+ getData: function getData(width, height, forceRGBoutput) {
|
|
|
+ var i;
|
|
|
+ var Y, Cb, Cr, K, C, M, Ye, R, G, B;
|
|
|
+ var colorTransform;
|
|
|
+ if (this.numComponents > 4) {
|
|
|
+ throw 'Unsupported color mode';
|
|
|
+ }
|
|
|
+ // type of data: Uint8Array(width * height * numComponents)
|
|
|
+ var data = this._getLinearizedBlockData(width, height);
|
|
|
+
|
|
|
+ if (this.numComponents === 3) {
|
|
|
+ return this._convertYccToRgb(data);
|
|
|
+ } else if (this.numComponents === 4) {
|
|
|
+ if (this._isColorConversionNeeded()) {
|
|
|
+ if (forceRGBoutput) {
|
|
|
+ return this._convertYcckToRgb(data);
|
|
|
} else {
|
|
|
- contribution = 1 - sign1 - sign1;
|
|
|
+ return this._convertYcckToCmyk(data);
|
|
|
}
|
|
|
- } else if (significance1) {
|
|
|
- sign0 = coefficentsSign[index - 1];
|
|
|
- contribution = 1 - sign0 - sign0;
|
|
|
} else {
|
|
|
- contribution = 0;
|
|
|
+ return this._convertCmykToRgb(data);
|
|
|
}
|
|
|
- var horizontalContribution = 3 * contribution;
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- // calculate vertical contribution and combine with the horizontal
|
|
|
- significance1 = (row > 0 && coefficentsMagnitude[index - width] !== 0);
|
|
|
- if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) {
|
|
|
- sign1 = coefficentsSign[index + width];
|
|
|
- if (significance1) {
|
|
|
- sign0 = coefficentsSign[index - width];
|
|
|
- contribution = 1 - sign1 - sign0 + horizontalContribution;
|
|
|
- } else {
|
|
|
- contribution = 1 - sign1 - sign1 + horizontalContribution;
|
|
|
+ return constructor;
|
|
|
+})();
|
|
|
+
|
|
|
+
|
|
|
+var JpxImage = (function JpxImageClosure() {
|
|
|
+ // Table E.1
|
|
|
+ var SubbandsGainLog2 = {
|
|
|
+ 'LL': 0,
|
|
|
+ 'LH': 1,
|
|
|
+ 'HL': 1,
|
|
|
+ 'HH': 2
|
|
|
+ };
|
|
|
+ function JpxImage() {
|
|
|
+ this.failOnCorruptedImage = false;
|
|
|
+ }
|
|
|
+ JpxImage.prototype = {
|
|
|
+ load: function JpxImage_load(url) {
|
|
|
+ var xhr = new XMLHttpRequest();
|
|
|
+ xhr.open('GET', url, true);
|
|
|
+ xhr.responseType = 'arraybuffer';
|
|
|
+ xhr.onload = (function() {
|
|
|
+ // TODO catch parse error
|
|
|
+ var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);
|
|
|
+ this.parse(data);
|
|
|
+ if (this.onload) {
|
|
|
+ this.onload();
|
|
|
+ }
|
|
|
+ }).bind(this);
|
|
|
+ xhr.send(null);
|
|
|
+ },
|
|
|
+ parse: function JpxImage_parse(data) {
|
|
|
+
|
|
|
+ var head = readUint16(data, 0);
|
|
|
+ // No box header, immediate start of codestream (SOC)
|
|
|
+ if (head === 0xFF4F) {
|
|
|
+ this.parseCodestream(data, 0, data.length);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var position = 0, length = data.length;
|
|
|
+ while (position < length) {
|
|
|
+ var headerSize = 8;
|
|
|
+ var lbox = readUint32(data, position);
|
|
|
+ var tbox = readUint32(data, position + 4);
|
|
|
+ position += headerSize;
|
|
|
+ if (lbox === 1) {
|
|
|
+ // XLBox: read UInt64 according to spec.
|
|
|
+ // JavaScript's int precision of 53 bit should be sufficient here.
|
|
|
+ lbox = readUint32(data, position) * 4294967296 +
|
|
|
+ readUint32(data, position + 4);
|
|
|
+ position += 8;
|
|
|
+ headerSize += 8;
|
|
|
+ }
|
|
|
+ if (lbox === 0) {
|
|
|
+ lbox = length - position + headerSize;
|
|
|
+ }
|
|
|
+ if (lbox < headerSize) {
|
|
|
+ error('JPX error: Invalid box field size');
|
|
|
+ }
|
|
|
+ var dataLength = lbox - headerSize;
|
|
|
+ var jumpDataLength = true;
|
|
|
+ switch (tbox) {
|
|
|
+ case 0x6A501A1A: // 'jP\032\032'
|
|
|
+ // TODO
|
|
|
+ break;
|
|
|
+ case 0x6A703268: // 'jp2h'
|
|
|
+ jumpDataLength = false; // parsing child boxes
|
|
|
+ break;
|
|
|
+ case 0x636F6C72: // 'colr'
|
|
|
+ // TODO
|
|
|
+ break;
|
|
|
+ case 0x6A703263: // 'jp2c'
|
|
|
+ this.parseCodestream(data, position, position + dataLength);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (jumpDataLength) {
|
|
|
+ position += dataLength;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ parseImageProperties: function JpxImage_parseImageProperties(stream) {
|
|
|
+ try {
|
|
|
+ var newByte = stream.getByte();
|
|
|
+ while (newByte >= 0) {
|
|
|
+ var oldByte = newByte;
|
|
|
+ newByte = stream.getByte();
|
|
|
+ var code = (oldByte << 8) | newByte;
|
|
|
+ // Image and tile size (SIZ)
|
|
|
+ if (code == 0xFF51) {
|
|
|
+ stream.skip(4);
|
|
|
+ var Xsiz = stream.getInt32() >>> 0; // Byte 4
|
|
|
+ var Ysiz = stream.getInt32() >>> 0; // Byte 8
|
|
|
+ var XOsiz = stream.getInt32() >>> 0; // Byte 12
|
|
|
+ var YOsiz = stream.getInt32() >>> 0; // Byte 16
|
|
|
+ stream.skip(16);
|
|
|
+ var Csiz = stream.getUint16(); // Byte 36
|
|
|
+ this.width = Xsiz - XOsiz;
|
|
|
+ this.height = Ysiz - YOsiz;
|
|
|
+ this.componentsCount = Csiz;
|
|
|
+ // Results are always returned as Uint8Arrays
|
|
|
+ this.bitsPerComponent = 8;
|
|
|
+ return;
|
|
|
}
|
|
|
- } else if (significance1) {
|
|
|
- sign0 = coefficentsSign[index - width];
|
|
|
- contribution = 1 - sign0 - sign0 + horizontalContribution;
|
|
|
+ }
|
|
|
+ throw 'No size marker found in JPX stream';
|
|
|
+ } catch (e) {
|
|
|
+ if (this.failOnCorruptedImage) {
|
|
|
+ error('JPX error: ' + e);
|
|
|
} else {
|
|
|
- contribution = horizontalContribution;
|
|
|
+ warn('JPX error: ' + e + '. Trying to recover');
|
|
|
}
|
|
|
+ }
|
|
|
+ },
|
|
|
+ parseCodestream: function JpxImage_parseCodestream(data, start, end) {
|
|
|
+ var context = {};
|
|
|
+ try {
|
|
|
+ var position = start;
|
|
|
+ while (position + 1 < end) {
|
|
|
+ var code = readUint16(data, position);
|
|
|
+ position += 2;
|
|
|
|
|
|
- if (contribution >= 0) {
|
|
|
- contextLabel = 9 + contribution;
|
|
|
- decoded = this.decoder.readBit(this.contexts, contextLabel);
|
|
|
+ var length = 0, j, sqcd, spqcds, spqcdSize, scalarExpounded, tile;
|
|
|
+ switch (code) {
|
|
|
+ case 0xFF4F: // Start of codestream (SOC)
|
|
|
+ context.mainHeader = true;
|
|
|
+ break;
|
|
|
+ case 0xFFD9: // End of codestream (EOC)
|
|
|
+ break;
|
|
|
+ case 0xFF51: // Image and tile size (SIZ)
|
|
|
+ length = readUint16(data, position);
|
|
|
+ var siz = {};
|
|
|
+ siz.Xsiz = readUint32(data, position + 4);
|
|
|
+ siz.Ysiz = readUint32(data, position + 8);
|
|
|
+ siz.XOsiz = readUint32(data, position + 12);
|
|
|
+ siz.YOsiz = readUint32(data, position + 16);
|
|
|
+ siz.XTsiz = readUint32(data, position + 20);
|
|
|
+ siz.YTsiz = readUint32(data, position + 24);
|
|
|
+ siz.XTOsiz = readUint32(data, position + 28);
|
|
|
+ siz.YTOsiz = readUint32(data, position + 32);
|
|
|
+ var componentsCount = readUint16(data, position + 36);
|
|
|
+ siz.Csiz = componentsCount;
|
|
|
+ var components = [];
|
|
|
+ j = position + 38;
|
|
|
+ for (var i = 0; i < componentsCount; i++) {
|
|
|
+ var component = {
|
|
|
+ precision: (data[j] & 0x7F) + 1,
|
|
|
+ isSigned: !!(data[j] & 0x80),
|
|
|
+ XRsiz: data[j + 1],
|
|
|
+ YRsiz: data[j + 1]
|
|
|
+ };
|
|
|
+ calculateComponentDimensions(component, siz);
|
|
|
+ components.push(component);
|
|
|
+ }
|
|
|
+ context.SIZ = siz;
|
|
|
+ context.components = components;
|
|
|
+ calculateTileGrids(context, components);
|
|
|
+ context.QCC = [];
|
|
|
+ context.COC = [];
|
|
|
+ break;
|
|
|
+ case 0xFF5C: // Quantization default (QCD)
|
|
|
+ length = readUint16(data, position);
|
|
|
+ var qcd = {};
|
|
|
+ j = position + 2;
|
|
|
+ sqcd = data[j++];
|
|
|
+ switch (sqcd & 0x1F) {
|
|
|
+ case 0:
|
|
|
+ spqcdSize = 8;
|
|
|
+ scalarExpounded = true;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ spqcdSize = 16;
|
|
|
+ scalarExpounded = false;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ spqcdSize = 16;
|
|
|
+ scalarExpounded = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw 'Invalid SQcd value ' + sqcd;
|
|
|
+ }
|
|
|
+ qcd.noQuantization = (spqcdSize == 8);
|
|
|
+ qcd.scalarExpounded = scalarExpounded;
|
|
|
+ qcd.guardBits = sqcd >> 5;
|
|
|
+ spqcds = [];
|
|
|
+ while (j < length + position) {
|
|
|
+ var spqcd = {};
|
|
|
+ if (spqcdSize == 8) {
|
|
|
+ spqcd.epsilon = data[j++] >> 3;
|
|
|
+ spqcd.mu = 0;
|
|
|
+ } else {
|
|
|
+ spqcd.epsilon = data[j] >> 3;
|
|
|
+ spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1];
|
|
|
+ j += 2;
|
|
|
+ }
|
|
|
+ spqcds.push(spqcd);
|
|
|
+ }
|
|
|
+ qcd.SPqcds = spqcds;
|
|
|
+ if (context.mainHeader) {
|
|
|
+ context.QCD = qcd;
|
|
|
+ } else {
|
|
|
+ context.currentTile.QCD = qcd;
|
|
|
+ context.currentTile.QCC = [];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 0xFF5D: // Quantization component (QCC)
|
|
|
+ length = readUint16(data, position);
|
|
|
+ var qcc = {};
|
|
|
+ j = position + 2;
|
|
|
+ var cqcc;
|
|
|
+ if (context.SIZ.Csiz < 257) {
|
|
|
+ cqcc = data[j++];
|
|
|
+ } else {
|
|
|
+ cqcc = readUint16(data, j);
|
|
|
+ j += 2;
|
|
|
+ }
|
|
|
+ sqcd = data[j++];
|
|
|
+ switch (sqcd & 0x1F) {
|
|
|
+ case 0:
|
|
|
+ spqcdSize = 8;
|
|
|
+ scalarExpounded = true;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ spqcdSize = 16;
|
|
|
+ scalarExpounded = false;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ spqcdSize = 16;
|
|
|
+ scalarExpounded = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw 'Invalid SQcd value ' + sqcd;
|
|
|
+ }
|
|
|
+ qcc.noQuantization = (spqcdSize == 8);
|
|
|
+ qcc.scalarExpounded = scalarExpounded;
|
|
|
+ qcc.guardBits = sqcd >> 5;
|
|
|
+ spqcds = [];
|
|
|
+ while (j < (length + position)) {
|
|
|
+ spqcd = {};
|
|
|
+ if (spqcdSize == 8) {
|
|
|
+ spqcd.epsilon = data[j++] >> 3;
|
|
|
+ spqcd.mu = 0;
|
|
|
+ } else {
|
|
|
+ spqcd.epsilon = data[j] >> 3;
|
|
|
+ spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1];
|
|
|
+ j += 2;
|
|
|
+ }
|
|
|
+ spqcds.push(spqcd);
|
|
|
+ }
|
|
|
+ qcc.SPqcds = spqcds;
|
|
|
+ if (context.mainHeader) {
|
|
|
+ context.QCC[cqcc] = qcc;
|
|
|
+ } else {
|
|
|
+ context.currentTile.QCC[cqcc] = qcc;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 0xFF52: // Coding style default (COD)
|
|
|
+ length = readUint16(data, position);
|
|
|
+ var cod = {};
|
|
|
+ j = position + 2;
|
|
|
+ var scod = data[j++];
|
|
|
+ cod.entropyCoderWithCustomPrecincts = !!(scod & 1);
|
|
|
+ cod.sopMarkerUsed = !!(scod & 2);
|
|
|
+ cod.ephMarkerUsed = !!(scod & 4);
|
|
|
+ cod.progressionOrder = data[j++];
|
|
|
+ cod.layersCount = readUint16(data, j);
|
|
|
+ j += 2;
|
|
|
+ cod.multipleComponentTransform = data[j++];
|
|
|
+
|
|
|
+ cod.decompositionLevelsCount = data[j++];
|
|
|
+ cod.xcb = (data[j++] & 0xF) + 2;
|
|
|
+ cod.ycb = (data[j++] & 0xF) + 2;
|
|
|
+ var blockStyle = data[j++];
|
|
|
+ cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1);
|
|
|
+ cod.resetContextProbabilities = !!(blockStyle & 2);
|
|
|
+ cod.terminationOnEachCodingPass = !!(blockStyle & 4);
|
|
|
+ cod.verticalyStripe = !!(blockStyle & 8);
|
|
|
+ cod.predictableTermination = !!(blockStyle & 16);
|
|
|
+ cod.segmentationSymbolUsed = !!(blockStyle & 32);
|
|
|
+ cod.reversibleTransformation = data[j++];
|
|
|
+ if (cod.entropyCoderWithCustomPrecincts) {
|
|
|
+ var precinctsSizes = [];
|
|
|
+ while (j < length + position) {
|
|
|
+ var precinctsSize = data[j++];
|
|
|
+ precinctsSizes.push({
|
|
|
+ PPx: precinctsSize & 0xF,
|
|
|
+ PPy: precinctsSize >> 4
|
|
|
+ });
|
|
|
+ }
|
|
|
+ cod.precinctsSizes = precinctsSizes;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cod.sopMarkerUsed || cod.ephMarkerUsed ||
|
|
|
+ cod.selectiveArithmeticCodingBypass ||
|
|
|
+ cod.resetContextProbabilities ||
|
|
|
+ cod.terminationOnEachCodingPass ||
|
|
|
+ cod.verticalyStripe || cod.predictableTermination) {
|
|
|
+ throw 'Unsupported COD options: ' +
|
|
|
+ globalScope.JSON.stringify(cod);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (context.mainHeader) {
|
|
|
+ context.COD = cod;
|
|
|
+ } else {
|
|
|
+ context.currentTile.COD = cod;
|
|
|
+ context.currentTile.COC = [];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 0xFF90: // Start of tile-part (SOT)
|
|
|
+ length = readUint16(data, position);
|
|
|
+ tile = {};
|
|
|
+ tile.index = readUint16(data, position + 2);
|
|
|
+ tile.length = readUint32(data, position + 4);
|
|
|
+ tile.dataEnd = tile.length + position - 2;
|
|
|
+ tile.partIndex = data[position + 8];
|
|
|
+ tile.partsCount = data[position + 9];
|
|
|
+
|
|
|
+ context.mainHeader = false;
|
|
|
+ if (tile.partIndex === 0) {
|
|
|
+ // reset component specific settings
|
|
|
+ tile.COD = context.COD;
|
|
|
+ tile.COC = context.COC.slice(0); // clone of the global COC
|
|
|
+ tile.QCD = context.QCD;
|
|
|
+ tile.QCC = context.QCC.slice(0); // clone of the global COC
|
|
|
+ }
|
|
|
+ context.currentTile = tile;
|
|
|
+ break;
|
|
|
+ case 0xFF93: // Start of data (SOD)
|
|
|
+ tile = context.currentTile;
|
|
|
+ if (tile.partIndex === 0) {
|
|
|
+ initializeTile(context, tile.index);
|
|
|
+ buildPackets(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ // moving to the end of the data
|
|
|
+ length = tile.dataEnd - position;
|
|
|
+ parseTilePackets(context, data, position, length);
|
|
|
+ break;
|
|
|
+ case 0xFF64: // Comment (COM)
|
|
|
+ length = readUint16(data, position);
|
|
|
+ // skipping content
|
|
|
+ break;
|
|
|
+ case 0xFF53: // Coding style component (COC)
|
|
|
+ throw 'Codestream code 0xFF53 (COC) is not implemented';
|
|
|
+ default:
|
|
|
+ throw 'Unknown codestream code: ' + code.toString(16);
|
|
|
+ }
|
|
|
+ position += length;
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ if (this.failOnCorruptedImage) {
|
|
|
+ error('JPX error: ' + e);
|
|
|
} else {
|
|
|
- contextLabel = 9 - contribution;
|
|
|
- decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1;
|
|
|
+ warn('JPX error: ' + e + '. Trying to recover');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.tiles = transformComponents(context);
|
|
|
+ this.width = context.SIZ.Xsiz - context.SIZ.XOsiz;
|
|
|
+ this.height = context.SIZ.Ysiz - context.SIZ.YOsiz;
|
|
|
+ this.componentsCount = context.SIZ.Csiz;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ function calculateComponentDimensions(component, siz) {
|
|
|
+ // Section B.2 Component mapping
|
|
|
+ component.x0 = Math.ceil(siz.XOsiz / component.XRsiz);
|
|
|
+ component.x1 = Math.ceil(siz.Xsiz / component.XRsiz);
|
|
|
+ component.y0 = Math.ceil(siz.YOsiz / component.YRsiz);
|
|
|
+ component.y1 = Math.ceil(siz.Ysiz / component.YRsiz);
|
|
|
+ component.width = component.x1 - component.x0;
|
|
|
+ component.height = component.y1 - component.y0;
|
|
|
+ }
|
|
|
+ function calculateTileGrids(context, components) {
|
|
|
+ var siz = context.SIZ;
|
|
|
+ // Section B.3 Division into tile and tile-components
|
|
|
+ var tile, tiles = [];
|
|
|
+ var numXtiles = Math.ceil((siz.Xsiz - siz.XTOsiz) / siz.XTsiz);
|
|
|
+ var numYtiles = Math.ceil((siz.Ysiz - siz.YTOsiz) / siz.YTsiz);
|
|
|
+ for (var q = 0; q < numYtiles; q++) {
|
|
|
+ for (var p = 0; p < numXtiles; p++) {
|
|
|
+ tile = {};
|
|
|
+ tile.tx0 = Math.max(siz.XTOsiz + p * siz.XTsiz, siz.XOsiz);
|
|
|
+ tile.ty0 = Math.max(siz.YTOsiz + q * siz.YTsiz, siz.YOsiz);
|
|
|
+ tile.tx1 = Math.min(siz.XTOsiz + (p + 1) * siz.XTsiz, siz.Xsiz);
|
|
|
+ tile.ty1 = Math.min(siz.YTOsiz + (q + 1) * siz.YTsiz, siz.Ysiz);
|
|
|
+ tile.width = tile.tx1 - tile.tx0;
|
|
|
+ tile.height = tile.ty1 - tile.ty0;
|
|
|
+ tile.components = [];
|
|
|
+ tiles.push(tile);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ context.tiles = tiles;
|
|
|
+
|
|
|
+ var componentsCount = siz.Csiz;
|
|
|
+ for (var i = 0, ii = componentsCount; i < ii; i++) {
|
|
|
+ var component = components[i];
|
|
|
+ for (var j = 0, jj = tiles.length; j < jj; j++) {
|
|
|
+ var tileComponent = {};
|
|
|
+ tile = tiles[j];
|
|
|
+ tileComponent.tcx0 = Math.ceil(tile.tx0 / component.XRsiz);
|
|
|
+ tileComponent.tcy0 = Math.ceil(tile.ty0 / component.YRsiz);
|
|
|
+ tileComponent.tcx1 = Math.ceil(tile.tx1 / component.XRsiz);
|
|
|
+ tileComponent.tcy1 = Math.ceil(tile.ty1 / component.YRsiz);
|
|
|
+ tileComponent.width = tileComponent.tcx1 - tileComponent.tcx0;
|
|
|
+ tileComponent.height = tileComponent.tcy1 - tileComponent.tcy0;
|
|
|
+ tile.components[i] = tileComponent;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function getBlocksDimensions(context, component, r) {
|
|
|
+ var codOrCoc = component.codingStyleParameters;
|
|
|
+ var result = {};
|
|
|
+ if (!codOrCoc.entropyCoderWithCustomPrecincts) {
|
|
|
+ result.PPx = 15;
|
|
|
+ result.PPy = 15;
|
|
|
+ } else {
|
|
|
+ result.PPx = codOrCoc.precinctsSizes[r].PPx;
|
|
|
+ result.PPy = codOrCoc.precinctsSizes[r].PPy;
|
|
|
+ }
|
|
|
+ // calculate codeblock size as described in section B.7
|
|
|
+ result.xcb_ = (r > 0 ? Math.min(codOrCoc.xcb, result.PPx - 1) :
|
|
|
+ Math.min(codOrCoc.xcb, result.PPx));
|
|
|
+ result.ycb_ = (r > 0 ? Math.min(codOrCoc.ycb, result.PPy - 1) :
|
|
|
+ Math.min(codOrCoc.ycb, result.PPy));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ function buildPrecincts(context, resolution, dimensions) {
|
|
|
+ // Section B.6 Division resolution to precincts
|
|
|
+ var precinctWidth = 1 << dimensions.PPx;
|
|
|
+ var precinctHeight = 1 << dimensions.PPy;
|
|
|
+ var numprecinctswide = (resolution.trx1 > resolution.trx0 ?
|
|
|
+ Math.ceil(resolution.trx1 / precinctWidth) -
|
|
|
+ Math.floor(resolution.trx0 / precinctWidth) : 0);
|
|
|
+ var numprecinctshigh = (resolution.try1 > resolution.try0 ?
|
|
|
+ Math.ceil(resolution.try1 / precinctHeight) -
|
|
|
+ Math.floor(resolution.try0 / precinctHeight) : 0);
|
|
|
+ var numprecincts = numprecinctswide * numprecinctshigh;
|
|
|
+ var precinctXOffset = Math.floor(resolution.trx0 / precinctWidth) *
|
|
|
+ precinctWidth;
|
|
|
+ var precinctYOffset = Math.floor(resolution.try0 / precinctHeight) *
|
|
|
+ precinctHeight;
|
|
|
+ resolution.precinctParameters = {
|
|
|
+ precinctXOffset: precinctXOffset,
|
|
|
+ precinctYOffset: precinctYOffset,
|
|
|
+ precinctWidth: precinctWidth,
|
|
|
+ precinctHeight: precinctHeight,
|
|
|
+ numprecinctswide: numprecinctswide,
|
|
|
+ numprecinctshigh: numprecinctshigh,
|
|
|
+ numprecincts: numprecincts
|
|
|
+ };
|
|
|
+ }
|
|
|
+ function buildCodeblocks(context, subband, dimensions) {
|
|
|
+ // Section B.7 Division sub-band into code-blocks
|
|
|
+ var xcb_ = dimensions.xcb_;
|
|
|
+ var ycb_ = dimensions.ycb_;
|
|
|
+ var codeblockWidth = 1 << xcb_;
|
|
|
+ var codeblockHeight = 1 << ycb_;
|
|
|
+ var cbx0 = subband.tbx0 >> xcb_;
|
|
|
+ var cby0 = subband.tby0 >> ycb_;
|
|
|
+ var cbx1 = (subband.tbx1 + codeblockWidth - 1) >> xcb_;
|
|
|
+ var cby1 = (subband.tby1 + codeblockHeight - 1) >> ycb_;
|
|
|
+ var precinctParameters = subband.resolution.precinctParameters;
|
|
|
+ var codeblocks = [];
|
|
|
+ var precincts = [];
|
|
|
+ var i, j, codeblock, precinctNumber;
|
|
|
+ for (j = cby0; j < cby1; j++) {
|
|
|
+ for (i = cbx0; i < cbx1; i++) {
|
|
|
+ codeblock = {
|
|
|
+ cbx: i,
|
|
|
+ cby: j,
|
|
|
+ tbx0: codeblockWidth * i,
|
|
|
+ tby0: codeblockHeight * j,
|
|
|
+ tbx1: codeblockWidth * (i + 1),
|
|
|
+ tby1: codeblockHeight * (j + 1)
|
|
|
+ };
|
|
|
+ // calculate precinct number
|
|
|
+ var pi = Math.floor((codeblock.tbx0 -
|
|
|
+ precinctParameters.precinctXOffset) /
|
|
|
+ precinctParameters.precinctWidth);
|
|
|
+ var pj = Math.floor((codeblock.tby0 -
|
|
|
+ precinctParameters.precinctYOffset) /
|
|
|
+ precinctParameters.precinctHeight);
|
|
|
+ precinctNumber = pj + pi * precinctParameters.numprecinctswide;
|
|
|
+ codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0);
|
|
|
+ codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0);
|
|
|
+ codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1);
|
|
|
+ codeblock.tby1_ = Math.min(subband.tby1, codeblock.tby1);
|
|
|
+ codeblock.precinctNumber = precinctNumber;
|
|
|
+ codeblock.subbandType = subband.type;
|
|
|
+ codeblock.Lblock = 3;
|
|
|
+ codeblocks.push(codeblock);
|
|
|
+ // building precinct for the sub-band
|
|
|
+ var precinct = precincts[precinctNumber];
|
|
|
+ if (precinct !== undefined) {
|
|
|
+ if (i < precinct.cbxMin) {
|
|
|
+ precinct.cbxMin = i;
|
|
|
+ } else if (i > precinct.cbxMax) {
|
|
|
+ precinct.cbxMax = i;
|
|
|
+ }
|
|
|
+ if (j < precinct.cbyMin) {
|
|
|
+ precinct.cbxMin = j;
|
|
|
+ } else if (j > precinct.cbyMax) {
|
|
|
+ precinct.cbyMax = j;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ precincts[precinctNumber] = precinct = {
|
|
|
+ cbxMin: i,
|
|
|
+ cbyMin: j,
|
|
|
+ cbxMax: i,
|
|
|
+ cbyMax: j
|
|
|
+ };
|
|
|
+ }
|
|
|
+ codeblock.precinct = precinct;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ subband.codeblockParameters = {
|
|
|
+ codeblockWidth: xcb_,
|
|
|
+ codeblockHeight: ycb_,
|
|
|
+ numcodeblockwide: cbx1 - cbx0 + 1,
|
|
|
+ numcodeblockhigh: cby1 - cby1 + 1
|
|
|
+ };
|
|
|
+ subband.codeblocks = codeblocks;
|
|
|
+ subband.precincts = precincts;
|
|
|
+ }
|
|
|
+ function createPacket(resolution, precinctNumber, layerNumber) {
|
|
|
+ var precinctCodeblocks = [];
|
|
|
+ // Section B.10.8 Order of info in packet
|
|
|
+ var subbands = resolution.subbands;
|
|
|
+ // sub-bands already ordered in 'LL', 'HL', 'LH', and 'HH' sequence
|
|
|
+ for (var i = 0, ii = subbands.length; i < ii; i++) {
|
|
|
+ var subband = subbands[i];
|
|
|
+ var codeblocks = subband.codeblocks;
|
|
|
+ for (var j = 0, jj = codeblocks.length; j < jj; j++) {
|
|
|
+ var codeblock = codeblocks[j];
|
|
|
+ if (codeblock.precinctNumber != precinctNumber) {
|
|
|
+ continue;
|
|
|
}
|
|
|
- return decoded;
|
|
|
- },
|
|
|
- runMagnitudeRefinementPass:
|
|
|
- function BitModel_runMagnitudeRefinementPass() {
|
|
|
- var decoder = this.decoder;
|
|
|
- var width = this.width, height = this.height;
|
|
|
- var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
- var neighborsSignificance = this.neighborsSignificance;
|
|
|
- var contexts = this.contexts;
|
|
|
- var bitsDecoded = this.bitsDecoded;
|
|
|
- var processingFlags = this.processingFlags;
|
|
|
- var processedMask = 1;
|
|
|
- var firstMagnitudeBitMask = 2;
|
|
|
- var length = width * height;
|
|
|
- var width4 = width * 4;
|
|
|
-
|
|
|
- for (var index0 = 0, indexNext; index0 < length; index0 = indexNext) {
|
|
|
- indexNext = Math.min(length, index0 + width4);
|
|
|
- for (var j = 0; j < width; j++) {
|
|
|
- for (var index = index0 + j; index < indexNext; index += width) {
|
|
|
+ precinctCodeblocks.push(codeblock);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ layerNumber: layerNumber,
|
|
|
+ codeblocks: precinctCodeblocks
|
|
|
+ };
|
|
|
+ }
|
|
|
+ function LayerResolutionComponentPositionIterator(context) {
|
|
|
+ var siz = context.SIZ;
|
|
|
+ var tileIndex = context.currentTile.index;
|
|
|
+ var tile = context.tiles[tileIndex];
|
|
|
+ var layersCount = tile.codingStyleDefaultParameters.layersCount;
|
|
|
+ var componentsCount = siz.Csiz;
|
|
|
+ var maxDecompositionLevelsCount = 0;
|
|
|
+ for (var q = 0; q < componentsCount; q++) {
|
|
|
+ maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount,
|
|
|
+ tile.components[q].codingStyleParameters.decompositionLevelsCount);
|
|
|
+ }
|
|
|
|
|
|
- // significant but not those that have just become
|
|
|
- if (!coefficentsMagnitude[index] ||
|
|
|
- (processingFlags[index] & processedMask) !== 0) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ var l = 0, r = 0, i = 0, k = 0;
|
|
|
|
|
|
- var contextLabel = 16;
|
|
|
- if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) {
|
|
|
- processingFlags[index] ^= firstMagnitudeBitMask;
|
|
|
- // first refinement
|
|
|
- var significance = neighborsSignificance[index] & 127;
|
|
|
- contextLabel = significance === 0 ? 15 : 14;
|
|
|
- }
|
|
|
+ this.nextPacket = function JpxImage_nextPacket() {
|
|
|
+ // Section B.12.1.1 Layer-resolution-component-position
|
|
|
+ for (; l < layersCount; l++) {
|
|
|
+ for (; r <= maxDecompositionLevelsCount; r++) {
|
|
|
+ for (; i < componentsCount; i++) {
|
|
|
+ var component = tile.components[i];
|
|
|
+ if (r > component.codingStyleParameters.decompositionLevelsCount) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- var bit = decoder.readBit(contexts, contextLabel);
|
|
|
- coefficentsMagnitude[index] =
|
|
|
- (coefficentsMagnitude[index] << 1) | bit;
|
|
|
- bitsDecoded[index]++;
|
|
|
- processingFlags[index] |= processedMask;
|
|
|
+ var resolution = component.resolutions[r];
|
|
|
+ var numprecincts = resolution.precinctParameters.numprecincts;
|
|
|
+ for (; k < numprecincts;) {
|
|
|
+ var packet = createPacket(resolution, k, l);
|
|
|
+ k++;
|
|
|
+ return packet;
|
|
|
}
|
|
|
+ k = 0;
|
|
|
}
|
|
|
+ i = 0;
|
|
|
}
|
|
|
- },
|
|
|
- runCleanupPass: function BitModel_runCleanupPass() {
|
|
|
- var decoder = this.decoder;
|
|
|
- var width = this.width, height = this.height;
|
|
|
- var neighborsSignificance = this.neighborsSignificance;
|
|
|
- var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
- var coefficentsSign = this.coefficentsSign;
|
|
|
- var contexts = this.contexts;
|
|
|
- var labels = this.contextLabelTable;
|
|
|
- var bitsDecoded = this.bitsDecoded;
|
|
|
- var processingFlags = this.processingFlags;
|
|
|
- var processedMask = 1;
|
|
|
- var firstMagnitudeBitMask = 2;
|
|
|
- var oneRowDown = width;
|
|
|
- var twoRowsDown = width * 2;
|
|
|
- var threeRowsDown = width * 3;
|
|
|
- var iNext;
|
|
|
- for (var i0 = 0; i0 < height; i0 = iNext) {
|
|
|
- iNext = Math.min(i0 + 4, height);
|
|
|
- var indexBase = i0 * width;
|
|
|
- var checkAllEmpty = i0 + 3 < height;
|
|
|
- for (var j = 0; j < width; j++) {
|
|
|
- var index0 = indexBase + j;
|
|
|
- // using the property: labels[neighborsSignificance[index]] == 0
|
|
|
- // when neighborsSignificance[index] == 0
|
|
|
- var allEmpty = (checkAllEmpty &&
|
|
|
- processingFlags[index0] === 0 &&
|
|
|
- processingFlags[index0 + oneRowDown] === 0 &&
|
|
|
- processingFlags[index0 + twoRowsDown] === 0 &&
|
|
|
- processingFlags[index0 + threeRowsDown] === 0 &&
|
|
|
- neighborsSignificance[index0] === 0 &&
|
|
|
- neighborsSignificance[index0 + oneRowDown] === 0 &&
|
|
|
- neighborsSignificance[index0 + twoRowsDown] === 0 &&
|
|
|
- neighborsSignificance[index0 + threeRowsDown] === 0);
|
|
|
- var i1 = 0, index = index0;
|
|
|
- var i = i0, sign;
|
|
|
- if (allEmpty) {
|
|
|
- var hasSignificantCoefficent =
|
|
|
- decoder.readBit(contexts, RUNLENGTH_CONTEXT);
|
|
|
- if (!hasSignificantCoefficent) {
|
|
|
- bitsDecoded[index0]++;
|
|
|
- bitsDecoded[index0 + oneRowDown]++;
|
|
|
- bitsDecoded[index0 + twoRowsDown]++;
|
|
|
- bitsDecoded[index0 + threeRowsDown]++;
|
|
|
- continue; // next column
|
|
|
- }
|
|
|
- i1 = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
|
|
|
- decoder.readBit(contexts, UNIFORM_CONTEXT);
|
|
|
- if (i1 !== 0) {
|
|
|
- i = i0 + i1;
|
|
|
- index += i1 * width;
|
|
|
- }
|
|
|
+ r = 0;
|
|
|
+ }
|
|
|
+ throw 'Out of packets';
|
|
|
+ };
|
|
|
+ }
|
|
|
+ function ResolutionLayerComponentPositionIterator(context) {
|
|
|
+ var siz = context.SIZ;
|
|
|
+ var tileIndex = context.currentTile.index;
|
|
|
+ var tile = context.tiles[tileIndex];
|
|
|
+ var layersCount = tile.codingStyleDefaultParameters.layersCount;
|
|
|
+ var componentsCount = siz.Csiz;
|
|
|
+ var maxDecompositionLevelsCount = 0;
|
|
|
+ for (var q = 0; q < componentsCount; q++) {
|
|
|
+ maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount,
|
|
|
+ tile.components[q].codingStyleParameters.decompositionLevelsCount);
|
|
|
+ }
|
|
|
|
|
|
- sign = this.decodeSignBit(i, j, index);
|
|
|
- coefficentsSign[index] = sign;
|
|
|
- coefficentsMagnitude[index] = 1;
|
|
|
- this.setNeighborsSignificance(i, j, index);
|
|
|
- processingFlags[index] |= firstMagnitudeBitMask;
|
|
|
+ var r = 0, l = 0, i = 0, k = 0;
|
|
|
|
|
|
- index = index0;
|
|
|
- for (var i2 = i0; i2 <= i; i2++, index += width) {
|
|
|
- bitsDecoded[index]++;
|
|
|
- }
|
|
|
+ this.nextPacket = function JpxImage_nextPacket() {
|
|
|
+ // Section B.12.1.2 Resolution-layer-component-position
|
|
|
+ for (; r <= maxDecompositionLevelsCount; r++) {
|
|
|
+ for (; l < layersCount; l++) {
|
|
|
+ for (; i < componentsCount; i++) {
|
|
|
+ var component = tile.components[i];
|
|
|
+ if (r > component.codingStyleParameters.decompositionLevelsCount) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- i1++;
|
|
|
+ var resolution = component.resolutions[r];
|
|
|
+ var numprecincts = resolution.precinctParameters.numprecincts;
|
|
|
+ for (; k < numprecincts;) {
|
|
|
+ var packet = createPacket(resolution, k, l);
|
|
|
+ k++;
|
|
|
+ return packet;
|
|
|
}
|
|
|
- for (i = i0 + i1; i < iNext; i++, index += width) {
|
|
|
- if (coefficentsMagnitude[index] ||
|
|
|
- (processingFlags[index] & processedMask) !== 0) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ k = 0;
|
|
|
+ }
|
|
|
+ i = 0;
|
|
|
+ }
|
|
|
+ l = 0;
|
|
|
+ }
|
|
|
+ throw 'Out of packets';
|
|
|
+ };
|
|
|
+ }
|
|
|
+ function buildPackets(context) {
|
|
|
+ var siz = context.SIZ;
|
|
|
+ var tileIndex = context.currentTile.index;
|
|
|
+ var tile = context.tiles[tileIndex];
|
|
|
+ var componentsCount = siz.Csiz;
|
|
|
+ // Creating resolutions and sub-bands for each component
|
|
|
+ for (var c = 0; c < componentsCount; c++) {
|
|
|
+ var component = tile.components[c];
|
|
|
+ var decompositionLevelsCount =
|
|
|
+ component.codingStyleParameters.decompositionLevelsCount;
|
|
|
+ // Section B.5 Resolution levels and sub-bands
|
|
|
+ var resolutions = [];
|
|
|
+ var subbands = [];
|
|
|
+ for (var r = 0; r <= decompositionLevelsCount; r++) {
|
|
|
+ var blocksDimensions = getBlocksDimensions(context, component, r);
|
|
|
+ var resolution = {};
|
|
|
+ var scale = 1 << (decompositionLevelsCount - r);
|
|
|
+ resolution.trx0 = Math.ceil(component.tcx0 / scale);
|
|
|
+ resolution.try0 = Math.ceil(component.tcy0 / scale);
|
|
|
+ resolution.trx1 = Math.ceil(component.tcx1 / scale);
|
|
|
+ resolution.try1 = Math.ceil(component.tcy1 / scale);
|
|
|
+ buildPrecincts(context, resolution, blocksDimensions);
|
|
|
+ resolutions.push(resolution);
|
|
|
|
|
|
- var contextLabel = labels[neighborsSignificance[index]];
|
|
|
- var decision = decoder.readBit(contexts, contextLabel);
|
|
|
- if (decision === 1) {
|
|
|
- sign = this.decodeSignBit(i, j, index);
|
|
|
- coefficentsSign[index] = sign;
|
|
|
- coefficentsMagnitude[index] = 1;
|
|
|
- this.setNeighborsSignificance(i, j, index);
|
|
|
- processingFlags[index] |= firstMagnitudeBitMask;
|
|
|
+ var subband;
|
|
|
+ if (r === 0) {
|
|
|
+ // one sub-band (LL) with last decomposition
|
|
|
+ subband = {};
|
|
|
+ subband.type = 'LL';
|
|
|
+ subband.tbx0 = Math.ceil(component.tcx0 / scale);
|
|
|
+ subband.tby0 = Math.ceil(component.tcy0 / scale);
|
|
|
+ subband.tbx1 = Math.ceil(component.tcx1 / scale);
|
|
|
+ subband.tby1 = Math.ceil(component.tcy1 / scale);
|
|
|
+ subband.resolution = resolution;
|
|
|
+ buildCodeblocks(context, subband, blocksDimensions);
|
|
|
+ subbands.push(subband);
|
|
|
+ resolution.subbands = [subband];
|
|
|
+ } else {
|
|
|
+ var bscale = 1 << (decompositionLevelsCount - r + 1);
|
|
|
+ var resolutionSubbands = [];
|
|
|
+ // three sub-bands (HL, LH and HH) with rest of decompositions
|
|
|
+ subband = {};
|
|
|
+ subband.type = 'HL';
|
|
|
+ subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
|
|
|
+ subband.tby0 = Math.ceil(component.tcy0 / bscale);
|
|
|
+ subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
|
|
|
+ subband.tby1 = Math.ceil(component.tcy1 / bscale);
|
|
|
+ subband.resolution = resolution;
|
|
|
+ buildCodeblocks(context, subband, blocksDimensions);
|
|
|
+ subbands.push(subband);
|
|
|
+ resolutionSubbands.push(subband);
|
|
|
+
|
|
|
+ subband = {};
|
|
|
+ subband.type = 'LH';
|
|
|
+ subband.tbx0 = Math.ceil(component.tcx0 / bscale);
|
|
|
+ subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
|
|
|
+ subband.tbx1 = Math.ceil(component.tcx1 / bscale);
|
|
|
+ subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
|
|
|
+ subband.resolution = resolution;
|
|
|
+ buildCodeblocks(context, subband, blocksDimensions);
|
|
|
+ subbands.push(subband);
|
|
|
+ resolutionSubbands.push(subband);
|
|
|
+
|
|
|
+ subband = {};
|
|
|
+ subband.type = 'HH';
|
|
|
+ subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
|
|
|
+ subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
|
|
|
+ subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
|
|
|
+ subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
|
|
|
+ subband.resolution = resolution;
|
|
|
+ buildCodeblocks(context, subband, blocksDimensions);
|
|
|
+ subbands.push(subband);
|
|
|
+ resolutionSubbands.push(subband);
|
|
|
+
|
|
|
+ resolution.subbands = resolutionSubbands;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ component.resolutions = resolutions;
|
|
|
+ component.subbands = subbands;
|
|
|
+ }
|
|
|
+ // Generate the packets sequence
|
|
|
+ var progressionOrder = tile.codingStyleDefaultParameters.progressionOrder;
|
|
|
+ switch (progressionOrder) {
|
|
|
+ case 0:
|
|
|
+ tile.packetsIterator =
|
|
|
+ new LayerResolutionComponentPositionIterator(context);
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ tile.packetsIterator =
|
|
|
+ new ResolutionLayerComponentPositionIterator(context);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw 'Unsupported progression order ' + progressionOrder;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function parseTilePackets(context, data, offset, dataLength) {
|
|
|
+ var position = 0;
|
|
|
+ var buffer, bufferSize = 0, skipNextBit = false;
|
|
|
+ function readBits(count) {
|
|
|
+ while (bufferSize < count) {
|
|
|
+ var b = data[offset + position];
|
|
|
+ position++;
|
|
|
+ if (skipNextBit) {
|
|
|
+ buffer = (buffer << 7) | b;
|
|
|
+ bufferSize += 7;
|
|
|
+ skipNextBit = false;
|
|
|
+ } else {
|
|
|
+ buffer = (buffer << 8) | b;
|
|
|
+ bufferSize += 8;
|
|
|
+ }
|
|
|
+ if (b == 0xFF) {
|
|
|
+ skipNextBit = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ bufferSize -= count;
|
|
|
+ return (buffer >>> bufferSize) & ((1 << count) - 1);
|
|
|
+ }
|
|
|
+ function alignToByte() {
|
|
|
+ bufferSize = 0;
|
|
|
+ if (skipNextBit) {
|
|
|
+ position++;
|
|
|
+ skipNextBit = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function readCodingpasses() {
|
|
|
+ if (readBits(1) === 0) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ if (readBits(1) === 0) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ var value = readBits(2);
|
|
|
+ if (value < 3) {
|
|
|
+ return value + 3;
|
|
|
+ }
|
|
|
+ value = readBits(5);
|
|
|
+ if (value < 31) {
|
|
|
+ return value + 6;
|
|
|
+ }
|
|
|
+ value = readBits(7);
|
|
|
+ return value + 37;
|
|
|
+ }
|
|
|
+ var tileIndex = context.currentTile.index;
|
|
|
+ var tile = context.tiles[tileIndex];
|
|
|
+ var packetsIterator = tile.packetsIterator;
|
|
|
+ while (position < dataLength) {
|
|
|
+ var packet = packetsIterator.nextPacket();
|
|
|
+ if (!readBits(1)) {
|
|
|
+ alignToByte();
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ var layerNumber = packet.layerNumber;
|
|
|
+ var queue = [], codeblock;
|
|
|
+ for (var i = 0, ii = packet.codeblocks.length; i < ii; i++) {
|
|
|
+ codeblock = packet.codeblocks[i];
|
|
|
+ var precinct = codeblock.precinct;
|
|
|
+ var codeblockColumn = codeblock.cbx - precinct.cbxMin;
|
|
|
+ var codeblockRow = codeblock.cby - precinct.cbyMin;
|
|
|
+ var codeblockIncluded = false;
|
|
|
+ var firstTimeInclusion = false;
|
|
|
+ var valueReady;
|
|
|
+ if ('included' in codeblock) {
|
|
|
+ codeblockIncluded = !!readBits(1);
|
|
|
+ } else {
|
|
|
+ // reading inclusion tree
|
|
|
+ precinct = codeblock.precinct;
|
|
|
+ var inclusionTree, zeroBitPlanesTree;
|
|
|
+ if ('inclusionTree' in precinct) {
|
|
|
+ inclusionTree = precinct.inclusionTree;
|
|
|
+ } else {
|
|
|
+ // building inclusion and zero bit-planes trees
|
|
|
+ var width = precinct.cbxMax - precinct.cbxMin + 1;
|
|
|
+ var height = precinct.cbyMax - precinct.cbyMin + 1;
|
|
|
+ inclusionTree = new InclusionTree(width, height, layerNumber);
|
|
|
+ zeroBitPlanesTree = new TagTree(width, height);
|
|
|
+ precinct.inclusionTree = inclusionTree;
|
|
|
+ precinct.zeroBitPlanesTree = zeroBitPlanesTree;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (inclusionTree.reset(codeblockColumn, codeblockRow, layerNumber)) {
|
|
|
+ while (true) {
|
|
|
+ if (readBits(1)) {
|
|
|
+ valueReady = !inclusionTree.nextLevel();
|
|
|
+ if (valueReady) {
|
|
|
+ codeblock.included = true;
|
|
|
+ codeblockIncluded = firstTimeInclusion = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ inclusionTree.incrementValue(layerNumber);
|
|
|
+ break;
|
|
|
}
|
|
|
- bitsDecoded[index]++;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- },
|
|
|
- checkSegmentationSymbol: function BitModel_checkSegmentationSymbol() {
|
|
|
- var decoder = this.decoder;
|
|
|
- var contexts = this.contexts;
|
|
|
- var symbol = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 3) |
|
|
|
- (decoder.readBit(contexts, UNIFORM_CONTEXT) << 2) |
|
|
|
- (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
|
|
|
- decoder.readBit(contexts, UNIFORM_CONTEXT);
|
|
|
- if (symbol != 0xA) {
|
|
|
- throw 'Invalid segmentation symbol';
|
|
|
+ if (!codeblockIncluded) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (firstTimeInclusion) {
|
|
|
+ zeroBitPlanesTree = precinct.zeroBitPlanesTree;
|
|
|
+ zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);
|
|
|
+ while (true) {
|
|
|
+ if (readBits(1)) {
|
|
|
+ valueReady = !zeroBitPlanesTree.nextLevel();
|
|
|
+ if (valueReady) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ zeroBitPlanesTree.incrementValue();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ codeblock.zeroBitPlanes = zeroBitPlanesTree.value;
|
|
|
+ }
|
|
|
+ var codingpasses = readCodingpasses();
|
|
|
+ while (readBits(1)) {
|
|
|
+ codeblock.Lblock++;
|
|
|
}
|
|
|
+ var codingpassesLog2 = log2(codingpasses);
|
|
|
+ // rounding down log2
|
|
|
+ var bits = ((codingpasses < (1 << codingpassesLog2)) ?
|
|
|
+ codingpassesLog2 - 1 : codingpassesLog2) + codeblock.Lblock;
|
|
|
+ var codedDataLength = readBits(bits);
|
|
|
+ queue.push({
|
|
|
+ codeblock: codeblock,
|
|
|
+ codingpasses: codingpasses,
|
|
|
+ dataLength: codedDataLength
|
|
|
+ });
|
|
|
}
|
|
|
- };
|
|
|
+ alignToByte();
|
|
|
+ while (queue.length > 0) {
|
|
|
+ var packetItem = queue.shift();
|
|
|
+ codeblock = packetItem.codeblock;
|
|
|
+ if (!('data' in codeblock)) {
|
|
|
+ codeblock.data = [];
|
|
|
+ }
|
|
|
+ codeblock.data.push({
|
|
|
+ data: data,
|
|
|
+ start: offset + position,
|
|
|
+ end: offset + position + packetItem.dataLength,
|
|
|
+ codingpasses: packetItem.codingpasses
|
|
|
+ });
|
|
|
+ position += packetItem.dataLength;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return position;
|
|
|
+ }
|
|
|
+ function copyCoefficients(coefficients, levelWidth, levelHeight, subband,
|
|
|
+ delta, mb, reversible, segmentationSymbolUsed) {
|
|
|
+ var x0 = subband.tbx0;
|
|
|
+ var y0 = subband.tby0;
|
|
|
+ var width = subband.tbx1 - subband.tbx0;
|
|
|
+ var codeblocks = subband.codeblocks;
|
|
|
+ var right = subband.type.charAt(0) === 'H' ? 1 : 0;
|
|
|
+ var bottom = subband.type.charAt(1) === 'H' ? levelWidth : 0;
|
|
|
|
|
|
- return BitModel;
|
|
|
- })();
|
|
|
+ for (var i = 0, ii = codeblocks.length; i < ii; ++i) {
|
|
|
+ var codeblock = codeblocks[i];
|
|
|
+ var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
|
|
|
+ var blockHeight = codeblock.tby1_ - codeblock.tby0_;
|
|
|
+ if (blockWidth === 0 || blockHeight === 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!('data' in codeblock)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- // Section F, Discrete wavelet transformation
|
|
|
- var Transform = (function TransformClosure() {
|
|
|
- function Transform() {}
|
|
|
+ var bitModel, currentCodingpassType;
|
|
|
+ bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
|
|
|
+ codeblock.zeroBitPlanes, mb);
|
|
|
+ currentCodingpassType = 2; // first bit plane starts from cleanup
|
|
|
|
|
|
- Transform.prototype.calculate =
|
|
|
- function transformCalculate(subbands, u0, v0) {
|
|
|
- var ll = subbands[0];
|
|
|
- for (var i = 1, ii = subbands.length; i < ii; i++) {
|
|
|
- ll = this.iterate(ll, subbands[i], u0, v0);
|
|
|
+ // collect data
|
|
|
+ var data = codeblock.data, totalLength = 0, codingpasses = 0;
|
|
|
+ var j, jj, dataItem;
|
|
|
+ for (j = 0, jj = data.length; j < jj; j++) {
|
|
|
+ dataItem = data[j];
|
|
|
+ totalLength += dataItem.end - dataItem.start;
|
|
|
+ codingpasses += dataItem.codingpasses;
|
|
|
}
|
|
|
- return ll;
|
|
|
- };
|
|
|
- Transform.prototype.extend = function extend(buffer, offset, size) {
|
|
|
- // Section F.3.7 extending... using max extension of 4
|
|
|
- var i1 = offset - 1, j1 = offset + 1;
|
|
|
- var i2 = offset + size - 2, j2 = offset + size;
|
|
|
- buffer[i1--] = buffer[j1++];
|
|
|
- buffer[j2++] = buffer[i2--];
|
|
|
- buffer[i1--] = buffer[j1++];
|
|
|
- buffer[j2++] = buffer[i2--];
|
|
|
- buffer[i1--] = buffer[j1++];
|
|
|
- buffer[j2++] = buffer[i2--];
|
|
|
- buffer[i1] = buffer[j1];
|
|
|
- buffer[j2] = buffer[i2];
|
|
|
- };
|
|
|
- Transform.prototype.iterate = function Transform_iterate(ll, hl_lh_hh,
|
|
|
- u0, v0) {
|
|
|
- var llWidth = ll.width, llHeight = ll.height, llItems = ll.items;
|
|
|
- var width = hl_lh_hh.width;
|
|
|
- var height = hl_lh_hh.height;
|
|
|
- var items = hl_lh_hh.items;
|
|
|
- var i, j, k, l, u, v;
|
|
|
+ var encodedData = new Uint8Array(totalLength);
|
|
|
+ var position = 0;
|
|
|
+ for (j = 0, jj = data.length; j < jj; j++) {
|
|
|
+ dataItem = data[j];
|
|
|
+ var chunk = dataItem.data.subarray(dataItem.start, dataItem.end);
|
|
|
+ encodedData.set(chunk, position);
|
|
|
+ position += chunk.length;
|
|
|
+ }
|
|
|
+ // decoding the item
|
|
|
+ var decoder = new ArithmeticDecoder(encodedData, 0, totalLength);
|
|
|
+ bitModel.setDecoder(decoder);
|
|
|
|
|
|
- // Interleave LL according to Section F.3.3
|
|
|
- for (k = 0, i = 0; i < llHeight; i++) {
|
|
|
- l = i * 2 * width;
|
|
|
- for (j = 0; j < llWidth; j++, k++, l += 2) {
|
|
|
- items[l] = llItems[k];
|
|
|
+ for (j = 0; j < codingpasses; j++) {
|
|
|
+ switch (currentCodingpassType) {
|
|
|
+ case 0:
|
|
|
+ bitModel.runSignificancePropogationPass();
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ bitModel.runMagnitudeRefinementPass();
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ bitModel.runCleanupPass();
|
|
|
+ if (segmentationSymbolUsed) {
|
|
|
+ bitModel.checkSegmentationSymbol();
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
+ currentCodingpassType = (currentCodingpassType + 1) % 3;
|
|
|
}
|
|
|
- // The LL band is not needed anymore.
|
|
|
- llItems = ll.items = null;
|
|
|
|
|
|
- var bufferPadding = 4;
|
|
|
- var rowBuffer = new Float32Array(width + 2 * bufferPadding);
|
|
|
-
|
|
|
- // Section F.3.4 HOR_SR
|
|
|
- if (width === 1) {
|
|
|
- // if width = 1, when u0 even keep items as is, when odd divide by 2
|
|
|
- if ((u0 & 1) !== 0) {
|
|
|
- for (v = 0, k = 0; v < height; v++, k += width) {
|
|
|
- items[k] *= 0.5;
|
|
|
+ var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width;
|
|
|
+ var sign = bitModel.coefficentsSign;
|
|
|
+ var magnitude = bitModel.coefficentsMagnitude;
|
|
|
+ var bitsDecoded = bitModel.bitsDecoded;
|
|
|
+ var magnitudeCorrection = reversible ? 0 : 0.5;
|
|
|
+ var k, n, nb;
|
|
|
+ position = 0;
|
|
|
+ // Do the interleaving of Section F.3.3 here, so we do not need
|
|
|
+ // to copy later. LL level is not interleaved, just copied.
|
|
|
+ var interleave = (subband.type !== 'LL');
|
|
|
+ for (j = 0; j < blockHeight; j++) {
|
|
|
+ var row = (offset / width) | 0; // row in the non-interleaved subband
|
|
|
+ var levelOffset = 2 * row * (levelWidth - width) + right + bottom;
|
|
|
+ for (k = 0; k < blockWidth; k++) {
|
|
|
+ n = magnitude[position];
|
|
|
+ if (n !== 0) {
|
|
|
+ n = (n + magnitudeCorrection) * delta;
|
|
|
+ if (sign[position] !== 0) {
|
|
|
+ n = -n;
|
|
|
+ }
|
|
|
+ nb = bitsDecoded[position];
|
|
|
+ var pos = interleave ? (levelOffset + (offset << 1)) : offset;
|
|
|
+ if (reversible && (nb >= mb)) {
|
|
|
+ coefficients[pos] = n;
|
|
|
+ } else {
|
|
|
+ coefficients[pos] = n * (1 << (mb - nb));
|
|
|
+ }
|
|
|
}
|
|
|
+ offset++;
|
|
|
+ position++;
|
|
|
}
|
|
|
- } else {
|
|
|
- for (v = 0, k = 0; v < height; v++, k += width) {
|
|
|
- rowBuffer.set(items.subarray(k, k + width), bufferPadding);
|
|
|
+ offset += width - blockWidth;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function transformTile(context, tile, c) {
|
|
|
+ var component = tile.components[c];
|
|
|
+ var codingStyleParameters = component.codingStyleParameters;
|
|
|
+ var quantizationParameters = component.quantizationParameters;
|
|
|
+ var decompositionLevelsCount =
|
|
|
+ codingStyleParameters.decompositionLevelsCount;
|
|
|
+ var spqcds = quantizationParameters.SPqcds;
|
|
|
+ var scalarExpounded = quantizationParameters.scalarExpounded;
|
|
|
+ var guardBits = quantizationParameters.guardBits;
|
|
|
+ var segmentationSymbolUsed = codingStyleParameters.segmentationSymbolUsed;
|
|
|
+ var precision = context.components[c].precision;
|
|
|
|
|
|
- this.extend(rowBuffer, bufferPadding, width);
|
|
|
- this.filter(rowBuffer, bufferPadding, width);
|
|
|
+ var reversible = codingStyleParameters.reversibleTransformation;
|
|
|
+ var transform = (reversible ? new ReversibleTransform() :
|
|
|
+ new IrreversibleTransform());
|
|
|
|
|
|
- items.set(
|
|
|
- rowBuffer.subarray(bufferPadding, bufferPadding + width),
|
|
|
- k);
|
|
|
- }
|
|
|
- }
|
|
|
+ var subbandCoefficients = [];
|
|
|
+ var b = 0;
|
|
|
+ for (var i = 0; i <= decompositionLevelsCount; i++) {
|
|
|
+ var resolution = component.resolutions[i];
|
|
|
|
|
|
- // Accesses to the items array can take long, because it may not fit into
|
|
|
- // CPU cache and has to be fetched from main memory. Since subsequent
|
|
|
- // accesses to the items array are not local when reading columns, we
|
|
|
- // have a cache miss every time. To reduce cache misses, get up to
|
|
|
- // 'numBuffers' items at a time and store them into the individual
|
|
|
- // buffers. The colBuffers should be small enough to fit into CPU cache.
|
|
|
- var numBuffers = 16;
|
|
|
- var colBuffers = [];
|
|
|
- for (i = 0; i < numBuffers; i++) {
|
|
|
- colBuffers.push(new Float32Array(height + 2 * bufferPadding));
|
|
|
- }
|
|
|
- var b, currentBuffer = 0;
|
|
|
- ll = bufferPadding + height;
|
|
|
+ var width = resolution.trx1 - resolution.trx0;
|
|
|
+ var height = resolution.try1 - resolution.try0;
|
|
|
+ // Allocate space for the whole sublevel.
|
|
|
+ var coefficients = new Float32Array(width * height);
|
|
|
|
|
|
- // Section F.3.5 VER_SR
|
|
|
- if (height === 1) {
|
|
|
- // if height = 1, when v0 even keep items as is, when odd divide by 2
|
|
|
- if ((v0 & 1) !== 0) {
|
|
|
- for (u = 0; u < width; u++) {
|
|
|
- items[u] *= 0.5;
|
|
|
- }
|
|
|
+ for (var j = 0, jj = resolution.subbands.length; j < jj; j++) {
|
|
|
+ var mu, epsilon;
|
|
|
+ if (!scalarExpounded) {
|
|
|
+ // formula E-5
|
|
|
+ mu = spqcds[0].mu;
|
|
|
+ epsilon = spqcds[0].epsilon + (i > 0 ? 1 - i : 0);
|
|
|
+ } else {
|
|
|
+ mu = spqcds[b].mu;
|
|
|
+ epsilon = spqcds[b].epsilon;
|
|
|
+ b++;
|
|
|
}
|
|
|
- } else {
|
|
|
- for (u = 0; u < width; u++) {
|
|
|
- // if we ran out of buffers, copy several image columns at once
|
|
|
- if (currentBuffer === 0) {
|
|
|
- numBuffers = Math.min(width - u, numBuffers);
|
|
|
- for (k = u, l = bufferPadding; l < ll; k += width, l++) {
|
|
|
- for (b = 0; b < numBuffers; b++) {
|
|
|
- colBuffers[b][l] = items[k + b];
|
|
|
- }
|
|
|
- }
|
|
|
- currentBuffer = numBuffers;
|
|
|
- }
|
|
|
|
|
|
- currentBuffer--;
|
|
|
- var buffer = colBuffers[currentBuffer];
|
|
|
- this.extend(buffer, bufferPadding, height);
|
|
|
- this.filter(buffer, bufferPadding, height);
|
|
|
+ var subband = resolution.subbands[j];
|
|
|
+ var gainLog2 = SubbandsGainLog2[subband.type];
|
|
|
|
|
|
- // If this is last buffer in this group of buffers, flush all buffers.
|
|
|
- if (currentBuffer === 0) {
|
|
|
- k = u - numBuffers + 1;
|
|
|
- for (l = bufferPadding; l < ll; k += width, l++) {
|
|
|
- for (b = 0; b < numBuffers; b++) {
|
|
|
- items[k + b] = colBuffers[b][l];
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // calulate quantization coefficient (Section E.1.1.1)
|
|
|
+ var delta = (reversible ? 1 :
|
|
|
+ Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048));
|
|
|
+ var mb = (guardBits + epsilon - 1);
|
|
|
|
|
|
- return {
|
|
|
+ // In the first resolution level, copyCoefficients will fill the
|
|
|
+ // whole array with coefficients. In the succeding passes,
|
|
|
+ // copyCoefficients will consecutively fill in the values that belong
|
|
|
+ // to the interleaved positions of the HL, LH, and HH coefficients.
|
|
|
+ // The LL coefficients will then be interleaved in Transform.iterate().
|
|
|
+ copyCoefficients(coefficients, width, height, subband, delta, mb,
|
|
|
+ reversible, segmentationSymbolUsed);
|
|
|
+ }
|
|
|
+ subbandCoefficients.push({
|
|
|
width: width,
|
|
|
height: height,
|
|
|
- items: items
|
|
|
- };
|
|
|
- };
|
|
|
- return Transform;
|
|
|
- })();
|
|
|
-
|
|
|
- // Section 3.8.2 Irreversible 9-7 filter
|
|
|
- var IrreversibleTransform = (function IrreversibleTransformClosure() {
|
|
|
- function IrreversibleTransform() {
|
|
|
- Transform.call(this);
|
|
|
+ items: coefficients
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- IrreversibleTransform.prototype = Object.create(Transform.prototype);
|
|
|
- IrreversibleTransform.prototype.filter =
|
|
|
- function irreversibleTransformFilter(x, offset, length) {
|
|
|
- var len = length >> 1;
|
|
|
- offset = offset | 0;
|
|
|
- var j, n, current, next;
|
|
|
-
|
|
|
- var alpha = -1.586134342059924;
|
|
|
- var beta = -0.052980118572961;
|
|
|
- var gamma = 0.882911075530934;
|
|
|
- var delta = 0.443506852043971;
|
|
|
- var K = 1.230174104914001;
|
|
|
- var K_ = 1 / K;
|
|
|
+ var result = transform.calculate(subbandCoefficients,
|
|
|
+ component.tcx0, component.tcy0);
|
|
|
+ return {
|
|
|
+ left: component.tcx0,
|
|
|
+ top: component.tcy0,
|
|
|
+ width: result.width,
|
|
|
+ height: result.height,
|
|
|
+ items: result.items
|
|
|
+ };
|
|
|
+ }
|
|
|
+ function transformComponents(context) {
|
|
|
+ var siz = context.SIZ;
|
|
|
+ var components = context.components;
|
|
|
+ var componentsCount = siz.Csiz;
|
|
|
+ var resultImages = [];
|
|
|
+ for (var i = 0, ii = context.tiles.length; i < ii; i++) {
|
|
|
+ var tile = context.tiles[i];
|
|
|
+ var transformedTiles = [];
|
|
|
+ var c;
|
|
|
+ for (c = 0; c < componentsCount; c++) {
|
|
|
+ transformedTiles[c] = transformTile(context, tile, c);
|
|
|
+ }
|
|
|
+ var tile0 = transformedTiles[0];
|
|
|
+ var out = new Uint8Array(tile0.items.length * componentsCount);
|
|
|
+ var result = {
|
|
|
+ left: tile0.left,
|
|
|
+ top: tile0.top,
|
|
|
+ width: tile0.width,
|
|
|
+ height: tile0.height,
|
|
|
+ items: out
|
|
|
+ };
|
|
|
|
|
|
- // step 1 is combined with step 3
|
|
|
+ // Section G.2.2 Inverse multi component transform
|
|
|
+ var shift, offset, max, min;
|
|
|
+ var pos = 0, j, jj, y0, y1, y2, r, g, b, k, val;
|
|
|
+ if (tile.codingStyleDefaultParameters.multipleComponentTransform) {
|
|
|
+ var fourComponents = componentsCount === 4;
|
|
|
+ var y0items = transformedTiles[0].items;
|
|
|
+ var y1items = transformedTiles[1].items;
|
|
|
+ var y2items = transformedTiles[2].items;
|
|
|
+ var y3items = fourComponents ? transformedTiles[3].items : null;
|
|
|
|
|
|
- // step 2
|
|
|
- j = offset - 3;
|
|
|
- for (n = len + 4; n--; j += 2) {
|
|
|
- x[j] *= K_;
|
|
|
- }
|
|
|
+ // HACK: The multiple component transform formulas below assume that
|
|
|
+ // all components have the same precision. With this in mind, we
|
|
|
+ // compute shift and offset only once.
|
|
|
+ shift = components[0].precision - 8;
|
|
|
+ offset = (128 << shift) + 0.5;
|
|
|
+ max = (127.5 * (1 << shift));
|
|
|
+ min = -max;
|
|
|
|
|
|
- // step 1 & 3
|
|
|
- j = offset - 2;
|
|
|
- current = delta * x[j -1];
|
|
|
- for (n = len + 3; n--; j += 2) {
|
|
|
- next = delta * x[j + 1];
|
|
|
- x[j] = K * x[j] - current - next;
|
|
|
- if (n--) {
|
|
|
- j += 2;
|
|
|
- current = delta * x[j + 1];
|
|
|
- x[j] = K * x[j] - current - next;
|
|
|
+ var component0 = tile.components[0];
|
|
|
+ if (!component0.codingStyleParameters.reversibleTransformation) {
|
|
|
+ // inverse irreversible multiple component transform
|
|
|
+ for (j = 0, jj = y0items.length; j < jj; ++j) {
|
|
|
+ y0 = y0items[j];
|
|
|
+ y1 = y1items[j];
|
|
|
+ y2 = y2items[j];
|
|
|
+ r = y0 + 1.402 * y2;
|
|
|
+ g = y0 - 0.34413 * y1 - 0.71414 * y2;
|
|
|
+ b = y0 + 1.772 * y1;
|
|
|
+ out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift;
|
|
|
+ out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift;
|
|
|
+ out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift;
|
|
|
+ if (fourComponents) {
|
|
|
+ k = y3items[j];
|
|
|
+ out[pos++] =
|
|
|
+ k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift;
|
|
|
+ }
|
|
|
+ }
|
|
|
} else {
|
|
|
- break;
|
|
|
+ // inverse reversible multiple component transform
|
|
|
+ for (j = 0, jj = y0items.length; j < jj; ++j) {
|
|
|
+ y0 = y0items[j];
|
|
|
+ y1 = y1items[j];
|
|
|
+ y2 = y2items[j];
|
|
|
+ g = y0 - ((y2 + y1) >> 2);
|
|
|
+ r = g + y2;
|
|
|
+ b = g + y1;
|
|
|
+ out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift;
|
|
|
+ out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift;
|
|
|
+ out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift;
|
|
|
+ if (fourComponents) {
|
|
|
+ k = y3items[j];
|
|
|
+ out[pos++] =
|
|
|
+ k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // step 4
|
|
|
- j = offset - 1;
|
|
|
- current = gamma * x[j - 1];
|
|
|
- for (n = len + 2; n--; j += 2) {
|
|
|
- next = gamma * x[j + 1];
|
|
|
- x[j] -= current + next;
|
|
|
- if (n--) {
|
|
|
- j += 2;
|
|
|
- current = gamma * x[j + 1];
|
|
|
- x[j] -= current + next;
|
|
|
- } else {
|
|
|
- break;
|
|
|
+ } else { // no multi-component transform
|
|
|
+ for (c = 0; c < componentsCount; c++) {
|
|
|
+ var items = transformedTiles[c].items;
|
|
|
+ shift = components[c].precision - 8;
|
|
|
+ offset = (128 << shift) + 0.5;
|
|
|
+ max = (127.5 * (1 << shift));
|
|
|
+ min = -max;
|
|
|
+ for (pos = c, j = 0, jj = items.length; j < jj; j++) {
|
|
|
+ val = items[j];
|
|
|
+ out[pos] = val <= min ? 0 :
|
|
|
+ val >= max ? 255 : (val + offset) >> shift;
|
|
|
+ pos += componentsCount;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+ resultImages.push(result);
|
|
|
+ }
|
|
|
+ return resultImages;
|
|
|
+ }
|
|
|
+ function initializeTile(context, tileIndex) {
|
|
|
+ var siz = context.SIZ;
|
|
|
+ var componentsCount = siz.Csiz;
|
|
|
+ var tile = context.tiles[tileIndex];
|
|
|
+ for (var c = 0; c < componentsCount; c++) {
|
|
|
+ var component = tile.components[c];
|
|
|
+ var qcdOrQcc = (c in context.currentTile.QCC ?
|
|
|
+ context.currentTile.QCC[c] : context.currentTile.QCD);
|
|
|
+ component.quantizationParameters = qcdOrQcc;
|
|
|
+ var codOrCoc = (c in context.currentTile.COC ?
|
|
|
+ context.currentTile.COC[c] : context.currentTile.COD);
|
|
|
+ component.codingStyleParameters = codOrCoc;
|
|
|
+ }
|
|
|
+ tile.codingStyleDefaultParameters = context.currentTile.COD;
|
|
|
+ }
|
|
|
|
|
|
- // step 5
|
|
|
- j = offset;
|
|
|
- current = beta * x[j - 1];
|
|
|
- for (n = len + 1; n--; j += 2) {
|
|
|
- next = beta * x[j + 1];
|
|
|
- x[j] -= current + next;
|
|
|
- if (n--) {
|
|
|
- j += 2;
|
|
|
- current = beta * x[j + 1];
|
|
|
- x[j] -= current + next;
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
+ // Section B.10.2 Tag trees
|
|
|
+ var TagTree = (function TagTreeClosure() {
|
|
|
+ function TagTree(width, height) {
|
|
|
+ var levelsLength = log2(Math.max(width, height)) + 1;
|
|
|
+ this.levels = [];
|
|
|
+ for (var i = 0; i < levelsLength; i++) {
|
|
|
+ var level = {
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ items: []
|
|
|
+ };
|
|
|
+ this.levels.push(level);
|
|
|
+ width = Math.ceil(width / 2);
|
|
|
+ height = Math.ceil(height / 2);
|
|
|
}
|
|
|
-
|
|
|
- // step 6
|
|
|
- if (len !== 0) {
|
|
|
- j = offset + 1;
|
|
|
- current = alpha * x[j - 1];
|
|
|
- for (n = len; n--; j += 2) {
|
|
|
- next = alpha * x[j + 1];
|
|
|
- x[j] -= current + next;
|
|
|
- if (n--) {
|
|
|
- j += 2;
|
|
|
- current = alpha * x[j + 1];
|
|
|
- x[j] -= current + next;
|
|
|
- } else {
|
|
|
+ }
|
|
|
+ TagTree.prototype = {
|
|
|
+ reset: function TagTree_reset(i, j) {
|
|
|
+ var currentLevel = 0, value = 0, level;
|
|
|
+ while (currentLevel < this.levels.length) {
|
|
|
+ level = this.levels[currentLevel];
|
|
|
+ var index = i + j * level.width;
|
|
|
+ if (index in level.items) {
|
|
|
+ value = level.items[index];
|
|
|
break;
|
|
|
}
|
|
|
+ level.index = index;
|
|
|
+ i >>= 1;
|
|
|
+ j >>= 1;
|
|
|
+ currentLevel++;
|
|
|
+ }
|
|
|
+ currentLevel--;
|
|
|
+ level = this.levels[currentLevel];
|
|
|
+ level.items[level.index] = value;
|
|
|
+ this.currentLevel = currentLevel;
|
|
|
+ delete this.value;
|
|
|
+ },
|
|
|
+ incrementValue: function TagTree_incrementValue() {
|
|
|
+ var level = this.levels[this.currentLevel];
|
|
|
+ level.items[level.index]++;
|
|
|
+ },
|
|
|
+ nextLevel: function TagTree_nextLevel() {
|
|
|
+ var currentLevel = this.currentLevel;
|
|
|
+ var level = this.levels[currentLevel];
|
|
|
+ var value = level.items[level.index];
|
|
|
+ currentLevel--;
|
|
|
+ if (currentLevel < 0) {
|
|
|
+ this.value = value;
|
|
|
+ return false;
|
|
|
}
|
|
|
+
|
|
|
+ this.currentLevel = currentLevel;
|
|
|
+ level = this.levels[currentLevel];
|
|
|
+ level.items[level.index] = value;
|
|
|
+ return true;
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
- return IrreversibleTransform;
|
|
|
+ return TagTree;
|
|
|
})();
|
|
|
|
|
|
- // Section 3.8.1 Reversible 5-3 filter
|
|
|
- var ReversibleTransform = (function ReversibleTransformClosure() {
|
|
|
- function ReversibleTransform() {
|
|
|
- Transform.call(this);
|
|
|
- }
|
|
|
+ var InclusionTree = (function InclusionTreeClosure() {
|
|
|
+ function InclusionTree(width, height, defaultValue) {
|
|
|
+ var levelsLength = log2(Math.max(width, height)) + 1;
|
|
|
+ this.levels = [];
|
|
|
+ for (var i = 0; i < levelsLength; i++) {
|
|
|
+ var items = new Uint8Array(width * height);
|
|
|
+ for (var j = 0, jj = items.length; j < jj; j++) {
|
|
|
+ items[j] = defaultValue;
|
|
|
+ }
|
|
|
|
|
|
- ReversibleTransform.prototype = Object.create(Transform.prototype);
|
|
|
- ReversibleTransform.prototype.filter =
|
|
|
- function reversibleTransformFilter(x, offset, length) {
|
|
|
- var len = length >> 1;
|
|
|
- offset = offset | 0;
|
|
|
- var j, n;
|
|
|
+ var level = {
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ items: items
|
|
|
+ };
|
|
|
+ this.levels.push(level);
|
|
|
|
|
|
- for (j = offset, n = len + 1; n--; j += 2) {
|
|
|
- x[j] -= (x[j - 1] + x[j + 1] + 2) >> 2;
|
|
|
+ width = Math.ceil(width / 2);
|
|
|
+ height = Math.ceil(height / 2);
|
|
|
}
|
|
|
+ }
|
|
|
+ InclusionTree.prototype = {
|
|
|
+ reset: function InclusionTree_reset(i, j, stopValue) {
|
|
|
+ var currentLevel = 0;
|
|
|
+ while (currentLevel < this.levels.length) {
|
|
|
+ var level = this.levels[currentLevel];
|
|
|
+ var index = i + j * level.width;
|
|
|
+ level.index = index;
|
|
|
+ var value = level.items[index];
|
|
|
|
|
|
- for (j = offset + 1, n = len; n--; j += 2) {
|
|
|
- x[j] += (x[j - 1] + x[j + 1]) >> 1;
|
|
|
+ if (value == 0xFF) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (value > stopValue) {
|
|
|
+ this.currentLevel = currentLevel;
|
|
|
+ // already know about this one, propagating the value to top levels
|
|
|
+ this.propagateValues();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ i >>= 1;
|
|
|
+ j >>= 1;
|
|
|
+ currentLevel++;
|
|
|
+ }
|
|
|
+ this.currentLevel = currentLevel - 1;
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ incrementValue: function InclusionTree_incrementValue(stopValue) {
|
|
|
+ var level = this.levels[this.currentLevel];
|
|
|
+ level.items[level.index] = stopValue + 1;
|
|
|
+ this.propagateValues();
|
|
|
+ },
|
|
|
+ propagateValues: function InclusionTree_propagateValues() {
|
|
|
+ var levelIndex = this.currentLevel;
|
|
|
+ var level = this.levels[levelIndex];
|
|
|
+ var currentValue = level.items[level.index];
|
|
|
+ while (--levelIndex >= 0) {
|
|
|
+ level = this.levels[levelIndex];
|
|
|
+ level.items[level.index] = currentValue;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ nextLevel: function InclusionTree_nextLevel() {
|
|
|
+ var currentLevel = this.currentLevel;
|
|
|
+ var level = this.levels[currentLevel];
|
|
|
+ var value = level.items[level.index];
|
|
|
+ level.items[level.index] = 0xFF;
|
|
|
+ currentLevel--;
|
|
|
+ if (currentLevel < 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.currentLevel = currentLevel;
|
|
|
+ level = this.levels[currentLevel];
|
|
|
+ level.items[level.index] = value;
|
|
|
+ return true;
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
- return ReversibleTransform;
|
|
|
+ return InclusionTree;
|
|
|
})();
|
|
|
|
|
|
- return JpxImage;
|
|
|
-})();
|
|
|
+ // Section D. Coefficient bit modeling
|
|
|
+ var BitModel = (function BitModelClosure() {
|
|
|
+ var UNIFORM_CONTEXT = 17;
|
|
|
+ var RUNLENGTH_CONTEXT = 18;
|
|
|
+ // Table D-1
|
|
|
+ // The index is binary presentation: 0dddvvhh, ddd - sum of Di (0..4),
|
|
|
+ // vv - sum of Vi (0..2), and hh - sum of Hi (0..2)
|
|
|
+ var LLAndLHContextsLabel = new Uint8Array([
|
|
|
+ 0, 5, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 1, 6, 8, 0, 3, 7, 8, 0, 4,
|
|
|
+ 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6,
|
|
|
+ 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8
|
|
|
+ ]);
|
|
|
+ var HLContextLabel = new Uint8Array([
|
|
|
+ 0, 3, 4, 0, 5, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 1, 3, 4, 0, 6, 7, 7, 0, 8,
|
|
|
+ 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3,
|
|
|
+ 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8
|
|
|
+ ]);
|
|
|
+ var HHContextLabel = new Uint8Array([
|
|
|
+ 0, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 4, 5, 0, 4, 5, 5, 0, 5,
|
|
|
+ 5, 5, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 8, 8,
|
|
|
+ 8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8
|
|
|
+ ]);
|
|
|
+
|
|
|
+ function BitModel(width, height, subband, zeroBitPlanes, mb) {
|
|
|
+ this.width = width;
|
|
|
+ this.height = height;
|
|
|
|
|
|
+ this.contextLabelTable = (subband == 'HH' ? HHContextLabel :
|
|
|
+ (subband == 'HL' ? HLContextLabel : LLAndLHContextsLabel));
|
|
|
|
|
|
+ var coefficientCount = width * height;
|
|
|
|
|
|
-var Jbig2Image = (function Jbig2ImageClosure() {
|
|
|
- // Utility data structures
|
|
|
- function ContextCache() {}
|
|
|
+ // coefficients outside the encoding region treated as insignificant
|
|
|
+ // add border state cells for significanceState
|
|
|
+ this.neighborsSignificance = new Uint8Array(coefficientCount);
|
|
|
+ this.coefficentsSign = new Uint8Array(coefficientCount);
|
|
|
+ this.coefficentsMagnitude = mb > 14 ? new Uint32Array(coefficientCount) :
|
|
|
+ mb > 6 ? new Uint16Array(coefficientCount) :
|
|
|
+ new Uint8Array(coefficientCount);
|
|
|
+ this.processingFlags = new Uint8Array(coefficientCount);
|
|
|
|
|
|
- ContextCache.prototype = {
|
|
|
- getContexts: function(id) {
|
|
|
- if (id in this) {
|
|
|
- return this[id];
|
|
|
+ var bitsDecoded = new Uint8Array(coefficientCount);
|
|
|
+ if (zeroBitPlanes !== 0) {
|
|
|
+ for (var i = 0; i < coefficientCount; i++) {
|
|
|
+ bitsDecoded[i] = zeroBitPlanes;
|
|
|
+ }
|
|
|
}
|
|
|
- return (this[id] = new Int8Array(1<<16));
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- function DecodingContext(data, start, end) {
|
|
|
- this.data = data;
|
|
|
- this.start = start;
|
|
|
- this.end = end;
|
|
|
- }
|
|
|
+ this.bitsDecoded = bitsDecoded;
|
|
|
|
|
|
- DecodingContext.prototype = {
|
|
|
- get decoder() {
|
|
|
- var decoder = new ArithmeticDecoder(this.data, this.start, this.end);
|
|
|
- return shadow(this, 'decoder', decoder);
|
|
|
- },
|
|
|
- get contextCache() {
|
|
|
- var cache = new ContextCache();
|
|
|
- return shadow(this, 'contextCache', cache);
|
|
|
+ this.reset();
|
|
|
}
|
|
|
- };
|
|
|
|
|
|
- // Annex A. Arithmetic Integer Decoding Procedure
|
|
|
- // A.2 Procedure for decoding values
|
|
|
- function decodeInteger(contextCache, procedure, decoder) {
|
|
|
- var contexts = contextCache.getContexts(procedure);
|
|
|
+ BitModel.prototype = {
|
|
|
+ setDecoder: function BitModel_setDecoder(decoder) {
|
|
|
+ this.decoder = decoder;
|
|
|
+ },
|
|
|
+ reset: function BitModel_reset() {
|
|
|
+ // We have 17 contexts that are accessed via context labels,
|
|
|
+ // plus the uniform and runlength context.
|
|
|
+ this.contexts = new Int8Array(19);
|
|
|
|
|
|
- var prev = 1;
|
|
|
- var state = 1, v = 0, s;
|
|
|
- var toRead = 32, offset = 4436; // defaults for state 7
|
|
|
- while (state) {
|
|
|
- var bit = decoder.readBit(contexts, prev);
|
|
|
- prev = (prev < 256 ? (prev << 1) | bit :
|
|
|
- (((prev << 1) | bit) & 511) | 256);
|
|
|
- switch (state) {
|
|
|
- case 1:
|
|
|
- s = !!bit;
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- if (bit) {
|
|
|
- break;
|
|
|
- }
|
|
|
- state = 7;
|
|
|
- toRead = 2;
|
|
|
- offset = 0;
|
|
|
- break;
|
|
|
- case 3:
|
|
|
- if (bit) {
|
|
|
- break;
|
|
|
- }
|
|
|
- state = 7;
|
|
|
- toRead = 4;
|
|
|
- offset = 4;
|
|
|
- break;
|
|
|
- case 4:
|
|
|
- if (bit) {
|
|
|
- break;
|
|
|
+ // Contexts are packed into 1 byte:
|
|
|
+ // highest 7 bits carry the index, lowest bit carries mps
|
|
|
+ this.contexts[0] = (4 << 1) | 0;
|
|
|
+ this.contexts[UNIFORM_CONTEXT] = (46 << 1) | 0;
|
|
|
+ this.contexts[RUNLENGTH_CONTEXT] = (3 << 1) | 0;
|
|
|
+ },
|
|
|
+ setNeighborsSignificance:
|
|
|
+ function BitModel_setNeighborsSignificance(row, column, index) {
|
|
|
+ var neighborsSignificance = this.neighborsSignificance;
|
|
|
+ var width = this.width, height = this.height;
|
|
|
+ var left = (column > 0);
|
|
|
+ var right = (column + 1 < width);
|
|
|
+ var i;
|
|
|
+
|
|
|
+ if (row > 0) {
|
|
|
+ i = index - width;
|
|
|
+ if (left) {
|
|
|
+ neighborsSignificance[i - 1] += 0x10;
|
|
|
}
|
|
|
- state = 7;
|
|
|
- toRead = 6;
|
|
|
- offset = 20;
|
|
|
- break;
|
|
|
- case 5:
|
|
|
- if (bit) {
|
|
|
- break;
|
|
|
+ if (right) {
|
|
|
+ neighborsSignificance[i + 1] += 0x10;
|
|
|
}
|
|
|
- state = 7;
|
|
|
- toRead = 8;
|
|
|
- offset = 84;
|
|
|
- break;
|
|
|
- case 6:
|
|
|
- if (bit) {
|
|
|
- break;
|
|
|
+ neighborsSignificance[i] += 0x04;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (row + 1 < height) {
|
|
|
+ i = index + width;
|
|
|
+ if (left) {
|
|
|
+ neighborsSignificance[i - 1] += 0x10;
|
|
|
}
|
|
|
- state = 7;
|
|
|
- toRead = 12;
|
|
|
- offset = 340;
|
|
|
- break;
|
|
|
- default:
|
|
|
- v = ((v << 1) | bit) >>> 0;
|
|
|
- if (--toRead === 0) {
|
|
|
- state = 0;
|
|
|
+ if (right) {
|
|
|
+ neighborsSignificance[i + 1] += 0x10;
|
|
|
}
|
|
|
- continue;
|
|
|
- }
|
|
|
- state++;
|
|
|
- }
|
|
|
- v += offset;
|
|
|
- return (!s ? v : (v > 0 ? -v : null));
|
|
|
- }
|
|
|
-
|
|
|
- // A.3 The IAID decoding procedure
|
|
|
- function decodeIAID(contextCache, decoder, codeLength) {
|
|
|
- var contexts = contextCache.getContexts('IAID');
|
|
|
-
|
|
|
- var prev = 1;
|
|
|
- for (var i = 0; i < codeLength; i++) {
|
|
|
- var bit = decoder.readBit(contexts, prev);
|
|
|
- prev = (prev << 1) | bit;
|
|
|
- }
|
|
|
- if (codeLength < 31) {
|
|
|
- return prev & ((1 << codeLength) - 1);
|
|
|
- }
|
|
|
- return prev & 0x7FFFFFFF;
|
|
|
- }
|
|
|
-
|
|
|
- // 7.3 Segment types
|
|
|
- var SegmentTypes = [
|
|
|
- 'SymbolDictionary', null, null, null, 'IntermediateTextRegion', null,
|
|
|
- 'ImmediateTextRegion', 'ImmediateLosslessTextRegion', null, null, null,
|
|
|
- null, null, null, null, null, 'patternDictionary', null, null, null,
|
|
|
- 'IntermediateHalftoneRegion', null, 'ImmediateHalftoneRegion',
|
|
|
- 'ImmediateLosslessHalftoneRegion', null, null, null, null, null, null, null,
|
|
|
- null, null, null, null, null, 'IntermediateGenericRegion', null,
|
|
|
- 'ImmediateGenericRegion', 'ImmediateLosslessGenericRegion',
|
|
|
- 'IntermediateGenericRefinementRegion', null,
|
|
|
- 'ImmediateGenericRefinementRegion',
|
|
|
- 'ImmediateLosslessGenericRefinementRegion', null, null, null, null,
|
|
|
- 'PageInformation', 'EndOfPage', 'EndOfStripe', 'EndOfFile', 'Profiles',
|
|
|
- 'Tables', null, null, null, null, null, null, null, null,
|
|
|
- 'Extension'
|
|
|
- ];
|
|
|
-
|
|
|
- var CodingTemplates = [
|
|
|
- [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: -2, y: -1},
|
|
|
- {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: 2, y: -1},
|
|
|
- {x: -4, y: 0}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}],
|
|
|
- [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: 2, y: -2},
|
|
|
- {x: -2, y: -1}, {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1},
|
|
|
- {x: 2, y: -1}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}],
|
|
|
- [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: -2, y: -1},
|
|
|
- {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: -2, y: 0},
|
|
|
- {x: -1, y: 0}],
|
|
|
- [{x: -3, y: -1}, {x: -2, y: -1}, {x: -1, y: -1}, {x: 0, y: -1},
|
|
|
- {x: 1, y: -1}, {x: -4, y: 0}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}]
|
|
|
- ];
|
|
|
-
|
|
|
- var RefinementTemplates = [
|
|
|
- {
|
|
|
- coding: [{x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}],
|
|
|
- reference: [{x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}, {x: 0, y: 0},
|
|
|
- {x: 1, y: 0}, {x: -1, y: 1}, {x: 0, y: 1}, {x: 1, y: 1}]
|
|
|
- },
|
|
|
- {
|
|
|
- coding: [{x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}],
|
|
|
- reference: [{x: 0, y: -1}, {x: -1, y: 0}, {x: 0, y: 0}, {x: 1, y: 0},
|
|
|
- {x: 0, y: 1}, {x: 1, y: 1}]
|
|
|
- }
|
|
|
- ];
|
|
|
-
|
|
|
- var ReusedContexts = [
|
|
|
- 0x1CD3, // '00111001101' (template) + '0011' (at),
|
|
|
- 0x079A, // '001111001101' + '0',
|
|
|
- 0x00E3, // '001110001' + '1',
|
|
|
- 0x018B // '011000101' + '1'
|
|
|
- ];
|
|
|
+ neighborsSignificance[i] += 0x04;
|
|
|
+ }
|
|
|
|
|
|
- var RefinementReusedContexts = [
|
|
|
- 0x0020, // '000' + '0' (coding) + '00010000' + '0' (reference)
|
|
|
- 0x0008 // '0000' + '001000'
|
|
|
- ];
|
|
|
+ if (left) {
|
|
|
+ neighborsSignificance[index - 1] += 0x01;
|
|
|
+ }
|
|
|
+ if (right) {
|
|
|
+ neighborsSignificance[index + 1] += 0x01;
|
|
|
+ }
|
|
|
+ neighborsSignificance[index] |= 0x80;
|
|
|
+ },
|
|
|
+ runSignificancePropogationPass:
|
|
|
+ function BitModel_runSignificancePropogationPass() {
|
|
|
+ var decoder = this.decoder;
|
|
|
+ var width = this.width, height = this.height;
|
|
|
+ var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
+ var coefficentsSign = this.coefficentsSign;
|
|
|
+ var neighborsSignificance = this.neighborsSignificance;
|
|
|
+ var processingFlags = this.processingFlags;
|
|
|
+ var contexts = this.contexts;
|
|
|
+ var labels = this.contextLabelTable;
|
|
|
+ var bitsDecoded = this.bitsDecoded;
|
|
|
+ var processedInverseMask = ~1;
|
|
|
+ var processedMask = 1;
|
|
|
+ var firstMagnitudeBitMask = 2;
|
|
|
|
|
|
- // 6.2 Generic Region Decoding Procedure
|
|
|
- function decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at,
|
|
|
- decodingContext) {
|
|
|
- if (mmr) {
|
|
|
- error('JBIG2 error: MMR encoding is not supported');
|
|
|
- }
|
|
|
+ for (var i0 = 0; i0 < height; i0 += 4) {
|
|
|
+ for (var j = 0; j < width; j++) {
|
|
|
+ var index = i0 * width + j;
|
|
|
+ for (var i1 = 0; i1 < 4; i1++, index += width) {
|
|
|
+ var i = i0 + i1;
|
|
|
+ if (i >= height) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // clear processed flag first
|
|
|
+ processingFlags[index] &= processedInverseMask;
|
|
|
|
|
|
- var useskip = !!skip;
|
|
|
- var template = CodingTemplates[templateIndex].concat(at);
|
|
|
+ if (coefficentsMagnitude[index] ||
|
|
|
+ !neighborsSignificance[index]) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- // Sorting is non-standard, and it is not required. But sorting increases
|
|
|
- // the number of template bits that can be reused from the previous
|
|
|
- // contextLabel in the main loop.
|
|
|
- template.sort(function (a, b) {
|
|
|
- return (a.y - b.y) || (a.x - b.x);
|
|
|
- });
|
|
|
+ var contextLabel = labels[neighborsSignificance[index]];
|
|
|
+ var decision = decoder.readBit(contexts, contextLabel);
|
|
|
+ if (decision) {
|
|
|
+ var sign = this.decodeSignBit(i, j, index);
|
|
|
+ coefficentsSign[index] = sign;
|
|
|
+ coefficentsMagnitude[index] = 1;
|
|
|
+ this.setNeighborsSignificance(i, j, index);
|
|
|
+ processingFlags[index] |= firstMagnitudeBitMask;
|
|
|
+ }
|
|
|
+ bitsDecoded[index]++;
|
|
|
+ processingFlags[index] |= processedMask;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ decodeSignBit: function BitModel_decodeSignBit(row, column, index) {
|
|
|
+ var width = this.width, height = this.height;
|
|
|
+ var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
+ var coefficentsSign = this.coefficentsSign;
|
|
|
+ var contribution, sign0, sign1, significance1;
|
|
|
+ var contextLabel, decoded;
|
|
|
|
|
|
- var templateLength = template.length;
|
|
|
- var templateX = new Int8Array(templateLength);
|
|
|
- var templateY = new Int8Array(templateLength);
|
|
|
- var changingTemplateEntries = [];
|
|
|
- var reuseMask = 0, minX = 0, maxX = 0, minY = 0;
|
|
|
- var c, k;
|
|
|
+ // calculate horizontal contribution
|
|
|
+ significance1 = (column > 0 && coefficentsMagnitude[index - 1] !== 0);
|
|
|
+ if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) {
|
|
|
+ sign1 = coefficentsSign[index + 1];
|
|
|
+ if (significance1) {
|
|
|
+ sign0 = coefficentsSign[index - 1];
|
|
|
+ contribution = 1 - sign1 - sign0;
|
|
|
+ } else {
|
|
|
+ contribution = 1 - sign1 - sign1;
|
|
|
+ }
|
|
|
+ } else if (significance1) {
|
|
|
+ sign0 = coefficentsSign[index - 1];
|
|
|
+ contribution = 1 - sign0 - sign0;
|
|
|
+ } else {
|
|
|
+ contribution = 0;
|
|
|
+ }
|
|
|
+ var horizontalContribution = 3 * contribution;
|
|
|
|
|
|
- for (k = 0; k < templateLength; k++) {
|
|
|
- templateX[k] = template[k].x;
|
|
|
- templateY[k] = template[k].y;
|
|
|
- minX = Math.min(minX, template[k].x);
|
|
|
- maxX = Math.max(maxX, template[k].x);
|
|
|
- minY = Math.min(minY, template[k].y);
|
|
|
- // Check if the template pixel appears in two consecutive context labels,
|
|
|
- // so it can be reused. Otherwise, we add it to the list of changing
|
|
|
- // template entries.
|
|
|
- if (k < templateLength - 1 &&
|
|
|
- template[k].y === template[k + 1].y &&
|
|
|
- template[k].x === template[k + 1].x - 1) {
|
|
|
- reuseMask |= 1 << (templateLength - 1 - k);
|
|
|
- } else {
|
|
|
- changingTemplateEntries.push(k);
|
|
|
- }
|
|
|
- }
|
|
|
- var changingEntriesLength = changingTemplateEntries.length;
|
|
|
+ // calculate vertical contribution and combine with the horizontal
|
|
|
+ significance1 = (row > 0 && coefficentsMagnitude[index - width] !== 0);
|
|
|
+ if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) {
|
|
|
+ sign1 = coefficentsSign[index + width];
|
|
|
+ if (significance1) {
|
|
|
+ sign0 = coefficentsSign[index - width];
|
|
|
+ contribution = 1 - sign1 - sign0 + horizontalContribution;
|
|
|
+ } else {
|
|
|
+ contribution = 1 - sign1 - sign1 + horizontalContribution;
|
|
|
+ }
|
|
|
+ } else if (significance1) {
|
|
|
+ sign0 = coefficentsSign[index - width];
|
|
|
+ contribution = 1 - sign0 - sign0 + horizontalContribution;
|
|
|
+ } else {
|
|
|
+ contribution = horizontalContribution;
|
|
|
+ }
|
|
|
|
|
|
- var changingTemplateX = new Int8Array(changingEntriesLength);
|
|
|
- var changingTemplateY = new Int8Array(changingEntriesLength);
|
|
|
- var changingTemplateBit = new Uint16Array(changingEntriesLength);
|
|
|
- for (c = 0; c < changingEntriesLength; c++) {
|
|
|
- k = changingTemplateEntries[c];
|
|
|
- changingTemplateX[c] = template[k].x;
|
|
|
- changingTemplateY[c] = template[k].y;
|
|
|
- changingTemplateBit[c] = 1 << (templateLength - 1 - k);
|
|
|
- }
|
|
|
+ if (contribution >= 0) {
|
|
|
+ contextLabel = 9 + contribution;
|
|
|
+ decoded = this.decoder.readBit(this.contexts, contextLabel);
|
|
|
+ } else {
|
|
|
+ contextLabel = 9 - contribution;
|
|
|
+ decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1;
|
|
|
+ }
|
|
|
+ return decoded;
|
|
|
+ },
|
|
|
+ runMagnitudeRefinementPass:
|
|
|
+ function BitModel_runMagnitudeRefinementPass() {
|
|
|
+ var decoder = this.decoder;
|
|
|
+ var width = this.width, height = this.height;
|
|
|
+ var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
+ var neighborsSignificance = this.neighborsSignificance;
|
|
|
+ var contexts = this.contexts;
|
|
|
+ var bitsDecoded = this.bitsDecoded;
|
|
|
+ var processingFlags = this.processingFlags;
|
|
|
+ var processedMask = 1;
|
|
|
+ var firstMagnitudeBitMask = 2;
|
|
|
+ var length = width * height;
|
|
|
+ var width4 = width * 4;
|
|
|
|
|
|
- // Get the safe bounding box edges from the width, height, minX, maxX, minY
|
|
|
- var sbb_left = -minX;
|
|
|
- var sbb_top = -minY;
|
|
|
- var sbb_right = width - maxX;
|
|
|
+ for (var index0 = 0, indexNext; index0 < length; index0 = indexNext) {
|
|
|
+ indexNext = Math.min(length, index0 + width4);
|
|
|
+ for (var j = 0; j < width; j++) {
|
|
|
+ for (var index = index0 + j; index < indexNext; index += width) {
|
|
|
|
|
|
- var pseudoPixelContext = ReusedContexts[templateIndex];
|
|
|
- var row = new Uint8Array(width);
|
|
|
- var bitmap = [];
|
|
|
+ // significant but not those that have just become
|
|
|
+ if (!coefficentsMagnitude[index] ||
|
|
|
+ (processingFlags[index] & processedMask) !== 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- var decoder = decodingContext.decoder;
|
|
|
- var contexts = decodingContext.contextCache.getContexts('GB');
|
|
|
+ var contextLabel = 16;
|
|
|
+ if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) {
|
|
|
+ processingFlags[index] ^= firstMagnitudeBitMask;
|
|
|
+ // first refinement
|
|
|
+ var significance = neighborsSignificance[index] & 127;
|
|
|
+ contextLabel = significance === 0 ? 15 : 14;
|
|
|
+ }
|
|
|
|
|
|
- var ltp = 0, j, i0, j0, contextLabel = 0, bit, shift;
|
|
|
- for (var i = 0; i < height; i++) {
|
|
|
- if (prediction) {
|
|
|
- var sltp = decoder.readBit(contexts, pseudoPixelContext);
|
|
|
- ltp ^= sltp;
|
|
|
- if (ltp) {
|
|
|
- bitmap.push(row); // duplicate previous row
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
- row = new Uint8Array(row);
|
|
|
- bitmap.push(row);
|
|
|
- for (j = 0; j < width; j++) {
|
|
|
- if (useskip && skip[i][j]) {
|
|
|
- row[j] = 0;
|
|
|
- continue;
|
|
|
- }
|
|
|
- // Are we in the middle of a scanline, so we can reuse contextLabel
|
|
|
- // bits?
|
|
|
- if (j >= sbb_left && j < sbb_right && i >= sbb_top) {
|
|
|
- // If yes, we can just shift the bits that are reusable and only
|
|
|
- // fetch the remaining ones.
|
|
|
- contextLabel = (contextLabel << 1) & reuseMask;
|
|
|
- for (k = 0; k < changingEntriesLength; k++) {
|
|
|
- i0 = i + changingTemplateY[k];
|
|
|
- j0 = j + changingTemplateX[k];
|
|
|
- bit = bitmap[i0][j0];
|
|
|
- if (bit) {
|
|
|
- bit = changingTemplateBit[k];
|
|
|
- contextLabel |= bit;
|
|
|
+ var bit = decoder.readBit(contexts, contextLabel);
|
|
|
+ coefficentsMagnitude[index] =
|
|
|
+ (coefficentsMagnitude[index] << 1) | bit;
|
|
|
+ bitsDecoded[index]++;
|
|
|
+ processingFlags[index] |= processedMask;
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
- // compute the contextLabel from scratch
|
|
|
- contextLabel = 0;
|
|
|
- shift = templateLength - 1;
|
|
|
- for (k = 0; k < templateLength; k++, shift--) {
|
|
|
- j0 = j + templateX[k];
|
|
|
- if (j0 >= 0 && j0 < width) {
|
|
|
- i0 = i + templateY[k];
|
|
|
- if (i0 >= 0) {
|
|
|
- bit = bitmap[i0][j0];
|
|
|
- if (bit) {
|
|
|
- contextLabel |= bit << shift;
|
|
|
- }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ runCleanupPass: function BitModel_runCleanupPass() {
|
|
|
+ var decoder = this.decoder;
|
|
|
+ var width = this.width, height = this.height;
|
|
|
+ var neighborsSignificance = this.neighborsSignificance;
|
|
|
+ var coefficentsMagnitude = this.coefficentsMagnitude;
|
|
|
+ var coefficentsSign = this.coefficentsSign;
|
|
|
+ var contexts = this.contexts;
|
|
|
+ var labels = this.contextLabelTable;
|
|
|
+ var bitsDecoded = this.bitsDecoded;
|
|
|
+ var processingFlags = this.processingFlags;
|
|
|
+ var processedMask = 1;
|
|
|
+ var firstMagnitudeBitMask = 2;
|
|
|
+ var oneRowDown = width;
|
|
|
+ var twoRowsDown = width * 2;
|
|
|
+ var threeRowsDown = width * 3;
|
|
|
+ var iNext;
|
|
|
+ for (var i0 = 0; i0 < height; i0 = iNext) {
|
|
|
+ iNext = Math.min(i0 + 4, height);
|
|
|
+ var indexBase = i0 * width;
|
|
|
+ var checkAllEmpty = i0 + 3 < height;
|
|
|
+ for (var j = 0; j < width; j++) {
|
|
|
+ var index0 = indexBase + j;
|
|
|
+ // using the property: labels[neighborsSignificance[index]] == 0
|
|
|
+ // when neighborsSignificance[index] == 0
|
|
|
+ var allEmpty = (checkAllEmpty &&
|
|
|
+ processingFlags[index0] === 0 &&
|
|
|
+ processingFlags[index0 + oneRowDown] === 0 &&
|
|
|
+ processingFlags[index0 + twoRowsDown] === 0 &&
|
|
|
+ processingFlags[index0 + threeRowsDown] === 0 &&
|
|
|
+ neighborsSignificance[index0] === 0 &&
|
|
|
+ neighborsSignificance[index0 + oneRowDown] === 0 &&
|
|
|
+ neighborsSignificance[index0 + twoRowsDown] === 0 &&
|
|
|
+ neighborsSignificance[index0 + threeRowsDown] === 0);
|
|
|
+ var i1 = 0, index = index0;
|
|
|
+ var i = i0, sign;
|
|
|
+ if (allEmpty) {
|
|
|
+ var hasSignificantCoefficent =
|
|
|
+ decoder.readBit(contexts, RUNLENGTH_CONTEXT);
|
|
|
+ if (!hasSignificantCoefficent) {
|
|
|
+ bitsDecoded[index0]++;
|
|
|
+ bitsDecoded[index0 + oneRowDown]++;
|
|
|
+ bitsDecoded[index0 + twoRowsDown]++;
|
|
|
+ bitsDecoded[index0 + threeRowsDown]++;
|
|
|
+ continue; // next column
|
|
|
+ }
|
|
|
+ i1 = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
|
|
|
+ decoder.readBit(contexts, UNIFORM_CONTEXT);
|
|
|
+ if (i1 !== 0) {
|
|
|
+ i = i0 + i1;
|
|
|
+ index += i1 * width;
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- var pixel = decoder.readBit(contexts, contextLabel);
|
|
|
- row[j] = pixel;
|
|
|
- }
|
|
|
- }
|
|
|
- return bitmap;
|
|
|
- }
|
|
|
-
|
|
|
- // 6.3.2 Generic Refinement Region Decoding Procedure
|
|
|
- function decodeRefinement(width, height, templateIndex, referenceBitmap,
|
|
|
- offsetX, offsetY, prediction, at,
|
|
|
- decodingContext) {
|
|
|
- var codingTemplate = RefinementTemplates[templateIndex].coding;
|
|
|
- if (templateIndex === 0) {
|
|
|
- codingTemplate = codingTemplate.concat([at[0]]);
|
|
|
- }
|
|
|
- var codingTemplateLength = codingTemplate.length;
|
|
|
- var codingTemplateX = new Int32Array(codingTemplateLength);
|
|
|
- var codingTemplateY = new Int32Array(codingTemplateLength);
|
|
|
- var k;
|
|
|
- for (k = 0; k < codingTemplateLength; k++) {
|
|
|
- codingTemplateX[k] = codingTemplate[k].x;
|
|
|
- codingTemplateY[k] = codingTemplate[k].y;
|
|
|
- }
|
|
|
|
|
|
- var referenceTemplate = RefinementTemplates[templateIndex].reference;
|
|
|
- if (templateIndex === 0) {
|
|
|
- referenceTemplate = referenceTemplate.concat([at[1]]);
|
|
|
- }
|
|
|
- var referenceTemplateLength = referenceTemplate.length;
|
|
|
- var referenceTemplateX = new Int32Array(referenceTemplateLength);
|
|
|
- var referenceTemplateY = new Int32Array(referenceTemplateLength);
|
|
|
- for (k = 0; k < referenceTemplateLength; k++) {
|
|
|
- referenceTemplateX[k] = referenceTemplate[k].x;
|
|
|
- referenceTemplateY[k] = referenceTemplate[k].y;
|
|
|
- }
|
|
|
- var referenceWidth = referenceBitmap[0].length;
|
|
|
- var referenceHeight = referenceBitmap.length;
|
|
|
+ sign = this.decodeSignBit(i, j, index);
|
|
|
+ coefficentsSign[index] = sign;
|
|
|
+ coefficentsMagnitude[index] = 1;
|
|
|
+ this.setNeighborsSignificance(i, j, index);
|
|
|
+ processingFlags[index] |= firstMagnitudeBitMask;
|
|
|
|
|
|
- var pseudoPixelContext = RefinementReusedContexts[templateIndex];
|
|
|
- var bitmap = [];
|
|
|
+ index = index0;
|
|
|
+ for (var i2 = i0; i2 <= i; i2++, index += width) {
|
|
|
+ bitsDecoded[index]++;
|
|
|
+ }
|
|
|
|
|
|
- var decoder = decodingContext.decoder;
|
|
|
- var contexts = decodingContext.contextCache.getContexts('GR');
|
|
|
+ i1++;
|
|
|
+ }
|
|
|
+ for (i = i0 + i1; i < iNext; i++, index += width) {
|
|
|
+ if (coefficentsMagnitude[index] ||
|
|
|
+ (processingFlags[index] & processedMask) !== 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- var ltp = 0;
|
|
|
- for (var i = 0; i < height; i++) {
|
|
|
- if (prediction) {
|
|
|
- var sltp = decoder.readBit(contexts, pseudoPixelContext);
|
|
|
- ltp ^= sltp;
|
|
|
- if (ltp) {
|
|
|
- error('JBIG2 error: prediction is not supported');
|
|
|
- }
|
|
|
- }
|
|
|
- var row = new Uint8Array(width);
|
|
|
- bitmap.push(row);
|
|
|
- for (var j = 0; j < width; j++) {
|
|
|
- var i0, j0;
|
|
|
- var contextLabel = 0;
|
|
|
- for (k = 0; k < codingTemplateLength; k++) {
|
|
|
- i0 = i + codingTemplateY[k];
|
|
|
- j0 = j + codingTemplateX[k];
|
|
|
- if (i0 < 0 || j0 < 0 || j0 >= width) {
|
|
|
- contextLabel <<= 1; // out of bound pixel
|
|
|
- } else {
|
|
|
- contextLabel = (contextLabel << 1) | bitmap[i0][j0];
|
|
|
+ var contextLabel = labels[neighborsSignificance[index]];
|
|
|
+ var decision = decoder.readBit(contexts, contextLabel);
|
|
|
+ if (decision === 1) {
|
|
|
+ sign = this.decodeSignBit(i, j, index);
|
|
|
+ coefficentsSign[index] = sign;
|
|
|
+ coefficentsMagnitude[index] = 1;
|
|
|
+ this.setNeighborsSignificance(i, j, index);
|
|
|
+ processingFlags[index] |= firstMagnitudeBitMask;
|
|
|
+ }
|
|
|
+ bitsDecoded[index]++;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- for (k = 0; k < referenceTemplateLength; k++) {
|
|
|
- i0 = i + referenceTemplateY[k] + offsetY;
|
|
|
- j0 = j + referenceTemplateX[k] + offsetX;
|
|
|
- if (i0 < 0 || i0 >= referenceHeight || j0 < 0 ||
|
|
|
- j0 >= referenceWidth) {
|
|
|
- contextLabel <<= 1; // out of bound pixel
|
|
|
- } else {
|
|
|
- contextLabel = (contextLabel << 1) | referenceBitmap[i0][j0];
|
|
|
- }
|
|
|
+ },
|
|
|
+ checkSegmentationSymbol: function BitModel_checkSegmentationSymbol() {
|
|
|
+ var decoder = this.decoder;
|
|
|
+ var contexts = this.contexts;
|
|
|
+ var symbol = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 3) |
|
|
|
+ (decoder.readBit(contexts, UNIFORM_CONTEXT) << 2) |
|
|
|
+ (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
|
|
|
+ decoder.readBit(contexts, UNIFORM_CONTEXT);
|
|
|
+ if (symbol != 0xA) {
|
|
|
+ throw 'Invalid segmentation symbol';
|
|
|
}
|
|
|
- var pixel = decoder.readBit(contexts, contextLabel);
|
|
|
- row[j] = pixel;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- return bitmap;
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- // 6.5.5 Decoding the symbol dictionary
|
|
|
- function decodeSymbolDictionary(huffman, refinement, symbols,
|
|
|
- numberOfNewSymbols, numberOfExportedSymbols,
|
|
|
- huffmanTables, templateIndex, at,
|
|
|
- refinementTemplateIndex, refinementAt,
|
|
|
- decodingContext) {
|
|
|
- if (huffman) {
|
|
|
- error('JBIG2 error: huffman is not supported');
|
|
|
- }
|
|
|
+ return BitModel;
|
|
|
+ })();
|
|
|
|
|
|
- var newSymbols = [];
|
|
|
- var currentHeight = 0;
|
|
|
- var symbolCodeLength = log2(symbols.length + numberOfNewSymbols);
|
|
|
+ // Section F, Discrete wavelet transformation
|
|
|
+ var Transform = (function TransformClosure() {
|
|
|
+ function Transform() {}
|
|
|
|
|
|
- var decoder = decodingContext.decoder;
|
|
|
- var contextCache = decodingContext.contextCache;
|
|
|
+ Transform.prototype.calculate =
|
|
|
+ function transformCalculate(subbands, u0, v0) {
|
|
|
+ var ll = subbands[0];
|
|
|
+ for (var i = 1, ii = subbands.length; i < ii; i++) {
|
|
|
+ ll = this.iterate(ll, subbands[i], u0, v0);
|
|
|
+ }
|
|
|
+ return ll;
|
|
|
+ };
|
|
|
+ Transform.prototype.extend = function extend(buffer, offset, size) {
|
|
|
+ // Section F.3.7 extending... using max extension of 4
|
|
|
+ var i1 = offset - 1, j1 = offset + 1;
|
|
|
+ var i2 = offset + size - 2, j2 = offset + size;
|
|
|
+ buffer[i1--] = buffer[j1++];
|
|
|
+ buffer[j2++] = buffer[i2--];
|
|
|
+ buffer[i1--] = buffer[j1++];
|
|
|
+ buffer[j2++] = buffer[i2--];
|
|
|
+ buffer[i1--] = buffer[j1++];
|
|
|
+ buffer[j2++] = buffer[i2--];
|
|
|
+ buffer[i1] = buffer[j1];
|
|
|
+ buffer[j2] = buffer[i2];
|
|
|
+ };
|
|
|
+ Transform.prototype.iterate = function Transform_iterate(ll, hl_lh_hh,
|
|
|
+ u0, v0) {
|
|
|
+ var llWidth = ll.width, llHeight = ll.height, llItems = ll.items;
|
|
|
+ var width = hl_lh_hh.width;
|
|
|
+ var height = hl_lh_hh.height;
|
|
|
+ var items = hl_lh_hh.items;
|
|
|
+ var i, j, k, l, u, v;
|
|
|
|
|
|
- while (newSymbols.length < numberOfNewSymbols) {
|
|
|
- var deltaHeight = decodeInteger(contextCache, 'IADH', decoder); // 6.5.6
|
|
|
- currentHeight += deltaHeight;
|
|
|
- var currentWidth = 0;
|
|
|
- var totalWidth = 0;
|
|
|
- while (true) {
|
|
|
- var deltaWidth = decodeInteger(contextCache, 'IADW', decoder); // 6.5.7
|
|
|
- if (deltaWidth === null) {
|
|
|
- break; // OOB
|
|
|
+ // Interleave LL according to Section F.3.3
|
|
|
+ for (k = 0, i = 0; i < llHeight; i++) {
|
|
|
+ l = i * 2 * width;
|
|
|
+ for (j = 0; j < llWidth; j++, k++, l += 2) {
|
|
|
+ items[l] = llItems[k];
|
|
|
}
|
|
|
- currentWidth += deltaWidth;
|
|
|
- totalWidth += currentWidth;
|
|
|
- var bitmap;
|
|
|
- if (refinement) {
|
|
|
- // 6.5.8.2 Refinement/aggregate-coded symbol bitmap
|
|
|
- var numberOfInstances = decodeInteger(contextCache, 'IAAI', decoder);
|
|
|
- if (numberOfInstances > 1) {
|
|
|
- bitmap = decodeTextRegion(huffman, refinement,
|
|
|
- currentWidth, currentHeight, 0,
|
|
|
- numberOfInstances, 1, //strip size
|
|
|
- symbols.concat(newSymbols),
|
|
|
- symbolCodeLength,
|
|
|
- 0, //transposed
|
|
|
- 0, //ds offset
|
|
|
- 1, //top left 7.4.3.1.1
|
|
|
- 0, //OR operator
|
|
|
- huffmanTables,
|
|
|
- refinementTemplateIndex, refinementAt,
|
|
|
- decodingContext);
|
|
|
- } else {
|
|
|
- var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
|
|
|
- var rdx = decodeInteger(contextCache, 'IARDX', decoder); // 6.4.11.3
|
|
|
- var rdy = decodeInteger(contextCache, 'IARDY', decoder); // 6.4.11.4
|
|
|
- var symbol = (symbolId < symbols.length ? symbols[symbolId] :
|
|
|
- newSymbols[symbolId - symbols.length]);
|
|
|
- bitmap = decodeRefinement(currentWidth, currentHeight,
|
|
|
- refinementTemplateIndex, symbol, rdx, rdy, false, refinementAt,
|
|
|
- decodingContext);
|
|
|
+ }
|
|
|
+ // The LL band is not needed anymore.
|
|
|
+ llItems = ll.items = null;
|
|
|
+
|
|
|
+ var bufferPadding = 4;
|
|
|
+ var rowBuffer = new Float32Array(width + 2 * bufferPadding);
|
|
|
+
|
|
|
+ // Section F.3.4 HOR_SR
|
|
|
+ if (width === 1) {
|
|
|
+ // if width = 1, when u0 even keep items as is, when odd divide by 2
|
|
|
+ if ((u0 & 1) !== 0) {
|
|
|
+ for (v = 0, k = 0; v < height; v++, k += width) {
|
|
|
+ items[k] *= 0.5;
|
|
|
}
|
|
|
- } else {
|
|
|
- // 6.5.8.1 Direct-coded symbol bitmap
|
|
|
- bitmap = decodeBitmap(false, currentWidth, currentHeight,
|
|
|
- templateIndex, false, null, at, decodingContext);
|
|
|
}
|
|
|
- newSymbols.push(bitmap);
|
|
|
- }
|
|
|
- }
|
|
|
- // 6.5.10 Exported symbols
|
|
|
- var exportedSymbols = [];
|
|
|
- var flags = [], currentFlag = false;
|
|
|
- var totalSymbolsLength = symbols.length + numberOfNewSymbols;
|
|
|
- while (flags.length < totalSymbolsLength) {
|
|
|
- var runLength = decodeInteger(contextCache, 'IAEX', decoder);
|
|
|
- while (runLength--) {
|
|
|
- flags.push(currentFlag);
|
|
|
- }
|
|
|
- currentFlag = !currentFlag;
|
|
|
- }
|
|
|
- for (var i = 0, ii = symbols.length; i < ii; i++) {
|
|
|
- if (flags[i]) {
|
|
|
- exportedSymbols.push(symbols[i]);
|
|
|
- }
|
|
|
- }
|
|
|
- for (var j = 0; j < numberOfNewSymbols; i++, j++) {
|
|
|
- if (flags[i]) {
|
|
|
- exportedSymbols.push(newSymbols[j]);
|
|
|
- }
|
|
|
- }
|
|
|
- return exportedSymbols;
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ for (v = 0, k = 0; v < height; v++, k += width) {
|
|
|
+ rowBuffer.set(items.subarray(k, k + width), bufferPadding);
|
|
|
|
|
|
- function decodeTextRegion(huffman, refinement, width, height,
|
|
|
- defaultPixelValue, numberOfSymbolInstances,
|
|
|
- stripSize, inputSymbols, symbolCodeLength,
|
|
|
- transposed, dsOffset, referenceCorner,
|
|
|
- combinationOperator, huffmanTables,
|
|
|
- refinementTemplateIndex, refinementAt,
|
|
|
- decodingContext) {
|
|
|
- if (huffman) {
|
|
|
- error('JBIG2 error: huffman is not supported');
|
|
|
- }
|
|
|
+ this.extend(rowBuffer, bufferPadding, width);
|
|
|
+ this.filter(rowBuffer, bufferPadding, width);
|
|
|
|
|
|
- // Prepare bitmap
|
|
|
- var bitmap = [];
|
|
|
- var i, row;
|
|
|
- for (i = 0; i < height; i++) {
|
|
|
- row = new Uint8Array(width);
|
|
|
- if (defaultPixelValue) {
|
|
|
- for (var j = 0; j < width; j++) {
|
|
|
- row[j] = defaultPixelValue;
|
|
|
+ items.set(
|
|
|
+ rowBuffer.subarray(bufferPadding, bufferPadding + width),
|
|
|
+ k);
|
|
|
}
|
|
|
}
|
|
|
- bitmap.push(row);
|
|
|
- }
|
|
|
|
|
|
- var decoder = decodingContext.decoder;
|
|
|
- var contextCache = decodingContext.contextCache;
|
|
|
- var stripT = -decodeInteger(contextCache, 'IADT', decoder); // 6.4.6
|
|
|
- var firstS = 0;
|
|
|
- i = 0;
|
|
|
- while (i < numberOfSymbolInstances) {
|
|
|
- var deltaT = decodeInteger(contextCache, 'IADT', decoder); // 6.4.6
|
|
|
- stripT += deltaT;
|
|
|
+ // Accesses to the items array can take long, because it may not fit into
|
|
|
+ // CPU cache and has to be fetched from main memory. Since subsequent
|
|
|
+ // accesses to the items array are not local when reading columns, we
|
|
|
+ // have a cache miss every time. To reduce cache misses, get up to
|
|
|
+ // 'numBuffers' items at a time and store them into the individual
|
|
|
+ // buffers. The colBuffers should be small enough to fit into CPU cache.
|
|
|
+ var numBuffers = 16;
|
|
|
+ var colBuffers = [];
|
|
|
+ for (i = 0; i < numBuffers; i++) {
|
|
|
+ colBuffers.push(new Float32Array(height + 2 * bufferPadding));
|
|
|
+ }
|
|
|
+ var b, currentBuffer = 0;
|
|
|
+ ll = bufferPadding + height;
|
|
|
|
|
|
- var deltaFirstS = decodeInteger(contextCache, 'IAFS', decoder); // 6.4.7
|
|
|
- firstS += deltaFirstS;
|
|
|
- var currentS = firstS;
|
|
|
- do {
|
|
|
- var currentT = (stripSize == 1 ? 0 :
|
|
|
- decodeInteger(contextCache, 'IAIT', decoder)); // 6.4.9
|
|
|
- var t = stripSize * stripT + currentT;
|
|
|
- var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
|
|
|
- var applyRefinement = (refinement &&
|
|
|
- decodeInteger(contextCache, 'IARI', decoder));
|
|
|
- var symbolBitmap = inputSymbols[symbolId];
|
|
|
- var symbolWidth = symbolBitmap[0].length;
|
|
|
- var symbolHeight = symbolBitmap.length;
|
|
|
- if (applyRefinement) {
|
|
|
- var rdw = decodeInteger(contextCache, 'IARDW', decoder); // 6.4.11.1
|
|
|
- var rdh = decodeInteger(contextCache, 'IARDH', decoder); // 6.4.11.2
|
|
|
- var rdx = decodeInteger(contextCache, 'IARDX', decoder); // 6.4.11.3
|
|
|
- var rdy = decodeInteger(contextCache, 'IARDY', decoder); // 6.4.11.4
|
|
|
- symbolWidth += rdw;
|
|
|
- symbolHeight += rdh;
|
|
|
- symbolBitmap = decodeRefinement(symbolWidth, symbolHeight,
|
|
|
- refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx,
|
|
|
- (rdh >> 1) + rdy, false, refinementAt,
|
|
|
- decodingContext);
|
|
|
+ // Section F.3.5 VER_SR
|
|
|
+ if (height === 1) {
|
|
|
+ // if height = 1, when v0 even keep items as is, when odd divide by 2
|
|
|
+ if ((v0 & 1) !== 0) {
|
|
|
+ for (u = 0; u < width; u++) {
|
|
|
+ items[u] *= 0.5;
|
|
|
+ }
|
|
|
}
|
|
|
- var offsetT = t - ((referenceCorner & 1) ? 0 : symbolHeight);
|
|
|
- var offsetS = currentS - ((referenceCorner & 2) ? symbolWidth : 0);
|
|
|
- var s2, t2, symbolRow;
|
|
|
- if (transposed) {
|
|
|
- // Place Symbol Bitmap from T1,S1
|
|
|
- for (s2 = 0; s2 < symbolHeight; s2++) {
|
|
|
- row = bitmap[offsetS + s2];
|
|
|
- if (!row) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- symbolRow = symbolBitmap[s2];
|
|
|
- // To ignore Parts of Symbol bitmap which goes
|
|
|
- // outside bitmap region
|
|
|
- var maxWidth = Math.min(width - offsetT, symbolWidth);
|
|
|
- switch (combinationOperator) {
|
|
|
- case 0: // OR
|
|
|
- for (t2 = 0; t2 < maxWidth; t2++) {
|
|
|
- row[offsetT + t2] |= symbolRow[t2];
|
|
|
- }
|
|
|
- break;
|
|
|
- case 2: // XOR
|
|
|
- for (t2 = 0; t2 < maxWidth; t2++) {
|
|
|
- row[offsetT + t2] ^= symbolRow[t2];
|
|
|
- }
|
|
|
- break;
|
|
|
- default:
|
|
|
- error('JBIG2 error: operator ' + combinationOperator +
|
|
|
- ' is not supported');
|
|
|
+ } else {
|
|
|
+ for (u = 0; u < width; u++) {
|
|
|
+ // if we ran out of buffers, copy several image columns at once
|
|
|
+ if (currentBuffer === 0) {
|
|
|
+ numBuffers = Math.min(width - u, numBuffers);
|
|
|
+ for (k = u, l = bufferPadding; l < ll; k += width, l++) {
|
|
|
+ for (b = 0; b < numBuffers; b++) {
|
|
|
+ colBuffers[b][l] = items[k + b];
|
|
|
+ }
|
|
|
}
|
|
|
+ currentBuffer = numBuffers;
|
|
|
}
|
|
|
- currentS += symbolHeight - 1;
|
|
|
- } else {
|
|
|
- for (t2 = 0; t2 < symbolHeight; t2++) {
|
|
|
- row = bitmap[offsetT + t2];
|
|
|
- if (!row) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- symbolRow = symbolBitmap[t2];
|
|
|
- switch (combinationOperator) {
|
|
|
- case 0: // OR
|
|
|
- for (s2 = 0; s2 < symbolWidth; s2++) {
|
|
|
- row[offsetS + s2] |= symbolRow[s2];
|
|
|
- }
|
|
|
- break;
|
|
|
- case 2: // XOR
|
|
|
- for (s2 = 0; s2 < symbolWidth; s2++) {
|
|
|
- row[offsetS + s2] ^= symbolRow[s2];
|
|
|
- }
|
|
|
- break;
|
|
|
- default:
|
|
|
- error('JBIG2 error: operator ' + combinationOperator +
|
|
|
- ' is not supported');
|
|
|
+
|
|
|
+ currentBuffer--;
|
|
|
+ var buffer = colBuffers[currentBuffer];
|
|
|
+ this.extend(buffer, bufferPadding, height);
|
|
|
+ this.filter(buffer, bufferPadding, height);
|
|
|
+
|
|
|
+ // If this is last buffer in this group of buffers, flush all buffers.
|
|
|
+ if (currentBuffer === 0) {
|
|
|
+ k = u - numBuffers + 1;
|
|
|
+ for (l = bufferPadding; l < ll; k += width, l++) {
|
|
|
+ for (b = 0; b < numBuffers; b++) {
|
|
|
+ items[k + b] = colBuffers[b][l];
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- currentS += symbolWidth - 1;
|
|
|
- }
|
|
|
- i++;
|
|
|
- var deltaS = decodeInteger(contextCache, 'IADS', decoder); // 6.4.8
|
|
|
- if (deltaS === null) {
|
|
|
- break; // OOB
|
|
|
}
|
|
|
- currentS += deltaS + dsOffset;
|
|
|
- } while (true);
|
|
|
- }
|
|
|
- return bitmap;
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- function readSegmentHeader(data, start) {
|
|
|
- var segmentHeader = {};
|
|
|
- segmentHeader.number = readUint32(data, start);
|
|
|
- var flags = data[start + 4];
|
|
|
- var segmentType = flags & 0x3F;
|
|
|
- if (!SegmentTypes[segmentType]) {
|
|
|
- error('JBIG2 error: invalid segment type: ' + segmentType);
|
|
|
+ return {
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ items: items
|
|
|
+ };
|
|
|
+ };
|
|
|
+ return Transform;
|
|
|
+ })();
|
|
|
+
|
|
|
+ // Section 3.8.2 Irreversible 9-7 filter
|
|
|
+ var IrreversibleTransform = (function IrreversibleTransformClosure() {
|
|
|
+ function IrreversibleTransform() {
|
|
|
+ Transform.call(this);
|
|
|
}
|
|
|
- segmentHeader.type = segmentType;
|
|
|
- segmentHeader.typeName = SegmentTypes[segmentType];
|
|
|
- segmentHeader.deferredNonRetain = !!(flags & 0x80);
|
|
|
|
|
|
- var pageAssociationFieldSize = !!(flags & 0x40);
|
|
|
- var referredFlags = data[start + 5];
|
|
|
- var referredToCount = (referredFlags >> 5) & 7;
|
|
|
- var retainBits = [referredFlags & 31];
|
|
|
- var position = start + 6;
|
|
|
- if (referredFlags == 7) {
|
|
|
- referredToCount = readUint32(data, position - 1) & 0x1FFFFFFF;
|
|
|
- position += 3;
|
|
|
- var bytes = (referredToCount + 7) >> 3;
|
|
|
- retainBits[0] = data[position++];
|
|
|
- while (--bytes > 0) {
|
|
|
- retainBits.push(data[position++]);
|
|
|
+ IrreversibleTransform.prototype = Object.create(Transform.prototype);
|
|
|
+ IrreversibleTransform.prototype.filter =
|
|
|
+ function irreversibleTransformFilter(x, offset, length) {
|
|
|
+ var len = length >> 1;
|
|
|
+ offset = offset | 0;
|
|
|
+ var j, n, current, next;
|
|
|
+
|
|
|
+ var alpha = -1.586134342059924;
|
|
|
+ var beta = -0.052980118572961;
|
|
|
+ var gamma = 0.882911075530934;
|
|
|
+ var delta = 0.443506852043971;
|
|
|
+ var K = 1.230174104914001;
|
|
|
+ var K_ = 1 / K;
|
|
|
+
|
|
|
+ // step 1 is combined with step 3
|
|
|
+
|
|
|
+ // step 2
|
|
|
+ j = offset - 3;
|
|
|
+ for (n = len + 4; n--; j += 2) {
|
|
|
+ x[j] *= K_;
|
|
|
+ }
|
|
|
+
|
|
|
+ // step 1 & 3
|
|
|
+ j = offset - 2;
|
|
|
+ current = delta * x[j -1];
|
|
|
+ for (n = len + 3; n--; j += 2) {
|
|
|
+ next = delta * x[j + 1];
|
|
|
+ x[j] = K * x[j] - current - next;
|
|
|
+ if (n--) {
|
|
|
+ j += 2;
|
|
|
+ current = delta * x[j + 1];
|
|
|
+ x[j] = K * x[j] - current - next;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // step 4
|
|
|
+ j = offset - 1;
|
|
|
+ current = gamma * x[j - 1];
|
|
|
+ for (n = len + 2; n--; j += 2) {
|
|
|
+ next = gamma * x[j + 1];
|
|
|
+ x[j] -= current + next;
|
|
|
+ if (n--) {
|
|
|
+ j += 2;
|
|
|
+ current = gamma * x[j + 1];
|
|
|
+ x[j] -= current + next;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- } else if (referredFlags == 5 || referredFlags == 6) {
|
|
|
- error('JBIG2 error: invalid referred-to flags');
|
|
|
- }
|
|
|
-
|
|
|
- segmentHeader.retainBits = retainBits;
|
|
|
- var referredToSegmentNumberSize = (segmentHeader.number <= 256 ? 1 :
|
|
|
- (segmentHeader.number <= 65536 ? 2 : 4));
|
|
|
- var referredTo = [];
|
|
|
- var i, ii;
|
|
|
- for (i = 0; i < referredToCount; i++) {
|
|
|
- var number = (referredToSegmentNumberSize == 1 ? data[position] :
|
|
|
- (referredToSegmentNumberSize == 2 ? readUint16(data, position) :
|
|
|
- readUint32(data, position)));
|
|
|
- referredTo.push(number);
|
|
|
- position += referredToSegmentNumberSize;
|
|
|
- }
|
|
|
- segmentHeader.referredTo = referredTo;
|
|
|
- if (!pageAssociationFieldSize) {
|
|
|
- segmentHeader.pageAssociation = data[position++];
|
|
|
- } else {
|
|
|
- segmentHeader.pageAssociation = readUint32(data, position);
|
|
|
- position += 4;
|
|
|
- }
|
|
|
- segmentHeader.length = readUint32(data, position);
|
|
|
- position += 4;
|
|
|
|
|
|
- if (segmentHeader.length == 0xFFFFFFFF) {
|
|
|
- // 7.2.7 Segment data length, unknown segment length
|
|
|
- if (segmentType === 38) { // ImmediateGenericRegion
|
|
|
- var genericRegionInfo = readRegionSegmentInformation(data, position);
|
|
|
- var genericRegionSegmentFlags = data[position +
|
|
|
- RegionSegmentInformationFieldLength];
|
|
|
- var genericRegionMmr = !!(genericRegionSegmentFlags & 1);
|
|
|
- // searching for the segment end
|
|
|
- var searchPatternLength = 6;
|
|
|
- var searchPattern = new Uint8Array(searchPatternLength);
|
|
|
- if (!genericRegionMmr) {
|
|
|
- searchPattern[0] = 0xFF;
|
|
|
- searchPattern[1] = 0xAC;
|
|
|
+ // step 5
|
|
|
+ j = offset;
|
|
|
+ current = beta * x[j - 1];
|
|
|
+ for (n = len + 1; n--; j += 2) {
|
|
|
+ next = beta * x[j + 1];
|
|
|
+ x[j] -= current + next;
|
|
|
+ if (n--) {
|
|
|
+ j += 2;
|
|
|
+ current = beta * x[j + 1];
|
|
|
+ x[j] -= current + next;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
}
|
|
|
- searchPattern[2] = (genericRegionInfo.height >>> 24) & 0xFF;
|
|
|
- searchPattern[3] = (genericRegionInfo.height >> 16) & 0xFF;
|
|
|
- searchPattern[4] = (genericRegionInfo.height >> 8) & 0xFF;
|
|
|
- searchPattern[5] = genericRegionInfo.height & 0xFF;
|
|
|
- for (i = position, ii = data.length; i < ii; i++) {
|
|
|
- var j = 0;
|
|
|
- while (j < searchPatternLength && searchPattern[j] === data[i + j]) {
|
|
|
- j++;
|
|
|
- }
|
|
|
- if (j == searchPatternLength) {
|
|
|
- segmentHeader.length = i + searchPatternLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ // step 6
|
|
|
+ if (len !== 0) {
|
|
|
+ j = offset + 1;
|
|
|
+ current = alpha * x[j - 1];
|
|
|
+ for (n = len; n--; j += 2) {
|
|
|
+ next = alpha * x[j + 1];
|
|
|
+ x[j] -= current + next;
|
|
|
+ if (n--) {
|
|
|
+ j += 2;
|
|
|
+ current = alpha * x[j + 1];
|
|
|
+ x[j] -= current + next;
|
|
|
+ } else {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- if (segmentHeader.length == 0xFFFFFFFF) {
|
|
|
- error('JBIG2 error: segment end was not found');
|
|
|
- }
|
|
|
- } else {
|
|
|
- error('JBIG2 error: invalid unknown segment length');
|
|
|
}
|
|
|
- }
|
|
|
- segmentHeader.headerEnd = position;
|
|
|
- return segmentHeader;
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- function readSegments(header, data, start, end) {
|
|
|
- var segments = [];
|
|
|
- var position = start;
|
|
|
- while (position < end) {
|
|
|
- var segmentHeader = readSegmentHeader(data, position);
|
|
|
- position = segmentHeader.headerEnd;
|
|
|
- var segment = {
|
|
|
- header: segmentHeader,
|
|
|
- data: data
|
|
|
- };
|
|
|
- if (!header.randomAccess) {
|
|
|
- segment.start = position;
|
|
|
- position += segmentHeader.length;
|
|
|
- segment.end = position;
|
|
|
- }
|
|
|
- segments.push(segment);
|
|
|
- if (segmentHeader.type == 51) {
|
|
|
- break; // end of file is found
|
|
|
- }
|
|
|
+ return IrreversibleTransform;
|
|
|
+ })();
|
|
|
+
|
|
|
+ // Section 3.8.1 Reversible 5-3 filter
|
|
|
+ var ReversibleTransform = (function ReversibleTransformClosure() {
|
|
|
+ function ReversibleTransform() {
|
|
|
+ Transform.call(this);
|
|
|
}
|
|
|
- if (header.randomAccess) {
|
|
|
- for (var i = 0, ii = segments.length; i < ii; i++) {
|
|
|
- segments[i].start = position;
|
|
|
- position += segments[i].header.length;
|
|
|
- segments[i].end = position;
|
|
|
+
|
|
|
+ ReversibleTransform.prototype = Object.create(Transform.prototype);
|
|
|
+ ReversibleTransform.prototype.filter =
|
|
|
+ function reversibleTransformFilter(x, offset, length) {
|
|
|
+ var len = length >> 1;
|
|
|
+ offset = offset | 0;
|
|
|
+ var j, n;
|
|
|
+
|
|
|
+ for (j = offset, n = len + 1; n--; j += 2) {
|
|
|
+ x[j] -= (x[j - 1] + x[j + 1] + 2) >> 2;
|
|
|
}
|
|
|
- }
|
|
|
- return segments;
|
|
|
- }
|
|
|
|
|
|
- // 7.4.1 Region segment information field
|
|
|
- function readRegionSegmentInformation(data, start) {
|
|
|
- return {
|
|
|
- width: readUint32(data, start),
|
|
|
- height: readUint32(data, start + 4),
|
|
|
- x: readUint32(data, start + 8),
|
|
|
- y: readUint32(data, start + 12),
|
|
|
- combinationOperator: data[start + 16] & 7
|
|
|
+ for (j = offset + 1, n = len; n--; j += 2) {
|
|
|
+ x[j] += (x[j - 1] + x[j + 1]) >> 1;
|
|
|
+ }
|
|
|
};
|
|
|
- }
|
|
|
- var RegionSegmentInformationFieldLength = 17;
|
|
|
|
|
|
- function processSegment(segment, visitor) {
|
|
|
- var header = segment.header;
|
|
|
+ return ReversibleTransform;
|
|
|
+ })();
|
|
|
|
|
|
- var data = segment.data, position = segment.start, end = segment.end;
|
|
|
- var args, at, i, atLength;
|
|
|
- switch (header.type) {
|
|
|
- case 0: // SymbolDictionary
|
|
|
- // 7.4.2 Symbol dictionary segment syntax
|
|
|
- var dictionary = {};
|
|
|
- var dictionaryFlags = readUint16(data, position); // 7.4.2.1.1
|
|
|
- dictionary.huffman = !!(dictionaryFlags & 1);
|
|
|
- dictionary.refinement = !!(dictionaryFlags & 2);
|
|
|
- dictionary.huffmanDHSelector = (dictionaryFlags >> 2) & 3;
|
|
|
- dictionary.huffmanDWSelector = (dictionaryFlags >> 4) & 3;
|
|
|
- dictionary.bitmapSizeSelector = (dictionaryFlags >> 6) & 1;
|
|
|
- dictionary.aggregationInstancesSelector = (dictionaryFlags >> 7) & 1;
|
|
|
- dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256);
|
|
|
- dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512);
|
|
|
- dictionary.template = (dictionaryFlags >> 10) & 3;
|
|
|
- dictionary.refinementTemplate = (dictionaryFlags >> 12) & 1;
|
|
|
- position += 2;
|
|
|
- if (!dictionary.huffman) {
|
|
|
- atLength = dictionary.template === 0 ? 4 : 1;
|
|
|
- at = [];
|
|
|
- for (i = 0; i < atLength; i++) {
|
|
|
- at.push({
|
|
|
- x: readInt8(data, position),
|
|
|
- y: readInt8(data, position + 1)
|
|
|
- });
|
|
|
- position += 2;
|
|
|
- }
|
|
|
- dictionary.at = at;
|
|
|
- }
|
|
|
- if (dictionary.refinement && !dictionary.refinementTemplate) {
|
|
|
- at = [];
|
|
|
- for (i = 0; i < 2; i++) {
|
|
|
- at.push({
|
|
|
- x: readInt8(data, position),
|
|
|
- y: readInt8(data, position + 1)
|
|
|
- });
|
|
|
- position += 2;
|
|
|
- }
|
|
|
- dictionary.refinementAt = at;
|
|
|
- }
|
|
|
- dictionary.numberOfExportedSymbols = readUint32(data, position);
|
|
|
- position += 4;
|
|
|
- dictionary.numberOfNewSymbols = readUint32(data, position);
|
|
|
- position += 4;
|
|
|
- args = [dictionary, header.number, header.referredTo,
|
|
|
- data, position, end];
|
|
|
- break;
|
|
|
- case 6: // ImmediateTextRegion
|
|
|
- case 7: // ImmediateLosslessTextRegion
|
|
|
- var textRegion = {};
|
|
|
- textRegion.info = readRegionSegmentInformation(data, position);
|
|
|
- position += RegionSegmentInformationFieldLength;
|
|
|
- var textRegionSegmentFlags = readUint16(data, position);
|
|
|
- position += 2;
|
|
|
- textRegion.huffman = !!(textRegionSegmentFlags & 1);
|
|
|
- textRegion.refinement = !!(textRegionSegmentFlags & 2);
|
|
|
- textRegion.stripSize = 1 << ((textRegionSegmentFlags >> 2) & 3);
|
|
|
- textRegion.referenceCorner = (textRegionSegmentFlags >> 4) & 3;
|
|
|
- textRegion.transposed = !!(textRegionSegmentFlags & 64);
|
|
|
- textRegion.combinationOperator = (textRegionSegmentFlags >> 7) & 3;
|
|
|
- textRegion.defaultPixelValue = (textRegionSegmentFlags >> 9) & 1;
|
|
|
- textRegion.dsOffset = (textRegionSegmentFlags << 17) >> 27;
|
|
|
- textRegion.refinementTemplate = (textRegionSegmentFlags >> 15) & 1;
|
|
|
- if (textRegion.huffman) {
|
|
|
- var textRegionHuffmanFlags = readUint16(data, position);
|
|
|
- position += 2;
|
|
|
- textRegion.huffmanFS = (textRegionHuffmanFlags) & 3;
|
|
|
- textRegion.huffmanDS = (textRegionHuffmanFlags >> 2) & 3;
|
|
|
- textRegion.huffmanDT = (textRegionHuffmanFlags >> 4) & 3;
|
|
|
- textRegion.huffmanRefinementDW = (textRegionHuffmanFlags >> 6) & 3;
|
|
|
- textRegion.huffmanRefinementDH = (textRegionHuffmanFlags >> 8) & 3;
|
|
|
- textRegion.huffmanRefinementDX = (textRegionHuffmanFlags >> 10) & 3;
|
|
|
- textRegion.huffmanRefinementDY = (textRegionHuffmanFlags >> 12) & 3;
|
|
|
- textRegion.huffmanRefinementSizeSelector =
|
|
|
- !!(textRegionHuffmanFlags & 14);
|
|
|
- }
|
|
|
- if (textRegion.refinement && !textRegion.refinementTemplate) {
|
|
|
- at = [];
|
|
|
- for (i = 0; i < 2; i++) {
|
|
|
- at.push({
|
|
|
- x: readInt8(data, position),
|
|
|
- y: readInt8(data, position + 1)
|
|
|
- });
|
|
|
- position += 2;
|
|
|
- }
|
|
|
- textRegion.refinementAt = at;
|
|
|
- }
|
|
|
- textRegion.numberOfSymbolInstances = readUint32(data, position);
|
|
|
- position += 4;
|
|
|
- // TODO 7.4.3.1.7 Symbol ID Huffman table decoding
|
|
|
- if (textRegion.huffman) {
|
|
|
- error('JBIG2 error: huffman is not supported');
|
|
|
- }
|
|
|
- args = [textRegion, header.referredTo, data, position, end];
|
|
|
- break;
|
|
|
- case 38: // ImmediateGenericRegion
|
|
|
- case 39: // ImmediateLosslessGenericRegion
|
|
|
- var genericRegion = {};
|
|
|
- genericRegion.info = readRegionSegmentInformation(data, position);
|
|
|
- position += RegionSegmentInformationFieldLength;
|
|
|
- var genericRegionSegmentFlags = data[position++];
|
|
|
- genericRegion.mmr = !!(genericRegionSegmentFlags & 1);
|
|
|
- genericRegion.template = (genericRegionSegmentFlags >> 1) & 3;
|
|
|
- genericRegion.prediction = !!(genericRegionSegmentFlags & 8);
|
|
|
- if (!genericRegion.mmr) {
|
|
|
- atLength = genericRegion.template === 0 ? 4 : 1;
|
|
|
- at = [];
|
|
|
- for (i = 0; i < atLength; i++) {
|
|
|
- at.push({
|
|
|
- x: readInt8(data, position),
|
|
|
- y: readInt8(data, position + 1)
|
|
|
- });
|
|
|
- position += 2;
|
|
|
- }
|
|
|
- genericRegion.at = at;
|
|
|
- }
|
|
|
- args = [genericRegion, data, position, end];
|
|
|
- break;
|
|
|
- case 48: // PageInformation
|
|
|
- var pageInfo = {
|
|
|
- width: readUint32(data, position),
|
|
|
- height: readUint32(data, position + 4),
|
|
|
- resolutionX: readUint32(data, position + 8),
|
|
|
- resolutionY: readUint32(data, position + 12)
|
|
|
- };
|
|
|
- if (pageInfo.height == 0xFFFFFFFF) {
|
|
|
- delete pageInfo.height;
|
|
|
- }
|
|
|
- var pageSegmentFlags = data[position + 16];
|
|
|
- var pageStripingInformatiom = readUint16(data, position + 17);
|
|
|
- pageInfo.lossless = !!(pageSegmentFlags & 1);
|
|
|
- pageInfo.refinement = !!(pageSegmentFlags & 2);
|
|
|
- pageInfo.defaultPixelValue = (pageSegmentFlags >> 2) & 1;
|
|
|
- pageInfo.combinationOperator = (pageSegmentFlags >> 3) & 3;
|
|
|
- pageInfo.requiresBuffer = !!(pageSegmentFlags & 32);
|
|
|
- pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64);
|
|
|
- args = [pageInfo];
|
|
|
- break;
|
|
|
- case 49: // EndOfPage
|
|
|
- break;
|
|
|
- case 50: // EndOfStripe
|
|
|
- break;
|
|
|
- case 51: // EndOfFile
|
|
|
- break;
|
|
|
- case 62: // 7.4.15 defines 2 extension types which
|
|
|
- // are comments and can be ignored.
|
|
|
- break;
|
|
|
- default:
|
|
|
- error('JBIG2 error: segment type ' + header.typeName + '(' +
|
|
|
- header.type + ') is not implemented');
|
|
|
- }
|
|
|
- var callbackName = 'on' + header.typeName;
|
|
|
- if (callbackName in visitor) {
|
|
|
- visitor[callbackName].apply(visitor, args);
|
|
|
- }
|
|
|
- }
|
|
|
+ return JpxImage;
|
|
|
+})();
|
|
|
|
|
|
- function processSegments(segments, visitor) {
|
|
|
- for (var i = 0, ii = segments.length; i < ii; i++) {
|
|
|
- processSegment(segments[i], visitor);
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- function parseJbig2(data, start, end) {
|
|
|
- var position = start;
|
|
|
- if (data[position] != 0x97 || data[position + 1] != 0x4A ||
|
|
|
- data[position + 2] != 0x42 || data[position + 3] != 0x32 ||
|
|
|
- data[position + 4] != 0x0D || data[position + 5] != 0x0A ||
|
|
|
- data[position + 6] != 0x1A || data[position + 7] != 0x0A) {
|
|
|
- error('JBIG2 error: invalid header');
|
|
|
- }
|
|
|
- var header = {};
|
|
|
- position += 8;
|
|
|
- var flags = data[position++];
|
|
|
- header.randomAccess = !(flags & 1);
|
|
|
- if (!(flags & 2)) {
|
|
|
- header.numberOfPages = readUint32(data, position);
|
|
|
- position += 4;
|
|
|
+
|
|
|
+var Jbig2Image = (function Jbig2ImageClosure() {
|
|
|
+ // Utility data structures
|
|
|
+ function ContextCache() {}
|
|
|
+
|
|
|
+ ContextCache.prototype = {
|
|
|
+ getContexts: function(id) {
|
|
|
+ if (id in this) {
|
|
|
+ return this[id];
|
|
|
+ }
|
|
|
+ return (this[id] = new Int8Array(1<<16));
|
|
|
}
|
|
|
- var segments = readSegments(header, data, position, end);
|
|
|
- error('Not implemented');
|
|
|
- // processSegments(segments, new SimpleSegmentVisitor());
|
|
|
+ };
|
|
|
+
|
|
|
+ function DecodingContext(data, start, end) {
|
|
|
+ this.data = data;
|
|
|
+ this.start = start;
|
|
|
+ this.end = end;
|
|
|
}
|
|
|
|
|
|
- function parseJbig2Chunks(chunks) {
|
|
|
- var visitor = new SimpleSegmentVisitor();
|
|
|
- for (var i = 0, ii = chunks.length; i < ii; i++) {
|
|
|
- var chunk = chunks[i];
|
|
|
- var segments = readSegments({}, chunk.data, chunk.start, chunk.end);
|
|
|
- processSegments(segments, visitor);
|
|
|
+ DecodingContext.prototype = {
|
|
|
+ get decoder() {
|
|
|
+ var decoder = new ArithmeticDecoder(this.data, this.start, this.end);
|
|
|
+ return shadow(this, 'decoder', decoder);
|
|
|
+ },
|
|
|
+ get contextCache() {
|
|
|
+ var cache = new ContextCache();
|
|
|
+ return shadow(this, 'contextCache', cache);
|
|
|
}
|
|
|
- return visitor.buffer;
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- function SimpleSegmentVisitor() {}
|
|
|
+ // Annex A. Arithmetic Integer Decoding Procedure
|
|
|
+ // A.2 Procedure for decoding values
|
|
|
+ function decodeInteger(contextCache, procedure, decoder) {
|
|
|
+ var contexts = contextCache.getContexts(procedure);
|
|
|
|
|
|
- SimpleSegmentVisitor.prototype = {
|
|
|
- onPageInformation: function SimpleSegmentVisitor_onPageInformation(info) {
|
|
|
- this.currentPageInfo = info;
|
|
|
- var rowSize = (info.width + 7) >> 3;
|
|
|
- var buffer = new Uint8Array(rowSize * info.height);
|
|
|
- // The contents of ArrayBuffers are initialized to 0.
|
|
|
- // Fill the buffer with 0xFF only if info.defaultPixelValue is set
|
|
|
- if (info.defaultPixelValue) {
|
|
|
- for (var i = 0, ii = buffer.length; i < ii; i++) {
|
|
|
- buffer[i] = 0xFF;
|
|
|
- }
|
|
|
- }
|
|
|
- this.buffer = buffer;
|
|
|
- },
|
|
|
- drawBitmap: function SimpleSegmentVisitor_drawBitmap(regionInfo, bitmap) {
|
|
|
- var pageInfo = this.currentPageInfo;
|
|
|
- var width = regionInfo.width, height = regionInfo.height;
|
|
|
- var rowSize = (pageInfo.width + 7) >> 3;
|
|
|
- var combinationOperator = pageInfo.combinationOperatorOverride ?
|
|
|
- regionInfo.combinationOperator : pageInfo.combinationOperator;
|
|
|
- var buffer = this.buffer;
|
|
|
- var mask0 = 128 >> (regionInfo.x & 7);
|
|
|
- var offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3);
|
|
|
- var i, j, mask, offset;
|
|
|
- switch (combinationOperator) {
|
|
|
- case 0: // OR
|
|
|
- for (i = 0; i < height; i++) {
|
|
|
- mask = mask0;
|
|
|
- offset = offset0;
|
|
|
- for (j = 0; j < width; j++) {
|
|
|
- if (bitmap[i][j]) {
|
|
|
- buffer[offset] |= mask;
|
|
|
- }
|
|
|
- mask >>= 1;
|
|
|
- if (!mask) {
|
|
|
- mask = 128;
|
|
|
- offset++;
|
|
|
- }
|
|
|
- }
|
|
|
- offset0 += rowSize;
|
|
|
+ var prev = 1;
|
|
|
+ var state = 1, v = 0, s;
|
|
|
+ var toRead = 32, offset = 4436; // defaults for state 7
|
|
|
+ while (state) {
|
|
|
+ var bit = decoder.readBit(contexts, prev);
|
|
|
+ prev = (prev < 256 ? (prev << 1) | bit :
|
|
|
+ (((prev << 1) | bit) & 511) | 256);
|
|
|
+ switch (state) {
|
|
|
+ case 1:
|
|
|
+ s = !!bit;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ if (bit) {
|
|
|
+ break;
|
|
|
}
|
|
|
- break;
|
|
|
- case 2: // XOR
|
|
|
- for (i = 0; i < height; i++) {
|
|
|
- mask = mask0;
|
|
|
- offset = offset0;
|
|
|
- for (j = 0; j < width; j++) {
|
|
|
- if (bitmap[i][j]) {
|
|
|
- buffer[offset] ^= mask;
|
|
|
- }
|
|
|
- mask >>= 1;
|
|
|
- if (!mask) {
|
|
|
- mask = 128;
|
|
|
- offset++;
|
|
|
- }
|
|
|
- }
|
|
|
- offset0 += rowSize;
|
|
|
+ state = 7;
|
|
|
+ toRead = 2;
|
|
|
+ offset = 0;
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ if (bit) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ state = 7;
|
|
|
+ toRead = 4;
|
|
|
+ offset = 4;
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ if (bit) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ state = 7;
|
|
|
+ toRead = 6;
|
|
|
+ offset = 20;
|
|
|
+ break;
|
|
|
+ case 5:
|
|
|
+ if (bit) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ state = 7;
|
|
|
+ toRead = 8;
|
|
|
+ offset = 84;
|
|
|
+ break;
|
|
|
+ case 6:
|
|
|
+ if (bit) {
|
|
|
+ break;
|
|
|
}
|
|
|
+ state = 7;
|
|
|
+ toRead = 12;
|
|
|
+ offset = 340;
|
|
|
break;
|
|
|
default:
|
|
|
- error('JBIG2 error: operator ' + combinationOperator +
|
|
|
- ' is not supported');
|
|
|
- }
|
|
|
- },
|
|
|
- onImmediateGenericRegion:
|
|
|
- function SimpleSegmentVisitor_onImmediateGenericRegion(region, data,
|
|
|
- start, end) {
|
|
|
- var regionInfo = region.info;
|
|
|
- var decodingContext = new DecodingContext(data, start, end);
|
|
|
- var bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height,
|
|
|
- region.template, region.prediction, null,
|
|
|
- region.at, decodingContext);
|
|
|
- this.drawBitmap(regionInfo, bitmap);
|
|
|
- },
|
|
|
- onImmediateLosslessGenericRegion:
|
|
|
- function SimpleSegmentVisitor_onImmediateLosslessGenericRegion() {
|
|
|
- this.onImmediateGenericRegion.apply(this, arguments);
|
|
|
- },
|
|
|
- onSymbolDictionary:
|
|
|
- function SimpleSegmentVisitor_onSymbolDictionary(dictionary,
|
|
|
- currentSegment,
|
|
|
- referredSegments,
|
|
|
- data, start, end) {
|
|
|
- var huffmanTables;
|
|
|
- if (dictionary.huffman) {
|
|
|
- error('JBIG2 error: huffman is not supported');
|
|
|
- }
|
|
|
-
|
|
|
- // Combines exported symbols from all referred segments
|
|
|
- var symbols = this.symbols;
|
|
|
- if (!symbols) {
|
|
|
- this.symbols = symbols = {};
|
|
|
+ v = ((v << 1) | bit) >>> 0;
|
|
|
+ if (--toRead === 0) {
|
|
|
+ state = 0;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
}
|
|
|
+ state++;
|
|
|
+ }
|
|
|
+ v += offset;
|
|
|
+ return (!s ? v : (v > 0 ? -v : null));
|
|
|
+ }
|
|
|
|
|
|
- var inputSymbols = [];
|
|
|
- for (var i = 0, ii = referredSegments.length; i < ii; i++) {
|
|
|
- inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
|
|
|
- }
|
|
|
+ // A.3 The IAID decoding procedure
|
|
|
+ function decodeIAID(contextCache, decoder, codeLength) {
|
|
|
+ var contexts = contextCache.getContexts('IAID');
|
|
|
|
|
|
- var decodingContext = new DecodingContext(data, start, end);
|
|
|
- symbols[currentSegment] = decodeSymbolDictionary(dictionary.huffman,
|
|
|
- dictionary.refinement, inputSymbols, dictionary.numberOfNewSymbols,
|
|
|
- dictionary.numberOfExportedSymbols, huffmanTables,
|
|
|
- dictionary.template, dictionary.at,
|
|
|
- dictionary.refinementTemplate, dictionary.refinementAt,
|
|
|
- decodingContext);
|
|
|
- },
|
|
|
- onImmediateTextRegion:
|
|
|
- function SimpleSegmentVisitor_onImmediateTextRegion(region,
|
|
|
- referredSegments,
|
|
|
- data, start, end) {
|
|
|
- var regionInfo = region.info;
|
|
|
- var huffmanTables;
|
|
|
+ var prev = 1;
|
|
|
+ for (var i = 0; i < codeLength; i++) {
|
|
|
+ var bit = decoder.readBit(contexts, prev);
|
|
|
+ prev = (prev << 1) | bit;
|
|
|
+ }
|
|
|
+ if (codeLength < 31) {
|
|
|
+ return prev & ((1 << codeLength) - 1);
|
|
|
+ }
|
|
|
+ return prev & 0x7FFFFFFF;
|
|
|
+ }
|
|
|
|
|
|
- // Combines exported symbols from all referred segments
|
|
|
- var symbols = this.symbols;
|
|
|
- var inputSymbols = [];
|
|
|
- for (var i = 0, ii = referredSegments.length; i < ii; i++) {
|
|
|
- inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
|
|
|
- }
|
|
|
- var symbolCodeLength = log2(inputSymbols.length);
|
|
|
+ // 7.3 Segment types
|
|
|
+ var SegmentTypes = [
|
|
|
+ 'SymbolDictionary', null, null, null, 'IntermediateTextRegion', null,
|
|
|
+ 'ImmediateTextRegion', 'ImmediateLosslessTextRegion', null, null, null,
|
|
|
+ null, null, null, null, null, 'patternDictionary', null, null, null,
|
|
|
+ 'IntermediateHalftoneRegion', null, 'ImmediateHalftoneRegion',
|
|
|
+ 'ImmediateLosslessHalftoneRegion', null, null, null, null, null, null, null,
|
|
|
+ null, null, null, null, null, 'IntermediateGenericRegion', null,
|
|
|
+ 'ImmediateGenericRegion', 'ImmediateLosslessGenericRegion',
|
|
|
+ 'IntermediateGenericRefinementRegion', null,
|
|
|
+ 'ImmediateGenericRefinementRegion',
|
|
|
+ 'ImmediateLosslessGenericRefinementRegion', null, null, null, null,
|
|
|
+ 'PageInformation', 'EndOfPage', 'EndOfStripe', 'EndOfFile', 'Profiles',
|
|
|
+ 'Tables', null, null, null, null, null, null, null, null,
|
|
|
+ 'Extension'
|
|
|
+ ];
|
|
|
|
|
|
- var decodingContext = new DecodingContext(data, start, end);
|
|
|
- var bitmap = decodeTextRegion(region.huffman, region.refinement,
|
|
|
- regionInfo.width, regionInfo.height, region.defaultPixelValue,
|
|
|
- region.numberOfSymbolInstances, region.stripSize, inputSymbols,
|
|
|
- symbolCodeLength, region.transposed, region.dsOffset,
|
|
|
- region.referenceCorner, region.combinationOperator, huffmanTables,
|
|
|
- region.refinementTemplate, region.refinementAt, decodingContext);
|
|
|
- this.drawBitmap(regionInfo, bitmap);
|
|
|
+ var CodingTemplates = [
|
|
|
+ [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: -2, y: -1},
|
|
|
+ {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: 2, y: -1},
|
|
|
+ {x: -4, y: 0}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}],
|
|
|
+ [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: 2, y: -2},
|
|
|
+ {x: -2, y: -1}, {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1},
|
|
|
+ {x: 2, y: -1}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}],
|
|
|
+ [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: -2, y: -1},
|
|
|
+ {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: -2, y: 0},
|
|
|
+ {x: -1, y: 0}],
|
|
|
+ [{x: -3, y: -1}, {x: -2, y: -1}, {x: -1, y: -1}, {x: 0, y: -1},
|
|
|
+ {x: 1, y: -1}, {x: -4, y: 0}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}]
|
|
|
+ ];
|
|
|
+
|
|
|
+ var RefinementTemplates = [
|
|
|
+ {
|
|
|
+ coding: [{x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}],
|
|
|
+ reference: [{x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}, {x: 0, y: 0},
|
|
|
+ {x: 1, y: 0}, {x: -1, y: 1}, {x: 0, y: 1}, {x: 1, y: 1}]
|
|
|
},
|
|
|
- onImmediateLosslessTextRegion:
|
|
|
- function SimpleSegmentVisitor_onImmediateLosslessTextRegion() {
|
|
|
- this.onImmediateTextRegion.apply(this, arguments);
|
|
|
+ {
|
|
|
+ coding: [{x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}],
|
|
|
+ reference: [{x: 0, y: -1}, {x: -1, y: 0}, {x: 0, y: 0}, {x: 1, y: 0},
|
|
|
+ {x: 0, y: 1}, {x: 1, y: 1}]
|
|
|
}
|
|
|
- };
|
|
|
+ ];
|
|
|
|
|
|
- function Jbig2Image() {}
|
|
|
+ var ReusedContexts = [
|
|
|
+ 0x1CD3, // '00111001101' (template) + '0011' (at),
|
|
|
+ 0x079A, // '001111001101' + '0',
|
|
|
+ 0x00E3, // '001110001' + '1',
|
|
|
+ 0x018B // '011000101' + '1'
|
|
|
+ ];
|
|
|
|
|
|
- Jbig2Image.prototype = {
|
|
|
- parseChunks: function Jbig2Image_parseChunks(chunks) {
|
|
|
- return parseJbig2Chunks(chunks);
|
|
|
+ var RefinementReusedContexts = [
|
|
|
+ 0x0020, // '000' + '0' (coding) + '00010000' + '0' (reference)
|
|
|
+ 0x0008 // '0000' + '001000'
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 6.2 Generic Region Decoding Procedure
|
|
|
+ function decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at,
|
|
|
+ decodingContext) {
|
|
|
+ if (mmr) {
|
|
|
+ error('JBIG2 error: MMR encoding is not supported');
|
|
|
}
|
|
|
- };
|
|
|
|
|
|
- return Jbig2Image;
|
|
|
-})();
|
|
|
+ var useskip = !!skip;
|
|
|
+ var template = CodingTemplates[templateIndex].concat(at);
|
|
|
+
|
|
|
+ // Sorting is non-standard, and it is not required. But sorting increases
|
|
|
+ // the number of template bits that can be reused from the previous
|
|
|
+ // contextLabel in the main loop.
|
|
|
+ template.sort(function (a, b) {
|
|
|
+ return (a.y - b.y) || (a.x - b.x);
|
|
|
+ });
|
|
|
|
|
|
+ var templateLength = template.length;
|
|
|
+ var templateX = new Int8Array(templateLength);
|
|
|
+ var templateY = new Int8Array(templateLength);
|
|
|
+ var changingTemplateEntries = [];
|
|
|
+ var reuseMask = 0, minX = 0, maxX = 0, minY = 0;
|
|
|
+ var c, k;
|
|
|
|
|
|
-var bidi = PDFJS.bidi = (function bidiClosure() {
|
|
|
- // Character types for symbols from 0000 to 00FF.
|
|
|
- var baseTypes = [
|
|
|
- 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
|
|
|
- 'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
|
|
- 'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
|
|
|
- 'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
|
|
|
- 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
|
|
|
- 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
- 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
|
|
|
- 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
- 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
- 'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
|
|
|
- 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
|
|
- 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
|
|
- 'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
|
|
|
- 'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
|
|
|
- 'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
- 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
- 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
- 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
- 'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
|
|
|
- ];
|
|
|
+ for (k = 0; k < templateLength; k++) {
|
|
|
+ templateX[k] = template[k].x;
|
|
|
+ templateY[k] = template[k].y;
|
|
|
+ minX = Math.min(minX, template[k].x);
|
|
|
+ maxX = Math.max(maxX, template[k].x);
|
|
|
+ minY = Math.min(minY, template[k].y);
|
|
|
+ // Check if the template pixel appears in two consecutive context labels,
|
|
|
+ // so it can be reused. Otherwise, we add it to the list of changing
|
|
|
+ // template entries.
|
|
|
+ if (k < templateLength - 1 &&
|
|
|
+ template[k].y === template[k + 1].y &&
|
|
|
+ template[k].x === template[k + 1].x - 1) {
|
|
|
+ reuseMask |= 1 << (templateLength - 1 - k);
|
|
|
+ } else {
|
|
|
+ changingTemplateEntries.push(k);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var changingEntriesLength = changingTemplateEntries.length;
|
|
|
|
|
|
- // Character types for symbols from 0600 to 06FF
|
|
|
- var arabicTypes = [
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
|
|
|
- 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
|
|
|
- 'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
|
|
|
- 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
|
|
|
- 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
- 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
|
|
|
- ];
|
|
|
+ var changingTemplateX = new Int8Array(changingEntriesLength);
|
|
|
+ var changingTemplateY = new Int8Array(changingEntriesLength);
|
|
|
+ var changingTemplateBit = new Uint16Array(changingEntriesLength);
|
|
|
+ for (c = 0; c < changingEntriesLength; c++) {
|
|
|
+ k = changingTemplateEntries[c];
|
|
|
+ changingTemplateX[c] = template[k].x;
|
|
|
+ changingTemplateY[c] = template[k].y;
|
|
|
+ changingTemplateBit[c] = 1 << (templateLength - 1 - k);
|
|
|
+ }
|
|
|
|
|
|
- function isOdd(i) {
|
|
|
- return (i & 1) !== 0;
|
|
|
- }
|
|
|
+ // Get the safe bounding box edges from the width, height, minX, maxX, minY
|
|
|
+ var sbb_left = -minX;
|
|
|
+ var sbb_top = -minY;
|
|
|
+ var sbb_right = width - maxX;
|
|
|
|
|
|
- function isEven(i) {
|
|
|
- return (i & 1) === 0;
|
|
|
- }
|
|
|
+ var pseudoPixelContext = ReusedContexts[templateIndex];
|
|
|
+ var row = new Uint8Array(width);
|
|
|
+ var bitmap = [];
|
|
|
|
|
|
- function findUnequal(arr, start, value) {
|
|
|
- for (var j = start, jj = arr.length; j < jj; ++j) {
|
|
|
- if (arr[j] != value) {
|
|
|
- return j;
|
|
|
+ var decoder = decodingContext.decoder;
|
|
|
+ var contexts = decodingContext.contextCache.getContexts('GB');
|
|
|
+
|
|
|
+ var ltp = 0, j, i0, j0, contextLabel = 0, bit, shift;
|
|
|
+ for (var i = 0; i < height; i++) {
|
|
|
+ if (prediction) {
|
|
|
+ var sltp = decoder.readBit(contexts, pseudoPixelContext);
|
|
|
+ ltp ^= sltp;
|
|
|
+ if (ltp) {
|
|
|
+ bitmap.push(row); // duplicate previous row
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ row = new Uint8Array(row);
|
|
|
+ bitmap.push(row);
|
|
|
+ for (j = 0; j < width; j++) {
|
|
|
+ if (useskip && skip[i][j]) {
|
|
|
+ row[j] = 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // Are we in the middle of a scanline, so we can reuse contextLabel
|
|
|
+ // bits?
|
|
|
+ if (j >= sbb_left && j < sbb_right && i >= sbb_top) {
|
|
|
+ // If yes, we can just shift the bits that are reusable and only
|
|
|
+ // fetch the remaining ones.
|
|
|
+ contextLabel = (contextLabel << 1) & reuseMask;
|
|
|
+ for (k = 0; k < changingEntriesLength; k++) {
|
|
|
+ i0 = i + changingTemplateY[k];
|
|
|
+ j0 = j + changingTemplateX[k];
|
|
|
+ bit = bitmap[i0][j0];
|
|
|
+ if (bit) {
|
|
|
+ bit = changingTemplateBit[k];
|
|
|
+ contextLabel |= bit;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // compute the contextLabel from scratch
|
|
|
+ contextLabel = 0;
|
|
|
+ shift = templateLength - 1;
|
|
|
+ for (k = 0; k < templateLength; k++, shift--) {
|
|
|
+ j0 = j + templateX[k];
|
|
|
+ if (j0 >= 0 && j0 < width) {
|
|
|
+ i0 = i + templateY[k];
|
|
|
+ if (i0 >= 0) {
|
|
|
+ bit = bitmap[i0][j0];
|
|
|
+ if (bit) {
|
|
|
+ contextLabel |= bit << shift;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var pixel = decoder.readBit(contexts, contextLabel);
|
|
|
+ row[j] = pixel;
|
|
|
}
|
|
|
}
|
|
|
- return j;
|
|
|
+ return bitmap;
|
|
|
}
|
|
|
|
|
|
- function setValues(arr, start, end, value) {
|
|
|
- for (var j = start; j < end; ++j) {
|
|
|
- arr[j] = value;
|
|
|
+ // 6.3.2 Generic Refinement Region Decoding Procedure
|
|
|
+ function decodeRefinement(width, height, templateIndex, referenceBitmap,
|
|
|
+ offsetX, offsetY, prediction, at,
|
|
|
+ decodingContext) {
|
|
|
+ var codingTemplate = RefinementTemplates[templateIndex].coding;
|
|
|
+ if (templateIndex === 0) {
|
|
|
+ codingTemplate = codingTemplate.concat([at[0]]);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- function reverseValues(arr, start, end) {
|
|
|
- for (var i = start, j = end - 1; i < j; ++i, --j) {
|
|
|
- var temp = arr[i];
|
|
|
- arr[i] = arr[j];
|
|
|
- arr[j] = temp;
|
|
|
+ var codingTemplateLength = codingTemplate.length;
|
|
|
+ var codingTemplateX = new Int32Array(codingTemplateLength);
|
|
|
+ var codingTemplateY = new Int32Array(codingTemplateLength);
|
|
|
+ var k;
|
|
|
+ for (k = 0; k < codingTemplateLength; k++) {
|
|
|
+ codingTemplateX[k] = codingTemplate[k].x;
|
|
|
+ codingTemplateY[k] = codingTemplate[k].y;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- function createBidiText(str, isLTR, vertical) {
|
|
|
- return {
|
|
|
- str: str,
|
|
|
- dir: (vertical ? 'ttb' : (isLTR ? 'ltr' : 'rtl'))
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- // These are used in bidi(), which is called frequently. We re-use them on
|
|
|
- // each call to avoid unnecessary allocations.
|
|
|
- var chars = [];
|
|
|
- var types = [];
|
|
|
|
|
|
- function bidi(str, startLevel, vertical) {
|
|
|
- var isLTR = true;
|
|
|
- var strLength = str.length;
|
|
|
- if (strLength === 0 || vertical) {
|
|
|
- return createBidiText(str, isLTR, vertical);
|
|
|
+ var referenceTemplate = RefinementTemplates[templateIndex].reference;
|
|
|
+ if (templateIndex === 0) {
|
|
|
+ referenceTemplate = referenceTemplate.concat([at[1]]);
|
|
|
}
|
|
|
+ var referenceTemplateLength = referenceTemplate.length;
|
|
|
+ var referenceTemplateX = new Int32Array(referenceTemplateLength);
|
|
|
+ var referenceTemplateY = new Int32Array(referenceTemplateLength);
|
|
|
+ for (k = 0; k < referenceTemplateLength; k++) {
|
|
|
+ referenceTemplateX[k] = referenceTemplate[k].x;
|
|
|
+ referenceTemplateY[k] = referenceTemplate[k].y;
|
|
|
+ }
|
|
|
+ var referenceWidth = referenceBitmap[0].length;
|
|
|
+ var referenceHeight = referenceBitmap.length;
|
|
|
|
|
|
- // Get types and fill arrays
|
|
|
- chars.length = 0;
|
|
|
- types.length = 0;
|
|
|
- var numBidi = 0;
|
|
|
+ var pseudoPixelContext = RefinementReusedContexts[templateIndex];
|
|
|
+ var bitmap = [];
|
|
|
|
|
|
- var i, ii;
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- chars[i] = str.charAt(i);
|
|
|
+ var decoder = decodingContext.decoder;
|
|
|
+ var contexts = decodingContext.contextCache.getContexts('GR');
|
|
|
|
|
|
- var charCode = str.charCodeAt(i);
|
|
|
- var charType = 'L';
|
|
|
- if (charCode <= 0x00ff) {
|
|
|
- charType = baseTypes[charCode];
|
|
|
- } else if (0x0590 <= charCode && charCode <= 0x05f4) {
|
|
|
- charType = 'R';
|
|
|
- } else if (0x0600 <= charCode && charCode <= 0x06ff) {
|
|
|
- charType = arabicTypes[charCode & 0xff];
|
|
|
- } else if (0x0700 <= charCode && charCode <= 0x08AC) {
|
|
|
- charType = 'AL';
|
|
|
+ var ltp = 0;
|
|
|
+ for (var i = 0; i < height; i++) {
|
|
|
+ if (prediction) {
|
|
|
+ var sltp = decoder.readBit(contexts, pseudoPixelContext);
|
|
|
+ ltp ^= sltp;
|
|
|
+ if (ltp) {
|
|
|
+ error('JBIG2 error: prediction is not supported');
|
|
|
+ }
|
|
|
}
|
|
|
- if (charType == 'R' || charType == 'AL' || charType == 'AN') {
|
|
|
- numBidi++;
|
|
|
+ var row = new Uint8Array(width);
|
|
|
+ bitmap.push(row);
|
|
|
+ for (var j = 0; j < width; j++) {
|
|
|
+ var i0, j0;
|
|
|
+ var contextLabel = 0;
|
|
|
+ for (k = 0; k < codingTemplateLength; k++) {
|
|
|
+ i0 = i + codingTemplateY[k];
|
|
|
+ j0 = j + codingTemplateX[k];
|
|
|
+ if (i0 < 0 || j0 < 0 || j0 >= width) {
|
|
|
+ contextLabel <<= 1; // out of bound pixel
|
|
|
+ } else {
|
|
|
+ contextLabel = (contextLabel << 1) | bitmap[i0][j0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (k = 0; k < referenceTemplateLength; k++) {
|
|
|
+ i0 = i + referenceTemplateY[k] + offsetY;
|
|
|
+ j0 = j + referenceTemplateX[k] + offsetX;
|
|
|
+ if (i0 < 0 || i0 >= referenceHeight || j0 < 0 ||
|
|
|
+ j0 >= referenceWidth) {
|
|
|
+ contextLabel <<= 1; // out of bound pixel
|
|
|
+ } else {
|
|
|
+ contextLabel = (contextLabel << 1) | referenceBitmap[i0][j0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var pixel = decoder.readBit(contexts, contextLabel);
|
|
|
+ row[j] = pixel;
|
|
|
}
|
|
|
- types[i] = charType;
|
|
|
}
|
|
|
|
|
|
- // Detect the bidi method
|
|
|
- // - If there are no rtl characters then no bidi needed
|
|
|
- // - If less than 30% chars are rtl then string is primarily ltr
|
|
|
- // - If more than 30% chars are rtl then string is primarily rtl
|
|
|
- if (numBidi === 0) {
|
|
|
- isLTR = true;
|
|
|
- return createBidiText(str, isLTR);
|
|
|
- }
|
|
|
+ return bitmap;
|
|
|
+ }
|
|
|
|
|
|
- if (startLevel == -1) {
|
|
|
- if ((strLength / numBidi) < 0.3) {
|
|
|
- isLTR = true;
|
|
|
- startLevel = 0;
|
|
|
- } else {
|
|
|
- isLTR = false;
|
|
|
- startLevel = 1;
|
|
|
- }
|
|
|
+ // 6.5.5 Decoding the symbol dictionary
|
|
|
+ function decodeSymbolDictionary(huffman, refinement, symbols,
|
|
|
+ numberOfNewSymbols, numberOfExportedSymbols,
|
|
|
+ huffmanTables, templateIndex, at,
|
|
|
+ refinementTemplateIndex, refinementAt,
|
|
|
+ decodingContext) {
|
|
|
+ if (huffman) {
|
|
|
+ error('JBIG2 error: huffman is not supported');
|
|
|
}
|
|
|
|
|
|
- var levels = [];
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- levels[i] = startLevel;
|
|
|
- }
|
|
|
+ var newSymbols = [];
|
|
|
+ var currentHeight = 0;
|
|
|
+ var symbolCodeLength = log2(symbols.length + numberOfNewSymbols);
|
|
|
|
|
|
- /*
|
|
|
- X1-X10: skip most of this, since we are NOT doing the embeddings.
|
|
|
- */
|
|
|
- var e = (isOdd(startLevel) ? 'R' : 'L');
|
|
|
- var sor = e;
|
|
|
- var eor = sor;
|
|
|
+ var decoder = decodingContext.decoder;
|
|
|
+ var contextCache = decodingContext.contextCache;
|
|
|
|
|
|
- /*
|
|
|
- W1. Examine each non-spacing mark (NSM) in the level run, and change the
|
|
|
- type of the NSM to the type of the previous character. If the NSM is at the
|
|
|
- start of the level run, it will get the type of sor.
|
|
|
- */
|
|
|
- var lastType = sor;
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- if (types[i] == 'NSM') {
|
|
|
- types[i] = lastType;
|
|
|
- } else {
|
|
|
- lastType = types[i];
|
|
|
+ while (newSymbols.length < numberOfNewSymbols) {
|
|
|
+ var deltaHeight = decodeInteger(contextCache, 'IADH', decoder); // 6.5.6
|
|
|
+ currentHeight += deltaHeight;
|
|
|
+ var currentWidth = 0;
|
|
|
+ var totalWidth = 0;
|
|
|
+ while (true) {
|
|
|
+ var deltaWidth = decodeInteger(contextCache, 'IADW', decoder); // 6.5.7
|
|
|
+ if (deltaWidth === null) {
|
|
|
+ break; // OOB
|
|
|
+ }
|
|
|
+ currentWidth += deltaWidth;
|
|
|
+ totalWidth += currentWidth;
|
|
|
+ var bitmap;
|
|
|
+ if (refinement) {
|
|
|
+ // 6.5.8.2 Refinement/aggregate-coded symbol bitmap
|
|
|
+ var numberOfInstances = decodeInteger(contextCache, 'IAAI', decoder);
|
|
|
+ if (numberOfInstances > 1) {
|
|
|
+ bitmap = decodeTextRegion(huffman, refinement,
|
|
|
+ currentWidth, currentHeight, 0,
|
|
|
+ numberOfInstances, 1, //strip size
|
|
|
+ symbols.concat(newSymbols),
|
|
|
+ symbolCodeLength,
|
|
|
+ 0, //transposed
|
|
|
+ 0, //ds offset
|
|
|
+ 1, //top left 7.4.3.1.1
|
|
|
+ 0, //OR operator
|
|
|
+ huffmanTables,
|
|
|
+ refinementTemplateIndex, refinementAt,
|
|
|
+ decodingContext);
|
|
|
+ } else {
|
|
|
+ var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
|
|
|
+ var rdx = decodeInteger(contextCache, 'IARDX', decoder); // 6.4.11.3
|
|
|
+ var rdy = decodeInteger(contextCache, 'IARDY', decoder); // 6.4.11.4
|
|
|
+ var symbol = (symbolId < symbols.length ? symbols[symbolId] :
|
|
|
+ newSymbols[symbolId - symbols.length]);
|
|
|
+ bitmap = decodeRefinement(currentWidth, currentHeight,
|
|
|
+ refinementTemplateIndex, symbol, rdx, rdy, false, refinementAt,
|
|
|
+ decodingContext);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 6.5.8.1 Direct-coded symbol bitmap
|
|
|
+ bitmap = decodeBitmap(false, currentWidth, currentHeight,
|
|
|
+ templateIndex, false, null, at, decodingContext);
|
|
|
+ }
|
|
|
+ newSymbols.push(bitmap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 6.5.10 Exported symbols
|
|
|
+ var exportedSymbols = [];
|
|
|
+ var flags = [], currentFlag = false;
|
|
|
+ var totalSymbolsLength = symbols.length + numberOfNewSymbols;
|
|
|
+ while (flags.length < totalSymbolsLength) {
|
|
|
+ var runLength = decodeInteger(contextCache, 'IAEX', decoder);
|
|
|
+ while (runLength--) {
|
|
|
+ flags.push(currentFlag);
|
|
|
+ }
|
|
|
+ currentFlag = !currentFlag;
|
|
|
+ }
|
|
|
+ for (var i = 0, ii = symbols.length; i < ii; i++) {
|
|
|
+ if (flags[i]) {
|
|
|
+ exportedSymbols.push(symbols[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (var j = 0; j < numberOfNewSymbols; i++, j++) {
|
|
|
+ if (flags[i]) {
|
|
|
+ exportedSymbols.push(newSymbols[j]);
|
|
|
}
|
|
|
}
|
|
|
+ return exportedSymbols;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- W2. Search backwards from each instance of a European number until the
|
|
|
- first strong type (R, L, AL, or sor) is found. If an AL is found, change
|
|
|
- the type of the European number to Arabic number.
|
|
|
- */
|
|
|
- lastType = sor;
|
|
|
- var t;
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- t = types[i];
|
|
|
- if (t == 'EN') {
|
|
|
- types[i] = (lastType == 'AL') ? 'AN' : 'EN';
|
|
|
- } else if (t == 'R' || t == 'L' || t == 'AL') {
|
|
|
- lastType = t;
|
|
|
- }
|
|
|
+ function decodeTextRegion(huffman, refinement, width, height,
|
|
|
+ defaultPixelValue, numberOfSymbolInstances,
|
|
|
+ stripSize, inputSymbols, symbolCodeLength,
|
|
|
+ transposed, dsOffset, referenceCorner,
|
|
|
+ combinationOperator, huffmanTables,
|
|
|
+ refinementTemplateIndex, refinementAt,
|
|
|
+ decodingContext) {
|
|
|
+ if (huffman) {
|
|
|
+ error('JBIG2 error: huffman is not supported');
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- W3. Change all ALs to R.
|
|
|
- */
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- t = types[i];
|
|
|
- if (t == 'AL') {
|
|
|
- types[i] = 'R';
|
|
|
+ // Prepare bitmap
|
|
|
+ var bitmap = [];
|
|
|
+ var i, row;
|
|
|
+ for (i = 0; i < height; i++) {
|
|
|
+ row = new Uint8Array(width);
|
|
|
+ if (defaultPixelValue) {
|
|
|
+ for (var j = 0; j < width; j++) {
|
|
|
+ row[j] = defaultPixelValue;
|
|
|
+ }
|
|
|
}
|
|
|
+ bitmap.push(row);
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- W4. A single European separator between two European numbers changes to a
|
|
|
- European number. A single common separator between two numbers of the same
|
|
|
- type changes to that type:
|
|
|
- */
|
|
|
- for (i = 1; i < strLength - 1; ++i) {
|
|
|
- if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN') {
|
|
|
- types[i] = 'EN';
|
|
|
- }
|
|
|
- if (types[i] == 'CS' && (types[i - 1] == 'EN' || types[i - 1] == 'AN') &&
|
|
|
- types[i + 1] == types[i - 1]) {
|
|
|
- types[i] = types[i - 1];
|
|
|
- }
|
|
|
- }
|
|
|
+ var decoder = decodingContext.decoder;
|
|
|
+ var contextCache = decodingContext.contextCache;
|
|
|
+ var stripT = -decodeInteger(contextCache, 'IADT', decoder); // 6.4.6
|
|
|
+ var firstS = 0;
|
|
|
+ i = 0;
|
|
|
+ while (i < numberOfSymbolInstances) {
|
|
|
+ var deltaT = decodeInteger(contextCache, 'IADT', decoder); // 6.4.6
|
|
|
+ stripT += deltaT;
|
|
|
|
|
|
- /*
|
|
|
- W5. A sequence of European terminators adjacent to European numbers changes
|
|
|
- to all European numbers:
|
|
|
- */
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- if (types[i] == 'EN') {
|
|
|
- // do before
|
|
|
- var j;
|
|
|
- for (j = i - 1; j >= 0; --j) {
|
|
|
- if (types[j] != 'ET') {
|
|
|
- break;
|
|
|
- }
|
|
|
- types[j] = 'EN';
|
|
|
+ var deltaFirstS = decodeInteger(contextCache, 'IAFS', decoder); // 6.4.7
|
|
|
+ firstS += deltaFirstS;
|
|
|
+ var currentS = firstS;
|
|
|
+ do {
|
|
|
+ var currentT = (stripSize == 1 ? 0 :
|
|
|
+ decodeInteger(contextCache, 'IAIT', decoder)); // 6.4.9
|
|
|
+ var t = stripSize * stripT + currentT;
|
|
|
+ var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
|
|
|
+ var applyRefinement = (refinement &&
|
|
|
+ decodeInteger(contextCache, 'IARI', decoder));
|
|
|
+ var symbolBitmap = inputSymbols[symbolId];
|
|
|
+ var symbolWidth = symbolBitmap[0].length;
|
|
|
+ var symbolHeight = symbolBitmap.length;
|
|
|
+ if (applyRefinement) {
|
|
|
+ var rdw = decodeInteger(contextCache, 'IARDW', decoder); // 6.4.11.1
|
|
|
+ var rdh = decodeInteger(contextCache, 'IARDH', decoder); // 6.4.11.2
|
|
|
+ var rdx = decodeInteger(contextCache, 'IARDX', decoder); // 6.4.11.3
|
|
|
+ var rdy = decodeInteger(contextCache, 'IARDY', decoder); // 6.4.11.4
|
|
|
+ symbolWidth += rdw;
|
|
|
+ symbolHeight += rdh;
|
|
|
+ symbolBitmap = decodeRefinement(symbolWidth, symbolHeight,
|
|
|
+ refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx,
|
|
|
+ (rdh >> 1) + rdy, false, refinementAt,
|
|
|
+ decodingContext);
|
|
|
}
|
|
|
- // do after
|
|
|
- for (j = i + 1; j < strLength; --j) {
|
|
|
- if (types[j] != 'ET') {
|
|
|
- break;
|
|
|
+ var offsetT = t - ((referenceCorner & 1) ? 0 : symbolHeight);
|
|
|
+ var offsetS = currentS - ((referenceCorner & 2) ? symbolWidth : 0);
|
|
|
+ var s2, t2, symbolRow;
|
|
|
+ if (transposed) {
|
|
|
+ // Place Symbol Bitmap from T1,S1
|
|
|
+ for (s2 = 0; s2 < symbolHeight; s2++) {
|
|
|
+ row = bitmap[offsetS + s2];
|
|
|
+ if (!row) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ symbolRow = symbolBitmap[s2];
|
|
|
+ // To ignore Parts of Symbol bitmap which goes
|
|
|
+ // outside bitmap region
|
|
|
+ var maxWidth = Math.min(width - offsetT, symbolWidth);
|
|
|
+ switch (combinationOperator) {
|
|
|
+ case 0: // OR
|
|
|
+ for (t2 = 0; t2 < maxWidth; t2++) {
|
|
|
+ row[offsetT + t2] |= symbolRow[t2];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 2: // XOR
|
|
|
+ for (t2 = 0; t2 < maxWidth; t2++) {
|
|
|
+ row[offsetT + t2] ^= symbolRow[t2];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ error('JBIG2 error: operator ' + combinationOperator +
|
|
|
+ ' is not supported');
|
|
|
+ }
|
|
|
}
|
|
|
- types[j] = 'EN';
|
|
|
+ currentS += symbolHeight - 1;
|
|
|
+ } else {
|
|
|
+ for (t2 = 0; t2 < symbolHeight; t2++) {
|
|
|
+ row = bitmap[offsetT + t2];
|
|
|
+ if (!row) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ symbolRow = symbolBitmap[t2];
|
|
|
+ switch (combinationOperator) {
|
|
|
+ case 0: // OR
|
|
|
+ for (s2 = 0; s2 < symbolWidth; s2++) {
|
|
|
+ row[offsetS + s2] |= symbolRow[s2];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 2: // XOR
|
|
|
+ for (s2 = 0; s2 < symbolWidth; s2++) {
|
|
|
+ row[offsetS + s2] ^= symbolRow[s2];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ error('JBIG2 error: operator ' + combinationOperator +
|
|
|
+ ' is not supported');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ currentS += symbolWidth - 1;
|
|
|
}
|
|
|
- }
|
|
|
+ i++;
|
|
|
+ var deltaS = decodeInteger(contextCache, 'IADS', decoder); // 6.4.8
|
|
|
+ if (deltaS === null) {
|
|
|
+ break; // OOB
|
|
|
+ }
|
|
|
+ currentS += deltaS + dsOffset;
|
|
|
+ } while (true);
|
|
|
}
|
|
|
+ return bitmap;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- W6. Otherwise, separators and terminators change to Other Neutral:
|
|
|
- */
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- t = types[i];
|
|
|
- if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS') {
|
|
|
- types[i] = 'ON';
|
|
|
- }
|
|
|
+ function readSegmentHeader(data, start) {
|
|
|
+ var segmentHeader = {};
|
|
|
+ segmentHeader.number = readUint32(data, start);
|
|
|
+ var flags = data[start + 4];
|
|
|
+ var segmentType = flags & 0x3F;
|
|
|
+ if (!SegmentTypes[segmentType]) {
|
|
|
+ error('JBIG2 error: invalid segment type: ' + segmentType);
|
|
|
}
|
|
|
+ segmentHeader.type = segmentType;
|
|
|
+ segmentHeader.typeName = SegmentTypes[segmentType];
|
|
|
+ segmentHeader.deferredNonRetain = !!(flags & 0x80);
|
|
|
|
|
|
- /*
|
|
|
- W7. Search backwards from each instance of a European number until the
|
|
|
- first strong type (R, L, or sor) is found. If an L is found, then change
|
|
|
- the type of the European number to L.
|
|
|
- */
|
|
|
- lastType = sor;
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- t = types[i];
|
|
|
- if (t == 'EN') {
|
|
|
- types[i] = ((lastType == 'L') ? 'L' : 'EN');
|
|
|
- } else if (t == 'R' || t == 'L') {
|
|
|
- lastType = t;
|
|
|
+ var pageAssociationFieldSize = !!(flags & 0x40);
|
|
|
+ var referredFlags = data[start + 5];
|
|
|
+ var referredToCount = (referredFlags >> 5) & 7;
|
|
|
+ var retainBits = [referredFlags & 31];
|
|
|
+ var position = start + 6;
|
|
|
+ if (referredFlags == 7) {
|
|
|
+ referredToCount = readUint32(data, position - 1) & 0x1FFFFFFF;
|
|
|
+ position += 3;
|
|
|
+ var bytes = (referredToCount + 7) >> 3;
|
|
|
+ retainBits[0] = data[position++];
|
|
|
+ while (--bytes > 0) {
|
|
|
+ retainBits.push(data[position++]);
|
|
|
}
|
|
|
+ } else if (referredFlags == 5 || referredFlags == 6) {
|
|
|
+ error('JBIG2 error: invalid referred-to flags');
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- N1. A sequence of neutrals takes the direction of the surrounding strong
|
|
|
- text if the text on both sides has the same direction. European and Arabic
|
|
|
- numbers are treated as though they were R. Start-of-level-run (sor) and
|
|
|
- end-of-level-run (eor) are used at level run boundaries.
|
|
|
- */
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- if (types[i] == 'ON') {
|
|
|
- var end = findUnequal(types, i + 1, 'ON');
|
|
|
- var before = sor;
|
|
|
- if (i > 0) {
|
|
|
- before = types[i - 1];
|
|
|
- }
|
|
|
+ segmentHeader.retainBits = retainBits;
|
|
|
+ var referredToSegmentNumberSize = (segmentHeader.number <= 256 ? 1 :
|
|
|
+ (segmentHeader.number <= 65536 ? 2 : 4));
|
|
|
+ var referredTo = [];
|
|
|
+ var i, ii;
|
|
|
+ for (i = 0; i < referredToCount; i++) {
|
|
|
+ var number = (referredToSegmentNumberSize == 1 ? data[position] :
|
|
|
+ (referredToSegmentNumberSize == 2 ? readUint16(data, position) :
|
|
|
+ readUint32(data, position)));
|
|
|
+ referredTo.push(number);
|
|
|
+ position += referredToSegmentNumberSize;
|
|
|
+ }
|
|
|
+ segmentHeader.referredTo = referredTo;
|
|
|
+ if (!pageAssociationFieldSize) {
|
|
|
+ segmentHeader.pageAssociation = data[position++];
|
|
|
+ } else {
|
|
|
+ segmentHeader.pageAssociation = readUint32(data, position);
|
|
|
+ position += 4;
|
|
|
+ }
|
|
|
+ segmentHeader.length = readUint32(data, position);
|
|
|
+ position += 4;
|
|
|
|
|
|
- var after = eor;
|
|
|
- if (end + 1 < strLength) {
|
|
|
- after = types[end + 1];
|
|
|
- }
|
|
|
- if (before != 'L') {
|
|
|
- before = 'R';
|
|
|
+ if (segmentHeader.length == 0xFFFFFFFF) {
|
|
|
+ // 7.2.7 Segment data length, unknown segment length
|
|
|
+ if (segmentType === 38) { // ImmediateGenericRegion
|
|
|
+ var genericRegionInfo = readRegionSegmentInformation(data, position);
|
|
|
+ var genericRegionSegmentFlags = data[position +
|
|
|
+ RegionSegmentInformationFieldLength];
|
|
|
+ var genericRegionMmr = !!(genericRegionSegmentFlags & 1);
|
|
|
+ // searching for the segment end
|
|
|
+ var searchPatternLength = 6;
|
|
|
+ var searchPattern = new Uint8Array(searchPatternLength);
|
|
|
+ if (!genericRegionMmr) {
|
|
|
+ searchPattern[0] = 0xFF;
|
|
|
+ searchPattern[1] = 0xAC;
|
|
|
}
|
|
|
- if (after != 'L') {
|
|
|
- after = 'R';
|
|
|
+ searchPattern[2] = (genericRegionInfo.height >>> 24) & 0xFF;
|
|
|
+ searchPattern[3] = (genericRegionInfo.height >> 16) & 0xFF;
|
|
|
+ searchPattern[4] = (genericRegionInfo.height >> 8) & 0xFF;
|
|
|
+ searchPattern[5] = genericRegionInfo.height & 0xFF;
|
|
|
+ for (i = position, ii = data.length; i < ii; i++) {
|
|
|
+ var j = 0;
|
|
|
+ while (j < searchPatternLength && searchPattern[j] === data[i + j]) {
|
|
|
+ j++;
|
|
|
+ }
|
|
|
+ if (j == searchPatternLength) {
|
|
|
+ segmentHeader.length = i + searchPatternLength;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- if (before == after) {
|
|
|
- setValues(types, i, end, before);
|
|
|
+ if (segmentHeader.length == 0xFFFFFFFF) {
|
|
|
+ error('JBIG2 error: segment end was not found');
|
|
|
}
|
|
|
- i = end - 1; // reset to end (-1 so next iteration is ok)
|
|
|
+ } else {
|
|
|
+ error('JBIG2 error: invalid unknown segment length');
|
|
|
}
|
|
|
}
|
|
|
+ segmentHeader.headerEnd = position;
|
|
|
+ return segmentHeader;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- N2. Any remaining neutrals take the embedding direction.
|
|
|
- */
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- if (types[i] == 'ON') {
|
|
|
- types[i] = e;
|
|
|
+ function readSegments(header, data, start, end) {
|
|
|
+ var segments = [];
|
|
|
+ var position = start;
|
|
|
+ while (position < end) {
|
|
|
+ var segmentHeader = readSegmentHeader(data, position);
|
|
|
+ position = segmentHeader.headerEnd;
|
|
|
+ var segment = {
|
|
|
+ header: segmentHeader,
|
|
|
+ data: data
|
|
|
+ };
|
|
|
+ if (!header.randomAccess) {
|
|
|
+ segment.start = position;
|
|
|
+ position += segmentHeader.length;
|
|
|
+ segment.end = position;
|
|
|
+ }
|
|
|
+ segments.push(segment);
|
|
|
+ if (segmentHeader.type == 51) {
|
|
|
+ break; // end of file is found
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- I1. For all characters with an even (left-to-right) embedding direction,
|
|
|
- those of type R go up one level and those of type AN or EN go up two
|
|
|
- levels.
|
|
|
- I2. For all characters with an odd (right-to-left) embedding direction,
|
|
|
- those of type L, EN or AN go up one level.
|
|
|
- */
|
|
|
- for (i = 0; i < strLength; ++i) {
|
|
|
- t = types[i];
|
|
|
- if (isEven(levels[i])) {
|
|
|
- if (t == 'R') {
|
|
|
- levels[i] += 1;
|
|
|
- } else if (t == 'AN' || t == 'EN') {
|
|
|
- levels[i] += 2;
|
|
|
- }
|
|
|
- } else { // isOdd
|
|
|
- if (t == 'L' || t == 'AN' || t == 'EN') {
|
|
|
- levels[i] += 1;
|
|
|
- }
|
|
|
+ if (header.randomAccess) {
|
|
|
+ for (var i = 0, ii = segments.length; i < ii; i++) {
|
|
|
+ segments[i].start = position;
|
|
|
+ position += segments[i].header.length;
|
|
|
+ segments[i].end = position;
|
|
|
}
|
|
|
}
|
|
|
+ return segments;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- L1. On each line, reset the embedding level of the following characters to
|
|
|
- the paragraph embedding level:
|
|
|
-
|
|
|
- segment separators,
|
|
|
- paragraph separators,
|
|
|
- any sequence of whitespace characters preceding a segment separator or
|
|
|
- paragraph separator, and any sequence of white space characters at the end
|
|
|
- of the line.
|
|
|
- */
|
|
|
-
|
|
|
- // don't bother as text is only single line
|
|
|
-
|
|
|
- /*
|
|
|
- L2. From the highest level found in the text to the lowest odd level on
|
|
|
- each line, reverse any contiguous sequence of characters that are at that
|
|
|
- level or higher.
|
|
|
- */
|
|
|
+ // 7.4.1 Region segment information field
|
|
|
+ function readRegionSegmentInformation(data, start) {
|
|
|
+ return {
|
|
|
+ width: readUint32(data, start),
|
|
|
+ height: readUint32(data, start + 4),
|
|
|
+ x: readUint32(data, start + 8),
|
|
|
+ y: readUint32(data, start + 12),
|
|
|
+ combinationOperator: data[start + 16] & 7
|
|
|
+ };
|
|
|
+ }
|
|
|
+ var RegionSegmentInformationFieldLength = 17;
|
|
|
|
|
|
- // find highest level & lowest odd level
|
|
|
- var highestLevel = -1;
|
|
|
- var lowestOddLevel = 99;
|
|
|
- var level;
|
|
|
- for (i = 0, ii = levels.length; i < ii; ++i) {
|
|
|
- level = levels[i];
|
|
|
- if (highestLevel < level) {
|
|
|
- highestLevel = level;
|
|
|
- }
|
|
|
- if (lowestOddLevel > level && isOdd(level)) {
|
|
|
- lowestOddLevel = level;
|
|
|
- }
|
|
|
- }
|
|
|
+ function processSegment(segment, visitor) {
|
|
|
+ var header = segment.header;
|
|
|
|
|
|
- // now reverse between those limits
|
|
|
- for (level = highestLevel; level >= lowestOddLevel; --level) {
|
|
|
- // find segments to reverse
|
|
|
- var start = -1;
|
|
|
- for (i = 0, ii = levels.length; i < ii; ++i) {
|
|
|
- if (levels[i] < level) {
|
|
|
- if (start >= 0) {
|
|
|
- reverseValues(chars, start, i);
|
|
|
- start = -1;
|
|
|
+ var data = segment.data, position = segment.start, end = segment.end;
|
|
|
+ var args, at, i, atLength;
|
|
|
+ switch (header.type) {
|
|
|
+ case 0: // SymbolDictionary
|
|
|
+ // 7.4.2 Symbol dictionary segment syntax
|
|
|
+ var dictionary = {};
|
|
|
+ var dictionaryFlags = readUint16(data, position); // 7.4.2.1.1
|
|
|
+ dictionary.huffman = !!(dictionaryFlags & 1);
|
|
|
+ dictionary.refinement = !!(dictionaryFlags & 2);
|
|
|
+ dictionary.huffmanDHSelector = (dictionaryFlags >> 2) & 3;
|
|
|
+ dictionary.huffmanDWSelector = (dictionaryFlags >> 4) & 3;
|
|
|
+ dictionary.bitmapSizeSelector = (dictionaryFlags >> 6) & 1;
|
|
|
+ dictionary.aggregationInstancesSelector = (dictionaryFlags >> 7) & 1;
|
|
|
+ dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256);
|
|
|
+ dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512);
|
|
|
+ dictionary.template = (dictionaryFlags >> 10) & 3;
|
|
|
+ dictionary.refinementTemplate = (dictionaryFlags >> 12) & 1;
|
|
|
+ position += 2;
|
|
|
+ if (!dictionary.huffman) {
|
|
|
+ atLength = dictionary.template === 0 ? 4 : 1;
|
|
|
+ at = [];
|
|
|
+ for (i = 0; i < atLength; i++) {
|
|
|
+ at.push({
|
|
|
+ x: readInt8(data, position),
|
|
|
+ y: readInt8(data, position + 1)
|
|
|
+ });
|
|
|
+ position += 2;
|
|
|
}
|
|
|
- } else if (start < 0) {
|
|
|
- start = i;
|
|
|
+ dictionary.at = at;
|
|
|
+ }
|
|
|
+ if (dictionary.refinement && !dictionary.refinementTemplate) {
|
|
|
+ at = [];
|
|
|
+ for (i = 0; i < 2; i++) {
|
|
|
+ at.push({
|
|
|
+ x: readInt8(data, position),
|
|
|
+ y: readInt8(data, position + 1)
|
|
|
+ });
|
|
|
+ position += 2;
|
|
|
+ }
|
|
|
+ dictionary.refinementAt = at;
|
|
|
+ }
|
|
|
+ dictionary.numberOfExportedSymbols = readUint32(data, position);
|
|
|
+ position += 4;
|
|
|
+ dictionary.numberOfNewSymbols = readUint32(data, position);
|
|
|
+ position += 4;
|
|
|
+ args = [dictionary, header.number, header.referredTo,
|
|
|
+ data, position, end];
|
|
|
+ break;
|
|
|
+ case 6: // ImmediateTextRegion
|
|
|
+ case 7: // ImmediateLosslessTextRegion
|
|
|
+ var textRegion = {};
|
|
|
+ textRegion.info = readRegionSegmentInformation(data, position);
|
|
|
+ position += RegionSegmentInformationFieldLength;
|
|
|
+ var textRegionSegmentFlags = readUint16(data, position);
|
|
|
+ position += 2;
|
|
|
+ textRegion.huffman = !!(textRegionSegmentFlags & 1);
|
|
|
+ textRegion.refinement = !!(textRegionSegmentFlags & 2);
|
|
|
+ textRegion.stripSize = 1 << ((textRegionSegmentFlags >> 2) & 3);
|
|
|
+ textRegion.referenceCorner = (textRegionSegmentFlags >> 4) & 3;
|
|
|
+ textRegion.transposed = !!(textRegionSegmentFlags & 64);
|
|
|
+ textRegion.combinationOperator = (textRegionSegmentFlags >> 7) & 3;
|
|
|
+ textRegion.defaultPixelValue = (textRegionSegmentFlags >> 9) & 1;
|
|
|
+ textRegion.dsOffset = (textRegionSegmentFlags << 17) >> 27;
|
|
|
+ textRegion.refinementTemplate = (textRegionSegmentFlags >> 15) & 1;
|
|
|
+ if (textRegion.huffman) {
|
|
|
+ var textRegionHuffmanFlags = readUint16(data, position);
|
|
|
+ position += 2;
|
|
|
+ textRegion.huffmanFS = (textRegionHuffmanFlags) & 3;
|
|
|
+ textRegion.huffmanDS = (textRegionHuffmanFlags >> 2) & 3;
|
|
|
+ textRegion.huffmanDT = (textRegionHuffmanFlags >> 4) & 3;
|
|
|
+ textRegion.huffmanRefinementDW = (textRegionHuffmanFlags >> 6) & 3;
|
|
|
+ textRegion.huffmanRefinementDH = (textRegionHuffmanFlags >> 8) & 3;
|
|
|
+ textRegion.huffmanRefinementDX = (textRegionHuffmanFlags >> 10) & 3;
|
|
|
+ textRegion.huffmanRefinementDY = (textRegionHuffmanFlags >> 12) & 3;
|
|
|
+ textRegion.huffmanRefinementSizeSelector =
|
|
|
+ !!(textRegionHuffmanFlags & 14);
|
|
|
+ }
|
|
|
+ if (textRegion.refinement && !textRegion.refinementTemplate) {
|
|
|
+ at = [];
|
|
|
+ for (i = 0; i < 2; i++) {
|
|
|
+ at.push({
|
|
|
+ x: readInt8(data, position),
|
|
|
+ y: readInt8(data, position + 1)
|
|
|
+ });
|
|
|
+ position += 2;
|
|
|
+ }
|
|
|
+ textRegion.refinementAt = at;
|
|
|
+ }
|
|
|
+ textRegion.numberOfSymbolInstances = readUint32(data, position);
|
|
|
+ position += 4;
|
|
|
+ // TODO 7.4.3.1.7 Symbol ID Huffman table decoding
|
|
|
+ if (textRegion.huffman) {
|
|
|
+ error('JBIG2 error: huffman is not supported');
|
|
|
+ }
|
|
|
+ args = [textRegion, header.referredTo, data, position, end];
|
|
|
+ break;
|
|
|
+ case 38: // ImmediateGenericRegion
|
|
|
+ case 39: // ImmediateLosslessGenericRegion
|
|
|
+ var genericRegion = {};
|
|
|
+ genericRegion.info = readRegionSegmentInformation(data, position);
|
|
|
+ position += RegionSegmentInformationFieldLength;
|
|
|
+ var genericRegionSegmentFlags = data[position++];
|
|
|
+ genericRegion.mmr = !!(genericRegionSegmentFlags & 1);
|
|
|
+ genericRegion.template = (genericRegionSegmentFlags >> 1) & 3;
|
|
|
+ genericRegion.prediction = !!(genericRegionSegmentFlags & 8);
|
|
|
+ if (!genericRegion.mmr) {
|
|
|
+ atLength = genericRegion.template === 0 ? 4 : 1;
|
|
|
+ at = [];
|
|
|
+ for (i = 0; i < atLength; i++) {
|
|
|
+ at.push({
|
|
|
+ x: readInt8(data, position),
|
|
|
+ y: readInt8(data, position + 1)
|
|
|
+ });
|
|
|
+ position += 2;
|
|
|
+ }
|
|
|
+ genericRegion.at = at;
|
|
|
}
|
|
|
- }
|
|
|
- if (start >= 0) {
|
|
|
- reverseValues(chars, start, levels.length);
|
|
|
- }
|
|
|
+ args = [genericRegion, data, position, end];
|
|
|
+ break;
|
|
|
+ case 48: // PageInformation
|
|
|
+ var pageInfo = {
|
|
|
+ width: readUint32(data, position),
|
|
|
+ height: readUint32(data, position + 4),
|
|
|
+ resolutionX: readUint32(data, position + 8),
|
|
|
+ resolutionY: readUint32(data, position + 12)
|
|
|
+ };
|
|
|
+ if (pageInfo.height == 0xFFFFFFFF) {
|
|
|
+ delete pageInfo.height;
|
|
|
+ }
|
|
|
+ var pageSegmentFlags = data[position + 16];
|
|
|
+ var pageStripingInformatiom = readUint16(data, position + 17);
|
|
|
+ pageInfo.lossless = !!(pageSegmentFlags & 1);
|
|
|
+ pageInfo.refinement = !!(pageSegmentFlags & 2);
|
|
|
+ pageInfo.defaultPixelValue = (pageSegmentFlags >> 2) & 1;
|
|
|
+ pageInfo.combinationOperator = (pageSegmentFlags >> 3) & 3;
|
|
|
+ pageInfo.requiresBuffer = !!(pageSegmentFlags & 32);
|
|
|
+ pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64);
|
|
|
+ args = [pageInfo];
|
|
|
+ break;
|
|
|
+ case 49: // EndOfPage
|
|
|
+ break;
|
|
|
+ case 50: // EndOfStripe
|
|
|
+ break;
|
|
|
+ case 51: // EndOfFile
|
|
|
+ break;
|
|
|
+ case 62: // 7.4.15 defines 2 extension types which
|
|
|
+ // are comments and can be ignored.
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ error('JBIG2 error: segment type ' + header.typeName + '(' +
|
|
|
+ header.type + ') is not implemented');
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- L3. Combining marks applied to a right-to-left base character will at this
|
|
|
- point precede their base character. If the rendering engine expects them to
|
|
|
- follow the base characters in the final display process, then the ordering
|
|
|
- of the marks and the base character must be reversed.
|
|
|
- */
|
|
|
-
|
|
|
- // don't bother for now
|
|
|
-
|
|
|
- /*
|
|
|
- L4. A character that possesses the mirrored property as specified by
|
|
|
- Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
|
|
|
- directionality of that character is R.
|
|
|
- */
|
|
|
-
|
|
|
- // don't mirror as characters are already mirrored in the pdf
|
|
|
-
|
|
|
- // Finally, return string
|
|
|
- var result = '';
|
|
|
- for (i = 0, ii = chars.length; i < ii; ++i) {
|
|
|
- var ch = chars[i];
|
|
|
- if (ch != '<' && ch != '>') {
|
|
|
- result += ch;
|
|
|
- }
|
|
|
+ var callbackName = 'on' + header.typeName;
|
|
|
+ if (callbackName in visitor) {
|
|
|
+ visitor[callbackName].apply(visitor, args);
|
|
|
}
|
|
|
- return createBidiText(result, isLTR);
|
|
|
}
|
|
|
|
|
|
- return bidi;
|
|
|
-})();
|
|
|
-
|
|
|
-
|
|
|
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
-
|
|
|
-/* Copyright 2014 Opera Software ASA
|
|
|
- *
|
|
|
- * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
- * you may not use this file except in compliance with the License.
|
|
|
- * You may obtain a copy of the License at
|
|
|
- *
|
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
- *
|
|
|
- * Unless required by applicable law or agreed to in writing, software
|
|
|
- * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
- * See the License for the specific language governing permissions and
|
|
|
- * limitations under the License.
|
|
|
- *
|
|
|
- *
|
|
|
- * Based on https://code.google.com/p/smhasher/wiki/MurmurHash3.
|
|
|
- * Hashes roughly 100 KB per millisecond on i7 3.4 GHz.
|
|
|
- */
|
|
|
-/* globals Uint32ArrayView */
|
|
|
-
|
|
|
-'use strict';
|
|
|
+ function processSegments(segments, visitor) {
|
|
|
+ for (var i = 0, ii = segments.length; i < ii; i++) {
|
|
|
+ processSegment(segments[i], visitor);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-var MurmurHash3_64 = (function MurmurHash3_64Closure (seed) {
|
|
|
- // Workaround for missing math precison in JS.
|
|
|
- var MASK_HIGH = 0xffff0000;
|
|
|
- var MASK_LOW = 0xffff;
|
|
|
+ function parseJbig2(data, start, end) {
|
|
|
+ var position = start;
|
|
|
+ if (data[position] != 0x97 || data[position + 1] != 0x4A ||
|
|
|
+ data[position + 2] != 0x42 || data[position + 3] != 0x32 ||
|
|
|
+ data[position + 4] != 0x0D || data[position + 5] != 0x0A ||
|
|
|
+ data[position + 6] != 0x1A || data[position + 7] != 0x0A) {
|
|
|
+ error('JBIG2 error: invalid header');
|
|
|
+ }
|
|
|
+ var header = {};
|
|
|
+ position += 8;
|
|
|
+ var flags = data[position++];
|
|
|
+ header.randomAccess = !(flags & 1);
|
|
|
+ if (!(flags & 2)) {
|
|
|
+ header.numberOfPages = readUint32(data, position);
|
|
|
+ position += 4;
|
|
|
+ }
|
|
|
+ var segments = readSegments(header, data, position, end);
|
|
|
+ error('Not implemented');
|
|
|
+ // processSegments(segments, new SimpleSegmentVisitor());
|
|
|
+ }
|
|
|
|
|
|
- function MurmurHash3_64 (seed) {
|
|
|
- var SEED = 0xc3d2e1f0;
|
|
|
- this.h1 = seed ? seed & 0xffffffff : SEED;
|
|
|
- this.h2 = seed ? seed & 0xffffffff : SEED;
|
|
|
+ function parseJbig2Chunks(chunks) {
|
|
|
+ var visitor = new SimpleSegmentVisitor();
|
|
|
+ for (var i = 0, ii = chunks.length; i < ii; i++) {
|
|
|
+ var chunk = chunks[i];
|
|
|
+ var segments = readSegments({}, chunk.data, chunk.start, chunk.end);
|
|
|
+ processSegments(segments, visitor);
|
|
|
+ }
|
|
|
+ return visitor.buffer;
|
|
|
}
|
|
|
|
|
|
- MurmurHash3_64.prototype = {
|
|
|
- update: function MurmurHash3_64_update(input) {
|
|
|
- var useUint32ArrayView = false;
|
|
|
- var i;
|
|
|
- if (typeof input == 'string') {
|
|
|
- var data = new Uint8Array(input.length * 2);
|
|
|
- var length = 0;
|
|
|
- for (i = 0; i < input.length; i++) {
|
|
|
- var code = input.charCodeAt(i);
|
|
|
- if (code <= 0xff) {
|
|
|
- data[length++] = code;
|
|
|
+ function SimpleSegmentVisitor() {}
|
|
|
+
|
|
|
+ SimpleSegmentVisitor.prototype = {
|
|
|
+ onPageInformation: function SimpleSegmentVisitor_onPageInformation(info) {
|
|
|
+ this.currentPageInfo = info;
|
|
|
+ var rowSize = (info.width + 7) >> 3;
|
|
|
+ var buffer = new Uint8Array(rowSize * info.height);
|
|
|
+ // The contents of ArrayBuffers are initialized to 0.
|
|
|
+ // Fill the buffer with 0xFF only if info.defaultPixelValue is set
|
|
|
+ if (info.defaultPixelValue) {
|
|
|
+ for (var i = 0, ii = buffer.length; i < ii; i++) {
|
|
|
+ buffer[i] = 0xFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.buffer = buffer;
|
|
|
+ },
|
|
|
+ drawBitmap: function SimpleSegmentVisitor_drawBitmap(regionInfo, bitmap) {
|
|
|
+ var pageInfo = this.currentPageInfo;
|
|
|
+ var width = regionInfo.width, height = regionInfo.height;
|
|
|
+ var rowSize = (pageInfo.width + 7) >> 3;
|
|
|
+ var combinationOperator = pageInfo.combinationOperatorOverride ?
|
|
|
+ regionInfo.combinationOperator : pageInfo.combinationOperator;
|
|
|
+ var buffer = this.buffer;
|
|
|
+ var mask0 = 128 >> (regionInfo.x & 7);
|
|
|
+ var offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3);
|
|
|
+ var i, j, mask, offset;
|
|
|
+ switch (combinationOperator) {
|
|
|
+ case 0: // OR
|
|
|
+ for (i = 0; i < height; i++) {
|
|
|
+ mask = mask0;
|
|
|
+ offset = offset0;
|
|
|
+ for (j = 0; j < width; j++) {
|
|
|
+ if (bitmap[i][j]) {
|
|
|
+ buffer[offset] |= mask;
|
|
|
+ }
|
|
|
+ mask >>= 1;
|
|
|
+ if (!mask) {
|
|
|
+ mask = 128;
|
|
|
+ offset++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ offset0 += rowSize;
|
|
|
}
|
|
|
- else {
|
|
|
- data[length++] = code >>> 8;
|
|
|
- data[length++] = code & 0xff;
|
|
|
+ break;
|
|
|
+ case 2: // XOR
|
|
|
+ for (i = 0; i < height; i++) {
|
|
|
+ mask = mask0;
|
|
|
+ offset = offset0;
|
|
|
+ for (j = 0; j < width; j++) {
|
|
|
+ if (bitmap[i][j]) {
|
|
|
+ buffer[offset] ^= mask;
|
|
|
+ }
|
|
|
+ mask >>= 1;
|
|
|
+ if (!mask) {
|
|
|
+ mask = 128;
|
|
|
+ offset++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ offset0 += rowSize;
|
|
|
}
|
|
|
- }
|
|
|
- } else if (input instanceof Uint8Array) {
|
|
|
- data = input;
|
|
|
- length = data.length;
|
|
|
- } else if (typeof input === 'object' && ('length' in input)) {
|
|
|
- // processing regular arrays as well, e.g. for IE9
|
|
|
- data = input;
|
|
|
- length = data.length;
|
|
|
- useUint32ArrayView = true;
|
|
|
- } else {
|
|
|
- throw new Error('Wrong data format in MurmurHash3_64_update. ' +
|
|
|
- 'Input must be a string or array.');
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ error('JBIG2 error: operator ' + combinationOperator +
|
|
|
+ ' is not supported');
|
|
|
}
|
|
|
-
|
|
|
- var blockCounts = length >> 2;
|
|
|
- var tailLength = length - blockCounts * 4;
|
|
|
- // we don't care about endianness here
|
|
|
- var dataUint32 = useUint32ArrayView ?
|
|
|
- new Uint32ArrayView(data, blockCounts) :
|
|
|
- new Uint32Array(data.buffer, 0, blockCounts);
|
|
|
- var k1 = 0;
|
|
|
- var k2 = 0;
|
|
|
- var h1 = this.h1;
|
|
|
- var h2 = this.h2;
|
|
|
- var C1 = 0xcc9e2d51;
|
|
|
- var C2 = 0x1b873593;
|
|
|
- var C1_LOW = C1 & MASK_LOW;
|
|
|
- var C2_LOW = C2 & MASK_LOW;
|
|
|
-
|
|
|
- for (i = 0; i < blockCounts; i++) {
|
|
|
- if (i & 1) {
|
|
|
- k1 = dataUint32[i];
|
|
|
- k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
|
|
|
- k1 = k1 << 15 | k1 >>> 17;
|
|
|
- k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
|
|
|
- h1 ^= k1;
|
|
|
- h1 = h1 << 13 | h1 >>> 19;
|
|
|
- h1 = h1 * 5 + 0xe6546b64;
|
|
|
- } else {
|
|
|
- k2 = dataUint32[i];
|
|
|
- k2 = (k2 * C1 & MASK_HIGH) | (k2 * C1_LOW & MASK_LOW);
|
|
|
- k2 = k2 << 15 | k2 >>> 17;
|
|
|
- k2 = (k2 * C2 & MASK_HIGH) | (k2 * C2_LOW & MASK_LOW);
|
|
|
- h2 ^= k2;
|
|
|
- h2 = h2 << 13 | h2 >>> 19;
|
|
|
- h2 = h2 * 5 + 0xe6546b64;
|
|
|
- }
|
|
|
+ },
|
|
|
+ onImmediateGenericRegion:
|
|
|
+ function SimpleSegmentVisitor_onImmediateGenericRegion(region, data,
|
|
|
+ start, end) {
|
|
|
+ var regionInfo = region.info;
|
|
|
+ var decodingContext = new DecodingContext(data, start, end);
|
|
|
+ var bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height,
|
|
|
+ region.template, region.prediction, null,
|
|
|
+ region.at, decodingContext);
|
|
|
+ this.drawBitmap(regionInfo, bitmap);
|
|
|
+ },
|
|
|
+ onImmediateLosslessGenericRegion:
|
|
|
+ function SimpleSegmentVisitor_onImmediateLosslessGenericRegion() {
|
|
|
+ this.onImmediateGenericRegion.apply(this, arguments);
|
|
|
+ },
|
|
|
+ onSymbolDictionary:
|
|
|
+ function SimpleSegmentVisitor_onSymbolDictionary(dictionary,
|
|
|
+ currentSegment,
|
|
|
+ referredSegments,
|
|
|
+ data, start, end) {
|
|
|
+ var huffmanTables;
|
|
|
+ if (dictionary.huffman) {
|
|
|
+ error('JBIG2 error: huffman is not supported');
|
|
|
}
|
|
|
|
|
|
- k1 = 0;
|
|
|
+ // Combines exported symbols from all referred segments
|
|
|
+ var symbols = this.symbols;
|
|
|
+ if (!symbols) {
|
|
|
+ this.symbols = symbols = {};
|
|
|
+ }
|
|
|
|
|
|
- switch (tailLength) {
|
|
|
- case 3:
|
|
|
- k1 ^= data[blockCounts * 4 + 2] << 16;
|
|
|
- /* falls through */
|
|
|
- case 2:
|
|
|
- k1 ^= data[blockCounts * 4 + 1] << 8;
|
|
|
- /* falls through */
|
|
|
- case 1:
|
|
|
- k1 ^= data[blockCounts * 4];
|
|
|
- /* falls through */
|
|
|
- k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
|
|
|
- k1 = k1 << 15 | k1 >>> 17;
|
|
|
- k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
|
|
|
- if (blockCounts & 1) {
|
|
|
- h1 ^= k1;
|
|
|
- } else {
|
|
|
- h2 ^= k1;
|
|
|
- }
|
|
|
+ var inputSymbols = [];
|
|
|
+ for (var i = 0, ii = referredSegments.length; i < ii; i++) {
|
|
|
+ inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
|
|
|
}
|
|
|
|
|
|
- this.h1 = h1;
|
|
|
- this.h2 = h2;
|
|
|
- return this;
|
|
|
+ var decodingContext = new DecodingContext(data, start, end);
|
|
|
+ symbols[currentSegment] = decodeSymbolDictionary(dictionary.huffman,
|
|
|
+ dictionary.refinement, inputSymbols, dictionary.numberOfNewSymbols,
|
|
|
+ dictionary.numberOfExportedSymbols, huffmanTables,
|
|
|
+ dictionary.template, dictionary.at,
|
|
|
+ dictionary.refinementTemplate, dictionary.refinementAt,
|
|
|
+ decodingContext);
|
|
|
},
|
|
|
+ onImmediateTextRegion:
|
|
|
+ function SimpleSegmentVisitor_onImmediateTextRegion(region,
|
|
|
+ referredSegments,
|
|
|
+ data, start, end) {
|
|
|
+ var regionInfo = region.info;
|
|
|
+ var huffmanTables;
|
|
|
|
|
|
- hexdigest: function MurmurHash3_64_hexdigest () {
|
|
|
- var h1 = this.h1;
|
|
|
- var h2 = this.h2;
|
|
|
-
|
|
|
- h1 ^= h2 >>> 1;
|
|
|
- h1 = (h1 * 0xed558ccd & MASK_HIGH) | (h1 * 0x8ccd & MASK_LOW);
|
|
|
- h2 = (h2 * 0xff51afd7 & MASK_HIGH) |
|
|
|
- (((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16);
|
|
|
- h1 ^= h2 >>> 1;
|
|
|
- h1 = (h1 * 0x1a85ec53 & MASK_HIGH) | (h1 * 0xec53 & MASK_LOW);
|
|
|
- h2 = (h2 * 0xc4ceb9fe & MASK_HIGH) |
|
|
|
- (((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16);
|
|
|
- h1 ^= h2 >>> 1;
|
|
|
-
|
|
|
- for (var i = 0, arr = [h1, h2], str = ''; i < arr.length; i++) {
|
|
|
- var hex = (arr[i] >>> 0).toString(16);
|
|
|
- while (hex.length < 8) {
|
|
|
- hex = '0' + hex;
|
|
|
- }
|
|
|
- str += hex;
|
|
|
+ // Combines exported symbols from all referred segments
|
|
|
+ var symbols = this.symbols;
|
|
|
+ var inputSymbols = [];
|
|
|
+ for (var i = 0, ii = referredSegments.length; i < ii; i++) {
|
|
|
+ inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
|
|
|
}
|
|
|
+ var symbolCodeLength = log2(inputSymbols.length);
|
|
|
|
|
|
- return str;
|
|
|
+ var decodingContext = new DecodingContext(data, start, end);
|
|
|
+ var bitmap = decodeTextRegion(region.huffman, region.refinement,
|
|
|
+ regionInfo.width, regionInfo.height, region.defaultPixelValue,
|
|
|
+ region.numberOfSymbolInstances, region.stripSize, inputSymbols,
|
|
|
+ symbolCodeLength, region.transposed, region.dsOffset,
|
|
|
+ region.referenceCorner, region.combinationOperator, huffmanTables,
|
|
|
+ region.refinementTemplate, region.refinementAt, decodingContext);
|
|
|
+ this.drawBitmap(regionInfo, bitmap);
|
|
|
+ },
|
|
|
+ onImmediateLosslessTextRegion:
|
|
|
+ function SimpleSegmentVisitor_onImmediateLosslessTextRegion() {
|
|
|
+ this.onImmediateTextRegion.apply(this, arguments);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- return MurmurHash3_64;
|
|
|
-})();
|
|
|
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
|
|
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
-/*
|
|
|
- Copyright 2011 notmasteryet
|
|
|
-
|
|
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
- you may not use this file except in compliance with the License.
|
|
|
- You may obtain a copy of the License at
|
|
|
-
|
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
|
|
-
|
|
|
- Unless required by applicable law or agreed to in writing, software
|
|
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
- See the License for the specific language governing permissions and
|
|
|
- limitations under the License.
|
|
|
-*/
|
|
|
+ function Jbig2Image() {}
|
|
|
|
|
|
-// - The JPEG specification can be found in the ITU CCITT Recommendation T.81
|
|
|
-// (www.w3.org/Graphics/JPEG/itu-t81.pdf)
|
|
|
-// - The JFIF specification can be found in the JPEG File Interchange Format
|
|
|
-// (www.w3.org/Graphics/JPEG/jfif3.pdf)
|
|
|
-// - The Adobe Application-Specific JPEG markers in the Supporting the DCT Filters
|
|
|
-// in PostScript Level 2, Technical Note #5116
|
|
|
-// (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf)
|
|
|
+ Jbig2Image.prototype = {
|
|
|
+ parseChunks: function Jbig2Image_parseChunks(chunks) {
|
|
|
+ return parseJbig2Chunks(chunks);
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
-var JpegImage = (function jpegImage() {
|
|
|
- "use strict";
|
|
|
- var dctZigZag = new Int32Array([
|
|
|
- 0,
|
|
|
- 1, 8,
|
|
|
- 16, 9, 2,
|
|
|
- 3, 10, 17, 24,
|
|
|
- 32, 25, 18, 11, 4,
|
|
|
- 5, 12, 19, 26, 33, 40,
|
|
|
- 48, 41, 34, 27, 20, 13, 6,
|
|
|
- 7, 14, 21, 28, 35, 42, 49, 56,
|
|
|
- 57, 50, 43, 36, 29, 22, 15,
|
|
|
- 23, 30, 37, 44, 51, 58,
|
|
|
- 59, 52, 45, 38, 31,
|
|
|
- 39, 46, 53, 60,
|
|
|
- 61, 54, 47,
|
|
|
- 55, 62,
|
|
|
- 63
|
|
|
- ]);
|
|
|
+ return Jbig2Image;
|
|
|
+})();
|
|
|
|
|
|
- var dctCos1 = 4017 // cos(pi/16)
|
|
|
- var dctSin1 = 799 // sin(pi/16)
|
|
|
- var dctCos3 = 3406 // cos(3*pi/16)
|
|
|
- var dctSin3 = 2276 // sin(3*pi/16)
|
|
|
- var dctCos6 = 1567 // cos(6*pi/16)
|
|
|
- var dctSin6 = 3784 // sin(6*pi/16)
|
|
|
- var dctSqrt2 = 5793 // sqrt(2)
|
|
|
- var dctSqrt1d2 = 2896 // sqrt(2) / 2
|
|
|
|
|
|
- function constructor() {
|
|
|
- }
|
|
|
+var bidi = PDFJS.bidi = (function bidiClosure() {
|
|
|
+ // Character types for symbols from 0000 to 00FF.
|
|
|
+ var baseTypes = [
|
|
|
+ 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
|
|
|
+ 'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
|
|
+ 'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
|
|
|
+ 'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
|
|
|
+ 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
|
|
|
+ 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
+ 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
|
|
|
+ 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
+ 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
+ 'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
|
|
|
+ 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
|
|
+ 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
|
|
+ 'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
|
|
|
+ 'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
|
|
|
+ 'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
+ 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
+ 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
+ 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
|
|
+ 'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
|
|
|
+ ];
|
|
|
|
|
|
- function buildHuffmanTable(codeLengths, values) {
|
|
|
- var k = 0, code = [], i, j, length = 16;
|
|
|
- while (length > 0 && !codeLengths[length - 1])
|
|
|
- length--;
|
|
|
- code.push({children: [], index: 0});
|
|
|
- var p = code[0], q;
|
|
|
- for (i = 0; i < length; i++) {
|
|
|
- for (j = 0; j < codeLengths[i]; j++) {
|
|
|
- p = code.pop();
|
|
|
- p.children[p.index] = values[k];
|
|
|
- while (p.index > 0) {
|
|
|
- p = code.pop();
|
|
|
- }
|
|
|
- p.index++;
|
|
|
- code.push(p);
|
|
|
- while (code.length <= i) {
|
|
|
- code.push(q = {children: [], index: 0});
|
|
|
- p.children[p.index] = q.children;
|
|
|
- p = q;
|
|
|
- }
|
|
|
- k++;
|
|
|
- }
|
|
|
- if (i + 1 < length) {
|
|
|
- // p here points to last code
|
|
|
- code.push(q = {children: [], index: 0});
|
|
|
- p.children[p.index] = q.children;
|
|
|
- p = q;
|
|
|
- }
|
|
|
- }
|
|
|
- return code[0].children;
|
|
|
- }
|
|
|
+ // Character types for symbols from 0600 to 06FF
|
|
|
+ var arabicTypes = [
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
|
|
|
+ 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
|
|
|
+ 'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
|
|
|
+ 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
|
|
|
+ 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
|
|
+ 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
|
|
|
+ ];
|
|
|
|
|
|
- function getBlockBufferOffset(component, row, col) {
|
|
|
- return 64 * ((component.blocksPerLine + 1) * row + col);
|
|
|
+ function isOdd(i) {
|
|
|
+ return (i & 1) !== 0;
|
|
|
}
|
|
|
|
|
|
- function decodeScan(data, offset,
|
|
|
- frame, components, resetInterval,
|
|
|
- spectralStart, spectralEnd,
|
|
|
- successivePrev, successive) {
|
|
|
- var precision = frame.precision;
|
|
|
- var samplesPerLine = frame.samplesPerLine;
|
|
|
- var scanLines = frame.scanLines;
|
|
|
- var mcusPerLine = frame.mcusPerLine;
|
|
|
- var progressive = frame.progressive;
|
|
|
- var maxH = frame.maxH, maxV = frame.maxV;
|
|
|
-
|
|
|
- var startOffset = offset, bitsData = 0, bitsCount = 0;
|
|
|
-
|
|
|
- function readBit() {
|
|
|
- if (bitsCount > 0) {
|
|
|
- bitsCount--;
|
|
|
- return (bitsData >> bitsCount) & 1;
|
|
|
- }
|
|
|
- bitsData = data[offset++];
|
|
|
- if (bitsData == 0xFF) {
|
|
|
- var nextByte = data[offset++];
|
|
|
- if (nextByte) {
|
|
|
- throw "unexpected marker: " + ((bitsData << 8) | nextByte).toString(16);
|
|
|
- }
|
|
|
- // unstuff 0
|
|
|
- }
|
|
|
- bitsCount = 7;
|
|
|
- return bitsData >>> 7;
|
|
|
- }
|
|
|
+ function isEven(i) {
|
|
|
+ return (i & 1) === 0;
|
|
|
+ }
|
|
|
|
|
|
- function decodeHuffman(tree) {
|
|
|
- var node = tree;
|
|
|
- var bit;
|
|
|
- while ((bit = readBit()) !== null) {
|
|
|
- node = node[bit];
|
|
|
- if (typeof node === 'number')
|
|
|
- return node;
|
|
|
- if (typeof node !== 'object')
|
|
|
- throw "invalid huffman sequence";
|
|
|
+ function findUnequal(arr, start, value) {
|
|
|
+ for (var j = start, jj = arr.length; j < jj; ++j) {
|
|
|
+ if (arr[j] != value) {
|
|
|
+ return j;
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
+ return j;
|
|
|
+ }
|
|
|
|
|
|
- function receive(length) {
|
|
|
- var n = 0;
|
|
|
- while (length > 0) {
|
|
|
- var bit = readBit();
|
|
|
- if (bit === null) return;
|
|
|
- n = (n << 1) | bit;
|
|
|
- length--;
|
|
|
- }
|
|
|
- return n;
|
|
|
+ function setValues(arr, start, end, value) {
|
|
|
+ for (var j = start; j < end; ++j) {
|
|
|
+ arr[j] = value;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- function receiveAndExtend(length) {
|
|
|
- var n = receive(length);
|
|
|
- if (n >= 1 << (length - 1))
|
|
|
- return n;
|
|
|
- return n + (-1 << length) + 1;
|
|
|
+ function reverseValues(arr, start, end) {
|
|
|
+ for (var i = start, j = end - 1; i < j; ++i, --j) {
|
|
|
+ var temp = arr[i];
|
|
|
+ arr[i] = arr[j];
|
|
|
+ arr[j] = temp;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- function decodeBaseline(component, offset) {
|
|
|
- var t = decodeHuffman(component.huffmanTableDC);
|
|
|
- var diff = t === 0 ? 0 : receiveAndExtend(t);
|
|
|
- component.blockData[offset] = (component.pred += diff);
|
|
|
- var k = 1;
|
|
|
- while (k < 64) {
|
|
|
- var rs = decodeHuffman(component.huffmanTableAC);
|
|
|
- var s = rs & 15, r = rs >> 4;
|
|
|
- if (s === 0) {
|
|
|
- if (r < 15)
|
|
|
- break;
|
|
|
- k += 16;
|
|
|
- continue;
|
|
|
- }
|
|
|
- k += r;
|
|
|
- var z = dctZigZag[k];
|
|
|
- component.blockData[offset + z] = receiveAndExtend(s);
|
|
|
- k++;
|
|
|
- }
|
|
|
- }
|
|
|
+ function createBidiText(str, isLTR, vertical) {
|
|
|
+ return {
|
|
|
+ str: str,
|
|
|
+ dir: (vertical ? 'ttb' : (isLTR ? 'ltr' : 'rtl'))
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- function decodeDCFirst(component, offset) {
|
|
|
- var t = decodeHuffman(component.huffmanTableDC);
|
|
|
- var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive);
|
|
|
- component.blockData[offset] = (component.pred += diff);
|
|
|
- }
|
|
|
+ // These are used in bidi(), which is called frequently. We re-use them on
|
|
|
+ // each call to avoid unnecessary allocations.
|
|
|
+ var chars = [];
|
|
|
+ var types = [];
|
|
|
|
|
|
- function decodeDCSuccessive(component, offset) {
|
|
|
- component.blockData[offset] |= readBit() << successive;
|
|
|
+ function bidi(str, startLevel, vertical) {
|
|
|
+ var isLTR = true;
|
|
|
+ var strLength = str.length;
|
|
|
+ if (strLength === 0 || vertical) {
|
|
|
+ return createBidiText(str, isLTR, vertical);
|
|
|
}
|
|
|
|
|
|
- var eobrun = 0;
|
|
|
- function decodeACFirst(component, offset) {
|
|
|
- if (eobrun > 0) {
|
|
|
- eobrun--;
|
|
|
- return;
|
|
|
- }
|
|
|
- var k = spectralStart, e = spectralEnd;
|
|
|
- while (k <= e) {
|
|
|
- var rs = decodeHuffman(component.huffmanTableAC);
|
|
|
- var s = rs & 15, r = rs >> 4;
|
|
|
- if (s === 0) {
|
|
|
- if (r < 15) {
|
|
|
- eobrun = receive(r) + (1 << r) - 1;
|
|
|
- break;
|
|
|
- }
|
|
|
- k += 16;
|
|
|
- continue;
|
|
|
- }
|
|
|
- k += r;
|
|
|
- var z = dctZigZag[k];
|
|
|
- component.blockData[offset + z] = receiveAndExtend(s) * (1 << successive);
|
|
|
- k++;
|
|
|
- }
|
|
|
- }
|
|
|
+ // Get types and fill arrays
|
|
|
+ chars.length = 0;
|
|
|
+ types.length = 0;
|
|
|
+ var numBidi = 0;
|
|
|
|
|
|
- var successiveACState = 0, successiveACNextValue;
|
|
|
- function decodeACSuccessive(component, offset) {
|
|
|
- var k = spectralStart, e = spectralEnd, r = 0;
|
|
|
- while (k <= e) {
|
|
|
- var z = dctZigZag[k];
|
|
|
- switch (successiveACState) {
|
|
|
- case 0: // initial state
|
|
|
- var rs = decodeHuffman(component.huffmanTableAC);
|
|
|
- var s = rs & 15, r = rs >> 4;
|
|
|
- if (s === 0) {
|
|
|
- if (r < 15) {
|
|
|
- eobrun = receive(r) + (1 << r);
|
|
|
- successiveACState = 4;
|
|
|
- } else {
|
|
|
- r = 16;
|
|
|
- successiveACState = 1;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (s !== 1)
|
|
|
- throw "invalid ACn encoding";
|
|
|
- successiveACNextValue = receiveAndExtend(s);
|
|
|
- successiveACState = r ? 2 : 3;
|
|
|
- }
|
|
|
- continue;
|
|
|
- case 1: // skipping r zero items
|
|
|
- case 2:
|
|
|
- if (component.blockData[offset + z]) {
|
|
|
- component.blockData[offset + z] += (readBit() << successive);
|
|
|
- } else {
|
|
|
- r--;
|
|
|
- if (r === 0)
|
|
|
- successiveACState = successiveACState == 2 ? 3 : 0;
|
|
|
- }
|
|
|
- break;
|
|
|
- case 3: // set value for a zero item
|
|
|
- if (component.blockData[offset + z]) {
|
|
|
- component.blockData[offset + z] += (readBit() << successive);
|
|
|
- } else {
|
|
|
- component.blockData[offset + z] = successiveACNextValue << successive;
|
|
|
- successiveACState = 0;
|
|
|
- }
|
|
|
- break;
|
|
|
- case 4: // eob
|
|
|
- if (component.blockData[offset + z]) {
|
|
|
- component.blockData[offset + z] += (readBit() << successive);
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- k++;
|
|
|
+ var i, ii;
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ chars[i] = str.charAt(i);
|
|
|
+
|
|
|
+ var charCode = str.charCodeAt(i);
|
|
|
+ var charType = 'L';
|
|
|
+ if (charCode <= 0x00ff) {
|
|
|
+ charType = baseTypes[charCode];
|
|
|
+ } else if (0x0590 <= charCode && charCode <= 0x05f4) {
|
|
|
+ charType = 'R';
|
|
|
+ } else if (0x0600 <= charCode && charCode <= 0x06ff) {
|
|
|
+ charType = arabicTypes[charCode & 0xff];
|
|
|
+ } else if (0x0700 <= charCode && charCode <= 0x08AC) {
|
|
|
+ charType = 'AL';
|
|
|
}
|
|
|
- if (successiveACState === 4) {
|
|
|
- eobrun--;
|
|
|
- if (eobrun === 0)
|
|
|
- successiveACState = 0;
|
|
|
+ if (charType == 'R' || charType == 'AL' || charType == 'AN') {
|
|
|
+ numBidi++;
|
|
|
}
|
|
|
+ types[i] = charType;
|
|
|
}
|
|
|
|
|
|
- function decodeMcu(component, decode, mcu, row, col) {
|
|
|
- var mcuRow = (mcu / mcusPerLine) | 0;
|
|
|
- var mcuCol = mcu % mcusPerLine;
|
|
|
- var blockRow = mcuRow * component.v + row;
|
|
|
- var blockCol = mcuCol * component.h + col;
|
|
|
- var offset = getBlockBufferOffset(component, blockRow, blockCol);
|
|
|
- decode(component, offset);
|
|
|
- }
|
|
|
-
|
|
|
- function decodeBlock(component, decode, mcu) {
|
|
|
- var blockRow = (mcu / component.blocksPerLine) | 0;
|
|
|
- var blockCol = mcu % component.blocksPerLine;
|
|
|
- var offset = getBlockBufferOffset(component, blockRow, blockCol);
|
|
|
- decode(component, offset);
|
|
|
+ // Detect the bidi method
|
|
|
+ // - If there are no rtl characters then no bidi needed
|
|
|
+ // - If less than 30% chars are rtl then string is primarily ltr
|
|
|
+ // - If more than 30% chars are rtl then string is primarily rtl
|
|
|
+ if (numBidi === 0) {
|
|
|
+ isLTR = true;
|
|
|
+ return createBidiText(str, isLTR);
|
|
|
}
|
|
|
|
|
|
- var componentsLength = components.length;
|
|
|
- var component, i, j, k, n;
|
|
|
- var decodeFn;
|
|
|
- if (progressive) {
|
|
|
- if (spectralStart === 0)
|
|
|
- decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
|
|
|
- else
|
|
|
- decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
|
|
|
- } else {
|
|
|
- decodeFn = decodeBaseline;
|
|
|
+ if (startLevel == -1) {
|
|
|
+ if ((strLength / numBidi) < 0.3) {
|
|
|
+ isLTR = true;
|
|
|
+ startLevel = 0;
|
|
|
+ } else {
|
|
|
+ isLTR = false;
|
|
|
+ startLevel = 1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- var mcu = 0, marker;
|
|
|
- var mcuExpected;
|
|
|
- if (componentsLength == 1) {
|
|
|
- mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
|
|
|
- } else {
|
|
|
- mcuExpected = mcusPerLine * frame.mcusPerColumn;
|
|
|
- }
|
|
|
- if (!resetInterval) {
|
|
|
- resetInterval = mcuExpected;
|
|
|
+ var levels = [];
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ levels[i] = startLevel;
|
|
|
}
|
|
|
|
|
|
- var h, v;
|
|
|
- while (mcu < mcuExpected) {
|
|
|
- // reset interval stuff
|
|
|
- for (i = 0; i < componentsLength; i++) {
|
|
|
- components[i].pred = 0;
|
|
|
- }
|
|
|
- eobrun = 0;
|
|
|
+ /*
|
|
|
+ X1-X10: skip most of this, since we are NOT doing the embeddings.
|
|
|
+ */
|
|
|
+ var e = (isOdd(startLevel) ? 'R' : 'L');
|
|
|
+ var sor = e;
|
|
|
+ var eor = sor;
|
|
|
|
|
|
- if (componentsLength == 1) {
|
|
|
- component = components[0];
|
|
|
- for (n = 0; n < resetInterval; n++) {
|
|
|
- decodeBlock(component, decodeFn, mcu);
|
|
|
- mcu++;
|
|
|
- }
|
|
|
+ /*
|
|
|
+ W1. Examine each non-spacing mark (NSM) in the level run, and change the
|
|
|
+ type of the NSM to the type of the previous character. If the NSM is at the
|
|
|
+ start of the level run, it will get the type of sor.
|
|
|
+ */
|
|
|
+ var lastType = sor;
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ if (types[i] == 'NSM') {
|
|
|
+ types[i] = lastType;
|
|
|
} else {
|
|
|
- for (n = 0; n < resetInterval; n++) {
|
|
|
- for (i = 0; i < componentsLength; i++) {
|
|
|
- component = components[i];
|
|
|
- h = component.h;
|
|
|
- v = component.v;
|
|
|
- for (j = 0; j < v; j++) {
|
|
|
- for (k = 0; k < h; k++) {
|
|
|
- decodeMcu(component, decodeFn, mcu, j, k);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- mcu++;
|
|
|
- }
|
|
|
+ lastType = types[i];
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- // find marker
|
|
|
- bitsCount = 0;
|
|
|
- marker = (data[offset] << 8) | data[offset + 1];
|
|
|
- if (marker <= 0xFF00) {
|
|
|
- throw "marker was not found";
|
|
|
+ /*
|
|
|
+ W2. Search backwards from each instance of a European number until the
|
|
|
+ first strong type (R, L, AL, or sor) is found. If an AL is found, change
|
|
|
+ the type of the European number to Arabic number.
|
|
|
+ */
|
|
|
+ lastType = sor;
|
|
|
+ var t;
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ t = types[i];
|
|
|
+ if (t == 'EN') {
|
|
|
+ types[i] = (lastType == 'AL') ? 'AN' : 'EN';
|
|
|
+ } else if (t == 'R' || t == 'L' || t == 'AL') {
|
|
|
+ lastType = t;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
|
|
|
- offset += 2;
|
|
|
- } else {
|
|
|
- break;
|
|
|
+ /*
|
|
|
+ W3. Change all ALs to R.
|
|
|
+ */
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ t = types[i];
|
|
|
+ if (t == 'AL') {
|
|
|
+ types[i] = 'R';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return offset - startOffset;
|
|
|
- }
|
|
|
-
|
|
|
- // A port of poppler's IDCT method which in turn is taken from:
|
|
|
- // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
|
|
|
- // "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
|
|
|
- // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
|
|
|
- // 988-991.
|
|
|
- function quantizeAndInverse(component, blockBufferOffset, p) {
|
|
|
- var qt = component.quantizationTable;
|
|
|
- var v0, v1, v2, v3, v4, v5, v6, v7, t;
|
|
|
- var i;
|
|
|
-
|
|
|
- // dequant
|
|
|
- for (i = 0; i < 64; i++) {
|
|
|
- p[i] = component.blockData[blockBufferOffset + i] * qt[i];
|
|
|
+ /*
|
|
|
+ W4. A single European separator between two European numbers changes to a
|
|
|
+ European number. A single common separator between two numbers of the same
|
|
|
+ type changes to that type:
|
|
|
+ */
|
|
|
+ for (i = 1; i < strLength - 1; ++i) {
|
|
|
+ if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN') {
|
|
|
+ types[i] = 'EN';
|
|
|
+ }
|
|
|
+ if (types[i] == 'CS' && (types[i - 1] == 'EN' || types[i - 1] == 'AN') &&
|
|
|
+ types[i + 1] == types[i - 1]) {
|
|
|
+ types[i] = types[i - 1];
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // inverse DCT on rows
|
|
|
- for (i = 0; i < 8; ++i) {
|
|
|
- var row = 8 * i;
|
|
|
-
|
|
|
- // check for all-zero AC coefficients
|
|
|
- if (p[1 + row] == 0 && p[2 + row] == 0 && p[3 + row] == 0 &&
|
|
|
- p[4 + row] == 0 && p[5 + row] == 0 && p[6 + row] == 0 &&
|
|
|
- p[7 + row] == 0) {
|
|
|
- t = (dctSqrt2 * p[0 + row] + 512) >> 10;
|
|
|
- p[0 + row] = t;
|
|
|
- p[1 + row] = t;
|
|
|
- p[2 + row] = t;
|
|
|
- p[3 + row] = t;
|
|
|
- p[4 + row] = t;
|
|
|
- p[5 + row] = t;
|
|
|
- p[6 + row] = t;
|
|
|
- p[7 + row] = t;
|
|
|
- continue;
|
|
|
+ /*
|
|
|
+ W5. A sequence of European terminators adjacent to European numbers changes
|
|
|
+ to all European numbers:
|
|
|
+ */
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ if (types[i] == 'EN') {
|
|
|
+ // do before
|
|
|
+ var j;
|
|
|
+ for (j = i - 1; j >= 0; --j) {
|
|
|
+ if (types[j] != 'ET') {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ types[j] = 'EN';
|
|
|
+ }
|
|
|
+ // do after
|
|
|
+ for (j = i + 1; j < strLength; --j) {
|
|
|
+ if (types[j] != 'ET') {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ types[j] = 'EN';
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // stage 4
|
|
|
- v0 = (dctSqrt2 * p[0 + row] + 128) >> 8;
|
|
|
- v1 = (dctSqrt2 * p[4 + row] + 128) >> 8;
|
|
|
- v2 = p[2 + row];
|
|
|
- v3 = p[6 + row];
|
|
|
- v4 = (dctSqrt1d2 * (p[1 + row] - p[7 + row]) + 128) >> 8;
|
|
|
- v7 = (dctSqrt1d2 * (p[1 + row] + p[7 + row]) + 128) >> 8;
|
|
|
- v5 = p[3 + row] << 4;
|
|
|
- v6 = p[5 + row] << 4;
|
|
|
-
|
|
|
- // stage 3
|
|
|
- t = (v0 - v1+ 1) >> 1;
|
|
|
- v0 = (v0 + v1 + 1) >> 1;
|
|
|
- v1 = t;
|
|
|
- t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
|
|
|
- v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
|
|
|
- v3 = t;
|
|
|
- t = (v4 - v6 + 1) >> 1;
|
|
|
- v4 = (v4 + v6 + 1) >> 1;
|
|
|
- v6 = t;
|
|
|
- t = (v7 + v5 + 1) >> 1;
|
|
|
- v5 = (v7 - v5 + 1) >> 1;
|
|
|
- v7 = t;
|
|
|
-
|
|
|
- // stage 2
|
|
|
- t = (v0 - v3 + 1) >> 1;
|
|
|
- v0 = (v0 + v3 + 1) >> 1;
|
|
|
- v3 = t;
|
|
|
- t = (v1 - v2 + 1) >> 1;
|
|
|
- v1 = (v1 + v2 + 1) >> 1;
|
|
|
- v2 = t;
|
|
|
- t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
|
|
|
- v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
|
|
|
- v7 = t;
|
|
|
- t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
|
|
|
- v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
|
|
|
- v6 = t;
|
|
|
-
|
|
|
- // stage 1
|
|
|
- p[0 + row] = v0 + v7;
|
|
|
- p[7 + row] = v0 - v7;
|
|
|
- p[1 + row] = v1 + v6;
|
|
|
- p[6 + row] = v1 - v6;
|
|
|
- p[2 + row] = v2 + v5;
|
|
|
- p[5 + row] = v2 - v5;
|
|
|
- p[3 + row] = v3 + v4;
|
|
|
- p[4 + row] = v3 - v4;
|
|
|
}
|
|
|
|
|
|
- // inverse DCT on columns
|
|
|
- for (i = 0; i < 8; ++i) {
|
|
|
- var col = i;
|
|
|
-
|
|
|
- // check for all-zero AC coefficients
|
|
|
- if (p[1*8 + col] == 0 && p[2*8 + col] == 0 && p[3*8 + col] == 0 &&
|
|
|
- p[4*8 + col] == 0 && p[5*8 + col] == 0 && p[6*8 + col] == 0 &&
|
|
|
- p[7*8 + col] == 0) {
|
|
|
- t = (dctSqrt2 * p[i+0] + 8192) >> 14;
|
|
|
- p[0*8 + col] = t;
|
|
|
- p[1*8 + col] = t;
|
|
|
- p[2*8 + col] = t;
|
|
|
- p[3*8 + col] = t;
|
|
|
- p[4*8 + col] = t;
|
|
|
- p[5*8 + col] = t;
|
|
|
- p[6*8 + col] = t;
|
|
|
- p[7*8 + col] = t;
|
|
|
- continue;
|
|
|
+ /*
|
|
|
+ W6. Otherwise, separators and terminators change to Other Neutral:
|
|
|
+ */
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ t = types[i];
|
|
|
+ if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS') {
|
|
|
+ types[i] = 'ON';
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- // stage 4
|
|
|
- v0 = (dctSqrt2 * p[0*8 + col] + 2048) >> 12;
|
|
|
- v1 = (dctSqrt2 * p[4*8 + col] + 2048) >> 12;
|
|
|
- v2 = p[2*8 + col];
|
|
|
- v3 = p[6*8 + col];
|
|
|
- v4 = (dctSqrt1d2 * (p[1*8 + col] - p[7*8 + col]) + 2048) >> 12;
|
|
|
- v7 = (dctSqrt1d2 * (p[1*8 + col] + p[7*8 + col]) + 2048) >> 12;
|
|
|
- v5 = p[3*8 + col];
|
|
|
- v6 = p[5*8 + col];
|
|
|
-
|
|
|
- // stage 3
|
|
|
- t = (v0 - v1 + 1) >> 1;
|
|
|
- v0 = (v0 + v1 + 1) >> 1;
|
|
|
- v1 = t;
|
|
|
- t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
|
|
|
- v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
|
|
|
- v3 = t;
|
|
|
- t = (v4 - v6 + 1) >> 1;
|
|
|
- v4 = (v4 + v6 + 1) >> 1;
|
|
|
- v6 = t;
|
|
|
- t = (v7 + v5 + 1) >> 1;
|
|
|
- v5 = (v7 - v5 + 1) >> 1;
|
|
|
- v7 = t;
|
|
|
-
|
|
|
- // stage 2
|
|
|
- t = (v0 - v3 + 1) >> 1;
|
|
|
- v0 = (v0 + v3 + 1) >> 1;
|
|
|
- v3 = t;
|
|
|
- t = (v1 - v2 + 1) >> 1;
|
|
|
- v1 = (v1 + v2 + 1) >> 1;
|
|
|
- v2 = t;
|
|
|
- t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
|
|
|
- v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
|
|
|
- v7 = t;
|
|
|
- t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
|
|
|
- v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
|
|
|
- v6 = t;
|
|
|
-
|
|
|
- // stage 1
|
|
|
- p[0*8 + col] = v0 + v7;
|
|
|
- p[7*8 + col] = v0 - v7;
|
|
|
- p[1*8 + col] = v1 + v6;
|
|
|
- p[6*8 + col] = v1 - v6;
|
|
|
- p[2*8 + col] = v2 + v5;
|
|
|
- p[5*8 + col] = v2 - v5;
|
|
|
- p[3*8 + col] = v3 + v4;
|
|
|
- p[4*8 + col] = v3 - v4;
|
|
|
+ /*
|
|
|
+ W7. Search backwards from each instance of a European number until the
|
|
|
+ first strong type (R, L, or sor) is found. If an L is found, then change
|
|
|
+ the type of the European number to L.
|
|
|
+ */
|
|
|
+ lastType = sor;
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ t = types[i];
|
|
|
+ if (t == 'EN') {
|
|
|
+ types[i] = ((lastType == 'L') ? 'L' : 'EN');
|
|
|
+ } else if (t == 'R' || t == 'L') {
|
|
|
+ lastType = t;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // convert to 8-bit integers
|
|
|
- for (i = 0; i < 64; ++i) {
|
|
|
- var index = blockBufferOffset + i;
|
|
|
- component.blockData[index] = clampTo8bitInt((p[i] + 2056) >> 4);
|
|
|
+ /*
|
|
|
+ N1. A sequence of neutrals takes the direction of the surrounding strong
|
|
|
+ text if the text on both sides has the same direction. European and Arabic
|
|
|
+ numbers are treated as though they were R. Start-of-level-run (sor) and
|
|
|
+ end-of-level-run (eor) are used at level run boundaries.
|
|
|
+ */
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ if (types[i] == 'ON') {
|
|
|
+ var end = findUnequal(types, i + 1, 'ON');
|
|
|
+ var before = sor;
|
|
|
+ if (i > 0) {
|
|
|
+ before = types[i - 1];
|
|
|
+ }
|
|
|
+
|
|
|
+ var after = eor;
|
|
|
+ if (end + 1 < strLength) {
|
|
|
+ after = types[end + 1];
|
|
|
+ }
|
|
|
+ if (before != 'L') {
|
|
|
+ before = 'R';
|
|
|
+ }
|
|
|
+ if (after != 'L') {
|
|
|
+ after = 'R';
|
|
|
+ }
|
|
|
+ if (before == after) {
|
|
|
+ setValues(types, i, end, before);
|
|
|
+ }
|
|
|
+ i = end - 1; // reset to end (-1 so next iteration is ok)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- function buildComponentData(frame, component) {
|
|
|
- var lines = [];
|
|
|
- var blocksPerLine = component.blocksPerLine;
|
|
|
- var blocksPerColumn = component.blocksPerColumn;
|
|
|
- var samplesPerLine = blocksPerLine << 3;
|
|
|
- var computationBuffer = new Int32Array(64);
|
|
|
+ /*
|
|
|
+ N2. Any remaining neutrals take the embedding direction.
|
|
|
+ */
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ if (types[i] == 'ON') {
|
|
|
+ types[i] = e;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- var i, j, ll = 0;
|
|
|
- for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
|
|
|
- for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
|
|
|
- var offset = getBlockBufferOffset(component, blockRow, blockCol)
|
|
|
- quantizeAndInverse(component, offset, computationBuffer);
|
|
|
+ /*
|
|
|
+ I1. For all characters with an even (left-to-right) embedding direction,
|
|
|
+ those of type R go up one level and those of type AN or EN go up two
|
|
|
+ levels.
|
|
|
+ I2. For all characters with an odd (right-to-left) embedding direction,
|
|
|
+ those of type L, EN or AN go up one level.
|
|
|
+ */
|
|
|
+ for (i = 0; i < strLength; ++i) {
|
|
|
+ t = types[i];
|
|
|
+ if (isEven(levels[i])) {
|
|
|
+ if (t == 'R') {
|
|
|
+ levels[i] += 1;
|
|
|
+ } else if (t == 'AN' || t == 'EN') {
|
|
|
+ levels[i] += 2;
|
|
|
+ }
|
|
|
+ } else { // isOdd
|
|
|
+ if (t == 'L' || t == 'AN' || t == 'EN') {
|
|
|
+ levels[i] += 1;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- return component.blockData;
|
|
|
- }
|
|
|
|
|
|
- function clampTo8bitInt(a) {
|
|
|
- return a <= 0 ? 0 : a >= 255 ? 255 : a | 0;
|
|
|
- }
|
|
|
+ /*
|
|
|
+ L1. On each line, reset the embedding level of the following characters to
|
|
|
+ the paragraph embedding level:
|
|
|
|
|
|
- function clamp0to255(a) {
|
|
|
- return a <= 0 ? 0 : a >= 255 ? 255 : a;
|
|
|
- }
|
|
|
+ segment separators,
|
|
|
+ paragraph separators,
|
|
|
+ any sequence of whitespace characters preceding a segment separator or
|
|
|
+ paragraph separator, and any sequence of white space characters at the end
|
|
|
+ of the line.
|
|
|
+ */
|
|
|
|
|
|
- constructor.prototype = {
|
|
|
- load: function load(path) {
|
|
|
- var xhr = new XMLHttpRequest();
|
|
|
- xhr.open("GET", path, true);
|
|
|
- xhr.responseType = "arraybuffer";
|
|
|
- xhr.onload = (function() {
|
|
|
- // TODO catch parse error
|
|
|
- var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);
|
|
|
- this.parse(data);
|
|
|
- if (this.onload)
|
|
|
- this.onload();
|
|
|
- }).bind(this);
|
|
|
- xhr.send(null);
|
|
|
- },
|
|
|
+ // don't bother as text is only single line
|
|
|
|
|
|
- parse: function parse(data) {
|
|
|
+ /*
|
|
|
+ L2. From the highest level found in the text to the lowest odd level on
|
|
|
+ each line, reverse any contiguous sequence of characters that are at that
|
|
|
+ level or higher.
|
|
|
+ */
|
|
|
|
|
|
- function readUint16() {
|
|
|
- var value = (data[offset] << 8) | data[offset + 1];
|
|
|
- offset += 2;
|
|
|
- return value;
|
|
|
+ // find highest level & lowest odd level
|
|
|
+ var highestLevel = -1;
|
|
|
+ var lowestOddLevel = 99;
|
|
|
+ var level;
|
|
|
+ for (i = 0, ii = levels.length; i < ii; ++i) {
|
|
|
+ level = levels[i];
|
|
|
+ if (highestLevel < level) {
|
|
|
+ highestLevel = level;
|
|
|
}
|
|
|
-
|
|
|
- function readDataBlock() {
|
|
|
- var length = readUint16();
|
|
|
- var array = data.subarray(offset, offset + length - 2);
|
|
|
- offset += array.length;
|
|
|
- return array;
|
|
|
+ if (lowestOddLevel > level && isOdd(level)) {
|
|
|
+ lowestOddLevel = level;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- function prepareComponents(frame) {
|
|
|
- var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
|
|
|
- var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
|
|
|
- for (var i = 0; i < frame.components.length; i++) {
|
|
|
- component = frame.components[i];
|
|
|
- var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
|
|
|
- var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
|
|
|
- var blocksPerLineForMcu = mcusPerLine * component.h;
|
|
|
- var blocksPerColumnForMcu = mcusPerColumn * component.v;
|
|
|
-
|
|
|
- var blocksBufferSize = 64 * blocksPerColumnForMcu
|
|
|
- * (blocksPerLineForMcu + 1);
|
|
|
- component.blockData = new Int16Array(blocksBufferSize);
|
|
|
- component.blocksPerLine = blocksPerLine;
|
|
|
- component.blocksPerColumn = blocksPerColumn;
|
|
|
+ // now reverse between those limits
|
|
|
+ for (level = highestLevel; level >= lowestOddLevel; --level) {
|
|
|
+ // find segments to reverse
|
|
|
+ var start = -1;
|
|
|
+ for (i = 0, ii = levels.length; i < ii; ++i) {
|
|
|
+ if (levels[i] < level) {
|
|
|
+ if (start >= 0) {
|
|
|
+ reverseValues(chars, start, i);
|
|
|
+ start = -1;
|
|
|
+ }
|
|
|
+ } else if (start < 0) {
|
|
|
+ start = i;
|
|
|
}
|
|
|
- frame.mcusPerLine = mcusPerLine;
|
|
|
- frame.mcusPerColumn = mcusPerColumn;
|
|
|
}
|
|
|
-
|
|
|
- var offset = 0, length = data.length;
|
|
|
- var jfif = null;
|
|
|
- var adobe = null;
|
|
|
- var pixels = null;
|
|
|
- var frame, resetInterval;
|
|
|
- var quantizationTables = [];
|
|
|
- var huffmanTablesAC = [], huffmanTablesDC = [];
|
|
|
- var fileMarker = readUint16();
|
|
|
- if (fileMarker != 0xFFD8) { // SOI (Start of Image)
|
|
|
- throw "SOI not found";
|
|
|
+ if (start >= 0) {
|
|
|
+ reverseValues(chars, start, levels.length);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- fileMarker = readUint16();
|
|
|
- while (fileMarker != 0xFFD9) { // EOI (End of image)
|
|
|
- var i, j, l;
|
|
|
- switch(fileMarker) {
|
|
|
- case 0xFFE0: // APP0 (Application Specific)
|
|
|
- case 0xFFE1: // APP1
|
|
|
- case 0xFFE2: // APP2
|
|
|
- case 0xFFE3: // APP3
|
|
|
- case 0xFFE4: // APP4
|
|
|
- case 0xFFE5: // APP5
|
|
|
- case 0xFFE6: // APP6
|
|
|
- case 0xFFE7: // APP7
|
|
|
- case 0xFFE8: // APP8
|
|
|
- case 0xFFE9: // APP9
|
|
|
- case 0xFFEA: // APP10
|
|
|
- case 0xFFEB: // APP11
|
|
|
- case 0xFFEC: // APP12
|
|
|
- case 0xFFED: // APP13
|
|
|
- case 0xFFEE: // APP14
|
|
|
- case 0xFFEF: // APP15
|
|
|
- case 0xFFFE: // COM (Comment)
|
|
|
- var appData = readDataBlock();
|
|
|
-
|
|
|
- if (fileMarker === 0xFFE0) {
|
|
|
- if (appData[0] === 0x4A && appData[1] === 0x46 && appData[2] === 0x49 &&
|
|
|
- appData[3] === 0x46 && appData[4] === 0) { // 'JFIF\x00'
|
|
|
- jfif = {
|
|
|
- version: { major: appData[5], minor: appData[6] },
|
|
|
- densityUnits: appData[7],
|
|
|
- xDensity: (appData[8] << 8) | appData[9],
|
|
|
- yDensity: (appData[10] << 8) | appData[11],
|
|
|
- thumbWidth: appData[12],
|
|
|
- thumbHeight: appData[13],
|
|
|
- thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
- // TODO APP1 - Exif
|
|
|
- if (fileMarker === 0xFFEE) {
|
|
|
- if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6F &&
|
|
|
- appData[3] === 0x62 && appData[4] === 0x65 && appData[5] === 0) { // 'Adobe\x00'
|
|
|
- adobe = {
|
|
|
- version: appData[6],
|
|
|
- flags0: (appData[7] << 8) | appData[8],
|
|
|
- flags1: (appData[9] << 8) | appData[10],
|
|
|
- transformCode: appData[11]
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
+ /*
|
|
|
+ L3. Combining marks applied to a right-to-left base character will at this
|
|
|
+ point precede their base character. If the rendering engine expects them to
|
|
|
+ follow the base characters in the final display process, then the ordering
|
|
|
+ of the marks and the base character must be reversed.
|
|
|
+ */
|
|
|
|
|
|
- case 0xFFDB: // DQT (Define Quantization Tables)
|
|
|
- var quantizationTablesLength = readUint16();
|
|
|
- var quantizationTablesEnd = quantizationTablesLength + offset - 2;
|
|
|
- while (offset < quantizationTablesEnd) {
|
|
|
- var quantizationTableSpec = data[offset++];
|
|
|
- var tableData = new Int32Array(64);
|
|
|
- if ((quantizationTableSpec >> 4) === 0) { // 8 bit values
|
|
|
- for (j = 0; j < 64; j++) {
|
|
|
- var z = dctZigZag[j];
|
|
|
- tableData[z] = data[offset++];
|
|
|
- }
|
|
|
- } else if ((quantizationTableSpec >> 4) === 1) { //16 bit
|
|
|
- for (j = 0; j < 64; j++) {
|
|
|
- var z = dctZigZag[j];
|
|
|
- tableData[z] = readUint16();
|
|
|
- }
|
|
|
- } else
|
|
|
- throw "DQT: invalid table spec";
|
|
|
- quantizationTables[quantizationTableSpec & 15] = tableData;
|
|
|
- }
|
|
|
- break;
|
|
|
+ // don't bother for now
|
|
|
|
|
|
- case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT)
|
|
|
- case 0xFFC1: // SOF1 (Start of Frame, Extended DCT)
|
|
|
- case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT)
|
|
|
- if (frame) {
|
|
|
- throw "Only single frame JPEGs supported";
|
|
|
- }
|
|
|
- readUint16(); // skip data length
|
|
|
- frame = {};
|
|
|
- frame.extended = (fileMarker === 0xFFC1);
|
|
|
- frame.progressive = (fileMarker === 0xFFC2);
|
|
|
- frame.precision = data[offset++];
|
|
|
- frame.scanLines = readUint16();
|
|
|
- frame.samplesPerLine = readUint16();
|
|
|
- frame.components = [];
|
|
|
- frame.componentIds = {};
|
|
|
- var componentsCount = data[offset++], componentId;
|
|
|
- var maxH = 0, maxV = 0;
|
|
|
- for (i = 0; i < componentsCount; i++) {
|
|
|
- componentId = data[offset];
|
|
|
- var h = data[offset + 1] >> 4;
|
|
|
- var v = data[offset + 1] & 15;
|
|
|
- if (maxH < h) maxH = h;
|
|
|
- if (maxV < v) maxV = v;
|
|
|
- var qId = data[offset + 2];
|
|
|
- var l = frame.components.push({
|
|
|
- h: h,
|
|
|
- v: v,
|
|
|
- quantizationTable: quantizationTables[qId]
|
|
|
- });
|
|
|
- frame.componentIds[componentId] = l - 1;
|
|
|
- offset += 3;
|
|
|
- }
|
|
|
- frame.maxH = maxH;
|
|
|
- frame.maxV = maxV;
|
|
|
- prepareComponents(frame);
|
|
|
- break;
|
|
|
+ /*
|
|
|
+ L4. A character that possesses the mirrored property as specified by
|
|
|
+ Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
|
|
|
+ directionality of that character is R.
|
|
|
+ */
|
|
|
|
|
|
- case 0xFFC4: // DHT (Define Huffman Tables)
|
|
|
- var huffmanLength = readUint16();
|
|
|
- for (i = 2; i < huffmanLength;) {
|
|
|
- var huffmanTableSpec = data[offset++];
|
|
|
- var codeLengths = new Uint8Array(16);
|
|
|
- var codeLengthSum = 0;
|
|
|
- for (j = 0; j < 16; j++, offset++)
|
|
|
- codeLengthSum += (codeLengths[j] = data[offset]);
|
|
|
- var huffmanValues = new Uint8Array(codeLengthSum);
|
|
|
- for (j = 0; j < codeLengthSum; j++, offset++)
|
|
|
- huffmanValues[j] = data[offset];
|
|
|
- i += 17 + codeLengthSum;
|
|
|
+ // don't mirror as characters are already mirrored in the pdf
|
|
|
|
|
|
- ((huffmanTableSpec >> 4) === 0 ?
|
|
|
- huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] =
|
|
|
- buildHuffmanTable(codeLengths, huffmanValues);
|
|
|
- }
|
|
|
- break;
|
|
|
+ // Finally, return string
|
|
|
+ var result = '';
|
|
|
+ for (i = 0, ii = chars.length; i < ii; ++i) {
|
|
|
+ var ch = chars[i];
|
|
|
+ if (ch != '<' && ch != '>') {
|
|
|
+ result += ch;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return createBidiText(result, isLTR);
|
|
|
+ }
|
|
|
|
|
|
- case 0xFFDD: // DRI (Define Restart Interval)
|
|
|
- readUint16(); // skip data length
|
|
|
- resetInterval = readUint16();
|
|
|
- break;
|
|
|
+ return bidi;
|
|
|
+})();
|
|
|
|
|
|
- case 0xFFDA: // SOS (Start of Scan)
|
|
|
- var scanLength = readUint16();
|
|
|
- var selectorsCount = data[offset++];
|
|
|
- var components = [], component;
|
|
|
- for (i = 0; i < selectorsCount; i++) {
|
|
|
- var componentIndex = frame.componentIds[data[offset++]];
|
|
|
- component = frame.components[componentIndex];
|
|
|
- var tableSpec = data[offset++];
|
|
|
- component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
|
|
|
- component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
|
|
|
- components.push(component);
|
|
|
- }
|
|
|
- var spectralStart = data[offset++];
|
|
|
- var spectralEnd = data[offset++];
|
|
|
- var successiveApproximation = data[offset++];
|
|
|
- var processed = decodeScan(data, offset,
|
|
|
- frame, components, resetInterval,
|
|
|
- spectralStart, spectralEnd,
|
|
|
- successiveApproximation >> 4, successiveApproximation & 15);
|
|
|
- offset += processed;
|
|
|
- break;
|
|
|
- default:
|
|
|
- if (data[offset - 3] == 0xFF &&
|
|
|
- data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
|
|
|
- // could be incorrect encoding -- last 0xFF byte of the previous
|
|
|
- // block was eaten by the encoder
|
|
|
- offset -= 3;
|
|
|
- break;
|
|
|
- }
|
|
|
- throw "unknown JPEG marker " + fileMarker.toString(16);
|
|
|
- }
|
|
|
- fileMarker = readUint16();
|
|
|
- }
|
|
|
|
|
|
- this.width = frame.samplesPerLine;
|
|
|
- this.height = frame.scanLines;
|
|
|
- this.jfif = jfif;
|
|
|
- this.adobe = adobe;
|
|
|
- this.components = [];
|
|
|
- for (var i = 0; i < frame.components.length; i++) {
|
|
|
- var component = frame.components[i];
|
|
|
- this.components.push({
|
|
|
- output: buildComponentData(frame, component),
|
|
|
- scaleX: component.h / frame.maxH,
|
|
|
- scaleY: component.v / frame.maxV,
|
|
|
- blocksPerLine: component.blocksPerLine,
|
|
|
- blocksPerColumn: component.blocksPerColumn
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
|
|
|
- getData: function getData(width, height) {
|
|
|
- var scaleX = this.width / width, scaleY = this.height / height;
|
|
|
+/* Copyright 2014 Opera Software ASA
|
|
|
+ *
|
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+ * you may not use this file except in compliance with the License.
|
|
|
+ * You may obtain a copy of the License at
|
|
|
+ *
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+ *
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+ * See the License for the specific language governing permissions and
|
|
|
+ * limitations under the License.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * Based on https://code.google.com/p/smhasher/wiki/MurmurHash3.
|
|
|
+ * Hashes roughly 100 KB per millisecond on i7 3.4 GHz.
|
|
|
+ */
|
|
|
+/* globals Uint32ArrayView */
|
|
|
|
|
|
- var component, componentScaleX, componentScaleY;
|
|
|
- var x, y, i;
|
|
|
- var offset = 0;
|
|
|
- var Y, Cb, Cr, K, C, M, Ye, R, G, B;
|
|
|
- var colorTransform;
|
|
|
- var numComponents = this.components.length;
|
|
|
- var dataLength = width * height * numComponents;
|
|
|
- var data = new Uint8Array(dataLength);
|
|
|
- var componentLine;
|
|
|
+'use strict';
|
|
|
|
|
|
- // lineData is reused for all components. Assume first component is
|
|
|
- // the biggest
|
|
|
- var lineData = new Uint8Array((this.components[0].blocksPerLine << 3) *
|
|
|
- this.components[0].blocksPerColumn * 8);
|
|
|
+var MurmurHash3_64 = (function MurmurHash3_64Closure (seed) {
|
|
|
+ // Workaround for missing math precison in JS.
|
|
|
+ var MASK_HIGH = 0xffff0000;
|
|
|
+ var MASK_LOW = 0xffff;
|
|
|
|
|
|
- // First construct image data ...
|
|
|
- for (i = 0; i < numComponents; i++) {
|
|
|
- component = this.components[i];
|
|
|
- var blocksPerLine = component.blocksPerLine;
|
|
|
- var blocksPerColumn = component.blocksPerColumn;
|
|
|
- var samplesPerLine = blocksPerLine << 3;
|
|
|
+ function MurmurHash3_64 (seed) {
|
|
|
+ var SEED = 0xc3d2e1f0;
|
|
|
+ this.h1 = seed ? seed & 0xffffffff : SEED;
|
|
|
+ this.h2 = seed ? seed & 0xffffffff : SEED;
|
|
|
+ }
|
|
|
|
|
|
- var j, k, ll = 0;
|
|
|
- var lineOffset = 0;
|
|
|
- for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
|
|
|
- var scanLine = blockRow << 3;
|
|
|
- for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
|
|
|
- var bufferOffset = getBlockBufferOffset(component, blockRow, blockCol);
|
|
|
- var offset = 0, sample = blockCol << 3;
|
|
|
- for (j = 0; j < 8; j++) {
|
|
|
- var lineOffset = (scanLine + j) * samplesPerLine;
|
|
|
- for (k = 0; k < 8; k++) {
|
|
|
- lineData[lineOffset + sample + k] =
|
|
|
- component.output[bufferOffset + offset++];
|
|
|
- }
|
|
|
- }
|
|
|
+ MurmurHash3_64.prototype = {
|
|
|
+ update: function MurmurHash3_64_update(input) {
|
|
|
+ var useUint32ArrayView = false;
|
|
|
+ var i;
|
|
|
+ if (typeof input == 'string') {
|
|
|
+ var data = new Uint8Array(input.length * 2);
|
|
|
+ var length = 0;
|
|
|
+ for (i = 0; i < input.length; i++) {
|
|
|
+ var code = input.charCodeAt(i);
|
|
|
+ if (code <= 0xff) {
|
|
|
+ data[length++] = code;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ data[length++] = code >>> 8;
|
|
|
+ data[length++] = code & 0xff;
|
|
|
}
|
|
|
}
|
|
|
+ } else if (input instanceof Uint8Array) {
|
|
|
+ data = input;
|
|
|
+ length = data.length;
|
|
|
+ } else if (typeof input === 'object' && ('length' in input)) {
|
|
|
+ // processing regular arrays as well, e.g. for IE9
|
|
|
+ data = input;
|
|
|
+ length = data.length;
|
|
|
+ useUint32ArrayView = true;
|
|
|
+ } else {
|
|
|
+ throw new Error('Wrong data format in MurmurHash3_64_update. ' +
|
|
|
+ 'Input must be a string or array.');
|
|
|
+ }
|
|
|
|
|
|
- componentScaleX = component.scaleX * scaleX;
|
|
|
- componentScaleY = component.scaleY * scaleY;
|
|
|
- offset = i;
|
|
|
+ var blockCounts = length >> 2;
|
|
|
+ var tailLength = length - blockCounts * 4;
|
|
|
+ // we don't care about endianness here
|
|
|
+ var dataUint32 = useUint32ArrayView ?
|
|
|
+ new Uint32ArrayView(data, blockCounts) :
|
|
|
+ new Uint32Array(data.buffer, 0, blockCounts);
|
|
|
+ var k1 = 0;
|
|
|
+ var k2 = 0;
|
|
|
+ var h1 = this.h1;
|
|
|
+ var h2 = this.h2;
|
|
|
+ var C1 = 0xcc9e2d51;
|
|
|
+ var C2 = 0x1b873593;
|
|
|
+ var C1_LOW = C1 & MASK_LOW;
|
|
|
+ var C2_LOW = C2 & MASK_LOW;
|
|
|
|
|
|
- var cx, cy;
|
|
|
- var index;
|
|
|
- for (y = 0; y < height; y++) {
|
|
|
- for (x = 0; x < width; x++) {
|
|
|
- cy = 0 | (y * componentScaleY);
|
|
|
- cx = 0 | (x * componentScaleX);
|
|
|
- index = cy * samplesPerLine + cx;
|
|
|
- data[offset] = lineData[index];
|
|
|
- offset += numComponents;
|
|
|
- }
|
|
|
+ for (i = 0; i < blockCounts; i++) {
|
|
|
+ if (i & 1) {
|
|
|
+ k1 = dataUint32[i];
|
|
|
+ k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
|
|
|
+ k1 = k1 << 15 | k1 >>> 17;
|
|
|
+ k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
|
|
|
+ h1 ^= k1;
|
|
|
+ h1 = h1 << 13 | h1 >>> 19;
|
|
|
+ h1 = h1 * 5 + 0xe6546b64;
|
|
|
+ } else {
|
|
|
+ k2 = dataUint32[i];
|
|
|
+ k2 = (k2 * C1 & MASK_HIGH) | (k2 * C1_LOW & MASK_LOW);
|
|
|
+ k2 = k2 << 15 | k2 >>> 17;
|
|
|
+ k2 = (k2 * C2 & MASK_HIGH) | (k2 * C2_LOW & MASK_LOW);
|
|
|
+ h2 ^= k2;
|
|
|
+ h2 = h2 << 13 | h2 >>> 19;
|
|
|
+ h2 = h2 * 5 + 0xe6546b64;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // ... then transform colors, if necessary
|
|
|
- switch (numComponents) {
|
|
|
- case 1: case 2: break;
|
|
|
- // no color conversion for one or two compoenents
|
|
|
+ k1 = 0;
|
|
|
|
|
|
+ switch (tailLength) {
|
|
|
case 3:
|
|
|
- // The default transform for three components is true
|
|
|
- colorTransform = true;
|
|
|
- // The adobe transform marker overrides any previous setting
|
|
|
- if (this.adobe && this.adobe.transformCode)
|
|
|
- colorTransform = true;
|
|
|
- else if (typeof this.colorTransform !== 'undefined')
|
|
|
- colorTransform = !!this.colorTransform;
|
|
|
-
|
|
|
- if (colorTransform) {
|
|
|
- for (i = 0; i < dataLength; i += numComponents) {
|
|
|
- Y = data[i ];
|
|
|
- Cb = data[i + 1];
|
|
|
- Cr = data[i + 2];
|
|
|
-
|
|
|
- R = clamp0to255(Y + 1.402 * (Cr - 128));
|
|
|
- G = clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128));
|
|
|
- B = clamp0to255(Y + 1.772 * (Cb - 128));
|
|
|
-
|
|
|
- data[i ] = R;
|
|
|
- data[i + 1] = G;
|
|
|
- data[i + 2] = B;
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- case 4:
|
|
|
- // The default transform for four components is false
|
|
|
- colorTransform = false;
|
|
|
- // The adobe transform marker overrides any previous setting
|
|
|
- if (this.adobe && this.adobe.transformCode)
|
|
|
- colorTransform = true;
|
|
|
- else if (typeof this.colorTransform !== 'undefined')
|
|
|
- colorTransform = !!this.colorTransform;
|
|
|
-
|
|
|
- if (colorTransform) {
|
|
|
- for (i = 0; i < dataLength; i += numComponents) {
|
|
|
- Y = data[i];
|
|
|
- Cb = data[i + 1];
|
|
|
- Cr = data[i + 2];
|
|
|
-
|
|
|
- C = 255 - clamp0to255(Y + 1.402 * (Cr - 128));
|
|
|
- M = 255 - clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128));
|
|
|
- Ye = 255 - clamp0to255(Y + 1.772 * (Cb - 128));
|
|
|
-
|
|
|
- data[i ] = C;
|
|
|
- data[i + 1] = M;
|
|
|
- data[i + 2] = Ye;
|
|
|
- // K is unchanged
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw 'Unsupported color mode';
|
|
|
- }
|
|
|
- return data;
|
|
|
- },
|
|
|
- copyToImageData: function copyToImageData(imageData) {
|
|
|
- var width = imageData.width, height = imageData.height;
|
|
|
- var imageDataBytes = width * height * 4;
|
|
|
- var imageDataArray = imageData.data;
|
|
|
- var data = this.getData(width, height);
|
|
|
- var i = 0, j = 0;
|
|
|
- var Y, K, C, M, R, G, B;
|
|
|
- switch (this.components.length) {
|
|
|
+ k1 ^= data[blockCounts * 4 + 2] << 16;
|
|
|
+ /* falls through */
|
|
|
+ case 2:
|
|
|
+ k1 ^= data[blockCounts * 4 + 1] << 8;
|
|
|
+ /* falls through */
|
|
|
case 1:
|
|
|
- while (j < imageDataBytes) {
|
|
|
- Y = data[i++];
|
|
|
+ k1 ^= data[blockCounts * 4];
|
|
|
+ /* falls through */
|
|
|
+ k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
|
|
|
+ k1 = k1 << 15 | k1 >>> 17;
|
|
|
+ k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
|
|
|
+ if (blockCounts & 1) {
|
|
|
+ h1 ^= k1;
|
|
|
+ } else {
|
|
|
+ h2 ^= k1;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- imageDataArray[j++] = Y;
|
|
|
- imageDataArray[j++] = Y;
|
|
|
- imageDataArray[j++] = Y;
|
|
|
- imageDataArray[j++] = 255;
|
|
|
- }
|
|
|
- break;
|
|
|
- case 3:
|
|
|
- while (j < imageDataBytes) {
|
|
|
- R = data[i++];
|
|
|
- G = data[i++];
|
|
|
- B = data[i++];
|
|
|
+ this.h1 = h1;
|
|
|
+ this.h2 = h2;
|
|
|
+ return this;
|
|
|
+ },
|
|
|
|
|
|
- imageDataArray[j++] = R;
|
|
|
- imageDataArray[j++] = G;
|
|
|
- imageDataArray[j++] = B;
|
|
|
- imageDataArray[j++] = 255;
|
|
|
- }
|
|
|
- break;
|
|
|
- case 4:
|
|
|
- while (j < imageDataBytes) {
|
|
|
- C = data[i++];
|
|
|
- M = data[i++];
|
|
|
- Y = data[i++];
|
|
|
- K = data[i++];
|
|
|
+ hexdigest: function MurmurHash3_64_hexdigest () {
|
|
|
+ var h1 = this.h1;
|
|
|
+ var h2 = this.h2;
|
|
|
|
|
|
- R = 255 - clamp0to255(C * (1 - K / 255) + K);
|
|
|
- G = 255 - clamp0to255(M * (1 - K / 255) + K);
|
|
|
- B = 255 - clamp0to255(Y * (1 - K / 255) + K);
|
|
|
+ h1 ^= h2 >>> 1;
|
|
|
+ h1 = (h1 * 0xed558ccd & MASK_HIGH) | (h1 * 0x8ccd & MASK_LOW);
|
|
|
+ h2 = (h2 * 0xff51afd7 & MASK_HIGH) |
|
|
|
+ (((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16);
|
|
|
+ h1 ^= h2 >>> 1;
|
|
|
+ h1 = (h1 * 0x1a85ec53 & MASK_HIGH) | (h1 * 0xec53 & MASK_LOW);
|
|
|
+ h2 = (h2 * 0xc4ceb9fe & MASK_HIGH) |
|
|
|
+ (((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16);
|
|
|
+ h1 ^= h2 >>> 1;
|
|
|
|
|
|
- imageDataArray[j++] = R;
|
|
|
- imageDataArray[j++] = G;
|
|
|
- imageDataArray[j++] = B;
|
|
|
- imageDataArray[j++] = 255;
|
|
|
- }
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw 'Unsupported color mode';
|
|
|
+ for (var i = 0, arr = [h1, h2], str = ''; i < arr.length; i++) {
|
|
|
+ var hex = (arr[i] >>> 0).toString(16);
|
|
|
+ while (hex.length < 8) {
|
|
|
+ hex = '0' + hex;
|
|
|
+ }
|
|
|
+ str += hex;
|
|
|
}
|
|
|
+
|
|
|
+ return str;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- return constructor;
|
|
|
+ return MurmurHash3_64;
|
|
|
})();
|
|
|
|
|
|
|