aboutsummaryrefslogtreecommitdiff
path: root/old
diff options
context:
space:
mode:
Diffstat (limited to 'old')
-rw-r--r--old/Cargo.lock2545
-rw-r--r--old/Cargo.toml13
-rw-r--r--old/bv1/.gitignore2
-rw-r--r--old/bv1/app/Cargo.toml9
-rw-r--r--old/bv1/app/src/bin/bench.rs28
-rw-r--r--old/bv1/app/src/bin/main.rs71
-rw-r--r--old/bv1/app/src/bin/test.rs47
-rw-r--r--old/bv1/codec-web/Cargo.toml13
-rw-r--r--old/bv1/codec-web/src/lib.rs60
-rw-r--r--old/bv1/codec-web/web/.gitignore2
-rw-r--r--old/bv1/codec-web/web/index.html14
-rw-r--r--old/bv1/codec-web/web/main.ts69
-rw-r--r--old/bv1/codec/Cargo.toml12
-rw-r--r--old/bv1/codec/src/debug.rs42
-rw-r--r--old/bv1/codec/src/decode.rs75
-rw-r--r--old/bv1/codec/src/encode.rs210
-rw-r--r--old/bv1/codec/src/frameio.rs35
-rw-r--r--old/bv1/codec/src/huff.rs192
-rw-r--r--old/bv1/codec/src/impls.rs165
-rw-r--r--old/bv1/codec/src/lib.rs52
-rw-r--r--old/bv1/codec/src/serialize.rs116
-rw-r--r--old/bv1/codec/src/split.rs42
-rw-r--r--old/bv1/spec.md45
-rwxr-xr-xold/bv1/tools/decode-display2
-rwxr-xr-xold/bv1/tools/decode-display-debug2
-rwxr-xr-xold/bv1/tools/decode-save2
-rwxr-xr-xold/bv1/tools/decode-save-debug2
-rwxr-xr-xold/bv1/tools/encode2
-rw-r--r--old/dhwt-codec/.gitignore2
-rw-r--r--old/dhwt-codec/Cargo.lock356
-rw-r--r--old/dhwt-codec/Cargo.toml9
-rwxr-xr-xold/dhwt-codec/run16
-rw-r--r--old/dhwt-codec/src/bin/decode.rs57
-rw-r--r--old/dhwt-codec/src/bin/encode.rs57
-rw-r--r--old/dhwt-codec/src/bin/export.rs42
-rw-r--r--old/dhwt-codec/src/bin/import.rs51
-rw-r--r--old/dhwt-codec/src/io.rs71
-rw-r--r--old/dhwt-codec/src/lib.rs23
-rw-r--r--old/dhwt-codec/src/transform.rs42
-rw-r--r--old/dhwt-codec/src/trim.rs36
-rw-r--r--old/dhwt-codec/src/view.rs42
-rw-r--r--old/difftree/Cargo.lock156
-rw-r--r--old/difftree/Cargo.toml10
-rw-r--r--old/difftree/src/main.rs206
-rw-r--r--old/evc/.gitignore6
-rw-r--r--old/evc/Cargo.lock663
-rw-r--r--old/evc/Cargo.toml26
-rwxr-xr-xold/evc/scripts/bench_modes22
-rwxr-xr-xold/evc/scripts/bench_out11
-rwxr-xr-xold/evc/scripts/gen10
-rwxr-xr-xold/evc/scripts/report9
-rwxr-xr-xold/evc/scripts/stream7
-rwxr-xr-xold/evc/scripts/stream-nodebug7
-rw-r--r--old/evc/spec.md60
-rw-r--r--old/evc/src/bin/decode.rs63
-rw-r--r--old/evc/src/bin/encode.rs111
-rw-r--r--old/evc/src/bin/info.rs10
-rw-r--r--old/evc/src/block.rs123
-rw-r--r--old/evc/src/codec/compress.rs155
-rw-r--r--old/evc/src/codec/decode.rs34
-rw-r--r--old/evc/src/codec/encode/advanced.rs115
-rw-r--r--old/evc/src/codec/encode/mod.rs112
-rw-r--r--old/evc/src/codec/encode/simple.rs65
-rw-r--r--old/evc/src/codec/mod.rs3
-rw-r--r--old/evc/src/debug.rs103
-rw-r--r--old/evc/src/format/header.rs29
-rw-r--r--old/evc/src/format/mod.rs2
-rw-r--r--old/evc/src/format/ser.rs335
-rw-r--r--old/evc/src/frame.rs109
-rw-r--r--old/evc/src/helpers/matrix.rs33
-rw-r--r--old/evc/src/helpers/mod.rs4
-rw-r--r--old/evc/src/helpers/pixel.rs81
-rw-r--r--old/evc/src/helpers/threading.rs26
-rw-r--r--old/evc/src/helpers/vector.rs126
-rw-r--r--old/evc/src/lib.rs13
-rw-r--r--old/evc/src/refsampler.rs51
-rw-r--r--old/evc/src/view.rs241
-rw-r--r--old/flowy/Cargo.toml13
-rw-r--r--old/flowy/src/main.rs132
-rw-r--r--old/flowy/src/motion/debug.rs176
-rw-r--r--old/flowy/src/motion/debug.wgsl50
-rw-r--r--old/flowy/src/motion/dec.rs156
-rw-r--r--old/flowy/src/motion/dec.wgsl31
-rw-r--r--old/flowy/src/motion/enc-old.wgsl88
-rw-r--r--old/flowy/src/motion/enc.rs160
-rw-r--r--old/flowy/src/motion/enc.wgsl121
-rw-r--r--old/flowy/src/motion/mod.rs203
-rw-r--r--old/framework/Cargo.toml6
-rw-r--r--old/framework/src/common/huffman.rs181
-rw-r--r--old/framework/src/common/mod.rs1
-rw-r--r--old/framework/src/lib.rs153
-rw-r--r--old/framework/src/vector.rs128
-rw-r--r--old/readme.md20
-rw-r--r--old/vgcodec/.gitignore3
-rw-r--r--old/vgcodec/Cargo.lock1441
-rw-r--r--old/vgcodec/Cargo.toml15
-rw-r--r--old/vgcodec/src/app.rs59
-rw-r--r--old/vgcodec/src/approximate.rs175
-rw-r--r--old/vgcodec/src/diff.rs122
-rw-r--r--old/vgcodec/src/diff.wgsl16
-rw-r--r--old/vgcodec/src/export.rs83
-rw-r--r--old/vgcodec/src/helper.rs40
-rw-r--r--old/vgcodec/src/main.rs60
-rw-r--r--old/vgcodec/src/paint.rs92
-rw-r--r--old/vgcodec/src/paint.wgsl22
105 files changed, 11557 insertions, 0 deletions
diff --git a/old/Cargo.lock b/old/Cargo.lock
new file mode 100644
index 0000000..4876026
--- /dev/null
+++ b/old/Cargo.lock
@@ -0,0 +1,2545 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "aligned-vec"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
+
+[[package]]
+name = "app"
+version = "0.1.0"
+dependencies = [
+ "bv1",
+ "clap",
+]
+
+[[package]]
+name = "arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+
+[[package]]
+name = "arg_enum_proc_macro"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "ash"
+version = "0.37.3+1.3.251"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
+dependencies = [
+ "libloading 0.7.4",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "av1-grain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf"
+dependencies = [
+ "anyhow",
+ "arrayvec",
+ "log",
+ "nom",
+ "num-rational",
+ "v_frame",
+]
+
+[[package]]
+name = "avif-serialize"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "bincode"
+version = "2.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
+dependencies = [
+ "bincode_derive",
+ "serde",
+]
+
+[[package]]
+name = "bincode_derive"
+version = "2.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c"
+dependencies = [
+ "virtue",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "bitstream-io"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "built"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41bfbdb21256b87a8b5e80fab81a8eed158178e812fd7ba451907518b2742f16"
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "bv1"
+version = "0.1.0"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "bytemuck"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
+[[package]]
+name = "cc"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
+dependencies = [
+ "jobserver",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "codec-web"
+version = "0.1.0"
+dependencies = [
+ "bv1",
+ "console_error_panic_hook",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+
+[[package]]
+name = "com"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6"
+dependencies = [
+ "com_macros",
+]
+
+[[package]]
+name = "com_macros"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5"
+dependencies = [
+ "com_macros_support",
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "com_macros_support"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "console"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "unicode-width",
+ "windows-sys",
+]
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "d3d12"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813"
+dependencies = [
+ "bitflags 2.5.0",
+ "libloading 0.8.3",
+ "winapi",
+]
+
+[[package]]
+name = "dhwt-codec"
+version = "0.1.0"
+dependencies = [
+ "bincode",
+ "clap",
+ "rayon",
+]
+
+[[package]]
+name = "difftree"
+version = "0.1.0"
+dependencies = [
+ "framework",
+ "rand",
+ "rayon",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "either"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
+
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
+[[package]]
+name = "env_filter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "humantime",
+ "log",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "exr"
+version = "1.72.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
+dependencies = [
+ "bit_field",
+ "flume",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fdeflate"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "flowy"
+version = "0.1.0"
+dependencies = [
+ "bytemuck",
+ "env_logger",
+ "framework",
+ "log",
+ "oneshot",
+ "pollster",
+ "wgpu",
+]
+
+[[package]]
+name = "flume"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "framework"
+version = "0.1.0"
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-intrusive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
+dependencies = [
+ "futures-core",
+ "lock_api",
+ "parking_lot",
+]
+
+[[package]]
+name = "generator"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
+dependencies = [
+ "cc",
+ "libc",
+ "log",
+ "rustversion",
+ "windows 0.48.0",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gif"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glow"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
+dependencies = [
+ "bitflags 2.5.0",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "gpu-allocator"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884"
+dependencies = [
+ "log",
+ "presser",
+ "thiserror",
+ "winapi",
+ "windows 0.52.0",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557"
+dependencies = [
+ "bitflags 2.5.0",
+ "gpu-descriptor-types",
+ "hashbrown",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+]
+
+[[package]]
+name = "hassle-rs"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
+dependencies = [
+ "bitflags 2.5.0",
+ "com",
+ "libc",
+ "libloading 0.8.3",
+ "thiserror",
+ "widestring",
+ "winapi",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "image"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "image-webp",
+ "num-traits",
+ "png",
+ "qoi",
+ "ravif",
+ "rayon",
+ "rgb",
+ "tiff",
+ "zune-core",
+ "zune-jpeg",
+]
+
+[[package]]
+name = "image-webp"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d"
+dependencies = [
+ "byteorder-lite",
+ "thiserror",
+]
+
+[[package]]
+name = "imgref"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
+
+[[package]]
+name = "indexmap"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "indicatif"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3"
+dependencies = [
+ "console",
+ "instant",
+ "number_prefix",
+ "portable-atomic",
+ "unicode-width",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "interpolate_name"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
+dependencies = [
+ "libc",
+ "libloading 0.8.3",
+ "pkg-config",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
+name = "libc"
+version = "0.2.154"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
+dependencies = [
+ "arbitrary",
+ "cc",
+ "once_cell",
+]
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "libreschmux"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "env_logger",
+ "indicatif",
+ "log",
+ "proc-macro2",
+ "rayon",
+ "rustdct",
+]
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "loom"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
+dependencies = [
+ "cfg-if",
+ "generator",
+ "pin-utils",
+ "scoped-tls",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "loop9"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
+dependencies = [
+ "imgref",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "maybe-rayon"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
+dependencies = [
+ "cfg-if",
+ "rayon",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "metal"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb"
+dependencies = [
+ "bitflags 2.5.0",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+ "simd-adler32",
+]
+
+[[package]]
+name = "naga"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231"
+dependencies = [
+ "arrayvec",
+ "bit-set",
+ "bitflags 2.5.0",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap",
+ "log",
+ "num-traits",
+ "rustc-hash",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
+[[package]]
+name = "ndk-sys"
+version = "0.5.0+25.2.9519653"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "noop_proc_macro"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "number_prefix"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "oneshot"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4"
+dependencies = [
+ "loom",
+]
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "png"
+version = "0.17.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "pollster"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
+
+[[package]]
+name = "portable-atomic"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "presser"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
+
+[[package]]
+name = "primal-check"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0"
+dependencies = [
+ "num-integer",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
+dependencies = [
+ "profiling-procmacros",
+]
+
+[[package]]
+name = "profiling-procmacros"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
+dependencies = [
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "range-alloc"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
+
+[[package]]
+name = "rav1e"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
+dependencies = [
+ "arbitrary",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "av1-grain",
+ "bitstream-io",
+ "built",
+ "cfg-if",
+ "interpolate_name",
+ "itertools",
+ "libc",
+ "libfuzzer-sys",
+ "log",
+ "maybe-rayon",
+ "new_debug_unreachable",
+ "noop_proc_macro",
+ "num-derive",
+ "num-traits",
+ "once_cell",
+ "paste",
+ "profiling",
+ "rand",
+ "rand_chacha",
+ "simd_helpers",
+ "system-deps",
+ "thiserror",
+ "v_frame",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ravif"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234"
+dependencies = [
+ "avif-serialize",
+ "imgref",
+ "loop9",
+ "quick-error",
+ "rav1e",
+ "rayon",
+ "rgb",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cc3bcbdb1ddfc11e700e62968e6b4cc9c75bb466464ad28fb61c5b2c964418b"
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.6",
+ "regex-syntax 0.8.3",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.3",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "renderdoc-sys"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
+
+[[package]]
+name = "rgb"
+version = "0.8.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustdct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551"
+dependencies = [
+ "rustfft",
+]
+
+[[package]]
+name = "rustfft"
+version = "6.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43806561bc506d0c5d160643ad742e3161049ac01027b5e6d7524091fd401d86"
+dependencies = [
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "primal-check",
+ "strength_reduce",
+ "transpose",
+ "version_check",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.200"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.200"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "simd_helpers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
+dependencies = [
+ "quote",
+]
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spirv"
+version = "0.3.0+sdk-1.3.268.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strength_reduce"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "transpose"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e"
+dependencies = [
+ "num-integer",
+ "strength_reduce",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "v_frame"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b"
+dependencies = [
+ "aligned-vec",
+ "num-traits",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "vgcodec"
+version = "0.1.0"
+dependencies = [
+ "bytemuck",
+ "clap",
+ "env_logger",
+ "futures-intrusive",
+ "image",
+ "log",
+ "pollster",
+ "rand",
+ "wgpu",
+]
+
+[[package]]
+name = "virtue"
+version = "0.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
+
+[[package]]
+name = "wgpu"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32ff1bfee408e1028e2e3acbf6d32d98b08a5a059ccbf5f33305534453ba5d3e"
+dependencies = [
+ "arrayvec",
+ "cfg-if",
+ "cfg_aliases",
+ "document-features",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac6a86eaa5e763e59c73cf9e97d55fffd4dfda69fd8bda19589fcf851ddfef1f"
+dependencies = [
+ "arrayvec",
+ "bit-vec",
+ "bitflags 2.5.0",
+ "cfg_aliases",
+ "codespan-reporting",
+ "document-features",
+ "indexmap",
+ "log",
+ "naga",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "web-sys",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d71c8ae05170583049b65ee562fd839fdc0b3e9ddb84f4e40c9d5f8ea0d4c8c"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags 2.5.0",
+ "block",
+ "cfg_aliases",
+ "core-graphics-types",
+ "d3d12",
+ "glow",
+ "glutin_wgl_sys",
+ "gpu-alloc",
+ "gpu-allocator",
+ "gpu-descriptor",
+ "hassle-rs",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading 0.8.3",
+ "log",
+ "metal",
+ "naga",
+ "ndk-sys",
+ "objc",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef"
+dependencies = [
+ "bitflags 2.5.0",
+ "js-sys",
+ "web-sys",
+]
+
+[[package]]
+name = "widestring"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
+[[package]]
+name = "winnow"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "zune-core"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448"
+dependencies = [
+ "zune-core",
+]
diff --git a/old/Cargo.toml b/old/Cargo.toml
new file mode 100644
index 0000000..5312e98
--- /dev/null
+++ b/old/Cargo.toml
@@ -0,0 +1,13 @@
+[workspace]
+members = [
+ "dhwt-codec",
+ "vgcodec",
+ "difftree",
+ "evc",
+ "framework",
+ "flowy",
+ "bv1/app",
+ "bv1/codec",
+ "bv1/codec-web",
+]
+resolver = "2"
diff --git a/old/bv1/.gitignore b/old/bv1/.gitignore
new file mode 100644
index 0000000..2f22d72
--- /dev/null
+++ b/old/bv1/.gitignore
@@ -0,0 +1,2 @@
+/data
+/target
diff --git a/old/bv1/app/Cargo.toml b/old/bv1/app/Cargo.toml
new file mode 100644
index 0000000..89d4543
--- /dev/null
+++ b/old/bv1/app/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "app"
+version = "0.1.0"
+edition = "2021"
+default-run = "main"
+
+[dependencies]
+clap = { version = "4.5.4", features = ["derive"] }
+bv1 = { path = "../codec" }
diff --git a/old/bv1/app/src/bin/bench.rs b/old/bv1/app/src/bin/bench.rs
new file mode 100644
index 0000000..86edcac
--- /dev/null
+++ b/old/bv1/app/src/bin/bench.rs
@@ -0,0 +1,28 @@
+use bv1::{encode::diff, Frame, Ref, View, P2};
+use std::time::Instant;
+
+fn measure(f: impl FnOnce()) {
+ let t1 = Instant::now();
+ f();
+ let t2 = Instant::now();
+ eprintln!("took {:?}", (t2 - t1));
+}
+
+fn main() {
+ let size = P2 { x: 2000, y: 2000 };
+ let f1 = Frame::new(size);
+ let f2 = Frame::new(size);
+ measure(|| {
+ diff([&f1, &f2], View::all(size), Ref::default());
+ });
+}
+
+// #[test]
+// fn bench_fast_diff() {
+// let size = P2 { x: 2000, y: 2000 };
+// let f1 = Frame::new(size);
+// let f2 = Frame::new(size);
+// measure(|| {
+// diff_fast([&f1, &f2], View::all(size), Ref::default());
+// });
+// }
diff --git a/old/bv1/app/src/bin/main.rs b/old/bv1/app/src/bin/main.rs
new file mode 100644
index 0000000..1898bf1
--- /dev/null
+++ b/old/bv1/app/src/bin/main.rs
@@ -0,0 +1,71 @@
+use bv1::{
+ decode,
+ encode::{encode, EncodeConfig},
+ P2,
+};
+use clap::{Parser, Subcommand};
+use std::io::{stdin, stdout};
+
+#[derive(Parser)]
+#[clap(about, version)]
+struct Args {
+ // Width of the video signal
+ #[arg(short = 'W', long)]
+ width: u16,
+ // Height of the video signal
+ #[arg(short = 'H', long)]
+ height: u16,
+ #[clap(subcommand)]
+ action: Action,
+}
+
+#[derive(Clone, Subcommand)]
+enum Action {
+ // Compress video
+ Encode {
+ #[arg(short, long, default_value_t = 800)]
+ max_block_size: usize,
+ #[arg(short, long, default_value_t = 10_000)]
+ attention_split: u32,
+ #[arg(short, long, default_value_t = 10.)]
+ threshold: f32,
+ #[arg(short, long, default_value_t = 10)]
+ keyframe_interval: usize,
+ },
+ // Decompress video
+ Decode {
+ #[arg(short, long)]
+ debug: bool,
+ },
+}
+
+fn main() {
+ let args = Args::parse();
+
+ let size = P2 {
+ x: args.width as i32,
+ y: args.height as i32,
+ };
+ match args.action {
+ Action::Encode {
+ max_block_size,
+ threshold,
+ attention_split,
+ keyframe_interval,
+ } => {
+ let config = EncodeConfig {
+ min_block_size: 16,
+ motion_split_f: 2.,
+ threshold,
+ max_block_size,
+ attention_split,
+ keyframe_interval,
+ };
+
+ encode(config, size, stdin(), stdout()).unwrap();
+ }
+ Action::Decode { debug } => {
+ decode(size, debug, stdin(), stdout()).unwrap();
+ }
+ }
+}
diff --git a/old/bv1/app/src/bin/test.rs b/old/bv1/app/src/bin/test.rs
new file mode 100644
index 0000000..4071ff1
--- /dev/null
+++ b/old/bv1/app/src/bin/test.rs
@@ -0,0 +1,47 @@
+#[cfg(test)]
+mod test {
+
+ use bv1::huff::{read_huff, write_huff, BitIO};
+ use std::io::Cursor;
+
+ #[test]
+ fn test_bitio() {
+ let mut buf = Vec::<u8>::new();
+
+ {
+ let mut b = BitIO::new(Cursor::new(&mut buf));
+ b.wbit(true).unwrap();
+ b.wbit(true).unwrap();
+ b.wbit(true).unwrap();
+ b.wbit(true).unwrap();
+ b.wbit(false).unwrap();
+ b.wbit(true).unwrap();
+ b.wbit(true).unwrap();
+ b.wbit(true).unwrap();
+ b.wbit(true).unwrap();
+ b.wbyte(0xff).unwrap();
+ b.flush().unwrap();
+ }
+ {
+ let mut b = BitIO::new(Cursor::new(&mut buf));
+ for _ in 0..17 {
+ let _v = b.rbit().unwrap();
+ // eprintln!("{:?}", _v)
+ }
+ }
+ }
+
+ #[test]
+ fn test_huff() {
+ let a = vec![1, 2, 3, 4, 5, 1, 3, 6, 3, 2, 4, 6, 7, 4, 3, 2, 1, 3, 4];
+ let mut b = vec![];
+
+ let mut buf = Vec::<u8>::new();
+ write_huff(&a, &mut Cursor::new(&mut buf)).unwrap();
+ read_huff(&mut Cursor::new(&mut buf), &mut b).unwrap();
+
+ assert_eq!(a, b)
+ }
+}
+
+fn main() {}
diff --git a/old/bv1/codec-web/Cargo.toml b/old/bv1/codec-web/Cargo.toml
new file mode 100644
index 0000000..7c1892c
--- /dev/null
+++ b/old/bv1/codec-web/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "codec-web"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+bv1 = { path = "../codec" }
+wasm-bindgen = "0.2.92"
+js-sys = "0.3.69"
+console_error_panic_hook = "0.1.7"
diff --git a/old/bv1/codec-web/src/lib.rs b/old/bv1/codec-web/src/lib.rs
new file mode 100644
index 0000000..cb2ad3c
--- /dev/null
+++ b/old/bv1/codec-web/src/lib.rs
@@ -0,0 +1,60 @@
+use bv1::{Decoder, Frame, P2};
+use std::{collections::VecDeque, sync::RwLock};
+use wasm_bindgen::prelude::*;
+
+static DECODER: RwLock<Option<State>> = RwLock::new(None);
+
+struct State {
+ buffer: VecDeque<u8>,
+ frame: Frame,
+ decoder: Decoder,
+}
+
+// #[wasm_bindgen]
+// extern "C" {
+// #[wasm_bindgen(js_namespace = console)]
+// fn log(s: &str);
+// #[wasm_bindgen(js_namespace = console, js_name = "log")]
+// fn logs(s: String);
+// }
+
+#[wasm_bindgen(start)]
+fn panic_init() {
+ std::panic::set_hook(Box::new(console_error_panic_hook::hook));
+}
+
+#[wasm_bindgen]
+pub fn decode_init(width: i32, height: i32) {
+ let size = P2 {
+ x: width,
+ y: height,
+ };
+ *DECODER.write().unwrap() = Some(State {
+ frame: Frame::new(size),
+ decoder: Decoder::new(size),
+ buffer: VecDeque::new(),
+ });
+}
+
+#[wasm_bindgen]
+pub fn decode_frame(buf: &[u8], debug: bool) -> Vec<u8> {
+ let mut arr = Vec::new();
+ let mut g = DECODER.write().unwrap();
+ let state = g.as_mut().unwrap();
+ state.buffer.extend(buf.iter());
+
+ state
+ .decoder
+ .decode_frame(&mut state.buffer, &mut state.frame, debug)
+ .unwrap();
+
+ for y in 0..state.frame.size.y {
+ for x in 0..state.frame.size.x {
+ arr.push(state.frame[P2 { x, y }].r.clamp(0, 255) as u8);
+ arr.push(state.frame[P2 { x, y }].g.clamp(0, 255) as u8);
+ arr.push(state.frame[P2 { x, y }].b.clamp(0, 255) as u8);
+ }
+ }
+
+ arr
+}
diff --git a/old/bv1/codec-web/web/.gitignore b/old/bv1/codec-web/web/.gitignore
new file mode 100644
index 0000000..93d7118
--- /dev/null
+++ b/old/bv1/codec-web/web/.gitignore
@@ -0,0 +1,2 @@
+/codec_web*
+/bundle*
diff --git a/old/bv1/codec-web/web/index.html b/old/bv1/codec-web/web/index.html
new file mode 100644
index 0000000..b572671
--- /dev/null
+++ b/old/bv1/codec-web/web/index.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>bv1 web player</title>
+ <script defer async src="./bundle.js" type="module"></script>
+ </head>
+ <body>
+ <noscript>need js</noscript>
+ <p>loading…</p>
+ </body>
+</html>
diff --git a/old/bv1/codec-web/web/main.ts b/old/bv1/codec-web/web/main.ts
new file mode 100644
index 0000000..b1b1df7
--- /dev/null
+++ b/old/bv1/codec-web/web/main.ts
@@ -0,0 +1,69 @@
+/// <reference lib="dom" />
+
+import init, { decode_frame, decode_init } from "./codec_web.js"
+console.log("init wasm");
+await init()
+console.log("done");
+
+index("..")
+
+async function index(url: string) {
+ const res = await fetch(url)
+ if (!res.ok) throw new Error("not ok");
+ document.body.innerHTML = await res.text();
+ const h1 = document.createElement("h1")
+ h1.textContent = "bv1 web player"
+ document.body.prepend(h1)
+ document.body.querySelectorAll("a").forEach(e => {
+ const u = url + "/" + e.textContent
+ e.onclick = ev => {
+ ev.preventDefault()
+ if (!u.endsWith(".bv1")) return alert("thats not bv1!")
+ document.body.innerHTML = ""
+ play(u.toString())
+ }
+ })
+}
+
+async function play(url: string) {
+ decode_init(1920, 1080)
+ document.body.innerText = "downloading…"
+ const res = await fetch(url)
+ if (!res.ok) throw new Error("not ok");
+ let buf: Uint8Array | undefined = new Uint8Array(await res.arrayBuffer())
+ document.body.innerText = ""
+
+ const canvas = document.createElement("canvas")
+ canvas.width = 1920
+ canvas.height = 1080
+ document.body.append(canvas)
+ document.body.style.backgroundColor = "#111"
+ const ctx = canvas.getContext("2d")!
+ let debug = false;
+
+ document.body.addEventListener("keydown", ev => {
+ if (ev.code == "KeyD") debug = !debug;
+ })
+
+ setInterval(() => {
+ const frame = decode_frame(buf ?? new Uint8Array(), debug);
+ buf = undefined
+
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ const data = imageData.data;
+
+ for (let y = 0; y < 1080; y++) {
+ for (let x = 0; x < 1920; x++) {
+ const ti = (x + y * 1920) * 4;
+ const si = (x + y * 1920) * 3;
+ data[ti + 0] = frame[si + 0]
+ data[ti + 1] = frame[si + 1]
+ data[ti + 2] = frame[si + 2]
+ data[ti + 3] = 255
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+
+ }, 1000 / 30)
+}
diff --git a/old/bv1/codec/Cargo.toml b/old/bv1/codec/Cargo.toml
new file mode 100644
index 0000000..d4c9002
--- /dev/null
+++ b/old/bv1/codec/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "bv1"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+rayon = {version="1.10.0",optional = true}
+
+[features]
+default = ["parallel"]
+parallel = ["dep:rayon"]
+
diff --git a/old/bv1/codec/src/debug.rs b/old/bv1/codec/src/debug.rs
new file mode 100644
index 0000000..d6c37c2
--- /dev/null
+++ b/old/bv1/codec/src/debug.rs
@@ -0,0 +1,42 @@
+use crate::{split::split, Block, Frame, Pixel, View, P2};
+
+pub fn draw_debug(frame: &mut Frame, view: View, block: &Block) {
+ match block {
+ Block::Lit(_) => rect(frame, view, Pixel::GREEN),
+ Block::Split(a, b) => {
+ let [av, bv] = split(view);
+ draw_debug(frame, av, &a);
+ draw_debug(frame, bv, &b);
+ }
+ Block::Ref(r) => {
+ let v = View {
+ a: view.a,
+ b: view.a + P2 { x: 2, y: 2 },
+ };
+ if r.pos_off != P2::ZERO {
+ fill_rect(frame, v + P2 { x: 0, y: 0 }, Pixel::BLUE)
+ }
+ if r.color_off != Pixel::BLACK {
+ fill_rect(frame, v + P2 { x: 2, y: 0 }, Pixel::RED)
+ }
+ }
+ }
+}
+
+fn rect(frame: &mut Frame, view: View, color: Pixel) {
+ for x in view.a.x..view.b.x {
+ frame[P2 { x, y: view.a.y }] = color;
+ frame[P2 { x, y: view.b.y - 1 }] = color;
+ }
+ for y in view.a.y..view.b.y {
+ frame[P2 { y, x: view.a.x }] = color;
+ frame[P2 { y, x: view.b.x - 1 }] = color;
+ }
+}
+fn fill_rect(frame: &mut Frame, view: View, color: Pixel) {
+ for y in view.a.y..view.b.y {
+ for x in view.a.x..view.b.x {
+ frame[P2 { x, y }] = color;
+ }
+ }
+}
diff --git a/old/bv1/codec/src/decode.rs b/old/bv1/codec/src/decode.rs
new file mode 100644
index 0000000..aaebc6a
--- /dev/null
+++ b/old/bv1/codec/src/decode.rs
@@ -0,0 +1,75 @@
+use crate::{
+ debug::draw_debug, huff::read_huff, impls::join, split::split, Block, Frame, View, P2,
+};
+use std::io::{BufReader, BufWriter, Read, Result, Write};
+
+pub fn decode(size: P2, debug: bool, input: impl Read, output: impl Write) -> Result<()> {
+ let mut input = BufReader::new(input);
+ let mut output = BufWriter::new(output);
+ let mut d = Decoder::new(size);
+ let mut f = Frame::new(size);
+ loop {
+ d.decode_frame(&mut input, &mut f, debug)?;
+ Frame::write(&mut output, &f)?;
+ }
+}
+
+pub struct Decoder {
+ last_frame: Frame,
+ size: P2,
+}
+
+impl Decoder {
+ pub fn new(size: P2) -> Self {
+ Self {
+ size,
+ last_frame: Frame::new(size),
+ }
+ }
+ pub fn decode_frame(
+ &mut self,
+ mut input: impl Read,
+ output: &mut Frame,
+ debug: bool,
+ ) -> Result<()> {
+ let huff = true;
+ let b = if huff {
+ let mut buf = vec![];
+ read_huff(&mut input, &mut buf)?;
+ let mut buf = std::io::Cursor::new(&mut buf);
+ Block::read(&mut buf, View::all(self.size))?
+ } else {
+ Block::read(&mut input, View::all(self.size))?
+ };
+
+ decode_block(&self.last_frame, output, View::all(self.size), &b);
+ self.last_frame.pixels.copy_from_slice(&output.pixels); // TODO use mem::swap
+ if debug {
+ draw_debug(output, View::all(self.size), &b);
+ }
+ Ok(())
+ }
+}
+
+pub fn decode_block(last_frame: &Frame, frame: &mut Frame, view: View, block: &Block) {
+ match block {
+ Block::Lit(pxs) => frame.import(view, &pxs),
+ Block::Split(a, b) => {
+ let [av, bv] = split(view);
+ let (frame1, frame2) =
+ unsafe { (&mut *(frame as *mut Frame), &mut *(frame as *mut Frame)) };
+ join(
+ || decode_block(last_frame, frame1, av, &a),
+ || decode_block(last_frame, frame2, bv, &b),
+ );
+ }
+ Block::Ref(r) => {
+ for y in view.a.y..view.b.y {
+ for x in view.a.x..view.b.x {
+ let p = P2 { x, y };
+ frame[p] = last_frame[p + r.pos_off] + r.color_off
+ }
+ }
+ }
+ }
+}
diff --git a/old/bv1/codec/src/encode.rs b/old/bv1/codec/src/encode.rs
new file mode 100644
index 0000000..31e1066
--- /dev/null
+++ b/old/bv1/codec/src/encode.rs
@@ -0,0 +1,210 @@
+use crate::huff::write_huff;
+use crate::impls::join;
+use crate::split::split;
+use crate::{decode::decode_block, Block, Frame, Pixel, Ref, View, P2};
+use std::io::{BufReader, BufWriter, Read, Write};
+use std::time::Instant;
+
+#[derive(Debug, Clone)]
+pub struct EncodeConfig {
+ pub threshold: f32,
+ pub max_block_size: usize,
+ pub min_block_size: usize,
+ pub attention_split: u32,
+ pub motion_split_f: f32,
+ pub keyframe_interval: usize,
+}
+
+pub fn encode(
+ config: EncodeConfig,
+ size: P2,
+ input: impl Read,
+ output: impl Write,
+) -> std::io::Result<()> {
+ let mut input = BufReader::new(input);
+ let mut output = BufWriter::new(output);
+
+ let mut last_frame = Frame::new(size);
+ for frame_number in 0.. {
+ let mut frame = Frame::read(&mut input, size)?;
+
+ let mut config = config.clone();
+ if frame_number % config.keyframe_interval != 0 {
+ config.threshold = std::f32::INFINITY;
+ }
+
+ let t = Instant::now();
+ let b: Block = encode_block(&last_frame, &frame, View::all(size), &config);
+ let time_encode = t.elapsed();
+
+ let t = Instant::now();
+ decode_block(&last_frame, &mut frame, View::all(size), &b);
+ last_frame = frame;
+ let time_decode = t.elapsed();
+
+ if true {
+ let mut buf = vec![];
+ let mut bufw = std::io::Cursor::new(&mut buf);
+ b.write(&mut bufw)?;
+ drop(bufw);
+ let t = Instant::now();
+ let bits_raw = buf.len() * 8;
+ let bits_huff = write_huff(&buf, &mut output)?;
+ let time_huff = t.elapsed();
+ drop(buf);
+
+ eprintln!(
+ "frame {frame_number}: {:?}",
+ time_decode + time_huff + time_encode
+ );
+ eprintln!(
+ "\tencode {time_encode:?} ({:.2}%)",
+ (bits_raw as f32 / (size.area() * 24) as f32) * 100.0
+ );
+ eprintln!(
+ "\thuff {time_huff:?} ({:.2}%)",
+ (bits_huff as f32 / bits_raw as f32) * 100.0
+ );
+ eprintln!("\tdecode {time_decode:?}");
+ } else {
+ b.write(&mut output)?;
+ }
+ }
+ Ok(())
+}
+
+pub fn encode_block(last_frame: &Frame, frame: &Frame, view: View, config: &EncodeConfig) -> Block {
+ let view_area = view.size().area();
+ let thres = config.threshold * view_area as f32;
+
+ if view_area > config.max_block_size
+ || (view_area > config.min_block_size
+ && (total_contrast(frame, view) > config.attention_split
+ || diff([last_frame, frame], view, Ref::default())
+ > (thres * config.motion_split_f) as u32))
+ {
+ let [av, bv] = split(view);
+ let (ab, bb) = join(
+ || encode_block(last_frame, frame, av, config),
+ || encode_block(last_frame, frame, bv, config),
+ );
+ return Block::Split(Box::new(ab), Box::new(bb));
+ }
+
+ let mut r = Ref::default();
+ let mut d = diff([last_frame, frame], view, r);
+
+ let target_average = average_color(frame, view);
+
+ for granularity in [1, 2, 3, 5, 7, 5, 3, 2, 1] {
+ let (nd, nrp) = optimize_ref(last_frame, frame, view, r, granularity, target_average);
+ if nd < d {
+ r = nrp;
+ d = nd;
+ } else {
+ break;
+ }
+ }
+
+ if d < thres as u32 {
+ return Block::Ref(r);
+ } else {
+ Block::Lit(frame.export(view))
+ }
+}
+
+pub fn optimize_ref(
+ last_frame: &Frame,
+ frame: &Frame,
+ view: View,
+ r: Ref,
+ g: i32,
+ target_average: Pixel,
+) -> (u32, Ref) {
+ let g2 = g * 2;
+ [
+ Some(r.apply(|r| r.pos_off += P2 { x: g, y: 0 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: g, y: g })),
+ Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g })),
+ Some(r.apply(|r| r.pos_off += P2 { x: -g, y: g })),
+ Some(r.apply(|r| r.pos_off += P2 { x: -g, y: 0 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: -g, y: -g })),
+ Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g })),
+ Some(r.apply(|r| r.pos_off += P2 { x: g, y: -g })),
+ Some(r.apply(|r| r.pos_off += P2 { x: g2, y: 0 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: g2, y: g2 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g2 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: g2 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: 0 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: -g2 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g2 })),
+ Some(r.apply(|r| r.pos_off += P2 { x: g2, y: -g2 })),
+ {
+ let mut r = r;
+ let last_avr = average_color(last_frame, view);
+ let diff = target_average - last_avr;
+ r.color_off = diff;
+ if diff != Pixel::BLACK {
+ Some(r)
+ } else {
+ None
+ }
+ },
+ ]
+ .into_iter()
+ .flatten()
+ .map(|r| (diff([last_frame, frame], view, r), r))
+ .min_by_key(|e| e.0)
+ .unwrap()
+}
+
+pub fn total_contrast(frame: &Frame, view: View) -> u32 {
+ let mut k = 0;
+ for y in view.a.y..view.b.y - 1 {
+ for x in view.a.x..view.b.x - 1 {
+ let p = P2 { x, y };
+ k += pixel_diff(frame[p], frame[p + P2::X]).pow(2);
+ k += pixel_diff(frame[p], frame[p + P2::Y]).pow(2);
+ }
+ }
+ k
+}
+
+pub fn average_color(frame: &Frame, view: View) -> Pixel {
+ let mut r = 0u32;
+ let mut g = 0u32;
+ let mut b = 0u32;
+
+ for y in view.a.y..view.b.y {
+ for x in view.a.x..view.b.x {
+ let p = frame[P2 { x, y }];
+ r += p.r as u32;
+ g += p.g as u32;
+ b += p.b as u32;
+ }
+ }
+ let area = view.size().area() as u32;
+ Pixel {
+ r: (r / area) as i16,
+ g: (g / area) as i16,
+ b: (b / area) as i16,
+ }
+}
+
+pub fn diff([frame1, frame2]: [&Frame; 2], view: View, rp: Ref) -> u32 {
+ let mut k = 0;
+ for y in view.a.y..view.b.y {
+ for x in view.a.x..view.b.x {
+ let pos = P2 { x, y };
+ let p1 = frame1[pos + rp.pos_off] + rp.color_off;
+ let p2 = frame2[pos];
+ k += pixel_diff(p1, p2)
+ }
+ }
+ k
+}
+
+#[inline(always)]
+pub fn pixel_diff(p1: Pixel, p2: Pixel) -> u32 {
+ p1.r.abs_diff(p2.r) as u32 + p1.g.abs_diff(p2.g) as u32 + p1.b.abs_diff(p2.b) as u32
+}
diff --git a/old/bv1/codec/src/frameio.rs b/old/bv1/codec/src/frameio.rs
new file mode 100644
index 0000000..f6cbcbf
--- /dev/null
+++ b/old/bv1/codec/src/frameio.rs
@@ -0,0 +1,35 @@
+use crate::{Frame, Pixel, PixelValue, P2};
+use std::io::{Read, Result, Write};
+
+impl Frame {
+ pub fn read(inp: &mut impl Read, size: P2) -> Result<Frame> {
+ let mut f = Frame::new(size);
+
+ for y in 0..size.y {
+ for x in 0..size.x {
+ let mut cc = [0u8; 3];
+ inp.read_exact(&mut cc)?;
+ f[P2 { x, y }] = Pixel {
+ r: cc[0] as PixelValue,
+ g: cc[1] as PixelValue,
+ b: cc[2] as PixelValue,
+ };
+ }
+ }
+ Ok(f)
+ }
+
+ pub fn write(out: &mut impl Write, frame: &Frame) -> Result<()> {
+ for y in 0..frame.size.y {
+ for x in 0..frame.size.x {
+ let p = frame[P2 { x, y }];
+ let mut cc = [0u8; 3];
+ cc[0] = p.r.clamp(0, 255) as u8;
+ cc[1] = p.g.clamp(0, 255) as u8;
+ cc[2] = p.b.clamp(0, 255) as u8;
+ out.write_all(&mut cc)?;
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/old/bv1/codec/src/huff.rs b/old/bv1/codec/src/huff.rs
new file mode 100644
index 0000000..0dbb6ce
--- /dev/null
+++ b/old/bv1/codec/src/huff.rs
@@ -0,0 +1,192 @@
+use std::io::{Read, Result, Write};
+
+#[derive(Debug, Clone)]
+enum HT {
+ Branch(Box<HT>, Box<HT>),
+ Terminal(u8),
+}
+
+pub fn write_huff(buf: &[u8], w: &mut impl Write) -> Result<usize> {
+ let mut w = BitIO::new(w);
+ assert!(buf.len() <= 0xffffff, "huff frame too big");
+ w.wbyte((buf.len() & 0xff) as u8)?;
+ w.wbyte(((buf.len() >> 8) & 0xff) as u8)?;
+ w.wbyte(((buf.len() >> 16) & 0xff) as u8)?;
+
+ let mut probs = [0usize; 256];
+ for b in buf {
+ probs[*b as usize] += 1;
+ }
+ let tree = HT::from_probabilities(probs);
+ let mut table = [0u32; 256];
+ tree.create_lut(&mut table, 1);
+ tree.write(&mut w)?;
+
+ for b in buf {
+ let mut k = table[*b as usize];
+ while k != 1 {
+ w.wbit((k & 1) == 1)?;
+ k >>= 1;
+ }
+ }
+
+ w.flush()?;
+ Ok(w.position)
+}
+
+pub fn read_huff(r: &mut impl Read, buf: &mut Vec<u8>) -> Result<usize> {
+ let mut r = BitIO::new(r);
+
+ let mut len = 0usize;
+ len |= r.rbyte()? as usize;
+ len |= (r.rbyte()? as usize) << 8;
+ len |= (r.rbyte()? as usize) << 16;
+
+ let root = HT::read(&mut r)?;
+ let root = match &root {
+ HT::Branch(a, b) => [a, b],
+ _ => panic!("no!"),
+ };
+
+ let mut cursor = root;
+ while buf.len() != len {
+ let v = r.rbit()?;
+ match cursor[v as usize].as_ref() {
+ HT::Branch(a, b) => {
+ cursor = [a, b];
+ }
+ HT::Terminal(n) => {
+ buf.push(*n);
+ cursor = root;
+ }
+ }
+ }
+ Ok(r.position)
+}
+
+impl HT {
+ pub fn from_probabilities(ps: [usize; 256]) -> Self {
+ let mut parts = ps
+ .into_iter()
+ .enumerate()
+ .map(|(n, p)| (p, HT::Terminal(n as u8)))
+ .collect::<Vec<_>>();
+
+ while parts.len() != 1 {
+ parts.sort_by_key(|e| -(e.0 as isize));
+ let ((ap, at), (bp, bt)) = (parts.pop().unwrap(), parts.pop().unwrap());
+ parts.push((ap + bp + 1, HT::Branch(Box::new(at), Box::new(bt))))
+ }
+ parts[0].1.clone()
+ }
+ pub fn create_lut(&self, table: &mut [u32; 256], mut prefix: u32) {
+ assert!(self.depth() < 30, "too deep! doesnt fit {}", self.depth());
+ match self {
+ HT::Branch(a, b) => {
+ let pz = 32 - prefix.leading_zeros();
+ prefix ^= 1 << pz;
+ prefix ^= 1 << (pz - 1);
+ a.create_lut(table, prefix);
+ prefix ^= 1 << (pz - 1);
+ b.create_lut(table, prefix);
+ }
+ HT::Terminal(n) => {
+ assert_eq!(table[*n as usize], 0);
+ table[*n as usize] = prefix
+ }
+ }
+ }
+ pub fn depth(&self) -> usize {
+ match self {
+ HT::Branch(a, b) => a.depth().max(b.depth()) + 1,
+ HT::Terminal(_) => 0,
+ }
+ }
+ pub fn write(&self, w: &mut BitIO<impl Write>) -> Result<()> {
+ match self {
+ HT::Branch(a, b) => {
+ w.wbit(false)?;
+ a.write(w)?;
+ b.write(w)?;
+ }
+ HT::Terminal(n) => {
+ w.wbit(true)?;
+ w.wbyte(*n)?;
+ }
+ }
+ Ok(())
+ }
+ pub fn read(r: &mut BitIO<impl Read>) -> Result<Self> {
+ match r.rbit()? {
+ false => Ok(Self::Branch(
+ Box::new(Self::read(r)?),
+ Box::new(Self::read(r)?),
+ )),
+ true => Ok(Self::Terminal(r.rbyte()?)),
+ }
+ }
+}
+
+pub struct BitIO<T> {
+ inner: T,
+ byte: u8,
+ position: usize,
+}
+impl<T> BitIO<T> {
+ pub fn new(inner: T) -> Self {
+ Self {
+ inner,
+ byte: 0,
+ position: 0,
+ }
+ }
+}
+impl<T: Write> BitIO<T> {
+ #[inline]
+ pub fn wbit(&mut self, b: bool) -> Result<()> {
+ self.byte <<= 1;
+ self.byte |= b as u8;
+ self.position += 1;
+ if self.position & 0b111 == 0 {
+ self.inner.write_all(&[self.byte])?;
+ }
+ Ok(())
+ }
+ #[inline]
+ pub fn wbyte(&mut self, v: u8) -> Result<()> {
+ for i in 0..8 {
+ self.wbit((v & (1 << i)) != 0)?;
+ }
+ Ok(())
+ }
+ pub fn flush(&mut self) -> Result<()> {
+ while self.position & 0b111 != 0 {
+ self.wbit(false)?;
+ }
+ Ok(())
+ }
+}
+
+impl<T: Read> BitIO<T> {
+ #[inline]
+ pub fn rbit(&mut self) -> Result<bool> {
+ if self.position & 0b111 == 0 {
+ let mut buf = [0];
+ self.inner.read_exact(&mut buf)?;
+ self.byte = buf[0];
+ }
+
+ let v = (self.byte & 0b10000000) != 0;
+ self.byte <<= 1;
+ self.position += 1;
+ Ok(v)
+ }
+ #[inline]
+ pub fn rbyte(&mut self) -> Result<u8> {
+ let mut v = 0u8;
+ for i in 0..8 {
+ v |= (self.rbit()? as u8) << i;
+ }
+ Ok(v)
+ }
+}
diff --git a/old/bv1/codec/src/impls.rs b/old/bv1/codec/src/impls.rs
new file mode 100644
index 0000000..b4cc119
--- /dev/null
+++ b/old/bv1/codec/src/impls.rs
@@ -0,0 +1,165 @@
+use crate::{Frame, Pixel, Ref, View, P2};
+use std::ops::{Add, AddAssign, Index, IndexMut, Sub};
+
+#[cfg(feature = "parallel")]
+pub use rayon::join;
+#[cfg(not(feature = "parallel"))]
+pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
+where
+ A: FnOnce() -> RA + Send,
+ B: FnOnce() -> RB + Send,
+ RA: Send,
+ RB: Send,
+{
+ (oper_a(), oper_b())
+}
+
+impl Frame {
+ pub fn export(&self, view: View) -> Vec<Pixel> {
+ let mut o = vec![];
+ for y in view.a.y..view.b.y {
+ for x in view.a.x..view.b.x {
+ o.push(self[P2 { x, y }])
+ }
+ }
+ o
+ }
+ pub fn import(&mut self, view: View, mut source: &[Pixel]) {
+ for y in view.a.y..view.b.y {
+ for x in view.a.x..view.b.x {
+ self[P2 { x, y }] = source[0];
+ source = &source[1..];
+ }
+ }
+ assert_eq!(source.len(), 0)
+ }
+ pub fn new(size: P2) -> Self {
+ Self {
+ pixels: vec![Pixel::default(); size.area()],
+ size,
+ }
+ }
+}
+impl Ref {
+ #[inline]
+ pub fn apply<F: Fn(&mut Self)>(mut self, f: F) -> Self {
+ f(&mut self);
+ self
+ }
+}
+impl Pixel {
+ pub const BLACK: Pixel = Pixel { r: 0, g: 0, b: 0 };
+ pub const RED: Pixel = Pixel { r: 255, g: 0, b: 0 };
+ pub const GREEN: Pixel = Pixel { r: 0, g: 255, b: 0 };
+ pub const BLUE: Pixel = Pixel { r: 0, g: 0, b: 255 };
+}
+impl AddAssign for P2 {
+ fn add_assign(&mut self, rhs: Self) {
+ self.x += rhs.x;
+ self.y += rhs.y;
+ }
+}
+impl Add for Pixel {
+ type Output = Pixel;
+ #[inline]
+ fn add(self, rhs: Self) -> Self::Output {
+ Self {
+ r: self.r + rhs.r,
+ g: self.g + rhs.g,
+ b: self.b + rhs.b,
+ }
+ }
+}
+impl Sub for Pixel {
+ type Output = Pixel;
+ #[inline]
+ fn sub(self, rhs: Self) -> Self::Output {
+ Self {
+ r: self.r - rhs.r,
+ g: self.g - rhs.g,
+ b: self.b - rhs.b,
+ }
+ }
+}
+impl P2 {
+ pub const ZERO: P2 = P2 { x: 0, y: 0 };
+ pub const X: P2 = P2 { x: 1, y: 0 };
+ pub const Y: P2 = P2 { x: 0, y: 1 };
+
+ #[inline]
+ pub fn area(&self) -> usize {
+ (self.x * self.y) as usize
+ }
+}
+impl View {
+ #[inline]
+ pub fn all(b: P2) -> Self {
+ Self {
+ a: P2::default(),
+ b,
+ }
+ }
+ #[inline]
+ pub fn size(&self) -> P2 {
+ self.b - self.a
+ }
+}
+impl Add<P2> for View {
+ type Output = View;
+ #[inline]
+ fn add(self, rhs: P2) -> Self::Output {
+ View {
+ a: self.a + rhs,
+ b: self.b + rhs,
+ }
+ }
+}
+impl Add for P2 {
+ type Output = P2;
+ #[inline]
+ fn add(self, rhs: Self) -> Self::Output {
+ Self {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ }
+ }
+}
+impl Sub for P2 {
+ type Output = P2;
+ #[inline]
+ fn sub(self, rhs: Self) -> Self::Output {
+ Self {
+ x: self.x - rhs.x,
+ y: self.y - rhs.y,
+ }
+ }
+}
+
+impl Index<P2> for Frame {
+ type Output = Pixel;
+ #[inline]
+ fn index(&self, P2 { x, y }: P2) -> &Self::Output {
+ &self
+ .pixels
+ .get(x as usize + (y as usize * self.size.x as usize))
+ .unwrap_or(&Pixel { r: 0, g: 0, b: 0 })
+ }
+}
+impl IndexMut<P2> for Frame {
+ #[inline]
+ fn index_mut(&mut self, P2 { x, y }: P2) -> &mut Self::Output {
+ &mut self.pixels[x as usize + (y as usize * self.size.x as usize)]
+ }
+}
+
+pub trait ToArray {
+ type Output;
+ fn to_array(self) -> [Self::Output; 2];
+}
+impl<A> ToArray for (A, A) {
+ type Output = A;
+ #[inline]
+ fn to_array(self) -> [A; 2] {
+ [self.0, self.1]
+ }
+}
diff --git a/old/bv1/codec/src/lib.rs b/old/bv1/codec/src/lib.rs
new file mode 100644
index 0000000..7041950
--- /dev/null
+++ b/old/bv1/codec/src/lib.rs
@@ -0,0 +1,52 @@
+#![feature(portable_simd)]
+
+pub mod debug;
+pub mod decode;
+pub mod encode;
+pub mod frameio;
+pub mod huff;
+pub mod impls;
+pub mod serialize;
+pub mod split;
+
+pub type PixelValue = i16;
+
+pub use decode::{decode, Decoder};
+pub use encode::encode;
+
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
+pub struct Pixel {
+ pub r: PixelValue,
+ pub g: PixelValue,
+ pub b: PixelValue,
+}
+
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
+pub struct P2 {
+ pub x: i32,
+ pub y: i32,
+}
+
+pub struct Frame {
+ pub size: P2,
+ pub pixels: Vec<Pixel>,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct View {
+ pub a: P2,
+ pub b: P2,
+}
+
+#[derive(Debug, Clone)]
+pub enum Block {
+ Split(Box<Block>, Box<Block>),
+ Lit(Vec<Pixel>),
+ Ref(Ref),
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+pub struct Ref {
+ pub pos_off: P2,
+ pub color_off: Pixel,
+}
diff --git a/old/bv1/codec/src/serialize.rs b/old/bv1/codec/src/serialize.rs
new file mode 100644
index 0000000..86b2919
--- /dev/null
+++ b/old/bv1/codec/src/serialize.rs
@@ -0,0 +1,116 @@
+use crate::{split::split, Block, Pixel, Ref, View, P2};
+use std::io::{Read, Result, Write};
+
+impl Pixel {
+ #[inline]
+ pub fn write(&self, w: &mut impl Write) -> Result<()> {
+ w.write_all(&[
+ self.r.clamp(0, 255) as u8,
+ self.g.clamp(0, 255) as u8,
+ self.b.clamp(0, 255) as u8,
+ ])
+ }
+ #[inline]
+ pub fn read(r: &mut impl Read) -> Result<Pixel> {
+ Ok(Self {
+ r: read_byte(r)? as i16,
+ g: read_byte(r)? as i16,
+ b: read_byte(r)? as i16,
+ })
+ }
+}
+impl Pixel {
+ #[inline]
+ pub fn write_full(&self, w: &mut impl Write) -> Result<()> {
+ write_word(w, self.r)?;
+ write_word(w, self.g)?;
+ write_word(w, self.b)?;
+ Ok(())
+ }
+ #[inline]
+ pub fn read_full(r: &mut impl Read) -> Result<Pixel> {
+ Ok(Self {
+ r: read_word(r)?,
+ g: read_word(r)?,
+ b: read_word(r)?,
+ })
+ }
+}
+impl P2 {
+ #[inline]
+ pub fn write(&self, w: &mut impl Write) -> Result<()> {
+ write_word(w, self.x as i16)?;
+ write_word(w, self.y as i16)?;
+ Ok(())
+ }
+
+ #[inline]
+ pub fn read(r: &mut impl Read) -> Result<P2> {
+ Ok(Self {
+ x: read_word(r)? as i32,
+ y: read_word(r)? as i32,
+ })
+ }
+}
+
+impl Block {
+ pub fn write(&self, w: &mut impl Write) -> Result<()> {
+ match self {
+ Block::Split(a, b) => {
+ w.write_all(&[0])?;
+ a.write(w)?;
+ b.write(w)?;
+ }
+ Block::Lit(pixels) => {
+ w.write_all(&[1])?;
+ for p in pixels {
+ p.write(w)?;
+ }
+ }
+ Block::Ref(k) => {
+ w.write_all(&[2])?;
+ k.pos_off.write(w)?;
+ k.color_off.write_full(w)?;
+ }
+ }
+ Ok(())
+ }
+ pub fn read(r: &mut impl Read, view: View) -> Result<Block> {
+ match read_byte(r)? {
+ 0 => {
+ let [av, bv] = split(view);
+ Ok(Block::Split(
+ Box::new(Block::read(r, av)?),
+ Box::new(Block::read(r, bv)?),
+ ))
+ }
+ 1 => {
+ let mut px = vec![];
+ for _ in 0..view.size().area() {
+ px.push(Pixel::read(r)?)
+ }
+ Ok(Block::Lit(px))
+ }
+ 2 => Ok(Block::Ref(Ref {
+ pos_off: P2::read(r)?,
+ color_off: Pixel::read_full(r)?,
+ })),
+ _ => Err(std::io::Error::other("unknown block variant")),
+ }
+ }
+}
+
+#[inline]
+fn read_byte(r: &mut impl Read) -> Result<u8> {
+ let mut buf = [0u8];
+ r.read_exact(&mut buf)?;
+ Ok(buf[0])
+}
+#[inline]
+fn write_word(w: &mut impl Write, v: i16) -> Result<()> {
+ w.write_all(&[(v & 0xff) as u8, (v >> 8) as u8])
+}
+#[inline]
+fn read_word(r: &mut impl Read) -> Result<i16> {
+ Ok((read_byte(r)? as u16 | ((read_byte(r)? as u16) << 8)) as i16)
+}
diff --git a/old/bv1/codec/src/split.rs b/old/bv1/codec/src/split.rs
new file mode 100644
index 0000000..c17179e
--- /dev/null
+++ b/old/bv1/codec/src/split.rs
@@ -0,0 +1,42 @@
+use crate::{View, P2};
+
+pub fn split(view: View) -> [View; 2] {
+ let s = view.size();
+ if s.x > s.y {
+ let mid_x = (view.a.x + view.b.x) / 2;
+ [
+ View {
+ a: view.a,
+ b: P2 {
+ x: mid_x,
+ y: view.b.y,
+ },
+ },
+ View {
+ a: P2 {
+ x: mid_x,
+ y: view.a.y,
+ },
+ b: view.b,
+ },
+ ]
+ } else {
+ let mid_y = (view.a.y + view.b.y) / 2;
+ [
+ View {
+ a: view.a,
+ b: P2 {
+ x: view.b.x,
+ y: mid_y,
+ },
+ },
+ View {
+ a: P2 {
+ x: view.a.x,
+ y: mid_y,
+ },
+ b: view.b,
+ },
+ ]
+ }
+}
diff --git a/old/bv1/spec.md b/old/bv1/spec.md
new file mode 100644
index 0000000..ca30a39
--- /dev/null
+++ b/old/bv1/spec.md
@@ -0,0 +1,45 @@
+# BOMedia Video 1
+
+The codec compresses image sequences by avoiding redundancies by reusing parts
+of previous frames. A frame is one block that is then subdivided further as
+needed.
+
+## Literal-Block
+
+Stores the pixels' RGB values as is.
+
+## Split-Block
+
+Delegates to two sub-blocks. The block is split orthorgonally on the longest
+axis. If needed, the left/top block is rounded down and the right/bottom rounded
+up.
+
+## Reference-Block
+
+Indicates that parts of the last frame are reused. The data reused is at the
+position of this block in the last frame with the translation added.
+
+## File format
+
+- magic bytes: `5e b1 c3 08`
+- resolution: _`u16, u16`_
+- frame count: _`u64`_
+- frames (repeated [frame count]-times): Frame (see below)
+
+## Frame format
+
+- block kind: _`u8` (see tags below)_
+- block: _one of the following_
+ - 0 **Split-Block**
+ - sub-blocks: _`[block; 2]` (see above)_
+ - 1 **Literal-Block**
+ - pixels: _`[[u8; 3]; (inferred)]`_ (stored line-by-line)
+ - 2 **Reference-Block**
+ - position_offset: _`[i8; 2]`_
+ - color_offset: _`[i16; 3]`_
+
+### Data Types
+
+- _`u<n>`_: unsigned n-bit integer (little-endian)
+- _`i<n>`_: signed n-bit integer using twos-complement (little-endian)
+- _`[<T>; <N>]`_: Array of T with length N
diff --git a/old/bv1/tools/decode-display b/old/bv1/tools/decode-display
new file mode 100755
index 0000000..d7b8feb
--- /dev/null
+++ b/old/bv1/tools/decode-display
@@ -0,0 +1,2 @@
+#!/bin/fish
+cargo run --release -- -W 1920 -H 1080 decode | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 \ No newline at end of file
diff --git a/old/bv1/tools/decode-display-debug b/old/bv1/tools/decode-display-debug
new file mode 100755
index 0000000..97a2b90
--- /dev/null
+++ b/old/bv1/tools/decode-display-debug
@@ -0,0 +1,2 @@
+#!/bin/fish
+cargo run --release -- -W 1920 -H 1080 decode --debug | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 \ No newline at end of file
diff --git a/old/bv1/tools/decode-save b/old/bv1/tools/decode-save
new file mode 100755
index 0000000..1f19994
--- /dev/null
+++ b/old/bv1/tools/decode-save
@@ -0,0 +1,2 @@
+#!/bin/fish
+cargo run --release -- -W 1920 -H 1080 decode | ffmpeg -pixel_format rgb24 -video_size 1920x1080 -framerate 30 -f rawvideo -i pipe:0 -i $argv[1] -map '0:v' -map '1:a' -c:v libsvtav1 -y data/output.webm
diff --git a/old/bv1/tools/decode-save-debug b/old/bv1/tools/decode-save-debug
new file mode 100755
index 0000000..16de45a
--- /dev/null
+++ b/old/bv1/tools/decode-save-debug
@@ -0,0 +1,2 @@
+#!/bin/fish
+cargo run --release -- -W 1920 -H 1080 decode --debug | ffmpeg -pixel_format rgb24 -video_size 1920x1080 -framerate 30 -f rawvideo -i pipe:0 -i $argv[1] -map '0:v' -map '1:a' -c:v libsvtav1 -y data/output-debug.webm
diff --git a/old/bv1/tools/encode b/old/bv1/tools/encode
new file mode 100755
index 0000000..abab962
--- /dev/null
+++ b/old/bv1/tools/encode
@@ -0,0 +1,2 @@
+#!/bin/fish
+ffmpeg -loglevel quiet -i $argv[1] -vf scale=1920x1080,format=rgb24 -f rawvideo pipe:1 | cargo run --release -- -W 1920 -H 1080 encode $argv[2..]
diff --git a/old/dhwt-codec/.gitignore b/old/dhwt-codec/.gitignore
new file mode 100644
index 0000000..495c6ed
--- /dev/null
+++ b/old/dhwt-codec/.gitignore
@@ -0,0 +1,2 @@
+/target
+/a
diff --git a/old/dhwt-codec/Cargo.lock b/old/dhwt-codec/Cargo.lock
new file mode 100644
index 0000000..7e5de36
--- /dev/null
+++ b/old/dhwt-codec/Cargo.lock
@@ -0,0 +1,356 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anstream"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bincode"
+version = "2.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
+dependencies = [
+ "bincode_derive",
+ "serde",
+]
+
+[[package]]
+name = "bincode_derive"
+version = "2.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c"
+dependencies = [
+ "virtue",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "dhwt-codec"
+version = "0.1.0"
+dependencies = [
+ "bincode",
+ "clap",
+ "rayon",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rayon"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.190"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.190"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "virtue"
+version = "0.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/old/dhwt-codec/Cargo.toml b/old/dhwt-codec/Cargo.toml
new file mode 100644
index 0000000..5a3fe24
--- /dev/null
+++ b/old/dhwt-codec/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "dhwt-codec"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+bincode = "2.0.0-rc.3"
+clap = { version = "4.5.4", features = ["derive"] }
+rayon = "1.10.0"
diff --git a/old/dhwt-codec/run b/old/dhwt-codec/run
new file mode 100755
index 0000000..9f7013e
--- /dev/null
+++ b/old/dhwt-codec/run
@@ -0,0 +1,16 @@
+#!/bin/fish
+
+set f $argv[1]
+set x $argv[2]
+set y $argv[3]
+set z $argv[4]
+
+# cat /dev/urandom | cargo run --release --bin import -- -x $x -y $y -z $z a/inp
+ffmpeg -i $f -frames:v $z -filter_complex [0]scale={$x}x{$y},format=rgb24 -f rawvideo pipe:1 | cargo run --release --bin import -- -x $x -y $y -z $z a/inp
+
+time cargo run --release --bin encode -- -x $x -y $y -z $z a/inp a/enc
+time cargo run --release --bin decode -- -x $x -y $y -z $z a/enc a/out
+
+cargo run --release --bin export -- -x (math $x / 2) -y (math $y / 2) -z (math $z / 2) a/enc | ffmpeg -y -pixel_format rgb24 -f rawvideo -video_size (math $x / 2)x(math $y / 2) -i pipe:0 a/enc.webm
+cargo run --release --bin export -- -x $x -y $y -z $z a/out | ffmpeg -y -pixel_format rgb24 -f rawvideo -video_size {$x}x{$y} -i pipe:0 a/out.webm
+# cargo run --release --bin export -- -x $x -y $y -z $z a/raw_out | ffplay -pixel_format rgb24 -f rawvideo -video_size {$x}x{$y} pipe:0
diff --git a/old/dhwt-codec/src/bin/decode.rs b/old/dhwt-codec/src/bin/decode.rs
new file mode 100644
index 0000000..1930dfb
--- /dev/null
+++ b/old/dhwt-codec/src/bin/decode.rs
@@ -0,0 +1,57 @@
+use clap::Parser;
+use dhwt_codec::{
+ io::{empty_videobuf, infile, outfile, read_videobuf_small, write_videobuf, VideoBuf},
+ transform, trim,
+ view::{BufferView, IndexMode},
+ CommonArgs,
+};
+use rayon::prelude::{IntoParallelIterator, ParallelIterator};
+
+fn main() {
+ let args = CommonArgs::parse();
+
+ let mut inf = infile(&args.infile);
+ let mut of = outfile(&args.outfile);
+
+ for c in 0..args.channels {
+ eprintln!("encoding channel #{c}");
+ let a = empty_videobuf(args.x, args.y, args.z);
+ let b = read_videobuf_small(&mut inf);
+
+ eprintln!("\tdecoding Z");
+ (0..args.x).into_par_iter().for_each(|x| {
+ for y in 0..args.y {
+ run_mode(make_mut(&b), make_mut(&a), IndexMode::XY(x, y), args.z)
+ }
+ });
+ eprintln!("\tdecoding Y");
+ (0..args.x).into_par_iter().for_each(|x| {
+ for z in 0..args.z {
+ run_mode(make_mut(&a), make_mut(&b), IndexMode::XZ(x, z), args.y)
+ }
+ });
+ eprintln!("\tdecoding X");
+ (0..args.y).into_par_iter().for_each(|y| {
+ for z in 0..args.z {
+ run_mode(make_mut(&b), make_mut(&a), IndexMode::YZ(y, z), args.x)
+ }
+ });
+ write_videobuf(&mut of, a);
+ }
+}
+
+fn run_mode(a: &mut VideoBuf, b: &mut VideoBuf, mode: IndexMode, size: usize) {
+ trim::untrim(size, &mut BufferView::new(b, mode));
+ transform::decode(
+ size,
+ &mut BufferView::new(a, mode),
+ &mut BufferView::new(b, mode),
+ );
+}
+
+fn make_mut<T>(r: &T) -> &mut T {
+ #[allow(mutable_transmutes)]
+ unsafe {
+ std::mem::transmute::<&T, &mut T>(r)
+ }
+}
diff --git a/old/dhwt-codec/src/bin/encode.rs b/old/dhwt-codec/src/bin/encode.rs
new file mode 100644
index 0000000..db4c70e
--- /dev/null
+++ b/old/dhwt-codec/src/bin/encode.rs
@@ -0,0 +1,57 @@
+use clap::Parser;
+use dhwt_codec::{
+ io::{empty_videobuf, infile, outfile, read_videobuf, write_videobuf_small, VideoBuf},
+ transform, trim,
+ view::{BufferView, IndexMode},
+ CommonArgs,
+};
+use rayon::prelude::{IntoParallelIterator, ParallelIterator};
+
+fn main() {
+ let args = CommonArgs::parse();
+
+ let mut inf = infile(&args.infile);
+ let mut of = outfile(&args.outfile);
+
+ for c in 0..args.channels {
+ eprintln!("encoding channel #{c}");
+ let o = empty_videobuf(args.x, args.y, args.z);
+ let i = read_videobuf(&mut inf);
+
+ eprintln!("\tencoding X");
+ (0..args.y).into_par_iter().for_each(|y| {
+ for z in 0..args.z {
+ run_mode(make_mut(&i), make_mut(&o), IndexMode::YZ(y, z), args.x)
+ }
+ });
+ eprintln!("\tencoding Y");
+ (0..args.x).into_par_iter().for_each(|x| {
+ for z in 0..args.z {
+ run_mode(make_mut(&o), make_mut(&i), IndexMode::XZ(x, z), args.y)
+ }
+ });
+ eprintln!("\tencoding Z");
+ (0..args.x).into_par_iter().for_each(|x| {
+ for y in 0..args.y {
+ run_mode(make_mut(&i), make_mut(&o), IndexMode::XY(x, y), args.z)
+ }
+ });
+ write_videobuf_small(&mut of, o);
+ }
+}
+
+fn run_mode(a: &mut VideoBuf, b: &mut VideoBuf, mode: IndexMode, size: usize) {
+ transform::encode(
+ size,
+ &mut BufferView::new(a, mode),
+ &mut BufferView::new(b, mode),
+ );
+ trim::trim(size, &mut BufferView::new(b, mode));
+}
+
+fn make_mut<T>(r: &T) -> &mut T {
+ #[allow(mutable_transmutes)]
+ unsafe {
+ std::mem::transmute::<&T, &mut T>(r)
+ }
+}
diff --git a/old/dhwt-codec/src/bin/export.rs b/old/dhwt-codec/src/bin/export.rs
new file mode 100644
index 0000000..73f3067
--- /dev/null
+++ b/old/dhwt-codec/src/bin/export.rs
@@ -0,0 +1,42 @@
+use clap::Parser;
+use dhwt_codec::io::{infile, read_videobuf};
+use std::io::{stdout, BufWriter, Write};
+
+#[derive(Parser)]
+#[clap(about)]
+struct ExportArgs {
+ #[arg(short)]
+ x: usize,
+ #[arg(short)]
+ y: usize,
+ #[arg(short)]
+ z: usize,
+
+ #[arg(short, long, default_value = "3")]
+ channels: usize,
+
+ infile: String,
+}
+
+fn main() {
+ let args = ExportArgs::parse();
+
+ let mut i = infile(&args.infile);
+ let mut writer = BufWriter::new(stdout());
+
+ let mut channels = vec![];
+ for _ in 0..args.channels {
+ channels.push(read_videobuf(&mut i))
+ }
+
+ for z in 0..args.z {
+ for y in 0..args.y {
+ for x in 0..args.x {
+ for c in 0..args.channels {
+ writer.write_all(&[channels[c][x][y][z] as u8]).unwrap();
+ }
+ }
+ }
+ }
+ writer.flush().unwrap();
+}
diff --git a/old/dhwt-codec/src/bin/import.rs b/old/dhwt-codec/src/bin/import.rs
new file mode 100644
index 0000000..cd2a35e
--- /dev/null
+++ b/old/dhwt-codec/src/bin/import.rs
@@ -0,0 +1,51 @@
+use clap::Parser;
+use dhwt_codec::io::{outfile, write_videobuf, Value};
+use std::io::{stdin, Read};
+
+#[derive(Parser)]
+#[clap(about)]
+struct ImportArgs {
+ #[arg(short)]
+ x: usize,
+ #[arg(short)]
+ y: usize,
+ #[arg(short)]
+ z: usize,
+
+ #[arg(short, long, default_value = "3")]
+ channels: usize,
+
+ outfile: String,
+}
+
+fn main() {
+ let args = ImportArgs::parse();
+
+ let mut rawbuf = (0..(args.x * args.y * args.z * args.channels))
+ .map(|_| 0u8)
+ .collect::<Vec<_>>();
+ stdin().read_exact(&mut rawbuf).unwrap();
+
+ let mut o = outfile(&args.outfile);
+
+ for c in 0..args.channels {
+ let mut cols = vec![];
+ for x in 0..args.x {
+ let mut col = vec![];
+ for y in 0..args.y {
+ let mut span = vec![];
+ for z in 0..args.z {
+ span.push(
+ rawbuf[c
+ + (x * args.channels)
+ + (y * args.channels * args.x)
+ + (z * args.channels * args.x * args.y)] as Value,
+ );
+ }
+ col.push(span);
+ }
+ cols.push(col)
+ }
+ write_videobuf(&mut o, cols)
+ }
+}
diff --git a/old/dhwt-codec/src/io.rs b/old/dhwt-codec/src/io.rs
new file mode 100644
index 0000000..c60bc79
--- /dev/null
+++ b/old/dhwt-codec/src/io.rs
@@ -0,0 +1,71 @@
+use bincode::config;
+use std::{
+ fs::File,
+ io::{BufReader, BufWriter, Read, Write},
+};
+
+pub type Value = f32;
+pub type VideoBuf = Vec<Vec<Vec<Value>>>;
+pub const ZERO: Value = 0 as Value;
+pub const TWO: Value = 2 as Value;
+
+pub fn empty_videobuf(x: usize, y: usize, z: usize) -> VideoBuf {
+ (0..x)
+ .map(|_| (0..y).map(|_| (0..z).map(|_| ZERO).collect()).collect())
+ .collect()
+}
+
+pub fn outfile(p: &str) -> impl Write {
+ BufWriter::new(File::create(p).unwrap())
+}
+pub fn infile(p: &str) -> impl Read {
+ BufReader::new(File::open(p).unwrap())
+}
+
+pub fn read_videobuf(f: &mut impl Read) -> VideoBuf {
+ bincode::decode_from_std_read(f, config::standard()).unwrap()
+}
+pub fn write_videobuf(f: &mut impl Write, i: VideoBuf) {
+ bincode::encode_into_std_write(i, f, config::standard()).unwrap();
+}
+
+pub fn write_videobuf_small(f: &mut impl Write, mut i: VideoBuf) {
+ for _ in 0..(i.len() / 2) {
+ i.pop();
+ }
+ for i in &mut i {
+ for _ in 0..(i.len() / 2) {
+ i.pop();
+ }
+ for i in i {
+ for _ in 0..(i.len() / 2) {
+ i.pop();
+ }
+ }
+ }
+ write_videobuf(f, i);
+}
+pub fn read_videobuf_small(f: &mut impl Read) -> VideoBuf {
+ let mut i = read_videobuf(f);
+
+ for i in &mut i {
+ for i in i {
+ for _ in 0..i.len() {
+ i.push(ZERO);
+ }
+ }
+ }
+ for i in &mut i {
+ for _ in 0..i.len() {
+ i.push((0..i[0].len()).map(|_| ZERO).collect());
+ }
+ }
+ for _ in 0..i.len() {
+ i.push(
+ (0..i[0].len())
+ .map(|_| ((0..i[0][0].len()).map(|_| ZERO)).collect())
+ .collect(),
+ );
+ }
+ i
+}
diff --git a/old/dhwt-codec/src/lib.rs b/old/dhwt-codec/src/lib.rs
new file mode 100644
index 0000000..69d6b4c
--- /dev/null
+++ b/old/dhwt-codec/src/lib.rs
@@ -0,0 +1,23 @@
+use clap::Parser;
+
+pub mod io;
+pub mod transform;
+pub mod trim;
+pub mod view;
+
+#[derive(Parser)]
+#[clap(about)]
+pub struct CommonArgs {
+ #[arg(short)]
+ pub x: usize,
+ #[arg(short)]
+ pub y: usize,
+ #[arg(short)]
+ pub z: usize,
+
+ #[arg(short, long, default_value = "3")]
+ pub channels: usize,
+
+ pub infile: String,
+ pub outfile: String,
+}
diff --git a/old/dhwt-codec/src/transform.rs b/old/dhwt-codec/src/transform.rs
new file mode 100644
index 0000000..82bccd1
--- /dev/null
+++ b/old/dhwt-codec/src/transform.rs
@@ -0,0 +1,42 @@
+use crate::io::{Value, TWO};
+use std::ops::{Index, IndexMut};
+
+pub fn encode<X: Index<usize, Output = Value> + IndexMut<usize, Output = Value>>(
+ size: usize,
+ a: &mut X,
+ b: &mut X,
+) {
+ let mut k = size;
+ while k != 1 {
+ k /= 2;
+ for i in 0..k {
+ let x = a[i * 2];
+ let y = a[i * 2 + 1];
+ b[i] = x + y;
+ b[k + i] = x - y;
+ }
+ for i in 0..k {
+ a[i] = b[i]
+ }
+ }
+}
+
+pub fn decode<X: Index<usize, Output = Value> + IndexMut<usize, Output = Value>>(
+ size: usize,
+ a: &mut X,
+ b: &mut X,
+) {
+ let mut k = 1;
+ while k != size {
+ for i in 0..k {
+ let avr = a[i] / TWO;
+ let spread = a[i + k] / TWO;
+ b[i * 2] = avr + spread;
+ b[i * 2 + 1] = avr - spread;
+ }
+ k *= 2;
+ for i in 0..k {
+ a[i] = b[i]
+ }
+ }
+}
diff --git a/old/dhwt-codec/src/trim.rs b/old/dhwt-codec/src/trim.rs
new file mode 100644
index 0000000..85a920a
--- /dev/null
+++ b/old/dhwt-codec/src/trim.rs
@@ -0,0 +1,36 @@
+use crate::io::{Value, TWO, ZERO};
+use std::ops::{Index, IndexMut};
+
+pub fn trim<X: Index<usize, Output = Value> + IndexMut<usize, Output = Value>>(
+ size: usize,
+ a: &mut X,
+) {
+ let half = size / 2;
+ let quarter = size / 4;
+ for i in 0..(size / 2 / 4) {
+ let hi = half + i * 4;
+ let qi = quarter + i * 2;
+ a[qi] = (a[qi + 0] + a[qi + 1]) / TWO;
+ a[qi + 1] = (a[hi + 0] + a[hi + 1] + a[hi + 2] + a[hi + 3]) / (TWO * TWO);
+ }
+ for i in half..size {
+ a[i] = ZERO;
+ }
+}
+
+pub fn untrim<X: Index<usize, Output = Value> + IndexMut<usize, Output = Value>>(
+ size: usize,
+ a: &mut X,
+) {
+ let half = size / 2;
+ let quarter = size / 4;
+ for i in 0..(size / 2 / 4) {
+ let hi = half + i * 4;
+ let qi = quarter + i * 2;
+ a[hi + 0] = a[qi + 1];
+ a[hi + 1] = a[qi + 1];
+ a[hi + 2] = a[qi + 1];
+ a[hi + 3] = a[qi + 1];
+ a[qi + 1] = a[qi];
+ }
+}
diff --git a/old/dhwt-codec/src/view.rs b/old/dhwt-codec/src/view.rs
new file mode 100644
index 0000000..0f2ecf7
--- /dev/null
+++ b/old/dhwt-codec/src/view.rs
@@ -0,0 +1,42 @@
+use std::ops::{Index, IndexMut};
+
+use crate::io::{VideoBuf, Value};
+
+#[derive(Copy, Clone, Debug)]
+pub enum IndexMode {
+ XY(usize, usize),
+ XZ(usize, usize),
+ YZ(usize, usize),
+}
+
+pub struct BufferView<'a> {
+ mode: IndexMode,
+ buf: &'a mut VideoBuf,
+}
+
+impl<'a> BufferView<'a> {
+ pub fn new(buf: &'a mut VideoBuf, mode: IndexMode) -> Self {
+ BufferView { mode, buf }
+ }
+}
+
+impl Index<usize> for BufferView<'_> {
+ type Output = Value;
+
+ fn index(&self, a: usize) -> &Self::Output {
+ match self.mode {
+ IndexMode::XY(x, y) => &self.buf[x][y][a],
+ IndexMode::XZ(x, z) => &self.buf[x][a][z],
+ IndexMode::YZ(y, z) => &self.buf[a][y][z],
+ }
+ }
+}
+impl IndexMut<usize> for BufferView<'_> {
+ fn index_mut(&mut self, a: usize) -> &mut Self::Output {
+ match self.mode {
+ IndexMode::XY(x, y) => &mut self.buf[x][y][a],
+ IndexMode::XZ(x, z) => &mut self.buf[x][a][z],
+ IndexMode::YZ(y, z) => &mut self.buf[a][y][z],
+ }
+ }
+}
diff --git a/old/difftree/Cargo.lock b/old/difftree/Cargo.lock
new file mode 100644
index 0000000..6093019
--- /dev/null
+++ b/old/difftree/Cargo.lock
@@ -0,0 +1,156 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rayon"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "tweak"
+version = "0.1.0"
+dependencies = [
+ "rand",
+ "rayon",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
diff --git a/old/difftree/Cargo.toml b/old/difftree/Cargo.toml
new file mode 100644
index 0000000..e6b1ff3
--- /dev/null
+++ b/old/difftree/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "difftree"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+rayon = "1.10.0"
+rand = "0.8.5"
+
+framework = { path = "../framework" }
diff --git a/old/difftree/src/main.rs b/old/difftree/src/main.rs
new file mode 100644
index 0000000..9b6d35f
--- /dev/null
+++ b/old/difftree/src/main.rs
@@ -0,0 +1,206 @@
+use framework::{
+ common::huffman::encode_huff,
+ vector::{UVec2, Vec2},
+ Frame, Framework, Pixel,
+};
+use rayon::join;
+
+fn main() {
+ let (mut framework, params) = Framework::init();
+ let root = Area {
+ x1: 0,
+ y1: 0,
+ x2: params.width,
+ y2: params.height,
+ };
+
+ match params.mode {
+ framework::CodecMode::Encode => {
+ let mut oframe = Frame::new(params.width, params.height);
+ let mut dframe = Frame::new(params.width, params.height);
+ let mut out = Vec::<u8>::new();
+ while let Some(iframe) = framework.next_frame() {
+ let t = encode(&oframe, &iframe, root);
+ decode(&mut oframe, root, &t);
+ write(&mut out, &t);
+
+ if params.debug == 1 {
+ dframe.pixels.copy_from_slice(&oframe.pixels);
+ debug(&mut dframe, root, &t);
+ framework.decode_done(&dframe);
+ } else if params.debug == 2 {
+ debug_diff(&mut dframe, root, &t);
+ framework.decode_done(&dframe);
+ } else {
+ framework.decode_done(&oframe)
+ }
+
+ let huff = encode_huff(&out);
+ framework.encode_done(&huff);
+ out.clear();
+ }
+ }
+ framework::CodecMode::Decode => todo!(),
+ }
+}
+
+pub fn write(w: &mut Vec<u8>, t: &DiffTree) {
+ match t {
+ DiffTree::Split([a, b]) => {
+ w.push(0);
+ write(w, a);
+ write(w, b);
+ }
+ DiffTree::Diff(d) => w.extend([1, d.r as u8, d.g as u8, d.b as u8]),
+ }
+}
+
+pub fn decode(f: &mut Frame, area: Area, tree: &DiffTree) {
+ match tree {
+ DiffTree::Split([ta, tb]) => {
+ let (aa, ab) = area.split();
+ decode(f, aa, ta);
+ decode(f, ab, tb);
+ }
+ DiffTree::Diff(diff) => {
+ apply_area_diff(f, area, *diff);
+ }
+ }
+}
+pub fn debug(f: &mut Frame, area: Area, tree: &DiffTree) {
+ match tree {
+ DiffTree::Split([ta, tb]) => {
+ let (aa, ab) = area.split();
+ debug(f, aa, ta);
+ debug(f, ab, tb);
+ }
+ DiffTree::Diff(_diff) => {
+ let Area { x1, y1, x2, y2 } = area;
+ for x in x1..x2 {
+ for y in y1..y2 {
+ f[(x, y)] = Pixel { r: 0, g: 0, b: 255 }
+ }
+ }
+ }
+ }
+}
+pub fn debug_diff(f: &mut Frame, area: Area, tree: &DiffTree) {
+ match tree {
+ DiffTree::Split([ta, tb]) => {
+ let (aa, ab) = area.split();
+ debug_diff(f, aa, ta);
+ debug_diff(f, ab, tb);
+ }
+ DiffTree::Diff(diff) => {
+ let Area { x1, y1, x2, y2 } = area;
+ for x in x1..x2 {
+ for y in y1..y2 {
+ f[(x, y)] = Pixel {
+ r: 127u8.saturating_add_signed(diff.r),
+ b: 127u8.saturating_add_signed(diff.b),
+ g: 127u8.saturating_add_signed(diff.g),
+ }
+ }
+ }
+ }
+ }
+}
+
+pub fn encode(a: &Frame, b: &Frame, area: Area) -> DiffTree {
+ if area.area() == 1 {
+ DiffTree::Diff(diff_pixel(
+ a,
+ b,
+ Vec2 {
+ x: area.x1,
+ y: area.y1,
+ },
+ ))
+ } else {
+ let (aa, ba) = area.split();
+ let (at, bt) = join(|| encode(a, b, aa), || encode(a, b, ba));
+ // let (at, bt) = (encode(a, b, aa), encode(a, b, ba));
+
+ match (&at, &bt) {
+ (DiffTree::Diff(ad), DiffTree::Diff(bd)) => {
+ let d_r = ad.r.abs_diff(bd.r);
+ let d_g = ad.g.abs_diff(bd.g);
+ let d_b = ad.b.abs_diff(bd.b);
+
+ let visdiff = (d_r as usize + d_g as usize + d_b as usize) * aa.area();
+ if visdiff < 100 {
+ return DiffTree::Diff(Pixel {
+ r: ((ad.r as i16 + bd.r as i16) / 2) as i8,
+ g: ((ad.g as i16 + bd.g as i16) / 2) as i8,
+ b: ((ad.b as i16 + bd.b as i16) / 2) as i8,
+ });
+ }
+ }
+ _ => (),
+ }
+ DiffTree::Split([Box::new(at), Box::new(bt)])
+ }
+}
+
+pub enum DiffTree {
+ Split([Box<DiffTree>; 2]),
+ Diff(Pixel<i8>),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Area {
+ x1: usize,
+ y1: usize,
+ x2: usize,
+ y2: usize,
+}
+
+#[inline]
+fn diff_clamp(x: u8, y: u8) -> i8 {
+ if y >= x {
+ (y - x).min(127) as i8
+ } else {
+ -((x - y).min(128) as i8)
+ }
+}
+pub fn diff_pixel(a: &Frame, b: &Frame, p: UVec2) -> Pixel<i8> {
+ let (ap, bp) = (a[p], b[p]);
+ Pixel {
+ r: diff_clamp(ap.r, bp.r),
+ g: diff_clamp(ap.g, bp.g),
+ b: diff_clamp(ap.b, bp.b),
+ }
+}
+
+pub fn apply_area_diff(frame: &mut Frame, Area { x1, y1, x2, y2 }: Area, diff: Pixel<i8>) {
+ for x in x1..x2 {
+ for y in y1..y2 {
+ let p = &mut frame[(x, y)];
+ p.r = p.r.saturating_add_signed(diff.r);
+ p.g = p.g.saturating_add_signed(diff.g);
+ p.b = p.b.saturating_add_signed(diff.b);
+ }
+ }
+}
+
+impl Area {
+ pub fn area(&self) -> usize {
+ self.width() as usize * self.height() as usize
+ }
+ pub fn width(&self) -> usize {
+ self.x2 - self.x1
+ }
+ pub fn height(&self) -> usize {
+ self.y2 - self.y1
+ }
+ pub fn split(&self) -> (Self, Self) {
+ let Area { x1, y1, x2, y2 } = *self;
+ if self.width() > self.height() {
+ let xm = (self.x1 + self.x2) / 2;
+ (Self { x1, x2: xm, y1, y2 }, Self { x1: xm, x2, y1, y2 })
+ } else {
+ let ym = (self.y1 + self.y2) / 2;
+ (Self { x1, x2, y1, y2: ym }, Self { x1, x2, y1: ym, y2 })
+ }
+ }
+}
diff --git a/old/evc/.gitignore b/old/evc/.gitignore
new file mode 100644
index 0000000..b88f3af
--- /dev/null
+++ b/old/evc/.gitignore
@@ -0,0 +1,6 @@
+/target
+/samples
+/reports
+/flamegraph.svg
+/perf.data*
+
diff --git a/old/evc/Cargo.lock b/old/evc/Cargo.lock
new file mode 100644
index 0000000..727d55e
--- /dev/null
+++ b/old/evc/Cargo.lock
@@ -0,0 +1,663 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
+dependencies = [
+ "bitflags 1.3.2",
+ "clap_derive",
+ "clap_lex",
+ "is-terminal",
+ "once_cell",
+ "strsim",
+ "termcolor",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "console"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9b6515d269224923b26b5febea2ed42b2d5f2ce37284a4dd670fedd6cb8347a"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "unicode-width",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
+[[package]]
+name = "env_logger"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "indicatif"
+version = "0.17.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19"
+dependencies = [
+ "console",
+ "number_prefix",
+ "portable-atomic",
+ "unicode-width",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+dependencies = [
+ "hermit-abi",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "libreschmux"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "env_logger",
+ "indicatif",
+ "log",
+ "proc-macro2",
+ "rayon",
+ "rustdct",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "number_prefix"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
+
+[[package]]
+name = "once_cell"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
+
+[[package]]
+name = "portable-atomic"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
+
+[[package]]
+name = "primal-check"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0"
+dependencies = [
+ "num-integer",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rayon"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "rustdct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551"
+dependencies = [
+ "rustfft",
+]
+
+[[package]]
+name = "rustfft"
+version = "6.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434"
+dependencies = [
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "primal-check",
+ "strength_reduce",
+ "transpose",
+ "version_check",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "strength_reduce"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "transpose"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6522d49d03727ffb138ae4cbc1283d3774f0d10aa7f9bf52e6784c45daf9b23"
+dependencies = [
+ "num-integer",
+ "strength_reduce",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/old/evc/Cargo.toml b/old/evc/Cargo.toml
new file mode 100644
index 0000000..84df2aa
--- /dev/null
+++ b/old/evc/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "libreschmux"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+clap = { version = "*", features = ["derive"] }
+anyhow = "1.0.82"
+log = "0.4.21"
+rayon = "1.10.0"
+env_logger = "0.11.3"
+rustdct = "0.7.1"
+indicatif = "*"
+proc-macro2 = "1.0.81"
+
+[[bin]]
+path = "src/bin/encode.rs"
+name = "reschmux-encode"
+
+[[bin]]
+path = "src/bin/decode.rs"
+name = "reschmux-decode"
+
+[[bin]]
+path = "src/bin/info.rs"
+name = "reschmux-info"
diff --git a/old/evc/scripts/bench_modes b/old/evc/scripts/bench_modes
new file mode 100755
index 0000000..2e66454
--- /dev/null
+++ b/old/evc/scripts/bench_modes
@@ -0,0 +1,22 @@
+#!/bin/fish
+set w $argv[1]
+set h $argv[2]
+set t $argv[3]
+
+ffmpeg -hide_banner -i $argv[4] -to {$t} -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 >samples/raw
+# ffmpeg -hide_banner -y -i $argv[4] -to {$t} -vf scale={$w}x{$h},fps=30,format=rgb24 -c:v vp9 samples/reference.webm
+
+echo
+echo "file: "$argv[4]
+echo "resolution: "{$w}x{$h}
+echo "frames: "(math $t \* 30)
+echo "reference (raw): "(du -h samples/raw | cut -f 1)
+echo "reference (input): "(du -h $argv[4] | cut -f 1)
+# echo "reference (vp8): "(du -h samples/reference.webm | cut -f 1)
+# for mode in trivial simple-exhaustive simple-fast advanced advanced-partial
+for mode in trivial simple-fast advanced
+ echo -----------
+ echo "mode: $mode"
+ echo "time: $(command time -f %U ./target/release/reschmux-encode -W {$w} -H {$h} --mode $mode $argv[5..] <samples/raw >samples/encoded-$mode 2>| tail -n 1)s"
+ echo "size: $(du -h samples/encoded-$mode | cut -f 1)"
+end
diff --git a/old/evc/scripts/bench_out b/old/evc/scripts/bench_out
new file mode 100755
index 0000000..d55cf08
--- /dev/null
+++ b/old/evc/scripts/bench_out
@@ -0,0 +1,11 @@
+#!/bin/fish
+set w $argv[1]
+set h $argv[2]
+
+# for mode in trivial simple-exhaustive simple-fast advanced advanced-partial
+for mode in trivial simple-fast advanced
+ cargo run --release --bin reschmux-decode -- --debug < samples/encoded-$mode |
+ ffmpeg -y -hide_banner -framerate 25 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded-$mode-debug.mp4
+ cargo run --release --bin reschmux-decode -- < samples/encoded-$mode |
+ ffmpeg -y -hide_banner -framerate 25 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded-$mode.mp4
+end
diff --git a/old/evc/scripts/gen b/old/evc/scripts/gen
new file mode 100755
index 0000000..6c8fa3c
--- /dev/null
+++ b/old/evc/scripts/gen
@@ -0,0 +1,10 @@
+#!/bin/fish
+set w $argv[1]
+set h $argv[2]
+ffmpeg -hide_banner -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 |
+ LOG=info cargo run --release --bin reschmux-encode -- -W {$w} -H {$h} $argv[4..] >samples/encoded
+ffmpeg -hide_banner -y -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 samples/reference.webm
+LOG=info cargo run --release --bin decode -- <samples/encoded |
+ ffmpeg -hide_banner -y -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded.webm
+LOG=info cargo run --release --bin decode -- --debug <samples/encoded |
+ ffmpeg -hide_banner -y -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo -i pipe:0 samples/decoded-debug.webm
diff --git a/old/evc/scripts/report b/old/evc/scripts/report
new file mode 100755
index 0000000..06a5517
--- /dev/null
+++ b/old/evc/scripts/report
@@ -0,0 +1,9 @@
+#!/bin/fish
+cargo build --release
+begin
+ echo "version: $argv[1]"
+ echo "commit: $(cat ../.git/refs/heads/master | head -c 10)"
+ echo "encode: $(command time -f %U ./target/release/reschmux-encode -W (math 1920 / 2) -H (math 1080 / 2) <samples/raw >samples/encoded 2>| tail -n 1)s"
+ echo "decode: $(command time -f %U ./target/release/reschmux-decode <samples/encoded >samples/decoded 2>| tail -n 1)s"
+ echo "size: $(du -h samples/encoded | cut -f 1)"
+end | tee -a reports/(date)
diff --git a/old/evc/scripts/stream b/old/evc/scripts/stream
new file mode 100755
index 0000000..7e9400d
--- /dev/null
+++ b/old/evc/scripts/stream
@@ -0,0 +1,7 @@
+#!/bin/fish
+set w $argv[1]
+set h $argv[2]
+ffmpeg -hide_banner -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 |
+ cargo run --release --bin reschmux-encode -- -W {$w} -H {$h} $argv[4..] |
+ cargo run --release --bin reschmux-decode -- --debug |
+ ffplay -hide_banner -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo pipe:0
diff --git a/old/evc/scripts/stream-nodebug b/old/evc/scripts/stream-nodebug
new file mode 100755
index 0000000..380ec5f
--- /dev/null
+++ b/old/evc/scripts/stream-nodebug
@@ -0,0 +1,7 @@
+#!/bin/fish
+set w $argv[1]
+set h $argv[2]
+ffmpeg -hide_banner -i $argv[3] -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 |
+ cargo run --release --bin reschmux-encode -- -W {$w} -H {$h} $argv[4..] |
+ cargo run --release --bin reschmux-decode -- |
+ ffplay -hide_banner -framerate 30 -video_size {$w}x{$h} -pixel_format rgb24 -f rawvideo pipe:0
diff --git a/old/evc/spec.md b/old/evc/spec.md
new file mode 100644
index 0000000..42be71f
--- /dev/null
+++ b/old/evc/spec.md
@@ -0,0 +1,60 @@
+# Low-Efficiency Video Codec
+
+The codec compresses image sequences by avoiding redundancies by reusing parts
+of previous frames. A frame is one block that is then subdivided further as
+needed.
+
+## Literal-Block
+
+Stores the pixels' RGB values as is.
+
+## Split-Block
+
+Delegates to two sub-blocks. The block is split orthorgonally on the longest
+axis. If needed, the left/top block is rounded down and the right/bottom.
+
+## Reference-Block
+
+Indicates that parts of the last frame are reused. The data reused is at the
+position of this block in the last frame with the translation added.
+
+## Advanced-Reference-Block
+
+Like Reference-Block but instead translates by non-integer amount of pixels,
+applying a 2x2 matrix from the center of the block and multiplying the color
+value of each component, interpolating if necessary.
+
+## Compressed-Literal-Block
+
+_**JPEG? just DCT? idk**_
+
+## File format
+
+- magic bytes: `5e b1 c3 08`
+- resolution: _`u16, u16`_
+- frame count: _`u64`_
+- frames (repeated [frame count]-times)
+ - block kind: _`u8` (see tags below)_
+ - block: _one of the following_
+ - 0 **Literal-Block**
+ - pixels: _`[[u8; 3]; (inferred)]`_ (stored line-by-line)
+ - 1 **Compressed-Literal-Block**
+ - length: _`u32`_
+ - data: _`[[u8; length]`_
+ - 2 **Split-Block**
+ - sub-blocks: _`[block; 2]` (see above)_
+ - 3 **Reference-Block**
+ - translation: _`i8, i8`_
+ - 4 **Advanced-Reference-Block**
+ - translation: _`s8, s8`_ (translation)
+ - transform: _`s8, s8, s8, s8`_ (2x2-matrix applied before sampling
+ relative to the center)
+ - value_scale: _`i8`_ (multiplication of each color component by $1.05^n$)
+
+### Data Types
+
+- _`u<n>`_: unsigned n-bit integer (little-endian)
+- _`i<n>`_: signed n-bit integer using twos-complement (little-endian)
+- _`s8`_: 8-bit scalar. When read as _`i8`_ represents a value of
+ $\frac{x}{|x|} * \sqrt{2}^{|{x}| - 4}$ for $x \neq 0$, otherwise 0
+- _`[<T>; <N>]`_: Array of T with length N
diff --git a/old/evc/src/bin/decode.rs b/old/evc/src/bin/decode.rs
new file mode 100644
index 0000000..fbf5624
--- /dev/null
+++ b/old/evc/src/bin/decode.rs
@@ -0,0 +1,63 @@
+#![feature(box_patterns)]
+use anyhow::Context;
+use clap::Parser;
+use indicatif::ProgressBar;
+use libreschmux::{
+ block::Block,
+ codec::decode::{decode_block, DecodeConfig},
+ debug::draw_debug,
+ format::{header::Header, ser::Source},
+ frame::Frame,
+};
+use log::{info, warn};
+use std::io::{BufReader, BufWriter};
+
+#[derive(Parser)]
+#[clap(about, version)]
+pub struct DecodeArgs {
+ #[arg(long)]
+ debug: bool,
+}
+
+fn main() -> anyhow::Result<()> {
+ env_logger::init_from_env("LOG");
+ let args = DecodeArgs::parse();
+
+ let mut input = BufReader::new(std::io::stdin());
+ let mut output = BufWriter::new(std::io::stdout());
+
+ let header = input.get::<Header>().context("reading header")?;
+ info!("{header:?}");
+ if header.resolution.x * header.resolution.y > 100_000_000 {
+ warn!("resolution is quite big. video is likely corrupt.");
+ }
+ let size = header.resolution;
+
+ let config = DecodeConfig {};
+
+ let progress_bar = ProgressBar::new(header.frame_count as u64);
+
+ let mut prev = Frame::new(size);
+ for i in 0..header.frame_count {
+ info!("decode frame {i}");
+
+ let block = Block::read(&mut input, size).context("reading encoded frame")?;
+ let mut frame = Frame::new(size);
+
+ decode_block(&block, frame.view_mut(), prev.view(), &config);
+
+ progress_bar.inc(1);
+
+ if args.debug {
+ let mut f2 = frame.clone();
+ draw_debug(&block, f2.view_mut());
+ f2.write(&mut output).context("writing raw frame")?;
+ } else {
+ frame.write(&mut output).context("writing raw frame")?;
+ }
+
+ prev = frame;
+ }
+ drop(input);
+ Ok(())
+}
diff --git a/old/evc/src/bin/encode.rs b/old/evc/src/bin/encode.rs
new file mode 100644
index 0000000..43f2c57
--- /dev/null
+++ b/old/evc/src/bin/encode.rs
@@ -0,0 +1,111 @@
+use anyhow::Context;
+use clap::Parser;
+use indicatif::ProgressBar;
+use libreschmux::{
+ codec::{
+ decode::{decode_block, DecodeConfig},
+ encode::{encode_block, EncodeConfig, EncodeMode},
+ },
+ format::{header::Header, ser::Sink},
+ frame::Frame,
+ helpers::vector::Vec2,
+};
+use log::info;
+use std::io::{BufReader, BufWriter};
+
+#[derive(Parser)]
+#[clap(about, version)]
+pub struct EncodeArgs {
+ #[arg(short = 'W', long)]
+ width: usize,
+ #[arg(short = 'H', long)]
+ height: usize,
+
+ #[arg(short, long)]
+ mode: EncodeMode,
+
+ #[arg(long)]
+ no_linear_transform: bool,
+ #[arg(long)]
+ no_value_scale: bool,
+ #[arg(long)]
+ no_translate: bool,
+
+ #[arg[short = 'L', long]]
+ length: Option<usize>,
+
+ #[arg(short, long, default_value = "8")]
+ min_block_size: isize,
+
+ #[arg(short = 't', long, default_value = "200")]
+ ref_thres: f64,
+}
+
+fn main() -> anyhow::Result<()> {
+ env_logger::init_from_env("LOG");
+ let args = EncodeArgs::parse();
+
+ let mut input = BufReader::new(std::io::stdin());
+ let mut output = BufWriter::new(std::io::stdout());
+
+ let config = EncodeConfig {
+ mode: args.mode,
+ ref_thres: args.ref_thres,
+ weight_factor: 50.0,
+ max_diff_area: 10_000,
+ min_block_size: args.min_block_size,
+ do_translate: !args.no_translate,
+ do_linear_transform: !args.no_linear_transform,
+ do_value_scale: !args.no_value_scale,
+ };
+
+ let size = Vec2 {
+ x: args.width as isize,
+ y: args.height as isize,
+ };
+ output
+ .put(Header {
+ resolution: size,
+ frame_count: args.length.unwrap_or(usize::MAX),
+ })
+ .context("writing header")?;
+
+ let progress_bar = args.length.map(|len| ProgressBar::new(len as u64));
+ let mut prev_frame = Frame::new(size);
+
+ for i in 0.. {
+ info!("encode frame {i}");
+ let mut frame = Frame::read(&mut input, size).context("reading raw frame")?;
+
+ let v1 = frame.view();
+ let v2 = prev_frame.view();
+
+ let (error, root) = encode_block(v1, v2, &config);
+
+ // compress_block(
+ // &mut root,
+ // Vec2 {
+ // x: size.x as usize,
+ // y: size.y as usize,
+ // },
+ // );
+
+ root.write(&mut output, size)
+ .context("writing encoded frame")?;
+
+ info!(
+ "cumulative error: {error} ({} per pixel)",
+ error / frame.view().area() as f64
+ );
+
+ if let Some(progress_bar) = &progress_bar {
+ progress_bar.inc(1);
+ }
+
+ decode_block(&root, frame.view_mut(), prev_frame.view(), &DecodeConfig {});
+
+ prev_frame = frame;
+ }
+
+ Ok(())
+}
diff --git a/old/evc/src/bin/info.rs b/old/evc/src/bin/info.rs
new file mode 100644
index 0000000..ebd8442
--- /dev/null
+++ b/old/evc/src/bin/info.rs
@@ -0,0 +1,10 @@
+use anyhow::Context;
+use libreschmux::format::{header::Header, ser::Source};
+use std::io::BufReader;
+
+fn main() {
+ env_logger::init_from_env("LOG");
+ let mut input = BufReader::new(std::io::stdin());
+ let header = input.get::<Header>().context("reading header").unwrap();
+ eprintln!("{header:#?}")
+}
diff --git a/old/evc/src/block.rs b/old/evc/src/block.rs
new file mode 100644
index 0000000..d0f940b
--- /dev/null
+++ b/old/evc/src/block.rs
@@ -0,0 +1,123 @@
+use crate::{
+ format::ser::{ConstSizeSerExt, Ser, Sink, Small, Source},
+ helpers::vector::Vec2,
+ helpers::{matrix::Mat2, pixel::Pixel},
+};
+use anyhow::bail;
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum Block {
+ Literal(Vec<Pixel>),
+ CompressedLiteral(Vec<u8>),
+ Split(Box<[Block; 2]>),
+ Reference { translation: Vec2<isize> },
+ AdvancedReference(AdvancedReference),
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct AdvancedReference {
+ pub translation: Vec2<i8>,
+ pub transform: Mat2<i8>,
+ pub value_scale: i8,
+}
+
+impl Block {
+ pub const REFZERO: Block = Block::Reference {
+ translation: Vec2::<isize>::ZERO,
+ };
+
+ pub fn write(&self, sink: &mut impl std::io::Write, size: Vec2<isize>) -> anyhow::Result<()> {
+ match &self {
+ Block::Literal(pixels) => {
+ sink.put(0u8)?;
+ pixels.write_const_size(sink, size.area() as usize)?;
+ }
+ Block::CompressedLiteral(data) => {
+ sink.put(1u8)?;
+ data.write(sink)?;
+ }
+ Block::Split(box [a, b]) => {
+ sink.put(2u8)?;
+ let [asize, bsize] = split_size(size);
+ a.write(sink, asize)?;
+ b.write(sink, bsize)?;
+ }
+ Block::Reference { translation } => {
+ sink.put(3u8)?;
+ sink.put(Small(*translation))?;
+ }
+ Block::AdvancedReference(AdvancedReference {
+ translation,
+ transform,
+ value_scale,
+ }) => {
+ sink.put(4u8)?;
+ sink.put((*translation, *transform, *value_scale))?;
+ }
+ }
+ Ok(())
+ }
+
+ pub fn read(source: &mut impl std::io::Read, size: Vec2<isize>) -> anyhow::Result<Self> {
+ let variant = source.get::<u8>()?;
+ Ok(match variant {
+ 0 => Block::Literal(Vec::read_const_size(source, size.area() as usize)?),
+ 1 => Block::CompressedLiteral(Vec::read(source)?),
+ 2 => Block::Split(Box::new({
+ let [asize, bsize] = split_size(size);
+ let a = Block::read(source, asize)?;
+ let b = Block::read(source, bsize)?;
+ [a, b]
+ })),
+ 3 => Block::Reference {
+ translation: source.get::<Small<Vec2<isize>>>()?.0,
+ },
+ 4 => Block::AdvancedReference(AdvancedReference {
+ translation: source.get()?,
+ transform: source.get()?,
+ value_scale: source.get()?,
+ }),
+ x => bail!("corrupt block type ({})", x),
+ })
+ }
+}
+
+pub fn split_size(size: Vec2<isize>) -> [Vec2<isize>; 2] {
+ let vert = size.x > size.y;
+ [
+ if vert {
+ (size.x / 2, size.y).into()
+ } else {
+ (size.x, size.y / 2).into()
+ },
+ if vert {
+ (size.x - size.x / 2, size.y).into()
+ } else {
+ (size.x, size.y - size.y / 2).into()
+ },
+ ]
+}
+
+impl Block {
+ pub fn is_literal(&self) -> bool {
+ matches!(self, Block::Literal(..))
+ }
+ pub fn identical_ref(a: &Self, b: &Self) -> bool {
+ matches!(a, Block::Reference { .. }) && a == b
+ }
+}
+
+impl Default for AdvancedReference {
+ fn default() -> Self {
+ Self {
+ translation: Vec2 { x: 0, y: 0 },
+ transform: Mat2 {
+ a: 4,
+ b: 0,
+ c: 0,
+ d: 4,
+ },
+ value_scale: 0,
+ }
+ }
+}
diff --git a/old/evc/src/codec/compress.rs b/old/evc/src/codec/compress.rs
new file mode 100644
index 0000000..09d1f29
--- /dev/null
+++ b/old/evc/src/codec/compress.rs
@@ -0,0 +1,155 @@
+use crate::{
+ block::Block,
+ frame::Frame,
+ helpers::{pixel::Pixel, vector::Vec2},
+ view::View,
+};
+use rustdct::DctPlanner;
+
+pub fn compress_block(block: &mut Block, size: Vec2<usize>) {
+ match block {
+ Block::Literal(p) => {
+ let comp = lit_compress(size.x, size.y, &p);
+ *block = Block::CompressedLiteral(comp)
+ }
+ Block::Split(box [a, b]) => {
+ let vert = size.x > size.y;
+ compress_block(
+ a,
+ if vert {
+ Vec2 {
+ x: size.x / 2,
+ y: size.y,
+ }
+ } else {
+ Vec2 {
+ x: size.x,
+ y: size.y / 2,
+ }
+ },
+ );
+ compress_block(
+ b,
+ if vert {
+ Vec2 {
+ x: size.x - size.x / 2,
+ y: size.y,
+ }
+ } else {
+ Vec2 {
+ x: size.x,
+ y: size.y - size.y / 2,
+ }
+ },
+ );
+ }
+ _ => (),
+ }
+}
+
+pub fn lit_compress(w: usize, h: usize, pixels: &[Pixel]) -> Vec<u8> {
+ let mut out = vec![];
+ for ci in 0..3 {
+ let mut ch = vec![0.; w * h];
+ for y in 0..h {
+ for x in 0..w {
+ let p = pixels[x + y * w];
+ ch[x + y * w] = match ci {
+ 0 => p.r,
+ 1 => p.g,
+ 2 => p.b,
+ _ => unreachable!(),
+ } as f32
+ }
+ }
+
+ norm_dct_channel(w, h, &mut ch);
+ for i in 0..w * h {
+ out.extend(unsafe { std::mem::transmute::<_, [u8; 4]>(ch[i]) });
+ }
+ }
+ out
+}
+
+pub fn lit_decompress(compressed: &[u8], mut target: View<&mut Frame>) {
+ let (w, h) = (target.size.x as usize, target.size.y as usize);
+ for ci in 0..3 {
+ let mut ch = compressed[ci * w * h..(ci + 1) * w * h]
+ .chunks_exact(4)
+ .map(|v| unsafe {
+ std::mem::transmute::<_, f32>(TryInto::<[u8; 4]>::try_into(v).unwrap())
+ })
+ .collect::<Vec<_>>();
+ norm_idct_channel(w, h, &mut ch);
+ for y in 0..h {
+ for x in 0..w {
+ let p = &mut target[Vec2 {
+ x: x as isize,
+ y: y as isize,
+ }];
+ *(match ci {
+ 0 => &mut p.r,
+ 1 => &mut p.g,
+ 2 => &mut p.b,
+ _ => unreachable!(),
+ }) = ch[x + y * w] as u8
+ }
+ }
+ }
+}
+
+fn norm_dct_channel(w: usize, h: usize, values: &mut [f32]) {
+ let d = DctPlanner::new().plan_dct2(w);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values.chunks_exact_mut(w) {
+ d.process_dct2_with_scratch(c, &mut temp)
+ }
+
+ let mut values_trans = transpose(w, h, values);
+ let d = DctPlanner::new().plan_dct2(h);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values_trans.chunks_exact_mut(h) {
+ d.process_dct2_with_scratch(c, &mut temp)
+ }
+
+ let scale = 2. / (w as f32 * h as f32).sqrt();
+ for (i, v) in values_trans.iter().enumerate() {
+ values[i] = *v * scale * 127.0
+ }
+}
+
+fn norm_idct_channel(h: usize, w: usize, values: &mut [f32]) {
+ let scale = 2. / (w as f32 * h as f32).sqrt();
+ for v in values.iter_mut() {
+ *v /= scale * 127.0
+ }
+
+ let d = DctPlanner::new().plan_dct2(w);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values.chunks_exact_mut(w) {
+ d.process_dct3_with_scratch(c, &mut temp)
+ }
+
+ let mut values_trans = transpose(w, h, values);
+ let d = DctPlanner::new().plan_dct2(h);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values_trans.chunks_exact_mut(h) {
+ d.process_dct3_with_scratch(c, &mut temp)
+ }
+ values.copy_from_slice(&values_trans)
+}
+
+fn transpose(w: usize, h: usize, values: &[f32]) -> Vec<f32> {
+ let mut io = 0;
+ let mut it;
+ let mut transposed = vec![0.; values.len()];
+ for row in 0..h {
+ it = row;
+ for _ in 0..w {
+ transposed[it] = values[io];
+ io += 1;
+ it += h;
+ }
+ }
+ transposed
+}
diff --git a/old/evc/src/codec/decode.rs b/old/evc/src/codec/decode.rs
new file mode 100644
index 0000000..c042278
--- /dev/null
+++ b/old/evc/src/codec/decode.rs
@@ -0,0 +1,34 @@
+use super::compress::lit_decompress;
+use crate::{block::Block, frame::Frame, refsampler::Sampler, view::View};
+
+pub struct DecodeConfig {}
+
+pub fn decode_block(
+ block: &Block,
+ mut target: View<&mut Frame>,
+ prev: View<&Frame>,
+ config: &DecodeConfig,
+) {
+ match &block {
+ Block::Literal(pixels) => target.set_pixels(pixels),
+ Block::Split(box [a, b]) => {
+ let [a, b] = unsafe { std::mem::transmute::<_, [&'static Block; 2]>([a, b]) };
+ let [at, bt] = unsafe {
+ std::mem::transmute::<_, [View<&'static mut Frame>; 2]>(target.split_mut_unsafe())
+ };
+ let [ap, bp] =
+ unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) };
+ let config = unsafe { std::mem::transmute::<_, &'static DecodeConfig>(config) };
+
+ rayon::join(
+ move || decode_block(a, at, ap, config),
+ move || decode_block(b, bt, bp, config),
+ );
+ }
+ Block::CompressedLiteral(data) => {
+ lit_decompress(&data, target);
+ }
+ Block::Reference { translation } => target.copy_from(&prev.offset(*translation)),
+ Block::AdvancedReference(r) => target.copy_from_sampler(&Sampler::from_refblock(prev, r)),
+ }
+}
diff --git a/old/evc/src/codec/encode/advanced.rs b/old/evc/src/codec/encode/advanced.rs
new file mode 100644
index 0000000..0e45176
--- /dev/null
+++ b/old/evc/src/codec/encode/advanced.rs
@@ -0,0 +1,115 @@
+use super::EncodeConfig;
+use crate::{
+ block::{AdvancedReference, Block},
+ frame::Frame,
+ refsampler::Sampler,
+ view::View,
+};
+
+pub fn default(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ config: &EncodeConfig,
+ max_diff: f64,
+) -> (f64, Block) {
+ let mut pm = AdvancedReference::default();
+ let sampler = Sampler::from_refblock(prev.clone(), &pm);
+ let mut diff = View::diff_sampler(&view, &sampler);
+ if diff < max_diff {
+ (diff, Block::REFZERO)
+ } else {
+ loop {
+ let (mut d, mut p) = (diff, pm.clone());
+
+ if config.do_translate {
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1);
+ }
+ if config.do_value_scale {
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1);
+ }
+ if config.do_linear_transform {
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d += 1);
+ }
+
+ if d >= diff {
+ break (diff, Block::AdvancedReference(pm));
+ }
+
+ diff = d;
+ pm = p;
+ }
+ }
+}
+
+pub fn partial(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ _config: &EncodeConfig,
+ max_diff: f64,
+) -> (f64, Block) {
+ let mut pm = AdvancedReference::default();
+ let sampler = Sampler::from_refblock(prev.clone(), &pm);
+ let mut diff = View::diff_sampler(&view, &sampler);
+ if diff < max_diff {
+ (diff, Block::REFZERO)
+ } else {
+ loop {
+ let (mut d, mut p) = (diff, pm.clone());
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1);
+
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1);
+
+ if d >= diff {
+ break (diff, Block::AdvancedReference(pm));
+ }
+ diff = d;
+ pm = p;
+ }
+ }
+}
+
+#[inline]
+fn pk<F: FnMut(&mut AdvancedReference) -> ()>(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ diff: &mut f64,
+ params: &mut AdvancedReference,
+ initial_params: &AdvancedReference,
+ mut f: F,
+) {
+ let mut p = initial_params.clone();
+ f(&mut p);
+ let sampler = Sampler::from_refblock(prev.clone(), &p);
+ let d = View::diff_sampler(view, &sampler);
+ if d < *diff {
+ *diff = d;
+ *params = p;
+ }
+}
diff --git a/old/evc/src/codec/encode/mod.rs b/old/evc/src/codec/encode/mod.rs
new file mode 100644
index 0000000..30342f0
--- /dev/null
+++ b/old/evc/src/codec/encode/mod.rs
@@ -0,0 +1,112 @@
+pub mod advanced;
+pub mod simple;
+
+use crate::{
+ block::Block,
+ frame::Frame,
+ helpers::{pixel::Pixel, vector::Vec2},
+ view::View,
+};
+use clap::ValueEnum;
+
+#[derive(Debug, Clone)]
+pub struct EncodeConfig {
+ pub mode: EncodeMode,
+ pub ref_thres: f64,
+ pub max_diff_area: isize,
+ pub min_block_size: isize,
+ pub weight_factor: f64,
+
+ pub do_translate: bool,
+ pub do_value_scale: bool,
+ pub do_linear_transform: bool,
+}
+
+#[derive(Debug, Clone, ValueEnum)]
+pub enum EncodeMode {
+ Trivial,
+ SimpleFast,
+ SimpleExhaustive,
+ Advanced,
+ AdvancedPartial,
+}
+
+pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfig) -> (f64, Block) {
+ let (diff, refblock) = if view.area() > config.max_diff_area {
+ (
+ f64::INFINITY,
+ Block::Reference {
+ translation: Vec2::<isize>::ZERO,
+ },
+ )
+ } else {
+ let weight = importance(&view).max(0.5);
+ let irrelevance = config.weight_factor / weight;
+ let max_diff = config.ref_thres - irrelevance;
+ let (diff, refblock) = match config.mode {
+ EncodeMode::Trivial => (
+ View::diff(&view, &prev),
+ Block::Reference {
+ translation: Vec2::<isize>::ZERO,
+ },
+ ),
+ EncodeMode::SimpleExhaustive => simple::exhaustive(&view, &prev, config, max_diff),
+ EncodeMode::SimpleFast => simple::fast(&view, &prev, config, max_diff),
+ EncodeMode::Advanced => advanced::default(&view, &prev, config, max_diff),
+ EncodeMode::AdvancedPartial => advanced::partial(&view, &prev, config, max_diff),
+ };
+ (diff - irrelevance, refblock)
+ };
+ if diff < config.ref_thres {
+ (diff, refblock)
+ } else {
+ if view.size.x < config.min_block_size || view.size.y < config.min_block_size {
+ (0.0, Block::Literal(view.pixels()))
+ } else {
+ let [av, bv] =
+ unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(view.split()) };
+ let [ap, bp] =
+ unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) };
+ let config = unsafe { std::mem::transmute::<_, &'static EncodeConfig>(config) };
+
+ // only bother to do multithreading, when the block is big.
+ let ((ad, a), (bd, b)) = if view.area() > 100 {
+ rayon::join(
+ || encode_block(av, ap, config),
+ || encode_block(bv, bp, config),
+ )
+ } else {
+ (encode_block(av, ap, config), encode_block(bv, bp, config))
+ };
+
+ (
+ ad + bd,
+ if a.is_literal() && b.is_literal() {
+ Block::Literal(view.pixels())
+ } else if Block::identical_ref(&a, &b) {
+ Block::Reference {
+ translation: Vec2::<isize>::ZERO,
+ }
+ } else {
+ Block::Split(Box::new([a, b]))
+ },
+ )
+ }
+ }
+}
+
+pub fn importance(view: &View<&Frame>) -> f64 {
+ let mut acc = 0;
+ for x in 0..view.size.x {
+ for y in 0..view.size.y {
+ let p = Vec2 { x, y };
+ if x > 0 {
+ acc += Pixel::distance(view[p], view[p + Vec2::<isize>::LEFT]);
+ }
+ if y > 0 {
+ acc += Pixel::distance(view[p], view[p + Vec2::<isize>::UP]);
+ }
+ }
+ }
+ (acc / view.area() as usize) as f64
+}
diff --git a/old/evc/src/codec/encode/simple.rs b/old/evc/src/codec/encode/simple.rs
new file mode 100644
index 0000000..2a971af
--- /dev/null
+++ b/old/evc/src/codec/encode/simple.rs
@@ -0,0 +1,65 @@
+use crate::{block::Block, frame::Frame, helpers::vector::Vec2, view::View};
+
+use super::EncodeConfig;
+
+pub fn exhaustive(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ _config: &EncodeConfig,
+ _max_diff: f64,
+) -> (f64, Block) {
+ let mut diff = f64::INFINITY;
+ let mut translation = Vec2::<isize>::ZERO;
+ const OFFSETS: &[isize] = &[-64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64];
+ for x in OFFSETS {
+ for y in OFFSETS {
+ let t = Vec2 { x: *x, y: *y };
+ let d = View::diff(&view, &prev.offset(t));
+ if d < diff {
+ translation = t;
+ diff = d;
+ }
+ }
+ }
+ (diff, Block::Reference { translation })
+}
+
+pub fn fast(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ _config: &EncodeConfig,
+ max_diff: f64,
+) -> (f64, Block) {
+ let mut offset = Vec2::<isize>::ZERO;
+ let mut diff = View::diff(&view, &prev);
+ if diff < max_diff {
+ (diff, Block::REFZERO)
+ } else {
+ loop {
+ let (mut d, mut o) = (diff, offset);
+ let mut probe = |test_o: Vec2<isize>| {
+ let test_d = View::diff(&prev.clone().offset(test_o), &view);
+ if test_d < d {
+ d = test_d;
+ o = test_o;
+ }
+ };
+
+ probe(offset + Vec2 { x: 8, y: 0 });
+ probe(offset + Vec2 { x: -8, y: 0 });
+ probe(offset + Vec2 { x: 0, y: 8 });
+ probe(offset + Vec2 { x: 0, y: -8 });
+
+ probe(offset + Vec2 { x: 1, y: 0 });
+ probe(offset + Vec2 { x: -1, y: 0 });
+ probe(offset + Vec2 { x: 0, y: 1 });
+ probe(offset + Vec2 { x: 0, y: -1 });
+
+ if d >= diff {
+ break (diff, Block::Reference { translation: o });
+ }
+ diff = d;
+ offset = o;
+ }
+ }
+}
diff --git a/old/evc/src/codec/mod.rs b/old/evc/src/codec/mod.rs
new file mode 100644
index 0000000..3203e6e
--- /dev/null
+++ b/old/evc/src/codec/mod.rs
@@ -0,0 +1,3 @@
+pub mod decode;
+pub mod encode;
+pub mod compress;
diff --git a/old/evc/src/debug.rs b/old/evc/src/debug.rs
new file mode 100644
index 0000000..a7a9545
--- /dev/null
+++ b/old/evc/src/debug.rs
@@ -0,0 +1,103 @@
+use crate::{
+ block::Block,
+ format::ser::map_scalar8,
+ frame::Frame,
+ helpers::vector::Vec2,
+ helpers::{matrix::Mat2, pixel::Pixel},
+ view::View,
+};
+
+impl View<&mut Frame> {
+ pub fn draw_box(&mut self, color: Pixel) {
+ let w = self.size.x;
+ let h = self.size.y;
+ for x in 0..w {
+ self[(x, 0)] = color;
+ self[(x, h - 1)] = color;
+ }
+ for y in 0..h {
+ self[(0, y)] = color;
+ self[(w - 1, y)] = color;
+ }
+ }
+}
+
+impl Frame {
+ pub fn draw_line(&mut self, start: Vec2<f32>, end: Vec2<f32>, color: Pixel) {
+ let diff = end - start;
+ let len = (diff.x * diff.x + diff.y * diff.y).sqrt();
+ let normal = Vec2 {
+ x: diff.x / len,
+ y: diff.y / len,
+ };
+ let mut cursor = start.clone();
+ let mut lc = 0.0;
+ while lc < len {
+ self.set(cursor.into(), color);
+ lc += 0.5;
+ cursor = cursor + normal.scale(0.5);
+ }
+ }
+}
+
+impl Pixel {
+ pub const RED: Pixel = Pixel { r: 255, g: 0, b: 0 };
+ pub const GREEN: Pixel = Pixel { r: 0, g: 255, b: 0 };
+ pub const BLUE: Pixel = Pixel { r: 0, g: 0, b: 255 };
+ pub const MAGENTA: Pixel = Pixel {
+ r: 255,
+ g: 0,
+ b: 255,
+ };
+ pub const CYAN: Pixel = Pixel {
+ r: 0,
+ g: 255,
+ b: 255,
+ };
+}
+
+pub fn draw_debug(block: &Block, mut target: View<&mut Frame>) {
+ match &block {
+ Block::Literal(_) | Block::CompressedLiteral(_) => {
+ target.draw_box(Pixel::GREEN);
+ }
+ Block::Split(box [a, b]) => {
+ let [at, bt] = target.split_mut_unsafe();
+ draw_debug(a, at);
+ draw_debug(b, bt);
+ }
+ Block::Reference { translation } => {
+ target.draw_box(Pixel::BLUE);
+ target.frame.draw_line(
+ target.center().into(),
+ (target.center() + *translation).into(),
+ Pixel::RED,
+ )
+ }
+ Block::AdvancedReference(r) => {
+ let mat = Mat2 {
+ a: map_scalar8(r.transform.a),
+ b: map_scalar8(r.transform.b),
+ c: map_scalar8(r.transform.c),
+ d: map_scalar8(r.transform.d),
+ };
+ let translation = Vec2 {
+ x: map_scalar8(r.translation.x),
+ y: map_scalar8(r.translation.y),
+ };
+ let halfsize = Into::<Vec2<f32>>::into(target.size).scale(0.5);
+ let transform = |p| translation + mat.transform(p - halfsize) + halfsize;
+ let (tl, tr, bl, br) = (
+ transform(Vec2::<f32>::ZERO) + target.offset.into(),
+ transform(target.size.x_only().into()) + target.offset.into(),
+ transform(target.size.y_only().into()) + target.offset.into(),
+ transform(target.size.into()) + target.offset.into(),
+ );
+ target.draw_box(Pixel::CYAN);
+ target.frame.draw_line(tl, tr, Pixel::MAGENTA);
+ target.frame.draw_line(tr, br, Pixel::MAGENTA);
+ target.frame.draw_line(bl, br, Pixel::MAGENTA);
+ target.frame.draw_line(tl, bl, Pixel::MAGENTA);
+ }
+ };
+}
diff --git a/old/evc/src/format/header.rs b/old/evc/src/format/header.rs
new file mode 100644
index 0000000..ecbae89
--- /dev/null
+++ b/old/evc/src/format/header.rs
@@ -0,0 +1,29 @@
+use crate::{
+ format::ser::{Ser, Sink, Source},
+ helpers::vector::Vec2,
+};
+
+#[derive(Debug, Clone, PartialEq, Copy)]
+pub struct Header {
+ pub resolution: Vec2<isize>,
+ pub frame_count: usize,
+}
+
+pub const MAGIC: [u8; 4] = [0x5eu8, 0xb1u8, 0xc3u8, 0x08u8];
+
+impl Ser for Header {
+ fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> {
+ sink.put(MAGIC)?;
+ sink.put((Into::<Vec2<u16>>::into(self.resolution), self.frame_count))?;
+ Ok(())
+ }
+
+ fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> {
+ assert_eq!(source.get::<[u8; 4]>()?, MAGIC);
+ let (resolution, frame_count): (Vec2<u16>, usize) = source.get()?;
+ Ok(Self {
+ resolution: resolution.into(),
+ frame_count,
+ })
+ }
+}
diff --git a/old/evc/src/format/mod.rs b/old/evc/src/format/mod.rs
new file mode 100644
index 0000000..d4fb18c
--- /dev/null
+++ b/old/evc/src/format/mod.rs
@@ -0,0 +1,2 @@
+pub mod header;
+pub mod ser; \ No newline at end of file
diff --git a/old/evc/src/format/ser.rs b/old/evc/src/format/ser.rs
new file mode 100644
index 0000000..f063377
--- /dev/null
+++ b/old/evc/src/format/ser.rs
@@ -0,0 +1,335 @@
+use anyhow::Context;
+use std::io::{Read, Write};
+
+use crate::helpers::{matrix::Mat2, vector::Vec2};
+
+pub trait Sink {
+ fn put<V: Ser>(&mut self, value: V) -> anyhow::Result<()>;
+}
+pub trait Source {
+ fn get<V: Ser>(&mut self) -> anyhow::Result<V>;
+}
+
+impl<T: Write> Sink for T {
+ fn put<V: Ser>(&mut self, value: V) -> anyhow::Result<()> {
+ value.write(self)
+ }
+}
+impl<T: Read> Source for T {
+ fn get<V: Ser>(&mut self) -> anyhow::Result<V> {
+ V::read(self)
+ }
+}
+
+pub trait Ser: Sized {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()>;
+ fn read(source: &mut impl Read) -> anyhow::Result<Self>;
+}
+
+impl<A: Ser, B: Ser> Ser for (A, B) {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ self.0.write(sink).context("first tuple field")?;
+ self.1.write(sink).context("second tuple field")?;
+ Ok(())
+ }
+
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ Ok((A::read(source)?, B::read(source)?))
+ }
+}
+impl<A: Ser, B: Ser, C: Ser> Ser for (A, B, C) {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ self.0.write(sink).context("first tuple field")?;
+ self.1.write(sink).context("second tuple field")?;
+ self.2.write(sink).context("third tuple field")?;
+ Ok(())
+ }
+
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ Ok((A::read(source)?, B::read(source)?, C::read(source)?))
+ }
+}
+impl<A: Ser, B: Ser, C: Ser, D: Ser> Ser for (A, B, C, D) {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ self.0.write(sink).context("first tuple field")?;
+ self.1.write(sink).context("second tuple field")?;
+ self.2.write(sink).context("third tuple field")?;
+ self.3.write(sink).context("fourth tuple field")?;
+ Ok(())
+ }
+
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ Ok((
+ A::read(source)?,
+ B::read(source)?,
+ C::read(source)?,
+ D::read(source)?,
+ ))
+ }
+}
+
+impl<A: Ser, const N: usize> Ser for [A; N] {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ for e in self {
+ e.write(sink).context("some array")?;
+ }
+ Ok(())
+ }
+
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut k: [A; N] = unsafe { std::mem::zeroed() };
+ for i in 0..N {
+ k[i] = A::read(source)?;
+ }
+ Ok(k)
+ }
+}
+
+impl<T: Ser> Ser for Vec<T> {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ self.len().write(sink)?;
+ for e in self {
+ e.write(sink).context("some vec")?;
+ }
+ Ok(())
+ }
+
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut v = vec![];
+ for _ in 0..usize::read(source)? {
+ v.push(T::read(source)?)
+ }
+ Ok(v)
+ }
+}
+
+pub trait ConstSizeSerExt: Sized {
+ fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()>;
+ fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result<Self>;
+}
+impl<T: Ser> ConstSizeSerExt for Vec<T> {
+ fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()> {
+ assert_eq!(self.len(), size);
+ for e in self {
+ e.write(sink).context("some const-size vec")?;
+ }
+ Ok(())
+ }
+
+ fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result<Self> {
+ let mut v = vec![];
+ for _ in 0..size {
+ v.push(T::read(source)?)
+ }
+ Ok(v)
+ }
+}
+
+impl Ser for u8 {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink.write_all(&[*self]).context("write u8")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 1];
+ source.read_exact(&mut buf)?;
+ Ok(buf[0])
+ }
+}
+impl Ser for i8 {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 1]>(self) })
+ .context("write i8")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 1];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+impl Ser for u16 {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 2]>(self) })
+ .context("write u16")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 2];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+impl Ser for u32 {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 4]>(self) })
+ .context("write u32")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 4];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+impl Ser for u64 {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) })
+ .context("write u64")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 8];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+impl Ser for usize {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) })
+ .context("write usize")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 8];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+impl Ser for isize {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) })
+ .context("write isize")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 8];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+impl Ser for f32 {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 4]>(self) })
+ .context("write f32")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 4];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+impl Ser for f64 {
+ fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> {
+ Ok(sink
+ .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) })
+ .context("write f64")?)
+ }
+ fn read(source: &mut impl Read) -> anyhow::Result<Self> {
+ let mut buf = [0u8; 8];
+ source.read_exact(&mut buf)?;
+ Ok(unsafe { std::mem::transmute_copy(&buf) })
+ }
+}
+
+impl<T: Ser + Copy> Ser for Vec2<T> {
+ fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> {
+ sink.put((self.x, self.y))
+ }
+
+ fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> {
+ let (x, y) = source.get()?;
+ Ok(Vec2 { x, y })
+ }
+}
+impl<T: Ser + Copy> Ser for Mat2<T> {
+ fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> {
+ sink.put((self.a, self.b, self.c, self.d))
+ }
+
+ fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> {
+ let (a, b, c, d) = source.get()?;
+ Ok(Mat2 { a, b, c, d })
+ }
+}
+
+pub struct Small<T>(pub T);
+impl Ser for Small<Vec2<isize>> {
+ fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> {
+ sink.put((self.0.x as i8, self.0.y as i8))
+ }
+
+ fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> {
+ let (x, y): (i8, i8) = source.get()?;
+ Ok(Small(Vec2 {
+ x: x as isize,
+ y: y as isize,
+ }))
+ }
+}
+
+pub fn map_scalar8(v: i8) -> f32 {
+ match v {
+ 0 => 0.0,
+ x if x > 0 => 2f32.powf((x as f32).abs() / 2.0 - 2.0),
+ x => -2f32.powf((-x as f32).abs() / 2.0 - 2.0),
+ }
+}
+// const SCALAR8: [f32; 256] = gen_scalar8_lookup();
+// const fn gen_scalar8_lookup() -> [f32; 256] {
+// let mut a = [0.0; 256];
+// let mut i = 0usize;
+// while i < 256 {
+// a[i as usize] = if i == 0 {
+// 0.0
+// } else {
+// let x = i as i8 as f32;
+// x.signum() * 2f32.powf(x.abs() / 2.0 - 2.0)
+// };
+// i += 1;
+// }
+// a
+// }
+
+#[cfg(test)]
+mod test {
+ use super::{Ser, Sink};
+ use crate::format::header::Header;
+ use crate::{format::ser::Source, helpers::vector::Vec2};
+ use std::fmt::Debug;
+ use std::io::Cursor;
+
+ fn test_ser<T: PartialEq + Ser + Debug + Clone>(value: T) {
+ let mut buf = vec![];
+ Cursor::new(&mut buf).put(value.clone()).unwrap();
+ assert_eq!(value, Cursor::new(&mut buf).get().unwrap());
+ }
+
+ #[test]
+ fn simple() {
+ let mut buf = vec![];
+ Cursor::new(&mut buf).put(10usize).unwrap();
+ assert_eq!(10usize, Cursor::new(&mut buf).get().unwrap());
+ }
+ #[test]
+ fn tuple() {
+ let mut buf = vec![];
+ Cursor::new(&mut buf).put((10usize, 5u8, 3u16)).unwrap();
+ assert_eq!((10usize, 5u8, 3u16), Cursor::new(&mut buf).get().unwrap());
+ }
+ #[test]
+ fn header() {
+ test_ser(Header {
+ frame_count: 123,
+ resolution: Vec2 { x: 13, y: 37 },
+ });
+ }
+ #[test]
+ fn vec() {
+ test_ser(vec![1u16, 2, 3, 4]);
+ }
+ #[test]
+ fn array() {
+ test_ser([1u16, 2, 3, 4]);
+ }
+}
diff --git a/old/evc/src/frame.rs b/old/evc/src/frame.rs
new file mode 100644
index 0000000..78d0e73
--- /dev/null
+++ b/old/evc/src/frame.rs
@@ -0,0 +1,109 @@
+use crate::{
+ format::ser::{Sink, Source},
+ helpers::{pixel::Pixel, vector::Vec2},
+ view::View,
+};
+use std::ops::{Index, IndexMut};
+
+#[derive(Debug, Clone)]
+pub struct Frame {
+ pub size: Vec2<isize>,
+ buffer: Vec<Pixel>,
+}
+
+impl Frame {
+ pub fn new(size: Vec2<isize>) -> Self {
+ Self {
+ size,
+ buffer: (0..size.x * size.y).map(|_| Pixel::default()).collect(),
+ }
+ }
+ pub fn read(source: &mut impl Source, size: Vec2<isize>) -> anyhow::Result<Self> {
+ let mut frame = Frame::new(size);
+ for y in 0..size.y {
+ for x in 0..size.x {
+ let pixel = source.get::<Pixel>()?;
+ frame[(x, y)] = pixel;
+ }
+ }
+ Ok(frame)
+ }
+ pub fn write(&self, sink: &mut impl Sink) -> anyhow::Result<()> {
+ for y in 0..self.size.y {
+ for x in 0..self.size.x {
+ sink.put(self[(x, y)])?;
+ }
+ }
+ Ok(())
+ }
+ pub fn view<'a>(&'a self) -> View<&'a Frame> {
+ View::new(self, Vec2::<isize>::ZERO, self.size)
+ }
+ pub fn view_mut<'a>(&'a mut self) -> View<&'a mut Frame> {
+ View::new(self, Vec2::<isize>::ZERO, self.size)
+ }
+ pub fn view_area<'a>(&'a self, offset: Vec2<isize>, size: Vec2<isize>) -> View<&'a Frame> {
+ View::new(self, offset, size)
+ }
+ pub fn view_area_mut<'a>(
+ &'a mut self,
+ offset: Vec2<isize>,
+ size: Vec2<isize>,
+ ) -> View<&'a mut Frame> {
+ View::new(self, offset, size)
+ }
+ pub fn set(&mut self, pos: Vec2<isize>, color: Pixel) {
+ if pos.x >= 0 && pos.y >= 0 && pos.x < self.size.x && pos.y < self.size.y {
+ self[pos] = color
+ }
+ }
+}
+
+impl Index<Vec2<isize>> for Frame {
+ type Output = Pixel;
+ #[inline]
+ fn index(&self, Vec2 { x, y }: Vec2<isize>) -> &Self::Output {
+ &self.buffer
+ [(x.clamp(0, self.size.x - 1) + y.clamp(0, self.size.y - 1) * self.size.x) as usize]
+ }
+}
+impl IndexMut<Vec2<isize>> for Frame {
+ #[inline]
+ fn index_mut(&mut self, Vec2 { x, y }: Vec2<isize>) -> &mut Self::Output {
+ &mut self.buffer[(x + y * self.size.x) as usize]
+ }
+}
+
+impl Frame {
+ #[inline]
+ pub fn sample(&self, p: Vec2<f32>) -> Pixel {
+ self[Vec2 {
+ x: p.x as isize,
+ y: p.y as isize,
+ }]
+ // let fx = p.x.floor() as isize;
+ // let fy = p.y.floor() as isize;
+ // let cx = p.x.ceil() as isize;
+ // let cy = p.y.ceil() as isize;
+
+ // // TODO dont loose accuracy here
+ // Pixel::average(
+ // Pixel::average(self[Vec2 { x: fx, y: fy }], self[Vec2 { x: fx, y: cy }]),
+ // Pixel::average(self[Vec2 { x: cx, y: fx }], self[Vec2 { x: cx, y: fy }]),
+ // )
+ }
+}
+
+impl Index<(isize, isize)> for Frame {
+ type Output = Pixel;
+ #[inline]
+ fn index(&self, (x, y): (isize, isize)) -> &Self::Output {
+ &self[Vec2 { x, y }]
+ }
+}
+impl IndexMut<(isize, isize)> for Frame {
+ #[inline]
+ fn index_mut(&mut self, (x, y): (isize, isize)) -> &mut Self::Output {
+ &mut self[Vec2 { x, y }]
+ }
+}
diff --git a/old/evc/src/helpers/matrix.rs b/old/evc/src/helpers/matrix.rs
new file mode 100644
index 0000000..0007440
--- /dev/null
+++ b/old/evc/src/helpers/matrix.rs
@@ -0,0 +1,33 @@
+use crate::helpers::vector::Vec2;
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Mat2<T> {
+ pub a: T,
+ pub b: T,
+ pub c: T,
+ pub d: T,
+}
+
+impl<T: std::ops::Mul<Output = T> + std::ops::Add<Output = T> + Copy> Mat2<T> {
+ #[inline]
+ pub fn transform(&self, v: Vec2<T>) -> Vec2<T> {
+ Vec2 {
+ x: self.a * v.x + self.b * v.y,
+ y: self.c * v.x + self.d * v.y,
+ }
+ }
+}
+
+impl<T: std::ops::Mul<Output = T> + std::ops::Add<Output = T> + Copy> std::ops::Mul for Mat2<T> {
+ type Output = Mat2<T>;
+ #[inline]
+ fn mul(self, rhs: Mat2<T>) -> Mat2<T> {
+ let (x, y) = (self, rhs);
+ Mat2 {
+ a: x.a * y.a + x.b * y.c,
+ b: x.a * y.b + x.b * y.d,
+ c: x.c * y.a + x.d * y.c,
+ d: x.c * y.b + x.d * y.d,
+ }
+ }
+}
diff --git a/old/evc/src/helpers/mod.rs b/old/evc/src/helpers/mod.rs
new file mode 100644
index 0000000..d3aa3d2
--- /dev/null
+++ b/old/evc/src/helpers/mod.rs
@@ -0,0 +1,4 @@
+pub mod vector;
+pub mod threading;
+pub mod matrix;
+pub mod pixel;
diff --git a/old/evc/src/helpers/pixel.rs b/old/evc/src/helpers/pixel.rs
new file mode 100644
index 0000000..39fe98c
--- /dev/null
+++ b/old/evc/src/helpers/pixel.rs
@@ -0,0 +1,81 @@
+use crate::format::ser::{Ser, Sink, Source};
+
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+pub struct Pixel {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+impl Ser for Pixel {
+ fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> {
+ sink.put((self.r, self.g, self.b))
+ }
+
+ fn read(source: &mut impl std::io::Read) -> anyhow::Result<Self> {
+ let (r, g, b) = source.get()?;
+ Ok(Self { r, g, b })
+ }
+}
+
+impl Pixel {
+ pub const BLACK: Pixel = Pixel { r: 0, g: 0, b: 0 };
+ #[inline]
+ pub fn distance(a: Pixel, b: Pixel) -> usize {
+ let (rd, gd, bd) = (
+ a.r.abs_diff(b.r) as usize,
+ a.g.abs_diff(b.g) as usize,
+ a.b.abs_diff(b.b) as usize,
+ );
+ // fast_sqrt(rd * rd + gd * gd + bd * bd)
+ // SQRT[rd + gd + bd]
+ rd + gd + bd
+ }
+
+ #[inline]
+ pub fn average(a: Pixel, b: Pixel) -> Pixel {
+ Pixel {
+ r: ((a.r as u16 + b.r as u16) >> 1) as u8,
+ g: ((a.g as u16 + b.g as u16) >> 1) as u8,
+ b: ((a.b as u16 + b.b as u16) >> 1) as u8,
+ }
+ }
+
+ #[inline]
+ pub fn scale(&self, factor: f32) -> Pixel {
+ Pixel {
+ r: ((self.r as f32) * factor).clamp(0.0, 255.0) as u8,
+ g: ((self.g as f32) * factor).clamp(0.0, 255.0) as u8,
+ b: ((self.b as f32) * factor).clamp(0.0, 255.0) as u8,
+ }
+ }
+}
+
+pub const SQRT: [usize; 256 * 3] = gen_sqrt_lookup();
+
+const fn gen_sqrt_lookup<const N: usize>() -> [usize; N] {
+ let mut arr = [0; N];
+ let mut i = 0;
+ while i < N {
+ arr[i] = sqrt(i as f32) as usize;
+ i += 1;
+ }
+ arr
+}
+
+const fn sqrt(x: f32) -> f32 {
+ let a = 1.0;
+ let a = (a + x / a) * 0.5;
+ let a = (a + x / a) * 0.5;
+ let a = (a + x / a) * 0.5;
+ a
+}
+
+pub fn fast_sqrt(x: usize) -> usize {
+ let a = 1;
+ let a = (a + x / (a + 1)) / 2;
+ let a = (a + x / (a + 1)) / 2;
+ // let a = (a + x / (a + 1)) / 2;
+ // let a = (a + x / (a + 1)) / 2;
+ a
+}
diff --git a/old/evc/src/helpers/threading.rs b/old/evc/src/helpers/threading.rs
new file mode 100644
index 0000000..3291172
--- /dev/null
+++ b/old/evc/src/helpers/threading.rs
@@ -0,0 +1,26 @@
+use std::{
+ sync::atomic::{AtomicUsize, Ordering},
+ thread,
+};
+
+static THREADS_RUNNING: AtomicUsize = AtomicUsize::new(0);
+
+pub fn both_par<F1, F2, O1, O2>(f1: F1, f2: F2, max_threads: usize) -> (O1, O2)
+where
+ F1: FnOnce() -> O1 + Send + 'static,
+ O1: Send + 'static,
+ F2: FnOnce() -> O2,
+{
+ if THREADS_RUNNING.load(Ordering::Relaxed) < max_threads {
+ THREADS_RUNNING.fetch_add(1, Ordering::Relaxed);
+
+ let o1h = thread::spawn(move || f1());
+ let o2 = f2();
+ let o1 = o1h.join().unwrap();
+
+ THREADS_RUNNING.fetch_sub(1, Ordering::Relaxed);
+ (o1, o2)
+ } else {
+ (f1(), f2())
+ }
+}
diff --git a/old/evc/src/helpers/vector.rs b/old/evc/src/helpers/vector.rs
new file mode 100644
index 0000000..a4766d1
--- /dev/null
+++ b/old/evc/src/helpers/vector.rs
@@ -0,0 +1,126 @@
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Vec2<T> {
+ pub x: T,
+ pub y: T,
+}
+
+impl From<Vec2<isize>> for Vec2<u16> {
+ fn from(value: Vec2<isize>) -> Self {
+ Self {
+ x: value.x as u16,
+ y: value.y as u16,
+ }
+ }
+}
+impl From<Vec2<u16>> for Vec2<isize> {
+ fn from(value: Vec2<u16>) -> Self {
+ Self {
+ x: value.x as isize,
+ y: value.y as isize,
+ }
+ }
+}
+impl From<Vec2<f32>> for Vec2<isize> {
+ fn from(value: Vec2<f32>) -> Self {
+ Self {
+ x: value.x as isize,
+ y: value.y as isize,
+ }
+ }
+}
+
+impl Vec2<isize> {
+ pub const ZERO: Vec2<isize> = Vec2 { x: 0, y: 0 };
+ pub const UP: Vec2<isize> = Vec2 { x: 0, y: -1 };
+ pub const LEFT: Vec2<isize> = Vec2 { x: -1, y: 0 };
+}
+impl Vec2<f32> {
+ pub const ZERO: Vec2<f32> = Vec2 { x: 0.0, y: 0.0 };
+ pub const UP: Vec2<f32> = Vec2 { x: 0.0, y: -1.0 };
+ pub const LEFT: Vec2<f32> = Vec2 { x: -1.0, y: 0.0 };
+}
+
+impl<T: std::ops::Div<Output = T> + Copy> Vec2<T> {
+ pub fn downscale(&self, f: T) -> Self {
+ Self {
+ x: self.x / f,
+ y: self.y / f,
+ }
+ }
+}
+
+impl<T: std::ops::Mul<Output = T> + Copy> Vec2<T> {
+ pub fn scale(&self, f: T) -> Self {
+ Self {
+ x: self.x * f,
+ y: self.y * f,
+ }
+ }
+ pub fn area(&self) -> T {
+ self.x * self.y
+ }
+}
+
+impl Vec2<isize> {
+ pub fn x_only(&self) -> Self {
+ Self { x: self.x, y: 0 }
+ }
+ pub fn y_only(&self) -> Self {
+ Self { x: 0, y: self.y }
+ }
+}
+
+impl Into<Vec2<f32>> for Vec2<isize> {
+ fn into(self) -> Vec2<f32> {
+ Vec2 {
+ x: self.x as f32,
+ y: self.y as f32,
+ }
+ }
+}
+impl From<(isize, isize)> for Vec2<f32> {
+ fn from((x, y): (isize, isize)) -> Self {
+ Vec2 {
+ x: x as f32,
+ y: y as f32,
+ }
+ }
+}
+
+impl<T: std::ops::Add> std::ops::Add for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn add(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ }
+ }
+}
+impl<T: std::ops::Sub> std::ops::Sub for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn sub(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x - rhs.x,
+ y: self.y - rhs.y,
+ }
+ }
+}
+impl<T: std::ops::Mul> std::ops::Mul for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn mul(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x * rhs.x,
+ y: self.y * rhs.y,
+ }
+ }
+}
+
+impl<T> From<(T, T)> for Vec2<T> {
+ #[inline]
+ fn from((x, y): (T, T)) -> Self {
+ Vec2 { x, y }
+ }
+}
diff --git a/old/evc/src/lib.rs b/old/evc/src/lib.rs
new file mode 100644
index 0000000..d269471
--- /dev/null
+++ b/old/evc/src/lib.rs
@@ -0,0 +1,13 @@
+#![feature(box_patterns)]
+#![feature(const_fn_floating_point_arithmetic)]
+// #![feature(const_for)]
+// #![feature(const_mut_refs)]
+
+pub mod block;
+pub mod codec;
+pub mod debug;
+pub mod frame;
+pub mod view;
+pub mod refsampler;
+pub mod helpers;
+pub mod format;
diff --git a/old/evc/src/refsampler.rs b/old/evc/src/refsampler.rs
new file mode 100644
index 0000000..8a8f44f
--- /dev/null
+++ b/old/evc/src/refsampler.rs
@@ -0,0 +1,51 @@
+use crate::{
+ block::AdvancedReference,
+ format::ser::map_scalar8,
+ frame::Frame,
+ helpers::{matrix::Mat2, pixel::Pixel, vector::Vec2},
+ view::View,
+};
+
+#[derive(Debug, Clone)]
+pub struct Sampler<'a> {
+ pub view: View<&'a Frame>,
+ pub halfsize: Vec2<f32>,
+
+ pub translation: Vec2<f32>,
+ pub transform: Mat2<f32>,
+
+ pub value_scale: f32,
+}
+
+impl<'a> Sampler<'a> {
+ #[inline]
+ pub fn sample(&self, p: Vec2<f32>) -> Pixel {
+ self.view
+ .sample(self.translation + self.transform.transform(p - self.halfsize) + self.halfsize)
+ .scale(self.value_scale)
+ }
+ pub fn from_refblock(
+ view: View<&'a Frame>,
+ AdvancedReference {
+ translation,
+ transform,
+ value_scale,
+ }: &AdvancedReference,
+ ) -> Self {
+ Self {
+ transform: Mat2 {
+ a: map_scalar8(transform.a),
+ b: map_scalar8(transform.b),
+ c: map_scalar8(transform.c),
+ d: map_scalar8(transform.d),
+ },
+ halfsize: Into::<Vec2<f32>>::into(view.size).scale(0.5),
+ translation: Vec2 {
+ x: map_scalar8(translation.x),
+ y: map_scalar8(translation.y),
+ },
+ value_scale: 1.05f32.powf(*value_scale as f32),
+ view,
+ }
+ }
+}
diff --git a/old/evc/src/view.rs b/old/evc/src/view.rs
new file mode 100644
index 0000000..6f34965
--- /dev/null
+++ b/old/evc/src/view.rs
@@ -0,0 +1,241 @@
+use crate::{
+ frame::Frame,
+ helpers::{pixel::Pixel, vector::Vec2},
+ refsampler::Sampler,
+};
+use std::ops::{Index, IndexMut};
+
+#[derive(Debug, Clone)]
+pub struct View<T> {
+ pub frame: T,
+ pub offset: Vec2<isize>,
+ pub size: Vec2<isize>,
+}
+
+impl<T> View<T> {
+ pub fn new(frame: T, offset: Vec2<isize>, size: Vec2<isize>) -> Self {
+ Self {
+ frame,
+ offset,
+ size,
+ }
+ }
+ pub fn area(&self) -> isize {
+ self.size.x * self.size.y
+ }
+ pub fn center(&self) -> Vec2<isize> {
+ self.offset + self.size.downscale(2)
+ }
+}
+
+impl<T> View<&mut T> {
+ pub fn split_mut_unsafe(&mut self) -> [Self; 2] {
+ let vert = self.size.x > self.size.y;
+ [
+ Self {
+ frame: unsafe { std::mem::transmute::<&mut T, &mut T>(&mut self.frame) },
+ offset: self.offset,
+ size: if vert {
+ Vec2 {
+ x: self.size.x / 2,
+ y: self.size.y,
+ }
+ } else {
+ Vec2 {
+ x: self.size.x,
+ y: self.size.y / 2,
+ }
+ },
+ },
+ Self {
+ frame: unsafe { std::mem::transmute::<&mut T, &mut T>(&mut self.frame) },
+ offset: if vert {
+ Vec2 {
+ x: self.offset.x + self.size.x / 2,
+ y: self.offset.y,
+ }
+ } else {
+ Vec2 {
+ x: self.offset.x,
+ y: self.offset.y + self.size.y / 2,
+ }
+ },
+ size: if vert {
+ Vec2 {
+ x: self.size.x - self.size.x / 2,
+ y: self.size.y,
+ }
+ } else {
+ Vec2 {
+ x: self.size.x,
+ y: self.size.y - self.size.y / 2,
+ }
+ },
+ },
+ ]
+ }
+}
+impl<T: Copy> View<T> {
+ pub fn offset(&self, offset: Vec2<isize>) -> Self {
+ Self {
+ frame: self.frame,
+ offset: self.offset + offset,
+ size: self.size,
+ }
+ }
+ pub fn split(&self) -> [Self; 2] {
+ let vert = self.size.x > self.size.y;
+ [
+ Self {
+ frame: self.frame,
+ offset: self.offset,
+ size: if vert {
+ Vec2 {
+ x: self.size.x / 2,
+ y: self.size.y,
+ }
+ } else {
+ Vec2 {
+ x: self.size.x,
+ y: self.size.y / 2,
+ }
+ },
+ },
+ Self {
+ frame: self.frame,
+ offset: if vert {
+ Vec2 {
+ x: self.offset.x + self.size.x / 2,
+ y: self.offset.y,
+ }
+ } else {
+ Vec2 {
+ x: self.offset.x,
+ y: self.offset.y + self.size.y / 2,
+ }
+ },
+ size: if vert {
+ Vec2 {
+ x: self.size.x - self.size.x / 2,
+ y: self.size.y,
+ }
+ } else {
+ Vec2 {
+ x: self.size.x,
+ y: self.size.y - self.size.y / 2,
+ }
+ },
+ },
+ ]
+ }
+}
+impl<T: Index<Vec2<isize>, Output = Pixel>> View<&T> {
+ pub fn diff(va: &Self, vb: &Self) -> f64 {
+ assert_eq!(va.size, vb.size);
+ let mut acc = 0;
+ for x in 0..va.size.x {
+ for y in 0..va.size.y {
+ let a = va[(x, y)];
+ let b = vb[(x, y)];
+ acc += Pixel::distance(a, b);
+ }
+ }
+ acc as f64
+ }
+ pub fn diff_sampler(va: &Self, vb: &Sampler<'_>) -> f64 {
+ assert_eq!(va.size, vb.view.size);
+ let mut acc = 0;
+ for x in 0..va.size.x {
+ for y in 0..va.size.y {
+ let a = va[(x, y)];
+ let b = vb.sample(Vec2 {
+ x: x as f32,
+ y: y as f32,
+ });
+ acc += Pixel::distance(a, b);
+ }
+ }
+ acc as f64
+ }
+
+ pub fn pixels(&self) -> Vec<Pixel> {
+ let mut v = vec![];
+ for y in 0..self.size.y {
+ for x in 0..self.size.x {
+ v.push(self[(x, y)]);
+ }
+ }
+ v
+ }
+}
+impl View<&mut Frame> {
+ pub fn copy_from(&mut self, other: &View<&Frame>) {
+ for x in 0..self.size.x {
+ for y in 0..self.size.y {
+ self[(x, y)] = other[(x, y)];
+ }
+ }
+ }
+ pub fn copy_from_sampler(&mut self, other: &Sampler) {
+ for x in 0..self.size.x {
+ for y in 0..self.size.y {
+ self[(x, y)] = other.sample((x, y).into());
+ }
+ }
+ }
+
+ pub fn set_pixels(&mut self, pixels: &Vec<Pixel>) {
+ for y in 0..self.size.y {
+ for x in 0..self.size.x {
+ self[(x, y)] = pixels[(y * self.size.x + x) as usize]
+ }
+ }
+ }
+}
+impl View<&Frame> {
+ pub fn sample(&self, p: Vec2<f32>) -> Pixel {
+ self.frame.sample(p + self.offset.into())
+ }
+}
+
+impl<T: Index<Vec2<isize>, Output = Pixel>> Index<Vec2<isize>> for View<&T> {
+ type Output = Pixel;
+ #[inline]
+ fn index(&self, p: Vec2<isize>) -> &Self::Output {
+ &self.frame[self.offset + p]
+ }
+}
+impl<T: Index<Vec2<isize>, Output = Pixel>> Index<Vec2<isize>> for View<&mut T> {
+ type Output = Pixel;
+ #[inline]
+ fn index(&self, p: Vec2<isize>) -> &Self::Output {
+ &self.frame[self.offset + p]
+ }
+}
+impl<T: IndexMut<Vec2<isize>, Output = Pixel>> IndexMut<Vec2<isize>> for View<&mut T> {
+ #[inline]
+ fn index_mut(&mut self, p: Vec2<isize>) -> &mut Self::Output {
+ &mut self.frame[self.offset + p]
+ }
+}
+
+impl<T: Index<Vec2<isize>, Output = Pixel>> Index<(isize, isize)> for View<&T> {
+ type Output = Pixel;
+ #[inline]
+ fn index(&self, (x, y): (isize, isize)) -> &Self::Output {
+ &self[Vec2 { x, y }]
+ }
+}
+impl<T: Index<Vec2<isize>, Output = Pixel>> Index<(isize, isize)> for View<&mut T> {
+ type Output = Pixel;
+ #[inline]
+ fn index(&self, (x, y): (isize, isize)) -> &Self::Output {
+ &self[Vec2 { x, y }]
+ }
+}
+impl<T: IndexMut<Vec2<isize>, Output = Pixel>> IndexMut<(isize, isize)> for View<&mut T> {
+ #[inline]
+ fn index_mut(&mut self, (x, y): (isize, isize)) -> &mut Self::Output {
+ &mut self[Vec2 { x, y }]
+ }
+}
diff --git a/old/flowy/Cargo.toml b/old/flowy/Cargo.toml
new file mode 100644
index 0000000..5829cae
--- /dev/null
+++ b/old/flowy/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "flowy"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+wgpu = "0.20.0"
+bytemuck = { version = "1.15.0", features = ["derive"] }
+pollster = "0.3.0"
+env_logger = "0.11.3"
+log = "0.4.21"
+oneshot = "0.1.6"
+framework = { path = "../framework" }
diff --git a/old/flowy/src/main.rs b/old/flowy/src/main.rs
new file mode 100644
index 0000000..535de7e
--- /dev/null
+++ b/old/flowy/src/main.rs
@@ -0,0 +1,132 @@
+pub mod motion;
+
+use framework::{CodecMode, Framework};
+use log::{debug, info};
+use motion::{dec::MotionDecoder, enc::MotionEncoder, CommonBuffers, Params};
+use pollster::FutureExt;
+use std::{
+ io::{stdin, stdout, ErrorKind, Read, Write},
+ process::exit,
+ time::Instant,
+};
+use wgpu::{
+ Backends, DeviceDescriptor, Extent3d, Features, Instance, InstanceDescriptor, Limits,
+ MaintainBase, PowerPreference, RequestAdapterOptions,
+};
+
+use crate::motion::{debug::MotionDebugger, RoundParams};
+
+fn main() {
+ env_logger::init_from_env("LOG");
+ info!("allocating some stuff");
+ let t = Instant::now();
+
+ let instance = Instance::new(InstanceDescriptor {
+ backends: Backends::all(),
+ ..Default::default()
+ });
+
+ let adapter = instance
+ .request_adapter(&RequestAdapterOptions {
+ power_preference: PowerPreference::HighPerformance,
+ force_fallback_adapter: false,
+ compatible_surface: None,
+ })
+ .block_on()
+ .unwrap();
+
+ let (device, queue) = adapter
+ .request_device(
+ &DeviceDescriptor {
+ label: None,
+ required_features: Features::empty(),
+ required_limits: Limits::default(),
+ },
+ None,
+ )
+ .block_on()
+ .unwrap();
+
+ let (mut framework, fparams) = Framework::init();
+
+ let (width, height) = (fparams.width, fparams.height);
+ let bsize = 8;
+ let params = Params {
+ width,
+ height,
+ extent: Extent3d {
+ width: width as u32,
+ height: height as u32,
+ depth_or_array_layers: 1,
+ },
+ blocks_x: width / bsize,
+ blocks_y: height / bsize,
+ block_width: bsize,
+ block_height: bsize,
+ blocks: (width / bsize) * (height / bsize),
+ init_debug: true,
+ };
+
+ let bufs = CommonBuffers::create(&device, &params);
+ let menc = MotionEncoder::create(&device, &params, &bufs);
+ let mdec = MotionDecoder::create(&device, &params, &bufs);
+ let mdeb = MotionDebugger::create(&device, &params, &bufs);
+
+ let mut buffer = vec![0u8; width * height * 4];
+
+ menc.write_uniforms(&queue);
+ mdec.write_uniforms(&queue);
+ mdeb.write_uniforms(&queue);
+
+ info!("ready (setup took {:?})", t.elapsed());
+
+ let mut i = 0;
+ loop {
+ let rp = RoundParams {
+ swap: i % 2,
+ debug: fparams.debug == 2,
+ preview: fparams.debug > 0,
+ };
+ debug!("{params:?} {rp:?}");
+ debug!("read");
+ match stdin().read_exact(&mut buffer) {
+ Ok(_) => (),
+ Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
+ break;
+ }
+ Err(e) => Err(e).unwrap(),
+ }
+
+ framework.next_frame_manual();
+
+ debug!("upload");
+ bufs.upload_texture(&queue, &params, &rp, &buffer);
+
+ debug!("compute");
+ let mut encoder = device.create_command_encoder(&Default::default());
+
+ if let CodecMode::Encode = fparams.mode {
+ menc.pass(&mut encoder, &params, &rp);
+ }
+ mdec.pass(&mut encoder, &params, &rp);
+ if rp.debug {
+ mdeb.pass(&mut encoder, &params, &rp);
+ }
+ if rp.preview {
+ bufs.prepare_texture_download(&mut encoder, &params, &rp);
+ }
+
+ queue.submit(Some(encoder.finish()));
+ device.poll(MaintainBase::Wait);
+
+ debug!("download");
+ bufs.download_texture(&device, &mut buffer);
+
+ framework.encode_done(&[]);
+ debug!("write");
+ stdout().write_all(&buffer).unwrap();
+ i += 1;
+ }
+ eprintln!("done");
+ exit(0);
+}
diff --git a/old/flowy/src/motion/debug.rs b/old/flowy/src/motion/debug.rs
new file mode 100644
index 0000000..5e5f4ee
--- /dev/null
+++ b/old/flowy/src/motion/debug.rs
@@ -0,0 +1,176 @@
+use super::{CommonBuffers, Params, RoundParams};
+use bytemuck::{Pod, Zeroable};
+use std::mem::size_of;
+use wgpu::{
+ include_wgsl, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
+ BindGroupLayoutEntry, BindingType, Buffer, BufferDescriptor, BufferUsages, CommandEncoder,
+ ComputePipeline, ComputePipelineDescriptor, Device, PipelineCompilationOptions,
+ PipelineLayoutDescriptor, Queue, ShaderStages, TextureSampleType, TextureViewDimension,
+};
+
+pub struct MotionDebugger {
+ pipeline: ComputePipeline,
+ bind_groups: [BindGroup; 2],
+
+ uniform_buffer: Buffer,
+ uniform: DebuggerUniform,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
+pub struct DebuggerUniform {
+ block_size: [i32; 2],
+ output_stride: i32,
+ _pad: u32,
+}
+
+impl MotionDebugger {
+ pub fn create(device: &Device, params: &Params, bufs: &CommonBuffers) -> Self {
+ let uniform_buffer = device.create_buffer(&BufferDescriptor {
+ label: Some("encoder uniforms"),
+ size: size_of::<DebuggerUniform>() as u64,
+ usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+ let uniform = DebuggerUniform {
+ block_size: [params.block_width as i32, params.block_height as i32],
+ output_stride: (params.width / params.block_width) as i32,
+ ..Default::default()
+ };
+
+ let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
+ label: None,
+ entries: &[
+ BindGroupLayoutEntry {
+ binding: 0,
+ count: None,
+ ty: BindingType::Buffer {
+ ty: wgpu::BufferBindingType::Uniform,
+ has_dynamic_offset: false,
+ min_binding_size: None,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 1,
+ count: None,
+ ty: BindingType::Buffer {
+ has_dynamic_offset: false,
+ min_binding_size: None,
+ ty: wgpu::BufferBindingType::Storage { read_only: true },
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 2,
+ count: None,
+ ty: BindingType::Texture {
+ sample_type: TextureSampleType::Float { filterable: false },
+ view_dimension: TextureViewDimension::D2,
+ multisampled: false,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 3,
+ count: None,
+ ty: BindingType::Texture {
+ sample_type: TextureSampleType::Float { filterable: false },
+ view_dimension: TextureViewDimension::D2,
+ multisampled: false,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 4,
+ count: None,
+ ty: BindingType::StorageTexture {
+ access: wgpu::StorageTextureAccess::WriteOnly,
+ format: wgpu::TextureFormat::Rgba8Unorm,
+ view_dimension: TextureViewDimension::D2,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ ],
+ });
+
+ let bind_groups = [0, 1].map(|i| {
+ device.create_bind_group(&BindGroupDescriptor {
+ label: None,
+ layout: &bind_group_layout,
+ entries: &[
+ BindGroupEntry {
+ binding: 0,
+ resource: uniform_buffer.as_entire_binding(),
+ },
+ BindGroupEntry {
+ binding: 1,
+ resource: bufs.offsets.as_entire_binding(),
+ },
+ BindGroupEntry {
+ binding: 2,
+ resource: wgpu::BindingResource::TextureView(
+ &bufs.textures[i].create_view(&Default::default()),
+ ),
+ },
+ BindGroupEntry {
+ binding: 3,
+ resource: wgpu::BindingResource::TextureView(
+ &bufs.textures[1 - i].create_view(&Default::default()),
+ ),
+ },
+ BindGroupEntry {
+ binding: 4,
+ resource: wgpu::BindingResource::TextureView(
+ &bufs
+ .debug_output
+ .as_ref()
+ .unwrap()
+ .create_view(&Default::default()),
+ ),
+ },
+ ],
+ })
+ });
+
+ let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
+ label: None,
+ bind_group_layouts: &[&bind_group_layout],
+ push_constant_ranges: &[],
+ });
+
+ let module = device.create_shader_module(include_wgsl!("debug.wgsl"));
+ let pipeline = device.create_compute_pipeline(&ComputePipelineDescriptor {
+ label: None,
+ layout: Some(&pipeline_layout),
+ module: &module,
+ entry_point: "main",
+ compilation_options: PipelineCompilationOptions::default(),
+ });
+
+ Self {
+ bind_groups,
+ uniform,
+ uniform_buffer,
+ pipeline,
+ }
+ }
+
+ pub fn write_uniforms(&self, queue: &Queue) {
+ queue.write_buffer(
+ &self.uniform_buffer,
+ 0,
+ bytemuck::cast_slice(&[self.uniform]),
+ )
+ }
+
+ pub fn pass(&self, encoder: &mut CommandEncoder, params: &Params, rp: &RoundParams) {
+ let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
+ label: None,
+ timestamp_writes: None,
+ });
+ cpass.set_pipeline(&self.pipeline);
+ cpass.set_bind_group(0, &self.bind_groups[rp.swap], &[]);
+ cpass.dispatch_workgroups(params.blocks_x as u32, params.blocks_y as u32, 1);
+ }
+}
diff --git a/old/flowy/src/motion/debug.wgsl b/old/flowy/src/motion/debug.wgsl
new file mode 100644
index 0000000..d2c56a4
--- /dev/null
+++ b/old/flowy/src/motion/debug.wgsl
@@ -0,0 +1,50 @@
+
+
+struct Params {
+ block_size: vec2<i32>,
+ offsets_stride: u32
+}
+
+struct BlockOffset {
+ score: f32,
+ offset: vec2<i32>,
+ tint: vec3<f32>,
+}
+
+@group(0) @binding(0) var<uniform> params: Params;
+@group(0) @binding(1) var<storage, read> offsets: array<BlockOffset>;
+@group(0) @binding(2) var next: texture_2d<f32>;
+@group(0) @binding(3) var prev: texture_2d<f32>;
+@group(0) @binding(4) var out: texture_storage_2d<rgba8unorm, write>;
+
+@compute @workgroup_size(1)fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ let uv = vec2<i32>(global_id.xy) * params.block_size;
+
+ let bl = offsets[global_id.x + global_id.y * params.offsets_stride];
+
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv + vec2(x, y);
+ let col = vec4(colormap_vec(vec2<f32>(bl.offset) * 0.08),1.);
+ textureStore(out, base, col);
+ }
+ }
+
+ // for (var x = 0; x < params.block_size.x; x++) {
+ // for (var y = 0; y < params.block_size.y; y++) {
+ // let base = uv + vec2(x, y);
+ // let col_dec = textureLoad(prev, base + bl.offset, 0) + vec4(bl.tint, 1.);
+ // let col_orig = textureLoad(next, base, 0);
+
+ // let col = vec4( saturate((0.5) + (col_orig.rgb - col_dec.rgb) * 100.), 1.0);
+
+ // textureStore(out, base, col);
+ // }
+ // }
+}
+
+
+fn colormap_vec(v: vec2<f32>) -> vec3<f32> {
+ return vec3(v.y, v.x - 0.5 * v.y, -v.x - 0.5 * v.y);
+}
+
diff --git a/old/flowy/src/motion/dec.rs b/old/flowy/src/motion/dec.rs
new file mode 100644
index 0000000..cc1812f
--- /dev/null
+++ b/old/flowy/src/motion/dec.rs
@@ -0,0 +1,156 @@
+use super::{CommonBuffers, Params, RoundParams};
+use bytemuck::{Pod, Zeroable};
+use std::mem::size_of;
+use wgpu::{
+ include_wgsl, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
+ BindGroupLayoutEntry, BindingType, Buffer, BufferDescriptor, BufferUsages, CommandEncoder,
+ ComputePipeline, ComputePipelineDescriptor, Device, PipelineCompilationOptions,
+ PipelineLayoutDescriptor, Queue, ShaderStages, TextureSampleType, TextureViewDimension,
+};
+
+pub struct MotionDecoder {
+ pipeline: ComputePipeline,
+ bind_groups: [BindGroup; 2],
+
+ uniform_buffer: Buffer,
+ uniform: DecoderUniform,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
+pub struct DecoderUniform {
+ block_size: [i32; 2],
+ output_stride: i32,
+ _pad: u32,
+}
+
+impl MotionDecoder {
+ pub fn create(device: &Device, params: &Params, bufs: &CommonBuffers) -> Self {
+ let uniform_buffer = device.create_buffer(&BufferDescriptor {
+ label: Some("encoder uniforms"),
+ size: size_of::<DecoderUniform>() as u64,
+ usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+ let uniform = DecoderUniform {
+ block_size: [params.block_width as i32, params.block_height as i32],
+ output_stride: (params.width / params.block_width) as i32,
+ ..Default::default()
+ };
+
+ let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
+ label: None,
+ entries: &[
+ BindGroupLayoutEntry {
+ binding: 0,
+ count: None,
+ ty: BindingType::Buffer {
+ ty: wgpu::BufferBindingType::Uniform,
+ has_dynamic_offset: false,
+ min_binding_size: None,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 1,
+ count: None,
+ ty: BindingType::Buffer {
+ has_dynamic_offset: false,
+ min_binding_size: None,
+ ty: wgpu::BufferBindingType::Storage { read_only: true },
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 2,
+ count: None,
+ ty: BindingType::StorageTexture {
+ access: wgpu::StorageTextureAccess::WriteOnly,
+ format: wgpu::TextureFormat::Rgba8Unorm,
+ view_dimension: TextureViewDimension::D2,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 3,
+ count: None,
+ ty: BindingType::Texture {
+ sample_type: TextureSampleType::Float { filterable: false },
+ view_dimension: TextureViewDimension::D2,
+ multisampled: false,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ ],
+ });
+
+ let bind_groups = [0, 1].map(|i| {
+ device.create_bind_group(&BindGroupDescriptor {
+ label: None,
+ layout: &bind_group_layout,
+ entries: &[
+ BindGroupEntry {
+ binding: 0,
+ resource: uniform_buffer.as_entire_binding(),
+ },
+ BindGroupEntry {
+ binding: 1,
+ resource: bufs.offsets.as_entire_binding(),
+ },
+ BindGroupEntry {
+ binding: 2,
+ resource: wgpu::BindingResource::TextureView(
+ &bufs.textures[i].create_view(&Default::default()),
+ ),
+ },
+ BindGroupEntry {
+ binding: 3,
+ resource: wgpu::BindingResource::TextureView(
+ &bufs.textures[1 - i].create_view(&Default::default()),
+ ),
+ },
+ ],
+ })
+ });
+
+ let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
+ label: None,
+ bind_group_layouts: &[&bind_group_layout],
+ push_constant_ranges: &[],
+ });
+
+ let module = device.create_shader_module(include_wgsl!("dec.wgsl"));
+ let pipeline = device.create_compute_pipeline(&ComputePipelineDescriptor {
+ label: None,
+ layout: Some(&pipeline_layout),
+ compilation_options: PipelineCompilationOptions::default(),
+ module: &module,
+ entry_point: "main",
+ });
+
+ Self {
+ bind_groups,
+ uniform,
+ uniform_buffer,
+ pipeline,
+ }
+ }
+
+ pub fn write_uniforms(&self, queue: &Queue) {
+ queue.write_buffer(
+ &self.uniform_buffer,
+ 0,
+ bytemuck::cast_slice(&[self.uniform]),
+ )
+ }
+
+ pub fn pass(&self, encoder: &mut CommandEncoder, params: &Params, rp: &RoundParams) {
+ let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
+ label: None,
+ timestamp_writes: None,
+ });
+ cpass.set_pipeline(&self.pipeline);
+ cpass.set_bind_group(0, &self.bind_groups[rp.swap], &[]);
+ cpass.dispatch_workgroups(params.blocks_x as u32, params.blocks_y as u32, 1);
+ }
+}
diff --git a/old/flowy/src/motion/dec.wgsl b/old/flowy/src/motion/dec.wgsl
new file mode 100644
index 0000000..f4db974
--- /dev/null
+++ b/old/flowy/src/motion/dec.wgsl
@@ -0,0 +1,31 @@
+
+struct Params {
+ block_size: vec2<i32>,
+ offsets_stride: u32
+}
+
+struct BlockOffset {
+ score: f32,
+ offset: vec2<i32>,
+ tint: vec3<f32>,
+}
+
+@group(0) @binding(0) var<uniform> params: Params;
+@group(0) @binding(1) var<storage, read> offsets: array<BlockOffset>;
+@group(0) @binding(2) var next: texture_storage_2d<rgba8unorm, write>;
+@group(0) @binding(3) var prev: texture_2d<f32>;
+
+@compute @workgroup_size(1) fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ let uv = vec2<i32>(global_id.xy) * params.block_size;
+
+ let bl = offsets[global_id.x + global_id.y * params.offsets_stride];
+
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv+vec2(x,y);
+ let col = textureLoad(prev, base+bl.offset, 0)+vec4(bl.tint,1.);
+ textureStore(next, base, col);
+ }}
+}
+
+
diff --git a/old/flowy/src/motion/enc-old.wgsl b/old/flowy/src/motion/enc-old.wgsl
new file mode 100644
index 0000000..b94bac9
--- /dev/null
+++ b/old/flowy/src/motion/enc-old.wgsl
@@ -0,0 +1,88 @@
+
+struct Params {
+ block_size: vec2<i32>,
+ offsets_stride: u32,
+ search_radius: i32,
+ skip_threshold: f32,
+}
+
+struct BlockOffset {
+ score: f32,
+ offset: vec2<i32>,
+ tint: vec3<f32>,
+}
+
+@group(0) @binding(0) var<uniform> params: Params;
+@group(0) @binding(1) var<storage, read_write> offsets: array<BlockOffset>;
+@group(0) @binding(2) var next: texture_2d<f32>;
+@group(0) @binding(3) var prev: texture_2d<f32>;
+
+@compute @workgroup_size(1) fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ let uv = vec2<i32>(global_id.xy) * params.block_size;
+
+ let _f = search_offset(uv);
+ let best_err = _f.error;
+ let best_offset = _f.offset;
+
+ var best_tint = vec3(0.);
+
+ var average_pcol = vec3(0.);
+ var average_ncol = vec3(0.);
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv+vec2(x,y);
+ average_pcol += textureLoad(prev, base+best_offset, 0).rgb;
+ average_ncol += textureLoad(next, base, 0).rgb;
+ }}
+
+ let tint = (average_ncol - average_pcol) / f32(params.block_size.x * params.block_size.y);
+
+ var err = 0.;
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv+vec2(x,y);
+ let pcol = textureLoad(prev, base+best_offset, 0).rgb+tint;
+ let ncol = textureLoad(next, base, 0).rgb;
+ err += distance(pcol, ncol);
+ }}
+ if err < best_err {
+ best_tint = tint;
+ }
+
+ offsets[global_id.x + global_id.y * params.offsets_stride] = BlockOffset(best_err, best_offset, best_tint);
+}
+
+struct SearchRes {offset: vec2<i32>, error: f32}
+
+fn search_offset(uv: vec2<i32>) -> SearchRes {
+ var best_err = 100000000.;
+ var best_offset = vec2(0);
+ // TODO: better ordering
+ for (var ox = -params.search_radius; ox <= params.search_radius; ox++) {
+ for (var oy = -params.search_radius; oy <= params.search_radius; oy++) {
+ let offset = vec2(ox,oy);
+
+ var err = 0.;
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv+vec2(x,y);
+ let pcol = textureLoad(prev, base+offset, 0).rgb;
+ let ncol = textureLoad(next, base, 0).rgb;
+ err += distance(pcol, ncol);
+ }}
+
+ if err < best_err {
+ best_err = err;
+ best_offset = offset;
+ if err < params.skip_threshold {
+ return SearchRes(offset, err);
+ }
+ }
+ }}
+ return SearchRes(best_offset,best_err);
+}
+
+// fn colormap_vec(v: vec2<f32>) -> vec3<f32> {
+// return vec3(v.y, v.x - 0.5 * v.y, -v.x - 0.5 * v.y);
+// }
+
diff --git a/old/flowy/src/motion/enc.rs b/old/flowy/src/motion/enc.rs
new file mode 100644
index 0000000..a8fd96f
--- /dev/null
+++ b/old/flowy/src/motion/enc.rs
@@ -0,0 +1,160 @@
+use super::{CommonBuffers, Params, RoundParams};
+use bytemuck::{Pod, Zeroable};
+use std::mem::size_of;
+use wgpu::{
+ include_wgsl, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
+ BindGroupLayoutEntry, BindingType, Buffer, BufferDescriptor, BufferUsages, CommandEncoder,
+ ComputePipeline, ComputePipelineDescriptor, Device, PipelineCompilationOptions,
+ PipelineLayoutDescriptor, Queue, ShaderStages, TextureSampleType, TextureViewDimension,
+};
+
+pub struct MotionEncoder {
+ pipeline: ComputePipeline,
+ bind_groups: [BindGroup; 2],
+
+ uniform_buffer: Buffer,
+ uniform: EncoderUniform,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
+pub struct EncoderUniform {
+ block_size: [i32; 2],
+ output_stride: u32,
+ search_radius: i32,
+ skip_threshold: f32,
+ _pad: u32,
+}
+
+impl MotionEncoder {
+ pub fn create(device: &Device, params: &Params, bufs: &CommonBuffers) -> Self {
+ let uniform_buffer = device.create_buffer(&BufferDescriptor {
+ label: Some("encoder uniforms"),
+ size: size_of::<EncoderUniform>() as u64,
+ usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+ let uniform = EncoderUniform {
+ block_size: [params.block_width as i32, params.block_height as i32],
+ output_stride: (params.width / params.block_width) as u32,
+ search_radius: 24,
+ skip_threshold: 1.,
+ ..Default::default()
+ };
+
+ let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
+ label: None,
+ entries: &[
+ BindGroupLayoutEntry {
+ binding: 0,
+ count: None,
+ ty: BindingType::Buffer {
+ ty: wgpu::BufferBindingType::Uniform,
+ has_dynamic_offset: false,
+ min_binding_size: None,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 1,
+ count: None,
+ ty: BindingType::Buffer {
+ has_dynamic_offset: false,
+ min_binding_size: None,
+ ty: wgpu::BufferBindingType::Storage { read_only: false },
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 2,
+ count: None,
+ ty: BindingType::Texture {
+ sample_type: TextureSampleType::Float { filterable: false },
+ view_dimension: TextureViewDimension::D2,
+ multisampled: false,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ BindGroupLayoutEntry {
+ binding: 3,
+ count: None,
+ ty: BindingType::Texture {
+ sample_type: TextureSampleType::Float { filterable: false },
+ view_dimension: TextureViewDimension::D2,
+ multisampled: false,
+ },
+ visibility: ShaderStages::COMPUTE,
+ },
+ ],
+ });
+
+ let bind_groups = [0, 1].map(|i| {
+ device.create_bind_group(&BindGroupDescriptor {
+ label: None,
+ layout: &bind_group_layout,
+ entries: &[
+ BindGroupEntry {
+ binding: 0,
+ resource: uniform_buffer.as_entire_binding(),
+ },
+ BindGroupEntry {
+ binding: 1,
+ resource: bufs.offsets.as_entire_binding(),
+ },
+ BindGroupEntry {
+ binding: 2,
+ resource: wgpu::BindingResource::TextureView(
+ &bufs.textures[i].create_view(&Default::default()),
+ ),
+ },
+ BindGroupEntry {
+ binding: 3,
+ resource: wgpu::BindingResource::TextureView(
+ &bufs.textures[1 - i].create_view(&Default::default()),
+ ),
+ },
+ ],
+ })
+ });
+
+ let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
+ label: None,
+ bind_group_layouts: &[&bind_group_layout],
+ push_constant_ranges: &[],
+ });
+
+ let module = device.create_shader_module(include_wgsl!("enc.wgsl"));
+ let pipeline = device.create_compute_pipeline(&ComputePipelineDescriptor {
+ label: None,
+ compilation_options: PipelineCompilationOptions::default(),
+ layout: Some(&pipeline_layout),
+ module: &module,
+ entry_point: "main",
+ });
+
+ Self {
+ bind_groups,
+ uniform,
+ uniform_buffer,
+ pipeline,
+ }
+ }
+
+ pub fn write_uniforms(&self, queue: &Queue) {
+ queue.write_buffer(
+ &self.uniform_buffer,
+ 0,
+ bytemuck::cast_slice(&[self.uniform]),
+ )
+ }
+
+ pub fn pass(&self, encoder: &mut CommandEncoder, params: &Params, rp: &RoundParams) {
+ let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
+ label: None,
+ timestamp_writes: None,
+ });
+ cpass.set_pipeline(&self.pipeline);
+ cpass.set_bind_group(0, &self.bind_groups[rp.swap], &[]);
+ cpass.dispatch_workgroups(params.blocks_x as u32, params.blocks_y as u32, 1);
+ }
+}
diff --git a/old/flowy/src/motion/enc.wgsl b/old/flowy/src/motion/enc.wgsl
new file mode 100644
index 0000000..dbc6410
--- /dev/null
+++ b/old/flowy/src/motion/enc.wgsl
@@ -0,0 +1,121 @@
+
+struct Params {
+ block_size: vec2<i32>,
+ offsets_stride: u32,
+ search_radius: i32,
+ skip_threshold: f32,
+}
+
+struct BlockOffset {
+ score: f32,
+ offset: vec2<i32>,
+ tint: vec3<f32>,
+}
+
+@group(0) @binding(0) var<uniform> params: Params;
+@group(0) @binding(1) var<storage, read_write> offsets: array<BlockOffset>;
+@group(0) @binding(2) var next: texture_2d<f32>;
+@group(0) @binding(3) var prev: texture_2d<f32>;
+
+var<private> best_offset: vec2<i32> = vec2(0);
+var<private> best_error: f32 = 100000.;
+var<private> best_tint: vec3<f32> = vec3(0.);
+
+@compute @workgroup_size(1)
+fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ let uv = vec2<i32>(global_id.xy) * params.block_size;
+
+ loop {
+ test_offset(uv, vec2(0, 0));
+ if best_error < params.skip_threshold { break; }
+ apply_tint(uv);
+ if best_error < params.skip_threshold { break; }
+ best_tint = vec3(0.);
+ best_error = 10000000.;
+
+ do_dist(uv, 64);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 32);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 24);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 16);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 12);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 8);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 6);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 4);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 3);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 2);
+ if best_error < params.skip_threshold { break; }
+ do_dist(uv, 1);
+ if best_error < params.skip_threshold { break; }
+
+ apply_tint(uv);
+ break;
+ }
+
+ offsets[global_id.x + global_id.y * params.offsets_stride] = BlockOffset(best_error, best_offset, best_tint);
+}
+
+fn do_dist(uv: vec2<i32>, n: i32) {
+ test_offset(uv, vec2(0, n));
+ test_offset(uv, vec2(n, n));
+ test_offset(uv, vec2(n, 0));
+ test_offset(uv, vec2(n, -n));
+ test_offset(uv, vec2(0, -n));
+ test_offset(uv, vec2(-n, -n));
+ test_offset(uv, vec2(-n, 0));
+ test_offset(uv, vec2(-n, n));
+}
+
+fn apply_tint(uv: vec2<i32>) {
+
+ var average_pcol = vec3(0.);
+ var average_ncol = vec3(0.);
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv + vec2(x, y);
+ average_pcol += textureLoad(prev, base + best_offset, 0).rgb;
+ average_ncol += textureLoad(next, base, 0).rgb;
+ }
+ }
+
+ let tint = (average_ncol - average_pcol) / f32(params.block_size.x * params.block_size.y);
+
+ var err = 0.;
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv + vec2(x, y);
+ let pcol = textureLoad(prev, base + best_offset, 0).rgb + tint;
+ let ncol = textureLoad(next, base, 0).rgb;
+ err += distance(pcol, ncol);
+ }
+ }
+ if err < best_error {
+ best_error = err;
+ best_tint = tint;
+ }
+}
+
+fn test_offset(uv: vec2<i32>, offset: vec2<i32>) {
+ var err = 0.;
+ for (var x = 0; x < params.block_size.x; x++) {
+ for (var y = 0; y < params.block_size.y; y++) {
+ let base = uv + vec2(x, y);
+ let pcol = textureLoad(prev, base + offset, 0).rgb;
+ let ncol = textureLoad(next, base, 0).rgb;
+ err += distance(pcol, ncol);
+ }
+ }
+ if err < best_error {
+ best_error = err;
+ best_offset = offset;
+ }
+}
+
diff --git a/old/flowy/src/motion/mod.rs b/old/flowy/src/motion/mod.rs
new file mode 100644
index 0000000..337290f
--- /dev/null
+++ b/old/flowy/src/motion/mod.rs
@@ -0,0 +1,203 @@
+pub mod debug;
+pub mod dec;
+pub mod enc;
+
+use std::mem::size_of;
+use wgpu::{
+ Buffer, BufferUsages, CommandEncoder, Device, Extent3d, ImageCopyTexture, Origin3d, Queue,
+ Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
+};
+
+#[derive(Debug)]
+pub struct Params {
+ pub width: usize,
+ pub height: usize,
+ pub extent: Extent3d,
+ pub block_width: usize,
+ pub block_height: usize,
+ pub blocks_x: usize,
+ pub blocks_y: usize,
+ pub blocks: usize,
+ pub init_debug: bool,
+}
+
+#[derive(Debug)]
+pub struct RoundParams {
+ pub swap: usize,
+ pub debug: bool,
+ pub preview: bool,
+}
+
+pub struct CommonBuffers {
+ textures: [Texture; 2],
+ offsets: Buffer,
+ offsets_download: Option<Buffer>,
+ debug_output: Option<Texture>,
+ texture_download: Option<Buffer>,
+}
+
+#[repr(C)]
+pub struct BlockOffset {
+ score: f32,
+ _pad: u32,
+ offset: [f32; 2],
+ tint: [f32; 3],
+}
+
+impl CommonBuffers {
+ pub fn create(device: &Device, params: &Params) -> Self {
+ let textures = [(), ()].map(|_| {
+ device.create_texture(&TextureDescriptor {
+ label: None,
+ size: params.extent,
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: TextureDimension::D2,
+ format: TextureFormat::Rgba8Unorm,
+ usage: TextureUsages::TEXTURE_BINDING
+ | TextureUsages::STORAGE_BINDING
+ | TextureUsages::COPY_DST
+ | TextureUsages::COPY_SRC,
+ view_formats: &[],
+ })
+ });
+
+ let debug_output = Some(device.create_texture(&TextureDescriptor {
+ label: None,
+ size: params.extent,
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: TextureDimension::D2,
+ format: TextureFormat::Rgba8Unorm,
+ usage: TextureUsages::STORAGE_BINDING | TextureUsages::COPY_SRC,
+ view_formats: &[],
+ }));
+
+ let texture_download = Some(device.create_buffer(&wgpu::BufferDescriptor {
+ label: None,
+ size: (params.width * params.height * 4) as u64,
+ usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
+ mapped_at_creation: false,
+ }));
+
+ let offsets = device.create_buffer(&wgpu::BufferDescriptor {
+ label: None,
+ size: (params.blocks * size_of::<BlockOffset>()) as u64,
+ usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
+ mapped_at_creation: false,
+ });
+ let offsets_download = Some(device.create_buffer(&wgpu::BufferDescriptor {
+ label: None,
+ size: (params.blocks * size_of::<BlockOffset>()) as u64,
+ usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
+ mapped_at_creation: false,
+ }));
+
+ Self {
+ debug_output,
+ textures,
+ offsets_download,
+ offsets,
+ texture_download,
+ }
+ }
+
+ pub fn upload_texture(&self, queue: &Queue, params: &Params, rp: &RoundParams, buffer: &[u8]) {
+ queue.write_texture(
+ ImageCopyTexture {
+ aspect: wgpu::TextureAspect::All,
+ mip_level: 0,
+ origin: Origin3d::ZERO,
+ texture: &self.textures[rp.swap],
+ },
+ buffer,
+ wgpu::ImageDataLayout {
+ offset: 0,
+ bytes_per_row: Some(params.extent.width * 4),
+ rows_per_image: Some(params.extent.height),
+ },
+ params.extent,
+ );
+ }
+ // pub fn upload_offsets(&self, queue: &Queue, params: &Params, rp: &RoundParams, buffer: &[u8]) {
+ // queue.write_texture(
+ // ImageCopyTexture {
+ // aspect: wgpu::TextureAspect::All,
+ // mip_level: 0,
+ // origin: Origin3d::ZERO,
+ // texture: &self.textures[rp.swap],
+ // },
+ // buffer,
+ // wgpu::ImageDataLayout {
+ // offset: 0,
+ // bytes_per_row: Some(params.extent.width * 4),
+ // rows_per_image: Some(params.extent.height),
+ // },
+ // params.extent,
+ // );
+ // }
+
+ pub fn prepare_texture_download(
+ &self,
+ encoder: &mut CommandEncoder,
+ params: &Params,
+ rp: &RoundParams,
+ ) {
+ encoder.copy_texture_to_buffer(
+ wgpu::ImageCopyTexture {
+ texture: if rp.debug {
+ self.debug_output.as_ref().unwrap()
+ } else {
+ &self.textures[rp.swap]
+ },
+ mip_level: 0,
+ origin: wgpu::Origin3d::ZERO,
+ aspect: wgpu::TextureAspect::All,
+ },
+ wgpu::ImageCopyBuffer {
+ buffer: self.texture_download.as_ref().unwrap(),
+ layout: wgpu::ImageDataLayout {
+ offset: 0,
+ bytes_per_row: Some(params.extent.width * 4),
+ rows_per_image: Some(params.extent.height),
+ },
+ },
+ params.extent,
+ );
+ }
+ pub fn prepare_offsets_download(&self, encoder: &mut CommandEncoder, params: &Params) {
+ encoder.copy_buffer_to_buffer(
+ &self.offsets,
+ 0,
+ self.offsets_download.as_ref().unwrap(),
+ 0,
+ (params.blocks * size_of::<BlockOffset>()) as u64,
+ );
+ }
+
+ pub fn download_offsets(&self, device: &Device, buffer: &mut [u8]) {
+ let buffer_slice = self.offsets_download.as_ref().unwrap().slice(..);
+ let (sender, receiver) = oneshot::channel();
+ buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
+ device.poll(wgpu::Maintain::Wait);
+ receiver.recv().unwrap().unwrap();
+ {
+ let view = buffer_slice.get_mapped_range();
+ buffer.copy_from_slice(&view[..]);
+ }
+ self.offsets_download.as_ref().unwrap().unmap();
+ }
+
+ pub fn download_texture(&self, device: &Device, buffer: &mut [u8]) {
+ let buffer_slice = self.texture_download.as_ref().unwrap().slice(..);
+ let (sender, receiver) = oneshot::channel();
+ buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
+ device.poll(wgpu::Maintain::Wait);
+ receiver.recv().unwrap().unwrap();
+ {
+ let view = buffer_slice.get_mapped_range();
+ buffer.copy_from_slice(&view[..]);
+ }
+ self.texture_download.as_ref().unwrap().unmap();
+ }
+}
diff --git a/old/framework/Cargo.toml b/old/framework/Cargo.toml
new file mode 100644
index 0000000..265f119
--- /dev/null
+++ b/old/framework/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "framework"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/old/framework/src/common/huffman.rs b/old/framework/src/common/huffman.rs
new file mode 100644
index 0000000..474cfab
--- /dev/null
+++ b/old/framework/src/common/huffman.rs
@@ -0,0 +1,181 @@
+#[derive(Debug, Clone)]
+enum HT {
+ Branch(Box<HT>, Box<HT>),
+ Terminal(u8),
+}
+
+pub fn encode_huff(buf: &[u8]) -> Vec<u8> {
+ let mut w = BitIO::new(Vec::new());
+ assert!(buf.len() <= 0xffffff, "huff frame too big");
+ w.wbyte((buf.len() & 0xff) as u8);
+ w.wbyte(((buf.len() >> 8) & 0xff) as u8);
+ w.wbyte(((buf.len() >> 16) & 0xff) as u8);
+
+ let mut probs = [0usize; 256];
+ for b in buf {
+ probs[*b as usize] += 1;
+ }
+ let tree = HT::from_probabilities(probs);
+ let mut table = [0u32; 256];
+ tree.create_lut(&mut table, 1);
+ tree.write(&mut w);
+
+ for b in buf {
+ let mut k = table[*b as usize];
+ while k != 1 {
+ w.wbit((k & 1) == 1);
+ k >>= 1;
+ }
+ }
+
+ w.flush();
+ w.buffer
+}
+
+pub fn read_huff(r: Vec<u8>) -> Vec<u8> {
+ let mut r = BitIO::new(r);
+
+ let mut len = 0usize;
+ len |= r.rbyte() as usize;
+ len |= (r.rbyte() as usize) << 8;
+ len |= (r.rbyte() as usize) << 16;
+
+ let root = HT::read(&mut r);
+ let root = match &root {
+ HT::Branch(a, b) => [a, b],
+ _ => panic!("no!"),
+ };
+
+ let mut cursor = root;
+ let mut buf = Vec::new();
+ while buf.len() != len {
+ let v = r.rbit();
+ match cursor[v as usize].as_ref() {
+ HT::Branch(a, b) => {
+ cursor = [a, b];
+ }
+ HT::Terminal(n) => {
+ buf.push(*n);
+ cursor = root;
+ }
+ }
+ }
+ buf
+}
+
+impl HT {
+ pub fn from_probabilities(ps: [usize; 256]) -> Self {
+ let mut parts = ps
+ .into_iter()
+ .enumerate()
+ .map(|(n, p)| (p, HT::Terminal(n as u8)))
+ .collect::<Vec<_>>();
+
+ while parts.len() != 1 {
+ parts.sort_by_key(|e| -(e.0 as isize));
+ let ((ap, at), (bp, bt)) = (parts.pop().unwrap(), parts.pop().unwrap());
+ parts.push((ap + bp + 1, HT::Branch(Box::new(at), Box::new(bt))))
+ }
+ parts[0].1.clone()
+ }
+ pub fn create_lut(&self, table: &mut [u32; 256], mut prefix: u32) {
+ assert!(self.depth() < 30, "too deep! doesnt fit {}", self.depth());
+ match self {
+ HT::Branch(a, b) => {
+ let pz = 32 - prefix.leading_zeros();
+ prefix ^= 1 << pz;
+ prefix ^= 1 << (pz - 1);
+ a.create_lut(table, prefix);
+ prefix ^= 1 << (pz - 1);
+ b.create_lut(table, prefix);
+ }
+ HT::Terminal(n) => {
+ assert_eq!(table[*n as usize], 0);
+ table[*n as usize] = prefix
+ }
+ }
+ }
+ pub fn depth(&self) -> usize {
+ match self {
+ HT::Branch(a, b) => a.depth().max(b.depth()) + 1,
+ HT::Terminal(_) => 0,
+ }
+ }
+ pub fn write(&self, w: &mut BitIO) {
+ match self {
+ HT::Branch(a, b) => {
+ w.wbit(false);
+ a.write(w);
+ b.write(w);
+ }
+ HT::Terminal(n) => {
+ w.wbit(true);
+ w.wbyte(*n);
+ }
+ }
+ }
+ pub fn read(r: &mut BitIO) -> Self {
+ match r.rbit() {
+ false => Self::Branch(Box::new(Self::read(r)), Box::new(Self::read(r))),
+ true => Self::Terminal(r.rbyte()),
+ }
+ }
+}
+
+pub struct BitIO {
+ buffer: Vec<u8>,
+ byte: u8,
+ position: usize,
+}
+impl BitIO {
+ pub fn new(inner: Vec<u8>) -> Self {
+ Self {
+ buffer: inner,
+ byte: 0,
+ position: 0,
+ }
+ }
+ #[inline]
+ pub fn wbit(&mut self, b: bool) {
+ self.byte <<= 1;
+ self.byte |= b as u8;
+ self.position += 1;
+ if self.position & 0b111 == 0 {
+ self.buffer.push(self.byte)
+ }
+ }
+ #[inline]
+ pub fn wbyte(&mut self, v: u8) {
+ for i in 0..8 {
+ self.wbit((v & (1 << i)) != 0);
+ }
+ }
+
+ #[inline]
+ pub fn flush(&mut self) {
+ while self.position & 0b111 != 0 {
+ self.wbit(false);
+ }
+ }
+
+ #[inline]
+ pub fn rbit(&mut self) -> bool {
+ if self.position & 0b111 == 0 {
+ self.byte = self.buffer[self.position >> 3];
+ }
+
+ let v = (self.byte & 0b10000000) != 0;
+ self.byte <<= 1;
+ self.position += 1;
+ v
+ }
+
+ #[inline]
+ pub fn rbyte(&mut self) -> u8 {
+ let mut v = 0u8;
+ for i in 0..8 {
+ v |= (self.rbit() as u8) << i;
+ }
+ v
+ }
+}
diff --git a/old/framework/src/common/mod.rs b/old/framework/src/common/mod.rs
new file mode 100644
index 0000000..6f86024
--- /dev/null
+++ b/old/framework/src/common/mod.rs
@@ -0,0 +1 @@
+pub mod huffman;
diff --git a/old/framework/src/lib.rs b/old/framework/src/lib.rs
new file mode 100644
index 0000000..af535c5
--- /dev/null
+++ b/old/framework/src/lib.rs
@@ -0,0 +1,153 @@
+use std::{
+ io::{self, stdin, stdout, Read, Write},
+ ops::{Index, IndexMut},
+ time::Instant,
+};
+use vector::{UVec2, Vec2};
+
+pub mod common;
+pub mod vector;
+
+#[derive(Clone)]
+pub struct CodecParams {
+ pub width: usize,
+ pub height: usize,
+ pub debug: usize,
+ pub mode: CodecMode,
+}
+#[derive(Clone)]
+pub enum CodecMode {
+ Encode,
+ Decode,
+}
+
+pub struct Framework {
+ process_start: Instant,
+ params: CodecParams,
+}
+
+impl Framework {
+ pub fn init() -> (Self, CodecParams) {
+ let width = std::env::var("V_WIDTH").unwrap().parse().unwrap();
+ let height = std::env::var("V_HEIGHT").unwrap().parse().unwrap();
+ let debug = std::env::var("V_DEBUG").unwrap().parse().unwrap();
+ let mode = match std::env::var("V_MODE").unwrap().as_str() {
+ "encode" => CodecMode::Encode,
+ "decode" => CodecMode::Decode,
+ _ => panic!("invalid mode"),
+ };
+ let params = CodecParams {
+ height,
+ width,
+ debug,
+ mode,
+ };
+ (
+ Self {
+ params: params.clone(),
+ process_start: Instant::now(),
+ },
+ params,
+ )
+ }
+
+ pub fn next_frame(&mut self) -> Option<Frame> {
+ let f = Some(Frame::read(self.params.width, self.params.height, stdin()).unwrap());
+ self.next_frame_manual();
+ f
+ }
+ pub fn next_frame_manual(&mut self) {
+ self.process_start = Instant::now();
+ }
+ pub fn encode_done(&mut self, output: &[u8]) {
+ let el = self.process_start.elapsed();
+ eprintln!(
+ "size={}KB ratio={:.02} t={el:?}",
+ output.len() / 1000,
+ (self.params.width * self.params.height * 3) as f32 / output.len() as f32
+ )
+ }
+
+ pub fn next_chunk(&mut self, _buffer: &mut Vec<u8>) -> bool {
+ true
+ }
+ pub fn decode_done(&mut self, output: &Frame) {
+ output.write(stdout()).unwrap();
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Frame {
+ pub width: usize,
+ pub height: usize,
+ pub pixels: Vec<Pixel<u8>>,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Pixel<T> {
+ pub r: T,
+ pub g: T,
+ pub b: T,
+}
+pub type EPixel = Pixel<u8>;
+
+impl Frame {
+ pub fn new(width: usize, height: usize) -> Self {
+ Self {
+ height,
+ width,
+ pixels: vec![Pixel::BLACK; width * height],
+ }
+ }
+ pub fn read(width: usize, height: usize, mut r: impl Read) -> io::Result<Self> {
+ let mut f = Frame::new(width, height);
+ let p = unsafe {
+ std::slice::from_raw_parts_mut(
+ f.pixels.as_mut_slice().as_mut_ptr() as *mut u8,
+ width * height * 3,
+ )
+ };
+ r.read_exact(p)?;
+ Ok(f)
+ }
+ pub fn write(&self, mut w: impl Write) -> io::Result<()> {
+ let p = unsafe {
+ std::slice::from_raw_parts(
+ self.pixels.as_slice().as_ptr() as *const u8,
+ self.width * self.height * 3,
+ )
+ };
+ w.write_all(p)
+ }
+}
+
+impl Index<(usize, usize)> for Frame {
+ type Output = Pixel<u8>;
+ #[inline]
+ fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
+ &self.pixels[x + y * self.width]
+ }
+}
+impl IndexMut<(usize, usize)> for Frame {
+ #[inline]
+ fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output {
+ &mut self.pixels[x + y * self.width]
+ }
+}
+impl Index<UVec2> for Frame {
+ type Output = Pixel<u8>;
+ #[inline]
+ fn index(&self, Vec2 { x, y }: UVec2) -> &Self::Output {
+ &self.pixels[x + y * self.width]
+ }
+}
+impl IndexMut<UVec2> for Frame {
+ #[inline]
+ fn index_mut(&mut self, Vec2 { x, y }: UVec2) -> &mut Self::Output {
+ &mut self.pixels[x + y * self.width]
+ }
+}
+
+impl Pixel<u8> {
+ pub const BLACK: Pixel<u8> = Pixel { r: 0, g: 0, b: 0 };
+}
diff --git a/old/framework/src/vector.rs b/old/framework/src/vector.rs
new file mode 100644
index 0000000..5227d04
--- /dev/null
+++ b/old/framework/src/vector.rs
@@ -0,0 +1,128 @@
+pub type UVec2 = Vec2<usize>;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Vec2<T> {
+ pub x: T,
+ pub y: T,
+}
+
+impl From<Vec2<isize>> for Vec2<u16> {
+ fn from(value: Vec2<isize>) -> Self {
+ Self {
+ x: value.x as u16,
+ y: value.y as u16,
+ }
+ }
+}
+impl From<Vec2<u16>> for Vec2<isize> {
+ fn from(value: Vec2<u16>) -> Self {
+ Self {
+ x: value.x as isize,
+ y: value.y as isize,
+ }
+ }
+}
+impl From<Vec2<f32>> for Vec2<isize> {
+ fn from(value: Vec2<f32>) -> Self {
+ Self {
+ x: value.x as isize,
+ y: value.y as isize,
+ }
+ }
+}
+
+impl Vec2<isize> {
+ pub const ZERO: Vec2<isize> = Vec2 { x: 0, y: 0 };
+ pub const UP: Vec2<isize> = Vec2 { x: 0, y: -1 };
+ pub const LEFT: Vec2<isize> = Vec2 { x: -1, y: 0 };
+}
+impl Vec2<f32> {
+ pub const ZERO: Vec2<f32> = Vec2 { x: 0.0, y: 0.0 };
+ pub const UP: Vec2<f32> = Vec2 { x: 0.0, y: -1.0 };
+ pub const LEFT: Vec2<f32> = Vec2 { x: -1.0, y: 0.0 };
+}
+
+impl<T: std::ops::Div<Output = T> + Copy> Vec2<T> {
+ pub fn downscale(&self, f: T) -> Self {
+ Self {
+ x: self.x / f,
+ y: self.y / f,
+ }
+ }
+}
+
+impl<T: std::ops::Mul<Output = T> + Copy> Vec2<T> {
+ pub fn scale(&self, f: T) -> Self {
+ Self {
+ x: self.x * f,
+ y: self.y * f,
+ }
+ }
+ pub fn area(&self) -> T {
+ self.x * self.y
+ }
+}
+
+impl Vec2<isize> {
+ pub fn x_only(&self) -> Self {
+ Self { x: self.x, y: 0 }
+ }
+ pub fn y_only(&self) -> Self {
+ Self { x: 0, y: self.y }
+ }
+}
+
+impl Into<Vec2<f32>> for Vec2<isize> {
+ fn into(self) -> Vec2<f32> {
+ Vec2 {
+ x: self.x as f32,
+ y: self.y as f32,
+ }
+ }
+}
+impl From<(isize, isize)> for Vec2<f32> {
+ fn from((x, y): (isize, isize)) -> Self {
+ Vec2 {
+ x: x as f32,
+ y: y as f32,
+ }
+ }
+}
+
+impl<T: std::ops::Add> std::ops::Add for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn add(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ }
+ }
+}
+impl<T: std::ops::Sub> std::ops::Sub for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn sub(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x - rhs.x,
+ y: self.y - rhs.y,
+ }
+ }
+}
+impl<T: std::ops::Mul> std::ops::Mul for Vec2<T> {
+ type Output = Vec2<T::Output>;
+ #[inline]
+ fn mul(self, rhs: Self) -> Self::Output {
+ Vec2 {
+ x: self.x * rhs.x,
+ y: self.y * rhs.y,
+ }
+ }
+}
+
+impl<T> From<(T, T)> for Vec2<T> {
+ #[inline]
+ fn from((x, y): (T, T)) -> Self {
+ Vec2 { x, y }
+ }
+}
diff --git a/old/readme.md b/old/readme.md
new file mode 100644
index 0000000..c1287b9
--- /dev/null
+++ b/old/readme.md
@@ -0,0 +1,20 @@
+# Experimental Video Codecs
+
+My experiments on lossy video compression.
+
+## Other codecs
+
+- `bv1` A somewhat minimal video codec that is also the best one i could come up
+ with.
+- `evc` is my first proper attempt at video compression. features motion
+ compensation and broken dct.
+- `vgcodec` approximates images by drawing circles (on the GPU) (which doesnt
+ work well).
+- (`dhwt-codec` tries to compress using a discrete haar wavelet across all three
+ dimensions. which doesnt work well either)
+
+## Trying it out
+
+- `cd bv1`
+- Run `./tools/encode sample-video.mkv | ./tools/decode-display` (or
+ decode-display-debug to show debug overlay)
diff --git a/old/vgcodec/.gitignore b/old/vgcodec/.gitignore
new file mode 100644
index 0000000..775fc88
--- /dev/null
+++ b/old/vgcodec/.gitignore
@@ -0,0 +1,3 @@
+/target
+/a
+
diff --git a/old/vgcodec/Cargo.lock b/old/vgcodec/Cargo.lock
new file mode 100644
index 0000000..41f0fcf
--- /dev/null
+++ b/old/vgcodec/Cargo.lock
@@ -0,0 +1,1441 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+
+[[package]]
+name = "ash"
+version = "0.37.0+1.3.209"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "006ca68e0f2b03f22d6fa9f2860f85aed430d257fec20f8879b2145e7c7ae1a6"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bit_field"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "bumpalo"
+version = "3.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+
+[[package]]
+name = "bytemuck"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fe233b960f12f8007e3db2d136e3cb1c291bfd7396e384ee76025fc1a3932b4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cc"
+version = "1.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "clap"
+version = "4.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "clap_lex",
+ "once_cell",
+ "strsim",
+ "termcolor",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96bf8df95e795db1a4aca2957ad884a2df35413b24bbeb3114422f3cc21498e8"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "422f23e724af1240ec469ea1e834d87a4b59ce2efe2c6a96256b0c47e2fd86aa"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "cty"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
+
+[[package]]
+name = "d3d12"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "827914e1f53b1e0e025ecd3d967a7836b7bcb54520f90e21ef8df7b4d88a2759"
+dependencies = [
+ "bitflags",
+ "libloading",
+ "winapi",
+]
+
+[[package]]
+name = "either"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+
+[[package]]
+name = "env_logger"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "exr"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eb5f255b5980bb0c8cf676b675d1a99be40f316881444f44e0462eaf5df5ded"
+dependencies = [
+ "bit_field",
+ "flume",
+ "half",
+ "lebe",
+ "miniz_oxide 0.6.2",
+ "smallvec",
+ "threadpool",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide 0.5.4",
+]
+
+[[package]]
+name = "flume"
+version = "0.10.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "nanorand",
+ "pin-project",
+ "spin",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "futures-core"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
+
+[[package]]
+name = "futures-intrusive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
+dependencies = [
+ "futures-core",
+ "lock_api",
+ "parking_lot",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gif"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "glow"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d"
+dependencies = [
+ "bitflags",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
+dependencies = [
+ "bitflags",
+ "gpu-descriptor-types",
+ "hashbrown",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "half"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "image"
+version = "0.24.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "jpeg-decoder",
+ "num-rational",
+ "num-traits",
+ "png",
+ "scoped_threadpool",
+ "tiff",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
+dependencies = [
+ "libc",
+ "libloading",
+ "pkg-config",
+]
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
+name = "libc"
+version = "0.2.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metal"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060"
+dependencies = [
+ "bitflags",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "naga"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "262d2840e72dbe250e8cf2f522d080988dfca624c4112c096238a4845f591707"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap",
+ "log",
+ "num-traits",
+ "rustc-hash",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
+[[package]]
+name = "nanorand"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
+[[package]]
+name = "png"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "flate2",
+ "miniz_oxide 0.6.2",
+]
+
+[[package]]
+name = "pollster"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df"
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "range-alloc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a"
+dependencies = [
+ "cty",
+]
+
+[[package]]
+name = "rayon"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
+dependencies = [
+ "crossbeam-deque",
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "renderdoc-sys"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "scoped_threadpool"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "slotmap"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "spin"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spirv"
+version = "0.2.0+1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
+dependencies = [
+ "bitflags",
+ "num-traits",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "tiff"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17def29300a156c19ae30814710d9c63cd50288a49c6fd3a10ccfbe4cf886fd"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "vgcodec"
+version = "0.1.0"
+dependencies = [
+ "bytemuck",
+ "clap",
+ "env_logger",
+ "futures-intrusive",
+ "image",
+ "log",
+ "pollster",
+ "rand",
+ "wgpu",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
+
+[[package]]
+name = "wgpu"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2272b17bffc8a0c7d53897435da7c1db587c87d3a14e8dae9cdb8d1d210fc0f"
+dependencies = [
+ "arrayvec",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot",
+ "raw-window-handle",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73d14cad393054caf992ee02b7da6a372245d39a484f7461c1f44f6f6359bd28"
+dependencies = [
+ "arrayvec",
+ "bit-vec",
+ "bitflags",
+ "cfg_aliases",
+ "codespan-reporting",
+ "fxhash",
+ "log",
+ "naga",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "thiserror",
+ "web-sys",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cc320a61acb26be4f549c9b1b53405c10a223fbfea363ec39474c32c348d12f"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags",
+ "block",
+ "core-graphics-types",
+ "d3d12",
+ "foreign-types",
+ "fxhash",
+ "glow",
+ "gpu-alloc",
+ "gpu-descriptor",
+ "js-sys",
+ "khronos-egl",
+ "libloading",
+ "log",
+ "metal",
+ "naga",
+ "objc",
+ "parking_lot",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb6b28ef22cac17b9109b25b3bf8c9a103eeb293d7c5f78653979b09140375f6"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
diff --git a/old/vgcodec/Cargo.toml b/old/vgcodec/Cargo.toml
new file mode 100644
index 0000000..a6162dc
--- /dev/null
+++ b/old/vgcodec/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "vgcodec"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+wgpu = "0.20.0"
+log = "0.4.21"
+env_logger = "0.11.3"
+pollster = "0.3.0"
+bytemuck = { version = "1.15.0", features = ["derive"] }
+futures-intrusive = "0.5.0"
+image = "0.25.1"
+rand = "0.8.5"
+clap = { version = "4.5.4", features = ["derive"] }
diff --git a/old/vgcodec/src/app.rs b/old/vgcodec/src/app.rs
new file mode 100644
index 0000000..b51284c
--- /dev/null
+++ b/old/vgcodec/src/app.rs
@@ -0,0 +1,59 @@
+use std::sync::Arc;
+
+use wgpu::{Adapter, Device, Extent3d, ImageCopyTexture, Instance, Origin3d, Queue, Texture};
+
+pub struct App {
+ pub instance: Instance,
+ pub device: Device,
+ pub adapter: Adapter,
+ pub queue: Queue,
+}
+
+impl App {
+ pub async fn new() -> Arc<Self> {
+ let instance = wgpu::Instance::new(wgpu::Backends::all());
+ let adapter = instance
+ .request_adapter(&wgpu::RequestAdapterOptions::default())
+ .await
+ .unwrap();
+ let (device, queue) = adapter
+ .request_device(
+ &wgpu::DeviceDescriptor {
+ label: None,
+ features: wgpu::Features::empty(),
+ limits: wgpu::Limits::downlevel_defaults(),
+ },
+ None,
+ )
+ .await
+ .unwrap();
+ Arc::new(Self {
+ adapter,
+ device,
+ instance,
+ queue,
+ })
+ }
+
+ pub fn copy_texture(&self, source: &Texture, destination: &Texture, size: Extent3d) {
+ let mut encoder = self
+ .device
+ .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+ encoder.copy_texture_to_texture(
+ ImageCopyTexture {
+ aspect: wgpu::TextureAspect::All,
+ mip_level: 0,
+ origin: Origin3d::ZERO,
+ texture: source,
+ },
+ ImageCopyTexture {
+ aspect: wgpu::TextureAspect::All,
+ mip_level: 0,
+ origin: Origin3d::ZERO,
+ texture: destination,
+ },
+ size,
+ );
+ self.queue.submit(Some(encoder.finish()));
+ }
+}
diff --git a/old/vgcodec/src/approximate.rs b/old/vgcodec/src/approximate.rs
new file mode 100644
index 0000000..5bff325
--- /dev/null
+++ b/old/vgcodec/src/approximate.rs
@@ -0,0 +1,175 @@
+use crate::{
+ app::App,
+ diff::Differ,
+ export::Exporter,
+ paint::{PaintUniforms, Painter},
+};
+use log::{debug, info, warn};
+use rand::Rng;
+use std::sync::Arc;
+use wgpu::{Extent3d, Texture, TextureUsages};
+
+#[allow(unused)]
+pub struct Approximator {
+ app: Arc<App>,
+ size: Extent3d,
+ differ: Differ,
+ painter: Painter,
+ exporter: Exporter,
+
+ tex_approx: Texture,
+ tex_savestate: Texture,
+ tex_target: Texture,
+}
+
+impl Approximator {
+ pub fn new(app: &Arc<App>, tex_target: Texture, size: Extent3d) -> Self {
+ let App { device, .. } = app.as_ref();
+ let tex_approx = device.create_texture(&wgpu::TextureDescriptor {
+ size,
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: wgpu::TextureDimension::D2,
+ format: wgpu::TextureFormat::Rgba8Unorm,
+ usage: TextureUsages::COPY_DST
+ | TextureUsages::TEXTURE_BINDING
+ | TextureUsages::STORAGE_BINDING
+ | TextureUsages::COPY_SRC,
+ label: None,
+ });
+ let tex_savestate = device.create_texture(&wgpu::TextureDescriptor {
+ size,
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: wgpu::TextureDimension::D2,
+ format: wgpu::TextureFormat::Rgba8Unorm,
+ usage: TextureUsages::COPY_DST | TextureUsages::COPY_SRC,
+ label: None,
+ });
+
+ // let initial_init = (0..)
+ // write_texture(queue, &tex_approx, initial_init, size);
+
+ let differ = Differ::new(&app, size, &tex_approx, &tex_target);
+ let painter = Painter::new(&app, size, &tex_approx);
+ let exporter = Exporter::new(&app, size);
+
+ Approximator {
+ app: app.clone(),
+ size,
+ differ,
+ tex_savestate,
+ tex_approx,
+ tex_target,
+ painter,
+ exporter,
+ }
+ }
+
+ pub fn save(&self) {
+ self.app
+ .copy_texture(&self.tex_approx, &self.tex_savestate, self.size);
+ }
+ pub fn restore(&self) {
+ self.app
+ .copy_texture(&self.tex_savestate, &self.tex_approx, self.size);
+ }
+
+ pub async fn run(&mut self, iters: usize, out: &str) -> Vec<PaintUniforms> {
+ let rscale = self.size.width as f32 * 0.1;
+ let mut objects = vec![];
+ for i in 0..iters {
+ let mut p = PaintUniforms {
+ x: rand::thread_rng().gen_range(0.0..self.size.width as f32),
+ y: rand::thread_rng().gen_range(0.0..self.size.height as f32),
+ rx: rscale,
+ ry: rscale,
+ r: rand::thread_rng().gen_range(0.0..1.0),
+ g: rand::thread_rng().gen_range(0.0..1.0),
+ b: rand::thread_rng().gen_range(0.0..1.0),
+ };
+
+ // Find initial diff
+ self.save();
+ self.painter.run(p);
+ let initial_diff = pollster::block_on(self.differ.run());
+ debug!("initial diff={initial_diff}");
+ self.restore();
+
+ let mut cd = initial_diff;
+ self.save();
+ let ok = loop {
+ let mut q = 0;
+ let mut k = |k: usize| {
+ q += k;
+ q == 0
+ };
+ if k(self.optimize_param(&mut cd, &mut p, "more red", |p| p.r += 0.1)) {
+ k(self.optimize_param(&mut cd, &mut p, "less red", |p| p.r -= 0.1));
+ }
+ if k(self.optimize_param(&mut cd, &mut p, "more green", |p| p.g += 0.1)) {
+ k(self.optimize_param(&mut cd, &mut p, "less green", |p| p.g -= 0.1));
+ }
+ if k(self.optimize_param(&mut cd, &mut p, "more blue", |p| p.b += 0.1)) {
+ k(self.optimize_param(&mut cd, &mut p, "less blue", |p| p.b -= 0.1));
+ }
+ if k(self.optimize_param(&mut cd, &mut p, "increase rx", |p| p.rx *= 1.5)) {
+ k(self.optimize_param(&mut cd, &mut p, "decrease rx", |p| p.rx /= 1.5));
+ }
+ if k(self.optimize_param(&mut cd, &mut p, "increase ry", |p| p.ry *= 1.5)) {
+ k(self.optimize_param(&mut cd, &mut p, "decrease ry", |p| p.ry /= 1.5));
+ }
+ if k(self.optimize_param(&mut cd, &mut p, "move right", |p| p.x += 8.0)) {
+ k(self.optimize_param(&mut cd, &mut p, "move left", |p| p.x -= 8.0));
+ }
+ if k(self.optimize_param(&mut cd, &mut p, "move down", |p| p.y += 8.0)) {
+ k(self.optimize_param(&mut cd, &mut p, "move up", |p| p.y -= 8.0));
+ }
+ if p.rx < 5.0 || p.ry < 5.0 {
+ break false;
+ }
+ if q == 0 {
+ break true;
+ }
+ };
+ if ok {
+ self.painter.run(p);
+ info!("{i} (improvement={})", initial_diff - cd);
+ objects.push(p);
+ } else {
+ warn!("object aborted");
+ }
+ }
+ self.exporter.run(&self.tex_approx, out).await;
+ return objects;
+ }
+
+ pub fn optimize_param<F>(
+ &self,
+ current_diff: &mut u32,
+ params: &mut PaintUniforms,
+ label: &'static str,
+ f: F,
+ ) -> usize
+ where
+ F: Fn(&mut PaintUniforms) -> (),
+ {
+ let mut p = params.clone();
+ let mut i = 0;
+ loop {
+ f(&mut p);
+ self.painter.run(p);
+ let diff = pollster::block_on(self.differ.run());
+ // pollster::block_on(self.exporter.run(&self.tex_approx, &format!("a/snap.png")));
+ debug!("try {label:?} ({})", diff as i64 - *current_diff as i64);
+ self.restore();
+ if diff >= *current_diff {
+ break i;
+ }
+ debug!("applied {label:?}");
+ i += 1;
+ *current_diff = diff;
+ *params = p
+ }
+ }
+}
diff --git a/old/vgcodec/src/diff.rs b/old/vgcodec/src/diff.rs
new file mode 100644
index 0000000..4a7453c
--- /dev/null
+++ b/old/vgcodec/src/diff.rs
@@ -0,0 +1,122 @@
+use std::{borrow::Cow, sync::Arc};
+use wgpu::{util::DeviceExt, BindGroup, Buffer, ComputePipeline, Extent3d, Texture};
+
+use crate::app::App;
+
+pub struct Differ {
+ app: Arc<App>,
+ size: Extent3d,
+
+ pipeline: ComputePipeline,
+ bind_group: BindGroup,
+
+ counter_buffer_size: u64,
+ counter_buffer: Buffer,
+ counter_staging_buffer: Buffer,
+}
+
+impl Differ {
+ pub fn new(app: &Arc<App>, extent: Extent3d, tex_a: &Texture, tex_b: &Texture) -> Self {
+ let App { device, .. } = app.as_ref();
+ let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
+ label: None,
+ source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("diff.wgsl"))),
+ });
+
+ let counter_buffer_size = std::mem::size_of::<u32>() as wgpu::BufferAddress;
+ let counter_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+ label: None,
+ size: counter_buffer_size,
+ usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+ let counter_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: None,
+ contents: bytemuck::cast_slice(&[0u32]),
+ usage: wgpu::BufferUsages::STORAGE
+ | wgpu::BufferUsages::COPY_DST
+ | wgpu::BufferUsages::COPY_SRC,
+ });
+
+ let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
+ label: None,
+ layout: None,
+ module: &cs_module,
+ entry_point: "main",
+ });
+
+ let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: None,
+ layout: &pipeline.get_bind_group_layout(0),
+ entries: &[
+ wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::TextureView(
+ &tex_a.create_view(&wgpu::TextureViewDescriptor::default()),
+ ),
+ },
+ wgpu::BindGroupEntry {
+ binding: 1,
+ resource: wgpu::BindingResource::TextureView(
+ &tex_b.create_view(&wgpu::TextureViewDescriptor::default()),
+ ),
+ },
+ wgpu::BindGroupEntry {
+ binding: 2,
+ resource: counter_buffer.as_entire_binding(),
+ },
+ ],
+ });
+ Self {
+ app: app.clone(),
+ size: extent,
+ pipeline,
+ counter_buffer_size,
+ counter_buffer,
+ bind_group,
+ counter_staging_buffer,
+ }
+ }
+
+ pub async fn run(&self) -> u32 {
+ let App { device, queue, .. } = self.app.as_ref();
+
+ queue.write_buffer(&self.counter_buffer, 0, bytemuck::cast_slice(&[0u32]));
+
+ let mut encoder =
+ device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+ {
+ let mut cpass =
+ encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None });
+ cpass.set_pipeline(&self.pipeline);
+ cpass.set_bind_group(0, &self.bind_group, &[]);
+ cpass.dispatch_workgroups(self.size.width, self.size.height, 1);
+ }
+
+ encoder.copy_buffer_to_buffer(
+ &self.counter_buffer,
+ 0,
+ &self.counter_staging_buffer,
+ 0,
+ self.counter_buffer_size,
+ );
+
+ queue.submit(Some(encoder.finish()));
+
+ let buffer_slice = self.counter_staging_buffer.slice(..);
+ let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
+ buffer_slice.map_async(wgpu::MapMode::Read, move |v| {
+ sender.send(v.unwrap()).unwrap()
+ });
+ device.poll(wgpu::Maintain::Wait);
+ receiver.receive().await;
+
+ let data = buffer_slice.get_mapped_range();
+ let result: u32 = bytemuck::cast_slice(&data).to_vec()[0];
+
+ drop(data);
+ self.counter_staging_buffer.unmap();
+
+ result
+ }
+}
diff --git a/old/vgcodec/src/diff.wgsl b/old/vgcodec/src/diff.wgsl
new file mode 100644
index 0000000..82e169b
--- /dev/null
+++ b/old/vgcodec/src/diff.wgsl
@@ -0,0 +1,16 @@
+@group(0) @binding(0)
+var tex_a: texture_2d<f32>;
+@group(0) @binding(1)
+var tex_b: texture_2d<f32>;
+
+@group(0) @binding(2)
+var<storage, read_write> exp: atomic<u32>;
+
+@compute @workgroup_size(1)
+fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ var col_a = textureLoad(tex_a, vec2(i32(global_id.x), i32(global_id.y)), 0);
+ var col_b = textureLoad(tex_b, vec2(i32(global_id.x), i32(global_id.y)), 0);
+ var diff = length(col_a - col_b);
+ atomicAdd(&exp, u32(diff * 1000.0));
+}
+
diff --git a/old/vgcodec/src/export.rs b/old/vgcodec/src/export.rs
new file mode 100644
index 0000000..e020c5d
--- /dev/null
+++ b/old/vgcodec/src/export.rs
@@ -0,0 +1,83 @@
+use crate::app::App;
+use image::RgbaImage;
+use std::{num::NonZeroU32, sync::Arc};
+use wgpu::{
+ Buffer, Extent3d, ImageCopyBuffer, ImageCopyTexture, ImageDataLayout, Origin3d, Texture,
+};
+
+pub struct Exporter {
+ app: Arc<App>,
+ size: Extent3d,
+
+ padded_bytes_per_row: u32,
+ export_buffer: Buffer,
+}
+
+impl Exporter {
+ pub fn new(app: &Arc<App>, size: Extent3d) -> Self {
+ let App { device, .. } = app.as_ref();
+
+ let bytes_per_pixel = std::mem::size_of::<u32>() as u32;
+ let unpadded_bytes_per_row = size.width * bytes_per_pixel;
+ let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
+ let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
+ let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
+
+ let export_buffer_size = (padded_bytes_per_row * size.height) as u64;
+ let export_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+ label: None,
+ size: export_buffer_size,
+ usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+
+ Self {
+ padded_bytes_per_row,
+ app: app.clone(),
+ size,
+ export_buffer,
+ }
+ }
+ pub async fn run(&self, texture: &Texture, save_path: &str) {
+ let App { device, queue, .. } = self.app.as_ref();
+
+ let mut encoder =
+ device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+
+ encoder.copy_texture_to_buffer(
+ ImageCopyTexture {
+ texture,
+ aspect: wgpu::TextureAspect::All,
+ mip_level: 0,
+ origin: Origin3d::ZERO,
+ },
+ ImageCopyBuffer {
+ buffer: &self.export_buffer,
+ layout: ImageDataLayout {
+ offset: 0,
+ bytes_per_row: Some(NonZeroU32::new(self.padded_bytes_per_row).unwrap()),
+ rows_per_image: None,
+ },
+ },
+ self.size,
+ );
+
+ queue.submit(Some(encoder.finish()));
+
+ let buffer_slice = self.export_buffer.slice(..);
+ let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
+ buffer_slice.map_async(wgpu::MapMode::Read, move |v| {
+ sender.send(v.unwrap()).unwrap()
+ });
+ device.poll(wgpu::Maintain::Wait);
+ receiver.receive().await;
+
+ let data = buffer_slice.get_mapped_range();
+ let result: Vec<u8> = bytemuck::cast_slice(&data).to_vec();
+ let image = RgbaImage::from_raw(self.size.width, self.size.height, result).unwrap();
+ image.save(save_path).unwrap();
+
+ drop(data);
+ self.export_buffer.unmap();
+ }
+}
diff --git a/old/vgcodec/src/helper.rs b/old/vgcodec/src/helper.rs
new file mode 100644
index 0000000..13f9c1a
--- /dev/null
+++ b/old/vgcodec/src/helper.rs
@@ -0,0 +1,40 @@
+use log::info;
+use wgpu::{Extent3d, Queue, Texture};
+
+pub fn write_texture(queue: &Queue, target: &Texture, data: &[u8], size: Extent3d) {
+ info!("uploading texture {size:?} ({} bytes)", data.len());
+
+ let bytes_per_pixel = std::mem::size_of::<u32>() as u32;
+ let unpadded_bytes_per_row = size.width * bytes_per_pixel;
+ let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
+ let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
+ let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
+
+ let mut padded = vec![];
+ for y in 0..(size.height as usize) {
+ for x in 0..(size.width as usize) {
+ for c in 0..4 {
+ padded.push(data[c + x * 4 + y * 4 * size.width as usize])
+ }
+ }
+ for _ in 0..padded_bytes_per_row_padding {
+ padded.push(0)
+ }
+ }
+
+ queue.write_texture(
+ wgpu::ImageCopyTexture {
+ texture: &target,
+ mip_level: 0,
+ origin: wgpu::Origin3d::ZERO,
+ aspect: wgpu::TextureAspect::All,
+ },
+ &padded,
+ wgpu::ImageDataLayout {
+ offset: 0,
+ bytes_per_row: Some(std::num::NonZeroU32::try_from(padded_bytes_per_row).unwrap()),
+ rows_per_image: None,
+ },
+ size,
+ );
+}
diff --git a/old/vgcodec/src/main.rs b/old/vgcodec/src/main.rs
new file mode 100644
index 0000000..9cae234
--- /dev/null
+++ b/old/vgcodec/src/main.rs
@@ -0,0 +1,60 @@
+pub mod app;
+pub mod approximate;
+pub mod diff;
+pub mod export;
+pub mod helper;
+pub mod paint;
+
+use app::App;
+use approximate::Approximator;
+use clap::Parser;
+use helper::write_texture;
+use log::info;
+use wgpu::TextureUsages;
+
+fn main() {
+ env_logger::init_from_env("LOG");
+ pollster::block_on(run());
+}
+
+#[derive(Parser)]
+#[clap(about)]
+struct Args {
+ #[clap(short = 'I', long)]
+ iterations: usize,
+ #[clap(short = 'o', long)]
+ outfile: String,
+ infile: String,
+}
+
+async fn run() {
+ let args = Args::parse();
+ let app = app::App::new().await;
+
+ let App { device, queue, .. } = app.as_ref();
+
+ let img_target = image::open(&args.infile).unwrap().into_rgba8();
+
+ let size = wgpu::Extent3d {
+ width: img_target.width(),
+ height: img_target.height(),
+ depth_or_array_layers: 1,
+ };
+
+ let tex_target = device.create_texture(&wgpu::TextureDescriptor {
+ size,
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: wgpu::TextureDimension::D2,
+ format: wgpu::TextureFormat::Rgba8Unorm,
+ usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_SRC,
+ label: None,
+ });
+
+ let img_raw = img_target.into_raw();
+ info!("{}", img_raw.len());
+ write_texture(queue, &tex_target, &img_raw, size);
+
+ let mut a = Approximator::new(&app, tex_target, size);
+ a.run(args.iterations, &args.outfile).await;
+}
diff --git a/old/vgcodec/src/paint.rs b/old/vgcodec/src/paint.rs
new file mode 100644
index 0000000..33ab572
--- /dev/null
+++ b/old/vgcodec/src/paint.rs
@@ -0,0 +1,92 @@
+use bytemuck::{Pod, Zeroable};
+use std::{borrow::Cow, sync::Arc};
+use wgpu::{util::DeviceExt, BindGroup, Buffer, ComputePipeline, Extent3d, Texture};
+
+use crate::app::App;
+
+pub struct Painter {
+ app: Arc<App>,
+ size: Extent3d,
+
+ pipeline: ComputePipeline,
+ bind_group: BindGroup,
+
+ uniform_buffer: Buffer,
+}
+
+#[repr(C)]
+#[derive(Pod, Zeroable, Clone, Copy)]
+pub struct PaintUniforms {
+ pub x: f32,
+ pub y: f32,
+ pub rx: f32,
+ pub ry: f32,
+ pub r: f32,
+ pub g: f32,
+ pub b: f32,
+}
+
+impl Painter {
+ pub fn new(app: &Arc<App>, extent: Extent3d, texture: &Texture) -> Self {
+ let App { device, .. } = app.as_ref();
+ let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
+ label: None,
+ source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("paint.wgsl"))),
+ });
+
+ // let uniform_buffer_size = std::mem::size_of::<PaintUniforms>() as wgpu::BufferAddress;
+ let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: None,
+ contents: bytemuck::cast_slice(&[PaintUniforms::zeroed()]),
+ usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
+ });
+
+ let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
+ label: None,
+ layout: None,
+ module: &cs_module,
+ entry_point: "main",
+ });
+
+ let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: None,
+ layout: &pipeline.get_bind_group_layout(0),
+ entries: &[
+ wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::TextureView(
+ &texture.create_view(&wgpu::TextureViewDescriptor::default()),
+ ),
+ },
+ wgpu::BindGroupEntry {
+ binding: 1,
+ resource: uniform_buffer.as_entire_binding(),
+ },
+ ],
+ });
+ Self {
+ app: app.clone(),
+ size: extent,
+ pipeline,
+ uniform_buffer,
+ bind_group,
+ }
+ }
+
+ pub fn run(&self, params: PaintUniforms) {
+ let App { device, queue, .. } = self.app.as_ref();
+
+ queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[params]));
+
+ let mut encoder =
+ device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+ {
+ let mut cpass =
+ encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None });
+ cpass.set_pipeline(&self.pipeline);
+ cpass.set_bind_group(0, &self.bind_group, &[]);
+ cpass.dispatch_workgroups(self.size.width, self.size.height, 1);
+ }
+ queue.submit(Some(encoder.finish()));
+ }
+}
diff --git a/old/vgcodec/src/paint.wgsl b/old/vgcodec/src/paint.wgsl
new file mode 100644
index 0000000..7006310
--- /dev/null
+++ b/old/vgcodec/src/paint.wgsl
@@ -0,0 +1,22 @@
+struct Uniforms {
+ x: f32,
+ y: f32,
+ rx: f32,
+ ry: f32,
+ r: f32,
+ g: f32,
+ b: f32
+};
+
+@group(0) @binding(0) var tex: texture_storage_2d<rgba8unorm, write>;
+@group(0) @binding(1) var<uniform> uniforms: Uniforms;
+
+@compute @workgroup_size(1)
+fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ let coords = global_id.xy;
+ let kc = vec2<f32>(coords) - vec2(uniforms.x, uniforms.y);
+ let d = length(kc / vec2(uniforms.rx, uniforms.ry));
+ if d < 1.0 {
+ textureStore(tex, vec2<i32>(coords), vec4<f32>(vec3<f32>(uniforms.r, uniforms.g, uniforms.b), 1.0));
+ }
+}