diff --git a/libsquoosh/src/codecs.ts b/libsquoosh/src/codecs.ts index 98052cd6..0517f217 100644 --- a/libsquoosh/src/codecs.ts +++ b/libsquoosh/src/codecs.ts @@ -29,14 +29,23 @@ interface ResizeWithAspectParams { target_height: number; } -interface ResizeInstantiateOptions { +export interface ResizeOptions { width: number; height: number; - method: string; + method: 'triangle' | 'catrom' | 'mitchell' | 'lanczos3'; premultiply: boolean; linearRGB: boolean; } +export interface QuantOptions { + numColors: number; + dither: number; +} + +export interface RotateOptions { + numRotations: number; +} + declare global { // Needed for being able to use ImageData as type in codec types type ImageData = import('./image_data.js').default; @@ -52,6 +61,7 @@ import mozEnc from '../../codecs/mozjpeg/enc/mozjpeg_node_enc.js'; import mozEncWasm from 'asset-url:../../codecs/mozjpeg/enc/mozjpeg_node_enc.wasm'; import mozDec from '../../codecs/mozjpeg/dec/mozjpeg_node_dec.js'; import mozDecWasm from 'asset-url:../../codecs/mozjpeg/dec/mozjpeg_node_dec.wasm'; +import type { EncodeOptions as MozJPEGEncodeOptions } from '../../codecs/mozjpeg/enc/mozjpeg_enc'; // WebP import type { WebPModule as WebPEncodeModule } from '../../codecs/webp/enc/webp_enc'; @@ -59,6 +69,7 @@ import webpEnc from '../../codecs/webp/enc/webp_node_enc.js'; import webpEncWasm from 'asset-url:../../codecs/webp/enc/webp_node_enc.wasm'; import webpDec from '../../codecs/webp/dec/webp_node_dec.js'; import webpDecWasm from 'asset-url:../../codecs/webp/dec/webp_node_dec.wasm'; +import type { EncodeOptions as WebPEncodeOptions } from '../../codecs/webp/enc/webp_enc.js'; // AVIF import type { AVIFModule as AVIFEncodeModule } from '../../codecs/avif/enc/avif_enc'; @@ -69,6 +80,7 @@ import avifEncMtWorker from 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.wo import avifEncMtWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc_mt.wasm'; import avifDec from '../../codecs/avif/dec/avif_node_dec.js'; import avifDecWasm from 'asset-url:../../codecs/avif/dec/avif_node_dec.wasm'; +import type { EncodeOptions as AvifEncodeOptions } from '../../codecs/avif/enc/avif_enc.js'; // JXL import type { JXLModule as JXLEncodeModule } from '../../codecs/jxl/enc/jxl_enc'; @@ -76,6 +88,7 @@ import jxlEnc from '../../codecs/jxl/enc/jxl_node_enc.js'; import jxlEncWasm from 'asset-url:../../codecs/jxl/enc/jxl_node_enc.wasm'; import jxlDec from '../../codecs/jxl/dec/jxl_node_dec.js'; import jxlDecWasm from 'asset-url:../../codecs/jxl/dec/jxl_node_dec.wasm'; +import type { EncodeOptions as JxlEncodeOptions } from '../../codecs/jxl/enc/jxl_enc.js'; // WP2 import type { WP2Module as WP2EncodeModule } from '../../codecs/wp2/enc/wp2_enc'; @@ -83,6 +96,7 @@ import wp2Enc from '../../codecs/wp2/enc/wp2_node_enc.js'; import wp2EncWasm from 'asset-url:../../codecs/wp2/enc/wp2_node_enc.wasm'; import wp2Dec from '../../codecs/wp2/dec/wp2_node_dec.js'; import wp2DecWasm from 'asset-url:../../codecs/wp2/dec/wp2_node_dec.wasm'; +import type { EncodeOptions as WP2EncodeOptions } from '../../codecs/wp2/enc/wp2_enc.js'; // PNG import * as pngEncDec from '../../codecs/png/pkg/squoosh_png.js'; @@ -95,6 +109,9 @@ const pngEncDecPromise = pngEncDec.default( 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))); +interface OxiPngEncodeOptions { + level: number; +} // Resize import * as resize from '../../codecs/resize/pkg/squoosh_resize.js'; @@ -171,13 +188,7 @@ export const preprocessors = { buffer: Uint8Array, input_width: number, input_height: number, - { - width, - height, - method, - premultiply, - linearRGB, - }: ResizeInstantiateOptions, + { width, height, method, premultiply, linearRGB }: ResizeOptions, ) => { ({ width, height } = resizeWithAspect({ input_width, @@ -218,7 +229,7 @@ export const preprocessors = { buffer: Uint8Array, width: number, height: number, - { numColors, dither }: { numColors: number; dither: number }, + { numColors, dither }: QuantOptions, ) => new ImageData( imageQuant.quantize(buffer, width, height, numColors, dither), @@ -239,7 +250,7 @@ export const preprocessors = { buffer: Uint8Array, width: number, height: number, - { numRotations }: { numRotations: number }, + { numRotations }: RotateOptions, ) => { const degrees = (numRotations * 90) % 360; const sameDimensions = degrees == 0 || degrees == 180; @@ -401,13 +412,13 @@ export const codecs = { jxlEncWasm, ), defaultEncoderOptions: { - speed: 4, + effort: 1, quality: 75, progressive: false, epf: -1, - nearLossless: 0, lossyPalette: false, decodingSpeedTier: 0, + photonNoiseIso: 0, }, autoOptimize: { option: 'quality', @@ -480,3 +491,12 @@ export const codecs = { }, }, } as const; + +export { + MozJPEGEncodeOptions, + WebPEncodeOptions, + AvifEncodeOptions, + JxlEncodeOptions, + WP2EncodeOptions, + OxiPngEncodeOptions, +}; diff --git a/libsquoosh/src/index.ts b/libsquoosh/src/index.ts index 48c15091..65c9bec8 100644 --- a/libsquoosh/src/index.ts +++ b/libsquoosh/src/index.ts @@ -2,7 +2,19 @@ import { isMainThread } from 'worker_threads'; import { cpus } from 'os'; import { promises as fsp } from 'fs'; -import { codecs as encoders, preprocessors } from './codecs.js'; +import { + AvifEncodeOptions, + codecs as encoders, + JxlEncodeOptions, + MozJPEGEncodeOptions, + OxiPngEncodeOptions, + preprocessors, + QuantOptions, + ResizeOptions, + RotateOptions, + WebPEncodeOptions, + WP2EncodeOptions, +} from './codecs.js'; import WorkerPool from './worker_pool.js'; import { autoOptimize } from './auto-optimizer.js'; import type ImageData from './image_data'; @@ -12,6 +24,12 @@ type EncoderKey = keyof typeof encoders; type PreprocessorKey = keyof typeof preprocessors; type FileLike = Buffer | ArrayBuffer | string | ArrayBufferView; +type PreprocessOptions = { + resize?: ResizeOptions; + quant?: QuantOptions; + rotate?: RotateOptions; +}; + async function decodeFile({ file, }: { @@ -183,10 +201,10 @@ class Image { /** * Define one or several preprocessors to use on the image. - * @param {object} preprocessOptions - An object with preprocessors to use, and their settings. + * @param {PreprocessOptions} preprocessOptions - An object with preprocessors to use, and their settings. * @returns {Promise} - A promise that resolves when all preprocessors have completed their work. */ - async preprocess(preprocessOptions = {}) { + async preprocess(preprocessOptions: PreprocessOptions = {}) { for (const [name, options] of Object.entries(preprocessOptions)) { if (!Object.keys(preprocessors).includes(name)) { throw Error(`Invalid preprocessor "${name}"`); @@ -217,7 +235,12 @@ class Image { optimizerButteraugliTarget?: number; maxOptimizerRounds?: number; } & { - [key in EncoderKey]?: any; // any is okay for now + mozjpeg?: Partial; + webp?: Partial; + avif?: Partial; + jxl?: Partial; + wp2?: Partial; + oxipng?: Partial; } = {}, ): Promise { const { bitmap } = await this.decoded;