From abcc72a173d6c88f01938ef8326b63b6e12cdafb Mon Sep 17 00:00:00 2001 From: fkaelberer Date: Fri, 28 Feb 2014 10:53:09 +0100 Subject: [PATCH] Faster JBIG2 decoding --- src/core/jbig2.js | 63 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/src/core/jbig2.js b/src/core/jbig2.js index 78a5b501d..f543e5899 100755 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -368,13 +368,39 @@ var Jbig2Image = (function Jbig2ImageClosure() { 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 Int32Array(templateLength); - var templateY = new Int32Array(templateLength); + var templateX = new Int8Array(templateLength); + var templateY = new Int8Array(templateLength); + var changingTemplateEntries = []; + var reuseMask = 0, minX = 0, maxX = 0, minY = 0; + for (var 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); + } } + changingTemplateEntries = new Uint8Array(changingTemplateEntries); + var changingEntriesLength = changingTemplateEntries.length; var pseudoPixelContext = ReusedContexts[templateIndex]; var bitmap = []; @@ -382,7 +408,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { var decoder = decodingContext.decoder; var contexts = decodingContext.contextCache.getContexts('GB'); - var ltp = 0; + var ltp = 0, c, j, i0, j0, k, contextLabel = 0; for (var i = 0; i < height; i++) { if (prediction) { var sltp = decoder.readBit(contexts, pseudoPixelContext); @@ -394,18 +420,33 @@ var Jbig2Image = (function Jbig2ImageClosure() { } var row = new Uint8Array(width); bitmap.push(row); - for (var j = 0; j < width; j++) { + for (j = 0; j < width; j++) { if (useskip && skip[i][j]) { row[j] = 0; continue; } - var contextLabel = 0; - for (var k = 0; k < templateLength; k++) { - var i0 = i + templateY[k], j0 = j + templateX[k]; - if (i0 < 0 || j0 < 0 || j0 >= width) - contextLabel <<= 1; // out of bound pixel - else - contextLabel = (contextLabel << 1) | bitmap[i0][j0]; + // 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 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); + } + } else { + // compute the contextLabel from scratch + contextLabel = 0; + for (k = 0; k < templateLength; k++) { + i0 = i + templateY[k]; + j0 = j + templateX[k]; + if (i0 >= 0 && j0 >= 0 && j0 < width) { + contextLabel |= bitmap[i0][j0] << (templateLength - 1 - k); + } + } } var pixel = decoder.readBit(contexts, contextLabel); row[j] = pixel;