From 55c85169440305d8019ed56804d145276d6cd1b0 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sat, 13 Jun 2026 00:04:09 +0200 Subject: [PATCH] Use the `convertRGBToRGBA` helper with RGB images in `putBinaryImageData` This removes a little bit of code duplication, which only exist since the `src/display/canvas.js` code pre-dates the helper function by many years. Note: Given that `OffscreenCanvas` is enabled by default there's currently not a lot of test coverage for this code-path, hence the added browser-test. --- src/display/canvas.js | 59 +++++++++++++++------------------------ src/shared/image_utils.js | 7 ++++- test/test_manifest.json | 9 ++++++ 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/display/canvas.js b/src/display/canvas.js index 9e282b58e..e1d7b3b76 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -17,6 +17,10 @@ import { CanvasNestedDependencyTracker, Dependencies, } from "./canvas_dependency_tracker.js"; +import { + convertBlackAndWhiteToRGBA, + convertRGBToRGBA, +} from "../shared/image_utils.js"; import { F32_BBOX_INIT, FeatureTest, @@ -45,7 +49,6 @@ import { PathType, TilingPattern, } from "./pattern_helper.js"; -import { convertBlackAndWhiteToRGBA } from "../shared/image_utils.js"; import { MathClamp } from "../shared/math_clamp.js"; // contexts store most of the state we need natively. @@ -333,40 +336,36 @@ function putBinaryImageData(ctx, imgData) { // will (conceptually) put pixels past the bounds of the canvas. But // that's ok; any such pixels are ignored. - const height = imgData.height, - width = imgData.width; + const { width, height, kind } = imgData; const partialChunkHeight = height % FULL_CHUNK_HEIGHT; const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); - let srcPos = 0, - destPos; + let srcPos = 0; const src = imgData.data; const dest = chunkImgData.data; - let i, j, thisChunkHeight, elemsInThisChunk; + let i; // There are multiple forms in which the pixel data can be passed, and // imgData.kind tells us which one this is. - if (imgData.kind === ImageKind.GRAYSCALE_1BPP) { + if (kind === ImageKind.GRAYSCALE_1BPP) { // Grayscale, 1 bit per pixel (i.e. black-and-white). for (i = 0; i < totalChunks; i++) { - thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; - ({ srcPos } = convertBlackAndWhiteToRGBA({ src, srcPos, dest, width, - height: thisChunkHeight, + height: i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight, })); ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); } - } else if (imgData.kind === ImageKind.RGBA_32BPP) { + } else if (kind === ImageKind.RGBA_32BPP) { // RGBA, 32-bits per pixel. - j = 0; - elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; + let j = 0; + let elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; for (i = 0; i < fullChunks; i++) { dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); srcPos += elemsInThisChunk; @@ -380,28 +379,21 @@ function putBinaryImageData(ctx, imgData) { ctx.putImageData(chunkImgData, 0, j); } - } else if (imgData.kind === ImageKind.RGB_24BPP) { + } else if (kind === ImageKind.RGB_24BPP) { // RGB, 24-bits per pixel. - thisChunkHeight = FULL_CHUNK_HEIGHT; - elemsInThisChunk = width * thisChunkHeight; for (i = 0; i < totalChunks; i++) { - if (i >= fullChunks) { - thisChunkHeight = partialChunkHeight; - elemsInThisChunk = width * thisChunkHeight; - } - - destPos = 0; - for (j = elemsInThisChunk; j--; ) { - dest[destPos++] = src[srcPos++]; - dest[destPos++] = src[srcPos++]; - dest[destPos++] = src[srcPos++]; - dest[destPos++] = 255; - } + ({ srcPos } = convertRGBToRGBA({ + src, + srcPos, + dest: new Uint32Array(dest.buffer), + width, + height: i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight, + })); ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); } } else { - throw new Error(`bad image kind: ${imgData.kind}`); + throw new Error(`bad image kind: ${kind}`); } } @@ -413,8 +405,7 @@ function putBinaryImageMask(ctx, imgData) { } // Slow path: OffscreenCanvas isn't available in the worker. - const height = imgData.height, - width = imgData.width; + const { width, height } = imgData; const partialChunkHeight = height % FULL_CHUNK_HEIGHT; const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; @@ -425,18 +416,14 @@ function putBinaryImageMask(ctx, imgData) { const dest = chunkImgData.data; for (let i = 0; i < totalChunks; i++) { - const thisChunkHeight = - i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; - // Expand the mask so it can be used by the canvas. Any required // inversion has already been handled. - ({ srcPos } = convertBlackAndWhiteToRGBA({ src, srcPos, dest, width, - height: thisChunkHeight, + height: i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight, nonBlackColor: 0, })); diff --git a/src/shared/image_utils.js b/src/shared/image_utils.js index abb3b0849..c3aa395b8 100644 --- a/src/shared/image_utils.js +++ b/src/shared/image_utils.js @@ -139,4 +139,9 @@ function grayToRGBA(src, dest) { } } -export { convertBlackAndWhiteToRGBA, convertToRGBA, grayToRGBA }; +export { + convertBlackAndWhiteToRGBA, + convertRGBToRGBA, + convertToRGBA, + grayToRGBA, +}; diff --git a/test/test_manifest.json b/test/test_manifest.json index 9314e31fc..856bec321 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -3574,6 +3574,15 @@ "rounds": 1, "type": "eq" }, + { + "id": "cmykjpeg-disable-isOffscreenCanvasSupported", + "file": "pdfs/cmykjpeg.pdf", + "md5": "85d162b48ce98503a382d96f574f70a2", + "link": false, + "rounds": 1, + "type": "eq", + "isOffscreenCanvasSupported": false + }, { "id": "cmykjpeg_nowasm", "file": "pdfs/cmykjpeg.pdf",