aboutsummaryrefslogtreecommitdiff
path: root/old/evc
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-05-05 15:09:54 +0200
committermetamuffin <metamuffin@disroot.org>2025-05-05 15:09:54 +0200
commit306f96164784a8cbf405e72fa4364d6523366e95 (patch)
tree51717fc139871baa438aad806f4923669ae0896c /old/evc
parent9cc089e2d6e841879e430b01d2f3d92c8820523e (diff)
downloadvideo-codec-experiments-306f96164784a8cbf405e72fa4364d6523366e95.tar
video-codec-experiments-306f96164784a8cbf405e72fa4364d6523366e95.tar.bz2
video-codec-experiments-306f96164784a8cbf405e72fa4364d6523366e95.tar.zst
old dir
Diffstat (limited to 'old/evc')
-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
33 files changed, 2765 insertions, 0 deletions
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 }]
+ }
+}