diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 5e5c27ffbc..7f8ca120d9 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -112,6 +112,29 @@ jobs: mkdir -p website/public mv website/other/dist/* website/public + - name: 💿 Obtain cache of auto-generated code docs artifacts + id: cache-website-code-docs + uses: actions/cache/restore@v3 + with: + path: artifacts + key: website-code-docs + + - name: 📁 Fallback in case auto-generated code docs artifacts weren't cached + if: steps.cache-website-code-docs.outputs.cache-hit != 'true' + run: | + echo "🦀 Initial system version of Rust:" + rustc --version + rustup update stable + echo "🦀 Latest updated version of Rust:" + rustc --version + cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree + mkdir artifacts + mv hierarchical_message_system_tree.txt artifacts/hierarchical_message_system_tree.txt + + - name: 🚚 Move `artifacts` contents to `website/public` + run: | + mv artifacts/* website/public + - name: 📤 Publish to Cloudflare Pages id: cloudflare uses: cloudflare/pages-action@1 diff --git a/Cargo.lock b/Cargo.lock index 9405b0b39b..cf65dd37b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "ab_glyph" -version = "0.2.30" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea" +checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19135c0c7a60bfee564dbe44ab5ce0557c6bf3884e5291a50be76a15640c4fbd" +checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" dependencies = [ "arrayvec", ] @@ -546,9 +546,9 @@ dependencies = [ [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" dependencies = [ "proc-macro2", "quote", @@ -666,7 +666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", - "toml", + "toml 0.8.23", ] [[package]] @@ -677,9 +677,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -775,9 +775,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" dependencies = [ "clap_builder", "clap_derive", @@ -785,9 +785,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" dependencies = [ "anstream", "anstyle", @@ -797,9 +797,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1009,9 +1009,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1394,14 +1394,14 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "embed-resource" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0963f530273dc3022ab2bdc3fcd6d488e850256f2284a82b7413cb9481ee85dd" +checksum = "4c6d81016d6c977deefb2ef8d8290da019e27cc26167e102185da528e6c0ab38" dependencies = [ "cc", "memchr", "rustc_version", - "toml", + "toml 0.9.2", "vswhom", "winreg", ] @@ -2284,7 +2284,7 @@ dependencies = [ "num-traits", "parley", "petgraph 0.7.1", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "rustc-hash 2.1.1", "serde", @@ -2305,7 +2305,7 @@ dependencies = [ "log", "math-parser", "node-macro", - "rand 0.9.1", + "rand 0.9.2", ] [[package]] @@ -2337,7 +2337,7 @@ dependencies = [ "image", "ndarray", "node-macro", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "serde", "specta", @@ -2366,7 +2366,7 @@ dependencies = [ "log", "ndarray", "node-macro", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "reqwest", "tokio", @@ -2730,9 +2730,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", @@ -2746,7 +2746,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -3079,6 +3079,17 @@ dependencies = [ "wgpu-executor", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -3314,11 +3325,12 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" dependencies = [ "arrayvec", + "euclid", "serde", "smallvec", ] @@ -3367,9 +3379,9 @@ checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libfuzzer-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", @@ -3403,13 +3415,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.13", + "redox_syscall 0.5.15", ] [[package]] @@ -3565,9 +3577,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" dependencies = [ "libc", ] @@ -4310,7 +4322,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.13", + "redox_syscall 0.5.15", "smallvec", "windows-targets 0.52.6", ] @@ -4597,13 +4609,13 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.2" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", "indexmap 2.10.0", - "quick-xml", + "quick-xml 0.38.0", "serde", "time", ] @@ -4651,17 +4663,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -4882,6 +4893,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.8" @@ -4895,7 +4915,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -4911,7 +4931,7 @@ dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash 2.1.1", "rustls", @@ -4932,7 +4952,7 @@ dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -4979,9 +4999,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -5181,9 +5201,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" dependencies = [ "bitflags 2.9.1", ] @@ -5323,9 +5343,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" dependencies = [ "bytemuck", ] @@ -5404,22 +5424,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ "once_cell", "ring", @@ -5441,9 +5461,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -5527,9 +5547,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "ref-cast", @@ -5679,9 +5699,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" dependencies = [ "itoa", "memchr", @@ -5719,6 +5739,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -5743,7 +5772,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.10.0", "schemars 0.9.0", - "schemars 1.0.3", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -5808,9 +5837,9 @@ dependencies = [ [[package]] name = "shared_child" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2778001df1384cf20b6dc5a5a90f48da35539885edaaefd887f8d744e939c0b" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" dependencies = [ "libc", "sigchld", @@ -5825,9 +5854,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sigchld" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1219ef50fc0fdb04fcc243e6aa27f855553434ffafe4fa26554efb78b5b4bf89" +checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" dependencies = [ "libc", "os_pipe", @@ -5977,6 +6006,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "softbuffer" version = "0.4.6" @@ -5993,7 +6032,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core 0.2.2", "raw-window-handle", - "redox_syscall 0.5.13", + "redox_syscall 0.5.15", "wasm-bindgen", "web-sys", "windows-sys 0.59.0", @@ -6262,7 +6301,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.23", "version-compare", ] @@ -6324,9 +6363,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.6.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d" +checksum = "352a4bc7bf6c25f5624227e3641adf475a6535707451b09bb83271df8b7a6ac7" dependencies = [ "anyhow", "bytes", @@ -6374,9 +6413,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83" +checksum = "182d688496c06bf08ea896459bf483eb29cdff35c1c4c115fb14053514303064" dependencies = [ "anyhow", "cargo_toml", @@ -6390,15 +6429,15 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml", + "toml 0.8.23", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406" +checksum = "b54a99a6cd8e01abcfa61508177e6096a4fe2681efecee9214e962f2f073ae4a" dependencies = [ "base64 0.22.1", "brotli", @@ -6423,9 +6462,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc" +checksum = "7945b14dc45e23532f2ded6e120170bbdd4af5ceaa45784a6b33d250fbce3f9e" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -6437,9 +6476,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3" +checksum = "5bd5c1e56990c70a906ef67a9851bbdba9136d26075ee9a2b19c8b46986b3e02" dependencies = [ "anyhow", "glob", @@ -6448,15 +6487,15 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml", + "toml 0.8.23", "walkdir", ] [[package]] name = "tauri-plugin-fs" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c341290d31991dbca38b31d412c73dfbdb070bb11536784f19dd2211d13b778f" +checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99" dependencies = [ "anyhow", "dunce", @@ -6470,15 +6509,15 @@ dependencies = [ "tauri-plugin", "tauri-utils", "thiserror 2.0.12", - "toml", + "toml 0.8.23", "url", ] [[package]] name = "tauri-plugin-http" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c1a38da944b357ffa23bafd563b1579f18e6fbd118fcd84769406d35dcc5c7" +checksum = "fcde333d97e565a7765aad82f32d8672458f7bd77b6ee653830d5dded9d7b5c2" dependencies = [ "bytes", "cookie_store", @@ -6521,9 +6560,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4" +checksum = "2b1cc885be806ea15ff7b0eb47098a7b16323d9228876afda329e34e2d6c4676" dependencies = [ "cookie", "dpi", @@ -6543,9 +6582,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad" +checksum = "fe653a2fbbef19fe898efc774bc52c8742576342a33d3d028c189b57eb1d2439" dependencies = [ "gtk", "http", @@ -6570,9 +6609,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e" +checksum = "9330c15cabfe1d9f213478c9e8ec2b0c76dab26bb6f314b8ad1c8a568c1d186e" dependencies = [ "anyhow", "brotli", @@ -6599,7 +6638,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.12", - "toml", + "toml 0.8.23", "url", "urlpattern", "uuid", @@ -6614,7 +6653,7 @@ checksum = "e8d321dbc6f998d825ab3f0d62673e810c861aac2d0de2cc2c395328f1d113b4" dependencies = [ "embed-resource", "indexmap 2.10.0", - "toml", + "toml 0.8.23", ] [[package]] @@ -6626,7 +6665,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -6804,16 +6843,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", - "socket2", + "slab", + "socket2 0.5.10", "tokio-macros", "windows-sys 0.52.0", ] @@ -6869,11 +6910,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit 0.22.27", ] +[[package]] +name = "toml" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +dependencies = [ + "indexmap 2.10.0", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow 0.7.12", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -6883,6 +6939,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -6890,7 +6955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.10.0", - "toml_datetime", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -6901,7 +6966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ "indexmap 2.10.0", - "toml_datetime", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -6913,10 +6978,19 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.10.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", - "winnow 0.7.11", + "winnow 0.7.12", +] + +[[package]] +name = "toml_parser" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +dependencies = [ + "winnow 0.7.12", ] [[package]] @@ -6925,6 +6999,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + [[package]] name = "tower" version = "0.5.2" @@ -7143,9 +7223,9 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "untrusted" @@ -7254,7 +7334,7 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vello" version = "0.5.0" -source = "git+https://github.com/linebender/vello.git#87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b" +source = "git+https://github.com/linebender/vello.git#daf940230a24cbb123a458b6de95721af47aef98" dependencies = [ "bytemuck", "futures-intrusive", @@ -7272,7 +7352,7 @@ dependencies = [ [[package]] name = "vello_encoding" version = "0.5.0" -source = "git+https://github.com/linebender/vello.git#87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b" +source = "git+https://github.com/linebender/vello.git#daf940230a24cbb123a458b6de95721af47aef98" dependencies = [ "bytemuck", "guillotiere", @@ -7284,7 +7364,7 @@ dependencies = [ [[package]] name = "vello_shaders" version = "0.5.0" -source = "git+https://github.com/linebender/vello.git#87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b" +source = "git+https://github.com/linebender/vello.git#daf940230a24cbb123a458b6de95721af47aef98" dependencies = [ "bytemuck", "log", @@ -7542,7 +7622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.37.5", "quote", ] @@ -7634,9 +7714,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -8436,9 +8516,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -8587,9 +8667,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "xmlwriter" @@ -8736,9 +8816,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7384255a918371b5af158218d131530f694de9ad3815ebdd0453a940485cb0fa" +checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" dependencies = [ "zune-core", ] diff --git a/editor/src/messages/portfolio/spreadsheet/spreadsheet_message_handler.rs b/editor/src/messages/portfolio/spreadsheet/spreadsheet_message_handler.rs index 52e8a76798..185347a82e 100644 --- a/editor/src/messages/portfolio/spreadsheet/spreadsheet_message_handler.rs +++ b/editor/src/messages/portfolio/spreadsheet/spreadsheet_message_handler.rs @@ -300,6 +300,8 @@ impl InstanceLayout for Instances { TextButton::new(instance.instance.identifier()) .on_update(move |_| SpreadsheetMessage::PushToInstancePath { index }.into()) .widget_holder(), + // Add the mask information here. We'll simply display "Yes" or "No". + TextLabel::new(if instance.mask.is_some() { "Yes" } else { "No" }.to_string()).widget_holder(), TextLabel::new(format!( "Location: ({} px, {} px) — Rotation: {rotation:2}° — Scale: ({}x, {}x)", round(translation.x), @@ -314,7 +316,8 @@ impl InstanceLayout for Instances { }) .collect::>(); - rows.insert(0, column_headings(&["", "instance", "transform", "alpha_blending", "source_node_id"])); + // Add "mask" to the column headings + rows.insert(0, column_headings(&["", "instance", "mask", "transform", "alpha_blending", "source_node_id"])); let instances = vec![TextLabel::new("Instances:").widget_holder()]; vec![LayoutGroup::Row { widgets: instances }, LayoutGroup::Table { rows }] diff --git a/node-graph/gcore/src/graphic_element.rs b/node-graph/gcore/src/graphic_element.rs index d238205308..c0e3198bcd 100644 --- a/node-graph/gcore/src/graphic_element.rs +++ b/node-graph/gcore/src/graphic_element.rs @@ -41,6 +41,7 @@ pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D) for (graphic_element, source_node_id) in old.elements { graphic_group_table.push(Instance { instance: graphic_element, + mask: None, transform: old.transform, alpha_blending: old.alpha_blending, source_node_id, @@ -56,6 +57,7 @@ pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D) for (graphic_element, source_node_id) in &instance.instance.elements { graphic_group_table.push(Instance { instance: graphic_element.clone(), + mask: None, transform: *instance.transform, alpha_blending: *instance.alpha_blending, source_node_id: *source_node_id, @@ -313,6 +315,7 @@ pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D) for (artboard, source_node_id) in artboard_group.artboards { table.push(Instance { instance: artboard, + mask: None, transform: DAffine2::IDENTITY, alpha_blending: AlphaBlending::default(), source_node_id, @@ -346,6 +349,7 @@ async fn layer( stack.push(Instance { instance: element, + mask: None, transform: DAffine2::IDENTITY, alpha_blending: AlphaBlending::default(), source_node_id, @@ -407,6 +411,7 @@ async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: boo _ => { output_group_table.push(Instance { instance: current_element, + mask: None, transform: *current_instance.transform, alpha_blending: *current_instance.alpha_blending, source_node_id: reference, @@ -445,6 +450,7 @@ async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTabl for current_element in vector_instance.instance_ref_iter() { output_group_table.push(Instance { instance: current_element.instance.clone(), + mask: None, transform: *current_instance.transform * *current_element.transform, alpha_blending: AlphaBlending { blend_mode: current_element.alpha_blending.blend_mode, @@ -513,6 +519,7 @@ pub async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboards.push(Instance { instance: artboard, + mask: None, transform: DAffine2::IDENTITY, alpha_blending: AlphaBlending::default(), source_node_id: encapsulating_node_id, diff --git a/node-graph/gcore/src/instances.rs b/node-graph/gcore/src/instances.rs index 4f0442edda..69b59280d2 100644 --- a/node-graph/gcore/src/instances.rs +++ b/node-graph/gcore/src/instances.rs @@ -1,14 +1,18 @@ -use crate::AlphaBlending; use crate::transform::ApplyTransform; use crate::uuid::NodeId; +use crate::{AlphaBlending, GraphicElement}; use dyn_any::StaticType; use glam::DAffine2; use std::hash::Hash; +pub type Mask = Option; + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct Instances { #[serde(alias = "instances")] instance: Vec, + #[serde(default = "one_mask_default")] + mask: Vec, #[serde(default = "one_daffine2_default")] transform: Vec, #[serde(default = "one_alpha_blending_default")] @@ -21,6 +25,7 @@ impl Instances { pub fn new(instance: T) -> Self { Self { instance: vec![instance], + mask: vec![None], transform: vec![DAffine2::IDENTITY], alpha_blending: vec![AlphaBlending::default()], source_node_id: vec![None], @@ -30,6 +35,7 @@ impl Instances { pub fn new_instance(instance: Instance) -> Self { Self { instance: vec![instance.instance], + mask: vec![instance.mask], transform: vec![instance.transform], alpha_blending: vec![instance.alpha_blending], source_node_id: vec![instance.source_node_id], @@ -39,6 +45,7 @@ impl Instances { pub fn with_capacity(capacity: usize) -> Self { Self { instance: Vec::with_capacity(capacity), + mask: Vec::with_capacity(capacity), transform: Vec::with_capacity(capacity), alpha_blending: Vec::with_capacity(capacity), source_node_id: Vec::with_capacity(capacity), @@ -47,6 +54,7 @@ impl Instances { pub fn push(&mut self, instance: Instance) { self.instance.push(instance.instance); + self.mask.push(instance.mask); self.transform.push(instance.transform); self.alpha_blending.push(instance.alpha_blending); self.source_node_id.push(instance.source_node_id); @@ -62,11 +70,13 @@ impl Instances { pub fn instance_iter(self) -> impl DoubleEndedIterator> { self.instance .into_iter() + .zip(self.mask) .zip(self.transform) .zip(self.alpha_blending) .zip(self.source_node_id) - .map(|(((instance, transform), alpha_blending), source_node_id)| Instance { + .map(|((((instance, mask), transform), alpha_blending), source_node_id)| Instance { instance, + mask, transform, alpha_blending, source_node_id, @@ -76,11 +86,13 @@ impl Instances { pub fn instance_ref_iter(&self) -> impl DoubleEndedIterator> + Clone { self.instance .iter() + .zip(self.mask.iter()) .zip(self.transform.iter()) .zip(self.alpha_blending.iter()) .zip(self.source_node_id.iter()) - .map(|(((instance, transform), alpha_blending), source_node_id)| InstanceRef { + .map(|((((instance, mask), transform), alpha_blending), source_node_id)| InstanceRef { instance, + mask, transform, alpha_blending, source_node_id, @@ -90,11 +102,13 @@ impl Instances { pub fn instance_mut_iter(&mut self) -> impl DoubleEndedIterator> { self.instance .iter_mut() + .zip(self.mask.iter_mut()) .zip(self.transform.iter_mut()) .zip(self.alpha_blending.iter_mut()) .zip(self.source_node_id.iter_mut()) - .map(|(((instance, transform), alpha_blending), source_node_id)| InstanceMut { + .map(|((((instance, mask), transform), alpha_blending), source_node_id)| InstanceMut { instance, + mask, transform, alpha_blending, source_node_id, @@ -108,6 +122,7 @@ impl Instances { Some(InstanceRef { instance: &self.instance[index], + mask: &self.mask[index], transform: &self.transform[index], alpha_blending: &self.alpha_blending[index], source_node_id: &self.source_node_id[index], @@ -121,6 +136,7 @@ impl Instances { Some(InstanceMut { instance: &mut self.instance[index], + mask: &mut self.mask[index], transform: &mut self.transform[index], alpha_blending: &mut self.alpha_blending[index], source_node_id: &mut self.source_node_id[index], @@ -140,6 +156,7 @@ impl Default for Instances { fn default() -> Self { Self { instance: Vec::new(), + mask: Vec::new(), transform: Vec::new(), alpha_blending: Vec::new(), source_node_id: Vec::new(), @@ -179,6 +196,9 @@ unsafe impl StaticType for Instances { type Static = Instances; } +fn one_mask_default() -> Vec { + vec![None] +} impl FromIterator> for Instances { fn from_iter>>(iter: I) -> Self { let iter = iter.into_iter(); @@ -204,6 +224,7 @@ fn one_source_node_id_default() -> Vec> { #[derive(Copy, Clone, Debug, PartialEq)] pub struct InstanceRef<'a, T> { pub instance: &'a T, + pub mask: &'a Mask, pub transform: &'a DAffine2, pub alpha_blending: &'a AlphaBlending, pub source_node_id: &'a Option, @@ -216,6 +237,7 @@ impl InstanceRef<'_, T> { { Instance { instance: self.instance.clone(), + mask: self.mask.clone(), transform: *self.transform, alpha_blending: *self.alpha_blending, source_node_id: *self.source_node_id, @@ -226,14 +248,16 @@ impl InstanceRef<'_, T> { #[derive(Debug)] pub struct InstanceMut<'a, T> { pub instance: &'a mut T, + pub mask: &'a mut Mask, pub transform: &'a mut DAffine2, pub alpha_blending: &'a mut AlphaBlending, pub source_node_id: &'a mut Option, } -#[derive(Copy, Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Instance { pub instance: T, + pub mask: Mask, pub transform: DAffine2, pub alpha_blending: AlphaBlending, pub source_node_id: Option, @@ -246,6 +270,7 @@ impl Instance { { Instance { instance: self.instance.into(), + mask: self.mask, transform: self.transform, alpha_blending: self.alpha_blending, source_node_id: self.source_node_id, @@ -255,6 +280,7 @@ impl Instance { pub fn to_instance_ref(&self) -> InstanceRef<'_, T> { InstanceRef { instance: &self.instance, + mask: &self.mask, transform: &self.transform, alpha_blending: &self.alpha_blending, source_node_id: &self.source_node_id, @@ -264,6 +290,7 @@ impl Instance { pub fn to_instance_mut(&mut self) -> InstanceMut<'_, T> { InstanceMut { instance: &mut self.instance, + mask: &mut self.mask, transform: &mut self.transform, alpha_blending: &mut self.alpha_blending, source_node_id: &mut self.source_node_id, @@ -273,6 +300,7 @@ impl Instance { pub fn to_table(self) -> Instances { Instances { instance: vec![self.instance], + mask: vec![self.mask], transform: vec![self.transform], alpha_blending: vec![self.alpha_blending], source_node_id: vec![self.source_node_id], diff --git a/node-graph/gcore/src/raster/image.rs b/node-graph/gcore/src/raster/image.rs index e93fe60ba2..6e0232825b 100644 --- a/node-graph/gcore/src/raster/image.rs +++ b/node-graph/gcore/src/raster/image.rs @@ -390,6 +390,7 @@ pub fn migrate_image_frame_instance<'de, D: serde::Deserializer<'de>>(deserializ }, FormatVersions::OldImageFrame(image_frame_with_transform_and_blending) => Instance { instance: Raster::new_cpu(image_frame_with_transform_and_blending.image), + mask: None, transform: image_frame_with_transform_and_blending.transform, alpha_blending: image_frame_with_transform_and_blending.alpha_blending, source_node_id: None, @@ -458,6 +459,7 @@ impl From> for Image { // for image_frame_instance in image_frame_table.instance_iter() { // result_table.push(Instance { // instance: image_frame_instance.instance, +// mask: image_frame_instance.mask, // transform: image_frame_instance.transform, // alpha_blending: image_frame_instance.alpha_blending, // source_node_id: image_frame_instance.source_node_id, diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index ca2349feed..d54406845d 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -543,6 +543,7 @@ async fn round_corners( transform: source_transform, alpha_blending: Default::default(), source_node_id: None, + mask: None, } }) .collect() @@ -777,6 +778,7 @@ async fn auto_tangents( Instance { instance: result, + mask: None, transform, alpha_blending, source_node_id, diff --git a/node-graph/gpath-bool/src/lib.rs b/node-graph/gpath-bool/src/lib.rs index 5a1b08b630..51a823bf65 100644 --- a/node-graph/gpath-bool/src/lib.rs +++ b/node-graph/gpath-bool/src/lib.rs @@ -195,6 +195,7 @@ fn difference<'a>(vector_data: impl DoubleEndedIterator( _: impl Ctx, /// The image to be masked. - image: RasterDataTable, + #[implementations( + VectorDataTable, + VectorDataTable, + VectorDataTable, + RasterDataTable, + RasterDataTable, + RasterDataTable, + GraphicGroupTable, + GraphicGroupTable, + GraphicGroupTable + )] + mut image: Instances, /// The stencil to be used for masking. #[expose] - stencil: RasterDataTable, -) -> RasterDataTable { + #[implementations( + VectorDataTable, + RasterDataTable, + GraphicGroupTable, + VectorDataTable, + RasterDataTable, + GraphicGroupTable, + VectorDataTable, + RasterDataTable, + GraphicGroupTable + )] + stencil: Instances, +) -> Instances +where + Instances: Into + Clone, +{ + for instance in image.instance_mut_iter() { + *instance.mask = Some(stencil.clone().into()); + } + image +} + +// TODO: Use as in-place raster modifier +fn _mask_lambda(image: RasterDataTable, stencil: RasterDataTable) -> RasterDataTable { // TODO: Support multiple stencil instances let Some(stencil_instance) = stencil.instance_iter().next() else { // No stencil provided so we return the original image diff --git a/node-graph/gsvg-renderer/src/renderer.rs b/node-graph/gsvg-renderer/src/renderer.rs index dd3a1ebb08..d50a9a6288 100644 --- a/node-graph/gsvg-renderer/src/renderer.rs +++ b/node-graph/gsvg-renderer/src/renderer.rs @@ -272,6 +272,10 @@ impl GraphicElementRendered for GraphicGroupTable { attributes.push(mask_type.to_attribute(), selector); } + + if let Some(mask) = instance.mask { + attributes.push_mask(mask, render_params.for_clipper()); + } }, |render| { instance.instance.render_svg(render, render_params); @@ -316,6 +320,20 @@ impl GraphicElementRendered for GraphicGroupTable { } } + let mut masked = false; + if let Some(mask) = instance.mask { + let bounds = mask.bounding_box(transform, true); + + if let Some(bounds) = bounds { + masked = true; + let rect = vello::kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y); + + scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); + mask.render_to_vello(scene, transform, context, &render_params.for_clipper()); + scene.push_layer(peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcIn), 1., kurbo::Affine::IDENTITY, &rect); + } + } + let next_clips = iter.peek().is_some_and(|next_instance| next_instance.instance.had_clip_enabled()); if next_clips && mask_instance_state.is_none() { mask_instance_state = Some((instance.instance, transform)); @@ -350,6 +368,11 @@ impl GraphicElementRendered for GraphicGroupTable { instance.instance.render_to_vello(scene, transform, context, render_params); } + if masked { + scene.pop_layer(); + scene.pop_layer(); + } + if layer { scene.pop_layer(); } @@ -451,6 +474,7 @@ impl GraphicElementRendered for VectorDataTable { let vector_row = VectorDataTable::new_instance(Instance { instance: fill_instance, + mask: None, alpha_blending: *instance.alpha_blending, transform: *instance.transform, source_node_id: None, @@ -499,6 +523,11 @@ impl GraphicElementRendered for VectorDataTable { let selector = format!("url(#{id})"); attributes.push(mask_type.to_attribute(), selector); } + + if let Some(mask) = instance.mask { + attributes.push_mask(mask, render_params.for_clipper()); + } + attributes.push_val(fill_and_stroke); let factor = if render_params.for_mask { 1. } else { instance.alpha_blending.fill }; @@ -555,6 +584,20 @@ impl GraphicElementRendered for VectorDataTable { ); } + let mut masked = false; + if let Some(mask) = instance.mask { + let bounds = mask.bounding_box(element_transform, true); + + if let Some(bounds) = bounds { + masked = true; + let rect = vello::kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y); + + scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); + mask.render_to_vello(scene, element_transform, _context, &render_params.for_clipper()); + scene.push_layer(peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcIn), 1., kurbo::Affine::IDENTITY, &rect); + } + } + let can_draw_aligned_stroke = instance.instance.style.stroke().is_some_and(|stroke| stroke.has_renderable_stroke() && stroke.align.is_not_centered()) && instance.instance.stroke_bezier_paths().all(|path| path.closed()); @@ -570,6 +613,7 @@ impl GraphicElementRendered for VectorDataTable { let vector_data = VectorDataTable::new_instance(Instance { instance: fill_instance, + mask: None, alpha_blending: *instance.alpha_blending, transform: *instance.transform, source_node_id: None, @@ -721,6 +765,11 @@ impl GraphicElementRendered for VectorDataTable { scene.pop_layer(); } + if masked { + scene.pop_layer(); + scene.pop_layer(); + } + // If we pushed a layer for opacity or a blend mode, we need to pop it if layer { scene.pop_layer(); @@ -959,29 +1008,40 @@ impl GraphicElementRendered for RasterDataTable { base64::engine::general_purpose::STANDARD.encode_string(output, &mut base64_string); base64_string }); - render.leaf_tag("image", |attributes| { - attributes.push("width", 1.to_string()); - attributes.push("height", 1.to_string()); - attributes.push("preserveAspectRatio", "none"); - attributes.push("href", base64_string); - let matrix = format_transform_matrix(transform); - if !matrix.is_empty() { - attributes.push("transform", matrix); - } - let factor = if render_params.for_mask { 1. } else { instance.alpha_blending.fill }; - let opacity = instance.alpha_blending.opacity * factor; - if opacity < 1. { - attributes.push("opacity", opacity.to_string()); - } - if instance.alpha_blending.blend_mode != BlendMode::default() { - attributes.push("style", instance.alpha_blending.blend_mode.render()); - } - }); + + let render_image = |render: &mut SvgRender| { + render.leaf_tag("image", |attributes| { + attributes.push("width", 1.to_string()); + attributes.push("height", 1.to_string()); + attributes.push("preserveAspectRatio", "none"); + attributes.push("href", base64_string); + let matrix = format_transform_matrix(transform); + if !matrix.is_empty() { + attributes.push("transform", matrix); + } + let factor = if render_params.for_mask { 1. } else { instance.alpha_blending.fill }; + let opacity = instance.alpha_blending.opacity * factor; + if opacity < 1. { + attributes.push("opacity", opacity.to_string()); + } + if instance.alpha_blending.blend_mode != BlendMode::default() { + attributes.push("style", instance.alpha_blending.blend_mode.render()); + } + }) + }; + + // Unlike path and g elements, the mask attribute of image element is broken and + // behaves differently across different browsers. And so we use g to have it work correctly. + if let Some(mask) = instance.mask { + render.parent_tag("g", |attributes| attributes.push_mask(mask, render_params.for_clipper()), render_image); + } else { + render_image(render) + } } } #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, _: &mut RenderContext, _render_params: &RenderParams) { + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) { use vello::peniko; for instance in self.instance_ref_iter() { @@ -992,7 +1052,26 @@ impl GraphicElementRendered for RasterDataTable { let image = peniko::Image::new(image.to_flat_u8().0.into(), peniko::ImageFormat::Rgba8, image.width, image.height).with_extend(peniko::Extend::Repeat); let transform = transform * *instance.transform * DAffine2::from_scale(1. / DVec2::new(image.width as f64, image.height as f64)); + let mut masked = false; + if let Some(mask) = instance.mask { + let bounds = mask.bounding_box(transform, true); + + if let Some(bounds) = bounds { + masked = true; + let rect = vello::kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y); + + scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); + mask.render_to_vello(scene, transform, _context, &render_params.for_clipper()); + scene.push_layer(peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcIn), 1., kurbo::Affine::IDENTITY, &rect); + } + } + scene.draw_image(&image, kurbo::Affine::new(transform.to_cols_array())); + + if masked { + scene.pop_layer(); + scene.pop_layer(); + } } } @@ -1275,4 +1354,17 @@ impl SvgRenderAttrs<'_> { pub fn push_val(&mut self, value: impl Into) { self.0.svg.push(value.into()); } + fn push_mask(&mut self, mask: &GraphicElement, render_params: RenderParams) { + let uuid = generate_uuid(); + let mut svg = SvgRender::new(); + mask.render_svg(&mut svg, &render_params); + + let id = format!("mask-{}", uuid); + write!(&mut self.0.svg_defs, r##"{}"##, svg.svg_defs).unwrap(); + write!(&mut self.0.svg_defs, r##"{}"##, svg.svg.to_svg_string()).unwrap(); + + let selector = format!("url(#{id})"); + + self.push("mask", selector); + } }