Compare commits

...

272 Commits

Author SHA1 Message Date
Jake Archibald
1b7d3fa394 1.9.1 2020-02-27 11:37:53 +00:00
Jake Archibald
650db99818 Package-json update 2020-02-27 11:37:32 +00:00
Surma
7638bb795e Merge pull request #727 from GoogleChromeLabs/optipng
optipng build improvements
2020-02-27 12:19:22 +01:00
Ingvar Stepanyan
570e604be0 optipng: switch to bundled zlib and libpng
Benefits:
 - newer versions of the libraries
    - zlib: 1.2.8 -> 1.2.11
	- libpng: 1.6.18beta04 -> 1.6.34
 - much fewer dependencies to install (as libs are already in optipng archive and we don't need napa)
 - much smaller build thanks to customised versions of zlib and libpng containing only APIs necessary for optipng itself: 238950 -> 177359 bytes
 - much faster build thanks to preconfigured libpng and stripped APIs: 2m15s -> 40s
 - much simpler build script: 77 -> 46 lines
2020-02-25 18:45:47 +00:00
Ingvar Stepanyan
a056d1c363 Switch to make to build optipng
Mostly a build config simplification for now, no noticeable changes in time or output size.
2020-02-25 18:20:05 +00:00
Ingvar Stepanyan
fce61c8c89 Switch to emscripten-upstream
Before: 255184 bytes, 2m15s
After: 238270 bytes, 2m6s
2020-02-25 17:14:16 +00:00
Ingvar Stepanyan
d60d0ae47d Update Emscripten (1.39.4) 2020-02-25 16:53:27 +00:00
Jake Archibald
8bf741ed4e 1.9.0 2019-10-30 13:20:33 +00:00
Surma
9f660e5178 add maskable icon (#709)
add maskable icon
2019-10-30 12:25:41 +00:00
Masataka Yakura
ef2318bcc1 support maskable icon
add a new entry to the icons member referring the maskable icon file added in 364a5db
2019-10-30 19:47:45 +09:00
Masataka Yakura
364a5db5d5 add maskable icon file 2019-10-30 19:44:49 +09:00
Surma
4a01d0d548 Update dependency sass-loader to v7.3.1 (#685)
Update dependency sass-loader to v7.3.1
2019-08-22 11:40:24 +01:00
renovate[bot]
4a9c28f89f Update dependency sass-loader to v7.3.1 2019-08-21 16:19:16 +00:00
Surma
3aed873467 Update dependency tslint to v5.19.0 (#686)
Update dependency tslint to v5.19.0
2019-08-21 17:15:47 +01:00
renovate[bot]
2b7f059b8f Update dependency tslint to v5.19.0 2019-08-20 19:54:25 +00:00
Surma
33726e9c68 Update dependency husky to v3.0.4 (#683)
Update dependency husky to v3.0.4
2019-08-17 22:35:54 +01:00
renovate[bot]
3d29f486e7 Update dependency husky to v3.0.4 2019-08-17 13:28:25 +00:00
Surma
79e8a26f06 Update dependency readdirp to v3.1.2 (#681)
Update dependency readdirp to v3.1.2
2019-08-14 15:12:06 +01:00
renovate[bot]
e8efd54766 Update dependency readdirp to v3.1.2 2019-08-14 14:06:24 +00:00
Surma
edcc774c16 Update dependency webpack-dev-server to v3.8.0 (#679)
Update dependency webpack-dev-server to v3.8.0
2019-08-10 00:42:52 +01:00
renovate[bot]
746b33a590 Update dependency webpack-dev-server to v3.8.0 2019-08-09 17:20:25 +00:00
Surma
8a264efc67 Update dependency sass-loader to v7.2.0 (#678)
Update dependency sass-loader to v7.2.0
2019-08-09 14:35:29 +01:00
renovate[bot]
f0af6e97a0 Update dependency sass-loader to v7.2.0 2019-08-09 10:31:33 +00:00
Surma
043107c101 Update dependency file-loader to v4.2.0 (#675)
Update dependency file-loader to v4.2.0
2019-08-09 10:22:19 +01:00
renovate[bot]
e9e3b923e0 Update dependency file-loader to v4.2.0 2019-08-08 12:48:13 +00:00
Surma
aea6e91b1d Update dependency husky to v3.0.3 (#677)
Update dependency husky to v3.0.3
2019-08-08 13:46:49 +01:00
renovate[bot]
9c4717a13d Update dependency husky to v3.0.3 2019-08-08 12:43:05 +00:00
Surma
559cabce9e Update dependency @types/node to v10.14.15 (#676)
Update dependency @types/node to v10.14.15
2019-08-08 09:59:35 +01:00
renovate[bot]
7f6a3de7ca Update dependency @types/node to v10.14.15 2019-08-07 21:33:12 +00:00
Surma
f3adc87f52 Update Node.js to v10.16.2 (#674)
Update Node.js to v10.16.2
2019-08-07 14:34:18 +01:00
renovate[bot]
6499e9f63d Update Node.js to v10.16.2 2019-08-06 22:41:02 +00:00
Surma
ac1f104e49 Update dependency style-loader to v1 (#673)
Update dependency style-loader to v1
2019-08-06 18:11:48 +01:00
renovate[bot]
87f89e6b2f Update dependency style-loader to v1 2019-08-06 10:41:31 +00:00
Ingvar Stepanyan
7f6404d5a8 Delete .gitignore in resize codec 2019-08-05 15:13:40 +01:00
Ingvar Stepanyan
8e857cd393 Use native Wasm+Webpack support for Rust codecs
This delegates loading of Wasm modules to Webpack itself, making wrapper code simpler.

Emscripten-generated modules are still using custom loading glue as they're not compatible with Webpack.
2019-08-05 15:13:40 +01:00
Ingvar Stepanyan
b8f801333d Switch to prebuilt WABT and wasm-pack
Significantly speeds up `npm run build:image` commands (as they don't need to compile anything anymore) and slightly reduces size of Docker images:
 - `squoosh-rotate`: 1.87GB -> 1.84GB
 - `squoosh-resize`: 2.02GB -> 1.85GB
 - `squoosh-hqx`: 2.06GB -> 1.9GB
2019-08-02 17:23:09 +01:00
Ingvar Stepanyan
499956e216 Switch to stable Rust
Nightly is no longer necessary for wee_alloc, so we don't need to rely on unstable versions of Rust anymore.

As a bonus, this reduces size of each Rust-related Docker image by >1 GB:
 - `squoosh-rotate`: 2.94GB -> 1.87GB
 - `squoosh-resize`: 3.09GB -> 2.02GB
 - `squoosh-hqx`: 3.13GB -> 2.06GB
2019-08-02 16:14:44 +01:00
Surma
3932ee2c00 Rename resize package (#668)
Rename `resize` package
2019-08-02 15:21:41 +01:00
Ingvar Stepanyan
68177b7590 Rename resize package
Without this, it was creating files in `pkg` prefixed with `squooshresize`, which were ignored by Git.
2019-08-02 14:53:39 +01:00
Surma
0a2a4122dc Update build.sh 2019-08-02 12:47:12 +01:00
Surma
e6299569d0 Update dependency tslint to v5.18.0 (#633)
Update dependency tslint to v5.18.0
2019-08-01 09:27:35 +01:00
renovate[bot]
578ad8c291 Update dependency tslint to v5.18.0 2019-07-31 20:16:43 +00:00
Surma
eaa294b689 Merge pull request #667 from GoogleChromeLabs/renovate/node-10.x
Update Node.js to v10.16.1
2019-07-31 21:14:35 +01:00
renovate[bot]
6e97cfb8d5 Update Node.js to v10.16.1 2019-07-31 19:49:00 +00:00
Surma
80a68c48b2 Update dependency webpack-dev-server to v3.7.2 (#628)
Update dependency webpack-dev-server to v3.7.2
2019-07-31 15:16:17 +01:00
renovate[bot]
98865f8141 Update dependency webpack-dev-server to v3.7.2 2019-07-31 13:59:11 +00:00
Ingvar Stepanyan
78da9fd144 Fix build on Windows (#666)
* Use `require` for reading JSON

JSON is natively supported by Node.js, so no point in using a custom helper here.

* Fix build on Windows

Fixes #465.
2019-07-31 14:32:26 +01:00
Surma
94c50a989b Update dependency terser-webpack-plugin to v1.4.0 (#665)
Update dependency terser-webpack-plugin to v1.4.0
2019-07-31 14:08:09 +01:00
renovate[bot]
3e525e767c Update dependency terser-webpack-plugin to v1.4.1 2019-07-31 12:18:23 +00:00
Surma
dfcc1e24e4 Update dependency url-loader to v2.1.0 (#656)
Update dependency url-loader to v2.1.0
2019-07-30 19:12:05 +01:00
renovate[bot]
f3aa8edfca Update dependency url-loader to v2.1.0 2019-07-30 11:00:32 +00:00
Surma
ffd7ee6013 Update dependency webpack-bundle-analyzer to v3.4.1 (#664)
Update dependency webpack-bundle-analyzer to v3.4.1
2019-07-30 11:57:30 +01:00
renovate[bot]
64bb09dbc5 Update dependency webpack-bundle-analyzer to v3.4.1 2019-07-30 06:57:22 +00:00
Surma
35fcbc40ed Update dependency husky to v3.0.2 (#663)
Update dependency husky to v3.0.2
2019-07-29 18:35:05 +01:00
renovate[bot]
1b55a48680 Update dependency husky to v3.0.2 2019-07-29 17:32:53 +00:00
Surma
88fbb19d29 Update dependency @types/node to v10.14.13 (#654)
Update dependency @types/node to v10.14.13
2019-07-29 15:35:38 +01:00
renovate[bot]
3dc1501ff7 Update dependency @types/node to v10.14.13 2019-07-27 13:47:47 +00:00
Surma
b7576c559a Update dependency pretty-bytes to v5.3.0 (#662)
Update dependency pretty-bytes to v5.3.0
2019-07-27 14:46:39 +01:00
renovate[bot]
69ed2e1d56 Update dependency pretty-bytes to v5.3.0 2019-07-26 20:28:22 +00:00
Surma
55f7f3d72a Update dependency copy-webpack-plugin to v5.0.4 (#661)
Update dependency copy-webpack-plugin to v5.0.4
2019-07-26 12:05:12 +01:00
renovate[bot]
055d0b4ea1 Update dependency copy-webpack-plugin to v5.0.4 2019-07-26 10:42:39 +00:00
Surma
7735346ed0 Update dependency critters-webpack-plugin to v2.4.0 (#660)
Update dependency critters-webpack-plugin to v2.4.0
2019-07-23 19:47:12 +01:00
renovate[bot]
2a06fdbbe0 Update dependency critters-webpack-plugin to v2.4.0 2019-07-23 14:37:32 +00:00
Surma
19169199e9 Update dependency raw-loader to v3.1.0 (#657)
Update dependency raw-loader to v3.1.0
2019-07-19 12:02:05 +01:00
renovate[bot]
76f3c39b78 Update dependency raw-loader to v3.1.0 2019-07-18 23:26:24 +00:00
Surma
8da52acc4c Update dependency husky to v3.0.1 (#658)
Update dependency husky to v3.0.1
2019-07-19 00:23:39 +01:00
renovate[bot]
9253522a3a Update dependency husky to v3.0.1 2019-07-18 21:54:45 +00:00
Surma
cf39a3e5a5 Update dependency file-loader to v4.1.0 (#655)
Update dependency file-loader to v4.1.0
2019-07-18 22:52:27 +01:00
renovate[bot]
a08877f0ac Update dependency file-loader to v4.1.0 2019-07-18 11:14:26 +00:00
Surma
2f0dc1c067 Update dependency mini-css-extract-plugin to v0.8.0 (#653)
Update dependency mini-css-extract-plugin to v0.8.0
2019-07-17 16:37:17 +01:00
renovate[bot]
535d8c9142 Update dependency mini-css-extract-plugin to v0.8.0 2019-07-16 20:52:34 +00:00
Surma
6dc2ba3bef Update dependency @types/node to v10.14.12 (#646)
Update dependency @types/node to v10.14.12
2019-07-15 15:56:29 +01:00
renovate[bot]
93cfdc8f98 Update dependency @types/node to v10.14.12 2019-07-14 18:48:32 +00:00
Surma
671544349e Merge pull request #652 from GoogleChromeLabs/renovate/script-ext-html-webpack-plugin-2.x
Update dependency script-ext-html-webpack-plugin to v2.1.4
2019-07-14 19:47:18 +01:00
renovate[bot]
d3c1939692 Update dependency script-ext-html-webpack-plugin to v2.1.4 2019-07-12 20:36:17 +00:00
Surma
99642aef96 Update dependency typescript to v3.5.3 (#650)
Update dependency typescript to v3.5.3
2019-07-09 17:56:25 +01:00
renovate[bot]
ac4942b640 Update dependency typescript to v3.5.3 2019-07-08 22:36:40 +00:00
Surma
23cb004a86 Update dependency chokidar to v3.0.2 (#649)
Update dependency chokidar to v3.0.2
2019-07-08 11:49:13 +01:00
renovate[bot]
4b6de60978 Update dependency chokidar to v3.0.2 2019-07-08 10:33:35 +00:00
Surma
de204aa56a Update dependency readdirp to v3.1.1 (#648)
Update dependency readdirp to v3.1.1
2019-07-08 11:31:31 +01:00
renovate[bot]
976f811b36 Update dependency readdirp to v3.1.1 2019-07-06 23:55:06 +00:00
Surma
855fc9e602 Update dependency @types/node to v10.14.10 (#634)
Update dependency @types/node to v10.14.10
2019-07-03 10:34:32 +01:00
renovate[bot]
a8848e717c Update dependency @types/node to v10.14.10 2019-07-02 15:57:11 +00:00
Surma
90b99faf8b Update dependency travis-size-report to v1.1.0 (#625)
Update dependency travis-size-report to v1.1.0
2019-07-02 16:55:37 +01:00
renovate[bot]
d3cfffbbcf Update dependency travis-size-report to v1.1.0 2019-07-02 14:27:10 +00:00
Surma
fade7f9be8 Update dependency url-loader to v2.0.1 (#638)
Update dependency url-loader to v2.0.1
2019-07-02 15:24:31 +01:00
renovate[bot]
b10dd055d3 Update dependency url-loader to v2.0.1 2019-07-02 09:19:05 +00:00
Surma
7532f01222 Update dependency husky to v3 (#645)
Update dependency husky to v3
2019-07-02 12:12:33 +03:00
renovate[bot]
2df09efdee Update dependency husky to v3 2019-07-01 21:30:38 +00:00
Surma
bed4f49a12 Merge pull request #640 from GoogleChromeLabs/renovate/husky-2.x
Update dependency husky to v2.7.0
2019-06-27 18:44:45 +03:00
renovate[bot]
2b7fefff8b Update dependency husky to v2.7.0 2019-06-27 15:40:39 +00:00
Surma
bdcf2519ce Merge pull request #630 from GoogleChromeLabs/the-nittest-of-nits
Fix nit
2019-06-19 14:04:34 +01:00
Mathias Bynens
3f8afb72a6 Fix nit 2019-06-19 14:44:23 +02:00
Jake Archibald
2f834db224 Quick doc fix 2019-06-19 13:02:59 +01:00
Jake Archibald
3541ce7492 1.8.1 2019-06-19 12:25:18 +01:00
Jake Archibald
b6fae0eb4c Hqx fix (#629)
* Optimising wasm so it doesn't break Chrome

* Simpler dockerfile (thanks surma!)
2019-06-19 12:24:24 +01:00
Surma
fbaa282f07 Update dependency ts-loader to v6.0.3 (#627)
Update dependency ts-loader to v6.0.3
2019-06-18 12:56:24 +01:00
renovate[bot]
6136ae7411 Update dependency ts-loader to v6.0.3 2019-06-18 11:30:57 +00:00
Jake Archibald
f024747299 1.8.0 2019-06-18 12:24:41 +01:00
Surma
66feffcc49 Add Hq{2,3,4}x (#624)
* Scaling works on native

* It works in wasm

* Integrate with UI

* Remove benchmark

* Integrate Hqx into Resizer module

* Link against repo for hqx

* Remove unused defaultOpts

* Re-add test file

* Adding size dropdown

* Chrome: go and sit on the naughty step

* Better docs

* Review

* Add link to crbug

* Update src/codecs/processor-worker/index.ts

Co-Authored-By: Jake Archibald <jaffathecake@gmail.com>

* Terminate worker inbetween resize jobs
2019-06-18 12:23:22 +01:00
Jake Archibald
24254df7db 1.7.0 2019-06-17 09:43:02 +01:00
Jake Archibald
cae73f1f1b Add share target (#469)
* Quick test

* More testing

* More testing

* Removing transfer for now

* Changing name so it's easier to tell them apart when installed

* Disable minification to ease debugging

* Adding navigate lock

* lol oops

* Add minifying back

* Removing minification again, for debugging

* Removing broadcast channel bits, to simplify the code

* Revert "Removing broadcast channel bits, to simplify the code"

This reverts commit 0b2a3ecf2986aae0dd65fdd1ddda2bd9e4e1eac7.

* I think this fixes it

* Refactor

* Suppress flash of home screen during share target

* Almost ready, so switching to real name

* Removing log

* Ahh yes the trailing comma thing

* Removing use of BroadcastChannel

* Reducing ternary
2019-06-17 09:42:10 +01:00
Surma
073a52213e Update dependency ejs to v2.6.2 (#623)
Update dependency ejs to v2.6.2
2019-06-16 11:33:13 +01:00
renovate[bot]
0d34485a00 Update dependency ejs to v2.6.2 2019-06-15 15:29:04 +00:00
Surma
65519d539e Update dependency tslint-config-semistandard to v8.0.1 (#621)
Update dependency tslint-config-semistandard to v8.0.1
2019-06-14 16:37:05 +01:00
renovate[bot]
ec44a8e817 Update dependency tslint-config-semistandard to v8.0.1 2019-06-14 11:19:50 +00:00
Surma
920f225cb0 Update Node.js to v10.16.0 (#602)
Update Node.js to v10.16.0
2019-06-14 12:18:13 +01:00
renovate[bot]
d5d4bd61ff Update Node.js to v10.16.0 2019-06-13 22:32:04 +00:00
Surma
5656d10b67 Update dependency typescript to v3.5.2 (#620)
Update dependency typescript to v3.5.2
2019-06-13 23:30:52 +01:00
renovate[bot]
adb4b6468b Update dependency typescript to v3.5.2 2019-06-13 17:50:44 +00:00
Surma
0a293d7f63 Update dependency webpack-dev-server to v3.7.1 (#607)
Update dependency webpack-dev-server to v3.7.1
2019-06-13 16:23:36 +01:00
renovate[bot]
27cb5afd5b Update dependency webpack-dev-server to v3.7.1 2019-06-12 09:52:59 +00:00
Surma
5e58fc6b04 Update dependency webpack-cli to v3.3.4 (#616)
Update dependency webpack-cli to v3.3.4
2019-06-12 10:49:55 +01:00
renovate[bot]
e820adfc96 Update dependency webpack-cli to v3.3.4 2019-06-12 09:17:55 +00:00
Surma
5a8a114dcb Update dependency husky to v2.4.1 (#618)
Update dependency husky to v2.4.1
2019-06-12 10:15:05 +01:00
renovate[bot]
a1c685e820 Update dependency husky to v2.4.1 2019-06-12 09:03:46 +00:00
Surma
377dcfcc1b Update dependency @webpack-cli/serve to v0.1.8 (#615)
Update dependency @webpack-cli/serve to v0.1.8
2019-06-12 10:01:36 +01:00
renovate[bot]
913e67ca93 Update dependency @webpack-cli/serve to v0.1.8 2019-06-07 11:40:52 +00:00
Surma
fb1a97c7d4 Update dependency readdirp to v3.0.2 (#609)
Update dependency readdirp to v3.0.2
2019-06-06 17:43:12 +02:00
renovate[bot]
42eef6945d Update dependency readdirp to v3.0.2 2019-06-06 15:32:57 +00:00
Surma
d04b08d640 Update dependency chokidar to v3.0.1 (#610)
Update dependency chokidar to v3.0.1
2019-06-06 17:30:35 +02:00
renovate[bot]
c547dd10d3 Update dependency chokidar to v3.0.1 2019-06-06 15:08:02 +00:00
Surma
f4579da9c9 Update dependency husky to v2.4.0 (#614)
Update dependency husky to v2.4.0
2019-06-06 17:05:49 +02:00
renovate[bot]
37dc585d80 Update dependency husky to v2.4.0 2019-06-06 13:22:43 +00:00
Surma
bc0a425a0f Merge pull request #613 from GoogleChromeLabs/renovate/url-loader-2.x
Update dependency url-loader to v2
2019-06-06 15:19:57 +02:00
renovate[bot]
b696f246a1 Update dependency url-loader to v2 2019-06-06 12:54:19 +00:00
Surma
e6e197e140 Merge pull request #612 from GoogleChromeLabs/renovate/file-loader-4.x
Update dependency file-loader to v4
2019-06-06 14:48:13 +02:00
renovate[bot]
1c0e8a1fd3 Update dependency file-loader to v4 2019-06-06 11:51:55 +00:00
Surma
e3e154fa1a Update dependency raw-loader to v3 (#611)
Update dependency raw-loader to v3
2019-06-06 13:46:28 +02:00
renovate[bot]
73b7c437f9 Update dependency raw-loader to v3 2019-06-05 10:18:17 +00:00
Surma
719168be77 Merge pull request #606 from GoogleChromeLabs/renovate/ts-loader-6.x
Update dependency ts-loader to v6.0.2
2019-05-31 08:31:41 +01:00
renovate[bot]
a2021b175c Update dependency ts-loader to v6.0.2 2019-05-31 04:42:55 +00:00
Surma
9a388fbd13 Update dependency tslint to v5.17.0 (#605)
Update dependency tslint to v5.17.0
2019-05-30 22:58:11 +01:00
renovate[bot]
1ba0452540 Update dependency tslint to v5.17.0 2019-05-30 20:14:45 +00:00
Surma
73df9f18f0 Update dependency typescript to v3.5.1 (#603)
Update dependency typescript to v3.5.1
2019-05-29 17:40:23 +01:00
renovate[bot]
0e7521877b Update dependency typescript to v3.5.1 2019-05-29 16:35:42 +00:00
Surma
120b37c192 Merge pull request #601 from GoogleChromeLabs/renovate/mini-css-extract-plugin-0.x
Update dependency mini-css-extract-plugin to v0.7.0
2019-05-27 17:56:42 +01:00
renovate[bot]
5f3502b838 Update dependency mini-css-extract-plugin to v0.7.0 2019-05-27 16:17:10 +00:00
Surma
33f99432c5 Update dependency terser-webpack-plugin to v1.3.0 (#600)
Update dependency terser-webpack-plugin to v1.3.0
2019-05-24 17:00:36 +01:00
renovate[bot]
85756ff5df Update dependency terser-webpack-plugin to v1.3.0 2019-05-24 13:30:05 +00:00
Surma
84e567ad6a Update dependency gzip-size to v5.1.1 (#598)
Update dependency gzip-size to v5.1.1
2019-05-22 10:25:46 +01:00
renovate[bot]
39281331fa Update dependency gzip-size to v5.1.1 2019-05-21 04:42:37 +00:00
Surma
438ce2ce63 Pin dependency travis-size-report to 1.0.1 (#597)
Pin dependency travis-size-report to 1.0.1
2019-05-20 12:43:52 +01:00
renovate[bot]
a13e17e256 Pin dependency travis-size-report to 1.0.1 2019-05-20 10:31:09 +00:00
Jake Archibald
cd6db2d776 Using travis-size-report (#596)
* Using travis-size-report

* No maps in size report
2019-05-20 11:29:43 +01:00
Cătălin Mariș
a08662b617 Further optimize logo.svg (#584) 2019-05-20 10:57:24 +01:00
Surma
003ec9de35 Update dependency ts-loader to v6.0.1 (#595)
Update dependency ts-loader to v6.0.1
2019-05-19 16:45:55 +01:00
renovate[bot]
e7f76ca0b8 Update dependency ts-loader to v6.0.1 2019-05-19 02:21:11 +00:00
Surma
21111e2927 Update dependency @types/node to v10.14.7 (#592)
Update dependency @types/node to v10.14.7
2019-05-17 23:44:21 +01:00
renovate[bot]
445c3ef32c Update dependency @types/node to v10.14.7 2019-05-17 21:29:24 +00:00
Surma
9df5542ee1 Update dependency webpack-dev-server to v3.4.1 (#591)
Update dependency webpack-dev-server to v3.4.1
2019-05-17 18:05:38 +01:00
renovate[bot]
fffc4a0cd1 Update dependency webpack-dev-server to v3.4.1 2019-05-17 17:00:55 +00:00
Surma
50a8743be3 Update dependency node-fetch to v2.6.0 (#589)
Update dependency node-fetch to v2.6.0
2019-05-17 14:54:38 +01:00
renovate[bot]
8480bc7dbd Update dependency node-fetch to v2.6.0 2019-05-17 13:51:46 +00:00
Surma
72e4546922 Update dependency ts-loader to v6 (#582)
Update dependency ts-loader to v6
2019-05-17 14:50:49 +01:00
renovate[bot]
11be5babca Update dependency ts-loader to v6 2019-05-17 13:36:00 +00:00
Surma
17e5db2427 Update dependency webpack-dev-server to v3.4.0 (#590)
Update dependency webpack-dev-server to v3.4.0
2019-05-17 14:34:01 +01:00
renovate[bot]
465093eb07 Update dependency webpack-dev-server to v3.4.0 2019-05-17 12:46:02 +00:00
Surma
b430ac1041 Update dependency terser-webpack-plugin to v1.2.4 (#588)
Update dependency terser-webpack-plugin to v1.2.4
2019-05-15 13:45:55 +01:00
renovate[bot]
ea96847c1e Update dependency terser-webpack-plugin to v1.2.4 2019-05-15 08:20:25 +00:00
Surma
3b5106a61d Update dependency husky to v2.3.0 (#587)
Update dependency husky to v2.3.0
2019-05-14 17:55:04 +01:00
renovate[bot]
9f611b0b52 Update dependency husky to v2.3.0 2019-05-14 16:47:41 +00:00
Surma
18a6b3c3e5 Update dependency husky to v2 (#567)
Update dependency husky to v2
2019-05-05 13:09:06 -07:00
renovate[bot]
d9ed4e18ea Update dependency husky to v2 2019-05-05 19:59:35 +00:00
Surma
9e757aa896 Update dependency chokidar to v3 (#575)
Update dependency chokidar to v3
2019-05-05 12:57:03 -07:00
renovate[bot]
89b58bb446 Update dependency chokidar to v3 2019-05-05 19:50:09 +00:00
Surma
e80ca583cc Update dependency ts-loader to v5.4.5 (#570)
Update dependency ts-loader to v5.4.5
2019-05-05 12:48:15 -07:00
renovate[bot]
2ecc81b34f Update dependency ts-loader to v5.4.5 2019-05-05 19:37:22 +00:00
Surma
60e98ee34f Update dependency readdirp to v3.0.1 (#568)
Update dependency readdirp to v3.0.1
2019-05-05 12:36:14 -07:00
renovate[bot]
538ea89ea9 Update dependency readdirp to v3.0.1 2019-05-05 19:25:37 +00:00
Surma
3990e11e0a Update dependency node-fetch to v2.5.0 (#569)
Update dependency node-fetch to v2.5.0
2019-05-05 12:23:29 -07:00
renovate[bot]
0251f88fe5 Update dependency node-fetch to v2.5.0 2019-05-05 19:15:45 +00:00
Surma
bbcb959b11 Update dependency webpack-dev-server to v3.3.1 (#539)
Update dependency webpack-dev-server to v3.3.1
2019-05-05 12:14:34 -07:00
renovate[bot]
b5e928bac9 Update dependency webpack-dev-server to v3.3.1 2019-05-05 13:46:57 +00:00
Surma
6592dee4a9 Update dependency webpack-cli to v3.3.2 (#578)
Update dependency webpack-cli to v3.3.2
2019-05-05 06:43:42 -07:00
renovate[bot]
9b3d72191e Update dependency webpack-cli to v3.3.2 2019-05-04 20:21:13 +00:00
Surma
a92e5b48ff Update dependency node-sass to v4.12.0 (#572)
Update dependency node-sass to v4.12.0
2019-04-30 20:08:58 +01:00
renovate[bot]
e355764ab0 Update dependency node-sass to v4.12.0 2019-04-30 09:20:29 +00:00
Surma
c5efd5a8bf Update dependency @types/node to v10.14.6 (#571)
Update dependency @types/node to v10.14.6
2019-04-30 10:18:47 +01:00
renovate[bot]
385461944b Update dependency @types/node to v10.14.6 2019-04-26 19:48:01 +00:00
Surma
8385ba3274 Update dependency copy-webpack-plugin to v5.0.3 (#566)
Update dependency copy-webpack-plugin to v5.0.3
2019-04-24 16:24:54 +01:00
renovate[bot]
6ca6a77595 Update dependency copy-webpack-plugin to v5.0.3 2019-04-24 15:22:25 +00:00
Surma
14c837e894 Update dependency webpack-cli to v3.3.1 (#558)
Update dependency webpack-cli to v3.3.1
2019-04-24 15:32:24 +01:00
renovate[bot]
33346d7cb6 Update dependency webpack-cli to v3.3.1 2019-04-24 10:09:19 +00:00
Surma
05d4f531e2 Update dependency pretty-bytes to v5.2.0 (#565)
Update dependency pretty-bytes to v5.2.0
2019-04-24 11:07:25 +01:00
renovate[bot]
ede2c49b12 Update dependency pretty-bytes to v5.2.0 2019-04-24 03:30:07 +00:00
Surma
16acd32c68 Update dependency typescript to v3.4.5 (#562)
Update dependency typescript to v3.4.5
2019-04-23 23:29:37 +01:00
renovate[bot]
cc90192860 Update dependency typescript to v3.4.5 2019-04-23 22:22:19 +00:00
Surma
3607005fa8 Update dependency ts-loader to v5.4.3 (#561)
Update dependency ts-loader to v5.4.3
2019-04-23 23:21:05 +01:00
renovate[bot]
7646a64f94 Update dependency ts-loader to v5.4.3 2019-04-22 20:40:38 +00:00
Surma
c8ce6ce27b Merge pull request #556 from GoogleChromeLabs/renovate/node-10.x
Update dependency @types/node to v10.14.5
2019-04-19 22:18:51 +01:00
renovate[bot]
1240474c4b Update dependency @types/node to v10.14.5 2019-04-19 20:50:39 +00:00
Surma
f5e84441c0 Merge pull request #555 from GoogleChromeLabs/renovate/typescript-3.x
Update dependency typescript to v3.4.4
2019-04-19 12:24:46 +01:00
renovate[bot]
6bc19d78bc Update dependency typescript to v3.4.4 2019-04-18 23:28:35 +00:00
Surma
a4437d2873 Merge pull request #554 from GoogleChromeLabs/renovate/webcomponents-custom-elements-1.x
Update dependency @webcomponents/custom-elements to v1.2.4
2019-04-19 00:26:44 +01:00
renovate[bot]
cd19650748 Update dependency @webcomponents/custom-elements to v1.2.4 2019-04-18 21:26:08 +00:00
Surma
253315b3b1 Merge pull request #552 from GoogleChromeLabs/renovate/readdirp-3.x
Update dependency readdirp to v3
2019-04-17 23:11:50 +01:00
renovate[bot]
4203ad9a13 Update dependency readdirp to v3 2019-04-17 17:52:20 +00:00
Surma
6958202f9d Merge pull request #551 from GoogleChromeLabs/renovate/escape-string-regexp-2.x
Update dependency escape-string-regexp to v2
2019-04-17 12:44:59 +01:00
renovate[bot]
386fef063f Update dependency escape-string-regexp to v2 2019-04-17 07:51:24 +00:00
Surma
d7246ca427 Merge pull request #549 from GoogleChromeLabs/renovate/tslint-5.x
Update dependency tslint to v5.16.0
2019-04-16 23:57:41 +01:00
renovate[bot]
fd024853b6 Update dependency tslint to v5.16.0 2019-04-16 22:24:34 +00:00
Surma
bd53d17876 Merge pull request #548 from GoogleChromeLabs/renovate/webcomponents-custom-elements-1.x
Update dependency @webcomponents/custom-elements to v1.2.3
2019-04-16 08:47:51 +01:00
renovate[bot]
3f87f571f4 Update dependency @webcomponents/custom-elements to v1.2.3 2019-04-16 04:18:42 +00:00
Surma
1c69a6f1b7 Merge pull request #547 from GoogleChromeLabs/renovate/gzip-size-5.x
Update dependency gzip-size to v5.1.0
2019-04-15 10:01:59 +01:00
renovate[bot]
82419cbb6e Update dependency gzip-size to v5.1.0 2019-04-15 03:27:15 +00:00
Surma
db65630c8d Merge pull request #546 from GoogleChromeLabs/renovate/webpack-bundle-analyzer-3.x
Update dependency webpack-bundle-analyzer to v3.3.2
2019-04-12 04:24:49 -04:00
renovate[bot]
b8e54b947f Update dependency webpack-bundle-analyzer to v3.3.2 2019-04-11 14:33:46 +00:00
Surma
f23897108d Merge pull request #545 from GoogleChromeLabs/renovate/webpack-bundle-analyzer-3.x
Update dependency webpack-bundle-analyzer to v3.3.0
2019-04-10 09:31:01 -04:00
renovate[bot]
1efe5b21f0 Update dependency webpack-bundle-analyzer to v3.3.0 2019-04-10 13:08:07 +00:00
Surma
fc71b4d249 Merge pull request #544 from GoogleChromeLabs/renovate/tslint-config-semistandard-8.x
Update dependency tslint-config-semistandard to v8
2019-04-10 09:06:26 -04:00
renovate[bot]
dc81d46556 Update dependency tslint-config-semistandard to v8 2019-04-10 12:47:55 +00:00
Surma
56c2080f43 Merge pull request #543 from GoogleChromeLabs/renovate/mini-css-extract-plugin-0.x
Update dependency mini-css-extract-plugin to v0.6.0
2019-04-10 08:44:19 -04:00
renovate[bot]
4cc50fcaa5 Update dependency mini-css-extract-plugin to v0.6.0 2019-04-10 10:41:06 +00:00
Surma
2d67562576 Merge pull request #542 from GoogleChromeLabs/renovate/typescript-3.x
Update dependency typescript to v3.4.3
2019-04-09 22:57:59 -04:00
renovate[bot]
76f2d7afa7 Update dependency typescript to v3.4.3 2019-04-09 23:34:01 +00:00
Jake Archibald
80fa9c4f21 Fixing quote type 2019-04-08 12:35:05 -04:00
A2ZH
8f215a5b4b fix select the same file bug (#538) 2019-04-08 12:33:07 -04:00
Surma
bffd9cb52a Merge pull request #536 from GoogleChromeLabs/renovate/webpack-bundle-analyzer-3.x
Update dependency webpack-bundle-analyzer to v3.2.0
2019-04-06 13:21:28 +01:00
renovate[bot]
74b1ff5b10 Update dependency webpack-bundle-analyzer to v3.2.0 2019-04-06 11:32:43 +00:00
Surma
3c0079fea0 Merge pull request #534 from GoogleChromeLabs/renovate/webassembly-js-api-0.x
Update dependency @types/webassembly-js-api to v0.0.3
2019-04-06 12:31:03 +01:00
renovate[bot]
c0a9723d20 Update dependency @types/webassembly-js-api to v0.0.3 2019-04-06 10:54:58 +00:00
Surma
a4f0a76200 Merge pull request #535 from GoogleChromeLabs/renovate/typescript-3.x
Update dependency typescript to v3.4.2
2019-04-06 11:54:14 +01:00
renovate[bot]
eb57b0130b Update dependency typescript to v3.4.2 2019-04-05 21:16:13 +00:00
Surma
f8c37c7abc Merge pull request #528 from GoogleChromeLabs/renovate/tslint-react-4.x
Update dependency tslint-react to v4
2019-04-02 11:06:56 +01:00
renovate[bot]
72373f8812 Update dependency tslint-react to v4 2019-04-02 09:53:36 +00:00
Surma
b285e99e7d Merge pull request #533 from GoogleChromeLabs/renovate/tslint-5.x
Update dependency tslint to v5.15.0
2019-04-02 10:51:06 +01:00
renovate[bot]
f9a6b88bb6 Update dependency tslint to v5.15.0 2019-04-01 23:03:59 +00:00
Surma
4a65d506f2 Merge pull request #529 from GoogleChromeLabs/renovate/webcomponents-custom-elements-1.x
Update dependency @webcomponents/custom-elements to v1.2.2
2019-04-01 16:11:35 +01:00
renovate[bot]
3bc03c90fd Update dependency @webcomponents/custom-elements to v1.2.2 2019-04-01 14:48:19 +00:00
Surma
c35dfa4ac5 Merge pull request #532 from GoogleChromeLabs/renovate/idb-keyval-3.x
Update dependency idb-keyval to v3.2.0
2019-04-01 15:47:14 +01:00
renovate[bot]
1d2a9a9dde Update dependency idb-keyval to v3.2.0 2019-04-01 13:41:10 +00:00
Surma
925220bb13 Merge pull request #531 from GoogleChromeLabs/renovate/typescript-3.x
Update dependency typescript to v3.4.1
2019-03-31 19:21:09 +01:00
renovate[bot]
16d088bfe3 Update dependency typescript to v3.4.1 2019-03-29 17:49:28 +00:00
Surma
b8e22ee435 Merge pull request #527 from GoogleChromeLabs/renovate/node-10.x
Update dependency @types/node to v10.14.4
2019-03-25 23:05:18 +00:00
renovate[bot]
60dea4b932 Update dependency @types/node to v10.14.4 2019-03-25 20:47:44 +00:00
Surma
8ae1a42e4b Merge pull request #525 from GoogleChromeLabs/renovate/chokidar-2.x
Update dependency chokidar to v2.1.5
2019-03-23 10:21:50 +00:00
renovate[bot]
cfdc7a46e6 Update dependency chokidar to v2.1.5 2019-03-22 21:21:55 +00:00
Surma
afb23adcbf Merge pull request #524 from GoogleChromeLabs/renovate/node-10.x
Update dependency @types/node to v10.14.3
2019-03-22 19:24:36 +00:00
renovate[bot]
3b84a474b8 Update dependency @types/node to v10.14.3 2019-03-22 19:20:48 +00:00
Surma
e96bb04e88 Merge pull request #523 from GoogleChromeLabs/renovate/copy-webpack-plugin-5.x
Update dependency copy-webpack-plugin to v5.0.2
2019-03-22 16:34:45 +00:00
renovate[bot]
2e3b8507b2 Update dependency copy-webpack-plugin to v5.0.2 2019-03-22 15:56:33 +00:00
Surma
e12c69f1a6 Merge pull request #522 from GoogleChromeLabs/renovate/chokidar-2.x
Update dependency chokidar to v2.1.4
2019-03-22 10:51:12 +00:00
renovate[bot]
d049a23469 Update dependency chokidar to v2.1.4 2019-03-22 10:15:07 +00:00
Surma
2633f427c8 Merge pull request #521 from GoogleChromeLabs/renovate/node-10.x
Update dependency @types/node to v10.14.2
2019-03-22 00:08:32 +00:00
renovate[bot]
ff920f1d7b Update dependency @types/node to v10.14.2 2019-03-21 23:02:17 +00:00
Surma
fd69560025 Merge pull request #515 from GoogleChromeLabs/renovate/webpack-cli-serve-0.x
Update dependency @webpack-cli/serve to v0.1.5
2019-03-20 18:02:40 +00:00
renovate[bot]
ba51e47e05 Update dependency @webpack-cli/serve to v0.1.5 2019-03-20 17:56:47 +00:00
Surma
409f552274 Merge pull request #519 from GoogleChromeLabs/renovate/typescript-3.x
Update dependency typescript to v3.3.4000
2019-03-20 17:55:12 +00:00
renovate[bot]
69a7c184bd Update dependency typescript to v3.3.4000 2019-03-20 17:10:39 +00:00
Surma
3039c84738 Merge pull request #514 from GoogleChromeLabs/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.0
2019-03-20 17:08:22 +00:00
renovate[bot]
bfa5cd085d Update dependency webpack-cli to v3.3.0 2019-03-20 10:49:10 +00:00
Surma
7c282b30b1 Merge pull request #518 from GoogleChromeLabs/renovate/raw-loader-2.x
Update dependency raw-loader to v2
2019-03-18 15:11:32 +00:00
renovate[bot]
06fa3c541e Update dependency raw-loader to v2 2019-03-18 13:26:03 +00:00
Surma
c9e31ac1f7 Merge pull request #512 from GoogleChromeLabs/renovate/node-10.x
Update dependency @types/node to v10.14.1
2019-03-13 11:38:42 +00:00
renovate[bot]
e248486d3d Update dependency @types/node to v10.14.1 2019-03-13 09:16:26 +00:00
Surma
b32a52d236 Merge pull request #513 from GoogleChromeLabs/renovate/tslint-5.x
Update dependency tslint to v5.14.0
2019-03-13 09:15:28 +00:00
renovate[bot]
a24b4d6d4d Update dependency tslint to v5.14.0 2019-03-13 04:24:01 +00:00
Jake Archibald
b831aa0075 1.6.0 2019-03-12 14:10:19 +00:00
Surma
bf4d4b78cb Implement sRGB color conversion (#510)
* Add sRGB -> RGB conversion before resize

* Add clamping for color space conversions

* Clip for demultiplication as well

* Fixing linear <-> srgb conversion

* Update benchmark

* Decouple srgb calculations

* Generate lookup tables

* Update src/codecs/resize/options.tsx

* Defaulting on, renaming, removing redundant state
2019-03-12 14:09:35 +00:00
Surma
496896e36e Merge pull request #511 from GoogleChromeLabs/renovate/copy-webpack-plugin-5.x
Update dependency copy-webpack-plugin to v5.0.1
2019-03-11 18:22:51 +00:00
renovate[bot]
6b88ec1f8a Update dependency copy-webpack-plugin to v5.0.1 2019-03-11 13:24:05 +00:00
Surma
3af5f3a96d Merge pull request #508 from GoogleChromeLabs/renovate/typed-css-modules-0.x
Update dependency typed-css-modules to v0.4.2
2019-03-09 18:21:29 +00:00
renovate[bot]
ddc5564515 Update dependency typed-css-modules to v0.4.2 2019-03-08 11:22:51 +00:00
Jake Archibald
bc5da7ef06 1.5.0 2019-03-08 11:19:34 +00:00
Surma
45221c0b03 Implement alpha premultiplication (#507)
* Implement alpha premultiplication

* Add benchmark to resize

* Only display "Premultiply alpha" if it's one of the rust resize types.

* Add comment about division by zero
2019-03-08 11:18:59 +00:00
Surma
d29cf2ffa7 Merge pull request #501 from GoogleChromeLabs/renovate/node-10.x
Update dependency @types/node to v10.12.30
2019-03-06 22:02:52 +00:00
renovate[bot]
f6c0b89d1f Update dependency @types/node to v10.12.30 2019-03-06 20:14:19 +00:00
Jake Archibald
ecd9e06665 1.4.0 2019-03-06 17:20:54 +00:00
Jake Archibald
9e5b66d5f4 Better resize methods (#498)
* Port resize to wasm

* Expose resize algorithms

* Lanczos3 working!

* lol copy paste

* Adding support for other resizers

* Don’t track generated README

* Cache wasm instance
2019-03-06 17:20:25 +00:00
Surma
8c35c3cdaa Merge pull request #497 from GoogleChromeLabs/renovate/node-10.x
Update Node.js to v10.15.3
2019-03-05 23:26:23 +00:00
renovate[bot]
828a6240fe Update Node.js to v10.15.3 2019-03-05 20:51:40 +00:00
68 changed files with 6338 additions and 6945 deletions

2
.nvmrc
View File

@@ -1 +1 @@
10.15.2 10.16.2

5
codecs/hqx/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
**/*.rs.bk
target
Cargo.lock
bin/
pkg/README.md

37
codecs/hqx/Cargo.toml Normal file
View File

@@ -0,0 +1,37 @@
[package]
name = "squooshhqx"
version = "0.1.0"
authors = ["Surma <surma@surma.link>"]
[lib]
crate-type = ["cdylib"]
[features]
default = ["console_error_panic_hook", "wee_alloc"]
[dependencies]
cfg-if = "0.1.2"
wasm-bindgen = "0.2.38"
# lazy_static = "1.0.0"
hqx = {git = "https://github.com/CryZe/wasmboy-rs", tag="v0.1.2"}
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.1", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.2", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.2"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
lto = true

12
codecs/hqx/Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM rust
RUN rustup target add wasm32-unknown-unknown
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
RUN mkdir /opt/binaryen && \
curl -L https://github.com/WebAssembly/binaryen/releases/download/1.38.32/binaryen-1.38.32-x86-linux.tar.gz | tar -xzf - -C /opt/binaryen --strip 1
RUN mkdir /opt/wabt && \
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.11/wabt-1.0.11-linux.tar.gz | tar -xzf - -C /opt/wabt --strip 1
ENV PATH="/opt/binaryen:/opt/wabt:${PATH}"
WORKDIR /src

25
codecs/hqx/build.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -e
echo "============================================="
echo "Compiling wasm"
echo "============================================="
(
wasm-pack build
wasm-strip pkg/squooshhqx_bg.wasm
echo "Optimising Wasm so it doesn't break Chrome (this takes like 10-15mins. get a cup of tea)"
echo "Once https://crbug.com/974804 is fixed, we can remove this step"
wasm-opt -Os --no-validation -o pkg/squooshhqx_bg.wasm pkg/squooshhqx_bg.wasm
rm pkg/.gitignore
)
echo "============================================="
echo "Compiling wasm done"
echo "============================================="
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "Did you update your docker image?"
echo "Run \`docker pull ubuntu\`"
echo "Run \`docker pull rust\`"
echo "Run \`docker build -t squoosh-hqx .\`"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

4
codecs/hqx/package-lock.json generated Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "hqx",
"lockfileVersion": 1
}

7
codecs/hqx/package.json Normal file
View File

@@ -0,0 +1,7 @@
{
"name": "hqx",
"scripts": {
"build:image": "docker build -t squoosh-hqx .",
"build": "docker run --rm -v $(pwd):/src squoosh-hqx ./build.sh"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "squooshhqx",
"collaborators": [
"Surma <surma@surma.link>"
],
"version": "0.1.0",
"files": [
"squooshhqx_bg.wasm",
"squooshhqx.js",
"squooshhqx.d.ts"
],
"module": "squooshhqx.js",
"types": "squooshhqx.d.ts",
"sideEffects": "false"
}

9
codecs/hqx/pkg/squooshhqx.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/* tslint:disable */
/**
* @param {Uint32Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} factor
* @returns {Uint32Array}
*/
export function resize(input_image: Uint32Array, input_width: number, input_height: number, factor: number): Uint32Array;

View File

@@ -0,0 +1,46 @@
import * as wasm from './squooshhqx_bg.wasm';
let cachegetUint32Memory = null;
function getUint32Memory() {
if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) {
cachegetUint32Memory = new Uint32Array(wasm.memory.buffer);
}
return cachegetUint32Memory;
}
let WASM_VECTOR_LEN = 0;
function passArray32ToWasm(arg) {
const ptr = wasm.__wbindgen_malloc(arg.length * 4);
getUint32Memory().set(arg, ptr / 4);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory = null;
function getInt32Memory() {
if (cachegetInt32Memory === null || cachegetInt32Memory.buffer !== wasm.memory.buffer) {
cachegetInt32Memory = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory;
}
function getArrayU32FromWasm(ptr, len) {
return getUint32Memory().subarray(ptr / 4, ptr / 4 + len);
}
/**
* @param {Uint32Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} factor
* @returns {Uint32Array}
*/
export function resize(input_image, input_width, input_height, factor) {
const retptr = 8;
const ret = wasm.resize(retptr, passArray32ToWasm(input_image), WASM_VECTOR_LEN, input_width, input_height, factor);
const memi32 = getInt32Memory();
const v0 = getArrayU32FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice();
wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 4);
return v0;
}

5
codecs/hqx/pkg/squooshhqx_bg.d.ts vendored Normal file
View File

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

Binary file not shown.

55
codecs/hqx/src/lib.rs Normal file
View File

@@ -0,0 +1,55 @@
extern crate cfg_if;
extern crate hqx;
extern crate wasm_bindgen;
mod utils;
use cfg_if::cfg_if;
use wasm_bindgen::prelude::*;
cfg_if! {
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
if #[cfg(feature = "wee_alloc")] {
extern crate wee_alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
}
}
#[wasm_bindgen]
#[no_mangle]
pub fn resize(
input_image: Vec<u32>,
input_width: usize,
input_height: usize,
factor: usize,
) -> Vec<u32> {
let num_output_pixels = input_width * input_height * factor * factor;
let mut output_image = Vec::<u32>::with_capacity(num_output_pixels * 4);
output_image.resize(num_output_pixels, 0);
match factor {
2 => hqx::hq2x(
input_image.as_slice(),
output_image.as_mut_slice(),
input_width,
input_height,
),
3 => hqx::hq3x(
input_image.as_slice(),
output_image.as_mut_slice(),
input_width,
input_height,
),
4 => hqx::hq4x(
input_image.as_slice(),
output_image.as_mut_slice(),
input_width,
input_height,
),
_ => unreachable!(),
};
return output_image;
}

17
codecs/hqx/src/utils.rs Normal file
View File

@@ -0,0 +1,17 @@
use cfg_if::cfg_if;
cfg_if! {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
if #[cfg(feature = "console_error_panic_hook")] {
extern crate console_error_panic_hook;
pub use self::console_error_panic_hook::set_once as set_panic_hook;
} else {
#[inline]
pub fn set_panic_hook() {}
}
}

View File

@@ -4,84 +4,40 @@ set -e
export OPTIMIZE="-Os" export OPTIMIZE="-Os"
export PREFIX="/src/build" export PREFIX="/src/build"
export CFLAGS="${OPTIMIZE} -I${PREFIX}/include/"
export CPPFLAGS="${OPTIMIZE} -I${PREFIX}/include/"
export LDFLAGS="${OPTIMIZE} -L${PREFIX}/lib/"
apt-get update
apt-get install -qqy autoconf libtool
echo "============================================="
echo "Compiling zlib"
echo "============================================="
test -n "$SKIP_ZLIB" || (
cd node_modules/zlib
emconfigure ./configure --prefix=${PREFIX}/
emmake make
emmake make install
)
echo "============================================="
echo "Compiling zlib done"
echo "============================================="
echo "============================================="
echo "Compiling libpng"
echo "============================================="
test -n "$SKIP_LIBPNG" || (
cd node_modules/libpng
autoreconf -i
emconfigure ./configure --with-zlib-prefix=${PREFIX}/ --prefix=${PREFIX}/
emmake make
emmake make install
)
echo "============================================="
echo "Compiling libpng done"
echo "============================================="
echo "=============================================" echo "============================================="
echo "Compiling optipng" echo "Compiling optipng"
echo "=============================================" echo "============================================="
( (
emcc \ cd node_modules/optipng
${OPTIMIZE} \ CFLAGS="${OPTIMIZE} -Isrc/zlib" emconfigure ./configure --prefix=${PREFIX}
-Wno-implicit-function-declaration \ emmake make
-I ${PREFIX}/include \ emmake make install
-I node_modules/optipng/src/opngreduc \ mkdir -p ${PREFIX}/lib
-I node_modules/optipng/src/pngxtern \ mv ${PREFIX}/bin/optipng ${PREFIX}/lib/liboptipng.so
-I node_modules/optipng/src/cexcept \
-I node_modules/optipng/src/gifread \
-I node_modules/optipng/src/pnmio \
-I node_modules/optipng/src/minitiff \
--std=c99 -c \
node_modules/optipng/src/opngreduc/*.c \
node_modules/optipng/src/pngxtern/*.c \
node_modules/optipng/src/gifread/*.c \
node_modules/optipng/src/minitiff/*.c \
node_modules/optipng/src/pnmio/*.c \
node_modules/optipng/src/optipng/*.c
emcc \
--bind \
${OPTIMIZE} \
-s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME="optipng"' \
-I ${PREFIX}/include \
-I node_modules/optipng/src/opngreduc \
-I node_modules/optipng/src/pngxtern \
-I node_modules/optipng/src/cexcept \
-I node_modules/optipng/src/gifread \
-I node_modules/optipng/src/pnmio \
-I node_modules/optipng/src/minitiff \
-o "optipng.js" \
--std=c++11 \
optipng.cpp \
*.o \
${PREFIX}/lib/libz.so ${PREFIX}/lib/libpng.a
) )
echo "=============================================" echo "============================================="
echo "Compiling optipng done" echo "Compiling optipng done"
echo "=============================================" echo "============================================="
echo "============================================="
echo "Compiling optipng wrapper"
echo "============================================="
(
emcc \
--bind \
${OPTIMIZE} \
-s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME="optipng"' \
-o "optipng.js" \
--std=c++11 \
optipng.cpp \
${PREFIX}/lib/liboptipng.so
)
echo "============================================="
echo "Compiling optipng wrapper done"
echo "============================================="
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "Did you update your docker image?" echo "Did you update your docker image?"
echo "Run \`docker pull trzeci/emscripten\`" echo "Run \`docker pull trzeci/emscripten-upstream\`"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{ {
"name": "optipng", "name": "optipng",
"scripts": { "scripts": {
"install": "tar-dependency install && napa", "install": "tar-dependency install",
"build": "npm run build:wasm", "build": "npm run build:wasm",
"build:wasm": "docker run --rm -v $(pwd):/src -e SKIP_ZLIB=\"${SKIP_ZLIB}\" -e SKIP_LIBPNG=\"${SKIP_LIBPNG}\" trzeci/emscripten ./build.sh" "build:wasm": "docker run --rm -v $(pwd):/src trzeci/emscripten-upstream ./build.sh"
}, },
"tarDependencies": { "tarDependencies": {
"node_modules/optipng": { "node_modules/optipng": {
@@ -11,12 +11,7 @@
"strip": 1 "strip": 1
} }
}, },
"napa": {
"libpng": "emscripten-ports/libpng",
"zlib": "emscripten-ports/zlib"
},
"dependencies": { "dependencies": {
"napa": "3.0.0",
"tar-dependency": "0.0.3" "tar-dependency": "0.0.3"
} }
} }

6
codecs/resize/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
**/*.rs.bk
target
Cargo.lock
bin/
pkg/README.md
lut.inc

37
codecs/resize/Cargo.toml Normal file
View File

@@ -0,0 +1,37 @@
[package]
name = "resize"
version = "0.1.0"
authors = ["Surma <surma@surma.link>"]
[lib]
#crate-type = ["cdylib", "rlib"]
crate-type = ["cdylib"]
[features]
default = ["console_error_panic_hook", "wee_alloc"]
[dependencies]
cfg-if = "0.1.2"
wasm-bindgen = "0.2.38"
resize = "0.3.0"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.1", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.2", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.2"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
lto = true

9
codecs/resize/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM rust
RUN rustup target add wasm32-unknown-unknown
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
RUN mkdir /opt/wabt && \
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.11/wabt-1.0.11-linux.tar.gz | tar -xzf - -C /opt/wabt --strip 1
ENV PATH="/opt/wabt:${PATH}"
WORKDIR /src

23
codecs/resize/build.rs Normal file
View File

@@ -0,0 +1,23 @@
include!("./src/srgb.rs");
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut srgb_to_linear_lut = String::from("static SRGB_TO_LINEAR_LUT: [f32; 256] = [");
let mut linear_to_srgb_lut = String::from("static LINEAR_TO_SRGB_LUT: [f32; 256] = [");
for i in 0..256 {
srgb_to_linear_lut.push_str(&format!("{0:.7}", srgb_to_linear((i as f32) / 255.0)));
srgb_to_linear_lut.push_str(",");
linear_to_srgb_lut.push_str(&format!("{0:.7}", linear_to_srgb((i as f32) / 255.0)));
linear_to_srgb_lut.push_str(",");
}
srgb_to_linear_lut.pop().unwrap();
linear_to_srgb_lut.pop().unwrap();
srgb_to_linear_lut.push_str("];");
linear_to_srgb_lut.push_str("];");
let mut file = std::fs::File::create("src/lut.inc")?;
file.write_all(srgb_to_linear_lut.as_bytes())?;
file.write_all(linear_to_srgb_lut.as_bytes())?;
Ok(())
}

22
codecs/resize/build.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
set -e
echo "============================================="
echo "Compiling wasm"
echo "============================================="
(
wasm-pack build
wasm-strip pkg/resize_bg.wasm
rm pkg/.gitignore
)
echo "============================================="
echo "Compiling wasm done"
echo "============================================="
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "Did you update your docker image?"
echo "Run \`docker pull ubuntu\`"
echo "Run \`docker pull rust\`"
echo "Run \`docker build -t squoosh-resize .\`"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

4
codecs/resize/package-lock.json generated Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "resize",
"lockfileVersion": 1
}

View File

@@ -0,0 +1,7 @@
{
"name": "resize",
"scripts": {
"build:image": "docker build -t squoosh-resize .",
"build": "docker run --rm -v $(pwd):/src squoosh-resize ./build.sh"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "resize",
"collaborators": [
"Surma <surma@surma.link>"
],
"version": "0.1.0",
"files": [
"resize_bg.wasm",
"resize.js",
"resize.d.ts"
],
"module": "resize.js",
"types": "resize.d.ts",
"sideEffects": "false"
}

13
codecs/resize/pkg/resize.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
/* tslint:disable */
/**
* @param {Uint8Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} output_width
* @param {number} output_height
* @param {number} typ_idx
* @param {boolean} premultiply
* @param {boolean} color_space_conversion
* @returns {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): Uint8Array;

View File

@@ -0,0 +1,50 @@
import * as wasm from './resize_bg.wasm';
let cachegetUint8Memory = null;
function getUint8Memory() {
if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) {
cachegetUint8Memory = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory;
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm(arg) {
const ptr = wasm.__wbindgen_malloc(arg.length * 1);
getUint8Memory().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory = null;
function getInt32Memory() {
if (cachegetInt32Memory === null || cachegetInt32Memory.buffer !== wasm.memory.buffer) {
cachegetInt32Memory = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory;
}
function getArrayU8FromWasm(ptr, len) {
return getUint8Memory().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} output_width
* @param {number} output_height
* @param {number} typ_idx
* @param {boolean} premultiply
* @param {boolean} color_space_conversion
* @returns {Uint8Array}
*/
export function resize(input_image, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion) {
const retptr = 8;
const ret = wasm.resize(retptr, passArray8ToWasm(input_image), WASM_VECTOR_LEN, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion);
const memi32 = getInt32Memory();
const v0 = getArrayU8FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice();
wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 1);
return v0;
}

5
codecs/resize/pkg/resize_bg.d.ts vendored Normal file
View File

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

Binary file not shown.

121
codecs/resize/src/lib.rs Normal file
View File

@@ -0,0 +1,121 @@
extern crate cfg_if;
extern crate resize;
extern crate wasm_bindgen;
mod utils;
use cfg_if::cfg_if;
use resize::Pixel::RGBA;
use resize::Type;
use wasm_bindgen::prelude::*;
mod srgb;
use srgb::Clamp;
cfg_if! {
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
if #[cfg(feature = "wee_alloc")] {
extern crate wee_alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
}
}
include!("./lut.inc");
// If `with_space_conversion` is true, this function returns 2 functions that
// convert from sRGB to linear RGB and vice versa. If `with_space_conversion` is
// false, the 2 functions returned do nothing.
fn converter_funcs(with_space_conversion: bool) -> ((fn(u8) -> f32), (fn(f32) -> u8)) {
if with_space_conversion {
(
|v| SRGB_TO_LINEAR_LUT[v as usize] * 255.0,
|v| (LINEAR_TO_SRGB_LUT[v as usize] * 255.0) as u8,
)
} else {
(|v| v as f32, |v| v as u8)
}
}
// If `with_alpha_premultiplication` is true, this function returns a function
// that premultiply the alpha channel with the given channel value and another
// function that reverses that process. If `with_alpha_premultiplication` is
// false, the functions just return the channel value.
fn alpha_multiplier_funcs(
with_alpha_premultiplication: bool,
) -> ((fn(f32, u8) -> u8), (fn(u8, u8) -> f32)) {
if with_alpha_premultiplication {
(
|v, a| (v * (a as f32) / 255.0) as u8,
|v, a| (v as f32) * 255.0 / (a as f32).clamp(0.0, 255.0),
)
} else {
(|v, _a| v as u8, |v, _a| v as f32)
}
}
#[wasm_bindgen]
#[no_mangle]
pub fn resize(
mut input_image: Vec<u8>,
input_width: usize,
input_height: usize,
output_width: usize,
output_height: usize,
typ_idx: usize,
premultiply: bool,
color_space_conversion: bool,
) -> Vec<u8> {
let typ = match typ_idx {
0 => Type::Triangle,
1 => Type::Catrom,
2 => Type::Mitchell,
3 => Type::Lanczos3,
_ => panic!("Nope"),
};
let num_input_pixels = input_width * input_height;
let num_output_pixels = output_width * output_height;
let (to_linear, to_color_space) = converter_funcs(color_space_conversion);
let (premultiplier, demultiplier) = alpha_multiplier_funcs(premultiply);
// If both options are false, there is no preprocessing on the pixel valus
// and we can skip the loop.
if premultiply || color_space_conversion {
for i in 0..num_input_pixels {
for j in 0..3 {
input_image[4 * i + j] =
premultiplier(to_linear(input_image[4 * i + j]), input_image[4 * i + 3]);
}
}
}
let mut resizer = resize::new(
input_width,
input_height,
output_width,
output_height,
RGBA,
typ,
);
let mut output_image = Vec::<u8>::with_capacity(num_output_pixels * 4);
output_image.resize(num_output_pixels * 4, 0);
resizer.resize(input_image.as_slice(), output_image.as_mut_slice());
if premultiply || color_space_conversion {
for i in 0..num_output_pixels {
for j in 0..3 {
// We dont need to worry about division by zero, as division by zero
// is well-defined on floats to return ±Inf. ±Inf is converted to 0
// when casting to integers.
output_image[4 * i + j] = to_color_space(demultiplier(
output_image[4 * i + j],
output_image[4 * i + 3],
));
}
}
}
return output_image;
}

29
codecs/resize/src/srgb.rs Normal file
View File

@@ -0,0 +1,29 @@
pub trait Clamp: std::cmp::PartialOrd + Sized {
fn clamp(self, min: Self, max: Self) -> Self {
if self.lt(&min) {
min
} else if self.gt(&max) {
max
} else {
self
}
}
}
impl Clamp for f32 {}
pub fn srgb_to_linear(v: f32) -> f32 {
if v < 0.04045 {
v / 12.92
} else {
((v + 0.055) / 1.055).powf(2.4).clamp(0.0, 1.0)
}
}
pub fn linear_to_srgb(v: f32) -> f32 {
if v < 0.0031308 {
v * 12.92
} else {
(1.055 * v.powf(1.0 / 2.4) - 0.055).clamp(0.0, 1.0)
}
}

View File

@@ -0,0 +1,17 @@
use cfg_if::cfg_if;
cfg_if! {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
if #[cfg(feature = "console_error_panic_hook")] {
extern crate console_error_panic_hook;
pub use self::console_error_panic_hook::set_once as set_panic_hook;
} else {
#[inline]
pub fn set_panic_hook() {}
}
}

View File

@@ -1,17 +1,8 @@
FROM ubuntu
RUN apt-get update && \
apt-get install -qqy git build-essential cmake python2.7
RUN git clone --recursive https://github.com/WebAssembly/wabt /usr/src/wabt
RUN mkdir -p /usr/src/wabt/build
WORKDIR /usr/src/wabt/build
RUN cmake .. -DCMAKE_INSTALL_PREFIX=/opt/wabt && \
make && \
make install
FROM rust FROM rust
RUN rustup install nightly && \ RUN rustup target add wasm32-unknown-unknown
rustup target add --toolchain nightly wasm32-unknown-unknown
COPY --from=0 /opt/wabt /opt/wabt RUN mkdir /opt/wabt && \
ENV PATH="/opt/wabt/bin:${PATH}" curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.11/wabt-1.0.11-linux.tar.gz | tar -xzf - -C /opt/wabt --strip 1
ENV PATH="/opt/wabt:${PATH}"
WORKDIR /src WORKDIR /src

View File

@@ -6,8 +6,7 @@ echo "============================================="
echo "Compiling wasm" echo "Compiling wasm"
echo "=============================================" echo "============================================="
( (
rustup run nightly \ cargo build \
cargo build \
--target wasm32-unknown-unknown \ --target wasm32-unknown-unknown \
--release --release
cp target/wasm32-unknown-unknown/release/rotate.wasm . cp target/wasm32-unknown-unknown/release/rotate.wasm .

Binary file not shown.

View File

@@ -1,203 +0,0 @@
const path = require('path');
const { URL } = require('url');
const gzipSize = require('gzip-size');
const fetch = require('node-fetch');
const prettyBytes = require('pretty-bytes');
const escapeRE = require('escape-string-regexp');
const readdirp = require('readdirp');
const chalk = new require('chalk').constructor({ level: 4 });
function fetchTravis(path, options = {}) {
const url = new URL(path, 'https://api.travis-ci.org');
url.search = new URLSearchParams(options);
return fetch(url, {
headers: { 'Travis-API-Version': '3' },
});
}
function fetchTravisBuildInfo(user, repo, branch) {
return fetchTravis(`/repo/${encodeURIComponent(`${user}/${repo}`)}/builds`, {
'branch.name': branch,
state: 'passed',
limit: 1,
event_type: 'push',
}).then(r => r.json());
}
function fetchTravisText(path) {
return fetchTravis(path).then(r => r.text());
}
/**
* Recursively-read a directory and turn it into an array of { name, size, gzipSize }
*/
async function dirToInfoArray(startPath) {
const results = await new Promise((resolve, reject) => {
readdirp({ root: startPath }, (err, results) => {
if (err) reject(err); else resolve(results);
});
});
return Promise.all(
results.files.map(async (entry) => ({
name: entry.path,
gzipSize: await gzipSize.file(entry.fullPath),
size: entry.stat.size,
})),
);
}
/**
* Try to treat two entries with different file name hashes as the same file.
*/
function findHashedMatch(name, buildInfo) {
const nameParts = /^(.+\.)[a-f0-9]+(\..+)$/.exec(name);
if (!nameParts) return;
const matchRe = new RegExp(`^${escapeRE(nameParts[1])}[a-f0-9]+${escapeRE(nameParts[2])}$`);
const matchingEntry = buildInfo.find(entry => matchRe.test(entry.name));
return matchingEntry;
}
const buildSizePrefix = '=== BUILD SIZES: ';
const buildSizePrefixRe = new RegExp(`^${escapeRE(buildSizePrefix)}(.+)$`, 'm');
async function getPreviousBuildInfo() {
const buildData = await fetchTravisBuildInfo('GoogleChromeLabs', 'squoosh', 'master');
const jobUrl = buildData.builds[0].jobs[0]['@href'];
const log = await fetchTravisText(jobUrl + '/log.txt');
const reResult = buildSizePrefixRe.exec(log);
if (!reResult) return;
return JSON.parse(reResult[1]);
}
/**
* Generate an array that represents the difference between builds.
* Returns an array of { beforeName, afterName, beforeSize, afterSize }.
* Sizes are gzipped size.
* Before/after properties are missing if resource isn't in the previous/new build.
*/
function getChanges(previousBuildInfo, buildInfo) {
const buildChanges = [];
const alsoInPreviousBuild = new Set();
for (const oldEntry of previousBuildInfo) {
const newEntry = buildInfo.find(entry => entry.name === oldEntry.name) ||
findHashedMatch(oldEntry.name, buildInfo);
// Entry is in previous build, but not the new build.
if (!newEntry) {
buildChanges.push({
beforeName: oldEntry.name,
beforeSize: oldEntry.gzipSize,
});
continue;
}
// Mark this entry so we know we've dealt with it.
alsoInPreviousBuild.add(newEntry);
// If they're the same, just ignore.
// Using size rather than gzip size. I've seen different platforms produce different zipped
// sizes.
if (
oldEntry.size === newEntry.size &&
oldEntry.name === newEntry.name
) continue;
// Entry is in both builds (maybe renamed).
buildChanges.push({
beforeName: oldEntry.name,
afterName: newEntry.name,
beforeSize: oldEntry.gzipSize,
afterSize: newEntry.gzipSize,
});
}
// Look for entries that are only in the new build.
for (const newEntry of buildInfo) {
if (alsoInPreviousBuild.has(newEntry)) continue;
buildChanges.push({
afterName: newEntry.name,
afterSize: newEntry.gzipSize,
});
}
return buildChanges;
}
async function main() {
// Output the current build sizes for later retrieval.
const buildInfo = await dirToInfoArray(__dirname + '/../build');
console.log(buildSizePrefix + JSON.stringify(buildInfo));
console.log('\nBuild change report:');
let previousBuildInfo;
try {
previousBuildInfo = await getPreviousBuildInfo();
} catch (err) {
console.log(` Couldn't parse previous build info`);
return;
}
if (!previousBuildInfo) {
console.log(` Couldn't find previous build info`);
return;
}
const buildChanges = getChanges(previousBuildInfo, buildInfo);
if (buildChanges.length === 0) {
console.log(' No changes');
return;
}
// One letter references, so it's easier to get the spacing right.
const y = chalk.yellow;
const g = chalk.green;
const r = chalk.red;
for (const change of buildChanges) {
// New file.
if (!change.beforeSize) {
console.log(` ${g('ADDED')} ${change.afterName} - ${prettyBytes(change.afterSize)}`);
continue;
}
// Removed file.
if (!change.afterSize) {
console.log(` ${r('REMOVED')} ${change.beforeName} - was ${prettyBytes(change.beforeSize)}`);
continue;
}
// Changed file.
let size;
if (change.beforeSize === change.afterSize) {
// Just renamed.
size = `${prettyBytes(change.afterSize)} -> no change`;
} else {
const color = change.afterSize > change.beforeSize ? r : g;
const sizeDiff = prettyBytes(change.afterSize - change.beforeSize, { signed: true });
const relativeDiff = Math.round((change.afterSize / change.beforeSize) * 1000) / 1000;
size = `${prettyBytes(change.beforeSize)} -> ${prettyBytes(change.afterSize)}` +
' (' +
color(`${sizeDiff}, ${relativeDiff}x`) +
')';
}
console.log(` ${y('CHANGED')} ${change.afterName} - ${size}`);
if (change.beforeName !== change.afterName) {
console.log(` Renamed from: ${change.beforeName}`);
}
}
}
main();

BIN
icon-large-maskable.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

10714
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
{ {
"private": true, "private": true,
"name": "squoosh", "name": "squoosh",
"version": "1.3.4", "version": "1.9.1",
"license": "apache-2.0", "license": "apache-2.0",
"scripts": { "scripts": {
"start": "webpack-dev-server --host 0.0.0.0 --hot", "start": "webpack-dev-server --host 0.0.0.0 --hot",
"build": "webpack -p", "build": "webpack -p",
"lint": "tslint -c tslint.json -p tsconfig.json -t verbose 'src/**/*.{ts,tsx,js,jsx}'", "lint": "tslint -c tslint.json -p tsconfig.json -t verbose",
"lintfix": "tslint -c tslint.json -p tsconfig.json -t verbose --fix 'src/**/*.{ts,tsx,js,jsx}'", "lintfix": "tslint -c tslint.json -p tsconfig.json -t verbose --fix 'src/**/*.{ts,tsx,js,jsx}'",
"sizereport": "node config/size-report.js" "sizereport": "sizereport --config"
}, },
"husky": { "husky": {
"hooks": { "hooks": {
@@ -16,60 +16,61 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@types/node": "10.12.29", "@types/node": "10.14.15",
"@types/pretty-bytes": "5.1.0", "@types/pretty-bytes": "5.1.0",
"@types/webassembly-js-api": "0.0.2", "@types/webassembly-js-api": "0.0.3",
"@webcomponents/custom-elements": "1.2.1", "@webcomponents/custom-elements": "1.2.4",
"@webpack-cli/serve": "0.1.3", "@webpack-cli/serve": "0.1.8",
"assets-webpack-plugin": "3.9.10", "assets-webpack-plugin": "3.9.10",
"chokidar": "2.1.2",
"chalk": "2.4.2", "chalk": "2.4.2",
"chokidar": "3.0.2",
"classnames": "2.2.6", "classnames": "2.2.6",
"clean-webpack-plugin": "1.0.1", "clean-webpack-plugin": "1.0.1",
"comlink": "3.1.1", "comlink": "3.1.1",
"copy-webpack-plugin": "5.0.0", "copy-webpack-plugin": "5.0.4",
"critters-webpack-plugin": "2.3.0", "critters-webpack-plugin": "2.4.0",
"css-loader": "1.0.1", "css-loader": "1.0.1",
"ejs": "2.6.1", "ejs": "2.6.2",
"escape-string-regexp": "1.0.5", "escape-string-regexp": "2.0.0",
"exports-loader": "0.7.0", "exports-loader": "0.7.0",
"file-drop-element": "0.2.0", "file-drop-element": "0.2.0",
"file-loader": "3.0.1", "file-loader": "4.2.0",
"gzip-size": "5.0.0", "gzip-size": "5.1.1",
"html-webpack-plugin": "3.2.0", "html-webpack-plugin": "3.2.0",
"husky": "1.3.1", "husky": "3.0.4",
"idb-keyval": "3.1.0", "idb-keyval": "3.2.0",
"linkstate": "1.1.1", "linkstate": "1.1.1",
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
"mini-css-extract-plugin": "0.5.0", "mini-css-extract-plugin": "0.8.0",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"node-fetch": "2.3.0", "node-fetch": "2.6.0",
"node-sass": "4.11.0", "node-sass": "4.12.0",
"optimize-css-assets-webpack-plugin": "5.0.1", "optimize-css-assets-webpack-plugin": "5.0.1",
"pointer-tracker": "2.0.3", "pointer-tracker": "2.0.3",
"preact": "8.4.2", "preact": "8.4.2",
"prerender-loader": "1.3.0", "prerender-loader": "1.3.0",
"pretty-bytes": "5.1.0", "pretty-bytes": "5.3.0",
"progress-bar-webpack-plugin": "1.12.1", "progress-bar-webpack-plugin": "1.12.1",
"raw-loader": "1.0.0", "raw-loader": "3.1.0",
"readdirp": "2.2.1", "readdirp": "3.1.2",
"sass-loader": "7.1.0", "sass-loader": "7.3.1",
"script-ext-html-webpack-plugin": "2.1.3", "script-ext-html-webpack-plugin": "2.1.4",
"source-map-loader": "0.2.4", "source-map-loader": "0.2.4",
"style-loader": "0.23.1", "style-loader": "1.0.0",
"terser-webpack-plugin": "1.2.3", "terser-webpack-plugin": "1.4.1",
"ts-loader": "5.3.3", "travis-size-report": "1.1.0",
"tslint": "5.13.1", "ts-loader": "6.0.3",
"tslint": "5.19.0",
"tslint-config-airbnb": "5.11.1", "tslint-config-airbnb": "5.11.1",
"tslint-config-semistandard": "7.0.0", "tslint-config-semistandard": "8.0.1",
"tslint-react": "3.6.0", "tslint-react": "4.0.0",
"typed-css-modules": "0.4.1", "typed-css-modules": "0.4.2",
"typescript": "3.3.3333", "typescript": "3.5.3",
"url-loader": "1.1.2", "url-loader": "2.1.0",
"webpack": "4.28.0", "webpack": "4.28.0",
"webpack-bundle-analyzer": "3.1.0", "webpack-bundle-analyzer": "3.4.1",
"webpack-cli": "3.2.3", "webpack-cli": "3.3.4",
"webpack-dev-server": "3.2.1", "webpack-dev-server": "3.8.0",
"worker-plugin": "3.1.0" "worker-plugin": "3.1.0"
} }
} }

14
sizereport.config.js Normal file
View File

@@ -0,0 +1,14 @@
const escapeRE = require("escape-string-regexp");
module.exports = {
repo: "GoogleChromeLabs/squoosh",
path: "build/**/!(*.map)",
branch: "master",
findRenamed(path, newPaths) {
const nameParts = /^(.+\.)[a-f0-9]+(\..+)$/.exec(path);
if (!nameParts) return;
const matchRe = new RegExp(`^${escapeRE(nameParts[1])}[a-f0-9]+${escapeRE(nameParts[2])}$`);
return newPaths.find(newPath => matchRe.test(newPath));
}
};

View File

@@ -0,0 +1,3 @@
export interface HqxOptions {
factor: 2 | 3 | 4;
}

View File

@@ -0,0 +1,20 @@
import { resize } from '../../../codecs/hqx/pkg';
import { HqxOptions } from './processor-meta';
export async function hqx(
data: ImageData,
opts: HqxOptions,
): Promise<ImageData> {
const input = data;
const result = resize(
new Uint32Array(input.data.buffer),
input.width,
input.height,
opts.factor,
);
return new ImageData(
new Uint8ClampedArray(result.buffer),
data.width * opts.factor,
data.height * opts.factor,
);
}

View File

@@ -1,12 +1,12 @@
import imagequant, { QuantizerModule } from '../../../codecs/imagequant/imagequant'; import imagequant, { QuantizerModule } from '../../../codecs/imagequant/imagequant';
import wasmUrl from '../../../codecs/imagequant/imagequant.wasm'; import wasmUrl from '../../../codecs/imagequant/imagequant.wasm';
import { QuantizeOptions } from './processor-meta'; import { QuantizeOptions } from './processor-meta';
import { initWasmModule } from '../util'; import { initEmscriptenModule } from '../util';
let emscriptenModule: Promise<QuantizerModule>; let emscriptenModule: Promise<QuantizerModule>;
export async function process(data: ImageData, opts: QuantizeOptions): Promise<ImageData> { export async function process(data: ImageData, opts: QuantizeOptions): Promise<ImageData> {
if (!emscriptenModule) emscriptenModule = initWasmModule(imagequant, wasmUrl); if (!emscriptenModule) emscriptenModule = initEmscriptenModule(imagequant, wasmUrl);
const module = await emscriptenModule; const module = await emscriptenModule;

View File

@@ -1,12 +1,12 @@
import mozjpeg_enc, { MozJPEGModule } from '../../../codecs/mozjpeg_enc/mozjpeg_enc'; import mozjpeg_enc, { MozJPEGModule } from '../../../codecs/mozjpeg_enc/mozjpeg_enc';
import wasmUrl from '../../../codecs/mozjpeg_enc/mozjpeg_enc.wasm'; import wasmUrl from '../../../codecs/mozjpeg_enc/mozjpeg_enc.wasm';
import { EncodeOptions } from './encoder-meta'; import { EncodeOptions } from './encoder-meta';
import { initWasmModule } from '../util'; import { initEmscriptenModule } from '../util';
let emscriptenModule: Promise<MozJPEGModule>; let emscriptenModule: Promise<MozJPEGModule>;
export async function encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> { export async function encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> {
if (!emscriptenModule) emscriptenModule = initWasmModule(mozjpeg_enc, wasmUrl); if (!emscriptenModule) emscriptenModule = initEmscriptenModule(mozjpeg_enc, wasmUrl);
const module = await emscriptenModule; const module = await emscriptenModule;
const resultView = module.encode(data.data, data.width, data.height, options); const resultView = module.encode(data.data, data.width, data.height, options);

View File

@@ -1,12 +1,12 @@
import optipng, { OptiPngModule } from '../../../codecs/optipng/optipng'; import optipng, { OptiPngModule } from '../../../codecs/optipng/optipng';
import wasmUrl from '../../../codecs/optipng/optipng.wasm'; import wasmUrl from '../../../codecs/optipng/optipng.wasm';
import { EncodeOptions } from './encoder-meta'; import { EncodeOptions } from './encoder-meta';
import { initWasmModule } from '../util'; import { initEmscriptenModule } from '../util';
let emscriptenModule: Promise<OptiPngModule>; let emscriptenModule: Promise<OptiPngModule>;
export async function compress(data: BufferSource, options: EncodeOptions): Promise<ArrayBuffer> { export async function compress(data: BufferSource, options: EncodeOptions): Promise<ArrayBuffer> {
if (!emscriptenModule) emscriptenModule = initWasmModule(optipng, wasmUrl); if (!emscriptenModule) emscriptenModule = initEmscriptenModule(optipng, wasmUrl);
const module = await emscriptenModule; const module = await emscriptenModule;
const resultView = module.compress(data, options); const resultView = module.compress(data, options);

View File

@@ -1,4 +1,6 @@
import { expose } from 'comlink'; import { expose } from 'comlink';
import { isHqx } from '../resize/processor-meta';
import { clamp } from '../util';
async function mozjpegEncode( async function mozjpegEncode(
data: ImageData, options: import('../mozjpeg/encoder-meta').EncodeOptions, data: ImageData, options: import('../mozjpeg/encoder-meta').EncodeOptions,
@@ -28,6 +30,28 @@ async function rotate(
return rotate(data, opts); return rotate(data, opts);
} }
async function resize(
data: ImageData, opts: import('../resize/processor-meta').WorkerResizeOptions,
): Promise<ImageData> {
if (isHqx(opts)) {
const { hqx } = await import(
/* webpackChunkName: "process-hqx" */
'../hqx/processor');
const widthRatio = opts.width / data.width;
const heightRatio = opts.height / data.height;
const ratio = Math.max(widthRatio, heightRatio);
if (ratio <= 1) return data;
const factor = clamp(Math.ceil(ratio), { min: 2, max: 4 }) as 2|3|4;
return hqx(data, { factor });
}
const { resize } = await import(
/* webpackChunkName: "process-resize" */
'../resize/processor');
return resize(data, opts);
}
async function optiPngEncode( async function optiPngEncode(
data: BufferSource, options: import('../optipng/encoder-meta').EncodeOptions, data: BufferSource, options: import('../optipng/encoder-meta').EncodeOptions,
): Promise<ArrayBuffer> { ): Promise<ArrayBuffer> {
@@ -53,7 +77,15 @@ async function webpDecode(data: ArrayBuffer): Promise<ImageData> {
return decode(data); return decode(data);
} }
const exports = { mozjpegEncode, quantize, rotate, optiPngEncode, webpEncode, webpDecode }; const exports = {
mozjpegEncode,
quantize,
rotate,
resize,
optiPngEncode,
webpEncode,
webpDecode,
};
export type ProcessorWorkerApi = typeof exports; export type ProcessorWorkerApi = typeof exports;
expose(exports, self); expose(exports, self);

View File

@@ -6,8 +6,8 @@ import { EncodeOptions as OptiPNGEncoderOptions } from './optipng/encoder-meta';
import { EncodeOptions as WebPEncoderOptions } from './webp/encoder-meta'; import { EncodeOptions as WebPEncoderOptions } from './webp/encoder-meta';
import { EncodeOptions as BrowserJPEGOptions } from './browser-jpeg/encoder-meta'; import { EncodeOptions as BrowserJPEGOptions } from './browser-jpeg/encoder-meta';
import { EncodeOptions as BrowserWebpEncodeOptions } from './browser-webp/encoder-meta'; import { EncodeOptions as BrowserWebpEncodeOptions } from './browser-webp/encoder-meta';
import { BitmapResizeOptions, VectorResizeOptions } from './resize/processor-meta'; import { BrowserResizeOptions, VectorResizeOptions } from './resize/processor-meta';
import { resize, vectorResize } from './resize/processor'; import { browserResize, vectorResize } from './resize/processor-sync';
import * as browserBMP from './browser-bmp/encoder'; import * as browserBMP from './browser-bmp/encoder';
import * as browserPNG from './browser-png/encoder'; import * as browserPNG from './browser-png/encoder';
import * as browserJPEG from './browser-jpeg/encoder'; import * as browserJPEG from './browser-jpeg/encoder';
@@ -16,6 +16,7 @@ import * as browserGIF from './browser-gif/encoder';
import * as browserTIFF from './browser-tiff/encoder'; import * as browserTIFF from './browser-tiff/encoder';
import * as browserJP2 from './browser-jp2/encoder'; import * as browserJP2 from './browser-jp2/encoder';
import * as browserPDF from './browser-pdf/encoder'; import * as browserPDF from './browser-pdf/encoder';
import { bind } from '../lib/initial-util';
type ProcessorWorkerApi = import('./processor-worker').ProcessorWorkerApi; type ProcessorWorkerApi = import('./processor-worker').ProcessorWorkerApi;
@@ -94,14 +95,7 @@ export default class Processor {
if (!this._worker) return; if (!this._worker) return;
// If the worker is unused for 10 seconds, remove it to save memory. // If the worker is unused for 10 seconds, remove it to save memory.
this._workerTimeoutId = self.setTimeout( this._workerTimeoutId = self.setTimeout(this.terminateWorker, workerTimeout);
() => {
if (!this._worker) return;
this._worker.terminate();
this._worker = undefined;
},
workerTimeout,
);
} }
/** Abort the current job, if any */ /** Abort the current job, if any */
@@ -111,7 +105,11 @@ export default class Processor {
this._abortRejector(new DOMException('Aborted', 'AbortError')); this._abortRejector(new DOMException('Aborted', 'AbortError'));
this._abortRejector = undefined; this._abortRejector = undefined;
this._busy = false; this._busy = false;
this.terminateWorker();
}
@bind
terminateWorker() {
if (!this._worker) return; if (!this._worker) return;
this._worker.terminate(); this._worker.terminate();
this._worker = undefined; this._worker = undefined;
@@ -130,6 +128,13 @@ export default class Processor {
return this._workerApi!.rotate(data, opts); return this._workerApi!.rotate(data, opts);
} }
@Processor._processingJob({ needsWorker: true })
workerResize(
data: ImageData, opts: import('./resize/processor-meta').WorkerResizeOptions,
): Promise<ImageData> {
return this._workerApi!.resize(data, opts);
}
@Processor._processingJob({ needsWorker: true }) @Processor._processingJob({ needsWorker: true })
mozjpegEncode( mozjpegEncode(
data: ImageData, opts: MozJPEGEncoderOptions, data: ImageData, opts: MozJPEGEncoderOptions,
@@ -202,9 +207,9 @@ export default class Processor {
// Synchronous jobs // Synchronous jobs
resize(data: ImageData, opts: BitmapResizeOptions) { resize(data: ImageData, opts: BrowserResizeOptions) {
this.abortCurrent(); this.abortCurrent();
return resize(data, opts); return browserResize(data, opts);
} }
vectorResize(data: HTMLImageElement, opts: VectorResizeOptions) { vectorResize(data: HTMLImageElement, opts: VectorResizeOptions) {

View File

@@ -1,8 +1,10 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import linkState from 'linkstate'; import linkState from 'linkstate';
import { bind, linkRef } from '../../lib/initial-util'; import { bind, linkRef } from '../../lib/initial-util';
import { inputFieldValueAsNumber, inputFieldValue, preventDefault } from '../../lib/util'; import {
import { ResizeOptions } from './processor-meta'; inputFieldValueAsNumber, inputFieldValue, preventDefault, inputFieldChecked,
} from '../../lib/util';
import { ResizeOptions, isWorkerOptions } from './processor-meta';
import * as style from '../../components/Options/style.scss'; import * as style from '../../components/Options/style.scss';
import Checkbox from '../../components/checkbox'; import Checkbox from '../../components/checkbox';
import Expander from '../../components/expander'; import Expander from '../../components/expander';
@@ -10,8 +12,9 @@ import Select from '../../components/select';
interface Props { interface Props {
isVector: Boolean; isVector: Boolean;
inputWidth: number;
inputHeight: number;
options: ResizeOptions; options: ResizeOptions;
aspect: number;
onChange(newOptions: ResizeOptions): void; onChange(newOptions: ResizeOptions): void;
} }
@@ -19,12 +22,21 @@ interface State {
maintainAspect: boolean; maintainAspect: boolean;
} }
const sizePresets = [0.25, 0.3333, 0.5, 1, 2, 3, 4];
export default class ResizerOptions extends Component<Props, State> { export default class ResizerOptions extends Component<Props, State> {
state: State = { state: State = {
maintainAspect: true, maintainAspect: true,
}; };
form?: HTMLFormElement; private form?: HTMLFormElement;
private presetWidths: { [idx: number]: number } = {};
private presetHeights: { [idx: number]: number } = {};
constructor(props: Props) {
super(props);
this.generatePresetValues(props.inputWidth, props.inputHeight);
}
private reportOptions() { private reportOptions() {
const form = this.form!; const form = this.form!;
@@ -38,6 +50,8 @@ export default class ResizerOptions extends Component<Props, State> {
width: inputFieldValueAsNumber(width), width: inputFieldValueAsNumber(width),
height: inputFieldValueAsNumber(height), height: inputFieldValueAsNumber(height),
method: form.resizeMethod.value, method: form.resizeMethod.value,
premultiply: inputFieldChecked(form.premultiply, true),
linearRGB: inputFieldChecked(form.linearRGB, true),
// Casting, as the formfield only returns the correct values. // Casting, as the formfield only returns the correct values.
fitMethod: inputFieldValue(form.fitMethod, options.fitMethod) as ResizeOptions['fitMethod'], fitMethod: inputFieldValue(form.fitMethod, options.fitMethod) as ResizeOptions['fitMethod'],
}; };
@@ -49,18 +63,31 @@ export default class ResizerOptions extends Component<Props, State> {
this.reportOptions(); this.reportOptions();
} }
private getAspect() {
return this.props.inputWidth / this.props.inputHeight;
}
componentDidUpdate(prevProps: Props, prevState: State) { componentDidUpdate(prevProps: Props, prevState: State) {
if (!prevState.maintainAspect && this.state.maintainAspect) { if (!prevState.maintainAspect && this.state.maintainAspect) {
this.form!.height.value = Math.round(Number(this.form!.width.value) / this.props.aspect); this.form!.height.value = Math.round(Number(this.form!.width.value) / this.getAspect());
this.reportOptions(); this.reportOptions();
} }
} }
componentWillReceiveProps(nextProps: Props) {
if (
this.props.inputWidth !== nextProps.inputWidth ||
this.props.inputHeight !== nextProps.inputHeight
) {
this.generatePresetValues(nextProps.inputWidth, nextProps.inputHeight);
}
}
@bind @bind
private onWidthInput() { private onWidthInput() {
if (this.state.maintainAspect) { if (this.state.maintainAspect) {
const width = inputFieldValueAsNumber(this.form!.width); const width = inputFieldValueAsNumber(this.form!.width);
this.form!.height.value = Math.round(width / this.props.aspect); this.form!.height.value = Math.round(width / this.getAspect());
} }
this.reportOptions(); this.reportOptions();
@@ -70,12 +97,44 @@ export default class ResizerOptions extends Component<Props, State> {
private onHeightInput() { private onHeightInput() {
if (this.state.maintainAspect) { if (this.state.maintainAspect) {
const height = inputFieldValueAsNumber(this.form!.height); const height = inputFieldValueAsNumber(this.form!.height);
this.form!.width.value = Math.round(height * this.props.aspect); this.form!.width.value = Math.round(height * this.getAspect());
} }
this.reportOptions(); this.reportOptions();
} }
private generatePresetValues(width: number, height: number) {
for (const preset of sizePresets) {
this.presetWidths[preset] = Math.round(width * preset);
this.presetHeights[preset] = Math.round(height * preset);
}
}
private getPreset(): number | string {
const { width, height } = this.props.options;
for (const preset of sizePresets) {
if (
width === this.presetWidths[preset] &&
height === this.presetHeights[preset]
) return preset;
}
return 'custom';
}
@bind
private onPresetChange(event: Event) {
const select = event.target as HTMLSelectElement;
if (select.value === 'custom') return;
const multiplier = Number(select.value);
(this.form!.width as HTMLInputElement).value =
Math.round(this.props.inputWidth * multiplier) + '';
(this.form!.height as HTMLInputElement).value =
Math.round(this.props.inputHeight * multiplier) + '';
this.reportOptions();
}
render({ options, isVector }: Props, { maintainAspect }: State) { render({ options, isVector }: Props, { maintainAspect }: State) {
return ( return (
<form ref={linkRef(this, 'form')} class={style.optionsSection} onSubmit={preventDefault}> <form ref={linkRef(this, 'form')} class={style.optionsSection} onSubmit={preventDefault}>
@@ -87,12 +146,26 @@ export default class ResizerOptions extends Component<Props, State> {
onChange={this.onChange} onChange={this.onChange}
> >
{isVector && <option value="vector">Vector</option>} {isVector && <option value="vector">Vector</option>}
<option value="lanczos3">Lanczos3</option>
<option value="mitchell">Mitchell</option>
<option value="catrom">Catmull-Rom</option>
<option value="triangle">Triangle (bilinear)</option>
<option value="hqx">hqx (pixel art)</option>
<option value="browser-pixelated">Browser pixelated</option> <option value="browser-pixelated">Browser pixelated</option>
<option value="browser-low">Browser low quality</option> <option value="browser-low">Browser low quality</option>
<option value="browser-medium">Browser medium quality</option> <option value="browser-medium">Browser medium quality</option>
<option value="browser-high">Browser high quality</option> <option value="browser-high">Browser high quality</option>
</Select> </Select>
</label> </label>
<label class={style.optionTextFirst}>
Preset:
<Select value={this.getPreset()} onChange={this.onPresetChange}>
{sizePresets.map(preset =>
<option value={preset}>{preset * 100}%</option>,
)}
<option value="custom">Custom</option>
</Select>
</label>
<label class={style.optionTextFirst}> <label class={style.optionTextFirst}>
Width: Width:
<input <input
@@ -117,6 +190,30 @@ export default class ResizerOptions extends Component<Props, State> {
onInput={this.onHeightInput} onInput={this.onHeightInput}
/> />
</label> </label>
<Expander>
{isWorkerOptions(options) ?
<label class={style.optionInputFirst}>
<Checkbox
name="premultiply"
checked={options.premultiply}
onChange={this.onChange}
/>
Premultiply alpha channel
</label>
: null
}
{isWorkerOptions(options) ?
<label class={style.optionInputFirst}>
<Checkbox
name="linearRGB"
checked={options.linearRGB}
onChange={this.onChange}
/>
Linear RGB
</label>
: null
}
</Expander>
<label class={style.optionInputFirst}> <label class={style.optionInputFirst}>
<Checkbox <Checkbox
name="maintainAspect" name="maintainAspect"

View File

@@ -1,26 +1,75 @@
type BitmapResizeMethods = 'browser-pixelated' | 'browser-low' | 'browser-medium' | 'browser-high'; type BrowserResizeMethods =
| 'browser-pixelated'
| 'browser-low'
| 'browser-medium'
| 'browser-high';
type WorkerResizeMethods =
| 'triangle'
| 'catrom'
| 'mitchell'
| 'lanczos3'
| 'hqx';
const workerResizeMethods: WorkerResizeMethods[] = [
'triangle',
'catrom',
'mitchell',
'lanczos3',
'hqx',
];
export interface ResizeOptions { export type ResizeOptions =
| BrowserResizeOptions
| WorkerResizeOptions
| VectorResizeOptions;
export interface ResizeOptionsCommon {
width: number; width: number;
height: number; height: number;
method: 'vector' | BitmapResizeMethods;
fitMethod: 'stretch' | 'contain'; fitMethod: 'stretch' | 'contain';
} }
export interface BitmapResizeOptions extends ResizeOptions { export interface BrowserResizeOptions extends ResizeOptionsCommon {
method: BitmapResizeMethods; method: BrowserResizeMethods;
} }
export interface VectorResizeOptions extends ResizeOptions { export interface WorkerResizeOptions extends ResizeOptionsCommon {
method: WorkerResizeMethods;
premultiply: boolean;
linearRGB: boolean;
}
export interface VectorResizeOptions extends ResizeOptionsCommon {
method: 'vector'; method: 'vector';
} }
/**
* Return whether a set of options are worker resize options.
*
* @param opts
*/
export function isWorkerOptions(
opts: ResizeOptions,
): opts is WorkerResizeOptions {
return (workerResizeMethods as string[]).includes(opts.method);
}
/**
* Return whether a set of options are from the HQ<n>X set
*
* @param opts
*/
export function isHqx(opts: ResizeOptions): opts is WorkerResizeOptions {
return opts.method === 'hqx';
}
export const defaultOptions: ResizeOptions = { export const defaultOptions: ResizeOptions = {
// Width and height will always default to the image size. // Width and height will always default to the image size.
// This is set elsewhere. // This is set elsewhere.
width: 1, width: 1,
height: 1, height: 1,
// This will be set to 'vector' if the input is SVG. // This will be set to 'vector' if the input is SVG.
method: 'browser-high', method: 'lanczos3',
fitMethod: 'stretch', fitMethod: 'stretch',
premultiply: true,
linearRGB: true,
}; };

View File

@@ -0,0 +1,35 @@
import { nativeResize, NativeResizeMethod, drawableToImageData } from '../../lib/util';
import { BrowserResizeOptions, VectorResizeOptions } from './processor-meta';
import { getContainOffsets } from './util';
export function browserResize(data: ImageData, opts: BrowserResizeOptions): ImageData {
let sx = 0;
let sy = 0;
let sw = data.width;
let sh = data.height;
if (opts.fitMethod === 'contain') {
({ sx, sy, sw, sh } = getContainOffsets(sw, sh, opts.width, opts.height));
}
return nativeResize(
data, sx, sy, sw, sh, opts.width, opts.height,
opts.method.slice('browser-'.length) as NativeResizeMethod,
);
}
export function vectorResize(data: HTMLImageElement, opts: VectorResizeOptions): ImageData {
let sx = 0;
let sy = 0;
let sw = data.width;
let sh = data.height;
if (opts.fitMethod === 'contain') {
({ sx, sy, sw, sh } = getContainOffsets(sw, sh, opts.width, opts.height));
}
return drawableToImageData(data, {
sx, sy, sw, sh,
width: opts.width, height: opts.height,
});
}

View File

@@ -1,49 +1,39 @@
import { nativeResize, NativeResizeMethod, drawableToImageData } from '../../lib/util'; import { WorkerResizeOptions } from './processor-meta';
import { BitmapResizeOptions, VectorResizeOptions } from './processor-meta'; import { getContainOffsets } from './util';
import { resize as codecResize } from '../../../codecs/resize/pkg';
function getContainOffsets(sw: number, sh: number, dw: number, dh: number) { function crop(data: ImageData, sx: number, sy: number, sw: number, sh: number): ImageData {
const currentAspect = sw / sh; const inputPixels = new Uint32Array(data.data.buffer);
const endAspect = dw / dh;
if (endAspect > currentAspect) { // Copy within the same buffer for speed and memory efficiency.
const newSh = sw / endAspect; for (let y = 0; y < sh; y += 1) {
const newSy = (sh - newSh) / 2; const start = ((y + sy) * data.width) + sx;
return { sw, sh: newSh, sx: 0, sy: newSy }; inputPixels.copyWithin(y * sw, start, start + sw);
} }
const newSw = sh * endAspect; return new ImageData(
const newSx = (sw - newSw) / 2; new Uint8ClampedArray(inputPixels.buffer.slice(0, sw * sh * 4)),
return { sh, sw: newSw, sx: newSx, sy: 0 }; sw, sh,
}
export function resize(data: ImageData, opts: BitmapResizeOptions): ImageData {
let sx = 0;
let sy = 0;
let sw = data.width;
let sh = data.height;
if (opts.fitMethod === 'contain') {
({ sx, sy, sw, sh } = getContainOffsets(sw, sh, opts.width, opts.height));
}
return nativeResize(
data, sx, sy, sw, sh, opts.width, opts.height,
opts.method.slice('browser-'.length) as NativeResizeMethod,
); );
} }
export function vectorResize(data: HTMLImageElement, opts: VectorResizeOptions): ImageData { /** Resize methods by index */
let sx = 0; const resizeMethods: WorkerResizeOptions['method'][] = [
let sy = 0; 'triangle', 'catrom', 'mitchell', 'lanczos3',
let sw = data.width; ];
let sh = data.height;
export async function resize(data: ImageData, opts: WorkerResizeOptions): Promise<ImageData> {
let input = data;
if (opts.fitMethod === 'contain') { if (opts.fitMethod === 'contain') {
({ sx, sy, sw, sh } = getContainOffsets(sw, sh, opts.width, opts.height)); const { sx, sy, sw, sh } = getContainOffsets(data.width, data.height, opts.width, opts.height);
input = crop(input, Math.round(sx), Math.round(sy), Math.round(sw), Math.round(sh));
} }
return drawableToImageData(data, { const result = codecResize(
sx, sy, sw, sh, new Uint8Array(input.data.buffer), input.width, input.height, opts.width, opts.height,
width: opts.width, height: opts.height, resizeMethods.indexOf(opts.method), opts.premultiply, opts.linearRGB,
}); );
return new ImageData(new Uint8ClampedArray(result.buffer), opts.width, opts.height);
} }

14
src/codecs/resize/util.ts Normal file
View File

@@ -0,0 +1,14 @@
export function getContainOffsets(sw: number, sh: number, dw: number, dh: number) {
const currentAspect = sw / sh;
const endAspect = dw / dh;
if (endAspect > currentAspect) {
const newSh = sw / endAspect;
const newSy = (sh - newSh) / 2;
return { sw, sh: newSh, sx: 0, sy: newSy };
}
const newSw = sh * endAspect;
const newSx = (sw - newSw) / 2;
return { sh, sw: newSw, sx: newSx, sy: 0 };
}

View File

@@ -2,7 +2,7 @@ type ModuleFactory<M extends EmscriptenWasm.Module> = (
opts: EmscriptenWasm.ModuleOpts, opts: EmscriptenWasm.ModuleOpts,
) => M; ) => M;
export function initWasmModule<T extends EmscriptenWasm.Module>( export function initEmscriptenModule<T extends EmscriptenWasm.Module>(
moduleFactory: ModuleFactory<T>, moduleFactory: ModuleFactory<T>,
wasmUrl: string, wasmUrl: string,
): Promise<T> { ): Promise<T> {
@@ -25,3 +25,12 @@ export function initWasmModule<T extends EmscriptenWasm.Module>(
}); });
}); });
} }
interface ClampOpts {
min?: number;
max?: number;
}
export function clamp(x: number, opts: ClampOpts): number {
return Math.min(Math.max(x, opts.min || Number.MIN_VALUE), opts.max || Number.MAX_VALUE);
}

View File

@@ -1,11 +1,11 @@
import webp_dec, { WebPModule } from '../../../codecs/webp_dec/webp_dec'; import webp_dec, { WebPModule } from '../../../codecs/webp_dec/webp_dec';
import wasmUrl from '../../../codecs/webp_dec/webp_dec.wasm'; import wasmUrl from '../../../codecs/webp_dec/webp_dec.wasm';
import { initWasmModule } from '../util'; import { initEmscriptenModule } from '../util';
let emscriptenModule: Promise<WebPModule>; let emscriptenModule: Promise<WebPModule>;
export async function decode(data: ArrayBuffer): Promise<ImageData> { export async function decode(data: ArrayBuffer): Promise<ImageData> {
if (!emscriptenModule) emscriptenModule = initWasmModule(webp_dec, wasmUrl); if (!emscriptenModule) emscriptenModule = initEmscriptenModule(webp_dec, wasmUrl);
const module = await emscriptenModule; const module = await emscriptenModule;
const rawImage = module.decode(data); const rawImage = module.decode(data);

View File

@@ -1,12 +1,12 @@
import webp_enc, { WebPModule } from '../../../codecs/webp_enc/webp_enc'; import webp_enc, { WebPModule } from '../../../codecs/webp_enc/webp_enc';
import wasmUrl from '../../../codecs/webp_enc/webp_enc.wasm'; import wasmUrl from '../../../codecs/webp_enc/webp_enc.wasm';
import { EncodeOptions } from './encoder-meta'; import { EncodeOptions } from './encoder-meta';
import { initWasmModule } from '../util'; import { initEmscriptenModule } from '../util';
let emscriptenModule: Promise<WebPModule>; let emscriptenModule: Promise<WebPModule>;
export async function encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> { export async function encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> {
if (!emscriptenModule) emscriptenModule = initWasmModule(webp_enc, wasmUrl); if (!emscriptenModule) emscriptenModule = initEmscriptenModule(webp_enc, wasmUrl);
const module = await emscriptenModule; const module = await emscriptenModule;
const resultView = module.encode(data.data, data.width, data.height, options); const resultView = module.encode(data.data, data.width, data.height, options);

View File

@@ -14,9 +14,10 @@ const ROUTE_EDITOR = '/editor';
const compressPromise = import( const compressPromise = import(
/* webpackChunkName: "main-app" */ /* webpackChunkName: "main-app" */
'../compress'); '../compress');
const offlinerPromise = import(
/* webpackChunkName: "offliner" */ const swBridgePromise = import(
'../../lib/offliner'); /* webpackChunkName: "sw-bridge" */
'../../lib/sw-bridge');
function back() { function back() {
window.history.back(); window.history.back();
@@ -25,6 +26,7 @@ function back() {
interface Props {} interface Props {}
interface State { interface State {
awaitingShareTarget: boolean;
file?: File | Fileish; file?: File | Fileish;
isEditorOpen: Boolean; isEditorOpen: Boolean;
Compress?: typeof import('../compress').default; Compress?: typeof import('../compress').default;
@@ -32,6 +34,7 @@ interface State {
export default class App extends Component<Props, State> { export default class App extends Component<Props, State> {
state: State = { state: State = {
awaitingShareTarget: new URL(location.href).searchParams.has('share-target'),
isEditorOpen: false, isEditorOpen: false,
file: undefined, file: undefined,
Compress: undefined, Compress: undefined,
@@ -48,7 +51,15 @@ export default class App extends Component<Props, State> {
this.showSnack('Failed to load app'); this.showSnack('Failed to load app');
}); });
offlinerPromise.then(({ offliner }) => offliner(this.showSnack)); swBridgePromise.then(async ({ offliner, getSharedImage }) => {
offliner(this.showSnack);
if (!this.state.awaitingShareTarget) return;
const file = await getSharedImage();
// Remove the ?share-target from the URL
history.replaceState('', '', '/');
this.openEditor();
this.setState({ file, awaitingShareTarget: false });
});
// In development, persist application state across hot reloads: // In development, persist application state across hot reloads:
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
@@ -99,19 +110,25 @@ export default class App extends Component<Props, State> {
@bind @bind
private openEditor() { private openEditor() {
if (this.state.isEditorOpen) return; if (this.state.isEditorOpen) return;
history.pushState(null, '', ROUTE_EDITOR); // Change path, but preserve query string.
const editorURL = new URL(location.href);
editorURL.pathname = ROUTE_EDITOR;
history.pushState(null, '', editorURL.href);
this.setState({ isEditorOpen: true }); this.setState({ isEditorOpen: true });
} }
render({}: Props, { file, isEditorOpen, Compress }: State) { render({}: Props, { file, isEditorOpen, Compress, awaitingShareTarget }: State) {
const showSpinner = awaitingShareTarget || (isEditorOpen && !Compress);
return ( return (
<div id="app" class={style.app}> <div id="app" class={style.app}>
<file-drop accept="image/*" onfiledrop={this.onFileDrop} class={style.drop}> <file-drop accept="image/*" onfiledrop={this.onFileDrop} class={style.drop}>
{!isEditorOpen {
? <Intro onFile={this.onIntroPickFile} showSnack={this.showSnack} /> showSpinner
: (Compress) ? <loading-spinner class={style.appLoader}/>
? <Compress file={file!} showSnack={this.showSnack} onBack={back} /> : isEditorOpen
: <loading-spinner class={style.appLoader}/> ? Compress && <Compress file={file!} showSnack={this.showSnack} onBack={back} />
: <Intro onFile={this.onIntroPickFile} showSnack={this.showSnack} />
} }
<snack-bar ref={linkRef(this, 'snackbar')} /> <snack-bar ref={linkRef(this, 'snackbar')} />
</file-drop> </file-drop>

View File

@@ -146,12 +146,14 @@ export default class Options extends Component<Props, State> {
{preprocessorState.resize.enabled ? {preprocessorState.resize.enabled ?
<ResizeOptionsComponent <ResizeOptionsComponent
isVector={Boolean(source && source.vectorImage)} isVector={Boolean(source && source.vectorImage)}
aspect={source ? source.processed.width / source.processed.height : 1} inputWidth={source ? source.processed.width : 1}
inputHeight={source ? source.processed.height : 1}
options={preprocessorState.resize} options={preprocessorState.resize}
onChange={this.onResizeOptionsChange} onChange={this.onResizeOptionsChange}
/> />
: null} : null}
</Expander> </Expander>
<label class={style.sectionEnabler}> <label class={style.sectionEnabler}>
<Checkbox <Checkbox
name="quantizer.enable" name="quantizer.enable"
@@ -178,6 +180,7 @@ export default class Options extends Component<Props, State> {
{encoderSupportMap ? {encoderSupportMap ?
<Select value={encoderState.type} onChange={this.onEncoderTypeChange} large> <Select value={encoderState.type} onChange={this.onEncoderTypeChange} large>
{encoders.filter(encoder => encoderSupportMap[encoder.type]).map(encoder => ( {encoders.filter(encoder => encoderSupportMap[encoder.type]).map(encoder => (
// tslint:disable-next-line:jsx-key
<option value={encoder.type}>{encoder.label}</option> <option value={encoder.type}>{encoder.label}</option>
))} ))}
</Select> </Select>

View File

@@ -18,20 +18,14 @@ import * as browserTIFF from '../../codecs/browser-tiff/encoder-meta';
import * as browserJP2 from '../../codecs/browser-jp2/encoder-meta'; import * as browserJP2 from '../../codecs/browser-jp2/encoder-meta';
import * as browserBMP from '../../codecs/browser-bmp/encoder-meta'; import * as browserBMP from '../../codecs/browser-bmp/encoder-meta';
import * as browserPDF from '../../codecs/browser-pdf/encoder-meta'; import * as browserPDF from '../../codecs/browser-pdf/encoder-meta';
import { import { EncoderState, EncoderType, EncoderOptions, encoderMap } from '../../codecs/encoders';
EncoderState, import { PreprocessorState, defaultPreprocessorState } from '../../codecs/preprocessors';
EncoderType,
EncoderOptions,
encoderMap,
} from '../../codecs/encoders';
import {
PreprocessorState,
defaultPreprocessorState,
} from '../../codecs/preprocessors';
import { decodeImage } from '../../codecs/decoders'; import { decodeImage } from '../../codecs/decoders';
import { cleanMerge, cleanSet } from '../../lib/clean-modify'; import { cleanMerge, cleanSet } from '../../lib/clean-modify';
import Processor from '../../codecs/processor'; import Processor from '../../codecs/processor';
import { VectorResizeOptions, BitmapResizeOptions } from '../../codecs/resize/processor-meta'; import {
BrowserResizeOptions, isWorkerOptions as isWorkerResizeOptions, isHqx, WorkerResizeOptions,
} from '../../codecs/resize/processor-meta';
import './custom-els/MultiPanel'; import './custom-els/MultiPanel';
import Results from '../results'; import Results from '../results';
import { ExpandIcon, CopyAcrossIconProps } from '../../lib/icons'; import { ExpandIcon, CopyAcrossIconProps } from '../../lib/icons';
@@ -110,10 +104,24 @@ async function preprocessImage(
if (preprocessData.resize.method === 'vector' && source.vectorImage) { if (preprocessData.resize.method === 'vector' && source.vectorImage) {
result = processor.vectorResize( result = processor.vectorResize(
source.vectorImage, source.vectorImage,
preprocessData.resize as VectorResizeOptions, preprocessData.resize,
); );
} else if (isHqx(preprocessData.resize)) {
// Hqx can only do x2, x3 or x4.
result = await processor.workerResize(result, preprocessData.resize);
// Seems like the globals from Rust from hqx and resize are conflicting.
// For now we can fix that by terminating the worker.
// TODO: Use wasm-bindgens new --web target to create a proper ES6 module
// and remove this.
processor.terminateWorker();
// If the target size is not a clean x2, x3 or x4, use Catmull-Rom
// for the remaining scaling.
const pixelOpts = { ...preprocessData.resize, method: 'catrom' };
result = await processor.workerResize(result, pixelOpts as WorkerResizeOptions);
} else if (isWorkerResizeOptions(preprocessData.resize)) {
result = await processor.workerResize(result, preprocessData.resize);
} else { } else {
result = processor.resize(result, preprocessData.resize as BitmapResizeOptions); result = processor.resize(result, preprocessData.resize as BrowserResizeOptions);
} }
} }
if (preprocessData.quantizer.enabled) { if (preprocessData.quantizer.enabled) {
@@ -248,7 +256,7 @@ export default class Compress extends Component<Props, State> {
this.widthQuery.addListener(this.onMobileWidthChange); this.widthQuery.addListener(this.onMobileWidthChange);
this.updateFile(props.file); this.updateFile(props.file);
import('../../lib/offliner').then(({ mainAppLoaded }) => mainAppLoaded()); import('../../lib/sw-bridge').then(({ mainAppLoaded }) => mainAppLoaded());
} }
@bind @bind
@@ -441,7 +449,7 @@ export default class Compress extends Component<Props, State> {
newState = cleanMerge(newState, `sides.${i}.latestSettings.preprocessorState.resize`, { newState = cleanMerge(newState, `sides.${i}.latestSettings.preprocessorState.resize`, {
width: processed.width, width: processed.width,
height: processed.height, height: processed.height,
method: vectorImage ? 'vector' : 'browser-high', method: vectorImage ? 'vector' : 'lanczos3',
}); });
} }
@@ -571,6 +579,7 @@ export default class Compress extends Component<Props, State> {
const [leftImageData, rightImageData] = sides.map(i => i.data); const [leftImageData, rightImageData] = sides.map(i => i.data);
const options = sides.map((side, index) => ( const options = sides.map((side, index) => (
// tslint:disable-next-line:jsx-key
<Options <Options
source={source} source={source}
mobileView={mobileView} mobileView={mobileView}
@@ -586,6 +595,7 @@ export default class Compress extends Component<Props, State> {
(mobileView ? ['down', 'up'] : ['right', 'left']) as CopyAcrossIconProps['copyDirection'][]; (mobileView ? ['down', 'up'] : ['right', 'left']) as CopyAcrossIconProps['copyDirection'][];
const results = sides.map((side, index) => ( const results = sides.map((side, index) => (
// tslint:disable-next-line:jsx-key
<Results <Results
downloadUrl={side.downloadUrl} downloadUrl={side.downloadUrl}
imageFile={side.file} imageFile={side.file}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -53,11 +53,17 @@ export default class Intro extends Component<Props, State> {
state: State = {}; state: State = {};
private fileInput?: HTMLInputElement; private fileInput?: HTMLInputElement;
@bind
private resetFileInput() {
this.fileInput!.value = '';
}
@bind @bind
private onFileChange(event: Event): void { private onFileChange(event: Event): void {
const fileInput = event.target as HTMLInputElement; const fileInput = event.target as HTMLInputElement;
const file = fileInput.files && fileInput.files[0]; const file = fileInput.files && fileInput.files[0];
if (!file) return; if (!file) return;
this.resetFileInput();
this.props.onFile(file); this.props.onFile(file);
} }

View File

@@ -40,6 +40,23 @@ async function updateReady(reg: ServiceWorkerRegistration): Promise<void> {
}); });
} }
/** Wait for a shared image */
export function getSharedImage(): Promise<File> {
return new Promise((resolve) => {
const onmessage = (event: MessageEvent) => {
if (event.data.action !== 'load-image') return;
resolve(event.data.file);
navigator.serviceWorker.removeEventListener('message', onmessage);
};
navigator.serviceWorker.addEventListener('message', onmessage);
// This message is picked up by the service worker - it's how it knows we're ready to receive
// the file.
navigator.serviceWorker.controller!.postMessage('share-ready');
});
}
/** Set up the service worker and monitor changes */ /** Set up the service worker and monitor changes */
export async function offliner(showSnack: SnackBarElement['showSnackbar']) { export async function offliner(showSnack: SnackBarElement['showSnackbar']) {
// This needs to be a typeof because Webpack. // This needs to be a typeof because Webpack.

View File

@@ -11,6 +11,25 @@
"src": "/assets/icon-large.png", "src": "/assets/icon-large.png",
"type": "image/png", "type": "image/png",
"sizes": "1024x1024" "sizes": "1024x1024"
},
{
"src": "/assets/icon-large-maskable.png",
"type": "image/png",
"sizes": "1024x1024",
"purpose": "maskable"
} }
] ],
"share_target": {
"action": "/?share-target",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"files": [
{
"name": "file",
"accept": ["image/*"]
}
]
}
}
} }

View File

@@ -1,5 +1,6 @@
import { import {
cacheOrNetworkAndCache, cleanupCache, cacheOrNetwork, cacheBasics, cacheAdditionalProcessors, cacheOrNetworkAndCache, cleanupCache, cacheOrNetwork, cacheBasics, cacheAdditionalProcessors,
serveShareTarget,
} from './util'; } from './util';
import { get } from 'idb-keyval'; import { get } from 'idb-keyval';
@@ -40,14 +41,23 @@ self.addEventListener('activate', (event) => {
}); });
self.addEventListener('fetch', (event) => { self.addEventListener('fetch', (event) => {
// We only care about GET.
if (event.request.method !== 'GET') return;
const url = new URL(event.request.url); const url = new URL(event.request.url);
// Don't care about other-origin URLs // Don't care about other-origin URLs
if (url.origin !== location.origin) return; if (url.origin !== location.origin) return;
if (
url.pathname === '/' &&
url.searchParams.has('share-target') &&
event.request.method === 'POST'
) {
serveShareTarget(event);
return;
}
// We only care about GET from here on in.
if (event.request.method !== 'GET') return;
if (url.pathname.startsWith('/demo-') || url.pathname.startsWith('/wc-polyfill')) { if (url.pathname.startsWith('/demo-') || url.pathname.startsWith('/wc-polyfill')) {
cacheOrNetworkAndCache(event, dynamicCache); cacheOrNetworkAndCache(event, dynamicCache);
cleanupCache(event, dynamicCache, BUILD_ASSETS); cleanupCache(event, dynamicCache, BUILD_ASSETS);

View File

@@ -1,8 +1,11 @@
import webpDataUrl from 'url-loader!../codecs/tiny.webp'; import webpDataUrl from 'url-loader!../codecs/tiny.webp';
// Give TypeScript the correct global.
declare var self: ServiceWorkerGlobalScope;
export function cacheOrNetwork(event: FetchEvent): void { export function cacheOrNetwork(event: FetchEvent): void {
event.respondWith(async function () { event.respondWith(async function () {
const cachedResponse = await caches.match(event.request); const cachedResponse = await caches.match(event.request, { ignoreSearch: true });
return cachedResponse || fetch(event.request); return cachedResponse || fetch(event.request);
}()); }());
} }
@@ -29,6 +32,23 @@ export function cacheOrNetworkAndCache(event: FetchEvent, cacheName: string): vo
}()); }());
} }
export function serveShareTarget(event: FetchEvent): void {
const dataPromise = event.request.formData();
// Redirect so the user can refresh the page without resending data.
// @ts-ignore It doesn't like me giving a response to respondWith, although it's allowed.
event.respondWith(Response.redirect('/?share-target'));
event.waitUntil(async function () {
// The page sends this message to tell the service worker it's ready to receive the file.
await nextMessage('share-ready');
const client = await self.clients.get(event.resultingClientId);
const data = await dataPromise;
const file = data.get('file');
client.postMessage({ file, action: 'load-image' });
}());
}
export function cleanupCache(event: FetchEvent, cacheName: string, keepAssets: string[]) { export function cleanupCache(event: FetchEvent, cacheName: string, keepAssets: string[]) {
event.waitUntil(async function () { event.waitUntil(async function () {
const cache = await caches.open(cacheName); const cache = await caches.open(cacheName);
@@ -104,3 +124,26 @@ export async function cacheAdditionalProcessors(cacheName: string, buildAssets:
const cache = await caches.open(cacheName); const cache = await caches.open(cacheName);
await cache.addAll(toCache); await cache.addAll(toCache);
} }
const nextMessageResolveMap = new Map<string, (() => void)[]>();
/**
* Wait on a message with a particular event.data value.
*
* @param dataVal The event.data value.
*/
function nextMessage(dataVal: string): Promise<void> {
return new Promise((resolve) => {
if (!nextMessageResolveMap.has(dataVal)) {
nextMessageResolveMap.set(dataVal, []);
}
nextMessageResolveMap.get(dataVal)!.push(resolve);
});
}
self.addEventListener('message', (event) => {
const resolvers = nextMessageResolveMap.get(event.data);
if (!resolvers) return;
nextMessageResolveMap.delete(event.data);
for (const resolve of resolvers) resolve();
});

View File

@@ -12,7 +12,8 @@
"variable-name": [true, "check-format", "allow-leading-underscore"], "variable-name": [true, "check-format", "allow-leading-underscore"],
"no-duplicate-imports": false, "no-duplicate-imports": false,
"prefer-template": [true, "allow-single-concat"], "prefer-template": [true, "allow-single-concat"],
"import-name": false "import-name": false,
"jsx-key": false
}, },
"linterOptions": { "linterOptions": {
"exclude": [ "exclude": [

View File

@@ -1,4 +1,3 @@
const fs = require('fs');
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const CleanPlugin = require('clean-webpack-plugin'); const CleanPlugin = require('clean-webpack-plugin');
@@ -17,11 +16,7 @@ const CrittersPlugin = require('critters-webpack-plugin');
const AssetTemplatePlugin = require('./config/asset-template-plugin'); const AssetTemplatePlugin = require('./config/asset-template-plugin');
const addCssTypes = require('./config/add-css-types'); const addCssTypes = require('./config/add-css-types');
function readJson (filename) { const VERSION = require('./package.json').version;
return JSON.parse(fs.readFileSync(filename));
}
const VERSION = readJson('./package.json').version;
module.exports = async function (_, env) { module.exports = async function (_, env) {
const isProd = env.mode === 'production'; const isProd = env.mode === 'production';
@@ -147,11 +142,14 @@ module.exports = async function (_, env) {
}, },
{ {
// All the codec files define a global with the same name as their file name. `exports-loader` attaches those to `module.exports`. // All the codec files define a global with the same name as their file name. `exports-loader` attaches those to `module.exports`.
test: /\/codecs\/.*\.js$/, test: /\.js$/,
include: path.join(__dirname, 'src/codecs'),
loader: 'exports-loader' loader: 'exports-loader'
}, },
{ {
test: /\/codecs\/.*\.wasm$/, // Emscripten modules don't work with Webpack's Wasm loader.
test: /\.wasm$/,
exclude: /_bg\.wasm$/,
// This is needed to make webpack NOT process wasm files. // This is needed to make webpack NOT process wasm files.
// See https://github.com/webpack/webpack/issues/6725 // See https://github.com/webpack/webpack/issues/6725
type: 'javascript/auto', type: 'javascript/auto',
@@ -160,6 +158,11 @@ module.exports = async function (_, env) {
name: '[name].[hash:5].[ext]', name: '[name].[hash:5].[ext]',
}, },
}, },
{
// Wasm modules generated by Rust + wasm-pack work great with Webpack.
test: /_bg\.wasm$/,
type: 'webassembly/experimental',
},
{ {
test: /\.(png|svg|jpg|gif)$/, test: /\.(png|svg|jpg|gif)$/,
loader: 'file-loader', loader: 'file-loader',
@@ -172,7 +175,7 @@ module.exports = async function (_, env) {
plugins: [ plugins: [
new webpack.IgnorePlugin( new webpack.IgnorePlugin(
/(fs|crypto|path)/, /(fs|crypto|path)/,
new RegExp(`${path.sep}codecs${path.sep}`) /[/\\]codecs[/\\]/
), ),
// Pretty progressbar showing build progress: // Pretty progressbar showing build progress:
@@ -239,7 +242,7 @@ module.exports = async function (_, env) {
removeRedundantAttributes: true, removeRedundantAttributes: true,
removeComments: true removeComments: true
}, },
manifest: readJson('./src/manifest.json'), manifest: require('./src/manifest.json'),
inject: 'body', inject: 'body',
compile: true compile: true
}), }),