Remove all the useless subarrays when using qcms.

It reduces the memory use and the newly added function `getRgbHex` is 4 times faster.
This commit is contained in:
Calixte Denizet 2025-05-19 13:53:35 +02:00
parent 2b9f621087
commit 782e883a87
4 changed files with 82 additions and 36 deletions

20
external/qcms/qcms.js vendored
View File

@ -1,5 +1,5 @@
/* THIS FILE IS GENERATED - DO NOT EDIT */ /* THIS FILE IS GENERATED - DO NOT EDIT */
import { copy_result, copy_rgb } from './qcms_utils.js'; import { copy_result, copy_rgb, make_cssRGB } from './qcms_utils.js';
let wasm; let wasm;
@ -48,9 +48,10 @@ export function qcms_convert_array(transformer, src) {
* This function is called directly from JavaScript. * This function is called directly from JavaScript.
* @param {number} transformer * @param {number} transformer
* @param {number} src * @param {number} src
* @param {boolean} css
*/ */
export function qcms_convert_one(transformer, src) { export function qcms_convert_one(transformer, src, css) {
wasm.qcms_convert_one(transformer, src); wasm.qcms_convert_one(transformer, src, css);
} }
/** /**
@ -61,9 +62,10 @@ export function qcms_convert_one(transformer, src) {
* @param {number} src1 * @param {number} src1
* @param {number} src2 * @param {number} src2
* @param {number} src3 * @param {number} src3
* @param {boolean} css
*/ */
export function qcms_convert_three(transformer, src1, src2, src3) { export function qcms_convert_three(transformer, src1, src2, src3, css) {
wasm.qcms_convert_three(transformer, src1, src2, src3); wasm.qcms_convert_three(transformer, src1, src2, src3, css);
} }
/** /**
@ -75,9 +77,10 @@ export function qcms_convert_three(transformer, src1, src2, src3) {
* @param {number} src2 * @param {number} src2
* @param {number} src3 * @param {number} src3
* @param {number} src4 * @param {number} src4
* @param {boolean} css
*/ */
export function qcms_convert_four(transformer, src1, src2, src3, src4) { export function qcms_convert_four(transformer, src1, src2, src3, src4, css) {
wasm.qcms_convert_four(transformer, src1, src2, src3, src4); wasm.qcms_convert_four(transformer, src1, src2, src3, src4, css);
} }
/** /**
@ -167,6 +170,9 @@ function __wbg_get_imports() {
imports.wbg.__wbg_copyrgb_d60ce17bb05d9b67 = function(arg0) { imports.wbg.__wbg_copyrgb_d60ce17bb05d9b67 = function(arg0) {
copy_rgb(arg0 >>> 0); copy_rgb(arg0 >>> 0);
}; };
imports.wbg.__wbg_makecssRGB_893bf0cd9fdb302d = function(arg0) {
make_cssRGB(arg0 >>> 0);
};
imports.wbg.__wbindgen_init_externref_table = function() { imports.wbg.__wbindgen_init_externref_table = function() {
const table = wasm.__wbindgen_export_0; const table = wasm.__wbindgen_export_0;
const offset = table.grow(4); const offset = table.grow(4);

Binary file not shown.

View File

@ -14,41 +14,71 @@
*/ */
class QCMS { class QCMS {
static _module = null; static #memoryArray = null;
static _memory = null;
static _mustAddAlpha = false; static _mustAddAlpha = false;
static _destBuffer = null; static _destBuffer = null;
static _destOffset = 0;
static _destLength = 0;
static _cssColor = "";
static _makeHexColor = null;
static get _memoryArray() {
const array = this.#memoryArray;
if (array?.byteLength) {
return array;
}
return (this.#memoryArray = new Uint8Array(this._memory.buffer));
}
} }
function copy_result(ptr, len) { function copy_result(ptr, len) {
// This function is called from the wasm module (it's an external // 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 // "C" function). Its goal is to copy the result from the wasm memory
// to the destination buffer without any intermediate copies. // to the destination buffer without any intermediate copies.
const { _module, _mustAddAlpha, _destBuffer } = QCMS; const { _mustAddAlpha, _destBuffer, _destOffset, _destLength, _memoryArray } =
const result = new Uint8Array(_module.memory.buffer, ptr, len); QCMS;
if (result.length === _destBuffer.length) { if (len === _destLength) {
_destBuffer.set(result); _destBuffer.set(_memoryArray.subarray(ptr, ptr + len), _destOffset);
return; return;
} }
if (_mustAddAlpha) { if (_mustAddAlpha) {
for (let i = 0, j = 0, ii = result.length; i < ii; i += 3, j += 4) { for (let i = ptr, ii = ptr + len, j = _destOffset; i < ii; i += 3, j += 4) {
_destBuffer[j] = result[i]; _destBuffer[j] = _memoryArray[i];
_destBuffer[j + 1] = result[i + 1]; _destBuffer[j + 1] = _memoryArray[i + 1];
_destBuffer[j + 2] = result[i + 2]; _destBuffer[j + 2] = _memoryArray[i + 2];
_destBuffer[j + 3] = 255; _destBuffer[j + 3] = 255;
} }
} else { } else {
for (let i = 0, j = 0, ii = result.length; i < ii; i += 3, j += 4) { for (let i = ptr, ii = ptr + len, j = _destOffset; i < ii; i += 3, j += 4) {
_destBuffer[j] = result[i]; _destBuffer[j] = _memoryArray[i];
_destBuffer[j + 1] = result[i + 1]; _destBuffer[j + 1] = _memoryArray[i + 1];
_destBuffer[j + 2] = result[i + 2]; _destBuffer[j + 2] = _memoryArray[i + 2];
} }
} }
} }
function copy_rgb(ptr) { function copy_rgb(ptr) {
QCMS._destBuffer.set(new Uint8Array(QCMS._module.memory.buffer, ptr, 3)); const { _destBuffer, _destOffset, _memoryArray } = QCMS;
_destBuffer[_destOffset] = _memoryArray[ptr];
_destBuffer[_destOffset + 1] = _memoryArray[ptr + 1];
_destBuffer[_destOffset + 2] = _memoryArray[ptr + 2];
} }
export { copy_result, copy_rgb, QCMS }; 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 };

View File

@ -24,7 +24,7 @@ import {
qcms_drop_transformer, qcms_drop_transformer,
qcms_transformer_from_memory, qcms_transformer_from_memory,
} from "../../external/qcms/qcms.js"; } from "../../external/qcms/qcms.js";
import { shadow, warn } from "../shared/util.js"; import { shadow, Util, warn } from "../shared/util.js";
import { ColorSpace } from "./colorspace.js"; import { ColorSpace } from "./colorspace.js";
import { QCMS } from "../../external/qcms/qcms_utils.js"; import { QCMS } from "../../external/qcms/qcms_utils.js";
@ -63,28 +63,30 @@ class IccColorSpace extends ColorSpace {
switch (numComps) { switch (numComps) {
case 1: case 1:
inType = DataType.Gray8; inType = DataType.Gray8;
this.#convertPixel = (src, srcOffset) => this.#convertPixel = (src, srcOffset, css) =>
qcms_convert_one(this.#transformer, src[srcOffset] * 255); qcms_convert_one(this.#transformer, src[srcOffset] * 255, css);
break; break;
case 3: case 3:
inType = DataType.RGB8; inType = DataType.RGB8;
this.#convertPixel = (src, srcOffset) => this.#convertPixel = (src, srcOffset, css) =>
qcms_convert_three( qcms_convert_three(
this.#transformer, this.#transformer,
src[srcOffset] * 255, src[srcOffset] * 255,
src[srcOffset + 1] * 255, src[srcOffset + 1] * 255,
src[srcOffset + 2] * 255 src[srcOffset + 2] * 255,
css
); );
break; break;
case 4: case 4:
inType = DataType.CMYK; inType = DataType.CMYK;
this.#convertPixel = (src, srcOffset) => this.#convertPixel = (src, srcOffset, css) =>
qcms_convert_four( qcms_convert_four(
this.#transformer, this.#transformer,
src[srcOffset] * 255, src[srcOffset] * 255,
src[srcOffset + 1] * 255, src[srcOffset + 1] * 255,
src[srcOffset + 2] * 255, src[srcOffset + 2] * 255,
src[srcOffset + 3] * 255 src[srcOffset + 3] * 255,
css
); );
break; break;
default: default:
@ -101,9 +103,16 @@ class IccColorSpace extends ColorSpace {
IccColorSpace.#finalizer.register(this, this.#transformer); IccColorSpace.#finalizer.register(this, this.#transformer);
} }
getRgbHex(src, srcOffset) {
this.#convertPixel(src, srcOffset, /* css */ true);
return QCMS._cssColor;
}
getRgbItem(src, srcOffset, dest, destOffset) { getRgbItem(src, srcOffset, dest, destOffset) {
QCMS._destBuffer = dest.subarray(destOffset, destOffset + 3); QCMS._destBuffer = dest;
this.#convertPixel(src, srcOffset); QCMS._destOffset = destOffset;
QCMS._destLength = 3;
this.#convertPixel(src, srcOffset, /* css */ false);
QCMS._destBuffer = null; QCMS._destBuffer = null;
} }
@ -116,10 +125,9 @@ class IccColorSpace extends ColorSpace {
} }
} }
QCMS._mustAddAlpha = alpha01 && dest.buffer === src.buffer; QCMS._mustAddAlpha = alpha01 && dest.buffer === src.buffer;
QCMS._destBuffer = dest.subarray( QCMS._destBuffer = dest;
destOffset, QCMS._destOffset = destOffset;
destOffset + count * (3 + alpha01) QCMS._destLength = count * (3 + alpha01);
);
qcms_convert_array(this.#transformer, src); qcms_convert_array(this.#transformer, src);
QCMS._mustAddAlpha = false; QCMS._mustAddAlpha = false;
QCMS._destBuffer = null; QCMS._destBuffer = null;
@ -143,10 +151,12 @@ class IccColorSpace extends ColorSpace {
if (this.#useWasm) { if (this.#useWasm) {
if (this.#wasmUrl) { if (this.#wasmUrl) {
try { try {
this._module = QCMS._module = initSync({ this._module = initSync({
module: fetchSync(`${this.#wasmUrl}qcms_bg.wasm`), module: fetchSync(`${this.#wasmUrl}qcms_bg.wasm`),
}); });
isUsable = !!this._module; isUsable = !!this._module;
QCMS._memory = this._module.memory;
QCMS._makeHexColor = Util.makeHexColor;
} catch (e) { } catch (e) {
warn(`ICCBased color space: "${e}".`); warn(`ICCBased color space: "${e}".`);
} }