v_frame-0.3.9/.cargo_vcs_info.json0000644000000001360000000000100124640ustar { "git": { "sha1": "e01ef7d4fbde65ca4daaf8bf8d93eee17a1703cc" }, "path_in_vcs": "" }v_frame-0.3.9/.github/workflows/crate.yml000064400000000000000000000036771046102023000165070ustar 00000000000000name: Crate on: push: branches: [ "main" ] pull_request: branches: [ "main" ] env: CARGO_TERM_COLOR: always CARGO_INCREMENTAL: 0 jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build run: cargo clippy - name: Run tests run: cargo test - name: Install cargo-msrv uses: taiki-e/install-action@v2 with: tool: cargo-msrv - name: Validate minimum Rust version run: | cargo msrv verify test-wasm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown - name: Install wasm-pack uses: taiki-e/install-action@v2 with: tool: wasm-pack - name: Run tests run: wasm-pack test --headless --chrome --firefox miri: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust nightly and miri uses: dtolnay/rust-toolchain@nightly with: components: miri, rust-src - name: Run miri env: RUSTFLAGS: -Zrandomize-layout MIRIFLAGS: -Zmiri-symbolic-alignment-check run: cargo miri test code-coverage: needs: [ miri, build ] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust stable and LLVM tools uses: dtolnay/rust-toolchain@stable with: components: llvm-tools-preview - name: Install cargo-llvm-cov uses: taiki-e/install-action@v2 with: tool: cargo-llvm-cov - name: Generate code coverage run: cargo llvm-cov --lcov --output-path lcov.log - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} files: lcov.log fail_ci_if_error: true v_frame-0.3.9/.gitignore000064400000000000000000000000241046102023000132400ustar 00000000000000/target /Cargo.lock v_frame-0.3.9/CHANGELOG.md000064400000000000000000000015021046102023000130630ustar 00000000000000## Version 0.3.8 - Eliminate unsound code - Remove several unnecessary dependencies - Internal refactoring ## Version 0.3.7 - Add documentation for `Frame` struct - Replace `hawktracer` with `tracing` crate ## Version 0.3.6 - Revert changes in downsampling in 0.3.4 which changed its behavior ## Version 0.3.5 - Bump num-derive to 0.4 ## Version 0.3.4 - Fix cases of unsoundness (#14) - Slight optimizations for downsampling ## Version 0.3.3 - Add `row_cropped` and `row_slice_cropped` methods to get rows without padding - Make `RowsIter` and `RowsIterMut` return rows without right-side padding for greater consistency/predictability - Fix clippy lints ## Version 0.3.1 - Add `rows_iter_mut` method to `Plane` ## Version 0.2.6 - Split into separate repository - Remove unused rayon dependency - Fix some clippy lints v_frame-0.3.9/Cargo.lock0000644000000456060000000000100104520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[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.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" dependencies = [ "equator", "serde", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bumpalo" version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" dependencies = [ "shlex", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "criterion" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "itertools 0.13.0", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools 0.10.5", ] [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 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.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equator" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" dependencies = [ "equator-macro", ] [[package]] name = "equator-macro" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "half" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minicov" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" dependencies = [ "cc", "walkdir", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "plotters" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" dependencies = [ "profiling-procmacros", "tracing", ] [[package]] name = "profiling-procmacros" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", "syn", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[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 = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustversion" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "v_frame" version = "0.3.9" dependencies = [ "aligned-vec", "criterion", "num-traits", "profiling", "serde", "tracing", "wasm-bindgen", "wasm-bindgen-test", ] [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-test" version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ "js-sys", "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", ] [[package]] name = "wasm-bindgen-test-macro" version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "web-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" v_frame-0.3.9/Cargo.toml0000644000000034240000000000100104650ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.80.0" name = "v_frame" version = "0.3.9" authors = ["Luca Barbato "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Video Frame data structures, originally part of rav1e" readme = "README.md" license = "BSD-2-Clause" repository = "https://github.com/rust-av/v_frame" [features] profiling = ["dep:profiling"] serialize = [ "serde", "aligned-vec/serde", ] tracing = [ "profiling", "dep:tracing", "profiling/profile-with-tracing", ] [lib] name = "v_frame" path = "src/lib.rs" [[bench]] name = "bench" path = "benches/bench.rs" harness = false [dependencies.aligned-vec] version = ">=0.5.0, <0.7" [dependencies.num-traits] version = "0.2" [dependencies.profiling] version = "1" optional = true [dependencies.serde] version = "1.0" features = ["derive"] optional = true [dependencies.tracing] version = "0.1.40" optional = true [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies.criterion] version = "0.6" [target.'cfg(target_arch = "wasm32")'.dev-dependencies.criterion] version = "0.6" default-features = false [target.wasm32-unknown-unknown.dependencies.wasm-bindgen] version = "0.2" [target.wasm32-unknown-unknown.dev-dependencies.wasm-bindgen-test] version = "0.3" v_frame-0.3.9/Cargo.toml.orig000064400000000000000000000040251046102023000141440ustar 00000000000000[package] name = "v_frame" version = "0.3.9" rust-version = "1.80.0" description = "Video Frame data structures, originally part of rav1e" license = "BSD-2-Clause" authors = ["Luca Barbato "] edition = "2021" repository = "https://github.com/rust-av/v_frame" [features] serialize = ["serde", "aligned-vec/serde"] profiling = ["dep:profiling"] tracing = ["profiling", "dep:tracing", "profiling/profile-with-tracing"] [dependencies] num-traits = "0.2" serde = { version = "1.0", features = ["derive"], optional = true } aligned-vec = ">=0.5.0, <0.7" # Profiling dependencies profiling = { version = "1", optional = true } tracing = { version = "0.1.40", optional = true } [[bench]] name = "bench" harness = false # # WebAssembly/wasm32 target support below # # There are two main WASM targets: # - wasm32-unknown-unknown: Base WebAssembly standard, used in browsers # - wasm32-wasi: Newer extension/standard, comes with basic std library, # not supported in browsers (yet) # # Some things work in all WASM configurations, some work only on one of these two, # some things don't work on wasm32 at all. # # wasm32-unknown-unknown requires wasm_bindgen and wasm_bindgen_test to work, # but these can cause problems on wasm32-wasi. So we use either # - target_arch = "wasm32" for things that (don't) work on all WebAssembly targets or # - explicit targetting for wasm32-unknown-unknown or wasm32-wasi. # In Cargo.toml, this is done via [target..dependencies] # In code, this is done via cfg(all(target_arch = "wasm32", target_os = "wasi"/"unknown")) # The rayon feature does not work on any wasm32 target [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] criterion = "0.6" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] criterion = { version = "0.6", default-features = false } # wasm-bindgen is only needed for wasm32-unknown-unknown and not other wasm32 targets [target.wasm32-unknown-unknown.dependencies] wasm-bindgen = "0.2" [target.wasm32-unknown-unknown.dev-dependencies] wasm-bindgen-test = "0.3" v_frame-0.3.9/LICENSE000064400000000000000000000024641046102023000122670ustar 00000000000000BSD 2-Clause License Copyright (c) 2017-2022, the rav1e contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. v_frame-0.3.9/README.md000064400000000000000000000013211046102023000125300ustar 00000000000000# v_frame [![docs.rs](https://img.shields.io/docsrs/v_frame)](https://docs.rs/v_frame) [![Crates.io](https://img.shields.io/crates/v/v_frame)](https://crates.io/crates/v_frame) [![LICENSE](https://img.shields.io/crates/l/v_frame)](https://github.com/rust-av/v_frame/blob/main/LICENSE) [![dependency status](https://deps.rs/repo/github/rust-av/v_frame/status.svg)](https://deps.rs/repo/github/rust-av/v_frame) [![codecov](https://codecov.io/github/rust-av/v_frame/branch/main/graph/badge.svg?token=MKT1AZREF0)](https://codecov.io/github/rust-av/v_frame) Crate providing structs and utilities for handling YUV Frames and Planes. Originally used by rav1e, now branched into its own crate for wider use throughout Rust AV. v_frame-0.3.9/benches/bench.rs000064400000000000000000000066521046102023000143210ustar 00000000000000use std::hint::black_box; use criterion::{criterion_group, criterion_main, Criterion}; use v_frame::frame::Frame; use v_frame::pixel::{CastFromPrimitive, ChromaSampling}; use v_frame::plane::Plane; fn frame(c: &mut Criterion) { c.bench_function("frame new_with_padding padding=0", |b| { b.iter(|| { Frame::::new_with_padding( black_box(640), black_box(480), black_box(ChromaSampling::Cs420), black_box(0), ) }) }); c.bench_function("frame new_with_padding padding!=0", |b| { b.iter(|| { Frame::::new_with_padding( black_box(640), black_box(480), black_box(ChromaSampling::Cs420), black_box(24), ) }) }); } fn plane(c: &mut Criterion) { c.bench_function("plane new padding=0", |b| { b.iter(|| { Plane::::new( black_box(640), black_box(480), black_box(1), black_box(1), black_box(0), black_box(0), ) }) }); c.bench_function("plane new padding!=0", |b| { b.iter(|| { Plane::::new( black_box(640), black_box(480), black_box(1), black_box(1), black_box(24), black_box(24), ) }) }); let p8b = Plane::::new( black_box(640), black_box(480), black_box(1), black_box(1), black_box(0), black_box(0), ); // We need to use PerIteration because we modify the Plane every iteration let batch_size = criterion::BatchSize::PerIteration; c.bench_function("plane clone", |b| b.iter(|| p8b.clone())); c.bench_function("plane pad", |b| { b.iter_batched_ref( || p8b.clone(), |p| p.pad(black_box(680), black_box(520)), batch_size, ) }); let data_8b: Vec = vec![2; 640 * 480]; c.bench_function("plane copy_from_raw_u8 8-bit", |b| { b.iter_batched_ref( || p8b.clone(), |p| p.copy_from_raw_u8(black_box(&data_8b), black_box(640), black_box(1)), batch_size, ) }); let p10b = Plane::::new( black_box(640), black_box(480), black_box(1), black_box(1), black_box(0), black_box(0), ); let data_10b: Vec = vec![2; 640 * 480 * 2]; c.bench_function("plane copy_from_raw_u8 10-bit", |b| { b.iter_batched_ref( || p10b.clone(), |p| p.copy_from_raw_u8(black_box(&data_10b), black_box(640), black_box(2)), batch_size, ) }); c.bench_function("plane downsampled", |b| { b.iter(|| p8b.downsampled(black_box(320), black_box(240))) }); c.bench_function("plane downscale", |b| b.iter(|| p8b.downscale::<2>())); // This may seem silly to benchmark, but there is some math in the iterator // that has been known to hinder compiler optimizations c.bench_function("plane rows_iter", |b| { b.iter(|| { p8b.rows_iter() .map(|r| r.iter().map(|v| u8::cast_from(*v) as u64).sum::()) .collect::>() }) }); } criterion_group!(benches, frame, plane); criterion_main!(benches); v_frame-0.3.9/clippy.toml000064400000000000000000000000161046102023000134460ustar 00000000000000msrv = "1.64" v_frame-0.3.9/src/frame.rs000064400000000000000000000045161046102023000135110ustar 00000000000000// Copyright (c) 2018-2020, The rav1e contributors. All rights reserved // // This source code is subject to the terms of the BSD 2 Clause License and // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License // was not distributed with this source code in the LICENSE file, you can // obtain it at www.aomedia.org/license/software. If the Alliance for Open // Media Patent License 1.0 was not distributed with this source code in the // PATENTS file, you can obtain it at www.aomedia.org/license/patent. use crate::math::*; use crate::pixel::*; use crate::plane::*; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; /// Represents a raw video frame #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Frame { /// Planes constituting the frame. pub planes: [Plane; 3], } impl Frame { /// Creates a new frame with the given parameters. /// /// Allocates data for the planes. pub fn new_with_padding( width: usize, height: usize, chroma_sampling: ChromaSampling, luma_padding: usize, ) -> Self { let luma_width = width.align_power_of_two(3); let luma_height = height.align_power_of_two(3); let (chroma_decimation_x, chroma_decimation_y) = chroma_sampling.get_decimation().unwrap_or((0, 0)); let (chroma_width, chroma_height) = chroma_sampling.get_chroma_dimensions(luma_width, luma_height); let chroma_padding_x = luma_padding >> chroma_decimation_x; let chroma_padding_y = luma_padding >> chroma_decimation_y; Frame { planes: [ Plane::new(luma_width, luma_height, 0, 0, luma_padding, luma_padding), Plane::new( chroma_width, chroma_height, chroma_decimation_x, chroma_decimation_y, chroma_padding_x, chroma_padding_y, ), Plane::new( chroma_width, chroma_height, chroma_decimation_x, chroma_decimation_y, chroma_padding_x, chroma_padding_y, ), ], } } } v_frame-0.3.9/src/lib.rs000064400000000000000000000061161046102023000131630ustar 00000000000000// Copyright (c) 2020, The rav1e contributors. All rights reserved // // This source code is subject to the terms of the BSD 2 Clause License and // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License // was not distributed with this source code in the LICENSE file, you can // obtain it at www.aomedia.org/license/software. If the Alliance for Open // Media Patent License 1.0 was not distributed with this source code in the // PATENTS file, you can obtain it at www.aomedia.org/license/patent. // Safety lints #![deny(bare_trait_objects)] #![deny(clippy::as_ptr_cast_mut)] #![deny(clippy::large_stack_arrays)] // Performance lints #![warn(clippy::inefficient_to_string)] #![warn(clippy::invalid_upcast_comparisons)] #![warn(clippy::iter_with_drain)] #![warn(clippy::linkedlist)] #![warn(clippy::mutex_integer)] #![warn(clippy::naive_bytecount)] #![warn(clippy::needless_bitwise_bool)] #![warn(clippy::needless_collect)] #![warn(clippy::or_fun_call)] #![warn(clippy::stable_sort_primitive)] #![warn(clippy::suboptimal_flops)] #![warn(clippy::trivial_regex)] #![warn(clippy::trivially_copy_pass_by_ref)] #![warn(clippy::unnecessary_join)] #![warn(clippy::unused_async)] #![warn(clippy::zero_sized_map_values)] // Correctness lints #![deny(clippy::case_sensitive_file_extension_comparisons)] #![deny(clippy::copy_iterator)] #![deny(clippy::expl_impl_clone_on_copy)] #![deny(clippy::float_cmp)] #![warn(clippy::imprecise_flops)] #![deny(clippy::manual_instant_elapsed)] #![deny(clippy::mem_forget)] #![deny(clippy::path_buf_push_overwrite)] #![deny(clippy::same_functions_in_if_condition)] #![deny(clippy::unchecked_duration_subtraction)] #![deny(clippy::unicode_not_nfc)] // Clarity/formatting lints #![warn(clippy::checked_conversions)] #![warn(clippy::derive_partial_eq_without_eq)] #![allow(clippy::enum_variant_names)] #![warn(clippy::explicit_deref_methods)] #![warn(clippy::filter_map_next)] #![warn(clippy::flat_map_option)] #![warn(clippy::fn_params_excessive_bools)] #![warn(clippy::implicit_clone)] #![warn(clippy::iter_not_returning_iterator)] #![warn(clippy::iter_on_empty_collections)] #![warn(clippy::macro_use_imports)] #![warn(clippy::manual_clamp)] #![warn(clippy::manual_let_else)] #![warn(clippy::manual_ok_or)] #![warn(clippy::manual_string_new)] #![warn(clippy::map_flatten)] #![warn(clippy::match_bool)] #![warn(clippy::mut_mut)] #![warn(clippy::needless_borrow)] #![warn(clippy::needless_continue)] #![warn(clippy::range_minus_one)] #![warn(clippy::range_plus_one)] #![warn(clippy::ref_binding_to_reference)] #![warn(clippy::ref_option_ref)] #![warn(clippy::trait_duplication_in_bounds)] #![warn(clippy::unused_peekable)] #![warn(clippy::unused_rounding)] #![warn(clippy::unused_self)] #![allow(clippy::upper_case_acronyms)] #![warn(clippy::verbose_bit_mask)] #![warn(clippy::verbose_file_reads)] // Documentation lints #![warn(clippy::doc_link_with_quotes)] #![warn(clippy::doc_markdown)] #![warn(clippy::missing_errors_doc)] #![warn(clippy::missing_panics_doc)] pub mod frame; pub mod math; pub mod pixel; pub mod plane; pub mod prelude { pub use crate::math::*; pub use crate::pixel::*; } v_frame-0.3.9/src/math.rs000064400000000000000000000073511046102023000133500ustar 00000000000000// Copyright (c) 2017-2020, The rav1e contributors. All rights reserved // // This source code is subject to the terms of the BSD 2 Clause License and // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License // was not distributed with this source code in the LICENSE file, you can // obtain it at www.aomedia.org/license/software. If the Alliance for Open // Media Patent License 1.0 was not distributed with this source code in the // PATENTS file, you can obtain it at www.aomedia.org/license/patent. use num_traits::PrimInt; use std::mem::size_of; pub trait Fixed { fn floor_log2(&self, n: usize) -> usize; fn ceil_log2(&self, n: usize) -> usize; fn align_power_of_two(&self, n: usize) -> usize; fn align_power_of_two_and_shift(&self, n: usize) -> usize; } impl Fixed for usize { #[inline] fn floor_log2(&self, n: usize) -> usize { self & !((1 << n) - 1) } #[inline] fn ceil_log2(&self, n: usize) -> usize { (self + (1 << n) - 1).floor_log2(n) } #[inline] fn align_power_of_two(&self, n: usize) -> usize { self.ceil_log2(n) } #[inline] fn align_power_of_two_and_shift(&self, n: usize) -> usize { (self + (1 << n) - 1) >> n } } pub fn clamp(input: T, min: T, max: T) -> T { if input < min { min } else if input > max { max } else { input } } pub trait ILog: PrimInt { // Integer binary logarithm of an integer value. // Returns floor(log2(self)) + 1, or 0 if self == 0. // This is the number of bits that would be required to represent self in two's // complement notation with all of the leading zeros stripped. // TODO: Mark const once trait functions can be constant fn ilog(self) -> usize { size_of::() * 8 - self.leading_zeros() as usize } } impl ILog for T where T: PrimInt {} #[inline(always)] pub fn msb(x: i32) -> i32 { debug_assert!(x > 0); 31 ^ (x.leading_zeros() as i32) } #[inline(always)] pub const fn round_shift(value: i32, bit: usize) -> i32 { (value + (1 << bit >> 1)) >> bit } #[cfg(test)] mod tests { use super::*; #[test] fn test_floor_log2() { assert_eq!(123usize.floor_log2(4), 112); assert_eq!(16usize.floor_log2(4), 16); assert_eq!(0usize.floor_log2(4), 0); } #[test] fn test_ceil_log2() { assert_eq!(123usize.ceil_log2(4), 128); assert_eq!(16usize.ceil_log2(4), 16); assert_eq!(0usize.ceil_log2(4), 0); } #[test] fn test_align_power_of_two() { assert_eq!(123usize.align_power_of_two(4), 128); assert_eq!(16usize.align_power_of_two(4), 16); assert_eq!(0usize.align_power_of_two(4), 0); } #[test] fn test_align_power_of_two_and_shift() { assert_eq!(123usize.align_power_of_two_and_shift(4), 8); assert_eq!(16usize.align_power_of_two_and_shift(4), 1); assert_eq!(0usize.align_power_of_two_and_shift(4), 0); } #[test] fn test_clamp() { assert_eq!(clamp(5, 0, 10), 5); assert_eq!(clamp(-1, 0, 10), 0); assert_eq!(clamp(11, 0, 10), 10); } #[test] fn test_ilog() { assert_eq!(ILog::ilog(0i32), 0); assert_eq!(ILog::ilog(1i32), 1); assert_eq!(ILog::ilog(2i32), 2); assert_eq!(ILog::ilog(3i32), 2); assert_eq!(ILog::ilog(4i32), 3); } #[test] fn test_msb() { assert_eq!(msb(1), 0); assert_eq!(msb(2), 1); assert_eq!(msb(3), 1); assert_eq!(msb(4), 2); } #[test] fn test_round_shift() { assert_eq!(round_shift(7, 2), 2); assert_eq!(round_shift(8, 2), 2); assert_eq!(round_shift(9, 2), 2); assert_eq!(round_shift(10, 2), 3); } } v_frame-0.3.9/src/pixel.rs000064400000000000000000000222311046102023000135320ustar 00000000000000// Copyright (c) 2017-2021, The rav1e contributors. All rights reserved // // This source code is subject to the terms of the BSD 2 Clause License and // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License // was not distributed with this source code in the LICENSE file, you can // obtain it at www.aomedia.org/license/software. If the Alliance for Open // Media Patent License 1.0 was not distributed with this source code in the // PATENTS file, you can obtain it at www.aomedia.org/license/patent. #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] use wasm_bindgen::prelude::*; use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed}; use std::fmt; use std::fmt::{Debug, Display}; use std::mem::size_of; use std::ops::AddAssign; /// Trait for casting between primitive types. pub trait CastFromPrimitive: Copy + 'static { /// Casts the given value into `Self`. fn cast_from(v: T) -> Self; } macro_rules! impl_cast_from_primitive { ( $T:ty => $U:ty ) => { impl CastFromPrimitive<$U> for $T { #[inline(always)] fn cast_from(v: $U) -> Self { v as Self } } }; ( $T:ty => { $( $U:ty ),* } ) => { $( impl_cast_from_primitive!($T => $U); )* }; } // casts to { u8, u16 } are implemented separately using Pixel, so that the // compiler understands that CastFromPrimitive is always implemented impl_cast_from_primitive!(u8 => { u32, u64, usize }); impl_cast_from_primitive!(u8 => { i8, i64, isize }); impl_cast_from_primitive!(u16 => { u32, u64, usize }); impl_cast_from_primitive!(u16 => { i8, i64, isize }); impl_cast_from_primitive!(i16 => { u32, u64, usize }); impl_cast_from_primitive!(i16 => { i8, i64, isize }); impl_cast_from_primitive!(i32 => { u32, u64, usize }); impl_cast_from_primitive!(i32 => { i8, i64, isize }); pub trait RegisteredPrimitive: PrimInt + AsPrimitive + AsPrimitive + AsPrimitive + AsPrimitive + AsPrimitive + AsPrimitive + CastFromPrimitive + CastFromPrimitive + CastFromPrimitive + CastFromPrimitive + CastFromPrimitive + CastFromPrimitive { } impl RegisteredPrimitive for u8 {} impl RegisteredPrimitive for u16 {} impl RegisteredPrimitive for i16 {} impl RegisteredPrimitive for i32 {} macro_rules! impl_cast_from_pixel_to_primitive { ( $T:ty ) => { impl CastFromPrimitive for $T { #[inline(always)] fn cast_from(v: T) -> Self { v.as_() } } }; } impl_cast_from_pixel_to_primitive!(u8); impl_cast_from_pixel_to_primitive!(i16); impl_cast_from_pixel_to_primitive!(u16); impl_cast_from_pixel_to_primitive!(i32); impl_cast_from_pixel_to_primitive!(u32); /// Types that can be used as pixel types. #[derive(PartialEq, Eq)] pub enum PixelType { /// 8 bits per pixel, stored in a `u8`. U8, /// 10 or 12 bits per pixel, stored in a `u16`. U16, } /// A type that can be used as a pixel type. pub trait Pixel: RegisteredPrimitive + Into + Into + Debug + Display + Send + Sync + 'static { type Coeff: Coefficient; /// Returns a [`PixelType`] variant corresponding to this type. /// /// [`PixelType`]: enum.PixelType.html fn type_enum() -> PixelType; /// Converts stride in pixels to stride in bytes. #[inline] fn to_asm_stride(in_stride: usize) -> isize { (in_stride * size_of::()) as isize } } impl Pixel for u8 { type Coeff = i16; #[inline] fn type_enum() -> PixelType { PixelType::U8 } } impl Pixel for u16 { type Coeff = i32; #[inline] fn type_enum() -> PixelType { PixelType::U16 } } pub trait Coefficient: RegisteredPrimitive + Into + AddAssign + Signed + Debug + 'static { type Pixel: Pixel; } impl Coefficient for i16 { type Pixel = u8; } impl Coefficient for i32 { type Pixel = u16; } /// Chroma subsampling format #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] #[repr(C)] pub enum ChromaSampling { /// Both vertically and horizontally subsampled. #[default] Cs420, /// Horizontally subsampled. Cs422, /// Not subsampled. Cs444, /// Monochrome. Cs400, } impl FromPrimitive for ChromaSampling { fn from_i64(n: i64) -> Option { use ChromaSampling::*; match n { n if n == Cs420 as i64 => Some(Cs420), n if n == Cs422 as i64 => Some(Cs422), n if n == Cs444 as i64 => Some(Cs444), n if n == Cs400 as i64 => Some(Cs400), _ => None, } } fn from_u64(n: u64) -> Option { ChromaSampling::from_i64(n as i64) } } impl fmt::Display for ChromaSampling { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!( f, "{}", match self { ChromaSampling::Cs420 => "4:2:0", ChromaSampling::Cs422 => "4:2:2", ChromaSampling::Cs444 => "4:4:4", ChromaSampling::Cs400 => "Monochrome", } ) } } impl ChromaSampling { /// Provides the amount to right shift the luma plane dimensions to get the /// chroma plane dimensions. /// Only values 0 or 1 are ever returned. /// The plane dimensions must also be rounded up to accommodate odd luma plane /// sizes. /// Cs400 returns None, as there are no chroma planes. pub const fn get_decimation(self) -> Option<(usize, usize)> { use self::ChromaSampling::*; match self { Cs420 => Some((1, 1)), Cs422 => Some((1, 0)), Cs444 => Some((0, 0)), Cs400 => None, } } /// Calculates the size of a chroma plane for this sampling type, given the luma plane dimensions. pub const fn get_chroma_dimensions( self, luma_width: usize, luma_height: usize, ) -> (usize, usize) { if let Some((ss_x, ss_y)) = self.get_decimation() { ((luma_width + ss_x) >> ss_x, (luma_height + ss_y) >> ss_y) } else { (0, 0) } } } #[cfg(test)] mod test { use super::*; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] use wasm_bindgen_test::*; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] wasm_bindgen_test_configure!(run_in_browser); #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn asm_stride() { let tests = [(7, 7, 14), (12, 12, 24), (1234, 1234, 2468)]; for (in_stride, u8_bytes, u16_bytes) in tests { assert_eq!(u8::to_asm_stride(in_stride), u8_bytes); assert_eq!(u16::to_asm_stride(in_stride), u16_bytes); } } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn type_enum() { assert!(u8::type_enum() == PixelType::U8); assert!(u16::type_enum() == PixelType::U16); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn chroma_sampling_from_int() { let expected = [ (-1, None), (0, Some(ChromaSampling::Cs420)), (1, Some(ChromaSampling::Cs422)), (2, Some(ChromaSampling::Cs444)), (3, Some(ChromaSampling::Cs400)), (4, None), ]; for (int, chroma_sampling) in expected { let converted = ChromaSampling::from_i32(int); assert_eq!(chroma_sampling, converted); let converted_uint = ChromaSampling::from_u32(int as u32); assert_eq!(chroma_sampling, converted_uint); } } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn display_chroma_sampling() { use std::fmt::Write; let all_cs = [ (ChromaSampling::Cs420, "4:2:0"), (ChromaSampling::Cs422, "4:2:2"), (ChromaSampling::Cs444, "4:4:4"), (ChromaSampling::Cs400, "Monochrome"), ]; for (cs, expected) in all_cs { let mut s = String::new(); write!(&mut s, "{cs}").expect("can display"); assert_eq!(&s, expected); } } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn chroma_sampling_dimensions() { let tests = [ ((1024, 768), ChromaSampling::Cs444, (1024, 768)), ((1024, 768), ChromaSampling::Cs422, (512, 768)), ((1024, 768), ChromaSampling::Cs420, (512, 384)), ((1024, 768), ChromaSampling::Cs400, (0, 0)), // Check odd width/height ((1023, 768), ChromaSampling::Cs422, (512, 768)), ((1023, 767), ChromaSampling::Cs420, (512, 384)), ]; for (luma, cs, expected_chroma) in tests { let chroma = cs.get_chroma_dimensions(luma.0, luma.1); assert_eq!(chroma, expected_chroma); } } } v_frame-0.3.9/src/plane.rs000064400000000000000000001135671046102023000135250ustar 00000000000000// Copyright (c) 2017-2021, The rav1e contributors. All rights reserved // // This source code is subject to the terms of the BSD 2 Clause License and // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License // was not distributed with this source code in the LICENSE file, you can // obtain it at www.aomedia.org/license/software. If the Alliance for Open // Media Patent License 1.0 was not distributed with this source code in the // PATENTS file, you can obtain it at www.aomedia.org/license/patent. use std::fmt::{Debug, Display, Formatter}; use std::iter::{self, FusedIterator}; use std::marker::PhantomData; use std::mem::size_of; use std::ops::{Index, IndexMut, Range}; use aligned_vec::{ABox, AVec, ConstAlign}; use crate::math::*; use crate::pixel::*; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; /// Plane-specific configuration. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct PlaneConfig { /// Data stride. pub stride: usize, /// Allocated height in pixels. pub alloc_height: usize, /// Width in pixels. pub width: usize, /// Height in pixels. pub height: usize, /// Decimator along the X axis. /// /// For example, for chroma planes in a 4:2:0 configuration this would be 1. pub xdec: usize, /// Decimator along the Y axis. /// /// For example, for chroma planes in a 4:2:0 configuration this would be 1. pub ydec: usize, /// Number of padding pixels on the right. pub xpad: usize, /// Number of padding pixels on the bottom. pub ypad: usize, /// X where the data starts. pub xorigin: usize, /// Y where the data starts. pub yorigin: usize, } impl PlaneConfig { /// Stride alignment in bytes. const STRIDE_ALIGNMENT_LOG2: usize = 6; #[inline] pub fn new( width: usize, height: usize, xdec: usize, ydec: usize, xpad: usize, ypad: usize, type_size: usize, ) -> Self { let xorigin = xpad.align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size); let yorigin = ypad; let stride = (xorigin + width + xpad) .align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size); let alloc_height = yorigin + height + ypad; PlaneConfig { stride, alloc_height, width, height, xdec, ydec, xpad, ypad, xorigin, yorigin, } } } /// Absolute offset in pixels inside a plane #[derive(Clone, Copy, Debug, Default)] pub struct PlaneOffset { pub x: isize, pub y: isize, } /// Backing buffer for the Plane data /// /// The buffer is padded and aligned according to the architecture-specific /// SIMD constraints. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct PlaneData { #[cfg(not(target_arch = "wasm32"))] data: ABox<[T], ConstAlign<{ 1 << 6 }>>, #[cfg(target_arch = "wasm32")] data: ABox<[T], ConstAlign<{ 1 << 3 }>>, } unsafe impl Send for PlaneData {} unsafe impl Sync for PlaneData {} impl std::ops::Deref for PlaneData { type Target = [T]; fn deref(&self) -> &[T] { self.data.as_ref() } } impl std::ops::DerefMut for PlaneData { fn deref_mut(&mut self) -> &mut [T] { self.data.as_mut() } } impl PlaneData { #[cfg(target_arch = "wasm32")] // FIXME: wasm32 allocator fails for alignment larger than 3 const DATA_ALIGNMENT: usize = 1 << 3; #[cfg(not(target_arch = "wasm32"))] const DATA_ALIGNMENT: usize = 1 << 6; pub fn new(len: usize) -> Self { Self { data: AVec::from_iter( Self::DATA_ALIGNMENT, iter::repeat(T::cast_from(128)).take(len), ) .into_boxed_slice(), } } fn from_slice(data: &[T]) -> Self { Self { data: AVec::from_slice(Self::DATA_ALIGNMENT, data).into_boxed_slice(), } } } /// One data plane of a frame. /// /// For example, a plane can be a Y luma plane or a U or V chroma plane. #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Plane { // TODO: it is used by encoder to copy by plane and by tiling, make it // private again once tiling is moved and a copy_plane fn is added. // pub data: PlaneData, /// Plane configuration. pub cfg: PlaneConfig, } impl Debug for Plane where T: Display, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "Plane {{ data: [{}, ...], cfg: {:?} }}", self.data[0], self.cfg ) } } impl Plane { /// Allocates and returns a new plane. pub fn new( width: usize, height: usize, xdec: usize, ydec: usize, xpad: usize, ypad: usize, ) -> Self { let cfg = PlaneConfig::new(width, height, xdec, ydec, xpad, ypad, size_of::()); let data = PlaneData::new(cfg.stride * cfg.alloc_height); Plane { data, cfg } } /// # Panics /// /// - If `len` is not a multiple of `stride` pub fn from_slice(data: &[T], stride: usize) -> Self { let len = data.len(); assert!(len % stride == 0); Self { data: PlaneData::from_slice(data), cfg: PlaneConfig { stride, alloc_height: len / stride, width: stride, height: len / stride, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 0, yorigin: 0, }, } } pub fn pad(&mut self, w: usize, h: usize) { let xorigin = self.cfg.xorigin; let yorigin = self.cfg.yorigin; let stride = self.cfg.stride; let alloc_height = self.cfg.alloc_height; let width = (w + self.cfg.xdec) >> self.cfg.xdec; let height = (h + self.cfg.ydec) >> self.cfg.ydec; if xorigin > 0 { for y in 0..height { let base = (yorigin + y) * stride; let fill_val = self.data[base + xorigin]; for val in &mut self.data[base..base + xorigin] { *val = fill_val; } } } if xorigin + width < stride { for y in 0..height { let base = (yorigin + y) * stride + xorigin + width; let fill_val = self.data[base - 1]; for val in &mut self.data[base..base + stride - (xorigin + width)] { *val = fill_val; } } } if yorigin > 0 { let (top, bottom) = self.data.split_at_mut(yorigin * stride); let src = &bottom[..stride]; for y in 0..yorigin { let dst = &mut top[y * stride..(y + 1) * stride]; dst.copy_from_slice(src); } } if yorigin + height < self.cfg.alloc_height { let (top, bottom) = self.data.split_at_mut((yorigin + height) * stride); let src = &top[(yorigin + height - 1) * stride..]; for y in 0..alloc_height - (yorigin + height) { let dst = &mut bottom[y * stride..(y + 1) * stride]; dst.copy_from_slice(src); } } } /// Minimally test that the plane has been padded. pub fn probe_padding(&self, w: usize, h: usize) -> bool { let PlaneConfig { xorigin, yorigin, stride, alloc_height, xdec, ydec, .. } = self.cfg; let width = (w + xdec) >> xdec; let height = (h + ydec) >> ydec; let corner = (yorigin + height - 1) * stride + xorigin + width - 1; let corner_value = self.data[corner]; self.data[(yorigin + height) * stride - 1] == corner_value && self.data[(alloc_height - 1) * stride + xorigin + width - 1] == corner_value && self.data[alloc_height * stride - 1] == corner_value } pub fn slice(&self, po: PlaneOffset) -> PlaneSlice<'_, T> { PlaneSlice { plane: self, x: po.x, y: po.y, } } pub fn mut_slice(&mut self, po: PlaneOffset) -> PlaneMutSlice<'_, T> { PlaneMutSlice { plane: self, x: po.x, y: po.y, } } #[inline] fn index(&self, x: usize, y: usize) -> usize { (y + self.cfg.yorigin) * self.cfg.stride + (x + self.cfg.xorigin) } /// This version of the function crops off the padding on the right side of the image #[inline] pub fn row_range_cropped(&self, x: isize, y: isize) -> Range { debug_assert!(self.cfg.yorigin as isize + y >= 0); debug_assert!(self.cfg.xorigin as isize + x >= 0); let base_y = (self.cfg.yorigin as isize + y) as usize; let base_x = (self.cfg.xorigin as isize + x) as usize; let base = base_y * self.cfg.stride + base_x; let width = (self.cfg.width as isize - x) as usize; base..base + width } /// This version of the function includes the padding on the right side of the image #[inline] pub fn row_range(&self, x: isize, y: isize) -> Range { debug_assert!(self.cfg.yorigin as isize + y >= 0); debug_assert!(self.cfg.xorigin as isize + x >= 0); let base_y = (self.cfg.yorigin as isize + y) as usize; let base_x = (self.cfg.xorigin as isize + x) as usize; let base = base_y * self.cfg.stride + base_x; let width = self.cfg.stride - base_x; base..base + width } /// Returns the pixel at the given coordinates. pub fn p(&self, x: usize, y: usize) -> T { self.data[self.index(x, y)] } /// Returns plane data starting from the origin. pub fn data_origin(&self) -> &[T] { &self.data[self.index(0, 0)..] } /// Returns mutable plane data starting from the origin. pub fn data_origin_mut(&mut self) -> &mut [T] { let i = self.index(0, 0); &mut self.data[i..] } /// Copies data into the plane from a pixel array. /// /// # Panics /// /// - If `source_bytewidth` does not match the generic `T` of `Plane` pub fn copy_from_raw_u8( &mut self, source: &[u8], source_stride: usize, source_bytewidth: usize, ) { let stride = self.cfg.stride; assert!(stride != 0); assert!(source_stride != 0); for (self_row, source_row) in self .data_origin_mut() .chunks_exact_mut(stride) .zip(source.chunks_exact(source_stride)) { match source_bytewidth { 1 => { for (self_pixel, source_pixel) in self_row.iter_mut().zip(source_row.iter()) { *self_pixel = T::cast_from(*source_pixel); } } 2 => { assert!( size_of::() == 2, "source bytewidth ({}) cannot fit in Plane", source_bytewidth ); debug_assert!(T::type_enum() == PixelType::U16); // SAFETY: because of the assert it is safe to assume that T == u16 let self_row: &mut [u16] = unsafe { std::mem::transmute(self_row) }; // SAFETY: we reinterpret the slice of bytes as a slice of elements of // [u8; 2] to allow for more efficient codegen with from_le_bytes let source_row: &[[u8; 2]] = unsafe { std::slice::from_raw_parts(source_row.as_ptr().cast(), source_row.len() / 2) }; for (self_pixel, bytes) in self_row.iter_mut().zip(source_row) { *self_pixel = u16::from_le_bytes(*bytes); } } _ => {} } } } /// Copies data from a plane into a pixel array. /// /// # Panics /// /// - If `dest_bytewidth` does not match the generic `T` of `Plane` pub fn copy_to_raw_u8(&self, dest: &mut [u8], dest_stride: usize, dest_bytewidth: usize) { let stride = self.cfg.stride; for (self_row, dest_row) in self .data_origin() .chunks_exact(stride) .zip(dest.chunks_exact_mut(dest_stride)) { match dest_bytewidth { 1 => { for (self_pixel, dest_pixel) in self_row[..self.cfg.width].iter().zip(dest_row.iter_mut()) { *dest_pixel = u8::cast_from(*self_pixel); } } 2 => { assert!( size_of::() >= 2, "dest bytewidth ({}) cannot fit in Plane", dest_bytewidth ); // SAFETY: we reinterpret the slice of bytes as a slice // of [u8; 2] with half the elements let dest_row: &mut [[u8; 2]] = unsafe { std::slice::from_raw_parts_mut( dest_row.as_mut_ptr().cast(), dest_row.len() / 2, ) }; for (self_pixel, bytes) in self_row[..self.cfg.width].iter().zip(dest_row) { *bytes = u16::cast_from(*self_pixel).to_le_bytes(); } } _ => {} } } } /// Returns plane with half the resolution for width and height. /// Downscaled with 2x2 box filter. /// Padded to dimensions with `frame_width` and `frame_height`. /// /// # Panics /// /// - If the requested width and height are > half the input width or height pub fn downsampled(&self, frame_width: usize, frame_height: usize) -> Plane { let src = self; let mut new = Plane::new( (src.cfg.width + 1) / 2, (src.cfg.height + 1) / 2, src.cfg.xdec + 1, src.cfg.ydec + 1, src.cfg.xpad / 2, src.cfg.ypad / 2, ); let width = new.cfg.width; let height = new.cfg.height; assert!(width * 2 <= src.cfg.stride - src.cfg.xorigin); assert!(height * 2 <= src.cfg.alloc_height - src.cfg.yorigin); let data_origin = src.data_origin(); for (row_idx, dst_row) in new .mut_slice(PlaneOffset::default()) .rows_iter_mut() .enumerate() .take(height) { let src_top_row = &data_origin[(src.cfg.stride * row_idx * 2)..][..(2 * width)]; let src_bottom_row = &data_origin[(src.cfg.stride * (row_idx * 2 + 1))..][..(2 * width)]; for ((dst, a), b) in dst_row .iter_mut() .zip(src_top_row.chunks_exact(2)) .zip(src_bottom_row.chunks_exact(2)) { let sum = u32::cast_from(a[0]) + u32::cast_from(a[1]) + u32::cast_from(b[0]) + u32::cast_from(b[1]); let avg = (sum + 2) >> 2; *dst = T::cast_from(avg); } } new.pad(frame_width, frame_height); new } /// Returns a plane downscaled from the source plane by a factor of `scale` (not padded) pub fn downscale(&self) -> Plane { let mut new_plane = Plane::new(self.cfg.width / SCALE, self.cfg.height / SCALE, 0, 0, 0, 0); self.downscale_in_place::(&mut new_plane); new_plane } /// Downscales the source plane by a factor of `scale`, writing the result to `in_plane` (not padded) /// /// # Panics /// /// - If the current plane's width and height are not at least `SCALE` times the `in_plane`'s #[cfg_attr(feature = "profiling", profiling::function(downscale_in_place))] pub fn downscale_in_place(&self, in_plane: &mut Plane) { let stride = in_plane.cfg.stride; let width = in_plane.cfg.width; let height = in_plane.cfg.height; if stride == 0 || self.cfg.stride == 0 { panic!("stride cannot be 0"); } assert!(width * SCALE <= self.cfg.stride - self.cfg.xorigin); assert!(height * SCALE <= self.cfg.alloc_height - self.cfg.yorigin); // SAFETY: Bounds checks have been removed for performance reasons unsafe { let src = self; let box_pixels = SCALE * SCALE; let half_box_pixels = box_pixels as u32 / 2; // Used for rounding int division let data_origin = src.data_origin(); let plane_data_mut_slice = &mut *in_plane.data; // Iter dst rows for row_idx in 0..height { let dst_row = plane_data_mut_slice.get_unchecked_mut(row_idx * stride..); // Iter dst cols for (col_idx, dst) in dst_row.get_unchecked_mut(..width).iter_mut().enumerate() { macro_rules! generate_inner_loop { ($x:ty) => { let mut sum = half_box_pixels as $x; // Sum box of size scale * scale // Iter src row for y in 0..SCALE { let src_row_idx = row_idx * SCALE + y; let src_row = data_origin.get_unchecked((src_row_idx * src.cfg.stride)..); // Iter src col for x in 0..SCALE { let src_col_idx = col_idx * SCALE + x; sum += <$x>::cast_from(*src_row.get_unchecked(src_col_idx)); } } // Box average let avg = sum as usize / box_pixels; *dst = T::cast_from(avg); }; } // Use 16 bit precision if overflow would not happen if T::type_enum() == PixelType::U8 && SCALE as u128 * SCALE as u128 * (u8::MAX as u128) + half_box_pixels as u128 <= u16::MAX as u128 { generate_inner_loop!(u16); } else { generate_inner_loop!(u32); } } } } } /// Iterates over the pixels in the plane, skipping the padding. pub fn iter(&self) -> PlaneIter<'_, T> { PlaneIter::new(self) } /// Iterates over the lines of the plane pub fn rows_iter(&self) -> RowsIter<'_, T> { RowsIter { plane: self, x: 0, y: 0, } } pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> { RowsIterMut { plane: self as *mut Plane, x: 0, y: 0, phantom: PhantomData, } } /// Return a line pub fn row(&self, y: isize) -> &[T] { let range = self.row_range(0, y); &self.data[range] } } /// Iterator over plane pixels, skipping padding. #[derive(Debug)] pub struct PlaneIter<'a, T: Pixel> { plane: &'a Plane, y: usize, x: usize, } impl<'a, T: Pixel> PlaneIter<'a, T> { /// Creates a new iterator. pub fn new(plane: &'a Plane) -> Self { Self { plane, y: 0, x: 0 } } fn width(&self) -> usize { self.plane.cfg.width } fn height(&self) -> usize { self.plane.cfg.height } } impl Iterator for PlaneIter<'_, T> { type Item = T; fn next(&mut self) -> Option<::Item> { if self.y == self.height() { return None; } let pixel = self.plane.p(self.x, self.y); if self.x == self.width() - 1 { self.x = 0; self.y += 1; } else { self.x += 1; } Some(pixel) } } impl FusedIterator for PlaneIter<'_, T> {} // A Plane, PlaneSlice, or PlaneRegion is assumed to include or be able to include // padding on the edge of the frame #[derive(Clone, Copy, Debug)] pub struct PlaneSlice<'a, T: Pixel> { pub plane: &'a Plane, pub x: isize, pub y: isize, } // A RowsIter or RowsIterMut is assumed to crop the padding from the frame edges pub struct RowsIter<'a, T: Pixel> { plane: &'a Plane, x: isize, y: isize, } impl<'a, T: Pixel> Iterator for RowsIter<'a, T> { type Item = &'a [T]; fn next(&mut self) -> Option { if self.plane.cfg.height as isize > self.y { // cannot directly return self.ps.row(row) due to lifetime issue let range = self.plane.row_range_cropped(self.x, self.y); self.y += 1; Some(&self.plane.data[range]) } else { None } } fn size_hint(&self) -> (usize, Option) { let remaining = self.plane.cfg.height as isize - self.y; debug_assert!(remaining >= 0); let remaining = remaining as usize; (remaining, Some(remaining)) } } impl ExactSizeIterator for RowsIter<'_, T> {} impl FusedIterator for RowsIter<'_, T> {} impl<'a, T: Pixel> PlaneSlice<'a, T> { #[allow(unused)] pub fn as_ptr(&self) -> *const T { self[0].as_ptr() } pub fn rows_iter(&self) -> RowsIter<'_, T> { RowsIter { plane: self.plane, x: self.x, y: self.y, } } pub fn clamp(&self) -> PlaneSlice<'a, T> { PlaneSlice { plane: self.plane, x: self.x.clamp( -(self.plane.cfg.xorigin as isize), self.plane.cfg.width as isize, ), y: self.y.clamp( -(self.plane.cfg.yorigin as isize), self.plane.cfg.height as isize, ), } } pub fn subslice(&self, xo: usize, yo: usize) -> PlaneSlice<'a, T> { PlaneSlice { plane: self.plane, x: self.x + xo as isize, y: self.y + yo as isize, } } pub fn reslice(&self, xo: isize, yo: isize) -> PlaneSlice<'a, T> { PlaneSlice { plane: self.plane, x: self.x + xo, y: self.y + yo, } } /// A slice starting i pixels above the current one. pub fn go_up(&self, i: usize) -> PlaneSlice<'a, T> { PlaneSlice { plane: self.plane, x: self.x, y: self.y - i as isize, } } /// A slice starting i pixels to the left of the current one. pub fn go_left(&self, i: usize) -> PlaneSlice<'a, T> { PlaneSlice { plane: self.plane, x: self.x - i as isize, y: self.y, } } pub fn p(&self, add_x: usize, add_y: usize) -> T { let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize; let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize; self.plane.data[new_y * self.plane.cfg.stride + new_x] } /// Checks if `add_y` and `add_x` lies in the allocated bounds of the /// underlying plane. pub fn accessible(&self, add_x: usize, add_y: usize) -> bool { let y = (self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize; let x = (self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize; y < self.plane.cfg.alloc_height && x < self.plane.cfg.stride } /// Checks if -`sub_x` and -`sub_y` lies in the allocated bounds of the /// underlying plane. pub fn accessible_neg(&self, sub_x: usize, sub_y: usize) -> bool { let y = self.y - sub_y as isize + self.plane.cfg.yorigin as isize; let x = self.x - sub_x as isize + self.plane.cfg.xorigin as isize; y >= 0 && x >= 0 } /// This version of the function crops off the padding on the right side of the image pub fn row_cropped(&self, y: usize) -> &[T] { let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize; let x = (self.x + self.plane.cfg.xorigin as isize) as usize; let start = y * self.plane.cfg.stride + x; let width = (self.plane.cfg.width as isize - self.x) as usize; &self.plane.data[start..start + width] } /// This version of the function includes the padding on the right side of the image pub fn row(&self, y: usize) -> &[T] { let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize; let x = (self.x + self.plane.cfg.xorigin as isize) as usize; let start = y * self.plane.cfg.stride + x; let width = self.plane.cfg.stride - x; &self.plane.data[start..start + width] } } impl Index for PlaneSlice<'_, T> { type Output = [T]; fn index(&self, index: usize) -> &Self::Output { let range = self.plane.row_range(self.x, self.y + index as isize); &self.plane.data[range] } } pub struct PlaneMutSlice<'a, T: Pixel> { pub plane: &'a mut Plane, pub x: isize, pub y: isize, } pub struct RowsIterMut<'a, T: Pixel> { plane: *mut Plane, x: isize, y: isize, phantom: PhantomData<&'a mut Plane>, } impl<'a, T: Pixel> Iterator for RowsIterMut<'a, T> { type Item = &'a mut [T]; fn next(&mut self) -> Option { // SAFETY: there could not be a concurrent call using a mutable reference to the plane let plane = unsafe { &mut *self.plane }; if plane.cfg.height as isize > self.y { // cannot directly return self.ps.row(row) due to lifetime issue let range = plane.row_range_cropped(self.x, self.y); self.y += 1; Some(&mut plane.data[range]) } else { None } } fn size_hint(&self) -> (usize, Option) { // SAFETY: there could not be a concurrent call using a mutable reference to the plane let plane = unsafe { &mut *self.plane }; let remaining = plane.cfg.height as isize - self.y; debug_assert!(remaining >= 0); let remaining = remaining as usize; (remaining, Some(remaining)) } } impl ExactSizeIterator for RowsIterMut<'_, T> {} impl FusedIterator for RowsIterMut<'_, T> {} impl PlaneMutSlice<'_, T> { #[allow(unused)] pub fn rows_iter(&self) -> RowsIter<'_, T> { RowsIter { plane: self.plane, x: self.x, y: self.y, } } pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> { RowsIterMut { plane: self.plane as *mut Plane, x: self.x, y: self.y, phantom: PhantomData, } } #[allow(unused)] pub fn subslice(&mut self, xo: usize, yo: usize) -> PlaneMutSlice<'_, T> { PlaneMutSlice { plane: self.plane, x: self.x + xo as isize, y: self.y + yo as isize, } } } impl Index for PlaneMutSlice<'_, T> { type Output = [T]; fn index(&self, index: usize) -> &Self::Output { let range = self.plane.row_range(self.x, self.y + index as isize); &self.plane.data[range] } } impl IndexMut for PlaneMutSlice<'_, T> { fn index_mut(&mut self, index: usize) -> &mut Self::Output { let range = self.plane.row_range(self.x, self.y + index as isize); &mut self.plane.data[range] } } #[cfg(test)] pub mod test { use super::*; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] use wasm_bindgen_test::*; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] wasm_bindgen_test_configure!(run_in_browser); #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn copy_from_raw_u8() { #[rustfmt::skip] let mut plane = Plane::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 8, 7, 6, 5, 0, 0, 0, 0, 9, 8, 7, 6, 0, 0, 0, 0, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], 8); let input = vec![42u8; 64]; plane.copy_from_raw_u8(&input, 8, 1); println!("{:?}", &plane.data[..10]); assert_eq!(&input[..64], &plane.data[..64]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn copy_to_raw_u8() { #[rustfmt::skip] let plane = Plane::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 8, 7, 6, 5, 0, 0, 0, 0, 9, 8, 7, 6, 0, 0, 0, 0, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], 8); let mut output = vec![42u8; 64]; plane.copy_to_raw_u8(&mut output, 8, 1); println!("{:?}", &plane.data[..10]); assert_eq!(&output[..64], &plane.data[..64]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn test_plane_downsample() { #[rustfmt::skip] let plane = Plane:: { data: PlaneData::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 8, 7, 6, 5, 0, 0, 0, 0, 9, 8, 7, 6, 0, 0, 0, 0, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]), cfg: PlaneConfig { stride: 8, alloc_height: 9, width: 4, height: 4, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 2, yorigin: 3, }, }; let downsampled = plane.downsampled(4, 4); #[rustfmt::skip] let expected = &[ 5, 5, 6, 6, ]; let v: Vec<_> = downsampled.iter().collect(); assert_eq!(&expected[..], &v[..]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn test_plane_downsample_odd() { #[rustfmt::skip] let plane = Plane:: { data: PlaneData::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 8, 7, 6, 5, 0, 0, 0, 0, 9, 8, 7, 6, 0, 0, 0, 0, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]), cfg: PlaneConfig { stride: 8, alloc_height: 9, width: 3, height: 3, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 2, yorigin: 3, }, }; let downsampled = plane.downsampled(3, 3); #[rustfmt::skip] let expected = &[ 5, 5, 6, 6, ]; let v: Vec<_> = downsampled.iter().collect(); assert_eq!(&expected[..], &v[..]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn test_plane_downscale() { #[rustfmt::skip] let plane = Plane:: { data: PlaneData::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 5, 0, 0, 0, 0, 2, 3, 6, 7, 0, 0, 0, 0, 8, 9, 7, 5, 0, 0, 0, 0, 9, 8, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]), cfg: PlaneConfig { stride: 8, alloc_height: 9, width: 4, height: 4, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 2, yorigin: 3, }, }; let downscaled = plane.downscale::<2>(); #[rustfmt::skip] let expected = &[ 2, 6, 9, 4 ]; let v: Vec<_> = downscaled.iter().collect(); assert_eq!(&expected[..], &v[..]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn test_plane_downscale_odd() { #[rustfmt::skip] let plane = Plane:: { data: PlaneData::from_slice(&[ 1, 2, 3, 4, 1, 2, 3, 4, 0, 0, 8, 7, 6, 5, 8, 7, 6, 5, 8, 7, 6, 5, 8, 7, 6, 5, 8, 7, 0, 0, 2, 3, 4, 5, 0, 0, 9, 8, 7, 6, 0, 0, 0, 0, 2, 3, 4, 5, 0, 0, 0, 0, 2, 3, 4, 5, ]), cfg: PlaneConfig { stride: 8, alloc_height: 7, width: 8, height: 7, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 0, yorigin: 0, }, }; let downscaled = plane.downscale::<3>(); #[rustfmt::skip] let expected = &[ 4, 5, 3, 3 ]; let v: Vec<_> = downscaled.iter().collect(); assert_eq!(&expected[..], &v[..]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn test_plane_downscale_odd_2() { #[rustfmt::skip] let plane = Plane:: { data: PlaneData::from_slice(&[ 9, 8, 3, 1, 0, 1, 4, 5, 0, 0, 0, 1, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 2, 3, 6, 7, 0, 0, 0, 0, 0, 0, 0, 8, 9, 7, 5, 0, 0, 0, 0, 9, 8, 3, 1, 0, 1, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 6, 7, 0, 0, 0, 0, 0, 0, 0, 8, 9, 7, 5, 0, 0, 0, 0, 9, 8, 3, 1, 0, 0 ]), cfg: PlaneConfig { stride: 10, alloc_height: 10, width: 10, height: 10, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 0, yorigin: 0, }, }; let downscaled = plane.downscale::<3>(); #[rustfmt::skip] let expected = &[ 3, 1, 2, 4, 4, 1, 0, 0, 4, ]; let v: Vec<_> = downscaled.iter().collect(); assert_eq!(&expected[..], &v[..]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn test_plane_pad() { #[rustfmt::skip] let mut plane = Plane:: { data: PlaneData::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 8, 7, 6, 5, 0, 0, 0, 0, 9, 8, 7, 6, 0, 0, 0, 0, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]), cfg: PlaneConfig { stride: 8, alloc_height: 9, width: 4, height: 4, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 2, yorigin: 3, }, }; plane.pad(4, 4); #[rustfmt::skip] assert_eq!(&[ 1, 1, 1, 2, 3, 4, 4, 4, 1, 1, 1, 2, 3, 4, 4, 4, 1, 1, 1, 2, 3, 4, 4, 4, 1, 1, 1, 2, 3, 4, 4, 4, 8, 8, 8, 7, 6, 5, 5, 5, 9, 9, 9, 8, 7, 6, 6, 6, 2, 2, 2, 3, 4, 5, 5, 5, 2, 2, 2, 3, 4, 5, 5, 5, 2, 2, 2, 3, 4, 5, 5, 5, ], &plane.data[..]); } #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)] #[test] fn test_pixel_iterator() { #[rustfmt::skip] let plane = Plane:: { data: PlaneData::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 8, 7, 6, 5, 0, 0, 0, 0, 9, 8, 7, 6, 0, 0, 0, 0, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]), cfg: PlaneConfig { stride: 8, alloc_height: 9, width: 4, height: 4, xdec: 0, ydec: 0, xpad: 0, ypad: 0, xorigin: 2, yorigin: 3, }, }; let pixels: Vec = plane.iter().collect(); assert_eq!( &[1, 2, 3, 4, 8, 7, 6, 5, 9, 8, 7, 6, 2, 3, 4, 5,][..], &pixels[..] ); } }