Downscale jpeg2000 images, if needed, while decoding them
It fixes #19517.
This commit is contained in:
parent
06f44916c8
commit
ac925f4f1b
3
external/openjpeg/openjpeg.js
vendored
3
external/openjpeg/openjpeg.js
vendored
File diff suppressed because one or more lines are too long
BIN
external/openjpeg/openjpeg.wasm
vendored
BIN
external/openjpeg/openjpeg.wasm
vendored
Binary file not shown.
19
external/openjpeg/openjpeg_nowasm_fallback.js
vendored
19
external/openjpeg/openjpeg_nowasm_fallback.js
vendored
File diff suppressed because one or more lines are too long
@ -112,11 +112,22 @@ class PDFImage {
|
||||
bitsPerComponent: image.bitsPerComponent,
|
||||
} = JpxImage.parseImageProperties(image.stream));
|
||||
image.stream.reset();
|
||||
const reducePower = ImageResizer.getReducePowerForJPX(
|
||||
image.width,
|
||||
image.height,
|
||||
image.numComps
|
||||
);
|
||||
this.jpxDecoderOptions = {
|
||||
numComponents: 0,
|
||||
isIndexedColormap: false,
|
||||
smaskInData: dict.has("SMaskInData"),
|
||||
reducePower,
|
||||
};
|
||||
if (reducePower) {
|
||||
const factor = 2 ** reducePower;
|
||||
image.width = Math.ceil(image.width / factor);
|
||||
image.height = Math.ceil(image.height / factor);
|
||||
}
|
||||
break;
|
||||
case "JBIG2Decode":
|
||||
image.bitsPerComponent = 1;
|
||||
|
||||
@ -96,6 +96,30 @@ class ImageResizer {
|
||||
return area > maxArea;
|
||||
}
|
||||
|
||||
static getReducePowerForJPX(width, height, componentsCount) {
|
||||
const area = width * height;
|
||||
// The maximum memory we've in the wasm runtime is 2GB.
|
||||
// Each component is 4 bytes and we can't allocate all the memory just for
|
||||
// the buffers so we limit the size to 1GB / (componentsCount * 4).
|
||||
// We could use more than 2GB by setting MAXIMUM_MEMORY but it would take
|
||||
// too much time to decode a big image.
|
||||
const maxJPXArea = 2 ** 30 / (componentsCount * 4);
|
||||
if (!this.needsToBeResized(width, height)) {
|
||||
if (area > maxJPXArea) {
|
||||
// The image is too large, we need to rescale it.
|
||||
return Math.ceil(Math.log2(area / maxJPXArea));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const { MAX_DIM, MAX_AREA } = this;
|
||||
const minFactor = Math.max(
|
||||
width / MAX_DIM,
|
||||
height / MAX_DIM,
|
||||
Math.sqrt(area / Math.min(maxJPXArea, MAX_AREA))
|
||||
);
|
||||
return Math.ceil(Math.log2(minFactor));
|
||||
}
|
||||
|
||||
static get MAX_DIM() {
|
||||
return shadow(
|
||||
this,
|
||||
|
||||
@ -92,7 +92,12 @@ class JpxImage {
|
||||
|
||||
static async decode(
|
||||
bytes,
|
||||
{ numComponents = 4, isIndexedColormap = false, smaskInData = false } = {}
|
||||
{
|
||||
numComponents = 4,
|
||||
isIndexedColormap = false,
|
||||
smaskInData = false,
|
||||
reducePower = 0,
|
||||
} = {}
|
||||
) {
|
||||
if (!this.#modulePromise) {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
@ -119,13 +124,14 @@ class JpxImage {
|
||||
try {
|
||||
const size = bytes.length;
|
||||
ptr = module._malloc(size);
|
||||
module.HEAPU8.set(bytes, ptr);
|
||||
module.writeArrayToMemory(bytes, ptr);
|
||||
const ret = module._jp2_decode(
|
||||
ptr,
|
||||
size,
|
||||
numComponents > 0 ? numComponents : 0,
|
||||
!!isIndexedColormap,
|
||||
!!smaskInData
|
||||
!!smaskInData,
|
||||
reducePower
|
||||
);
|
||||
if (ret) {
|
||||
const { errorMessages } = module;
|
||||
|
||||
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -724,3 +724,4 @@
|
||||
!issue16742.pdf
|
||||
!chrome-text-selection-markedContent.pdf
|
||||
!bug1963407.pdf
|
||||
!issue19517.pdf
|
||||
|
||||
BIN
test/pdfs/issue19517.pdf
Executable file
BIN
test/pdfs/issue19517.pdf
Executable file
Binary file not shown.
@ -12107,5 +12107,12 @@
|
||||
"value": "Hello World"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "issue19517",
|
||||
"file": "pdfs/issue19517.pdf",
|
||||
"md5": "2abe67c8b34522feb6b85d252dde9d3e",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
}
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user