Merge pull request #12458 from Snuffleupagus/LocalTilingPatternCache

Add local caching of TilingPatterns in `PartialEvaluator.getOperatorList` (issue 2765 and 8473)
This commit is contained in:
Tim van der Meij 2020-10-08 22:47:13 +02:00 committed by GitHub
commit e59a90d8e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 69 deletions

View File

@ -81,6 +81,7 @@ import {
LocalColorSpaceCache, LocalColorSpaceCache,
LocalGStateCache, LocalGStateCache,
LocalImageCache, LocalImageCache,
LocalTilingPatternCache,
} from "./image_utils.js"; } from "./image_utils.js";
import { bidi } from "./bidi.js"; import { bidi } from "./bidi.js";
import { ColorSpace } from "./colorspace.js"; import { ColorSpace } from "./colorspace.js";
@ -716,12 +717,14 @@ class PartialEvaluator {
handleTilingType( handleTilingType(
fn, fn,
args, color,
resources, resources,
pattern, pattern,
patternDict, patternDict,
operatorList, operatorList,
task task,
cacheKey,
localTilingPatternCache
) { ) {
// Create an IR of the pattern code. // Create an IR of the pattern code.
const tilingOpList = new OperatorList(); const tilingOpList = new OperatorList();
@ -739,38 +742,39 @@ class PartialEvaluator {
operatorList: tilingOpList, operatorList: tilingOpList,
}) })
.then(function () { .then(function () {
return getTilingPatternIR( const operatorListIR = tilingOpList.getIR();
{ const tilingPatternIR = getTilingPatternIR(
fnArray: tilingOpList.fnArray, operatorListIR,
argsArray: tilingOpList.argsArray,
},
patternDict, patternDict,
args color
); );
}) // Add the dependencies to the parent operator list so they are
.then( // resolved before the sub operator list is executed synchronously.
function (tilingPatternIR) { operatorList.addDependencies(tilingOpList.dependencies);
// Add the dependencies to the parent operator list so they are operatorList.addOp(fn, tilingPatternIR);
// resolved before the sub operator list is executed synchronously.
operatorList.addDependencies(tilingOpList.dependencies); if (cacheKey) {
operatorList.addOp(fn, tilingPatternIR); localTilingPatternCache.set(cacheKey, patternDict.objId, {
}, operatorListIR,
reason => { dict: patternDict,
if (reason instanceof AbortException) { });
return;
}
if (this.options.ignoreErrors) {
// Error(s) in the TilingPattern -- sending unsupported feature
// notification and allow rendering to continue.
this.handler.send("UnsupportedFeature", {
featureId: UNSUPPORTED_FEATURES.errorTilingPattern,
});
warn(`handleTilingType - ignoring pattern: "${reason}".`);
return;
}
throw reason;
} }
); })
.catch(reason => {
if (reason instanceof AbortException) {
return;
}
if (this.options.ignoreErrors) {
// Error(s) in the TilingPattern -- sending unsupported feature
// notification and allow rendering to continue.
this.handler.send("UnsupportedFeature", {
featureId: UNSUPPORTED_FEATURES.errorTilingPattern,
});
warn(`handleTilingType - ignoring pattern: "${reason}".`);
return;
}
throw reason;
});
} }
handleSetFont(resources, fontArgs, fontRef, operatorList, task, state) { handleSetFont(resources, fontArgs, fontRef, operatorList, task, state) {
@ -1221,7 +1225,7 @@ class PartialEvaluator {
}); });
} }
async handleColorN( handleColorN(
operatorList, operatorList,
fn, fn,
args, args,
@ -1229,43 +1233,70 @@ class PartialEvaluator {
patterns, patterns,
resources, resources,
task, task,
localColorSpaceCache localColorSpaceCache,
localTilingPatternCache
) { ) {
// compile tiling patterns // compile tiling patterns
var patternName = args[args.length - 1]; const patternName = args[args.length - 1];
// SCN/scn applies patterns along with normal colors // SCN/scn applies patterns along with normal colors
var pattern; if (patternName instanceof Name) {
if (isName(patternName) && (pattern = patterns.get(patternName.name))) { const localTilingPattern = localTilingPatternCache.getByName(patternName);
var dict = isStream(pattern) ? pattern.dict : pattern; if (localTilingPattern) {
var typeNum = dict.get("PatternType"); try {
const color = cs.base ? cs.base.getRgb(args, 0) : null;
if (typeNum === PatternType.TILING) { const tilingPatternIR = getTilingPatternIR(
var color = cs.base ? cs.base.getRgb(args, 0) : null; localTilingPattern.operatorListIR,
return this.handleTilingType( localTilingPattern.dict,
fn, color
color, );
resources, operatorList.addOp(fn, tilingPatternIR);
pattern, return undefined;
dict, } catch (ex) {
operatorList, if (ex instanceof MissingDataException) {
task throw ex;
); }
} else if (typeNum === PatternType.SHADING) { // Handle any errors during normal TilingPattern parsing.
var shading = dict.get("Shading"); }
var matrix = dict.getArray("Matrix"); }
pattern = Pattern.parseShading( // TODO: Attempt to lookup cached TilingPatterns by reference as well,
shading, // if and only if there are PDF documents where doing so would
matrix, // significantly improve performance.
this.xref,
resources, let pattern = patterns.get(patternName.name);
this.handler, if (pattern) {
this._pdfFunctionFactory, var dict = isStream(pattern) ? pattern.dict : pattern;
localColorSpaceCache var typeNum = dict.get("PatternType");
);
operatorList.addOp(fn, pattern.getIR()); if (typeNum === PatternType.TILING) {
return undefined; const color = cs.base ? cs.base.getRgb(args, 0) : null;
return this.handleTilingType(
fn,
color,
resources,
pattern,
dict,
operatorList,
task,
patternName,
localTilingPatternCache
);
} else if (typeNum === PatternType.SHADING) {
var shading = dict.get("Shading");
var matrix = dict.getArray("Matrix");
pattern = Pattern.parseShading(
shading,
matrix,
this.xref,
resources,
this.handler,
this._pdfFunctionFactory,
localColorSpaceCache
);
operatorList.addOp(fn, pattern.getIR());
return undefined;
}
throw new FormatError(`Unknown PatternType: ${typeNum}`);
} }
throw new FormatError(`Unknown PatternType: ${typeNum}`);
} }
throw new FormatError(`Unknown PatternName: ${patternName}`); throw new FormatError(`Unknown PatternName: ${patternName}`);
} }
@ -1349,6 +1380,7 @@ class PartialEvaluator {
const localImageCache = new LocalImageCache(); const localImageCache = new LocalImageCache();
const localColorSpaceCache = new LocalColorSpaceCache(); const localColorSpaceCache = new LocalColorSpaceCache();
const localGStateCache = new LocalGStateCache(); const localGStateCache = new LocalGStateCache();
const localTilingPatternCache = new LocalTilingPatternCache();
var xobjs = resources.get("XObject") || Dict.empty; var xobjs = resources.get("XObject") || Dict.empty;
var patterns = resources.get("Pattern") || Dict.empty; var patterns = resources.get("Pattern") || Dict.empty;
@ -1704,7 +1736,8 @@ class PartialEvaluator {
patterns, patterns,
resources, resources,
task, task,
localColorSpaceCache localColorSpaceCache,
localTilingPatternCache
) )
); );
return; return;
@ -1724,7 +1757,8 @@ class PartialEvaluator {
patterns, patterns,
resources, resources,
task, task,
localColorSpaceCache localColorSpaceCache,
localTilingPatternCache
) )
); );
return; return;

