1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-22 16:18:08 +02:00

[api-minor] Add a jpx decoder based on OpenJPEG 2.5.2

The decoder is compiled in WASM:
https://github.com/mozilla/pdf.js.openjpeg

Fixes #17289, #17061, #16485, #13051, #6365, #4648, #12213.
This commit is contained in:
Calixte Denizet 2024-04-15 10:12:05 +02:00
parent 2e94511330
commit 2e83cfbbc1
20 changed files with 172 additions and 2347 deletions

View file

@ -16,11 +16,16 @@
import { clearPatternCaches } from "./pattern.js";
import { clearPrimitiveCaches } from "./primitives.js";
import { clearUnicodeCaches } from "./unicode.js";
import { JpxImage } from "./jpx.js";
function clearGlobalCaches() {
clearPatternCaches();
clearPrimitiveCaches();
clearUnicodeCaches();
// Remove the global `JpxImage` instance, since it may hold a reference to
// the WebAssembly module.
JpxImage.cleanup();
}
export { clearGlobalCaches };

View file

@ -73,7 +73,7 @@ class DecodeStream extends BaseStream {
return this.buffer[this.pos++];
}
getBytes(length) {
getBytes(length, ignoreColorSpace = false) {
const pos = this.pos;
let end;
@ -82,7 +82,7 @@ class DecodeStream extends BaseStream {
end = pos + length;
while (!this.eof && this.bufferLength < end) {
this.readBlock();
this.readBlock(ignoreColorSpace);
}
const bufEnd = this.bufferLength;
if (end > bufEnd) {
@ -90,7 +90,7 @@ class DecodeStream extends BaseStream {
}
} else {
while (!this.eof) {
this.readBlock();
this.readBlock(ignoreColorSpace);
}
end = this.bufferLength;
}

View file

@ -104,6 +104,7 @@ class PDFImage {
localColorSpaceCache,
}) {
this.image = image;
let jpxDecode = false;
const dict = image.dict;
const filter = dict.get("F", "Filter");
@ -118,14 +119,14 @@ class PDFImage {
}
switch (filterName) {
case "JPXDecode":
const jpxImage = new JpxImage();
jpxImage.parseImageProperties(image.stream);
({
width: image.width,
height: image.height,
componentsCount: image.numComps,
bitsPerComponent: image.bitsPerComponent,
} = JpxImage.parseImageProperties(image.stream));
image.stream.reset();
image.width = jpxImage.width;
image.height = jpxImage.height;
image.bitsPerComponent = jpxImage.bitsPerComponent;
image.numComps = jpxImage.componentsCount;
jpxDecode = true;
break;
case "JBIG2Decode":
image.bitsPerComponent = 1;
@ -197,6 +198,7 @@ class PDFImage {
);
}
}
this.colorSpace = ColorSpace.parse({
cs: colorSpace,
xref,
@ -205,6 +207,10 @@ class PDFImage {
localColorSpaceCache,
});
this.numComps = this.colorSpace.numComps;
// If the jpx image has a color space then it musn't be used in order to
// be able to use the color space that comes from the pdf.
this.ignoreColorSpace = jpxDecode && this.colorSpace.name === "Indexed";
}
this.decode = dict.getArray("D", "Decode");
@ -984,7 +990,7 @@ class PDFImage {
this.image.drawHeight = drawHeight || this.height;
this.image.forceRGBA = !!forceRGBA;
this.image.forceRGB = !!forceRGB;
const imageBytes = this.image.getBytes(length);
const imageBytes = this.image.getBytes(length, this.ignoreColorSpace);
// If imageBytes came from a DecodeStream, we're safe to transfer it
// (and thus detach its underlying buffer) because it will constitute

File diff suppressed because it is too large Load diff

View file

@ -41,44 +41,12 @@ class JpxStream extends DecodeStream {
// directly insert all of its data into `this.buffer`.
}
readBlock() {
readBlock(ignoreColorSpace) {
if (this.eof) {
return;
}
const jpxImage = new JpxImage();
jpxImage.parse(this.bytes);
const width = jpxImage.width;
const height = jpxImage.height;
const componentsCount = jpxImage.componentsCount;
const tileCount = jpxImage.tiles.length;
if (tileCount === 1) {
this.buffer = jpxImage.tiles[0].items;
} else {
const data = new Uint8ClampedArray(width * height * componentsCount);
for (let k = 0; k < tileCount; k++) {
const tileComponents = jpxImage.tiles[k];
const tileWidth = tileComponents.width;
const tileHeight = tileComponents.height;
const tileLeft = tileComponents.left;
const tileTop = tileComponents.top;
const src = tileComponents.items;
let srcPosition = 0;
let dataPosition = (width * tileTop + tileLeft) * componentsCount;
const imgRowSize = width * componentsCount;
const tileRowSize = tileWidth * componentsCount;
for (let j = 0; j < tileHeight; j++) {
const rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize);
data.set(rowBytes, dataPosition);
srcPosition += tileRowSize;
dataPosition += imgRowSize;
}
}
this.buffer = data;
}
this.buffer = JpxImage.decode(this.bytes, ignoreColorSpace);
this.bufferLength = this.buffer.length;
this.eof = true;
}