[Pkg-javascript-commits] [pdf.js] 24/161: Caching inlined mask images

David Prévot taffit at moszumanska.debian.org
Sat Apr 19 14:16:19 UTC 2014


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository pdf.js.

commit 257898b359e108fa373115870c89605ba270c090
Author: Yury Delendik <ydelendik at mozilla.com>
Date:   Mon Feb 24 09:59:02 2014 -0600

    Caching inlined mask images
---
 src/core/evaluator.js | 98 +++++++++++++++++++++++++++++++++++++++++----------
 src/core/parser.js    | 45 ++++++++++++++++++++++-
 src/display/canvas.js | 61 ++++++++++++++++++++++++--------
 src/shared/util.js    |  3 +-
 4 files changed, 172 insertions(+), 35 deletions(-)

diff --git a/src/core/evaluator.js b/src/core/evaluator.js
index d05f1ee..54b4a9e 100644
--- a/src/core/evaluator.js
+++ b/src/core/evaluator.js
@@ -152,10 +152,16 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
         var canTransfer = image instanceof DecodeStream;
         var inverseDecode = !!decode && decode[0] > 0;
 
-        operatorList.addOp(OPS.paintImageMaskXObject,
-          [PDFImage.createMask(imgArray, width, height, canTransfer,
-                               inverseDecode)]
-        );
+        var imgData = PDFImage.createMask(imgArray, width, height,
+                                          canTransfer, inverseDecode);
+        imgData.cached = true;
+        var args = [imgData];
+        operatorList.addOp(OPS.paintImageMaskXObject, args);
+        if (cacheKey) {
+          cache.key = cacheKey;
+          cache.fn = OPS.paintImageMaskXObject;
+          cache.args = args;
+        }
         return;
       }
 
@@ -553,8 +559,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
               args[0] = loadedName;
               break;
             case OPS.endInlineImage:
+              var cacheKey = args[0].cacheKey;
+              if (cacheKey && imageCache.key === cacheKey) {
+                operatorList.addOp(imageCache.fn, imageCache.args);
+                args = [];
+                continue;
+              }
               self.buildPaintImageXObject(resources, args[0], true,
-                                          operatorList);
+                                          operatorList, cacheKey, imageCache);
               args = [];
               continue;
             case OPS.save:
@@ -1276,7 +1288,9 @@ var OperatorList = (function OperatorListClosure() {
           case OPS.paintInlineImageXObjectGroup:
           case OPS.paintImageMaskXObject:
             var arg = argsArray[i][0]; // first param in imgData
-            transfers.push(arg.data.buffer);
+            if (!arg.cached) {
+              transfers.push(arg.data.buffer);
+            }
             break;
         }
       }
@@ -1774,6 +1788,7 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
       // searching for (save, transform, paintImageMaskXObject, restore)+
       var MIN_IMAGES_IN_MASKS_BLOCK = 10;
       var MAX_IMAGES_IN_MASKS_BLOCK = 100;
+      var MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
 
       var fnArray = context.fnArray, argsArray = context.argsArray;
       var j = context.currentOperation - 3, i = j + 4;
@@ -1781,24 +1796,69 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
 
       for (; i < ii && fnArray[i - 4] === fnArray[i]; i++) {
       }
-      var count = Math.min((i - j) >> 2, MAX_IMAGES_IN_MASKS_BLOCK);
+      var count = (i - j) >> 2;
       if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
         context.currentOperation = i - 1;
         return;
       }
-      var images = [];
-      for (var q = 0; q < count; q++) {
-        var transform = argsArray[j + (q << 2) + 1];
-        var maskParams = argsArray[j + (q << 2) + 2][0];
-        images.push({data: maskParams.data, width: maskParams.width,
-          height: maskParams.height, transform: transform});
+
+      var isSameImage = false;
+      if (argsArray[j + 1][1] === 0 && argsArray[j + 1][2] === 0) {
+        i = j + 4;
+        isSameImage = true;
+        for (var q = 1; q < count; q++, i += 4) {
+          var prevTransformArgs = argsArray[i - 3];
+          var transformArgs = argsArray[i + 1];
+          if (argsArray[i - 2][0] !== argsArray[i + 2][0] ||
+              prevTransformArgs[0] !== transformArgs[0] ||
+              prevTransformArgs[1] !== transformArgs[1] ||
+              prevTransformArgs[2] !== transformArgs[2] ||
+              prevTransformArgs[3] !== transformArgs[3]) {
+            if (q < MIN_IMAGES_IN_MASKS_BLOCK) {
+              isSameImage = false;
+            } else {
+              count = q;
+            }
+            break; // different image or transform
+          }
+        }
       }
-      // replacing queue items
-      squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectGroup);
-      argsArray.splice(j, count * 4, [images]);
 
