├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── core ├── Cargo.toml ├── README.md ├── build.rs ├── examples │ └── compare.rs └── src │ ├── compare.rs │ ├── decoder.rs │ ├── encoder.rs │ ├── error.rs │ ├── expander.rs │ ├── lib.rs │ ├── webp.c │ └── webp.rs ├── fixtures ├── 005a.png ├── 005b.png ├── sample0.png ├── sample0.webp ├── sample1.png └── sample1.webp ├── js ├── .gitignore ├── README.md ├── examples │ └── index.mjs ├── index.core.wasm ├── index.core2.wasm ├── index.d.ts ├── index.mjs ├── interfaces │ ├── bokuweb-image-diff-types.d.ts │ ├── wasi-cli-environment.d.ts │ ├── wasi-cli-exit.d.ts │ ├── wasi-cli-stderr.d.ts │ ├── wasi-cli-stdin.d.ts │ ├── wasi-cli-stdout.d.ts │ ├── wasi-cli-terminal-input.d.ts │ ├── wasi-cli-terminal-output.d.ts │ ├── wasi-cli-terminal-stderr.d.ts │ ├── wasi-cli-terminal-stdin.d.ts │ ├── wasi-cli-terminal-stdout.d.ts │ ├── wasi-clocks-wall-clock.d.ts │ ├── wasi-filesystem-preopens.d.ts │ ├── wasi-filesystem-types.d.ts │ ├── wasi-io-error.d.ts │ ├── wasi-io-streams.d.ts │ ├── wasi-random-random.d.ts │ └── wasi-sockets-tcp.d.ts ├── package.json └── pnpm-lock.yaml └── renovate.json /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: [push, pull_request] 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | jobs: 8 | node: 9 | runs-on: ubuntu-20.04 10 | defaults: 11 | run: 12 | working-directory: js 13 | steps: 14 | - uses: actions/checkout@master 15 | - uses: actions/setup-node@master 16 | - uses: pnpm/action-setup@v2 17 | with: 18 | version: 8 19 | - name: install dependencies 20 | run: pnpm i --frozen-lockfile 21 | - name: test 22 | run: node examples/index.mjs 23 | deno: 24 | runs-on: ubuntu-20.04 25 | defaults: 26 | run: 27 | working-directory: js 28 | steps: 29 | - uses: actions/checkout@master 30 | - uses: denoland/setup-deno@v1 31 | with: 32 | deno-version: v1.x 33 | - name: test 34 | run: deno run -A examples/index.mjs 35 | 36 | clippy: 37 | name: Clippy 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v1 41 | with: 42 | submodules: true 43 | - uses: actions-rs/toolchain@v1 44 | with: 45 | profile: minimal 46 | toolchain: 1.74.0 47 | override: true 48 | - run: rustup component add clippy 49 | - uses: actions-rs/cargo@v1 50 | with: 51 | command: clippy 52 | args: -- -D warnings 53 | 54 | example: 55 | name: Example 56 | runs-on: ubuntu-latest 57 | defaults: 58 | run: 59 | working-directory: core 60 | steps: 61 | - uses: actions/checkout@v1 62 | with: 63 | submodules: true 64 | - uses: actions-rs/toolchain@v1 65 | with: 66 | profile: minimal 67 | toolchain: 1.74.0 68 | override: true 69 | - run: rustup component add clippy 70 | - uses: actions-rs/cargo@v1 71 | with: 72 | command: run 73 | args: --example compare 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | memo.md 3 | .wasm 4 | .js 5 | wasi-sdk 6 | node/generated### Generated by gibo (https://github.com/simonwhitaker/gibo) 7 | ### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Rust.gitignore 8 | 9 | # Generated by Cargo 10 | # will have compiled files and executables 11 | debug/ 12 | target/ 13 | 14 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 15 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 16 | Cargo.lock 17 | 18 | # These are backup files generated by rustfmt 19 | **/*.rs.bk 20 | 21 | # MSVC Windows builds of rustc generate these, which store debugging information 22 | *.pdb 23 | 24 | 25 | ### Generated by gibo (https://github.com/simonwhitaker/gibo) 26 | ### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Node.gitignore 27 | 28 | # Logs 29 | logs 30 | *.log 31 | npm-debug.log* 32 | yarn-debug.log* 33 | yarn-error.log* 34 | lerna-debug.log* 35 | .pnpm-debug.log* 36 | 37 | # Diagnostic reports (https://nodejs.org/api/report.html) 38 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 39 | 40 | # Runtime data 41 | pids 42 | *.pid 43 | *.seed 44 | *.pid.lock 45 | 46 | # Directory for instrumented libs generated by jscoverage/JSCover 47 | lib-cov 48 | 49 | # Coverage directory used by tools like istanbul 50 | coverage 51 | *.lcov 52 | 53 | # nyc test coverage 54 | .nyc_output 55 | 56 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 57 | .grunt 58 | 59 | # Bower dependency directory (https://bower.io/) 60 | bower_components 61 | 62 | # node-waf configuration 63 | .lock-wscript 64 | 65 | # Compiled binary addons (https://nodejs.org/api/addons.html) 66 | build/Release 67 | 68 | # Dependency directories 69 | node_modules/ 70 | jspm_packages/ 71 | 72 | # Snowpack dependency directory (https://snowpack.dev/) 73 | web_modules/ 74 | 75 | # TypeScript cache 76 | *.tsbuildinfo 77 | 78 | # Optional npm cache directory 79 | .npm 80 | 81 | # Optional eslint cache 82 | .eslintcache 83 | 84 | # Optional stylelint cache 85 | .stylelintcache 86 | 87 | # Microbundle cache 88 | .rpt2_cache/ 89 | .rts2_cache_cjs/ 90 | .rts2_cache_es/ 91 | .rts2_cache_umd/ 92 | 93 | # Optional REPL history 94 | .node_repl_history 95 | 96 | # Output of 'npm pack' 97 | *.tgz 98 | 99 | # Yarn Integrity file 100 | .yarn-integrity 101 | 102 | # dotenv environment variable files 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | .cache 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | .next 115 | out 116 | 117 | # Nuxt.js build / generate output 118 | .nuxt 119 | dist 120 | 121 | # Gatsby files 122 | .cache/ 123 | # Comment in the public line in if your project uses Gatsby and not Next.js 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | # public 126 | 127 | # vuepress build output 128 | .vuepress/dist 129 | 130 | # vuepress v2.x temp and cache directory 131 | .temp 132 | .cache 133 | 134 | # Docusaurus cache and generated files 135 | .docusaurus 136 | 137 | # Serverless directories 138 | .serverless/ 139 | 140 | # FuseBox cache 141 | .fusebox/ 142 | 143 | # DynamoDB Local files 144 | .dynamodb/ 145 | 146 | # TernJS port file 147 | .tern-port 148 | 149 | # Stores VSCode versions used for testing VSCode extensions 150 | .vscode-test 151 | 152 | # yarn v2 153 | .yarn/cache 154 | .yarn/unplugged 155 | .yarn/build-state.yml 156 | .yarn/install-state.gz 157 | .pnp.* 158 | 159 | 160 | diff.webp 161 | diff0.webp 162 | diff1.webp 163 | component.wasm -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libwebp"] 2 | path = libwebp 3 | url = https://github.com/webmproject/libwebp.git 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.0.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.63" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" 31 | dependencies = [ 32 | "addr2line", 33 | "cc", 34 | "cfg-if", 35 | "libc", 36 | "miniz_oxide 0.4.4", 37 | "object", 38 | "rustc-demangle", 39 | ] 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "1.3.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 46 | 47 | [[package]] 48 | name = "bytemuck" 49 | version = "1.14.3" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" 52 | 53 | [[package]] 54 | name = "byteorder-lite" 55 | version = "0.1.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" 58 | 59 | [[package]] 60 | name = "cc" 61 | version = "1.1.37" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" 64 | dependencies = [ 65 | "shlex", 66 | ] 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "1.0.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 73 | 74 | [[package]] 75 | name = "color_quant" 76 | version = "1.1.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 79 | 80 | [[package]] 81 | name = "crc32fast" 82 | version = "1.3.1" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" 85 | dependencies = [ 86 | "cfg-if", 87 | ] 88 | 89 | [[package]] 90 | name = "failure" 91 | version = "0.1.8" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" 94 | dependencies = [ 95 | "backtrace", 96 | "failure_derive", 97 | ] 98 | 99 | [[package]] 100 | name = "failure_derive" 101 | version = "0.1.8" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" 104 | dependencies = [ 105 | "proc-macro2", 106 | "quote", 107 | "syn 1.0.85", 108 | "synstructure", 109 | ] 110 | 111 | [[package]] 112 | name = "fdeflate" 113 | version = "0.3.1" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" 116 | dependencies = [ 117 | "simd-adler32", 118 | ] 119 | 120 | [[package]] 121 | name = "flate2" 122 | version = "1.0.24" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" 125 | dependencies = [ 126 | "crc32fast", 127 | "miniz_oxide 0.5.3", 128 | ] 129 | 130 | [[package]] 131 | name = "gif" 132 | version = "0.13.1" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" 135 | dependencies = [ 136 | "color_quant", 137 | "weezl", 138 | ] 139 | 140 | [[package]] 141 | name = "gimli" 142 | version = "0.26.1" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" 145 | 146 | [[package]] 147 | name = "image" 148 | version = "0.25.5" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" 151 | dependencies = [ 152 | "bytemuck", 153 | "byteorder-lite", 154 | "color_quant", 155 | "gif", 156 | "num-traits", 157 | "png", 158 | "tiff", 159 | "zune-core", 160 | "zune-jpeg", 161 | ] 162 | 163 | [[package]] 164 | name = "image-diff-rs" 165 | version = "0.1.0" 166 | dependencies = [ 167 | "cc", 168 | "image", 169 | "pixelmatch-rs", 170 | "thiserror", 171 | ] 172 | 173 | [[package]] 174 | name = "jpeg-decoder" 175 | version = "0.3.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" 178 | 179 | [[package]] 180 | name = "libc" 181 | version = "0.2.126" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 184 | 185 | [[package]] 186 | name = "memchr" 187 | version = "2.4.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 190 | 191 | [[package]] 192 | name = "miniz_oxide" 193 | version = "0.4.4" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 196 | dependencies = [ 197 | "adler", 198 | "autocfg", 199 | ] 200 | 201 | [[package]] 202 | name = "miniz_oxide" 203 | version = "0.5.3" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" 206 | dependencies = [ 207 | "adler", 208 | ] 209 | 210 | [[package]] 211 | name = "miniz_oxide" 212 | version = "0.7.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 215 | dependencies = [ 216 | "adler", 217 | "simd-adler32", 218 | ] 219 | 220 | [[package]] 221 | name = "num-traits" 222 | version = "0.2.14" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 225 | dependencies = [ 226 | "autocfg", 227 | ] 228 | 229 | [[package]] 230 | name = "object" 231 | version = "0.27.1" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" 234 | dependencies = [ 235 | "memchr", 236 | ] 237 | 238 | [[package]] 239 | name = "pixelmatch-rs" 240 | version = "0.1.0" 241 | source = "git+https://github.com/bokuweb/pixelmatch-rs.git#0f81680bf816b75bddebf01eb2f6e0b054ca3a89" 242 | dependencies = [ 243 | "failure", 244 | ] 245 | 246 | [[package]] 247 | name = "png" 248 | version = "0.17.10" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" 251 | dependencies = [ 252 | "bitflags", 253 | "crc32fast", 254 | "fdeflate", 255 | "flate2", 256 | "miniz_oxide 0.7.1", 257 | ] 258 | 259 | [[package]] 260 | name = "proc-macro2" 261 | version = "1.0.89" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" 264 | dependencies = [ 265 | "unicode-ident", 266 | ] 267 | 268 | [[package]] 269 | name = "quote" 270 | version = "1.0.37" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 273 | dependencies = [ 274 | "proc-macro2", 275 | ] 276 | 277 | [[package]] 278 | name = "rustc-demangle" 279 | version = "0.1.21" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 282 | 283 | [[package]] 284 | name = "shlex" 285 | version = "1.3.0" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 288 | 289 | [[package]] 290 | name = "simd-adler32" 291 | version = "0.3.7" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 294 | 295 | [[package]] 296 | name = "syn" 297 | version = "1.0.85" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" 300 | dependencies = [ 301 | "proc-macro2", 302 | "quote", 303 | "unicode-xid", 304 | ] 305 | 306 | [[package]] 307 | name = "syn" 308 | version = "2.0.87" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 311 | dependencies = [ 312 | "proc-macro2", 313 | "quote", 314 | "unicode-ident", 315 | ] 316 | 317 | [[package]] 318 | name = "synstructure" 319 | version = "0.12.6" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 322 | dependencies = [ 323 | "proc-macro2", 324 | "quote", 325 | "syn 1.0.85", 326 | "unicode-xid", 327 | ] 328 | 329 | [[package]] 330 | name = "thiserror" 331 | version = "2.0.1" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "07c1e40dd48a282ae8edc36c732cbc219144b87fb6a4c7316d611c6b1f06ec0c" 334 | dependencies = [ 335 | "thiserror-impl", 336 | ] 337 | 338 | [[package]] 339 | name = "thiserror-impl" 340 | version = "2.0.1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "874aa7e446f1da8d9c3a5c95b1c5eb41d800045252121dc7f8e0ba370cee55f5" 343 | dependencies = [ 344 | "proc-macro2", 345 | "quote", 346 | "syn 2.0.87", 347 | ] 348 | 349 | [[package]] 350 | name = "tiff" 351 | version = "0.9.0" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" 354 | dependencies = [ 355 | "flate2", 356 | "jpeg-decoder", 357 | "weezl", 358 | ] 359 | 360 | [[package]] 361 | name = "unicode-ident" 362 | version = "1.0.12" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 365 | 366 | [[package]] 367 | name = "unicode-xid" 368 | version = "0.2.2" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 371 | 372 | [[package]] 373 | name = "weezl" 374 | version = "0.1.8" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" 377 | 378 | [[package]] 379 | name = "zune-core" 380 | version = "0.4.12" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" 383 | 384 | [[package]] 385 | name = "zune-jpeg" 386 | version = "0.4.13" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" 389 | dependencies = [ 390 | "zune-core", 391 | ] 392 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "core" 5 | ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 bokuweb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # image-diff-rs 2 | 3 | Build Status 4 | 5 | This project provides an image differencing library that supports `PNG`,`JPEG`,`GIF`,`TIFF`,and `WebP` formats for `Node.js`, `Deno`, and `Rust`. For more details, please refer to each respective directory. 6 | 7 | The code for `Node.js` and `Deno` is generated using `wit-bindgen` and `jco`. 8 | 9 | - JS: https://github.com/bokuweb/image-diff-rs/tree/main/js 10 | - Wasm: https://github.com/bokuweb/image-diff-rs/tree/main/wasm 11 | - Rust: https://github.com/bokuweb/image-diff-rs/tree/main/core 12 | 13 | ## Demo 14 | 15 | | img1 | img2 | diff | 16 | | --------------- |---------------| -------------------- | 17 | | ![](https://github.com/bokuweb/pixelmatch-rs/raw/main/fixtures/001a.png) | ![](https://github.com/bokuweb/pixelmatch-rs/raw/main/fixtures/001b.png) |![](https://github.com/bokuweb/pixelmatch-rs/raw/main/assets/diff1.png)| 18 | 19 | ## JavaScript 20 | 21 | 22 | ### installation 23 | 24 | ``` 25 | npm install @bokuweb/image-diff-wasm 26 | ``` 27 | 28 | ### examples 29 | 30 | ```js 31 | import { readFile } from "node:fs/promises"; 32 | import { diff } from "@bokuweb/image-diff-wasm"; 33 | 34 | const imga = await readFile(PATH_TO_IMAGE_A); 35 | const imgb = await readFile(PATH_TO_IMAGE_B); 36 | 37 | const result = diff(imga, imgb, { enableAntiAlias: true, threshold: 0.01 }); 38 | ``` 39 | 40 | ### API 41 | 42 | #### diff(imga: Uint8Array, imgb: Uint8Array, opts: Opts): Output; 43 | 44 | The diff function is designed to compare two images and identify their differences. 45 | It takes two image buffers as input and returns an `Output` object containing information about the differences. 46 | 47 | ##### Input 48 | 49 | - `imga`: Uint8Array: The first image buffer. 50 | - `imgb`: Uint8Array: The second image buffer. 51 | - `opts`: Opts: Options object for the function. 52 | 53 | ```Typescript 54 | export interface Opts { 55 | threshold?: number, 56 | includeAntiAlias?: boolean, 57 | } 58 | ``` 59 | 60 | - `threshold`: Matching threshold, ranges from 0 to 1. Smaller values make the comparison more sensitive. 0.1 by default. 61 | - `includeAntiAlias`: The flag of antialias. If omitted false. 62 | 63 | ##### Output 64 | 65 | ```Typescript 66 | export interface Output { 67 | diffCount: number, 68 | diffImage: Uint8Array, 69 | width: number, 70 | height: number, 71 | } 72 | ``` 73 | 74 | - `diffCount`: The number of pixels that differ between the two images. 75 | - `diffImage`: The buffer of the difference image in `WebP` format. 76 | - `width`: The width of the difference image. 77 | - `height`: The height of the difference image. 78 | 79 | ##### Error 80 | 81 | The function may throw following values as `ComponentError`. 82 | 83 | ```Typescript 84 | export type Error = ErrorDecode | ErrorEncode; 85 | export interface ErrorDecode { 86 | tag: 'decode', 87 | val: string, 88 | } 89 | export interface ErrorEncode { 90 | tag: 'encode', 91 | val: string, 92 | } 93 | ``` 94 | 95 | ## Rust 96 | 97 | ### Installation 98 | 99 | ``` toml 100 | image-diff-rs = { git = "https://github.com/bokuweb/image-diff-rs.git" } 101 | ``` 102 | 103 | ### examples 104 | 105 | ```Rust 106 | pub fn main() { 107 | let imga = std::fs::read("../fixtures/sample0.webp").unwrap(); 108 | let imgb = std::fs::read("../fixtures/sample1.webp").unwrap(); 109 | 110 | let _result = diff( 111 | imga, 112 | imgb, 113 | &DiffOption { 114 | threshold: Some(0.01), 115 | include_anti_alias: Some(true), 116 | }, 117 | ) 118 | .unwrap(); 119 | } 120 | ``` 121 | 122 | ``` sh 123 | cargo run --example compare 124 | ``` 125 | 126 | ## Wasm 127 | 128 | ### Generate JS code from wasm component. 129 | 130 | ```sh 131 | AR=llvm-ar CFLAGS='--sysroot ../wasi-sdk/share/wasi-sysroot' cargo wasi build --release 132 | ``` 133 | 134 | ```sh 135 | wasm-tools component new target/wasm32-wasi/release/image_diff_wasm.wasm -o wasm/component.wasm --adapt wasm/wasi_snapshot_preview1.wasm 136 | ``` 137 | 138 | ```sh 139 | jco transpile wasm/component.wasm -o js --name index && mv js/index.js js/index.mjs 140 | ``` 141 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "image-diff-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "image_diff_rs" 10 | path = "src/lib.rs" 11 | 12 | [dependencies] 13 | pixelmatch-rs = { git = "https://github.com/bokuweb/pixelmatch-rs.git" } 14 | image = { version = "0.25.0", default-features = false, features=["gif", "jpeg", "png", "tiff", "bmp"] } 15 | thiserror = "2.0" 16 | 17 | [build-dependencies] 18 | cc = "1.0" -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 | # image-diff-rs 2 | 3 | Build Status 4 | 5 | This library is an image differencing crate that supports PNG, JPEG, GIF, TIFF, and WebP formats. 6 | 7 | ## installation 8 | 9 | ``` 10 | cargo add image-diff-rs 11 | ``` 12 | 13 | ## examples 14 | 15 | ```Rust 16 | pub fn main() { 17 | let imga = std::fs::read("../fixtures/sample0.webp").unwrap(); 18 | let imgb = std::fs::read("../fixtures/sample1.webp").unwrap(); 19 | 20 | let _result = diff( 21 | imga, 22 | imgb, 23 | &DiffOption { 24 | threshold: Some(0.01), 25 | include_anti_alias: Some(true), 26 | }, 27 | ) 28 | .unwrap(); 29 | } 30 | ``` 31 | 32 | ``` sh 33 | cargo run --example compare 34 | ``` 35 | 36 | ## Demo 37 | 38 | | img1 | img2 | diff | 39 | | --------------- |---------------| -------------------- | 40 | | ![](https://github.com/bokuweb/pixelmatch-rs/raw/main/fixtures/001a.png) | ![](https://github.com/bokuweb/pixelmatch-rs/raw/main/fixtures/001b.png) |![](https://github.com/bokuweb/pixelmatch-rs/raw/main/assets/diff1.png)| 41 | 42 | ## License 43 | 44 | Rust glue is provided under the MIT License, and the libwebp is provided by Google under the BSD 3-Clause License. 45 | -------------------------------------------------------------------------------- /core/build.rs: -------------------------------------------------------------------------------- 1 | extern crate cc; 2 | 3 | fn main() { 4 | let target = std::env::var("TARGET").expect("TARGET environment variable not set"); 5 | if target == "wasm32-wasi-preview1-threads" || target == "wasm32-wasip1-threads" { 6 | // let wasi_sdk_path = 7 | // std::env::var("WASI_SDK_PATH").expect("WASI_SDK_PATH environment variable not set"); 8 | // println!( 9 | // "cargo:rustc-env=CFLAGS=--sysroot {}/share/wasi-sysroot", 10 | // wasi_sdk_path 11 | // ); 12 | if let Ok(cflags) = std::env::var("CFLAGS") { 13 | println!("cargo:rustc-env=CFLAGS={}", cflags); 14 | } 15 | } 16 | 17 | cc::Build::new() 18 | .file("src/webp.c") 19 | .file("../libwebp/src/dec/alpha_dec.c") 20 | .file("../libwebp/src/dec/buffer_dec.c") 21 | .file("../libwebp/src/dec/frame_dec.c") 22 | .file("../libwebp/src/dec/idec_dec.c") 23 | .file("../libwebp/src/dec/io_dec.c") 24 | .file("../libwebp/src/dec/quant_dec.c") 25 | .file("../libwebp/src/dec/tree_dec.c") 26 | .file("../libwebp/src/dec/vp8_dec.c") 27 | .file("../libwebp/src/dec/vp8l_dec.c") 28 | .file("../libwebp/src/dec/webp_dec.c") 29 | .file("../libwebp/src/enc/alpha_enc.c") 30 | .file("../libwebp/src/enc/analysis_enc.c") 31 | .file("../libwebp/src/enc/backward_references_cost_enc.c") 32 | .file("../libwebp/src/enc/backward_references_enc.c") 33 | .file("../libwebp/src/enc/config_enc.c") 34 | .file("../libwebp/src/enc/cost_enc.c") 35 | .file("../libwebp/src/enc/filter_enc.c") 36 | .file("../libwebp/src/enc/frame_enc.c") 37 | .file("../libwebp/src/enc/histogram_enc.c") 38 | .file("../libwebp/src/enc/iterator_enc.c") 39 | .file("../libwebp/src/enc/near_lossless_enc.c") 40 | .file("../libwebp/src/enc/picture_enc.c") 41 | .file("../libwebp/src/enc/picture_csp_enc.c") 42 | .file("../libwebp/src/enc/picture_psnr_enc.c") 43 | .file("../libwebp/src/enc/picture_rescale_enc.c") 44 | .file("../libwebp/src/enc/picture_tools_enc.c") 45 | .file("../libwebp/src/enc/predictor_enc.c") 46 | .file("../libwebp/src/enc/quant_enc.c") 47 | .file("../libwebp/src/enc/syntax_enc.c") 48 | .file("../libwebp/src/enc/token_enc.c") 49 | .file("../libwebp/src/enc/tree_enc.c") 50 | .file("../libwebp/src/enc/vp8l_enc.c") 51 | .file("../libwebp/src/enc/webp_enc.c") 52 | .file("../libwebp/src/dsp/alpha_processing.c") 53 | .file("../libwebp/src/dsp/alpha_processing_mips_dsp_r2.c") 54 | .file("../libwebp/src/dsp/cpu.c") 55 | .file("../libwebp/src/dsp/dec.c") 56 | .file("../libwebp/src/dsp/dec_clip_tables.c") 57 | .file("../libwebp/src/dsp/dec_mips32.c") 58 | .file("../libwebp/src/dsp/dec_mips_dsp_r2.c") 59 | .file("../libwebp/src/dsp/filters.c") 60 | .file("../libwebp/src/dsp/filters_mips_dsp_r2.c") 61 | .file("../libwebp/src/dsp/lossless.c") 62 | .file("../libwebp/src/dsp/lossless_mips_dsp_r2.c") 63 | .file("../libwebp/src/dsp/rescaler.c") 64 | .file("../libwebp/src/dsp/rescaler_mips32.c") 65 | .file("../libwebp/src/dsp/rescaler_mips_dsp_r2.c") 66 | .file("../libwebp/src/dsp/upsampling.c") 67 | .file("../libwebp/src/dsp/upsampling_mips_dsp_r2.c") 68 | .file("../libwebp/src/dsp/yuv.c") 69 | .file("../libwebp/src/dsp/yuv_mips32.c") 70 | .file("../libwebp/src/dsp/yuv_mips_dsp_r2.c") 71 | .file("../libwebp/src/dsp/cost.c") 72 | .file("../libwebp/src/dsp/cost_mips32.c") 73 | .file("../libwebp/src/dsp/cost_mips_dsp_r2.c") 74 | .file("../libwebp/src/dsp/cost_neon.c") 75 | .file("../libwebp/src/dsp/enc.c") 76 | .file("../libwebp/src/dsp/enc_mips32.c") 77 | .file("../libwebp/src/dsp/enc_mips_dsp_r2.c") 78 | .file("../libwebp/src/dsp/lossless_enc.c") 79 | .file("../libwebp/src/dsp/lossless_enc_mips32.c") 80 | .file("../libwebp/src/dsp/lossless_enc_mips_dsp_r2.c") 81 | .file("../libwebp/src/dsp/ssim.c") 82 | .file("../libwebp/src/dsp/alpha_processing_sse41.c") 83 | .file("../libwebp/src/dsp/dec_sse41.c") 84 | .file("../libwebp/src/dsp/upsampling_sse41.c") 85 | .file("../libwebp/src/dsp/yuv_sse41.c") 86 | .file("../libwebp/src/dsp/alpha_processing_sse2.c") 87 | .file("../libwebp/src/dsp/dec_sse2.c") 88 | .file("../libwebp/src/dsp/filters_sse2.c") 89 | .file("../libwebp/src/dsp/lossless_sse2.c") 90 | .file("../libwebp/src/dsp/rescaler_sse2.c") 91 | .file("../libwebp/src/dsp/upsampling_sse2.c") 92 | .file("../libwebp/src/dsp/yuv_sse2.c") 93 | .file("../libwebp/src/dsp/alpha_processing_neon.c") 94 | .file("../libwebp/src/dsp/dec_neon.c") 95 | .file("../libwebp/src/dsp/filters_neon.c") 96 | .file("../libwebp/src/dsp/lossless_neon.c") 97 | .file("../libwebp/src/dsp/rescaler_neon.c") 98 | .file("../libwebp/src/dsp/upsampling_neon.c") 99 | .file("../libwebp/src/dsp/yuv_neon.c") 100 | .file("../libwebp/src/dsp/dec_msa.c") 101 | .file("../libwebp/src/dsp/filters_msa.c") 102 | .file("../libwebp/src/dsp/lossless_msa.c") 103 | .file("../libwebp/src/dsp/rescaler_msa.c") 104 | .file("../libwebp/src/dsp/upsampling_msa.c") 105 | .file("../libwebp/src/dsp/cost_sse2.c") 106 | .file("../libwebp/src/dsp/enc_sse2.c") 107 | .file("../libwebp/src/dsp/lossless_enc_sse2.c") 108 | .file("../libwebp/src/dsp/ssim_sse2.c") 109 | .file("../libwebp/src/dsp/enc_sse41.c") 110 | .file("../libwebp/src/dsp/lossless_enc_sse41.c") 111 | .file("../libwebp/src/dsp/enc_neon.c") 112 | .file("../libwebp/src/dsp/lossless_enc_neon.c") 113 | .file("../libwebp/src/dsp/enc_msa.c") 114 | .file("../libwebp/src/dsp/lossless_enc_msa.c") 115 | .file("../libwebp/src/utils/bit_reader_utils.c") 116 | .file("../libwebp/src/utils/color_cache_utils.c") 117 | .file("../libwebp/src/utils/filters_utils.c") 118 | .file("../libwebp/src/utils/huffman_utils.c") 119 | .file("../libwebp/src/utils/quant_levels_dec_utils.c") 120 | .file("../libwebp/src/utils/rescaler_utils.c") 121 | .file("../libwebp/src/utils/random_utils.c") 122 | .file("../libwebp/src/utils/thread_utils.c") 123 | .file("../libwebp/src/utils/utils.c") 124 | .file("../libwebp/src/utils/bit_writer_utils.c") 125 | .file("../libwebp/src/utils/huffman_encode_utils.c") 126 | .file("../libwebp/src/utils/quant_levels_utils.c") 127 | .file("../libwebp/src/utils/palette.c") 128 | .file("../libwebp/sharpyuv/sharpyuv_dsp.c") 129 | .file("../libwebp/sharpyuv/sharpyuv_csp.c") 130 | .file("../libwebp/sharpyuv/sharpyuv_gamma.c") 131 | .file("../libwebp/sharpyuv/sharpyuv.c") 132 | .file("../libwebp/sharpyuv/sharpyuv_neon.c") 133 | .file("../libwebp/sharpyuv/sharpyuv_cpu.c") 134 | .file("../libwebp/sharpyuv/sharpyuv_sse2.c") 135 | .include("../libwebp") 136 | .compile("webp"); 137 | } 138 | -------------------------------------------------------------------------------- /core/examples/compare.rs: -------------------------------------------------------------------------------- 1 | use image_diff_rs::*; 2 | 3 | pub fn main() { 4 | let imga = std::fs::read("./fixtures/sample0.webp").unwrap(); 5 | let imgb = std::fs::read("./fixtures/sample1.webp").unwrap(); 6 | 7 | let result = diff( 8 | imga, 9 | imgb, 10 | &DiffOption { 11 | threshold: Some(0.01), 12 | include_anti_alias: Some(true), 13 | }, 14 | ) 15 | .unwrap(); 16 | 17 | if let DiffOutput::NotEq { diff_count, .. } = result { 18 | assert_eq!(diff_count, 3454); 19 | } else { 20 | unreachable!() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/compare.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use pixelmatch::*; 4 | 5 | use super::*; 6 | 7 | /// The output produced by the `diff` function. 8 | /// 9 | /// Contains detailed information about the differences between two images. 10 | /// 11 | #[derive(Debug, PartialEq, Clone)] 12 | pub enum DiffOutput { 13 | NotEq { 14 | /// The total number of pixels that differ between the two images. 15 | diff_count: usize, 16 | /// A `Vec` containing the diff image data (WebP format). 17 | diff_image: Vec, 18 | /// The width of the diff image. 19 | width: u32, 20 | /// The height of the diff image. 21 | height: u32, 22 | }, 23 | Eq, 24 | } 25 | 26 | #[derive(Debug, Default, PartialEq, Clone)] 27 | pub struct CompareInput> { 28 | pub actual_filename: P, 29 | pub expected_filename: P, 30 | pub diff_filename: P, 31 | pub threshold: Option, 32 | pub include_anti_alias: Option, 33 | } 34 | 35 | impl> CompareInput

