diff --git a/src/core/colorspace.js b/src/core/colorspace.js index 7da5daa9df850..9f5ce00b76db5 100644 --- a/src/core/colorspace.js +++ b/src/core/colorspace.js @@ -15,6 +15,7 @@ import { assert, + FeatureTest, FormatError, info, shadow, @@ -60,6 +61,61 @@ function resizeRgbImage(src, dest, w1, h1, w2, h2, alpha01) { } } +function resizeRgbaImage(src, dest, w1, h1, w2, h2, alpha01) { + const xRatio = w1 / w2; + const yRatio = h1 / h2; + let newIndex = 0; + const xScaled = new Uint16Array(w2); + + if (alpha01 === 1) { + for (let i = 0; i < w2; i++) { + xScaled[i] = Math.floor(i * xRatio); + } + const src32 = new Uint32Array(src.buffer); + const dest32 = new Uint32Array(dest.buffer); + const rgbMask = FeatureTest.isLittleEndian ? 0x00ffffff : 0xffffff00; + for (let i = 0; i < h2; i++) { + const buf = src32.subarray(Math.floor(i * yRatio) * w1); + for (let j = 0; j < w2; j++) { + dest32[newIndex++] |= buf[xScaled[j]] & rgbMask; + } + } + } else { + const COMPONENTS = 4; + const w1Scanline = w1 * COMPONENTS; + for (let i = 0; i < w2; i++) { + xScaled[i] = Math.floor(i * xRatio) * COMPONENTS; + } + for (let i = 0; i < h2; i++) { + const buf = src.subarray(Math.floor(i * yRatio) * w1Scanline); + for (let j = 0; j < w2; j++) { + const oldIndex = xScaled[j]; + dest[newIndex++] = buf[oldIndex]; + dest[newIndex++] = buf[oldIndex + 1]; + dest[newIndex++] = buf[oldIndex + 2]; + } + } + } +} + +function copyRgbaImage(src, dest, alpha01) { + if (alpha01 === 1) { + const src32 = new Uint32Array(src.buffer); + const dest32 = new Uint32Array(dest.buffer); + const rgbMask = FeatureTest.isLittleEndian ? 0x00ffffff : 0xffffff00; + for (let i = 0, ii = src32.length; i < ii; i++) { + dest32[i] |= src32[i] & rgbMask; + } + } else { + let j = 0; + for (let i = 0, ii = src.length; i < ii; i += 4) { + dest[j++] = src[i]; + dest[j++] = src[i + 1]; + dest[j++] = src[i + 2]; + } + } +} + class ColorSpace { constructor(name, numComps) { if ( @@ -806,6 +862,38 @@ class DeviceRgbaCS extends ColorSpace { isPassthrough(bits) { return bits === 8; } + + fillRgb( + dest, + originalWidth, + originalHeight, + width, + height, + actualHeight, + bpc, + comps, + alpha01 + ) { + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + assert( + dest instanceof Uint8ClampedArray, + 'DeviceRgbaCS.fillRgb: Unsupported "dest" type.' + ); + } + if (originalHeight !== height || originalWidth !== width) { + resizeRgbaImage( + comps, + dest, + originalWidth, + originalHeight, + width, + height, + alpha01 + ); + } else { + copyRgbaImage(comps, dest, alpha01); + } + } } /** diff --git a/src/core/image.js b/src/core/image.js index 7e3e39a95a7ec..876f4e5a46fc8 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -705,7 +705,7 @@ class PDFImage { isOffscreenCanvasSupported && ImageResizer.needsToBeResized(drawWidth, drawHeight); - if (this.colorSpace.name === "DeviceRGBA") { + if (!this.smask && !this.mask && this.colorSpace.name === "DeviceRGBA") { imgData.kind = ImageKind.RGBA_32BPP; const imgArray = (imgData.data = await this.getImageBytes( originalHeight * originalWidth * 4, diff --git a/test/pdfs/issue18896.pdf.link b/test/pdfs/issue18896.pdf.link new file mode 100644 index 0000000000000..4ec102f698772 --- /dev/null +++ b/test/pdfs/issue18896.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/17365176/issue18896.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index d9f04533552a9..dcf9307fbf03c 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -10700,5 +10700,14 @@ "rounds": 1, "type": "eq", "talos": false + }, + { + "id": "issue18896", + "file": "pdfs/issue18896.pdf", + "md5": "4acb4df0f38c976dce5e841e6de1f875", + "rounds": 1, + "type": "eq", + "link": true, + "talos": false } ]