Merge pull request #21587 from calixteman/smask_in_data

Implement SmaskInData == 2 for JPX images
This commit is contained in:
Tim van der Meij 2026-07-19 13:57:41 +02:00 committed by GitHub
commit 4f7f3baed4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 61 additions and 17 deletions

View File

@ -119,7 +119,7 @@ class PDFImage {
this.jpxDecoderOptions = { this.jpxDecoderOptions = {
numComponents: 0, numComponents: 0,
isIndexedColormap: false, isIndexedColormap: false,
smaskInData: dict.has("SMaskInData"), smaskInData: dict.get("SMaskInData") >= 1,
reducePower, reducePower,
}; };
if (reducePower) { if (reducePower) {
@ -196,6 +196,25 @@ class PDFImage {
if (!this.imageMask) { if (!this.imageMask) {
let colorSpace = dict.getRaw("CS") || dict.getRaw("ColorSpace"); let colorSpace = dict.getRaw("CS") || dict.getRaw("ColorSpace");
const hasColorSpace = !!colorSpace; const hasColorSpace = !!colorSpace;
if (
this.jpxDecoderOptions?.smaskInData &&
dict.get("SMaskInData") === 2
) {
this.jpxPremultiplied = true;
if (this.matte) {
const matteColorSpace = ColorSpaceUtils.parse({
cs: hasColorSpace ? colorSpace : Name.get("DeviceRGB"),
xref,
resources: isInline ? res : null,
pdfFunctionFactory,
globalColorSpaceCache,
localColorSpaceCache,
});
this.preblendMatte = matteColorSpace.getRgb(this.matte, 0);
}
}
if (!hasColorSpace) { if (!hasColorSpace) {
if (this.jpxDecoderOptions) { if (this.jpxDecoderOptions) {
colorSpace = Name.get("DeviceRGBA"); colorSpace = Name.get("DeviceRGBA");
@ -654,22 +673,7 @@ class PDFImage {
}); });
} }
undoPreblend(buffer, width, height) { static #undoPreblend(buffer, length, matteR, matteG, matteB) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
buffer instanceof Uint8ClampedArray,
'PDFImage.undoPreblend: Unsupported "buffer" type.'
);
}
const matte = this.smask?.matte;
if (!matte) {
return;
}
const matteRgb = this.colorSpace.getRgb(matte, 0);
const matteR = matteRgb[0];
const matteG = matteRgb[1];
const matteB = matteRgb[2];
const length = width * height * 4;
for (let i = 0; i < length; i += 4) { for (let i = 0; i < length; i += 4) {
const alpha = buffer[i + 3]; const alpha = buffer[i + 3];
if (alpha === 0) { if (alpha === 0) {
@ -687,6 +691,27 @@ class PDFImage {
} }
} }
undoPreblend(buffer, width, height) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
buffer instanceof Uint8ClampedArray,
'PDFImage.undoPreblend: Unsupported "buffer" type.'
);
}
const matte = this.smask?.matte;
if (!matte) {
return;
}
const matteRgb = this.colorSpace.getRgb(matte, 0);
PDFImage.#undoPreblend(
buffer,
width * height * 4,
matteRgb[0],
matteRgb[1],
matteRgb[2]
);
}
async createImageData(forceRGBA = false, isOffscreenCanvasSupported = false) { async createImageData(forceRGBA = false, isOffscreenCanvasSupported = false) {
const drawWidth = this.drawWidth; const drawWidth = this.drawWidth;
const drawHeight = this.drawHeight; const drawHeight = this.drawHeight;
@ -717,6 +742,17 @@ class PDFImage {
{ internal: isOffscreenCanvasSupported && mustBeResized } { internal: isOffscreenCanvasSupported && mustBeResized }
)); ));
if (this.jpxPremultiplied) {
const matteRgb = this.preblendMatte;
PDFImage.#undoPreblend(
imgArray,
imgArray.length,
matteRgb?.[0] ?? 0,
matteRgb?.[1] ?? 0,
matteRgb?.[2] ?? 0
);
}
if (isOffscreenCanvasSupported) { if (isOffscreenCanvasSupported) {
if (!mustBeResized) { if (!mustBeResized) {
return this.createBitmap( return this.createBitmap(

View File

@ -945,3 +945,4 @@
!issue20504_skia.pdf !issue20504_skia.pdf
!issue21579.pdf !issue21579.pdf
!saslprep-r6.pdf !saslprep-r6.pdf
!jpx_smaskindata.pdf

Binary file not shown.

View File

@ -14479,5 +14479,12 @@
"rounds": 1, "rounds": 1,
"type": "eq", "type": "eq",
"password": "S\u00AASL\u00ADprep" "password": "S\u00AASL\u00ADprep"
},
{
"id": "jpx_smaskindata",
"file": "pdfs/jpx_smaskindata.pdf",
"md5": "f7a8628b14ca176700247f106c16812a",
"rounds": 1,
"type": "eq"
} }
] ]