Build preprocessor pipeline
This commit is contained in:
@ -30,11 +30,71 @@ import * as oxipng from "../../codecs/oxipng/pkg/squoosh_oxipng.js";
|
||||
import oxipngWasm from "asset-url:../../codecs/oxipng/pkg/squoosh_oxipng_bg.wasm";
|
||||
const oxipngPromise = oxipng.default(fsp.readFile(pathify(oxipngWasm)));
|
||||
|
||||
import * as resize from "../../codecs/resize/pkg/squoosh_resize.js";
|
||||
import resizeWasm from "asset-url:../../codecs/resize/pkg/squoosh_resize_bg.wasm";
|
||||
const resizePromise = resize.default(fsp.readFile(pathify(resizeWasm)));
|
||||
|
||||
// Our decoders currently rely on a `ImageData` global.
|
||||
import ImageData from "./image_data.js";
|
||||
globalThis.ImageData = ImageData;
|
||||
|
||||
export default {
|
||||
function resizeNameToIndex(name) {
|
||||
switch (name) {
|
||||
case "triangle":
|
||||
return 0;
|
||||
case "catrom":
|
||||
return 1;
|
||||
case "mitchell":
|
||||
return 2;
|
||||
case "lanczos3":
|
||||
return 3;
|
||||
default:
|
||||
throw Error(`Unknown resize algorithm "${name}"`);
|
||||
}
|
||||
}
|
||||
|
||||
export const preprocessors = {
|
||||
resize: {
|
||||
name: "Resize",
|
||||
description: "Resize the image before compressing",
|
||||
instantiate: async () => {
|
||||
await resizePromise;
|
||||
return (
|
||||
buffer,
|
||||
input_width,
|
||||
input_height,
|
||||
{ width, height, method, premultiply, linearRGB }
|
||||
) =>
|
||||
new ImageData(
|
||||
resize.resize(
|
||||
buffer,
|
||||
input_width,
|
||||
input_height,
|
||||
width,
|
||||
height,
|
||||
resizeNameToIndex(method),
|
||||
premultiply,
|
||||
linearRGB
|
||||
),
|
||||
width,
|
||||
height
|
||||
);
|
||||
},
|
||||
defaultOptions: {
|
||||
// Width and height will always default to the image size.
|
||||
// This is set elsewhere.
|
||||
width: 1,
|
||||
height: 1,
|
||||
// This will be set to 'vector' if the input is SVG.
|
||||
method: "lanczos3",
|
||||
fitMethod: "stretch",
|
||||
premultiply: true,
|
||||
linearRGB: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const codecs = {
|
||||
mozjpeg: {
|
||||
name: "MozJPEG",
|
||||
extension: "jpg",
|
||||
|
@ -8,7 +8,7 @@ import { version } from "json:../package.json";
|
||||
import ora from 'ora';
|
||||
import kleur from 'kleur';
|
||||
|
||||
import supportedFormats from "./codecs.js";
|
||||
import {codecs as supportedFormats, preprocessors} from "./codecs.js";
|
||||
import WorkerPool from "./worker_pool.js";
|
||||
import { autoOptimize } from "./auto-optimizer.js";
|
||||
|
||||
@ -47,6 +47,16 @@ async function decodeFile(file) {
|
||||
};
|
||||
}
|
||||
|
||||
async function preprocessImage({
|
||||
preprocessorName,
|
||||
options,
|
||||
file
|
||||
}) {
|
||||
const preprocessor = await preprocessors[preprocessorName].instantiate();
|
||||
file.bitmap= await preprocessor(file.bitmap.data, file.bitmap.width, file.bitmap.height, options);
|
||||
return file;
|
||||
}
|
||||
|
||||
async function encodeFile({
|
||||
file,
|
||||
size,
|
||||
@ -110,12 +120,16 @@ async function encodeFile({
|
||||
// both decoding and encoding go through the worker pool
|
||||
function handleJob(params) {
|
||||
const { operation } = params;
|
||||
if (operation === 'encode') {
|
||||
switch(operation) {
|
||||
case "encode":
|
||||
return encodeFile(params);
|
||||
}
|
||||
if (operation === 'decode') {
|
||||
case "decode":
|
||||
return decodeFile(params.file);
|
||||
}
|
||||
case "preprocess":
|
||||
return preprocessImage(params);
|
||||
default:
|
||||
throw Error(`Invalid job "${operation}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function progressTracker(results) {
|
||||
@ -175,7 +189,7 @@ async function processFiles(files) {
|
||||
await fsp.mkdir(program.outputDir, { recursive: true });
|
||||
|
||||
let decoded = 0;
|
||||
const decodedFiles = await Promise.all(files.map(async file => {
|
||||
let decodedFiles = await Promise.all(files.map(async file => {
|
||||
const result = await workerPool.dispatchJob({ operation: 'decode', file });
|
||||
results.set(file, {
|
||||
file: result.file,
|
||||
@ -186,6 +200,31 @@ async function processFiles(files) {
|
||||
return result;
|
||||
}));
|
||||
|
||||
for (const [preprocessorName, value] of Object.entries(preprocessors)) {
|
||||
if(!program[preprocessorName]) {
|
||||
continue;
|
||||
}
|
||||
const preprocessorParam = program[preprocessorName];
|
||||
const preprocessorOptions = Object.assign(
|
||||
{},
|
||||
value.defaultOptions,
|
||||
JSON5.parse(preprocessorParam)
|
||||
);
|
||||
|
||||
decodedFiles = await Promise.all(decodedFiles.map(async file => {
|
||||
return workerPool.dispatchJob({
|
||||
file,
|
||||
operation: "preprocess",
|
||||
preprocessorName,
|
||||
options: preprocessorOptions
|
||||
});
|
||||
}));
|
||||
|
||||
for (const { file, bitmap, size } of decodedFiles) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
progress.progressOffset = decoded;
|
||||
progress.setStatus('Encoding ' + kleur.dim(`(${parallelism} threads)`));
|
||||
progress.setProgress(0, files.length);
|
||||
@ -261,6 +300,13 @@ if (isMainThread) {
|
||||
)
|
||||
.action(processFiles);
|
||||
|
||||
// Create a CLI option for each supported preprocessor
|
||||
for (const [key, value] of Object.entries(preprocessors)) {
|
||||
program.option(
|
||||
`--${key} [config]`,
|
||||
value.description
|
||||
);
|
||||
}
|
||||
// Create a CLI option for each supported encoder
|
||||
for (const [key, value] of Object.entries(supportedFormats)) {
|
||||
program.option(
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "resize",
|
||||
"scripts": {
|
||||
"build": "../build-rust.sh"
|
||||
"build": "../build-rust.sh rm -rf pkg && wasm-pack build --debug --target web -- --verbose --locked && rm pkg/.gitignore"
|
||||
}
|
||||
}
|
||||
|
20
codecs/resize/pkg/squoosh_resize.d.ts
vendored
20
codecs/resize/pkg/squoosh_resize.d.ts
vendored
@ -12,3 +12,23 @@
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function resize(input_image: Uint8Array, input_width: number, input_height: number, output_width: number, output_height: number, typ_idx: number, premultiply: boolean, color_space_conversion: boolean): Uint8Array;
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly resize: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => void;
|
||||
readonly __wbindgen_malloc: (a: number) => number;
|
||||
readonly __wbindgen_free: (a: number, b: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
||||
* for everything else, calls `WebAssembly.instantiate` directly.
|
||||
*
|
||||
* @param {InitInput | Promise<InitInput>} module_or_path
|
||||
*
|
||||
* @returns {Promise<InitOutput>}
|
||||
*/
|
||||
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;
|
||||
|
@ -1,2 +1,135 @@
|
||||
import * as wasm from "./squoosh_resize_bg.wasm";
|
||||
export * from "./squoosh_resize_bg.js";
|
||||
|
||||
let wasm;
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
let cachegetUint8Memory0 = null;
|
||||
function getUint8Memory0() {
|
||||
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint8Memory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
function passArray8ToWasm0(arg, malloc) {
|
||||
const ptr = malloc(arg.length * 1);
|
||||
getUint8Memory0().set(arg, ptr / 1);
|
||||
WASM_VECTOR_LEN = arg.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function _assertNum(n) {
|
||||
if (typeof(n) !== 'number') throw new Error('expected a number argument');
|
||||
}
|
||||
|
||||
function _assertBoolean(n) {
|
||||
if (typeof(n) !== 'boolean') {
|
||||
throw new Error('expected a boolean argument');
|
||||
}
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
}
|
||||
/**
|
||||
* @param {Uint8Array} input_image
|
||||
* @param {number} input_width
|
||||
* @param {number} input_height
|
||||
* @param {number} output_width
|
||||
* @param {number} output_height
|
||||
* @param {number} typ_idx
|
||||
* @param {boolean} premultiply
|
||||
* @param {boolean} color_space_conversion
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function resize(input_image, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion) {
|
||||
var ptr0 = passArray8ToWasm0(input_image, wasm.__wbindgen_malloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
_assertNum(input_width);
|
||||
_assertNum(input_height);
|
||||
_assertNum(output_width);
|
||||
_assertNum(output_height);
|
||||
_assertNum(typ_idx);
|
||||
_assertBoolean(premultiply);
|
||||
_assertBoolean(color_space_conversion);
|
||||
wasm.resize(8, ptr0, len0, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion);
|
||||
var r0 = getInt32Memory0()[8 / 4 + 0];
|
||||
var r1 = getInt32Memory0()[8 / 4 + 1];
|
||||
var v1 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v1;
|
||||
}
|
||||
|
||||
async function load(module, imports) {
|
||||
if (typeof Response === 'function' && module instanceof Response) {
|
||||
|
||||
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||
try {
|
||||
return await WebAssembly.instantiateStreaming(module, imports);
|
||||
|
||||
} catch (e) {
|
||||
if (module.headers.get('Content-Type') != 'application/wasm') {
|
||||
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
|
||||
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bytes = await module.arrayBuffer();
|
||||
return await WebAssembly.instantiate(bytes, imports);
|
||||
|
||||
} else {
|
||||
|
||||
const instance = await WebAssembly.instantiate(module, imports);
|
||||
|
||||
if (instance instanceof WebAssembly.Instance) {
|
||||
return { instance, module };
|
||||
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function init(input) {
|
||||
if (typeof input === 'undefined') {
|
||||
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
|
||||
}
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
|
||||
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
|
||||
input = fetch(input);
|
||||
}
|
||||
|
||||
const { instance, module } = await load(await input, imports);
|
||||
|
||||
wasm = instance.exports;
|
||||
init.__wbindgen_wasm_module = module;
|
||||
|
||||
return wasm;
|
||||
}
|
||||
|
||||
export default init;
|
||||
|
||||
|
@ -1,52 +0,0 @@
|
||||
import * as wasm from './squoosh_resize_bg.wasm';
|
||||
|
||||
let cachegetUint8Memory0 = null;
|
||||
function getUint8Memory0() {
|
||||
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint8Memory0;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
function passArray8ToWasm0(arg, malloc) {
|
||||
const ptr = malloc(arg.length * 1);
|
||||
getUint8Memory0().set(arg, ptr / 1);
|
||||
WASM_VECTOR_LEN = arg.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
}
|
||||
/**
|
||||
* @param {Uint8Array} input_image
|
||||
* @param {number} input_width
|
||||
* @param {number} input_height
|
||||
* @param {number} output_width
|
||||
* @param {number} output_height
|
||||
* @param {number} typ_idx
|
||||
* @param {boolean} premultiply
|
||||
* @param {boolean} color_space_conversion
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function resize(input_image, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion) {
|
||||
var ptr0 = passArray8ToWasm0(input_image, wasm.__wbindgen_malloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
wasm.resize(8, ptr0, len0, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion);
|
||||
var r0 = getInt32Memory0()[8 / 4 + 0];
|
||||
var r1 = getInt32Memory0()[8 / 4 + 1];
|
||||
var v1 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v1;
|
||||
}
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user