View File

@ -133,6 +133,29 @@ class LocalGStateCache extends BaseLocalCache {
} }
} }
class LocalTilingPatternCache extends BaseLocalCache {
set(name, ref = null, data) {
if (!name) {
throw new Error(
'LocalTilingPatternCache.set - expected "name" argument.'
);
}
if (ref) {
if (this._imageCache.has(ref)) {
return;
}
this._nameRefMap.set(name, ref);
this._imageCache.put(ref, data);
return;
}
// name
if (this._imageMap.has(name)) {
return;
}
this._imageMap.set(name, data);
}
}
class GlobalImageCache { class GlobalImageCache {
static get NUM_PAGES_THRESHOLD() { static get NUM_PAGES_THRESHOLD() {
return shadow(this, "NUM_PAGES_THRESHOLD", 2); return shadow(this, "NUM_PAGES_THRESHOLD", 2);
@ -231,5 +254,6 @@ export {
LocalColorSpaceCache, LocalColorSpaceCache,
LocalFunctionCache, LocalFunctionCache,
LocalGStateCache, LocalGStateCache,
LocalTilingPatternCache,
GlobalImageCache, GlobalImageCache,
}; };

View File

@ -967,7 +967,7 @@ Shadings.Dummy = (function DummyClosure() {
return Dummy; return Dummy;
})(); })();
function getTilingPatternIR(operatorList, dict, args) { function getTilingPatternIR(operatorList, dict, color) {
const matrix = dict.getArray("Matrix"); const matrix = dict.getArray("Matrix");
const bbox = Util.normalizeRect(dict.getArray("BBox")); const bbox = Util.normalizeRect(dict.getArray("BBox"));
const xstep = dict.get("XStep"); const xstep = dict.get("XStep");
@ -983,7 +983,7 @@ function getTilingPatternIR(operatorList, dict, args) {
return [ return [
"TilingPattern", "TilingPattern",
args, color,
operatorList, operatorList,
matrix, matrix,
bbox, bbox,