-      context.currentOperation = j;
-      context.operationsLength -= count * 4 - 1;
+      if (isSameImage) {
+        count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);
+        var positions = new Float32Array(count * 2);
+        i = j + 1;
+        for (var q = 0; q < count; q++) {
+          var transformArgs = argsArray[i];
+          positions[(q << 1)] = transformArgs[4];
+          positions[(q << 1) + 1] = transformArgs[5];
+          i += 4;
+        }
+
+        // replacing queue items
+        squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectRepeat);
+        argsArray.splice(j, count * 4, [argsArray[j + 2][0],
+          argsArray[j + 1][0], argsArray[j + 1][3], positions]);
+
+        context.currentOperation = j;
+        context.operationsLength -= count * 4 - 1;
+      } else {
+        count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);
+        var images = [];
+        for (var q = 0; q < count; q++) {
+          var transformArgs = argsArray[j + (q << 2) + 1];
+          var maskParams = argsArray[j + (q << 2) + 2][0];
+          images.push({data: maskParams.data, width: maskParams.width,
+            height: maskParams.height, transform: transformArgs});
+        }
+
+        // replacing queue items
+        squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectGroup);
+        argsArray.splice(j, count * 4, [images]);
+
+        context.currentOperation = j;
+        context.operationsLength -= count * 4 - 1;
+      }
     });
 
   addState(InitialState,
@@ -1848,7 +1908,7 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
       var args = [argsArray[j + 2][0], argsArray[j + 1][0],
         argsArray[j + 1][3], positions];
       // replacing queue items
-      squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectRepeat);
+      squash(fnArray, j, count * 4, OPS.paintImageXObjectRepeat);
       argsArray.splice(j, count * 4, args);
 
       context.currentOperation = j;
