├── .github └── workflows │ ├── add_to_project.yml │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .rive_head ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets └── rating-animation.riv ├── cliff.toml ├── examples └── viewer │ ├── Cargo.toml │ ├── README.md │ └── src │ └── main.rs ├── release-plz.toml └── rive-rs ├── Cargo.toml ├── build.rs └── src ├── artboard ├── components │ ├── mod.rs │ └── text_value_run.rs └── mod.rs ├── ffi.cpp ├── ffi.rs ├── file.rs ├── instantiate.rs ├── lib.rs ├── linear_animation.rs ├── path.rs ├── raw_iter.rs ├── renderer.rs ├── scene.rs ├── state_machine ├── events │ ├── mod.rs │ └── properties.rs ├── inputs.rs └── mod.rs └── vello ├── mod.rs └── util.rs /.github/workflows/add_to_project.yml: -------------------------------------------------------------------------------- 1 | name: Adds all new issues to project board 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v0.5.0 14 | with: 15 | project-url: https://github.com/orgs/rive-app/projects/12/views/1 16 | github-token: ${{ secrets.ADD_TO_PROJECT_ACTION }} 17 | 18 | - uses: actions/github-script@v6 19 | with: 20 | script: | 21 | github.rest.issues.addLabels({ 22 | issue_number: context.issue.number, 23 | owner: context.repo.owner, 24 | repo: context.repo.repo, 25 | labels: ["triage"] 26 | }) 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous integration 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | check: 8 | name: Check 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | submodules: recursive 14 | - uses: dtolnay/rust-toolchain@1.72.0 15 | - uses: Swatinem/rust-cache@v2 16 | - run: cargo check --all --no-default-features 17 | - run: cargo check --all --all-features 18 | 19 | build-clang: 20 | name: Build (clang) 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | submodules: recursive 26 | - uses: dtolnay/rust-toolchain@1.72.0 27 | - uses: Swatinem/rust-cache@v2 28 | - name: Install clang 29 | run: | 30 | sudo apt update 31 | sudo apt install clang 32 | - run: CC="clang" CXX="clang++" cargo build --all --no-default-features 33 | - run: CC="clang" CXX="clang++" cargo build --all --all-features 34 | 35 | build-gcc: 36 | name: Build (GCC) 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v4 40 | with: 41 | submodules: recursive 42 | - uses: dtolnay/rust-toolchain@1.72.0 43 | - uses: Swatinem/rust-cache@v2 44 | - run: CC="gcc" CXX="g++" cargo build --all --no-default-features 45 | - run: CC="gcc" CXX="g++" cargo build --all --all-features 46 | 47 | test: 48 | name: Test 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v4 52 | with: 53 | submodules: recursive 54 | - uses: dtolnay/rust-toolchain@1.72.0 55 | - uses: Swatinem/rust-cache@v2 56 | - name: Install clang 57 | run: | 58 | sudo apt update 59 | sudo apt install clang 60 | - run: cargo test --all --no-default-features 61 | - run: cargo test --all --all-features 62 | 63 | fmt: 64 | name: Rustfmt 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v4 68 | with: 69 | submodules: recursive 70 | - uses: dtolnay/rust-toolchain@1.72.0 71 | with: 72 | components: rustfmt 73 | - run: cargo fmt --all -- --check 74 | 75 | clippy: 76 | name: Clippy 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v4 80 | with: 81 | submodules: recursive 82 | - uses: dtolnay/rust-toolchain@1.72.0 83 | with: 84 | components: clippy 85 | - uses: Swatinem/rust-cache@v2 86 | - name: Install clang 87 | run: | 88 | sudo apt update 89 | sudo apt install clang 90 | - run: cargo clippy --all --no-default-features -- -D warnings 91 | - run: cargo clippy --all --all-features -- -D warnings 92 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | pull-requests: write 5 | contents: write 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | release-plz: 14 | name: Release 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: Install Rust toolchain 22 | uses: dtolnay/rust-toolchain@stable 23 | - name: Run release-plz 24 | uses: MarcoIeni/release-plz-action@v0.5 25 | with: 26 | command: release-pr 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/SheenBidi"] 2 | path = submodules/SheenBidi 3 | url = https://github.com/Tehreer/SheenBidi 4 | [submodule "submodules/rive-cpp"] 5 | path = submodules/rive-cpp 6 | url = https://github.com/rive-app/rive-cpp 7 | [submodule "submodules/harfbuzz"] 8 | path = submodules/harfbuzz 9 | url = https://github.com/rive-app/harfbuzz 10 | -------------------------------------------------------------------------------- /.rive_head: -------------------------------------------------------------------------------- 1 | 7c8352ad63f205ed8f38271704b5f880edaf573b 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love contributions! If you want to run the project locally to test out changes, run the examples, 4 | or just see how things work under the hood, read on below. 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ab_glyph" 7 | version = "0.2.22" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b1061f3ff92c2f65800df1f12fc7b4ff44ee14783104187dd04dfee6f11b0fd2" 10 | dependencies = [ 11 | "ab_glyph_rasterizer", 12 | "owned_ttf_parser", 13 | ] 14 | 15 | [[package]] 16 | name = "ab_glyph_rasterizer" 17 | version = "0.1.8" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" 20 | 21 | [[package]] 22 | name = "addr2line" 23 | version = "0.21.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 26 | dependencies = [ 27 | "gimli", 28 | ] 29 | 30 | [[package]] 31 | name = "adler" 32 | version = "1.0.2" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 35 | 36 | [[package]] 37 | name = "ahash" 38 | version = "0.8.3" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 41 | dependencies = [ 42 | "cfg-if", 43 | "once_cell", 44 | "version_check", 45 | ] 46 | 47 | [[package]] 48 | name = "allocator-api2" 49 | version = "0.2.16" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" 52 | 53 | [[package]] 54 | name = "android-activity" 55 | version = "0.4.3" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0" 58 | dependencies = [ 59 | "android-properties", 60 | "bitflags 1.3.2", 61 | "cc", 62 | "jni-sys", 63 | "libc", 64 | "log", 65 | "ndk", 66 | "ndk-context", 67 | "ndk-sys", 68 | "num_enum 0.6.1", 69 | ] 70 | 71 | [[package]] 72 | name = "android-properties" 73 | version = "0.2.2" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" 76 | 77 | [[package]] 78 | name = "android_system_properties" 79 | version = "0.1.5" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 82 | dependencies = [ 83 | "libc", 84 | ] 85 | 86 | [[package]] 87 | name = "arrayref" 88 | version = "0.3.7" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" 91 | 92 | [[package]] 93 | name = "arrayvec" 94 | version = "0.7.4" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" 97 | 98 | [[package]] 99 | name = "ash" 100 | version = "0.37.3+1.3.251" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" 103 | dependencies = [ 104 | "libloading 0.7.4", 105 | ] 106 | 107 | [[package]] 108 | name = "autocfg" 109 | version = "1.1.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 112 | 113 | [[package]] 114 | name = "backtrace" 115 | version = "0.3.69" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 118 | dependencies = [ 119 | "addr2line", 120 | "cc", 121 | "cfg-if", 122 | "libc", 123 | "miniz_oxide", 124 | "object", 125 | "rustc-demangle", 126 | ] 127 | 128 | [[package]] 129 | name = "bit-set" 130 | version = "0.5.3" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 133 | dependencies = [ 134 | "bit-vec", 135 | ] 136 | 137 | [[package]] 138 | name = "bit-vec" 139 | version = "0.6.3" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 142 | 143 | [[package]] 144 | name = "bit_field" 145 | version = "0.10.2" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 148 | 149 | [[package]] 150 | name = "bitflags" 151 | version = "1.3.2" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 154 | 155 | [[package]] 156 | name = "bitflags" 157 | version = "2.4.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 160 | 161 | [[package]] 162 | name = "block" 163 | version = "0.1.6" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 166 | 167 | [[package]] 168 | name = "block-sys" 169 | version = "0.1.0-beta.1" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" 172 | dependencies = [ 173 | "objc-sys", 174 | ] 175 | 176 | [[package]] 177 | name = "block2" 178 | version = "0.2.0-alpha.6" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" 181 | dependencies = [ 182 | "block-sys", 183 | "objc2-encode", 184 | ] 185 | 186 | [[package]] 187 | name = "bumpalo" 188 | version = "3.14.0" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 191 | 192 | [[package]] 193 | name = "bytemuck" 194 | version = "1.14.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" 197 | dependencies = [ 198 | "bytemuck_derive", 199 | ] 200 | 201 | [[package]] 202 | name = "bytemuck_derive" 203 | version = "1.5.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" 206 | dependencies = [ 207 | "proc-macro2", 208 | "quote", 209 | "syn 2.0.38", 210 | ] 211 | 212 | [[package]] 213 | name = "byteorder" 214 | version = "1.5.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 217 | 218 | [[package]] 219 | name = "calloop" 220 | version = "0.10.6" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" 223 | dependencies = [ 224 | "bitflags 1.3.2", 225 | "log", 226 | "nix 0.25.1", 227 | "slotmap", 228 | "thiserror", 229 | "vec_map", 230 | ] 231 | 232 | [[package]] 233 | name = "cc" 234 | version = "1.0.83" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 237 | dependencies = [ 238 | "jobserver", 239 | "libc", 240 | ] 241 | 242 | [[package]] 243 | name = "cfg-if" 244 | version = "1.0.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 247 | 248 | [[package]] 249 | name = "cfg_aliases" 250 | version = "0.1.1" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 253 | 254 | [[package]] 255 | name = "codespan-reporting" 256 | version = "0.11.1" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 259 | dependencies = [ 260 | "termcolor", 261 | "unicode-width", 262 | ] 263 | 264 | [[package]] 265 | name = "color_quant" 266 | version = "1.1.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 269 | 270 | [[package]] 271 | name = "com-rs" 272 | version = "0.2.1" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" 275 | 276 | [[package]] 277 | name = "core-foundation" 278 | version = "0.9.3" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 281 | dependencies = [ 282 | "core-foundation-sys", 283 | "libc", 284 | ] 285 | 286 | [[package]] 287 | name = "core-foundation-sys" 288 | version = "0.8.4" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 291 | 292 | [[package]] 293 | name = "core-graphics" 294 | version = "0.22.3" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" 297 | dependencies = [ 298 | "bitflags 1.3.2", 299 | "core-foundation", 300 | "core-graphics-types", 301 | "foreign-types 0.3.2", 302 | "libc", 303 | ] 304 | 305 | [[package]] 306 | name = "core-graphics-types" 307 | version = "0.1.2" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" 310 | dependencies = [ 311 | "bitflags 1.3.2", 312 | "core-foundation", 313 | "libc", 314 | ] 315 | 316 | [[package]] 317 | name = "crc32fast" 318 | version = "1.3.2" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 321 | dependencies = [ 322 | "cfg-if", 323 | ] 324 | 325 | [[package]] 326 | name = "crossbeam-deque" 327 | version = "0.8.3" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 330 | dependencies = [ 331 | "cfg-if", 332 | "crossbeam-epoch", 333 | "crossbeam-utils", 334 | ] 335 | 336 | [[package]] 337 | name = "crossbeam-epoch" 338 | version = "0.9.15" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" 341 | dependencies = [ 342 | "autocfg", 343 | "cfg-if", 344 | "crossbeam-utils", 345 | "memoffset 0.9.0", 346 | "scopeguard", 347 | ] 348 | 349 | [[package]] 350 | name = "crossbeam-utils" 351 | version = "0.8.16" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 354 | dependencies = [ 355 | "cfg-if", 356 | ] 357 | 358 | [[package]] 359 | name = "crunchy" 360 | version = "0.2.2" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 363 | 364 | [[package]] 365 | name = "d3d12" 366 | version = "0.7.0" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20" 369 | dependencies = [ 370 | "bitflags 2.4.1", 371 | "libloading 0.8.1", 372 | "winapi", 373 | ] 374 | 375 | [[package]] 376 | name = "dispatch" 377 | version = "0.2.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" 380 | 381 | [[package]] 382 | name = "dlib" 383 | version = "0.5.2" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" 386 | dependencies = [ 387 | "libloading 0.8.1", 388 | ] 389 | 390 | [[package]] 391 | name = "downcast-rs" 392 | version = "1.2.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 395 | 396 | [[package]] 397 | name = "either" 398 | version = "1.9.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 401 | 402 | [[package]] 403 | name = "equivalent" 404 | version = "1.0.1" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 407 | 408 | [[package]] 409 | name = "euclid" 410 | version = "0.22.9" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" 413 | dependencies = [ 414 | "num-traits", 415 | ] 416 | 417 | [[package]] 418 | name = "exr" 419 | version = "1.71.0" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" 422 | dependencies = [ 423 | "bit_field", 424 | "flume", 425 | "half", 426 | "lebe", 427 | "miniz_oxide", 428 | "rayon-core", 429 | "smallvec", 430 | "zune-inflate", 431 | ] 432 | 433 | [[package]] 434 | name = "fdeflate" 435 | version = "0.3.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" 438 | dependencies = [ 439 | "simd-adler32", 440 | ] 441 | 442 | [[package]] 443 | name = "fello" 444 | version = "0.1.0" 445 | source = "git+https://github.com/dfrg/fount?rev=dadbcf75695f035ca46766bfd60555d05bd421b1#dadbcf75695f035ca46766bfd60555d05bd421b1" 446 | dependencies = [ 447 | "read-fonts", 448 | ] 449 | 450 | [[package]] 451 | name = "flate2" 452 | version = "1.0.28" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 455 | dependencies = [ 456 | "crc32fast", 457 | "miniz_oxide", 458 | ] 459 | 460 | [[package]] 461 | name = "flume" 462 | version = "0.11.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" 465 | dependencies = [ 466 | "spin", 467 | ] 468 | 469 | [[package]] 470 | name = "font-types" 471 | version = "0.3.4" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "6978d65d61022aa249fefdd914dc8215757f617f1a697c496ef6b42013366567" 474 | 475 | [[package]] 476 | name = "foreign-types" 477 | version = "0.3.2" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 480 | dependencies = [ 481 | "foreign-types-shared 0.1.1", 482 | ] 483 | 484 | [[package]] 485 | name = "foreign-types" 486 | version = "0.5.0" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 489 | dependencies = [ 490 | "foreign-types-macros", 491 | "foreign-types-shared 0.3.1", 492 | ] 493 | 494 | [[package]] 495 | name = "foreign-types-macros" 496 | version = "0.2.3" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" 499 | dependencies = [ 500 | "proc-macro2", 501 | "quote", 502 | "syn 2.0.38", 503 | ] 504 | 505 | [[package]] 506 | name = "foreign-types-shared" 507 | version = "0.1.1" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 510 | 511 | [[package]] 512 | name = "foreign-types-shared" 513 | version = "0.3.1" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 516 | 517 | [[package]] 518 | name = "futures-core" 519 | version = "0.3.28" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 522 | 523 | [[package]] 524 | name = "futures-intrusive" 525 | version = "0.5.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" 528 | dependencies = [ 529 | "futures-core", 530 | "lock_api", 531 | "parking_lot", 532 | ] 533 | 534 | [[package]] 535 | name = "gif" 536 | version = "0.12.0" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" 539 | dependencies = [ 540 | "color_quant", 541 | "weezl", 542 | ] 543 | 544 | [[package]] 545 | name = "gimli" 546 | version = "0.28.0" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" 549 | 550 | [[package]] 551 | name = "glow" 552 | version = "0.12.3" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "ca0fe580e4b60a8ab24a868bc08e2f03cbcb20d3d676601fa909386713333728" 555 | dependencies = [ 556 | "js-sys", 557 | "slotmap", 558 | "wasm-bindgen", 559 | "web-sys", 560 | ] 561 | 562 | [[package]] 563 | name = "gpu-alloc" 564 | version = "0.6.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" 567 | dependencies = [ 568 | "bitflags 2.4.1", 569 | "gpu-alloc-types", 570 | ] 571 | 572 | [[package]] 573 | name = "gpu-alloc-types" 574 | version = "0.3.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" 577 | dependencies = [ 578 | "bitflags 2.4.1", 579 | ] 580 | 581 | [[package]] 582 | name = "gpu-allocator" 583 | version = "0.22.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "ce95f9e2e11c2c6fadfce42b5af60005db06576f231f5c92550fdded43c423e8" 586 | dependencies = [ 587 | "backtrace", 588 | "log", 589 | "thiserror", 590 | "winapi", 591 | "windows", 592 | ] 593 | 594 | [[package]] 595 | name = "gpu-descriptor" 596 | version = "0.2.4" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" 599 | dependencies = [ 600 | "bitflags 2.4.1", 601 | "gpu-descriptor-types", 602 | "hashbrown 0.14.1", 603 | ] 604 | 605 | [[package]] 606 | name = "gpu-descriptor-types" 607 | version = "0.1.2" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" 610 | dependencies = [ 611 | "bitflags 2.4.1", 612 | ] 613 | 614 | [[package]] 615 | name = "guillotiere" 616 | version = "0.6.2" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" 619 | dependencies = [ 620 | "euclid", 621 | "svg_fmt", 622 | ] 623 | 624 | [[package]] 625 | name = "half" 626 | version = "2.2.1" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" 629 | dependencies = [ 630 | "crunchy", 631 | ] 632 | 633 | [[package]] 634 | name = "hashbrown" 635 | version = "0.12.3" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 638 | 639 | [[package]] 640 | name = "hashbrown" 641 | version = "0.14.1" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" 644 | dependencies = [ 645 | "ahash", 646 | "allocator-api2", 647 | ] 648 | 649 | [[package]] 650 | name = "hassle-rs" 651 | version = "0.10.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "1397650ee315e8891a0df210707f0fc61771b0cc518c3023896064c5407cb3b0" 654 | dependencies = [ 655 | "bitflags 1.3.2", 656 | "com-rs", 657 | "libc", 658 | "libloading 0.7.4", 659 | "thiserror", 660 | "widestring", 661 | "winapi", 662 | ] 663 | 664 | [[package]] 665 | name = "hexf-parse" 666 | version = "0.2.1" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 669 | 670 | [[package]] 671 | name = "image" 672 | version = "0.24.7" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" 675 | dependencies = [ 676 | "bytemuck", 677 | "byteorder", 678 | "color_quant", 679 | "exr", 680 | "gif", 681 | "jpeg-decoder", 682 | "num-rational", 683 | "num-traits", 684 | "png", 685 | "qoi", 686 | "tiff", 687 | ] 688 | 689 | [[package]] 690 | name = "indexmap" 691 | version = "1.9.3" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 694 | dependencies = [ 695 | "autocfg", 696 | "hashbrown 0.12.3", 697 | ] 698 | 699 | [[package]] 700 | name = "indexmap" 701 | version = "2.0.2" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" 704 | dependencies = [ 705 | "equivalent", 706 | "hashbrown 0.14.1", 707 | ] 708 | 709 | [[package]] 710 | name = "instant" 711 | version = "0.1.12" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 714 | dependencies = [ 715 | "cfg-if", 716 | "js-sys", 717 | "wasm-bindgen", 718 | "web-sys", 719 | ] 720 | 721 | [[package]] 722 | name = "jni-sys" 723 | version = "0.3.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 726 | 727 | [[package]] 728 | name = "jobserver" 729 | version = "0.1.27" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" 732 | dependencies = [ 733 | "libc", 734 | ] 735 | 736 | [[package]] 737 | name = "jpeg-decoder" 738 | version = "0.3.0" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" 741 | dependencies = [ 742 | "rayon", 743 | ] 744 | 745 | [[package]] 746 | name = "js-sys" 747 | version = "0.3.64" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 750 | dependencies = [ 751 | "wasm-bindgen", 752 | ] 753 | 754 | [[package]] 755 | name = "khronos-egl" 756 | version = "4.1.0" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" 759 | dependencies = [ 760 | "libc", 761 | "libloading 0.7.4", 762 | "pkg-config", 763 | ] 764 | 765 | [[package]] 766 | name = "kurbo" 767 | version = "0.9.5" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" 770 | dependencies = [ 771 | "arrayvec", 772 | ] 773 | 774 | [[package]] 775 | name = "lazy_static" 776 | version = "1.4.0" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 779 | 780 | [[package]] 781 | name = "lebe" 782 | version = "0.5.2" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" 785 | 786 | [[package]] 787 | name = "libc" 788 | version = "0.2.149" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" 791 | 792 | [[package]] 793 | name = "libloading" 794 | version = "0.7.4" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" 797 | dependencies = [ 798 | "cfg-if", 799 | "winapi", 800 | ] 801 | 802 | [[package]] 803 | name = "libloading" 804 | version = "0.8.1" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" 807 | dependencies = [ 808 | "cfg-if", 809 | "windows-sys 0.48.0", 810 | ] 811 | 812 | [[package]] 813 | name = "lock_api" 814 | version = "0.4.10" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" 817 | dependencies = [ 818 | "autocfg", 819 | "scopeguard", 820 | ] 821 | 822 | [[package]] 823 | name = "log" 824 | version = "0.4.20" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 827 | 828 | [[package]] 829 | name = "malloc_buf" 830 | version = "0.0.6" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 833 | dependencies = [ 834 | "libc", 835 | ] 836 | 837 | [[package]] 838 | name = "memchr" 839 | version = "2.6.4" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 842 | 843 | [[package]] 844 | name = "memmap2" 845 | version = "0.5.10" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" 848 | dependencies = [ 849 | "libc", 850 | ] 851 | 852 | [[package]] 853 | name = "memoffset" 854 | version = "0.6.5" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 857 | dependencies = [ 858 | "autocfg", 859 | ] 860 | 861 | [[package]] 862 | name = "memoffset" 863 | version = "0.9.0" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 866 | dependencies = [ 867 | "autocfg", 868 | ] 869 | 870 | [[package]] 871 | name = "metal" 872 | version = "0.26.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "623b5e6cefd76e58f774bd3cc0c6f5c7615c58c03a97815245a25c3c9bdee318" 875 | dependencies = [ 876 | "bitflags 2.4.1", 877 | "block", 878 | "core-graphics-types", 879 | "foreign-types 0.5.0", 880 | "log", 881 | "objc", 882 | "paste", 883 | ] 884 | 885 | [[package]] 886 | name = "minimal-lexical" 887 | version = "0.2.1" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 890 | 891 | [[package]] 892 | name = "miniz_oxide" 893 | version = "0.7.1" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 896 | dependencies = [ 897 | "adler", 898 | "simd-adler32", 899 | ] 900 | 901 | [[package]] 902 | name = "mio" 903 | version = "0.8.8" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" 906 | dependencies = [ 907 | "libc", 908 | "log", 909 | "wasi", 910 | "windows-sys 0.48.0", 911 | ] 912 | 913 | [[package]] 914 | name = "naga" 915 | version = "0.13.0" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "c1ceaaa4eedaece7e4ec08c55c640ba03dbb73fb812a6570a59bcf1930d0f70e" 918 | dependencies = [ 919 | "bit-set", 920 | "bitflags 2.4.1", 921 | "codespan-reporting", 922 | "hexf-parse", 923 | "indexmap 1.9.3", 924 | "log", 925 | "num-traits", 926 | "rustc-hash", 927 | "spirv", 928 | "termcolor", 929 | "thiserror", 930 | "unicode-xid", 931 | ] 932 | 933 | [[package]] 934 | name = "ndk" 935 | version = "0.7.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" 938 | dependencies = [ 939 | "bitflags 1.3.2", 940 | "jni-sys", 941 | "ndk-sys", 942 | "num_enum 0.5.11", 943 | "raw-window-handle", 944 | "thiserror", 945 | ] 946 | 947 | [[package]] 948 | name = "ndk-context" 949 | version = "0.1.1" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 952 | 953 | [[package]] 954 | name = "ndk-sys" 955 | version = "0.4.1+23.1.7779620" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" 958 | dependencies = [ 959 | "jni-sys", 960 | ] 961 | 962 | [[package]] 963 | name = "nix" 964 | version = "0.24.3" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" 967 | dependencies = [ 968 | "bitflags 1.3.2", 969 | "cfg-if", 970 | "libc", 971 | "memoffset 0.6.5", 972 | ] 973 | 974 | [[package]] 975 | name = "nix" 976 | version = "0.25.1" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" 979 | dependencies = [ 980 | "autocfg", 981 | "bitflags 1.3.2", 982 | "cfg-if", 983 | "libc", 984 | "memoffset 0.6.5", 985 | ] 986 | 987 | [[package]] 988 | name = "nom" 989 | version = "7.1.3" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 992 | dependencies = [ 993 | "memchr", 994 | "minimal-lexical", 995 | ] 996 | 997 | [[package]] 998 | name = "num-integer" 999 | version = "0.1.45" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 1002 | dependencies = [ 1003 | "autocfg", 1004 | "num-traits", 1005 | ] 1006 | 1007 | [[package]] 1008 | name = "num-rational" 1009 | version = "0.4.1" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 1012 | dependencies = [ 1013 | "autocfg", 1014 | "num-integer", 1015 | "num-traits", 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "num-traits" 1020 | version = "0.2.17" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 1023 | dependencies = [ 1024 | "autocfg", 1025 | ] 1026 | 1027 | [[package]] 1028 | name = "num_enum" 1029 | version = "0.5.11" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" 1032 | dependencies = [ 1033 | "num_enum_derive 0.5.11", 1034 | ] 1035 | 1036 | [[package]] 1037 | name = "num_enum" 1038 | version = "0.6.1" 1039 | source = "registry+https://github.com/rust-lang/crates.io-index" 1040 | checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" 1041 | dependencies = [ 1042 | "num_enum_derive 0.6.1", 1043 | ] 1044 | 1045 | [[package]] 1046 | name = "num_enum_derive" 1047 | version = "0.5.11" 1048 | source = "registry+https://github.com/rust-lang/crates.io-index" 1049 | checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" 1050 | dependencies = [ 1051 | "proc-macro-crate", 1052 | "proc-macro2", 1053 | "quote", 1054 | "syn 1.0.109", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "num_enum_derive" 1059 | version = "0.6.1" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" 1062 | dependencies = [ 1063 | "proc-macro-crate", 1064 | "proc-macro2", 1065 | "quote", 1066 | "syn 2.0.38", 1067 | ] 1068 | 1069 | [[package]] 1070 | name = "objc" 1071 | version = "0.2.7" 1072 | source = "registry+https://github.com/rust-lang/crates.io-index" 1073 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 1074 | dependencies = [ 1075 | "malloc_buf", 1076 | "objc_exception", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "objc-sys" 1081 | version = "0.2.0-beta.2" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" 1084 | 1085 | [[package]] 1086 | name = "objc2" 1087 | version = "0.3.0-beta.3.patch-leaks.3" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" 1090 | dependencies = [ 1091 | "block2", 1092 | "objc-sys", 1093 | "objc2-encode", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "objc2-encode" 1098 | version = "2.0.0-pre.2" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" 1101 | dependencies = [ 1102 | "objc-sys", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "objc_exception" 1107 | version = "0.1.2" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" 1110 | dependencies = [ 1111 | "cc", 1112 | ] 1113 | 1114 | [[package]] 1115 | name = "object" 1116 | version = "0.32.1" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" 1119 | dependencies = [ 1120 | "memchr", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "once_cell" 1125 | version = "1.18.0" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 1128 | 1129 | [[package]] 1130 | name = "orbclient" 1131 | version = "0.3.46" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "8378ac0dfbd4e7895f2d2c1f1345cab3836910baf3a300b000d04250f0c8428f" 1134 | dependencies = [ 1135 | "redox_syscall", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "owned_ttf_parser" 1140 | version = "0.19.0" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" 1143 | dependencies = [ 1144 | "ttf-parser", 1145 | ] 1146 | 1147 | [[package]] 1148 | name = "parking_lot" 1149 | version = "0.12.1" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1152 | dependencies = [ 1153 | "lock_api", 1154 | "parking_lot_core", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "parking_lot_core" 1159 | version = "0.9.8" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" 1162 | dependencies = [ 1163 | "cfg-if", 1164 | "libc", 1165 | "redox_syscall", 1166 | "smallvec", 1167 | "windows-targets 0.48.5", 1168 | ] 1169 | 1170 | [[package]] 1171 | name = "paste" 1172 | version = "1.0.14" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 1175 | 1176 | [[package]] 1177 | name = "peniko" 1178 | version = "0.1.0" 1179 | source = "git+https://github.com/linebender/peniko?rev=cafdac9a211a0fb2fec5656bd663d1ac770bcc81#cafdac9a211a0fb2fec5656bd663d1ac770bcc81" 1180 | dependencies = [ 1181 | "kurbo", 1182 | "smallvec", 1183 | ] 1184 | 1185 | [[package]] 1186 | name = "percent-encoding" 1187 | version = "2.3.0" 1188 | source = "registry+https://github.com/rust-lang/crates.io-index" 1189 | checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" 1190 | 1191 | [[package]] 1192 | name = "pkg-config" 1193 | version = "0.3.27" 1194 | source = "registry+https://github.com/rust-lang/crates.io-index" 1195 | checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" 1196 | 1197 | [[package]] 1198 | name = "png" 1199 | version = "0.17.10" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" 1202 | dependencies = [ 1203 | "bitflags 1.3.2", 1204 | "crc32fast", 1205 | "fdeflate", 1206 | "flate2", 1207 | "miniz_oxide", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "pollster" 1212 | version = "0.3.0" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" 1215 | 1216 | [[package]] 1217 | name = "proc-macro-crate" 1218 | version = "1.3.1" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 1221 | dependencies = [ 1222 | "once_cell", 1223 | "toml_edit", 1224 | ] 1225 | 1226 | [[package]] 1227 | name = "proc-macro2" 1228 | version = "1.0.69" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 1231 | dependencies = [ 1232 | "unicode-ident", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "profiling" 1237 | version = "1.0.11" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "f89dff0959d98c9758c88826cc002e2c3d0b9dfac4139711d1f30de442f1139b" 1240 | 1241 | [[package]] 1242 | name = "qoi" 1243 | version = "0.4.1" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" 1246 | dependencies = [ 1247 | "bytemuck", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "quote" 1252 | version = "1.0.33" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 1255 | dependencies = [ 1256 | "proc-macro2", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "range-alloc" 1261 | version = "0.1.3" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" 1264 | 1265 | [[package]] 1266 | name = "raw-window-handle" 1267 | version = "0.5.2" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" 1270 | 1271 | [[package]] 1272 | name = "rayon" 1273 | version = "1.8.0" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" 1276 | dependencies = [ 1277 | "either", 1278 | "rayon-core", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "rayon-core" 1283 | version = "1.12.0" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" 1286 | dependencies = [ 1287 | "crossbeam-deque", 1288 | "crossbeam-utils", 1289 | ] 1290 | 1291 | [[package]] 1292 | name = "read-fonts" 1293 | version = "0.10.0" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "87d08214643b2df95b0b3955cd9f264bcfab22b73470b83df4992df523b4d6eb" 1296 | dependencies = [ 1297 | "font-types", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "redox_syscall" 1302 | version = "0.3.5" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 1305 | dependencies = [ 1306 | "bitflags 1.3.2", 1307 | ] 1308 | 1309 | [[package]] 1310 | name = "renderdoc-sys" 1311 | version = "1.0.0" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" 1314 | 1315 | [[package]] 1316 | name = "rive-rs" 1317 | version = "0.1.0" 1318 | dependencies = [ 1319 | "bitflags 2.4.1", 1320 | "bytemuck", 1321 | "cc", 1322 | "image", 1323 | "smallvec", 1324 | "vello", 1325 | "walkdir", 1326 | ] 1327 | 1328 | [[package]] 1329 | name = "rustc-demangle" 1330 | version = "0.1.23" 1331 | source = "registry+https://github.com/rust-lang/crates.io-index" 1332 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1333 | 1334 | [[package]] 1335 | name = "rustc-hash" 1336 | version = "1.1.0" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1339 | 1340 | [[package]] 1341 | name = "same-file" 1342 | version = "1.0.6" 1343 | source = "registry+https://github.com/rust-lang/crates.io-index" 1344 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1345 | dependencies = [ 1346 | "winapi-util", 1347 | ] 1348 | 1349 | [[package]] 1350 | name = "scoped-tls" 1351 | version = "1.0.1" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 1354 | 1355 | [[package]] 1356 | name = "scopeguard" 1357 | version = "1.2.0" 1358 | source = "registry+https://github.com/rust-lang/crates.io-index" 1359 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1360 | 1361 | [[package]] 1362 | name = "sctk-adwaita" 1363 | version = "0.5.4" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" 1366 | dependencies = [ 1367 | "ab_glyph", 1368 | "log", 1369 | "memmap2", 1370 | "smithay-client-toolkit", 1371 | "tiny-skia", 1372 | ] 1373 | 1374 | [[package]] 1375 | name = "simd-adler32" 1376 | version = "0.3.7" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 1379 | 1380 | [[package]] 1381 | name = "slotmap" 1382 | version = "1.0.6" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" 1385 | dependencies = [ 1386 | "version_check", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "smallvec" 1391 | version = "1.11.1" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" 1394 | 1395 | [[package]] 1396 | name = "smithay-client-toolkit" 1397 | version = "0.16.1" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" 1400 | dependencies = [ 1401 | "bitflags 1.3.2", 1402 | "calloop", 1403 | "dlib", 1404 | "lazy_static", 1405 | "log", 1406 | "memmap2", 1407 | "nix 0.24.3", 1408 | "pkg-config", 1409 | "wayland-client", 1410 | "wayland-cursor", 1411 | "wayland-protocols", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "spin" 1416 | version = "0.9.8" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1419 | dependencies = [ 1420 | "lock_api", 1421 | ] 1422 | 1423 | [[package]] 1424 | name = "spirv" 1425 | version = "0.2.0+1.5.4" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" 1428 | dependencies = [ 1429 | "bitflags 1.3.2", 1430 | "num-traits", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "static_assertions" 1435 | version = "1.1.0" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1438 | 1439 | [[package]] 1440 | name = "strict-num" 1441 | version = "0.1.1" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" 1444 | 1445 | [[package]] 1446 | name = "svg_fmt" 1447 | version = "0.4.1" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" 1450 | 1451 | [[package]] 1452 | name = "syn" 1453 | version = "1.0.109" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1456 | dependencies = [ 1457 | "proc-macro2", 1458 | "quote", 1459 | "unicode-ident", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "syn" 1464 | version = "2.0.38" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" 1467 | dependencies = [ 1468 | "proc-macro2", 1469 | "quote", 1470 | "unicode-ident", 1471 | ] 1472 | 1473 | [[package]] 1474 | name = "termcolor" 1475 | version = "1.3.0" 1476 | source = "registry+https://github.com/rust-lang/crates.io-index" 1477 | checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" 1478 | dependencies = [ 1479 | "winapi-util", 1480 | ] 1481 | 1482 | [[package]] 1483 | name = "thiserror" 1484 | version = "1.0.49" 1485 | source = "registry+https://github.com/rust-lang/crates.io-index" 1486 | checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" 1487 | dependencies = [ 1488 | "thiserror-impl", 1489 | ] 1490 | 1491 | [[package]] 1492 | name = "thiserror-impl" 1493 | version = "1.0.49" 1494 | source = "registry+https://github.com/rust-lang/crates.io-index" 1495 | checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" 1496 | dependencies = [ 1497 | "proc-macro2", 1498 | "quote", 1499 | "syn 2.0.38", 1500 | ] 1501 | 1502 | [[package]] 1503 | name = "tiff" 1504 | version = "0.9.0" 1505 | source = "registry+https://github.com/rust-lang/crates.io-index" 1506 | checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" 1507 | dependencies = [ 1508 | "flate2", 1509 | "jpeg-decoder", 1510 | "weezl", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "tiny-skia" 1515 | version = "0.8.4" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" 1518 | dependencies = [ 1519 | "arrayref", 1520 | "arrayvec", 1521 | "bytemuck", 1522 | "cfg-if", 1523 | "png", 1524 | "tiny-skia-path", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "tiny-skia-path" 1529 | version = "0.8.4" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" 1532 | dependencies = [ 1533 | "arrayref", 1534 | "bytemuck", 1535 | "strict-num", 1536 | ] 1537 | 1538 | [[package]] 1539 | name = "toml_datetime" 1540 | version = "0.6.3" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" 1543 | 1544 | [[package]] 1545 | name = "toml_edit" 1546 | version = "0.19.15" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" 1549 | dependencies = [ 1550 | "indexmap 2.0.2", 1551 | "toml_datetime", 1552 | "winnow", 1553 | ] 1554 | 1555 | [[package]] 1556 | name = "ttf-parser" 1557 | version = "0.19.2" 1558 | source = "registry+https://github.com/rust-lang/crates.io-index" 1559 | checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" 1560 | 1561 | [[package]] 1562 | name = "unicode-ident" 1563 | version = "1.0.12" 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" 1565 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1566 | 1567 | [[package]] 1568 | name = "unicode-width" 1569 | version = "0.1.11" 1570 | source = "registry+https://github.com/rust-lang/crates.io-index" 1571 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 1572 | 1573 | [[package]] 1574 | name = "unicode-xid" 1575 | version = "0.2.4" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 1578 | 1579 | [[package]] 1580 | name = "vec_map" 1581 | version = "0.8.2" 1582 | source = "registry+https://github.com/rust-lang/crates.io-index" 1583 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1584 | 1585 | [[package]] 1586 | name = "vello" 1587 | version = "0.0.1" 1588 | source = "git+https://github.com/linebender/vello?rev=ee3a076#ee3a076b291d206c361431cc841407adf265c692" 1589 | dependencies = [ 1590 | "bytemuck", 1591 | "fello", 1592 | "futures-intrusive", 1593 | "peniko", 1594 | "raw-window-handle", 1595 | "vello_encoding", 1596 | "wgpu", 1597 | ] 1598 | 1599 | [[package]] 1600 | name = "vello_encoding" 1601 | version = "0.1.0" 1602 | source = "git+https://github.com/linebender/vello?rev=ee3a076#ee3a076b291d206c361431cc841407adf265c692" 1603 | dependencies = [ 1604 | "bytemuck", 1605 | "fello", 1606 | "guillotiere", 1607 | "peniko", 1608 | ] 1609 | 1610 | [[package]] 1611 | name = "version_check" 1612 | version = "0.9.4" 1613 | source = "registry+https://github.com/rust-lang/crates.io-index" 1614 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1615 | 1616 | [[package]] 1617 | name = "viewer" 1618 | version = "0.1.0" 1619 | dependencies = [ 1620 | "pollster", 1621 | "rive-rs", 1622 | "vello", 1623 | "wgpu", 1624 | "winit", 1625 | ] 1626 | 1627 | [[package]] 1628 | name = "walkdir" 1629 | version = "2.4.0" 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" 1631 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 1632 | dependencies = [ 1633 | "same-file", 1634 | "winapi-util", 1635 | ] 1636 | 1637 | [[package]] 1638 | name = "wasi" 1639 | version = "0.11.0+wasi-snapshot-preview1" 1640 | source = "registry+https://github.com/rust-lang/crates.io-index" 1641 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1642 | 1643 | [[package]] 1644 | name = "wasm-bindgen" 1645 | version = "0.2.87" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 1648 | dependencies = [ 1649 | "cfg-if", 1650 | "wasm-bindgen-macro", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "wasm-bindgen-backend" 1655 | version = "0.2.87" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 1658 | dependencies = [ 1659 | "bumpalo", 1660 | "log", 1661 | "once_cell", 1662 | "proc-macro2", 1663 | "quote", 1664 | "syn 2.0.38", 1665 | "wasm-bindgen-shared", 1666 | ] 1667 | 1668 | [[package]] 1669 | name = "wasm-bindgen-futures" 1670 | version = "0.4.37" 1671 | source = "registry+https://github.com/rust-lang/crates.io-index" 1672 | checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" 1673 | dependencies = [ 1674 | "cfg-if", 1675 | "js-sys", 1676 | "wasm-bindgen", 1677 | "web-sys", 1678 | ] 1679 | 1680 | [[package]] 1681 | name = "wasm-bindgen-macro" 1682 | version = "0.2.87" 1683 | source = "registry+https://github.com/rust-lang/crates.io-index" 1684 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 1685 | dependencies = [ 1686 | "quote", 1687 | "wasm-bindgen-macro-support", 1688 | ] 1689 | 1690 | [[package]] 1691 | name = "wasm-bindgen-macro-support" 1692 | version = "0.2.87" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 1695 | dependencies = [ 1696 | "proc-macro2", 1697 | "quote", 1698 | "syn 2.0.38", 1699 | "wasm-bindgen-backend", 1700 | "wasm-bindgen-shared", 1701 | ] 1702 | 1703 | [[package]] 1704 | name = "wasm-bindgen-shared" 1705 | version = "0.2.87" 1706 | source = "registry+https://github.com/rust-lang/crates.io-index" 1707 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 1708 | 1709 | [[package]] 1710 | name = "wayland-client" 1711 | version = "0.29.5" 1712 | source = "registry+https://github.com/rust-lang/crates.io-index" 1713 | checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" 1714 | dependencies = [ 1715 | "bitflags 1.3.2", 1716 | "downcast-rs", 1717 | "libc", 1718 | "nix 0.24.3", 1719 | "scoped-tls", 1720 | "wayland-commons", 1721 | "wayland-scanner", 1722 | "wayland-sys", 1723 | ] 1724 | 1725 | [[package]] 1726 | name = "wayland-commons" 1727 | version = "0.29.5" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" 1730 | dependencies = [ 1731 | "nix 0.24.3", 1732 | "once_cell", 1733 | "smallvec", 1734 | "wayland-sys", 1735 | ] 1736 | 1737 | [[package]] 1738 | name = "wayland-cursor" 1739 | version = "0.29.5" 1740 | source = "registry+https://github.com/rust-lang/crates.io-index" 1741 | checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" 1742 | dependencies = [ 1743 | "nix 0.24.3", 1744 | "wayland-client", 1745 | "xcursor", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "wayland-protocols" 1750 | version = "0.29.5" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" 1753 | dependencies = [ 1754 | "bitflags 1.3.2", 1755 | "wayland-client", 1756 | "wayland-commons", 1757 | "wayland-scanner", 1758 | ] 1759 | 1760 | [[package]] 1761 | name = "wayland-scanner" 1762 | version = "0.29.5" 1763 | source = "registry+https://github.com/rust-lang/crates.io-index" 1764 | checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" 1765 | dependencies = [ 1766 | "proc-macro2", 1767 | "quote", 1768 | "xml-rs", 1769 | ] 1770 | 1771 | [[package]] 1772 | name = "wayland-sys" 1773 | version = "0.29.5" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" 1776 | dependencies = [ 1777 | "dlib", 1778 | "lazy_static", 1779 | "pkg-config", 1780 | ] 1781 | 1782 | [[package]] 1783 | name = "web-sys" 1784 | version = "0.3.64" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" 1787 | dependencies = [ 1788 | "js-sys", 1789 | "wasm-bindgen", 1790 | ] 1791 | 1792 | [[package]] 1793 | name = "weezl" 1794 | version = "0.1.7" 1795 | source = "registry+https://github.com/rust-lang/crates.io-index" 1796 | checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" 1797 | 1798 | [[package]] 1799 | name = "wgpu" 1800 | version = "0.17.1" 1801 | source = "registry+https://github.com/rust-lang/crates.io-index" 1802 | checksum = "ed547920565c56c7a29afb4538ac5ae5048865a5d2f05bff3ad4fbeb921a9a2c" 1803 | dependencies = [ 1804 | "arrayvec", 1805 | "cfg-if", 1806 | "js-sys", 1807 | "log", 1808 | "naga", 1809 | "parking_lot", 1810 | "profiling", 1811 | "raw-window-handle", 1812 | "smallvec", 1813 | "static_assertions", 1814 | "wasm-bindgen", 1815 | "wasm-bindgen-futures", 1816 | "web-sys", 1817 | "wgpu-core", 1818 | "wgpu-hal", 1819 | "wgpu-types", 1820 | ] 1821 | 1822 | [[package]] 1823 | name = "wgpu-core" 1824 | version = "0.17.1" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "0f8a44dd301a30ceeed3c27d8c0090433d3da04d7b2a4042738095a424d12ae7" 1827 | dependencies = [ 1828 | "arrayvec", 1829 | "bit-vec", 1830 | "bitflags 2.4.1", 1831 | "codespan-reporting", 1832 | "log", 1833 | "naga", 1834 | "parking_lot", 1835 | "profiling", 1836 | "raw-window-handle", 1837 | "rustc-hash", 1838 | "smallvec", 1839 | "thiserror", 1840 | "web-sys", 1841 | "wgpu-hal", 1842 | "wgpu-types", 1843 | ] 1844 | 1845 | [[package]] 1846 | name = "wgpu-hal" 1847 | version = "0.17.2" 1848 | source = "registry+https://github.com/rust-lang/crates.io-index" 1849 | checksum = "9a80bf0e3c77399bb52850cb0830af9bad073d5cfcb9dd8253bef8125c42db17" 1850 | dependencies = [ 1851 | "android_system_properties", 1852 | "arrayvec", 1853 | "ash", 1854 | "bit-set", 1855 | "bitflags 2.4.1", 1856 | "block", 1857 | "core-graphics-types", 1858 | "d3d12", 1859 | "glow", 1860 | "gpu-alloc", 1861 | "gpu-allocator", 1862 | "gpu-descriptor", 1863 | "hassle-rs", 1864 | "js-sys", 1865 | "khronos-egl", 1866 | "libc", 1867 | "libloading 0.8.1", 1868 | "log", 1869 | "metal", 1870 | "naga", 1871 | "objc", 1872 | "parking_lot", 1873 | "profiling", 1874 | "range-alloc", 1875 | "raw-window-handle", 1876 | "renderdoc-sys", 1877 | "rustc-hash", 1878 | "smallvec", 1879 | "thiserror", 1880 | "wasm-bindgen", 1881 | "web-sys", 1882 | "wgpu-types", 1883 | "winapi", 1884 | ] 1885 | 1886 | [[package]] 1887 | name = "wgpu-types" 1888 | version = "0.17.0" 1889 | source = "registry+https://github.com/rust-lang/crates.io-index" 1890 | checksum = "ee64d7398d0c2f9ca48922c902ef69c42d000c759f3db41e355f4a570b052b67" 1891 | dependencies = [ 1892 | "bitflags 2.4.1", 1893 | "js-sys", 1894 | "web-sys", 1895 | ] 1896 | 1897 | [[package]] 1898 | name = "widestring" 1899 | version = "1.0.2" 1900 | source = "registry+https://github.com/rust-lang/crates.io-index" 1901 | checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" 1902 | 1903 | [[package]] 1904 | name = "winapi" 1905 | version = "0.3.9" 1906 | source = "registry+https://github.com/rust-lang/crates.io-index" 1907 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1908 | dependencies = [ 1909 | "winapi-i686-pc-windows-gnu", 1910 | "winapi-x86_64-pc-windows-gnu", 1911 | ] 1912 | 1913 | [[package]] 1914 | name = "winapi-i686-pc-windows-gnu" 1915 | version = "0.4.0" 1916 | source = "registry+https://github.com/rust-lang/crates.io-index" 1917 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1918 | 1919 | [[package]] 1920 | name = "winapi-util" 1921 | version = "0.1.6" 1922 | source = "registry+https://github.com/rust-lang/crates.io-index" 1923 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 1924 | dependencies = [ 1925 | "winapi", 1926 | ] 1927 | 1928 | [[package]] 1929 | name = "winapi-x86_64-pc-windows-gnu" 1930 | version = "0.4.0" 1931 | source = "registry+https://github.com/rust-lang/crates.io-index" 1932 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1933 | 1934 | [[package]] 1935 | name = "windows" 1936 | version = "0.44.0" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" 1939 | dependencies = [ 1940 | "windows-targets 0.42.2", 1941 | ] 1942 | 1943 | [[package]] 1944 | name = "windows-sys" 1945 | version = "0.45.0" 1946 | source = "registry+https://github.com/rust-lang/crates.io-index" 1947 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1948 | dependencies = [ 1949 | "windows-targets 0.42.2", 1950 | ] 1951 | 1952 | [[package]] 1953 | name = "windows-sys" 1954 | version = "0.48.0" 1955 | source = "registry+https://github.com/rust-lang/crates.io-index" 1956 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1957 | dependencies = [ 1958 | "windows-targets 0.48.5", 1959 | ] 1960 | 1961 | [[package]] 1962 | name = "windows-targets" 1963 | version = "0.42.2" 1964 | source = "registry+https://github.com/rust-lang/crates.io-index" 1965 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 1966 | dependencies = [ 1967 | "windows_aarch64_gnullvm 0.42.2", 1968 | "windows_aarch64_msvc 0.42.2", 1969 | "windows_i686_gnu 0.42.2", 1970 | "windows_i686_msvc 0.42.2", 1971 | "windows_x86_64_gnu 0.42.2", 1972 | "windows_x86_64_gnullvm 0.42.2", 1973 | "windows_x86_64_msvc 0.42.2", 1974 | ] 1975 | 1976 | [[package]] 1977 | name = "windows-targets" 1978 | version = "0.48.5" 1979 | source = "registry+https://github.com/rust-lang/crates.io-index" 1980 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1981 | dependencies = [ 1982 | "windows_aarch64_gnullvm 0.48.5", 1983 | "windows_aarch64_msvc 0.48.5", 1984 | "windows_i686_gnu 0.48.5", 1985 | "windows_i686_msvc 0.48.5", 1986 | "windows_x86_64_gnu 0.48.5", 1987 | "windows_x86_64_gnullvm 0.48.5", 1988 | "windows_x86_64_msvc 0.48.5", 1989 | ] 1990 | 1991 | [[package]] 1992 | name = "windows_aarch64_gnullvm" 1993 | version = "0.42.2" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 1996 | 1997 | [[package]] 1998 | name = "windows_aarch64_gnullvm" 1999 | version = "0.48.5" 2000 | source = "registry+https://github.com/rust-lang/crates.io-index" 2001 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2002 | 2003 | [[package]] 2004 | name = "windows_aarch64_msvc" 2005 | version = "0.42.2" 2006 | source = "registry+https://github.com/rust-lang/crates.io-index" 2007 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 2008 | 2009 | [[package]] 2010 | name = "windows_aarch64_msvc" 2011 | version = "0.48.5" 2012 | source = "registry+https://github.com/rust-lang/crates.io-index" 2013 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2014 | 2015 | [[package]] 2016 | name = "windows_i686_gnu" 2017 | version = "0.42.2" 2018 | source = "registry+https://github.com/rust-lang/crates.io-index" 2019 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 2020 | 2021 | [[package]] 2022 | name = "windows_i686_gnu" 2023 | version = "0.48.5" 2024 | source = "registry+https://github.com/rust-lang/crates.io-index" 2025 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2026 | 2027 | [[package]] 2028 | name = "windows_i686_msvc" 2029 | version = "0.42.2" 2030 | source = "registry+https://github.com/rust-lang/crates.io-index" 2031 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 2032 | 2033 | [[package]] 2034 | name = "windows_i686_msvc" 2035 | version = "0.48.5" 2036 | source = "registry+https://github.com/rust-lang/crates.io-index" 2037 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2038 | 2039 | [[package]] 2040 | name = "windows_x86_64_gnu" 2041 | version = "0.42.2" 2042 | source = "registry+https://github.com/rust-lang/crates.io-index" 2043 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 2044 | 2045 | [[package]] 2046 | name = "windows_x86_64_gnu" 2047 | version = "0.48.5" 2048 | source = "registry+https://github.com/rust-lang/crates.io-index" 2049 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2050 | 2051 | [[package]] 2052 | name = "windows_x86_64_gnullvm" 2053 | version = "0.42.2" 2054 | source = "registry+https://github.com/rust-lang/crates.io-index" 2055 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 2056 | 2057 | [[package]] 2058 | name = "windows_x86_64_gnullvm" 2059 | version = "0.48.5" 2060 | source = "registry+https://github.com/rust-lang/crates.io-index" 2061 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2062 | 2063 | [[package]] 2064 | name = "windows_x86_64_msvc" 2065 | version = "0.42.2" 2066 | source = "registry+https://github.com/rust-lang/crates.io-index" 2067 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 2068 | 2069 | [[package]] 2070 | name = "windows_x86_64_msvc" 2071 | version = "0.48.5" 2072 | source = "registry+https://github.com/rust-lang/crates.io-index" 2073 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2074 | 2075 | [[package]] 2076 | name = "winit" 2077 | version = "0.28.7" 2078 | source = "registry+https://github.com/rust-lang/crates.io-index" 2079 | checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" 2080 | dependencies = [ 2081 | "android-activity", 2082 | "bitflags 1.3.2", 2083 | "cfg_aliases", 2084 | "core-foundation", 2085 | "core-graphics", 2086 | "dispatch", 2087 | "instant", 2088 | "libc", 2089 | "log", 2090 | "mio", 2091 | "ndk", 2092 | "objc2", 2093 | "once_cell", 2094 | "orbclient", 2095 | "percent-encoding", 2096 | "raw-window-handle", 2097 | "redox_syscall", 2098 | "sctk-adwaita", 2099 | "smithay-client-toolkit", 2100 | "wasm-bindgen", 2101 | "wayland-client", 2102 | "wayland-commons", 2103 | "wayland-protocols", 2104 | "wayland-scanner", 2105 | "web-sys", 2106 | "windows-sys 0.45.0", 2107 | "x11-dl", 2108 | ] 2109 | 2110 | [[package]] 2111 | name = "winnow" 2112 | version = "0.5.17" 2113 | source = "registry+https://github.com/rust-lang/crates.io-index" 2114 | checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" 2115 | dependencies = [ 2116 | "memchr", 2117 | ] 2118 | 2119 | [[package]] 2120 | name = "x11-dl" 2121 | version = "2.21.0" 2122 | source = "registry+https://github.com/rust-lang/crates.io-index" 2123 | checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" 2124 | dependencies = [ 2125 | "libc", 2126 | "once_cell", 2127 | "pkg-config", 2128 | ] 2129 | 2130 | [[package]] 2131 | name = "xcursor" 2132 | version = "0.3.4" 2133 | source = "registry+https://github.com/rust-lang/crates.io-index" 2134 | checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" 2135 | dependencies = [ 2136 | "nom", 2137 | ] 2138 | 2139 | [[package]] 2140 | name = "xml-rs" 2141 | version = "0.8.19" 2142 | source = "registry+https://github.com/rust-lang/crates.io-index" 2143 | checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" 2144 | 2145 | [[package]] 2146 | name = "zune-inflate" 2147 | version = "0.2.54" 2148 | source = "registry+https://github.com/rust-lang/crates.io-index" 2149 | checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" 2150 | dependencies = [ 2151 | "simd-adler32", 2152 | ] 2153 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["rive-rs", "examples/*"] 3 | resolver = "2" 4 | 5 | [workspace.dependencies] 6 | vello = { git = "https://github.com/linebender/vello", rev = "ee3a076" } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Rive 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI](https://github.com/rive-app/rive-rs/actions/workflows/ci.yml/badge.svg) 2 | ![Discord badge](https://img.shields.io/discord/532365473602600965) 3 | ![Twitter handle](https://img.shields.io/twitter/follow/rive_app.svg?style=social&label=Follow) 4 | 5 | # rive-rs 6 | 7 | ![Rive hero image](https://cdn.rive.app/rive_logo_dark_bg.png) 8 | 9 | A Rust runtime library for [Rive](https://rive.app). 10 | 11 | > [!NOTE] 12 | > This runtime uses [Vello](https://github.com/linebender/vello) as a render back-end, which has certain limitations. Refer to [Known Issues](#known-issues) for details. Efforts are underway to incorporate the [Rive Renderer](https://rive.app/renderer) as another back-end. 13 | 14 | ## Table of contents 15 | 16 | - ⭐️ [Rive Overview](#rive-overview) 17 | - 🚀 [Getting Started](#getting-started) 18 | - 👨‍💻 [Contributing](#contributing) 19 | - ❓ [Issues](#issues) 20 | 21 | ## Rive overview 22 | 23 | [Rive](https://rive.app) is a real-time interactive design and animation tool that helps teams 24 | create and run interactive animations anywhere. Designers and developers use our collaborative 25 | editor to create motion graphics that respond to different states and user inputs. Our lightweight 26 | open-source runtime libraries allow them to load their animations into apps, games, and websites. 27 | 28 | 🏡 [Homepage](https://rive.app/) 29 | 30 | 📘 [Rive Documentation](https://rive.app/community/doc) 31 | 32 | 🛠 [Rive Forums](https://rive.app/community/forums/home) 33 | 34 | ## Getting started 35 | 36 | You will need a Rust toolchain and a C compiler to build. You can can install 37 | the Rust toolchain using [rustup]. 38 | 39 | ### Get submodules 40 | 41 | To be able to compile the repo, you need to also clone/update the submodules: 42 | 43 | ```bash 44 | $ git submodule update --init 45 | ``` 46 | ...or, when cloning: 47 | 48 | ```bash 49 | $ git clone --recurse-submodules git://github.com/rive-app/rive-rs.git 50 | ``` 51 | 52 | ### Running the viewer 53 | 54 | To open the included viewer, run: 55 | 56 | ```bash 57 | $ cargo run --release 58 | ``` 59 | 60 | Then, drop any `.riv` file into the window to open it. Scroll to control the size of 61 | the grid of copies. 62 | 63 | [rustup]: https://rustup.rs 64 | 65 | ### Awesome Rive 66 | 67 | For even more examples and resources on using Rive at runtime or in other tools, checkout the [awesome-rive](https://github.com/rive-app/awesome-rive) repo. 68 | 69 | See the [rive-bevy repository](https://github.com/rive-app/rive-bevy) that makes use of this runtime. 70 | 71 | ## Contributing 72 | 73 | We love contributions! Check out our [contributing docs](./CONTRIBUTING.md) to get more details into 74 | how to run this project, the examples, and more all locally. 75 | 76 | ## Issues 77 | 78 | Have an issue with using the runtime, or want to suggest a feature/API to help make your development 79 | life better? Log an issue in our [issues](https://github.com/rive-app/rive-rs/issues) tab! You 80 | can also browse older issues and discussion threads there to see solutions that may have worked for 81 | common problems. 82 | 83 | ### Known issues 84 | 85 | The existing [Vello](https://github.com/linebender/vello) render back-end may lead to some inconsistencies in comparison to the original design: 86 | 87 | - Image meshes: They can exhibit small inconsistencies at triangle borders, there can be gaps between triangles, transparent meshes will overdraw at triangle borders. 88 | - Very high number of clips: Vello is currently rendering very high numbers of clips incorrectly. 89 | - All strokes will have round joins and caps. 90 | 91 | Efforts are being made to make the [Rive Renderer](https://rive.app/renderer) available. You'll then have the choice to select your preferred renderer. 92 | 93 | -------------------------------------------------------------------------------- /assets/rating-animation.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-rs/e0c8f8cc5245ac07442179f0d05fc10154f53292/assets/rating-animation.riv -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | [git] 2 | commit_parsers = [ 3 | { message = "^feat", group = "added" }, 4 | { message = "^changed", group = "changed" }, 5 | { message = "^deprecated", group = "deprecated" }, 6 | { message = "^removed", group = "removed" }, 7 | { message = "^fix", group = "fixed" }, 8 | { message = "^security", group = "security" }, 9 | ] 10 | -------------------------------------------------------------------------------- /examples/viewer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "viewer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | pollster = "0.3.0" 9 | rive-rs = { path = "../../rive-rs", features = ["vello"] } 10 | vello = { workspace = true } 11 | wgpu = "0.17.0" 12 | winit = "0.28.6" 13 | -------------------------------------------------------------------------------- /examples/viewer/README.md: -------------------------------------------------------------------------------- 1 | # Vello back-end for Rive 2 | 3 | Small application for viewing `.riv` files rendered with Vello. It uses [winit] 4 | for creating the window and [image] to decode images. 5 | 6 | ## Usage 7 | 8 | Drop any `.riv` file into the window to open it. Scroll to control the size of 9 | the grid of copies. 10 | 11 | ## Caveats 12 | 13 | The current implementation is a work-in-progress and might exhibit artifacts or 14 | render incorrectly. 15 | 16 | Only tested on macOS for the time being. 17 | 18 | [winit]: https://github.com/rust-windowing/winit 19 | [image]: https://github.com/image-rs/image 20 | -------------------------------------------------------------------------------- /examples/viewer/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, time::Instant}; 2 | 3 | use rive_rs::{Artboard, File, Handle, Instantiate, Viewport}; 4 | use vello::{ 5 | kurbo::{Affine, Rect, Vec2}, 6 | peniko::{Color, Fill}, 7 | util::{RenderContext, RenderSurface}, 8 | Renderer, RendererOptions, Scene, SceneBuilder, 9 | }; 10 | use winit::{ 11 | dpi::LogicalSize, 12 | event::{ElementState, Event, MouseButton, WindowEvent}, 13 | event_loop::{ControlFlow, EventLoop}, 14 | window::{Window, WindowBuilder}, 15 | }; 16 | 17 | struct RenderState { 18 | surface: RenderSurface, 19 | window: Window, 20 | } 21 | 22 | const INITIAL_WINDOW_SIZE: LogicalSize = LogicalSize::new(700, 700); 23 | const FRAME_STATS_CAPACITY: usize = 30; 24 | const SCROLL_FACTOR_THRESHOLD: f64 = 100.0; 25 | 26 | fn main() { 27 | let mut viewport = Viewport::default(); 28 | let mut scene: Option> = None; 29 | 30 | let event_loop = EventLoop::new(); 31 | let mut cached_window: Option = None; 32 | let mut renderer: Option = None; 33 | let mut render_cx = RenderContext::new().unwrap(); 34 | let mut render_state: Option = None; 35 | 36 | let mut mouse_pos = Vec2::default(); 37 | let mut scroll_delta = 0.0; 38 | let mut frame_start_time = Instant::now(); 39 | let mut stats = Vec::with_capacity(FRAME_STATS_CAPACITY); 40 | 41 | event_loop.run(move |event, _event_loop, control_flow| match event { 42 | Event::WindowEvent { ref event, .. } => { 43 | let Some(render_state) = &mut render_state else { 44 | return; 45 | }; 46 | 47 | match event { 48 | WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, 49 | WindowEvent::Resized(size) => { 50 | viewport.resize(size.width, size.height); 51 | 52 | render_cx.resize_surface(&mut render_state.surface, size.width, size.height); 53 | render_state.window.request_redraw(); 54 | } 55 | WindowEvent::MouseInput { 56 | state, 57 | button: MouseButton::Left, 58 | .. 59 | } => { 60 | if let Some(scene) = &mut scene { 61 | match state { 62 | ElementState::Pressed => scene.pointer_down( 63 | mouse_pos.x as f32, 64 | mouse_pos.y as f32, 65 | &viewport, 66 | ), 67 | ElementState::Released => { 68 | scene.pointer_up(mouse_pos.x as f32, mouse_pos.y as f32, &viewport) 69 | } 70 | } 71 | } 72 | } 73 | WindowEvent::CursorMoved { position, .. } => { 74 | mouse_pos = Vec2::new(position.x, position.y); 75 | if let Some(scene) = &mut scene { 76 | scene.pointer_move(mouse_pos.x as f32, mouse_pos.y as f32, &viewport); 77 | } 78 | } 79 | WindowEvent::MouseWheel { delta, .. } => match delta { 80 | winit::event::MouseScrollDelta::LineDelta(_, lines_y) => { 81 | scroll_delta = (scroll_delta 82 | - (*lines_y as f64).signum() * SCROLL_FACTOR_THRESHOLD) 83 | .max(0.0); 84 | } 85 | winit::event::MouseScrollDelta::PixelDelta(pixels) => { 86 | scroll_delta = (scroll_delta - pixels.y).max(0.0); 87 | } 88 | }, 89 | WindowEvent::DroppedFile(path) => { 90 | scene = Some({ 91 | let file = File::new(&fs::read(path).unwrap()).unwrap(); 92 | let artboard = Artboard::instantiate(&file, Handle::Default).unwrap(); 93 | 94 | Box::::instantiate(&artboard, Handle::Default).unwrap() 95 | }); 96 | } 97 | _ => {} 98 | } 99 | } 100 | Event::MainEventsCleared => { 101 | if let Some(render_state) = &mut render_state { 102 | render_state.window.request_redraw(); 103 | } 104 | } 105 | Event::RedrawRequested(_) => { 106 | let mut rive_renderer = rive_rs::Renderer::default(); 107 | let factor = (scroll_delta / SCROLL_FACTOR_THRESHOLD).max(1.0) as u32; 108 | 109 | let elapsed = &frame_start_time.elapsed(); 110 | stats.push(elapsed.as_secs_f64()); 111 | 112 | if stats.len() == FRAME_STATS_CAPACITY { 113 | let average = stats.drain(..).sum::() / FRAME_STATS_CAPACITY as f64; 114 | 115 | if let Some(state) = &mut render_state { 116 | let copies = (factor > 1) 117 | .then(|| format!(" ({} copies)", factor.pow(2))) 118 | .unwrap_or_default(); 119 | state.window.set_title(&format!( 120 | "Rive on Vello demo | {:.2}ms{}", 121 | average * 1000.0, 122 | copies 123 | )); 124 | } 125 | } 126 | 127 | frame_start_time = Instant::now(); 128 | 129 | let Some(render_state) = &mut render_state else { 130 | return; 131 | }; 132 | let width = render_state.surface.config.width; 133 | let height = render_state.surface.config.height; 134 | let device_handle = &render_cx.devices[render_state.surface.dev_id]; 135 | 136 | let render_params = vello::RenderParams { 137 | base_color: Color::DIM_GRAY, 138 | width, 139 | height, 140 | }; 141 | 142 | let surface_texture = render_state 143 | .surface 144 | .surface 145 | .get_current_texture() 146 | .expect("failed to get surface texture"); 147 | 148 | let mut vello_scene = Scene::default(); 149 | let mut builder = SceneBuilder::for_scene(&mut vello_scene); 150 | 151 | if let Some(scene) = &mut scene { 152 | scene.advance_and_maybe_draw(&mut rive_renderer, *elapsed, &mut viewport); 153 | 154 | for i in 0..factor.pow(2) { 155 | builder.append( 156 | rive_renderer.scene(), 157 | Some( 158 | Affine::default() 159 | .then_scale(1.0 / factor as f64) 160 | .then_translate(Vec2::new( 161 | (i % factor) as f64 * width as f64 / factor as f64, 162 | (i / factor) as f64 * height as f64 / factor as f64, 163 | )), 164 | ), 165 | ); 166 | } 167 | } else { 168 | // Vello doesn't draw base color when there is no geometry. 169 | builder.fill( 170 | Fill::NonZero, 171 | Affine::IDENTITY, 172 | Color::TRANSPARENT, 173 | None, 174 | &Rect::new(0.0, 0.0, 0.0, 0.0), 175 | ); 176 | } 177 | 178 | if !vello_scene.data().is_empty() { 179 | vello::block_on_wgpu( 180 | &device_handle.device, 181 | renderer.as_mut().unwrap().render_to_surface_async( 182 | &device_handle.device, 183 | &device_handle.queue, 184 | &vello_scene, 185 | &surface_texture, 186 | &render_params, 187 | ), 188 | ) 189 | .expect("failed to render to surface"); 190 | } 191 | 192 | surface_texture.present(); 193 | device_handle.device.poll(wgpu::Maintain::Poll); 194 | } 195 | Event::Suspended => { 196 | if let Some(render_state) = render_state.take() { 197 | cached_window = Some(render_state.window); 198 | } 199 | *control_flow = ControlFlow::Wait; 200 | } 201 | Event::Resumed => { 202 | if render_state.is_some() { 203 | return; 204 | } 205 | 206 | let window = cached_window.take().unwrap_or_else(|| { 207 | WindowBuilder::new() 208 | .with_inner_size(INITIAL_WINDOW_SIZE) 209 | .with_resizable(true) 210 | .with_title("Rive on Vello demo") 211 | .build(_event_loop) 212 | .unwrap() 213 | }); 214 | let size = window.inner_size(); 215 | let surface_future = render_cx.create_surface(&window, size.width, size.height); 216 | 217 | let surface = pollster::block_on(surface_future).expect("Error creating surface"); 218 | render_state = { 219 | let render_state = RenderState { window, surface }; 220 | renderer = Some( 221 | Renderer::new( 222 | &render_cx.devices[render_state.surface.dev_id].device, 223 | &RendererOptions { 224 | surface_format: Some(render_state.surface.format), 225 | timestamp_period: render_cx.devices[render_state.surface.dev_id] 226 | .queue 227 | .get_timestamp_period(), 228 | use_cpu: false, 229 | }, 230 | ) 231 | .expect("Could create renderer"), 232 | ); 233 | Some(render_state) 234 | }; 235 | *control_flow = ControlFlow::Poll; 236 | } 237 | _ => {} 238 | }); 239 | } 240 | -------------------------------------------------------------------------------- /release-plz.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | changelog_config = "cliff.toml" 3 | changelog_update = false 4 | 5 | [[package]] 6 | name = "rive-rs" 7 | changelog_update = true 8 | changelog_path = "./CHANGELOG.md" 9 | -------------------------------------------------------------------------------- /rive-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rive-rs" 3 | description = "Rive runtime bindings" 4 | version = "0.1.0" 5 | edition = "2021" 6 | authors = ["Dragoș Tiselice "] 7 | homepage = "https://rive.app/" 8 | repository = "https://github.com/rive-app/rive-rs" 9 | documentation = "https://docs.rs/rive-rs" 10 | keywords = ["rive", "runtime", "animation", "state-machine"] 11 | categories = ["multimedia"] 12 | license = "MIT" 13 | readme = "../README.md" 14 | 15 | [features] 16 | default = ["text", "layout"] 17 | text = [] 18 | layout = [] 19 | vello = ["dep:bytemuck", "dep:image", "dep:smallvec", "dep:vello"] 20 | 21 | [build-dependencies] 22 | cc = { version = "1.0.83", features = ["parallel"] } 23 | walkdir = "2.4.0" 24 | 25 | [dependencies] 26 | bitflags = "2.4.0" 27 | bytemuck = { version = "1.14.0", optional = true } 28 | image = { version = "0.24.6", optional = true } 29 | smallvec = { version = "1.8.0", optional = true } 30 | vello = { workspace = true, optional = true } 31 | -------------------------------------------------------------------------------- /rive-rs/build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | ffi::OsString, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use walkdir::WalkDir; 8 | 9 | fn all_files_with_extension>( 10 | path: P, 11 | extension: &str, 12 | ) -> impl Iterator + '_ { 13 | WalkDir::new(path).into_iter().filter_map(move |entry| { 14 | entry 15 | .ok() 16 | .map(|entry| entry.into_path()) 17 | .filter(|path| path.extension() == Some(&OsString::from(extension))) 18 | }) 19 | } 20 | 21 | fn main() { 22 | println!("cargo:rerun-if-changed=src/ffi.cpp"); 23 | 24 | let rive_cpp_path = env::var("RIVE_CPP_PATH") 25 | .map(PathBuf::from) 26 | .unwrap_or_else(|_| PathBuf::from("../submodules/rive-cpp")); 27 | 28 | cc::Build::new() 29 | .cpp(true) 30 | .include(rive_cpp_path.join("include")) 31 | .file("src/ffi.cpp") 32 | .flag("-std=c++14") 33 | .warnings(false) 34 | .compile("rive-ffi"); 35 | 36 | if cfg!(feature = "layout") { 37 | cc::Build::new() 38 | .cpp(true) 39 | .flag("-std=c++11") 40 | .files(all_files_with_extension("../submodules/yoga/yoga", "cpp")) 41 | .include("../submodules/yoga") 42 | .define("YOGA_EXPORT=", None) 43 | .warnings(false) 44 | .compile("yoga"); 45 | } 46 | 47 | if cfg!(feature = "text") { 48 | let target = env::var("TARGET").unwrap(); 49 | let profile = env::var("PROFILE").unwrap(); 50 | 51 | let mut cfg = cc::Build::new(); 52 | cfg.cpp(true) 53 | .flag_if_supported("-std=c++11") // for unix 54 | .warnings(false) 55 | .file("../submodules/harfbuzz/src/harfbuzz.cc"); 56 | 57 | if !target.contains("windows") { 58 | cfg.define("HAVE_PTHREAD", "1"); 59 | } 60 | 61 | if target.contains("apple") && profile.contains("release") { 62 | cfg.define("HAVE_CORETEXT", "1"); 63 | } 64 | 65 | if target.contains("windows") { 66 | cfg.define("HAVE_DIRECTWRITE", "1"); 67 | } 68 | 69 | if target.contains("windows-gnu") { 70 | cfg.flag("-Wa,-mbig-obj"); 71 | } 72 | 73 | cfg.compile("harfbuzz"); 74 | 75 | cc::Build::new() 76 | .files(all_files_with_extension( 77 | "../submodules/SheenBidi/Source", 78 | "c", 79 | )) 80 | .include("../submodules/SheenBidi/Headers") 81 | .warnings(false) 82 | .compile("sheenbidi"); 83 | } 84 | 85 | let mut cfg = cc::Build::new(); 86 | cfg.cpp(true) 87 | .include(rive_cpp_path.join("include")) 88 | .files(all_files_with_extension(rive_cpp_path.join("src"), "cpp")) 89 | .flag("-std=c++14") 90 | .define("_RIVE_INTERNAL_", None) 91 | .warnings(false); 92 | 93 | if cfg!(feature = "text") { 94 | cfg.include("../submodules/harfbuzz/src") 95 | .include("../submodules/SheenBidi/Headers") 96 | .flag_if_supported("-Wno-deprecated-declarations") 97 | .define("WITH_RIVE_TEXT", None); 98 | } 99 | if cfg!(feature = "layout") { 100 | cfg.include("../submodules/yoga") 101 | .flag_if_supported("-Wno-deprecated-declarations") 102 | .define("WITH_RIVE_LAYOUT", None) 103 | .define("YOGA_EXPORT=", None); 104 | } 105 | 106 | cfg.compile("rive"); 107 | } 108 | -------------------------------------------------------------------------------- /rive-rs/src/artboard/components/mod.rs: -------------------------------------------------------------------------------- 1 | use core::{marker::PhantomData, ptr, slice, str}; 2 | 3 | use crate::{ 4 | ffi, 5 | raw_iter::{impl_iter, Raw}, 6 | }; 7 | 8 | mod text_value_run; 9 | 10 | pub use text_value_run::TextValueRun; 11 | 12 | pub struct Component<'a> { 13 | raw_component: *mut ffi::Component, 14 | _phantom: PhantomData<&'a ()>, 15 | } 16 | 17 | impl Component<'_> { 18 | pub fn name(&self) -> &str { 19 | let mut data = ptr::null(); 20 | let mut len = 0; 21 | 22 | let bytes = unsafe { 23 | ffi::rive_rs_component_name( 24 | self.raw_component, 25 | &mut data as *mut *const u8, 26 | &mut len as *mut usize, 27 | ); 28 | slice::from_raw_parts(data, len) 29 | }; 30 | 31 | str::from_utf8(bytes).expect("component name is invalid UTF-8") 32 | } 33 | } 34 | 35 | #[derive(Clone, Copy, Debug)] 36 | pub(crate) struct RawArtboard(pub *mut ffi::Artboard); 37 | 38 | impl Raw for RawArtboard { 39 | type Item<'a> = Component<'a>; 40 | 41 | fn len(self) -> usize { 42 | unsafe { ffi::rive_rs_artboard_component_count(self.0) } 43 | } 44 | 45 | unsafe fn get<'a>(self, index: usize) -> Self::Item<'a> { 46 | Component { 47 | raw_component: ffi::rive_rs_artboard_get_component(self.0, index), 48 | _phantom: PhantomData, 49 | } 50 | } 51 | } 52 | 53 | impl_iter!(Components, Component, RawArtboard, 'a); 54 | 55 | macro_rules! try_from_component { 56 | ( $component:ident, $raw_name:ident, $type_id:expr ) => { 57 | impl<'a> TryFrom> for $component<'a> { 58 | type Error = (); 59 | 60 | fn try_from( 61 | value: crate::artboard::components::Component<'a>, 62 | ) -> Result { 63 | unsafe { 64 | (crate::ffi::rive_rs_component_type_id(value.raw_component) == $type_id) 65 | .then(|| Self { 66 | $raw_name: value.raw_component as *mut crate::ffi::$component, 67 | _phantom: ::core::marker::PhantomData, 68 | }) 69 | .ok_or(()) 70 | } 71 | } 72 | } 73 | }; 74 | } 75 | 76 | pub(crate) use try_from_component; 77 | -------------------------------------------------------------------------------- /rive-rs/src/artboard/components/text_value_run.rs: -------------------------------------------------------------------------------- 1 | use core::{marker::PhantomData, ptr, slice, str}; 2 | 3 | use crate::ffi; 4 | 5 | use super::try_from_component; 6 | 7 | pub struct TextValueRun<'a> { 8 | raw_text_value_run: *mut ffi::TextValueRun, 9 | _phantom: PhantomData<&'a ()>, 10 | } 11 | 12 | impl TextValueRun<'_> { 13 | pub fn get_text(&self) -> &str { 14 | let mut data = ptr::null(); 15 | let mut len = 0; 16 | 17 | let bytes = unsafe { 18 | ffi::rive_rs_text_value_run_get_text( 19 | self.raw_text_value_run, 20 | &mut data as *mut *const u8, 21 | &mut len as *mut usize, 22 | ); 23 | slice::from_raw_parts(data, len) 24 | }; 25 | 26 | str::from_utf8(bytes).expect("text value run text is invalid UTF-8") 27 | } 28 | 29 | pub fn set_text(&mut self, text: &str) { 30 | unsafe { 31 | ffi::rive_rs_text_value_run_set_text( 32 | self.raw_text_value_run, 33 | text.as_ptr(), 34 | text.len(), 35 | ); 36 | } 37 | } 38 | } 39 | 40 | try_from_component!(TextValueRun, raw_text_value_run, 135); 41 | -------------------------------------------------------------------------------- /rive-rs/src/artboard/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use core::{fmt, marker::PhantomData, ptr::NonNull}; 3 | 4 | use crate::{ 5 | ffi, 6 | file::{File, FileInner}, 7 | instantiate::{Handle, Instantiate}, 8 | renderer::Renderer, 9 | }; 10 | 11 | use self::components::Components; 12 | 13 | pub mod components; 14 | 15 | #[derive(Debug)] 16 | pub(crate) struct ArtboardInner { 17 | _file: Arc, 18 | pub(crate) raw_artboard: *mut ffi::Artboard, 19 | } 20 | 21 | impl Drop for ArtboardInner { 22 | fn drop(&mut self) { 23 | unsafe { 24 | ffi::rive_rs_artboard_instance_release(self.raw_artboard); 25 | } 26 | } 27 | } 28 | 29 | unsafe impl Send for ArtboardInner {} 30 | unsafe impl Sync for ArtboardInner {} 31 | 32 | pub struct Artboard { 33 | inner: Arc, 34 | _phantom: PhantomData, 35 | } 36 | 37 | impl Artboard { 38 | pub(crate) fn from_inner(inner: Arc) -> Self { 39 | Self { 40 | inner, 41 | _phantom: PhantomData, 42 | } 43 | } 44 | 45 | pub(crate) fn as_inner(&self) -> &Arc { 46 | &self.inner 47 | } 48 | 49 | #[inline] 50 | pub fn components(&mut self) -> Components { 51 | Components::new(components::RawArtboard(self.inner.raw_artboard)) 52 | } 53 | } 54 | 55 | impl Instantiate for Artboard { 56 | type From = File; 57 | 58 | #[inline] 59 | fn instantiate(file: &Self::From, handle: Handle) -> Option { 60 | let mut raw_artboard: Option> = None; 61 | 62 | match handle { 63 | Handle::Default => unsafe { 64 | ffi::rive_rs_instantiate_artboard(file.as_inner().raw_file, None, &mut raw_artboard) 65 | }, 66 | Handle::Index(ref index) => unsafe { 67 | ffi::rive_rs_instantiate_artboard( 68 | file.as_inner().raw_file, 69 | Some(index.into()), 70 | &mut raw_artboard, 71 | ) 72 | }, 73 | Handle::Name(name) => unsafe { 74 | ffi::rive_rs_instantiate_artboard_by_name( 75 | file.as_inner().raw_file, 76 | name.as_ptr(), 77 | name.len(), 78 | &mut raw_artboard, 79 | ) 80 | }, 81 | } 82 | 83 | raw_artboard.map(|raw_artboard| Artboard { 84 | inner: Arc::new(ArtboardInner { 85 | _file: file.as_inner().clone(), 86 | raw_artboard: raw_artboard.as_ptr(), 87 | }), 88 | _phantom: PhantomData, 89 | }) 90 | } 91 | } 92 | 93 | impl fmt::Debug for Artboard { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | f.debug_struct("Ardboard").finish() 96 | } 97 | } 98 | 99 | unsafe impl Send for Artboard {} 100 | unsafe impl Sync for Artboard {} 101 | -------------------------------------------------------------------------------- /rive-rs/src/ffi.cpp: -------------------------------------------------------------------------------- 1 | #include "rive/animation/linear_animation_instance.hpp" 2 | #include "rive/animation/state_machine_bool.hpp" 3 | #include "rive/animation/state_machine_input.hpp" 4 | #include "rive/animation/state_machine_input_instance.hpp" 5 | #include "rive/animation/state_machine_instance.hpp" 6 | #include "rive/animation/state_machine_number.hpp" 7 | #include "rive/animation/state_machine_trigger.hpp" 8 | #include "rive/custom_property_boolean.hpp" 9 | #include "rive/custom_property_number.hpp" 10 | #include "rive/custom_property_string.hpp" 11 | #include "rive/event.hpp" 12 | #include "rive/factory.hpp" 13 | #include "rive/file.hpp" 14 | #include "rive/math/path_types.hpp" 15 | #include "rive/math/raw_path.hpp" 16 | #include "rive/math/vec2d.hpp" 17 | #include "rive/renderer.hpp" 18 | #include "rive/text/text_value_run.hpp" 19 | 20 | extern "C" 21 | { 22 | using namespace rive; 23 | 24 | typedef struct RawRustBuffer RawRustBuffer; 25 | typedef struct RawRustPath RawRustPath; 26 | typedef struct RawRustPaint RawRustPaint; 27 | typedef struct RawRustGradient RawRustGradient; 28 | typedef struct RawRustImage RawRustImage; 29 | typedef struct RawRustFactory RawRustFactory; 30 | typedef struct RawRustRenderer RawRustRenderer; 31 | typedef struct RawRustString RawRustString; 32 | typedef struct RawRustBTreeMap RawRustBTreeMap; 33 | 34 | void rive_rs_allocate_string(const RawRustString* string, 35 | const char* data, 36 | size_t len); 37 | 38 | typedef struct RawString 39 | { 40 | const char* data; 41 | size_t len; 42 | } RawString; 43 | enum class PropertyTag : uint8_t 44 | { 45 | Bool, 46 | Number, 47 | String, 48 | }; 49 | typedef union Property 50 | { 51 | bool boolean; 52 | float number; 53 | RawString string; 54 | } Property; 55 | 56 | void rive_rs_insert_property(const RawRustBTreeMap* properties, 57 | const char* key_data, 58 | size_t key_len, 59 | PropertyTag value_tag, 60 | Property value_payload); 61 | 62 | typedef struct RendererEntries 63 | { 64 | const RawRustBuffer* (*buffer_new)(RenderBufferType, 65 | RenderBufferFlags, 66 | size_t); 67 | void (*buffer_release)(const RawRustBuffer*); 68 | uint8_t* (*buffer_map)(const RawRustBuffer*); 69 | void (*buffer_unmap)(const RawRustBuffer*); 70 | const RawRustPath* (*path_default)(); 71 | const RawRustPath* (*path_new)(RawPath::Iter*, size_t, FillRule); 72 | void (*path_release)(const RawRustPath*); 73 | void (*path_reset)(const RawRustPath*); 74 | void (*path_extend)(const RawRustPath*, 75 | const RawRustPath*, 76 | const float* transform); 77 | void (*path_set_fill_rule)(const RawRustPath*, FillRule); 78 | void (*path_move_to)(const RawRustPath*, float, float); 79 | void (*path_line_to)(const RawRustPath*, float, float); 80 | void (*path_cubic_to)(const RawRustPath*, 81 | float, 82 | float, 83 | float, 84 | float, 85 | float, 86 | float); 87 | void (*path_close)(const RawRustPath*); 88 | const RawRustPaint* (*paint_default)(); 89 | void (*paint_release)(const RawRustPaint*); 90 | void (*paint_set_style)(const RawRustPaint*, RenderPaintStyle); 91 | void (*paint_set_color)(const RawRustPaint*, ColorInt); 92 | void (*paint_set_thickness)(const RawRustPaint*, float); 93 | void (*paint_set_join)(const RawRustPaint*, StrokeJoin); 94 | void (*paint_set_cap)(const RawRustPaint*, StrokeCap); 95 | void (*paint_set_blend_mode)(const RawRustPaint*, BlendMode); 96 | void (*paint_set_gradient)(const RawRustPaint*, const RawRustGradient*); 97 | void (*paint_invalidate_stroke)(const RawRustPaint*); 98 | const RawRustGradient* (*gradient_new_linear)(float, 99 | float, 100 | float, 101 | float, 102 | const ColorInt*, 103 | const float*, 104 | size_t); 105 | const RawRustGradient* (*gradient_new_radial)(float, 106 | float, 107 | float, 108 | const ColorInt*, 109 | const float*, 110 | size_t); 111 | void (*gradient_release)(const RawRustGradient*); 112 | const RawRustImage* (*image_decode)(const uint8_t*, size_t); 113 | void (*image_release)(const RawRustImage*); 114 | void (*renderer_state_push)(const RawRustRenderer*); 115 | void (*renderer_state_pop)(const RawRustRenderer*); 116 | void (*renderer_transform)(const RawRustRenderer*, const float*); 117 | void (*renderer_set_clip)(const RawRustRenderer*, const RawRustPath*); 118 | void (*renderer_draw_path)(const RawRustRenderer*, 119 | const RawRustPath*, 120 | const RawRustPaint*); 121 | void (*renderer_draw_image)(const RawRustRenderer*, 122 | const RawRustImage*, 123 | BlendMode, 124 | float); 125 | void (*renderer_draw_image_mesh)(const RawRustRenderer*, 126 | const RawRustImage*, 127 | const RawRustBuffer*, 128 | const RawRustBuffer*, 129 | const RawRustBuffer*, 130 | BlendMode, 131 | float); 132 | } RendererEntries; 133 | 134 | class RustBuffer : public LITE_RTTI_OVERRIDE(RenderBuffer, RustBuffer) 135 | { 136 | private: 137 | const RawRustBuffer* m_buffer; 138 | const RendererEntries* m_entries; 139 | 140 | public: 141 | RustBuffer(RenderBufferType type, 142 | RenderBufferFlags flags, 143 | size_t sizeInBytes, 144 | const RendererEntries* entries) : 145 | lite_rtti_override(type, flags, sizeInBytes), 146 | m_buffer(entries->buffer_new(type, flags, sizeInBytes)), 147 | m_entries(entries) 148 | {} 149 | ~RustBuffer() override { m_entries->buffer_release(m_buffer); } 150 | 151 | const RawRustBuffer* buffer() const { return m_buffer; } 152 | 153 | void* onMap() override 154 | { 155 | return static_cast(m_entries->buffer_map(m_buffer)); 156 | } 157 | void onUnmap() override { m_entries->buffer_unmap(m_buffer); } 158 | }; 159 | 160 | class RustShader : public LITE_RTTI_OVERRIDE(RenderShader, RustShader) 161 | { 162 | private: 163 | const RawRustGradient* m_gradient; 164 | const RendererEntries* m_entries; 165 | 166 | public: 167 | RustShader(const RawRustGradient* gradient, 168 | const RendererEntries* entries) : 169 | m_gradient(gradient), m_entries(entries) 170 | {} 171 | ~RustShader() override { m_entries->gradient_release(m_gradient); } 172 | 173 | const RawRustGradient* gradient() const { return m_gradient; } 174 | }; 175 | 176 | class RustImage : public LITE_RTTI_OVERRIDE(RenderImage, RustImage) 177 | { 178 | private: 179 | const RawRustImage* m_image; 180 | const RendererEntries* m_entries; 181 | 182 | public: 183 | RustImage(const RawRustImage* image, const RendererEntries* entries) : 184 | m_image(image), m_entries(entries) 185 | {} 186 | ~RustImage() override { m_entries->image_release(m_image); } 187 | 188 | const RawRustImage* image() const { return m_image; } 189 | }; 190 | 191 | class RustPath : public LITE_RTTI_OVERRIDE(RenderPath, RustPath) 192 | { 193 | private: 194 | const RawRustPath* m_path; 195 | const RendererEntries* m_entries; 196 | 197 | public: 198 | RustPath(const RawRustPath* path, const RendererEntries* entries) : 199 | m_path(path), m_entries(entries) 200 | {} 201 | ~RustPath() override { m_entries->path_release(m_path); } 202 | 203 | const RawRustPath* path() const { return m_path; } 204 | 205 | void rewind() override { m_entries->path_reset(m_path); } 206 | 207 | void addRawPath(const RawPath& path) override 208 | { 209 | auto iter = path.begin(); 210 | auto renderPath = 211 | make_rcp(m_entries->path_new(&iter, 212 | path.verbs().size(), 213 | FillRule::nonZero), 214 | m_entries); 215 | Mat2D transform; 216 | addRenderPath(renderPath.get(), transform); 217 | } 218 | 219 | void addRenderPath(RenderPath* path, const Mat2D& transform) override 220 | { 221 | LITE_RTTI_CAST_OR_RETURN(rustPath, RustPath*, path); 222 | m_entries->path_extend(m_path, 223 | rustPath->m_path, 224 | transform.values()); 225 | } 226 | void fillRule(FillRule value) override 227 | { 228 | m_entries->path_set_fill_rule(m_path, value); 229 | } 230 | void moveTo(float x, float y) override 231 | { 232 | m_entries->path_move_to(m_path, x, y); 233 | } 234 | void lineTo(float x, float y) override 235 | { 236 | m_entries->path_line_to(m_path, x, y); 237 | } 238 | void cubicTo(float ox, float oy, float ix, float iy, float x, float y) 239 | override 240 | { 241 | m_entries->path_cubic_to(m_path, ox, oy, ix, iy, x, y); 242 | } 243 | virtual void close() override { m_entries->path_close(m_path); } 244 | }; 245 | 246 | class RustPaint : public LITE_RTTI_OVERRIDE(RenderPaint, RustPaint) 247 | { 248 | private: 249 | const RawRustPaint* m_paint; 250 | const RendererEntries* m_entries; 251 | 252 | public: 253 | RustPaint(const RawRustPaint* paint, const RendererEntries* entries) : 254 | m_paint(paint), m_entries(entries) 255 | {} 256 | ~RustPaint() override { m_entries->paint_release(m_paint); } 257 | 258 | const RawRustPaint* paint() const { return m_paint; } 259 | 260 | void style(RenderPaintStyle style) override 261 | { 262 | m_entries->paint_set_style(m_paint, style); 263 | } 264 | void color(unsigned int value) override 265 | { 266 | m_entries->paint_set_color(m_paint, value); 267 | } 268 | void thickness(float value) override 269 | { 270 | m_entries->paint_set_thickness(m_paint, value); 271 | } 272 | void join(StrokeJoin value) override 273 | { 274 | m_entries->paint_set_join(m_paint, value); 275 | } 276 | void cap(StrokeCap value) override 277 | { 278 | m_entries->paint_set_cap(m_paint, value); 279 | } 280 | void blendMode(BlendMode value) override 281 | { 282 | m_entries->paint_set_blend_mode(m_paint, value); 283 | } 284 | void shader(rcp shader) override 285 | { 286 | auto rustShader = lite_rtti_cast(shader.get()); 287 | 288 | if (rustShader) 289 | { 290 | m_entries->paint_set_gradient(m_paint, rustShader->gradient()); 291 | } 292 | } 293 | void invalidateStroke() override 294 | { 295 | m_entries->paint_invalidate_stroke(m_paint); 296 | } 297 | }; 298 | 299 | class RustFactory : public Factory 300 | { 301 | private: 302 | const RendererEntries* m_entries; 303 | 304 | public: 305 | RustFactory(const RendererEntries* entries) : m_entries(entries) {} 306 | 307 | rcp makeRenderBuffer(RenderBufferType type, 308 | RenderBufferFlags flags, 309 | size_t len_in_bytes) override 310 | { 311 | return rcp( 312 | new RustBuffer(type, flags, len_in_bytes, m_entries)); 313 | } 314 | 315 | rcp makeLinearGradient(float sx, 316 | float sy, 317 | float ex, 318 | float ey, 319 | const ColorInt colors[], 320 | const float stops[], 321 | size_t count) override 322 | { 323 | const RawRustGradient* gradient = 324 | m_entries 325 | ->gradient_new_linear(sx, sy, ex, ey, colors, stops, count); 326 | return rcp( 327 | new RustShader(std::move(gradient), m_entries)); 328 | } 329 | 330 | rcp makeRadialGradient(float cx, 331 | float cy, 332 | float radius, 333 | const ColorInt colors[], 334 | const float stops[], 335 | size_t count) override 336 | { 337 | const RawRustGradient* gradient = 338 | m_entries 339 | ->gradient_new_radial(cx, cy, radius, colors, stops, count); 340 | return rcp( 341 | new RustShader(std::move(gradient), m_entries)); 342 | } 343 | 344 | rcp makeRenderPath(RawPath& path, 345 | FillRule fill_rule) override 346 | { 347 | auto iter = path.begin(); 348 | return make_rcp( 349 | m_entries->path_new(&iter, path.verbs().size(), fill_rule), 350 | m_entries); 351 | } 352 | 353 | rcp makeEmptyRenderPath() override 354 | { 355 | return make_rcp(m_entries->path_default(), m_entries); 356 | } 357 | 358 | rcp makeRenderPaint() override 359 | { 360 | return make_rcp(m_entries->paint_default(), m_entries); 361 | } 362 | 363 | rcp decodeImage(Span encoded) override 364 | { 365 | return make_rcp( 366 | m_entries->image_decode(encoded.data(), encoded.size()), 367 | m_entries); 368 | } 369 | }; 370 | 371 | class RustRenderer : public Renderer 372 | { 373 | private: 374 | const RawRustRenderer* m_renderer; 375 | const RendererEntries* m_entries; 376 | 377 | public: 378 | RustRenderer(const RawRustRenderer* renderer, 379 | const RendererEntries* entries) : 380 | m_renderer(renderer), m_entries(entries) 381 | {} 382 | ~RustRenderer() override {} 383 | 384 | void save() override { m_entries->renderer_state_push(m_renderer); } 385 | void restore() override { m_entries->renderer_state_pop(m_renderer); } 386 | void transform(const Mat2D& transform) override 387 | { 388 | m_entries->renderer_transform(m_renderer, transform.values()); 389 | } 390 | void clipPath(RenderPath* path) override 391 | { 392 | LITE_RTTI_CAST_OR_RETURN(rustPath, RustPath*, path); 393 | m_entries->renderer_set_clip(m_renderer, rustPath->path()); 394 | } 395 | void drawPath(RenderPath* path, RenderPaint* paint) override 396 | { 397 | LITE_RTTI_CAST_OR_RETURN(rustPath, RustPath*, path); 398 | LITE_RTTI_CAST_OR_RETURN(rustPaint, RustPaint*, paint); 399 | m_entries->renderer_draw_path(m_renderer, 400 | rustPath->path(), 401 | rustPaint->paint()); 402 | } 403 | void drawImage(const RenderImage* image, 404 | const rive::ImageSampler options, 405 | BlendMode blend_mode, 406 | float opacity) override 407 | { 408 | LITE_RTTI_CAST_OR_RETURN(rustImage, const RustImage*, image); 409 | m_entries->renderer_draw_image(m_renderer, 410 | rustImage->image(), 411 | blend_mode, 412 | opacity); 413 | } 414 | void drawImageMesh(const RenderImage* image, 415 | const rive::ImageSampler options, 416 | rcp vertices_f32, 417 | rcp uvCoords_f32, 418 | rcp indices_u16, 419 | uint32_t, 420 | uint32_t, 421 | BlendMode blend_mode, 422 | float opacity) override 423 | { 424 | LITE_RTTI_CAST_OR_RETURN(rustImage, const RustImage*, image); 425 | LITE_RTTI_CAST_OR_RETURN(rustVertices, 426 | RustBuffer*, 427 | vertices_f32.get()); 428 | LITE_RTTI_CAST_OR_RETURN(rustUVCoords, 429 | RustBuffer*, 430 | uvCoords_f32.get()); 431 | LITE_RTTI_CAST_OR_RETURN(rustIndices, 432 | RustBuffer*, 433 | indices_u16.get()); 434 | m_entries->renderer_draw_image_mesh(m_renderer, 435 | rustImage->image(), 436 | rustVertices->buffer(), 437 | rustUVCoords->buffer(), 438 | rustIndices->buffer(), 439 | blend_mode, 440 | opacity); 441 | } 442 | }; 443 | 444 | typedef struct Command 445 | { 446 | PathVerb verb; 447 | const Vec2D* points; 448 | } Command; 449 | 450 | enum class InputTag : uint8_t 451 | { 452 | Bool, 453 | Number, 454 | Trigger, 455 | }; 456 | 457 | const File* rive_rs_file_new(const uint8_t* data, 458 | size_t len, 459 | const RendererEntries* entries, 460 | ImportResult* result, 461 | RustFactory** factory) 462 | { 463 | RustFactory* rust_factory = new RustFactory(entries); 464 | auto file = rive::File::import({data, len}, rust_factory, result); 465 | 466 | *factory = rust_factory; 467 | 468 | return static_cast(file.release()); 469 | } 470 | 471 | void rive_rs_file_release(const File* file, RustFactory* factory) 472 | { 473 | std::unique_ptr val(std::move(const_cast(file))); 474 | delete factory; 475 | } 476 | 477 | void rive_rs_instantiate_artboard(const File* file, 478 | const size_t* index, 479 | ArtboardInstance** artboard_instance) 480 | { 481 | if (index) 482 | { 483 | if (*index < file->artboardCount()) 484 | { 485 | *artboard_instance = file->artboardAt(*index).release(); 486 | } 487 | } 488 | else 489 | { 490 | auto ptr = file->artboardDefault(); 491 | if (ptr) 492 | { 493 | *artboard_instance = ptr.release(); 494 | } 495 | } 496 | 497 | if (*artboard_instance) 498 | { 499 | (*artboard_instance)->advance(0.0f); 500 | } 501 | } 502 | 503 | void rive_rs_instantiate_artboard_by_name( 504 | const File* file, 505 | const char* data, 506 | size_t len, 507 | ArtboardInstance** artboard_instance) 508 | { 509 | *artboard_instance = file->artboardNamed({data, len}).release(); 510 | 511 | if (*artboard_instance) 512 | { 513 | (*artboard_instance)->advance(0.0f); 514 | } 515 | } 516 | 517 | void rive_rs_artboard_instance_release( 518 | const ArtboardInstance* artboard_instance) 519 | { 520 | std::unique_ptr val( 521 | std::move(const_cast(artboard_instance))); 522 | } 523 | 524 | size_t rive_rs_artboard_component_count( 525 | const ArtboardInstance* artboard_instance) 526 | { 527 | return artboard_instance->objects().size(); 528 | } 529 | 530 | const Core* rive_rs_artboard_get_component( 531 | const ArtboardInstance* artboard_instance, 532 | size_t index) 533 | { 534 | return artboard_instance->objects()[index]; 535 | } 536 | 537 | uint16_t rive_rs_component_type_id(const Core* component) 538 | { 539 | return component->coreType(); 540 | } 541 | 542 | void rive_rs_component_name(const Component* component, 543 | const char** data, 544 | size_t* len) 545 | { 546 | if (static_cast(component)->is()) 547 | { 548 | *data = component->name().data(); 549 | *len = component->name().size(); 550 | } 551 | else 552 | { 553 | *len = 0; 554 | } 555 | } 556 | 557 | void rive_rs_text_value_run_get_text(const TextValueRun* text_value_run, 558 | const char** data, 559 | size_t* len) 560 | { 561 | *data = text_value_run->text().data(); 562 | *len = text_value_run->text().size(); 563 | } 564 | 565 | void rive_rs_text_value_run_set_text(TextValueRun* text_value_run, 566 | const char* data, 567 | size_t len) 568 | { 569 | text_value_run->text({data, len}); 570 | } 571 | 572 | void rive_rs_instantiate_linear_animation( 573 | ArtboardInstance* artboard_instance, 574 | const size_t* index, 575 | LinearAnimationInstance** linear_animation) 576 | { 577 | if (index) 578 | { 579 | if (*index < (size_t)artboard_instance->animationCount()) 580 | { 581 | *linear_animation = 582 | artboard_instance->animationAt(*index).release(); 583 | } 584 | } 585 | else 586 | { 587 | auto ptr = artboard_instance->animationAt(0); 588 | if (ptr) 589 | { 590 | *linear_animation = ptr.release(); 591 | } 592 | } 593 | } 594 | 595 | void rive_rs_instantiate_linear_animation_by_name( 596 | ArtboardInstance* artboard_instance, 597 | const char* data, 598 | size_t len, 599 | LinearAnimationInstance** linear_animation) 600 | { 601 | *linear_animation = 602 | artboard_instance->animationNamed({data, len}).release(); 603 | } 604 | 605 | float rive_rs_linear_animation_time( 606 | const LinearAnimationInstance* linear_animation) 607 | { 608 | return linear_animation->time(); 609 | } 610 | 611 | void rive_rs_linear_animation_set_time( 612 | LinearAnimationInstance* linear_animation, 613 | float time) 614 | { 615 | linear_animation->time(time); 616 | } 617 | 618 | bool rive_rs_linear_animation_is_forwards( 619 | const LinearAnimationInstance* linear_animation) 620 | { 621 | return linear_animation->direction() == 1; 622 | } 623 | 624 | void rive_rs_linear_animation_set_is_forwards( 625 | LinearAnimationInstance* linear_animation, 626 | bool is_forwards) 627 | { 628 | linear_animation->direction(is_forwards ? 1 : -1); 629 | } 630 | 631 | bool rive_rs_linear_animation_advance( 632 | LinearAnimationInstance* linear_animation, 633 | float elapsed) 634 | { 635 | return linear_animation->advance(elapsed); 636 | } 637 | 638 | void rive_rs_linear_animation_apply( 639 | const LinearAnimationInstance* linear_animation, 640 | float mix) 641 | { 642 | linear_animation->apply(mix); 643 | } 644 | 645 | bool rive_rs_linear_animation_did_loop( 646 | const LinearAnimationInstance* linear_animation) 647 | { 648 | return linear_animation->didLoop(); 649 | } 650 | 651 | void rive_rs_linear_animation_set_loop( 652 | LinearAnimationInstance* linear_animation, 653 | Loop loop) 654 | { 655 | linear_animation->loopValue(static_cast(loop)); 656 | } 657 | 658 | bool rive_rs_linear_animation_is_done( 659 | const LinearAnimationInstance* linear_animation) 660 | { 661 | return !linear_animation->keepGoing(); 662 | } 663 | 664 | void rive_rs_instantiate_state_machine(ArtboardInstance* artboard_instance, 665 | const size_t* index, 666 | StateMachineInstance** state_machine) 667 | { 668 | 669 | if (index) 670 | { 671 | if (*index < (size_t)artboard_instance->stateMachineCount()) 672 | { 673 | *state_machine = 674 | artboard_instance->stateMachineAt(*index).release(); 675 | } 676 | } 677 | else 678 | { 679 | auto ptr = artboard_instance->defaultStateMachine(); 680 | if (ptr) 681 | { 682 | *state_machine = ptr.release(); 683 | } 684 | else if (artboard_instance->stateMachineCount()) 685 | { 686 | *state_machine = artboard_instance->stateMachineAt(0).release(); 687 | } 688 | } 689 | } 690 | 691 | void rive_rs_instantiate_state_machine_by_name( 692 | ArtboardInstance* artboard_instance, 693 | const char* data, 694 | size_t len, 695 | StateMachineInstance** state_machine) 696 | { 697 | *state_machine = 698 | artboard_instance->stateMachineNamed({data, len}).release(); 699 | } 700 | 701 | void rive_rs_state_machine_get_event( 702 | const StateMachineInstance* state_machine_instance, 703 | size_t index, 704 | Event** event, 705 | float* delay) 706 | { 707 | auto event_report = state_machine_instance->reportedEventAt(index); 708 | *event = event_report.event(); 709 | *delay = event_report.secondsDelay(); 710 | } 711 | 712 | size_t rive_rs_state_machine_event_count( 713 | const StateMachineInstance* state_machine_instance) 714 | { 715 | return state_machine_instance->reportedEventCount(); 716 | } 717 | 718 | void rive_rs_event_name(const Event* event, const RawRustString* string) 719 | { 720 | rive_rs_allocate_string(string, 721 | event->name().data(), 722 | event->name().size()); 723 | } 724 | 725 | void rive_rs_event_properties(const Event* event, 726 | const RawRustBTreeMap* properties) 727 | { 728 | for (auto child : event->children()) 729 | { 730 | switch (child->coreType()) 731 | { 732 | case CustomPropertyBoolean::typeKey: 733 | { 734 | auto boolean = static_cast(child); 735 | 736 | Property property; 737 | property.boolean = boolean->propertyValue(); 738 | 739 | rive_rs_insert_property(properties, 740 | child->name().data(), 741 | child->name().size(), 742 | PropertyTag::Bool, 743 | property); 744 | 745 | break; 746 | } 747 | case CustomPropertyNumber::typeKey: 748 | { 749 | auto number = static_cast(child); 750 | 751 | Property property; 752 | property.number = number->propertyValue(); 753 | 754 | rive_rs_insert_property(properties, 755 | child->name().data(), 756 | child->name().size(), 757 | PropertyTag::Number, 758 | property); 759 | 760 | break; 761 | } 762 | case CustomPropertyString::typeKey: 763 | { 764 | auto string = static_cast(child); 765 | 766 | Property property; 767 | property.string = {string->propertyValue().data(), 768 | string->propertyValue().size()}; 769 | 770 | rive_rs_insert_property(properties, 771 | child->name().data(), 772 | child->name().size(), 773 | PropertyTag::String, 774 | property); 775 | 776 | break; 777 | } 778 | } 779 | } 780 | } 781 | 782 | void rive_rs_state_machine_get_input( 783 | const StateMachineInstance* state_machine_instance, 784 | size_t index, 785 | InputTag* input_tag, 786 | SMIInput** input) 787 | { 788 | *input = state_machine_instance->input(index); 789 | 790 | if ((*input)->input()->is()) 791 | { 792 | *input_tag = InputTag::Bool; 793 | } 794 | 795 | if ((*input)->input()->is()) 796 | { 797 | *input_tag = InputTag::Number; 798 | } 799 | 800 | if ((*input)->input()->is()) 801 | { 802 | *input_tag = InputTag::Trigger; 803 | } 804 | } 805 | 806 | size_t rive_rs_state_machine_input_count( 807 | const StateMachineInstance* state_machine_instance) 808 | { 809 | return state_machine_instance->inputCount(); 810 | } 811 | 812 | const SMIBool* rive_rs_state_machine_get_bool( 813 | const StateMachineInstance* state_machine_instance, 814 | const char* name, 815 | size_t len) 816 | { 817 | std::string n(name, len); 818 | 819 | return state_machine_instance->getBool({name, len}); 820 | } 821 | 822 | const SMINumber* rive_rs_state_machine_get_number( 823 | const StateMachineInstance* state_machine_instance, 824 | const char* name, 825 | size_t len) 826 | { 827 | std::string n(name, len); 828 | 829 | return state_machine_instance->getNumber({name, len}); 830 | } 831 | 832 | const SMITrigger* rive_rs_state_machine_get_trigger( 833 | const StateMachineInstance* state_machine_instance, 834 | const char* name, 835 | size_t len) 836 | { 837 | std::string n(name, len); 838 | 839 | return state_machine_instance->getTrigger({name, len}); 840 | } 841 | 842 | void rive_rs_input_name(const SMIInput* input, 843 | const char** data, 844 | size_t* len) 845 | { 846 | *data = input->name().data(); 847 | *len = input->name().size(); 848 | } 849 | 850 | bool rive_rs_bool_get(const SMIBool* bool_) { return bool_->value(); } 851 | 852 | void rive_rs_bool_set(SMIBool* bool_, bool val) { bool_->value(val); } 853 | 854 | float rive_rs_number_get(const SMINumber* number) 855 | { 856 | return number->value(); 857 | } 858 | 859 | void rive_rs_number_set(SMINumber* number, float val) 860 | { 861 | number->value(val); 862 | } 863 | 864 | void rive_rs_trigger_fire(SMITrigger* trigger) { trigger->fire(); } 865 | 866 | void rive_rs_scene_release(const Scene* scene) 867 | { 868 | std::unique_ptr val(std::move(const_cast(scene))); 869 | } 870 | 871 | Command rive_rs_commands_next(RawPath::Iter* commands) 872 | { 873 | auto tuple = **commands; 874 | ++*commands; 875 | return {std::get<0>(tuple), std::get<1>(tuple)}; 876 | } 877 | 878 | float rive_rs_scene_width(const Scene* scene) { return scene->width(); } 879 | 880 | float rive_rs_scene_height(const Scene* scene) { return scene->height(); } 881 | 882 | Loop rive_rs_scene_loop(const Scene* scene) { return scene->loop(); } 883 | 884 | bool rive_rs_scene_is_translucent(const Scene* scene) 885 | { 886 | return scene->isTranslucent(); 887 | } 888 | 889 | float rive_rs_scene_duration(const Scene* scene) 890 | { 891 | return scene->durationSeconds(); 892 | } 893 | 894 | bool rive_rs_scene_advance_and_apply(Scene* scene, float elapsed) 895 | { 896 | return scene->advanceAndApply(elapsed); 897 | } 898 | 899 | void rive_rs_scene_draw(Scene* scene, 900 | const RawRustRenderer* renderer, 901 | const RendererEntries* entries) 902 | { 903 | RustRenderer rust_renderer(renderer, entries); 904 | scene->draw(&rust_renderer); 905 | } 906 | 907 | void rive_rs_scene_pointer_down(Scene* scene, float x, float y) 908 | { 909 | scene->pointerDown({x, y}); 910 | } 911 | 912 | void rive_rs_scene_pointer_move(Scene* scene, float x, float y) 913 | { 914 | scene->pointerMove({x, y}); 915 | } 916 | 917 | void rive_rs_scene_pointer_up(Scene* scene, float x, float y) 918 | { 919 | scene->pointerUp({x, y}); 920 | } 921 | 922 | void rive_rs_artboard_instance_transforms( 923 | const ArtboardInstance* artboard_instance, 924 | uint32_t width, 925 | uint32_t height, 926 | float* view_transform, 927 | float* inverse_view_transform) 928 | { 929 | auto view_transform_mat = 930 | rive::computeAlignment(rive::Fit::contain, 931 | rive::Alignment::center, 932 | rive::AABB(0, 0, width, height), 933 | artboard_instance->bounds()); 934 | auto inverse_view_transform_mat = view_transform_mat.invertOrIdentity(); 935 | 936 | std::copy(view_transform_mat.values(), 937 | view_transform_mat.values() + 6, 938 | view_transform); 939 | std::copy(inverse_view_transform_mat.values(), 940 | inverse_view_transform_mat.values() + 6, 941 | inverse_view_transform); 942 | } 943 | } 944 | -------------------------------------------------------------------------------- /rive-rs/src/ffi.rs: -------------------------------------------------------------------------------- 1 | use alloc::{boxed::Box, collections::BTreeMap, string::String}; 2 | use core::{ 3 | ptr::{self, NonNull}, 4 | slice, 5 | }; 6 | 7 | use crate::{ 8 | linear_animation::Loop, 9 | path::{self, FillRule, Point, Verb}, 10 | renderer::{ 11 | BlendMode, Buffer, BufferFlags, BufferType, Color, Gradient, Image, Paint, PaintStyle, 12 | Path, Renderer, StrokeCap, StrokeJoin, 13 | }, 14 | state_machine, 15 | }; 16 | 17 | #[repr(C)] 18 | #[derive(Clone, Copy, Debug)] 19 | pub struct RawString { 20 | data: *const u8, 21 | len: usize, 22 | } 23 | 24 | #[repr(C)] 25 | #[derive(Clone, Copy, Debug)] 26 | pub struct Command { 27 | pub verb: Verb, 28 | pub points: *const Point, 29 | } 30 | 31 | #[derive(Clone, Copy)] 32 | pub enum Commands {} 33 | 34 | #[repr(C)] 35 | #[derive(Clone, Copy, Debug)] 36 | pub enum FileResult { 37 | Success, 38 | #[allow(dead_code)] 39 | UnsupportedVersion, 40 | #[allow(dead_code)] 41 | Malformed, 42 | } 43 | 44 | #[derive(Clone, Copy)] 45 | pub enum Factory {} 46 | 47 | #[derive(Clone, Copy)] 48 | pub enum File {} 49 | 50 | #[derive(Clone, Copy)] 51 | pub enum Artboard {} 52 | 53 | #[derive(Clone, Copy)] 54 | pub enum Component {} 55 | 56 | #[derive(Clone, Copy)] 57 | pub enum TextValueRun {} 58 | 59 | #[derive(Clone, Copy)] 60 | pub enum LinearAnimation {} 61 | 62 | #[derive(Clone, Copy)] 63 | pub enum StateMachine {} 64 | 65 | #[derive(Clone, Copy)] 66 | pub enum Scene {} 67 | 68 | #[allow(dead_code)] 69 | #[repr(u8)] 70 | #[derive(Clone, Copy, Debug)] 71 | pub enum PropertyTag { 72 | Bool, 73 | Number, 74 | String, 75 | } 76 | 77 | #[repr(C)] 78 | #[derive(Clone, Copy)] 79 | pub union Property { 80 | bool: bool, 81 | number: f32, 82 | string: RawString, 83 | } 84 | 85 | #[derive(Clone, Copy)] 86 | pub enum Event {} 87 | 88 | #[derive(Clone, Copy)] 89 | pub enum Input {} 90 | 91 | #[allow(dead_code)] 92 | #[repr(u8)] 93 | #[derive(Clone, Copy, Debug)] 94 | pub enum InputTag { 95 | Bool, 96 | Number, 97 | Trigger, 98 | } 99 | 100 | #[derive(Clone, Copy)] 101 | pub enum Bool {} 102 | 103 | #[derive(Clone, Copy)] 104 | pub enum Number {} 105 | 106 | #[derive(Clone, Copy)] 107 | pub enum Trigger {} 108 | 109 | #[no_mangle] 110 | unsafe extern "C" fn rive_rs_allocate_string(string: *mut String, data: *const u8, len: usize) { 111 | let string = &mut *string; 112 | 113 | if let Ok(converted) = String::from_utf8(slice::from_raw_parts(data, len).to_vec()) { 114 | *string = converted; 115 | } 116 | } 117 | 118 | #[no_mangle] 119 | unsafe extern "C" fn rive_rs_insert_property( 120 | properties: *mut BTreeMap, 121 | key_data: *const u8, 122 | key_len: usize, 123 | value_tag: PropertyTag, 124 | value_payload: Property, 125 | ) { 126 | let properties = &mut *properties; 127 | 128 | if let Ok(key) = String::from_utf8(slice::from_raw_parts(key_data, key_len).to_vec()) { 129 | let value = match value_tag { 130 | PropertyTag::Bool => state_machine::Property::Bool(value_payload.bool), 131 | PropertyTag::Number => state_machine::Property::Number(value_payload.number), 132 | PropertyTag::String => { 133 | if let Ok(string) = String::from_utf8( 134 | slice::from_raw_parts(value_payload.string.data, value_payload.string.len) 135 | .to_vec(), 136 | ) { 137 | state_machine::Property::String(string) 138 | } else { 139 | return; 140 | } 141 | } 142 | }; 143 | 144 | properties.insert(key, value); 145 | } 146 | } 147 | 148 | #[repr(C)] 149 | pub struct RendererEntries { 150 | buffer_new: unsafe extern "C" fn(BufferType, BufferFlags, usize) -> *mut R::Buffer, 151 | buffer_release: unsafe extern "C" fn(*mut R::Buffer), 152 | buffer_map: unsafe extern "C" fn(*mut R::Buffer) -> *mut u8, 153 | buffer_unmap: unsafe extern "C" fn(*mut R::Buffer), 154 | path_default: unsafe extern "C" fn() -> *mut R::Path, 155 | path_new: unsafe extern "C" fn(*mut Commands, usize, FillRule) -> *mut R::Path, 156 | path_release: unsafe extern "C" fn(*mut R::Path), 157 | path_reset: unsafe extern "C" fn(*mut R::Path), 158 | path_extend: unsafe extern "C" fn(*mut R::Path, *const R::Path, transform: *const [f32; 6]), 159 | path_set_fill_rule: unsafe extern "C" fn(*mut R::Path, FillRule), 160 | path_move_to: unsafe extern "C" fn(*mut R::Path, f32, f32), 161 | path_line_to: unsafe extern "C" fn(*mut R::Path, f32, f32), 162 | path_cubic_to: unsafe extern "C" fn(*mut R::Path, f32, f32, f32, f32, f32, f32), 163 | path_close: unsafe extern "C" fn(*mut R::Path), 164 | paint_default: unsafe extern "C" fn() -> *mut R::Paint, 165 | paint_release: unsafe extern "C" fn(*mut R::Paint), 166 | paint_set_style: unsafe extern "C" fn(*mut R::Paint, PaintStyle), 167 | paint_set_color: unsafe extern "C" fn(*mut R::Paint, Color), 168 | paint_set_thickness: unsafe extern "C" fn(*mut R::Paint, f32), 169 | paint_set_join: unsafe extern "C" fn(*mut R::Paint, StrokeJoin), 170 | paint_set_cap: unsafe extern "C" fn(*mut R::Paint, StrokeCap), 171 | paint_set_blend_mode: unsafe extern "C" fn(*mut R::Paint, BlendMode), 172 | paint_set_gradient: unsafe extern "C" fn(*mut R::Paint, *const R::Gradient), 173 | paint_invalidate_stroke: unsafe extern "C" fn(*mut R::Paint), 174 | gradient_new_linear: unsafe extern "C" fn( 175 | f32, 176 | f32, 177 | f32, 178 | f32, 179 | *const Color, 180 | *const f32, 181 | usize, 182 | ) -> *mut R::Gradient, 183 | gradient_new_radial: 184 | unsafe extern "C" fn(f32, f32, f32, *const Color, *const f32, usize) -> *mut R::Gradient, 185 | gradient_release: unsafe extern "C" fn(*mut R::Gradient), 186 | image_decode: unsafe extern "C" fn(*const u8, usize) -> *mut R::Image, 187 | image_release: unsafe extern "C" fn(*mut R::Image), 188 | renderer_state_push: unsafe extern "C" fn(*mut R), 189 | renderer_state_pop: unsafe extern "C" fn(*mut R), 190 | renderer_transform: unsafe extern "C" fn(*mut R, transform: *const [f32; 6]), 191 | renderer_set_clip: unsafe extern "C" fn(*mut R, path: *const R::Path), 192 | renderer_draw_path: unsafe extern "C" fn(*mut R, path: *const R::Path, paint: *const R::Paint), 193 | renderer_draw_image: 194 | unsafe extern "C" fn(*mut R, image: *const R::Image, blend_mode: BlendMode, opacity: f32), 195 | renderer_draw_image_mesh: unsafe extern "C" fn( 196 | *mut R, 197 | image: *const R::Image, 198 | vertices: *const R::Buffer, 199 | uvs: *const R::Buffer, 200 | indices: *const R::Buffer, 201 | blend_mode: BlendMode, 202 | opacity: f32, 203 | ), 204 | } 205 | 206 | impl RendererEntries { 207 | pub const ENTRIES: &'static Self = { 208 | unsafe extern "C" fn buffer_new( 209 | r#type: BufferType, 210 | flags: BufferFlags, 211 | len_in_bytes: usize, 212 | ) -> *mut R::Buffer { 213 | Box::into_raw(Box::new(R::Buffer::new(r#type, flags, len_in_bytes))) 214 | } 215 | 216 | unsafe extern "C" fn buffer_release(buffer: *mut R::Buffer) { 217 | drop(Box::from_raw(buffer)) 218 | } 219 | 220 | unsafe extern "C" fn buffer_map(buffer: *mut R::Buffer) -> *mut u8 { 221 | (*buffer).map().as_mut_ptr() 222 | } 223 | 224 | unsafe extern "C" fn buffer_unmap(buffer: *mut R::Buffer) { 225 | (*buffer).unmap(); 226 | } 227 | 228 | unsafe extern "C" fn path_default() -> *mut R::Path { 229 | Box::into_raw(Box::default()) 230 | } 231 | 232 | unsafe extern "C" fn path_new( 233 | commands: *mut Commands, 234 | len: usize, 235 | fill_rule: FillRule, 236 | ) -> *mut R::Path { 237 | Box::into_raw(Box::new(R::Path::new( 238 | &mut path::Commands::new(commands, len), 239 | fill_rule, 240 | ))) 241 | } 242 | 243 | unsafe extern "C" fn path_release(path: *mut R::Path) { 244 | drop(Box::from_raw(path)) 245 | } 246 | 247 | unsafe extern "C" fn path_reset(path: *mut R::Path) { 248 | (*path).reset(); 249 | } 250 | 251 | unsafe extern "C" fn path_extend( 252 | path: *mut R::Path, 253 | other: *const R::Path, 254 | transform: *const [f32; 6], 255 | ) { 256 | (*path).extend(&*other, &*transform); 257 | } 258 | 259 | unsafe extern "C" fn path_set_fill_rule( 260 | path: *mut R::Path, 261 | fill_rule: FillRule, 262 | ) { 263 | (*path).set_fill_rule(fill_rule); 264 | } 265 | 266 | unsafe extern "C" fn path_move_to(path: *mut R::Path, x: f32, y: f32) { 267 | (*path).move_to(x, y); 268 | } 269 | 270 | unsafe extern "C" fn path_line_to(path: *mut R::Path, x: f32, y: f32) { 271 | (*path).line_to(x, y); 272 | } 273 | 274 | unsafe extern "C" fn path_cubic_to( 275 | path: *mut R::Path, 276 | ox: f32, 277 | oy: f32, 278 | ix: f32, 279 | iy: f32, 280 | x: f32, 281 | y: f32, 282 | ) { 283 | (*path).cubic_to(ox, oy, ix, iy, x, y); 284 | } 285 | 286 | unsafe extern "C" fn path_close(path: *mut R::Path) { 287 | (*path).close(); 288 | } 289 | 290 | unsafe extern "C" fn paint_default() -> *mut R::Paint { 291 | Box::into_raw(Box::default()) 292 | } 293 | 294 | unsafe extern "C" fn paint_release(paint: *mut R::Paint) { 295 | drop(Box::from_raw(paint)) 296 | } 297 | 298 | unsafe extern "C" fn paint_set_style(paint: *mut R::Paint, style: PaintStyle) { 299 | (*paint).set_style(style); 300 | } 301 | 302 | unsafe extern "C" fn paint_set_color(paint: *mut R::Paint, color: Color) { 303 | (*paint).set_color(color); 304 | } 305 | 306 | unsafe extern "C" fn paint_set_thickness( 307 | paint: *mut R::Paint, 308 | thickness: f32, 309 | ) { 310 | (*paint).set_thickness(thickness); 311 | } 312 | 313 | unsafe extern "C" fn paint_set_join(paint: *mut R::Paint, join: StrokeJoin) { 314 | (*paint).set_join(join); 315 | } 316 | 317 | unsafe extern "C" fn paint_set_cap(paint: *mut R::Paint, cap: StrokeCap) { 318 | (*paint).set_cap(cap); 319 | } 320 | 321 | unsafe extern "C" fn paint_set_blend_mode( 322 | paint: *mut R::Paint, 323 | blend_mode: BlendMode, 324 | ) { 325 | (*paint).set_blend_mode(blend_mode); 326 | } 327 | 328 | unsafe extern "C" fn paint_set_gradient( 329 | paint: *mut R::Paint, 330 | gradient: *const R::Gradient, 331 | ) { 332 | (*paint).set_gradient(&*gradient); 333 | } 334 | 335 | unsafe extern "C" fn paint_invalidate_stroke(paint: *mut R::Paint) { 336 | (*paint).invalidate_stroke(); 337 | } 338 | 339 | unsafe extern "C" fn gradient_new_linear( 340 | sx: f32, 341 | sy: f32, 342 | ex: f32, 343 | ey: f32, 344 | colors: *const Color, 345 | stops: *const f32, 346 | len: usize, 347 | ) -> *mut R::Gradient { 348 | Box::into_raw(Box::new(R::Gradient::new_linear( 349 | sx, 350 | sy, 351 | ex, 352 | ey, 353 | slice::from_raw_parts(colors, len), 354 | slice::from_raw_parts(stops, len), 355 | ))) 356 | } 357 | 358 | unsafe extern "C" fn gradient_new_radial( 359 | cx: f32, 360 | cy: f32, 361 | radius: f32, 362 | colors: *const Color, 363 | stops: *const f32, 364 | len: usize, 365 | ) -> *mut R::Gradient { 366 | Box::into_raw(Box::new(R::Gradient::new_radial( 367 | cx, 368 | cy, 369 | radius, 370 | slice::from_raw_parts(colors, len), 371 | slice::from_raw_parts(stops, len), 372 | ))) 373 | } 374 | 375 | unsafe extern "C" fn gradient_release(gradient: *mut R::Gradient) { 376 | drop(Box::from_raw(gradient)) 377 | } 378 | 379 | unsafe extern "C" fn image_deocde( 380 | data: *const u8, 381 | len: usize, 382 | ) -> *mut R::Image { 383 | R::Image::decode(slice::from_raw_parts(data, len)) 384 | .map(|image| Box::into_raw(Box::new(image))) 385 | .unwrap_or(ptr::null_mut()) 386 | } 387 | 388 | unsafe extern "C" fn image_release(image: *mut R::Image) { 389 | drop(Box::from_raw(image)) 390 | } 391 | 392 | unsafe extern "C" fn renderer_state_push(renderer: *mut R) { 393 | (*renderer).state_push(); 394 | } 395 | 396 | unsafe extern "C" fn renderer_state_pop(renderer: *mut R) { 397 | (*renderer).state_pop(); 398 | } 399 | 400 | unsafe extern "C" fn renderer_transform( 401 | renderer: *mut R, 402 | transform: *const [f32; 6], 403 | ) { 404 | (*renderer).transform(&*transform); 405 | } 406 | 407 | unsafe extern "C" fn renderer_set_clip( 408 | renderer: *mut R, 409 | path: *const R::Path, 410 | ) { 411 | (*renderer).set_clip(&*path); 412 | } 413 | 414 | unsafe extern "C" fn renderer_draw_path( 415 | renderer: *mut R, 416 | path: *const R::Path, 417 | paint: *const R::Paint, 418 | ) { 419 | (*renderer).draw_path(&*path, &*paint); 420 | } 421 | 422 | unsafe extern "C" fn renderer_draw_image( 423 | renderer: *mut R, 424 | image: *const R::Image, 425 | blend_mode: BlendMode, 426 | opacity: f32, 427 | ) { 428 | (*renderer).draw_image(&*image, blend_mode, opacity); 429 | } 430 | 431 | unsafe extern "C" fn renderer_draw_image_mesh( 432 | renderer: *mut R, 433 | image: *const R::Image, 434 | vertices: *const R::Buffer, 435 | uvs: *const R::Buffer, 436 | indices: *const R::Buffer, 437 | blend_mode: BlendMode, 438 | opacity: f32, 439 | ) { 440 | (*renderer).draw_image_mesh(&*image, &*vertices, &*uvs, &*indices, blend_mode, opacity); 441 | } 442 | 443 | &Self { 444 | buffer_new: buffer_new::, 445 | buffer_release: buffer_release::, 446 | buffer_map: buffer_map::, 447 | buffer_unmap: buffer_unmap::, 448 | path_default: path_default::, 449 | path_new: path_new::, 450 | path_release: path_release::, 451 | path_reset: path_reset::, 452 | path_extend: path_extend::, 453 | path_set_fill_rule: path_set_fill_rule::, 454 | path_move_to: path_move_to::, 455 | path_line_to: path_line_to::, 456 | path_cubic_to: path_cubic_to::, 457 | path_close: path_close::, 458 | paint_default: paint_default::, 459 | paint_release: paint_release::, 460 | paint_set_style: paint_set_style::, 461 | paint_set_color: paint_set_color::, 462 | paint_set_thickness: paint_set_thickness::, 463 | paint_set_join: paint_set_join::, 464 | paint_set_cap: paint_set_cap::, 465 | paint_set_blend_mode: paint_set_blend_mode::, 466 | paint_set_gradient: paint_set_gradient::, 467 | paint_invalidate_stroke: paint_invalidate_stroke::, 468 | gradient_new_linear: gradient_new_linear::, 469 | gradient_new_radial: gradient_new_radial::, 470 | gradient_release: gradient_release::, 471 | image_decode: image_deocde::, 472 | image_release: image_release::, 473 | renderer_state_push: renderer_state_push::, 474 | renderer_state_pop: renderer_state_pop::, 475 | renderer_transform: renderer_transform::, 476 | renderer_set_clip: renderer_set_clip::, 477 | renderer_draw_path: renderer_draw_path::, 478 | renderer_draw_image: renderer_draw_image::, 479 | renderer_draw_image_mesh: renderer_draw_image_mesh::, 480 | } 481 | }; 482 | } 483 | 484 | extern "C" { 485 | #[allow(improper_ctypes)] 486 | pub fn rive_rs_file_new( 487 | data: *const u8, 488 | len: usize, 489 | entries: *const (), 490 | result: *mut FileResult, 491 | factory: *mut *mut Factory, 492 | ) -> *const File; 493 | pub fn rive_rs_file_release(file: *const File, factory: *mut Factory); 494 | pub fn rive_rs_instantiate_artboard( 495 | file: *const File, 496 | index: Option>, 497 | artboard: *mut Option>, 498 | ); 499 | pub fn rive_rs_instantiate_artboard_by_name( 500 | file: *const File, 501 | data: *const u8, 502 | len: usize, 503 | raw_artboard: *mut Option>, 504 | ); 505 | pub fn rive_rs_artboard_instance_release(artboard_instance: *mut Artboard); 506 | pub fn rive_rs_artboard_component_count(artboard_instance: *mut Artboard) -> usize; 507 | pub fn rive_rs_artboard_get_component( 508 | artboard_instance: *mut Artboard, 509 | index: usize, 510 | ) -> *mut Component; 511 | pub fn rive_rs_component_type_id(component: *const Component) -> u16; 512 | pub fn rive_rs_component_name( 513 | component: *const Component, 514 | data: *mut *const u8, 515 | len: *mut usize, 516 | ); 517 | pub fn rive_rs_text_value_run_get_text( 518 | text_value_run: *const TextValueRun, 519 | data: *mut *const u8, 520 | len: *mut usize, 521 | ); 522 | pub fn rive_rs_text_value_run_set_text( 523 | text_value_run: *mut TextValueRun, 524 | data: *const u8, 525 | len: usize, 526 | ); 527 | pub fn rive_rs_instantiate_linear_animation( 528 | artboard: *mut Artboard, 529 | index: Option>, 530 | linear_animation: *mut Option>, 531 | ); 532 | pub fn rive_rs_instantiate_linear_animation_by_name( 533 | artboard: *mut Artboard, 534 | data: *const u8, 535 | len: usize, 536 | linear_animation: *mut Option>, 537 | ); 538 | pub fn rive_rs_linear_animation_time(linear_animation: *mut LinearAnimation) -> f32; 539 | pub fn rive_rs_linear_animation_set_time(linear_animation: *mut LinearAnimation, time: f32); 540 | pub fn rive_rs_linear_animation_is_forwards(linear_animation: *mut LinearAnimation) -> bool; 541 | pub fn rive_rs_linear_animation_set_is_forwards( 542 | linear_animation: *mut LinearAnimation, 543 | is_forwards: bool, 544 | ); 545 | pub fn rive_rs_linear_animation_advance( 546 | linear_animation: *mut LinearAnimation, 547 | elapsed: f32, 548 | ) -> bool; 549 | pub fn rive_rs_linear_animation_apply(linear_animation: *mut LinearAnimation, mix: f32); 550 | pub fn rive_rs_linear_animation_did_loop(linear_animation: *mut LinearAnimation) -> bool; 551 | pub fn rive_rs_linear_animation_set_loop(linear_animation: *mut LinearAnimation, r#loop: Loop); 552 | pub fn rive_rs_linear_animation_is_done(linear_animation: *mut LinearAnimation) -> bool; 553 | pub fn rive_rs_instantiate_state_machine( 554 | artboard: *mut Artboard, 555 | index: Option>, 556 | state_machine: *mut Option>, 557 | ); 558 | pub fn rive_rs_instantiate_state_machine_by_name( 559 | artboard: *mut Artboard, 560 | data: *const u8, 561 | len: usize, 562 | state_machine: *mut Option>, 563 | ); 564 | pub fn rive_rs_state_machine_get_event( 565 | state_machine: *mut StateMachine, 566 | index: usize, 567 | input: *mut *mut Event, 568 | delay: *mut f32, 569 | ); 570 | pub fn rive_rs_state_machine_event_count(state_machine: *mut StateMachine) -> usize; 571 | #[allow(improper_ctypes)] 572 | pub fn rive_rs_event_name(event: *mut Event, string: *mut String); 573 | #[allow(improper_ctypes)] 574 | pub fn rive_rs_event_properties( 575 | event: *mut Event, 576 | properties: *mut BTreeMap, 577 | ); 578 | pub fn rive_rs_state_machine_get_input( 579 | state_machine: *mut StateMachine, 580 | index: usize, 581 | input_tag: *mut InputTag, 582 | input: *mut *mut Input, 583 | ); 584 | pub fn rive_rs_state_machine_input_count(state_machine: *mut StateMachine) -> usize; 585 | pub fn rive_rs_state_machine_get_bool( 586 | state_machine: *mut StateMachine, 587 | name: *const u8, 588 | len: usize, 589 | ) -> *mut Bool; 590 | pub fn rive_rs_state_machine_get_number( 591 | state_machine: *mut StateMachine, 592 | name: *const u8, 593 | len: usize, 594 | ) -> *mut Number; 595 | pub fn rive_rs_state_machine_get_trigger( 596 | state_machine: *mut StateMachine, 597 | name: *const u8, 598 | len: usize, 599 | ) -> *mut Trigger; 600 | pub fn rive_rs_input_name(input: *mut Input, data: *mut *const u8, len: *mut usize); 601 | pub fn rive_rs_bool_get(bool: *mut Bool) -> bool; 602 | pub fn rive_rs_bool_set(bool: *mut Bool, val: bool); 603 | pub fn rive_rs_number_get(number: *mut Number) -> f32; 604 | pub fn rive_rs_number_set(number: *mut Number, val: f32); 605 | pub fn rive_rs_trigger_fire(trigger: *mut Trigger); 606 | pub fn rive_rs_artboard_instance_transforms( 607 | artboard_instance: *mut Artboard, 608 | width: u32, 609 | height: u32, 610 | view_transform: *mut f32, 611 | inverse_view_transform: *mut f32, 612 | ); 613 | pub fn rive_rs_scene_release(scene: *mut Scene); 614 | pub fn rive_rs_commands_next(commands: *mut Commands) -> Command; 615 | pub fn rive_rs_scene_width(scene: *mut Scene) -> f32; 616 | pub fn rive_rs_scene_height(scene: *mut Scene) -> f32; 617 | pub fn rive_rs_scene_loop(scene: *mut Scene) -> Loop; 618 | pub fn rive_rs_scene_is_translucent(scene: *mut Scene) -> bool; 619 | pub fn rive_rs_scene_duration(scene: *mut Scene) -> f32; 620 | pub fn rive_rs_scene_advance_and_apply(scene: *mut Scene, elapsed: f32) -> bool; 621 | pub fn rive_rs_scene_draw(scene: *mut Scene, renderer: *mut (), entries: *const ()); 622 | pub fn rive_rs_scene_pointer_down(scene: *mut Scene, x: f32, y: f32); 623 | pub fn rive_rs_scene_pointer_move(scene: *mut Scene, x: f32, y: f32); 624 | pub fn rive_rs_scene_pointer_up(scene: *mut Scene, x: f32, y: f32); 625 | } 626 | -------------------------------------------------------------------------------- /rive-rs/src/file.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use core::{fmt, marker::PhantomData, ptr}; 3 | 4 | use crate::{ 5 | ffi::{self}, 6 | renderer::Renderer, 7 | }; 8 | 9 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 10 | pub enum Error { 11 | /// Indicates that the Rive file is not supported by this runtime. 12 | UnsupportedVersion, 13 | /// Indicates that the there is a formatting problem in the file itself. 14 | Malformed, 15 | } 16 | 17 | impl fmt::Display for Error { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | match self { 20 | Error::UnsupportedVersion => f.write_str("unsupported Rive version"), 21 | Error::Malformed => f.write_str("file is incorrectly encoded"), 22 | } 23 | } 24 | } 25 | 26 | #[cfg(feature = "vello")] 27 | impl std::error::Error for Error {} 28 | 29 | #[derive(Debug)] 30 | pub(crate) struct FileInner { 31 | pub raw_file: *const ffi::File, 32 | raw_factory: *mut ffi::Factory, 33 | } 34 | 35 | impl Drop for FileInner { 36 | fn drop(&mut self) { 37 | unsafe { 38 | ffi::rive_rs_file_release(self.raw_file, self.raw_factory); 39 | } 40 | } 41 | } 42 | 43 | unsafe impl Send for FileInner {} 44 | unsafe impl Sync for FileInner {} 45 | 46 | pub struct File { 47 | inner: Arc, 48 | _phantom: PhantomData, 49 | } 50 | 51 | impl File { 52 | #[inline] 53 | pub fn new(data: &[u8]) -> Result { 54 | let mut result = ffi::FileResult::Success; 55 | let mut raw_factory = ptr::null_mut(); 56 | 57 | let raw_file = unsafe { 58 | ffi::rive_rs_file_new( 59 | data.as_ptr(), 60 | data.len(), 61 | ffi::RendererEntries::::ENTRIES as *const ffi::RendererEntries as *const (), 62 | &mut result as *mut ffi::FileResult, 63 | &mut raw_factory as *mut *mut ffi::Factory, 64 | ) 65 | }; 66 | 67 | match result { 68 | ffi::FileResult::Success => Ok(Self { 69 | inner: Arc::new(FileInner { 70 | raw_file, 71 | raw_factory, 72 | }), 73 | _phantom: PhantomData, 74 | }), 75 | ffi::FileResult::UnsupportedVersion => Err(Error::UnsupportedVersion), 76 | ffi::FileResult::Malformed => Err(Error::Malformed), 77 | } 78 | } 79 | 80 | pub(crate) fn as_inner(&self) -> &Arc { 81 | &self.inner 82 | } 83 | } 84 | 85 | impl fmt::Debug for File { 86 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 87 | f.debug_struct("File").finish() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /rive-rs/src/instantiate.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::Cow; 2 | 3 | #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] 4 | pub enum Handle { 5 | #[default] 6 | Default, 7 | Index(usize), 8 | Name(Cow<'static, str>), 9 | } 10 | 11 | pub trait Instantiate: Sized { 12 | type From; 13 | 14 | fn instantiate(from: &Self::From, handle: Handle) -> Option; 15 | } 16 | -------------------------------------------------------------------------------- /rive-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "vello"), no_std)] 2 | 3 | //! # Rive runtime bindings 4 | //! 5 | //! [Rive] is a real-time interactive design and animation tool that helps teams create and run 6 | //! interactive animations anywhere. Designers and developers use our collaborative editor to 7 | //! create motion graphics that respond to different states and user inputs. Our lightweight 8 | //! open-source runtime libraries allow them to load their animations into apps, games, and 9 | //! websites. 10 | //! 11 | //! [Rive]: https://rive.app 12 | #![cfg_attr( 13 | feature = "vello", 14 | doc = r##" 15 | 16 | ## Examples 17 | 18 | ``` 19 | # use std::time::Instant; 20 | # use rive_rs::{Artboard, File, Handle, Instantiate, scene::Scene as _, Scene, StateMachine}; 21 | let file = File::new(include_bytes!("../../assets/rating-animation.riv")).unwrap(); 22 | let artboard = Artboard::instantiate(&file, Handle::Default).unwrap(); 23 | let mut state_machine = StateMachine::instantiate(&artboard, Handle::Default).unwrap(); 24 | 25 | let mut last_instant = Instant::now(); 26 | for r in 1..=5 { 27 | let mut rating = state_machine.get_number("rating").unwrap(); 28 | rating.set(r as f32); 29 | 30 | for event in state_machine.events() { 31 | dbg!(event); 32 | } 33 | 34 | state_machine.advance_and_apply(last_instant.elapsed()); 35 | 36 | last_instant = Instant::now(); 37 | } 38 | ``` 39 | "## 40 | )] 41 | 42 | extern crate alloc; 43 | 44 | mod artboard; 45 | mod ffi; 46 | mod file; 47 | mod instantiate; 48 | mod linear_animation; 49 | pub mod path; 50 | mod raw_iter; 51 | pub mod renderer; 52 | pub mod scene; 53 | pub mod state_machine; 54 | #[cfg(feature = "vello")] 55 | pub mod vello; 56 | 57 | pub use crate::{ 58 | artboard::components, 59 | file::Error, 60 | instantiate::{Handle, Instantiate}, 61 | linear_animation::{Direction, Loop}, 62 | scene::Viewport, 63 | }; 64 | 65 | #[cfg(not(feature = "vello"))] 66 | pub use crate::{ 67 | artboard::Artboard, file::File, linear_animation::LinearAnimation, scene::Scene, 68 | state_machine::StateMachine, 69 | }; 70 | 71 | #[cfg(feature = "vello")] 72 | pub type Artboard = artboard::Artboard; 73 | #[cfg(feature = "vello")] 74 | pub type File = file::File; 75 | #[cfg(feature = "vello")] 76 | pub type LinearAnimation = linear_animation::LinearAnimation; 77 | #[cfg(feature = "vello")] 78 | pub type StateMachine = state_machine::StateMachine; 79 | #[cfg(feature = "vello")] 80 | pub use crate::vello::Renderer; 81 | 82 | #[cfg(feature = "vello")] 83 | pub trait Scene: scene::Scene {} 84 | #[cfg(feature = "vello")] 85 | impl> Scene for T {} 86 | 87 | #[cfg(feature = "vello")] 88 | impl Instantiate for Box { 89 | type From = Artboard; 90 | 91 | fn instantiate(from: &Self::From, handle: Handle) -> Option { 92 | StateMachine::instantiate(from, handle.clone()) 93 | .map(|sm| Box::new(sm) as Box) 94 | .or_else(|| { 95 | LinearAnimation::instantiate(from, handle).map(|la| Box::new(la) as Box) 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /rive-rs/src/linear_animation.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use core::{fmt, marker::PhantomData, ptr::NonNull, time::Duration}; 3 | 4 | use crate::{ 5 | artboard::{Artboard, ArtboardInner}, 6 | ffi, 7 | instantiate::{Handle, Instantiate}, 8 | renderer::Renderer, 9 | scene::impl_scene, 10 | }; 11 | 12 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 13 | pub enum Direction { 14 | Forwards, 15 | Backwards, 16 | } 17 | 18 | #[repr(u32)] 19 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 20 | pub enum Loop { 21 | /// Play until the duration or end of work area of the animation. 22 | OneShot = 0, 23 | /// Play until the duration or end of work area of the animation and 24 | /// then go back to the start (0 seconds). 25 | Loop = 1, 26 | /// Play to the end of the duration/work area and then play back. 27 | PingPong = 2, 28 | } 29 | 30 | pub struct LinearAnimation { 31 | artboard: Arc, 32 | raw_linear_animation: *mut ffi::LinearAnimation, 33 | _phantom: PhantomData, 34 | } 35 | 36 | impl Instantiate for LinearAnimation { 37 | type From = Artboard; 38 | 39 | #[inline] 40 | fn instantiate(artboard: &Self::From, handle: Handle) -> Option { 41 | let mut raw_linear_animation: Option> = None; 42 | 43 | match handle { 44 | Handle::Default => unsafe { 45 | ffi::rive_rs_instantiate_linear_animation( 46 | artboard.as_inner().raw_artboard, 47 | None, 48 | &mut raw_linear_animation, 49 | ) 50 | }, 51 | Handle::Index(ref index) => unsafe { 52 | ffi::rive_rs_instantiate_linear_animation( 53 | artboard.as_inner().raw_artboard, 54 | Some(index.into()), 55 | &mut raw_linear_animation, 56 | ) 57 | }, 58 | Handle::Name(name) => unsafe { 59 | ffi::rive_rs_instantiate_linear_animation_by_name( 60 | artboard.as_inner().raw_artboard, 61 | name.as_ptr(), 62 | name.len(), 63 | &mut raw_linear_animation, 64 | ) 65 | }, 66 | } 67 | 68 | raw_linear_animation.map(|raw_linear_animation| LinearAnimation { 69 | artboard: artboard.as_inner().clone(), 70 | raw_linear_animation: raw_linear_animation.as_ptr(), 71 | _phantom: PhantomData, 72 | }) 73 | } 74 | } 75 | 76 | impl LinearAnimation { 77 | fn raw_artboard(&self) -> *mut ffi::Artboard { 78 | self.artboard.raw_artboard 79 | } 80 | 81 | pub fn artboard(&self) -> Artboard { 82 | Artboard::from_inner(self.artboard.clone()) 83 | } 84 | 85 | fn raw_scene(&self) -> *mut ffi::Scene { 86 | self.raw_linear_animation as *mut ffi::Scene 87 | } 88 | 89 | pub fn time(&self) -> Duration { 90 | Duration::from_secs_f32(unsafe { 91 | ffi::rive_rs_linear_animation_time(self.raw_linear_animation) 92 | }) 93 | } 94 | 95 | pub fn set_time(&mut self, time: Duration) { 96 | unsafe { 97 | ffi::rive_rs_linear_animation_set_time(self.raw_linear_animation, time.as_secs_f32()); 98 | } 99 | } 100 | 101 | pub fn direction(&self) -> Direction { 102 | match unsafe { ffi::rive_rs_linear_animation_is_forwards(self.raw_linear_animation) } { 103 | true => Direction::Forwards, 104 | false => Direction::Backwards, 105 | } 106 | } 107 | 108 | pub fn set_direction(&mut self, direction: Direction) { 109 | unsafe { 110 | ffi::rive_rs_linear_animation_set_is_forwards( 111 | self.raw_linear_animation, 112 | direction == Direction::Forwards, 113 | ); 114 | } 115 | } 116 | 117 | pub fn advance(&mut self, elapsed: Duration) -> bool { 118 | unsafe { 119 | ffi::rive_rs_linear_animation_advance(self.raw_linear_animation, elapsed.as_secs_f32()) 120 | } 121 | } 122 | 123 | pub fn apply(&mut self, mix: f32) { 124 | unsafe { ffi::rive_rs_linear_animation_apply(self.raw_linear_animation, mix) } 125 | } 126 | 127 | pub fn did_loop(&self) -> bool { 128 | unsafe { ffi::rive_rs_linear_animation_did_loop(self.raw_linear_animation) } 129 | } 130 | 131 | pub fn set_loop(&mut self, r#loop: Loop) { 132 | unsafe { 133 | ffi::rive_rs_linear_animation_set_loop(self.raw_linear_animation, r#loop); 134 | } 135 | } 136 | 137 | pub fn is_done(&self) -> bool { 138 | unsafe { ffi::rive_rs_linear_animation_is_done(self.raw_linear_animation) } 139 | } 140 | } 141 | 142 | impl fmt::Debug for LinearAnimation { 143 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 144 | f.debug_struct("LinearAnimation").finish() 145 | } 146 | } 147 | 148 | impl Drop for LinearAnimation { 149 | fn drop(&mut self) { 150 | unsafe { 151 | ffi::rive_rs_scene_release(self.raw_scene()); 152 | } 153 | } 154 | } 155 | 156 | unsafe impl Send for LinearAnimation {} 157 | unsafe impl Sync for LinearAnimation {} 158 | 159 | impl_scene!(LinearAnimation); 160 | -------------------------------------------------------------------------------- /rive-rs/src/path.rs: -------------------------------------------------------------------------------- 1 | use core::{fmt, slice}; 2 | 3 | use crate::ffi; 4 | 5 | #[repr(C)] 6 | #[derive(Clone, Copy, Debug, PartialEq)] 7 | pub struct Point { 8 | pub x: f32, 9 | pub y: f32, 10 | } 11 | 12 | #[repr(u32)] 13 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 14 | pub enum FillRule { 15 | NonZero, 16 | EvenOdd, 17 | } 18 | 19 | #[repr(u8)] 20 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 21 | pub enum Verb { 22 | Move = 0, 23 | Line = 1, 24 | Cubic = 4, 25 | Close = 5, 26 | } 27 | 28 | #[derive(Clone, Copy)] 29 | pub struct Commands { 30 | raw_commands: *mut ffi::Commands, 31 | len: usize, 32 | } 33 | 34 | impl Commands { 35 | pub(crate) fn new(raw_commands: *mut ffi::Commands, len: usize) -> Self { 36 | Self { raw_commands, len } 37 | } 38 | } 39 | 40 | impl fmt::Debug for Commands { 41 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 42 | f.debug_struct("Commands").field("len", &self.len).finish() 43 | } 44 | } 45 | 46 | impl<'c> Iterator for &'c mut Commands { 47 | type Item = (Verb, &'c [Point]); 48 | 49 | #[inline] 50 | fn next(&mut self) -> Option { 51 | self.len.checked_sub(1).map(|new_len| { 52 | self.len = new_len; 53 | 54 | let ffi::Command { verb, points } = 55 | unsafe { ffi::rive_rs_commands_next(self.raw_commands) }; 56 | 57 | match verb { 58 | Verb::Move => (verb, unsafe { slice::from_raw_parts(points, 1) }), 59 | Verb::Line => (verb, unsafe { slice::from_raw_parts(points.add(1), 1) }), 60 | Verb::Cubic => (verb, unsafe { slice::from_raw_parts(points.add(1), 3) }), 61 | Verb::Close => (verb, [].as_slice()), 62 | } 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rive-rs/src/raw_iter.rs: -------------------------------------------------------------------------------- 1 | use core::{marker::PhantomData, mem}; 2 | 3 | pub trait Raw: Copy { 4 | type Item<'r> 5 | where 6 | Self: 'r; 7 | 8 | fn len(self) -> usize; 9 | unsafe fn get<'r>(self, index: usize) -> Self::Item<'r>; 10 | } 11 | 12 | #[derive(Debug)] 13 | pub struct RawIter<'r, R: Raw> { 14 | raw: R, 15 | start: usize, 16 | end: usize, 17 | _phantom: PhantomData<&'r ()>, 18 | } 19 | 20 | impl<'r, R: Raw> RawIter<'r, R> { 21 | pub fn new(raw: R) -> Self { 22 | let end = raw.len(); 23 | Self { 24 | raw, 25 | start: 0, 26 | end, 27 | _phantom: PhantomData, 28 | } 29 | } 30 | 31 | pub fn next(&mut self) -> Option> { 32 | (self.start < self.end).then(|| { 33 | let start = self.start; 34 | let index = mem::replace(&mut self.start, start + 1); 35 | 36 | unsafe { self.raw.get(index) } 37 | }) 38 | } 39 | 40 | pub fn nth(&mut self, n: usize) -> Option> { 41 | let index = self.start + n; 42 | (index < self.end).then(|| unsafe { self.raw.get(index) }) 43 | } 44 | 45 | pub fn size_hint(&self) -> (usize, Option) { 46 | let len = self.end - self.start; 47 | (len, Some(len)) 48 | } 49 | 50 | pub fn next_back(&mut self) -> Option> { 51 | (self.start < self.end).then(|| { 52 | self.end -= 1; 53 | 54 | unsafe { self.raw.get(self.end) } 55 | }) 56 | } 57 | 58 | pub fn nth_back(&mut self, n: usize) -> Option> { 59 | let index = self.end.checked_sub(n + 1)?; 60 | (self.start <= index).then(|| unsafe { self.raw.get(index) }) 61 | } 62 | } 63 | 64 | macro_rules! impl_iter { 65 | ( @raw_iter_type $raw:ty, $lt:lifetime ) => (crate::raw_iter::RawIter<$lt, $raw>); 66 | 67 | ( @raw_iter_type $raw:ty ) => (crate::raw_iter::RawIter<'static, $raw>); 68 | 69 | ( $iter:ident, $item:ident, $raw:ty $( , $lt:lifetime )? ) => { 70 | #[derive(Debug)] 71 | pub struct $iter$(<$lt>)?(impl_iter!(@raw_iter_type $raw $(, $lt)?)); 72 | 73 | impl$(<$lt>)? $iter$(<$lt>)? { 74 | pub(crate) fn new(raw: $raw) -> Self { 75 | Self(crate::raw_iter::RawIter::new(raw)) 76 | } 77 | } 78 | 79 | impl$(<$lt>)? Iterator for $iter$(<$lt>)? { 80 | type Item = $item$(<$lt>)?; 81 | 82 | fn next(&mut self) -> Option { 83 | self.0.next() 84 | } 85 | 86 | fn nth(&mut self, n: usize) -> Option { 87 | self.0.nth(n) 88 | } 89 | 90 | fn size_hint(&self) -> (usize, Option) { 91 | self.0.size_hint() 92 | } 93 | } 94 | 95 | impl$(<$lt>)? DoubleEndedIterator for $iter$(<$lt>)? { 96 | fn next_back(&mut self) -> Option { 97 | self.0.next_back() 98 | } 99 | 100 | fn nth_back(&mut self, n: usize) -> Option { 101 | self.0.nth_back(n) 102 | } 103 | } 104 | 105 | impl$(<$lt>)? ExactSizeIterator for $iter$(<$lt>)? {} 106 | }; 107 | } 108 | 109 | pub(crate) use impl_iter; 110 | -------------------------------------------------------------------------------- /rive-rs/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use bitflags::bitflags; 2 | 3 | use crate::path::{Commands, FillRule}; 4 | 5 | #[repr(u32)] 6 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 7 | pub enum BufferType { 8 | Index, 9 | Vertex, 10 | } 11 | 12 | bitflags! { 13 | #[repr(C)] 14 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 15 | pub struct BufferFlags: u32 { 16 | const NONE = 0; 17 | const MAPPED_ONCE_AT_INITIALIZATION = 1; 18 | } 19 | } 20 | 21 | #[repr(u32)] 22 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 23 | pub enum StrokeJoin { 24 | Miter = 0, 25 | Round = 1, 26 | Bevel = 2, 27 | } 28 | 29 | #[repr(u32)] 30 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 31 | pub enum StrokeCap { 32 | Butt = 0, 33 | Round = 1, 34 | Square = 2, 35 | } 36 | 37 | #[repr(u32)] 38 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 39 | pub enum BlendMode { 40 | SrcOver = 3, 41 | Screen = 14, 42 | Overlay = 15, 43 | Darken = 16, 44 | Lighten = 17, 45 | ColorDodge = 18, 46 | ColorBurn = 19, 47 | HardLight = 20, 48 | SoftLight = 21, 49 | Difference = 22, 50 | Exclusion = 23, 51 | Multiply = 24, 52 | Hue = 25, 53 | Saturation = 26, 54 | Color = 27, 55 | Luminosity = 28, 56 | } 57 | 58 | #[repr(u32)] 59 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 60 | pub enum PaintStyle { 61 | Stroke, 62 | Fill, 63 | } 64 | 65 | #[repr(C)] 66 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 67 | pub struct Color { 68 | pub b: u8, 69 | pub g: u8, 70 | pub r: u8, 71 | pub a: u8, 72 | } 73 | 74 | pub trait Buffer: Sized { 75 | fn new(r#type: BufferType, flags: BufferFlags, len_in_bytes: usize) -> Self; 76 | fn map(&mut self) -> &mut [u8]; 77 | fn unmap(&mut self); 78 | } 79 | 80 | pub trait Path: Default + Sized { 81 | fn new(commands: &mut Commands, fill_rule: FillRule) -> Self; 82 | fn reset(&mut self); 83 | fn extend(&mut self, from: &Self, transform: &[f32; 6]); 84 | fn set_fill_rule(&mut self, fill_rule: FillRule); 85 | fn move_to(&mut self, x: f32, y: f32); 86 | fn line_to(&mut self, x: f32, y: f32); 87 | fn cubic_to(&mut self, ox: f32, oy: f32, ix: f32, iy: f32, x: f32, y: f32); 88 | fn close(&mut self); 89 | } 90 | 91 | pub trait Paint: Default + Sized { 92 | type Gradient: Gradient; 93 | 94 | fn set_style(&mut self, style: PaintStyle); 95 | fn set_color(&mut self, color: Color); 96 | fn set_thickness(&mut self, thickness: f32); 97 | fn set_join(&mut self, join: StrokeJoin); 98 | fn set_cap(&mut self, cap: StrokeCap); 99 | fn set_blend_mode(&mut self, blend_mode: BlendMode); 100 | fn set_gradient(&mut self, gradient: &Self::Gradient); 101 | fn invalidate_stroke(&mut self); 102 | } 103 | 104 | pub trait Gradient: Sized { 105 | fn new_linear(sx: f32, sy: f32, ex: f32, ey: f32, colors: &[Color], stops: &[f32]) -> Self; 106 | fn new_radial(cx: f32, cy: f32, radius: f32, colors: &[Color], stops: &[f32]) -> Self; 107 | } 108 | 109 | pub trait Image: Sized { 110 | fn decode(data: &[u8]) -> Option; 111 | } 112 | 113 | pub trait Renderer: Sized + 'static { 114 | type Buffer: Buffer; 115 | type Path: Path; 116 | type Paint: Paint; 117 | type Gradient: Gradient; 118 | type Image: Image; 119 | 120 | fn state_push(&mut self); 121 | fn state_pop(&mut self); 122 | fn transform(&mut self, transform: &[f32; 6]); 123 | fn set_clip(&mut self, path: &Self::Path); 124 | fn draw_path(&mut self, path: &Self::Path, paint: &Self::Paint); 125 | fn draw_image(&mut self, image: &Self::Image, blend_mode: BlendMode, opacity: f32); 126 | fn draw_image_mesh( 127 | &mut self, 128 | image: &Self::Image, 129 | vertices: &Self::Buffer, 130 | uvs: &Self::Buffer, 131 | indices: &Self::Buffer, 132 | blend_mode: BlendMode, 133 | opacity: f32, 134 | ); 135 | } 136 | -------------------------------------------------------------------------------- /rive-rs/src/scene.rs: -------------------------------------------------------------------------------- 1 | use core::{any::Any, time::Duration}; 2 | 3 | use alloc::boxed::Box; 4 | 5 | use crate::{ 6 | artboard::Artboard, 7 | instantiate::{Handle, Instantiate}, 8 | linear_animation::{LinearAnimation, Loop}, 9 | renderer::Renderer, 10 | state_machine::StateMachine, 11 | }; 12 | 13 | pub(crate) fn transform(x: f32, y: f32, t: &[f32; 6]) -> [f32; 2] { 14 | [t[0] * x + t[2] * y + t[4], t[1] * x + t[3] * y + t[5]] 15 | } 16 | 17 | #[derive(Clone, Debug)] 18 | pub struct Viewport { 19 | pub(crate) width: u32, 20 | pub(crate) height: u32, 21 | pub(crate) inverse_view_transform: [f32; 6], 22 | } 23 | 24 | impl Viewport { 25 | #[inline] 26 | pub fn width(&self) -> u32 { 27 | self.width 28 | } 29 | 30 | #[inline] 31 | pub fn height(&self) -> u32 { 32 | self.height 33 | } 34 | 35 | #[inline] 36 | pub fn resize(&mut self, width: u32, height: u32) { 37 | self.width = width; 38 | self.height = height; 39 | } 40 | } 41 | 42 | impl Default for Viewport { 43 | #[inline] 44 | fn default() -> Self { 45 | Self { 46 | width: 0, 47 | height: 0, 48 | inverse_view_transform: [1.0, 0.0, 0.0, 1.0, 0.0, 0.0], 49 | } 50 | } 51 | } 52 | 53 | pub trait Scene: Send + Sync { 54 | fn width(&self) -> f32; 55 | fn height(&self) -> f32; 56 | fn name(&self) -> &str; 57 | fn r#loop(&self) -> Loop; 58 | fn is_translucent(&self) -> bool; 59 | fn duration(&self) -> Option; 60 | fn pointer_down(&mut self, x: f32, y: f32, viewport: &Viewport); 61 | fn pointer_move(&mut self, x: f32, y: f32, viewport: &Viewport); 62 | fn pointer_up(&mut self, x: f32, y: f32, viewport: &Viewport); 63 | fn advance_and_apply(&mut self, elapsed: Duration) -> bool; 64 | fn draw(&self, renderer: &mut R); 65 | fn advance_and_maybe_draw( 66 | &mut self, 67 | renderer: &mut R, 68 | elapsed: Duration, 69 | viewport: &mut Viewport, 70 | ) -> bool; 71 | fn as_any(&self) -> &dyn Any; 72 | } 73 | 74 | macro_rules! impl_scene { 75 | ( $type:tt ) => { 76 | impl crate::scene::Scene for $type { 77 | fn as_any(&self) -> &dyn ::core::any::Any { 78 | self 79 | } 80 | 81 | #[inline] 82 | fn width(&self) -> f32 { 83 | unsafe { crate::ffi::rive_rs_scene_width(self.raw_scene()) } 84 | } 85 | 86 | #[inline] 87 | fn height(&self) -> f32 { 88 | unsafe { crate::ffi::rive_rs_scene_height(self.raw_scene()) } 89 | } 90 | 91 | #[inline] 92 | fn name(&self) -> &str { 93 | let mut data = ::core::ptr::null(); 94 | let mut len = 0; 95 | 96 | let bytes = unsafe { 97 | crate::ffi::rive_rs_component_name( 98 | self.raw_scene() as *mut crate::ffi::Component, 99 | &mut data as *mut *const u8, 100 | &mut len as *mut usize, 101 | ); 102 | ::core::slice::from_raw_parts(data, len) 103 | }; 104 | 105 | ::core::str::from_utf8(bytes).expect("scene name is invalid UTF-8") 106 | } 107 | 108 | #[inline] 109 | fn r#loop(&self) -> crate::linear_animation::Loop { 110 | unsafe { crate::ffi::rive_rs_scene_loop(self.raw_scene()) } 111 | } 112 | 113 | #[inline] 114 | fn is_translucent(&self) -> bool { 115 | unsafe { crate::ffi::rive_rs_scene_is_translucent(self.raw_scene()) } 116 | } 117 | 118 | #[inline] 119 | fn duration(&self) -> Option<::core::time::Duration> { 120 | ::core::time::Duration::try_from_secs_f32(unsafe { 121 | crate::ffi::rive_rs_scene_duration(self.raw_scene()) 122 | }) 123 | .ok() 124 | } 125 | 126 | #[inline] 127 | fn pointer_down(&mut self, x: f32, y: f32, viewport: &crate::scene::Viewport) { 128 | let [x, y] = crate::scene::transform(x, y, &viewport.inverse_view_transform); 129 | unsafe { 130 | crate::ffi::rive_rs_scene_pointer_down(self.raw_scene(), x, y); 131 | } 132 | } 133 | 134 | #[inline] 135 | fn pointer_move(&mut self, x: f32, y: f32, viewport: &crate::scene::Viewport) { 136 | let [x, y] = crate::scene::transform(x, y, &viewport.inverse_view_transform); 137 | unsafe { 138 | crate::ffi::rive_rs_scene_pointer_move(self.raw_scene(), x, y); 139 | } 140 | } 141 | 142 | #[inline] 143 | fn pointer_up(&mut self, x: f32, y: f32, viewport: &crate::scene::Viewport) { 144 | let [x, y] = crate::scene::transform(x, y, &viewport.inverse_view_transform); 145 | unsafe { 146 | crate::ffi::rive_rs_scene_pointer_up(self.raw_scene(), x, y); 147 | } 148 | } 149 | 150 | #[inline] 151 | fn advance_and_apply(&mut self, elapsed: ::core::time::Duration) -> bool { 152 | unsafe { 153 | crate::ffi::rive_rs_scene_advance_and_apply( 154 | self.raw_scene(), 155 | elapsed.as_secs_f32(), 156 | ) 157 | } 158 | } 159 | 160 | #[inline] 161 | fn draw(&self, renderer: &mut R) { 162 | unsafe { 163 | crate::ffi::rive_rs_scene_draw( 164 | self.raw_scene(), 165 | renderer as *mut R as *mut (), 166 | crate::ffi::RendererEntries::::ENTRIES 167 | as *const crate::ffi::RendererEntries as *const (), 168 | ); 169 | } 170 | } 171 | 172 | #[inline] 173 | fn advance_and_maybe_draw( 174 | &mut self, 175 | renderer: &mut R, 176 | elapsed: ::core::time::Duration, 177 | viewport: &mut crate::scene::Viewport, 178 | ) -> bool { 179 | let mut view_transform = [0.0; 6]; 180 | let mut inverse_view_transform = [0.0; 6]; 181 | 182 | unsafe { 183 | crate::ffi::rive_rs_artboard_instance_transforms( 184 | self.raw_artboard(), 185 | viewport.width, 186 | viewport.height, 187 | view_transform.as_mut_ptr(), 188 | inverse_view_transform.as_mut_ptr(), 189 | ); 190 | } 191 | 192 | viewport.inverse_view_transform = inverse_view_transform; 193 | 194 | if !self.advance_and_apply(elapsed) { 195 | return false; 196 | } 197 | 198 | renderer.state_push(); 199 | renderer.transform(&view_transform); 200 | 201 | self.draw(renderer); 202 | 203 | renderer.state_pop(); 204 | 205 | true 206 | } 207 | } 208 | }; 209 | } 210 | 211 | pub(crate) use impl_scene; 212 | 213 | impl Instantiate for Box> { 214 | type From = Artboard; 215 | 216 | fn instantiate(from: &Self::From, handle: Handle) -> Option { 217 | StateMachine::instantiate(from, handle.clone()) 218 | .map(|sm| Box::new(sm) as Box>) 219 | .or_else(|| { 220 | LinearAnimation::instantiate(from, handle) 221 | .map(|la| Box::new(la) as Box>) 222 | }) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /rive-rs/src/state_machine/events/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::{collections::BTreeMap, string::String}; 2 | use core::{ptr, time::Duration}; 3 | 4 | use crate::{ 5 | ffi, 6 | raw_iter::{impl_iter, Raw}, 7 | }; 8 | 9 | mod properties; 10 | 11 | pub use properties::Property; 12 | 13 | #[derive(Clone, Debug)] 14 | pub struct Event { 15 | pub name: String, 16 | pub delay: Duration, 17 | pub properties: BTreeMap, 18 | } 19 | 20 | #[derive(Clone, Copy, Debug)] 21 | pub(crate) struct RawStateMachine(pub *mut ffi::StateMachine); 22 | 23 | impl Raw for RawStateMachine { 24 | type Item<'s> = Event; 25 | 26 | fn len(self) -> usize { 27 | unsafe { ffi::rive_rs_state_machine_event_count(self.0) } 28 | } 29 | 30 | unsafe fn get<'s>(self, index: usize) -> Self::Item<'s> { 31 | let mut raw_event = ptr::null_mut(); 32 | let mut name = String::new(); 33 | let mut delay = 0.0; 34 | let mut properties = BTreeMap::new(); 35 | 36 | unsafe { 37 | ffi::rive_rs_state_machine_get_event( 38 | self.0, 39 | index, 40 | &mut raw_event as *mut *mut ffi::Event, 41 | &mut delay as *mut f32, 42 | ); 43 | ffi::rive_rs_event_name(raw_event, &mut name as *mut String); 44 | ffi::rive_rs_event_properties( 45 | raw_event, 46 | &mut properties as *mut BTreeMap, 47 | ); 48 | } 49 | 50 | Event { 51 | name, 52 | delay: Duration::from_secs_f32(delay), 53 | properties, 54 | } 55 | } 56 | } 57 | 58 | impl_iter!(EventIter, Event, RawStateMachine); 59 | -------------------------------------------------------------------------------- /rive-rs/src/state_machine/events/properties.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | 3 | #[derive(Clone, Debug)] 4 | pub enum Property { 5 | Bool(bool), 6 | Number(f32), 7 | String(String), 8 | } 9 | -------------------------------------------------------------------------------- /rive-rs/src/state_machine/inputs.rs: -------------------------------------------------------------------------------- 1 | use core::{fmt, marker::PhantomData, ptr, slice, str}; 2 | 3 | use crate::{ 4 | ffi, 5 | raw_iter::{impl_iter, Raw}, 6 | }; 7 | 8 | unsafe fn input_name<'s>(raw_input: *mut ffi::Input) -> &'s str { 9 | let mut data = ptr::null(); 10 | let mut len = 0; 11 | 12 | let bytes = unsafe { 13 | ffi::rive_rs_input_name( 14 | raw_input, 15 | &mut data as *mut *const u8, 16 | &mut len as *mut usize, 17 | ); 18 | slice::from_raw_parts(data, len) 19 | }; 20 | 21 | str::from_utf8(bytes).expect("input name is invalid UTF-8") 22 | } 23 | 24 | pub struct Bool<'s> { 25 | raw_bool: *mut ffi::Bool, 26 | _phantom: PhantomData<&'s ()>, 27 | } 28 | 29 | impl Bool<'_> { 30 | pub(crate) fn new(raw_bool: *mut ffi::Bool) -> Self { 31 | Self { 32 | raw_bool, 33 | _phantom: PhantomData, 34 | } 35 | } 36 | 37 | pub fn name(&self) -> &str { 38 | unsafe { input_name(self.raw_bool as *mut ffi::Input) } 39 | } 40 | 41 | pub fn get(&self) -> bool { 42 | unsafe { ffi::rive_rs_bool_get(self.raw_bool) } 43 | } 44 | 45 | pub fn set(&mut self, val: bool) { 46 | unsafe { 47 | ffi::rive_rs_bool_set(self.raw_bool, val); 48 | } 49 | } 50 | } 51 | 52 | impl<'s> fmt::Debug for Bool<'s> { 53 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 54 | f.debug_struct("Bool").field("name", &self.name()).finish() 55 | } 56 | } 57 | 58 | pub struct Number<'s> { 59 | raw_number: *mut ffi::Number, 60 | _phantom: PhantomData<&'s ()>, 61 | } 62 | 63 | impl Number<'_> { 64 | pub(crate) fn new(raw_number: *mut ffi::Number) -> Self { 65 | Self { 66 | raw_number, 67 | _phantom: PhantomData, 68 | } 69 | } 70 | 71 | pub fn name(&self) -> &str { 72 | unsafe { input_name(self.raw_number as *mut ffi::Input) } 73 | } 74 | 75 | pub fn get(&self) -> f32 { 76 | unsafe { ffi::rive_rs_number_get(self.raw_number) } 77 | } 78 | 79 | pub fn set(&mut self, val: f32) { 80 | unsafe { 81 | ffi::rive_rs_number_set(self.raw_number, val); 82 | } 83 | } 84 | } 85 | 86 | impl<'s> fmt::Debug for Number<'s> { 87 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 88 | f.debug_struct("Number") 89 | .field("name", &self.name()) 90 | .finish() 91 | } 92 | } 93 | 94 | pub struct Trigger<'s> { 95 | raw_trigger: *mut ffi::Trigger, 96 | _phantom: PhantomData<&'s ()>, 97 | } 98 | 99 | impl Trigger<'_> { 100 | pub(crate) fn new(raw_trigger: *mut ffi::Trigger) -> Self { 101 | Self { 102 | raw_trigger, 103 | _phantom: PhantomData, 104 | } 105 | } 106 | 107 | pub fn name(&self) -> &str { 108 | unsafe { input_name(self.raw_trigger as *mut ffi::Input) } 109 | } 110 | 111 | pub fn fire(&mut self) { 112 | unsafe { 113 | ffi::rive_rs_trigger_fire(self.raw_trigger); 114 | } 115 | } 116 | } 117 | 118 | impl<'s> fmt::Debug for Trigger<'s> { 119 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 120 | f.debug_struct("Trigger") 121 | .field("name", &self.name()) 122 | .finish() 123 | } 124 | } 125 | 126 | #[derive(Debug)] 127 | pub enum Input<'s> { 128 | Bool(Bool<'s>), 129 | Number(Number<'s>), 130 | Trigger(Trigger<'s>), 131 | } 132 | 133 | #[derive(Clone, Copy, Debug)] 134 | pub(crate) struct RawStateMachine(pub *mut ffi::StateMachine); 135 | 136 | impl Raw for RawStateMachine { 137 | type Item<'s> = Input<'s>; 138 | 139 | fn len(self) -> usize { 140 | unsafe { ffi::rive_rs_state_machine_input_count(self.0) } 141 | } 142 | 143 | unsafe fn get<'s>(self, index: usize) -> Self::Item<'s> { 144 | let mut input_tag = ffi::InputTag::Bool; 145 | let mut input = ptr::null_mut(); 146 | ffi::rive_rs_state_machine_get_input( 147 | self.0, 148 | index, 149 | &mut input_tag as *mut ffi::InputTag, 150 | &mut input as *mut *mut ffi::Input, 151 | ); 152 | 153 | match input_tag { 154 | ffi::InputTag::Bool => Input::Bool(Bool { 155 | raw_bool: input as *mut ffi::Bool, 156 | _phantom: PhantomData, 157 | }), 158 | ffi::InputTag::Number => Input::Number(Number { 159 | raw_number: input as *mut ffi::Number, 160 | _phantom: PhantomData, 161 | }), 162 | ffi::InputTag::Trigger => Input::Trigger(Trigger { 163 | raw_trigger: input as *mut ffi::Trigger, 164 | _phantom: PhantomData, 165 | }), 166 | } 167 | } 168 | } 169 | 170 | impl_iter!(InputIter, Input, RawStateMachine, 's); 171 | -------------------------------------------------------------------------------- /rive-rs/src/state_machine/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use core::{fmt, marker::PhantomData, ptr::NonNull}; 3 | 4 | use crate::{ 5 | artboard::{Artboard, ArtboardInner}, 6 | ffi, 7 | instantiate::{Handle, Instantiate}, 8 | renderer::Renderer, 9 | scene::impl_scene, 10 | }; 11 | 12 | mod events; 13 | mod inputs; 14 | 15 | pub use self::{ 16 | events::{Event, EventIter, Property}, 17 | inputs::{Bool, InputIter, Number, Trigger}, 18 | }; 19 | 20 | pub struct StateMachine { 21 | artboard: Arc, 22 | raw_state_machine: *mut ffi::StateMachine, 23 | _phantom: PhantomData, 24 | } 25 | 26 | impl Instantiate for StateMachine { 27 | type From = Artboard; 28 | 29 | #[inline] 30 | fn instantiate(artboard: &Self::From, handle: Handle) -> Option { 31 | let mut raw_state_machine: Option> = None; 32 | 33 | match handle { 34 | Handle::Default => unsafe { 35 | ffi::rive_rs_instantiate_state_machine( 36 | artboard.as_inner().raw_artboard, 37 | None, 38 | &mut raw_state_machine, 39 | ) 40 | }, 41 | Handle::Index(ref index) => unsafe { 42 | ffi::rive_rs_instantiate_state_machine( 43 | artboard.as_inner().raw_artboard, 44 | Some(index.into()), 45 | &mut raw_state_machine, 46 | ) 47 | }, 48 | Handle::Name(name) => unsafe { 49 | ffi::rive_rs_instantiate_state_machine_by_name( 50 | artboard.as_inner().raw_artboard, 51 | name.as_ptr(), 52 | name.len(), 53 | &mut raw_state_machine, 54 | ) 55 | }, 56 | } 57 | 58 | raw_state_machine.map(|raw_state_machine| StateMachine { 59 | artboard: artboard.as_inner().clone(), 60 | raw_state_machine: raw_state_machine.as_ptr(), 61 | _phantom: PhantomData, 62 | }) 63 | } 64 | } 65 | 66 | impl StateMachine { 67 | fn raw_artboard(&self) -> *mut ffi::Artboard { 68 | self.artboard.raw_artboard 69 | } 70 | 71 | pub fn artboard(&self) -> Artboard { 72 | Artboard::from_inner(self.artboard.clone()) 73 | } 74 | 75 | fn raw_scene(&self) -> *mut ffi::Scene { 76 | self.raw_state_machine as *mut ffi::Scene 77 | } 78 | 79 | #[inline] 80 | pub fn events(&self) -> EventIter { 81 | EventIter::new(events::RawStateMachine(self.raw_state_machine)) 82 | } 83 | 84 | #[inline] 85 | pub fn inputs(&self) -> InputIter { 86 | InputIter::new(inputs::RawStateMachine(self.raw_state_machine)) 87 | } 88 | 89 | #[inline] 90 | pub fn get_bool(&self, name: &str) -> Option { 91 | unsafe { 92 | NonNull::new(ffi::rive_rs_state_machine_get_bool( 93 | self.raw_state_machine, 94 | name.as_ptr(), 95 | name.len(), 96 | )) 97 | .map(|ptr| Bool::new(ptr.as_ptr())) 98 | } 99 | } 100 | 101 | #[inline] 102 | pub fn get_number(&self, name: &str) -> Option { 103 | unsafe { 104 | NonNull::new(ffi::rive_rs_state_machine_get_number( 105 | self.raw_state_machine, 106 | name.as_ptr(), 107 | name.len(), 108 | )) 109 | .map(|ptr| Number::new(ptr.as_ptr())) 110 | } 111 | } 112 | 113 | #[inline] 114 | pub fn get_trigger(&self, name: &str) -> Option { 115 | unsafe { 116 | NonNull::new(ffi::rive_rs_state_machine_get_trigger( 117 | self.raw_state_machine, 118 | name.as_ptr(), 119 | name.len(), 120 | )) 121 | .map(|ptr| Trigger::new(ptr.as_ptr())) 122 | } 123 | } 124 | } 125 | 126 | impl fmt::Debug for StateMachine { 127 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 128 | f.debug_struct("StateMachine").finish() 129 | } 130 | } 131 | 132 | impl Drop for StateMachine { 133 | fn drop(&mut self) { 134 | unsafe { 135 | ffi::rive_rs_scene_release(self.raw_scene()); 136 | } 137 | } 138 | } 139 | 140 | unsafe impl Send for StateMachine {} 141 | unsafe impl Sync for StateMachine {} 142 | 143 | impl_scene!(StateMachine); 144 | -------------------------------------------------------------------------------- /rive-rs/src/vello/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, io::Cursor}; 2 | 3 | use image::io::Reader; 4 | use smallvec::SmallVec; 5 | use vello::{ 6 | kurbo::{Affine, BezPath, Line, PathSeg, Point, Rect, Shape, Vec2}, 7 | peniko::{ 8 | self, BlendMode, Brush, BrushRef, Cap, Color, ColorStop, ColorStopsSource, Fill, Format, 9 | Join, Mix, Stroke, 10 | }, 11 | SceneBuilder, SceneFragment, 12 | }; 13 | 14 | mod util; 15 | 16 | use util::ScaleFromOrigin; 17 | 18 | use crate::renderer; 19 | 20 | fn to_vello_color(color: renderer::Color) -> Color { 21 | Color::rgba8(color.r, color.g, color.b, color.a) 22 | } 23 | 24 | fn to_vello_mix(blend_mode: renderer::BlendMode) -> Mix { 25 | match blend_mode { 26 | renderer::BlendMode::SrcOver => Mix::Normal, 27 | renderer::BlendMode::Screen => Mix::Screen, 28 | renderer::BlendMode::Overlay => Mix::Overlay, 29 | renderer::BlendMode::Darken => Mix::Darken, 30 | renderer::BlendMode::Lighten => Mix::Lighten, 31 | renderer::BlendMode::ColorDodge => Mix::ColorDodge, 32 | renderer::BlendMode::ColorBurn => Mix::ColorBurn, 33 | renderer::BlendMode::HardLight => Mix::HardLight, 34 | renderer::BlendMode::SoftLight => Mix::SoftLight, 35 | renderer::BlendMode::Difference => Mix::Difference, 36 | renderer::BlendMode::Exclusion => Mix::Exclusion, 37 | renderer::BlendMode::Multiply => Mix::Multiply, 38 | renderer::BlendMode::Hue => Mix::Hue, 39 | renderer::BlendMode::Saturation => Mix::Saturation, 40 | renderer::BlendMode::Color => Mix::Color, 41 | renderer::BlendMode::Luminosity => Mix::Luminosity, 42 | } 43 | } 44 | 45 | fn triangle_path(points: [Point; 3]) -> BezPath { 46 | BezPath::from_path_segments( 47 | [ 48 | PathSeg::Line(Line::new(points[0], points[1])), 49 | PathSeg::Line(Line::new(points[1], points[2])), 50 | PathSeg::Line(Line::new(points[2], points[0])), 51 | ] 52 | .into_iter(), 53 | ) 54 | } 55 | 56 | #[derive(Debug)] 57 | pub struct Buffer { 58 | inner: Vec, 59 | } 60 | 61 | impl Buffer { 62 | #[inline] 63 | pub fn as_f32_pairs(&self) -> &[[f32; 2]] { 64 | bytemuck::cast_slice(&self.inner) 65 | } 66 | 67 | #[inline] 68 | pub fn as_u16s(&self) -> &[u16] { 69 | bytemuck::cast_slice(&self.inner) 70 | } 71 | } 72 | 73 | impl renderer::Buffer for Buffer { 74 | #[inline] 75 | fn new( 76 | _type: renderer::BufferType, 77 | _flags: renderer::BufferFlags, 78 | len_in_bytes: usize, 79 | ) -> Self { 80 | Self { 81 | inner: vec![0; len_in_bytes], 82 | } 83 | } 84 | 85 | #[inline] 86 | fn map(&mut self) -> &mut [u8] { 87 | &mut self.inner 88 | } 89 | 90 | #[inline] 91 | fn unmap(&mut self) {} 92 | } 93 | 94 | #[derive(Debug)] 95 | pub struct Path { 96 | inner: BezPath, 97 | fill: Fill, 98 | } 99 | 100 | impl Default for Path { 101 | #[inline] 102 | fn default() -> Self { 103 | Self { 104 | inner: Default::default(), 105 | fill: Fill::NonZero, 106 | } 107 | } 108 | } 109 | 110 | impl renderer::Path for Path { 111 | fn new(commands: &mut crate::path::Commands, fill_rule: crate::path::FillRule) -> Self { 112 | let mut path = Self::default(); 113 | 114 | for (verb, points) in commands { 115 | match verb { 116 | crate::path::Verb::Move => path.move_to(points[0].x, points[0].y), 117 | crate::path::Verb::Line => path.line_to(points[0].x, points[0].y), 118 | crate::path::Verb::Cubic => path.cubic_to( 119 | points[0].x, 120 | points[0].y, 121 | points[1].x, 122 | points[1].y, 123 | points[2].x, 124 | points[2].y, 125 | ), 126 | crate::path::Verb::Close => path.close(), 127 | } 128 | } 129 | 130 | path.set_fill_rule(fill_rule); 131 | 132 | path 133 | } 134 | 135 | #[inline] 136 | fn reset(&mut self) { 137 | self.inner.truncate(0); 138 | } 139 | 140 | #[inline] 141 | fn extend(&mut self, from: &Self, transform: &[f32; 6]) { 142 | let mut from = from.inner.clone(); 143 | from.apply_affine(Affine::new(transform.map(Into::into))); 144 | 145 | self.inner.extend(from.elements().iter().cloned()); 146 | } 147 | 148 | #[inline] 149 | fn set_fill_rule(&mut self, fill_rule: crate::path::FillRule) { 150 | self.fill = match fill_rule { 151 | crate::path::FillRule::NonZero => Fill::NonZero, 152 | crate::path::FillRule::EvenOdd => Fill::EvenOdd, 153 | }; 154 | } 155 | 156 | #[inline] 157 | fn move_to(&mut self, x: f32, y: f32) { 158 | self.inner.move_to(Point::new(x as f64, y as f64)); 159 | } 160 | 161 | #[inline] 162 | fn line_to(&mut self, x: f32, y: f32) { 163 | self.inner.line_to(Point::new(x as f64, y as f64)); 164 | } 165 | 166 | #[inline] 167 | fn cubic_to(&mut self, ox: f32, oy: f32, ix: f32, iy: f32, x: f32, y: f32) { 168 | self.inner.curve_to( 169 | Point::new(ox as f64, oy as f64), 170 | Point::new(ix as f64, iy as f64), 171 | Point::new(x as f64, y as f64), 172 | ); 173 | } 174 | 175 | #[inline] 176 | fn close(&mut self) { 177 | self.inner.close_path(); 178 | } 179 | } 180 | 181 | #[derive(Debug)] 182 | struct SliceStops<'s> { 183 | colors: &'s [renderer::Color], 184 | stops: &'s [f32], 185 | } 186 | 187 | impl ColorStopsSource for SliceStops<'_> { 188 | fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) { 189 | vec.extend( 190 | self.colors 191 | .iter() 192 | .zip(self.stops.iter()) 193 | .map(|(&color, &offset)| ColorStop { 194 | offset, 195 | color: to_vello_color(color), 196 | }), 197 | ); 198 | } 199 | } 200 | 201 | #[derive(Debug)] 202 | enum RenderStyle { 203 | Fill, 204 | Stroke(Stroke), 205 | } 206 | 207 | #[derive(Debug)] 208 | pub struct Paint { 209 | style: RenderStyle, 210 | brush: Brush, 211 | blend_mode: BlendMode, 212 | } 213 | 214 | impl Default for Paint { 215 | #[inline] 216 | fn default() -> Self { 217 | Self { 218 | style: RenderStyle::Fill, 219 | brush: Brush::Solid(Color::TRANSPARENT), 220 | blend_mode: Mix::Normal.into(), 221 | } 222 | } 223 | } 224 | 225 | impl renderer::Paint for Paint { 226 | type Gradient = Gradient; 227 | 228 | #[inline] 229 | fn set_style(&mut self, style: renderer::PaintStyle) { 230 | self.style = match style { 231 | renderer::PaintStyle::Stroke => RenderStyle::Stroke(Stroke::new(0.0)), 232 | renderer::PaintStyle::Fill => RenderStyle::Fill, 233 | } 234 | } 235 | 236 | #[inline] 237 | fn set_color(&mut self, color: renderer::Color) { 238 | self.brush = Brush::Solid(to_vello_color(color)); 239 | } 240 | 241 | #[inline] 242 | fn set_thickness(&mut self, thickness: f32) { 243 | loop { 244 | if let RenderStyle::Stroke(stroke) = &mut self.style { 245 | stroke.width = thickness; 246 | break; 247 | } else { 248 | self.style = RenderStyle::Stroke(Stroke::new(0.0)); 249 | } 250 | } 251 | } 252 | 253 | #[inline] 254 | fn set_join(&mut self, join: renderer::StrokeJoin) { 255 | loop { 256 | if let RenderStyle::Stroke(stroke) = &mut self.style { 257 | stroke.join = match join { 258 | renderer::StrokeJoin::Miter => Join::Miter, 259 | renderer::StrokeJoin::Round => Join::Round, 260 | renderer::StrokeJoin::Bevel => Join::Bevel, 261 | }; 262 | break; 263 | } else { 264 | self.style = RenderStyle::Stroke(Stroke::new(0.0)); 265 | } 266 | } 267 | } 268 | 269 | #[inline] 270 | fn set_cap(&mut self, cap: renderer::StrokeCap) { 271 | loop { 272 | if let RenderStyle::Stroke(stroke) = &mut self.style { 273 | stroke.start_cap = match cap { 274 | renderer::StrokeCap::Butt => Cap::Butt, 275 | renderer::StrokeCap::Round => Cap::Round, 276 | renderer::StrokeCap::Square => Cap::Square, 277 | }; 278 | stroke.end_cap = stroke.start_cap; 279 | break; 280 | } else { 281 | self.style = RenderStyle::Stroke(Stroke::new(0.0)); 282 | } 283 | } 284 | } 285 | 286 | #[inline] 287 | fn set_blend_mode(&mut self, blend_mode: renderer::BlendMode) { 288 | self.blend_mode = to_vello_mix(blend_mode).into(); 289 | } 290 | 291 | #[inline] 292 | fn set_gradient(&mut self, gradient: &Self::Gradient) { 293 | self.brush = Brush::Gradient(gradient.inner.clone()); 294 | } 295 | 296 | #[inline] 297 | fn invalidate_stroke(&mut self) {} 298 | } 299 | 300 | #[derive(Debug)] 301 | pub struct Gradient { 302 | inner: peniko::Gradient, 303 | } 304 | 305 | impl renderer::Gradient for Gradient { 306 | #[inline] 307 | fn new_linear( 308 | sx: f32, 309 | sy: f32, 310 | ex: f32, 311 | ey: f32, 312 | colors: &[renderer::Color], 313 | stops: &[f32], 314 | ) -> Self { 315 | let stops = SliceStops { colors, stops }; 316 | Gradient { 317 | inner: peniko::Gradient::new_linear((sx as f64, sy as f64), (ex as f64, ey as f64)) 318 | .with_stops(stops), 319 | } 320 | } 321 | 322 | #[inline] 323 | fn new_radial( 324 | cx: f32, 325 | cy: f32, 326 | radius: f32, 327 | colors: &[renderer::Color], 328 | stops: &[f32], 329 | ) -> Self { 330 | let stops = SliceStops { colors, stops }; 331 | Gradient { 332 | inner: peniko::Gradient::new_radial((cx as f64, cy as f64), radius).with_stops(stops), 333 | } 334 | } 335 | } 336 | 337 | #[derive(Debug)] 338 | pub struct Image { 339 | inner: peniko::Image, 340 | } 341 | 342 | impl renderer::Image for Image { 343 | fn decode(data: &[u8]) -> Option { 344 | let image = Reader::new(Cursor::new(data)) 345 | .with_guessed_format() 346 | .ok()? 347 | .decode() 348 | .ok()? 349 | .into_rgba8(); 350 | let width = image.width(); 351 | let height = image.height(); 352 | 353 | Some(Image { 354 | inner: peniko::Image::new(image.into_raw().into(), Format::Rgba8, width, height), 355 | }) 356 | } 357 | } 358 | 359 | pub struct Renderer { 360 | scene: Box, 361 | builder: SceneBuilder<'static>, 362 | transforms: Vec, 363 | clips: Vec, 364 | } 365 | 366 | impl Renderer { 367 | pub fn scene(&self) -> &SceneFragment { 368 | &self.scene 369 | } 370 | 371 | pub fn into_scene(self) -> SceneFragment { 372 | *self.scene 373 | } 374 | 375 | fn last_transform(&mut self) -> &mut Affine { 376 | self.transforms.last_mut().unwrap() 377 | } 378 | 379 | fn last_clip(&mut self) -> &mut bool { 380 | self.clips.last_mut().unwrap() 381 | } 382 | } 383 | 384 | impl Default for Renderer { 385 | #[inline] 386 | fn default() -> Self { 387 | let mut scene = Box::::default(); 388 | let builder = { 389 | let scene_mut: &mut SceneFragment = &mut scene; 390 | SceneBuilder::for_fragment(unsafe { 391 | // Quite a hack until we have a better way to do this in Vello. 392 | // Pretend that the scene fragment pointer lives for 'static. 393 | std::mem::transmute(scene_mut) 394 | }) 395 | }; 396 | 397 | Self { 398 | scene, 399 | builder, 400 | transforms: vec![Affine::IDENTITY], 401 | clips: vec![false], 402 | } 403 | } 404 | } 405 | 406 | impl renderer::Renderer for Renderer { 407 | type Buffer = Buffer; 408 | 409 | type Path = Path; 410 | 411 | type Paint = Paint; 412 | 413 | type Gradient = Gradient; 414 | 415 | type Image = Image; 416 | 417 | #[inline] 418 | fn state_push(&mut self) { 419 | let last_transform = *self.last_transform(); 420 | 421 | self.transforms.push(last_transform); 422 | self.clips.push(false); 423 | } 424 | 425 | #[inline] 426 | fn state_pop(&mut self) { 427 | self.transforms.pop(); 428 | if self.clips.pop().unwrap_or_default() { 429 | self.builder.pop_layer(); 430 | } 431 | 432 | if self.transforms.is_empty() { 433 | self.transforms.push(Affine::IDENTITY); 434 | self.clips.push(false); 435 | } 436 | } 437 | 438 | #[inline] 439 | fn transform(&mut self, transform: &[f32; 6]) { 440 | let last_transform = self.last_transform(); 441 | *last_transform *= Affine::new((*transform).map(Into::into)); 442 | } 443 | 444 | #[inline] 445 | fn set_clip(&mut self, path: &Self::Path) { 446 | let transform = *self.last_transform(); 447 | 448 | if *self.last_clip() { 449 | self.builder.pop_layer(); 450 | } 451 | 452 | self.builder 453 | .push_layer(Mix::Clip, 1.0, transform, &path.inner); 454 | 455 | *self.last_clip() = true; 456 | } 457 | 458 | #[inline] 459 | fn draw_path(&mut self, path: &Self::Path, paint: &Self::Paint) { 460 | let transform = *self.last_transform(); 461 | 462 | let builder = &mut self.builder; 463 | 464 | let skip_blending = paint.blend_mode == Mix::Normal.into(); 465 | 466 | if !skip_blending { 467 | builder.push_layer(paint.blend_mode, 1.0, transform, &path.inner.bounding_box()); 468 | } 469 | 470 | match &paint.style { 471 | RenderStyle::Fill => { 472 | builder.fill(path.fill, transform, &paint.brush, None, &path.inner) 473 | } 474 | RenderStyle::Stroke(stroke) => { 475 | builder.stroke(stroke, transform, &paint.brush, None, &path.inner) 476 | } 477 | } 478 | 479 | if !skip_blending { 480 | builder.pop_layer(); 481 | } 482 | } 483 | 484 | #[inline] 485 | fn draw_image(&mut self, image: &Self::Image, blend_mode: renderer::BlendMode, opacity: f32) { 486 | let image = &image.inner; 487 | let mix: Mix = to_vello_mix(blend_mode); 488 | 489 | let transform = self.last_transform().pre_translate(Vec2::new( 490 | image.width as f64 * -0.5, 491 | image.height as f64 * -0.5, 492 | )); 493 | let rect = Rect::new(0.0, 0.0, image.width as f64, image.height as f64); 494 | 495 | let builder = &mut self.builder; 496 | 497 | let skip_blending = mix == Mix::Normal && opacity == 1.0; 498 | 499 | if skip_blending { 500 | builder.push_layer(mix, opacity, transform, &rect); 501 | } 502 | 503 | builder.draw_image(image, transform); 504 | 505 | if skip_blending { 506 | builder.pop_layer(); 507 | } 508 | } 509 | 510 | #[inline] 511 | fn draw_image_mesh( 512 | &mut self, 513 | image: &Self::Image, 514 | vertices: &Self::Buffer, 515 | uvs: &Self::Buffer, 516 | indices: &Self::Buffer, 517 | blend_mode: renderer::BlendMode, 518 | opacity: f32, 519 | ) { 520 | let image = &image.inner; 521 | let vertices = vertices.as_f32_pairs(); 522 | let uvs = uvs.as_f32_pairs(); 523 | 524 | let mix: Mix = to_vello_mix(blend_mode); 525 | 526 | for triangle_indices in indices.as_u16s().chunks_exact(3) { 527 | let points = [ 528 | vertices[triangle_indices[0] as usize], 529 | vertices[triangle_indices[1] as usize], 530 | vertices[triangle_indices[2] as usize], 531 | ]; 532 | let uvs = [ 533 | uvs[triangle_indices[0] as usize], 534 | uvs[triangle_indices[1] as usize], 535 | uvs[triangle_indices[2] as usize], 536 | ]; 537 | 538 | let center = Point::new( 539 | ((points[0][0] + points[1][0] + points[2][0]) / 3.0) as f64, 540 | ((points[0][1] + points[1][1] + points[2][1]) / 3.0) as f64, 541 | ); 542 | 543 | let path = triangle_path(points.map(|v| Point::new(v[0] as f64, v[1] as f64))); 544 | 545 | let transform = self.last_transform().pre_scale_from_origin(1.03, center); 546 | let brush_transform = 547 | util::map_uvs_to_triangle(&points, &uvs, image.width, image.height); 548 | 549 | let builder = &mut self.builder; 550 | 551 | let skip_blending = mix == Mix::Normal; 552 | 553 | if !skip_blending { 554 | builder.push_layer(mix, opacity, transform, &path.bounding_box()); 555 | } 556 | 557 | builder.fill( 558 | Fill::NonZero, 559 | transform, 560 | BrushRef::Image(image), 561 | Some(brush_transform), 562 | &path, 563 | ); 564 | 565 | if !skip_blending { 566 | builder.pop_layer(); 567 | } 568 | } 569 | } 570 | } 571 | 572 | impl fmt::Debug for Renderer { 573 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 574 | f.debug_struct("Renderer") 575 | .field("transforms", &self.transforms) 576 | .field("clips", &self.clips) 577 | .finish() 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /rive-rs/src/vello/util.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Mul, Sub}; 2 | 3 | use vello::kurbo::{Affine, Point}; 4 | 5 | #[derive(Clone, Copy, Debug)] 6 | struct Vec2 { 7 | x: f32, 8 | y: f32, 9 | } 10 | 11 | impl Add for Vec2 { 12 | type Output = Self; 13 | 14 | fn add(self, rhs: Self) -> Self::Output { 15 | Self { 16 | x: self.x + rhs.x, 17 | y: self.y + rhs.y, 18 | } 19 | } 20 | } 21 | 22 | impl Sub for Vec2 { 23 | type Output = Self; 24 | 25 | fn sub(self, rhs: Self) -> Self::Output { 26 | Self { 27 | x: self.x - rhs.x, 28 | y: self.y - rhs.y, 29 | } 30 | } 31 | } 32 | 33 | impl Mul for Vec2 { 34 | type Output = Self; 35 | 36 | fn mul(self, rhs: f32) -> Self::Output { 37 | Self { 38 | x: self.x * rhs, 39 | y: self.y * rhs, 40 | } 41 | } 42 | } 43 | 44 | /// Finds the affine transform that maps triangle `from` to triangle `to`. The algorithm is based 45 | /// on the [Simplex Affine Mapping] method which has a [Swift implementation]. 46 | /// 47 | /// [Simplex Affine Mapping]: https://www.researchgate.net/publication/332410209_Beginner%27s_guide_to_mapping_simplexes_affinely 48 | /// [Swift implementation]: https://rethunk.medium.com/finding-an-affine-transform-using-three-2d-point-correspondences-using-simplex-affine-mapping-255aeb4e8055 49 | fn simplex_affine_mapping(from: [Vec2; 3], to: [Vec2; 3]) -> Affine { 50 | let [a, b, c] = from; 51 | let [d, e, f] = to; 52 | 53 | let det_recip = (a.x * b.y + b.x * c.y + c.x * a.y - a.x * c.y - b.x * a.y - c.x * b.y).recip(); 54 | 55 | let p = (d * (b.y - c.y) - e * (a.y - c.y) + f * (a.y - b.y)) * det_recip; 56 | 57 | let q = (e * (a.x - c.x) - d * (b.x - c.x) - f * (a.x - b.x)) * det_recip; 58 | 59 | let t = (d * (b.x * c.y - b.y * c.x) - e * (a.x * c.y - a.y * c.x) 60 | + f * (a.x * b.y - a.y * b.x)) 61 | * det_recip; 62 | 63 | Affine::new([ 64 | p.x as f64, p.y as f64, q.x as f64, q.y as f64, t.x as f64, t.y as f64, 65 | ]) 66 | } 67 | 68 | pub fn map_uvs_to_triangle( 69 | points: &[[f32; 2]; 3], 70 | uvs: &[[f32; 2]; 3], 71 | width: u32, 72 | height: u32, 73 | ) -> Affine { 74 | simplex_affine_mapping( 75 | uvs.map(|v| Vec2 { 76 | x: v[0] * width as f32, 77 | y: v[1] * height as f32, 78 | }), 79 | points.map(|v| Vec2 { x: v[0], y: v[1] }), 80 | ) 81 | } 82 | 83 | pub trait ScaleFromOrigin { 84 | fn pre_scale_from_origin(self, scale: f64, origin: Point) -> Self; 85 | } 86 | 87 | impl ScaleFromOrigin for Affine { 88 | fn pre_scale_from_origin(self, scale: f64, origin: Point) -> Self { 89 | let origin = origin.to_vec2(); 90 | self.pre_translate(origin) 91 | .pre_scale(scale) 92 | .pre_translate(-origin) 93 | } 94 | } 95 | --------------------------------------------------------------------------------