Compare commits

..

125 Commits

Author SHA1 Message Date
Surma
f4f97abf9d Testing lol 2021-08-10 08:28:12 -04:00
Surma
7c9d3d6ba6 Compile with -s MINIMAL_RUNTIME=1 2021-08-10 07:31:16 -04:00
Surma
d526877147 Merge pull request #1099 from mikedelgaudio/add-meta-info 2021-07-27 11:48:04 +01:00
Jake Archibald
0ed7ef842f Include site origin in build 2021-07-27 11:30:58 +01:00
Mike DelGaudio
3d4c62fede Merge branch 'dev' into add-meta-info 2021-07-26 12:23:06 -04:00
Mike DelGaudio
ce7be359c0 remove keywords
Co-authored-by: Anton <dev@atjn.dk>
2021-07-26 12:23:00 -04:00
Surma
3f2dd66726 Merge pull request #1030 from veluca93/jxl_github 2021-07-23 13:49:46 +01:00
Surma
9198f748b8 Merge branch 'dev' into jxl_github 2021-07-23 13:37:37 +01:00
Surma
a726adf0e8 Merge pull request #1107 from styfle/patch-1
Fix typo uncluding => including
2021-07-23 13:15:21 +01:00
Steven
04580b0bcb Fix typo uncluding => including 2021-07-22 13:48:19 -04:00
Luca Versari
a040c47047 Update JPEG XL to latest version.
Also update repositories.
2021-07-21 18:55:52 +02:00
Surma
2427763a14 Squoosh CLI v0.7.2 2021-07-21 17:19:19 +01:00
Surma
c582c54922 libsquoosh v0.4.0 2021-07-21 17:15:59 +01:00
Surma
9ae27c1887 Merge pull request #1106 from GoogleChromeLabs/issue-1102 2021-07-21 17:03:21 +01:00
Surma
bf95eb39c2 Fix handling of PNGs that are not 8 bit 2021-07-21 16:16:44 +01:00
Surma
011c0346c1 Merge pull request #1100 from MaxGraey/opt-visdif-codec 2021-07-20 19:17:23 +01:00
Surma
9b36c3f9af Merge remote-tracking branch 'origin/dev' into opt-visdif-codec 2021-07-20 19:14:54 +01:00
Surma
490fe2aace Add build instructions 2021-07-20 19:14:23 +01:00
Mike DelGaudio
48efb4ddeb remove twitter image 2021-07-20 10:55:47 -04:00
Surma
0977bd94d3 Merge pull request #1074 from GoogleChromeLabs/avif-node-mt 2021-07-20 12:01:47 +01:00
Surma
d5f12a8c61 Merge remote-tracking branch 'origin/dev' into avif-node-mt 2021-07-20 11:51:10 +01:00
Surma
fb867dcdaa Review by rreverser 2021-07-20 11:50:48 +01:00
Surma
f038c1bd7d Update src/static-build/pages/index/index.tsx 2021-07-20 11:26:19 +01:00
Mike DelGaudio
b37cc0784f typo fix for secure 2021-07-19 20:31:58 -04:00
Mike DelGaudio
ff40000473 adding secure 2021-07-19 20:28:23 -04:00
Mike DelGaudio
7232524f4d add twitter 2021-07-19 20:20:03 -04:00
Surma
bf683cdf59 Merge pull request #1093 from mikedelgaudio/update-readme
[DOCS] Update README, Remove Passive Voice
2021-07-19 17:27:54 +01:00
Mike DelGaudio
f848a9384e Merge branch 'dev' into update-readme 2021-07-19 11:48:13 -04:00
Mike DelGaudio
d4d0db6c49 update header statement 2021-07-19 11:47:52 -04:00
Surma
0719abff27 Merge pull request #1081 from atjn/unused-code
Remove unused variable
2021-07-19 12:42:01 +01:00
Surma
2c561687af Merge branch 'dev' into unused-code 2021-07-19 12:39:51 +01:00
Surma
cd20082e5d Build wasm 2021-07-19 12:36:43 +01:00
MaxGraey
8262c79bb6 fix 2021-07-13 22:38:30 +03:00
MaxGraey
ef176d7565 optimize more 2021-07-13 22:38:10 +03:00
MaxGraey
3b0b7dbdb1 fix 2021-07-13 19:00:26 +03:00
MaxGraey
cc9a887386 optimize srgb -> linear preparation by using lookup table 2021-07-13 18:33:15 +03:00
Mike DelGaudio
71ca1ab0ca Update src/static-build/pages/index/index.tsx
Co-authored-by: Joan León <joan.leon@gmail.com>
2021-07-12 14:40:37 -04:00
Mike DelGaudio
f5d9535fd2 adding meta tags for better SEO 2021-07-12 11:13:51 -04:00
Mike DelGaudio
cea6a61366 Modify meta data 2021-07-09 15:53:18 -04:00
Mike DelGaudio
192cfc62ee Update README.md 2021-07-07 09:57:44 -04:00
Mike DelGaudio
fe21322b2b [DOCS] Update list of develop steps 2021-07-06 14:34:04 -04:00
Mike DelGaudio
d953822d19 [DOCS] Remove passive voice, simplify README 2021-07-05 22:37:04 -04:00
Mike DelGaudio
021b082884 [DOCS] Remove passive voice, simplify README 2021-07-05 22:24:20 -04:00
Surma
ad5002c79c Merge pull request #1078 from ergunsh/auto-optimizer-to-ts
Typescriptify auto optimizer in libSquoosh
2021-07-01 15:53:06 +01:00
Surma
883bb92e48 Merge branch 'dev' into auto-optimizer-to-ts 2021-07-01 15:45:38 +01:00
Surma
79efd0d32e Merge pull request #1080 from pekeq/doc-resize-with-aspect
libsquoosh/README: Add code example to resize with aspect ratio preserved.
2021-07-01 15:45:32 +01:00
Surma
99741d665d Merge branch 'dev' into auto-optimizer-to-ts 2021-07-01 15:22:23 +01:00
Surma
b38765b4e6 Merge branch 'dev' into doc-resize-with-aspect 2021-07-01 15:22:13 +01:00
Bob Fanger
a11ac15008 fix: Don't overwrite value while typing (#1082)
Fixes #316
2021-07-01 11:11:00 +01:00
atjn
4f6d21199c Remove unused variable 2021-07-01 00:08:58 +02:00
Hideo Matsumoto
fb7e00067f Update libsquoosh/README.md code example
Add code example to resize with aspect ratio preserved.
2021-06-30 22:43:20 +09:00
ergunsh
a2121ec47b Typescriptify auto optimizer in libSquoosh 2021-06-27 22:27:43 +02:00
Surma
d4056026fb Add AVIF thread support 2021-06-25 00:11:25 +01:00
Surma
d12b040bd3 Add threaded AVIF encoder for node 2021-06-24 17:07:57 +01:00
Surma
149ebf5a67 Merge pull request #1073 from atjn/patch-1
Fix codecs link
2021-06-24 16:35:31 +01:00
Anton
d8297aad10 Fix codecs link 2021-06-24 17:03:17 +02:00
Surma
0e84a5b5f7 Merge pull request #1071 from simon04/patch-1 2021-06-24 10:56:26 +01:00
Simon Legner
187a5bed01 cli/README: linkfix src/codecs.ts 2021-06-24 09:34:09 +02:00
Jake Archibald
07a288398e Spotted another leak (#1069) 2021-06-23 10:39:27 +01:00
Surma
dbb31a1add Merge remote-tracking branch 'origin/dev' into dev 2021-06-21 12:58:41 +01:00
Surma
6e58e8edb0 Merge pull request #1061 from atjn/engines
Specify supported node versions
2021-06-21 12:58:28 +01:00
Surma
955079b18f Merge branch 'dev' into engines 2021-06-21 12:53:32 +01:00
Vajahath Ahmed
f6f70c590e Add repo info in package.json so that people can backtrack from npm site (#1066) 2021-06-21 11:59:21 +01:00
Surma
55c4aa51ac Merge pull request #1063 from SimonGolms/patch-1
fix(cli): append suffix option at the end
2021-06-19 14:25:55 +01:00
Simon G
aea316c604 fix(cli): append suffix option at the end
- close #1062
2021-06-19 11:40:52 +02:00
atjn
d604e94ad2 Specify supported node versions 2021-06-18 21:18:37 +02:00
Surma
61de471e52 Merge pull request #1058 from atjn/fix-detectors
Fix WebP sniffing some more
2021-06-18 15:58:03 +01:00
atjn
955b2ac1ba Fix WebP sniffing some more 2021-06-18 15:51:20 +02:00
Surma
3d225966c5 CLI v0.7.1 2021-06-16 17:13:59 +01:00
Surma
851da25302 Update CLI dependency 2021-06-16 17:13:37 +01:00
Surma
32e3528666 libsquoosh v0.3.1 2021-06-16 17:11:15 +01:00
Surma
1e52837931 Oooops. Old files 2021-06-16 17:02:54 +01:00
Surma
5bc80e66ec Merge remote-tracking branch 'origin/dev' into visdif-error 2021-06-16 13:54:49 +01:00
Surma
b1f50cd27c Fix AVIF auto optimizer options 2021-06-16 13:54:17 +01:00
Surma
3d136016e2 Provide optimizer default value 2021-06-16 13:44:24 +01:00
Surma
14a715e952 Add test files for now 2021-06-16 12:56:37 +01:00
Surma
324b04d398 Merge pull request #1044 from ergunsh/libsquoosh-codecs-to-ts
Typescriptify libsquoosh's codecs.js and emscripten-utils.js
2021-06-16 11:47:21 +01:00
Surma
eb76fbc4bc Merge branch 'dev' into libsquoosh-codecs-to-ts 2021-06-16 11:43:35 +01:00
Jake Archibald
1d292468b0 Everything supports ResizeObserver now 2021-06-16 10:13:46 +01:00
Jake Archibald
828f9a5eeb Fix memory leaks (#1054) 2021-06-15 10:45:53 +01:00
Surma
5595525c8a Merge pull request #1053 from alex-drocks/patch-1
Update README.md @libSquoosh code example
2021-06-11 16:25:23 +01:00
Alexandre Desroches
5e14444b13 Update README.md
To close typo in example code as reported in issue #1051
2021-06-11 09:36:30 -04:00
Ergün Erdoğmuş
3d58c616af Merge branch 'dev' into libsquoosh-codecs-to-ts 2021-06-10 18:31:02 +02:00
Surma
d7090042a6 Fix alignment issue 2021-06-10 12:25:58 -04:00
Surma
be3249bf2f Add test case 2021-06-10 11:31:59 -04:00
ergunsh
1b59c3f47a Keep js extension while importing module & types 2021-06-09 18:11:30 +02:00
Surma
9a37cc7959 Merge pull request #1042 from GoogleChromeLabs/issue-1022 2021-06-09 12:17:00 +01:00
Surma
ffb9135a7a Merge remote-tracking branch 'origin/dev' into issue-1022 2021-06-09 06:35:00 -04:00
ergunsh
5707eeff41 Return Uint8ClampedArray in resize operation 2021-06-08 20:28:22 +02:00
ergunsh
a18ed360eb Address review comments
* Updated import to contain file extension `js`
* Separated WebAssembly definitions from missing-types
* Converted resizer.resize result to Uint8ClampedArray
* Moved ResizeInstantiateOptions to an interface
2021-06-08 20:09:37 +02:00
Surma
011d0c2099 Update AVIF 2021-06-08 16:51:08 +01:00
Surma
89105bbb22 Update wp2 2021-06-08 11:07:32 -04:00
Surma
5f7c619413 Update webp 2021-06-08 11:02:43 -04:00
Surma
3ec7d4db16 Update visdif 2021-06-08 10:59:47 -04:00
Surma
8ef8cef522 Update MozJPEG 2021-06-08 10:58:10 -04:00
Surma
f6431d8147 Update JXL 2021-06-08 10:54:16 -04:00
Surma
be037754ce Update imagequant 2021-06-08 10:43:33 -04:00
Surma
32107124e6 Update Emscripten version 2021-06-08 10:43:11 -04:00
ergunsh
1af5d1fa7b Typescriptify libsquoosh's codecs and emscripten-utils 2021-06-04 19:12:31 +02:00
Surma
08adfba8be Merge pull request #1037 from ergunsh/libsquoosh-ts-setup
Add initial ts setup for libsquoosh
2021-06-04 15:46:23 +01:00
Surma
5df04f6419 Update wp2 2021-06-04 09:51:10 -04:00
Surma
cf570a4d3f Update webp 2021-06-04 09:29:16 -04:00
Surma
2aa691339c Update visdif 2021-06-04 09:25:57 -04:00
Surma
9fcb4a4c55 Update mozjpeg 2021-06-04 09:23:23 -04:00
Surma
18e3d77aef Update jxl 2021-06-04 09:18:27 -04:00
Surma
6a4982bf4c Update imagequant 2021-06-04 08:52:46 -04:00
Surma
c7b998a877 Add flags 2021-06-04 08:33:57 -04:00
ergunsh
e5b2030666 Merge branch 'dev' into libsquoosh-ts-setup 2021-06-03 18:54:36 +02:00
ergunsh
0e09d0b33f Remove unnecessary missing-typed.d.ts for now
We don't need it for ImageData module
2021-06-03 18:50:44 +02:00
Surma
ed03a37fb8 Merge pull request #1041 from alexander-akait/issue-1034
fix: `main` filed
2021-06-03 11:18:41 +01:00
evilebottnawi
2bc4ab8fd6 fix: main filed 2021-06-03 12:36:49 +03:00
Ingvar Stepanyan
2037fe8964 Add an error for unsupported usage of new URL 2021-06-02 17:03:14 +01:00
Ingvar Stepanyan
d07c57ecaa Update lib/client-bundle-plugin.js 2021-06-02 17:03:14 +01:00
Ingvar Stepanyan
e7d55bf903 Update lib/entry-data-plugin.js
Co-authored-by: Jake Archibald <jaffathecake@gmail.com>
2021-06-02 17:03:14 +01:00
Ingvar Stepanyan
687cf5aae2 Bundle URL assets as dependencies
I've added support for `new URL(..., import.meta.url)` pattern accepted by many bundlers to wasm-bindgen and Emscripten, both for Wasm files as well as for side Workers autogenerated to support multithreading.

On the apps side like Squoosh this means we no longer have to manually specify URLs to be passed to wasm-bindgen or Emscripten init functions. In this PR I'm primarily doing just that - removing all the manual Wasm URL locators, as well as the associated logic.

One notable change on the build system side is that, in order for Wasm and multithreading-related Workers to be detected as dependencies, I'm now walking over `referencedFiles` in addition to `imports` when collecting list of dependencies in `getDependencies` function (client-bundle-plugin.js).

As a side effect, it also included other linked assets - images and CSS - which simplified quite a few caching lists in to-cache.ts as well. Instead of having to manually specify each asset that needs to be included, the logic is inverted and only few extra assets had to be excluded to keep the list of in items cached on the first load as low as it was before the change.

All in all, I think this simplification is worth it and even helped to accidentally uncover & fix one caching bug where WP2 regular JS was cached in addition to WP2 multithreaded JS simultaneously.
2021-06-02 17:03:14 +01:00
Ingvar Stepanyan
d63823d196 Add @web/rollup-plugin-import-meta-assets 2021-06-02 17:03:14 +01:00
Ingvar Stepanyan
7d111b6a43 Update wasm-bindgen 2021-06-02 17:03:14 +01:00
Ingvar Stepanyan
426f31e548 Upgrade Emscripten to 2.0.21
Few notes:
 - Lots of deprecated SIMD intrinsic warnings & errors in JPEG-XL -> Highway; had to suppress erorrs to make project build.
 - Moved couple of common link flags to cpp.Dockerfile (note: can't move `EXPORT_ES6` otherwise `configure` will fail).
 - MODULARIZE=1 is no longer necessary and implied by EXPORT_ES6.
 - EXPORT_NAME=... is no longer necessary in EXPORT_ES6.
 - Changed visdif to also use EXPORT_ES6 and ENVIRONMENT=node instead of generic JS.
2021-06-02 17:03:14 +01:00
Ingvar Stepanyan
dd0adba6b1 Revert "Remove SIMD for now (#1025)"
This reverts commit f779e13bc8.
2021-06-02 17:03:14 +01:00
ergunsh
30445927ea Add initial ts setup for libsquoosh
Convert `image_data` to typescript as an example
2021-05-31 21:45:28 +02:00
Surma
31b263fc27 Merge pull request #1035 from JustTestCode/patch-1
Update README.md
2021-05-30 10:06:01 +01:00
JustTestCode
fc590918ed Update README.md
I check 2 hours, why can't run...
2021-05-30 12:16:58 +08:00
Surma
c4783b03df Merge pull request #1031 from GoogleChromeLabs/defensive-web-codec 2021-05-27 10:30:38 +01:00
Jake Archibald
8f9c0ff0e7 More defensive use of isTypeSupported 2021-05-27 09:14:13 +01:00
Jake Archibald
40c81ef782 Remove circular dependency 2021-05-27 09:13:17 +01:00
138 changed files with 12273 additions and 2069 deletions

View File

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

View File

@@ -42,7 +42,7 @@ Options:
-h, --help display help for command -h, --help display help for command
``` ```
The default values for each `config` option can be found in the [`codecs.js`][codecs.js] file under `defaultEncoderOptions`. Every unspecified value will use the default value specified here. _Better documentation is needed here._ The default values for each `config` option can be found in the [`codecs.ts`][codecs.ts] file under `defaultEncoderOptions`. Every unspecified value will use the default value specified here. _Better documentation is needed here._
## Auto optimizer ## Auto optimizer
@@ -55,5 +55,5 @@ $ npx @squoosh/cli --wp2 auto test.png
``` ```
[squoosh]: https://squoosh.app [squoosh]: https://squoosh.app
[codecs.js]: https://github.com/GoogleChromeLabs/squoosh/blob/dev/libsquoosh/src/codecs.js [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

36
cli/package-lock.json generated
View File

@@ -1,15 +1,15 @@
{ {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.7.0", "version": "0.7.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.7.0", "version": "0.7.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@squoosh/lib": "^0.2.0", "@squoosh/lib": "^0.4.0",
"commander": "^7.2.0", "commander": "^7.2.0",
"json5": "^2.2.0", "json5": "^2.2.0",
"kleur": "^4.1.4", "kleur": "^4.1.4",
@@ -18,14 +18,21 @@
"bin": { "bin": {
"cli": "src/index.js", "cli": "src/index.js",
"squoosh-cli": "src/index.js" "squoosh-cli": "src/index.js"
},
"engines": {
"node": " ^12.20.2 || ^14.13.1 || ^16.0.0 "
} }
}, },
"node_modules/@squoosh/lib": { "node_modules/@squoosh/lib": {
"version": "0.2.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.4.0.tgz",
"integrity": "sha512-zKId9h/LzEnCdoOGnIgjzvqbk5g2aHgfEqgKQ+S+r5K3TasgD/DAsT6r7v6gXft1ao0f/00CTcwIp1KviWTQbw==", "integrity": "sha512-O1LyugWLZjMI4JZeZMA5vzfhfPjfMZXH5/HmVkRagP8B70wH3uoR7tjxfGNdSavey357MwL8YJDxbGwBBdHp7Q==",
"dependencies": { "dependencies": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3" "web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": " ^12.5.0 || ^14.0.0 || ^16.0.0 "
} }
}, },
"node_modules/ansi-regex": { "node_modules/ansi-regex": {
@@ -323,6 +330,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
}, },
"node_modules/wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"node_modules/wcwidth": { "node_modules/wcwidth": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
@@ -342,10 +354,11 @@
}, },
"dependencies": { "dependencies": {
"@squoosh/lib": { "@squoosh/lib": {
"version": "0.2.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@squoosh/lib/-/lib-0.4.0.tgz",
"integrity": "sha512-zKId9h/LzEnCdoOGnIgjzvqbk5g2aHgfEqgKQ+S+r5K3TasgD/DAsT6r7v6gXft1ao0f/00CTcwIp1KviWTQbw==", "integrity": "sha512-O1LyugWLZjMI4JZeZMA5vzfhfPjfMZXH5/HmVkRagP8B70wH3uoR7tjxfGNdSavey357MwL8YJDxbGwBBdHp7Q==",
"requires": { "requires": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3" "web-streams-polyfill": "^3.0.3"
} }
}, },
@@ -578,6 +591,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
}, },
"wasm-feature-detect": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
},
"wcwidth": { "wcwidth": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",

View File

@@ -1,9 +1,14 @@
{ {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.7.0", "version": "0.7.2",
"description": "A CLI for Squoosh", "description": "A CLI for Squoosh",
"public": true, "public": true,
"type": "module", "type": "module",
"homepage": "https://github.com/GoogleChromeLabs/squoosh",
"repository": {
"type": "git",
"url": "https://github.com/GoogleChromeLabs/squoosh.git"
},
"bin": { "bin": {
"squoosh-cli": "src/index.js", "squoosh-cli": "src/index.js",
"@squoosh/cli": "src/index.js" "@squoosh/cli": "src/index.js"
@@ -14,8 +19,11 @@
"keywords": [], "keywords": [],
"author": "Google Chrome Developers <chromium-dev@google.com>", "author": "Google Chrome Developers <chromium-dev@google.com>",
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": {
"node": " ^12.20.2 || ^14.13.1 || ^16.0.0 "
},
"dependencies": { "dependencies": {
"@squoosh/lib": "^0.2.0", "@squoosh/lib": "^0.4.0",
"commander": "^7.2.0", "commander": "^7.2.0",
"json5": "^2.2.0", "json5": "^2.2.0",
"kleur": "^4.1.4", "kleur": "^4.1.4",

View File

@@ -177,8 +177,8 @@ async function processFiles(files) {
jobsFinished++; jobsFinished++;
const outputPath = path.join( const outputPath = path.join(
program.opts().outputDir, program.opts().outputDir,
program.opts().suffix + path.basename(originalFile, path.extname(originalFile)) +
path.basename(originalFile, path.extname(originalFile)), 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}`;

View File

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

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -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;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,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/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}}; "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}};

File diff suppressed because one or more lines are too long

Binary file not shown.

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

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1 @@
"use strict";var Module={};if(typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string"){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",function(data){onmessage({data:data})});var nodeFS=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:function(f){(0,eval)(nodeFS.readFileSync(f,"utf8"))},postMessage:function(msg){parentPort.postMessage(msg)},performance:global.performance||{now:function(){return Date.now()}}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};function moduleLoaded(){}self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./avif_node_enc_mt.js")).then(function(exports){return exports.default(Module)}).then(function(instance){Module=instance;moduleLoaded()})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["PThread"].threadExit(result)}}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}};

View File

@@ -1,13 +1,14 @@
FROM emscripten/emsdk:2.0.21 FROM emscripten/emsdk:2.0.23
RUN apt-get update && apt-get install -qqy autoconf libtool pkg-config RUN apt-get update && apt-get install -qqy autoconf libtool pkg-config
ENV CFLAGS "-O3 -flto" ENV CFLAGS "-O3 -flto"
ENV CXXFLAGS "${CFLAGS} -std=c++17" ENV CXXFLAGS "${CFLAGS} -std=c++17"
ENV LDFLAGS "${CFLAGS} \ ENV LDFLAGS "${CFLAGS} \
--closure 1 \
-s FILESYSTEM=0 \ -s FILESYSTEM=0 \
-s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency \ -s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency \
-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 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

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
CODEC_URL = https://gitlab.com/wg1/jpeg-xl.git CODEC_URL = https://github.com/libjxl/libjxl.git
CODEC_VERSION = ab7c5e9b6795134377aa4846ceaae2c5bc504f76 CODEC_VERSION = 3f76321a7cbcf4f452894dc173eb7be60f508ecb
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

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

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

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -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;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,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/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}}; "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}};

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -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;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,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/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}}; "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}};

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

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

View File

@@ -9,9 +9,9 @@
* @param {number} typ_idx * @param {number} typ_idx
* @param {boolean} premultiply * @param {boolean} premultiply
* @param {boolean} color_space_conversion * @param {boolean} color_space_conversion
* @returns {Uint8Array} * @returns {Uint8ClampedArray}
*/ */
export function resize(input_image: Uint8Array, input_width: number, input_height: number, output_width: number, output_height: number, typ_idx: number, premultiply: boolean, color_space_conversion: boolean): Uint8Array; export function resize(input_image: Uint8Array, input_width: number, input_height: number, output_width: number, output_height: number, typ_idx: number, premultiply: boolean, color_space_conversion: boolean): Uint8ClampedArray;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;

View File

@@ -26,8 +26,16 @@ function getInt32Memory0() {
return cachegetInt32Memory0; return cachegetInt32Memory0;
} }
function getArrayU8FromWasm0(ptr, len) { let cachegetUint8ClampedMemory0 = null;
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); function getUint8ClampedMemory0() {
if (cachegetUint8ClampedMemory0 === null || cachegetUint8ClampedMemory0.buffer !== wasm.memory.buffer) {
cachegetUint8ClampedMemory0 = new Uint8ClampedArray(wasm.memory.buffer);
}
return cachegetUint8ClampedMemory0;
}
function getClampedArrayU8FromWasm0(ptr, len) {
return getUint8ClampedMemory0().subarray(ptr / 1, ptr / 1 + len);
} }
/** /**
* @param {Uint8Array} input_image * @param {Uint8Array} input_image
@@ -38,7 +46,7 @@ function getArrayU8FromWasm0(ptr, len) {
* @param {number} typ_idx * @param {number} typ_idx
* @param {boolean} premultiply * @param {boolean} premultiply
* @param {boolean} color_space_conversion * @param {boolean} color_space_conversion
* @returns {Uint8Array} * @returns {Uint8ClampedArray}
*/ */
export function resize(input_image, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion) { export function resize(input_image, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion) {
try { try {
@@ -48,7 +56,7 @@ export function resize(input_image, input_width, input_height, output_width, out
wasm.resize(retptr, ptr0, len0, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion); wasm.resize(retptr, ptr0, len0, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion);
var r0 = getInt32Memory0()[retptr / 4 + 0]; var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1]; var r1 = getInt32Memory0()[retptr / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice(); var v1 = getClampedArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1); wasm.__wbindgen_free(r0, r1 * 1);
return v1; return v1;
} finally { } finally {

7
codecs/resize/pkg/squoosh_resize_bg.wasm.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export function resize(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number): void;
export function __wbindgen_add_to_stack_pointer(a: number): number;
export function __wbindgen_malloc(a: number): number;
export function __wbindgen_free(a: number, b: number): void;

View File

@@ -8,6 +8,7 @@ use cfg_if::cfg_if;
use resize::Pixel; use resize::Pixel;
use resize::Type; use resize::Type;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::Clamped;
mod srgb; mod srgb;
use srgb::{linear_to_srgb, Clamp}; use srgb::{linear_to_srgb, Clamp};
@@ -66,7 +67,7 @@ pub fn resize(
typ_idx: usize, typ_idx: usize,
premultiply: bool, premultiply: bool,
color_space_conversion: bool, color_space_conversion: bool,
) -> Vec<u8> { ) -> Clamped<Vec<u8>> {
let typ = match typ_idx { let typ = match typ_idx {
0 => Type::Triangle, 0 => Type::Triangle,
1 => Type::Catrom, 1 => Type::Catrom,
@@ -91,7 +92,7 @@ pub fn resize(
typ, typ,
); );
resizer.resize(input_image.as_slice(), output_image.as_mut_slice()); resizer.resize(input_image.as_slice(), output_image.as_mut_slice());
return output_image; return Clamped(output_image);
} }
// Otherwise, we convert to f32 images to keep the // Otherwise, we convert to f32 images to keep the
@@ -138,5 +139,5 @@ pub fn resize(
.clamp(0.0, 255.0) as u8; .clamp(0.0, 255.0) as u8;
} }
return output_image; return Clamped(output_image);
} }

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

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

View File

@@ -1,5 +1,6 @@
{ {
"name": "avif", "name": "avif",
"type": "module",
"scripts": { "scripts": {
"build": "../build-cpp.sh" "build": "../build-cpp.sh"
} }

View File

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

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -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;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,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/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}}; "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}};

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -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;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,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/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}}; "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}};

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -20,7 +20,6 @@ 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);
@@ -28,24 +27,11 @@ 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];
} }
@@ -153,9 +139,9 @@ export default function (inputOptions, outputOptions, resolveFileUrl) {
if (property.startsWith(allSrcPlaceholder)) { if (property.startsWith(allSrcPlaceholder)) {
const allModules = [ const allModules = [
clientEntry, clientEntry,
...dependencies ...dependencies.map((name) =>
.map((name) => clientOutput.find((item) => item.fileName === name)) clientOutput.find((item) => item.fileName === name),
.filter((item) => item.code), ),
]; ];
const inlineDefines = [ const inlineDefines = [

View File

@@ -17,14 +17,10 @@ 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 {number} */ /** @type {string} */
let exportCounter; let exportCounter;
/** @type {Map<number, string>} */ /** @type {Map<number, string>} */
let counterToIdMap; let counterToIdMap;
@@ -73,11 +69,15 @@ 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(fileNameToURL(chunk.fileName)); return JSON.stringify(
chunk.fileName.slice(filenamePrefix.length),
);
} }
return JSON.stringify( return JSON.stringify(
getDependencies(chunks, chunk).map((filename) => fileNameToURL(filename)), getDependencies(chunks, chunk).map((item) =>
item.slice(filenamePrefix.length),
),
); );
}, },
); );

View File

@@ -30,7 +30,7 @@ const imagePath = 'path/to/image.png';
const image = imagePool.ingestImage(imagePath); const image = imagePool.ingestImage(imagePath);
``` ```
The `ingestImage` function can take anything the node [`readFile`][readfile] function can take, uncluding a buffer and `FileHandle`. The `ingestImage` function can take anything the node [`readFile`][readfile] function can take, including a buffer and `FileHandle`.
The returned `image` object is a representation of the original image, that you can now preprocess, encode, and extract information about. The returned `image` object is a representation of the original image, that you can now preprocess, encode, and extract information about.
@@ -39,18 +39,26 @@ 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 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.
resize: { resize: {
enabled: true, 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.
resize: {
enabled: true,
width: 100,
}
*/
} }
await image.preprocess(preprocessOptions); await image.preprocess(preprocessOptions);
const encodeOptions: { const encodeOptions = {
mozjpeg: {}, //an empty object means 'use default settings' mozjpeg: {}, //an empty object means 'use default settings'
jxl: { jxl: {
quality: 90, quality: 90,
@@ -60,7 +68,7 @@ await image.encode(encodeOptions);
``` ```
The default values for each option can be found in the [`codecs.js`][codecs.js] 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._
You can run your own code inbetween the different steps, if, for example, you want to change how much the image should be resized based on its original height. (See [Extracting image information](#extracting-image-information) to learn how to get the image dimensions). You can run your own code inbetween the different steps, if, for example, you want to change how much the image should be resized based on its original height. (See [Extracting image information](#extracting-image-information) to learn how to get the image dimensions).
@@ -81,7 +89,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.encodedWidth.mozjpeg).binary; const rawEncodedImage = (await image.encodedWith.mozjpeg).binary;
fs.writeFile('/path/to/new/image.jpg', rawEncodedImage); fs.writeFile('/path/to/new/image.jpg', rawEncodedImage);
``` ```
@@ -158,6 +166,6 @@ const encodeOptions: {
``` ```
[squoosh]: https://squoosh.app [squoosh]: https://squoosh.app
[codecs.js]: https://github.com/GoogleChromeLabs/squoosh/blob/dev/libsquoosh/src/codecs.js [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 [readfile]: https://nodejs.org/api/fs.html#fs_fspromises_readfile_path_options

View File

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

133
libsquoosh/lib/simple-ts.js Normal file
View File

@@ -0,0 +1,133 @@
/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { spawn } from 'child_process';
import { relative, join } from 'path';
import { promises as fsp } from 'fs';
import { promisify } from 'util';
import * as ts from 'typescript';
import glob from 'glob';
import { sync as whichSync } from 'which';
const globP = promisify(glob);
const tscPath = whichSync('tsc');
const extRe = /\.tsx?$/;
function loadConfig(mainPath) {
const fileName = ts.findConfigFile(mainPath, ts.sys.fileExists);
if (!fileName) throw Error('tsconfig not found');
const text = ts.sys.readFile(fileName);
const loadedConfig = ts.parseConfigFileTextToJson(fileName, text).config;
const parsedTsConfig = ts.parseJsonConfigFileContent(
loadedConfig,
ts.sys,
process.cwd(),
undefined,
fileName,
);
return parsedTsConfig;
}
export default function simpleTS(mainPath, { noBuild, watch } = {}) {
const config = loadConfig(mainPath);
const args = ['-b', mainPath];
let tsBuildDone;
async function watchBuiltFiles(rollupContext) {
const matches = await globP(config.options.outDir + '/**/*.js');
for (const match of matches) rollupContext.addWatchFile(match);
}
async function tsBuild(rollupContext) {
if (tsBuildDone) {
// Watch lists are cleared on each build, so we need to rewatch all the JS files.
await watchBuiltFiles(rollupContext);
return tsBuildDone;
}
if (noBuild) {
return (tsBuildDone = Promise.resolve());
}
tsBuildDone = Promise.resolve().then(async () => {
await new Promise((resolve) => {
const proc = spawn(tscPath, args, {
stdio: 'inherit',
});
proc.on('exit', (code) => {
if (code !== 0) {
throw Error('TypeScript build failed');
}
resolve();
});
});
await watchBuiltFiles(rollupContext);
if (watch) {
tsBuildDone.then(() => {
spawn(tscPath, [...args, '--watch', '--preserveWatchOutput'], {
stdio: 'inherit',
});
});
}
});
return tsBuildDone;
}
return {
name: 'simple-ts',
resolveId(id, importer) {
// If there isn't an importer, it's an entry point, so we don't need to resolve it relative
// to something.
if (!importer) return null;
const tsResolve = ts.resolveModuleName(
id,
importer,
config.options,
ts.sys,
);
if (
// It didn't find anything
!tsResolve.resolvedModule ||
// Or if it's linking to a definition file, it's something in node_modules,
// or something local like css.d.ts
tsResolve.resolvedModule.extension === '.d.ts'
) {
return null;
}
return tsResolve.resolvedModule.resolvedFileName;
},
async load(id) {
if (!extRe.test(id)) return null;
// TypeScript building is deferred until the first TS file load.
// This allows prerequisites to happen first,
// such as css.d.ts generation in css-plugin.
await tsBuild(this);
// Look for the JS equivalent in the tmp folder
const newId = join(
config.options.outDir,
relative(config.options.rootDir, id),
).replace(extRe, '.js');
return fsp.readFile(newId, { encoding: 'utf8' });
},
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{ {
"name": "@squoosh/lib", "name": "@squoosh/lib",
"version": "0.2.3", "version": "0.4.0",
"description": "A Node library for Squoosh", "description": "A Node library for Squoosh",
"public": true, "public": true,
"main": "/build/index.js", "main": "./build/index.js",
"files": [ "files": [
"/build/*" "/build/*"
], ],
@@ -12,8 +12,17 @@
}, },
"keywords": [], "keywords": [],
"author": "Google Chrome Developers <chromium-dev@google.com>", "author": "Google Chrome Developers <chromium-dev@google.com>",
"homepage": "https://github.com/GoogleChromeLabs/squoosh",
"repository": {
"type": "git",
"url": "https://github.com/GoogleChromeLabs/squoosh.git"
},
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": {
"node": " ^12.5.0 || ^14.0.0 || ^16.0.0 "
},
"dependencies": { "dependencies": {
"wasm-feature-detect": "^1.2.11",
"web-streams-polyfill": "^3.0.3" "web-streams-polyfill": "^3.0.3"
}, },
"devDependencies": { "devDependencies": {
@@ -22,7 +31,10 @@
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-node-resolve": "^11.2.1",
"@types/node": "^15.6.1",
"rollup": "^2.46.0", "rollup": "^2.46.0",
"rollup-plugin-terser": "^7.0.2" "rollup-plugin-terser": "^7.0.2",
"typescript": "^4.1.3",
"which": "^2.0.2"
} }
} }

View File

@@ -1,6 +1,8 @@
import resolve from '@rollup/plugin-node-resolve'; import resolve from '@rollup/plugin-node-resolve';
import cjs from '@rollup/plugin-commonjs'; import cjs from '@rollup/plugin-commonjs';
import simpleTS from './lib/simple-ts';
import asset from './lib/asset-plugin.js'; import asset from './lib/asset-plugin.js';
import chunk from './lib/chunk-plugin.js';
import json from './lib/json-plugin.js'; import json from './lib/json-plugin.js';
import autojson from './lib/autojson-plugin.js'; import autojson from './lib/autojson-plugin.js';
import { getBabelOutputPlugin } from '@rollup/plugin-babel'; import { getBabelOutputPlugin } from '@rollup/plugin-babel';
@@ -17,9 +19,11 @@ export default {
plugins: [ plugins: [
resolve(), resolve(),
cjs(), cjs(),
chunk(),
asset(), asset(),
autojson(), autojson(),
json(), json(),
simpleTS('.'),
getBabelOutputPlugin({ getBabelOutputPlugin({
babelrc: false, babelrc: false,
configFile: false, configFile: false,

132
libsquoosh/src/WebAssembly.d.ts vendored Normal file
View File

@@ -0,0 +1,132 @@
/**
* WebAssembly definitions are not available in `@types/node` yet,
* so these are copied from `lib.dom.d.ts`
*/
declare namespace WebAssembly {
interface CompileError {}
var CompileError: {
prototype: CompileError;
new (): CompileError;
};
interface Global {
value: any;
valueOf(): any;
}
var Global: {
prototype: Global;
new (descriptor: GlobalDescriptor, v?: any): Global;
};
interface Instance {
readonly exports: Exports;
}
var Instance: {
prototype: Instance;
new (module: Module, importObject?: Imports): Instance;
};
interface LinkError {}
var LinkError: {
prototype: LinkError;
new (): LinkError;
};
interface Memory {
readonly buffer: ArrayBuffer;
grow(delta: number): number;
}
var Memory: {
prototype: Memory;
new (descriptor: MemoryDescriptor): Memory;
};
interface Module {}
var Module: {
prototype: Module;
new (bytes: BufferSource): Module;
customSections(moduleObject: Module, sectionName: string): ArrayBuffer[];
exports(moduleObject: Module): ModuleExportDescriptor[];
imports(moduleObject: Module): ModuleImportDescriptor[];
};
interface RuntimeError {}
var RuntimeError: {
prototype: RuntimeError;
new (): RuntimeError;
};
interface Table {
readonly length: number;
get(index: number): Function | null;
grow(delta: number): number;
set(index: number, value: Function | null): void;
}
var Table: {
prototype: Table;
new (descriptor: TableDescriptor): Table;
};
interface GlobalDescriptor {
mutable?: boolean;
value: ValueType;
}
interface MemoryDescriptor {
initial: number;
maximum?: number;
}
interface ModuleExportDescriptor {
kind: ImportExportKind;
name: string;
}
interface ModuleImportDescriptor {
kind: ImportExportKind;
module: string;
name: string;
}
interface TableDescriptor {
element: TableKind;
initial: number;
maximum?: number;
}
interface WebAssemblyInstantiatedSource {
instance: Instance;
module: Module;
}
type ImportExportKind = 'function' | 'global' | 'memory' | 'table';
type TableKind = 'anyfunc';
type ValueType = 'f32' | 'f64' | 'i32' | 'i64';
type ExportValue = Function | Global | Memory | Table;
type Exports = Record<string, ExportValue>;
type ImportValue = ExportValue | number;
type ModuleImports = Record<string, ImportValue>;
type Imports = Record<string, ModuleImports>;
function compile(bytes: BufferSource): Promise<Module>;
// `compileStreaming` does not exist in NodeJS
// function compileStreaming(source: Response | Promise<Response>): Promise<Module>;
function instantiate(
bytes: BufferSource,
importObject?: Imports,
): Promise<WebAssemblyInstantiatedSource>;
function instantiate(
moduleObject: Module,
importObject?: Imports,
): Promise<Instance>;
// `instantiateStreaming` does not exist in NodeJS
// function instantiateStreaming(response: Response | PromiseLike<Response>, importObject?: Imports): Promise<WebAssemblyInstantiatedSource>;
function validate(bytes: BufferSource): boolean;
}

View File

@@ -2,6 +2,39 @@ import { instantiateEmscriptenWasm } from './emscripten-utils.js';
import visdif from '../../codecs/visdif/visdif.js'; import visdif from '../../codecs/visdif/visdif.js';
import visdifWasm from 'asset-url:../../codecs/visdif/visdif.wasm'; import visdifWasm from 'asset-url:../../codecs/visdif/visdif.wasm';
import type ImageData from './image_data';
interface VisDiff {
distance: (data: Uint8ClampedArray) => number;
delete: () => void;
}
interface VisdiffConstructor {
new (data: Uint8ClampedArray, width: number, height: number): VisDiff;
}
interface VisDiffModule extends EmscriptenWasm.Module {
VisDiff: VisdiffConstructor;
}
type VisDiffModuleFactory = EmscriptenWasm.ModuleFactory<VisDiffModule>;
interface BinarySearchParams {
min?: number;
max?: number;
epsilon?: number;
maxRounds?: number;
}
interface AutoOptimizeParams extends BinarySearchParams {
butteraugliDistanceGoal?: number;
}
interface AutoOptimizeResult {
bitmap: ImageData;
binary: Uint8Array;
quality: number;
}
// `measure` is a (async) function that takes exactly one numeric parameter and // `measure` is a (async) function that takes exactly one numeric parameter and
// returns a value. The function is assumed to be monotonic (an increase in `parameter` // returns a value. The function is assumed to be monotonic (an increase in `parameter`
@@ -9,9 +42,9 @@ import visdifWasm from 'asset-url:../../codecs/visdif/visdif.wasm';
// to find `parameter` such that `measure` returns `measureGoal`, within an error // to find `parameter` such that `measure` returns `measureGoal`, within an error
// of `epsilon`. It will use at most `maxRounds` attempts. // of `epsilon`. It will use at most `maxRounds` attempts.
export async function binarySearch( export async function binarySearch(
measureGoal, measureGoal: number,
measure, measure: (val: number) => Promise<number>,
{ min = 0, max = 100, epsilon = 0.1, maxRounds = 8 } = {}, { min = 0, max = 100, epsilon = 0.1, maxRounds = 6 }: BinarySearchParams = {},
) { ) {
let parameter = (max - min) / 2 + min; let parameter = (max - min) / 2 + min;
let delta = (max - min) / 4; let delta = (max - min) / 4;
@@ -33,12 +66,21 @@ export async function binarySearch(
} }
export async function autoOptimize( export async function autoOptimize(
bitmapIn, bitmapIn: ImageData,
encode, encode: (
decode, bitmap: ImageData,
{ butteraugliDistanceGoal = 1.4, ...otherOpts } = {}, quality: number,
) { ) => Promise<Uint8Array> | Uint8Array,
const { VisDiff } = await instantiateEmscriptenWasm(visdif, visdifWasm); decode: (binary: Uint8Array) => Promise<ImageData> | ImageData,
{
butteraugliDistanceGoal = 1.4,
...binarySearchParams
}: AutoOptimizeParams = {},
): Promise<AutoOptimizeResult> {
const { VisDiff } = await instantiateEmscriptenWasm(
visdif as VisDiffModuleFactory,
visdifWasm,
);
const comparator = new VisDiff( const comparator = new VisDiff(
bitmapIn.data, bitmapIn.data,
@@ -46,8 +88,11 @@ export async function autoOptimize(
bitmapIn.height, bitmapIn.height,
); );
let bitmapOut; // We're able to do non null assertion because
let binaryOut; // we know that binarySearch will set these values
let bitmapOut!: ImageData;
let binaryOut!: Uint8Array;
// Increasing quality means _decrease_ in Butteraugli distance. // Increasing quality means _decrease_ in Butteraugli distance.
// `binarySearch` assumes that increasing `parameter` will // `binarySearch` assumes that increasing `parameter` will
// increase the metric value. So multipliy Butteraugli values by -1. // increase the metric value. So multipliy Butteraugli values by -1.
@@ -58,7 +103,7 @@ export async function autoOptimize(
bitmapOut = await decode(binaryOut); bitmapOut = await decode(binaryOut);
return -1 * comparator.distance(bitmapOut.data); return -1 * comparator.distance(bitmapOut.data);
}, },
otherOpts, binarySearchParams,
); );
comparator.delete(); comparator.delete();

View File

@@ -1,5 +1,44 @@
import { promises as fsp } from 'fs'; import { promises as fsp } from 'fs';
import { instantiateEmscriptenWasm, pathify } from './emscripten-utils.js'; import { instantiateEmscriptenWasm, pathify } from './emscripten-utils.js';
import { threads } from 'wasm-feature-detect';
import { cpus } from 'os';
// We use `navigator.hardwareConcurrency` for Emscriptens pthread pool size.
// This is the only workaround I can get working without crying.
(globalThis as any).navigator = {
hardwareConcurrency: cpus().length,
};
interface RotateModuleInstance {
exports: {
memory: WebAssembly.Memory;
rotate(width: number, height: number, rotate: number): void;
};
}
interface ResizeWithAspectParams {
input_width: number;
input_height: number;
target_width: number;
target_height: number;
}
interface ResizeInstantiateOptions {
width: number;
height: number;
method: string;
premultiply: boolean;
linearRGB: boolean;
}
declare global {
// Needed for being able to use ImageData as type in codec types
type ImageData = typeof import('./image_data.js');
// Needed for being able to assign to `globalThis.ImageData`
var ImageData: ImageData['constructor'];
}
import type { QuantizerModule } from '../../codecs/imagequant/imagequant.js';
// MozJPEG // MozJPEG
import mozEnc from '../../codecs/mozjpeg/enc/mozjpeg_node_enc.js'; import mozEnc from '../../codecs/mozjpeg/enc/mozjpeg_node_enc.js';
@@ -16,6 +55,9 @@ import webpDecWasm from 'asset-url:../../codecs/webp/dec/webp_node_dec.wasm';
// AVIF // AVIF
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 avifEncMtWorker from 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.worker.js';
import avifEncMtWasm from 'asset-url:../../codecs/avif/enc/avif_node_enc_mt.wasm';
import avifDec from '../../codecs/avif/dec/avif_node_dec.js'; import 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';
@@ -51,16 +93,22 @@ const resizePromise = resize.default(fsp.readFile(pathify(resizeWasm)));
// rotate // rotate
import rotateWasm from 'asset-url:../../codecs/rotate/rotate.wasm'; import rotateWasm from 'asset-url:../../codecs/rotate/rotate.wasm';
// TODO(ergunsh): Type definitions of some modules do not exist
// Figure out creating type definitions for them and remove `allowJs` rule
// We shouldn't need to use Promise<QuantizerModule> below after getting type definitions for imageQuant
// ImageQuant // ImageQuant
import imageQuant from '../../codecs/imagequant/imagequant_node.js'; import imageQuant from '../../codecs/imagequant/imagequant_node.js';
import imageQuantWasm from 'asset-url:../../codecs/imagequant/imagequant_node.wasm'; import imageQuantWasm from 'asset-url:../../codecs/imagequant/imagequant_node.wasm';
const imageQuantPromise = instantiateEmscriptenWasm(imageQuant, imageQuantWasm); const imageQuantPromise: Promise<QuantizerModule> = instantiateEmscriptenWasm(
imageQuant,
imageQuantWasm,
);
// Our decoders currently rely on a `ImageData` global. // Our decoders currently rely on a `ImageData` global.
import ImageData from './image_data.js'; import ImageData from './image_data.js';
globalThis.ImageData = ImageData; globalThis.ImageData = ImageData;
function resizeNameToIndex(name) { function resizeNameToIndex(name: string) {
switch (name) { switch (name) {
case 'triangle': case 'triangle':
return 0; return 0;
@@ -80,25 +128,26 @@ function resizeWithAspect({
input_height, input_height,
target_width, target_width,
target_height, target_height,
}) { }: ResizeWithAspectParams): { width: number; height: number } {
if (!target_width && !target_height) { if (!target_width && !target_height) {
throw Error('Need to specify at least width or height when resizing'); throw Error('Need to specify at least width or height when resizing');
} }
if (target_width && target_height) { if (target_width && target_height) {
return { width: target_width, height: target_height }; return { width: target_width, height: target_height };
} }
if (!target_width) { if (!target_width) {
return { return {
width: Math.round((input_width / input_height) * target_height), width: Math.round((input_width / input_height) * target_height),
height: target_height, height: target_height,
}; };
} }
if (!target_height) {
return { return {
width: target_width, width: target_width,
height: Math.round((input_height / input_width) * target_width), height: Math.round((input_height / input_width) * target_width),
}; };
}
} }
export const preprocessors = { export const preprocessors = {
@@ -108,10 +157,16 @@ export const preprocessors = {
instantiate: async () => { instantiate: async () => {
await resizePromise; await resizePromise;
return ( return (
buffer, buffer: Uint8Array,
input_width, input_width: number,
input_height, input_height: number,
{ width, height, method, premultiply, linearRGB }, {
width,
height,
method,
premultiply,
linearRGB,
}: ResizeInstantiateOptions,
) => { ) => {
({ width, height } = resizeWithAspect({ ({ width, height } = resizeWithAspect({
input_width, input_width,
@@ -148,7 +203,12 @@ export const preprocessors = {
description: 'Reduce the number of colors used (aka. paletting)', description: 'Reduce the number of colors used (aka. paletting)',
instantiate: async () => { instantiate: async () => {
const imageQuant = await imageQuantPromise; const imageQuant = await imageQuantPromise;
return (buffer, width, height, { numColors, dither }) => return (
buffer: Uint8Array,
width: number,
height: number,
{ numColors, dither }: { numColors: number; dither: number },
) =>
new ImageData( new ImageData(
imageQuant.quantize(buffer, width, height, numColors, dither), imageQuant.quantize(buffer, width, height, numColors, dither),
width, width,
@@ -164,13 +224,18 @@ export const preprocessors = {
name: 'Rotate', name: 'Rotate',
description: 'Rotate image', description: 'Rotate image',
instantiate: async () => { instantiate: async () => {
return async (buffer, width, height, { numRotations }) => { return async (
buffer: Uint8Array,
width: number,
height: number,
{ numRotations }: { numRotations: number },
) => {
const degrees = (numRotations * 90) % 360; const degrees = (numRotations * 90) % 360;
const sameDimensions = degrees == 0 || degrees == 180; const sameDimensions = degrees == 0 || degrees == 180;
const size = width * height * 4; const size = width * height * 4;
const { instance } = await WebAssembly.instantiate( const instance = (
await fsp.readFile(pathify(rotateWasm)), await WebAssembly.instantiate(await fsp.readFile(pathify(rotateWasm)))
); ).instance as RotateModuleInstance;
const { memory } = instance.exports; const { memory } = instance.exports;
const additionalPagesNeeded = Math.ceil( const additionalPagesNeeded = Math.ceil(
(size * 2 - memory.buffer.byteLength + 8) / (64 * 1024), (size * 2 - memory.buffer.byteLength + 8) / (64 * 1024),
@@ -228,7 +293,7 @@ export const codecs = {
webp: { webp: {
name: 'WebP', name: 'WebP',
extension: 'webp', extension: 'webp',
detectors: [/^RIFF....WEBPVP8[LX ]/], detectors: [/^RIFF....WEBPVP8[LX ]/s],
dec: () => instantiateEmscriptenWasm(webpDec, webpDecWasm), dec: () => instantiateEmscriptenWasm(webpDec, webpDecWasm),
enc: () => instantiateEmscriptenWasm(webpEnc, webpEncWasm), enc: () => instantiateEmscriptenWasm(webpEnc, webpEncWasm),
defaultEncoderOptions: { defaultEncoderOptions: {
@@ -271,7 +336,16 @@ export const codecs = {
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, avifDecWasm),
enc: () => instantiateEmscriptenWasm(avifEnc, avifEncWasm), enc: async () => {
if (await threads()) {
return instantiateEmscriptenWasm(
avifEncMt,
avifEncMtWasm,
avifEncMtWorker,
);
}
return instantiateEmscriptenWasm(avifEnc, avifEncWasm);
},
defaultEncoderOptions: { defaultEncoderOptions: {
cqLevel: 33, cqLevel: 33,
cqAlphaLevel: -1, cqAlphaLevel: -1,
@@ -286,8 +360,8 @@ export const codecs = {
}, },
autoOptimize: { autoOptimize: {
option: 'cqLevel', option: 'cqLevel',
min: 0, min: 62,
max: 62, max: 0,
}, },
}, },
jxl: { jxl: {
@@ -346,13 +420,18 @@ export const codecs = {
await pngEncDecPromise; await pngEncDecPromise;
await oxipngPromise; await oxipngPromise;
return { return {
encode: (buffer, width, height, opts) => { encode: (
buffer: Uint8Array,
width: number,
height: number,
opts: { level: number },
) => {
const simplePng = pngEncDec.encode( const simplePng = pngEncDec.encode(
new Uint8Array(buffer), new Uint8Array(buffer),
width, width,
height, height,
); );
return oxipng.optimise(simplePng, opts.level); return oxipng.optimise(simplePng, opts.level, false);
}, },
}; };
}, },

View File

@@ -1,16 +0,0 @@
import { fileURLToPath } from 'url';
export function pathify(path) {
if (path.startsWith('file://')) {
path = fileURLToPath(path);
}
return path;
}
export function instantiateEmscriptenWasm(factory, path) {
return factory({
locateFile() {
return pathify(path);
},
});
}

View File

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

View File

@@ -1,7 +0,0 @@
export default class ImageData {
constructor(data, width, height) {
this.data = data;
this.width = width;
this.height = height;
}
}

View File

@@ -0,0 +1,11 @@
export default class ImageData {
readonly data: Uint8ClampedArray;
readonly width: number;
readonly height: number;
constructor(data: Uint8ClampedArray, width: number, height: number) {
this.data = data;
this.width = width;
this.height = height;
}
}

View File

@@ -180,9 +180,9 @@ class Image {
encName, encName,
encConfig, encConfig,
optimizerButteraugliTarget: Number( optimizerButteraugliTarget: Number(
encodeOptions.optimizerButteraugliTarget, encodeOptions.optimizerButteraugliTarget ?? 1.4,
), ),
maxOptimizerRounds: Number(encodeOptions.maxOptimizerRounds), maxOptimizerRounds: Number(encodeOptions.maxOptimizerRounds ?? 6),
}); });
} }
await Promise.all(Object.values(this.encodedWith)); await Promise.all(Object.values(this.encodedWith));

43
libsquoosh/src/missing-types.d.ts vendored Normal file
View File

@@ -0,0 +1,43 @@
/// <reference path="../../missing-types.d.ts" />
declare module 'asset-url:*' {
const value: string;
export default value;
}
// Somehow TS picks up definitions from the module itself
// instead of using `asset-url:*`. It is probably related to
// specifity of the module declaration and these declarations below fix it
declare module 'asset-url:../../codecs/png/pkg/squoosh_png_bg.wasm' {
const value: string;
export default value;
}
declare module 'asset-url:../../codecs/oxipng/pkg/squoosh_oxipng_bg.wasm' {
const value: string;
export default value;
}
declare module 'asset-url:../../codecs/resize/pkg/squoosh_resize_bg.wasm' {
const value: string;
export default value;
}
declare module 'chunk-url:../../codecs/avif/enc/avif_node_enc_mt.worker.js' {
const value: string;
export default value;
}
// These don't exist in NodeJS types so we're not able to use them but they are referenced in some emscripten and codec types
// Thus, we need to explicitly assign them to be `never`
// We're also not able to use the APIs that use these types
// So, if we want to use those APIs we need to supply its dependencies ourselves
// However, probably those APIs are more suited to be used in web (i.e. there can be other
// dependencies to web APIs that might not work in Node)
type RequestInfo = never;
type Response = never;
type WebGLRenderingContext = never;
type MessageEvent = never;
type BufferSource = ArrayBufferView | ArrayBuffer;
type URL = import('url').URL;

Some files were not shown because too many files have changed in this diff Show More