From e862c64bb88ed6bbdfafb4fb804055f9cfa4473c Mon Sep 17 00:00:00 2001 From: p01 Date: Mon, 3 Mar 2014 11:52:24 +0100 Subject: [PATCH 1/9] JBIG2 20% faster SimpleSegmentVisitor_drawBitmap Moved the switch(combinationOperator) outside the width and height loops Check bitmap[i][j] before applying the switch(combinationOperator) --- src/core/jbig2.js | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 22d8c91a1..945c382af 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -938,34 +938,42 @@ var Jbig2Image = (function Jbig2ImageClosure() { var combinationOperator = pageInfo.combinationOperatorOverride ? regionInfo.combinationOperator : pageInfo.combinationOperator; var buffer = this.buffer; - for (var i = 0; i < height; i++) { - var mask = 128 >> (regionInfo.x & 7); - var offset = (i + regionInfo.y) * rowSize + (regionInfo.x >> 3); - switch (combinationOperator) { - case 0: // OR + switch (combinationOperator) { + case 0: // OR + for (var i = 0; i < height; i++) { + var mask = 128 >> (regionInfo.x & 7); + var offset = (i + regionInfo.y) * rowSize + (regionInfo.x >> 3); for (var j = 0; j < width; j++) { - buffer[offset] |= bitmap[i][j] ? mask : 0; + if (bitmap[i][j]) { + buffer[offset] |= mask; + } mask >>= 1; if (!mask) { mask = 128; offset++; } } - break; - case 2: // XOR + } + break; + case 2: // XOR + for (var i = 0; i < height; i++) { + var mask = 128 >> (regionInfo.x & 7); + var offset = (i + regionInfo.y) * rowSize + (regionInfo.x >> 3); for (var j = 0; j < width; j++) { - buffer[offset] ^= bitmap[i][j] ? mask : 0; + if (bitmap[i][j]) { + buffer[offset] ^= mask; + } mask >>= 1; if (!mask) { mask = 128; offset++; } } - break; - default: - error('JBIG2 error: operator ' + combinationOperator + - ' is not supported'); - } + } + break; + default: + error('JBIG2 error: operator ' + combinationOperator + + ' is not supported'); } }, onImmediateGenericRegion: From 02a5f9435ac1fed58efad2a0feabd9ce1a44d3a2 Mon Sep 17 00:00:00 2001 From: p01 Date: Tue, 4 Mar 2014 17:28:53 +0100 Subject: [PATCH 2/9] More bit friendly decodeIAID and decodeInteger --- src/core/jbig2.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 945c382af..aad7d2922 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -105,7 +105,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { offset = 340; break; default: - v = v * 2 + bit; + v = ((v << 1) | bit) >>> 0; if (--toRead === 0) { state = 0; } @@ -124,12 +124,12 @@ var Jbig2Image = (function Jbig2ImageClosure() { var prev = 1; for (var i = 0; i < codeLength; i++) { var bit = decoder.readBit(contexts, prev); - prev = (prev * 2) + bit; + prev = (prev << 1) | bit; } if (codeLength < 31) { return prev & ((1 << codeLength) - 1); } - return prev - Math.pow(2, codeLength); + return prev & 0x7FFFFFFF; } // 7.3 Segment types From 2a9fe573c04d02dac89bde9ff75ef198624fbbb2 Mon Sep 17 00:00:00 2001 From: p01 Date: Fri, 7 Mar 2014 11:51:04 +0100 Subject: [PATCH 3/9] Nest JBIG2 prediction if statements together ltp can only become truthy if predition is true. --- src/core/jbig2.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index aad7d2922..111000886 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -269,10 +269,10 @@ var Jbig2Image = (function Jbig2ImageClosure() { if (prediction) { var sltp = decoder.readBit(contexts, pseudoPixelContext); ltp ^= sltp; - } - if (ltp) { - bitmap.push(bitmap[bitmap.length - 1]); // duplicate previous row - continue; + if (ltp) { + bitmap[i] = row;//bitmap[i - 1]); // duplicate previous row + continue; + } } var row = new Uint8Array(width); bitmap.push(row); From e2a4328ff26ffda170d385c58b292f5fa7df34fc Mon Sep 17 00:00:00 2001 From: p01 Date: Fri, 7 Mar 2014 12:12:45 +0100 Subject: [PATCH 4/9] Optimized bounding box tests in JBIG2's decodeBitmap --- src/core/jbig2.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 111000886..4cf95e572 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -258,13 +258,19 @@ var Jbig2Image = (function Jbig2ImageClosure() { changingTemplateEntries = new Uint8Array(changingTemplateEntries); var changingEntriesLength = changingTemplateEntries.length; + + // 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; + var pseudoPixelContext = ReusedContexts[templateIndex]; var bitmap = []; var decoder = decodingContext.decoder; var contexts = decodingContext.contextCache.getContexts('GB'); - var ltp = 0, c, j, i0, j0, k, contextLabel = 0; + var ltp = 0, c, j, i0, j0, k, contextLabel = 0, bit, shift; for (var i = 0; i < height; i++) { if (prediction) { var sltp = decoder.readBit(contexts, pseudoPixelContext); @@ -283,7 +289,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { } // Are we in the middle of a scanline, so we can reuse contextLabel // bits? - if (i + minY > 0 && j + minX >= 0 && j + maxX < width) { + 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; @@ -296,11 +302,17 @@ var Jbig2Image = (function Jbig2ImageClosure() { } else { // compute the contextLabel from scratch contextLabel = 0; - for (k = 0; k < templateLength; k++) { - i0 = i + templateY[k]; + var shift = templateLength - 1; + for (k = 0; k < templateLength; k++, shift--) { j0 = j + templateX[k]; - if (i0 >= 0 && j0 >= 0 && j0 < width) { - contextLabel |= bitmap[i0][j0] << (templateLength - 1 - k); + if (j0 >= 0 && j0 < width) { + i0 = i + templateY[k]; + if (i0 >= 0) { + bit = bitmap[i0][j0]; + if (bit) { + contextLabel |= bit << shift; + } + } } } } From 0bca56cbb236fbba01aa74db477ffcf3e53c2953 Mon Sep 17 00:00:00 2001 From: p01 Date: Fri, 7 Mar 2014 17:26:56 +0100 Subject: [PATCH 5/9] Use changingTemplateX, Y and Bit in JBIG2's decodeBitmap --- src/core/jbig2.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 4cf95e572..14ca23c9e 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -255,9 +255,17 @@ var Jbig2Image = (function Jbig2ImageClosure() { changingTemplateEntries.push(k); } } - changingTemplateEntries = new Uint8Array(changingTemplateEntries); var changingEntriesLength = changingTemplateEntries.length; + var changingTemplateX = new Int8Array(changingEntriesLength); + var changingTemplateY = new Int8Array(changingEntriesLength); + var changingTemplateBit = new Uint16Array(changingEntriesLength); + for (var c = 0; c < changingEntriesLength; c++) { + k = changingTemplateEntries[c]; + changingTemplateX[c] = template[k].x; + changingTemplateY[c] = template[k].y; + changingTemplateBit[c] = 1 << (templateLength - 1 - k); + } // Get the safe bounding box edges from the width, height, minX, maxX, minY var sbb_left = -minX; @@ -293,16 +301,19 @@ var Jbig2Image = (function Jbig2ImageClosure() { // If yes, we can just shift the bits that are reusable and only // fetch the remaining ones. contextLabel = (contextLabel << 1) & reuseMask; - for (c = 0; c < changingEntriesLength; c++) { - k = changingTemplateEntries[c]; - i0 = i + templateY[k]; - j0 = j + templateX[k]; - contextLabel |= bitmap[i0][j0] << (templateLength - 1 - k); + 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; - var shift = templateLength - 1; + shift = templateLength - 1; for (k = 0; k < templateLength; k++, shift--) { j0 = j + templateX[k]; if (j0 >= 0 && j0 < width) { From b2b3836683ec5915c65e35c64045b151c2eea541 Mon Sep 17 00:00:00 2001 From: p01 Date: Fri, 7 Mar 2014 17:29:14 +0100 Subject: [PATCH 6/9] Process only once the offset and starting mask JBIG2's drawBitmap --- src/core/jbig2.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 14ca23c9e..35f0a47ff 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -961,11 +961,13 @@ var Jbig2Image = (function Jbig2ImageClosure() { 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); switch (combinationOperator) { case 0: // OR for (var i = 0; i < height; i++) { - var mask = 128 >> (regionInfo.x & 7); - var offset = (i + regionInfo.y) * rowSize + (regionInfo.x >> 3); + var mask = mask0; + var offset = offset0; for (var j = 0; j < width; j++) { if (bitmap[i][j]) { buffer[offset] |= mask; @@ -976,12 +978,13 @@ var Jbig2Image = (function Jbig2ImageClosure() { offset++; } } + offset0 += rowSize; } break; case 2: // XOR for (var i = 0; i < height; i++) { - var mask = 128 >> (regionInfo.x & 7); - var offset = (i + regionInfo.y) * rowSize + (regionInfo.x >> 3); + var mask = mask0; + var offset = offset0; for (var j = 0; j < width; j++) { if (bitmap[i][j]) { buffer[offset] ^= mask; @@ -992,6 +995,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { offset++; } } + offset0 += rowSize; } break; default: From 840d9d40b6e7c4e3dc0b341b8f52bf68c7d3e53b Mon Sep 17 00:00:00 2001 From: p01 Date: Fri, 7 Mar 2014 17:30:24 +0100 Subject: [PATCH 7/9] Reuse the row Uint8Array in JBIG2's decodeBitmap --- src/core/jbig2.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 35f0a47ff..0b9a1546e 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -273,6 +273,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { var sbb_right = width - maxX; var pseudoPixelContext = ReusedContexts[templateIndex]; + var row = new Uint8Array(width); var bitmap = []; var decoder = decodingContext.decoder; @@ -284,11 +285,11 @@ var Jbig2Image = (function Jbig2ImageClosure() { var sltp = decoder.readBit(contexts, pseudoPixelContext); ltp ^= sltp; if (ltp) { - bitmap[i] = row;//bitmap[i - 1]); // duplicate previous row + bitmap.push(row); // duplicate previous row continue; } } - var row = new Uint8Array(width); + row = new Uint8Array(row); bitmap.push(row); for (j = 0; j < width; j++) { if (useskip && skip[i][j]) { From 2b6cb317db1e64017036bb0f7d35ee940d6133ef Mon Sep 17 00:00:00 2001 From: p01 Date: Mon, 10 Mar 2014 14:58:32 +0100 Subject: [PATCH 8/9] Optmized decodeRefinement, moving the prediction error outside the inner loop There is no need to slow down the inner loop with a test for ltp as it can only change if prediction is true in which case it only changes in the outer loop. --- src/core/jbig2.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 0b9a1546e..23d4bd54b 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -376,13 +376,13 @@ var Jbig2Image = (function Jbig2ImageClosure() { 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++) { - if (ltp) { - error('JBIG2 error: prediction is not supported'); - } var contextLabel = 0; for (var k = 0; k < codingTemplateLength; k++) { From bd9419f1efdb710cb1fa5955eeda1599f9568af4 Mon Sep 17 00:00:00 2001 From: p01 Date: Thu, 13 Mar 2014 15:09:33 +0100 Subject: [PATCH 9/9] Micro optimization of ArithmeticDecoder_readBit --- src/core/arithmetic_decoder.js | 37 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/core/arithmetic_decoder.js b/src/core/arithmetic_decoder.js index fae2edbe6..6d21cf63a 100644 --- a/src/core/arithmetic_decoder.js +++ b/src/core/arithmetic_decoder.js @@ -128,41 +128,39 @@ var ArithmeticDecoder = (function ArithmeticDecoderClosure() { var cx_index = contexts[pos] >> 1, cx_mps = contexts[pos] & 1; var qeTableIcx = QeTable[cx_index]; var qeIcx = qeTableIcx.qe; - var nmpsIcx = qeTableIcx.nmps; - var nlpsIcx = qeTableIcx.nlps; - var switchIcx = qeTableIcx.switchFlag; var d; - this.a -= qeIcx; + var a = this.a - qeIcx; if (this.chigh < qeIcx) { // exchangeLps - if (this.a < qeIcx) { - this.a = qeIcx; + if (a < qeIcx) { + a = qeIcx; d = cx_mps; - cx_index = nmpsIcx; + cx_index = qeTableIcx.nmps; } else { - this.a = qeIcx; - d = 1 - cx_mps; - if (switchIcx) { + a = qeIcx; + d = 1 ^ cx_mps; + if (qeTableIcx.switchFlag === 1) { cx_mps = d; } - cx_index = nlpsIcx; + cx_index = qeTableIcx.nlps; } } else { this.chigh -= qeIcx; - if ((this.a & 0x8000) !== 0) { + if ((a & 0x8000) !== 0) { + this.a = a; return cx_mps; } // exchangeMps - if (this.a < qeIcx) { - d = 1 - cx_mps; - if (switchIcx) { + if (a < qeIcx) { + d = 1 ^ cx_mps; + if (qeTableIcx.switchFlag === 1) { cx_mps = d; } - cx_index = nlpsIcx; + cx_index = qeTableIcx.nlps; } else { d = cx_mps; - cx_index = nmpsIcx; + cx_index = qeTableIcx.nmps; } } // C.3.3 renormD; @@ -171,11 +169,12 @@ var ArithmeticDecoder = (function ArithmeticDecoderClosure() { this.byteIn(); } - this.a <<= 1; + a <<= 1; this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1); this.clow = (this.clow << 1) & 0xFFFF; this.ct--; - } while ((this.a & 0x8000) === 0); + } while ((a & 0x8000) === 0); + this.a = a; contexts[pos] = cx_index << 1 | cx_mps; return d;