forked from external-repos/squoosh
Compare commits
67 Commits
minimal-ru
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
945cb28744 | ||
|
|
918c596cba | ||
|
|
42594277fd | ||
|
|
97d13de273 | ||
|
|
a1c3304c56 | ||
|
|
7a2fc917c1 | ||
|
|
1c4233b5ae | ||
|
|
4d67ec5a7b | ||
|
|
c24505230d | ||
|
|
2238d1abaf | ||
|
|
65ea02627b | ||
|
|
18b53e2b8a | ||
|
|
66ec763667 | ||
|
|
029eda7b37 | ||
|
|
e6810059ef | ||
|
|
4eba015009 | ||
|
|
dd49a1f23d | ||
|
|
5ecb99992c | ||
|
|
d9c6ebe0a2 | ||
|
|
fba67a7a18 | ||
|
|
129b925098 | ||
|
|
e23bc4d2e5 | ||
|
|
f523a07f01 | ||
|
|
e3f840c6da | ||
|
|
cad09160b6 | ||
|
|
9663c23489 | ||
|
|
95a1b35c91 | ||
|
|
914cdea41d | ||
|
|
6bfce29af6 | ||
|
|
64cad1cc23 | ||
|
|
87f25d909b | ||
|
|
bdfdaf53af | ||
|
|
1ab9e8b3ab | ||
|
|
2718077d3d | ||
|
|
255dfa434a | ||
|
|
0bef05bcd4 | ||
|
|
c4bc369c6b | ||
|
|
68ce8f420d | ||
|
|
27f103fee5 | ||
|
|
c21d3714a8 | ||
|
|
3ea0d88c4f | ||
|
|
00cfdafdf3 | ||
|
|
92f52319da | ||
|
|
023304803f | ||
|
|
6b08cd2355 | ||
|
|
db1a5138e6 | ||
|
|
a547491146 | ||
|
|
6e427f9208 | ||
|
|
0d35fbd349 | ||
|
|
4e901c714c | ||
|
|
b74788e036 | ||
|
|
14c3d190e9 | ||
|
|
202d0bc088 | ||
|
|
bdb5b16372 | ||
|
|
30140c2b7a | ||
|
|
10996cdbca | ||
|
|
e5806507d4 | ||
|
|
1c5b44f9a1 | ||
|
|
eeaa19589e | ||
|
|
35b8c56f1a | ||
|
|
816d1f92fd | ||
|
|
4091f2efec | ||
|
|
821d14c6ab | ||
|
|
a72ca46531 | ||
|
|
fafcf97f0c | ||
|
|
de4eb9c8f7 | ||
|
|
16a53caa48 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,2 +1,2 @@
|
|||||||
/codecs/**/*.js linguist-generated=true
|
/codecs/**/*.js linguist-generated -diff
|
||||||
/codecs/*/pkg*/*.d.ts linguist-generated=true
|
/codecs/*/pkg*/*.d.ts linguist-generated
|
||||||
|
|||||||
1
.husky/pre-commit
Executable file
1
.husky/pre-commit
Executable file
@@ -0,0 +1 @@
|
|||||||
|
npx lint-staged
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
# Project no longer maintained
|
||||||
|
|
||||||
|
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
|
||||||
|
|
||||||
# Squoosh CLI
|
# Squoosh CLI
|
||||||
|
|
||||||
Squoosh CLI is an _experimental_ way to run all the codecs you know from the [Squoosh] web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
Squoosh CLI is an _experimental_ way to run all the codecs you know from the [Squoosh] web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
||||||
|
|||||||
16
cli/package-lock.json
generated
16
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/cli",
|
"name": "@squoosh/cli",
|
||||||
"version": "0.7.2",
|
"version": "0.7.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@squoosh/cli",
|
"name": "@squoosh/cli",
|
||||||
"version": "0.7.2",
|
"version": "0.7.3",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@squoosh/lib": "^0.4.0",
|
"@squoosh/lib": "^0.4.0",
|
||||||
@@ -36,9 +36,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -363,9 +363,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
|
||||||
},
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/cli",
|
"name": "@squoosh/cli",
|
||||||
"version": "0.7.2",
|
"version": "0.7.3",
|
||||||
"description": "A CLI for Squoosh",
|
"description": "A CLI for Squoosh",
|
||||||
"public": true,
|
"public": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { program } from 'commander/esm.mjs';
|
|||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { promises as fsp } from 'fs';
|
import { promises as fsp } from 'fs';
|
||||||
|
import { cpus } from 'os';
|
||||||
import ora from 'ora';
|
import ora from 'ora';
|
||||||
import kleur from 'kleur';
|
import kleur from 'kleur';
|
||||||
|
|
||||||
@@ -75,7 +76,9 @@ async function getInputFiles(paths) {
|
|||||||
|
|
||||||
for (const inputPath of paths) {
|
for (const inputPath of paths) {
|
||||||
const files = (await fsp.lstat(inputPath)).isDirectory()
|
const files = (await fsp.lstat(inputPath)).isDirectory()
|
||||||
? (await fsp.readdir(inputPath, {withFileTypes: true})).filter(dirent => dirent.isFile()).map(dirent => path.join(inputPath, dirent.name))
|
? (await fsp.readdir(inputPath, { withFileTypes: true }))
|
||||||
|
.filter((dirent) => dirent.isFile())
|
||||||
|
.map((dirent) => path.join(inputPath, dirent.name))
|
||||||
: [inputPath];
|
: [inputPath];
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
try {
|
try {
|
||||||
@@ -101,7 +104,7 @@ async function getInputFiles(paths) {
|
|||||||
async function processFiles(files) {
|
async function processFiles(files) {
|
||||||
files = await getInputFiles(files);
|
files = await getInputFiles(files);
|
||||||
|
|
||||||
const imagePool = new ImagePool();
|
const imagePool = new ImagePool(cpus().length);
|
||||||
|
|
||||||
const results = new Map();
|
const results = new Map();
|
||||||
const progress = progressTracker(results);
|
const progress = progressTracker(results);
|
||||||
@@ -116,7 +119,8 @@ async function processFiles(files) {
|
|||||||
let decoded = 0;
|
let decoded = 0;
|
||||||
let decodedFiles = await Promise.all(
|
let decodedFiles = await Promise.all(
|
||||||
files.map(async (file) => {
|
files.map(async (file) => {
|
||||||
const image = imagePool.ingestImage(file);
|
const buffer = await fsp.readFile(file);
|
||||||
|
const image = imagePool.ingestImage(buffer);
|
||||||
await image.decoded;
|
await image.decoded;
|
||||||
results.set(image, {
|
results.set(image, {
|
||||||
file,
|
file,
|
||||||
@@ -178,7 +182,7 @@ async function processFiles(files) {
|
|||||||
const outputPath = path.join(
|
const outputPath = path.join(
|
||||||
program.opts().outputDir,
|
program.opts().outputDir,
|
||||||
path.basename(originalFile, path.extname(originalFile)) +
|
path.basename(originalFile, path.extname(originalFile)) +
|
||||||
program.opts().suffix
|
program.opts().suffix,
|
||||||
);
|
);
|
||||||
for (const output of Object.values(image.encodedWith)) {
|
for (const output of Object.values(image.encodedWith)) {
|
||||||
const outputFile = `${outputPath}.${(await output).extension}`;
|
const outputFile = `${outputPath}.${(await output).extension}`;
|
||||||
|
|||||||
10
codecs/avif/enc/avif_enc.js
generated
10
codecs/avif/enc/avif_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/avif/enc/avif_enc_mt.js
generated
2
codecs/avif/enc/avif_enc_mt.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/avif/enc/avif_enc_mt.worker.js
generated
2
codecs/avif/enc/avif_enc_mt.worker.js
generated
@@ -1 +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;function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){var imports={};imports["wasm"]=e.data.wasmModule;imports["wasmMemory"]=e.data.wasmMemory;imports["buffer"]=imports["wasmMemory"].buffer;imports["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);Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){{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}};
|
"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}};
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ ENV LDFLAGS "${CFLAGS} \
|
|||||||
-s ALLOW_MEMORY_GROWTH=1 \
|
-s ALLOW_MEMORY_GROWTH=1 \
|
||||||
-s TEXTDECODER=2 \
|
-s TEXTDECODER=2 \
|
||||||
-s NODEJS_CATCH_EXIT=0 -s NODEJS_CATCH_REJECTION=0 \
|
-s NODEJS_CATCH_EXIT=0 -s NODEJS_CATCH_REJECTION=0 \
|
||||||
-s MINIMAL_RUNTIME=1 \
|
|
||||||
"
|
"
|
||||||
# Build and cache standard libraries with these flags + Embind.
|
# Build and cache standard libraries with these flags + Embind.
|
||||||
RUN emcc ${CXXFLAGS} ${LDFLAGS} --bind -xc++ /dev/null -o /dev/null
|
RUN emcc ${CXXFLAGS} ${LDFLAGS} --bind -xc++ /dev/null -o /dev/null
|
||||||
|
|||||||
10
codecs/imagequant/imagequant.js
generated
10
codecs/imagequant/imagequant.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/imagequant/imagequant_node.js
generated
11
codecs/imagequant/imagequant_node.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
CODEC_URL = https://github.com/libjxl/libjxl.git
|
CODEC_URL = https://github.com/libjxl/libjxl.git
|
||||||
CODEC_VERSION = 3f76321a7cbcf4f452894dc173eb7be60f508ecb
|
CODEC_VERSION = 9f544641ec83f6abd9da598bdd08178ee8a003e0
|
||||||
CODEC_DIR = node_modules/jxl
|
CODEC_DIR = node_modules/jxl
|
||||||
CODEC_BUILD_ROOT := $(CODEC_DIR)/build
|
CODEC_BUILD_ROOT := $(CODEC_DIR)/build
|
||||||
CODEC_MT_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt
|
CODEC_MT_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt
|
||||||
@@ -75,6 +75,9 @@ $(CODEC_MT_SIMD_BUILD_DIR)/Makefile: CXXFLAGS+=-msimd128
|
|||||||
-DCMAKE_CROSSCOMPILING_EMULATOR=node \
|
-DCMAKE_CROSSCOMPILING_EMULATOR=node \
|
||||||
-B $(@D) \
|
-B $(@D) \
|
||||||
$(<D)
|
$(<D)
|
||||||
|
emcc -Wall -O3 -o $(CODEC_DIR)/third_party/skcms/skcms.cc.o -I$(CODEC_DIR)/third_party/skcms -c $(CODEC_DIR)/third_party/skcms/skcms.cc
|
||||||
|
llvm-ar rc $(CODEC_BUILD_DIR)/third_party/libskcms.a $(CODEC_DIR)/third_party/skcms/skcms.cc.o
|
||||||
|
rm $(CODEC_DIR)/third_party/skcms/skcms.cc.o
|
||||||
|
|
||||||
$(CODEC_DIR)/CMakeLists.txt:
|
$(CODEC_DIR)/CMakeLists.txt:
|
||||||
$(RM) -r $(@D)
|
$(RM) -r $(@D)
|
||||||
|
|||||||
10
codecs/jxl/dec/jxl_dec.js
generated
10
codecs/jxl/dec/jxl_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/jxl/dec/jxl_node_dec.js
generated
11
codecs/jxl/dec/jxl_node_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -4,21 +4,21 @@
|
|||||||
#include "lib/jxl/base/thread_pool_internal.h"
|
#include "lib/jxl/base/thread_pool_internal.h"
|
||||||
#include "lib/jxl/enc_external_image.h"
|
#include "lib/jxl/enc_external_image.h"
|
||||||
#include "lib/jxl/enc_file.h"
|
#include "lib/jxl/enc_file.h"
|
||||||
|
#include "lib/jxl/enc_color_management.h"
|
||||||
|
|
||||||
using namespace emscripten;
|
using namespace emscripten;
|
||||||
|
|
||||||
thread_local const val Uint8Array = val::global("Uint8Array");
|
thread_local const val Uint8Array = val::global("Uint8Array");
|
||||||
|
|
||||||
struct JXLOptions {
|
struct JXLOptions {
|
||||||
// 1 = slowest
|
int effort;
|
||||||
// 7 = fastest
|
|
||||||
int speed;
|
|
||||||
float quality;
|
float quality;
|
||||||
bool progressive;
|
bool progressive;
|
||||||
int epf;
|
int epf;
|
||||||
int nearLossless;
|
|
||||||
bool lossyPalette;
|
bool lossyPalette;
|
||||||
size_t decodingSpeedTier;
|
size_t decodingSpeedTier;
|
||||||
|
float photonNoiseIso;
|
||||||
|
bool lossyModular;
|
||||||
};
|
};
|
||||||
|
|
||||||
val encode(std::string image, int width, int height, JXLOptions options) {
|
val encode(std::string image, int width, int height, JXLOptions options) {
|
||||||
@@ -33,11 +33,14 @@ val encode(std::string image, int width, int height, JXLOptions options) {
|
|||||||
pool_ptr = &pool;
|
pool_ptr = &pool;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cparams.epf = options.epf;
|
size_t st = 10 - options.effort;
|
||||||
cparams.speed_tier = static_cast<jxl::SpeedTier>(options.speed);
|
cparams.speed_tier = jxl::SpeedTier(st);
|
||||||
cparams.decoding_speed_tier = options.decodingSpeedTier;
|
|
||||||
|
|
||||||
if (options.lossyPalette || options.nearLossless) {
|
cparams.epf = options.epf;
|
||||||
|
cparams.decoding_speed_tier = options.decodingSpeedTier;
|
||||||
|
cparams.photon_noise_iso = options.photonNoiseIso;
|
||||||
|
|
||||||
|
if (options.lossyPalette) {
|
||||||
cparams.lossy_palette = true;
|
cparams.lossy_palette = true;
|
||||||
cparams.palette_colors = 0;
|
cparams.palette_colors = 0;
|
||||||
cparams.options.predictor = jxl::Predictor::Zero;
|
cparams.options.predictor = jxl::Predictor::Zero;
|
||||||
@@ -49,11 +52,16 @@ val encode(std::string image, int width, int height, JXLOptions options) {
|
|||||||
float quality = options.quality;
|
float quality = options.quality;
|
||||||
|
|
||||||
// Quality settings roughly match libjpeg qualities.
|
// Quality settings roughly match libjpeg qualities.
|
||||||
if (quality < 7 || quality == 100) {
|
if (options.lossyModular || quality == 100) {
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
// Internal modular quality to roughly match VarDCT size.
|
// Internal modular quality to roughly match VarDCT size.
|
||||||
|
if (quality < 7) {
|
||||||
cparams.quality_pair.first = cparams.quality_pair.second =
|
cparams.quality_pair.first = cparams.quality_pair.second =
|
||||||
std::min(35 + (quality - 7) * 3.0f, 100.0f);
|
std::min(35 + (quality - 7) * 3.0f, 100.0f);
|
||||||
|
} else {
|
||||||
|
cparams.quality_pair.first = cparams.quality_pair.second =
|
||||||
|
std::min(35 + (quality - 7) * 65.f / 93.f, 100.0f);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cparams.modular_mode = false;
|
cparams.modular_mode = false;
|
||||||
if (quality >= 30) {
|
if (quality >= 30) {
|
||||||
@@ -90,14 +98,14 @@ val encode(std::string image, int width, int height, JXLOptions options) {
|
|||||||
jxl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(image.data()), image.size()), width,
|
jxl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(image.data()), image.size()), width,
|
||||||
height, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/true,
|
height, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/true,
|
||||||
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/8, /*endiannes=*/JXL_LITTLE_ENDIAN,
|
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/8, /*endiannes=*/JXL_LITTLE_ENDIAN,
|
||||||
/*flipped_y=*/false, pool_ptr, main);
|
/*flipped_y=*/false, pool_ptr, main, /*(only true if bits_per_sample==32) float_in=*/false);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return val::null();
|
return val::null();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto js_result = val::null();
|
auto js_result = val::null();
|
||||||
if (EncodeFile(cparams, &io, &passes_enc_state, &bytes, /*aux=*/nullptr, pool_ptr)) {
|
if (EncodeFile(cparams, &io, &passes_enc_state, &bytes, jxl::GetJxlCms(), /*aux=*/nullptr, pool_ptr)) {
|
||||||
js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data()));
|
js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,12 +114,13 @@ val encode(std::string image, int width, int height, JXLOptions options) {
|
|||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(my_module) {
|
EMSCRIPTEN_BINDINGS(my_module) {
|
||||||
value_object<JXLOptions>("JXLOptions")
|
value_object<JXLOptions>("JXLOptions")
|
||||||
.field("speed", &JXLOptions::speed)
|
.field("effort", &JXLOptions::effort)
|
||||||
.field("quality", &JXLOptions::quality)
|
.field("quality", &JXLOptions::quality)
|
||||||
.field("progressive", &JXLOptions::progressive)
|
.field("progressive", &JXLOptions::progressive)
|
||||||
.field("nearLossless", &JXLOptions::nearLossless)
|
|
||||||
.field("lossyPalette", &JXLOptions::lossyPalette)
|
.field("lossyPalette", &JXLOptions::lossyPalette)
|
||||||
.field("decodingSpeedTier", &JXLOptions::decodingSpeedTier)
|
.field("decodingSpeedTier", &JXLOptions::decodingSpeedTier)
|
||||||
|
.field("photonNoiseIso", &JXLOptions::photonNoiseIso)
|
||||||
|
.field("lossyModular", &JXLOptions::lossyModular)
|
||||||
.field("epf", &JXLOptions::epf);
|
.field("epf", &JXLOptions::epf);
|
||||||
|
|
||||||
function("encode", &encode);
|
function("encode", &encode);
|
||||||
|
|||||||
5
codecs/jxl/enc/jxl_enc.d.ts
vendored
5
codecs/jxl/enc/jxl_enc.d.ts
vendored
@@ -1,11 +1,12 @@
|
|||||||
export interface EncodeOptions {
|
export interface EncodeOptions {
|
||||||
speed: number;
|
effort: number;
|
||||||
quality: number;
|
quality: number;
|
||||||
progressive: boolean;
|
progressive: boolean;
|
||||||
epf: number;
|
epf: number;
|
||||||
nearLossless: number;
|
|
||||||
lossyPalette: boolean;
|
lossyPalette: boolean;
|
||||||
decodingSpeedTier: number;
|
decodingSpeedTier: number;
|
||||||
|
photonNoiseIso: number;
|
||||||
|
lossyModular: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JXLModule extends EmscriptenWasm.Module {
|
export interface JXLModule extends EmscriptenWasm.Module {
|
||||||
|
|||||||
10
codecs/jxl/enc/jxl_enc.js
generated
10
codecs/jxl/enc/jxl_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/jxl/enc/jxl_enc_mt.js
generated
2
codecs/jxl/enc/jxl_enc_mt.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/jxl/enc/jxl_enc_mt.worker.js
generated
2
codecs/jxl/enc/jxl_enc_mt.worker.js
generated
@@ -1 +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;function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){var imports={};imports["wasm"]=e.data.wasmModule;imports["wasmMemory"]=e.data.wasmMemory;imports["buffer"]=imports["wasmMemory"].buffer;imports["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);Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){{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}};
|
"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/jxl/enc/jxl_enc_mt_simd.js
generated
2
codecs/jxl/enc/jxl_enc_mt_simd.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/jxl/enc/jxl_enc_mt_simd.worker.js
generated
2
codecs/jxl/enc/jxl_enc_mt_simd.worker.js
generated
@@ -1 +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;function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){var imports={};imports["wasm"]=e.data.wasmModule;imports["wasmMemory"]=e.data.wasmMemory;imports["buffer"]=imports["wasmMemory"].buffer;imports["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);Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){{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}};
|
"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}};
|
||||||
|
|||||||
11
codecs/jxl/enc/jxl_node_enc.js
generated
11
codecs/jxl/enc/jxl_node_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/mozjpeg/dec/mozjpeg_node_dec.js
generated
11
codecs/mozjpeg/dec/mozjpeg_node_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -158,6 +158,11 @@ val encode(std::string image_in, int image_width, int image_height, MozJpegOptio
|
|||||||
if (!opts.auto_subsample && opts.color_space == JCS_YCbCr) {
|
if (!opts.auto_subsample && opts.color_space == JCS_YCbCr) {
|
||||||
cinfo.comp_info[0].h_samp_factor = opts.chroma_subsample;
|
cinfo.comp_info[0].h_samp_factor = opts.chroma_subsample;
|
||||||
cinfo.comp_info[0].v_samp_factor = opts.chroma_subsample;
|
cinfo.comp_info[0].v_samp_factor = opts.chroma_subsample;
|
||||||
|
|
||||||
|
if (opts.chroma_subsample > 2) {
|
||||||
|
// Otherwise encoding fails.
|
||||||
|
jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts.baseline && opts.progressive) {
|
if (!opts.baseline && opts.progressive) {
|
||||||
|
|||||||
10
codecs/mozjpeg/enc/mozjpeg_enc.js
generated
10
codecs/mozjpeg/enc/mozjpeg_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/mozjpeg/enc/mozjpeg_node_enc.js
generated
11
codecs/mozjpeg/enc/mozjpeg_node_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/visdif/visdif.js
generated
11
codecs/visdif/visdif.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
10
codecs/webp/dec/webp_dec.js
generated
10
codecs/webp/dec/webp_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/webp/dec/webp_node_dec.js
generated
11
codecs/webp/dec/webp_node_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
10
codecs/webp/enc/webp_enc.js
generated
10
codecs/webp/enc/webp_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
10
codecs/webp/enc/webp_enc_simd.js
generated
10
codecs/webp/enc/webp_enc_simd.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/webp/enc/webp_node_enc.js
generated
11
codecs/webp/enc/webp_node_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
10
codecs/wp2/dec/wp2_dec.js
generated
10
codecs/wp2/dec/wp2_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
11
codecs/wp2/dec/wp2_node_dec.js
generated
11
codecs/wp2/dec/wp2_node_dec.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
10
codecs/wp2/enc/wp2_enc.js
generated
10
codecs/wp2/enc/wp2_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/wp2/enc/wp2_enc_mt.js
generated
2
codecs/wp2/enc/wp2_enc_mt.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/wp2/enc/wp2_enc_mt.worker.js
generated
2
codecs/wp2/enc/wp2_enc_mt.worker.js
generated
@@ -1 +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;function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){var imports={};imports["wasm"]=e.data.wasmModule;imports["wasmMemory"]=e.data.wasmMemory;imports["buffer"]=imports["wasmMemory"].buffer;imports["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);Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){{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}};
|
"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/wp2/enc/wp2_enc_mt_simd.js
generated
2
codecs/wp2/enc/wp2_enc_mt_simd.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/wp2/enc/wp2_enc_mt_simd.worker.js
generated
2
codecs/wp2/enc/wp2_enc_mt_simd.worker.js
generated
@@ -1 +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;function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){var imports={};imports["wasm"]=e.data.wasmModule;imports["wasmMemory"]=e.data.wasmMemory;imports["buffer"]=imports["wasmMemory"].buffer;imports["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);Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){{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}};
|
"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}};
|
||||||
|
|||||||
11
codecs/wp2/enc/wp2_node_enc.js
generated
11
codecs/wp2/enc/wp2_node_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -20,6 +20,7 @@ const allSrcPlaceholder = 'CLIENT_BUNDLE_PLUGIN_ALL_SRC';
|
|||||||
|
|
||||||
export function getDependencies(clientOutput, item) {
|
export function getDependencies(clientOutput, item) {
|
||||||
const crawlDependencies = new Set([item.fileName]);
|
const crawlDependencies = new Set([item.fileName]);
|
||||||
|
const referencedFiles = new Set();
|
||||||
|
|
||||||
for (const fileName of crawlDependencies) {
|
for (const fileName of crawlDependencies) {
|
||||||
const chunk = clientOutput.find((v) => v.fileName === fileName);
|
const chunk = clientOutput.find((v) => v.fileName === fileName);
|
||||||
@@ -27,11 +28,24 @@ export function getDependencies(clientOutput, item) {
|
|||||||
for (const dep of chunk.imports) {
|
for (const dep of chunk.imports) {
|
||||||
crawlDependencies.add(dep);
|
crawlDependencies.add(dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const dep of chunk.referencedFiles) {
|
||||||
|
referencedFiles.add(dep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't add self as dependency
|
// Don't add self as dependency
|
||||||
crawlDependencies.delete(item.fileName);
|
crawlDependencies.delete(item.fileName);
|
||||||
|
|
||||||
|
// Merge referencedFiles as regular deps. They need to be in the same Set as
|
||||||
|
// some JS files might appear in both lists and need to be deduped too.
|
||||||
|
//
|
||||||
|
// Didn't do this as part of the main loop since their `chunk` can't have
|
||||||
|
// nested deps and sometimes might be missing altogether, depending on type.
|
||||||
|
for (const dep of referencedFiles) {
|
||||||
|
crawlDependencies.add(dep);
|
||||||
|
}
|
||||||
|
|
||||||
return [...crawlDependencies];
|
return [...crawlDependencies];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,9 +153,9 @@ export default function (inputOptions, outputOptions, resolveFileUrl) {
|
|||||||
if (property.startsWith(allSrcPlaceholder)) {
|
if (property.startsWith(allSrcPlaceholder)) {
|
||||||
const allModules = [
|
const allModules = [
|
||||||
clientEntry,
|
clientEntry,
|
||||||
...dependencies.map((name) =>
|
...dependencies
|
||||||
clientOutput.find((item) => item.fileName === name),
|
.map((name) => clientOutput.find((item) => item.fileName === name))
|
||||||
),
|
.filter((item) => item.code),
|
||||||
];
|
];
|
||||||
|
|
||||||
const inlineDefines = [
|
const inlineDefines = [
|
||||||
|
|||||||
@@ -14,28 +14,40 @@ import { promises as fs } from 'fs';
|
|||||||
|
|
||||||
import { lookup as lookupMime } from 'mime-types';
|
import { lookup as lookupMime } from 'mime-types';
|
||||||
|
|
||||||
const prefix = 'data-url:';
|
const prefix = /^data-url(-text)?:/;
|
||||||
|
|
||||||
export default function dataURLPlugin() {
|
export default function dataURLPlugin() {
|
||||||
return {
|
return {
|
||||||
name: 'data-url-plugin',
|
name: 'data-url-plugin',
|
||||||
async resolveId(id, importer) {
|
async resolveId(id, importer) {
|
||||||
if (!id.startsWith(prefix)) return;
|
const match = prefix.exec(id);
|
||||||
|
if (!match) return;
|
||||||
return (
|
return (
|
||||||
prefix + (await this.resolve(id.slice(prefix.length), importer)).id
|
match[0] + (await this.resolve(id.slice(match[0].length), importer)).id
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async load(id) {
|
async load(id) {
|
||||||
if (!id.startsWith(prefix)) return;
|
const match = prefix.exec(id);
|
||||||
const realId = id.slice(prefix.length);
|
if (!match) return;
|
||||||
|
|
||||||
|
const isText = !!match[1];
|
||||||
|
const realId = id.slice(match[0].length);
|
||||||
this.addWatchFile(realId);
|
this.addWatchFile(realId);
|
||||||
|
|
||||||
const source = await fs.readFile(realId);
|
const source = await fs.readFile(realId);
|
||||||
const type = lookupMime(realId) || 'text/plain';
|
const type = lookupMime(realId) || 'text/plain';
|
||||||
|
|
||||||
return `export default 'data:${type};base64,${source.toString(
|
if (isText) {
|
||||||
'base64',
|
const encodedBody = encodeURIComponent(source.toString('utf8'));
|
||||||
)}';`;
|
|
||||||
|
return `export default ${JSON.stringify(
|
||||||
|
`data:${type};charset=utf-8,${encodedBody}`,
|
||||||
|
)};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `export default ${JSON.stringify(
|
||||||
|
`data:${type};base64,${source.toString('base64')}`,
|
||||||
|
)};`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,14 @@ const prefix = 'entry-data:';
|
|||||||
const mainNamePlaceholder = 'ENTRY_DATA_PLUGIN_MAIN_NAME';
|
const mainNamePlaceholder = 'ENTRY_DATA_PLUGIN_MAIN_NAME';
|
||||||
const dependenciesPlaceholder = 'ENTRY_DATA_PLUGIN_DEPS';
|
const dependenciesPlaceholder = 'ENTRY_DATA_PLUGIN_DEPS';
|
||||||
const placeholderRe = /(ENTRY_DATA_PLUGIN_(?:MAIN_NAME|DEPS))(\d+)/g;
|
const placeholderRe = /(ENTRY_DATA_PLUGIN_(?:MAIN_NAME|DEPS))(\d+)/g;
|
||||||
const filenamePrefix = 'static/';
|
|
||||||
|
/** @param {string} fileName */
|
||||||
|
export function fileNameToURL(fileName) {
|
||||||
|
return fileName.replace(/^static\//, '/');
|
||||||
|
}
|
||||||
|
|
||||||
export default function entryDataPlugin() {
|
export default function entryDataPlugin() {
|
||||||
/** @type {string} */
|
/** @type {number} */
|
||||||
let exportCounter;
|
let exportCounter;
|
||||||
/** @type {Map<number, string>} */
|
/** @type {Map<number, string>} */
|
||||||
let counterToIdMap;
|
let counterToIdMap;
|
||||||
@@ -69,14 +73,12 @@ export default function entryDataPlugin() {
|
|||||||
if (!chunk) throw Error(`Cannot find ${id}`);
|
if (!chunk) throw Error(`Cannot find ${id}`);
|
||||||
|
|
||||||
if (placeholder === mainNamePlaceholder) {
|
if (placeholder === mainNamePlaceholder) {
|
||||||
return JSON.stringify(
|
return JSON.stringify(fileNameToURL(chunk.fileName));
|
||||||
chunk.fileName.slice(filenamePrefix.length),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSON.stringify(
|
return JSON.stringify(
|
||||||
getDependencies(chunks, chunk).map((item) =>
|
getDependencies(chunks, chunk).map((filename) =>
|
||||||
item.slice(filenamePrefix.length),
|
fileNameToURL(filename),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,10 +28,17 @@ if (!self.<%- amdFunctionName %>) {
|
|||||||
<% } else { %>
|
<% } else { %>
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
if ("document" in self) {
|
if ("document" in self) {
|
||||||
|
const link = document.createElement("link");
|
||||||
|
link.rel = "preload";
|
||||||
|
link.as = "script";
|
||||||
|
link.href = uri;
|
||||||
|
link.onload = () => {
|
||||||
const script = document.createElement("script");
|
const script = document.createElement("script");
|
||||||
script.src = uri;
|
script.src = uri;
|
||||||
script.onload = resolve;
|
script.onload = resolve;
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
|
};
|
||||||
|
document.head.appendChild(link);
|
||||||
} else {
|
} else {
|
||||||
self.nextDefineUri = uri;
|
self.nextDefineUri = uri;
|
||||||
importScripts(uri);
|
importScripts(uri);
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
# Project no longer maintained
|
||||||
|
|
||||||
|
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
|
||||||
|
|
||||||
# libSquoosh
|
# libSquoosh
|
||||||
|
|
||||||
libSquoosh is an _experimental_ way to run all the codecs you know from the [Squoosh] web app directly inside your own JavaScript program. libSquoosh uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
libSquoosh is an _experimental_ way to run all the codecs you know from the [Squoosh] web app directly inside your own JavaScript program. libSquoosh uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
||||||
@@ -16,21 +20,25 @@ You can start using the libSquoosh by adding these lines to the top of your JS p
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
import { ImagePool } from '@squoosh/lib';
|
import { ImagePool } from '@squoosh/lib';
|
||||||
const imagePool = new ImagePool();
|
import { cpus } from 'os';
|
||||||
|
const imagePool = new ImagePool(cpus().length);
|
||||||
```
|
```
|
||||||
|
|
||||||
This will create an image pool with an underlying processing pipeline that you can use to ingest and encode images. The ImagePool constructor takes one argument that defines how many parallel operations it is allowed to run at any given time. By default, this number is set to the amount of CPU cores available in the system it is running on.
|
This will create an image pool with an underlying processing pipeline that you can use to ingest and encode images. The ImagePool constructor takes one argument that defines how many parallel operations it is allowed to run at any given time.
|
||||||
|
|
||||||
|
:warning: Important! Make sure to only create 1 `ImagePool` when performing parallel image processing. If you create multiple pools, the `ImagePool` can run out of memory and crash. By reusing a single `ImagePool`, you can ensure that the backing worker queue and processing pipeline releases memory prior to processing the next image.
|
||||||
|
|
||||||
## Ingesting images
|
## Ingesting images
|
||||||
|
|
||||||
You can ingest a new image like so:
|
You can ingest a new image like so:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const imagePath = 'path/to/image.png';
|
import fs from 'fs/promises';
|
||||||
const image = imagePool.ingestImage(imagePath);
|
const file = await fs.readFile('./path/to/image.png');
|
||||||
|
const image = imagePool.ingestImage(file);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `ingestImage` function can take anything the node [`readFile`][readfile] function can take, including a buffer and `FileHandle`.
|
The `ingestImage` function can accept any [`ArrayBuffer`][arraybuffer] whether that is from `readFile()` or `fetch()`.
|
||||||
|
|
||||||
The returned `image` object is a representation of the original image, that you can now preprocess, encode, and extract information about.
|
The returned `image` object is a representation of the original image, that you can now preprocess, encode, and extract information about.
|
||||||
|
|
||||||
@@ -39,23 +47,19 @@ The returned `image` object is a representation of the original image, that you
|
|||||||
When an image has been ingested, you can start preprocessing it and encoding it to other formats. This example will resize the image and then encode it to a `.jpg` and `.jxl` image:
|
When an image has been ingested, you can start preprocessing it and encoding it to other formats. This example will resize the image and then encode it to a `.jpg` and `.jxl` image:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
await image.decoded; //Wait until the image is decoded before running preprocessors.
|
|
||||||
|
|
||||||
const preprocessOptions = {
|
const preprocessOptions = {
|
||||||
//When both width and height are specified, the image resized to specified size.
|
//When both width and height are specified, the image resized to specified size.
|
||||||
resize: {
|
resize: {
|
||||||
enabled: true,
|
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 50,
|
height: 50,
|
||||||
}
|
},
|
||||||
/*
|
/*
|
||||||
//When either width or height is specified, the image resized to specified size keeping aspect ratio.
|
//When either width or height is specified, the image resized to specified size keeping aspect ratio.
|
||||||
resize: {
|
resize: {
|
||||||
enabled: true,
|
|
||||||
width: 100,
|
width: 100,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
};
|
||||||
await image.preprocess(preprocessOptions);
|
await image.preprocess(preprocessOptions);
|
||||||
|
|
||||||
const encodeOptions = {
|
const encodeOptions = {
|
||||||
@@ -63,9 +67,8 @@ const encodeOptions = {
|
|||||||
jxl: {
|
jxl: {
|
||||||
quality: 90,
|
quality: 90,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
await image.encode(encodeOptions);
|
const result = await image.encode(encodeOptions);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The default values for each option can be found in the [`codecs.ts`][codecs.ts] file under `defaultEncoderOptions`. Every unspecified value will use the default value specified there. _Better documentation is needed here._
|
The default values for each option can be found in the [`codecs.ts`][codecs.ts] file under `defaultEncoderOptions`. Every unspecified value will use the default value specified there. _Better documentation is needed here._
|
||||||
@@ -89,7 +92,7 @@ When you have encoded an image, you normally want to write it to a file.
|
|||||||
This example takes an image that has been encoded as a `jpg` and writes it to a file:
|
This example takes an image that has been encoded as a `jpg` and writes it to a file:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const rawEncodedImage = (await image.encodedWith.mozjpeg).binary;
|
const rawEncodedImage = image.encodedWith.mozjpeg.binary;
|
||||||
|
|
||||||
fs.writeFile('/path/to/new/image.jpg', rawEncodedImage);
|
fs.writeFile('/path/to/new/image.jpg', rawEncodedImage);
|
||||||
```
|
```
|
||||||
@@ -100,10 +103,7 @@ This example iterates through all encoded versions of the image and writes them
|
|||||||
const newImagePath = '/path/to/image.'; //extension is added automatically
|
const newImagePath = '/path/to/image.'; //extension is added automatically
|
||||||
|
|
||||||
for (const encodedImage of Object.values(image.encodedWith)) {
|
for (const encodedImage of Object.values(image.encodedWith)) {
|
||||||
fs.writeFile(
|
fs.writeFile(newImagePath + encodedImage.extension, encodedImage.binary);
|
||||||
newImagePath + (await encodedImage).extension,
|
|
||||||
(await encodedImage).binary,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ console.log(await image.decoded);
|
|||||||
Information about an encoded image can be found at `Image.encodedWith[encoderName]`. It looks something like this:
|
Information about an encoded image can be found at `Image.encodedWith[encoderName]`. It looks something like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
console.log(await image.encodedWith.jxl);
|
console.log(image.encodedWith.jxl);
|
||||||
// Returns:
|
// Returns:
|
||||||
{
|
{
|
||||||
optionsUsed: {
|
optionsUsed: {
|
||||||
@@ -168,4 +168,4 @@ const encodeOptions: {
|
|||||||
[squoosh]: https://squoosh.app
|
[squoosh]: https://squoosh.app
|
||||||
[codecs.ts]: https://github.com/GoogleChromeLabs/squoosh/blob/dev/libsquoosh/src/codecs.ts
|
[codecs.ts]: https://github.com/GoogleChromeLabs/squoosh/blob/dev/libsquoosh/src/codecs.ts
|
||||||
[butteraugli]: https://github.com/google/butteraugli
|
[butteraugli]: https://github.com/google/butteraugli
|
||||||
[readfile]: https://nodejs.org/api/fs.html#fs_fspromises_readfile_path_options
|
[arraybuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
|
||||||
|
|||||||
26
libsquoosh/lib/move-d-ts.js
Normal file
26
libsquoosh/lib/move-d-ts.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const del = require('del');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const tsOutputDir = path.resolve('..', '.tmp', 'ts', 'libsquoosh');
|
||||||
|
const tsOutputSourceDir = path.join(tsOutputDir, 'src');
|
||||||
|
const buildDir = path.resolve('build');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await del(path.join(buildDir, '*.d.ts'));
|
||||||
|
const files = await fs.promises.readdir(tsOutputSourceDir);
|
||||||
|
|
||||||
|
const movePromises = [];
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.endsWith('d.ts') || file.endsWith('d.ts.map')) {
|
||||||
|
movePromises.push(
|
||||||
|
fs.promises.rename(
|
||||||
|
path.join(tsOutputSourceDir, file),
|
||||||
|
path.join(buildDir, file),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We need to remove `tsconfig.tsbuildinfo` otherwise TS does not emit unchanged `.d.ts` files
|
||||||
|
await del([path.join(tsOutputDir, 'tsconfig.tsbuildinfo')], { force: true });
|
||||||
|
})();
|
||||||
4
libsquoosh/package-lock.json
generated
4
libsquoosh/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/lib",
|
"name": "@squoosh/lib",
|
||||||
"version": "0.4.0",
|
"version": "0.5.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@squoosh/lib",
|
"name": "@squoosh/lib",
|
||||||
"version": "0.4.0",
|
"version": "0.5.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wasm-feature-detect": "^1.2.11",
|
"wasm-feature-detect": "^1.2.11",
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/lib",
|
"name": "@squoosh/lib",
|
||||||
"version": "0.4.0",
|
"version": "0.5.2",
|
||||||
"description": "A Node library for Squoosh",
|
"description": "A Node library for Squoosh",
|
||||||
"public": true,
|
"public": true,
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
|
"types": "./build/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"/build/*"
|
"/build/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c"
|
"build": "rollup -c && node lib/move-d-ts.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Google Chrome Developers <chromium-dev@google.com>",
|
"author": "Google Chrome Developers <chromium-dev@google.com>",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { builtinModules } from 'module';
|
|||||||
|
|
||||||
/** @type {import('rollup').RollupOptions} */
|
/** @type {import('rollup').RollupOptions} */
|
||||||
export default {
|
export default {
|
||||||
input: 'src/index.js',
|
input: 'src/index.ts',
|
||||||
output: {
|
output: {
|
||||||
dir: 'build',
|
dir: 'build',
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ import { cpus } from 'os';
|
|||||||
hardwareConcurrency: cpus().length,
|
hardwareConcurrency: cpus().length,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface DecodeModule extends EmscriptenWasm.Module {
|
||||||
|
decode: (data: Uint8Array) => ImageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DecodeModuleFactory = EmscriptenWasm.ModuleFactory<DecodeModule>;
|
||||||
|
|
||||||
interface RotateModuleInstance {
|
interface RotateModuleInstance {
|
||||||
exports: {
|
exports: {
|
||||||
memory: WebAssembly.Memory;
|
memory: WebAssembly.Memory;
|
||||||
@@ -23,17 +29,26 @@ interface ResizeWithAspectParams {
|
|||||||
target_height: number;
|
target_height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ResizeInstantiateOptions {
|
export interface ResizeOptions {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
method: string;
|
method: 'triangle' | 'catrom' | 'mitchell' | 'lanczos3';
|
||||||
premultiply: boolean;
|
premultiply: boolean;
|
||||||
linearRGB: boolean;
|
linearRGB: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QuantOptions {
|
||||||
|
numColors: number;
|
||||||
|
dither: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RotateOptions {
|
||||||
|
numRotations: number;
|
||||||
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// Needed for being able to use ImageData as type in codec types
|
// Needed for being able to use ImageData as type in codec types
|
||||||
type ImageData = typeof import('./image_data.js');
|
type ImageData = import('./image_data.js').default;
|
||||||
// Needed for being able to assign to `globalThis.ImageData`
|
// Needed for being able to assign to `globalThis.ImageData`
|
||||||
var ImageData: ImageData['constructor'];
|
var ImageData: ImageData['constructor'];
|
||||||
}
|
}
|
||||||
@@ -41,18 +56,23 @@ declare global {
|
|||||||
import type { QuantizerModule } from '../../codecs/imagequant/imagequant.js';
|
import type { QuantizerModule } from '../../codecs/imagequant/imagequant.js';
|
||||||
|
|
||||||
// MozJPEG
|
// MozJPEG
|
||||||
|
import type { MozJPEGModule as MozJPEGEncodeModule } from '../../codecs/mozjpeg/enc/mozjpeg_enc';
|
||||||
import mozEnc from '../../codecs/mozjpeg/enc/mozjpeg_node_enc.js';
|
import mozEnc from '../../codecs/mozjpeg/enc/mozjpeg_node_enc.js';
|
||||||
import mozEncWasm from 'asset-url:../../codecs/mozjpeg/enc/mozjpeg_node_enc.wasm';
|
import mozEncWasm from 'asset-url:../../codecs/mozjpeg/enc/mozjpeg_node_enc.wasm';
|
||||||
import mozDec from '../../codecs/mozjpeg/dec/mozjpeg_node_dec.js';
|
import mozDec from '../../codecs/mozjpeg/dec/mozjpeg_node_dec.js';
|
||||||
import mozDecWasm from 'asset-url:../../codecs/mozjpeg/dec/mozjpeg_node_dec.wasm';
|
import mozDecWasm from 'asset-url:../../codecs/mozjpeg/dec/mozjpeg_node_dec.wasm';
|
||||||
|
import type { EncodeOptions as MozJPEGEncodeOptions } from '../../codecs/mozjpeg/enc/mozjpeg_enc';
|
||||||
|
|
||||||
// WebP
|
// WebP
|
||||||
|
import type { WebPModule as WebPEncodeModule } from '../../codecs/webp/enc/webp_enc';
|
||||||
import webpEnc from '../../codecs/webp/enc/webp_node_enc.js';
|
import webpEnc from '../../codecs/webp/enc/webp_node_enc.js';
|
||||||
import webpEncWasm from 'asset-url:../../codecs/webp/enc/webp_node_enc.wasm';
|
import webpEncWasm from 'asset-url:../../codecs/webp/enc/webp_node_enc.wasm';
|
||||||
import webpDec from '../../codecs/webp/dec/webp_node_dec.js';
|
import webpDec from '../../codecs/webp/dec/webp_node_dec.js';
|
||||||
import webpDecWasm from 'asset-url:../../codecs/webp/dec/webp_node_dec.wasm';
|
import webpDecWasm from 'asset-url:../../codecs/webp/dec/webp_node_dec.wasm';
|
||||||
|
import type { EncodeOptions as WebPEncodeOptions } from '../../codecs/webp/enc/webp_enc.js';
|
||||||
|
|
||||||
// AVIF
|
// AVIF
|
||||||
|
import type { AVIFModule as AVIFEncodeModule } from '../../codecs/avif/enc/avif_enc';
|
||||||
import avifEnc from '../../codecs/avif/enc/avif_node_enc.js';
|
import avifEnc from '../../codecs/avif/enc/avif_node_enc.js';
|
||||||
import avifEncWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc.wasm';
|
import avifEncWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc.wasm';
|
||||||
import avifEncMt from '../../codecs/avif/enc/avif_node_enc_mt.js';
|
import avifEncMt from '../../codecs/avif/enc/avif_node_enc_mt.js';
|
||||||
@@ -60,18 +80,23 @@ import avifEncMtWorker from 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.wo
|
|||||||
import avifEncMtWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc_mt.wasm';
|
import avifEncMtWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc_mt.wasm';
|
||||||
import avifDec from '../../codecs/avif/dec/avif_node_dec.js';
|
import avifDec from '../../codecs/avif/dec/avif_node_dec.js';
|
||||||
import avifDecWasm from 'asset-url:../../codecs/avif/dec/avif_node_dec.wasm';
|
import avifDecWasm from 'asset-url:../../codecs/avif/dec/avif_node_dec.wasm';
|
||||||
|
import type { EncodeOptions as AvifEncodeOptions } from '../../codecs/avif/enc/avif_enc.js';
|
||||||
|
|
||||||
// JXL
|
// JXL
|
||||||
|
import type { JXLModule as JXLEncodeModule } from '../../codecs/jxl/enc/jxl_enc';
|
||||||
import jxlEnc from '../../codecs/jxl/enc/jxl_node_enc.js';
|
import jxlEnc from '../../codecs/jxl/enc/jxl_node_enc.js';
|
||||||
import jxlEncWasm from 'asset-url:../../codecs/jxl/enc/jxl_node_enc.wasm';
|
import jxlEncWasm from 'asset-url:../../codecs/jxl/enc/jxl_node_enc.wasm';
|
||||||
import jxlDec from '../../codecs/jxl/dec/jxl_node_dec.js';
|
import jxlDec from '../../codecs/jxl/dec/jxl_node_dec.js';
|
||||||
import jxlDecWasm from 'asset-url:../../codecs/jxl/dec/jxl_node_dec.wasm';
|
import jxlDecWasm from 'asset-url:../../codecs/jxl/dec/jxl_node_dec.wasm';
|
||||||
|
import type { EncodeOptions as JxlEncodeOptions } from '../../codecs/jxl/enc/jxl_enc.js';
|
||||||
|
|
||||||
// WP2
|
// WP2
|
||||||
|
import type { WP2Module as WP2EncodeModule } from '../../codecs/wp2/enc/wp2_enc';
|
||||||
import wp2Enc from '../../codecs/wp2/enc/wp2_node_enc.js';
|
import wp2Enc from '../../codecs/wp2/enc/wp2_node_enc.js';
|
||||||
import wp2EncWasm from 'asset-url:../../codecs/wp2/enc/wp2_node_enc.wasm';
|
import wp2EncWasm from 'asset-url:../../codecs/wp2/enc/wp2_node_enc.wasm';
|
||||||
import wp2Dec from '../../codecs/wp2/dec/wp2_node_dec.js';
|
import wp2Dec from '../../codecs/wp2/dec/wp2_node_dec.js';
|
||||||
import wp2DecWasm from 'asset-url:../../codecs/wp2/dec/wp2_node_dec.wasm';
|
import wp2DecWasm from 'asset-url:../../codecs/wp2/dec/wp2_node_dec.wasm';
|
||||||
|
import type { EncodeOptions as WP2EncodeOptions } from '../../codecs/wp2/enc/wp2_enc.js';
|
||||||
|
|
||||||
// PNG
|
// PNG
|
||||||
import * as pngEncDec from '../../codecs/png/pkg/squoosh_png.js';
|
import * as pngEncDec from '../../codecs/png/pkg/squoosh_png.js';
|
||||||
@@ -84,6 +109,9 @@ const pngEncDecPromise = pngEncDec.default(
|
|||||||
import * as oxipng from '../../codecs/oxipng/pkg/squoosh_oxipng.js';
|
import * as oxipng from '../../codecs/oxipng/pkg/squoosh_oxipng.js';
|
||||||
import oxipngWasm from 'asset-url:../../codecs/oxipng/pkg/squoosh_oxipng_bg.wasm';
|
import oxipngWasm from 'asset-url:../../codecs/oxipng/pkg/squoosh_oxipng_bg.wasm';
|
||||||
const oxipngPromise = oxipng.default(fsp.readFile(pathify(oxipngWasm)));
|
const oxipngPromise = oxipng.default(fsp.readFile(pathify(oxipngWasm)));
|
||||||
|
interface OxiPngEncodeOptions {
|
||||||
|
level: number;
|
||||||
|
}
|
||||||
|
|
||||||
// Resize
|
// Resize
|
||||||
import * as resize from '../../codecs/resize/pkg/squoosh_resize.js';
|
import * as resize from '../../codecs/resize/pkg/squoosh_resize.js';
|
||||||
@@ -160,13 +188,7 @@ export const preprocessors = {
|
|||||||
buffer: Uint8Array,
|
buffer: Uint8Array,
|
||||||
input_width: number,
|
input_width: number,
|
||||||
input_height: number,
|
input_height: number,
|
||||||
{
|
{ width, height, method, premultiply, linearRGB }: ResizeOptions,
|
||||||
width,
|
|
||||||
height,
|
|
||||||
method,
|
|
||||||
premultiply,
|
|
||||||
linearRGB,
|
|
||||||
}: ResizeInstantiateOptions,
|
|
||||||
) => {
|
) => {
|
||||||
({ width, height } = resizeWithAspect({
|
({ width, height } = resizeWithAspect({
|
||||||
input_width,
|
input_width,
|
||||||
@@ -207,7 +229,7 @@ export const preprocessors = {
|
|||||||
buffer: Uint8Array,
|
buffer: Uint8Array,
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
{ numColors, dither }: { numColors: number; dither: number },
|
{ numColors, dither }: QuantOptions,
|
||||||
) =>
|
) =>
|
||||||
new ImageData(
|
new ImageData(
|
||||||
imageQuant.quantize(buffer, width, height, numColors, dither),
|
imageQuant.quantize(buffer, width, height, numColors, dither),
|
||||||
@@ -228,7 +250,7 @@ export const preprocessors = {
|
|||||||
buffer: Uint8Array,
|
buffer: Uint8Array,
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
{ numRotations }: { numRotations: number },
|
{ numRotations }: RotateOptions,
|
||||||
) => {
|
) => {
|
||||||
const degrees = (numRotations * 90) % 360;
|
const degrees = (numRotations * 90) % 360;
|
||||||
const sameDimensions = degrees == 0 || degrees == 180;
|
const sameDimensions = degrees == 0 || degrees == 180;
|
||||||
@@ -257,15 +279,20 @@ export const preprocessors = {
|
|||||||
numRotations: 0,
|
numRotations: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
export const codecs = {
|
export const codecs = {
|
||||||
mozjpeg: {
|
mozjpeg: {
|
||||||
name: 'MozJPEG',
|
name: 'MozJPEG',
|
||||||
extension: 'jpg',
|
extension: 'jpg',
|
||||||
detectors: [/^\xFF\xD8\xFF/],
|
detectors: [/^\xFF\xD8\xFF/],
|
||||||
dec: () => instantiateEmscriptenWasm(mozDec, mozDecWasm),
|
dec: () =>
|
||||||
enc: () => instantiateEmscriptenWasm(mozEnc, mozEncWasm),
|
instantiateEmscriptenWasm(mozDec as DecodeModuleFactory, mozDecWasm),
|
||||||
|
enc: () =>
|
||||||
|
instantiateEmscriptenWasm(
|
||||||
|
mozEnc as EmscriptenWasm.ModuleFactory<MozJPEGEncodeModule>,
|
||||||
|
mozEncWasm,
|
||||||
|
),
|
||||||
defaultEncoderOptions: {
|
defaultEncoderOptions: {
|
||||||
quality: 75,
|
quality: 75,
|
||||||
baseline: false,
|
baseline: false,
|
||||||
@@ -294,8 +321,13 @@ export const codecs = {
|
|||||||
name: 'WebP',
|
name: 'WebP',
|
||||||
extension: 'webp',
|
extension: 'webp',
|
||||||
detectors: [/^RIFF....WEBPVP8[LX ]/s],
|
detectors: [/^RIFF....WEBPVP8[LX ]/s],
|
||||||
dec: () => instantiateEmscriptenWasm(webpDec, webpDecWasm),
|
dec: () =>
|
||||||
enc: () => instantiateEmscriptenWasm(webpEnc, webpEncWasm),
|
instantiateEmscriptenWasm(webpDec as DecodeModuleFactory, webpDecWasm),
|
||||||
|
enc: () =>
|
||||||
|
instantiateEmscriptenWasm(
|
||||||
|
webpEnc as EmscriptenWasm.ModuleFactory<WebPEncodeModule>,
|
||||||
|
webpEncWasm,
|
||||||
|
),
|
||||||
defaultEncoderOptions: {
|
defaultEncoderOptions: {
|
||||||
quality: 75,
|
quality: 75,
|
||||||
target_size: 0,
|
target_size: 0,
|
||||||
@@ -335,16 +367,20 @@ export const codecs = {
|
|||||||
name: 'AVIF',
|
name: 'AVIF',
|
||||||
extension: 'avif',
|
extension: 'avif',
|
||||||
detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/],
|
detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/],
|
||||||
dec: () => instantiateEmscriptenWasm(avifDec, avifDecWasm),
|
dec: () =>
|
||||||
|
instantiateEmscriptenWasm(avifDec as DecodeModuleFactory, avifDecWasm),
|
||||||
enc: async () => {
|
enc: async () => {
|
||||||
if (await threads()) {
|
if (await threads()) {
|
||||||
return instantiateEmscriptenWasm(
|
return instantiateEmscriptenWasm(
|
||||||
avifEncMt,
|
avifEncMt as EmscriptenWasm.ModuleFactory<AVIFEncodeModule>,
|
||||||
avifEncMtWasm,
|
avifEncMtWasm,
|
||||||
avifEncMtWorker,
|
avifEncMtWorker,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return instantiateEmscriptenWasm(avifEnc, avifEncWasm);
|
return instantiateEmscriptenWasm(
|
||||||
|
avifEnc as EmscriptenWasm.ModuleFactory<AVIFEncodeModule>,
|
||||||
|
avifEncWasm,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
defaultEncoderOptions: {
|
defaultEncoderOptions: {
|
||||||
cqLevel: 33,
|
cqLevel: 33,
|
||||||
@@ -368,16 +404,22 @@ export const codecs = {
|
|||||||
name: 'JPEG-XL',
|
name: 'JPEG-XL',
|
||||||
extension: 'jxl',
|
extension: 'jxl',
|
||||||
detectors: [/^\xff\x0a/],
|
detectors: [/^\xff\x0a/],
|
||||||
dec: () => instantiateEmscriptenWasm(jxlDec, jxlDecWasm),
|
dec: () =>
|
||||||
enc: () => instantiateEmscriptenWasm(jxlEnc, jxlEncWasm),
|
instantiateEmscriptenWasm(jxlDec as DecodeModuleFactory, jxlDecWasm),
|
||||||
|
enc: () =>
|
||||||
|
instantiateEmscriptenWasm(
|
||||||
|
jxlEnc as EmscriptenWasm.ModuleFactory<JXLEncodeModule>,
|
||||||
|
jxlEncWasm,
|
||||||
|
),
|
||||||
defaultEncoderOptions: {
|
defaultEncoderOptions: {
|
||||||
speed: 4,
|
effort: 1,
|
||||||
quality: 75,
|
quality: 75,
|
||||||
progressive: false,
|
progressive: false,
|
||||||
epf: -1,
|
epf: -1,
|
||||||
nearLossless: 0,
|
|
||||||
lossyPalette: false,
|
lossyPalette: false,
|
||||||
decodingSpeedTier: 0,
|
decodingSpeedTier: 0,
|
||||||
|
photonNoiseIso: 0,
|
||||||
|
lossyModular: false,
|
||||||
},
|
},
|
||||||
autoOptimize: {
|
autoOptimize: {
|
||||||
option: 'quality',
|
option: 'quality',
|
||||||
@@ -389,8 +431,13 @@ export const codecs = {
|
|||||||
name: 'WebP2',
|
name: 'WebP2',
|
||||||
extension: 'wp2',
|
extension: 'wp2',
|
||||||
detectors: [/^\xF4\xFF\x6F/],
|
detectors: [/^\xF4\xFF\x6F/],
|
||||||
dec: () => instantiateEmscriptenWasm(wp2Dec, wp2DecWasm),
|
dec: () =>
|
||||||
enc: () => instantiateEmscriptenWasm(wp2Enc, wp2EncWasm),
|
instantiateEmscriptenWasm(wp2Dec as DecodeModuleFactory, wp2DecWasm),
|
||||||
|
enc: () =>
|
||||||
|
instantiateEmscriptenWasm(
|
||||||
|
wp2Enc as EmscriptenWasm.ModuleFactory<WP2EncodeModule>,
|
||||||
|
wp2EncWasm,
|
||||||
|
),
|
||||||
defaultEncoderOptions: {
|
defaultEncoderOptions: {
|
||||||
quality: 75,
|
quality: 75,
|
||||||
alpha_quality: 75,
|
alpha_quality: 75,
|
||||||
@@ -421,7 +468,7 @@ export const codecs = {
|
|||||||
await oxipngPromise;
|
await oxipngPromise;
|
||||||
return {
|
return {
|
||||||
encode: (
|
encode: (
|
||||||
buffer: Uint8Array,
|
buffer: Uint8ClampedArray | ArrayBuffer,
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
opts: { level: number },
|
opts: { level: number },
|
||||||
@@ -444,4 +491,13 @@ export const codecs = {
|
|||||||
max: 1,
|
max: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export {
|
||||||
|
MozJPEGEncodeOptions,
|
||||||
|
WebPEncodeOptions,
|
||||||
|
AvifEncodeOptions,
|
||||||
|
JxlEncodeOptions,
|
||||||
|
WP2EncodeOptions,
|
||||||
|
OxiPngEncodeOptions,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function instantiateEmscriptenWasm<T extends EmscriptenWasm.Module>(
|
|||||||
// These will have changed in the bundling process and
|
// These will have changed in the bundling process and
|
||||||
// we need to inject them here.
|
// we need to inject them here.
|
||||||
if (requestPath.endsWith('.wasm')) return pathify(path);
|
if (requestPath.endsWith('.wasm')) return pathify(path);
|
||||||
if (requestPath.endsWith('.worker.js')) return new URL(workerJS).pathname;
|
if (requestPath.endsWith('.worker.js')) return pathify(workerJS);
|
||||||
return requestPath;
|
return requestPath;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,224 +0,0 @@
|
|||||||
import { isMainThread } from 'worker_threads';
|
|
||||||
import { cpus } from 'os';
|
|
||||||
import { promises as fsp } from 'fs';
|
|
||||||
|
|
||||||
import { codecs as encoders, preprocessors } from './codecs.js';
|
|
||||||
import WorkerPool from './worker_pool.js';
|
|
||||||
import { autoOptimize } from './auto-optimizer.js';
|
|
||||||
|
|
||||||
export { ImagePool, encoders, preprocessors };
|
|
||||||
|
|
||||||
async function decodeFile({ file }) {
|
|
||||||
let buffer;
|
|
||||||
if (ArrayBuffer.isView(file)) {
|
|
||||||
buffer = Buffer.from(file.buffer);
|
|
||||||
file = 'Binary blob';
|
|
||||||
} else if (file instanceof ArrayBuffer) {
|
|
||||||
buffer = Buffer.from(file);
|
|
||||||
file = 'Binary blob';
|
|
||||||
} else if (file instanceof Buffer) {
|
|
||||||
buffer = file;
|
|
||||||
file = 'Binary blob';
|
|
||||||
} else if (typeof file === 'string') {
|
|
||||||
buffer = await fsp.readFile(file);
|
|
||||||
} else {
|
|
||||||
throw Error('Unexpected input type');
|
|
||||||
}
|
|
||||||
const firstChunk = buffer.slice(0, 16);
|
|
||||||
const firstChunkString = Array.from(firstChunk)
|
|
||||||
.map((v) => String.fromCodePoint(v))
|
|
||||||
.join('');
|
|
||||||
const key = Object.entries(encoders).find(([name, { detectors }]) =>
|
|
||||||
detectors.some((detector) => detector.exec(firstChunkString)),
|
|
||||||
)?.[0];
|
|
||||||
if (!key) {
|
|
||||||
throw Error(`${file} has an unsupported format`);
|
|
||||||
}
|
|
||||||
const rgba = (await encoders[key].dec()).decode(new Uint8Array(buffer));
|
|
||||||
return {
|
|
||||||
bitmap: rgba,
|
|
||||||
size: buffer.length,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function preprocessImage({ preprocessorName, options, image }) {
|
|
||||||
const preprocessor = await preprocessors[preprocessorName].instantiate();
|
|
||||||
image.bitmap = await preprocessor(
|
|
||||||
image.bitmap.data,
|
|
||||||
image.bitmap.width,
|
|
||||||
image.bitmap.height,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function encodeImage({
|
|
||||||
bitmap: bitmapIn,
|
|
||||||
encName,
|
|
||||||
encConfig,
|
|
||||||
optimizerButteraugliTarget,
|
|
||||||
maxOptimizerRounds,
|
|
||||||
}) {
|
|
||||||
let binary;
|
|
||||||
let optionsUsed = encConfig;
|
|
||||||
const encoder = await encoders[encName].enc();
|
|
||||||
if (encConfig === 'auto') {
|
|
||||||
const optionToOptimize = encoders[encName].autoOptimize.option;
|
|
||||||
const decoder = await encoders[encName].dec();
|
|
||||||
const encode = (bitmapIn, quality) =>
|
|
||||||
encoder.encode(
|
|
||||||
bitmapIn.data,
|
|
||||||
bitmapIn.width,
|
|
||||||
bitmapIn.height,
|
|
||||||
Object.assign({}, encoders[encName].defaultEncoderOptions, {
|
|
||||||
[optionToOptimize]: quality,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const decode = (binary) => decoder.decode(binary);
|
|
||||||
const { binary: optimizedBinary, quality } = await autoOptimize(
|
|
||||||
bitmapIn,
|
|
||||||
encode,
|
|
||||||
decode,
|
|
||||||
{
|
|
||||||
min: encoders[encName].autoOptimize.min,
|
|
||||||
max: encoders[encName].autoOptimize.max,
|
|
||||||
butteraugliDistanceGoal: optimizerButteraugliTarget,
|
|
||||||
maxRounds: maxOptimizerRounds,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
binary = optimizedBinary;
|
|
||||||
optionsUsed = {
|
|
||||||
// 5 significant digits is enough
|
|
||||||
[optionToOptimize]: Math.round(quality * 10000) / 10000,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
binary = encoder.encode(
|
|
||||||
bitmapIn.data.buffer,
|
|
||||||
bitmapIn.width,
|
|
||||||
bitmapIn.height,
|
|
||||||
encConfig,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
optionsUsed,
|
|
||||||
binary,
|
|
||||||
extension: encoders[encName].extension,
|
|
||||||
size: binary.length,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// both decoding and encoding go through the worker pool
|
|
||||||
function handleJob(params) {
|
|
||||||
const { operation } = params;
|
|
||||||
switch (operation) {
|
|
||||||
case 'encode':
|
|
||||||
return encodeImage(params);
|
|
||||||
case 'decode':
|
|
||||||
return decodeFile(params);
|
|
||||||
case 'preprocess':
|
|
||||||
return preprocessImage(params);
|
|
||||||
default:
|
|
||||||
throw Error(`Invalid job "${operation}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an ingested image.
|
|
||||||
*/
|
|
||||||
class Image {
|
|
||||||
constructor(workerPool, file) {
|
|
||||||
this.file = file;
|
|
||||||
this.workerPool = workerPool;
|
|
||||||
this.decoded = workerPool.dispatchJob({ operation: 'decode', file });
|
|
||||||
this.encodedWith = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define one or several preprocessors to use on the image.
|
|
||||||
* @param {object} preprocessOptions - An object with preprocessors to use, and their settings.
|
|
||||||
* @returns {Promise<undefined>} - A promise that resolves when all preprocessors have completed their work.
|
|
||||||
*/
|
|
||||||
async preprocess(preprocessOptions = {}) {
|
|
||||||
for (const [name, options] of Object.entries(preprocessOptions)) {
|
|
||||||
if (!Object.keys(preprocessors).includes(name)) {
|
|
||||||
throw Error(`Invalid preprocessor "${name}"`);
|
|
||||||
}
|
|
||||||
const preprocessorOptions = Object.assign(
|
|
||||||
{},
|
|
||||||
preprocessors[name].defaultOptions,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
this.decoded = this.workerPool.dispatchJob({
|
|
||||||
operation: 'preprocess',
|
|
||||||
preprocessorName: name,
|
|
||||||
image: await this.decoded,
|
|
||||||
options: preprocessorOptions,
|
|
||||||
});
|
|
||||||
await this.decoded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define one or several encoders to use on the image.
|
|
||||||
* @param {object} encodeOptions - An object with encoders to use, and their settings.
|
|
||||||
* @returns {Promise<undefined>} - A promise that resolves when the image has been encoded with all the specified encoders.
|
|
||||||
*/
|
|
||||||
async encode(encodeOptions = {}) {
|
|
||||||
const { bitmap } = await this.decoded;
|
|
||||||
for (const [encName, options] of Object.entries(encodeOptions)) {
|
|
||||||
if (!Object.keys(encoders).includes(encName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const encRef = encoders[encName];
|
|
||||||
const encConfig =
|
|
||||||
typeof options === 'string'
|
|
||||||
? options
|
|
||||||
: Object.assign({}, encRef.defaultEncoderOptions, options);
|
|
||||||
this.encodedWith[encName] = this.workerPool.dispatchJob({
|
|
||||||
operation: 'encode',
|
|
||||||
bitmap,
|
|
||||||
encName,
|
|
||||||
encConfig,
|
|
||||||
optimizerButteraugliTarget: Number(
|
|
||||||
encodeOptions.optimizerButteraugliTarget ?? 1.4,
|
|
||||||
),
|
|
||||||
maxOptimizerRounds: Number(encodeOptions.maxOptimizerRounds ?? 6),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await Promise.all(Object.values(this.encodedWith));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A pool where images can be ingested and squooshed.
|
|
||||||
*/
|
|
||||||
class ImagePool {
|
|
||||||
/**
|
|
||||||
* Create a new pool.
|
|
||||||
* @param {number} [threads] - Number of concurrent image processes to run in the pool. Defaults to the number of CPU cores in the system.
|
|
||||||
*/
|
|
||||||
constructor(threads) {
|
|
||||||
this.workerPool = new WorkerPool(threads || cpus().length, __filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ingest an image into the image pool.
|
|
||||||
* @param {string | Buffer | URL | object} image - The image or path to the image that should be ingested and decoded.
|
|
||||||
* @returns {Image} - A custom class reference to the decoded image.
|
|
||||||
*/
|
|
||||||
ingestImage(image) {
|
|
||||||
return new Image(this.workerPool, image);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the underlying image processing pipeline. The already processed images will still be there, but no new processing can start.
|
|
||||||
* @returns {Promise<undefined>} - A promise that resolves when the underlying pipeline has closed.
|
|
||||||
*/
|
|
||||||
async close() {
|
|
||||||
await this.workerPool.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isMainThread) {
|
|
||||||
WorkerPool.useThisThreadAsWorker(handleJob);
|
|
||||||
}
|
|
||||||
308
libsquoosh/src/index.ts
Normal file
308
libsquoosh/src/index.ts
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
import { isMainThread } from 'worker_threads';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AvifEncodeOptions,
|
||||||
|
codecs as encoders,
|
||||||
|
JxlEncodeOptions,
|
||||||
|
MozJPEGEncodeOptions,
|
||||||
|
OxiPngEncodeOptions,
|
||||||
|
preprocessors,
|
||||||
|
QuantOptions,
|
||||||
|
ResizeOptions,
|
||||||
|
RotateOptions,
|
||||||
|
WebPEncodeOptions,
|
||||||
|
WP2EncodeOptions,
|
||||||
|
} from './codecs.js';
|
||||||
|
import WorkerPool from './worker_pool.js';
|
||||||
|
import { autoOptimize } from './auto-optimizer.js';
|
||||||
|
import type ImageData from './image_data';
|
||||||
|
|
||||||
|
export { ImagePool, encoders, preprocessors };
|
||||||
|
type EncoderKey = keyof typeof encoders;
|
||||||
|
type PreprocessorKey = keyof typeof preprocessors;
|
||||||
|
|
||||||
|
type PreprocessOptions = {
|
||||||
|
resize?: Partial<Omit<ResizeOptions, 'width' | 'height'>> &
|
||||||
|
(Pick<ResizeOptions, 'width'> | Pick<ResizeOptions, 'height'>);
|
||||||
|
quant?: Partial<QuantOptions>;
|
||||||
|
rotate?: Partial<RotateOptions>;
|
||||||
|
};
|
||||||
|
type EncodeResult = {
|
||||||
|
optionsUsed: object;
|
||||||
|
binary: Uint8Array;
|
||||||
|
extension: string;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
type EncoderOptions = {
|
||||||
|
mozjpeg?: Partial<MozJPEGEncodeOptions>;
|
||||||
|
webp?: Partial<WebPEncodeOptions>;
|
||||||
|
avif?: Partial<AvifEncodeOptions>;
|
||||||
|
jxl?: Partial<JxlEncodeOptions>;
|
||||||
|
wp2?: Partial<WP2EncodeOptions>;
|
||||||
|
oxipng?: Partial<OxiPngEncodeOptions>;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function decodeFile({
|
||||||
|
file,
|
||||||
|
}: {
|
||||||
|
file: ArrayBuffer | ArrayLike<number>;
|
||||||
|
}): Promise<{ bitmap: ImageData; size: number }> {
|
||||||
|
const array = new Uint8Array(file);
|
||||||
|
const firstChunk = array.slice(0, 16);
|
||||||
|
const firstChunkString = Array.from(firstChunk)
|
||||||
|
.map((v) => String.fromCodePoint(v))
|
||||||
|
.join('');
|
||||||
|
const key = Object.entries(encoders).find(([_name, { detectors }]) =>
|
||||||
|
detectors.some((detector) => detector.exec(firstChunkString)),
|
||||||
|
)?.[0] as EncoderKey | undefined;
|
||||||
|
if (!key) {
|
||||||
|
throw Error(`File has an unsupported format`);
|
||||||
|
}
|
||||||
|
const encoder = encoders[key];
|
||||||
|
const mod = await encoder.dec();
|
||||||
|
const rgba = mod.decode(array);
|
||||||
|
return {
|
||||||
|
bitmap: rgba,
|
||||||
|
size: array.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function preprocessImage({
|
||||||
|
preprocessorName,
|
||||||
|
options,
|
||||||
|
image,
|
||||||
|
}: {
|
||||||
|
preprocessorName: PreprocessorKey;
|
||||||
|
options: any;
|
||||||
|
image: { bitmap: ImageData };
|
||||||
|
}) {
|
||||||
|
const preprocessor = await preprocessors[preprocessorName].instantiate();
|
||||||
|
image.bitmap = await preprocessor(
|
||||||
|
Uint8Array.from(image.bitmap.data),
|
||||||
|
image.bitmap.width,
|
||||||
|
image.bitmap.height,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function encodeImage({
|
||||||
|
bitmap: bitmapIn,
|
||||||
|
encName,
|
||||||
|
encConfig,
|
||||||
|
optimizerButteraugliTarget,
|
||||||
|
maxOptimizerRounds,
|
||||||
|
}: {
|
||||||
|
bitmap: ImageData;
|
||||||
|
encName: EncoderKey;
|
||||||
|
encConfig: any;
|
||||||
|
optimizerButteraugliTarget: number;
|
||||||
|
maxOptimizerRounds: number;
|
||||||
|
}): Promise<EncodeResult> {
|
||||||
|
let binary: Uint8Array;
|
||||||
|
let optionsUsed = encConfig;
|
||||||
|
const encoder = await encoders[encName].enc();
|
||||||
|
if (encConfig === 'auto') {
|
||||||
|
const optionToOptimize = encoders[encName].autoOptimize.option;
|
||||||
|
const decoder = await encoders[encName].dec();
|
||||||
|
const encode = (bitmapIn: ImageData, quality: number) =>
|
||||||
|
encoder.encode(
|
||||||
|
bitmapIn.data,
|
||||||
|
bitmapIn.width,
|
||||||
|
bitmapIn.height,
|
||||||
|
Object.assign({}, encoders[encName].defaultEncoderOptions as any, {
|
||||||
|
[optionToOptimize]: quality,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const decode = (binary: Uint8Array) => decoder.decode(binary);
|
||||||
|
const nonNullEncode = (bitmap: ImageData, quality: number): Uint8Array => {
|
||||||
|
const result = encode(bitmap, quality);
|
||||||
|
if (!result) {
|
||||||
|
throw new Error('There was an error while encoding');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const { binary: optimizedBinary, quality } = await autoOptimize(
|
||||||
|
bitmapIn,
|
||||||
|
nonNullEncode,
|
||||||
|
decode,
|
||||||
|
{
|
||||||
|
min: encoders[encName].autoOptimize.min,
|
||||||
|
max: encoders[encName].autoOptimize.max,
|
||||||
|
butteraugliDistanceGoal: optimizerButteraugliTarget,
|
||||||
|
maxRounds: maxOptimizerRounds,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
binary = optimizedBinary;
|
||||||
|
optionsUsed = {
|
||||||
|
// 5 significant digits is enough
|
||||||
|
[optionToOptimize]: Math.round(quality * 10000) / 10000,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const result = encoder.encode(
|
||||||
|
bitmapIn.data.buffer,
|
||||||
|
bitmapIn.width,
|
||||||
|
bitmapIn.height,
|
||||||
|
encConfig,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error('There was an error while encoding');
|
||||||
|
}
|
||||||
|
|
||||||
|
binary = result;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
optionsUsed,
|
||||||
|
binary,
|
||||||
|
extension: encoders[encName].extension,
|
||||||
|
size: binary.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type EncodeParams = { operation: 'encode' } & Parameters<typeof encodeImage>[0];
|
||||||
|
type DecodeParams = { operation: 'decode' } & Parameters<typeof decodeFile>[0];
|
||||||
|
type PreprocessParams = { operation: 'preprocess' } & Parameters<
|
||||||
|
typeof preprocessImage
|
||||||
|
>[0];
|
||||||
|
type JobMessage = EncodeParams | DecodeParams | PreprocessParams;
|
||||||
|
|
||||||
|
function handleJob(params: JobMessage) {
|
||||||
|
switch (params.operation) {
|
||||||
|
case 'encode':
|
||||||
|
return encodeImage(params);
|
||||||
|
case 'decode':
|
||||||
|
return decodeFile(params);
|
||||||
|
case 'preprocess':
|
||||||
|
return preprocessImage(params);
|
||||||
|
default:
|
||||||
|
throw Error(`Invalid job "${(params as any).operation}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an ingested image.
|
||||||
|
*/
|
||||||
|
class Image {
|
||||||
|
public file: ArrayBuffer | ArrayLike<number>;
|
||||||
|
public workerPool: WorkerPool<JobMessage, any>;
|
||||||
|
public decoded: Promise<{ bitmap: ImageData }>;
|
||||||
|
public encodedWith: { [key in EncoderKey]?: EncodeResult };
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
workerPool: WorkerPool<JobMessage, any>,
|
||||||
|
file: ArrayBuffer | ArrayLike<number>,
|
||||||
|
) {
|
||||||
|
this.file = file;
|
||||||
|
this.workerPool = workerPool;
|
||||||
|
this.decoded = workerPool.dispatchJob({ operation: 'decode', file });
|
||||||
|
this.encodedWith = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define one or several preprocessors to use on the image.
|
||||||
|
* @param {PreprocessOptions} preprocessOptions - An object with preprocessors to use, and their settings.
|
||||||
|
* @returns {Promise<undefined>} - A promise that resolves when all preprocessors have completed their work.
|
||||||
|
*/
|
||||||
|
async preprocess(preprocessOptions: PreprocessOptions = {}) {
|
||||||
|
for (const [name, options] of Object.entries(preprocessOptions)) {
|
||||||
|
if (!Object.keys(preprocessors).includes(name)) {
|
||||||
|
throw Error(`Invalid preprocessor "${name}"`);
|
||||||
|
}
|
||||||
|
const preprocessorName = name as PreprocessorKey;
|
||||||
|
const preprocessorOptions = Object.assign(
|
||||||
|
{},
|
||||||
|
preprocessors[preprocessorName].defaultOptions,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
this.decoded = this.workerPool.dispatchJob({
|
||||||
|
operation: 'preprocess',
|
||||||
|
preprocessorName,
|
||||||
|
image: await this.decoded,
|
||||||
|
options: preprocessorOptions,
|
||||||
|
});
|
||||||
|
await this.decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define one or several encoders to use on the image.
|
||||||
|
* @param {object} encodeOptions - An object with encoders to use, and their settings.
|
||||||
|
* @returns {Promise<{ [key in keyof T]: EncodeResult }>} - A promise that resolves when the image has been encoded with all the specified encoders.
|
||||||
|
*/
|
||||||
|
async encode<T extends EncoderOptions>(
|
||||||
|
encodeOptions: {
|
||||||
|
optimizerButteraugliTarget?: number;
|
||||||
|
maxOptimizerRounds?: number;
|
||||||
|
} & T,
|
||||||
|
): Promise<{ [key in keyof T]: EncodeResult }> {
|
||||||
|
const { bitmap } = await this.decoded;
|
||||||
|
const setEncodedWithPromises = [];
|
||||||
|
for (const [name, options] of Object.entries(encodeOptions)) {
|
||||||
|
if (!Object.keys(encoders).includes(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const encName = name as EncoderKey;
|
||||||
|
const encRef = encoders[encName];
|
||||||
|
const encConfig =
|
||||||
|
typeof options === 'string'
|
||||||
|
? options
|
||||||
|
: Object.assign({}, encRef.defaultEncoderOptions, options);
|
||||||
|
setEncodedWithPromises.push(
|
||||||
|
this.workerPool
|
||||||
|
.dispatchJob({
|
||||||
|
operation: 'encode',
|
||||||
|
bitmap,
|
||||||
|
encName,
|
||||||
|
encConfig,
|
||||||
|
optimizerButteraugliTarget: Number(
|
||||||
|
encodeOptions.optimizerButteraugliTarget ?? 1.4,
|
||||||
|
),
|
||||||
|
maxOptimizerRounds: Number(encodeOptions.maxOptimizerRounds ?? 6),
|
||||||
|
})
|
||||||
|
.then((encodeResult) => {
|
||||||
|
this.encodedWith[encName] = encodeResult;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(setEncodedWithPromises);
|
||||||
|
return this.encodedWith as { [key in keyof T]: EncodeResult };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pool where images can be ingested and squooshed.
|
||||||
|
*/
|
||||||
|
class ImagePool {
|
||||||
|
public workerPool: WorkerPool<JobMessage, any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new pool.
|
||||||
|
* @param {number} [threads] - Number of concurrent image processes to run in the pool.
|
||||||
|
*/
|
||||||
|
constructor(threads: number) {
|
||||||
|
this.workerPool = new WorkerPool(threads, __filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ingest an image into the image pool.
|
||||||
|
* @param {ArrayBuffer | ArrayLike<number>} file - The image that should be ingested and decoded.
|
||||||
|
* @returns {Image} - A custom class reference to the decoded image.
|
||||||
|
*/
|
||||||
|
ingestImage(file: ArrayBuffer | ArrayLike<number>): Image {
|
||||||
|
return new Image(this.workerPool, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the underlying image processing pipeline. The already processed images will still be there, but no new processing can start.
|
||||||
|
* @returns {Promise<void>} - A promise that resolves when the underlying pipeline has closed.
|
||||||
|
*/
|
||||||
|
async close(): Promise<void> {
|
||||||
|
await this.workerPool.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isMainThread) {
|
||||||
|
WorkerPool.useThisThreadAsWorker(handleJob);
|
||||||
|
}
|
||||||
@@ -7,26 +7,19 @@ function uuid() {
|
|||||||
).join('');
|
).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function jobPromise(worker, msg) {
|
interface Job<I> {
|
||||||
return new Promise((resolve, reject) => {
|
msg: I;
|
||||||
const id = uuid();
|
resolve: Function;
|
||||||
worker.postMessage({ msg, id });
|
reject: Function;
|
||||||
worker.on('message', function f({ error, result, id: rid }) {
|
|
||||||
if (rid !== id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
worker.off('message', f);
|
|
||||||
resolve(result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class WorkerPool {
|
export default class WorkerPool<I, O> {
|
||||||
constructor(numWorkers, workerFile) {
|
public numWorkers: number;
|
||||||
|
public jobQueue: TransformStream<Job<I>, Job<I>>;
|
||||||
|
public workerQueue: TransformStream<Worker, Worker>;
|
||||||
|
public done: Promise<void>;
|
||||||
|
|
||||||
|
constructor(numWorkers: number, workerFile: string) {
|
||||||
this.numWorkers = numWorkers;
|
this.numWorkers = numWorkers;
|
||||||
this.jobQueue = new TransformStream();
|
this.jobQueue = new TransformStream();
|
||||||
this.workerQueue = new TransformStream();
|
this.workerQueue = new TransformStream();
|
||||||
@@ -48,9 +41,14 @@ export default class WorkerPool {
|
|||||||
await this._terminateAll();
|
await this._terminateAll();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
throw new Error('Reader did not return any value');
|
||||||
|
}
|
||||||
|
|
||||||
const { msg, resolve, reject } = value;
|
const { msg, resolve, reject } = value;
|
||||||
const worker = await this._nextWorker();
|
const worker = await this._nextWorker();
|
||||||
jobPromise(worker, msg)
|
this.jobPromise(worker, msg)
|
||||||
.then((result) => resolve(result))
|
.then((result) => resolve(result))
|
||||||
.catch((reason) => reject(reason))
|
.catch((reason) => reject(reason))
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -66,6 +64,10 @@ export default class WorkerPool {
|
|||||||
const reader = this.workerQueue.readable.getReader();
|
const reader = this.workerQueue.readable.getReader();
|
||||||
const { value } = await reader.read();
|
const { value } = await reader.read();
|
||||||
reader.releaseLock();
|
reader.releaseLock();
|
||||||
|
if (!value) {
|
||||||
|
throw new Error('No worker left');
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +84,7 @@ export default class WorkerPool {
|
|||||||
await this.done;
|
await this.done;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchJob(msg) {
|
dispatchJob(msg: I): Promise<O> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const writer = this.jobQueue.writable.getWriter();
|
const writer = this.jobQueue.writable.getWriter();
|
||||||
writer.write({ msg, resolve, reject });
|
writer.write({ msg, resolve, reject });
|
||||||
@@ -90,14 +92,32 @@ export default class WorkerPool {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static useThisThreadAsWorker(cb) {
|
private jobPromise(worker: Worker, msg: I) {
|
||||||
parentPort.on('message', async (data) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = uuid();
|
||||||
|
worker.postMessage({ msg, id });
|
||||||
|
worker.on('message', function f({ error, result, id: rid }) {
|
||||||
|
if (rid !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
worker.off('message', f);
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static useThisThreadAsWorker<I, O>(cb: (msg: I) => O) {
|
||||||
|
parentPort!.on('message', async (data) => {
|
||||||
const { msg, id } = data;
|
const { msg, id } = data;
|
||||||
try {
|
try {
|
||||||
const result = await cb(msg);
|
const result = await cb(msg);
|
||||||
parentPort.postMessage({ result, id });
|
parentPort!.postMessage({ result, id });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
parentPort.postMessage({ error: e.message, id });
|
parentPort!.postMessage({ error: e.message, id });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["esnext"],
|
"lib": ["esnext"],
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"allowJs": true
|
"allowJs": true,
|
||||||
|
"declaration": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "../codecs/**/*"]
|
"include": ["src/**/*", "../codecs/**/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
10
missing-types.d.ts
vendored
10
missing-types.d.ts
vendored
@@ -44,6 +44,16 @@ declare module 'data-url:*' {
|
|||||||
export default url;
|
export default url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module 'data-url-text:*' {
|
||||||
|
const url: string;
|
||||||
|
export default url;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'service-worker:*' {
|
||||||
|
const url: string;
|
||||||
|
export default url;
|
||||||
|
}
|
||||||
|
|
||||||
declare var ga: {
|
declare var ga: {
|
||||||
(...args: any[]): void;
|
(...args: any[]): void;
|
||||||
q: any[];
|
q: any[];
|
||||||
|
|||||||
368
package-lock.json
generated
368
package-lock.json
generated
@@ -16,15 +16,15 @@
|
|||||||
"@rollup/plugin-replace": "^2.3.4",
|
"@rollup/plugin-replace": "^2.3.4",
|
||||||
"@surma/rollup-plugin-off-main-thread": "^2.2.2",
|
"@surma/rollup-plugin-off-main-thread": "^2.2.2",
|
||||||
"@types/dedent": "^0.7.0",
|
"@types/dedent": "^0.7.0",
|
||||||
"@types/mime-types": "^2.1.0",
|
"@types/mime-types": "^2.1.1",
|
||||||
"@types/node": "^14.14.7",
|
"@types/node": "^16.11.1",
|
||||||
"@web/rollup-plugin-import-meta-assets": "^1.0.6",
|
"@web/rollup-plugin-import-meta-assets": "^1.0.6",
|
||||||
"comlink": "^4.3.0",
|
"comlink": "^4.3.0",
|
||||||
"cssnano": "^4.1.10",
|
"cssnano": "^4.1.10",
|
||||||
"dedent": "^0.7.0",
|
"dedent": "^0.7.0",
|
||||||
"del": "^5.1.0",
|
"del": "^5.1.0",
|
||||||
"file-drop-element": "^1.0.1",
|
"file-drop-element": "^1.0.1",
|
||||||
"husky": "^4.3.0",
|
"husky": "^7.0.2",
|
||||||
"idb-keyval": "^3.2.0",
|
"idb-keyval": "^3.2.0",
|
||||||
"image-size": "^0.9.3",
|
"image-size": "^0.9.3",
|
||||||
"linkstate": "^2.0.0",
|
"linkstate": "^2.0.0",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"lodash.camelcase": "^4.3.0",
|
"lodash.camelcase": "^4.3.0",
|
||||||
"mime-types": "^2.1.28",
|
"mime-types": "^2.1.28",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"pointer-tracker": "^2.4.0",
|
"pointer-tracker": "^2.5.3",
|
||||||
"postcss": "^7.0.35",
|
"postcss": "^7.0.35",
|
||||||
"postcss-modules": "^3.2.2",
|
"postcss-modules": "^3.2.2",
|
||||||
"postcss-nested": "^4.2.3",
|
"postcss-nested": "^4.2.3",
|
||||||
@@ -40,14 +40,28 @@
|
|||||||
"postcss-url": "^8.0.0",
|
"postcss-url": "^8.0.0",
|
||||||
"preact": "^10.5.5",
|
"preact": "^10.5.5",
|
||||||
"preact-render-to-string": "^5.1.11",
|
"preact-render-to-string": "^5.1.11",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.4.1",
|
||||||
"rollup": "^2.38.0",
|
"rollup": "^2.38.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"serve": "^11.3.2",
|
"serve": "^11.3.2",
|
||||||
"typescript": "^4.1.3",
|
"typescript": "^4.4.4",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"../pointer-tracker": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"extraneous": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"husky": "^4.2.5",
|
||||||
|
"lint-staged": "^10.2.11",
|
||||||
|
"prettier": "^2.0.5",
|
||||||
|
"rollup": "^2.23.1",
|
||||||
|
"rollup-plugin-terser": "^7.0.0",
|
||||||
|
"rollup-plugin-typescript2": "^0.27.2",
|
||||||
|
"typescript": "^3.9.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.10.4",
|
"version": "7.10.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
||||||
@@ -301,9 +315,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/mime-types": {
|
"node_modules/@types/mime-types": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz",
|
||||||
"integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=",
|
"integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/minimatch": {
|
"node_modules/@types/minimatch": {
|
||||||
@@ -313,9 +327,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "14.14.7",
|
"version": "16.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
||||||
"integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==",
|
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/parse-json": {
|
"node_modules/@types/parse-json": {
|
||||||
@@ -852,12 +866,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ci-info": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/clean-stack": {
|
"node_modules/clean-stack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
@@ -1162,12 +1170,6 @@
|
|||||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/compare-versions": {
|
|
||||||
"version": "3.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
|
|
||||||
"integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/compressible": {
|
"node_modules/compressible": {
|
||||||
"version": "2.0.18",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||||
@@ -2270,31 +2272,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/find-up": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"locate-path": "^5.0.0",
|
|
||||||
"path-exists": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/find-versions": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"semver-regex": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@@ -2471,28 +2448,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/husky": {
|
"node_modules/husky": {
|
||||||
"version": "4.3.0",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/husky/-/husky-7.0.2.tgz",
|
||||||
"integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==",
|
"integrity": "sha512-8yKEWNX4z2YsofXAMT7KvA1g8p+GxtB1ffV8XtpAEGuXNAbCV5wdNKH+qTpw8SM9fh4aMPDR+yQuKfgnreyZlg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
|
||||||
"chalk": "^4.0.0",
|
|
||||||
"ci-info": "^2.0.0",
|
|
||||||
"compare-versions": "^3.6.0",
|
|
||||||
"cosmiconfig": "^7.0.0",
|
|
||||||
"find-versions": "^3.2.0",
|
|
||||||
"opencollective-postinstall": "^2.0.2",
|
|
||||||
"pkg-dir": "^4.2.0",
|
|
||||||
"please-upgrade-node": "^3.2.0",
|
|
||||||
"slash": "^3.0.0",
|
|
||||||
"which-pm-runs": "^1.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"husky-run": "bin/run.js",
|
"husky": "lib/bin.js"
|
||||||
"husky-upgrade": "lib/upgrader/bin.js"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/typicode"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/icss-replace-symbols": {
|
"node_modules/icss-replace-symbols": {
|
||||||
@@ -3150,18 +3117,6 @@
|
|||||||
"node": ">=4.0.0"
|
"node": ">=4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/locate-path": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"p-locate": "^4.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lodash.camelcase": {
|
"node_modules/lodash.camelcase": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
@@ -3675,15 +3630,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/opencollective-postinstall": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
|
|
||||||
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"opencollective-postinstall": "index.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/p-finally": {
|
"node_modules/p-finally": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||||
@@ -3693,30 +3639,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/p-limit": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"p-try": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/p-locate": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"p-limit": "^2.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/p-map": {
|
"node_modules/p-map": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
|
||||||
@@ -3729,15 +3651,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/p-try": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@@ -3765,15 +3678,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-exists": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -3849,18 +3753,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pkg-dir": {
|
|
||||||
"version": "4.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
|
||||||
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"find-up": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/please-upgrade-node": {
|
"node_modules/please-upgrade-node": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
|
||||||
@@ -3871,9 +3763,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pointer-tracker": {
|
"node_modules/pointer-tracker": {
|
||||||
"version": "2.4.0",
|
"version": "2.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.5.3.tgz",
|
||||||
"integrity": "sha512-pWI2tpaM/XNtc9mUTv42Rmjf6mkHvE8LT5DDEq0G7baPNhxNM9E3CepubPplSoSLk9E5bwQrAMyDcPVmJyTW4g==",
|
"integrity": "sha512-LiJUeIbzk4dXq678YeyrZ++mdY17q4n/2sBHfU9wIuvmSzdiPgMvmvWN2g8mY4J7YwYOIrqrZUWP/MfFHVwYtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
@@ -7515,9 +7407,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "2.1.2",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
||||||
"integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==",
|
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin-prettier.js"
|
||||||
@@ -7794,15 +7686,6 @@
|
|||||||
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
|
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/semver-regex": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/serialize-javascript": {
|
"node_modules/serialize-javascript": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
||||||
@@ -8588,9 +8471,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.1.3",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||||
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
|
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -8694,12 +8577,6 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/which-pm-runs": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/widest-line": {
|
"node_modules/widest-line": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
|
||||||
@@ -8976,9 +8853,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@surma/rollup-plugin-off-main-thread": {
|
"@surma/rollup-plugin-off-main-thread": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.2.tgz",
|
||||||
"integrity": "sha512-7OU8wfyv18YPWVmecg2/0Jh+pm3lQbvPhIWHd1YQpoxPKPW/vsDNGBaCnMKsZbz29RjgCoXKugAjyagPncgdEw==",
|
"integrity": "sha512-dOD6nGZ79RmWKDRQuC7SOGXMvDkkLwBogu+epfVFMKiy2kOUtLZkb8wV/ettuMt37YJAJKYCKUmxSbZL2LkUQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ejs": "^3.1.6",
|
"ejs": "^3.1.6",
|
||||||
@@ -9026,9 +8903,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/mime-types": {
|
"@types/mime-types": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz",
|
||||||
"integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=",
|
"integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/minimatch": {
|
"@types/minimatch": {
|
||||||
@@ -9038,9 +8915,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "14.14.7",
|
"version": "16.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
||||||
"integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==",
|
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/parse-json": {
|
"@types/parse-json": {
|
||||||
@@ -9480,12 +9357,6 @@
|
|||||||
"supports-color": "^7.1.0"
|
"supports-color": "^7.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ci-info": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"clean-stack": {
|
"clean-stack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
@@ -9739,12 +9610,6 @@
|
|||||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"compare-versions": {
|
|
||||||
"version": "3.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
|
|
||||||
"integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"compressible": {
|
"compressible": {
|
||||||
"version": "2.0.18",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||||
@@ -10662,25 +10527,6 @@
|
|||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"find-up": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"locate-path": "^5.0.0",
|
|
||||||
"path-exists": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"find-versions": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"semver-regex": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@@ -10827,22 +10673,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"version": "4.3.0",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/husky/-/husky-7.0.2.tgz",
|
||||||
"integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==",
|
"integrity": "sha512-8yKEWNX4z2YsofXAMT7KvA1g8p+GxtB1ffV8XtpAEGuXNAbCV5wdNKH+qTpw8SM9fh4aMPDR+yQuKfgnreyZlg==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"requires": {
|
|
||||||
"chalk": "^4.0.0",
|
|
||||||
"ci-info": "^2.0.0",
|
|
||||||
"compare-versions": "^3.6.0",
|
|
||||||
"cosmiconfig": "^7.0.0",
|
|
||||||
"find-versions": "^3.2.0",
|
|
||||||
"opencollective-postinstall": "^2.0.2",
|
|
||||||
"pkg-dir": "^4.2.0",
|
|
||||||
"please-upgrade-node": "^3.2.0",
|
|
||||||
"slash": "^3.0.0",
|
|
||||||
"which-pm-runs": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"icss-replace-symbols": {
|
"icss-replace-symbols": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
@@ -11380,15 +11214,6 @@
|
|||||||
"json5": "^1.0.1"
|
"json5": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"locate-path": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"p-locate": "^4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lodash.camelcase": {
|
"lodash.camelcase": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
@@ -11801,36 +11626,12 @@
|
|||||||
"mimic-fn": "^2.1.0"
|
"mimic-fn": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"opencollective-postinstall": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
|
|
||||||
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"p-finally": {
|
"p-finally": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||||
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
|
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"p-limit": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"p-try": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"p-locate": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"p-limit": "^2.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"p-map": {
|
"p-map": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
|
||||||
@@ -11840,12 +11641,6 @@
|
|||||||
"aggregate-error": "^3.0.0"
|
"aggregate-error": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"p-try": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"parent-module": {
|
"parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@@ -11867,12 +11662,6 @@
|
|||||||
"lines-and-columns": "^1.1.6"
|
"lines-and-columns": "^1.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"path-exists": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"path-is-absolute": {
|
"path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -11927,15 +11716,6 @@
|
|||||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
|
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pkg-dir": {
|
|
||||||
"version": "4.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
|
||||||
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"find-up": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"please-upgrade-node": {
|
"please-upgrade-node": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
|
||||||
@@ -11946,9 +11726,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pointer-tracker": {
|
"pointer-tracker": {
|
||||||
"version": "2.4.0",
|
"version": "2.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.5.3.tgz",
|
||||||
"integrity": "sha512-pWI2tpaM/XNtc9mUTv42Rmjf6mkHvE8LT5DDEq0G7baPNhxNM9E3CepubPplSoSLk9E5bwQrAMyDcPVmJyTW4g==",
|
"integrity": "sha512-LiJUeIbzk4dXq678YeyrZ++mdY17q4n/2sBHfU9wIuvmSzdiPgMvmvWN2g8mY4J7YwYOIrqrZUWP/MfFHVwYtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
@@ -15015,9 +14795,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "2.1.2",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
||||||
"integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==",
|
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pretty-format": {
|
"pretty-format": {
|
||||||
@@ -15243,12 +15023,6 @@
|
|||||||
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
|
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"semver-regex": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"serialize-javascript": {
|
"serialize-javascript": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
||||||
@@ -15903,9 +15677,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.1.3",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||||
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
|
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uniq": {
|
"uniq": {
|
||||||
@@ -15980,9 +15754,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"wasm-feature-detect": {
|
"wasm-feature-detect": {
|
||||||
"version": "1.2.9",
|
"version": "1.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.9.tgz",
|
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
|
||||||
"integrity": "sha512-2E9/gtLVLpv2wnZDyYv8WY2dR9gHbmyv5uhZsnOcMSzqc78aGZpKQORPNcnrPwAU23nFUo7GAwKuoTAWRgsJ7Q=="
|
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
|
||||||
},
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
@@ -15993,12 +15767,6 @@
|
|||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"which-pm-runs": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"widest-line": {
|
"widest-line": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
|
||||||
|
|||||||
24
package.json
24
package.json
@@ -8,23 +8,24 @@
|
|||||||
"debug": "node --inspect-brk node_modules/.bin/rollup -c",
|
"debug": "node --inspect-brk node_modules/.bin/rollup -c",
|
||||||
"dev": "DEV_PORT=\"${DEV_PORT:=5000}\" run-p watch serve",
|
"dev": "DEV_PORT=\"${DEV_PORT:=5000}\" run-p watch serve",
|
||||||
"watch": "rollup -cw",
|
"watch": "rollup -cw",
|
||||||
"serve": "serve --listen=$DEV_PORT --config ../../../serve.json .tmp/build/static"
|
"serve": "serve --listen=$DEV_PORT --config ../../../serve.json .tmp/build/static",
|
||||||
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^17.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^11.1.0",
|
"@rollup/plugin-node-resolve": "^11.1.0",
|
||||||
"@rollup/plugin-replace": "^2.3.4",
|
"@rollup/plugin-replace": "^2.3.4",
|
||||||
"@surma/rollup-plugin-off-main-thread": "^2.2.1",
|
"@surma/rollup-plugin-off-main-thread": "^2.2.2",
|
||||||
"@types/dedent": "^0.7.0",
|
"@types/dedent": "^0.7.0",
|
||||||
"@types/mime-types": "^2.1.0",
|
"@types/mime-types": "^2.1.1",
|
||||||
"@types/node": "^14.14.7",
|
"@types/node": "^16.11.1",
|
||||||
"@web/rollup-plugin-import-meta-assets": "^1.0.6",
|
"@web/rollup-plugin-import-meta-assets": "^1.0.6",
|
||||||
"comlink": "^4.3.0",
|
"comlink": "^4.3.0",
|
||||||
"cssnano": "^4.1.10",
|
"cssnano": "^4.1.10",
|
||||||
"dedent": "^0.7.0",
|
"dedent": "^0.7.0",
|
||||||
"del": "^5.1.0",
|
"del": "^5.1.0",
|
||||||
"file-drop-element": "^1.0.1",
|
"file-drop-element": "^1.0.1",
|
||||||
"husky": "^4.3.0",
|
"husky": "^7.0.2",
|
||||||
"idb-keyval": "^3.2.0",
|
"idb-keyval": "^3.2.0",
|
||||||
"image-size": "^0.9.3",
|
"image-size": "^0.9.3",
|
||||||
"linkstate": "^2.0.0",
|
"linkstate": "^2.0.0",
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
"lodash.camelcase": "^4.3.0",
|
"lodash.camelcase": "^4.3.0",
|
||||||
"mime-types": "^2.1.28",
|
"mime-types": "^2.1.28",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"pointer-tracker": "^2.4.0",
|
"pointer-tracker": "^2.5.3",
|
||||||
"postcss": "^7.0.35",
|
"postcss": "^7.0.35",
|
||||||
"postcss-modules": "^3.2.2",
|
"postcss-modules": "^3.2.2",
|
||||||
"postcss-nested": "^4.2.3",
|
"postcss-nested": "^4.2.3",
|
||||||
@@ -40,24 +41,19 @@
|
|||||||
"postcss-url": "^8.0.0",
|
"postcss-url": "^8.0.0",
|
||||||
"preact": "^10.5.5",
|
"preact": "^10.5.5",
|
||||||
"preact-render-to-string": "^5.1.11",
|
"preact-render-to-string": "^5.1.11",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.4.1",
|
||||||
"rollup": "^2.38.0",
|
"rollup": "^2.38.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"serve": "^11.3.2",
|
"serve": "^11.3.2",
|
||||||
"typescript": "^4.1.3",
|
"typescript": "^4.4.4",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
},
|
},
|
||||||
"husky": {
|
|
||||||
"hooks": {
|
|
||||||
"pre-commit": "lint-staged"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,css,json,md,ts,tsx}": "prettier --write",
|
"*.{js,css,json,md,ts,tsx}": "prettier --write",
|
||||||
"*.{c,h,cpp,hpp}": "clang-format -i",
|
"*.{c,h,cpp,hpp}": "clang-format -i",
|
||||||
"*.rs": "rustfmt"
|
"*.rs": "rustfmt"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wasm-feature-detect": "^1.2.9"
|
"wasm-feature-detect": "^1.2.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import commonjs from '@rollup/plugin-commonjs';
|
|||||||
import { terser } from 'rollup-plugin-terser';
|
import { terser } from 'rollup-plugin-terser';
|
||||||
import OMT from '@surma/rollup-plugin-off-main-thread';
|
import OMT from '@surma/rollup-plugin-off-main-thread';
|
||||||
import replace from '@rollup/plugin-replace';
|
import replace from '@rollup/plugin-replace';
|
||||||
|
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
|
||||||
|
|
||||||
import simpleTS from './lib/simple-ts';
|
import simpleTS from './lib/simple-ts';
|
||||||
import clientBundlePlugin from './lib/client-bundle-plugin';
|
import clientBundlePlugin from './lib/client-bundle-plugin';
|
||||||
@@ -31,11 +32,11 @@ import featurePlugin from './lib/feature-plugin';
|
|||||||
import initialCssPlugin from './lib/initial-css-plugin';
|
import initialCssPlugin from './lib/initial-css-plugin';
|
||||||
import serviceWorkerPlugin from './lib/sw-plugin';
|
import serviceWorkerPlugin from './lib/sw-plugin';
|
||||||
import dataURLPlugin from './lib/data-url-plugin';
|
import dataURLPlugin from './lib/data-url-plugin';
|
||||||
|
import entryDataPlugin, { fileNameToURL } from './lib/entry-data-plugin';
|
||||||
import dedent from 'dedent';
|
import dedent from 'dedent';
|
||||||
import entryDataPlugin from './lib/entry-data-plugin';
|
|
||||||
|
|
||||||
function resolveFileUrl({ fileName }) {
|
function resolveFileUrl({ fileName }) {
|
||||||
return JSON.stringify(fileName.replace(/^static\//, '/'));
|
return JSON.stringify(fileNameToURL(fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveImportMetaUrlInStaticBuild(property, { moduleId }) {
|
function resolveImportMetaUrlInStaticBuild(property, { moduleId }) {
|
||||||
@@ -70,7 +71,7 @@ export default async function ({ watch }) {
|
|||||||
|
|
||||||
await del('.tmp/build');
|
await del('.tmp/build');
|
||||||
|
|
||||||
const isProduction = !watch && !process.env.DEBUG;
|
const isProduction = !watch;
|
||||||
|
|
||||||
const tsPluginInstance = simpleTS('.', {
|
const tsPluginInstance = simpleTS('.', {
|
||||||
watch,
|
watch,
|
||||||
@@ -118,6 +119,7 @@ export default async function ({ watch }) {
|
|||||||
plugins: [
|
plugins: [
|
||||||
{ resolveFileUrl },
|
{ resolveFileUrl },
|
||||||
OMT({ loader: await omtLoaderPromise }),
|
OMT({ loader: await omtLoaderPromise }),
|
||||||
|
importMetaAssets(),
|
||||||
serviceWorkerPlugin({
|
serviceWorkerPlugin({
|
||||||
output: 'static/serviceworker.js',
|
output: 'static/serviceworker.js',
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -37,8 +37,10 @@ main();
|
|||||||
ga('set', 'transport', 'beacon');
|
ga('set', 'transport', 'beacon');
|
||||||
ga('set', 'dimension1', displayMode);
|
ga('set', 'dimension1', displayMode);
|
||||||
ga('send', 'pageview', '/index.html', { title: 'Squoosh' });
|
ga('send', 'pageview', '/index.html', { title: 'Squoosh' });
|
||||||
// Load the GA script
|
// Load the GA script without keeping the browser spinner going.
|
||||||
|
addEventListener('load', () => {
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://www.google-analytics.com/analytics.js';
|
script.src = 'https://www.google-analytics.com/analytics.js';
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ export default class Checkbox extends Component<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<div class={style.checkbox}>
|
<div class={style.checkbox}>
|
||||||
{props.checked ? (
|
{props.checked ? (
|
||||||
|
props.disabled ? (
|
||||||
|
<CheckedIcon class={`${style.icon} ${style.disabled}`} />
|
||||||
|
) : (
|
||||||
<CheckedIcon class={`${style.icon} ${style.checked}`} />
|
<CheckedIcon class={`${style.icon} ${style.checked}`} />
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<UncheckedIcon class={style.icon} />
|
<UncheckedIcon class={style.icon} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -20,3 +20,7 @@
|
|||||||
.checked {
|
.checked {
|
||||||
fill: var(--main-theme-color);
|
fill: var(--main-theme-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
fill: var(--dark-gray);
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const REFLECTED_ATTRIBUTES = [
|
|||||||
'disabled',
|
'disabled',
|
||||||
];
|
];
|
||||||
|
|
||||||
function getPrescision(value: string): number {
|
function getPrecision(value: string): number {
|
||||||
const afterDecimal = value.split('.')[1];
|
const afterDecimal = value.split('.')[1];
|
||||||
return afterDecimal ? afterDecimal.length : 0;
|
return afterDecimal ? afterDecimal.length : 0;
|
||||||
}
|
}
|
||||||
@@ -112,18 +112,24 @@ class RangeInputElement extends HTMLElement {
|
|||||||
this.dispatchEvent(retargetted);
|
this.dispatchEvent(retargetted);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _getDisplayValue(value: number): string {
|
||||||
|
if (value >= 10000) return (value / 1000).toFixed(1) + 'k';
|
||||||
|
|
||||||
|
const labelPrecision =
|
||||||
|
Number(this.labelPrecision) || getPrecision(this.step) || 0;
|
||||||
|
return labelPrecision
|
||||||
|
? value.toFixed(labelPrecision)
|
||||||
|
: Math.round(value).toString();
|
||||||
|
}
|
||||||
|
|
||||||
private _update = () => {
|
private _update = () => {
|
||||||
// Not connected?
|
// Not connected?
|
||||||
if (!this._valueDisplay) return;
|
if (!this._valueDisplay) return;
|
||||||
const value = Number(this.value) || 0;
|
const value = Number(this.value) || 0;
|
||||||
const min = Number(this.min) || 0;
|
const min = Number(this.min) || 0;
|
||||||
const max = Number(this.max) || 100;
|
const max = Number(this.max) || 100;
|
||||||
const labelPrecision =
|
|
||||||
Number(this.labelPrecision) || getPrescision(this.step) || 0;
|
|
||||||
const percent = (100 * (value - min)) / (max - min);
|
const percent = (100 * (value - min)) / (max - min);
|
||||||
const displayValue = labelPrecision
|
const displayValue = this._getDisplayValue(value);
|
||||||
? value.toFixed(labelPrecision)
|
|
||||||
: Math.round(value).toString();
|
|
||||||
|
|
||||||
this._valueDisplay!.textContent = displayValue;
|
this._valueDisplay!.textContent = displayValue;
|
||||||
this.style.setProperty('--value-percent', percent + '%');
|
this.style.setProperty('--value-percent', percent + '%');
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
text-decoration-style: dotted;
|
text-decoration-style: dotted;
|
||||||
text-decoration-color: var(--main-theme-color);
|
text-decoration-color: var(--main-theme-color);
|
||||||
text-underline-position: under;
|
text-underline-position: under;
|
||||||
width: 48px;
|
width: 54px;
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,8 @@ type PartialButNotUndefined<T> = {
|
|||||||
[P in keyof T]: T[P];
|
[P in keyof T]: T[P];
|
||||||
};
|
};
|
||||||
|
|
||||||
const supportedEncoderMapP: Promise<PartialButNotUndefined<
|
const supportedEncoderMapP: Promise<PartialButNotUndefined<typeof encoderMap>> =
|
||||||
typeof encoderMap
|
(async () => {
|
||||||
>> = (async () => {
|
|
||||||
const supportedEncoderMap: PartialButNotUndefined<typeof encoderMap> = {
|
const supportedEncoderMap: PartialButNotUndefined<typeof encoderMap> = {
|
||||||
...encoderMap,
|
...encoderMap,
|
||||||
};
|
};
|
||||||
@@ -57,7 +56,7 @@ const supportedEncoderMapP: Promise<PartialButNotUndefined<
|
|||||||
);
|
);
|
||||||
|
|
||||||
return supportedEncoderMap;
|
return supportedEncoderMap;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
export default class Options extends Component<Props, State> {
|
export default class Options extends Component<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
.options-scroller {
|
.options-scroller {
|
||||||
--horizontal-padding: 15px;
|
--horizontal-padding: 15px;
|
||||||
border-radius: var(--scroller-radius);
|
border-radius: var(--scroller-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
/* At smaller widths, the multi-panel handles the scrolling */
|
/* At smaller widths, the multi-panel handles the scrolling */
|
||||||
@media (min-width: 600px) {
|
@media (min-width: 600px) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import PointerTracker, { Pointer } from 'pointer-tracker';
|
import PointerTracker, { Pointer } from 'pointer-tracker';
|
||||||
import 'add-css:./styles.css';
|
import 'add-css:./styles.css';
|
||||||
|
import { isSafari } from 'client/lazy-app/util';
|
||||||
|
|
||||||
interface Point {
|
interface Point {
|
||||||
clientX: number;
|
clientX: number;
|
||||||
@@ -81,6 +82,7 @@ function createPoint(): SVGPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MIN_SCALE = 0.01;
|
const MIN_SCALE = 0.01;
|
||||||
|
const MAX_SCALE = 100000;
|
||||||
|
|
||||||
export default class PinchZoom extends HTMLElement {
|
export default class PinchZoom extends HTMLElement {
|
||||||
// The element that we'll transform.
|
// The element that we'll transform.
|
||||||
@@ -104,14 +106,23 @@ export default class PinchZoom extends HTMLElement {
|
|||||||
const pointerTracker: PointerTracker = new PointerTracker(this, {
|
const pointerTracker: PointerTracker = new PointerTracker(this, {
|
||||||
start: (pointer, event) => {
|
start: (pointer, event) => {
|
||||||
// We only want to track 2 pointers at most
|
// We only want to track 2 pointers at most
|
||||||
if (pointerTracker.currentPointers.length === 2 || !this._positioningEl)
|
if (
|
||||||
|
pointerTracker.currentPointers.length === 2 ||
|
||||||
|
!this._positioningEl
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
move: (previousPointers) => {
|
move: (previousPointers) => {
|
||||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
||||||
},
|
},
|
||||||
|
// Unfortunately Safari on iOS has a bug where pointer event capturing
|
||||||
|
// doesn't work in some cases, and we hit those cases due to our event
|
||||||
|
// retargeting in pinch-zoom.
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=220196
|
||||||
|
avoidPointerEvents: isSafari,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addEventListener('wheel', (event) => this._onWheel(event));
|
this.addEventListener('wheel', (event) => this._onWheel(event));
|
||||||
@@ -244,6 +255,9 @@ export default class PinchZoom extends HTMLElement {
|
|||||||
// Avoid scaling to zero
|
// Avoid scaling to zero
|
||||||
if (scale < MIN_SCALE) return;
|
if (scale < MIN_SCALE) return;
|
||||||
|
|
||||||
|
// Avoid scaling to very large values
|
||||||
|
if (scale > MAX_SCALE) return;
|
||||||
|
|
||||||
// Return if there's no change
|
// Return if there's no change
|
||||||
if (scale === this.scale && x === this.x && y === this.y) return;
|
if (scale === this.scale && x === this.x && y === this.y) return;
|
||||||
|
|
||||||
@@ -296,9 +310,13 @@ export default class PinchZoom extends HTMLElement {
|
|||||||
deltaY *= 15;
|
deltaY *= 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const zoomingOut = deltaY > 0;
|
||||||
|
|
||||||
// ctrlKey is true when pinch-zooming on a trackpad.
|
// ctrlKey is true when pinch-zooming on a trackpad.
|
||||||
const divisor = ctrlKey ? 100 : 300;
|
const divisor = ctrlKey ? 100 : 300;
|
||||||
const scaleDiff = 1 - deltaY / divisor;
|
// when zooming out, invert the delta and the ratio to keep zoom stable
|
||||||
|
const ratio = 1 - (zoomingOut ? -deltaY : deltaY) / divisor;
|
||||||
|
const scaleDiff = zoomingOut ? 1 / ratio : ratio;
|
||||||
|
|
||||||
this._applyChange({
|
this._applyChange({
|
||||||
scaleDiff,
|
scaleDiff,
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scrubber {
|
.scrubber {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@@ -54,10 +55,6 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
|
|||||||
padding: 0 calc(var(--thumb-size) * 0.24);
|
padding: 0 calc(var(--thumb-size) * 0.24);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrubber svg {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-left {
|
.arrow-left {
|
||||||
fill: var(--pink);
|
fill: var(--pink);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import './custom-els/PinchZoom';
|
|||||||
import './custom-els/TwoUp';
|
import './custom-els/TwoUp';
|
||||||
import * as style from './style.css';
|
import * as style from './style.css';
|
||||||
import 'add-css:./style.css';
|
import 'add-css:./style.css';
|
||||||
import { shallowEqual } from '../../util';
|
import { shallowEqual, isSafari } from '../../util';
|
||||||
import {
|
import {
|
||||||
|
ToggleAliasingIcon,
|
||||||
|
ToggleAliasingActiveIcon,
|
||||||
ToggleBackgroundIcon,
|
ToggleBackgroundIcon,
|
||||||
AddIcon,
|
AddIcon,
|
||||||
RemoveIcon,
|
RemoveIcon,
|
||||||
@@ -19,7 +21,6 @@ import { cleanSet } from '../../util/clean-modify';
|
|||||||
import type { SourceImage } from '../../Compress';
|
import type { SourceImage } from '../../Compress';
|
||||||
import { linkRef } from 'shared/prerendered-app/util';
|
import { linkRef } from 'shared/prerendered-app/util';
|
||||||
import { drawDataToCanvas } from 'client/lazy-app/util/canvas';
|
import { drawDataToCanvas } from 'client/lazy-app/util/canvas';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
source?: SourceImage;
|
source?: SourceImage;
|
||||||
preprocessorState?: PreprocessorState;
|
preprocessorState?: PreprocessorState;
|
||||||
@@ -35,6 +36,7 @@ interface State {
|
|||||||
scale: number;
|
scale: number;
|
||||||
editingScale: boolean;
|
editingScale: boolean;
|
||||||
altBackground: boolean;
|
altBackground: boolean;
|
||||||
|
aliasing: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scaleToOpts: ScaleToOpts = {
|
const scaleToOpts: ScaleToOpts = {
|
||||||
@@ -49,6 +51,7 @@ export default class Output extends Component<Props, State> {
|
|||||||
scale: 1,
|
scale: 1,
|
||||||
editingScale: false,
|
editingScale: false,
|
||||||
altBackground: false,
|
altBackground: false,
|
||||||
|
aliasing: false,
|
||||||
};
|
};
|
||||||
canvasLeft?: HTMLCanvasElement;
|
canvasLeft?: HTMLCanvasElement;
|
||||||
canvasRight?: HTMLCanvasElement;
|
canvasRight?: HTMLCanvasElement;
|
||||||
@@ -145,6 +148,12 @@ export default class Output extends Component<Props, State> {
|
|||||||
return props.rightCompressed || (props.source && props.source.preprocessed);
|
return props.rightCompressed || (props.source && props.source.preprocessed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleAliasing = () => {
|
||||||
|
this.setState((state) => ({
|
||||||
|
aliasing: !state.aliasing,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
private toggleBackground = () => {
|
private toggleBackground = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
altBackground: !this.state.altBackground,
|
altBackground: !this.state.altBackground,
|
||||||
@@ -255,7 +264,7 @@ export default class Output extends Component<Props, State> {
|
|||||||
|
|
||||||
render(
|
render(
|
||||||
{ mobileView, leftImgContain, rightImgContain, source }: Props,
|
{ mobileView, leftImgContain, rightImgContain, source }: Props,
|
||||||
{ scale, editingScale, altBackground }: State,
|
{ scale, editingScale, altBackground, aliasing }: State,
|
||||||
) {
|
) {
|
||||||
const leftDraw = this.leftDrawable();
|
const leftDraw = this.leftDrawable();
|
||||||
const rightDraw = this.rightDrawable();
|
const rightDraw = this.rightDrawable();
|
||||||
@@ -275,7 +284,11 @@ export default class Output extends Component<Props, State> {
|
|||||||
onTouchStartCapture={this.onRetargetableEvent}
|
onTouchStartCapture={this.onRetargetableEvent}
|
||||||
onTouchEndCapture={this.onRetargetableEvent}
|
onTouchEndCapture={this.onRetargetableEvent}
|
||||||
onTouchMoveCapture={this.onRetargetableEvent}
|
onTouchMoveCapture={this.onRetargetableEvent}
|
||||||
onPointerDownCapture={this.onRetargetableEvent}
|
onPointerDownCapture={
|
||||||
|
// We avoid pointer events in our PinchZoom due to a Safari bug.
|
||||||
|
// That means we also need to avoid them here too, else we end up preventing the fallback mouse events.
|
||||||
|
isSafari ? undefined : this.onRetargetableEvent
|
||||||
|
}
|
||||||
onMouseDownCapture={this.onRetargetableEvent}
|
onMouseDownCapture={this.onRetargetableEvent}
|
||||||
onWheelCapture={this.onRetargetableEvent}
|
onWheelCapture={this.onRetargetableEvent}
|
||||||
>
|
>
|
||||||
@@ -285,7 +298,9 @@ export default class Output extends Component<Props, State> {
|
|||||||
ref={linkRef(this, 'pinchZoomLeft')}
|
ref={linkRef(this, 'pinchZoomLeft')}
|
||||||
>
|
>
|
||||||
<canvas
|
<canvas
|
||||||
class={style.pinchTarget}
|
class={`${style.pinchTarget} ${
|
||||||
|
aliasing ? style.pixelated : ''
|
||||||
|
}`}
|
||||||
ref={linkRef(this, 'canvasLeft')}
|
ref={linkRef(this, 'canvasLeft')}
|
||||||
width={leftDraw && leftDraw.width}
|
width={leftDraw && leftDraw.width}
|
||||||
height={leftDraw && leftDraw.height}
|
height={leftDraw && leftDraw.height}
|
||||||
@@ -301,7 +316,9 @@ export default class Output extends Component<Props, State> {
|
|||||||
ref={linkRef(this, 'pinchZoomRight')}
|
ref={linkRef(this, 'pinchZoomRight')}
|
||||||
>
|
>
|
||||||
<canvas
|
<canvas
|
||||||
class={style.pinchTarget}
|
class={`${style.pinchTarget} ${
|
||||||
|
aliasing ? style.pixelated : ''
|
||||||
|
}`}
|
||||||
ref={linkRef(this, 'canvasRight')}
|
ref={linkRef(this, 'canvasRight')}
|
||||||
width={rightDraw && rightDraw.width}
|
width={rightDraw && rightDraw.width}
|
||||||
height={rightDraw && rightDraw.height}
|
height={rightDraw && rightDraw.height}
|
||||||
@@ -345,10 +362,31 @@ export default class Output extends Component<Props, State> {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class={style.buttonGroup}>
|
<div class={style.buttonGroup}>
|
||||||
<button class={style.firstButton} onClick={this.onRotateClick}>
|
<button
|
||||||
|
class={style.firstButton}
|
||||||
|
onClick={this.onRotateClick}
|
||||||
|
title="Rotate"
|
||||||
|
>
|
||||||
<RotateIcon />
|
<RotateIcon />
|
||||||
</button>
|
</button>
|
||||||
<button class={style.lastButton} onClick={this.toggleBackground}>
|
{!isSafari && (
|
||||||
|
<button
|
||||||
|
class={style.button}
|
||||||
|
onClick={this.toggleAliasing}
|
||||||
|
title="Toggle smoothing"
|
||||||
|
>
|
||||||
|
{aliasing ? (
|
||||||
|
<ToggleAliasingActiveIcon />
|
||||||
|
) : (
|
||||||
|
<ToggleAliasingIcon />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
class={style.lastButton}
|
||||||
|
onClick={this.toggleBackground}
|
||||||
|
title="Toggle background"
|
||||||
|
>
|
||||||
{altBackground ? (
|
{altBackground ? (
|
||||||
<ToggleBackgroundActiveIcon />
|
<ToggleBackgroundActiveIcon />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
padding: 9px 66px;
|
padding: 9px 66px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
/* Allow clicks to fall through to the pinch zoom area */
|
/* Allow clicks to fall through to the pinch zoom area */
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -68,7 +69,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
margin: 0 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button,
|
.button,
|
||||||
@@ -86,8 +86,7 @@
|
|||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:focus {
|
&:focus-visible {
|
||||||
/* box-shadow: 0 0 0 2px var(--hot-pink); */
|
|
||||||
box-shadow: 0 0 0 2px #fff;
|
box-shadow: 0 0 0 2px #fff;
|
||||||
outline: none;
|
outline: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@@ -96,6 +95,7 @@
|
|||||||
|
|
||||||
.button {
|
.button {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(50, 50, 50, 0.92);
|
background: rgba(50, 50, 50, 0.92);
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
.last-button {
|
.last-button {
|
||||||
composes: button;
|
composes: button;
|
||||||
border-radius: 0 6px 6px 0;
|
border-radius: 0 6px 6px 0;
|
||||||
border-left-width: 1px;
|
border-right-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom {
|
.zoom {
|
||||||
@@ -161,3 +161,8 @@ input.zoom {
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pixelated {
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|||||||
@@ -114,9 +114,9 @@ async function decodeImage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise fall through and try built-in decoding for a laugh.
|
// Otherwise fall through and try built-in decoding for a laugh.
|
||||||
return await builtinDecode(signal, blob, mimeType);
|
return await builtinDecode(signal, blob);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.name === 'AbortError') throw err;
|
if (err instanceof Error && err.name === 'AbortError') throw err;
|
||||||
console.log(err);
|
console.log(err);
|
||||||
throw Error("Couldn't decode image");
|
throw Error("Couldn't decode image");
|
||||||
}
|
}
|
||||||
@@ -481,7 +481,7 @@ export default class Compress extends Component<Props, State> {
|
|||||||
open('https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli');
|
open('https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.props.showSnack(e);
|
this.props.showSnack(String(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -640,7 +640,7 @@ export default class Compress extends Component<Props, State> {
|
|||||||
return { sides };
|
return { sides };
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.name === 'AbortError') return;
|
if (err instanceof Error && err.name === 'AbortError') return;
|
||||||
this.props.showSnack(`Source decoding error: ${err}`);
|
this.props.showSnack(`Source decoding error: ${err}`);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -698,7 +698,7 @@ export default class Compress extends Component<Props, State> {
|
|||||||
return newState;
|
return newState;
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.name === 'AbortError') return;
|
if (err instanceof Error && err.name === 'AbortError') return;
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
this.props.showSnack(`Preprocessing error: ${err}`);
|
this.props.showSnack(`Preprocessing error: ${err}`);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -822,7 +822,7 @@ export default class Compress extends Component<Props, State> {
|
|||||||
|
|
||||||
this.activeSideJobs[sideIndex] = undefined;
|
this.activeSideJobs[sideIndex] = undefined;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.name === 'AbortError') return;
|
if (err instanceof Error && err.name === 'AbortError') return;
|
||||||
this.setState((currentState) => {
|
this.setState((currentState) => {
|
||||||
const sides = cleanMerge(currentState.sides, sideIndex, {
|
const sides = cleanMerge(currentState.sides, sideIndex, {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|||||||
@@ -11,6 +11,25 @@ const Icon = (props: preact.JSX.HTMLAttributes) => (
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const ToggleAliasingIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||||
|
<Icon {...props}>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="8"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ToggleAliasingActiveIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||||
|
<Icon {...props}>
|
||||||
|
<path d="M12 3h5v2h2v2h2v5h-2V9h-2V7h-2V5h-3V3M21 12v5h-2v2h-2v2h-5v-2h3v-2h2v-2h2v-3h2M12 21H7v-2H5v-2H3v-5h2v3h2v2h2v2h3v2M3 12V7h2V5h2V3h5v2H9v2H7v2H5v3H3" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
|
||||||
export const ToggleBackgroundIcon = (props: preact.JSX.HTMLAttributes) => (
|
export const ToggleBackgroundIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||||
<Icon {...props}>
|
<Icon {...props}>
|
||||||
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.9 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z" />
|
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.9 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z" />
|
||||||
|
|||||||
@@ -63,35 +63,17 @@ interface DrawableToImageDataOptions {
|
|||||||
sh?: number;
|
sh?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWidth(
|
|
||||||
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
|
|
||||||
): number {
|
|
||||||
if ('displayWidth' in drawable) {
|
|
||||||
return drawable.displayWidth;
|
|
||||||
}
|
|
||||||
return drawable.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHeight(
|
|
||||||
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
|
|
||||||
): number {
|
|
||||||
if ('displayHeight' in drawable) {
|
|
||||||
return drawable.displayHeight;
|
|
||||||
}
|
|
||||||
return drawable.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawableToImageData(
|
export function drawableToImageData(
|
||||||
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
|
drawable: ImageBitmap | HTMLImageElement,
|
||||||
opts: DrawableToImageDataOptions = {},
|
opts: DrawableToImageDataOptions = {},
|
||||||
): ImageData {
|
): ImageData {
|
||||||
const {
|
const {
|
||||||
width = getWidth(drawable),
|
width = drawable.width,
|
||||||
height = getHeight(drawable),
|
height = drawable.height,
|
||||||
sx = 0,
|
sx = 0,
|
||||||
sy = 0,
|
sy = 0,
|
||||||
sw = getWidth(drawable),
|
sw = drawable.width,
|
||||||
sh = getHeight(drawable),
|
sh = drawable.height,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
// Make canvas same size as image
|
// Make canvas same size as image
|
||||||
|
|||||||
@@ -10,10 +10,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as WebCodecs from '../util/web-codecs';
|
|
||||||
import { drawableToImageData } from './canvas';
|
import { drawableToImageData } from './canvas';
|
||||||
|
|
||||||
|
/** If render engine is Safari */
|
||||||
|
export const isSafari =
|
||||||
|
/Safari\//.test(navigator.userAgent) &&
|
||||||
|
!/Chrom(e|ium)\//.test(navigator.userAgent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two objects, returning a boolean indicating if
|
* Compare two objects, returning a boolean indicating if
|
||||||
* they have the same properties and strictly equal values.
|
* they have the same properties and strictly equal values.
|
||||||
@@ -134,15 +137,7 @@ export async function blobToImg(blob: Blob): Promise<HTMLImageElement> {
|
|||||||
export async function builtinDecode(
|
export async function builtinDecode(
|
||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
blob: Blob,
|
blob: Blob,
|
||||||
mimeType: string,
|
|
||||||
): Promise<ImageData> {
|
): Promise<ImageData> {
|
||||||
// If WebCodecs are supported, use that.
|
|
||||||
if (await WebCodecs.isTypeSupported(mimeType)) {
|
|
||||||
assertSignal(signal);
|
|
||||||
try {
|
|
||||||
return await abortable(signal, WebCodecs.decode(blob, mimeType));
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
assertSignal(signal);
|
assertSignal(signal);
|
||||||
|
|
||||||
// Prefer createImageBitmap as it's the off-thread option for Firefox.
|
// Prefer createImageBitmap as it's the off-thread option for Firefox.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user