Merge pull request #21440 from Snuffleupagus/putBinaryImageData-convertRGBToRGBA

Use the `convertRGBToRGBA` helper with RGB images in `putBinaryImageData`
This commit is contained in:
Tim van der Meij 2026-06-14 14:20:11 +02:00 committed by GitHub
commit ed1b2f91be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 37 deletions

View File

@ -17,6 +17,10 @@ import {
CanvasNestedDependencyTracker, CanvasNestedDependencyTracker,
Dependencies, Dependencies,
} from "./canvas_dependency_tracker.js"; } from "./canvas_dependency_tracker.js";
import {
convertBlackAndWhiteToRGBA,
convertRGBToRGBA,
} from "../shared/image_utils.js";
import { import {
F32_BBOX_INIT, F32_BBOX_INIT,
FeatureTest, FeatureTest,
@ -45,7 +49,6 @@ import {
PathType, PathType,
TilingPattern, TilingPattern,
} from "./pattern_helper.js"; } from "./pattern_helper.js";
import { convertBlackAndWhiteToRGBA } from "../shared/image_utils.js";
import { MathClamp } from "../shared/math_clamp.js"; import { MathClamp } from "../shared/math_clamp.js";
// <canvas> contexts store most of the state we need natively. // <canvas> 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 // will (conceptually) put pixels past the bounds of the canvas. But
// that's ok; any such pixels are ignored. // that's ok; any such pixels are ignored.
const height = imgData.height, const { width, height, kind } = imgData;
width = imgData.width;
const partialChunkHeight = height % FULL_CHUNK_HEIGHT; const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
let srcPos = 0, let srcPos = 0;
destPos;
const src = imgData.data; const src = imgData.data;
const dest = chunkImgData.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 // There are multiple forms in which the pixel data can be passed, and
// imgData.kind tells us which one this is. // 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). // Grayscale, 1 bit per pixel (i.e. black-and-white).
for (i = 0; i < totalChunks; i++) { for (i = 0; i < totalChunks; i++) {
thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
({ srcPos } = convertBlackAndWhiteToRGBA({ ({ srcPos } = convertBlackAndWhiteToRGBA({
src, src,
srcPos, srcPos,
dest, dest,
width, width,
height: thisChunkHeight, height: i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight,
})); }));
ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); 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. // RGBA, 32-bits per pixel.
j = 0; let j = 0;
elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; let elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
for (i = 0; i < fullChunks; i++) { for (i = 0; i < fullChunks; i++) {
dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
srcPos += elemsInThisChunk; srcPos += elemsInThisChunk;
@ -380,28 +379,21 @@ function putBinaryImageData(ctx, imgData) {
ctx.putImageData(chunkImgData, 0, j); ctx.putImageData(chunkImgData, 0, j);
} }
} else if (imgData.kind === ImageKind.RGB_24BPP) { } else if (kind === ImageKind.RGB_24BPP) {
// RGB, 24-bits per pixel. // RGB, 24-bits per pixel.
thisChunkHeight = FULL_CHUNK_HEIGHT;
elemsInThisChunk = width * thisChunkHeight;
for (i = 0; i < totalChunks; i++) { for (i = 0; i < totalChunks; i++) {
if (i >= fullChunks) { ({ srcPos } = convertRGBToRGBA({
thisChunkHeight = partialChunkHeight; src,
elemsInThisChunk = width * thisChunkHeight; srcPos,
} dest: new Uint32Array(dest.buffer),
width,
destPos = 0; height: i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight,
for (j = elemsInThisChunk; j--; ) { }));
dest[destPos++] = src[srcPos++];
dest[destPos++] = src[srcPos++];
dest[destPos++] = src[srcPos++];
dest[destPos++] = 255;
}
ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
} }
} else { } 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. // Slow path: OffscreenCanvas isn't available in the worker.
const height = imgData.height, const { width, height } = imgData;
width = imgData.width;
const partialChunkHeight = height % FULL_CHUNK_HEIGHT; const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
@ -425,18 +416,14 @@ function putBinaryImageMask(ctx, imgData) {
const dest = chunkImgData.data; const dest = chunkImgData.data;
for (let i = 0; i < totalChunks; i++) { 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 // Expand the mask so it can be used by the canvas. Any required
// inversion has already been handled. // inversion has already been handled.
({ srcPos } = convertBlackAndWhiteToRGBA({ ({ srcPos } = convertBlackAndWhiteToRGBA({
src, src,
srcPos, srcPos,
dest, dest,
width, width,
height: thisChunkHeight, height: i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight,
nonBlackColor: 0, nonBlackColor: 0,
})); }));

View File

@ -139,4 +139,9 @@ function grayToRGBA(src, dest) {
} }
} }
export { convertBlackAndWhiteToRGBA, convertToRGBA, grayToRGBA }; export {
convertBlackAndWhiteToRGBA,
convertRGBToRGBA,
convertToRGBA,
grayToRGBA,
};

View File

@ -3574,6 +3574,15 @@
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
}, },
{
"id": "cmykjpeg-disable-isOffscreenCanvasSupported",
"file": "pdfs/cmykjpeg.pdf",
"md5": "85d162b48ce98503a382d96f574f70a2",
"link": false,
"rounds": 1,
"type": "eq",
"isOffscreenCanvasSupported": false
},
{ {
"id": "cmykjpeg_nowasm", "id": "cmykjpeg_nowasm",
"file": "pdfs/cmykjpeg.pdf", "file": "pdfs/cmykjpeg.pdf",