Add a temporary function-cache in AlternateCS.prototype.getRgbBuffer

This supplements, rather than replaces, the existing caching in `PDFFunction.constructPostScript` since that one still makes sense given that functions are cached on the page-level.
Using an additional cache helps improve performance because:

 - There are many fewer function calls, and overall less parsing this way.

 - For the common case of `Uint8Array`-data we're able to use integer cache-keys, which is a lot faster than string concatenation.

This significantly improves performance of the `pr5134` test-case with `isEvalSupported = false` set, and testing locally in the viewer:

 - With the `master` branch and `isEvalSupported = true`, page 2 renders in approx. 340 milliseconds.

 - With the `master` branch and `isEvalSupported = false`, page 2 renders in approx. 1200 milliseconds.

 - With this patch and `isEvalSupported = false`, page 2 renders in approx. 380 milliseconds.

While this is obviously still slower, the difference is now small enough that it shouldn't be too much of an issue in practice (compare with PR 18070) and the `pr5134` test-case is an especially "bad" one w.r.t. its PostScript function use.
This commit is contained in:
Jonas Jenwald 2025-02-06 09:09:15 +01:00
parent d008452e80
commit f7ec74c474
4 changed files with 42 additions and 11 deletions

View File

@ -379,24 +379,46 @@ class AlternateCS extends ColorSpace {
: new Uint8ClampedArray(baseNumComps * count);
const numComps = this.numComps;
const tintCache = new Map();
// Use an integer cache-key when possible, since that's a lot faster than
// string concatenation.
const int32Key =
numComps <= 4 &&
(src instanceof Uint8Array || src instanceof Uint8ClampedArray);
const scaled = new Float32Array(numComps);
const tinted = new Float32Array(baseNumComps);
let i, j;
let i, j, key, val;
for (i = 0; i < count; i++) {
key = int32Key ? 0 : "";
for (j = 0; j < numComps; j++) {
scaled[j] = src[srcOffset++] * scale;
val = src[srcOffset++];
scaled[j] = val * scale;
key += int32Key ? val << (8 * j) : val + "_";
}
tintFn(scaled, 0, tinted, 0);
if (usesZeroToOneRange) {
for (j = 0; j < baseNumComps; j++) {
baseBuf[pos++] = tinted[j] * 255;
}
} else {
base.getRgbItem(tinted, 0, baseBuf, pos);
const cached = tintCache.get(key);
if (cached) {
baseBuf.set(cached, pos);
pos += baseNumComps;
} else {
tintFn(scaled, 0, tinted, 0);
if (usesZeroToOneRange) {
for (j = 0; j < baseNumComps; j++) {
baseBuf[pos++] = tinted[j] * 255;
}
} else {
base.getRgbItem(tinted, 0, baseBuf, pos);
pos += baseNumComps;
}
tintCache.set(key, baseBuf.slice(pos - baseNumComps, pos));
}
}
tintCache.clear();
if (!isPassthrough) {
base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01);

View File

@ -392,12 +392,11 @@ class PDFFunction {
// seen in our tests.
const MAX_CACHE_SIZE = 2048 * 4;
let cache_available = MAX_CACHE_SIZE;
const tmpBuf = new Float32Array(numInputs);
const input = new Float32Array(numInputs);
return function constructPostScriptFn(src, srcOffset, dest, destOffset) {
let i, value;
let key = "";
const input = tmpBuf;
for (i = 0; i < numInputs; i++) {
value = src[srcOffset + i];
input[i] = value;

View File

@ -0,0 +1 @@
https://web.archive.org/web/20140211020222/http://www.coachusa.com/CoachUsaAssets/files/97/route45.pdf

View File

@ -2254,6 +2254,15 @@
"lastPage": 1,
"type": "eq"
},
{
"id": "pr5134",
"file": "pdfs/pr5134.pdf",
"md5": "6a701a163472e071a2519348b55cbac1",
"rounds": 1,
"link": true,
"firstPage": 2,
"type": "eq"
},
{
"id": "issue5599",
"file": "pdfs/issue5599.pdf",