From abcc72a173d6c88f01938ef8326b63b6e12cdafb Mon Sep 17 00:00:00 2001
From: fkaelberer <o_0_o@gmx.de>
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;