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

Binary file not shown.

View File

@ -14,41 +14,71 @@
*/
class QCMS {
static _module = null;
static #memoryArray = null;
static _memory = null;
static _mustAddAlpha = false;
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) {
// 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 { _module, _mustAddAlpha, _destBuffer } = QCMS;
const result = new Uint8Array(_module.memory.buffer, ptr, len);
if (result.length === _destBuffer.length) {
_destBuffer.set(result);
const { _mustAddAlpha, _destBuffer, _destOffset, _destLength, _memoryArray } =
QCMS;
if (len === _destLength) {
_destBuffer.set(_memoryArray.subarray(ptr, ptr + len), _destOffset);
return;
}
if (_mustAddAlpha) {
for (let i = 0, j = 0, ii = result.length; i < ii; i += 3, j += 4) {
_destBuffer[j] = result[i];
_destBuffer[j + 1] = result[i + 1];
_destBuffer[j + 2] = result[i + 2];
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 = 0, j = 0, ii = result.length; i < ii; i += 3, j += 4) {
_destBuffer[j] = result[i];
_destBuffer[j + 1] = result[i + 1];
_destBuffer[j + 2] = result[i + 2];
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];
}
}
}
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_transformer_from_memory,
} 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 { QCMS } from "../../external/qcms/qcms_utils.js";
@ -63,28 +63,30 @@ class IccColorSpace extends ColorSpace {
switch (numComps) {
case 1:
inType = DataType.Gray8;
this.#convertPixel = (src, srcOffset) =>
qcms_convert_one(this.#transformer, src[srcOffset] * 255);
this.#convertPixel = (src, srcOffset, css) =>
qcms_convert_one(this.#transformer, src[srcOffset] * 255, css);
break;
case 3:
inType = DataType.RGB8;
this.#convertPixel = (src, srcOffset) =>
this.#convertPixel = (src, srcOffset, css) =>
qcms_convert_three(
this.#transformer,
src[srcOffset] * 255,
src[srcOffset + 1] * 255,
src[srcOffset + 2] * 255
src[srcOffset + 2] * 255,
css
);
break;
case 4:
inType = DataType.CMYK;
this.#convertPixel = (src, srcOffset) =>
this.#convertPixel = (src, srcOffset, css) =>
qcms_convert_four(
this.#transformer,
src[srcOffset] * 255,
src[srcOffset + 1] * 255,
src[srcOffset + 2] * 255,
src[srcOffset + 3] * 255
src[srcOffset + 3] * 255,
css
);
break;
default:
@ -101,9 +103,16 @@ class IccColorSpace extends ColorSpace {
IccColorSpace.#finalizer.register(this, this.#transformer);
}
getRgbHex(src, srcOffset) {
this.#convertPixel(src, srcOffset, /* css */ true);
return QCMS._cssColor;
}
getRgbItem(src, srcOffset, dest, destOffset) {
QCMS._destBuffer = dest.subarray(destOffset, destOffset + 3);
this.#convertPixel(src, srcOffset);
QCMS._destBuffer = dest;
QCMS._destOffset = destOffset;
QCMS._destLength = 3;
this.#convertPixel(src, srcOffset, /* css */ false);
QCMS._destBuffer = null;
}
@ -116,10 +125,9 @@ class IccColorSpace extends ColorSpace {
}
}
QCMS._mustAddAlpha = alpha01 && dest.buffer === src.buffer;
QCMS._destBuffer = dest.subarray(
destOffset,
destOffset + count * (3 + alpha01)
);
QCMS._destBuffer = dest;
QCMS._destOffset = destOffset;
QCMS._destLength = count * (3 + alpha01);
qcms_convert_array(this.#transformer, src);
QCMS._mustAddAlpha = false;
QCMS._destBuffer = null;
@ -143,10 +151,12 @@ class IccColorSpace extends ColorSpace {
if (this.#useWasm) {
if (this.#wasmUrl) {
try {
this._module = QCMS._module = initSync({
this._module = initSync({
module: fetchSync(`${this.#wasmUrl}qcms_bg.wasm`),
});
isUsable = !!this._module;
QCMS._memory = this._module.memory;
QCMS._makeHexColor = Util.makeHexColor;
} catch (e) {
warn(`ICCBased color space: "${e}".`);
}