Merge branch 'dev' into add-meta-info

This commit is contained in:
Mike DelGaudio
2021-07-26 12:23:06 -04:00
committed by GitHub
39 changed files with 229 additions and 68 deletions

View File

@@ -1,36 +1,42 @@
# [Squoosh]!
[Squoosh] is an image compression web app that allows you to dive into the advanced options provided
by various image compressors.
[Squoosh] is an image compression web app that reduces image sizes through numerous formats.
# API & CLI
Squoosh now has [an API](https://github.com/GoogleChromeLabs/squoosh/tree/dev/libsquoosh) and [a CLI](https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli) that allows you to compress many images at once.
Squoosh has [an API](https://github.com/GoogleChromeLabs/squoosh/tree/dev/libsquoosh) and [a CLI](https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli) to compress many images at once.
# Privacy
Google Analytics is used to record the following:
Squoosh does not send your image to a server. All image compression processes locally.
- [Basic visit data](https://support.google.com/analytics/answer/6004245?ref_topic=2919631).
- Before and after image size once an image is downloaded. These values are rounded to the nearest
kilobyte.
- If install is available, when Squoosh is installed, and what method was used to install Squoosh.
However, Squoosh utilizes Google Analytics to collect the following:
Image compression is handled locally; no additional data is sent to the server.
- [Basic visitor data](https://support.google.com/analytics/answer/6004245?ref_topic=2919631).
- The before and after image size value.
- If Squoosh PWA, the type of Squoosh installation.
- If Squoosh PWA, the installation time and date.
# Building locally
# Developing
Clone the repo, and:
To develop for Squoosh:
```sh
npm install
npm run build
```
1. Clone the repository
1. To install node packages, run:
```sh
npm install
```
1. Then build the app by running:
```sh
npm run build
```
1. After building, start the development server by running:
```sh
npm run dev
```
You can run the development server with:
# Contributing
```sh
npm run dev
```
Squoosh is an open-source project that appreciates all community involvement. To contribute to the project, follow the [contribute guide](/CONTRIBUTING.md).
[squoosh]: https://squoosh.app

33
cli/package-lock.json generated
View File

@@ -1,15 +1,15 @@
{
"name": "@squoosh/cli",
"version": "0.7.1",
"version": "0.7.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@squoosh/cli",
"version": "0.7.1",
"version": "0.7.2",
"license": "Apache-2.0",
"dependencies": {
"@squoosh/lib": "^0.3.1",
"@squoosh/lib": "^0.4.0",
"commander": "^7.2.0",
"json5": "^2.2.0",
"kleur": "^4.1.4",
@@ -24,11 +24,15 @@
}
},
"node_modules/@squoosh/lib": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.3.1.tgz",
"integrity": "sha512-ni8gyTGgW9lH/yqaqgkxerGT7daSWNU9mtvWqj+/1qqzIT9YGfFvnTtDQRMIRjRCKQ5pkK/COufvWKuzHI6Dxw==",
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.4.0.tgz",
"integrity": "sha512-O1LyugWLZjMI4JZeZMA5vzfhfPjfMZXH5/HmVkRagP8B70wH3uoR7tjxfGNdSavey357MwL8YJDxbGwBBdHp7Q==",
"dependencies": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": " ^12.5.0 || ^14.0.0 || ^16.0.0 "
}
},
"node_modules/ansi-regex": {
@@ -326,6 +330,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
@@ -345,10 +354,11 @@
},
"dependencies": {
"@squoosh/lib": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.3.1.tgz",
"integrity": "sha512-ni8gyTGgW9lH/yqaqgkxerGT7daSWNU9mtvWqj+/1qqzIT9YGfFvnTtDQRMIRjRCKQ5pkK/COufvWKuzHI6Dxw==",
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.4.0.tgz",
"integrity": "sha512-O1LyugWLZjMI4JZeZMA5vzfhfPjfMZXH5/HmVkRagP8B70wH3uoR7tjxfGNdSavey357MwL8YJDxbGwBBdHp7Q==",
"requires": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3"
}
},
@@ -581,6 +591,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@squoosh/cli",
"version": "0.7.1",
"version": "0.7.2",
"description": "A CLI for Squoosh",
"public": true,
"type": "module",
@@ -23,7 +23,7 @@
"node": " ^12.20.2 || ^14.13.1 || ^16.0.0 "
},
"dependencies": {
"@squoosh/lib": "^0.3.1",
"@squoosh/lib": "^0.4.0",
"commander": "^7.2.0",
"json5": "^2.2.0",
"kleur": "^4.1.4",

View File

@@ -16,10 +16,10 @@ export
OUT_ENC_JS = enc/avif_enc.js
OUT_NODE_ENC_JS = enc/avif_node_enc.js
OUT_ENC_MT_JS = enc/avif_enc_mt.js
OUT_NODE_ENC_MT_JS = enc/avif_node_enc_mt.js
OUT_DEC_JS = dec/avif_dec.js
OUT_NODE_DEC_JS = dec/avif_node_dec.js
OUT_ENC_CPP = enc/avif_enc.cpp
OUT_ENC_CPP = enc/avif_enc.cpp
OUT_DEC_CPP = dec/avif_dec.cpp
ENVIRONMENT = worker
@@ -28,9 +28,9 @@ HELPER_MAKEFLAGS := -f helper.Makefile
.PHONY: all clean
all: $(OUT_ENC_JS) $(OUT_DEC_JS) $(OUT_ENC_MT_JS) $(OUT_NODE_ENC_JS) $(OUT_NODE_DEC_JS)
all: $(OUT_ENC_JS) $(OUT_DEC_JS) $(OUT_ENC_MT_JS) $(OUT_NODE_ENC_JS) $(OUT_NODE_ENC_MT_JS) $(OUT_NODE_DEC_JS)
$(OUT_NODE_ENC_JS): ENVIRONMENT=node
$(OUT_NODE_ENC_JS) $(OUT_NODE_ENC_MT_JS): ENVIRONMENT=node
$(OUT_NODE_ENC_JS) $(OUT_ENC_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt
$(MAKE) \
$(HELPER_MAKEFLAGS) \
@@ -44,7 +44,7 @@ $(OUT_NODE_ENC_JS) $(OUT_ENC_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(L
ENVIRONMENT=$(ENVIRONMENT) \
LIBAVIF_FLAGS="-DAVIF_CODEC_AOM_DECODE=0"
$(OUT_ENC_MT_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt
$(OUT_ENC_MT_JS) $(OUT_NODE_ENC_MT_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt
$(MAKE) \
$(HELPER_MAKEFLAGS) \
OUT_JS=$@ \
@@ -89,4 +89,7 @@ $(LIBAOM_DIR)/CMakeLists.txt: $(LIBAOM_PACKAGE)
clean:
$(MAKE) $(HELPER_MAKEFLAGS) OUT_JS=$(OUT_ENC_JS) clean
$(MAKE) $(HELPER_MAKEFLAGS) OUT_JS=$(OUT_ENC_MT_JS) clean
$(MAKE) $(HELPER_MAKEFLAGS) OUT_JS=$(OUT_ENC_NODE_JS) clean
$(MAKE) $(HELPER_MAKEFLAGS) OUT_JS=$(OUT_ENC_NODE_MT_JS) clean
$(MAKE) $(HELPER_MAKEFLAGS) OUT_JS=$(OUT_DEC_JS) clean
$(MAKE) $(HELPER_MAKEFLAGS) OUT_JS=$(OUT_DEV_NODE_JS) clean

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

16
codecs/avif/enc/avif_node_enc_mt.js generated Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +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}};

View File

@@ -1,5 +1,5 @@
CODEC_URL = https://gitlab.com/wg1/jpeg-xl.git
CODEC_VERSION = ab7c5e9b6795134377aa4846ceaae2c5bc504f76
CODEC_URL = https://github.com/libjxl/libjxl.git
CODEC_VERSION = 3f76321a7cbcf4f452894dc173eb7be60f508ecb
CODEC_DIR = node_modules/jxl
CODEC_BUILD_ROOT := $(CODEC_DIR)/build
CODEC_MT_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt

Binary file not shown.

Binary file not shown.

View File

@@ -35,13 +35,15 @@ val encode(std::string image, int width, int height, JXLOptions options) {
cparams.epf = options.epf;
cparams.speed_tier = static_cast<jxl::SpeedTier>(options.speed);
cparams.near_lossless = options.nearLossless;
cparams.decoding_speed_tier = options.decodingSpeedTier;
if (options.lossyPalette) {
if (options.lossyPalette || options.nearLossless) {
cparams.lossy_palette = true;
cparams.palette_colors = 0;
cparams.options.predictor = jxl::Predictor::Zero;
// Near-lossless assumes -R 0
cparams.responsive = 0;
cparams.modular_mode = true;
}
float quality = options.quality;
@@ -77,12 +79,6 @@ val encode(std::string image, int width, int height, JXLOptions options) {
}
}
if (cparams.near_lossless) {
// Near-lossless assumes -R 0
cparams.responsive = 0;
cparams.modular_mode = true;
}
io.metadata.m.SetAlphaBits(8);
if (!io.metadata.size.Set(width, height)) {
return val::null();

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -53,7 +53,11 @@ where
#[wasm_bindgen]
pub fn decode(mut data: &[u8]) -> ImageData {
let mut decoder = png::Decoder::new(&mut data);
decoder.set_transformations(png::Transformations::EXPAND);
decoder.set_transformations(
png::Transformations::EXPAND | // Turn paletted images into RGB
png::Transformations::PACKING | // Turn images <8bit to 8bit
png::Transformations::STRIP_16, // Turn 16bit into 8 bit
);
let (info, mut reader) = decoder.read_info().unwrap_throw();
let num_pixels = (info.width * info.height) as usize;
let mut buf = vec![0; num_pixels * 4];

14
codecs/visdif/BUILD.md Normal file
View File

@@ -0,0 +1,14 @@
This codec currently needs monkey-patching of Emscripten
```
$ docker run --rm -it -v $(PWD):/src squoosh-cpp "/bin/bash"
# cat << EOF | patch /emsdk/upstream/emscripten/system/lib/dlmalloc.c
659c659
< #define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
---
> #define MALLOC_ALIGNMENT ((size_t)(16U))
EOF
# emcc --clear-cache
# /emsdk/upstream/emscripten/embuilder build libdlmalloc --force
# emmake make
```

View File

@@ -5,12 +5,19 @@
using namespace emscripten;
using namespace butteraugli;
#define GAMMA 2.2
static float SrgbToLinear[256];
inline void gammaLookupTable() {
SrgbToLinear[0] = 0;
for (int i = 1; i < 256; ++i) {
SrgbToLinear[i] = static_cast<float>(255.0 * pow(i / 255.0, GAMMA));
}
}
// Turns an interleaved RGBA buffer into 4 planes for each color channel
void planarize(std::vector<ImageF>& img,
const uint8_t* rgba,
int width,
int height,
float gamma = 2.2) {
void planarize(std::vector<ImageF>& img, const uint8_t* rgba, int width, int height) {
assert(img.size() == 0);
img.push_back(ImageF(width, height));
img.push_back(ImageF(width, height));
@@ -22,10 +29,10 @@ void planarize(std::vector<ImageF>& img,
float* const row_b = img[2].Row(y);
float* const row_a = img[3].Row(y);
for (int x = 0; x < width; x++) {
row_r[x] = 255.0 * pow(rgba[(y * width + x) * 4 + 0] / 255.0, gamma);
row_g[x] = 255.0 * pow(rgba[(y * width + x) * 4 + 1] / 255.0, gamma);
row_b[x] = 255.0 * pow(rgba[(y * width + x) * 4 + 2] / 255.0, gamma);
row_a[x] = 255.0 * pow(rgba[(y * width + x) * 4 + 3] / 255.0, gamma);
row_r[x] = SrgbToLinear[rgba[(y * width + x) * 4 + 0]];
row_g[x] = SrgbToLinear[rgba[(y * width + x) * 4 + 1]];
row_b[x] = SrgbToLinear[rgba[(y * width + x) * 4 + 2]];
row_a[x] = SrgbToLinear[rgba[(y * width + x) * 4 + 3]];
}
}
}
@@ -37,6 +44,7 @@ class VisDiff {
public:
VisDiff(std::string ref_img, int width, int height) {
gammaLookupTable();
planarize(this->ref_img, (uint8_t*)ref_img.c_str(), width, height);
this->width = width;
this->height = height;

Binary file not shown.

View File

@@ -30,7 +30,7 @@ const imagePath = 'path/to/image.png';
const image = imagePool.ingestImage(imagePath);
```
The `ingestImage` function can take anything the node [`readFile`][readfile] function can take, uncluding a buffer and `FileHandle`.
The `ingestImage` function can take anything the node [`readFile`][readfile] function can take, including a buffer and `FileHandle`.
The returned `image` object is a representation of the original image, that you can now preprocess, encode, and extract information about.

View File

@@ -0,0 +1,49 @@
/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { promises as fs } from 'fs';
import { basename } from 'path';
const defaultOpts = {
prefix: 'chunk-url',
};
export default function chunkPlugin(opts) {
opts = { ...defaultOpts, ...opts };
const prefix = opts.prefix + ':';
return {
name: 'chunk-plugin',
async resolveId(id, importer) {
if (!id.startsWith(prefix)) return;
const realId = id.slice(prefix.length);
const resolveResult = await this.resolve(realId, importer);
if (!resolveResult) {
throw Error(`Cannot find ${realId}`);
}
return prefix + resolveResult.id;
},
async load(id) {
if (!id.startsWith(prefix)) return;
const realId = id.slice(prefix.length);
const source = await fs.readFile(realId);
this.addWatchFile(realId);
return `export default import.meta.ROLLUP_FILE_URL_${this.emitFile({
type: 'chunk',
source,
id: realId,
})}`;
},
};
}

View File

@@ -1,14 +1,15 @@
{
"name": "@squoosh/lib",
"version": "0.3.1",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@squoosh/lib",
"version": "0.3.1",
"version": "0.4.0",
"license": "Apache-2.0",
"dependencies": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3"
},
"devDependencies": {
@@ -22,6 +23,9 @@
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.1.3",
"which": "^2.0.2"
},
"engines": {
"node": " ^12.5.0 || ^14.0.0 || ^16.0.0 "
}
},
"node_modules/@babel/code-frame": {
@@ -2055,6 +2059,11 @@
"node": ">=4"
}
},
"node_modules/wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"node_modules/web-streams-polyfill": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz",
@@ -3948,6 +3957,11 @@
"integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==",
"dev": true
},
"wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"web-streams-polyfill": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@squoosh/lib",
"version": "0.3.1",
"version": "0.4.0",
"description": "A Node library for Squoosh",
"public": true,
"main": "./build/index.js",
@@ -22,6 +22,7 @@
"node": " ^12.5.0 || ^14.0.0 || ^16.0.0 "
},
"dependencies": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3"
},
"devDependencies": {

View File

@@ -2,6 +2,7 @@ import resolve from '@rollup/plugin-node-resolve';
import cjs from '@rollup/plugin-commonjs';
import simpleTS from './lib/simple-ts';
import asset from './lib/asset-plugin.js';
import chunk from './lib/chunk-plugin.js';
import json from './lib/json-plugin.js';
import autojson from './lib/autojson-plugin.js';
import { getBabelOutputPlugin } from '@rollup/plugin-babel';
@@ -18,6 +19,7 @@ export default {
plugins: [
resolve(),
cjs(),
chunk(),
asset(),
autojson(),
json(),

View File

@@ -1,5 +1,13 @@
import { promises as fsp } from 'fs';
import { instantiateEmscriptenWasm, pathify } from './emscripten-utils.js';
import { threads } from 'wasm-feature-detect';
import { cpus } from 'os';
// We use `navigator.hardwareConcurrency` for Emscriptens pthread pool size.
// This is the only workaround I can get working without crying.
(globalThis as any).navigator = {
hardwareConcurrency: cpus().length,
};
interface RotateModuleInstance {
exports: {
@@ -47,6 +55,9 @@ import webpDecWasm from 'asset-url:../../codecs/webp/dec/webp_node_dec.wasm';
// AVIF
import avifEnc from '../../codecs/avif/enc/avif_node_enc.js';
import avifEncWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc.wasm';
import avifEncMt from '../../codecs/avif/enc/avif_node_enc_mt.js';
import avifEncMtWorker from 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.worker.js';
import avifEncMtWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc_mt.wasm';
import avifDec from '../../codecs/avif/dec/avif_node_dec.js';
import avifDecWasm from 'asset-url:../../codecs/avif/dec/avif_node_dec.wasm';
@@ -325,7 +336,16 @@ export const codecs = {
extension: 'avif',
detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/],
dec: () => instantiateEmscriptenWasm(avifDec, avifDecWasm),
enc: () => instantiateEmscriptenWasm(avifEnc, avifEncWasm),
enc: async () => {
if (await threads()) {
return instantiateEmscriptenWasm(
avifEncMt,
avifEncMtWasm,
avifEncMtWorker,
);
}
return instantiateEmscriptenWasm(avifEnc, avifEncWasm);
},
defaultEncoderOptions: {
cqLevel: 33,
cqAlphaLevel: -1,

View File

@@ -1,4 +1,4 @@
import { fileURLToPath } from 'url';
import { fileURLToPath, URL } from 'url';
export function pathify(path: string): string {
if (path.startsWith('file://')) {
@@ -10,10 +10,17 @@ export function pathify(path: string): string {
export function instantiateEmscriptenWasm<T extends EmscriptenWasm.Module>(
factory: EmscriptenWasm.ModuleFactory<T>,
path: string,
workerJS: string = '',
): Promise<T> {
return factory({
locateFile() {
return pathify(path);
locateFile(requestPath) {
// The glue code generated by emscripten uses the original
// file names of the worker file and the wasm binary.
// These will have changed in the bundling process and
// we need to inject them here.
if (requestPath.endsWith('.wasm')) return pathify(path);
if (requestPath.endsWith('.worker.js')) return new URL(workerJS).pathname;
return requestPath;
},
});
}

View File

@@ -23,6 +23,11 @@ declare module 'asset-url:../../codecs/resize/pkg/squoosh_resize_bg.wasm' {
export default value;
}
declare module 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.worker.js' {
const value: string;
export default value;
}
// These don't exist in NodeJS types so we're not able to use them but they are referenced in some emscripten and codec types
// Thus, we need to explicitly assign them to be `never`
// We're also not able to use the APIs that use these types

View File

@@ -64,7 +64,7 @@ export default class WorkerPool {
async _nextWorker() {
const reader = this.workerQueue.readable.getReader();
const { value, done } = await reader.read();
const { value } = await reader.read();
reader.releaseLock();
return value;
}