diff --git a/src/core/parser.js b/src/core/parser.js
index 5d7aba4..0eb62c5 100644
--- a/src/core/parser.js
+++ b/src/core/parser.js
@@ -32,6 +32,11 @@ var Parser = (function ParserClosure() {
     this.lexer = lexer;
     this.allowStreams = allowStreams;
     this.xref = xref;
+    this.imageCache = {
+      length: 0,
+      adler32: 0,
+      stream: null
+    };
     this.refill();
   }
 
@@ -169,10 +174,48 @@ var Parser = (function ParserClosure() {
 
       var length = (stream.pos - 4) - startPos;
       var imageStream = stream.makeSubStream(startPos, length, dict);
-      if (cipherTransform)
+
+      // trying to cache repeat images, first we are trying to "warm up" caching
+      // using length, then comparing adler32
+      var MAX_LENGTH_TO_CACHE = 1000;
+      var cacheImage = false, adler32;
+      if (length < MAX_LENGTH_TO_CACHE && this.imageCache.length === length) {
+        var imageBytes = imageStream.getBytes();
+        imageStream.reset();
+
+        var a = 1;
+        var b = 0;
+        for (var i = 0, ii = imageBytes.length; i < ii; ++i) {
+          a = (a + (imageBytes[i] & 0xff)) % 65521;
+          b = (b + a) % 65521;
+        }
+        adler32 = (b << 16) | a;
+
+        if (this.imageCache.stream && this.imageCache.adler32 === adler32) {
+          this.buf2 = Cmd.get('EI');
+          this.shift();
+
+          this.imageCache.stream.reset();
+          return this.imageCache.stream;
+        }
+        cacheImage = true;
+      }
+      if (!cacheImage && !this.imageCache.stream) {
+        this.imageCache.length = length;
+        this.imageCache.stream = null;
+      }
+
+      if (cipherTransform) {
         imageStream = cipherTransform.createStream(imageStream, length);
+      }
+
       imageStream = this.filter(imageStream, dict, length);
       imageStream.dict = dict;
+      if (cacheImage) {
+        imageStream.cacheKey = 'inline_' + length + '_' + adler32;
+        this.imageCache.adler32 = adler32;
+        this.imageCache.stream = imageStream;
+      }
 
       this.buf2 = Cmd.get('EI');
       this.shift();
diff --git a/src/display/canvas.js b/src/display/canvas.js
index 0226b87..0ff14ea 100644
--- a/src/display/canvas.js
+++ b/src/display/canvas.js
@@ -1924,6 +1924,39 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
       this.paintInlineImageXObject(maskCanvas.canvas);
     },
 
+    paintImageMaskXObjectRepeat:
+      function CanvasGraphics_paintImageMaskXObjectRepeat(imgData, scaleX,
+                                                          scaleY, positions) {
+      var width = imgData.width;
+      var height = imgData.height;
+      var ctx = this.ctx;
+
+      var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
+      var maskCtx = maskCanvas.context;
+      maskCtx.save();
+
+      putBinaryImageMask(maskCtx, imgData);
+
+      maskCtx.globalCompositeOperation = 'source-in';
+
+      var fillColor = this.current.fillColor;
+      maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
+        fillColor.type === 'Pattern') ?
+        fillColor.getPattern(maskCtx, this) : fillColor;
+      maskCtx.fillRect(0, 0, width, height);
+
+      maskCtx.restore();
+
+      for (var i = 0, ii = positions.length; i < ii; i += 2) {
+        ctx.save();
+        ctx.transform(scaleX, 0, 0, scaleY, positions[i], positions[i + 1]);
+        ctx.scale(1, -1);
+        ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
+          0, -1, 1, 1);
+        ctx.restore();
+      }
+    },
+
     paintImageMaskXObjectGroup:
       function CanvasGraphics_paintImageMaskXObjectGroup(images) {
       var ctx = this.ctx;
@@ -1966,22 +1999,22 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
       this.paintInlineImageXObject(imgData);
     },
 
-    paintImageMaskXObjectRepeat:
-      function CanvasGraphics_paintImageMaskXObjectRepeat(objId, scaleX, scaleY,
+    paintImageXObjectRepeat:
+      function CanvasGraphics_paintImageXObjectRepeat(objId, scaleX, scaleY,
                                                           positions) {
-        var imgData = this.objs.get(objId);
-        if (!imgData) {
-          error('Dependent image isn\'t ready yet');
-        }
+      var imgData = this.objs.get(objId);
+      if (!imgData) {
+        error('Dependent image isn\'t ready yet');
+      }
 
-        var width = imgData.width;
-        var height = imgData.height;
-        var map = [];
-        for (var i = 0, ii = positions.length; i < ii; i += 2) {
-          map.push({transform: [scaleX, 0, 0, scaleY, positions[i],
-                   positions[i + 1]], x: 0, y: 0, w: width, h: height});
-        }
-        this.paintInlineImageXObjectGroup(imgData, map);
+      var width = imgData.width;
+      var height = imgData.height;
+      var map = [];
+      for (var i = 0, ii = positions.length; i < ii; i += 2) {
+        map.push({transform: [scaleX, 0, 0, scaleY, positions[i],
+                 positions[i + 1]], x: 0, y: 0, w: width, h: height});
+      }
+      this.paintInlineImageXObjectGroup(imgData, map);
     },
 
     paintInlineImageXObject:
diff --git a/src/shared/util.js b/src/shared/util.js
index 65b07a5..4803e6d 100644
--- a/src/shared/util.js
+++ b/src/shared/util.js
@@ -150,7 +150,8 @@ var OPS = PDFJS.OPS = {
   paintImageXObject: 85,
   paintInlineImageXObject: 86,
   paintInlineImageXObjectGroup: 87,
-  paintImageMaskXObjectRepeat: 88
+  paintImageXObjectRepeat: 88,
+  paintImageMaskXObjectRepeat: 89
 };
 
 // A notice for devs. These are good for things that are helpful to devs, such

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/pdf.js.git



More information about the Pkg-javascript-commits mailing list