{ 36 | pub fn new(actual_filename: P, expected_filename: P, diff_filename: P) -> Self { 37 | Self { 38 | actual_filename, 39 | expected_filename, 40 | diff_filename, 41 | threshold: None, 42 | include_anti_alias: None, 43 | } 44 | } 45 | } 46 | 47 | #[derive(Debug, Default, PartialEq, Clone)] 48 | pub struct CompareOption { 49 | pub threshold: f32, 50 | pub enable_anti_alias: bool, 51 | } 52 | 53 | pub fn compare_buf( 54 | img1: &[u8], 55 | img2: &[u8], 56 | dimensions: (u32, u32), 57 | opt: CompareOption, 58 | ) -> Result { 59 | let result = pixelmatch( 60 | img1, 61 | img2, 62 | dimensions, 63 | Some(PixelmatchOption { 64 | threshold: opt.threshold, 65 | include_anti_alias: true, 66 | ..PixelmatchOption::default() 67 | }), 68 | ) 69 | .expect("pixelmatch should succeed, but it panicked. This appears to be a bug"); 70 | 71 | Ok(DiffOutput::NotEq { 72 | diff_count: result.diff_count, 73 | diff_image: result.diff_image, 74 | width: dimensions.0, 75 | height: dimensions.1, 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /core/src/decoder.rs: -------------------------------------------------------------------------------- 1 | use image::GenericImageView; 2 | 3 | use super::*; 4 | 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub struct DecodeOutput { 7 | pub buf: Vec, 8 | pub dimensions: (u32, u32), 9 | } 10 | 11 | pub fn decode_buf(buf: &[u8]) -> Result { 12 | match buf { 13 | // RIFF .... WEBP 14 | [b'R', b'I', b'F', b'F', _, _, _, _, b'W', b'E', b'B', b'P', ..] => { 15 | Ok(decode_webp_buf(buf).map_err(|_| { 16 | ImageDiffError::Decode(("Failed to decode as webp format").to_string()) 17 | })?) 18 | } 19 | _ => { 20 | let i = image::load_from_memory(buf); 21 | if let Ok(opened) = i { 22 | Ok(DecodeOutput { 23 | dimensions: opened.dimensions(), 24 | buf: opened.to_rgba8().to_vec(), 25 | }) 26 | } else { 27 | Err(ImageDiffError::Decode(i.unwrap_err().to_string())) 28 | } 29 | } 30 | } 31 | } 32 | 33 | #[test] 34 | fn test_decode_webp_buf() { 35 | let buf = include_bytes!("../../fixtures/sample0.webp"); 36 | let res = decode_buf(buf); 37 | assert!(res.is_ok()); 38 | let decoded = res.unwrap(); 39 | assert_eq!(decoded.dimensions.0, 800); 40 | assert_eq!(decoded.dimensions.1, 578); 41 | } 42 | 43 | #[test] 44 | fn test_decode_png_buf() { 45 | let buf = include_bytes!("../../fixtures/sample0.png"); 46 | let res = decode_buf(buf); 47 | assert!(res.is_ok()); 48 | let decoded = res.unwrap(); 49 | assert_eq!(decoded.dimensions.0, 800); 50 | assert_eq!(decoded.dimensions.1, 578); 51 | } 52 | -------------------------------------------------------------------------------- /core/src/encoder.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Debug, Clone, PartialEq)] 4 | pub struct EncodeOutput { 5 | pub buf: Vec, 6 | } 7 | 8 | pub fn encode(rgba: &[u8], width: u32, height: u32) -> Result, ImageDiffError> { 9 | encode_webp(rgba, width, height) 10 | } 11 | -------------------------------------------------------------------------------- /core/src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | 5 | /// Enum representing potential errors that can occur during the image diffing process. 6 | pub enum ImageDiffError { 7 | /// Error that occurs when decoding the input image data fail.s 8 | #[error("it is not able to decode {0}.")] 9 | Decode(String), 10 | /// Error that occurs when encoding the diff image data fails. 11 | #[error("it is not able to encode {0}.")] 12 | Encode(String), 13 | } 14 | -------------------------------------------------------------------------------- /core/src/expander.rs: -------------------------------------------------------------------------------- 1 | pub(crate) fn expand( 2 | original_image: Vec, 3 | original_dimensions: (u32, u32), 4 | expand_width: u32, 5 | expand_height: u32, 6 | ) -> Vec { 7 | let orig_width = original_dimensions.0; 8 | let orig_height = original_dimensions.1; 9 | 10 | if orig_width == expand_width && orig_height == expand_height { 11 | return original_image; 12 | } 13 | 14 | let mut new_data = vec![0; (expand_width * expand_height * 4) as usize]; 15 | 16 | for j in 0..expand_height { 17 | if j < orig_height { 18 | for i in 0..expand_width { 19 | let idx = (j * expand_width + i) * 4; 20 | if i < orig_width { 21 | let orig_idx = (j * orig_width + i) * 4; 22 | new_data[idx as usize] = original_image[orig_idx as usize]; 23 | new_data[(idx + 1) as usize] = original_image[(orig_idx + 1) as usize]; 24 | new_data[(idx + 2) as usize] = original_image[(orig_idx + 2) as usize]; 25 | new_data[(idx + 3) as usize] = original_image[(orig_idx + 3) as usize]; 26 | } 27 | } 28 | } 29 | } 30 | new_data 31 | } 32 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod compare; 2 | mod decoder; 3 | mod encoder; 4 | mod error; 5 | mod expander; 6 | mod webp; 7 | 8 | pub use compare::*; 9 | pub use decoder::*; 10 | pub use encoder::*; 11 | pub use error::*; 12 | pub use webp::*; 13 | 14 | /// Options for configuring the behavior of the `diff` function. 15 | /// 16 | /// This struct allows users to set various parameters that influence how the image 17 | /// difference calculation is performed. 18 | /// 19 | pub struct DiffOption { 20 | /// specifying the sensitivity threshold for pixel differences. 21 | /// a lower value means more sensitivity to small changes. 22 | pub threshold: Option, 23 | /// that determines whether to include anti-aliased pixels in the diff calculation. 24 | pub include_anti_alias: Option, 25 | } 26 | 27 | /// Compares two images and calculates the differences between them. 28 | /// 29 | /// This function takes two images as byte slices and an options struct, and returns 30 | /// a `Result` containing either the diff output or an error. 31 | /// 32 | /// # Arguments 33 | /// * `actual`: A bytes representing the first image to be compared. 34 | /// * `expected`: A bytes representing the second image to be compared. 35 | /// * `option`: A reference to a `DiffOption` struct that specifies additional options for the diff operation. 36 | /// 37 | pub fn diff( 38 | actual: impl AsRef<[u8]>, 39 | expected: impl AsRef<[u8]>, 40 | option: &DiffOption, 41 | ) -> Result { 42 | if actual.as_ref() == expected.as_ref() { 43 | return Ok(DiffOutput::Eq); 44 | } 45 | let img1 = decode_buf(actual.as_ref())?; 46 | let img2 = decode_buf(expected.as_ref())?; 47 | 48 | let w = std::cmp::max(img1.dimensions.0, img2.dimensions.0); 49 | let h = std::cmp::max(img1.dimensions.1, img2.dimensions.1); 50 | 51 | // expand ig size is not match. 52 | let expanded1 = expander::expand(img1.buf, img1.dimensions, w, h); 53 | let expanded2 = expander::expand(img2.buf, img2.dimensions, w, h); 54 | 55 | let result = compare_buf( 56 | &expanded1, 57 | &expanded2, 58 | (w, h), 59 | CompareOption { 60 | threshold: option.threshold.unwrap_or_default(), 61 | enable_anti_alias: option.include_anti_alias.unwrap_or_default(), 62 | }, 63 | )?; 64 | 65 | match result { 66 | DiffOutput::NotEq { 67 | diff_count, 68 | diff_image, 69 | width, 70 | height, 71 | } => Ok(DiffOutput::NotEq { 72 | diff_count, 73 | diff_image: encode(&diff_image, width, height)?, 74 | width, 75 | height, 76 | }), 77 | DiffOutput::Eq => Ok(DiffOutput::Eq), 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /core/src/webp.c: -------------------------------------------------------------------------------- 1 | #ifdef __EMSCRIPTEN__ 2 | #include "emscripten.h" 3 | #else 4 | #define EMSCRIPTEN_KEEPALIVE 5 | #endif 6 | 7 | #include "../../libwebp/src/webp/encode.h" 8 | #include "../../libwebp/src/webp/decode.h" 9 | 10 | EMSCRIPTEN_KEEPALIVE 11 | uint8_t* decode(const uint8_t* data, size_t data_size, int* width, int* height) { 12 | return WebPDecodeRGBA(data, data_size, width, height); 13 | } 14 | 15 | EMSCRIPTEN_KEEPALIVE 16 | size_t encode_lossless(const uint8_t* rgba, int width, int height, int stride, uint8_t** output) { 17 | return WebPEncodeLosslessRGBA(rgba, width, height, stride, output); 18 | } -------------------------------------------------------------------------------- /core/src/webp.rs: -------------------------------------------------------------------------------- 1 | // use std::fs::File; 2 | // use std::io::Read; 3 | use std::os::raw::*; 4 | // use std::path::Path; 5 | 6 | use super::*; 7 | 8 | extern "C" { 9 | fn decode( 10 | data: *const c_uchar, 11 | size: usize, 12 | width: &mut c_int, 13 | height: &mut c_int, 14 | ) -> *const c_uchar; 15 | #[allow(dead_code)] 16 | fn encode_lossless( 17 | rgba: *const c_uchar, 18 | width: c_int, 19 | height: c_int, 20 | stride: c_int, 21 | output: &mut *mut c_uchar, 22 | ) -> usize; 23 | } 24 | 25 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 26 | pub struct WebPError; 27 | 28 | impl std::fmt::Display for WebPError { 29 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 30 | f.write_str("WebP format error") 31 | } 32 | } 33 | 34 | impl std::error::Error for WebPError {} 35 | 36 | pub fn decode_webp_buf(data: &[u8]) -> Result { 37 | let mut w: i32 = 0; 38 | let mut h: i32 = 0; 39 | 40 | let result = unsafe { decode(data.as_ptr(), data.len(), &mut w, &mut h) }; 41 | if result.is_null() { 42 | return Err(WebPError); 43 | } 44 | Ok(DecodeOutput { 45 | buf: unsafe { std::slice::from_raw_parts(result, w as usize * h as usize * 4) }.to_vec(), 46 | dimensions: (w as u32, h as u32), 47 | }) 48 | } 49 | 50 | // pub(crate) fn decode_webp>(path: P) -> Result { 51 | // let mut file = File::open(path.as_ref())?; 52 | // let mut buf = Vec::new(); 53 | // let _ = file.read_to_end(&mut buf)?; 54 | // 55 | // decode_webp_buf(&buf).map_err(|_| { 56 | // ImageDiffError::Decode(path.as_ref().to_str().expect("should convert").to_string()) 57 | // }) 58 | // } 59 | 60 | fn encode_buf(rgba: &[u8], width: u32, height: u32) -> Result { 61 | // For now reserve rgba size. 62 | let mut output: Vec = Vec::with_capacity(rgba.len()); 63 | let mut ptr = output.as_mut_ptr(); 64 | 65 | let result = unsafe { 66 | encode_lossless( 67 | rgba.as_ptr(), 68 | width as i32, 69 | height as i32, 70 | (width * 4) as i32, 71 | &mut ptr, 72 | ) 73 | }; 74 | if result == 0 { 75 | return Err(WebPError); 76 | } 77 | let buf = unsafe { std::slice::from_raw_parts(ptr, result) }.to_vec(); 78 | Ok(EncodeOutput { buf }) 79 | } 80 | 81 | pub(crate) fn encode_webp(rgba: &[u8], width: u32, height: u32) -> Result, ImageDiffError> { 82 | let result = encode_buf(rgba, width, height); 83 | match result { 84 | Ok(result) => Ok(result.buf), 85 | _ => Err(ImageDiffError::Encode( 86 | "It is not able to encode.".to_string(), 87 | )), 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /fixtures/005a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/fixtures/005a.png -------------------------------------------------------------------------------- /fixtures/005b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/fixtures/005b.png -------------------------------------------------------------------------------- /fixtures/sample0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/fixtures/sample0.png -------------------------------------------------------------------------------- /fixtures/sample0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/fixtures/sample0.webp -------------------------------------------------------------------------------- /fixtures/sample1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/fixtures/sample1.png -------------------------------------------------------------------------------- /fixtures/sample1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/fixtures/sample1.webp -------------------------------------------------------------------------------- /js/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /js/README.md: -------------------------------------------------------------------------------- 1 | # @bokuweb/image-diff-wasm 2 | 3 | Build Status 4 | 5 | Image diff library for Node.js and Deno. 6 | This library is generated by `jco`. 7 | 8 | ## installation 9 | 10 | ``` 11 | npm install @bokuweb/image-diff-wasm 12 | ``` 13 | 14 | ## examples 15 | 16 | ```js 17 | import { readFile } from "node:fs/promises"; 18 | import { diff } from "@bokuweb/image-diff-wasm"; 19 | 20 | const imga = await readFile(PATH_TO_IMAGE_A); 21 | const imgb = await readFile(PATH_TO_IMAGE_B); 22 | 23 | const result = diff(imga, imgb, { enableAntiAlias: true, threshold: 0.01 }); 24 | ``` 25 | 26 | ## Demo 27 | 28 | | img1 | img2 | diff | 29 | | --------------- |---------------| -------------------- | 30 | | ![](https://github.com/bokuweb/pixelmatch-rs/raw/main/fixtures/001a.png) | ![](https://github.com/bokuweb/pixelmatch-rs/raw/main/fixtures/001b.png) |![](https://github.com/bokuweb/pixelmatch-rs/raw/main/assets/diff1.png)| 31 | 32 | ## API 33 | 34 | ### diff(imga: Uint8Array, imgb: Uint8Array, opts: Opts): Output; 35 | 36 | The diff function is designed to compare two images and identify their differences. 37 | It takes two image buffers as input and returns an `Output` object containing information about the differences. 38 | 39 | #### Input 40 | 41 | - `imga`: Uint8Array: The first image buffer. 42 | - `imgb`: Uint8Array: The second image buffer. 43 | - `opts`: Opts: Options object for the function. 44 | 45 | ```Typescript 46 | export interface Opts { 47 | threshold?: number, 48 | includeAntiAlias?: boolean, 49 | } 50 | ``` 51 | 52 | - `threshold`: Matching threshold, ranges from 0 to 1. Smaller values make the comparison more sensitive. 0.1 by default. 53 | - `includeAntiAlias`: The flag of antialias. If omitted false. 54 | 55 | #### Output 56 | 57 | ```Typescript 58 | export interface Output { 59 | diffCount: number, 60 | diffImage: Uint8Array, 61 | width: number, 62 | height: number, 63 | } 64 | ``` 65 | 66 | - `diffCount`: The number of pixels that differ between the two images. 67 | - `diffImage`: The buffer of the difference image in `WebP` format. 68 | - `width`: The width of the difference image. 69 | - `height`: The height of the difference image. 70 | 71 | #### Error 72 | 73 | The function may throw following values as `ComponentError`. 74 | 75 | ```Typescript 76 | export type Error = ErrorDecode | ErrorEncode; 77 | export interface ErrorDecode { 78 | tag: 'decode', 79 | val: string, 80 | } 81 | export interface ErrorEncode { 82 | tag: 'encode', 83 | val: string, 84 | } 85 | ``` 86 | ## License 87 | 88 | JS glue is provided under the MIT License, and the libwebp is provided by Google under the BSD 3-Clause License. 89 | -------------------------------------------------------------------------------- /js/examples/index.mjs: -------------------------------------------------------------------------------- 1 | import { readFile, writeFile } from "node:fs/promises"; 2 | import { fileURLToPath } from "node:url"; 3 | import { strictEqual } from "node:assert"; 4 | 5 | import path from "node:path"; 6 | 7 | import { diff } from "../index.mjs"; 8 | 9 | const __filename = fileURLToPath(import.meta.url); 10 | const __dirname = path.dirname(__filename); 11 | 12 | { 13 | const patha = path.resolve(__dirname, "../../fixtures/sample0.webp"); 14 | const imga = await readFile(patha); 15 | 16 | const pathb = path.resolve(__dirname, "../../fixtures/sample1.webp"); 17 | const imgb = await readFile(pathb); 18 | 19 | const result = diff(imga, imgb, { enableAntiAlias: true, threshold: 0.01 }); 20 | await writeFile("./diff0.webp", result.diffImage); 21 | 22 | strictEqual(result.diffCount, 3454); 23 | } 24 | 25 | { 26 | const patha = path.resolve(__dirname, "../../fixtures/sample0.webp"); 27 | const imga = await readFile(patha); 28 | 29 | const pathb = path.resolve(__dirname, "../../fixtures/005a.png"); 30 | const imgb = await readFile(pathb); 31 | 32 | const result = diff(imga, imgb, { enableAntiAlias: true, threshold: 0.01 }); 33 | await writeFile("./diff1.webp", result.diffImage); 34 | 35 | strictEqual(result.diffCount, 383111); 36 | } 37 | 38 | console.info("It works."); 39 | -------------------------------------------------------------------------------- /js/index.core.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/js/index.core.wasm -------------------------------------------------------------------------------- /js/index.core2.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/image-diff-rs/eb831a7f22b4190bbfa494e97aacdfb40cbfa1cf/js/index.core2.wasm -------------------------------------------------------------------------------- /js/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface Opts { 2 | threshold?: number; 3 | includeAntiAlias?: boolean; 4 | } 5 | export interface Output { 6 | diffCount: number; 7 | diffImage: Uint8Array; 8 | width: number; 9 | height: number; 10 | } 11 | export type Error = ErrorDecode | ErrorEncode; 12 | export interface ErrorDecode { 13 | tag: "decode"; 14 | val: string; 15 | } 16 | export interface ErrorEncode { 17 | tag: "encode"; 18 | val: string; 19 | } 20 | 21 | export function diff(imga: Uint8Array, imgb: Uint8Array, opts: Opts): Output; 22 | -------------------------------------------------------------------------------- /js/index.mjs: -------------------------------------------------------------------------------- 1 | import { environment, exit as exit$1, stderr, stdin, stdout, terminalInput, terminalOutput, terminalStderr, terminalStdin, terminalStdout } from '@bytecodealliance/preview2-shim/cli'; 2 | import { preopens, types } from '@bytecodealliance/preview2-shim/filesystem'; 3 | import { streams } from '@bytecodealliance/preview2-shim/io'; 4 | import { random } from '@bytecodealliance/preview2-shim/random'; 5 | const { getEnvironment } = environment; 6 | const { exit } = exit$1; 7 | const { getStderr } = stderr; 8 | const { getStdin } = stdin; 9 | const { getStdout } = stdout; 10 | const { TerminalInput } = terminalInput; 11 | const { TerminalOutput } = terminalOutput; 12 | const { getTerminalStderr } = terminalStderr; 13 | const { getTerminalStdin } = terminalStdin; 14 | const { getTerminalStdout } = terminalStdout; 15 | const { getDirectories } = preopens; 16 | const { Descriptor, 17 | DirectoryEntryStream, 18 | filesystemErrorCode } = types; 19 | const { Error: Error$1, 20 | InputStream, 21 | OutputStream } = streams; 22 | const { getRandomBytes } = random; 23 | 24 | const base64Compile = str => WebAssembly.compile(typeof Buffer !== 'undefined' ? Buffer.from(str, 'base64') : Uint8Array.from(atob(str), b => b.charCodeAt(0))); 25 | 26 | class ComponentError extends Error { 27 | constructor (value) { 28 | const enumerable = typeof value !== 'string'; 29 | super(enumerable ? `${String(value)} (see error.payload)` : value); 30 | Object.defineProperty(this, 'payload', { value, enumerable }); 31 | } 32 | } 33 | 34 | let dv = new DataView(new ArrayBuffer()); 35 | const dataView = mem => dv.buffer === mem.buffer ? dv : dv = new DataView(mem.buffer); 36 | 37 | const isNode = typeof process !== 'undefined' && process.versions && process.versions.node; 38 | let _fs; 39 | async function fetchCompile (url) { 40 | if (isNode) { 41 | _fs = _fs || await import('fs/promises'); 42 | return WebAssembly.compile(await _fs.readFile(url)); 43 | } 44 | return fetch(url).then(WebAssembly.compileStreaming); 45 | } 46 | 47 | function getErrorPayload(e) { 48 | if (e && hasOwnProperty.call(e, 'payload')) return e.payload; 49 | return e; 50 | } 51 | 52 | const hasOwnProperty = Object.prototype.hasOwnProperty; 53 | 54 | const instantiateCore = WebAssembly.instantiate; 55 | 56 | const resourceHandleSymbol = Symbol('resource'); 57 | 58 | const symbolDispose = Symbol.dispose || Symbol.for('dispose'); 59 | 60 | const toUint64 = val => BigInt.asUintN(64, val); 61 | 62 | function toUint32(val) { 63 | return val >>> 0; 64 | } 65 | 66 | const utf8Decoder = new TextDecoder(); 67 | 68 | const utf8Encoder = new TextEncoder(); 69 | 70 | let utf8EncodedLen = 0; 71 | function utf8Encode(s, realloc, memory) { 72 | if (typeof s !== 'string') throw new TypeError('expected a string'); 73 | if (s.length === 0) { 74 | utf8EncodedLen = 0; 75 | return 1; 76 | } 77 | let allocLen = 0; 78 | let ptr = 0; 79 | let writtenTotal = 0; 80 | while (s.length > 0) { 81 | ptr = realloc(ptr, allocLen, 1, allocLen + s.length); 82 | allocLen += s.length; 83 | const { read, written } = utf8Encoder.encodeInto( 84 | s, 85 | new Uint8Array(memory.buffer, ptr + writtenTotal, allocLen - writtenTotal), 86 | ); 87 | writtenTotal += written; 88 | s = s.slice(read); 89 | } 90 | if (allocLen > writtenTotal) 91 | ptr = realloc(ptr, allocLen, 1, writtenTotal); 92 | utf8EncodedLen = writtenTotal; 93 | return ptr; 94 | } 95 | 96 | let exports0; 97 | let exports1; 98 | 99 | function trampoline8() { 100 | const ret = getStderr(); 101 | if (!(ret instanceof OutputStream)) { 102 | throw new Error('Not a valid "OutputStream" resource.'); 103 | } 104 | const handle0 = handleCnt0++; 105 | handleTable0.set(handle0, { rep: ret, own: true }); 106 | return handle0; 107 | } 108 | 109 | function trampoline9(arg0) { 110 | let variant0; 111 | switch (arg0) { 112 | case 0: { 113 | variant0= { 114 | tag: 'ok', 115 | val: undefined 116 | }; 117 | break; 118 | } 119 | case 1: { 120 | variant0= { 121 | tag: 'err', 122 | val: undefined 123 | }; 124 | break; 125 | } 126 | default: { 127 | throw new TypeError('invalid variant discriminant for expected'); 128 | } 129 | } 130 | exit(variant0); 131 | } 132 | 133 | function trampoline10() { 134 | const ret = getStdin(); 135 | if (!(ret instanceof InputStream)) { 136 | throw new Error('Not a valid "InputStream" resource.'); 137 | } 138 | const handle0 = handleCnt2++; 139 | handleTable2.set(handle0, { rep: ret, own: true }); 140 | return handle0; 141 | } 142 | 143 | function trampoline11() { 144 | const ret = getStdout(); 145 | if (!(ret instanceof OutputStream)) { 146 | throw new Error('Not a valid "OutputStream" resource.'); 147 | } 148 | const handle0 = handleCnt0++; 149 | handleTable0.set(handle0, { rep: ret, own: true }); 150 | return handle0; 151 | } 152 | let exports2; 153 | 154 | function trampoline12(arg0) { 155 | const ret = getDirectories(); 156 | const vec3 = ret; 157 | const len3 = vec3.length; 158 | const result3 = realloc0(0, 0, 4, len3 * 12); 159 | for (let i = 0; i < vec3.length; i++) { 160 | const e = vec3[i]; 161 | const base = result3 + i * 12;const [tuple0_0, tuple0_1] = e; 162 | if (!(tuple0_0 instanceof Descriptor)) { 163 | throw new Error('Not a valid "Descriptor" resource.'); 164 | } 165 | const handle1 = handleCnt3++; 166 | handleTable3.set(handle1, { rep: tuple0_0, own: true }); 167 | dataView(memory0).setInt32(base + 0, handle1, true); 168 | const ptr2 = utf8Encode(tuple0_1, realloc0, memory0); 169 | const len2 = utf8EncodedLen; 170 | dataView(memory0).setInt32(base + 8, len2, true); 171 | dataView(memory0).setInt32(base + 4, ptr2, true); 172 | } 173 | dataView(memory0).setInt32(arg0 + 4, len3, true); 174 | dataView(memory0).setInt32(arg0 + 0, result3, true); 175 | } 176 | let memory0; 177 | let realloc0; 178 | 179 | function trampoline13(arg0, arg1, arg2) { 180 | const handle1 = arg0; 181 | const rsc0 = handleTable3.get(handle1).rep; 182 | let ret; 183 | try { 184 | ret = { tag: 'ok', val: Descriptor.prototype.writeViaStream.call(rsc0, BigInt.asUintN(64, arg1)) }; 185 | } catch (e) { 186 | ret = { tag: 'err', val: getErrorPayload(e) }; 187 | } 188 | const variant4 = ret; 189 | switch (variant4.tag) { 190 | case 'ok': { 191 | const e = variant4.val; 192 | dataView(memory0).setInt8(arg2 + 0, 0, true); 193 | if (!(e instanceof OutputStream)) { 194 | throw new Error('Not a valid "OutputStream" resource.'); 195 | } 196 | const handle2 = handleCnt0++; 197 | handleTable0.set(handle2, { rep: e, own: true }); 198 | dataView(memory0).setInt32(arg2 + 4, handle2, true); 199 | break; 200 | } 201 | case 'err': { 202 | const e = variant4.val; 203 | dataView(memory0).setInt8(arg2 + 0, 1, true); 204 | const val3 = e; 205 | let enum3; 206 | switch (val3) { 207 | case 'access': { 208 | enum3 = 0; 209 | break; 210 | } 211 | case 'would-block': { 212 | enum3 = 1; 213 | break; 214 | } 215 | case 'already': { 216 | enum3 = 2; 217 | break; 218 | } 219 | case 'bad-descriptor': { 220 | enum3 = 3; 221 | break; 222 | } 223 | case 'busy': { 224 | enum3 = 4; 225 | break; 226 | } 227 | case 'deadlock': { 228 | enum3 = 5; 229 | break; 230 | } 231 | case 'quota': { 232 | enum3 = 6; 233 | break; 234 | } 235 | case 'exist': { 236 | enum3 = 7; 237 | break; 238 | } 239 | case 'file-too-large': { 240 | enum3 = 8; 241 | break; 242 | } 243 | case 'illegal-byte-sequence': { 244 | enum3 = 9; 245 | break; 246 | } 247 | case 'in-progress': { 248 | enum3 = 10; 249 | break; 250 | } 251 | case 'interrupted': { 252 | enum3 = 11; 253 | break; 254 | } 255 | case 'invalid': { 256 | enum3 = 12; 257 | break; 258 | } 259 | case 'io': { 260 | enum3 = 13; 261 | break; 262 | } 263 | case 'is-directory': { 264 | enum3 = 14; 265 | break; 266 | } 267 | case 'loop': { 268 | enum3 = 15; 269 | break; 270 | } 271 | case 'too-many-links': { 272 | enum3 = 16; 273 | break; 274 | } 275 | case 'message-size': { 276 | enum3 = 17; 277 | break; 278 | } 279 | case 'name-too-long': { 280 | enum3 = 18; 281 | break; 282 | } 283 | case 'no-device': { 284 | enum3 = 19; 285 | break; 286 | } 287 | case 'no-entry': { 288 | enum3 = 20; 289 | break; 290 | } 291 | case 'no-lock': { 292 | enum3 = 21; 293 | break; 294 | } 295 | case 'insufficient-memory': { 296 | enum3 = 22; 297 | break; 298 | } 299 | case 'insufficient-space': { 300 | enum3 = 23; 301 | break; 302 | } 303 | case 'not-directory': { 304 | enum3 = 24; 305 | break; 306 | } 307 | case 'not-empty': { 308 | enum3 = 25; 309 | break; 310 | } 311 | case 'not-recoverable': { 312 | enum3 = 26; 313 | break; 314 | } 315 | case 'unsupported': { 316 | enum3 = 27; 317 | break; 318 | } 319 | case 'no-tty': { 320 | enum3 = 28; 321 | break; 322 | } 323 | case 'no-such-device': { 324 | enum3 = 29; 325 | break; 326 | } 327 | case 'overflow': { 328 | enum3 = 30; 329 | break; 330 | } 331 | case 'not-permitted': { 332 | enum3 = 31; 333 | break; 334 | } 335 | case 'pipe': { 336 | enum3 = 32; 337 | break; 338 | } 339 | case 'read-only': { 340 | enum3 = 33; 341 | break; 342 | } 343 | case 'invalid-seek': { 344 | enum3 = 34; 345 | break; 346 | } 347 | case 'text-file-busy': { 348 | enum3 = 35; 349 | break; 350 | } 351 | case 'cross-device': { 352 | enum3 = 36; 353 | break; 354 | } 355 | default: { 356 | if ((e) instanceof Error) { 357 | console.error(e); 358 | } 359 | 360 | throw new TypeError(`"${val3}" is not one of the cases of error-code`); 361 | } 362 | } 363 | dataView(memory0).setInt8(arg2 + 4, enum3, true); 364 | break; 365 | } 366 | default: { 367 | throw new TypeError('invalid variant specified for result'); 368 | } 369 | } 370 | } 371 | 372 | function trampoline14(arg0, arg1) { 373 | const handle1 = arg0; 374 | const rsc0 = handleTable3.get(handle1).rep; 375 | let ret; 376 | try { 377 | ret = { tag: 'ok', val: Descriptor.prototype.appendViaStream.call(rsc0) }; 378 | } catch (e) { 379 | ret = { tag: 'err', val: getErrorPayload(e) }; 380 | } 381 | const variant4 = ret; 382 | switch (variant4.tag) { 383 | case 'ok': { 384 | const e = variant4.val; 385 | dataView(memory0).setInt8(arg1 + 0, 0, true); 386 | if (!(e instanceof OutputStream)) { 387 | throw new Error('Not a valid "OutputStream" resource.'); 388 | } 389 | const handle2 = handleCnt0++; 390 | handleTable0.set(handle2, { rep: e, own: true }); 391 | dataView(memory0).setInt32(arg1 + 4, handle2, true); 392 | break; 393 | } 394 | case 'err': { 395 | const e = variant4.val; 396 | dataView(memory0).setInt8(arg1 + 0, 1, true); 397 | const val3 = e; 398 | let enum3; 399 | switch (val3) { 400 | case 'access': { 401 | enum3 = 0; 402 | break; 403 | } 404 | case 'would-block': { 405 | enum3 = 1; 406 | break; 407 | } 408 | case 'already': { 409 | enum3 = 2; 410 | break; 411 | } 412 | case 'bad-descriptor': { 413 | enum3 = 3; 414 | break; 415 | } 416 | case 'busy': { 417 | enum3 = 4; 418 | break; 419 | } 420 | case 'deadlock': { 421 | enum3 = 5; 422 | break; 423 | } 424 | case 'quota': { 425 | enum3 = 6; 426 | break; 427 | } 428 | case 'exist': { 429 | enum3 = 7; 430 | break; 431 | } 432 | case 'file-too-large': { 433 | enum3 = 8; 434 | break; 435 | } 436 | case 'illegal-byte-sequence': { 437 | enum3 = 9; 438 | break; 439 | } 440 | case 'in-progress': { 441 | enum3 = 10; 442 | break; 443 | } 444 | case 'interrupted': { 445 | enum3 = 11; 446 | break; 447 | } 448 | case 'invalid': { 449 | enum3 = 12; 450 | break; 451 | } 452 | case 'io': { 453 | enum3 = 13; 454 | break; 455 | } 456 | case 'is-directory': { 457 | enum3 = 14; 458 | break; 459 | } 460 | case 'loop': { 461 | enum3 = 15; 462 | break; 463 | } 464 | case 'too-many-links': { 465 | enum3 = 16; 466 | break; 467 | } 468 | case 'message-size': { 469 | enum3 = 17; 470 | break; 471 | } 472 | case 'name-too-long': { 473 | enum3 = 18; 474 | break; 475 | } 476 | case 'no-device': { 477 | enum3 = 19; 478 | break; 479 | } 480 | case 'no-entry': { 481 | enum3 = 20; 482 | break; 483 | } 484 | case 'no-lock': { 485 | enum3 = 21; 486 | break; 487 | } 488 | case 'insufficient-memory': { 489 | enum3 = 22; 490 | break; 491 | } 492 | case 'insufficient-space': { 493 | enum3 = 23; 494 | break; 495 | } 496 | case 'not-directory': { 497 | enum3 = 24; 498 | break; 499 | } 500 | case 'not-empty': { 501 | enum3 = 25; 502 | break; 503 | } 504 | case 'not-recoverable': { 505 | enum3 = 26; 506 | break; 507 | } 508 | case 'unsupported': { 509 | enum3 = 27; 510 | break; 511 | } 512 | case 'no-tty': { 513 | enum3 = 28; 514 | break; 515 | } 516 | case 'no-such-device': { 517 | enum3 = 29; 518 | break; 519 | } 520 | case 'overflow': { 521 | enum3 = 30; 522 | break; 523 | } 524 | case 'not-permitted': { 525 | enum3 = 31; 526 | break; 527 | } 528 | case 'pipe': { 529 | enum3 = 32; 530 | break; 531 | } 532 | case 'read-only': { 533 | enum3 = 33; 534 | break; 535 | } 536 | case 'invalid-seek': { 537 | enum3 = 34; 538 | break; 539 | } 540 | case 'text-file-busy': { 541 | enum3 = 35; 542 | break; 543 | } 544 | case 'cross-device': { 545 | enum3 = 36; 546 | break; 547 | } 548 | default: { 549 | if ((e) instanceof Error) { 550 | console.error(e); 551 | } 552 | 553 | throw new TypeError(`"${val3}" is not one of the cases of error-code`); 554 | } 555 | } 556 | dataView(memory0).setInt8(arg1 + 4, enum3, true); 557 | break; 558 | } 559 | default: { 560 | throw new TypeError('invalid variant specified for result'); 561 | } 562 | } 563 | } 564 | 565 | function trampoline15(arg0, arg1) { 566 | const handle1 = arg0; 567 | const rsc0 = handleTable3.get(handle1).rep; 568 | let ret; 569 | try { 570 | ret = { tag: 'ok', val: Descriptor.prototype.getType.call(rsc0) }; 571 | } catch (e) { 572 | ret = { tag: 'err', val: getErrorPayload(e) }; 573 | } 574 | const variant4 = ret; 575 | switch (variant4.tag) { 576 | case 'ok': { 577 | const e = variant4.val; 578 | dataView(memory0).setInt8(arg1 + 0, 0, true); 579 | const val2 = e; 580 | let enum2; 581 | switch (val2) { 582 | case 'unknown': { 583 | enum2 = 0; 584 | break; 585 | } 586 | case 'block-device': { 587 | enum2 = 1; 588 | break; 589 | } 590 | case 'character-device': { 591 | enum2 = 2; 592 | break; 593 | } 594 | case 'directory': { 595 | enum2 = 3; 596 | break; 597 | } 598 | case 'fifo': { 599 | enum2 = 4; 600 | break; 601 | } 602 | case 'symbolic-link': { 603 | enum2 = 5; 604 | break; 605 | } 606 | case 'regular-file': { 607 | enum2 = 6; 608 | break; 609 | } 610 | case 'socket': { 611 | enum2 = 7; 612 | break; 613 | } 614 | default: { 615 | if ((e) instanceof Error) { 616 | console.error(e); 617 | } 618 | 619 | throw new TypeError(`"${val2}" is not one of the cases of descriptor-type`); 620 | } 621 | } 622 | dataView(memory0).setInt8(arg1 + 1, enum2, true); 623 | break; 624 | } 625 | case 'err': { 626 | const e = variant4.val; 627 | dataView(memory0).setInt8(arg1 + 0, 1, true); 628 | const val3 = e; 629 | let enum3; 630 | switch (val3) { 631 | case 'access': { 632 | enum3 = 0; 633 | break; 634 | } 635 | case 'would-block': { 636 | enum3 = 1; 637 | break; 638 | } 639 | case 'already': { 640 | enum3 = 2; 641 | break; 642 | } 643 | case 'bad-descriptor': { 644 | enum3 = 3; 645 | break; 646 | } 647 | case 'busy': { 648 | enum3 = 4; 649 | break; 650 | } 651 | case 'deadlock': { 652 | enum3 = 5; 653 | break; 654 | } 655 | case 'quota': { 656 | enum3 = 6; 657 | break; 658 | } 659 | case 'exist': { 660 | enum3 = 7; 661 | break; 662 | } 663 | case 'file-too-large': { 664 | enum3 = 8; 665 | break; 666 | } 667 | case 'illegal-byte-sequence': { 668 | enum3 = 9; 669 | break; 670 | } 671 | case 'in-progress': { 672 | enum3 = 10; 673 | break; 674 | } 675 | case 'interrupted': { 676 | enum3 = 11; 677 | break; 678 | } 679 | case 'invalid': { 680 | enum3 = 12; 681 | break; 682 | } 683 | case 'io': { 684 | enum3 = 13; 685 | break; 686 | } 687 | case 'is-directory': { 688 | enum3 = 14; 689 | break; 690 | } 691 | case 'loop': { 692 | enum3 = 15; 693 | break; 694 | } 695 | case 'too-many-links': { 696 | enum3 = 16; 697 | break; 698 | } 699 | case 'message-size': { 700 | enum3 = 17; 701 | break; 702 | } 703 | case 'name-too-long': { 704 | enum3 = 18; 705 | break; 706 | } 707 | case 'no-device': { 708 | enum3 = 19; 709 | break; 710 | } 711 | case 'no-entry': { 712 | enum3 = 20; 713 | break; 714 | } 715 | case 'no-lock': { 716 | enum3 = 21; 717 | break; 718 | } 719 | case 'insufficient-memory': { 720 | enum3 = 22; 721 | break; 722 | } 723 | case 'insufficient-space': { 724 | enum3 = 23; 725 | break; 726 | } 727 | case 'not-directory': { 728 | enum3 = 24; 729 | break; 730 | } 731 | case 'not-empty': { 732 | enum3 = 25; 733 | break; 734 | } 735 | case 'not-recoverable': { 736 | enum3 = 26; 737 | break; 738 | } 739 | case 'unsupported': { 740 | enum3 = 27; 741 | break; 742 | } 743 | case 'no-tty': { 744 | enum3 = 28; 745 | break; 746 | } 747 | case 'no-such-device': { 748 | enum3 = 29; 749 | break; 750 | } 751 | case 'overflow': { 752 | enum3 = 30; 753 | break; 754 | } 755 | case 'not-permitted': { 756 | enum3 = 31; 757 | break; 758 | } 759 | case 'pipe': { 760 | enum3 = 32; 761 | break; 762 | } 763 | case 'read-only': { 764 | enum3 = 33; 765 | break; 766 | } 767 | case 'invalid-seek': { 768 | enum3 = 34; 769 | break; 770 | } 771 | case 'text-file-busy': { 772 | enum3 = 35; 773 | break; 774 | } 775 | case 'cross-device': { 776 | enum3 = 36; 777 | break; 778 | } 779 | default: { 780 | if ((e) instanceof Error) { 781 | console.error(e); 782 | } 783 | 784 | throw new TypeError(`"${val3}" is not one of the cases of error-code`); 785 | } 786 | } 787 | dataView(memory0).setInt8(arg1 + 1, enum3, true); 788 | break; 789 | } 790 | default: { 791 | throw new TypeError('invalid variant specified for result'); 792 | } 793 | } 794 | } 795 | 796 | function trampoline16(arg0, arg1) { 797 | const handle1 = arg0; 798 | const rsc0 = handleTable3.get(handle1).rep; 799 | let ret; 800 | try { 801 | ret = { tag: 'ok', val: Descriptor.prototype.stat.call(rsc0) }; 802 | } catch (e) { 803 | ret = { tag: 'err', val: getErrorPayload(e) }; 804 | } 805 | const variant11 = ret; 806 | switch (variant11.tag) { 807 | case 'ok': { 808 | const e = variant11.val; 809 | dataView(memory0).setInt8(arg1 + 0, 0, true); 810 | const {type: v2_0, linkCount: v2_1, size: v2_2, dataAccessTimestamp: v2_3, dataModificationTimestamp: v2_4, statusChangeTimestamp: v2_5 } = e; 811 | const val3 = v2_0; 812 | let enum3; 813 | switch (val3) { 814 | case 'unknown': { 815 | enum3 = 0; 816 | break; 817 | } 818 | case 'block-device': { 819 | enum3 = 1; 820 | break; 821 | } 822 | case 'character-device': { 823 | enum3 = 2; 824 | break; 825 | } 826 | case 'directory': { 827 | enum3 = 3; 828 | break; 829 | } 830 | case 'fifo': { 831 | enum3 = 4; 832 | break; 833 | } 834 | case 'symbolic-link': { 835 | enum3 = 5; 836 | break; 837 | } 838 | case 'regular-file': { 839 | enum3 = 6; 840 | break; 841 | } 842 | case 'socket': { 843 | enum3 = 7; 844 | break; 845 | } 846 | default: { 847 | if ((v2_0) instanceof Error) { 848 | console.error(v2_0); 849 | } 850 | 851 | throw new TypeError(`"${val3}" is not one of the cases of descriptor-type`); 852 | } 853 | } 854 | dataView(memory0).setInt8(arg1 + 8, enum3, true); 855 | dataView(memory0).setBigInt64(arg1 + 16, toUint64(v2_1), true); 856 | dataView(memory0).setBigInt64(arg1 + 24, toUint64(v2_2), true); 857 | const variant5 = v2_3; 858 | if (variant5 === null || variant5=== undefined) { 859 | dataView(memory0).setInt8(arg1 + 32, 0, true); 860 | } else { 861 | const e = variant5; 862 | dataView(memory0).setInt8(arg1 + 32, 1, true); 863 | const {seconds: v4_0, nanoseconds: v4_1 } = e; 864 | dataView(memory0).setBigInt64(arg1 + 40, toUint64(v4_0), true); 865 | dataView(memory0).setInt32(arg1 + 48, toUint32(v4_1), true); 866 | } 867 | const variant7 = v2_4; 868 | if (variant7 === null || variant7=== undefined) { 869 | dataView(memory0).setInt8(arg1 + 56, 0, true); 870 | } else { 871 | const e = variant7; 872 | dataView(memory0).setInt8(arg1 + 56, 1, true); 873 | const {seconds: v6_0, nanoseconds: v6_1 } = e; 874 | dataView(memory0).setBigInt64(arg1 + 64, toUint64(v6_0), true); 875 | dataView(memory0).setInt32(arg1 + 72, toUint32(v6_1), true); 876 | } 877 | const variant9 = v2_5; 878 | if (variant9 === null || variant9=== undefined) { 879 | dataView(memory0).setInt8(arg1 + 80, 0, true); 880 | } else { 881 | const e = variant9; 882 | dataView(memory0).setInt8(arg1 + 80, 1, true); 883 | const {seconds: v8_0, nanoseconds: v8_1 } = e; 884 | dataView(memory0).setBigInt64(arg1 + 88, toUint64(v8_0), true); 885 | dataView(memory0).setInt32(arg1 + 96, toUint32(v8_1), true); 886 | } 887 | break; 888 | } 889 | case 'err': { 890 | const e = variant11.val; 891 | dataView(memory0).setInt8(arg1 + 0, 1, true); 892 | const val10 = e; 893 | let enum10; 894 | switch (val10) { 895 | case 'access': { 896 | enum10 = 0; 897 | break; 898 | } 899 | case 'would-block': { 900 | enum10 = 1; 901 | break; 902 | } 903 | case 'already': { 904 | enum10 = 2; 905 | break; 906 | } 907 | case 'bad-descriptor': { 908 | enum10 = 3; 909 | break; 910 | } 911 | case 'busy': { 912 | enum10 = 4; 913 | break; 914 | } 915 | case 'deadlock': { 916 | enum10 = 5; 917 | break; 918 | } 919 | case 'quota': { 920 | enum10 = 6; 921 | break; 922 | } 923 | case 'exist': { 924 | enum10 = 7; 925 | break; 926 | } 927 | case 'file-too-large': { 928 | enum10 = 8; 929 | break; 930 | } 931 | case 'illegal-byte-sequence': { 932 | enum10 = 9; 933 | break; 934 | } 935 | case 'in-progress': { 936 | enum10 = 10; 937 | break; 938 | } 939 | case 'interrupted': { 940 | enum10 = 11; 941 | break; 942 | } 943 | case 'invalid': { 944 | enum10 = 12; 945 | break; 946 | } 947 | case 'io': { 948 | enum10 = 13; 949 | break; 950 | } 951 | case 'is-directory': { 952 | enum10 = 14; 953 | break; 954 | } 955 | case 'loop': { 956 | enum10 = 15; 957 | break; 958 | } 959 | case 'too-many-links': { 960 | enum10 = 16; 961 | break; 962 | } 963 | case 'message-size': { 964 | enum10 = 17; 965 | break; 966 | } 967 | case 'name-too-long': { 968 | enum10 = 18; 969 | break; 970 | } 971 | case 'no-device': { 972 | enum10 = 19; 973 | break; 974 | } 975 | case 'no-entry': { 976 | enum10 = 20; 977 | break; 978 | } 979 | case 'no-lock': { 980 | enum10 = 21; 981 | break; 982 | } 983 | case 'insufficient-memory': { 984 | enum10 = 22; 985 | break; 986 | } 987 | case 'insufficient-space': { 988 | enum10 = 23; 989 | break; 990 | } 991 | case 'not-directory': { 992 | enum10 = 24; 993 | break; 994 | } 995 | case 'not-empty': { 996 | enum10 = 25; 997 | break; 998 | } 999 | case 'not-recoverable': { 1000 | enum10 = 26; 1001 | break; 1002 | } 1003 | case 'unsupported': { 1004 | enum10 = 27; 1005 | break; 1006 | } 1007 | case 'no-tty': { 1008 | enum10 = 28; 1009 | break; 1010 | } 1011 | case 'no-such-device': { 1012 | enum10 = 29; 1013 | break; 1014 | } 1015 | case 'overflow': { 1016 | enum10 = 30; 1017 | break; 1018 | } 1019 | case 'not-permitted': { 1020 | enum10 = 31; 1021 | break; 1022 | } 1023 | case 'pipe': { 1024 | enum10 = 32; 1025 | break; 1026 | } 1027 | case 'read-only': { 1028 | enum10 = 33; 1029 | break; 1030 | } 1031 | case 'invalid-seek': { 1032 | enum10 = 34; 1033 | break; 1034 | } 1035 | case 'text-file-busy': { 1036 | enum10 = 35; 1037 | break; 1038 | } 1039 | case 'cross-device': { 1040 | enum10 = 36; 1041 | break; 1042 | } 1043 | default: { 1044 | if ((e) instanceof Error) { 1045 | console.error(e); 1046 | } 1047 | 1048 | throw new TypeError(`"${val10}" is not one of the cases of error-code`); 1049 | } 1050 | } 1051 | dataView(memory0).setInt8(arg1 + 8, enum10, true); 1052 | break; 1053 | } 1054 | default: { 1055 | throw new TypeError('invalid variant specified for result'); 1056 | } 1057 | } 1058 | } 1059 | 1060 | function trampoline17(arg0, arg1) { 1061 | const handle1 = arg0; 1062 | const rsc0 = handleTable1.get(handle1).rep; 1063 | const ret = filesystemErrorCode(rsc0); 1064 | const variant3 = ret; 1065 | if (variant3 === null || variant3=== undefined) { 1066 | dataView(memory0).setInt8(arg1 + 0, 0, true); 1067 | } else { 1068 | const e = variant3; 1069 | dataView(memory0).setInt8(arg1 + 0, 1, true); 1070 | const val2 = e; 1071 | let enum2; 1072 | switch (val2) { 1073 | case 'access': { 1074 | enum2 = 0; 1075 | break; 1076 | } 1077 | case 'would-block': { 1078 | enum2 = 1; 1079 | break; 1080 | } 1081 | case 'already': { 1082 | enum2 = 2; 1083 | break; 1084 | } 1085 | case 'bad-descriptor': { 1086 | enum2 = 3; 1087 | break; 1088 | } 1089 | case 'busy': { 1090 | enum2 = 4; 1091 | break; 1092 | } 1093 | case 'deadlock': { 1094 | enum2 = 5; 1095 | break; 1096 | } 1097 | case 'quota': { 1098 | enum2 = 6; 1099 | break; 1100 | } 1101 | case 'exist': { 1102 | enum2 = 7; 1103 | break; 1104 | } 1105 | case 'file-too-large': { 1106 | enum2 = 8; 1107 | break; 1108 | } 1109 | case 'illegal-byte-sequence': { 1110 | enum2 = 9; 1111 | break; 1112 | } 1113 | case 'in-progress': { 1114 | enum2 = 10; 1115 | break; 1116 | } 1117 | case 'interrupted': { 1118 | enum2 = 11; 1119 | break; 1120 | } 1121 | case 'invalid': { 1122 | enum2 = 12; 1123 | break; 1124 | } 1125 | case 'io': { 1126 | enum2 = 13; 1127 | break; 1128 | } 1129 | case 'is-directory': { 1130 | enum2 = 14; 1131 | break; 1132 | } 1133 | case 'loop': { 1134 | enum2 = 15; 1135 | break; 1136 | } 1137 | case 'too-many-links': { 1138 | enum2 = 16; 1139 | break; 1140 | } 1141 | case 'message-size': { 1142 | enum2 = 17; 1143 | break; 1144 | } 1145 | case 'name-too-long': { 1146 | enum2 = 18; 1147 | break; 1148 | } 1149 | case 'no-device': { 1150 | enum2 = 19; 1151 | break; 1152 | } 1153 | case 'no-entry': { 1154 | enum2 = 20; 1155 | break; 1156 | } 1157 | case 'no-lock': { 1158 | enum2 = 21; 1159 | break; 1160 | } 1161 | case 'insufficient-memory': { 1162 | enum2 = 22; 1163 | break; 1164 | } 1165 | case 'insufficient-space': { 1166 | enum2 = 23; 1167 | break; 1168 | } 1169 | case 'not-directory': { 1170 | enum2 = 24; 1171 | break; 1172 | } 1173 | case 'not-empty': { 1174 | enum2 = 25; 1175 | break; 1176 | } 1177 | case 'not-recoverable': { 1178 | enum2 = 26; 1179 | break; 1180 | } 1181 | case 'unsupported': { 1182 | enum2 = 27; 1183 | break; 1184 | } 1185 | case 'no-tty': { 1186 | enum2 = 28; 1187 | break; 1188 | } 1189 | case 'no-such-device': { 1190 | enum2 = 29; 1191 | break; 1192 | } 1193 | case 'overflow': { 1194 | enum2 = 30; 1195 | break; 1196 | } 1197 | case 'not-permitted': { 1198 | enum2 = 31; 1199 | break; 1200 | } 1201 | case 'pipe': { 1202 | enum2 = 32; 1203 | break; 1204 | } 1205 | case 'read-only': { 1206 | enum2 = 33; 1207 | break; 1208 | } 1209 | case 'invalid-seek': { 1210 | enum2 = 34; 1211 | break; 1212 | } 1213 | case 'text-file-busy': { 1214 | enum2 = 35; 1215 | break; 1216 | } 1217 | case 'cross-device': { 1218 | enum2 = 36; 1219 | break; 1220 | } 1221 | default: { 1222 | if ((e) instanceof Error) { 1223 | console.error(e); 1224 | } 1225 | 1226 | throw new TypeError(`"${val2}" is not one of the cases of error-code`); 1227 | } 1228 | } 1229 | dataView(memory0).setInt8(arg1 + 1, enum2, true); 1230 | } 1231 | } 1232 | 1233 | function trampoline18(arg0, arg1) { 1234 | const handle1 = arg0; 1235 | const rsc0 = handleTable0.get(handle1).rep; 1236 | let ret; 1237 | try { 1238 | ret = { tag: 'ok', val: OutputStream.prototype.checkWrite.call(rsc0) }; 1239 | } catch (e) { 1240 | ret = { tag: 'err', val: getErrorPayload(e) }; 1241 | } 1242 | const variant4 = ret; 1243 | switch (variant4.tag) { 1244 | case 'ok': { 1245 | const e = variant4.val; 1246 | dataView(memory0).setInt8(arg1 + 0, 0, true); 1247 | dataView(memory0).setBigInt64(arg1 + 8, toUint64(e), true); 1248 | break; 1249 | } 1250 | case 'err': { 1251 | const e = variant4.val; 1252 | dataView(memory0).setInt8(arg1 + 0, 1, true); 1253 | const variant3 = e; 1254 | switch (variant3.tag) { 1255 | case 'last-operation-failed': { 1256 | const e = variant3.val; 1257 | dataView(memory0).setInt8(arg1 + 8, 0, true); 1258 | if (!(e instanceof Error$1)) { 1259 | throw new Error('Not a valid "Error" resource.'); 1260 | } 1261 | const handle2 = handleCnt1++; 1262 | handleTable1.set(handle2, { rep: e, own: true }); 1263 | dataView(memory0).setInt32(arg1 + 12, handle2, true); 1264 | break; 1265 | } 1266 | case 'closed': { 1267 | dataView(memory0).setInt8(arg1 + 8, 1, true); 1268 | break; 1269 | } 1270 | default: { 1271 | throw new TypeError(`invalid variant ${JSON.stringify(variant3.tag)} specified for StreamError`); 1272 | } 1273 | } 1274 | break; 1275 | } 1276 | default: { 1277 | throw new TypeError('invalid variant specified for result'); 1278 | } 1279 | } 1280 | } 1281 | 1282 | function trampoline19(arg0, arg1, arg2, arg3) { 1283 | const handle1 = arg0; 1284 | const rsc0 = handleTable0.get(handle1).rep; 1285 | const ptr2 = arg1; 1286 | const len2 = arg2; 1287 | const result2 = new Uint8Array(memory0.buffer.slice(ptr2, ptr2 + len2 * 1)); 1288 | let ret; 1289 | try { 1290 | ret = { tag: 'ok', val: OutputStream.prototype.write.call(rsc0, result2) }; 1291 | } catch (e) { 1292 | ret = { tag: 'err', val: getErrorPayload(e) }; 1293 | } 1294 | const variant5 = ret; 1295 | switch (variant5.tag) { 1296 | case 'ok': { 1297 | const e = variant5.val; 1298 | dataView(memory0).setInt8(arg3 + 0, 0, true); 1299 | break; 1300 | } 1301 | case 'err': { 1302 | const e = variant5.val; 1303 | dataView(memory0).setInt8(arg3 + 0, 1, true); 1304 | const variant4 = e; 1305 | switch (variant4.tag) { 1306 | case 'last-operation-failed': { 1307 | const e = variant4.val; 1308 | dataView(memory0).setInt8(arg3 + 4, 0, true); 1309 | if (!(e instanceof Error$1)) { 1310 | throw new Error('Not a valid "Error" resource.'); 1311 | } 1312 | const handle3 = handleCnt1++; 1313 | handleTable1.set(handle3, { rep: e, own: true }); 1314 | dataView(memory0).setInt32(arg3 + 8, handle3, true); 1315 | break; 1316 | } 1317 | case 'closed': { 1318 | dataView(memory0).setInt8(arg3 + 4, 1, true); 1319 | break; 1320 | } 1321 | default: { 1322 | throw new TypeError(`invalid variant ${JSON.stringify(variant4.tag)} specified for StreamError`); 1323 | } 1324 | } 1325 | break; 1326 | } 1327 | default: { 1328 | throw new TypeError('invalid variant specified for result'); 1329 | } 1330 | } 1331 | } 1332 | 1333 | function trampoline20(arg0, arg1, arg2, arg3) { 1334 | const handle1 = arg0; 1335 | const rsc0 = handleTable0.get(handle1).rep; 1336 | const ptr2 = arg1; 1337 | const len2 = arg2; 1338 | const result2 = new Uint8Array(memory0.buffer.slice(ptr2, ptr2 + len2 * 1)); 1339 | let ret; 1340 | try { 1341 | ret = { tag: 'ok', val: OutputStream.prototype.blockingWriteAndFlush.call(rsc0, result2) }; 1342 | } catch (e) { 1343 | ret = { tag: 'err', val: getErrorPayload(e) }; 1344 | } 1345 | const variant5 = ret; 1346 | switch (variant5.tag) { 1347 | case 'ok': { 1348 | const e = variant5.val; 1349 | dataView(memory0).setInt8(arg3 + 0, 0, true); 1350 | break; 1351 | } 1352 | case 'err': { 1353 | const e = variant5.val; 1354 | dataView(memory0).setInt8(arg3 + 0, 1, true); 1355 | const variant4 = e; 1356 | switch (variant4.tag) { 1357 | case 'last-operation-failed': { 1358 | const e = variant4.val; 1359 | dataView(memory0).setInt8(arg3 + 4, 0, true); 1360 | if (!(e instanceof Error$1)) { 1361 | throw new Error('Not a valid "Error" resource.'); 1362 | } 1363 | const handle3 = handleCnt1++; 1364 | handleTable1.set(handle3, { rep: e, own: true }); 1365 | dataView(memory0).setInt32(arg3 + 8, handle3, true); 1366 | break; 1367 | } 1368 | case 'closed': { 1369 | dataView(memory0).setInt8(arg3 + 4, 1, true); 1370 | break; 1371 | } 1372 | default: { 1373 | throw new TypeError(`invalid variant ${JSON.stringify(variant4.tag)} specified for StreamError`); 1374 | } 1375 | } 1376 | break; 1377 | } 1378 | default: { 1379 | throw new TypeError('invalid variant specified for result'); 1380 | } 1381 | } 1382 | } 1383 | 1384 | function trampoline21(arg0, arg1) { 1385 | const handle1 = arg0; 1386 | const rsc0 = handleTable0.get(handle1).rep; 1387 | let ret; 1388 | try { 1389 | ret = { tag: 'ok', val: OutputStream.prototype.blockingFlush.call(rsc0) }; 1390 | } catch (e) { 1391 | ret = { tag: 'err', val: getErrorPayload(e) }; 1392 | } 1393 | const variant4 = ret; 1394 | switch (variant4.tag) { 1395 | case 'ok': { 1396 | const e = variant4.val; 1397 | dataView(memory0).setInt8(arg1 + 0, 0, true); 1398 | break; 1399 | } 1400 | case 'err': { 1401 | const e = variant4.val; 1402 | dataView(memory0).setInt8(arg1 + 0, 1, true); 1403 | const variant3 = e; 1404 | switch (variant3.tag) { 1405 | case 'last-operation-failed': { 1406 | const e = variant3.val; 1407 | dataView(memory0).setInt8(arg1 + 4, 0, true); 1408 | if (!(e instanceof Error$1)) { 1409 | throw new Error('Not a valid "Error" resource.'); 1410 | } 1411 | const handle2 = handleCnt1++; 1412 | handleTable1.set(handle2, { rep: e, own: true }); 1413 | dataView(memory0).setInt32(arg1 + 8, handle2, true); 1414 | break; 1415 | } 1416 | case 'closed': { 1417 | dataView(memory0).setInt8(arg1 + 4, 1, true); 1418 | break; 1419 | } 1420 | default: { 1421 | throw new TypeError(`invalid variant ${JSON.stringify(variant3.tag)} specified for StreamError`); 1422 | } 1423 | } 1424 | break; 1425 | } 1426 | default: { 1427 | throw new TypeError('invalid variant specified for result'); 1428 | } 1429 | } 1430 | } 1431 | 1432 | function trampoline22(arg0, arg1) { 1433 | const ret = getRandomBytes(BigInt.asUintN(64, arg0)); 1434 | const val0 = ret; 1435 | const len0 = val0.byteLength; 1436 | const ptr0 = realloc0(0, 0, 1, len0 * 1); 1437 | const src0 = new Uint8Array(val0.buffer || val0, val0.byteOffset, len0 * 1); 1438 | (new Uint8Array(memory0.buffer, ptr0, len0 * 1)).set(src0); 1439 | dataView(memory0).setInt32(arg1 + 4, len0, true); 1440 | dataView(memory0).setInt32(arg1 + 0, ptr0, true); 1441 | } 1442 | 1443 | function trampoline23(arg0) { 1444 | const ret = getEnvironment(); 1445 | const vec3 = ret; 1446 | const len3 = vec3.length; 1447 | const result3 = realloc0(0, 0, 4, len3 * 16); 1448 | for (let i = 0; i < vec3.length; i++) { 1449 | const e = vec3[i]; 1450 | const base = result3 + i * 16;const [tuple0_0, tuple0_1] = e; 1451 | const ptr1 = utf8Encode(tuple0_0, realloc0, memory0); 1452 | const len1 = utf8EncodedLen; 1453 | dataView(memory0).setInt32(base + 4, len1, true); 1454 | dataView(memory0).setInt32(base + 0, ptr1, true); 1455 | const ptr2 = utf8Encode(tuple0_1, realloc0, memory0); 1456 | const len2 = utf8EncodedLen; 1457 | dataView(memory0).setInt32(base + 12, len2, true); 1458 | dataView(memory0).setInt32(base + 8, ptr2, true); 1459 | } 1460 | dataView(memory0).setInt32(arg0 + 4, len3, true); 1461 | dataView(memory0).setInt32(arg0 + 0, result3, true); 1462 | } 1463 | 1464 | function trampoline24(arg0) { 1465 | const ret = getTerminalStdin(); 1466 | const variant1 = ret; 1467 | if (variant1 === null || variant1=== undefined) { 1468 | dataView(memory0).setInt8(arg0 + 0, 0, true); 1469 | } else { 1470 | const e = variant1; 1471 | dataView(memory0).setInt8(arg0 + 0, 1, true); 1472 | if (!(e instanceof TerminalInput)) { 1473 | throw new Error('Not a valid "TerminalInput" resource.'); 1474 | } 1475 | const handle0 = handleCnt6++; 1476 | handleTable6.set(handle0, { rep: e, own: true }); 1477 | dataView(memory0).setInt32(arg0 + 4, handle0, true); 1478 | } 1479 | } 1480 | 1481 | function trampoline25(arg0) { 1482 | const ret = getTerminalStdout(); 1483 | const variant1 = ret; 1484 | if (variant1 === null || variant1=== undefined) { 1485 | dataView(memory0).setInt8(arg0 + 0, 0, true); 1486 | } else { 1487 | const e = variant1; 1488 | dataView(memory0).setInt8(arg0 + 0, 1, true); 1489 | if (!(e instanceof TerminalOutput)) { 1490 | throw new Error('Not a valid "TerminalOutput" resource.'); 1491 | } 1492 | const handle0 = handleCnt7++; 1493 | handleTable7.set(handle0, { rep: e, own: true }); 1494 | dataView(memory0).setInt32(arg0 + 4, handle0, true); 1495 | } 1496 | } 1497 | 1498 | function trampoline26(arg0) { 1499 | const ret = getTerminalStderr(); 1500 | const variant1 = ret; 1501 | if (variant1 === null || variant1=== undefined) { 1502 | dataView(memory0).setInt8(arg0 + 0, 0, true); 1503 | } else { 1504 | const e = variant1; 1505 | dataView(memory0).setInt8(arg0 + 0, 1, true); 1506 | if (!(e instanceof TerminalOutput)) { 1507 | throw new Error('Not a valid "TerminalOutput" resource.'); 1508 | } 1509 | const handle0 = handleCnt7++; 1510 | handleTable7.set(handle0, { rep: e, own: true }); 1511 | dataView(memory0).setInt32(arg0 + 4, handle0, true); 1512 | } 1513 | } 1514 | let exports3; 1515 | let realloc1; 1516 | let postReturn0; 1517 | const handleTable4= new Map(); 1518 | let handleCnt4 = 0; 1519 | function trampoline0(handle) { 1520 | const handleEntry = handleTable4.get(handle); 1521 | if (!handleEntry) { 1522 | throw new Error(`Resource error: Invalid handle ${handle}`); 1523 | } 1524 | handleTable4.delete(handle); 1525 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1526 | handleEntry.rep[symbolDispose](); 1527 | } 1528 | } 1529 | const handleTable1= new Map(); 1530 | let handleCnt1 = 0; 1531 | function trampoline1(handle) { 1532 | const handleEntry = handleTable1.get(handle); 1533 | if (!handleEntry) { 1534 | throw new Error(`Resource error: Invalid handle ${handle}`); 1535 | } 1536 | handleTable1.delete(handle); 1537 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1538 | handleEntry.rep[symbolDispose](); 1539 | } 1540 | } 1541 | const handleTable2= new Map(); 1542 | let handleCnt2 = 0; 1543 | function trampoline2(handle) { 1544 | const handleEntry = handleTable2.get(handle); 1545 | if (!handleEntry) { 1546 | throw new Error(`Resource error: Invalid handle ${handle}`); 1547 | } 1548 | handleTable2.delete(handle); 1549 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1550 | handleEntry.rep[symbolDispose](); 1551 | } 1552 | } 1553 | const handleTable0= new Map(); 1554 | let handleCnt0 = 0; 1555 | function trampoline3(handle) { 1556 | const handleEntry = handleTable0.get(handle); 1557 | if (!handleEntry) { 1558 | throw new Error(`Resource error: Invalid handle ${handle}`); 1559 | } 1560 | handleTable0.delete(handle); 1561 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1562 | handleEntry.rep[symbolDispose](); 1563 | } 1564 | } 1565 | const handleTable3= new Map(); 1566 | let handleCnt3 = 0; 1567 | function trampoline4(handle) { 1568 | const handleEntry = handleTable3.get(handle); 1569 | if (!handleEntry) { 1570 | throw new Error(`Resource error: Invalid handle ${handle}`); 1571 | } 1572 | handleTable3.delete(handle); 1573 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1574 | handleEntry.rep[symbolDispose](); 1575 | } 1576 | } 1577 | const handleTable6= new Map(); 1578 | let handleCnt6 = 0; 1579 | function trampoline5(handle) { 1580 | const handleEntry = handleTable6.get(handle); 1581 | if (!handleEntry) { 1582 | throw new Error(`Resource error: Invalid handle ${handle}`); 1583 | } 1584 | handleTable6.delete(handle); 1585 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1586 | handleEntry.rep[symbolDispose](); 1587 | } 1588 | } 1589 | const handleTable7= new Map(); 1590 | let handleCnt7 = 0; 1591 | function trampoline6(handle) { 1592 | const handleEntry = handleTable7.get(handle); 1593 | if (!handleEntry) { 1594 | throw new Error(`Resource error: Invalid handle ${handle}`); 1595 | } 1596 | handleTable7.delete(handle); 1597 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1598 | handleEntry.rep[symbolDispose](); 1599 | } 1600 | } 1601 | const handleTable5= new Map(); 1602 | let handleCnt5 = 0; 1603 | function trampoline7(handle) { 1604 | const handleEntry = handleTable5.get(handle); 1605 | if (!handleEntry) { 1606 | throw new Error(`Resource error: Invalid handle ${handle}`); 1607 | } 1608 | handleTable5.delete(handle); 1609 | if (handleEntry.own && handleEntry.rep[symbolDispose]) { 1610 | handleEntry.rep[symbolDispose](); 1611 | } 1612 | } 1613 | 1614 | function diff(arg0, arg1, arg2) { 1615 | const val0 = arg0; 1616 | const len0 = val0.byteLength; 1617 | const ptr0 = realloc1(0, 0, 1, len0 * 1); 1618 | const src0 = new Uint8Array(val0.buffer || val0, val0.byteOffset, len0 * 1); 1619 | (new Uint8Array(memory0.buffer, ptr0, len0 * 1)).set(src0); 1620 | const val1 = arg1; 1621 | const len1 = val1.byteLength; 1622 | const ptr1 = realloc1(0, 0, 1, len1 * 1); 1623 | const src1 = new Uint8Array(val1.buffer || val1, val1.byteOffset, len1 * 1); 1624 | (new Uint8Array(memory0.buffer, ptr1, len1 * 1)).set(src1); 1625 | const {threshold: v2_0, includeAntiAlias: v2_1 } = arg2; 1626 | const variant3 = v2_0; 1627 | let variant3_0; 1628 | let variant3_1; 1629 | if (variant3 === null || variant3=== undefined) { 1630 | variant3_0 = 0; 1631 | variant3_1 = 0; 1632 | } else { 1633 | const e = variant3; 1634 | variant3_0 = 1; 1635 | variant3_1 = +e; 1636 | } 1637 | const variant4 = v2_1; 1638 | let variant4_0; 1639 | let variant4_1; 1640 | if (variant4 === null || variant4=== undefined) { 1641 | variant4_0 = 0; 1642 | variant4_1 = 0; 1643 | } else { 1644 | const e = variant4; 1645 | variant4_0 = 1; 1646 | variant4_1 = e ? 1 : 0; 1647 | } 1648 | const ret = exports1.diff(ptr0, len0, ptr1, len1, variant3_0, variant3_1, variant4_0, variant4_1); 1649 | let variant9; 1650 | switch (dataView(memory0).getUint8(ret + 0, true)) { 1651 | case 0: { 1652 | const ptr5 = dataView(memory0).getInt32(ret + 8, true); 1653 | const len5 = dataView(memory0).getInt32(ret + 12, true); 1654 | const result5 = new Uint8Array(memory0.buffer.slice(ptr5, ptr5 + len5 * 1)); 1655 | variant9= { 1656 | tag: 'ok', 1657 | val: { 1658 | diffCount: dataView(memory0).getInt32(ret + 4, true) >>> 0, 1659 | diffImage: result5, 1660 | width: dataView(memory0).getInt32(ret + 16, true) >>> 0, 1661 | height: dataView(memory0).getInt32(ret + 20, true) >>> 0, 1662 | } 1663 | }; 1664 | break; 1665 | } 1666 | case 1: { 1667 | let variant8; 1668 | switch (dataView(memory0).getUint8(ret + 4, true)) { 1669 | case 0: { 1670 | const ptr6 = dataView(memory0).getInt32(ret + 8, true); 1671 | const len6 = dataView(memory0).getInt32(ret + 12, true); 1672 | const result6 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr6, len6)); 1673 | variant8= { 1674 | tag: 'decode', 1675 | val: result6 1676 | }; 1677 | break; 1678 | } 1679 | case 1: { 1680 | const ptr7 = dataView(memory0).getInt32(ret + 8, true); 1681 | const len7 = dataView(memory0).getInt32(ret + 12, true); 1682 | const result7 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr7, len7)); 1683 | variant8= { 1684 | tag: 'encode', 1685 | val: result7 1686 | }; 1687 | break; 1688 | } 1689 | default: { 1690 | throw new TypeError('invalid variant discriminant for Error'); 1691 | } 1692 | } 1693 | variant9= { 1694 | tag: 'err', 1695 | val: variant8 1696 | }; 1697 | break; 1698 | } 1699 | default: { 1700 | throw new TypeError('invalid variant discriminant for expected'); 1701 | } 1702 | } 1703 | postReturn0(ret); 1704 | if (variant9.tag === 'err') { 1705 | throw new ComponentError(variant9.val); 1706 | } 1707 | return variant9.val; 1708 | } 1709 | 1710 | const $init = (async() => { 1711 | const module0 = fetchCompile(new URL('./index.core.wasm', import.meta.url)); 1712 | const module1 = fetchCompile(new URL('./index.core2.wasm', import.meta.url)); 1713 | const module2 = base64Compile('AGFzbQEAAAABQgtgAX8AYAN/fn8AYAJ/fwBgBH9/f38AYAJ+fwBgBH9/f38Bf2ACf38Bf2ABfwF/YAN/f38Bf2AEf35/fwF/YAF/AAMZGAABAgICAgIDAwIEAAAAAAUGBgYHBggJCgQFAXABGBgHehkBMAAAATEAAQEyAAIBMwADATQABAE1AAUBNgAGATcABwE4AAgBOQAJAjEwAAoCMTEACwIxMgAMAjEzAA0CMTQADgIxNQAPAjE2ABACMTcAEQIxOAASAjE5ABMCMjAAFAIyMQAVAjIyABYCMjMAFwgkaW1wb3J0cwEACqcCGAkAIABBABEAAAsNACAAIAEgAkEBEQEACwsAIAAgAUECEQIACwsAIAAgAUEDEQIACwsAIAAgAUEEEQIACwsAIAAgAUEFEQIACwsAIAAgAUEGEQIACw8AIAAgASACIANBBxEDAAsPACAAIAEgAiADQQgRAwALCwAgACABQQkRAgALCwAgACABQQoRBAALCQAgAEELEQAACwkAIABBDBEAAAsJACAAQQ0RAAALCQAgAEEOEQAACw8AIAAgASACIANBDxEFAAsLACAAIAFBEBEGAAsLACAAIAFBEREGAAsLACAAIAFBEhEGAAsJACAAQRMRBwALCwAgACABQRQRBgALDQAgACABIAJBFREIAAsPACAAIAEgAiADQRYRCQALCQAgAEEXEQoACwAuCXByb2R1Y2VycwEMcHJvY2Vzc2VkLWJ5AQ13aXQtY29tcG9uZW50BjAuMTguMgCoDARuYW1lABMSd2l0LWNvbXBvbmVudDpzaGltAYsMGABFaW5kaXJlY3Qtd2FzaTpmaWxlc3lzdGVtL3ByZW9wZW5zQDAuMi4wLXJjLTIwMjMtMTAtMTgtZ2V0LWRpcmVjdG9yaWVzAVZpbmRpcmVjdC13YXNpOmZpbGVzeXN0ZW0vdHlwZXNAMC4yLjAtcmMtMjAyMy0xMC0xOC1bbWV0aG9kXWRlc2NyaXB0b3Iud3JpdGUtdmlhLXN0cmVhbQJXaW5kaXJlY3Qtd2FzaTpmaWxlc3lzdGVtL3R5cGVzQDAuMi4wLXJjLTIwMjMtMTAtMTgtW21ldGhvZF1kZXNjcmlwdG9yLmFwcGVuZC12aWEtc3RyZWFtA05pbmRpcmVjdC13YXNpOmZpbGVzeXN0ZW0vdHlwZXNAMC4yLjAtcmMtMjAyMy0xMC0xOC1bbWV0aG9kXWRlc2NyaXB0b3IuZ2V0LXR5cGUESmluZGlyZWN0LXdhc2k6ZmlsZXN5c3RlbS90eXBlc0AwLjIuMC1yYy0yMDIzLTEwLTE4LVttZXRob2RdZGVzY3JpcHRvci5zdGF0BUhpbmRpcmVjdC13YXNpOmZpbGVzeXN0ZW0vdHlwZXNAMC4yLjAtcmMtMjAyMy0xMC0xOC1maWxlc3lzdGVtLWVycm9yLWNvZGUGTmluZGlyZWN0LXdhc2k6aW8vc3RyZWFtc0AwLjIuMC1yYy0yMDIzLTEwLTE4LVttZXRob2Rdb3V0cHV0LXN0cmVhbS5jaGVjay13cml0ZQdIaW5kaXJlY3Qtd2FzaTppby9zdHJlYW1zQDAuMi4wLXJjLTIwMjMtMTAtMTgtW21ldGhvZF1vdXRwdXQtc3RyZWFtLndyaXRlCFtpbmRpcmVjdC13YXNpOmlvL3N0cmVhbXNAMC4yLjAtcmMtMjAyMy0xMC0xOC1bbWV0aG9kXW91dHB1dC1zdHJlYW0uYmxvY2tpbmctd3JpdGUtYW5kLWZsdXNoCVFpbmRpcmVjdC13YXNpOmlvL3N0cmVhbXNAMC4yLjAtcmMtMjAyMy0xMC0xOC1bbWV0aG9kXW91dHB1dC1zdHJlYW0uYmxvY2tpbmctZmx1c2gKQGluZGlyZWN0LXdhc2k6cmFuZG9tL3JhbmRvbUAwLjIuMC1yYy0yMDIzLTEwLTE4LWdldC1yYW5kb20tYnl0ZXMLQWluZGlyZWN0LXdhc2k6Y2xpL2Vudmlyb25tZW50QDAuMi4wLXJjLTIwMjMtMTAtMTgtZ2V0LWVudmlyb25tZW50DEdpbmRpcmVjdC13YXNpOmNsaS90ZXJtaW5hbC1zdGRpbkAwLjIuMC1yYy0yMDIzLTEwLTE4LWdldC10ZXJtaW5hbC1zdGRpbg1JaW5kaXJlY3Qtd2FzaTpjbGkvdGVybWluYWwtc3Rkb3V0QDAuMi4wLXJjLTIwMjMtMTAtMTgtZ2V0LXRlcm1pbmFsLXN0ZG91dA5JaW5kaXJlY3Qtd2FzaTpjbGkvdGVybWluYWwtc3RkZXJyQDAuMi4wLXJjLTIwMjMtMTAtMTgtZ2V0LXRlcm1pbmFsLXN0ZGVycg8lYWRhcHQtd2FzaV9zbmFwc2hvdF9wcmV2aWV3MS1mZF93cml0ZRAnYWRhcHQtd2FzaV9zbmFwc2hvdF9wcmV2aWV3MS1yYW5kb21fZ2V0EShhZGFwdC13YXNpX3NuYXBzaG90X3ByZXZpZXcxLWVudmlyb25fZ2V0Ei5hZGFwdC13YXNpX3NuYXBzaG90X3ByZXZpZXcxLWVudmlyb25fc2l6ZXNfZ2V0EyVhZGFwdC13YXNpX3NuYXBzaG90X3ByZXZpZXcxLWZkX2Nsb3NlFCthZGFwdC13YXNpX3NuYXBzaG90X3ByZXZpZXcxLWZkX3ByZXN0YXRfZ2V0FTBhZGFwdC13YXNpX3NuYXBzaG90X3ByZXZpZXcxLWZkX3ByZXN0YXRfZGlyX25hbWUWJGFkYXB0LXdhc2lfc25hcHNob3RfcHJldmlldzEtZmRfc2VlaxcmYWRhcHQtd2FzaV9zbmFwc2hvdF9wcmV2aWV3MS1wcm9jX2V4aXQ'); 1714 | const module3 = base64Compile('AGFzbQEAAAABQgtgAX8AYAN/fn8AYAJ/fwBgBH9/f38AYAJ+fwBgBH9/f38Bf2ACf38Bf2ABfwF/YAN/f38Bf2AEf35/fwF/YAF/AAKWARkAATAAAAABMQABAAEyAAIAATMAAgABNAACAAE1AAIAATYAAgABNwADAAE4AAMAATkAAgACMTAABAACMTEAAAACMTIAAAACMTMAAAACMTQAAAACMTUABQACMTYABgACMTcABgACMTgABgACMTkABwACMjAABgACMjEACAACMjIACQACMjMACgAIJGltcG9ydHMBcAEYGAkeAQBBAAsYAAECAwQFBgcICQoLDA0ODxAREhMUFRYXAC4JcHJvZHVjZXJzAQxwcm9jZXNzZWQtYnkBDXdpdC1jb21wb25lbnQGMC4xOC4yABwEbmFtZQAVFHdpdC1jb21wb25lbnQ6Zml4dXBz'); 1715 | const instanceFlags0 = new WebAssembly.Global({ value: "i32", mutable: true }, 3); 1716 | Promise.all([module0, module1, module2, module3]).catch(() => {}); 1717 | ({ exports: exports0 } = await instantiateCore(await module2)); 1718 | ({ exports: exports1 } = await instantiateCore(await module0, { 1719 | wasi_snapshot_preview1: { 1720 | environ_get: exports0['17'], 1721 | environ_sizes_get: exports0['18'], 1722 | fd_close: exports0['19'], 1723 | fd_prestat_dir_name: exports0['21'], 1724 | fd_prestat_get: exports0['20'], 1725 | fd_seek: exports0['22'], 1726 | fd_write: exports0['15'], 1727 | proc_exit: exports0['23'], 1728 | random_get: exports0['16'], 1729 | }, 1730 | })); 1731 | ({ exports: exports2 } = await instantiateCore(await module1, { 1732 | __main_module__: { 1733 | cabi_realloc: exports1.cabi_realloc, 1734 | }, 1735 | env: { 1736 | memory: exports1.memory, 1737 | }, 1738 | 'wasi:cli/environment@0.2.0-rc-2023-10-18': { 1739 | 'get-environment': exports0['11'], 1740 | }, 1741 | 'wasi:cli/exit@0.2.0-rc-2023-10-18': { 1742 | exit: trampoline9, 1743 | }, 1744 | 'wasi:cli/stderr@0.2.0-rc-2023-10-18': { 1745 | 'get-stderr': trampoline8, 1746 | }, 1747 | 'wasi:cli/stdin@0.2.0-rc-2023-10-18': { 1748 | 'get-stdin': trampoline10, 1749 | }, 1750 | 'wasi:cli/stdout@0.2.0-rc-2023-10-18': { 1751 | 'get-stdout': trampoline11, 1752 | }, 1753 | 'wasi:cli/terminal-input@0.2.0-rc-2023-10-18': { 1754 | '[resource-drop]terminal-input': trampoline5, 1755 | }, 1756 | 'wasi:cli/terminal-output@0.2.0-rc-2023-10-18': { 1757 | '[resource-drop]terminal-output': trampoline6, 1758 | }, 1759 | 'wasi:cli/terminal-stderr@0.2.0-rc-2023-10-18': { 1760 | 'get-terminal-stderr': exports0['14'], 1761 | }, 1762 | 'wasi:cli/terminal-stdin@0.2.0-rc-2023-10-18': { 1763 | 'get-terminal-stdin': exports0['12'], 1764 | }, 1765 | 'wasi:cli/terminal-stdout@0.2.0-rc-2023-10-18': { 1766 | 'get-terminal-stdout': exports0['13'], 1767 | }, 1768 | 'wasi:filesystem/preopens@0.2.0-rc-2023-10-18': { 1769 | 'get-directories': exports0['0'], 1770 | }, 1771 | 'wasi:filesystem/types@0.2.0-rc-2023-10-18': { 1772 | '[method]descriptor.append-via-stream': exports0['2'], 1773 | '[method]descriptor.get-type': exports0['3'], 1774 | '[method]descriptor.stat': exports0['4'], 1775 | '[method]descriptor.write-via-stream': exports0['1'], 1776 | '[resource-drop]descriptor': trampoline4, 1777 | '[resource-drop]directory-entry-stream': trampoline0, 1778 | 'filesystem-error-code': exports0['5'], 1779 | }, 1780 | 'wasi:io/streams@0.2.0-rc-2023-10-18': { 1781 | '[method]output-stream.blocking-flush': exports0['9'], 1782 | '[method]output-stream.blocking-write-and-flush': exports0['8'], 1783 | '[method]output-stream.check-write': exports0['6'], 1784 | '[method]output-stream.write': exports0['7'], 1785 | '[resource-drop]error': trampoline1, 1786 | '[resource-drop]input-stream': trampoline2, 1787 | '[resource-drop]output-stream': trampoline3, 1788 | }, 1789 | 'wasi:random/random@0.2.0-rc-2023-10-18': { 1790 | 'get-random-bytes': exports0['10'], 1791 | }, 1792 | 'wasi:sockets/tcp@0.2.0-rc-2023-10-18': { 1793 | '[resource-drop]tcp-socket': trampoline7, 1794 | }, 1795 | })); 1796 | memory0 = exports1.memory; 1797 | realloc0 = exports2.cabi_import_realloc; 1798 | ({ exports: exports3 } = await instantiateCore(await module3, { 1799 | '': { 1800 | $imports: exports0.$imports, 1801 | '0': trampoline12, 1802 | '1': trampoline13, 1803 | '10': trampoline22, 1804 | '11': trampoline23, 1805 | '12': trampoline24, 1806 | '13': trampoline25, 1807 | '14': trampoline26, 1808 | '15': exports2.fd_write, 1809 | '16': exports2.random_get, 1810 | '17': exports2.environ_get, 1811 | '18': exports2.environ_sizes_get, 1812 | '19': exports2.fd_close, 1813 | '2': trampoline14, 1814 | '20': exports2.fd_prestat_get, 1815 | '21': exports2.fd_prestat_dir_name, 1816 | '22': exports2.fd_seek, 1817 | '23': exports2.proc_exit, 1818 | '3': trampoline15, 1819 | '4': trampoline16, 1820 | '5': trampoline17, 1821 | '6': trampoline18, 1822 | '7': trampoline19, 1823 | '8': trampoline20, 1824 | '9': trampoline21, 1825 | }, 1826 | })); 1827 | realloc1 = exports1.cabi_realloc; 1828 | postReturn0 = exports1.cabi_post_diff; 1829 | })(); 1830 | 1831 | await $init; 1832 | 1833 | export { diff } -------------------------------------------------------------------------------- /js/interfaces/bokuweb-image-diff-types.d.ts: -------------------------------------------------------------------------------- 1 | export namespace BokuwebImageDiffTypes { 2 | } 3 | export interface Opts { 4 | threshold?: number, 5 | includeAntiAlias?: boolean, 6 | } 7 | export interface Output { 8 | diffCount: number, 9 | diffImage: Uint8Array, 10 | width: number, 11 | height: number, 12 | } 13 | export type Error = ErrorDecode | ErrorEncode; 14 | export interface ErrorDecode { 15 | tag: 'decode', 16 | val: string, 17 | } 18 | export interface ErrorEncode { 19 | tag: 'encode', 20 | val: string, 21 | } 22 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-environment.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliEnvironment { 2 | export function getEnvironment(): [string, string][]; 3 | } 4 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-exit.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliExit { 2 | export function exit(status: Result): void; 3 | } 4 | export type Result = { tag: 'ok', val: T } | { tag: 'err', val: E }; 5 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-stderr.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliStderr { 2 | export function getStderr(): OutputStream; 3 | } 4 | import type { OutputStream } from '../interfaces/wasi-io-streams.js'; 5 | export { OutputStream }; 6 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-stdin.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliStdin { 2 | export function getStdin(): InputStream; 3 | } 4 | import type { InputStream } from '../interfaces/wasi-io-streams.js'; 5 | export { InputStream }; 6 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-stdout.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliStdout { 2 | export function getStdout(): OutputStream; 3 | } 4 | import type { OutputStream } from '../interfaces/wasi-io-streams.js'; 5 | export { OutputStream }; 6 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-terminal-input.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliTerminalInput { 2 | export { TerminalInput }; 3 | } 4 | 5 | export class TerminalInput { 6 | } 7 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-terminal-output.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliTerminalOutput { 2 | export { TerminalOutput }; 3 | } 4 | 5 | export class TerminalOutput { 6 | } 7 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-terminal-stderr.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliTerminalStderr { 2 | export function getTerminalStderr(): TerminalOutput | undefined; 3 | } 4 | import type { TerminalOutput } from '../interfaces/wasi-cli-terminal-output.js'; 5 | export { TerminalOutput }; 6 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-terminal-stdin.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliTerminalStdin { 2 | export function getTerminalStdin(): TerminalInput | undefined; 3 | } 4 | import type { TerminalInput } from '../interfaces/wasi-cli-terminal-input.js'; 5 | export { TerminalInput }; 6 | -------------------------------------------------------------------------------- /js/interfaces/wasi-cli-terminal-stdout.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiCliTerminalStdout { 2 | export function getTerminalStdout(): TerminalOutput | undefined; 3 | } 4 | import type { TerminalOutput } from '../interfaces/wasi-cli-terminal-output.js'; 5 | export { TerminalOutput }; 6 | -------------------------------------------------------------------------------- /js/interfaces/wasi-clocks-wall-clock.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiClocksWallClock { 2 | } 3 | export interface Datetime { 4 | seconds: bigint, 5 | nanoseconds: number, 6 | } 7 | -------------------------------------------------------------------------------- /js/interfaces/wasi-filesystem-preopens.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiFilesystemPreopens { 2 | export function getDirectories(): [Descriptor, string][]; 3 | } 4 | import type { Descriptor } from '../interfaces/wasi-filesystem-types.js'; 5 | export { Descriptor }; 6 | -------------------------------------------------------------------------------- /js/interfaces/wasi-filesystem-types.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiFilesystemTypes { 2 | export { Descriptor }; 3 | export function filesystemErrorCode(err: Error): ErrorCode | undefined; 4 | export { DirectoryEntryStream }; 5 | } 6 | export type Filesize = bigint; 7 | import type { OutputStream } from '../interfaces/wasi-io-streams.js'; 8 | export { OutputStream }; 9 | /** 10 | * # Variants 11 | * 12 | * ## `"access"` 13 | * 14 | * ## `"would-block"` 15 | * 16 | * ## `"already"` 17 | * 18 | * ## `"bad-descriptor"` 19 | * 20 | * ## `"busy"` 21 | * 22 | * ## `"deadlock"` 23 | * 24 | * ## `"quota"` 25 | * 26 | * ## `"exist"` 27 | * 28 | * ## `"file-too-large"` 29 | * 30 | * ## `"illegal-byte-sequence"` 31 | * 32 | * ## `"in-progress"` 33 | * 34 | * ## `"interrupted"` 35 | * 36 | * ## `"invalid"` 37 | * 38 | * ## `"io"` 39 | * 40 | * ## `"is-directory"` 41 | * 42 | * ## `"loop"` 43 | * 44 | * ## `"too-many-links"` 45 | * 46 | * ## `"message-size"` 47 | * 48 | * ## `"name-too-long"` 49 | * 50 | * ## `"no-device"` 51 | * 52 | * ## `"no-entry"` 53 | * 54 | * ## `"no-lock"` 55 | * 56 | * ## `"insufficient-memory"` 57 | * 58 | * ## `"insufficient-space"` 59 | * 60 | * ## `"not-directory"` 61 | * 62 | * ## `"not-empty"` 63 | * 64 | * ## `"not-recoverable"` 65 | * 66 | * ## `"unsupported"` 67 | * 68 | * ## `"no-tty"` 69 | * 70 | * ## `"no-such-device"` 71 | * 72 | * ## `"overflow"` 73 | * 74 | * ## `"not-permitted"` 75 | * 76 | * ## `"pipe"` 77 | * 78 | * ## `"read-only"` 79 | * 80 | * ## `"invalid-seek"` 81 | * 82 | * ## `"text-file-busy"` 83 | * 84 | * ## `"cross-device"` 85 | */ 86 | export type ErrorCode = 'access' | 'would-block' | 'already' | 'bad-descriptor' | 'busy' | 'deadlock' | 'quota' | 'exist' | 'file-too-large' | 'illegal-byte-sequence' | 'in-progress' | 'interrupted' | 'invalid' | 'io' | 'is-directory' | 'loop' | 'too-many-links' | 'message-size' | 'name-too-long' | 'no-device' | 'no-entry' | 'no-lock' | 'insufficient-memory' | 'insufficient-space' | 'not-directory' | 'not-empty' | 'not-recoverable' | 'unsupported' | 'no-tty' | 'no-such-device' | 'overflow' | 'not-permitted' | 'pipe' | 'read-only' | 'invalid-seek' | 'text-file-busy' | 'cross-device'; 87 | /** 88 | * # Variants 89 | * 90 | * ## `"unknown"` 91 | * 92 | * ## `"block-device"` 93 | * 94 | * ## `"character-device"` 95 | * 96 | * ## `"directory"` 97 | * 98 | * ## `"fifo"` 99 | * 100 | * ## `"symbolic-link"` 101 | * 102 | * ## `"regular-file"` 103 | * 104 | * ## `"socket"` 105 | */ 106 | export type DescriptorType = 'unknown' | 'block-device' | 'character-device' | 'directory' | 'fifo' | 'symbolic-link' | 'regular-file' | 'socket'; 107 | export type LinkCount = bigint; 108 | import type { Datetime } from '../interfaces/wasi-clocks-wall-clock.js'; 109 | export { Datetime }; 110 | export interface DescriptorStat { 111 | type: DescriptorType, 112 | linkCount: LinkCount, 113 | size: Filesize, 114 | dataAccessTimestamp?: Datetime, 115 | dataModificationTimestamp?: Datetime, 116 | statusChangeTimestamp?: Datetime, 117 | } 118 | import type { Error } from '../interfaces/wasi-io-streams.js'; 119 | export { Error }; 120 | 121 | export class DirectoryEntryStream { 122 | } 123 | 124 | export class Descriptor { 125 | writeViaStream(offset: Filesize): OutputStream; 126 | appendViaStream(): OutputStream; 127 | getType(): DescriptorType; 128 | stat(): DescriptorStat; 129 | } 130 | -------------------------------------------------------------------------------- /js/interfaces/wasi-io-error.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiIoError { 2 | export { Error }; 3 | } 4 | 5 | export class Error { 6 | } 7 | -------------------------------------------------------------------------------- /js/interfaces/wasi-io-streams.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiIoStreams { 2 | export { OutputStream }; 3 | export { Error }; 4 | export { InputStream }; 5 | } 6 | export type StreamError = StreamErrorLastOperationFailed | StreamErrorClosed; 7 | export interface StreamErrorLastOperationFailed { 8 | tag: 'last-operation-failed', 9 | val: Error, 10 | } 11 | export interface StreamErrorClosed { 12 | tag: 'closed', 13 | } 14 | 15 | export class OutputStream { 16 | checkWrite(): bigint; 17 | write(contents: Uint8Array): void; 18 | blockingWriteAndFlush(contents: Uint8Array): void; 19 | blockingFlush(): void; 20 | } 21 | 22 | export class InputStream { 23 | } 24 | 25 | export class Error { 26 | } 27 | -------------------------------------------------------------------------------- /js/interfaces/wasi-random-random.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiRandomRandom { 2 | export function getRandomBytes(len: bigint): Uint8Array; 3 | } 4 | -------------------------------------------------------------------------------- /js/interfaces/wasi-sockets-tcp.d.ts: -------------------------------------------------------------------------------- 1 | export namespace WasiSocketsTcp { 2 | export { TcpSocket }; 3 | } 4 | 5 | export class TcpSocket { 6 | } 7 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bokuweb/image-diff-wasm", 3 | "version": "0.1.0", 4 | "repository": "https://github.com/bokuweb/image-diff-rs", 5 | "author": "", 6 | "license": "MIT", 7 | "main": "index.mjs", 8 | "types": "index.d.ts", 9 | "scripts": { 10 | "test": "node examples/index.mjs" 11 | }, 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "keywords": [], 16 | "dependencies": { 17 | "@bytecodealliance/preview2-shim": "^0.0.21" 18 | }, 19 | "devDependencies": { 20 | "@bokuweb/image-diff-wasm": "0.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /js/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | '@bytecodealliance/preview2-shim': 9 | specifier: ^0.0.21 10 | version: 0.0.21 11 | 12 | devDependencies: 13 | '@bokuweb/image-diff-wasm': 14 | specifier: 0.1.0 15 | version: 0.1.0 16 | 17 | packages: 18 | 19 | /@bokuweb/image-diff-wasm@0.1.0: 20 | resolution: {integrity: sha512-huRRxcWfHaLEyOG40ByU9XwVc5TXZjZc1wFBOkNBhUTxcamvxwxwY2OnQeXIBv9rdSzlxjoXsasu2f8leYu4Yg==} 21 | dependencies: 22 | '@bytecodealliance/preview2-shim': 0.0.21 23 | dev: true 24 | 25 | /@bytecodealliance/preview2-shim@0.0.21: 26 | resolution: {integrity: sha512-VSqfQ0aAd9CV0KwCtXV8CZnJUMIZLyf3pbDWRNIEbg3YRETYbCrcwCsWDdEXr4qwUAsqjpgY9daCHyicmGlDfQ==} 27 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | --------------------------------------------------------------------------------