├── .npmignore ├── codecs ├── jxl │ ├── enc │ │ ├── jxl_enc_mt.d.ts │ │ ├── jxl_enc_mt_simd.d.ts │ │ ├── jxl_enc.wasm │ │ ├── jxl_enc_mt.wasm │ │ ├── jxl_node_enc.wasm │ │ ├── jxl_enc_mt_simd.wasm │ │ ├── jxl_enc.d.ts │ │ ├── jxl_enc_mt.worker.js │ │ ├── jxl_enc_mt_simd.worker.js │ │ └── jxl_enc.cpp │ └── dec │ │ ├── jxl_dec.wasm │ │ ├── jxl_node_dec.wasm │ │ ├── jxl_dec.d.ts │ │ └── jxl_dec.cpp ├── wp2 │ ├── enc │ │ ├── wp2_enc_mt.d.ts │ │ ├── wp2_enc_mt_simd.d.ts │ │ ├── wp2_enc.wasm │ │ ├── wp2_enc_mt.wasm │ │ ├── wp2_node_enc.wasm │ │ ├── wp2_enc_mt_simd.wasm │ │ ├── wp2_enc.d.ts │ │ ├── wp2_enc_mt.worker.js │ │ ├── wp2_enc_mt_simd.worker.js │ │ └── wp2_enc.cpp │ └── dec │ │ ├── wp2_dec.wasm │ │ ├── wp2_node_dec.wasm │ │ └── wp2_dec.d.ts ├── avif │ ├── enc │ │ ├── avif_enc_mt.d.ts │ │ ├── avif_enc.wasm │ │ ├── avif_enc_mt.wasm │ │ ├── avif_node_enc.wasm │ │ ├── avif_node_enc_mt.wasm │ │ ├── avif_enc.d.ts │ │ ├── README.md │ │ ├── avif_enc_mt.worker.js │ │ ├── avif_node_enc_mt.worker.js │ │ └── avif_enc.cpp │ └── dec │ │ ├── avif_dec.wasm │ │ ├── avif_node_dec.wasm │ │ ├── avif_dec.d.ts │ │ ├── README.md │ │ └── avif_dec.cpp ├── visdif │ ├── .gitignore │ └── visdif.wasm ├── webp │ ├── enc │ │ ├── webp_enc_simd.d.ts │ │ ├── webp_enc.wasm │ │ ├── webp_enc_simd.wasm │ │ ├── webp_node_enc.wasm │ │ ├── webp_enc.d.ts │ │ └── webp_enc.cpp │ └── dec │ │ ├── webp_dec.wasm │ │ ├── webp_node_dec.wasm │ │ └── webp_dec.d.ts ├── rotate │ ├── rotate.wasm │ └── benchmark.js ├── hqx │ └── pkg │ │ ├── squooshhqx_bg.wasm │ │ ├── squooshhqx.d.ts │ │ └── squooshhqx.js ├── imagequant │ ├── imagequant.wasm │ ├── imagequant_node.wasm │ ├── imagequant.d.ts │ └── example.html ├── mozjpeg │ ├── enc │ │ ├── mozjpeg_enc.wasm │ │ ├── mozjpeg_node_enc.wasm │ │ ├── mozjpeg_enc.d.ts │ │ └── mozjpeg_enc.cpp │ └── dec │ │ ├── mozjpeg_node_dec.wasm │ │ └── mozjpeg_dec.cpp ├── png │ └── pkg │ │ ├── squoosh_png_bg.wasm │ │ ├── squoosh_png_bg.wasm.d.ts │ │ ├── squoosh_png.d.ts │ │ └── squoosh_png.js ├── oxipng │ ├── pkg │ │ ├── squoosh_oxipng_bg.wasm │ │ ├── squoosh_oxipng_bg.wasm.d.ts │ │ ├── squoosh_oxipng.d.ts │ │ └── squoosh_oxipng.js │ └── pkg-parallel │ │ ├── squoosh_oxipng_bg.wasm │ │ ├── squoosh_oxipng_bg.wasm.d.ts │ │ ├── squoosh_oxipng.d.ts │ │ ├── snippets │ │ └── wasm-bindgen-rayon-3d2df09ebec17a22 │ │ │ └── src │ │ │ └── workerHelpers.js │ │ └── squoosh_oxipng.js └── resize │ └── pkg │ ├── squoosh_resize_bg.wasm │ ├── squoosh_resize_bg.wasm.d.ts │ ├── squoosh_resize.d.ts │ └── squoosh_resize.js ├── demo ├── public │ └── index.html ├── package.json ├── webpack.config.js └── src │ └── index.js ├── src ├── features │ ├── encoders │ │ ├── browserPNG │ │ │ ├── client │ │ │ │ ├── index.ts │ │ │ │ └── missing-types.d.ts │ │ │ └── shared │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── meta.ts │ │ ├── browserJPEG │ │ │ ├── client │ │ │ │ ├── index.ts │ │ │ │ └── missing-types.d.ts │ │ │ └── shared │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── meta.ts │ │ ├── browserGIF │ │ │ ├── client │ │ │ │ ├── index.ts │ │ │ │ └── missing-types.d.ts │ │ │ └── shared │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── meta.ts │ │ ├── mozJPEG │ │ │ ├── client │ │ │ │ ├── index.ts │ │ │ │ └── missing-types.d.ts │ │ │ ├── shared │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── meta.ts │ │ │ └── worker │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── mozjpegEncode.ts │ │ ├── avif │ │ │ ├── shared │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── meta.ts │ │ │ ├── worker │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── avifEncode.ts │ │ │ └── client │ │ │ │ └── index.ts │ │ ├── jxl │ │ │ ├── client │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ └── meta.ts │ │ │ └── worker │ │ │ │ └── jxlEncode.ts │ │ ├── oxiPNG │ │ │ ├── client │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ └── meta.ts │ │ │ └── worker │ │ │ │ └── oxipngEncode.ts │ │ ├── wp2 │ │ │ ├── client │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ └── meta.ts │ │ │ └── worker │ │ │ │ └── wp2Encode.ts │ │ └── webP │ │ │ ├── client │ │ │ └── index.ts │ │ │ ├── shared │ │ │ └── meta.ts │ │ │ └── worker │ │ │ └── webpEncode.ts │ ├── processors │ │ ├── quantize │ │ │ ├── client │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── meta.ts │ │ │ └── worker │ │ │ │ ├── missing-types.d.ts │ │ │ │ └── quantize.ts │ │ └── resize │ │ │ ├── client │ │ │ ├── missing-types.d.ts │ │ │ └── index.ts │ │ │ ├── shared │ │ │ ├── missing-types.d.ts │ │ │ ├── util.ts │ │ │ └── meta.ts │ │ │ └── worker │ │ │ ├── missing-types.d.ts │ │ │ └── resize.ts │ ├── client-utils │ │ └── index.ts │ ├── missing-types.d.ts │ ├── preprocessors │ │ └── rotate │ │ │ └── shared │ │ │ ├── missing-types.d.ts │ │ │ └── meta.ts │ ├── worker-utils │ │ └── index.ts │ └── decoders │ │ ├── jxl │ │ └── worker │ │ │ └── jxlDecode.ts │ │ ├── wp2 │ │ └── worker │ │ │ └── wp2Decode.ts │ │ ├── webp │ │ └── worker │ │ │ └── webpDecode.ts │ │ └── avif │ │ └── worker │ │ └── avifDecode.ts └── client │ └── lazy-app │ ├── util │ ├── web-codecs │ │ ├── index.ts │ │ └── missing-types.d.ts │ ├── cli.ts │ ├── clean-modify.ts │ └── canvas.ts │ ├── feature-meta │ └── index.ts │ └── Compress │ └── index.ts ├── tsconfig.json ├── LICENSE ├── README.md ├── package.json ├── missing-types.d.ts ├── .gitignore └── emscripten-types.d.ts /.npmignore: -------------------------------------------------------------------------------- 1 | /demo 2 | /src -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc_mt.d.ts: -------------------------------------------------------------------------------- 1 | export { default } from './jxl_enc'; 2 | -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc_mt.d.ts: -------------------------------------------------------------------------------- 1 | export { default } from './wp2_enc'; 2 | -------------------------------------------------------------------------------- /codecs/avif/enc/avif_enc_mt.d.ts: -------------------------------------------------------------------------------- 1 | export { default } from './avif_enc'; 2 | -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc_mt_simd.d.ts: -------------------------------------------------------------------------------- 1 | export { default } from './jxl_enc'; 2 | -------------------------------------------------------------------------------- /codecs/visdif/.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | target 3 | bin/ 4 | pkg/README.md 5 | -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc_mt_simd.d.ts: -------------------------------------------------------------------------------- 1 | export { default } from './wp2_enc'; 2 | -------------------------------------------------------------------------------- /codecs/webp/enc/webp_enc_simd.d.ts: -------------------------------------------------------------------------------- 1 | export { default } from './webp_enc.js'; 2 | -------------------------------------------------------------------------------- /codecs/rotate/rotate.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/rotate/rotate.wasm -------------------------------------------------------------------------------- /codecs/visdif/visdif.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/visdif/visdif.wasm -------------------------------------------------------------------------------- /codecs/jxl/dec/jxl_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/jxl/dec/jxl_dec.wasm -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/jxl/enc/jxl_enc.wasm -------------------------------------------------------------------------------- /codecs/wp2/dec/wp2_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/wp2/dec/wp2_dec.wasm -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/wp2/enc/wp2_enc.wasm -------------------------------------------------------------------------------- /codecs/avif/dec/avif_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/avif/dec/avif_dec.wasm -------------------------------------------------------------------------------- /codecs/avif/enc/avif_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/avif/enc/avif_enc.wasm -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc_mt.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/jxl/enc/jxl_enc_mt.wasm -------------------------------------------------------------------------------- /codecs/webp/dec/webp_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/webp/dec/webp_dec.wasm -------------------------------------------------------------------------------- /codecs/webp/enc/webp_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/webp/enc/webp_enc.wasm -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc_mt.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/wp2/enc/wp2_enc_mt.wasm -------------------------------------------------------------------------------- /codecs/avif/enc/avif_enc_mt.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/avif/enc/avif_enc_mt.wasm -------------------------------------------------------------------------------- /codecs/jxl/dec/jxl_node_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/jxl/dec/jxl_node_dec.wasm -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_node_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/jxl/enc/jxl_node_enc.wasm -------------------------------------------------------------------------------- /codecs/wp2/dec/wp2_node_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/wp2/dec/wp2_node_dec.wasm -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_node_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/wp2/enc/wp2_node_enc.wasm -------------------------------------------------------------------------------- /codecs/avif/dec/avif_node_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/avif/dec/avif_node_dec.wasm -------------------------------------------------------------------------------- /codecs/avif/enc/avif_node_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/avif/enc/avif_node_enc.wasm -------------------------------------------------------------------------------- /codecs/hqx/pkg/squooshhqx_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/hqx/pkg/squooshhqx_bg.wasm -------------------------------------------------------------------------------- /codecs/imagequant/imagequant.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/imagequant/imagequant.wasm -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc_mt_simd.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/jxl/enc/jxl_enc_mt_simd.wasm -------------------------------------------------------------------------------- /codecs/mozjpeg/enc/mozjpeg_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/mozjpeg/enc/mozjpeg_enc.wasm -------------------------------------------------------------------------------- /codecs/png/pkg/squoosh_png_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/png/pkg/squoosh_png_bg.wasm -------------------------------------------------------------------------------- /codecs/webp/dec/webp_node_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/webp/dec/webp_node_dec.wasm -------------------------------------------------------------------------------- /codecs/webp/enc/webp_enc_simd.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/webp/enc/webp_enc_simd.wasm -------------------------------------------------------------------------------- /codecs/webp/enc/webp_node_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/webp/enc/webp_node_enc.wasm -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc_mt_simd.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/wp2/enc/wp2_enc_mt_simd.wasm -------------------------------------------------------------------------------- /codecs/avif/enc/avif_node_enc_mt.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/avif/enc/avif_node_enc_mt.wasm -------------------------------------------------------------------------------- /codecs/imagequant/imagequant_node.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/imagequant/imagequant_node.wasm -------------------------------------------------------------------------------- /codecs/mozjpeg/dec/mozjpeg_node_dec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/mozjpeg/dec/mozjpeg_node_dec.wasm -------------------------------------------------------------------------------- /codecs/mozjpeg/enc/mozjpeg_node_enc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/mozjpeg/enc/mozjpeg_node_enc.wasm -------------------------------------------------------------------------------- /codecs/oxipng/pkg/squoosh_oxipng_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/oxipng/pkg/squoosh_oxipng_bg.wasm -------------------------------------------------------------------------------- /codecs/resize/pkg/squoosh_resize_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/resize/pkg/squoosh_resize_bg.wasm -------------------------------------------------------------------------------- /codecs/oxipng/pkg-parallel/squoosh_oxipng_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mario-huang/squoosh-browser/HEAD/codecs/oxipng/pkg-parallel/squoosh_oxipng_bg.wasm -------------------------------------------------------------------------------- /codecs/avif/dec/avif_dec.d.ts: -------------------------------------------------------------------------------- 1 | export interface AVIFModule extends EmscriptenWasm.Module { 2 | decode(data: BufferSource): ImageData | null; 3 | } 4 | 5 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 6 | 7 | export default moduleFactory; 8 | -------------------------------------------------------------------------------- /codecs/jxl/dec/jxl_dec.d.ts: -------------------------------------------------------------------------------- 1 | export interface JXLModule extends EmscriptenWasm.Module { 2 | decode(data: BufferSource): ImageData | null; 3 | } 4 | 5 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 6 | 7 | export default moduleFactory; 8 | -------------------------------------------------------------------------------- /codecs/webp/dec/webp_dec.d.ts: -------------------------------------------------------------------------------- 1 | export interface WebPModule extends EmscriptenWasm.Module { 2 | decode(data: BufferSource): ImageData | null; 3 | } 4 | 5 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 6 | 7 | export default moduleFactory; 8 | -------------------------------------------------------------------------------- /codecs/wp2/dec/wp2_dec.d.ts: -------------------------------------------------------------------------------- 1 | export interface WP2Module extends EmscriptenWasm.Module { 2 | decode(data: BufferSource): ImageData | null; 3 | } 4 | 5 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 6 | 7 | export default moduleFactory; 8 | -------------------------------------------------------------------------------- /demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Getting Started 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/features/encoders/browserPNG/client/index.ts: -------------------------------------------------------------------------------- 1 | import { canvasEncode } from '../../../../client/lazy-app/util/canvas'; 2 | import { EncodeOptions, mimeType } from '../shared/meta'; 3 | 4 | export const browserPNGEncode = ( 5 | imageData: ImageData, 6 | options: EncodeOptions, 7 | ) => canvasEncode(imageData, mimeType); 8 | -------------------------------------------------------------------------------- /src/features/encoders/browserJPEG/client/index.ts: -------------------------------------------------------------------------------- 1 | import { canvasEncode } from '../../../../client/lazy-app/util/canvas'; 2 | import { mimeType, EncodeOptions } from '../shared/meta'; 3 | 4 | export const browserJPEGEncode = ( 5 | imageData: ImageData, 6 | options: EncodeOptions, 7 | ) => canvasEncode(imageData, mimeType, options.quality); 8 | 9 | -------------------------------------------------------------------------------- /codecs/avif/dec/README.md: -------------------------------------------------------------------------------- 1 | # AVIF decoder 2 | 3 | - Source: 4 | - Version: v0.5.4 5 | 6 | ## Example 7 | 8 | See `example.html` 9 | 10 | ## API 11 | 12 | ### `RawImage decode(std::string buffer)` 13 | 14 | Decodes the given avif buffer into raw RGBA. `RawImage` is a class with 3 fields: `buffer`, `width`, and `height`. 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // "downlevelIteration": true, 4 | "declaration": true, 5 | "removeComments": true, 6 | // "allowSyntheticDefaultImports": true, 7 | // "sourceMap": false, 8 | "outDir": "./dist", 9 | // "incremental": true, 10 | "target":"ESNext", 11 | "module":"ESNext", 12 | "moduleResolution": "node", 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /src/features/encoders/browserGIF/client/index.ts: -------------------------------------------------------------------------------- 1 | import { canvasEncodeTest, canvasEncode } from '../../../../client/lazy-app/util/canvas'; 2 | import { EncodeOptions, mimeType } from '../shared/meta'; 3 | 4 | export const featureTest = () => canvasEncodeTest(mimeType); 5 | export const browserGIFEncode = ( 6 | imageData: ImageData, 7 | options: EncodeOptions, 8 | ) => canvasEncode(imageData, mimeType); 9 | -------------------------------------------------------------------------------- /codecs/oxipng/pkg/squoosh_oxipng_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function optimise(a: number, b: number, c: number, d: number, e: number): void; 5 | export function __wbindgen_add_to_stack_pointer(a: number): number; 6 | export function __wbindgen_malloc(a: number): number; 7 | export function __wbindgen_free(a: number, b: number): void; 8 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "build": "webpack", 7 | "start": "webpack serve --open" 8 | }, 9 | "dependencies": { 10 | "@yireen/squoosh-browser": "1.0.6" 11 | }, 12 | "devDependencies": { 13 | "webpack": "5.51.1", 14 | "webpack-cli": "4.8.0", 15 | "webpack-dev-server": "4.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /codecs/png/pkg/squoosh_png_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function encode(a: number, b: number, c: number, d: number, e: number): void; 5 | export function decode(a: number, b: number): number; 6 | export function __wbindgen_free(a: number, b: number): void; 7 | export function __wbindgen_add_to_stack_pointer(a: number): number; 8 | export function __wbindgen_malloc(a: number): number; 9 | -------------------------------------------------------------------------------- /codecs/resize/pkg/squoosh_resize_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function resize(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number): void; 5 | export function __wbindgen_add_to_stack_pointer(a: number): number; 6 | export function __wbindgen_malloc(a: number): number; 7 | export function __wbindgen_free(a: number, b: number): void; 8 | -------------------------------------------------------------------------------- /src/features/processors/quantize/client/index.ts: -------------------------------------------------------------------------------- 1 | import { Options as QuantizeOptions } from '../shared/meta'; 2 | import { 3 | inputFieldValueAsNumber, 4 | konami, 5 | preventDefault, 6 | } from '../../../../client/lazy-app/util'; 7 | 8 | 9 | const konamiPromise = konami(); 10 | 11 | interface Props { 12 | options: QuantizeOptions; 13 | onChange(newOptions: QuantizeOptions): void; 14 | } 15 | 16 | interface State { 17 | extendedSettings: boolean; 18 | } 19 | -------------------------------------------------------------------------------- /demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | devtool: 'inline-source-map', 6 | devServer: { 7 | static: './dist', 8 | }, 9 | entry: './src/index.js', 10 | output: { 11 | filename: 'main.js', 12 | path: path.resolve(__dirname, 'dist'), 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.wasm/, 18 | type: 'asset/resource' 19 | } 20 | ] 21 | } 22 | }; -------------------------------------------------------------------------------- /src/features/encoders/mozJPEG/client/index.ts: -------------------------------------------------------------------------------- 1 | import { EncodeOptions, MozJpegColorSpace } from '../shared/meta'; 2 | import encode from '../worker/mozjpegEncode'; 3 | 4 | export function mozJPEGEncode( 5 | imageData: ImageData, 6 | options: EncodeOptions, 7 | ) { 8 | return encode(imageData, options); 9 | } 10 | 11 | interface Props { 12 | options: EncodeOptions; 13 | onChange(newOptions: EncodeOptions): void; 14 | } 15 | 16 | interface State { 17 | showAdvanced: boolean; 18 | } 19 | -------------------------------------------------------------------------------- /src/features/client-utils/index.ts: -------------------------------------------------------------------------------- 1 | interface EncodeOptions { 2 | quality: number; 3 | } 4 | 5 | interface Props { 6 | options: EncodeOptions; 7 | onChange(newOptions: EncodeOptions): void; 8 | } 9 | 10 | interface QualityOptionArg { 11 | min?: number; 12 | max?: number; 13 | step?: number; 14 | } 15 | 16 | type Constructor = new (...args: any[]) => T; 17 | 18 | // TypeScript requires an exported type for returned classes. This serves as the 19 | // type for the class returned by `qualityOption`. -------------------------------------------------------------------------------- /codecs/imagequant/imagequant.d.ts: -------------------------------------------------------------------------------- 1 | export interface QuantizerModule extends EmscriptenWasm.Module { 2 | quantize( 3 | data: BufferSource, 4 | width: number, 5 | height: number, 6 | numColors: number, 7 | dither: number, 8 | ): Uint8ClampedArray; 9 | zx_quantize( 10 | data: BufferSource, 11 | width: number, 12 | height: number, 13 | dither: number, 14 | ): Uint8ClampedArray; 15 | } 16 | 17 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 18 | 19 | export default moduleFactory; 20 | -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc.d.ts: -------------------------------------------------------------------------------- 1 | export interface EncodeOptions { 2 | speed: number; 3 | quality: number; 4 | progressive: boolean; 5 | epf: number; 6 | nearLossless: number; 7 | lossyPalette: boolean; 8 | decodingSpeedTier: number; 9 | } 10 | 11 | export interface JXLModule extends EmscriptenWasm.Module { 12 | encode( 13 | data: BufferSource, 14 | width: number, 15 | height: number, 16 | options: EncodeOptions, 17 | ): Uint8Array | null; 18 | } 19 | 20 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 21 | 22 | export default moduleFactory; 23 | -------------------------------------------------------------------------------- /src/features/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /codecs/avif/enc/avif_enc.d.ts: -------------------------------------------------------------------------------- 1 | export const enum AVIFTune { 2 | auto, 3 | psnr, 4 | ssim, 5 | } 6 | 7 | export interface EncodeOptions { 8 | cqLevel: number; 9 | denoiseLevel: number; 10 | cqAlphaLevel: number; 11 | tileRowsLog2: number; 12 | tileColsLog2: number; 13 | speed: number; 14 | subsample: number; 15 | chromaDeltaQ: boolean; 16 | sharpness: number; 17 | tune: AVIFTune; 18 | } 19 | 20 | export interface AVIFModule extends EmscriptenWasm.Module { 21 | encode( 22 | data: BufferSource, 23 | width: number, 24 | height: number, 25 | options: EncodeOptions, 26 | ): Uint8Array | null; 27 | } 28 | 29 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 30 | 31 | export default moduleFactory; 32 | -------------------------------------------------------------------------------- /src/features/encoders/avif/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/avif/worker/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/mozJPEG/client/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/mozJPEG/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/mozJPEG/worker/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/browserGIF/client/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/browserGIF/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/browserJPEG/client/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/browserJPEG/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/browserPNG/client/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/browserPNG/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/preprocessors/rotate/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/processors/quantize/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/processors/quantize/worker/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/processors/resize/client/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/processors/resize/shared/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/processors/resize/worker/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | -------------------------------------------------------------------------------- /src/features/encoders/jxl/client/index.ts: -------------------------------------------------------------------------------- 1 | import { EncodeOptions } from '../shared/meta'; 2 | import { preventDefault, shallowEqual } from '../../../../client/lazy-app/util'; 3 | import encode from '../worker/jxlEncode'; 4 | 5 | export const jxlEncode = ( 6 | imageData: ImageData, 7 | options: EncodeOptions, 8 | ) => encode(imageData, options); 9 | 10 | interface Props { 11 | options: EncodeOptions; 12 | onChange(newOptions: EncodeOptions): void; 13 | } 14 | 15 | interface State { 16 | options: EncodeOptions; 17 | effort: number; 18 | quality: number; 19 | progressive: boolean; 20 | edgePreservingFilter: number; 21 | lossless: boolean; 22 | slightLoss: boolean; 23 | autoEdgePreservingFilter: boolean; 24 | decodingSpeedTier: number; 25 | } 26 | 27 | const maxSpeed = 7; 28 | -------------------------------------------------------------------------------- /codecs/avif/enc/README.md: -------------------------------------------------------------------------------- 1 | # AVIF encoder 2 | 3 | - Source: 4 | - Version: v0.5.4 5 | 6 | ## Example 7 | 8 | Run example.js 9 | 10 | ## API 11 | 12 | ### `Uint8Array encode(std::string image_in, int image_width, int image_height, AvifOptions opts)` 13 | 14 | Encodes the given image with given dimension to AVIF. Options looks like this: 15 | 16 | ```c++ 17 | struct AvifOptions { 18 | // 0 = lossless 19 | // 63 = worst quality 20 | int minQuantizer; 21 | int maxQuantizer; 22 | 23 | // [0 - 6] 24 | // Creates 2^n tiles in that dimension 25 | int tileRowsLog2; 26 | int tileColsLog2; 27 | 28 | // 0 = slowest 29 | // 10 = fastest 30 | int speed; 31 | 32 | // 0 = 4:2:0 33 | // 1 = 4:2:2 34 | // 2 = 4:4:4 35 | int subsample; 36 | }; 37 | ``` 38 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import Compress from '@yireen/squoosh-browser' 2 | 3 | const file = document.getElementById('file'); 4 | file.onchange = async (event) => { 5 | const image = event.target.files[0]; 6 | console.log('start'); 7 | console.log('time', new Date()); 8 | console.log('size', image.size); 9 | const compress = new Compress(image); 10 | const compressFile = await compress.process(); 11 | console.log('end'); 12 | console.log('time', new Date()); 13 | console.log('size', compressFile.size); 14 | const reader = new FileReader() 15 | reader.onload = () => { 16 | const img = new Image() 17 | img.src = reader.result 18 | img.style = 'width:30%;' 19 | document.body.appendChild(img) // reader.result为获取结果 20 | } 21 | reader.readAsDataURL(compressFile) 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/features/encoders/oxiPNG/client/index.ts: -------------------------------------------------------------------------------- 1 | import { canvasEncode } from '../../../../client/lazy-app/util/canvas'; 2 | import { 3 | abortable, 4 | blobToArrayBuffer, 5 | inputFieldChecked, 6 | } from '../../../../client/lazy-app/util'; 7 | import { EncodeOptions } from '../shared/meta'; 8 | import { inputFieldValueAsNumber, preventDefault } from '../../../../client/lazy-app/util'; 9 | import encode from '../worker/oxipngEncode'; 10 | 11 | export async function oxiPNGEncode( 12 | imageData: ImageData, 13 | options: EncodeOptions, 14 | ) { 15 | const pngBlob = await canvasEncode(imageData, 'image/png'); 16 | const pngBuffer = await blobToArrayBuffer(pngBlob); 17 | return encode(pngBuffer, options); 18 | } 19 | 20 | type Props = { 21 | options: EncodeOptions; 22 | onChange(newOptions: EncodeOptions): void; 23 | }; 24 | -------------------------------------------------------------------------------- /src/features/preprocessors/rotate/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | export interface Options { 14 | rotate: 0 | 90 | 180 | 270; 15 | } 16 | 17 | export const defaultOptions: Options = { 18 | rotate: 0, 19 | }; 20 | -------------------------------------------------------------------------------- /src/features/encoders/avif/client/index.ts: -------------------------------------------------------------------------------- 1 | import { EncodeOptions, defaultOptions, AVIFTune } from '../shared/meta'; 2 | import encode from '../worker/avifEncode'; 3 | 4 | 5 | export const avifEncode = ( 6 | imageData: ImageData, 7 | options: EncodeOptions, 8 | ) => encode(imageData, options); 9 | 10 | interface Props { 11 | options: EncodeOptions; 12 | onChange(newOptions: EncodeOptions): void; 13 | } 14 | 15 | interface State { 16 | options: EncodeOptions; 17 | lossless: boolean; 18 | quality: number; 19 | showAdvanced: boolean; 20 | separateAlpha: boolean; 21 | alphaQuality: number; 22 | chromaDeltaQ: boolean; 23 | subsample: number; 24 | tileRows: number; 25 | tileCols: number; 26 | effort: number; 27 | sharpness: number; 28 | denoiseLevel: number; 29 | aqMode: number; 30 | tune: AVIFTune; 31 | } 32 | 33 | const maxQuant = 63; 34 | const maxSpeed = 10; 35 | -------------------------------------------------------------------------------- /src/features/encoders/browserGIF/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | export interface EncodeOptions {} 14 | 15 | export const label = 'Browser GIF'; 16 | export const mimeType = 'image/gif'; 17 | export const extension = 'gif'; 18 | export const defaultOptions: EncodeOptions = {}; 19 | -------------------------------------------------------------------------------- /src/features/encoders/browserPNG/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | export interface EncodeOptions {} 14 | 15 | export const label = 'Browser PNG'; 16 | export const mimeType = 'image/png'; 17 | export const extension = 'png'; 18 | export const defaultOptions: EncodeOptions = {}; 19 | -------------------------------------------------------------------------------- /src/features/processors/quantize/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | export interface Options { 14 | zx: number; 15 | maxNumColors: number; 16 | dither: number; 17 | } 18 | 19 | export const defaultOptions: Options = { 20 | zx: 0, 21 | maxNumColors: 256, 22 | dither: 1.0, 23 | }; 24 | -------------------------------------------------------------------------------- /codecs/oxipng/pkg-parallel/squoosh_oxipng_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export function __wasm_init_memory(): void; 4 | export function optimise(a: number, b: number, c: number, d: number, e: number): void; 5 | export function __wbg_wbg_rayon_poolbuilder_free(a: number): void; 6 | export function wbg_rayon_poolbuilder_numThreads(a: number): number; 7 | export function wbg_rayon_poolbuilder_receiver(a: number): number; 8 | export function wbg_rayon_poolbuilder_build(a: number): void; 9 | export function initThreadPool(a: number): number; 10 | export function wbg_rayon_start_worker(a: number): void; 11 | export const __wbindgen_export_0: WebAssembly.Memory; 12 | export function __wbindgen_add_to_stack_pointer(a: number): number; 13 | export function __wbindgen_malloc(a: number): number; 14 | export function __wbindgen_free(a: number, b: number): void; 15 | export function __wbindgen_start(): void; 16 | -------------------------------------------------------------------------------- /src/features/encoders/wp2/client/index.ts: -------------------------------------------------------------------------------- 1 | import { EncodeOptions, UVMode, Csp } from '../shared/meta'; 2 | import { defaultOptions } from '../shared/meta'; 3 | import { preventDefault, shallowEqual } from '../../../../client/lazy-app/util'; 4 | import encode from '../worker/wp2Encode'; 5 | 6 | export const wp2Encode = ( 7 | imageData: ImageData, 8 | options: EncodeOptions, 9 | ) => encode(imageData, options); 10 | 11 | interface Props { 12 | options: EncodeOptions; 13 | onChange(newOptions: EncodeOptions): void; 14 | } 15 | 16 | interface State { 17 | options: EncodeOptions; 18 | effort: number; 19 | quality: number; 20 | alphaQuality: number; 21 | passes: number; 22 | sns: number; 23 | uvMode: number; 24 | lossless: boolean; 25 | slightLoss: number; 26 | colorSpace: number; 27 | errorDiffusion: number; 28 | useRandomMatrix: boolean; 29 | showAdvanced: boolean; 30 | separateAlpha: boolean; 31 | } 32 | -------------------------------------------------------------------------------- /src/features/encoders/browserJPEG/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | export interface EncodeOptions { 14 | quality: number; 15 | } 16 | 17 | export const label = 'Browser JPEG'; 18 | export const mimeType = 'image/jpeg'; 19 | export const extension = 'jpg'; 20 | export const defaultOptions: EncodeOptions = { quality: 0.75 }; 21 | -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc.d.ts: -------------------------------------------------------------------------------- 1 | export interface EncodeOptions { 2 | quality: number; 3 | alpha_quality: number; 4 | effort: number; 5 | pass: number; 6 | sns: number; 7 | uv_mode: UVMode; 8 | csp_type: Csp; 9 | error_diffusion: number; 10 | use_random_matrix: boolean; 11 | } 12 | 13 | export const enum UVMode { 14 | UVModeAdapt = 0, // Mix of 420 and 444 (per block) 15 | UVMode420, // All blocks 420 16 | UVMode444, // All blocks 444 17 | UVModeAuto, // Choose any of the above automatically 18 | } 19 | 20 | export const enum Csp { 21 | kYCoCg, 22 | kYCbCr, 23 | kCustom, 24 | kYIQ, 25 | } 26 | 27 | export interface WP2Module extends EmscriptenWasm.Module { 28 | encode( 29 | data: BufferSource, 30 | width: number, 31 | height: number, 32 | options: EncodeOptions, 33 | ): Uint8Array | null; 34 | } 35 | 36 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 37 | 38 | export default moduleFactory; 39 | -------------------------------------------------------------------------------- /src/features/encoders/oxiPNG/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | export interface EncodeOptions { 14 | level: number; 15 | interlace: boolean; 16 | } 17 | 18 | export const label = 'OxiPNG'; 19 | export const mimeType = 'image/png'; 20 | export const extension = 'png'; 21 | 22 | export const defaultOptions: EncodeOptions = { 23 | level: 2, 24 | interlace: false, 25 | }; 26 | -------------------------------------------------------------------------------- /codecs/mozjpeg/enc/mozjpeg_enc.d.ts: -------------------------------------------------------------------------------- 1 | export const enum MozJpegColorSpace { 2 | GRAYSCALE = 1, 3 | RGB, 4 | YCbCr, 5 | } 6 | 7 | export interface EncodeOptions { 8 | quality: number; 9 | baseline: boolean; 10 | arithmetic: boolean; 11 | progressive: boolean; 12 | optimize_coding: boolean; 13 | smoothing: number; 14 | color_space: MozJpegColorSpace; 15 | quant_table: number; 16 | trellis_multipass: boolean; 17 | trellis_opt_zero: boolean; 18 | trellis_opt_table: boolean; 19 | trellis_loops: number; 20 | auto_subsample: boolean; 21 | chroma_subsample: number; 22 | separate_chroma_quality: boolean; 23 | chroma_quality: number; 24 | } 25 | 26 | export interface MozJPEGModule extends EmscriptenWasm.Module { 27 | encode( 28 | data: BufferSource, 29 | width: number, 30 | height: number, 31 | options: EncodeOptions, 32 | ): Uint8Array; 33 | } 34 | 35 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 36 | 37 | export default moduleFactory; 38 | -------------------------------------------------------------------------------- /src/features/worker-utils/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | export function initEmscriptenModule( 14 | moduleFactory: EmscriptenWasm.ModuleFactory, 15 | ): Promise { 16 | return moduleFactory({ 17 | // Just to be safe, don't automatically invoke any wasm functions 18 | noInitialRun: true, 19 | }); 20 | } 21 | 22 | export function blobToArrayBuffer(blob: Blob): Promise { 23 | return new Response(blob).arrayBuffer(); 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mario 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/features/encoders/jxl/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { EncodeOptions } from '../../../../../codecs/jxl/enc/jxl_enc.js'; 14 | 15 | export { EncodeOptions }; 16 | 17 | export const label = 'JPEG XL (beta)'; 18 | export const mimeType = 'image/jxl'; 19 | export const extension = 'jxl'; 20 | export const defaultOptions: EncodeOptions = { 21 | speed: 4, 22 | quality: 75, 23 | progressive: false, 24 | epf: -1, 25 | nearLossless: 0, 26 | lossyPalette: false, 27 | decodingSpeedTier: 0, 28 | }; 29 | -------------------------------------------------------------------------------- /codecs/oxipng/pkg/squoosh_oxipng.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {Uint8Array} data 5 | * @param {number} level 6 | * @param {boolean} interlace 7 | * @returns {Uint8Array} 8 | */ 9 | export function optimise(data: Uint8Array, level: number, interlace: boolean): Uint8Array; 10 | 11 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 12 | 13 | export interface InitOutput { 14 | readonly memory: WebAssembly.Memory; 15 | readonly optimise: (a: number, b: number, c: number, d: number, e: number) => void; 16 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 17 | readonly __wbindgen_malloc: (a: number) => number; 18 | readonly __wbindgen_free: (a: number, b: number) => void; 19 | } 20 | 21 | /** 22 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 23 | * for everything else, calls `WebAssembly.instantiate` directly. 24 | * 25 | * @param {InitInput | Promise} module_or_path 26 | * 27 | * @returns {Promise} 28 | */ 29 | export default function init (module_or_path?: InitInput | Promise): Promise; 30 | -------------------------------------------------------------------------------- /codecs/webp/enc/webp_enc.d.ts: -------------------------------------------------------------------------------- 1 | export interface EncodeOptions { 2 | quality: number; 3 | target_size: number; 4 | target_PSNR: number; 5 | method: number; 6 | sns_strength: number; 7 | filter_strength: number; 8 | filter_sharpness: number; 9 | filter_type: number; 10 | partitions: number; 11 | segments: number; 12 | pass: number; 13 | show_compressed: number; 14 | preprocessing: number; 15 | autofilter: number; 16 | partition_limit: number; 17 | alpha_compression: number; 18 | alpha_filtering: number; 19 | alpha_quality: number; 20 | lossless: number; 21 | exact: number; 22 | image_hint: number; 23 | emulate_jpeg_size: number; 24 | thread_level: number; 25 | low_memory: number; 26 | near_lossless: number; 27 | use_delta_palette: number; 28 | use_sharp_yuv: number; 29 | } 30 | 31 | export interface WebPModule extends EmscriptenWasm.Module { 32 | encode( 33 | data: BufferSource, 34 | width: number, 35 | height: number, 36 | options: EncodeOptions, 37 | ): Uint8Array | null; 38 | } 39 | 40 | declare var moduleFactory: EmscriptenWasm.ModuleFactory; 41 | 42 | export default moduleFactory; 43 | -------------------------------------------------------------------------------- /src/client/lazy-app/util/web-codecs/index.ts: -------------------------------------------------------------------------------- 1 | import { drawableToImageData } from '../canvas'; 2 | 3 | const hasImageDecoder = typeof ImageDecoder !== 'undefined'; 4 | 5 | export async function isTypeSupported(mimeType: string): Promise { 6 | if (!hasImageDecoder) return false; 7 | // Some old versions of this API threw here. 8 | // It only impacted folks with experimental web platform flags enabled in Chrome 90. 9 | // The API was updated in Chrome 91. 10 | try { 11 | return await ImageDecoder.isTypeSupported(mimeType); 12 | } catch (err) { 13 | return false; 14 | } 15 | } 16 | 17 | export async function decode( 18 | blob: Blob | File, 19 | mimeType: string, 20 | ): Promise { 21 | if (!hasImageDecoder) { 22 | throw Error( 23 | `This browser does not support ImageDecoder. This function should not have been called.`, 24 | ); 25 | } 26 | const decoder = new ImageDecoder({ 27 | type: mimeType, 28 | // Non-obvious way to turn an Blob into a ReadableStream 29 | data: new Response(blob).body!, 30 | }); 31 | const { image } = await decoder.decode(); 32 | return drawableToImageData(image); 33 | } 34 | -------------------------------------------------------------------------------- /src/features/processors/resize/shared/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export function getContainOffsets( 15 | sw: number, 16 | sh: number, 17 | dw: number, 18 | dh: number, 19 | ) { 20 | const currentAspect = sw / sh; 21 | const endAspect = dw / dh; 22 | 23 | if (endAspect > currentAspect) { 24 | const newSh = sw / endAspect; 25 | const newSy = (sh - newSh) / 2; 26 | return { sw, sh: newSh, sx: 0, sy: newSy }; 27 | } 28 | 29 | const newSw = sh * endAspect; 30 | const newSx = (sw - newSw) / 2; 31 | return { sh, sw: newSw, sx: newSx, sy: 0 }; 32 | } 33 | -------------------------------------------------------------------------------- /src/features/encoders/avif/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import { EncodeOptions, AVIFTune } from '../../../../../codecs/avif/enc/avif_enc.js'; 14 | 15 | export { EncodeOptions, AVIFTune }; 16 | 17 | export const label = 'AVIF'; 18 | export const mimeType = 'image/avif'; 19 | export const extension = 'avif'; 20 | export const defaultOptions: EncodeOptions = { 21 | cqLevel: 33, 22 | cqAlphaLevel: -1, 23 | denoiseLevel: 0, 24 | tileColsLog2: 0, 25 | tileRowsLog2: 0, 26 | speed: 6, 27 | subsample: 1, 28 | chromaDeltaQ: false, 29 | sharpness: 0, 30 | tune: AVIFTune.auto, 31 | }; 32 | -------------------------------------------------------------------------------- /codecs/hqx/pkg/squooshhqx.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {Uint32Array} input_image 5 | * @param {number} input_width 6 | * @param {number} input_height 7 | * @param {number} factor 8 | * @returns {Uint32Array} 9 | */ 10 | export function resize(input_image: Uint32Array, input_width: number, input_height: number, factor: number): Uint32Array; 11 | 12 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 13 | 14 | export interface InitOutput { 15 | readonly memory: WebAssembly.Memory; 16 | readonly resize: (a: number, b: number, c: number, d: number, e: number, f: number) => void; 17 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 18 | readonly __wbindgen_malloc: (a: number) => number; 19 | readonly __wbindgen_free: (a: number, b: number) => void; 20 | } 21 | 22 | /** 23 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 24 | * for everything else, calls `WebAssembly.instantiate` directly. 25 | * 26 | * @param {InitInput | Promise} module_or_path 27 | * 28 | * @returns {Promise} 29 | */ 30 | export default function init (module_or_path?: InitInput | Promise): Promise; 31 | -------------------------------------------------------------------------------- /src/features/encoders/wp2/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { EncodeOptions } from '../../../../../codecs/wp2/enc/wp2_enc.js'; 14 | import { UVMode, Csp } from '../../../../../codecs/wp2/enc/wp2_enc.js'; 15 | 16 | export { EncodeOptions, UVMode, Csp }; 17 | 18 | export const label = 'WebP v2 (unstable)'; 19 | export const mimeType = 'image/webp2'; 20 | export const extension = 'wp2'; 21 | export const defaultOptions: EncodeOptions = { 22 | quality: 75, 23 | alpha_quality: 75, 24 | effort: 5, 25 | pass: 1, 26 | sns: 50, 27 | uv_mode: UVMode.UVModeAuto, 28 | csp_type: Csp.kYCoCg, 29 | error_diffusion: 0, 30 | use_random_matrix: false, 31 | }; 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # squoosh-browser 2 | An image compression tool run in browser while @squoosh/lib can not. 3 | 4 | # origin 5 | [Squoosh] is an image compression web app that reduces image sizes through numerous formats. 6 | 7 | Since @squoosh/lib can not run in browser, squoosh-browser is designed to solve it. 8 | 9 | It can process almost all image formats, such as "pdf", "gif", "png", "jpeg", "bmp", "tiff", "webp", "webp2", "avif", "jxl". 10 | 11 | # installation 12 | ``` 13 | yarn add @yireen/squoosh-browser 14 | ``` 15 | 16 | If you use webpack4, you also need 17 | ``` 18 | yarn add file-loader --dev 19 | ``` 20 | 21 | # config 22 | For webpack5 23 | ```js 24 | // webpack.config.js 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.wasm/, 29 | type: 'asset/resource' 30 | } 31 | ] 32 | } 33 | ``` 34 | 35 | For webpack4 36 | ```js 37 | // webpack.config.js 38 | module: { 39 | rules: [ 40 | { 41 | test: /\.wasm/, 42 | use: [ 43 | { 44 | loader: 'file-loader' 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | ``` 51 | 52 | # usage 53 | ```ts 54 | import Compress from '@yireen/squoosh-browser' 55 | 56 | const compress = new Compress(image); 57 | const compressFile = await compress.process(); 58 | } 59 | ``` 60 | 61 | 62 | [squoosh]: https://squoosh.app -------------------------------------------------------------------------------- /codecs/png/pkg/squoosh_png.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {Uint8Array} data 5 | * @param {number} width 6 | * @param {number} height 7 | * @returns {Uint8Array} 8 | */ 9 | export function encode(data: Uint8Array, width: number, height: number): Uint8Array; 10 | /** 11 | * @param {Uint8Array} data 12 | * @returns {ImageData} 13 | */ 14 | export function decode(data: Uint8Array): ImageData; 15 | 16 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 17 | 18 | export interface InitOutput { 19 | readonly memory: WebAssembly.Memory; 20 | readonly encode: (a: number, b: number, c: number, d: number, e: number) => void; 21 | readonly decode: (a: number, b: number) => number; 22 | readonly __wbindgen_free: (a: number, b: number) => void; 23 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 24 | readonly __wbindgen_malloc: (a: number) => number; 25 | } 26 | 27 | /** 28 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 29 | * for everything else, calls `WebAssembly.instantiate` directly. 30 | * 31 | * @param {InitInput | Promise} module_or_path 32 | * 33 | * @returns {Promise} 34 | */ 35 | export default function init (module_or_path?: InitInput | Promise): Promise; 36 | -------------------------------------------------------------------------------- /src/features/decoders/jxl/worker/jxlDecode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import jxlDecoder, { JXLModule } from '../../../../../codecs/jxl/dec/jxl_dec.js'; 14 | import { initEmscriptenModule, blobToArrayBuffer } from '../../../../features/worker-utils'; 15 | 16 | let emscriptenModule: Promise; 17 | 18 | export default async function decode(blob: Blob): Promise { 19 | if (!emscriptenModule) { 20 | emscriptenModule = initEmscriptenModule(jxlDecoder); 21 | } 22 | 23 | const [module, data] = await Promise.all([ 24 | emscriptenModule, 25 | blobToArrayBuffer(blob), 26 | ]); 27 | 28 | const result = module.decode(data); 29 | if (!result) throw new Error('Decoding error'); 30 | return result; 31 | } 32 | -------------------------------------------------------------------------------- /src/features/decoders/wp2/worker/wp2Decode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import wp2Decoder, { WP2Module } from '../../../../../codecs/wp2/dec/wp2_dec.js'; 14 | import { initEmscriptenModule, blobToArrayBuffer } from '../../../../features/worker-utils'; 15 | 16 | let emscriptenModule: Promise; 17 | 18 | export default async function decode(blob: Blob): Promise { 19 | if (!emscriptenModule) { 20 | emscriptenModule = initEmscriptenModule(wp2Decoder); 21 | } 22 | 23 | const [module, data] = await Promise.all([ 24 | emscriptenModule, 25 | blobToArrayBuffer(blob), 26 | ]); 27 | 28 | const result = module.decode(data); 29 | if (!result) throw new Error('Decoding error'); 30 | return result; 31 | } 32 | -------------------------------------------------------------------------------- /src/features/decoders/webp/worker/webpDecode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { WebPModule } from '../../../../../codecs/webp/dec/webp_dec.js'; 14 | import { initEmscriptenModule, blobToArrayBuffer } from '../../../../features/worker-utils'; 15 | 16 | let emscriptenModule: Promise; 17 | 18 | import decoder from '../../../../../codecs/webp/dec/webp_dec.js' 19 | 20 | export default async function decode(blob: Blob): Promise { 21 | if (!emscriptenModule) { 22 | emscriptenModule = initEmscriptenModule(decoder); 23 | } 24 | 25 | const [module, data] = await Promise.all([ 26 | emscriptenModule, 27 | blobToArrayBuffer(blob), 28 | ]); 29 | 30 | const result = module.decode(data); 31 | if (!result) throw new Error('Decoding error'); 32 | return result; 33 | } 34 | -------------------------------------------------------------------------------- /src/features/decoders/avif/worker/avifDecode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { AVIFModule } from '../../../../../codecs/avif/dec/avif_dec.js'; 14 | import { initEmscriptenModule, blobToArrayBuffer } from '../../../../features/worker-utils'; 15 | 16 | import decoder from '../../../../../codecs/avif/dec/avif_dec.js'; 17 | 18 | let emscriptenModule: Promise; 19 | 20 | export default async function avifDecode(blob: Blob): Promise { 21 | if (!emscriptenModule) { 22 | emscriptenModule = initEmscriptenModule(decoder); 23 | } 24 | 25 | const [module, data] = await Promise.all([ 26 | emscriptenModule, 27 | blobToArrayBuffer(blob), 28 | ]); 29 | 30 | const result = module.decode(data); 31 | if (!result) throw new Error('Decoding error'); 32 | return result; 33 | } 34 | -------------------------------------------------------------------------------- /src/features/encoders/mozJPEG/worker/mozjpegEncode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import mozjpeg_enc, { MozJPEGModule } from '../../../../../codecs/mozjpeg/enc/mozjpeg_enc.js'; 14 | import { EncodeOptions } from '../shared/meta'; 15 | import { initEmscriptenModule } from '../../../../features/worker-utils'; 16 | 17 | let emscriptenModule: Promise; 18 | 19 | export default async function encode( 20 | data: ImageData, 21 | options: EncodeOptions, 22 | ): Promise { 23 | if (!emscriptenModule) { 24 | emscriptenModule = initEmscriptenModule(mozjpeg_enc); 25 | } 26 | 27 | const module = await emscriptenModule; 28 | const resultView = module.encode(data.data, data.width, data.height, options); 29 | // wasm can’t run on SharedArrayBuffers, so we hard-cast to ArrayBuffer. 30 | return resultView.buffer as ArrayBuffer; 31 | } 32 | -------------------------------------------------------------------------------- /src/features/encoders/mozJPEG/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import { 14 | EncodeOptions, 15 | MozJpegColorSpace, 16 | } from '../../../../../codecs/mozjpeg/enc/mozjpeg_enc.js'; 17 | export { EncodeOptions, MozJpegColorSpace }; 18 | 19 | export const label = 'MozJPEG'; 20 | export const mimeType = 'image/jpeg'; 21 | export const extension = 'jpg'; 22 | export const defaultOptions: EncodeOptions = { 23 | quality: 75, 24 | baseline: false, 25 | arithmetic: false, 26 | progressive: true, 27 | optimize_coding: true, 28 | smoothing: 0, 29 | color_space: MozJpegColorSpace.YCbCr, 30 | quant_table: 3, 31 | trellis_multipass: false, 32 | trellis_opt_zero: false, 33 | trellis_opt_table: false, 34 | trellis_loops: 1, 35 | auto_subsample: true, 36 | chroma_subsample: 2, 37 | separate_chroma_quality: false, 38 | chroma_quality: 75, 39 | }; 40 | -------------------------------------------------------------------------------- /src/features/encoders/webP/client/index.ts: -------------------------------------------------------------------------------- 1 | import { EncodeOptions } from '../shared/meta'; 2 | import encode from '../worker/webpEncode'; 3 | 4 | export const webPEncode = ( 5 | imageData: ImageData, 6 | options: EncodeOptions, 7 | ) => encode(imageData, options); 8 | 9 | const enum WebPImageHint { 10 | WEBP_HINT_DEFAULT, // default preset. 11 | WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot 12 | WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting 13 | WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc). 14 | } 15 | 16 | interface Props { 17 | options: EncodeOptions; 18 | onChange(newOptions: EncodeOptions): void; 19 | } 20 | 21 | interface State { 22 | showAdvanced: boolean; 23 | } 24 | 25 | // From kLosslessPresets in config_enc.c 26 | // The format is [method, quality]. 27 | const losslessPresets: [number, number][] = [ 28 | [0, 0], 29 | [1, 20], 30 | [2, 25], 31 | [3, 30], 32 | [3, 50], 33 | [4, 50], 34 | [4, 75], 35 | [4, 90], 36 | [5, 90], 37 | [6, 100], 38 | ]; 39 | const losslessPresetDefault = 6; 40 | 41 | function determineLosslessQuality(quality: number, method: number): number { 42 | const index = losslessPresets.findIndex( 43 | ([presetMethod, presetQuality]) => 44 | presetMethod === method && presetQuality === quality, 45 | ); 46 | if (index !== -1) return index; 47 | // Quality doesn't match one of the presets. 48 | // This can happen when toggling 'lossless'. 49 | return losslessPresetDefault; 50 | } 51 | -------------------------------------------------------------------------------- /codecs/resize/pkg/squoosh_resize.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {Uint8Array} input_image 5 | * @param {number} input_width 6 | * @param {number} input_height 7 | * @param {number} output_width 8 | * @param {number} output_height 9 | * @param {number} typ_idx 10 | * @param {boolean} premultiply 11 | * @param {boolean} color_space_conversion 12 | * @returns {Uint8ClampedArray} 13 | */ 14 | 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): Uint8ClampedArray; 15 | 16 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 17 | 18 | export interface InitOutput { 19 | readonly memory: WebAssembly.Memory; 20 | readonly resize: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => void; 21 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 22 | readonly __wbindgen_malloc: (a: number) => number; 23 | readonly __wbindgen_free: (a: number, b: number) => void; 24 | } 25 | 26 | /** 27 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 28 | * for everything else, calls `WebAssembly.instantiate` directly. 29 | * 30 | * @param {InitInput | Promise} module_or_path 31 | * 32 | * @returns {Promise} 33 | */ 34 | export default function init (module_or_path?: InitInput | Promise): Promise; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@yireen/squoosh-browser", 3 | "version": "1.0.7", 4 | "description": "An image compression tool run in browser while @squoosh/lib can not.", 5 | "main": "dist/client/lazy-app/Compress/index.js", 6 | "author": "Mario ", 7 | "license": "MIT", 8 | "public": true, 9 | "homepage": "https://github.com/myeveryheart/squoosh-browser", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/myeveryheart/squoosh-browser.git" 13 | }, 14 | "scripts": { 15 | "build": "tsc", 16 | "prepublishOnly": "rm -rf dist & yarn run build" 17 | }, 18 | "dependencies": { 19 | "wasm-feature-detect": "1.2.11" 20 | }, 21 | "devDependencies": { 22 | "@types/node": "16.7.2", 23 | "@typescript-eslint/eslint-plugin": "4.29.3", 24 | "@typescript-eslint/parser": "4.29.3", 25 | "eslint": "7.32.0", 26 | "eslint-config-prettier": "8.3.0", 27 | "eslint-plugin-prettier": "3.4.1", 28 | "prettier": "2.3.2", 29 | "ts-node": "10.2.1", 30 | "tsconfig-paths": "3.11.0", 31 | "typescript": "4.4.2" 32 | }, 33 | "keywords": [ 34 | "squoosh", 35 | "image", 36 | "compress", 37 | "convert", 38 | "pdf", 39 | "gif", 40 | "png", 41 | "jpeg", 42 | "bmp", 43 | "tiff", 44 | "webp", 45 | "webp2", 46 | "avif", 47 | "jxl", 48 | "tiny", 49 | "tinypng", 50 | "js", 51 | "browser", 52 | "react", 53 | "vue" 54 | ], 55 | "engines": { 56 | "node": ">=14.0.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/features/processors/quantize/worker/quantize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import imagequant, { QuantizerModule } from '../../../../../codecs/imagequant/imagequant.js'; 14 | import { initEmscriptenModule } from '../../../../features/worker-utils'; 15 | import { Options } from '../shared/meta'; 16 | 17 | let emscriptenModule: Promise; 18 | 19 | export default async function process( 20 | data: ImageData, 21 | opts: Options, 22 | ): Promise { 23 | if (!emscriptenModule) { 24 | emscriptenModule = initEmscriptenModule(imagequant); 25 | } 26 | 27 | const module = await emscriptenModule; 28 | 29 | const result = opts.zx 30 | ? module.zx_quantize(data.data, data.width, data.height, opts.dither) 31 | : module.quantize( 32 | data.data, 33 | data.width, 34 | data.height, 35 | opts.maxNumColors, 36 | opts.dither, 37 | ); 38 | 39 | return new ImageData(result, data.width, data.height); 40 | } 41 | -------------------------------------------------------------------------------- /codecs/imagequant/example.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 53 | -------------------------------------------------------------------------------- /src/client/lazy-app/util/web-codecs/missing-types.d.ts: -------------------------------------------------------------------------------- 1 | interface ImageDecoderInit { 2 | type: string; 3 | data: BufferSource | ReadableStream; 4 | premultiplyAlpha?: PremultiplyAlpha; 5 | colorSpaceConversion?: ColorSpaceConversion; 6 | desiredWidth?: number; 7 | desiredHeight?: number; 8 | preferAnimation?: boolean; 9 | } 10 | 11 | interface ImageDecodeOptions { 12 | frameIndex: number; 13 | completeFramesOnly: boolean; 14 | } 15 | 16 | interface ImageDecodeResult { 17 | image: VideoFrame; 18 | complete: boolean; 19 | } 20 | 21 | // I didn’t do all the types because the class is kinda complex. 22 | // I focused on what we need. 23 | // See https://w3c.github.io/webcodecs/#videoframe 24 | declare class VideoFrame { 25 | displayWidth: number; 26 | displayHeight: number; 27 | } 28 | 29 | // Add VideoFrame to canvas’ drawImage() 30 | interface CanvasDrawImage { 31 | drawImage( 32 | image: CanvasImageSource | VideoFrame, 33 | dx: number, 34 | dy: number, 35 | ): void; 36 | drawImage( 37 | image: CanvasImageSource | VideoFrame, 38 | dx: number, 39 | dy: number, 40 | dw: number, 41 | dh: number, 42 | ): void; 43 | drawImage( 44 | image: CanvasImageSource | VideoFrame, 45 | sx: number, 46 | sy: number, 47 | sw: number, 48 | sh: number, 49 | dx: number, 50 | dy: number, 51 | dw: number, 52 | dh: number, 53 | ): void; 54 | } 55 | 56 | declare class ImageDecoder { 57 | static isTypeSupported(type: string): Promise; 58 | constructor(desc: ImageDecoderInit); 59 | decode(opts?: Partial): Promise; 60 | } 61 | -------------------------------------------------------------------------------- /src/features/encoders/webP/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { EncodeOptions } from '../../../../../codecs/webp/enc/webp_enc.js'; 14 | 15 | export { EncodeOptions }; 16 | 17 | export const label = 'WebP'; 18 | export const mimeType = 'image/webp'; 19 | export const extension = 'webp'; 20 | // These come from struct WebPConfig in encode.h. 21 | export const defaultOptions: EncodeOptions = { 22 | quality: 75, 23 | target_size: 0, 24 | target_PSNR: 0, 25 | method: 4, 26 | sns_strength: 50, 27 | filter_strength: 60, 28 | filter_sharpness: 0, 29 | filter_type: 1, 30 | partitions: 0, 31 | segments: 4, 32 | pass: 1, 33 | show_compressed: 0, 34 | preprocessing: 0, 35 | autofilter: 0, 36 | partition_limit: 0, 37 | alpha_compression: 1, 38 | alpha_filtering: 1, 39 | alpha_quality: 100, 40 | lossless: 0, 41 | exact: 0, 42 | image_hint: 0, 43 | emulate_jpeg_size: 0, 44 | thread_level: 0, 45 | low_memory: 0, 46 | near_lossless: 100, 47 | use_delta_palette: 0, 48 | use_sharp_yuv: 0, 49 | }; 50 | -------------------------------------------------------------------------------- /missing-types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /// 14 | 15 | declare module 'entry-data:*' { 16 | export const main: string; 17 | export const deps: string[]; 18 | } 19 | 20 | declare module 'url:*' { 21 | const value: string; 22 | export default value; 23 | } 24 | 25 | declare module 'file:*' { 26 | const value: string; 27 | export default value; 28 | } 29 | 30 | declare module 'img-url:*' { 31 | const value: string; 32 | export default value; 33 | export const width: number; 34 | export const height: number; 35 | } 36 | 37 | declare module 'omt:*' { 38 | const value: string; 39 | export default value; 40 | } 41 | 42 | declare module 'css:*' { 43 | const source: string; 44 | export default source; 45 | } 46 | 47 | declare module 'data-url:*' { 48 | const url: string; 49 | export default url; 50 | } 51 | 52 | declare module 'service-worker:*' { 53 | const url: string; 54 | export default url; 55 | } 56 | 57 | declare var ga: { 58 | (...args: any[]): void; 59 | q: any[]; 60 | }; 61 | 62 | declare const __PRODUCTION__: boolean; 63 | -------------------------------------------------------------------------------- /src/features/encoders/webP/worker/webpEncode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { WebPModule } from '../../../../../codecs/webp/enc/webp_enc.js'; 14 | import type { EncodeOptions } from '../shared/meta'; 15 | 16 | import { initEmscriptenModule } from '../../../../features/worker-utils'; 17 | import { simd } from 'wasm-feature-detect'; 18 | 19 | import webpEncoderSimd from '../../../../../codecs/webp/enc/webp_enc_simd.js'; 20 | import webpEncoder from '../../../../../codecs/webp/enc/webp_enc.js'; 21 | 22 | let emscriptenModule: Promise; 23 | 24 | async function init() { 25 | if (await simd()) { 26 | return initEmscriptenModule(webpEncoderSimd); 27 | } 28 | return initEmscriptenModule(webpEncoder); 29 | } 30 | 31 | export default async function encode( 32 | data: ImageData, 33 | options: EncodeOptions, 34 | ): Promise { 35 | if (!emscriptenModule) emscriptenModule = init(); 36 | 37 | const module = await emscriptenModule; 38 | const result = module.encode(data.data, data.width, data.height, options); 39 | 40 | if (!result) throw new Error('Encoding error.'); 41 | 42 | return result.buffer; 43 | } 44 | -------------------------------------------------------------------------------- /src/features/encoders/avif/worker/avifEncode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { AVIFModule } from '../../../../../codecs/avif/enc/avif_enc.js'; 14 | import type { EncodeOptions } from '../shared/meta'; 15 | import { initEmscriptenModule } from '../../../../features/worker-utils'; 16 | import { threads } from 'wasm-feature-detect'; 17 | 18 | import avifEncoderMt from '../../../../../codecs/avif/enc/avif_enc_mt.js'; 19 | import avifEncoder from '../../../../../codecs/avif/enc/avif_enc.js'; 20 | 21 | let emscriptenModule: Promise; 22 | 23 | async function init() { 24 | if (await threads()) { 25 | return initEmscriptenModule(avifEncoderMt); 26 | } 27 | return initEmscriptenModule(avifEncoder); 28 | } 29 | 30 | export default async function encode( 31 | data: ImageData, 32 | options: EncodeOptions, 33 | ): Promise { 34 | if (!emscriptenModule) emscriptenModule = init(); 35 | 36 | const module = await emscriptenModule; 37 | const result = module.encode(data.data, data.width, data.height, options); 38 | 39 | if (!result) throw new Error('Encoding error'); 40 | 41 | return result.buffer; 42 | } 43 | -------------------------------------------------------------------------------- /codecs/rotate/benchmark.js: -------------------------------------------------------------------------------- 1 | // THIS IS NOT A NODE SCRIPT 2 | // This is a d8 script. Please install jsvu[1] and install v8. 3 | // Then run `npm run --silent benchmark`. 4 | // [1]: https://github.com/GoogleChromeLabs/jsvu 5 | async function init() { 6 | // Adjustable constants. 7 | const imageDimensions = 4096; 8 | const iterations = new Array(100); 9 | 10 | // Constants. Don’t change. 11 | const imageByteSize = imageDimensions * imageDimensions * 4; 12 | const wasmPageSize = 64 * 1024; 13 | 14 | const buffer = readbuffer("rotate.wasm"); 15 | const { instance } = await WebAssembly.instantiate(buffer); 16 | 17 | const pagesAvailable = Math.floor( 18 | instance.exports.memory.buffer.byteLength / wasmPageSize 19 | ); 20 | const pagesNeeded = Math.floor((imageByteSize * 2 + 4) / wasmPageSize) + 1; 21 | const additionalPagesNeeded = pagesNeeded - pagesAvailable; 22 | if (additionalPagesNeeded > 0) { 23 | instance.exports.memory.grow(additionalPagesNeeded); 24 | } 25 | 26 | [0, 90, 180, 270].forEach(rotation => { 27 | print(`\n${rotation} degrees`); 28 | print(`==============================`); 29 | for (let i = 0; i < 100; i++) { 30 | const start = Date.now(); 31 | instance.exports.rotate(imageDimensions, imageDimensions, rotation); 32 | iterations[i] = Date.now() - start; 33 | } 34 | const average = iterations.reduce((sum, c) => sum + c) / iterations.length; 35 | const stddev = Math.sqrt( 36 | iterations 37 | .map(i => Math.pow(i - average, 2)) 38 | .reduce((sum, c) => sum + c) / iterations.length 39 | ); 40 | print(`n = ${iterations.length}`); 41 | print(`Average: ${average}`); 42 | print(`StdDev: ${stddev}`); 43 | }); 44 | } 45 | init().catch(e => console.error(e.stack)); 46 | -------------------------------------------------------------------------------- /src/features/encoders/oxiPNG/worker/oxipngEncode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import { EncodeOptions } from '../shared/meta'; 14 | import { threads } from 'wasm-feature-detect'; 15 | import initMt, { initThreadPool as initThreadPoolMt, optimise as optimiseMt } from '../../../../../codecs/oxipng/pkg-parallel/squoosh_oxipng.js'; 16 | import initSt, { optimise as optimiseSt } from '../../../../../codecs/oxipng/pkg/squoosh_oxipng.js'; 17 | 18 | async function initMT() { 19 | await initMt(); 20 | await initThreadPoolMt(navigator.hardwareConcurrency); 21 | return optimiseMt; 22 | } 23 | 24 | async function initST() { 25 | await initSt(); 26 | return optimiseSt; 27 | } 28 | 29 | let wasmReady: ReturnType; 30 | 31 | export default async function encode( 32 | data: ArrayBuffer, 33 | options: EncodeOptions, 34 | ): Promise { 35 | if (!wasmReady) { 36 | wasmReady = threads().then((hasThreads: boolean) => 37 | hasThreads ? initMT() : initST(), 38 | ); 39 | } 40 | 41 | const optimise = await wasmReady; 42 | return optimise(new Uint8Array(data), options.level, options.interlace) 43 | .buffer; 44 | } 45 | -------------------------------------------------------------------------------- /src/client/lazy-app/util/cli.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { EncoderState, ProcessorState } from '../feature-meta'; 15 | 16 | // Maps our encoder.type values to CLI parameter names 17 | const typeMap = new Map([ 18 | ['avif', '--avif'], 19 | ['jxl', '--jxl'], 20 | ['mozJPEG', '--mozjpeg'], 21 | ['oxiPNG', '--oxipng'], 22 | ['webP', '--webp'], 23 | ['wp2', '--wp2'], 24 | ]); 25 | 26 | // Same as JSON.stringify, but with single quotes around the entire value 27 | // so that shells don’t do weird stuff. 28 | function cliJson(v: T): string { 29 | return "'" + JSON.stringify(v) + "'"; 30 | } 31 | 32 | export function generateCliInvocation( 33 | encoder: EncoderState, 34 | processor: ProcessorState, 35 | ): string { 36 | if (!typeMap.has(encoder.type)) { 37 | throw Error(`Encoder ${encoder.type} is unsupported in the CLI`); 38 | } 39 | return [ 40 | 'npx', 41 | '@squoosh/cli', 42 | ...(processor.resize.enabled 43 | ? ['--resize', cliJson(processor.resize)] 44 | : []), 45 | ...(processor.quantize.enabled 46 | ? ['--quant', cliJson(processor.quantize)] 47 | : []), 48 | typeMap.get(encoder.type)!, 49 | cliJson(encoder.options), 50 | ].join(' '); 51 | } 52 | -------------------------------------------------------------------------------- /codecs/avif/dec/avif_dec.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "avif/avif.h" 4 | 5 | using namespace emscripten; 6 | 7 | thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray"); 8 | thread_local const val ImageData = val::global("ImageData"); 9 | 10 | val decode(std::string avifimage) { 11 | avifImage* image = avifImageCreateEmpty(); 12 | avifDecoder* decoder = avifDecoderCreate(); 13 | avifResult decodeResult = 14 | avifDecoderReadMemory(decoder, image, (uint8_t*)avifimage.c_str(), avifimage.length()); 15 | // image is an independent copy of decoded data, decoder may be destroyed here 16 | avifDecoderDestroy(decoder); 17 | 18 | val result = val::null(); 19 | 20 | if (decodeResult == AVIF_RESULT_OK) { 21 | // Convert to interleaved RGB(A)/BGR(A) using a libavif-allocated buffer. 22 | avifRGBImage rgb; 23 | avifRGBImageSetDefaults(&rgb, 24 | image); // Defaults to AVIF_RGB_FORMAT_RGBA which is what we want. 25 | rgb.depth = 8; // Does not need to match image->depth. We always want 8-bit pixels. 26 | 27 | avifRGBImageAllocatePixels(&rgb); 28 | avifImageYUVToRGB(image, &rgb); 29 | 30 | // We want to create a *copy* of the decoded data to be owned by the JavaScript side. 31 | // For that, we perform `new Uint8Array(wasmMemBuffer, wasmPtr, wasmSize).slice()`: 32 | result = ImageData.new_( 33 | Uint8ClampedArray.new_(typed_memory_view(rgb.rowBytes * rgb.height, rgb.pixels)), rgb.width, 34 | rgb.height); 35 | 36 | // Now we can safely free the RGB pixels: 37 | avifRGBImageFreePixels(&rgb); 38 | } 39 | 40 | // Image has been converted to RGB, we don't need the original anymore. 41 | avifImageDestroy(image); 42 | 43 | return result; 44 | } 45 | 46 | EMSCRIPTEN_BINDINGS(my_module) { 47 | function("decode", &decode); 48 | } 49 | -------------------------------------------------------------------------------- /src/features/encoders/jxl/worker/jxlEncode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { JXLModule } from '../../../../../codecs/jxl/enc/jxl_enc.js'; 14 | import type { EncodeOptions } from '../shared/meta'; 15 | 16 | import { initEmscriptenModule } from '../../../../features/worker-utils'; 17 | import { threads, simd } from 'wasm-feature-detect'; 18 | 19 | import jxlEncoderSimd from '../../../../../codecs/jxl/enc/jxl_enc_mt_simd.js'; 20 | import jxlEncoderMt from '../../../../../codecs/jxl/enc/jxl_enc_mt.js'; 21 | import jxlEncoder from '../../../../../codecs/jxl/enc/jxl_enc.js'; 22 | 23 | let emscriptenModule: Promise; 24 | 25 | async function init() { 26 | if (await threads()) { 27 | if (await simd()) { 28 | return initEmscriptenModule(jxlEncoderSimd); 29 | } 30 | return initEmscriptenModule(jxlEncoderMt); 31 | } 32 | return initEmscriptenModule(jxlEncoder); 33 | } 34 | 35 | export default async function encode( 36 | data: ImageData, 37 | options: EncodeOptions, 38 | ): Promise { 39 | if (!emscriptenModule) emscriptenModule = init(); 40 | 41 | const module = await emscriptenModule; 42 | const result = module.encode(data.data, data.width, data.height, options); 43 | 44 | if (!result) throw new Error('Encoding error.'); 45 | 46 | return result.buffer; 47 | } 48 | -------------------------------------------------------------------------------- /src/features/encoders/wp2/worker/wp2Encode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import type { WP2Module } from '../../../../../codecs/wp2/enc/wp2_enc.js'; 14 | import type { EncodeOptions } from '../shared/meta.js'; 15 | 16 | import { initEmscriptenModule } from '../../../../features/worker-utils'; 17 | import { threads, simd } from 'wasm-feature-detect'; 18 | 19 | import wp2EncoderSimd from '../../../../../codecs/wp2/enc/wp2_enc_mt_simd.js'; 20 | import wp2EncoderMt from '../../../../../codecs/wp2/enc/wp2_enc_mt.js'; 21 | import wp2Encoder from '../../../../../codecs/wp2/enc/wp2_enc.js'; 22 | 23 | let emscriptenModule: Promise; 24 | 25 | async function init() { 26 | if (await threads()) { 27 | if (await simd()) { 28 | return initEmscriptenModule(wp2EncoderSimd); 29 | } 30 | return initEmscriptenModule(wp2EncoderMt); 31 | } 32 | return initEmscriptenModule(wp2Encoder); 33 | } 34 | 35 | export default async function encode( 36 | data: ImageData, 37 | options: EncodeOptions, 38 | ): Promise { 39 | if (!emscriptenModule) emscriptenModule = init(); 40 | 41 | const module = await emscriptenModule; 42 | const result = module.encode(data.data, data.width, data.height, options); 43 | 44 | if (!result) throw new Error('Encoding error.'); 45 | 46 | return result.buffer; 47 | } 48 | -------------------------------------------------------------------------------- /codecs/mozjpeg/dec/mozjpeg_dec.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "config.h" 4 | #include "jpeglib.h" 5 | 6 | extern "C" { 7 | #include "cdjpeg.h" 8 | } 9 | 10 | using namespace emscripten; 11 | 12 | thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray"); 13 | thread_local const val ImageData = val::global("ImageData"); 14 | 15 | val decode(std::string image_in) { 16 | uint8_t* image_buffer = (uint8_t*)image_in.c_str(); 17 | 18 | jpeg_decompress_struct cinfo; 19 | jpeg_error_mgr jerr; 20 | // Initialize the JPEG decompression object with default error handling. 21 | cinfo.err = jpeg_std_error(&jerr); 22 | jpeg_create_decompress(&cinfo); 23 | 24 | jpeg_mem_src(&cinfo, image_buffer, image_in.length()); 25 | // Read file header, set default decompression parameters 26 | jpeg_read_header(&cinfo, TRUE); 27 | // Force RGBA decoding, even for grayscale images 28 | cinfo.out_color_space = JCS_EXT_RGBA; 29 | jpeg_start_decompress(&cinfo); 30 | 31 | // Prepare output buffer 32 | size_t output_size = cinfo.output_width * cinfo.output_height * 4; 33 | std::vector output_buffer(output_size); 34 | auto stride = cinfo.output_width * 4; 35 | 36 | // Process data 37 | while (cinfo.output_scanline < cinfo.output_height) { 38 | uint8_t* ptr = &output_buffer[stride * cinfo.output_scanline]; 39 | jpeg_read_scanlines(&cinfo, &ptr, 1); 40 | } 41 | jpeg_finish_decompress(&cinfo); 42 | 43 | // Step 7: release JPEG compression object 44 | 45 | auto data = Uint8ClampedArray.new_(typed_memory_view(output_size, &output_buffer[0])); 46 | auto js_result = ImageData.new_(data, cinfo.output_width, cinfo.output_height); 47 | 48 | // This is an important step since it will release a good deal of memory. 49 | jpeg_destroy_decompress(&cinfo); 50 | 51 | // And we're done! 52 | return js_result; 53 | } 54 | 55 | EMSCRIPTEN_BINDINGS(my_module) { 56 | function("decode", &decode); 57 | } 58 | -------------------------------------------------------------------------------- /src/client/lazy-app/util/clean-modify.ts: -------------------------------------------------------------------------------- 1 | function cleanSetOrMerge( 2 | source: A, 3 | keys: string | number | string[], 4 | toSetOrMerge: any[] | object, 5 | merge: boolean, 6 | ): A { 7 | const splitKeys = Array.isArray(keys) ? keys : ('' + keys).split('.'); 8 | 9 | // Going off road in terms of types, otherwise TypeScript doesn't like the access-by-index. 10 | // The assumptions in this code break if the object contains things which aren't arrays or 11 | // plain objects. 12 | let last = copy(source) as any; 13 | const newObject = last; 14 | 15 | const lastIndex = splitKeys.length - 1; 16 | 17 | for (const [i, key] of splitKeys.entries()) { 18 | if (i !== lastIndex) { 19 | // Copy everything along the path. 20 | last = last[key] = copy(last[key]); 21 | } else { 22 | // Merge or set. 23 | last[key] = merge 24 | ? Object.assign(copy(last[key]), toSetOrMerge) 25 | : toSetOrMerge; 26 | } 27 | } 28 | 29 | return newObject; 30 | } 31 | 32 | function copy(source: A): A { 33 | // Some type cheating here, as TypeScript can't infer between generic types. 34 | if (Array.isArray(source)) return [...source] as any; 35 | return { ...(source as any) }; 36 | } 37 | 38 | /** 39 | * @param source Object to copy from. 40 | * @param keys Path to modify, eg "foo.bar.baz". 41 | * @param toMerge A value to merge into the value at the path. 42 | */ 43 | export function cleanMerge( 44 | source: A, 45 | keys: string | number | string[], 46 | toMerge: any[] | object, 47 | ): A { 48 | return cleanSetOrMerge(source, keys, toMerge, true); 49 | } 50 | 51 | /** 52 | * @param source Object to copy from. 53 | * @param keys Path to modify, eg "foo.bar.baz". 54 | * @param newValue A value to set at the path. 55 | */ 56 | export function cleanSet( 57 | source: A, 58 | keys: string | number | string[], 59 | newValue: any, 60 | ): A { 61 | return cleanSetOrMerge(source, keys, newValue, false); 62 | } 63 | -------------------------------------------------------------------------------- /src/features/processors/resize/shared/meta.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | type BrowserResizeMethods = 15 | | 'browser-pixelated' 16 | | 'browser-low' 17 | | 'browser-medium' 18 | | 'browser-high'; 19 | type WorkerResizeMethods = 20 | | 'triangle' 21 | | 'catrom' 22 | | 'mitchell' 23 | | 'lanczos3' 24 | | 'hqx'; 25 | 26 | export const workerResizeMethods: WorkerResizeMethods[] = [ 27 | 'triangle', 28 | 'catrom', 29 | 'mitchell', 30 | 'lanczos3', 31 | 'hqx', 32 | ]; 33 | 34 | export type Options = 35 | | BrowserResizeOptions 36 | | WorkerResizeOptions 37 | | VectorResizeOptions; 38 | 39 | export interface ResizeOptionsCommon { 40 | width: number; 41 | height: number; 42 | fitMethod: 'stretch' | 'contain'; 43 | } 44 | 45 | export interface BrowserResizeOptions extends ResizeOptionsCommon { 46 | method: BrowserResizeMethods; 47 | } 48 | 49 | export interface WorkerResizeOptions extends ResizeOptionsCommon { 50 | method: WorkerResizeMethods; 51 | premultiply: boolean; 52 | linearRGB: boolean; 53 | } 54 | 55 | export interface VectorResizeOptions extends ResizeOptionsCommon { 56 | method: 'vector'; 57 | } 58 | 59 | export const defaultOptions: Options = { 60 | // Width and height will always default to the image size. 61 | // This is set elsewhere. 62 | width: 1, 63 | height: 1, 64 | // This will be set to 'vector' if the input is SVG. 65 | method: 'lanczos3', 66 | fitMethod: 'stretch', 67 | premultiply: true, 68 | linearRGB: true, 69 | }; 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /codecs/oxipng/pkg-parallel/squoosh_oxipng.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {Uint8Array} data 5 | * @param {number} level 6 | * @param {boolean} interlace 7 | * @returns {Uint8Array} 8 | */ 9 | export function optimise(data: Uint8Array, level: number, interlace: boolean): Uint8Array; 10 | /** 11 | * @param {number} num_threads 12 | * @returns {Promise} 13 | */ 14 | export function initThreadPool(num_threads: number): Promise; 15 | /** 16 | * @param {number} receiver 17 | */ 18 | export function wbg_rayon_start_worker(receiver: number): void; 19 | /** 20 | */ 21 | export class wbg_rayon_PoolBuilder { 22 | free(): void; 23 | /** 24 | * @returns {number} 25 | */ 26 | numThreads(): number; 27 | /** 28 | * @returns {number} 29 | */ 30 | receiver(): number; 31 | /** 32 | */ 33 | build(): void; 34 | } 35 | 36 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 37 | 38 | export interface InitOutput { 39 | readonly __wasm_init_memory: () => void; 40 | readonly optimise: (a: number, b: number, c: number, d: number, e: number) => void; 41 | readonly __wbg_wbg_rayon_poolbuilder_free: (a: number) => void; 42 | readonly wbg_rayon_poolbuilder_numThreads: (a: number) => number; 43 | readonly wbg_rayon_poolbuilder_receiver: (a: number) => number; 44 | readonly wbg_rayon_poolbuilder_build: (a: number) => void; 45 | readonly initThreadPool: (a: number) => number; 46 | readonly wbg_rayon_start_worker: (a: number) => void; 47 | readonly __wbindgen_export_0: WebAssembly.Memory; 48 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 49 | readonly __wbindgen_malloc: (a: number) => number; 50 | readonly __wbindgen_free: (a: number, b: number) => void; 51 | readonly __wbindgen_start: () => void; 52 | } 53 | 54 | /** 55 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 56 | * for everything else, calls `WebAssembly.instantiate` directly. 57 | * 58 | * @param {InitInput | Promise} module_or_path 59 | * @param {WebAssembly.Memory} maybe_memory 60 | * 61 | * @returns {Promise} 62 | */ 63 | export default function init (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory): Promise; 64 | -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc_mt.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./jxl_enc_mt.js")).then(function(exports){return exports.default(Module)}).then(function(instance){Module=instance;moduleLoaded()})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["PThread"].threadExit(result)}}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; 2 | -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc_mt.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./wp2_enc_mt.js")).then(function(exports){return exports.default(Module)}).then(function(instance){Module=instance;moduleLoaded()})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["PThread"].threadExit(result)}}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; 2 | -------------------------------------------------------------------------------- /codecs/avif/enc/avif_enc_mt.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./avif_enc_mt.js")).then(function(exports){return exports.default(Module)}).then(function(instance){Module=instance;moduleLoaded()})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["PThread"].threadExit(result)}}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; 2 | -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc_mt_simd.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./jxl_enc_mt_simd.js")).then(function(exports){return exports.default(Module)}).then(function(instance){Module=instance;moduleLoaded()})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["PThread"].threadExit(result)}}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; 2 | -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc_mt_simd.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./wp2_enc_mt_simd.js")).then(function(exports){return exports.default(Module)}).then(function(instance){Module=instance;moduleLoaded()})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["PThread"].threadExit(result)}}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; 2 | -------------------------------------------------------------------------------- /codecs/wp2/enc/wp2_enc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "src/wp2/encode.h" 6 | 7 | using namespace emscripten; 8 | 9 | thread_local const val Uint8Array = val::global("Uint8Array"); 10 | 11 | struct WP2Options { 12 | float quality; 13 | float alpha_quality; 14 | int effort; 15 | int pass; 16 | int uv_mode; 17 | float sns; 18 | int csp_type; 19 | int error_diffusion; 20 | bool use_random_matrix; 21 | }; 22 | 23 | val encode(std::string image_in, int image_width, int image_height, WP2Options options) { 24 | WP2::EncoderConfig config = {}; 25 | 26 | config.quality = options.quality; 27 | config.alpha_quality = options.alpha_quality; 28 | config.effort = options.effort; 29 | config.pass = options.pass; 30 | config.uv_mode = static_cast(options.uv_mode); 31 | config.csp_type = static_cast(options.csp_type); 32 | config.sns = options.sns; 33 | config.error_diffusion = options.error_diffusion; 34 | config.use_random_matrix = options.use_random_matrix; 35 | 36 | uint8_t* image_buffer = (uint8_t*)image_in.c_str(); 37 | WP2::ArgbBuffer src = WP2::ArgbBuffer(); 38 | WP2Status status = 39 | src.Import(WP2_rgbA_32, // Format. WP2_RGBA_32 is the same but NOT premultiplied alpha 40 | image_width, image_height, image_buffer, 4 * image_width); 41 | if (status != WP2_STATUS_OK) { 42 | return val::null(); 43 | } 44 | 45 | WP2::MemoryWriter memory_writer; 46 | // In WebP2, thread_level is number of *extra* threads to use (0 for no multithreading). 47 | config.thread_level = emscripten_num_logical_cores() - 1; 48 | status = WP2::Encode(src, &memory_writer, config); 49 | if (status != WP2_STATUS_OK) { 50 | return val::null(); 51 | } 52 | 53 | return Uint8Array.new_(typed_memory_view(memory_writer.size_, memory_writer.mem_)); 54 | } 55 | 56 | EMSCRIPTEN_BINDINGS(my_module) { 57 | value_object("WP2Options") 58 | .field("quality", &WP2Options::quality) 59 | .field("alpha_quality", &WP2Options::alpha_quality) 60 | .field("effort", &WP2Options::effort) 61 | .field("pass", &WP2Options::pass) 62 | .field("uv_mode", &WP2Options::uv_mode) 63 | .field("csp_type", &WP2Options::csp_type) 64 | .field("error_diffusion", &WP2Options::error_diffusion) 65 | .field("use_random_matrix", &WP2Options::use_random_matrix) 66 | .field("sns", &WP2Options::sns); 67 | 68 | function("encode", &encode); 69 | } 70 | -------------------------------------------------------------------------------- /src/features/processors/resize/client/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | builtinResize, 3 | BuiltinResizeMethod, 4 | drawableToImageData, 5 | } from '../../../../client/lazy-app/util/canvas'; 6 | import { 7 | BrowserResizeOptions, 8 | VectorResizeOptions, 9 | WorkerResizeOptions, 10 | Options as ResizeOptions, 11 | workerResizeMethods, 12 | } from '../shared/meta'; 13 | import { getContainOffsets } from '../shared/util'; 14 | import type { SourceImage } from '../../../../client/lazy-app/Compress'; 15 | 16 | import resize from '../worker/resize'; 17 | 18 | 19 | /** 20 | * Return whether a set of options are worker resize options. 21 | * 22 | * @param opts 23 | */ 24 | function isWorkerOptions(opts: ResizeOptions): opts is WorkerResizeOptions { 25 | return (workerResizeMethods as string[]).includes(opts.method); 26 | } 27 | 28 | function browserResize(data: ImageData, opts: BrowserResizeOptions): ImageData { 29 | let sx = 0; 30 | let sy = 0; 31 | let sw = data.width; 32 | let sh = data.height; 33 | 34 | if (opts.fitMethod === 'contain') { 35 | ({ sx, sy, sw, sh } = getContainOffsets(sw, sh, opts.width, opts.height)); 36 | } 37 | 38 | return builtinResize( 39 | data, 40 | sx, 41 | sy, 42 | sw, 43 | sh, 44 | opts.width, 45 | opts.height, 46 | opts.method.slice('browser-'.length) as BuiltinResizeMethod, 47 | ); 48 | } 49 | 50 | function vectorResize( 51 | data: HTMLImageElement, 52 | opts: VectorResizeOptions, 53 | ): ImageData { 54 | let sx = 0; 55 | let sy = 0; 56 | let sw = data.width; 57 | let sh = data.height; 58 | 59 | if (opts.fitMethod === 'contain') { 60 | ({ sx, sy, sw, sh } = getContainOffsets(sw, sh, opts.width, opts.height)); 61 | } 62 | 63 | return drawableToImageData(data, { 64 | sx, 65 | sy, 66 | sw, 67 | sh, 68 | width: opts.width, 69 | height: opts.height, 70 | }); 71 | } 72 | 73 | export async function resizeImage( 74 | source: SourceImage, 75 | options: ResizeOptions, 76 | ) { 77 | if (options.method === 'vector') { 78 | if (!source.vectorImage) throw Error('No vector image available'); 79 | return vectorResize(source.vectorImage, options); 80 | } 81 | if (isWorkerOptions(options)) { 82 | return resize(source.preprocessed, options); 83 | } 84 | return browserResize(source.preprocessed, options); 85 | } 86 | 87 | interface Props { 88 | isVector: Boolean; 89 | inputWidth: number; 90 | inputHeight: number; 91 | options: ResizeOptions; 92 | onChange(newOptions: ResizeOptions): void; 93 | } 94 | 95 | interface State { 96 | maintainAspect: boolean; 97 | } 98 | 99 | const sizePresets = [0.25, 0.3333, 0.5, 1, 2, 3, 4]; 100 | -------------------------------------------------------------------------------- /codecs/avif/enc/avif_node_enc_mt.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};if(typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string"){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",function(data){onmessage({data:data})});var nodeFS=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:function(f){(0,eval)(nodeFS.readFileSync(f,"utf8"))},postMessage:function(msg){parentPort.postMessage(msg)},performance:global.performance||{now:function(){return Date.now()}}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./avif_node_enc_mt.js")).then(function(exports){return exports.default(Module)}).then(function(instance){Module=instance;moduleLoaded()})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["PThread"].threadExit(result)}}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; 2 | -------------------------------------------------------------------------------- /codecs/webp/enc/webp_enc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "src/webp/encode.h" 7 | 8 | using namespace emscripten; 9 | 10 | int version() { 11 | return WebPGetEncoderVersion(); 12 | } 13 | 14 | thread_local const val Uint8Array = val::global("Uint8Array"); 15 | 16 | val encode(std::string img, int width, int height, WebPConfig config) { 17 | auto img_in = (uint8_t*)img.c_str(); 18 | 19 | // A lot of this is duplicated from Encode in picture_enc.c 20 | WebPPicture pic; 21 | WebPMemoryWriter wrt; 22 | int ok; 23 | 24 | if (!WebPPictureInit(&pic)) { 25 | // shouldn't happen, except if system installation is broken 26 | return val::null(); 27 | } 28 | 29 | // Allow quality to go higher than 0. 30 | config.qmax = 100; 31 | 32 | // Only use use_argb if we really need it, as it's slower. 33 | pic.use_argb = config.lossless || config.use_sharp_yuv || config.preprocessing > 0; 34 | pic.width = width; 35 | pic.height = height; 36 | pic.writer = WebPMemoryWrite; 37 | pic.custom_ptr = &wrt; 38 | 39 | WebPMemoryWriterInit(&wrt); 40 | 41 | ok = WebPPictureImportRGBA(&pic, img_in, width * 4) && WebPEncode(&config, &pic); 42 | WebPPictureFree(&pic); 43 | val js_result = ok ? Uint8Array.new_(typed_memory_view(wrt.size, wrt.mem)) : val::null(); 44 | WebPMemoryWriterClear(&wrt); 45 | return js_result; 46 | } 47 | 48 | EMSCRIPTEN_BINDINGS(my_module) { 49 | enum_("WebPImageHint") 50 | .value("WEBP_HINT_DEFAULT", WebPImageHint::WEBP_HINT_DEFAULT) 51 | .value("WEBP_HINT_PICTURE", WebPImageHint::WEBP_HINT_PICTURE) 52 | .value("WEBP_HINT_PHOTO", WebPImageHint::WEBP_HINT_PHOTO) 53 | .value("WEBP_HINT_GRAPH", WebPImageHint::WEBP_HINT_GRAPH); 54 | 55 | value_object("WebPConfig") 56 | .field("lossless", &WebPConfig::lossless) 57 | .field("quality", &WebPConfig::quality) 58 | .field("method", &WebPConfig::method) 59 | .field("image_hint", &WebPConfig::image_hint) 60 | .field("target_size", &WebPConfig::target_size) 61 | .field("target_PSNR", &WebPConfig::target_PSNR) 62 | .field("segments", &WebPConfig::segments) 63 | .field("sns_strength", &WebPConfig::sns_strength) 64 | .field("filter_strength", &WebPConfig::filter_strength) 65 | .field("filter_sharpness", &WebPConfig::filter_sharpness) 66 | .field("filter_type", &WebPConfig::filter_type) 67 | .field("autofilter", &WebPConfig::autofilter) 68 | .field("alpha_compression", &WebPConfig::alpha_compression) 69 | .field("alpha_filtering", &WebPConfig::alpha_filtering) 70 | .field("alpha_quality", &WebPConfig::alpha_quality) 71 | .field("pass", &WebPConfig::pass) 72 | .field("show_compressed", &WebPConfig::show_compressed) 73 | .field("preprocessing", &WebPConfig::preprocessing) 74 | .field("partitions", &WebPConfig::partitions) 75 | .field("partition_limit", &WebPConfig::partition_limit) 76 | .field("emulate_jpeg_size", &WebPConfig::emulate_jpeg_size) 77 | .field("low_memory", &WebPConfig::low_memory) 78 | .field("near_lossless", &WebPConfig::near_lossless) 79 | .field("exact", &WebPConfig::exact) 80 | .field("use_delta_palette", &WebPConfig::use_delta_palette) 81 | .field("use_sharp_yuv", &WebPConfig::use_sharp_yuv); 82 | 83 | function("version", &version); 84 | function("encode", &encode); 85 | } 86 | -------------------------------------------------------------------------------- /codecs/hqx/pkg/squooshhqx.js: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | let cachegetUint32Memory0 = null; 5 | function getUint32Memory0() { 6 | if (cachegetUint32Memory0 === null || cachegetUint32Memory0.buffer !== wasm.memory.buffer) { 7 | cachegetUint32Memory0 = new Uint32Array(wasm.memory.buffer); 8 | } 9 | return cachegetUint32Memory0; 10 | } 11 | 12 | let WASM_VECTOR_LEN = 0; 13 | 14 | function passArray32ToWasm0(arg, malloc) { 15 | const ptr = malloc(arg.length * 4); 16 | getUint32Memory0().set(arg, ptr / 4); 17 | WASM_VECTOR_LEN = arg.length; 18 | return ptr; 19 | } 20 | 21 | let cachegetInt32Memory0 = null; 22 | function getInt32Memory0() { 23 | if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { 24 | cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); 25 | } 26 | return cachegetInt32Memory0; 27 | } 28 | 29 | function getArrayU32FromWasm0(ptr, len) { 30 | return getUint32Memory0().subarray(ptr / 4, ptr / 4 + len); 31 | } 32 | /** 33 | * @param {Uint32Array} input_image 34 | * @param {number} input_width 35 | * @param {number} input_height 36 | * @param {number} factor 37 | * @returns {Uint32Array} 38 | */ 39 | export function resize(input_image, input_width, input_height, factor) { 40 | try { 41 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 42 | var ptr0 = passArray32ToWasm0(input_image, wasm.__wbindgen_malloc); 43 | var len0 = WASM_VECTOR_LEN; 44 | wasm.resize(retptr, ptr0, len0, input_width, input_height, factor); 45 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 46 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 47 | var v1 = getArrayU32FromWasm0(r0, r1).slice(); 48 | wasm.__wbindgen_free(r0, r1 * 4); 49 | return v1; 50 | } finally { 51 | wasm.__wbindgen_add_to_stack_pointer(16); 52 | } 53 | } 54 | 55 | async function load(module, imports) { 56 | if (typeof Response === 'function' && module instanceof Response) { 57 | if (typeof WebAssembly.instantiateStreaming === 'function') { 58 | try { 59 | return await WebAssembly.instantiateStreaming(module, imports); 60 | 61 | } catch (e) { 62 | if (module.headers.get('Content-Type') != 'application/wasm') { 63 | 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); 64 | 65 | } else { 66 | throw e; 67 | } 68 | } 69 | } 70 | 71 | const bytes = await module.arrayBuffer(); 72 | return await WebAssembly.instantiate(bytes, imports); 73 | 74 | } else { 75 | const instance = await WebAssembly.instantiate(module, imports); 76 | 77 | if (instance instanceof WebAssembly.Instance) { 78 | return { instance, module }; 79 | 80 | } else { 81 | return instance; 82 | } 83 | } 84 | } 85 | 86 | async function init(input) { 87 | if (typeof input === 'undefined') { 88 | input = new URL('squooshhqx_bg.wasm', import.meta.url); 89 | } 90 | const imports = {}; 91 | 92 | 93 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 94 | input = fetch(input); 95 | } 96 | 97 | 98 | 99 | const { instance, module } = await load(await input, imports); 100 | 101 | wasm = instance.exports; 102 | init.__wbindgen_wasm_module = module; 103 | 104 | return wasm; 105 | } 106 | 107 | export default init; 108 | 109 | -------------------------------------------------------------------------------- /src/features/processors/resize/worker/resize.ts: -------------------------------------------------------------------------------- 1 | import type { WorkerResizeOptions } from '../shared/meta'; 2 | import { getContainOffsets } from '../shared/util'; 3 | import initResizeWasm, { resize as wasmResize } from '../../../../../codecs/resize/pkg/squoosh_resize.js'; 4 | import initHqxWasm, { resize as wasmHqx } from '../../../../../codecs/hqx/pkg/squooshhqx.js'; 5 | 6 | interface HqxResizeOptions extends WorkerResizeOptions { 7 | method: 'hqx'; 8 | } 9 | 10 | function optsIsHqxOpts(opts: WorkerResizeOptions): opts is HqxResizeOptions { 11 | return opts.method === 'hqx'; 12 | } 13 | 14 | function crop( 15 | data: ImageData, 16 | sx: number, 17 | sy: number, 18 | sw: number, 19 | sh: number, 20 | ): ImageData { 21 | const inputPixels = new Uint32Array(data.data.buffer); 22 | 23 | // Copy within the same buffer for speed and memory efficiency. 24 | for (let y = 0; y < sh; y += 1) { 25 | const start = (y + sy) * data.width + sx; 26 | inputPixels.copyWithin(y * sw, start, start + sw); 27 | } 28 | 29 | return new ImageData( 30 | new Uint8ClampedArray(inputPixels.buffer.slice(0, sw * sh * 4)), 31 | sw, 32 | sh, 33 | ); 34 | } 35 | 36 | interface ClampOpts { 37 | min?: number; 38 | max?: number; 39 | } 40 | 41 | function clamp( 42 | num: number, 43 | { min = Number.MIN_VALUE, max = Number.MAX_VALUE }: ClampOpts, 44 | ): number { 45 | return Math.min(Math.max(num, min), max); 46 | } 47 | 48 | /** Resize methods by index */ 49 | const resizeMethods: WorkerResizeOptions['method'][] = [ 50 | 'triangle', 51 | 'catrom', 52 | 'mitchell', 53 | 'lanczos3', 54 | ]; 55 | 56 | let resizeWasmReady: Promise; 57 | let hqxWasmReady: Promise; 58 | 59 | async function hqx( 60 | input: ImageData, 61 | opts: HqxResizeOptions, 62 | ): Promise { 63 | if (!hqxWasmReady) { 64 | hqxWasmReady = initHqxWasm(); 65 | } 66 | 67 | await hqxWasmReady; 68 | 69 | const widthRatio = opts.width / input.width; 70 | const heightRatio = opts.height / input.height; 71 | const ratio = Math.max(widthRatio, heightRatio); 72 | const factor = clamp(Math.ceil(ratio), { min: 1, max: 4 }) as 1 | 2 | 3 | 4; 73 | 74 | if (factor === 1) return input; 75 | 76 | const result = wasmHqx( 77 | new Uint32Array(input.data.buffer), 78 | input.width, 79 | input.height, 80 | factor, 81 | ); 82 | 83 | return new ImageData( 84 | new Uint8ClampedArray(result.buffer), 85 | input.width * factor, 86 | input.height * factor, 87 | ); 88 | } 89 | 90 | export default async function resize( 91 | data: ImageData, 92 | opts: WorkerResizeOptions, 93 | ): Promise { 94 | let input = data; 95 | 96 | if (!resizeWasmReady) { 97 | resizeWasmReady = initResizeWasm(); 98 | } 99 | 100 | if (optsIsHqxOpts(opts)) { 101 | input = await hqx(input, opts); 102 | // Regular resize to make up the difference 103 | opts = { ...opts, method: 'catrom' }; 104 | } 105 | 106 | await resizeWasmReady; 107 | 108 | if (opts.fitMethod === 'contain') { 109 | const { sx, sy, sw, sh } = getContainOffsets( 110 | data.width, 111 | data.height, 112 | opts.width, 113 | opts.height, 114 | ); 115 | input = crop( 116 | input, 117 | Math.round(sx), 118 | Math.round(sy), 119 | Math.round(sw), 120 | Math.round(sh), 121 | ); 122 | } 123 | 124 | const result = wasmResize( 125 | new Uint8Array(input.data.buffer), 126 | input.width, 127 | input.height, 128 | opts.width, 129 | opts.height, 130 | resizeMethods.indexOf(opts.method), 131 | opts.premultiply, 132 | opts.linearRGB, 133 | ); 134 | 135 | return new ImageData( 136 | new Uint8ClampedArray(result.buffer), 137 | opts.width, 138 | opts.height, 139 | ); 140 | } 141 | -------------------------------------------------------------------------------- /codecs/jxl/enc/jxl_enc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "lib/jxl/base/thread_pool_internal.h" 5 | #include "lib/jxl/enc_external_image.h" 6 | #include "lib/jxl/enc_file.h" 7 | 8 | using namespace emscripten; 9 | 10 | thread_local const val Uint8Array = val::global("Uint8Array"); 11 | 12 | struct JXLOptions { 13 | // 1 = slowest 14 | // 7 = fastest 15 | int speed; 16 | float quality; 17 | bool progressive; 18 | int epf; 19 | int nearLossless; 20 | bool lossyPalette; 21 | size_t decodingSpeedTier; 22 | }; 23 | 24 | val encode(std::string image, int width, int height, JXLOptions options) { 25 | jxl::CompressParams cparams; 26 | jxl::PassesEncoderState passes_enc_state; 27 | jxl::CodecInOut io; 28 | jxl::PaddedBytes bytes; 29 | jxl::ImageBundle* main = &io.Main(); 30 | jxl::ThreadPoolInternal* pool_ptr = nullptr; 31 | #ifdef __EMSCRIPTEN_PTHREADS__ 32 | jxl::ThreadPoolInternal pool; 33 | pool_ptr = &pool; 34 | #endif 35 | 36 | cparams.epf = options.epf; 37 | cparams.speed_tier = static_cast(options.speed); 38 | cparams.decoding_speed_tier = options.decodingSpeedTier; 39 | 40 | if (options.lossyPalette || options.nearLossless) { 41 | cparams.lossy_palette = true; 42 | cparams.palette_colors = 0; 43 | cparams.options.predictor = jxl::Predictor::Zero; 44 | // Near-lossless assumes -R 0 45 | cparams.responsive = 0; 46 | cparams.modular_mode = true; 47 | } 48 | 49 | float quality = options.quality; 50 | 51 | // Quality settings roughly match libjpeg qualities. 52 | if (quality < 7 || quality == 100) { 53 | cparams.modular_mode = true; 54 | // Internal modular quality to roughly match VarDCT size. 55 | cparams.quality_pair.first = cparams.quality_pair.second = 56 | std::min(35 + (quality - 7) * 3.0f, 100.0f); 57 | } else { 58 | cparams.modular_mode = false; 59 | if (quality >= 30) { 60 | cparams.butteraugli_distance = 0.1 + (100 - quality) * 0.09; 61 | } else { 62 | cparams.butteraugli_distance = 6.4 + pow(2.5, (30 - quality) / 5.0f) / 6.25f; 63 | } 64 | } 65 | 66 | if (options.progressive) { 67 | cparams.qprogressive_mode = true; 68 | cparams.responsive = 1; 69 | if (!cparams.modular_mode) { 70 | cparams.progressive_dc = 1; 71 | } 72 | } 73 | 74 | if (cparams.modular_mode) { 75 | if (cparams.quality_pair.first != 100 || cparams.quality_pair.second != 100) { 76 | cparams.color_transform = jxl::ColorTransform::kXYB; 77 | } else { 78 | cparams.color_transform = jxl::ColorTransform::kNone; 79 | } 80 | } 81 | 82 | io.metadata.m.SetAlphaBits(8); 83 | if (!io.metadata.size.Set(width, height)) { 84 | return val::null(); 85 | } 86 | 87 | uint8_t* inBuffer = (uint8_t*)image.c_str(); 88 | 89 | auto result = jxl::ConvertFromExternal( 90 | jxl::Span(reinterpret_cast(image.data()), image.size()), width, 91 | height, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/true, 92 | /*alpha_is_premultiplied=*/false, /*bits_per_sample=*/8, /*endiannes=*/JXL_LITTLE_ENDIAN, 93 | /*flipped_y=*/false, pool_ptr, main); 94 | 95 | if (!result) { 96 | return val::null(); 97 | } 98 | 99 | auto js_result = val::null(); 100 | if (EncodeFile(cparams, &io, &passes_enc_state, &bytes, /*aux=*/nullptr, pool_ptr)) { 101 | js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data())); 102 | } 103 | 104 | return js_result; 105 | } 106 | 107 | EMSCRIPTEN_BINDINGS(my_module) { 108 | value_object("JXLOptions") 109 | .field("speed", &JXLOptions::speed) 110 | .field("quality", &JXLOptions::quality) 111 | .field("progressive", &JXLOptions::progressive) 112 | .field("nearLossless", &JXLOptions::nearLossless) 113 | .field("lossyPalette", &JXLOptions::lossyPalette) 114 | .field("decodingSpeedTier", &JXLOptions::decodingSpeedTier) 115 | .field("epf", &JXLOptions::epf); 116 | 117 | function("encode", &encode); 118 | } 119 | -------------------------------------------------------------------------------- /codecs/oxipng/pkg/squoosh_oxipng.js: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 5 | 6 | cachedTextDecoder.decode(); 7 | 8 | let cachegetUint8Memory0 = null; 9 | function getUint8Memory0() { 10 | if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { 11 | cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); 12 | } 13 | return cachegetUint8Memory0; 14 | } 15 | 16 | function getStringFromWasm0(ptr, len) { 17 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 18 | } 19 | 20 | let WASM_VECTOR_LEN = 0; 21 | 22 | function passArray8ToWasm0(arg, malloc) { 23 | const ptr = malloc(arg.length * 1); 24 | getUint8Memory0().set(arg, ptr / 1); 25 | WASM_VECTOR_LEN = arg.length; 26 | return ptr; 27 | } 28 | 29 | let cachegetInt32Memory0 = null; 30 | function getInt32Memory0() { 31 | if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { 32 | cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); 33 | } 34 | return cachegetInt32Memory0; 35 | } 36 | 37 | function getArrayU8FromWasm0(ptr, len) { 38 | return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); 39 | } 40 | /** 41 | * @param {Uint8Array} data 42 | * @param {number} level 43 | * @param {boolean} interlace 44 | * @returns {Uint8Array} 45 | */ 46 | export function optimise(data, level, interlace) { 47 | try { 48 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 49 | var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); 50 | var len0 = WASM_VECTOR_LEN; 51 | wasm.optimise(retptr, ptr0, len0, level, interlace); 52 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 53 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 54 | var v1 = getArrayU8FromWasm0(r0, r1).slice(); 55 | wasm.__wbindgen_free(r0, r1 * 1); 56 | return v1; 57 | } finally { 58 | wasm.__wbindgen_add_to_stack_pointer(16); 59 | } 60 | } 61 | 62 | async function load(module, imports) { 63 | if (typeof Response === 'function' && module instanceof Response) { 64 | if (typeof WebAssembly.instantiateStreaming === 'function') { 65 | try { 66 | return await WebAssembly.instantiateStreaming(module, imports); 67 | 68 | } catch (e) { 69 | if (module.headers.get('Content-Type') != 'application/wasm') { 70 | 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); 71 | 72 | } else { 73 | throw e; 74 | } 75 | } 76 | } 77 | 78 | const bytes = await module.arrayBuffer(); 79 | return await WebAssembly.instantiate(bytes, imports); 80 | 81 | } else { 82 | const instance = await WebAssembly.instantiate(module, imports); 83 | 84 | if (instance instanceof WebAssembly.Instance) { 85 | return { instance, module }; 86 | 87 | } else { 88 | return instance; 89 | } 90 | } 91 | } 92 | 93 | async function init(input) { 94 | if (typeof input === 'undefined') { 95 | input = new URL('squoosh_oxipng_bg.wasm', import.meta.url); 96 | } 97 | const imports = {}; 98 | imports.wbg = {}; 99 | imports.wbg.__wbindgen_throw = function(arg0, arg1) { 100 | throw new Error(getStringFromWasm0(arg0, arg1)); 101 | }; 102 | 103 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 104 | input = fetch(input); 105 | } 106 | 107 | 108 | 109 | const { instance, module } = await load(await input, imports); 110 | 111 | wasm = instance.exports; 112 | init.__wbindgen_wasm_module = module; 113 | 114 | return wasm; 115 | } 116 | 117 | export default init; 118 | 119 | -------------------------------------------------------------------------------- /src/client/lazy-app/feature-meta/index.ts: -------------------------------------------------------------------------------- 1 | // This file is autogenerated by lib/feature-plugin.js 2 | 3 | import * as avifEncoderMeta from '../../../features/encoders/avif/shared/meta'; 4 | import * as browserGIFEncoderMeta from '../../../features/encoders/browserGIF/shared/meta'; 5 | import * as browserJPEGEncoderMeta from '../../../features/encoders/browserJPEG/shared/meta'; 6 | import * as browserPNGEncoderMeta from '../../../features/encoders/browserPNG/shared/meta'; 7 | import * as jxlEncoderMeta from '../../../features/encoders/jxl/shared/meta'; 8 | import * as mozJPEGEncoderMeta from '../../../features/encoders/mozJPEG/shared/meta'; 9 | import * as oxiPNGEncoderMeta from '../../../features/encoders/oxiPNG/shared/meta'; 10 | import * as webPEncoderMeta from '../../../features/encoders/webP/shared/meta'; 11 | import * as wp2EncoderMeta from '../../../features/encoders/wp2/shared/meta'; 12 | import * as avifEncoderEntry from '../../../features/encoders/avif/client'; 13 | import * as browserGIFEncoderEntry from '../../../features/encoders/browserGIF/client'; 14 | import * as browserJPEGEncoderEntry from '../../../features/encoders/browserJPEG/client'; 15 | import * as browserPNGEncoderEntry from '../../../features/encoders/browserPNG/client'; 16 | import * as jxlEncoderEntry from '../../../features/encoders/jxl/client'; 17 | import * as mozJPEGEncoderEntry from '../../../features/encoders/mozJPEG/client'; 18 | import * as oxiPNGEncoderEntry from '../../../features/encoders/oxiPNG/client'; 19 | import * as webPEncoderEntry from '../../../features/encoders/webP/client'; 20 | import * as wp2EncoderEntry from '../../../features/encoders/wp2/client'; 21 | export type EncoderState = 22 | | { type: "avif", options: avifEncoderMeta.EncodeOptions } 23 | | { type: "browserGIF", options: browserGIFEncoderMeta.EncodeOptions } 24 | | { type: "browserJPEG", options: browserJPEGEncoderMeta.EncodeOptions } 25 | | { type: "browserPNG", options: browserPNGEncoderMeta.EncodeOptions } 26 | | { type: "jxl", options: jxlEncoderMeta.EncodeOptions } 27 | | { type: "mozJPEG", options: mozJPEGEncoderMeta.EncodeOptions } 28 | | { type: "oxiPNG", options: oxiPNGEncoderMeta.EncodeOptions } 29 | | { type: "webP", options: webPEncoderMeta.EncodeOptions } 30 | | { type: "wp2", options: wp2EncoderMeta.EncodeOptions } 31 | ; 32 | export type EncoderOptions = 33 | | avifEncoderMeta.EncodeOptions 34 | | browserGIFEncoderMeta.EncodeOptions 35 | | browserJPEGEncoderMeta.EncodeOptions 36 | | browserPNGEncoderMeta.EncodeOptions 37 | | jxlEncoderMeta.EncodeOptions 38 | | mozJPEGEncoderMeta.EncodeOptions 39 | | oxiPNGEncoderMeta.EncodeOptions 40 | | webPEncoderMeta.EncodeOptions 41 | | wp2EncoderMeta.EncodeOptions 42 | ; 43 | export const encoderMap = { 44 | avif: { meta: avifEncoderMeta, ...avifEncoderEntry }, 45 | browserGIF: { meta: browserGIFEncoderMeta, ...browserGIFEncoderEntry }, 46 | browserJPEG: { meta: browserJPEGEncoderMeta, ...browserJPEGEncoderEntry }, 47 | browserPNG: { meta: browserPNGEncoderMeta, ...browserPNGEncoderEntry }, 48 | jxl: { meta: jxlEncoderMeta, ...jxlEncoderEntry }, 49 | mozJPEG: { meta: mozJPEGEncoderMeta, ...mozJPEGEncoderEntry }, 50 | oxiPNG: { meta: oxiPNGEncoderMeta, ...oxiPNGEncoderEntry }, 51 | webP: { meta: webPEncoderMeta, ...webPEncoderEntry }, 52 | wp2: { meta: wp2EncoderMeta, ...wp2EncoderEntry }, 53 | }; 54 | export type EncoderType = keyof typeof encoderMap 55 | import * as quantizeProcessorMeta from '../../../features/processors/quantize/shared/meta'; 56 | import * as resizeProcessorMeta from '../../../features/processors/resize/shared/meta'; 57 | interface Enableable { enabled: boolean; } 58 | export interface ProcessorOptions { 59 | quantize: quantizeProcessorMeta.Options; 60 | resize: resizeProcessorMeta.Options; 61 | } 62 | export interface ProcessorState { 63 | quantize: Enableable & quantizeProcessorMeta.Options; 64 | resize: Enableable & resizeProcessorMeta.Options; 65 | } 66 | export const defaultProcessorState: ProcessorState = { 67 | quantize: { enabled: false, ...quantizeProcessorMeta.defaultOptions }, 68 | resize: { enabled: false, ...resizeProcessorMeta.defaultOptions }, 69 | } 70 | import * as rotatePreprocessorMeta from '../../../features/preprocessors/rotate/shared/meta'; 71 | export interface PreprocessorState { 72 | rotate: rotatePreprocessorMeta.Options, 73 | } 74 | export const defaultPreprocessorState: PreprocessorState = { 75 | rotate: rotatePreprocessorMeta.defaultOptions, 76 | }; -------------------------------------------------------------------------------- /codecs/oxipng/pkg-parallel/snippets/wasm-bindgen-rayon-3d2df09ebec17a22/src/workerHelpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // Note: we use `wasm_bindgen_worker_`-prefixed message types to make sure 15 | // we can handle bundling into other files, which might happen to have their 16 | // own `postMessage`/`onmessage` communication channels. 17 | // 18 | // If we didn't take that into the account, we could send much simpler signals 19 | // like just `0` or whatever, but the code would be less resilient. 20 | 21 | function waitForMsgType(target, type) { 22 | return new Promise(resolve => { 23 | target.addEventListener('message', function onMsg({ data }) { 24 | if (data == null || data.type !== type) return; 25 | target.removeEventListener('message', onMsg); 26 | resolve(data); 27 | }); 28 | }); 29 | } 30 | 31 | waitForMsgType(self, 'wasm_bindgen_worker_init').then(async data => { 32 | // # Note 1 33 | // Our JS should have been generated in 34 | // `[out-dir]/snippets/wasm-bindgen-rayon-[hash]/workerHelpers.js`, 35 | // resolve the main module via `../../..`. 36 | // 37 | // This might need updating if the generated structure changes on wasm-bindgen 38 | // side ever in the future, but works well with bundlers today. The whole 39 | // point of this crate, after all, is to abstract away unstable features 40 | // and temporary bugs so that you don't need to deal with them in your code. 41 | // 42 | // # Note 2 43 | // This could be a regular import, but then some bundlers complain about 44 | // circular deps. 45 | // 46 | // Dynamic import could be cheap if this file was inlined into the parent, 47 | // which would require us just using `../../..` in `new Worker` below, 48 | // but that doesn't work because wasm-pack unconditionally adds 49 | // "sideEffects":false (see below). 50 | // 51 | // OTOH, even though it can't be inlined, it should be still reasonably 52 | // cheap since the requested file is already in cache (it was loaded by 53 | // the main thread). 54 | const pkg = await import('../../../squoosh_oxipng'); 55 | await pkg.default(data.module, data.memory); 56 | postMessage({ type: 'wasm_bindgen_worker_ready' }); 57 | pkg.wbg_rayon_start_worker(data.receiver); 58 | }); 59 | 60 | export async function startWorkers(module, memory, builder) { 61 | const workerInit = { 62 | type: 'wasm_bindgen_worker_init', 63 | module, 64 | memory, 65 | receiver: builder.receiver() 66 | }; 67 | 68 | try { 69 | await Promise.all( 70 | Array.from({ length: builder.numThreads() }, () => { 71 | // Self-spawn into a new Worker. 72 | // 73 | // TODO: while `new URL('...', import.meta.url) becomes a semi-standard 74 | // way to get asset URLs relative to the module across various bundlers 75 | // and browser, ideally we should switch to `import.meta.resolve` 76 | // once it becomes a standard. 77 | // 78 | // Note: we could use `../../..` as the URL here to inline workerHelpers.js 79 | // into the parent entry instead of creating another split point - 80 | // this would be preferable from optimization perspective - 81 | // however, Webpack then eliminates all message handler code 82 | // because wasm-pack produces "sideEffects":false in package.json 83 | // unconditionally. 84 | // 85 | // The only way to work around that is to have side effect code 86 | // in an entry point such as Worker file itself. 87 | const worker = new Worker(new URL('./workerHelpers.js', import.meta.url), { 88 | type: 'module' 89 | }); 90 | worker.postMessage(workerInit); 91 | return waitForMsgType(worker, 'wasm_bindgen_worker_ready'); 92 | }) 93 | ); 94 | builder.build(); 95 | } finally { 96 | builder.free(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /codecs/resize/pkg/squoosh_resize.js: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | let cachegetUint8Memory0 = null; 5 | function getUint8Memory0() { 6 | if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { 7 | cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); 8 | } 9 | return cachegetUint8Memory0; 10 | } 11 | 12 | let WASM_VECTOR_LEN = 0; 13 | 14 | function passArray8ToWasm0(arg, malloc) { 15 | const ptr = malloc(arg.length * 1); 16 | getUint8Memory0().set(arg, ptr / 1); 17 | WASM_VECTOR_LEN = arg.length; 18 | return ptr; 19 | } 20 | 21 | let cachegetInt32Memory0 = null; 22 | function getInt32Memory0() { 23 | if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { 24 | cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); 25 | } 26 | return cachegetInt32Memory0; 27 | } 28 | 29 | let cachegetUint8ClampedMemory0 = null; 30 | function getUint8ClampedMemory0() { 31 | if (cachegetUint8ClampedMemory0 === null || cachegetUint8ClampedMemory0.buffer !== wasm.memory.buffer) { 32 | cachegetUint8ClampedMemory0 = new Uint8ClampedArray(wasm.memory.buffer); 33 | } 34 | return cachegetUint8ClampedMemory0; 35 | } 36 | 37 | function getClampedArrayU8FromWasm0(ptr, len) { 38 | return getUint8ClampedMemory0().subarray(ptr / 1, ptr / 1 + len); 39 | } 40 | /** 41 | * @param {Uint8Array} input_image 42 | * @param {number} input_width 43 | * @param {number} input_height 44 | * @param {number} output_width 45 | * @param {number} output_height 46 | * @param {number} typ_idx 47 | * @param {boolean} premultiply 48 | * @param {boolean} color_space_conversion 49 | * @returns {Uint8ClampedArray} 50 | */ 51 | export function resize(input_image, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion) { 52 | try { 53 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 54 | var ptr0 = passArray8ToWasm0(input_image, wasm.__wbindgen_malloc); 55 | var len0 = WASM_VECTOR_LEN; 56 | wasm.resize(retptr, ptr0, len0, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion); 57 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 58 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 59 | var v1 = getClampedArrayU8FromWasm0(r0, r1).slice(); 60 | wasm.__wbindgen_free(r0, r1 * 1); 61 | return v1; 62 | } finally { 63 | wasm.__wbindgen_add_to_stack_pointer(16); 64 | } 65 | } 66 | 67 | async function load(module, imports) { 68 | if (typeof Response === 'function' && module instanceof Response) { 69 | if (typeof WebAssembly.instantiateStreaming === 'function') { 70 | try { 71 | return await WebAssembly.instantiateStreaming(module, imports); 72 | 73 | } catch (e) { 74 | if (module.headers.get('Content-Type') != 'application/wasm') { 75 | 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); 76 | 77 | } else { 78 | throw e; 79 | } 80 | } 81 | } 82 | 83 | const bytes = await module.arrayBuffer(); 84 | return await WebAssembly.instantiate(bytes, imports); 85 | 86 | } else { 87 | const instance = await WebAssembly.instantiate(module, imports); 88 | 89 | if (instance instanceof WebAssembly.Instance) { 90 | return { instance, module }; 91 | 92 | } else { 93 | return instance; 94 | } 95 | } 96 | } 97 | 98 | async function init(input) { 99 | if (typeof input === 'undefined') { 100 | input = new URL('squoosh_resize_bg.wasm', import.meta.url); 101 | } 102 | const imports = {}; 103 | 104 | 105 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 106 | input = fetch(input); 107 | } 108 | 109 | 110 | 111 | const { instance, module } = await load(await input, imports); 112 | 113 | wasm = instance.exports; 114 | init.__wbindgen_wasm_module = module; 115 | 116 | return wasm; 117 | } 118 | 119 | export default init; 120 | 121 | -------------------------------------------------------------------------------- /emscripten-types.d.ts: -------------------------------------------------------------------------------- 1 | // These types roughly model the object that the JS files generated by Emscripten define. Copied from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/emscripten/index.d.ts and turned into a type definition rather than a global to support our way of using Emscripten. 2 | // TODO(@surma): Upstream this? 3 | declare namespace EmscriptenWasm { 4 | type ModuleFactory = ( 5 | moduleOverrides?: ModuleOpts, 6 | ) => Promise; 7 | 8 | type EnvironmentType = 'WEB' | 'NODE' | 'SHELL' | 'WORKER'; 9 | 10 | // Options object for modularized Emscripten files. Shoe-horned by @surma. 11 | // FIXME: This an incomplete definition! 12 | interface ModuleOpts { 13 | mainScriptUrlOrBlob?: string; 14 | noInitialRun?: boolean; 15 | locateFile?: (url: string) => string; 16 | onRuntimeInitialized?: () => void; 17 | } 18 | 19 | interface Module { 20 | print(str: string): void; 21 | printErr(str: string): void; 22 | arguments: string[]; 23 | environment: EnvironmentType; 24 | preInit: { (): void }[]; 25 | preRun: { (): void }[]; 26 | postRun: { (): void }[]; 27 | preinitializedWebGLContext: WebGLRenderingContext; 28 | noInitialRun: boolean; 29 | noExitRuntime: boolean; 30 | logReadFiles: boolean; 31 | filePackagePrefixURL: string; 32 | wasmBinary: ArrayBuffer; 33 | 34 | destroy(object: object): void; 35 | getPreloadedPackage( 36 | remotePackageName: string, 37 | remotePackageSize: number, 38 | ): ArrayBuffer; 39 | instantiateWasm( 40 | imports: WebAssembly.Imports, 41 | successCallback: (module: WebAssembly.Module) => void, 42 | ): WebAssembly.Exports; 43 | locateFile(url: string): string; 44 | onCustomMessage(event: MessageEvent): void; 45 | 46 | Runtime: any; 47 | 48 | ccall( 49 | ident: string, 50 | returnType: string | null, 51 | argTypes: string[], 52 | args: any[], 53 | ): any; 54 | cwrap(ident: string, returnType: string | null, argTypes: string[]): any; 55 | 56 | setValue(ptr: number, value: any, type: string, noSafe?: boolean): void; 57 | getValue(ptr: number, type: string, noSafe?: boolean): number; 58 | 59 | ALLOC_NORMAL: number; 60 | ALLOC_STACK: number; 61 | ALLOC_STATIC: number; 62 | ALLOC_DYNAMIC: number; 63 | ALLOC_NONE: number; 64 | 65 | allocate(slab: any, types: string, allocator: number, ptr: number): number; 66 | allocate( 67 | slab: any, 68 | types: string[], 69 | allocator: number, 70 | ptr: number, 71 | ): number; 72 | 73 | Pointer_stringify(ptr: number, length?: number): string; 74 | UTF16ToString(ptr: number): string; 75 | stringToUTF16(str: string, outPtr: number): void; 76 | UTF32ToString(ptr: number): string; 77 | stringToUTF32(str: string, outPtr: number): void; 78 | 79 | // USE_TYPED_ARRAYS == 1 80 | HEAP: Int32Array; 81 | IHEAP: Int32Array; 82 | FHEAP: Float64Array; 83 | 84 | // USE_TYPED_ARRAYS == 2 85 | HEAP8: Int8Array; 86 | HEAP16: Int16Array; 87 | HEAP32: Int32Array; 88 | HEAPU8: Uint8Array; 89 | HEAPU16: Uint16Array; 90 | HEAPU32: Uint32Array; 91 | HEAPF32: Float32Array; 92 | HEAPF64: Float64Array; 93 | 94 | TOTAL_STACK: number; 95 | TOTAL_MEMORY: number; 96 | FAST_MEMORY: number; 97 | 98 | addOnPreRun(cb: () => any): void; 99 | addOnInit(cb: () => any): void; 100 | addOnPreMain(cb: () => any): void; 101 | addOnExit(cb: () => any): void; 102 | addOnPostRun(cb: () => any): void; 103 | 104 | // Tools 105 | intArrayFromString( 106 | stringy: string, 107 | dontAddNull?: boolean, 108 | length?: number, 109 | ): number[]; 110 | intArrayToString(array: number[]): string; 111 | writeStringToMemory( 112 | str: string, 113 | buffer: number, 114 | dontAddNull: boolean, 115 | ): void; 116 | writeArrayToMemory(array: number[], buffer: number): void; 117 | writeAsciiToMemory(str: string, buffer: number, dontAddNull: boolean): void; 118 | 119 | addRunDependency(id: any): void; 120 | removeRunDependency(id: any): void; 121 | 122 | preloadedImages: any; 123 | preloadedAudios: any; 124 | 125 | _malloc(size: number): number; 126 | _free(ptr: number): void; 127 | 128 | // Augmentations below by @surma. 129 | onRuntimeInitialized: () => void | null; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /codecs/jxl/dec/jxl_dec.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "lib/jxl/color_encoding_internal.h" 6 | 7 | #include "skcms.h" 8 | 9 | using namespace emscripten; 10 | 11 | thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray"); 12 | thread_local const val ImageData = val::global("ImageData"); 13 | 14 | // R, G, B, A 15 | #define COMPONENTS_PER_PIXEL 4 16 | 17 | #ifndef JXL_DEBUG_ON_ALL_ERROR 18 | #define JXL_DEBUG_ON_ALL_ERROR 0 19 | #endif 20 | 21 | #if JXL_DEBUG_ON_ALL_ERROR 22 | #define EXPECT_TRUE(a) \ 23 | if (!(a)) { \ 24 | fprintf(stderr, "Assertion failure (%d): %s\n", __LINE__, #a); \ 25 | return val::null(); \ 26 | } 27 | #define EXPECT_EQ(a, b) \ 28 | { \ 29 | int a_ = a; \ 30 | int b_ = b; \ 31 | if (a_ != b_) { \ 32 | fprintf(stderr, "Assertion failure (%d): %s (%d) != %s (%d)\n", __LINE__, #a, a_, #b, b_); \ 33 | return val::null(); \ 34 | } \ 35 | } 36 | #else 37 | #define EXPECT_TRUE(a) \ 38 | if (!(a)) { \ 39 | return val::null(); \ 40 | } 41 | 42 | #define EXPECT_EQ(a, b) EXPECT_TRUE((a) == (b)); 43 | #endif 44 | 45 | val decode(std::string data) { 46 | std::unique_ptr> 48 | dec(JxlDecoderCreate(nullptr)); 49 | EXPECT_EQ(JXL_DEC_SUCCESS, 50 | JxlDecoderSubscribeEvents( 51 | dec.get(), JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE)); 52 | 53 | auto next_in = (const uint8_t*)data.c_str(); 54 | auto avail_in = data.size(); 55 | JxlDecoderSetInput(dec.get(), next_in, avail_in); 56 | EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec.get())); 57 | JxlBasicInfo info; 58 | EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec.get(), &info)); 59 | size_t pixel_count = info.xsize * info.ysize; 60 | size_t component_count = pixel_count * COMPONENTS_PER_PIXEL; 61 | 62 | EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec.get())); 63 | static const JxlPixelFormat format = {COMPONENTS_PER_PIXEL, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; 64 | size_t icc_size; 65 | EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetICCProfileSize(dec.get(), &format, 66 | JXL_COLOR_PROFILE_TARGET_DATA, &icc_size)); 67 | std::vector icc_profile(icc_size); 68 | EXPECT_EQ(JXL_DEC_SUCCESS, 69 | JxlDecoderGetColorAsICCProfile(dec.get(), &format, JXL_COLOR_PROFILE_TARGET_DATA, 70 | icc_profile.data(), icc_profile.size())); 71 | 72 | EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec.get())); 73 | size_t buffer_size; 74 | EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)); 75 | EXPECT_EQ(buffer_size, component_count * sizeof(float)); 76 | 77 | auto float_pixels = std::make_unique(component_count); 78 | EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(dec.get(), &format, float_pixels.get(), 79 | component_count * sizeof(float))); 80 | EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec.get())); 81 | 82 | auto byte_pixels = std::make_unique(component_count); 83 | // Convert to sRGB. 84 | skcms_ICCProfile jxl_profile; 85 | EXPECT_TRUE(skcms_Parse(icc_profile.data(), icc_profile.size(), &jxl_profile)); 86 | EXPECT_TRUE(skcms_Transform( 87 | float_pixels.get(), skcms_PixelFormat_RGBA_ffff, 88 | info.alpha_premultiplied ? skcms_AlphaFormat_PremulAsEncoded : skcms_AlphaFormat_Unpremul, 89 | &jxl_profile, byte_pixels.get(), skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul, 90 | skcms_sRGB_profile(), pixel_count)); 91 | 92 | return ImageData.new_( 93 | Uint8ClampedArray.new_(typed_memory_view(component_count, byte_pixels.get())), info.xsize, 94 | info.ysize); 95 | } 96 | 97 | EMSCRIPTEN_BINDINGS(my_module) { 98 | function("decode", &decode); 99 | } 100 | -------------------------------------------------------------------------------- /src/client/lazy-app/util/canvas.ts: -------------------------------------------------------------------------------- 1 | /** Replace the contents of a canvas with the given data */ 2 | export function drawDataToCanvas(canvas: HTMLCanvasElement, data: ImageData) { 3 | const ctx = canvas.getContext('2d'); 4 | if (!ctx) throw Error('Canvas not initialized'); 5 | ctx.clearRect(0, 0, canvas.width, canvas.height); 6 | ctx.putImageData(data, 0, 0); 7 | } 8 | 9 | /** 10 | * Encode some image data in a given format using the browser's encoders 11 | * 12 | * @param {ImageData} data 13 | * @param {string} type A mime type, eg image/jpeg. 14 | * @param {number} [quality] Between 0-1. 15 | */ 16 | export async function canvasEncode( 17 | data: ImageData, 18 | type: string, 19 | quality?: number, 20 | ): Promise { 21 | const canvas = document.createElement('canvas'); 22 | canvas.width = data.width; 23 | canvas.height = data.height; 24 | const ctx = canvas.getContext('2d'); 25 | if (!ctx) throw Error('Canvas not initialized'); 26 | ctx.putImageData(data, 0, 0); 27 | 28 | let blob: Blob | null; 29 | 30 | if ('toBlob' in canvas) { 31 | blob = await new Promise((r) => 32 | canvas.toBlob(r, type, quality), 33 | ); 34 | } else { 35 | // Welcome to Edge. 36 | // TypeScript thinks `canvas` is 'never', so it needs casting. 37 | const dataUrl = (canvas as HTMLCanvasElement).toDataURL(type, quality); 38 | const result = /data:([^;]+);base64,(.*)$/.exec(dataUrl); 39 | 40 | if (!result) throw Error('Data URL reading failed'); 41 | 42 | const outputType = result[1]; 43 | const binaryStr = atob(result[2]); 44 | const data = new Uint8Array(binaryStr.length); 45 | 46 | for (let i = 0; i < data.length; i += 1) { 47 | data[i] = binaryStr.charCodeAt(i); 48 | } 49 | 50 | blob = new Blob([data], { type: outputType }); 51 | } 52 | 53 | if (!blob) throw Error('Encoding failed'); 54 | return blob; 55 | } 56 | 57 | interface DrawableToImageDataOptions { 58 | width?: number; 59 | height?: number; 60 | sx?: number; 61 | sy?: number; 62 | sw?: number; 63 | sh?: number; 64 | } 65 | 66 | function getWidth( 67 | drawable: ImageBitmap | HTMLImageElement | VideoFrame, 68 | ): number { 69 | if ('displayWidth' in drawable) { 70 | return drawable.displayWidth; 71 | } 72 | return drawable.width; 73 | } 74 | 75 | function getHeight( 76 | drawable: ImageBitmap | HTMLImageElement | VideoFrame, 77 | ): number { 78 | if ('displayHeight' in drawable) { 79 | return drawable.displayHeight; 80 | } 81 | return drawable.height; 82 | } 83 | 84 | export function drawableToImageData( 85 | drawable: ImageBitmap | HTMLImageElement | VideoFrame, 86 | opts: DrawableToImageDataOptions = {}, 87 | ): ImageData { 88 | const { 89 | width = getWidth(drawable), 90 | height = getHeight(drawable), 91 | sx = 0, 92 | sy = 0, 93 | sw = getWidth(drawable), 94 | sh = getHeight(drawable), 95 | } = opts; 96 | 97 | // Make canvas same size as image 98 | const canvas = document.createElement('canvas'); 99 | canvas.width = width; 100 | canvas.height = height; 101 | // Draw image onto canvas 102 | const ctx = canvas.getContext('2d'); 103 | if (!ctx) throw new Error('Could not create canvas context'); 104 | ctx.drawImage(drawable, sx, sy, sw, sh, 0, 0, width, height); 105 | return ctx.getImageData(0, 0, width, height); 106 | } 107 | 108 | export type BuiltinResizeMethod = 'pixelated' | 'low' | 'medium' | 'high'; 109 | 110 | export function builtinResize( 111 | data: ImageData, 112 | sx: number, 113 | sy: number, 114 | sw: number, 115 | sh: number, 116 | dw: number, 117 | dh: number, 118 | method: BuiltinResizeMethod, 119 | ): ImageData { 120 | const canvasSource = document.createElement('canvas'); 121 | canvasSource.width = data.width; 122 | canvasSource.height = data.height; 123 | drawDataToCanvas(canvasSource, data); 124 | 125 | const canvasDest = document.createElement('canvas'); 126 | canvasDest.width = dw; 127 | canvasDest.height = dh; 128 | const ctx = canvasDest.getContext('2d'); 129 | if (!ctx) throw new Error('Could not create canvas context'); 130 | 131 | if (method === 'pixelated') { 132 | ctx.imageSmoothingEnabled = false; 133 | } else { 134 | ctx.imageSmoothingQuality = method; 135 | } 136 | 137 | ctx.drawImage(canvasSource, sx, sy, sw, sh, 0, 0, dw, dh); 138 | return ctx.getImageData(0, 0, dw, dh); 139 | } 140 | 141 | /** 142 | * Test whether can encode to a particular type. 143 | */ 144 | export async function canvasEncodeTest(mimeType: string): Promise { 145 | try { 146 | const blob = await canvasEncode(new ImageData(1, 1), mimeType); 147 | // According to the spec, the blob should be null if the format isn't supported… 148 | if (!blob) return false; 149 | // …but Safari & Firefox fall back to PNG, so we need to check the mime type. 150 | return blob.type === mimeType; 151 | } catch (err) { 152 | return false; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /codecs/avif/enc/avif_enc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "avif/avif.h" 5 | 6 | using namespace emscripten; 7 | 8 | struct AvifOptions { 9 | // [0 - 63] 10 | // 0 = lossless 11 | // 63 = worst quality 12 | int cqLevel; 13 | // As above, but -1 means 'use cqLevel' 14 | int cqAlphaLevel; 15 | // [0 - 6] 16 | // Creates 2^n tiles in that dimension 17 | int tileRowsLog2; 18 | int tileColsLog2; 19 | // [0 - 10] 20 | // 0 = slowest 21 | // 10 = fastest 22 | int speed; 23 | // 0 = 4:0:0 24 | // 1 = 4:2:0 25 | // 2 = 4:2:2 26 | // 3 = 4:4:4 27 | int subsample; 28 | // Extra chroma compression 29 | bool chromaDeltaQ; 30 | // 0-7 31 | int sharpness; 32 | // 0 = auto 33 | // 1 = PSNR 34 | // 2 = SSIM 35 | int tune; 36 | // 0-50 37 | int denoiseLevel; 38 | }; 39 | 40 | thread_local const val Uint8Array = val::global("Uint8Array"); 41 | 42 | val encode(std::string buffer, int width, int height, AvifOptions options) { 43 | avifRWData output = AVIF_DATA_EMPTY; 44 | int depth = 8; 45 | avifPixelFormat format; 46 | switch (options.subsample) { 47 | case 0: 48 | format = AVIF_PIXEL_FORMAT_YUV400; 49 | break; 50 | case 1: 51 | format = AVIF_PIXEL_FORMAT_YUV420; 52 | break; 53 | case 2: 54 | format = AVIF_PIXEL_FORMAT_YUV422; 55 | break; 56 | case 3: 57 | format = AVIF_PIXEL_FORMAT_YUV444; 58 | break; 59 | } 60 | 61 | bool lossless = options.cqLevel == AVIF_QUANTIZER_LOSSLESS && 62 | options.cqAlphaLevel <= AVIF_QUANTIZER_LOSSLESS && 63 | format == AVIF_PIXEL_FORMAT_YUV444; 64 | 65 | avifImage* image = avifImageCreate(width, height, depth, format); 66 | 67 | if (lossless) { 68 | image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY; 69 | } else { 70 | image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601; 71 | } 72 | 73 | uint8_t* rgba = reinterpret_cast(const_cast(buffer.data())); 74 | 75 | avifRGBImage srcRGB; 76 | avifRGBImageSetDefaults(&srcRGB, image); 77 | srcRGB.pixels = rgba; 78 | srcRGB.rowBytes = width * 4; 79 | avifImageRGBToYUV(image, &srcRGB); 80 | 81 | avifEncoder* encoder = avifEncoderCreate(); 82 | 83 | if (lossless) { 84 | encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS; 85 | encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS; 86 | encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS; 87 | encoder->maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS; 88 | } else { 89 | encoder->minQuantizer = AVIF_QUANTIZER_BEST_QUALITY; 90 | encoder->maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY; 91 | encoder->minQuantizerAlpha = AVIF_QUANTIZER_BEST_QUALITY; 92 | encoder->maxQuantizerAlpha = AVIF_QUANTIZER_WORST_QUALITY; 93 | avifEncoderSetCodecSpecificOption(encoder, "end-usage", "q"); 94 | avifEncoderSetCodecSpecificOption(encoder, "cq-level", std::to_string(options.cqLevel).c_str()); 95 | avifEncoderSetCodecSpecificOption(encoder, "sharpness", 96 | std::to_string(options.sharpness).c_str()); 97 | 98 | if (options.cqAlphaLevel != -1) { 99 | avifEncoderSetCodecSpecificOption(encoder, "alpha:cq-level", 100 | std::to_string(options.cqAlphaLevel).c_str()); 101 | } 102 | 103 | if (options.tune == 2 || (options.tune == 0 && options.cqLevel <= 32)) { 104 | avifEncoderSetCodecSpecificOption(encoder, "tune", "ssim"); 105 | } 106 | 107 | if (options.chromaDeltaQ) { 108 | avifEncoderSetCodecSpecificOption(encoder, "enable-chroma-deltaq", "1"); 109 | } 110 | 111 | avifEncoderSetCodecSpecificOption(encoder, "color:denoise-noise-level", 112 | std::to_string(options.denoiseLevel).c_str()); 113 | } 114 | 115 | encoder->maxThreads = emscripten_num_logical_cores(); 116 | encoder->tileRowsLog2 = options.tileRowsLog2; 117 | encoder->tileColsLog2 = options.tileColsLog2; 118 | encoder->speed = options.speed; 119 | 120 | avifResult encodeResult = avifEncoderWrite(encoder, image, &output); 121 | auto js_result = val::null(); 122 | if (encodeResult == AVIF_RESULT_OK) { 123 | js_result = Uint8Array.new_(typed_memory_view(output.size, output.data)); 124 | } 125 | 126 | avifImageDestroy(image); 127 | avifEncoderDestroy(encoder); 128 | avifRWDataFree(&output); 129 | return js_result; 130 | } 131 | 132 | EMSCRIPTEN_BINDINGS(my_module) { 133 | value_object("AvifOptions") 134 | .field("cqLevel", &AvifOptions::cqLevel) 135 | .field("cqAlphaLevel", &AvifOptions::cqAlphaLevel) 136 | .field("tileRowsLog2", &AvifOptions::tileRowsLog2) 137 | .field("tileColsLog2", &AvifOptions::tileColsLog2) 138 | .field("speed", &AvifOptions::speed) 139 | .field("chromaDeltaQ", &AvifOptions::chromaDeltaQ) 140 | .field("sharpness", &AvifOptions::sharpness) 141 | .field("tune", &AvifOptions::tune) 142 | .field("denoiseLevel", &AvifOptions::denoiseLevel) 143 | .field("subsample", &AvifOptions::subsample); 144 | 145 | function("encode", &encode); 146 | } 147 | -------------------------------------------------------------------------------- /codecs/png/pkg/squoosh_png.js: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 5 | 6 | cachedTextDecoder.decode(); 7 | 8 | let cachegetUint8Memory0 = null; 9 | function getUint8Memory0() { 10 | if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { 11 | cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); 12 | } 13 | return cachegetUint8Memory0; 14 | } 15 | 16 | function getStringFromWasm0(ptr, len) { 17 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 18 | } 19 | 20 | let cachegetUint8ClampedMemory0 = null; 21 | function getUint8ClampedMemory0() { 22 | if (cachegetUint8ClampedMemory0 === null || cachegetUint8ClampedMemory0.buffer !== wasm.memory.buffer) { 23 | cachegetUint8ClampedMemory0 = new Uint8ClampedArray(wasm.memory.buffer); 24 | } 25 | return cachegetUint8ClampedMemory0; 26 | } 27 | 28 | function getClampedArrayU8FromWasm0(ptr, len) { 29 | return getUint8ClampedMemory0().subarray(ptr / 1, ptr / 1 + len); 30 | } 31 | 32 | const heap = new Array(32).fill(undefined); 33 | 34 | heap.push(undefined, null, true, false); 35 | 36 | let heap_next = heap.length; 37 | 38 | function addHeapObject(obj) { 39 | if (heap_next === heap.length) heap.push(heap.length + 1); 40 | const idx = heap_next; 41 | heap_next = heap[idx]; 42 | 43 | heap[idx] = obj; 44 | return idx; 45 | } 46 | 47 | let WASM_VECTOR_LEN = 0; 48 | 49 | function passArray8ToWasm0(arg, malloc) { 50 | const ptr = malloc(arg.length * 1); 51 | getUint8Memory0().set(arg, ptr / 1); 52 | WASM_VECTOR_LEN = arg.length; 53 | return ptr; 54 | } 55 | 56 | let cachegetInt32Memory0 = null; 57 | function getInt32Memory0() { 58 | if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { 59 | cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); 60 | } 61 | return cachegetInt32Memory0; 62 | } 63 | 64 | function getArrayU8FromWasm0(ptr, len) { 65 | return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); 66 | } 67 | /** 68 | * @param {Uint8Array} data 69 | * @param {number} width 70 | * @param {number} height 71 | * @returns {Uint8Array} 72 | */ 73 | export function encode(data, width, height) { 74 | try { 75 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 76 | var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); 77 | var len0 = WASM_VECTOR_LEN; 78 | wasm.encode(retptr, ptr0, len0, width, height); 79 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 80 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 81 | var v1 = getArrayU8FromWasm0(r0, r1).slice(); 82 | wasm.__wbindgen_free(r0, r1 * 1); 83 | return v1; 84 | } finally { 85 | wasm.__wbindgen_add_to_stack_pointer(16); 86 | } 87 | } 88 | 89 | function getObject(idx) { return heap[idx]; } 90 | 91 | function dropObject(idx) { 92 | if (idx < 36) return; 93 | heap[idx] = heap_next; 94 | heap_next = idx; 95 | } 96 | 97 | function takeObject(idx) { 98 | const ret = getObject(idx); 99 | dropObject(idx); 100 | return ret; 101 | } 102 | /** 103 | * @param {Uint8Array} data 104 | * @returns {ImageData} 105 | */ 106 | export function decode(data) { 107 | var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); 108 | var len0 = WASM_VECTOR_LEN; 109 | var ret = wasm.decode(ptr0, len0); 110 | return takeObject(ret); 111 | } 112 | 113 | async function load(module, imports) { 114 | if (typeof Response === 'function' && module instanceof Response) { 115 | if (typeof WebAssembly.instantiateStreaming === 'function') { 116 | try { 117 | return await WebAssembly.instantiateStreaming(module, imports); 118 | 119 | } catch (e) { 120 | if (module.headers.get('Content-Type') != 'application/wasm') { 121 | 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); 122 | 123 | } else { 124 | throw e; 125 | } 126 | } 127 | } 128 | 129 | const bytes = await module.arrayBuffer(); 130 | return await WebAssembly.instantiate(bytes, imports); 131 | 132 | } else { 133 | const instance = await WebAssembly.instantiate(module, imports); 134 | 135 | if (instance instanceof WebAssembly.Instance) { 136 | return { instance, module }; 137 | 138 | } else { 139 | return instance; 140 | } 141 | } 142 | } 143 | 144 | async function init(input) { 145 | if (typeof input === 'undefined') { 146 | input = new URL('squoosh_png_bg.wasm', import.meta.url); 147 | } 148 | const imports = {}; 149 | imports.wbg = {}; 150 | imports.wbg.__wbg_newwithownedu8clampedarrayandsh_787b2db8ea6bfd62 = function(arg0, arg1, arg2, arg3) { 151 | var v0 = getClampedArrayU8FromWasm0(arg0, arg1).slice(); 152 | wasm.__wbindgen_free(arg0, arg1 * 1); 153 | var ret = new ImageData(v0, arg2 >>> 0, arg3 >>> 0); 154 | return addHeapObject(ret); 155 | }; 156 | imports.wbg.__wbindgen_throw = function(arg0, arg1) { 157 | throw new Error(getStringFromWasm0(arg0, arg1)); 158 | }; 159 | 160 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 161 | input = fetch(input); 162 | } 163 | 164 | 165 | 166 | const { instance, module } = await load(await input, imports); 167 | 168 | wasm = instance.exports; 169 | init.__wbindgen_wasm_module = module; 170 | 171 | return wasm; 172 | } 173 | 174 | export default init; 175 | 176 | -------------------------------------------------------------------------------- /codecs/oxipng/pkg-parallel/squoosh_oxipng.js: -------------------------------------------------------------------------------- 1 | import { startWorkers } from './snippets/wasm-bindgen-rayon-3d2df09ebec17a22/src/workerHelpers.js'; 2 | 3 | let wasm; 4 | 5 | let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 6 | 7 | cachedTextDecoder.decode(); 8 | 9 | let cachegetUint8Memory0 = null; 10 | function getUint8Memory0() { 11 | if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.__wbindgen_export_0.buffer) { 12 | cachegetUint8Memory0 = new Uint8Array(wasm.__wbindgen_export_0.buffer); 13 | } 14 | return cachegetUint8Memory0; 15 | } 16 | 17 | function getStringFromWasm0(ptr, len) { 18 | return cachedTextDecoder.decode(getUint8Memory0().slice(ptr, ptr + len)); 19 | } 20 | 21 | const heap = new Array(32).fill(undefined); 22 | 23 | heap.push(undefined, null, true, false); 24 | 25 | let heap_next = heap.length; 26 | 27 | function addHeapObject(obj) { 28 | if (heap_next === heap.length) heap.push(heap.length + 1); 29 | const idx = heap_next; 30 | heap_next = heap[idx]; 31 | 32 | heap[idx] = obj; 33 | return idx; 34 | } 35 | 36 | let WASM_VECTOR_LEN = 0; 37 | 38 | function passArray8ToWasm0(arg, malloc) { 39 | const ptr = malloc(arg.length * 1); 40 | getUint8Memory0().set(arg, ptr / 1); 41 | WASM_VECTOR_LEN = arg.length; 42 | return ptr; 43 | } 44 | 45 | let cachegetInt32Memory0 = null; 46 | function getInt32Memory0() { 47 | if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.__wbindgen_export_0.buffer) { 48 | cachegetInt32Memory0 = new Int32Array(wasm.__wbindgen_export_0.buffer); 49 | } 50 | return cachegetInt32Memory0; 51 | } 52 | 53 | function getArrayU8FromWasm0(ptr, len) { 54 | return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); 55 | } 56 | /** 57 | * @param {Uint8Array} data 58 | * @param {number} level 59 | * @param {boolean} interlace 60 | * @returns {Uint8Array} 61 | */ 62 | export function optimise(data, level, interlace) { 63 | try { 64 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 65 | var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); 66 | var len0 = WASM_VECTOR_LEN; 67 | wasm.optimise(retptr, ptr0, len0, level, interlace); 68 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 69 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 70 | var v1 = getArrayU8FromWasm0(r0, r1).slice(); 71 | wasm.__wbindgen_free(r0, r1 * 1); 72 | return v1; 73 | } finally { 74 | wasm.__wbindgen_add_to_stack_pointer(16); 75 | } 76 | } 77 | 78 | function getObject(idx) { return heap[idx]; } 79 | 80 | function dropObject(idx) { 81 | if (idx < 36) return; 82 | heap[idx] = heap_next; 83 | heap_next = idx; 84 | } 85 | 86 | function takeObject(idx) { 87 | const ret = getObject(idx); 88 | dropObject(idx); 89 | return ret; 90 | } 91 | /** 92 | * @param {number} num_threads 93 | * @returns {Promise} 94 | */ 95 | export function initThreadPool(num_threads) { 96 | var ret = wasm.initThreadPool(num_threads); 97 | return takeObject(ret); 98 | } 99 | 100 | /** 101 | * @param {number} receiver 102 | */ 103 | export function wbg_rayon_start_worker(receiver) { 104 | wasm.wbg_rayon_start_worker(receiver); 105 | } 106 | 107 | /** 108 | */ 109 | export class wbg_rayon_PoolBuilder { 110 | 111 | static __wrap(ptr) { 112 | const obj = Object.create(wbg_rayon_PoolBuilder.prototype); 113 | obj.ptr = ptr; 114 | 115 | return obj; 116 | } 117 | 118 | __destroy_into_raw() { 119 | const ptr = this.ptr; 120 | this.ptr = 0; 121 | 122 | return ptr; 123 | } 124 | 125 | free() { 126 | const ptr = this.__destroy_into_raw(); 127 | wasm.__wbg_wbg_rayon_poolbuilder_free(ptr); 128 | } 129 | /** 130 | * @returns {number} 131 | */ 132 | numThreads() { 133 | var ret = wasm.wbg_rayon_poolbuilder_numThreads(this.ptr); 134 | return ret >>> 0; 135 | } 136 | /** 137 | * @returns {number} 138 | */ 139 | receiver() { 140 | var ret = wasm.wbg_rayon_poolbuilder_receiver(this.ptr); 141 | return ret; 142 | } 143 | /** 144 | */ 145 | build() { 146 | wasm.wbg_rayon_poolbuilder_build(this.ptr); 147 | } 148 | } 149 | 150 | async function load(module, imports) { 151 | if (typeof Response === 'function' && module instanceof Response) { 152 | if (typeof WebAssembly.instantiateStreaming === 'function') { 153 | try { 154 | return await WebAssembly.instantiateStreaming(module, imports); 155 | 156 | } catch (e) { 157 | if (module.headers.get('Content-Type') != 'application/wasm') { 158 | 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); 159 | 160 | } else { 161 | throw e; 162 | } 163 | } 164 | } 165 | 166 | const bytes = await module.arrayBuffer(); 167 | return await WebAssembly.instantiate(bytes, imports); 168 | 169 | } else { 170 | const instance = await WebAssembly.instantiate(module, imports); 171 | 172 | if (instance instanceof WebAssembly.Instance) { 173 | return { instance, module }; 174 | 175 | } else { 176 | return instance; 177 | } 178 | } 179 | } 180 | 181 | async function init(input, maybe_memory) { 182 | if (typeof input === 'undefined') { 183 | input = new URL('squoosh_oxipng_bg.wasm', import.meta.url); 184 | } 185 | const imports = {}; 186 | imports.wbg = {}; 187 | imports.wbg.__wbindgen_throw = function(arg0, arg1) { 188 | throw new Error(getStringFromWasm0(arg0, arg1)); 189 | }; 190 | imports.wbg.__wbindgen_module = function() { 191 | var ret = init.__wbindgen_wasm_module; 192 | return addHeapObject(ret); 193 | }; 194 | imports.wbg.__wbindgen_memory = function() { 195 | var ret = wasm.__wbindgen_export_0; 196 | return addHeapObject(ret); 197 | }; 198 | imports.wbg.__wbg_startWorkers_914655bb4d5bb5e1 = function(arg0, arg1, arg2) { 199 | var ret = startWorkers(takeObject(arg0), takeObject(arg1), wbg_rayon_PoolBuilder.__wrap(arg2)); 200 | return addHeapObject(ret); 201 | }; 202 | 203 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 204 | input = fetch(input); 205 | } 206 | 207 | imports.wbg.memory = maybe_memory || new WebAssembly.Memory({initial:17,maximum:16384,shared:true}); 208 | 209 | const { instance, module } = await load(await input, imports); 210 | 211 | wasm = instance.exports; 212 | init.__wbindgen_wasm_module = module; 213 | wasm.__wbindgen_start(); 214 | return wasm; 215 | } 216 | 217 | export default init; 218 | 219 | -------------------------------------------------------------------------------- /src/client/lazy-app/Compress/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | blobToImg, 3 | blobToText, 4 | builtinDecode, 5 | sniffMimeType, 6 | canDecodeImageType, 7 | ImageMimeTypes, 8 | } from '../util'; 9 | import { 10 | PreprocessorState, 11 | ProcessorState, 12 | EncoderState, 13 | encoderMap, 14 | defaultPreprocessorState, 15 | defaultProcessorState, 16 | } from '../feature-meta'; 17 | import { drawableToImageData } from '../util/canvas'; 18 | 19 | import avifDecode from '../../../features/decoders/avif/worker/avifDecode'; 20 | import jxlDecode from '../../../features/decoders/jxl/worker/jxlDecode'; 21 | import webpDecode from '../../../features/decoders/webp/worker/webpDecode'; 22 | import wp2Decode from '../../../features/decoders/wp2/worker/wp2Decode'; 23 | import { browserGIFEncode } from '../../../features/encoders/browserGIF/client'; 24 | import { avifEncode } from '../../../features/encoders/avif/client'; 25 | import { browserJPEGEncode } from '../../../features/encoders/browserJPEG/client'; 26 | import { browserPNGEncode } from '../../../features/encoders/browserPNG/client'; 27 | import { jxlEncode } from '../../../features/encoders/jxl/client'; 28 | import { mozJPEGEncode } from '../../../features/encoders/mozJPEG/client'; 29 | import { oxiPNGEncode } from '../../../features/encoders/oxiPNG/client'; 30 | import { webPEncode } from '../../../features/encoders/webP/client'; 31 | import { wp2Encode } from '../../../features/encoders/wp2/client'; 32 | import { resizeImage } from '../../../features/processors/resize/client'; 33 | import quantize from '../../../features/processors/quantize/worker/quantize'; 34 | 35 | export interface SourceImage { 36 | file: File; 37 | decoded: ImageData; 38 | preprocessed: ImageData; 39 | vectorImage?: HTMLImageElement; 40 | } 41 | 42 | interface Setting { 43 | preprocessorState: PreprocessorState; 44 | processorState: ProcessorState; 45 | encoderState: EncoderState; 46 | } 47 | 48 | // 解码 49 | async function decodeImage( 50 | blob: Blob, 51 | ): Promise { 52 | const mimeType = await sniffMimeType(blob); 53 | const canDecode = await canDecodeImageType(mimeType); 54 | if (!canDecode) { 55 | // 用外置解码 56 | switch (mimeType) { 57 | case 'image/avif': 58 | return await avifDecode(blob); 59 | case 'image/webp': 60 | return await webpDecode(blob); 61 | case 'image/jxl': 62 | return await jxlDecode(blob); 63 | case 'image/webp2': 64 | return await wp2Decode(blob); 65 | default: 66 | break; 67 | } 68 | } 69 | // Otherwise fall through and try built-in decoding for a laugh. 70 | // 用内置解码 71 | return await builtinDecode(blob, mimeType); 72 | } 73 | 74 | // 预处理 75 | async function preprocessImage( 76 | data: ImageData, 77 | preprocessorState: PreprocessorState, 78 | ): Promise { 79 | let processedData = data; 80 | 81 | if (preprocessorState.rotate.rotate !== 0) { 82 | 83 | } 84 | 85 | return processedData; 86 | } 87 | 88 | // 加工 89 | async function processImage( 90 | source: SourceImage, 91 | processorState: ProcessorState, 92 | ): Promise { 93 | let result = source.preprocessed; 94 | if (processorState.resize.enabled) { 95 | result = await resizeImage(source, processorState.resize); 96 | } 97 | if (processorState.quantize.enabled) { 98 | result = await quantize( 99 | result, 100 | processorState.quantize, 101 | ); 102 | } 103 | return result; 104 | } 105 | 106 | // 编码 107 | async function compressImage( 108 | image: ImageData, 109 | encodeData: EncoderState, 110 | sourceFilename: string, 111 | ): Promise { 112 | const encoder = encoderMap[encodeData.type]; 113 | let compressedData: Blob | ArrayBuffer; 114 | switch (encodeData.type) { 115 | case "avif": 116 | compressedData = await avifEncode(image, encodeData.options) 117 | break; 118 | case "browserGIF": 119 | compressedData = await browserGIFEncode(image, encodeData.options) 120 | break; 121 | case "browserJPEG": 122 | compressedData = await browserJPEGEncode(image, encodeData.options) 123 | break; 124 | case "browserPNG": 125 | compressedData = await browserPNGEncode(image, encodeData.options) 126 | break; 127 | case "jxl": 128 | compressedData = await jxlEncode(image, encodeData.options) 129 | break; 130 | case "mozJPEG": 131 | compressedData = await mozJPEGEncode(image, encodeData.options) 132 | break; 133 | case "oxiPNG": 134 | compressedData = await oxiPNGEncode(image, encodeData.options) 135 | break; 136 | case "webP": 137 | compressedData = await webPEncode(image, encodeData.options) 138 | break; 139 | case "wp2": 140 | compressedData = await wp2Encode(image, encodeData.options) 141 | break; 142 | default: 143 | compressedData = new Blob(); 144 | break; 145 | } 146 | 147 | // This type ensures the image mimetype is consistent with our mimetype sniffer 148 | const type: ImageMimeTypes = encoder.meta.mimeType; 149 | 150 | return new File( 151 | [compressedData], 152 | sourceFilename.replace(/.[^.]*$/, `.${encoder.meta.extension}`), 153 | { type }, 154 | ); 155 | } 156 | 157 | // 特殊处理SVG 158 | async function processSvg( 159 | blob: Blob, 160 | ): Promise { 161 | // Firefox throws if you try to draw an SVG to canvas that doesn't have width/height. 162 | // In Chrome it loads, but drawImage behaves weirdly. 163 | // This function sets width/height if it isn't already set. 164 | const parser = new DOMParser(); 165 | const text = await blobToText(blob); 166 | const document = parser.parseFromString(text, 'image/svg+xml'); 167 | const svg = document.documentElement!; 168 | 169 | if (svg.hasAttribute('width') && svg.hasAttribute('height')) { 170 | return blobToImg(blob); 171 | } 172 | 173 | const viewBox = svg.getAttribute('viewBox'); 174 | if (viewBox === null) throw Error('SVG must have width/height or viewBox'); 175 | 176 | const viewboxParts = viewBox.split(/\s+/); 177 | svg.setAttribute('width', viewboxParts[2]); 178 | svg.setAttribute('height', viewboxParts[3]); 179 | 180 | const serializer = new XMLSerializer(); 181 | const newSource = serializer.serializeToString(document); 182 | return blobToImg(new Blob([newSource], { type: 'image/svg+xml' })); 183 | } 184 | 185 | 186 | export default class Compress { 187 | 188 | // 需要处理的文件 189 | private file: File; 190 | // 编码设置 191 | private setting: Setting = { 192 | "encoderState": { 193 | type: 'webP', 194 | options: encoderMap.webP.meta.defaultOptions, 195 | }, "processorState": defaultProcessorState, "preprocessorState": defaultPreprocessorState 196 | }; 197 | 198 | constructor(file: File, setting?: Setting) { 199 | this.file = file; 200 | if (setting) { 201 | this.setting = setting; 202 | } 203 | } 204 | 205 | // 处理 206 | async process(): Promise { 207 | let decoded: ImageData; 208 | let vectorImage: HTMLImageElement | undefined; 209 | 210 | // 解码 211 | if (this.file.type.startsWith('image/svg+xml')) { 212 | vectorImage = await processSvg(this.file); 213 | decoded = drawableToImageData(vectorImage); 214 | } else { 215 | decoded = await decodeImage(this.file); 216 | } 217 | 218 | // 预处理 219 | const preprocessed = await preprocessImage(decoded, this.setting.preprocessorState); 220 | 221 | // 加工 222 | const source: SourceImage = { "file": this.file, decoded, vectorImage, preprocessed }; 223 | const processed = await processImage(source, this.setting.processorState); 224 | 225 | // 编码 226 | const file = await compressImage( 227 | processed, 228 | this.setting.encoderState, 229 | source.file.name, 230 | ); 231 | 232 | return file; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /codecs/mozjpeg/enc/mozjpeg_enc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "config.h" 9 | #include "jpeglib.h" 10 | 11 | extern "C" { 12 | #include "cdjpeg.h" 13 | } 14 | 15 | using namespace emscripten; 16 | 17 | // MozJPEG doesn’t expose a numeric version, so I have to do some fun C macro 18 | // hackery to turn it into a string. More details here: 19 | // https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html 20 | #define xstr(s) str(s) 21 | #define str(s) #s 22 | 23 | struct MozJpegOptions { 24 | int quality; 25 | bool baseline; 26 | bool arithmetic; 27 | bool progressive; 28 | bool optimize_coding; 29 | int smoothing; 30 | int color_space; 31 | int quant_table; 32 | bool trellis_multipass; 33 | bool trellis_opt_zero; 34 | bool trellis_opt_table; 35 | int trellis_loops; 36 | bool auto_subsample; 37 | int chroma_subsample; 38 | bool separate_chroma_quality; 39 | int chroma_quality; 40 | }; 41 | 42 | int version() { 43 | char buffer[] = xstr(MOZJPEG_VERSION); 44 | int version = 0; 45 | int last_index = 0; 46 | for (int i = 0; i < strlen(buffer); i++) { 47 | if (buffer[i] == '.') { 48 | buffer[i] = '\0'; 49 | version = version << 8 | atoi(&buffer[last_index]); 50 | buffer[i] = '.'; 51 | last_index = i + 1; 52 | } 53 | } 54 | version = version << 8 | atoi(&buffer[last_index]); 55 | return version; 56 | } 57 | 58 | thread_local const val Uint8Array = val::global("Uint8Array"); 59 | 60 | val encode(std::string image_in, int image_width, int image_height, MozJpegOptions opts) { 61 | uint8_t* image_buffer = (uint8_t*)image_in.c_str(); 62 | 63 | // The code below is basically the `write_JPEG_file` function from 64 | // https://github.com/mozilla/mozjpeg/blob/master/example.c 65 | // I just write to memory instead of a file. 66 | 67 | /* Step 1: allocate and initialize JPEG compression object */ 68 | 69 | /* This struct contains the JPEG compression parameters and pointers to 70 | * working space (which is allocated as needed by the JPEG library). 71 | * It is possible to have several such structures, representing multiple 72 | * compression/decompression processes, in existence at once. We refer 73 | * to any one struct (and its associated working data) as a "JPEG object". 74 | */ 75 | jpeg_compress_struct cinfo; 76 | /* This struct represents a JPEG error handler. It is declared separately 77 | * because applications often want to supply a specialized error handler 78 | * (see the second half of this file for an example). But here we just 79 | * take the easy way out and use the standard error handler, which will 80 | * print a message on stderr and call exit() if compression fails. 81 | * Note that this struct must live as long as the main JPEG parameter 82 | * struct, to avoid dangling-pointer problems. 83 | */ 84 | jpeg_error_mgr jerr; 85 | /* We have to set up the error handler first, in case the initialization 86 | * step fails. (Unlikely, but it could happen if you are out of memory.) 87 | * This routine fills in the contents of struct jerr, and returns jerr's 88 | * address which we place into the link field in cinfo. 89 | */ 90 | cinfo.err = jpeg_std_error(&jerr); 91 | /* Now we can initialize the JPEG compression object. */ 92 | jpeg_create_compress(&cinfo); 93 | 94 | /* Step 2: specify data destination (eg, a file) */ 95 | /* Note: steps 2 and 3 can be done in either order. */ 96 | 97 | /* Here we use the library-supplied code to send compressed data to a 98 | * stdio stream. You can also write your own code to do something else. 99 | * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that 100 | * requires it in order to write binary files. 101 | */ 102 | // if ((outfile = fopen(filename, "wb")) == NULL) { 103 | // fprintf(stderr, "can't open %s\n", filename); 104 | // exit(1); 105 | // } 106 | uint8_t* output = nullptr; 107 | unsigned long size = 0; 108 | jpeg_mem_dest(&cinfo, &output, &size); 109 | 110 | /* Step 3: set parameters for compression */ 111 | 112 | /* First we supply a description of the input image. 113 | * Four fields of the cinfo struct must be filled in: 114 | */ 115 | cinfo.image_width = image_width; /* image width and height, in pixels */ 116 | cinfo.image_height = image_height; 117 | cinfo.input_components = 4; /* # of color components per pixel */ 118 | cinfo.in_color_space = JCS_EXT_RGBA; /* colorspace of input image */ 119 | /* Now use the library's routine to set default compression parameters. 120 | * (You must set at least cinfo.in_color_space before calling this, 121 | * since the defaults depend on the source color space.) 122 | */ 123 | jpeg_set_defaults(&cinfo); 124 | 125 | jpeg_set_colorspace(&cinfo, (J_COLOR_SPACE)opts.color_space); 126 | 127 | if (opts.quant_table != -1) { 128 | jpeg_c_set_int_param(&cinfo, JINT_BASE_QUANT_TBL_IDX, opts.quant_table); 129 | } 130 | 131 | cinfo.optimize_coding = opts.optimize_coding; 132 | 133 | if (opts.arithmetic) { 134 | cinfo.arith_code = TRUE; 135 | cinfo.optimize_coding = FALSE; 136 | } 137 | 138 | cinfo.smoothing_factor = opts.smoothing; 139 | 140 | jpeg_c_set_bool_param(&cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, opts.trellis_multipass); 141 | jpeg_c_set_bool_param(&cinfo, JBOOLEAN_TRELLIS_EOB_OPT, opts.trellis_opt_zero); 142 | jpeg_c_set_bool_param(&cinfo, JBOOLEAN_TRELLIS_Q_OPT, opts.trellis_opt_table); 143 | jpeg_c_set_int_param(&cinfo, JINT_TRELLIS_NUM_LOOPS, opts.trellis_loops); 144 | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 0); 145 | 146 | // A little hacky to build a string for this, but it means we can use 147 | // set_quality_ratings which does some useful heuristic stuff. 148 | std::string quality_str = std::to_string(opts.quality); 149 | 150 | if (opts.separate_chroma_quality && opts.color_space == JCS_YCbCr) { 151 | quality_str += "," + std::to_string(opts.chroma_quality); 152 | } 153 | 154 | char const* pqual = quality_str.c_str(); 155 | 156 | set_quality_ratings(&cinfo, (char*)pqual, opts.baseline); 157 | 158 | if (!opts.auto_subsample && opts.color_space == JCS_YCbCr) { 159 | cinfo.comp_info[0].h_samp_factor = opts.chroma_subsample; 160 | cinfo.comp_info[0].v_samp_factor = opts.chroma_subsample; 161 | } 162 | 163 | if (!opts.baseline && opts.progressive) { 164 | jpeg_simple_progression(&cinfo); 165 | } else { 166 | cinfo.num_scans = 0; 167 | cinfo.scan_info = NULL; 168 | } 169 | /* Step 4: Start compressor */ 170 | 171 | /* TRUE ensures that we will write a complete interchange-JPEG file. 172 | * Pass TRUE unless you are very sure of what you're doing. 173 | */ 174 | jpeg_start_compress(&cinfo, TRUE); 175 | 176 | /* Step 5: while (scan lines remain to be written) */ 177 | /* jpeg_write_scanlines(...); */ 178 | 179 | /* Here we use the library's state variable cinfo.next_scanline as the 180 | * loop counter, so that we don't have to keep track ourselves. 181 | * To keep things simple, we pass one scanline per call; you can pass 182 | * more if you wish, though. 183 | */ 184 | int row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */ 185 | 186 | while (cinfo.next_scanline < cinfo.image_height) { 187 | /* jpeg_write_scanlines expects an array of pointers to scanlines. 188 | * Here the array is only one element long, but you could pass 189 | * more than one scanline at a time if that's more convenient. 190 | */ 191 | 192 | JSAMPROW row_pointer = 193 | &image_buffer[cinfo.next_scanline * row_stride]; /* pointer to JSAMPLE row[s] */ 194 | (void)jpeg_write_scanlines(&cinfo, &row_pointer, 1); 195 | } 196 | 197 | /* Step 6: Finish compression */ 198 | 199 | jpeg_finish_compress(&cinfo); 200 | 201 | /* Step 7: release JPEG compression object */ 202 | 203 | auto js_result = Uint8Array.new_(typed_memory_view(size, output)); 204 | 205 | /* This is an important step since it will release a good deal of memory. */ 206 | jpeg_destroy_compress(&cinfo); 207 | free(output); 208 | 209 | /* And we're done! */ 210 | return js_result; 211 | } 212 | 213 | EMSCRIPTEN_BINDINGS(my_module) { 214 | value_object("MozJpegOptions") 215 | .field("quality", &MozJpegOptions::quality) 216 | .field("baseline", &MozJpegOptions::baseline) 217 | .field("arithmetic", &MozJpegOptions::arithmetic) 218 | .field("progressive", &MozJpegOptions::progressive) 219 | .field("optimize_coding", &MozJpegOptions::optimize_coding) 220 | .field("smoothing", &MozJpegOptions::smoothing) 221 | .field("color_space", &MozJpegOptions::color_space) 222 | .field("quant_table", &MozJpegOptions::quant_table) 223 | .field("trellis_multipass", &MozJpegOptions::trellis_multipass) 224 | .field("trellis_opt_zero", &MozJpegOptions::trellis_opt_zero) 225 | .field("trellis_opt_table", &MozJpegOptions::trellis_opt_table) 226 | .field("trellis_loops", &MozJpegOptions::trellis_loops) 227 | .field("chroma_subsample", &MozJpegOptions::chroma_subsample) 228 | .field("auto_subsample", &MozJpegOptions::auto_subsample) 229 | .field("separate_chroma_quality", &MozJpegOptions::separate_chroma_quality) 230 | .field("chroma_quality", &MozJpegOptions::chroma_quality); 231 | 232 | function("version", &version); 233 | function("encode", &encode); 234 | } 235 | --------------------------------------------------------------------------------