Merge pull request #21646 from calixteman/update/pdf.js.qcms

Update qcms wrapper
This commit is contained in:
Tim van der Meij 2026-07-28 20:25:15 +02:00 committed by GitHub
commit 01ba4c476d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 77 additions and 84 deletions

45
external/qcms/qcms.js vendored
View File

@ -1,5 +1,5 @@
/* THIS FILE IS GENERATED - DO NOT EDIT */
import { copy_result, copy_rgb, make_cssRGB } from './qcms_utils.js';
import { copy_result } from './qcms_utils.js';
/**
@ -25,16 +25,20 @@ export const Intent = Object.freeze({
});
/**
* Converts `src` and hands the result to `copy_result`, laid out as RGB, or as
* RGBA with an opaque alpha when `add_alpha` is set.
*
* # Safety
*
* This function is called directly from JavaScript.
* @param {number} transformer
* @param {Uint8Array} src
* @param {boolean} add_alpha
*/
export function qcms_convert_array(transformer, src) {
export function qcms_convert_array(transformer, src, add_alpha) {
const ptr0 = passArray8ToWasm0(src, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.qcms_convert_array(transformer, ptr0, len0);
wasm.qcms_convert_array(transformer, ptr0, len0, add_alpha);
}
/**
@ -46,10 +50,11 @@ export function qcms_convert_array(transformer, src) {
* @param {number} src2
* @param {number} src3
* @param {number} src4
* @param {boolean} css
* @returns {number}
*/
export function qcms_convert_four(transformer, src1, src2, src3, src4, css) {
wasm.qcms_convert_four(transformer, src1, src2, src3, src4, css);
export function qcms_convert_four(transformer, src1, src2, src3, src4) {
const ret = wasm.qcms_convert_four(transformer, src1, src2, src3, src4);
return ret >>> 0;
}
/**
@ -58,10 +63,11 @@ export function qcms_convert_four(transformer, src1, src2, src3, src4, css) {
* This function is called directly from JavaScript.
* @param {number} transformer
* @param {number} src
* @param {boolean} css
* @returns {number}
*/
export function qcms_convert_one(transformer, src, css) {
wasm.qcms_convert_one(transformer, src, css);
export function qcms_convert_one(transformer, src) {
const ret = wasm.qcms_convert_one(transformer, src);
return ret >>> 0;
}
/**
@ -72,10 +78,11 @@ export function qcms_convert_one(transformer, src, css) {
* @param {number} src1
* @param {number} src2
* @param {number} src3
* @param {boolean} css
* @returns {number}
*/
export function qcms_convert_three(transformer, src1, src2, src3, css) {
wasm.qcms_convert_three(transformer, src1, src2, src3, css);
export function qcms_convert_three(transformer, src1, src2, src3) {
const ret = wasm.qcms_convert_three(transformer, src1, src2, src3);
return ret >>> 0;
}
/**
@ -106,18 +113,12 @@ export function qcms_transformer_from_memory(mem, in_type, intent) {
function __wbg_get_imports() {
const import0 = {
__proto__: null,
__wbg___wbindgen_throw_6b64449b9b9ed33c: function(arg0, arg1) {
__wbg___wbindgen_throw_344f42d3211c4765: function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
},
__wbg_copy_result_0d15f3bf9d9012ae: function(arg0, arg1) {
copy_result(arg0 >>> 0, arg1 >>> 0);
},
__wbg_copy_rgb_0106d9d9464fce43: function(arg0) {
copy_rgb(arg0 >>> 0);
},
__wbg_make_cssRGB_8e24b34f71f5363e: function(arg0) {
make_cssRGB(arg0 >>> 0);
},
__wbindgen_init_externref_table: function() {
const table = wasm.__wbindgen_externrefs;
const offset = table.grow(4);
@ -135,8 +136,7 @@ function __wbg_get_imports() {
}
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return decodeText(ptr, len);
return decodeText(ptr >>> 0, len);
}
let cachedUint8ArrayMemory0 = null;
@ -170,8 +170,9 @@ function decodeText(ptr, len) {
let WASM_VECTOR_LEN = 0;
let wasmModule, wasm;
let wasmModule, wasmInstance, wasm;
function __wbg_finalize_init(instance, module) {
wasmInstance = instance;
wasm = instance.exports;
wasmModule = module;
cachedUint8ArrayMemory0 = null;

Binary file not shown.

View File

@ -13,22 +13,28 @@
* limitations under the License.
*/
// Alpha lives in the high byte of a pixel on a little-endian host and in the
// low byte on a big-endian one.
const ALPHA_MASK =
new Uint8Array(new Uint32Array([1]).buffer)[0] === 1 ? 0xff000000 : 0x000000ff;
const RGB_MASK = ~ALPHA_MASK;
class QCMS {
static #memoryArray = null;
static _memory = null;
static _mustAddAlpha = false;
// Where the next `qcms_convert_array` result should land.
static _destBuffer = null;
static _destOffset = 0;
static _destLength = 0;
static _cssColor = "";
static _makeHexColor = null;
// Set when the destination is RGBA and its alpha channel already holds
// something worth keeping, which is the case whenever the image has an
// /SMask: `fillOpacity` has run by then. The Wasm side does not know about
// that, so it always fills alpha in, and the bytes are merged here instead of
// copied wholesale.
static _keepAlpha = false;
static get _memoryArray() {
const array = this.#memoryArray;
@ -42,43 +48,31 @@ class QCMS {
function copy_result(ptr, len) {
// This function is called from the wasm module (it's an external
// "C" function). Its goal is to copy the result from the wasm memory
// to the destination buffer without any intermediate copies.
const { _mustAddAlpha, _destBuffer, _destOffset, _destLength, _memoryArray } =
QCMS;
if (len === _destLength) {
// to the destination buffer without any intermediate copies. The wasm side
// has already laid the result out the way the caller asked for it, so this is
// one bulk copy unless the destination's alpha has to survive.
const { _destBuffer, _destOffset, _keepAlpha, _memoryArray } = QCMS;
if (!_keepAlpha) {
_destBuffer.set(_memoryArray.subarray(ptr, ptr + len), _destOffset);
return;
}
if (_mustAddAlpha) {
for (let i = ptr, ii = ptr + len, j = _destOffset; i < ii; i += 3, j += 4) {
_destBuffer[j] = _memoryArray[i];
_destBuffer[j + 1] = _memoryArray[i + 1];
_destBuffer[j + 2] = _memoryArray[i + 2];
_destBuffer[j + 3] = 255;
}
} else {
for (let i = ptr, ii = ptr + len, j = _destOffset; i < ii; i += 3, j += 4) {
_destBuffer[j] = _memoryArray[i];
_destBuffer[j + 1] = _memoryArray[i + 1];
_destBuffer[j + 2] = _memoryArray[i + 2];
const count = len >> 2;
const destStart = _destBuffer.byteOffset + _destOffset;
if (((destStart | ptr) & 3) === 0) {
// Both sides are pixel-aligned, so RGB can be merged a whole pixel at a
// time. `len` is a multiple of 4 here: the wasm side wrote RGBA.
const dest32 = new Uint32Array(_destBuffer.buffer, destStart, count);
const src32 = new Uint32Array(QCMS._memory.buffer, ptr, count);
for (let i = 0; i < count; i++) {
dest32[i] = (dest32[i] & ALPHA_MASK) | (src32[i] & RGB_MASK);
}
return;
}
for (let i = ptr, ii = ptr + len, j = _destOffset; i < ii; i += 4, j += 4) {
_destBuffer[j] = _memoryArray[i];
_destBuffer[j + 1] = _memoryArray[i + 1];
_destBuffer[j + 2] = _memoryArray[i + 2];
}
}
function copy_rgb(ptr) {
const { _destBuffer, _destOffset, _memoryArray } = QCMS;
_destBuffer[_destOffset] = _memoryArray[ptr];
_destBuffer[_destOffset + 1] = _memoryArray[ptr + 1];
_destBuffer[_destOffset + 2] = _memoryArray[ptr + 2];
}
function make_cssRGB(ptr) {
const { _memoryArray } = QCMS;
QCMS._cssColor = QCMS._makeHexColor(
_memoryArray[ptr],
_memoryArray[ptr + 1],
_memoryArray[ptr + 2]
);
}
export { copy_result, copy_rgb, make_cssRGB, QCMS };
export { copy_result, QCMS };

View File

@ -61,30 +61,28 @@ class IccColorSpace extends ColorSpace {
switch (numComps) {
case 1:
inType = DataType.Gray8;
this.#convertPixel = (src, srcOffset, css) =>
qcms_convert_one(this.#transformer, src[srcOffset] * 255, css);
this.#convertPixel = (src, srcOffset) =>
qcms_convert_one(this.#transformer, src[srcOffset] * 255);
break;
case 3:
inType = DataType.RGB8;
this.#convertPixel = (src, srcOffset, css) =>
this.#convertPixel = (src, srcOffset) =>
qcms_convert_three(
this.#transformer,
src[srcOffset] * 255,
src[srcOffset + 1] * 255,
src[srcOffset + 2] * 255,
css
src[srcOffset + 2] * 255
);
break;
case 4:
inType = DataType.CMYK;
this.#convertPixel = (src, srcOffset, css) =>
this.#convertPixel = (src, srcOffset) =>
qcms_convert_four(
this.#transformer,
src[srcOffset] * 255,
src[srcOffset + 1] * 255,
src[srcOffset + 2] * 255,
src[srcOffset + 3] * 255,
css
src[srcOffset + 3] * 255
);
break;
default:
@ -105,16 +103,15 @@ class IccColorSpace extends ColorSpace {
}
getRgbHex(src, srcOffset) {
this.#convertPixel(src, srcOffset, /* css */ true);
return QCMS._cssColor;
const color = this.#convertPixel(src, srcOffset);
return Util.makeHexColor(color >> 16, (color >> 8) & 0xff, color & 0xff);
}
getRgbItem(src, srcOffset, dest, destOffset) {
QCMS._destBuffer = dest;
QCMS._destOffset = destOffset;
QCMS._destLength = 3;
this.#convertPixel(src, srcOffset, /* css */ false);
QCMS._destBuffer = null;
const color = this.#convertPixel(src, srcOffset);
dest[destOffset] = color >> 16;
dest[destOffset + 1] = (color >> 8) & 0xff;
dest[destOffset + 2] = color & 0xff;
}
getRgbItems(src, count, dest, destOffset, alpha01) {
@ -127,8 +124,10 @@ class IccColorSpace extends ColorSpace {
}
QCMS._destBuffer = dest;
QCMS._destOffset = destOffset;
QCMS._destLength = count * (3 + alpha01);
qcms_convert_array(this.#transformer, scaled);
// `scaled` is freshly allocated, so unlike in getRgbBuffer it can never
// alias `dest`: an RGBA destination always has an alpha channel to keep.
QCMS._keepAlpha = alpha01 === 1;
qcms_convert_array(this.#transformer, scaled, alpha01 === 1);
QCMS._destBuffer = null;
}
@ -140,12 +139,12 @@ class IccColorSpace extends ColorSpace {
src[i] *= scale;
}
}
QCMS._mustAddAlpha = alpha01 && dest.buffer === src.buffer;
QCMS._destBuffer = dest;
QCMS._destOffset = destOffset;
QCMS._destLength = count * (3 + alpha01);
qcms_convert_array(this.#transformer, src);
QCMS._mustAddAlpha = false;
// The wasm side always fills alpha in, so say when the destination's own
// alpha must survive: an /SMask has been decoded into it by now.
QCMS._keepAlpha = alpha01 === 1 && dest.buffer !== src.buffer;
qcms_convert_array(this.#transformer, src, alpha01 === 1);
QCMS._destBuffer = null;
}
@ -172,7 +171,6 @@ class IccColorSpace extends ColorSpace {
});
isUsable = !!this._module;
QCMS._memory = this._module.memory;
QCMS._makeHexColor = Util.makeHexColor.bind(Util);
} catch (e) {
warn(`ICCBased color space: "${e}".`);
}