mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-23 08:38:06 +02:00
Merge pull request #18047 from Snuffleupagus/issue-18042
Avoid re-parsing global images that failed decoding (issue 18042, PR 17428 follow-up)
This commit is contained in:
commit
4db843617f
6 changed files with 109 additions and 23 deletions
|
@ -793,30 +793,42 @@ class PartialEvaluator {
|
|||
args = [objId, w, h];
|
||||
operatorList.addImageOps(OPS.paintImageXObject, args, optionalContent);
|
||||
|
||||
// For large (at least 500x500) or more complex images that we'll cache
|
||||
// globally, check if the image is still cached locally on the main-thread
|
||||
// to avoid having to re-parse the image (since that can be slow).
|
||||
if (
|
||||
cacheGlobally &&
|
||||
(w * h > 250000 || dict.has("SMask") || dict.has("Mask"))
|
||||
) {
|
||||
const localLength = await this.handler.sendWithPromise("commonobj", [
|
||||
objId,
|
||||
"CopyLocalImage",
|
||||
{ imageRef },
|
||||
]);
|
||||
|
||||
if (localLength) {
|
||||
if (cacheGlobally) {
|
||||
if (this.globalImageCache.hasDecodeFailed(imageRef)) {
|
||||
this.globalImageCache.setData(imageRef, {
|
||||
objId,
|
||||
fn: OPS.paintImageXObject,
|
||||
args,
|
||||
optionalContent,
|
||||
byteSize: 0, // Temporary entry, to avoid `setData` returning early.
|
||||
byteSize: 0, // Data is `null`, since decoding failed previously.
|
||||
});
|
||||
this.globalImageCache.addByteSize(imageRef, localLength);
|
||||
|
||||
this._sendImgData(objId, /* imgData = */ null, cacheGlobally);
|
||||
return;
|
||||
}
|
||||
|
||||
// For large (at least 500x500) or more complex images that we'll cache
|
||||
// globally, check if the image is still cached locally on the main-thread
|
||||
// to avoid having to re-parse the image (since that can be slow).
|
||||
if (w * h > 250000 || dict.has("SMask") || dict.has("Mask")) {
|
||||
const localLength = await this.handler.sendWithPromise("commonobj", [
|
||||
objId,
|
||||
"CopyLocalImage",
|
||||
{ imageRef },
|
||||
]);
|
||||
|
||||
if (localLength) {
|
||||
this.globalImageCache.setData(imageRef, {
|
||||
objId,
|
||||
fn: OPS.paintImageXObject,
|
||||
args,
|
||||
optionalContent,
|
||||
byteSize: 0, // Temporary entry, to avoid `setData` returning early.
|
||||
});
|
||||
this.globalImageCache.addByteSize(imageRef, localLength);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PDFImage.buildImage({
|
||||
|
@ -846,6 +858,9 @@ class PartialEvaluator {
|
|||
.catch(reason => {
|
||||
warn(`Unable to decode image "${objId}": "${reason}".`);
|
||||
|
||||
if (imageRef) {
|
||||
this.globalImageCache.addDecodeFailed(imageRef);
|
||||
}
|
||||
return this._sendImgData(objId, /* imgData = */ null, cacheGlobally);
|
||||
});
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
unreachable,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { RefSetCache } from "./primitives.js";
|
||||
import { RefSet, RefSetCache } from "./primitives.js";
|
||||
|
||||
class BaseLocalCache {
|
||||
constructor(options) {
|
||||
|
@ -178,6 +178,8 @@ class GlobalImageCache {
|
|||
|
||||
static MAX_BYTE_SIZE = 5 * MAX_IMAGE_SIZE_TO_CACHE;
|
||||
|
||||
#decodeFailedSet = new RefSet();
|
||||
|
||||
constructor() {
|
||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||
assert(
|
||||
|
@ -189,7 +191,7 @@ class GlobalImageCache {
|
|||
this._imageCache = new RefSetCache();
|
||||
}
|
||||
|
||||
get _byteSize() {
|
||||
get #byteSize() {
|
||||
let byteSize = 0;
|
||||
for (const imageData of this._imageCache) {
|
||||
byteSize += imageData.byteSize;
|
||||
|
@ -197,11 +199,11 @@ class GlobalImageCache {
|
|||
return byteSize;
|
||||
}
|
||||
|
||||
get _cacheLimitReached() {
|
||||
get #cacheLimitReached() {
|
||||
if (this._imageCache.size < GlobalImageCache.MIN_IMAGES_TO_CACHE) {
|
||||
return false;
|
||||
}
|
||||
if (this._byteSize < GlobalImageCache.MAX_BYTE_SIZE) {
|
||||
if (this.#byteSize < GlobalImageCache.MAX_BYTE_SIZE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -218,12 +220,20 @@ class GlobalImageCache {
|
|||
if (pageIndexSet.size < GlobalImageCache.NUM_PAGES_THRESHOLD) {
|
||||
return false;
|
||||
}
|
||||
if (!this._imageCache.has(ref) && this._cacheLimitReached) {
|
||||
if (!this._imageCache.has(ref) && this.#cacheLimitReached) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
addDecodeFailed(ref) {
|
||||
this.#decodeFailedSet.put(ref);
|
||||
}
|
||||
|
||||
hasDecodeFailed(ref) {
|
||||
return this.#decodeFailedSet.has(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* PLEASE NOTE: Must be called *after* the `setData` method.
|
||||
*/
|
||||
|
@ -265,7 +275,7 @@ class GlobalImageCache {
|
|||
if (this._imageCache.has(ref)) {
|
||||
return;
|
||||
}
|
||||
if (this._cacheLimitReached) {
|
||||
if (this.#cacheLimitReached) {
|
||||
warn("GlobalImageCache.setData - cache limit reached.");
|
||||
return;
|
||||
}
|
||||
|
@ -274,6 +284,7 @@ class GlobalImageCache {
|
|||
|
||||
clear(onlyData = false) {
|
||||
if (!onlyData) {
|
||||
this.#decodeFailedSet.clear();
|
||||
this._refCache.clear();
|
||||
}
|
||||
this._imageCache.clear();
|
||||
|
|
|
@ -2783,7 +2783,7 @@ class WorkerTransport {
|
|||
|
||||
for (const pageProxy of this.#pageCache.values()) {
|
||||
for (const [, data] of pageProxy.objs) {
|
||||
if (data.ref !== imageRef) {
|
||||
if (data?.ref !== imageRef) {
|
||||
continue;
|
||||
}
|
||||
if (!data.dataLen) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue