Merge pull request #4365 from fkaelberer/FixJpxParsing
Fix parsing of JP2 images
This commit is contained in:
commit
28d5ddbe2d
125
src/core/jpx.js
125
src/core/jpx.js
@ -50,6 +50,14 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
n = n * 256 + (data[offset + i] & 0xFF);
|
n = n * 256 + (data[offset + i] & 0xFF);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var head = readUint(data, 0, 2);
|
||||||
|
// No box header, immediate start of codestream (SOC)
|
||||||
|
if (head === 0xFF4F) {
|
||||||
|
this.parseCodestream(data, 0, data.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var position = 0, length = data.length;
|
var position = 0, length = data.length;
|
||||||
while (position < length) {
|
while (position < length) {
|
||||||
var headerSize = 8;
|
var headerSize = 8;
|
||||||
@ -254,9 +262,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
cod.segmentationSymbolUsed = !!(blockStyle & 32);
|
cod.segmentationSymbolUsed = !!(blockStyle & 32);
|
||||||
cod.transformation = data[j++];
|
cod.transformation = data[j++];
|
||||||
if (cod.entropyCoderWithCustomPrecincts) {
|
if (cod.entropyCoderWithCustomPrecincts) {
|
||||||
var precinctsSizes = {};
|
var precinctsSizes = [];
|
||||||
while (j < length + position) {
|
while (j < length + position) {
|
||||||
var precinctsSize = data[j];
|
var precinctsSize = data[j++];
|
||||||
precinctsSizes.push({
|
precinctsSizes.push({
|
||||||
PPx: precinctsSize & 0xF,
|
PPx: precinctsSize & 0xF,
|
||||||
PPy: precinctsSize >> 4
|
PPy: precinctsSize >> 4
|
||||||
@ -519,8 +527,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
var codeblocks = subband.codeblocks;
|
var codeblocks = subband.codeblocks;
|
||||||
for (var j = 0, jj = codeblocks.length; j < jj; j++) {
|
for (var j = 0, jj = codeblocks.length; j < jj; j++) {
|
||||||
var codeblock = codeblocks[j];
|
var codeblock = codeblocks[j];
|
||||||
if (codeblock.precinctNumber != precinctNumber)
|
if (codeblock.precinctNumber != precinctNumber) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
precinctCodeblocks.push(codeblock);
|
precinctCodeblocks.push(codeblock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -734,17 +743,21 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
}
|
}
|
||||||
function readCodingpasses() {
|
function readCodingpasses() {
|
||||||
var value = readBits(1);
|
var value = readBits(1);
|
||||||
if (value === 0)
|
if (value === 0) {
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
value = (value << 1) | readBits(1);
|
value = (value << 1) | readBits(1);
|
||||||
if (value == 0x02)
|
if (value == 0x02) {
|
||||||
return 2;
|
return 2;
|
||||||
|
}
|
||||||
value = (value << 2) | readBits(2);
|
value = (value << 2) | readBits(2);
|
||||||
if (value <= 0x0E)
|
if (value <= 0x0E) {
|
||||||
return (value & 0x03) + 3;
|
return (value & 0x03) + 3;
|
||||||
|
}
|
||||||
value = (value << 5) | readBits(5);
|
value = (value << 5) | readBits(5);
|
||||||
if (value <= 0x1FE)
|
if (value <= 0x1FE) {
|
||||||
return (value & 0x1F) + 6;
|
return (value & 0x1F) + 6;
|
||||||
|
}
|
||||||
value = (value << 7) | readBits(7);
|
value = (value << 7) | readBits(7);
|
||||||
return (value & 0x7F) + 37;
|
return (value & 0x7F) + 37;
|
||||||
}
|
}
|
||||||
@ -800,24 +813,28 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!codeblockIncluded)
|
if (!codeblockIncluded) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
if (firstTimeInclusion) {
|
if (firstTimeInclusion) {
|
||||||
zeroBitPlanesTree = precinct.zeroBitPlanesTree;
|
zeroBitPlanesTree = precinct.zeroBitPlanesTree;
|
||||||
zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);
|
zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);
|
||||||
while (true) {
|
while (true) {
|
||||||
if (readBits(1)) {
|
if (readBits(1)) {
|
||||||
var valueReady = !zeroBitPlanesTree.nextLevel();
|
var valueReady = !zeroBitPlanesTree.nextLevel();
|
||||||
if (valueReady)
|
if (valueReady) {
|
||||||
break;
|
break;
|
||||||
} else
|
}
|
||||||
|
} else {
|
||||||
zeroBitPlanesTree.incrementValue();
|
zeroBitPlanesTree.incrementValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
codeblock.zeroBitPlanes = zeroBitPlanesTree.value;
|
codeblock.zeroBitPlanes = zeroBitPlanesTree.value;
|
||||||
}
|
}
|
||||||
var codingpasses = readCodingpasses();
|
var codingpasses = readCodingpasses();
|
||||||
while (readBits(1))
|
while (readBits(1)) {
|
||||||
codeblock.Lblock++;
|
codeblock.Lblock++;
|
||||||
|
}
|
||||||
var codingpassesLog2 = log2(codingpasses);
|
var codingpassesLog2 = log2(codingpasses);
|
||||||
// rounding down log2
|
// rounding down log2
|
||||||
var bits = ((codingpasses < (1 << codingpassesLog2)) ?
|
var bits = ((codingpasses < (1 << codingpassesLog2)) ?
|
||||||
@ -833,8 +850,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
var packetItem = queue.shift();
|
var packetItem = queue.shift();
|
||||||
var codeblock = packetItem.codeblock;
|
var codeblock = packetItem.codeblock;
|
||||||
if (!('data' in codeblock))
|
if (!('data' in codeblock)) {
|
||||||
codeblock.data = [];
|
codeblock.data = [];
|
||||||
|
}
|
||||||
codeblock.data.push({
|
codeblock.data.push({
|
||||||
data: data,
|
data: data,
|
||||||
start: offset + position,
|
start: offset + position,
|
||||||
@ -854,10 +872,12 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
var codeblock = codeblocks[i];
|
var codeblock = codeblocks[i];
|
||||||
var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
|
var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
|
||||||
var blockHeight = codeblock.tby1_ - codeblock.tby0_;
|
var blockHeight = codeblock.tby1_ - codeblock.tby0_;
|
||||||
if (blockWidth === 0 || blockHeight === 0)
|
if (blockWidth === 0 || blockHeight === 0) {
|
||||||
continue;
|
continue;
|
||||||
if (!('data' in codeblock))
|
}
|
||||||
|
if (!('data' in codeblock)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var bitModel, currentCodingpassType;
|
var bitModel, currentCodingpassType;
|
||||||
bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
|
bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
|
||||||
@ -892,8 +912,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
bitModel.runCleanupPass();
|
bitModel.runCleanupPass();
|
||||||
if (segmentationSymbolUsed)
|
if (segmentationSymbolUsed) {
|
||||||
bitModel.checkSegmentationSymbol();
|
bitModel.checkSegmentationSymbol();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
currentCodingpassType = (currentCodingpassType + 1) % 3;
|
currentCodingpassType = (currentCodingpassType + 1) % 3;
|
||||||
@ -911,8 +932,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
// not all bitplanes were decoded for reversible transformation
|
// not all bitplanes were decoded for reversible transformation
|
||||||
n += n < 0 ? n - r : n > 0 ? n + r : 0;
|
n += n < 0 ? n - r : n > 0 ? n + r : 0;
|
||||||
correction = 1 << (mb - nb);
|
correction = 1 << (mb - nb);
|
||||||
} else
|
} else {
|
||||||
correction = 1;
|
correction = 1;
|
||||||
|
}
|
||||||
coefficients[offset++] = n * correction * delta;
|
coefficients[offset++] = n * correction * delta;
|
||||||
position++;
|
position++;
|
||||||
}
|
}
|
||||||
@ -1017,14 +1039,16 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
// Section G.1 DC level shifting to unsigned component values
|
// Section G.1 DC level shifting to unsigned component values
|
||||||
for (var c = 0; c < componentsCount; c++) {
|
for (var c = 0; c < componentsCount; c++) {
|
||||||
var component = components[c];
|
var component = components[c];
|
||||||
if (component.isSigned)
|
if (component.isSigned) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var offset = 1 << (component.precision - 1);
|
var offset = 1 << (component.precision - 1);
|
||||||
var tileImage = result[c];
|
var tileImage = result[c];
|
||||||
var items = tileImage.items;
|
var items = tileImage.items;
|
||||||
for (var j = 0, jj = items.length; j < jj; j++)
|
for (var j = 0, jj = items.length; j < jj; j++) {
|
||||||
items[j] += offset;
|
items[j] += offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To simplify things: shift and clamp output to 8 bit unsigned
|
// To simplify things: shift and clamp output to 8 bit unsigned
|
||||||
@ -1129,8 +1153,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
this.levels = [];
|
this.levels = [];
|
||||||
for (var i = 0; i < levelsLength; i++) {
|
for (var i = 0; i < levelsLength; i++) {
|
||||||
var items = new Uint8Array(width * height);
|
var items = new Uint8Array(width * height);
|
||||||
for (var j = 0, jj = items.length; j < jj; j++)
|
for (var j = 0, jj = items.length; j < jj; j++) {
|
||||||
items[j] = defaultValue;
|
items[j] = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
var level = {
|
var level = {
|
||||||
width: width,
|
width: width,
|
||||||
@ -1152,8 +1177,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
level.index = index;
|
level.index = index;
|
||||||
var value = level.items[index];
|
var value = level.items[index];
|
||||||
|
|
||||||
if (value == 0xFF)
|
if (value == 0xFF) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (value > stopValue) {
|
if (value > stopValue) {
|
||||||
this.currentLevel = currentLevel;
|
this.currentLevel = currentLevel;
|
||||||
@ -1189,8 +1215,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
var value = level.items[level.index];
|
var value = level.items[level.index];
|
||||||
level.items[level.index] = 0xFF;
|
level.items[level.index] = 0xFF;
|
||||||
currentLevel--;
|
currentLevel--;
|
||||||
if (currentLevel < 0)
|
if (currentLevel < 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
this.currentLevel = currentLevel;
|
this.currentLevel = currentLevel;
|
||||||
var level = this.levels[currentLevel];
|
var level = this.levels[currentLevel];
|
||||||
@ -1316,8 +1343,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
},
|
},
|
||||||
renormD: function ArithmeticDecoder_renormD() {
|
renormD: function ArithmeticDecoder_renormD() {
|
||||||
do {
|
do {
|
||||||
if (this.ct === 0)
|
if (this.ct === 0) {
|
||||||
this.byteIn();
|
this.byteIn();
|
||||||
|
}
|
||||||
|
|
||||||
this.a <<= 1;
|
this.a <<= 1;
|
||||||
this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1);
|
this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1);
|
||||||
@ -1388,12 +1416,14 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
// Table D-2
|
// Table D-2
|
||||||
function calcSignContribution(significance0, sign0, significance1, sign1) {
|
function calcSignContribution(significance0, sign0, significance1, sign1) {
|
||||||
if (significance1) {
|
if (significance1) {
|
||||||
if (!sign1)
|
if (!sign1) {
|
||||||
return significance0 ? (!sign0 ? 1 : 0) : 1;
|
return significance0 ? (!sign0 ? 1 : 0) : 1;
|
||||||
else
|
} else {
|
||||||
return significance0 ? (!sign0 ? 0 : -1) : -1;
|
return significance0 ? (!sign0 ? 0 : -1) : -1;
|
||||||
} else
|
}
|
||||||
|
} else {
|
||||||
return significance0 ? (!sign0 ? 1 : -1) : 0;
|
return significance0 ? (!sign0 ? 1 : -1) : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Table D-3
|
// Table D-3
|
||||||
var SignContextLabels = [
|
var SignContextLabels = [
|
||||||
@ -1441,8 +1471,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
this.runLengthContext = {index: 3, mps: 0};
|
this.runLengthContext = {index: 3, mps: 0};
|
||||||
this.contexts = [];
|
this.contexts = [];
|
||||||
this.contexts.push({index: 4, mps: 0});
|
this.contexts.push({index: 4, mps: 0});
|
||||||
for (var i = 1; i <= 16; i++)
|
for (var i = 1; i <= 16; i++) {
|
||||||
this.contexts.push({index: 0, mps: 0});
|
this.contexts.push({index: 0, mps: 0});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setNeighborsSignificance:
|
setNeighborsSignificance:
|
||||||
function BitModel_setNeighborsSignificance(row, column) {
|
function BitModel_setNeighborsSignificance(row, column) {
|
||||||
@ -1450,23 +1481,29 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
var width = this.width, height = this.height;
|
var width = this.width, height = this.height;
|
||||||
var index = row * width + column;
|
var index = row * width + column;
|
||||||
if (row > 0) {
|
if (row > 0) {
|
||||||
if (column > 0)
|
if (column > 0) {
|
||||||
neighborsSignificance[index - width - 1] += 0x10;
|
neighborsSignificance[index - width - 1] += 0x10;
|
||||||
if (column + 1 < width)
|
}
|
||||||
|
if (column + 1 < width) {
|
||||||
neighborsSignificance[index - width + 1] += 0x10;
|
neighborsSignificance[index - width + 1] += 0x10;
|
||||||
|
}
|
||||||
neighborsSignificance[index - width] += 0x04;
|
neighborsSignificance[index - width] += 0x04;
|
||||||
}
|
}
|
||||||
if (row + 1 < height) {
|
if (row + 1 < height) {
|
||||||
if (column > 0)
|
if (column > 0) {
|
||||||
neighborsSignificance[index + width - 1] += 0x10;
|
neighborsSignificance[index + width - 1] += 0x10;
|
||||||
if (column + 1 < width)
|
}
|
||||||
|
if (column + 1 < width) {
|
||||||
neighborsSignificance[index + width + 1] += 0x10;
|
neighborsSignificance[index + width + 1] += 0x10;
|
||||||
|
}
|
||||||
neighborsSignificance[index + width] += 0x04;
|
neighborsSignificance[index + width] += 0x04;
|
||||||
}
|
}
|
||||||
if (column > 0)
|
if (column > 0) {
|
||||||
neighborsSignificance[index - 1] += 0x01;
|
neighborsSignificance[index - 1] += 0x01;
|
||||||
if (column + 1 < width)
|
}
|
||||||
|
if (column + 1 < width) {
|
||||||
neighborsSignificance[index + 1] += 0x01;
|
neighborsSignificance[index + 1] += 0x01;
|
||||||
|
}
|
||||||
neighborsSignificance[index] |= 0x80;
|
neighborsSignificance[index] |= 0x80;
|
||||||
},
|
},
|
||||||
runSignificancePropogationPass:
|
runSignificancePropogationPass:
|
||||||
@ -1485,16 +1522,18 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
var processedInverseMask = ~1;
|
var processedInverseMask = ~1;
|
||||||
var processedMask = 1;
|
var processedMask = 1;
|
||||||
var firstMagnitudeBitMask = 2;
|
var firstMagnitudeBitMask = 2;
|
||||||
for (var q = 0, qq = width * height; q < qq; q++)
|
for (var q = 0, qq = width * height; q < qq; q++) {
|
||||||
processingFlags[q] &= processedInverseMask;
|
processingFlags[q] &= processedInverseMask;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i0 = 0; i0 < height; i0 += 4) {
|
for (var i0 = 0; i0 < height; i0 += 4) {
|
||||||
for (var j = 0; j < width; j++) {
|
for (var j = 0; j < width; j++) {
|
||||||
var index = i0 * width + j;
|
var index = i0 * width + j;
|
||||||
for (var i1 = 0; i1 < 4; i1++, index += width) {
|
for (var i1 = 0; i1 < 4; i1++, index += width) {
|
||||||
var i = i0 + i1;
|
var i = i0 + i1;
|
||||||
if (i >= height)
|
if (i >= height) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (coefficentsMagnitude[index] || !neighborsSignificance[index])
|
if (coefficentsMagnitude[index] || !neighborsSignificance[index])
|
||||||
continue;
|
continue;
|
||||||
@ -1553,14 +1592,16 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
for (var j = 0; j < width; j++) {
|
for (var j = 0; j < width; j++) {
|
||||||
for (var i1 = 0; i1 < 4; i1++) {
|
for (var i1 = 0; i1 < 4; i1++) {
|
||||||
var i = i0 + i1;
|
var i = i0 + i1;
|
||||||
if (i >= height)
|
if (i >= height) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
var index = i * width + j;
|
var index = i * width + j;
|
||||||
|
|
||||||
// significant but not those that have just become
|
// significant but not those that have just become
|
||||||
if (!coefficentsMagnitude[index] ||
|
if (!coefficentsMagnitude[index] ||
|
||||||
(processingFlags[index] & processedMask) !== 0)
|
(processingFlags[index] & processedMask) !== 0) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var contextLabel = 16;
|
var contextLabel = 16;
|
||||||
if ((processingFlags[index] &
|
if ((processingFlags[index] &
|
||||||
@ -1637,19 +1678,22 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
processingFlags[index] |= firstMagnitudeBitMask;
|
processingFlags[index] |= firstMagnitudeBitMask;
|
||||||
|
|
||||||
index = index0;
|
index = index0;
|
||||||
for (var i2 = i0; i2 <= i; i2++, index += width)
|
for (var i2 = i0; i2 <= i; i2++, index += width) {
|
||||||
bitsDecoded[index]++;
|
bitsDecoded[index]++;
|
||||||
|
}
|
||||||
|
|
||||||
i1++;
|
i1++;
|
||||||
}
|
}
|
||||||
for (; i1 < 4; i1++, index += width) {
|
for (; i1 < 4; i1++, index += width) {
|
||||||
i = i0 + i1;
|
i = i0 + i1;
|
||||||
if (i >= height)
|
if (i >= height) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (coefficentsMagnitude[index] ||
|
if (coefficentsMagnitude[index] ||
|
||||||
(processingFlags[index] & processedMask) !== 0)
|
(processingFlags[index] & processedMask) !== 0) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var contextLabel = labels[neighborsSignificance[index]];
|
var contextLabel = labels[neighborsSignificance[index]];
|
||||||
cx = contexts[contextLabel];
|
cx = contexts[contextLabel];
|
||||||
@ -1671,8 +1715,9 @@ var JpxImage = (function JpxImageClosure() {
|
|||||||
var cx = this.uniformContext;
|
var cx = this.uniformContext;
|
||||||
var symbol = (decoder.readBit(cx) << 3) | (decoder.readBit(cx) << 2) |
|
var symbol = (decoder.readBit(cx) << 3) | (decoder.readBit(cx) << 2) |
|
||||||
(decoder.readBit(cx) << 1) | decoder.readBit(cx);
|
(decoder.readBit(cx) << 1) | decoder.readBit(cx);
|
||||||
if (symbol != 0xA)
|
if (symbol != 0xA) {
|
||||||
throw 'Invalid segmentation symbol';
|
throw 'Invalid segmentation symbol';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user