mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-10 20:51:29 +11:00
Merge pull request #190 from dfrg/api2
Remove piet API and replace with scene fragments
This commit is contained in:
commit
4edea5fbc9
451
Cargo.lock
generated
451
Cargo.lock
generated
|
@ -17,12 +17,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arrayref"
|
|
||||||
version = "0.3.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -86,9 +80,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.11.0"
|
version = "3.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
|
@ -193,15 +187,6 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cmake"
|
|
||||||
version = "0.1.48"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cocoa"
|
name = "cocoa"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
|
@ -213,7 +198,7 @@ dependencies = [
|
||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"foreign-types 0.3.2",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
@ -228,7 +213,7 @@ dependencies = [
|
||||||
"block",
|
"block",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types 0.3.2",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
@ -258,7 +243,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types 0.3.2",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -270,19 +255,7 @@ checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"foreign-types 0.3.2",
|
"foreign-types",
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-text"
|
|
||||||
version = "19.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25"
|
|
||||||
dependencies = [
|
|
||||||
"core-foundation",
|
|
||||||
"core-graphics",
|
|
||||||
"foreign-types 0.3.2",
|
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -295,29 +268,6 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossfont"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f66b1c1979c4362323f03ab6bf7fb522902bfc418e0c37319ab347f9561d980f"
|
|
||||||
dependencies = [
|
|
||||||
"cocoa",
|
|
||||||
"core-foundation",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"core-graphics",
|
|
||||||
"core-text",
|
|
||||||
"dwrote",
|
|
||||||
"foreign-types 0.5.0",
|
|
||||||
"freetype-rs",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"objc",
|
|
||||||
"once_cell",
|
|
||||||
"pkg-config",
|
|
||||||
"servo-fontconfig",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cty"
|
name = "cty"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -415,30 +365,6 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dwrote"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"winapi",
|
|
||||||
"wio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "expat-sys"
|
|
||||||
version = "2.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa"
|
|
||||||
dependencies = [
|
|
||||||
"cmake",
|
|
||||||
"pkg-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -470,28 +396,7 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"foreign-types-shared 0.1.1",
|
"foreign-types-shared",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
|
||||||
dependencies = [
|
|
||||||
"foreign-types-macros",
|
|
||||||
"foreign-types-shared 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types-macros"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -500,34 +405,6 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types-shared"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "freetype-rs"
|
|
||||||
version = "0.26.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"freetype-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "freetype-sys"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a"
|
|
||||||
dependencies = [
|
|
||||||
"cmake",
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
|
@ -610,9 +487,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.3"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni-sys"
|
name = "jni-sys"
|
||||||
|
@ -655,9 +532,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.133"
|
version = "0.2.135"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
|
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -697,12 +574,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matches"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -727,20 +598,6 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "metal"
|
|
||||||
version = "0.22.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"block",
|
|
||||||
"cocoa-foundation",
|
|
||||||
"foreign-types 0.3.2",
|
|
||||||
"log",
|
|
||||||
"objc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metal"
|
name = "metal"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
|
@ -750,7 +607,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"block",
|
"block",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types 0.3.2",
|
"foreign-types",
|
||||||
"log",
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
@ -779,7 +636,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys",
|
"windows-sys 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -979,15 +836,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-sys",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1002,41 +859,31 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"metal 0.22.0",
|
"metal",
|
||||||
"objc",
|
"objc",
|
||||||
"piet-gpu",
|
"piet-gpu",
|
||||||
"piet-gpu-hal",
|
"piet-gpu-hal",
|
||||||
"piet-scene",
|
"piet-scene",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "piet"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f00543608fb5ee6063f5ff1259246ae23073c1a5e413e643d0469da3d4b7b4de"
|
|
||||||
dependencies = [
|
|
||||||
"kurbo 0.7.1",
|
|
||||||
"unic-bidi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "piet-gpu"
|
name = "piet-gpu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"clap 3.2.22",
|
"clap 3.2.22",
|
||||||
|
"kurbo 0.8.3",
|
||||||
"ndk 0.3.0",
|
"ndk 0.3.0",
|
||||||
"ndk-glue 0.3.0",
|
"ndk-glue 0.3.0",
|
||||||
"ndk-sys 0.2.2",
|
"ndk-sys 0.2.2",
|
||||||
"piet",
|
|
||||||
"piet-gpu-hal",
|
"piet-gpu-hal",
|
||||||
"piet-gpu-types",
|
"piet-gpu-types",
|
||||||
|
"piet-scene",
|
||||||
"png",
|
"png",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"raw-window-handle 0.3.4",
|
"raw-window-handle 0.3.4",
|
||||||
"raw-window-handle 0.5.0",
|
"raw-window-handle 0.5.0",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"swash",
|
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1060,8 +907,8 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types 0.3.2",
|
"foreign-types",
|
||||||
"metal 0.24.0",
|
"metal",
|
||||||
"objc",
|
"objc",
|
||||||
"raw-window-handle 0.5.0",
|
"raw-window-handle 0.5.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
@ -1151,9 +998,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.43"
|
version = "1.0.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1311,15 +1158,6 @@ version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "safe_arch"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -1332,32 +1170,20 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sctk-adwaita"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "04b7c47a572f73de28bee5b5060d085b42b6ce1e4ee2b49c956ea7b25e94b6f0"
|
|
||||||
dependencies = [
|
|
||||||
"crossfont",
|
|
||||||
"log",
|
|
||||||
"smithay-client-toolkit",
|
|
||||||
"tiny-skia",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.144"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
|
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.144"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
|
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1366,36 +1192,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.85"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "servo-fontconfig"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"servo-fontconfig-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "servo-fontconfig-sys"
|
|
||||||
version = "5.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388"
|
|
||||||
dependencies = [
|
|
||||||
"expat-sys",
|
|
||||||
"freetype-sys",
|
|
||||||
"pkg-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slotmap"
|
name = "slotmap"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -1407,9 +1212,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.9.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay-client-toolkit"
|
name = "smithay-client-toolkit"
|
||||||
|
@ -1448,21 +1253,11 @@ version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "swash"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1bdb2004b76b5f5f3afe722d70b1aea160d2362cb7e40716a7106197ee7f82d"
|
|
||||||
dependencies = [
|
|
||||||
"yazi",
|
|
||||||
"zeno",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.100"
|
version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
|
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1509,49 +1304,24 @@ checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.35"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
|
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.35"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
|
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tiny-skia"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82"
|
|
||||||
dependencies = [
|
|
||||||
"arrayref",
|
|
||||||
"arrayvec 0.5.2",
|
|
||||||
"bytemuck",
|
|
||||||
"cfg-if",
|
|
||||||
"png",
|
|
||||||
"safe_arch",
|
|
||||||
"tiny-skia-path",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tiny-skia-path"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c"
|
|
||||||
dependencies = [
|
|
||||||
"arrayref",
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
|
@ -1561,62 +1331,11 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unic-bidi"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1356b759fb6a82050666f11dce4b6fe3571781f1449f3ef78074e408d468ec09"
|
|
||||||
dependencies = [
|
|
||||||
"matches",
|
|
||||||
"unic-ucd-bidi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unic-char-property"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
|
|
||||||
dependencies = [
|
|
||||||
"unic-char-range",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unic-char-range"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unic-common"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unic-ucd-bidi"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c"
|
|
||||||
dependencies = [
|
|
||||||
"unic-char-property",
|
|
||||||
"unic-char-range",
|
|
||||||
"unic-ucd-version",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unic-ucd-version"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
|
|
||||||
dependencies = [
|
|
||||||
"unic-common",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.4"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
|
@ -1828,37 +1547,88 @@ version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.36.1",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.36.1",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.36.1",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.36.1",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc 0.42.0",
|
||||||
|
"windows_i686_gnu 0.42.0",
|
||||||
|
"windows_i686_msvc 0.42.0",
|
||||||
|
"windows_x86_64_gnu 0.42.0",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
|
@ -1866,10 +1636,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winit"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.27.3"
|
version = "0.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a22e94ba35ca3ff11820044bfa0dc48b95a3a15569c0068555566a12ef41c9e5"
|
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winit"
|
||||||
|
version = "0.27.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37f64802920c4c35d12a53dad5e0c55bbc3004d8dc4f2e4dd64ad02c5665d7aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
|
@ -1888,13 +1664,12 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"raw-window-handle 0.4.3",
|
"raw-window-handle 0.4.3",
|
||||||
"raw-window-handle 0.5.0",
|
"raw-window-handle 0.5.0",
|
||||||
"sctk-adwaita",
|
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"windows-sys",
|
"windows-sys 0.36.1",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1935,18 +1710,6 @@ checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xmlparser"
|
name = "xmlparser"
|
||||||
version = "0.13.3"
|
version = "0.13.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8"
|
checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "yazi"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c03b3e19c937b5b9bd8e52b1c88f30cce5c0d33d676cf174866175bb794ff658"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zeno"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c110ba09c9b3a43edd4803d570df0da2414fed6e822e22b976a4e3ef50860701"
|
|
||||||
|
|
|
@ -14,9 +14,9 @@ piet-gpu-hal = { path = "../piet-gpu-hal" }
|
||||||
piet-scene = { path = "../piet-scene" }
|
piet-scene = { path = "../piet-scene" }
|
||||||
|
|
||||||
[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies]
|
[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies]
|
||||||
metal = "0.22"
|
metal = "0.24"
|
||||||
|
objc = "0.2.7"
|
||||||
cocoa = "0.24.0"
|
cocoa = "0.24.0"
|
||||||
objc = "0.2.5"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cbindgen = "0.20.0"
|
cbindgen = "0.20.0"
|
||||||
|
|
|
@ -26,9 +26,7 @@
|
||||||
|
|
||||||
mod render;
|
mod render;
|
||||||
|
|
||||||
use piet_scene::brush::{Brush, Color};
|
use piet_scene::{Brush, Color, Fill, PathElement};
|
||||||
use piet_scene::path::Element;
|
|
||||||
use piet_scene::scene::Fill;
|
|
||||||
use render::*;
|
use render::*;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
|
@ -199,7 +197,7 @@ pub struct PgpuTransform {
|
||||||
pub dy: f32,
|
pub dy: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PgpuTransform> for PgpuAffine {
|
impl From<PgpuTransform> for piet_scene::Affine {
|
||||||
fn from(xform: PgpuTransform) -> Self {
|
fn from(xform: PgpuTransform) -> Self {
|
||||||
Self {
|
Self {
|
||||||
xx: xform.xx,
|
xx: xform.xx,
|
||||||
|
@ -212,8 +210,6 @@ impl From<PgpuTransform> for PgpuAffine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PgpuAffine = piet_scene::geometry::Affine;
|
|
||||||
|
|
||||||
/// Creates a new builder for filling a piet-gpu scene. The specified scene
|
/// Creates a new builder for filling a piet-gpu scene. The specified scene
|
||||||
/// should not be accessed while the builder is live.
|
/// should not be accessed while the builder is live.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -243,7 +239,7 @@ pub unsafe extern "C" fn pgpu_scene_builder_add_glyph(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for PgpuPathIter {
|
impl Iterator for PgpuPathIter {
|
||||||
type Item = piet_scene::path::Element;
|
type Item = PathElement;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let mut el = PgpuPathElement {
|
let mut el = PgpuPathElement {
|
||||||
|
@ -253,17 +249,17 @@ impl Iterator for PgpuPathIter {
|
||||||
if (self.next_element)(self.context, &mut el as _) {
|
if (self.next_element)(self.context, &mut el as _) {
|
||||||
let p = &el.points;
|
let p = &el.points;
|
||||||
Some(match el.verb {
|
Some(match el.verb {
|
||||||
PgpuPathVerb::MoveTo => Element::MoveTo((p[0].x, p[0].y).into()),
|
PgpuPathVerb::MoveTo => PathElement::MoveTo((p[0].x, p[0].y).into()),
|
||||||
PgpuPathVerb::LineTo => Element::LineTo((p[0].x, p[0].y).into()),
|
PgpuPathVerb::LineTo => PathElement::LineTo((p[0].x, p[0].y).into()),
|
||||||
PgpuPathVerb::QuadTo => {
|
PgpuPathVerb::QuadTo => {
|
||||||
Element::QuadTo((p[0].x, p[0].y).into(), (p[1].x, p[1].y).into())
|
PathElement::QuadTo((p[0].x, p[0].y).into(), (p[1].x, p[1].y).into())
|
||||||
}
|
}
|
||||||
PgpuPathVerb::CurveTo => Element::CurveTo(
|
PgpuPathVerb::CurveTo => PathElement::CurveTo(
|
||||||
(p[0].x, p[0].y).into(),
|
(p[0].x, p[0].y).into(),
|
||||||
(p[1].x, p[1].y).into(),
|
(p[1].x, p[1].y).into(),
|
||||||
(p[2].x, p[2].y).into(),
|
(p[2].x, p[2].y).into(),
|
||||||
),
|
),
|
||||||
PgpuPathVerb::Close => Element::Close,
|
PgpuPathVerb::Close => PathElement::Close,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -277,8 +273,8 @@ pub unsafe extern "C" fn pgpu_scene_builder_transform(
|
||||||
builder: *mut PgpuSceneBuilder<'static>,
|
builder: *mut PgpuSceneBuilder<'static>,
|
||||||
transform: *const PgpuTransform,
|
transform: *const PgpuTransform,
|
||||||
) {
|
) {
|
||||||
if !transform.is_null() {
|
if let Some(transform) = transform.as_ref() {
|
||||||
(*builder).0.transform((*transform).into())
|
(*builder).transform = (*transform).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,9 +308,13 @@ pub unsafe extern "C" fn pgpu_scene_builder_fill_path(
|
||||||
} else {
|
} else {
|
||||||
Some((*brush_transform).into())
|
Some((*brush_transform).into())
|
||||||
};
|
};
|
||||||
(*builder)
|
(*builder).builder.fill(
|
||||||
.0
|
fill,
|
||||||
.fill(fill, &brush, brush_transform, (*path).clone());
|
(*builder).transform,
|
||||||
|
&brush,
|
||||||
|
brush_transform,
|
||||||
|
(*path).clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends a scene fragment to the underlying scene or fragment. The
|
/// Appends a scene fragment to the underlying scene or fragment. The
|
||||||
|
@ -333,7 +333,7 @@ pub unsafe extern "C" fn pgpu_scene_builder_append_fragment(
|
||||||
} else {
|
} else {
|
||||||
Some((*transform).into())
|
Some((*transform).into())
|
||||||
};
|
};
|
||||||
(*builder).0.append(&(*fragment).0, transform);
|
(*builder).builder.append(&(*fragment).0, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalizes the scene builder, making the underlying scene ready for
|
/// Finalizes the scene builder, making the underlying scene ready for
|
||||||
|
@ -445,7 +445,7 @@ pub unsafe extern "C" fn pgpu_glyph_bbox(
|
||||||
glyph: *const PgpuGlyph,
|
glyph: *const PgpuGlyph,
|
||||||
transform: &[f32; 6],
|
transform: &[f32; 6],
|
||||||
) -> PgpuRect {
|
) -> PgpuRect {
|
||||||
let transform = piet_scene::geometry::Affine::new(transform);
|
let transform = piet_scene::Affine::new(transform);
|
||||||
let rect = (*glyph).bbox(Some(transform));
|
let rect = (*glyph).bbox(Some(transform));
|
||||||
PgpuRect {
|
PgpuRect {
|
||||||
x0: rect.min.x,
|
x0: rect.min.x,
|
||||||
|
|
|
@ -14,13 +14,11 @@
|
||||||
//
|
//
|
||||||
// Also licensed under MIT license, at your choice.
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
use piet_gpu::{EncodedSceneRef, PixelFormat, RenderConfig};
|
use piet_gpu::{PixelFormat, RenderConfig};
|
||||||
use piet_gpu_hal::{QueryPool, Session};
|
use piet_gpu_hal::{QueryPool, Session};
|
||||||
use piet_scene::geometry::{Affine, Rect};
|
|
||||||
use piet_scene::glyph::pinot::{types::Tag, FontDataRef};
|
use piet_scene::glyph::pinot::{types::Tag, FontDataRef};
|
||||||
use piet_scene::glyph::{GlyphContext, GlyphProvider};
|
use piet_scene::glyph::{GlyphContext, GlyphProvider};
|
||||||
use piet_scene::resource::ResourceContext;
|
use piet_scene::{Affine, Rect, Scene, SceneFragment};
|
||||||
use piet_scene::scene::{Fragment, Scene};
|
|
||||||
|
|
||||||
/// State and resources for rendering a scene.
|
/// State and resources for rendering a scene.
|
||||||
pub struct PgpuRenderer {
|
pub struct PgpuRenderer {
|
||||||
|
@ -89,7 +87,7 @@ impl PgpuRenderer {
|
||||||
.session
|
.session
|
||||||
.image_from_raw_mtl(target, self.width, self.height);
|
.image_from_raw_mtl(target, self.width, self.height);
|
||||||
if let Some(renderer) = &mut self.pgpu_renderer {
|
if let Some(renderer) = &mut self.pgpu_renderer {
|
||||||
renderer.upload_scene(&scene.encoded_scene(), 0).unwrap();
|
renderer.upload_scene(&scene.0, 0).unwrap();
|
||||||
renderer.record(&mut cmd_buf, &self.query_pool, 0);
|
renderer.record(&mut cmd_buf, &self.query_pool, 0);
|
||||||
// TODO later: we can bind the destination image and avoid the copy.
|
// TODO later: we can bind the destination image and avoid the copy.
|
||||||
cmd_buf.blit_image(&renderer.image_dev, &dst_image);
|
cmd_buf.blit_image(&renderer.image_dev, &dst_image);
|
||||||
|
@ -105,67 +103,50 @@ impl PgpuRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encoded streams and resources describing a vector graphics scene.
|
/// Encoded streams and resources describing a vector graphics scene.
|
||||||
pub struct PgpuScene {
|
pub struct PgpuScene(pub Scene);
|
||||||
scene: Scene,
|
|
||||||
rcx: ResourceContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PgpuScene {
|
impl PgpuScene {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self(Scene::default())
|
||||||
scene: Scene::default(),
|
|
||||||
rcx: ResourceContext::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn builder(&mut self) -> PgpuSceneBuilder {
|
pub fn builder(&mut self) -> PgpuSceneBuilder {
|
||||||
self.rcx.advance();
|
PgpuSceneBuilder {
|
||||||
PgpuSceneBuilder(piet_scene::scene::build_scene(
|
builder: piet_scene::SceneBuilder::for_scene(&mut self.0),
|
||||||
&mut self.scene,
|
transform: Affine::IDENTITY,
|
||||||
&mut self.rcx,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encoded_scene<'a>(&'a self) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> {
|
|
||||||
let d = self.scene.data();
|
|
||||||
EncodedSceneRef {
|
|
||||||
transform_stream: &d.transform_stream,
|
|
||||||
tag_stream: &d.tag_stream,
|
|
||||||
pathseg_stream: &d.pathseg_stream,
|
|
||||||
linewidth_stream: &d.linewidth_stream,
|
|
||||||
drawtag_stream: &d.drawtag_stream,
|
|
||||||
drawdata_stream: &d.drawdata_stream,
|
|
||||||
n_path: d.n_path,
|
|
||||||
n_pathseg: d.n_pathseg,
|
|
||||||
n_clip: d.n_clip,
|
|
||||||
ramp_data: self.rcx.ramp_data(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encoded streams and resources describing a vector graphics scene fragment.
|
/// Encoded streams and resources describing a vector graphics scene fragment.
|
||||||
pub struct PgpuSceneFragment(pub Fragment);
|
pub struct PgpuSceneFragment(pub SceneFragment);
|
||||||
|
|
||||||
impl PgpuSceneFragment {
|
impl PgpuSceneFragment {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(Fragment::default())
|
Self(SceneFragment::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn builder(&mut self) -> PgpuSceneBuilder {
|
pub fn builder(&mut self) -> PgpuSceneBuilder {
|
||||||
PgpuSceneBuilder(piet_scene::scene::build_fragment(&mut self.0))
|
PgpuSceneBuilder {
|
||||||
|
builder: piet_scene::SceneBuilder::for_fragment(&mut self.0),
|
||||||
|
transform: Affine::IDENTITY,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for constructing an encoded scene.
|
/// Builder for constructing an encoded scene.
|
||||||
pub struct PgpuSceneBuilder<'a>(pub piet_scene::scene::Builder<'a>);
|
pub struct PgpuSceneBuilder<'a> {
|
||||||
|
pub builder: piet_scene::SceneBuilder<'a>,
|
||||||
|
pub transform: Affine,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> PgpuSceneBuilder<'a> {
|
impl<'a> PgpuSceneBuilder<'a> {
|
||||||
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) {
|
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::Affine) {
|
||||||
self.0.append(&glyph.fragment, Some(*transform));
|
self.builder.append(&glyph.fragment, Some(*transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(self) {
|
pub fn finish(self) {
|
||||||
self.0.finish();
|
self.builder.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +197,7 @@ pub struct PgpuGlyphProvider<'a>(GlyphProvider<'a>);
|
||||||
|
|
||||||
impl<'a> PgpuGlyphProvider<'a> {
|
impl<'a> PgpuGlyphProvider<'a> {
|
||||||
pub fn get(&mut self, gid: u16) -> Option<PgpuGlyph> {
|
pub fn get(&mut self, gid: u16) -> Option<PgpuGlyph> {
|
||||||
let fragment = self.0.get(gid)?;
|
let fragment = self.0.get(gid, None)?;
|
||||||
Some(PgpuGlyph { fragment })
|
Some(PgpuGlyph { fragment })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +209,7 @@ impl<'a> PgpuGlyphProvider<'a> {
|
||||||
|
|
||||||
/// Encoded (possibly color) outline for a glyph.
|
/// Encoded (possibly color) outline for a glyph.
|
||||||
pub struct PgpuGlyph {
|
pub struct PgpuGlyph {
|
||||||
fragment: Fragment,
|
fragment: SceneFragment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PgpuGlyph {
|
impl PgpuGlyph {
|
||||||
|
|
|
@ -331,7 +331,7 @@ impl crate::backend::Device for Dx12Device {
|
||||||
) -> Result<Self::Image, Error> {
|
) -> Result<Self::Image, Error> {
|
||||||
let format = match format {
|
let format = match format {
|
||||||
ImageFormat::A8 => winapi::shared::dxgiformat::DXGI_FORMAT_R8_UNORM,
|
ImageFormat::A8 => winapi::shared::dxgiformat::DXGI_FORMAT_R8_UNORM,
|
||||||
ImageFormat::Rgba8 => winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM,
|
ImageFormat::Rgba8 | ImageFormat::Surface => winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||||
};
|
};
|
||||||
let resource = self
|
let resource = self
|
||||||
.device
|
.device
|
||||||
|
|
|
@ -98,6 +98,8 @@ pub enum ImageFormat {
|
||||||
A8,
|
A8,
|
||||||
// 8 bit per pixel RGBA
|
// 8 bit per pixel RGBA
|
||||||
Rgba8,
|
Rgba8,
|
||||||
|
// Format that matches the target surface
|
||||||
|
Surface,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
|
|
@ -351,7 +351,8 @@ impl crate::backend::Device for MtlDevice {
|
||||||
//desc.set_mipmap_level_count(1);
|
//desc.set_mipmap_level_count(1);
|
||||||
let mtl_format = match format {
|
let mtl_format = match format {
|
||||||
ImageFormat::A8 => metal::MTLPixelFormat::R8Unorm,
|
ImageFormat::A8 => metal::MTLPixelFormat::R8Unorm,
|
||||||
ImageFormat::Rgba8 => metal::MTLPixelFormat::BGRA8Unorm,
|
ImageFormat::Rgba8 => metal::MTLPixelFormat::RGBA8Unorm,
|
||||||
|
ImageFormat::Surface => metal::MTLPixelFormat::BGRA8Unorm,
|
||||||
};
|
};
|
||||||
desc.set_pixel_format(mtl_format);
|
desc.set_pixel_format(mtl_format);
|
||||||
desc.set_usage(metal::MTLTextureUsage::ShaderRead | metal::MTLTextureUsage::ShaderWrite);
|
desc.set_usage(metal::MTLTextureUsage::ShaderRead | metal::MTLTextureUsage::ShaderWrite);
|
||||||
|
|
|
@ -588,7 +588,7 @@ impl crate::backend::Device for VkDevice {
|
||||||
| vk::ImageUsageFlags::TRANSFER_DST;
|
| vk::ImageUsageFlags::TRANSFER_DST;
|
||||||
let vk_format = match format {
|
let vk_format = match format {
|
||||||
ImageFormat::A8 => vk::Format::R8_UNORM,
|
ImageFormat::A8 => vk::Format::R8_UNORM,
|
||||||
ImageFormat::Rgba8 => vk::Format::R8G8B8A8_UNORM,
|
ImageFormat::Rgba8 | ImageFormat::Surface => vk::Format::R8G8B8A8_UNORM,
|
||||||
};
|
};
|
||||||
let image = device.create_image(
|
let image = device.create_image(
|
||||||
&vk::ImageCreateInfo::builder()
|
&vk::ImageCreateInfo::builder()
|
||||||
|
|
|
@ -26,16 +26,19 @@ path = "../piet-gpu-hal"
|
||||||
[dependencies.piet-gpu-types]
|
[dependencies.piet-gpu-types]
|
||||||
path = "../piet-gpu-types"
|
path = "../piet-gpu-types"
|
||||||
|
|
||||||
|
[dependencies.piet-scene]
|
||||||
|
path = "../piet-scene"
|
||||||
|
features = ["kurbo"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
piet = "0.2.0"
|
|
||||||
png = "0.17.6"
|
png = "0.17.6"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
roxmltree = "0.13"
|
roxmltree = "0.13"
|
||||||
winit = {version = "0.27.3", default-features = false, features = ["x11", "wayland", "wayland-dlopen"]}
|
winit = {version = "0.27.3", default-features = false, features = ["x11", "wayland", "wayland-dlopen"]}
|
||||||
raw-window-handle = "0.5"
|
raw-window-handle = "0.5"
|
||||||
clap = "3.2.22"
|
clap = "3.2.22"
|
||||||
swash = "0.1.4"
|
|
||||||
bytemuck = { version = "1.7.2", features = ["derive"] }
|
bytemuck = { version = "1.7.2", features = ["derive"] }
|
||||||
|
kurbo = "0.8.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
ndk = "0.3"
|
ndk = "0.3"
|
||||||
|
|
|
@ -16,10 +16,8 @@ use piet_gpu_hal::{
|
||||||
Error, ImageLayout, Instance, InstanceFlags, Semaphore, Session, Surface, Swapchain,
|
Error, ImageLayout, Instance, InstanceFlags, Semaphore, Session, Surface, Swapchain,
|
||||||
};
|
};
|
||||||
|
|
||||||
use piet::kurbo::Point;
|
use piet_gpu::{samples, RenderDriver, Renderer, SimpleText};
|
||||||
use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder};
|
use piet_scene::{Scene, SceneBuilder};
|
||||||
|
|
||||||
use piet_gpu::{test_scenes, PietGpuRenderContext, RenderDriver, Renderer};
|
|
||||||
|
|
||||||
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
|
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -115,14 +113,14 @@ impl GfxState {
|
||||||
info_string = stats.short_summary();
|
info_string = stats.short_summary();
|
||||||
println!("{}", info_string);
|
println!("{}", info_string);
|
||||||
}
|
}
|
||||||
let mut ctx = PietGpuRenderContext::new();
|
let mut text = SimpleText::new();
|
||||||
test_scenes::render_anim_frame(&mut ctx, self.current_frame);
|
let mut scene = Scene::default();
|
||||||
//test_scenes::render_tiger(&mut ctx);
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||||
render_info_string(&mut ctx, &info_string);
|
samples::render_anim_frame(&mut builder, self.current_frame);
|
||||||
if let Err(e) = self
|
//samples::render_tiger(&mut builder, false);
|
||||||
.render_driver
|
render_info(&mut text, &mut builder, &info_string);
|
||||||
.upload_render_ctx(&self.session, &mut ctx)
|
builder.finish();
|
||||||
{
|
if let Err(e) = self.render_driver.upload_scene(&self.session, &scene) {
|
||||||
println!("error in uploading: {}", e);
|
println!("error in uploading: {}", e);
|
||||||
}
|
}
|
||||||
let (image_idx, acquisition_semaphore) = self.swapchain.next().unwrap();
|
let (image_idx, acquisition_semaphore) = self.swapchain.next().unwrap();
|
||||||
|
@ -154,12 +152,13 @@ impl GfxState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_info_string(rc: &mut impl RenderContext, info: &str) {
|
fn render_info(simple_text: &mut SimpleText, sb: &mut SceneBuilder, info: &str) {
|
||||||
let layout = rc
|
simple_text.add(
|
||||||
.text()
|
sb,
|
||||||
.new_text_layout(info.to_string())
|
None,
|
||||||
.default_attribute(TextAttribute::FontSize(60.0))
|
60.0,
|
||||||
.build()
|
None,
|
||||||
.unwrap();
|
piet_scene::Affine::translate(110.0, 120.0),
|
||||||
rc.draw_text(&layout, Point::new(110.0, 120.0));
|
info,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ use clap::{App, Arg};
|
||||||
|
|
||||||
use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session};
|
use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session};
|
||||||
|
|
||||||
use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, RenderDriver, Renderer};
|
use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer};
|
||||||
|
use piet_scene::{Scene, SceneBuilder};
|
||||||
|
|
||||||
const WIDTH: usize = 2048;
|
const WIDTH: usize = 2048;
|
||||||
const HEIGHT: usize = 1536;
|
const HEIGHT: usize = 1536;
|
||||||
|
@ -227,11 +228,11 @@ fn main() -> Result<(), Error> {
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
let instance = Instance::new(InstanceFlags::default())?;
|
let instance = Instance::new(InstanceFlags::default())?;
|
||||||
|
let mut scene = Scene::default();
|
||||||
unsafe {
|
unsafe {
|
||||||
let device = instance.device()?;
|
let device = instance.device()?;
|
||||||
let session = Session::new(device);
|
let session = Session::new(device);
|
||||||
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||||
let mut ctx = PietGpuRenderContext::new();
|
|
||||||
if let Some(input) = matches.value_of("INPUT") {
|
if let Some(input) = matches.value_of("INPUT") {
|
||||||
let mut scale = matches
|
let mut scale = matches
|
||||||
.value_of("scale")
|
.value_of("scale")
|
||||||
|
@ -244,16 +245,17 @@ fn main() -> Result<(), Error> {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
let svg = PicoSvg::load(&xml_str, scale).unwrap();
|
let svg = PicoSvg::load(&xml_str, scale).unwrap();
|
||||||
println!("parsing time: {:?}", start.elapsed());
|
println!("parsing time: {:?}", start.elapsed());
|
||||||
test_scenes::render_svg(&mut ctx, &svg);
|
samples::render_svg(&mut builder, &svg, true);
|
||||||
} else {
|
} else {
|
||||||
//test_scenes::render_scene(&mut ctx);
|
//test_scenes::render_scene(&mut ctx);
|
||||||
test_scenes::render_blend_grid(&mut ctx);
|
samples::render_blend_grid(&mut builder);
|
||||||
}
|
}
|
||||||
|
builder.finish();
|
||||||
|
|
||||||
let renderer = Renderer::new(&session, WIDTH, HEIGHT, 1)?;
|
let renderer = Renderer::new(&session, WIDTH, HEIGHT, 1)?;
|
||||||
let mut render_driver = RenderDriver::new(&session, 1, renderer);
|
let mut render_driver = RenderDriver::new(&session, 1, renderer);
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
render_driver.upload_render_ctx(&session, &mut ctx)?;
|
render_driver.upload_scene(&session, &scene)?;
|
||||||
let image_usage = BufferUsage::MAP_READ | BufferUsage::COPY_DST;
|
let image_usage = BufferUsage::MAP_READ | BufferUsage::COPY_DST;
|
||||||
let image_buf = session.create_buffer((WIDTH * HEIGHT * 4) as u64, image_usage)?;
|
let image_buf = session.create_buffer((WIDTH * HEIGHT * 4) as u64, image_usage)?;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use piet::kurbo::Point;
|
use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer, SimpleText};
|
||||||
use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder};
|
|
||||||
use piet_gpu_hal::{Error, ImageLayout, Instance, InstanceFlags, Session};
|
use piet_gpu_hal::{Error, ImageLayout, Instance, InstanceFlags, Session};
|
||||||
|
use piet_scene::{Scene, SceneBuilder};
|
||||||
use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, RenderDriver, Renderer};
|
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
@ -61,6 +59,8 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
let instance = Instance::new(InstanceFlags::default())?;
|
let instance = Instance::new(InstanceFlags::default())?;
|
||||||
let mut info_string = "info".to_string();
|
let mut info_string = "info".to_string();
|
||||||
|
let mut scene = Scene::default();
|
||||||
|
let mut simple_text = piet_gpu::SimpleText::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
let display_handle = window.raw_display_handle();
|
let display_handle = window.raw_display_handle();
|
||||||
let window_handle = window.raw_window_handle();
|
let window_handle = window.raw_window_handle();
|
||||||
|
@ -76,7 +76,7 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
let renderer = Renderer::new(&session, WIDTH, HEIGHT, NUM_FRAMES)?;
|
let renderer = Renderer::new(&session, WIDTH, HEIGHT, NUM_FRAMES)?;
|
||||||
let mut render_driver = RenderDriver::new(&session, NUM_FRAMES, renderer);
|
let mut render_driver = RenderDriver::new(&session, NUM_FRAMES, renderer);
|
||||||
let mut mode = 0usize;
|
let mut sample_index = 0usize;
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
*control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event
|
*control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event
|
||||||
|
@ -91,8 +91,12 @@ fn main() -> Result<(), Error> {
|
||||||
WindowEvent::KeyboardInput { input, .. } => {
|
WindowEvent::KeyboardInput { input, .. } => {
|
||||||
if input.state == ElementState::Pressed {
|
if input.state == ElementState::Pressed {
|
||||||
match input.virtual_keycode {
|
match input.virtual_keycode {
|
||||||
Some(VirtualKeyCode::Left) => mode = mode.wrapping_sub(1),
|
Some(VirtualKeyCode::Left) => {
|
||||||
Some(VirtualKeyCode::Right) => mode = mode.wrapping_add(1),
|
sample_index = sample_index.saturating_sub(1)
|
||||||
|
}
|
||||||
|
Some(VirtualKeyCode::Right) => {
|
||||||
|
sample_index = sample_index.saturating_add(1)
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,53 +115,35 @@ fn main() -> Result<(), Error> {
|
||||||
info_string = stats.short_summary();
|
info_string = stats.short_summary();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ctx = PietGpuRenderContext::new();
|
|
||||||
let test_blend = false;
|
|
||||||
if let Some(svg) = &svg {
|
if let Some(svg) = &svg {
|
||||||
test_scenes::render_svg(&mut ctx, svg);
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||||
} else if test_blend {
|
samples::render_svg(&mut builder, svg, false);
|
||||||
use piet_gpu::{Blend, BlendMode::*, CompositionMode::*};
|
render_info(&mut simple_text, &mut builder, &info_string);
|
||||||
let blends = [
|
builder.finish();
|
||||||
Blend::new(Normal, SrcOver),
|
if let Err(e) = render_driver.upload_scene(&session, &scene) {
|
||||||
Blend::new(Multiply, SrcOver),
|
|
||||||
Blend::new(Screen, SrcOver),
|
|
||||||
Blend::new(Overlay, SrcOver),
|
|
||||||
Blend::new(Darken, SrcOver),
|
|
||||||
Blend::new(Lighten, SrcOver),
|
|
||||||
Blend::new(ColorDodge, SrcOver),
|
|
||||||
Blend::new(ColorBurn, SrcOver),
|
|
||||||
Blend::new(HardLight, SrcOver),
|
|
||||||
Blend::new(SoftLight, SrcOver),
|
|
||||||
Blend::new(Difference, SrcOver),
|
|
||||||
Blend::new(Exclusion, SrcOver),
|
|
||||||
Blend::new(Hue, SrcOver),
|
|
||||||
Blend::new(Saturation, SrcOver),
|
|
||||||
Blend::new(Color, SrcOver),
|
|
||||||
Blend::new(Luminosity, SrcOver),
|
|
||||||
Blend::new(Normal, Clear),
|
|
||||||
Blend::new(Normal, Copy),
|
|
||||||
Blend::new(Normal, Dest),
|
|
||||||
Blend::new(Normal, SrcOver),
|
|
||||||
Blend::new(Normal, DestOver),
|
|
||||||
Blend::new(Normal, SrcIn),
|
|
||||||
Blend::new(Normal, DestIn),
|
|
||||||
Blend::new(Normal, SrcOut),
|
|
||||||
Blend::new(Normal, DestOut),
|
|
||||||
Blend::new(Normal, SrcAtop),
|
|
||||||
Blend::new(Normal, DestAtop),
|
|
||||||
Blend::new(Normal, Xor),
|
|
||||||
Blend::new(Normal, Plus),
|
|
||||||
];
|
|
||||||
let blend = blends[mode % blends.len()];
|
|
||||||
test_scenes::render_blend_test(&mut ctx, current_frame, blend);
|
|
||||||
info_string = format!("{:?}", blend);
|
|
||||||
} else {
|
|
||||||
test_scenes::render_anim_frame(&mut ctx, current_frame);
|
|
||||||
}
|
|
||||||
render_info_string(&mut ctx, &info_string);
|
|
||||||
if let Err(e) = render_driver.upload_render_ctx(&session, &mut ctx) {
|
|
||||||
println!("error in uploading: {}", e);
|
println!("error in uploading: {}", e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||||
|
|
||||||
|
const N_SAMPLES: usize = 5;
|
||||||
|
match sample_index % N_SAMPLES {
|
||||||
|
0 => samples::render_anim_frame(
|
||||||
|
&mut builder,
|
||||||
|
&mut simple_text,
|
||||||
|
current_frame,
|
||||||
|
),
|
||||||
|
1 => samples::render_blend_grid(&mut builder),
|
||||||
|
2 => samples::render_tiger(&mut builder, false),
|
||||||
|
3 => samples::render_brush_transform(&mut builder, current_frame),
|
||||||
|
_ => samples::render_scene(&mut builder),
|
||||||
|
}
|
||||||
|
render_info(&mut simple_text, &mut builder, &info_string);
|
||||||
|
builder.finish();
|
||||||
|
if let Err(e) = render_driver.upload_scene(&session, &scene) {
|
||||||
|
println!("error in uploading: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (image_idx, acquisition_semaphore) = swapchain.next().unwrap();
|
let (image_idx, acquisition_semaphore) = swapchain.next().unwrap();
|
||||||
let swap_image = swapchain.image(image_idx);
|
let swap_image = swapchain.image(image_idx);
|
||||||
|
@ -197,12 +183,13 @@ fn main() -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_info_string(rc: &mut impl RenderContext, info: &str) {
|
fn render_info(simple_text: &mut SimpleText, sb: &mut SceneBuilder, info: &str) {
|
||||||
let layout = rc
|
simple_text.add(
|
||||||
.text()
|
sb,
|
||||||
.new_text_layout(info.to_string())
|
None,
|
||||||
.default_attribute(TextAttribute::FontSize(40.0))
|
40.0,
|
||||||
.build()
|
None,
|
||||||
.unwrap();
|
piet_scene::Affine::translate(110.0, 50.0),
|
||||||
rc.draw_text(&layout, Point::new(110.0, 50.0));
|
info,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
// Copyright 2022 The piet-gpu authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Also licensed under MIT license, at your choice.
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub enum BlendMode {
|
|
||||||
Normal = 0,
|
|
||||||
Multiply = 1,
|
|
||||||
Screen = 2,
|
|
||||||
Overlay = 3,
|
|
||||||
Darken = 4,
|
|
||||||
Lighten = 5,
|
|
||||||
ColorDodge = 6,
|
|
||||||
ColorBurn = 7,
|
|
||||||
HardLight = 8,
|
|
||||||
SoftLight = 9,
|
|
||||||
Difference = 10,
|
|
||||||
Exclusion = 11,
|
|
||||||
Hue = 12,
|
|
||||||
Saturation = 13,
|
|
||||||
Color = 14,
|
|
||||||
Luminosity = 15,
|
|
||||||
// Clip is the same as normal, but doesn't always push a blend group.
|
|
||||||
Clip = 128,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub enum CompositionMode {
|
|
||||||
Clear = 0,
|
|
||||||
Copy = 1,
|
|
||||||
Dest = 2,
|
|
||||||
SrcOver = 3,
|
|
||||||
DestOver = 4,
|
|
||||||
SrcIn = 5,
|
|
||||||
DestIn = 6,
|
|
||||||
SrcOut = 7,
|
|
||||||
DestOut = 8,
|
|
||||||
SrcAtop = 9,
|
|
||||||
DestAtop = 10,
|
|
||||||
Xor = 11,
|
|
||||||
Plus = 12,
|
|
||||||
PlusLighter = 13,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub struct Blend {
|
|
||||||
pub mode: BlendMode,
|
|
||||||
pub composition_mode: CompositionMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Blend {
|
|
||||||
pub fn new(mode: BlendMode, composition_mode: CompositionMode) -> Self {
|
|
||||||
Self {
|
|
||||||
mode,
|
|
||||||
composition_mode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn pack(&self) -> u32 {
|
|
||||||
(self.mode as u32) << 8 | self.composition_mode as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Blend {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
mode: BlendMode::Clip,
|
|
||||||
composition_mode: CompositionMode::SrcOver,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BlendMode> for Blend {
|
|
||||||
fn from(mode: BlendMode) -> Self {
|
|
||||||
Self {
|
|
||||||
mode,
|
|
||||||
composition_mode: CompositionMode::SrcOver,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CompositionMode> for Blend {
|
|
||||||
fn from(mode: CompositionMode) -> Self {
|
|
||||||
Self {
|
|
||||||
mode: BlendMode::Normal,
|
|
||||||
composition_mode: mode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,301 +0,0 @@
|
||||||
// Copyright 2021 The piet-gpu authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Also licensed under MIT license, at your choice.
|
|
||||||
|
|
||||||
//! Low-level scene encoding.
|
|
||||||
|
|
||||||
use crate::{Blend, SceneStats, DRAWTAG_SIZE, TRANSFORM_SIZE};
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
|
||||||
use piet_gpu_hal::BufWrite;
|
|
||||||
|
|
||||||
use crate::stages::{self, PathEncoder, Transform, DRAW_PART_SIZE, PATHSEG_PART_SIZE};
|
|
||||||
|
|
||||||
pub struct Encoder {
|
|
||||||
transform_stream: Vec<stages::Transform>,
|
|
||||||
tag_stream: Vec<u8>,
|
|
||||||
pathseg_stream: Vec<u8>,
|
|
||||||
linewidth_stream: Vec<f32>,
|
|
||||||
drawtag_stream: Vec<u32>,
|
|
||||||
drawdata_stream: Vec<u8>,
|
|
||||||
n_path: u32,
|
|
||||||
n_pathseg: u32,
|
|
||||||
n_clip: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct EncodedSceneRef<'a, T: Copy + Pod> {
|
|
||||||
pub transform_stream: &'a [T],
|
|
||||||
pub tag_stream: &'a [u8],
|
|
||||||
pub pathseg_stream: &'a [u8],
|
|
||||||
pub linewidth_stream: &'a [f32],
|
|
||||||
pub drawtag_stream: &'a [u32],
|
|
||||||
pub drawdata_stream: &'a [u8],
|
|
||||||
pub n_path: u32,
|
|
||||||
pub n_pathseg: u32,
|
|
||||||
pub n_clip: u32,
|
|
||||||
pub ramp_data: &'a [u32],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Copy + Pod> EncodedSceneRef<'a, T> {
|
|
||||||
pub(crate) fn stats(&self) -> SceneStats {
|
|
||||||
SceneStats {
|
|
||||||
n_drawobj: self.drawtag_stream.len(),
|
|
||||||
drawdata_len: self.drawdata_stream.len(),
|
|
||||||
n_transform: self.transform_stream.len(),
|
|
||||||
linewidth_len: std::mem::size_of_val(self.linewidth_stream),
|
|
||||||
pathseg_len: self.pathseg_stream.len(),
|
|
||||||
n_pathtag: self.tag_stream.len(),
|
|
||||||
|
|
||||||
n_path: self.n_path,
|
|
||||||
n_pathseg: self.n_pathseg,
|
|
||||||
n_clip: self.n_clip,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_scene(&self, buf: &mut BufWrite) {
|
|
||||||
buf.extend_slice(&self.drawtag_stream);
|
|
||||||
let n_drawobj = self.drawtag_stream.len();
|
|
||||||
buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE);
|
|
||||||
buf.extend_slice(&self.drawdata_stream);
|
|
||||||
buf.extend_slice(&self.transform_stream);
|
|
||||||
buf.extend_slice(&self.linewidth_stream);
|
|
||||||
buf.extend_slice(&self.tag_stream);
|
|
||||||
let n_pathtag = self.tag_stream.len();
|
|
||||||
buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize));
|
|
||||||
buf.extend_slice(&self.pathseg_stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A scene fragment encoding a glyph.
|
|
||||||
///
|
|
||||||
/// This is a reduced version of the full encoder.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct GlyphEncoder {
|
|
||||||
tag_stream: Vec<u8>,
|
|
||||||
pathseg_stream: Vec<u8>,
|
|
||||||
drawtag_stream: Vec<u32>,
|
|
||||||
drawdata_stream: Vec<u8>,
|
|
||||||
n_path: u32,
|
|
||||||
n_pathseg: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tags for draw objects. See shader/drawtag.h for the authoritative source.
|
|
||||||
const DRAWTAG_FILLCOLOR: u32 = 0x44;
|
|
||||||
const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;
|
|
||||||
const DRAWTAG_FILLRADGRADIENT: u32 = 0x2dc;
|
|
||||||
const DRAWTAG_BEGINCLIP: u32 = 0x05;
|
|
||||||
const DRAWTAG_ENDCLIP: u32 = 0x25;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
|
||||||
pub struct FillColor {
|
|
||||||
rgba_color: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
|
||||||
pub struct FillLinGradient {
|
|
||||||
index: u32,
|
|
||||||
p0: [f32; 2],
|
|
||||||
p1: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
|
||||||
pub struct FillRadGradient {
|
|
||||||
index: u32,
|
|
||||||
p0: [f32; 2],
|
|
||||||
p1: [f32; 2],
|
|
||||||
r0: f32,
|
|
||||||
r1: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
|
||||||
pub struct FillImage {
|
|
||||||
index: u32,
|
|
||||||
// [i16; 2]
|
|
||||||
offset: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
|
||||||
pub struct Clip {
|
|
||||||
blend: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encoder {
|
|
||||||
pub fn new() -> Encoder {
|
|
||||||
Encoder {
|
|
||||||
transform_stream: vec![Transform::IDENTITY],
|
|
||||||
tag_stream: Vec::new(),
|
|
||||||
pathseg_stream: Vec::new(),
|
|
||||||
linewidth_stream: vec![-1.0],
|
|
||||||
drawtag_stream: Vec::new(),
|
|
||||||
drawdata_stream: Vec::new(),
|
|
||||||
n_path: 0,
|
|
||||||
n_pathseg: 0,
|
|
||||||
n_clip: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path_encoder(&mut self) -> PathEncoder {
|
|
||||||
PathEncoder::new(&mut self.tag_stream, &mut self.pathseg_stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish_path(&mut self, n_pathseg: u32) {
|
|
||||||
self.n_path += 1;
|
|
||||||
self.n_pathseg += n_pathseg;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transform(&mut self, transform: Transform) {
|
|
||||||
self.tag_stream.push(0x20);
|
|
||||||
self.transform_stream.push(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap the last two tags in the tag stream; used for transformed
|
|
||||||
// gradients.
|
|
||||||
pub fn swap_last_tags(&mut self) {
|
|
||||||
let len = self.tag_stream.len();
|
|
||||||
self.tag_stream.swap(len - 1, len - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -1.0 means "fill"
|
|
||||||
pub fn linewidth(&mut self, linewidth: f32) {
|
|
||||||
self.tag_stream.push(0x40);
|
|
||||||
self.linewidth_stream.push(linewidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode a fill color draw object.
|
|
||||||
///
|
|
||||||
/// This should be encoded after a path.
|
|
||||||
pub fn fill_color(&mut self, rgba_color: u32) {
|
|
||||||
self.drawtag_stream.push(DRAWTAG_FILLCOLOR);
|
|
||||||
let element = FillColor { rgba_color };
|
|
||||||
self.drawdata_stream.extend(bytemuck::bytes_of(&element));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode a fill linear gradient draw object.
|
|
||||||
///
|
|
||||||
/// This should be encoded after a path.
|
|
||||||
pub fn fill_lin_gradient(&mut self, index: u32, p0: [f32; 2], p1: [f32; 2]) {
|
|
||||||
self.drawtag_stream.push(DRAWTAG_FILLLINGRADIENT);
|
|
||||||
let element = FillLinGradient { index, p0, p1 };
|
|
||||||
self.drawdata_stream.extend(bytemuck::bytes_of(&element));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode a fill radial gradient draw object.
|
|
||||||
///
|
|
||||||
/// This should be encoded after a path.
|
|
||||||
pub fn fill_rad_gradient(&mut self, index: u32, p0: [f32; 2], p1: [f32; 2], r0: f32, r1: f32) {
|
|
||||||
self.drawtag_stream.push(DRAWTAG_FILLRADGRADIENT);
|
|
||||||
let element = FillRadGradient {
|
|
||||||
index,
|
|
||||||
p0,
|
|
||||||
p1,
|
|
||||||
r0,
|
|
||||||
r1,
|
|
||||||
};
|
|
||||||
self.drawdata_stream.extend(bytemuck::bytes_of(&element));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start a clip.
|
|
||||||
pub fn begin_clip(&mut self, blend: Option<Blend>) {
|
|
||||||
self.drawtag_stream.push(DRAWTAG_BEGINCLIP);
|
|
||||||
let element = Clip {
|
|
||||||
blend: blend.unwrap_or(Blend::default()).pack(),
|
|
||||||
};
|
|
||||||
self.drawdata_stream.extend(bytemuck::bytes_of(&element));
|
|
||||||
self.n_clip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end_clip(&mut self, blend: Option<Blend>) {
|
|
||||||
self.drawtag_stream.push(DRAWTAG_ENDCLIP);
|
|
||||||
let element = Clip {
|
|
||||||
blend: blend.unwrap_or(Blend::default()).pack(),
|
|
||||||
};
|
|
||||||
self.drawdata_stream.extend(bytemuck::bytes_of(&element));
|
|
||||||
// This is a dummy path, and will go away with the new clip impl.
|
|
||||||
self.tag_stream.push(0x10);
|
|
||||||
self.n_path += 1;
|
|
||||||
self.n_clip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_scene(&self, buf: &mut BufWrite) {
|
|
||||||
buf.extend_slice(&self.drawtag_stream);
|
|
||||||
let n_drawobj = self.drawtag_stream.len();
|
|
||||||
buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE);
|
|
||||||
buf.extend_slice(&self.drawdata_stream);
|
|
||||||
buf.extend_slice(&self.transform_stream);
|
|
||||||
buf.extend_slice(&self.linewidth_stream);
|
|
||||||
buf.extend_slice(&self.tag_stream);
|
|
||||||
let n_pathtag = self.tag_stream.len();
|
|
||||||
buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize));
|
|
||||||
buf.extend_slice(&self.pathseg_stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn stats(&self) -> SceneStats {
|
|
||||||
SceneStats {
|
|
||||||
n_drawobj: self.drawtag_stream.len(),
|
|
||||||
drawdata_len: self.drawdata_stream.len(),
|
|
||||||
n_transform: self.transform_stream.len(),
|
|
||||||
linewidth_len: std::mem::size_of_val(&*self.linewidth_stream),
|
|
||||||
n_pathtag: self.tag_stream.len(),
|
|
||||||
pathseg_len: self.pathseg_stream.len(),
|
|
||||||
|
|
||||||
n_path: self.n_path,
|
|
||||||
n_pathseg: self.n_pathseg,
|
|
||||||
n_clip: self.n_clip,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) {
|
|
||||||
self.tag_stream.extend(&glyph.tag_stream);
|
|
||||||
self.pathseg_stream.extend(&glyph.pathseg_stream);
|
|
||||||
self.drawtag_stream.extend(&glyph.drawtag_stream);
|
|
||||||
self.drawdata_stream.extend(&glyph.drawdata_stream);
|
|
||||||
self.n_path += glyph.n_path;
|
|
||||||
self.n_pathseg += glyph.n_pathseg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn padding(x: usize, align: usize) -> usize {
|
|
||||||
x.wrapping_neg() & (align - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlyphEncoder {
|
|
||||||
pub(crate) fn path_encoder(&mut self) -> PathEncoder {
|
|
||||||
PathEncoder::new(&mut self.tag_stream, &mut self.pathseg_stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn finish_path(&mut self, n_pathseg: u32) {
|
|
||||||
self.n_path += 1;
|
|
||||||
self.n_pathseg += n_pathseg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode a fill color draw object.
|
|
||||||
///
|
|
||||||
/// This should be encoded after a path.
|
|
||||||
pub(crate) fn fill_color(&mut self, rgba_color: u32) {
|
|
||||||
self.drawtag_stream.push(DRAWTAG_FILLCOLOR);
|
|
||||||
let element = FillColor { rgba_color };
|
|
||||||
self.drawdata_stream.extend(bytemuck::bytes_of(&element));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_color(&self) -> bool {
|
|
||||||
!self.drawtag_stream.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
// Copyright 2022 The piet-gpu authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Also licensed under MIT license, at your choice.
|
|
||||||
|
|
||||||
//! An experimental API for glyph rendering.
|
|
||||||
|
|
||||||
use piet::{kurbo::Affine, RenderContext};
|
|
||||||
use swash::{scale::ScaleContext, CacheKey, FontDataRef};
|
|
||||||
|
|
||||||
use crate::{encoder::GlyphEncoder, PietGpuRenderContext};
|
|
||||||
|
|
||||||
pub struct GlyphRenderer {
|
|
||||||
pub render_ctx: PietGpuRenderContext,
|
|
||||||
scale_context: ScaleContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct FontId(CacheKey);
|
|
||||||
|
|
||||||
impl GlyphRenderer {
|
|
||||||
pub fn new() -> GlyphRenderer {
|
|
||||||
let render_ctx = PietGpuRenderContext::new();
|
|
||||||
let scale_context = ScaleContext::new();
|
|
||||||
GlyphRenderer {
|
|
||||||
render_ctx,
|
|
||||||
scale_context,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn add_glyph(
|
|
||||||
&mut self,
|
|
||||||
font_data: &[u8],
|
|
||||||
font_id: u64,
|
|
||||||
glyph_id: u16,
|
|
||||||
transform: [f32; 6],
|
|
||||||
) {
|
|
||||||
// This transmute is dodgy because the definition in swash isn't repr(transparent).
|
|
||||||
// I think the best solution is to have a from_u64 method, but we'll work that out
|
|
||||||
// later.
|
|
||||||
let font_id = FontId(std::mem::transmute(font_id));
|
|
||||||
let encoder = self.make_glyph(font_data, font_id, glyph_id);
|
|
||||||
const DEFAULT_UPEM: u16 = 2048;
|
|
||||||
let affine = Affine::new([
|
|
||||||
transform[0] as f64,
|
|
||||||
transform[1] as f64,
|
|
||||||
transform[2] as f64,
|
|
||||||
transform[3] as f64,
|
|
||||||
transform[4] as f64,
|
|
||||||
transform[5] as f64,
|
|
||||||
]) * Affine::scale(1.0 / DEFAULT_UPEM as f64);
|
|
||||||
self.render_ctx.transform(affine);
|
|
||||||
self.render_ctx.encode_glyph(&encoder);
|
|
||||||
// TODO: don't fill glyph if RGBA
|
|
||||||
self.render_ctx.fill_glyph(0xff_ff_ff_ff);
|
|
||||||
self.render_ctx.transform(affine.inverse());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.render_ctx = PietGpuRenderContext::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_glyph(&mut self, font_data: &[u8], font_id: FontId, glyph_id: u16) -> GlyphEncoder {
|
|
||||||
let mut encoder = GlyphEncoder::default();
|
|
||||||
let font_data = FontDataRef::new(font_data).expect("invalid font");
|
|
||||||
let mut font_ref = font_data.get(0).expect("invalid font index");
|
|
||||||
font_ref.key = font_id.0;
|
|
||||||
let mut scaler = self.scale_context.builder(font_ref).size(2048.).build();
|
|
||||||
if let Some(outline) = scaler.scale_outline(glyph_id) {
|
|
||||||
crate::text::append_outline(&mut encoder, outline.verbs(), outline.points());
|
|
||||||
} else {
|
|
||||||
println!("failed to scale");
|
|
||||||
}
|
|
||||||
encoder
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,249 +0,0 @@
|
||||||
// Copyright 2021 The piet-gpu authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Also licensed under MIT license, at your choice.
|
|
||||||
|
|
||||||
//! Implementation of gradients.
|
|
||||||
|
|
||||||
use std::collections::hash_map::{Entry, HashMap};
|
|
||||||
|
|
||||||
use piet::kurbo::Point;
|
|
||||||
use piet::{Color, FixedLinearGradient, FixedRadialGradient, GradientStop};
|
|
||||||
|
|
||||||
/// Radial gradient compatible with COLRv1 spec
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Colrv1RadialGradient {
|
|
||||||
/// The center of the iner circle.
|
|
||||||
pub center0: Point,
|
|
||||||
/// The offset of the origin relative to the center.
|
|
||||||
pub center1: Point,
|
|
||||||
/// The radius of the inner circle.
|
|
||||||
pub radius0: f64,
|
|
||||||
/// The radius of the outer circle.
|
|
||||||
pub radius1: f64,
|
|
||||||
/// The stops.
|
|
||||||
pub stops: Vec<GradientStop>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BakedGradient {
|
|
||||||
ramp: Vec<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LinearGradient {
|
|
||||||
pub(crate) start: [f32; 2],
|
|
||||||
pub(crate) end: [f32; 2],
|
|
||||||
pub(crate) ramp_id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RadialGradient {
|
|
||||||
pub(crate) start: [f32; 2],
|
|
||||||
pub(crate) end: [f32; 2],
|
|
||||||
pub(crate) r0: f32,
|
|
||||||
pub(crate) r1: f32,
|
|
||||||
pub(crate) ramp_id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct RampCache {
|
|
||||||
ramps: Vec<GradientRamp>,
|
|
||||||
map: HashMap<GradientRamp, usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
|
||||||
struct GradientRamp(Vec<u32>);
|
|
||||||
|
|
||||||
pub const N_SAMPLES: usize = 512;
|
|
||||||
// TODO: make this dynamic
|
|
||||||
pub const N_GRADIENTS: usize = 256;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct PremulRgba([f64; 4]);
|
|
||||||
|
|
||||||
impl PremulRgba {
|
|
||||||
fn from_color(c: &Color) -> PremulRgba {
|
|
||||||
let rgba = c.as_rgba();
|
|
||||||
let a = rgba.3;
|
|
||||||
// TODO: sRGB nonlinearity? This is complicated.
|
|
||||||
PremulRgba([rgba.0 * a, rgba.1 * a, rgba.2 * a, a])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_u32(&self) -> u32 {
|
|
||||||
let z = self.0;
|
|
||||||
let r = (z[0].max(0.0).min(1.0) * 255.0).round() as u32;
|
|
||||||
let g = (z[1].max(0.0).min(1.0) * 255.0).round() as u32;
|
|
||||||
let b = (z[2].max(0.0).min(1.0) * 255.0).round() as u32;
|
|
||||||
let a = (z[3].max(0.0).min(1.0) * 255.0).round() as u32;
|
|
||||||
r | (g << 8) | (b << 16) | (a << 24)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lerp(&self, other: PremulRgba, t: f64) -> PremulRgba {
|
|
||||||
fn l(a: f64, b: f64, t: f64) -> f64 {
|
|
||||||
a * (1.0 - t) + b * t
|
|
||||||
}
|
|
||||||
let a = self.0;
|
|
||||||
let b = other.0;
|
|
||||||
PremulRgba([
|
|
||||||
l(a[0], b[0], t),
|
|
||||||
l(a[1], b[1], t),
|
|
||||||
l(a[2], b[2], t),
|
|
||||||
l(a[3], b[3], t),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GradientRamp {
|
|
||||||
fn from_stops(stops: &[GradientStop]) -> GradientRamp {
|
|
||||||
let mut last_u = 0.0;
|
|
||||||
let mut last_c = PremulRgba::from_color(&stops[0].color);
|
|
||||||
let mut this_u = last_u;
|
|
||||||
let mut this_c = last_c;
|
|
||||||
let mut j = 0;
|
|
||||||
let v = (0..N_SAMPLES)
|
|
||||||
.map(|i| {
|
|
||||||
let u = (i as f64) / (N_SAMPLES - 1) as f64;
|
|
||||||
while u > this_u {
|
|
||||||
last_u = this_u;
|
|
||||||
last_c = this_c;
|
|
||||||
if let Some(s) = stops.get(j + 1) {
|
|
||||||
this_u = s.pos as f64;
|
|
||||||
this_c = PremulRgba::from_color(&s.color);
|
|
||||||
j += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let du = this_u - last_u;
|
|
||||||
let c = if du < 1e-9 {
|
|
||||||
this_c
|
|
||||||
} else {
|
|
||||||
last_c.lerp(this_c, (u - last_u) / du)
|
|
||||||
};
|
|
||||||
c.to_u32()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
GradientRamp(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For debugging/development.
|
|
||||||
pub(crate) fn dump(&self) {
|
|
||||||
for val in &self.0 {
|
|
||||||
println!("{:x}", val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RampCache {
|
|
||||||
/// Add a gradient ramp to the cache.
|
|
||||||
///
|
|
||||||
/// Currently there is no eviction, so if the gradient is animating, there may
|
|
||||||
/// be resource leaks. In order to support lifetime management, the signature
|
|
||||||
/// should probably change so it returns a ref-counted handle, so that eviction
|
|
||||||
/// is deferred until the last handle is dropped.
|
|
||||||
///
|
|
||||||
/// This function is pretty expensive, but the result is lightweight.
|
|
||||||
fn add_ramp(&mut self, ramp: &[GradientStop]) -> usize {
|
|
||||||
let ramp = GradientRamp::from_stops(ramp);
|
|
||||||
match self.map.entry(ramp) {
|
|
||||||
Entry::Occupied(o) => *o.get(),
|
|
||||||
Entry::Vacant(v) => {
|
|
||||||
let idx = self.ramps.len();
|
|
||||||
self.ramps.push(v.key().clone());
|
|
||||||
v.insert(idx);
|
|
||||||
idx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_linear_gradient(&mut self, lin: &FixedLinearGradient) -> LinearGradient {
|
|
||||||
let ramp_id = self.add_ramp(&lin.stops);
|
|
||||||
LinearGradient {
|
|
||||||
ramp_id: ramp_id as u32,
|
|
||||||
start: crate::render_ctx::to_f32_2(lin.start),
|
|
||||||
end: crate::render_ctx::to_f32_2(lin.end),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_radial_gradient(&mut self, rad: &FixedRadialGradient) -> RadialGradient {
|
|
||||||
let ramp_id = self.add_ramp(&rad.stops);
|
|
||||||
RadialGradient {
|
|
||||||
ramp_id: ramp_id as u32,
|
|
||||||
start: crate::render_ctx::to_f32_2(rad.center + rad.origin_offset),
|
|
||||||
end: crate::render_ctx::to_f32_2(rad.center),
|
|
||||||
r0: 0.0,
|
|
||||||
r1: rad.radius as f32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_radial_gradient_colrv1(&mut self, rad: &Colrv1RadialGradient) -> RadialGradient {
|
|
||||||
let ramp_id = self.add_ramp(&rad.stops);
|
|
||||||
RadialGradient {
|
|
||||||
ramp_id: ramp_id as u32,
|
|
||||||
start: crate::render_ctx::to_f32_2(rad.center0),
|
|
||||||
end: crate::render_ctx::to_f32_2(rad.center1),
|
|
||||||
r0: rad.radius0 as f32,
|
|
||||||
r1: rad.radius1 as f32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dump the contents of a gradient. This is for debugging.
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn dump_gradient(&self, lin: &LinearGradient) {
|
|
||||||
println!("id = {}", lin.ramp_id);
|
|
||||||
self.ramps[lin.ramp_id as usize].dump();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the ramp data.
|
|
||||||
///
|
|
||||||
/// This concatenates all the ramps; we'll want a more sophisticated approach to
|
|
||||||
/// incremental update.
|
|
||||||
pub fn get_ramp_data(&self) -> Vec<u32> {
|
|
||||||
let mut result = Vec::with_capacity(N_SAMPLES * self.ramps.len());
|
|
||||||
for ramp in &self.ramps {
|
|
||||||
result.extend(&ramp.0);
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::RampCache;
|
|
||||||
use piet::kurbo::Point;
|
|
||||||
use piet::{Color, FixedLinearGradient, GradientStop};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_ramp() {
|
|
||||||
let stops = vec![
|
|
||||||
GradientStop {
|
|
||||||
color: Color::WHITE,
|
|
||||||
pos: 0.0,
|
|
||||||
},
|
|
||||||
GradientStop {
|
|
||||||
color: Color::BLACK,
|
|
||||||
pos: 1.0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let mut cache = RampCache::default();
|
|
||||||
let lin = FixedLinearGradient {
|
|
||||||
start: Point::new(0.0, 0.0),
|
|
||||||
end: Point::new(0.0, 1.0),
|
|
||||||
stops,
|
|
||||||
};
|
|
||||||
let our_lin = cache.add_linear_gradient(&lin);
|
|
||||||
cache.dump_gradient(&our_lin);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +1,26 @@
|
||||||
mod blend;
|
|
||||||
mod encoder;
|
|
||||||
pub mod glyph_render;
|
|
||||||
mod gradient;
|
|
||||||
mod pico_svg;
|
mod pico_svg;
|
||||||
mod render_ctx;
|
mod ramp;
|
||||||
mod render_driver;
|
mod render_driver;
|
||||||
|
pub mod samples;
|
||||||
|
mod simple_text;
|
||||||
pub mod stages;
|
pub mod stages;
|
||||||
pub mod test_scenes;
|
|
||||||
mod text;
|
pub use piet_scene as scene;
|
||||||
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use scene::ResourcePatch;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
pub use blend::{Blend, BlendMode, CompositionMode};
|
|
||||||
pub use encoder::EncodedSceneRef;
|
|
||||||
pub use gradient::Colrv1RadialGradient;
|
|
||||||
pub use render_ctx::PietGpuRenderContext;
|
|
||||||
pub use render_driver::RenderDriver;
|
pub use render_driver::RenderDriver;
|
||||||
|
pub use simple_text::SimpleText;
|
||||||
use piet::kurbo::Vec2;
|
|
||||||
use piet::{ImageFormat, RenderContext};
|
|
||||||
|
|
||||||
use piet_gpu_hal::{
|
use piet_gpu_hal::{
|
||||||
include_shader, BindType, Buffer, BufferUsage, CmdBuf, ComputePassDescriptor, DescriptorSet,
|
include_shader, BindType, BufWrite, Buffer, BufferUsage, CmdBuf, ComputePassDescriptor,
|
||||||
Error, Image, ImageLayout, Pipeline, QueryPool, Session,
|
DescriptorSet, Error, Image, ImageLayout, Pipeline, QueryPool, Session,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use piet_scene::Scene;
|
||||||
|
|
||||||
pub use pico_svg::PicoSvg;
|
pub use pico_svg::PicoSvg;
|
||||||
use stages::{ClipBinding, ElementBinding, ElementCode, DRAW_PART_SIZE, PATHSEG_PART_SIZE};
|
use stages::{ClipBinding, ElementBinding, ElementCode, DRAW_PART_SIZE, PATHSEG_PART_SIZE};
|
||||||
|
|
||||||
|
@ -36,6 +31,10 @@ const TILE_H: usize = 16;
|
||||||
|
|
||||||
const PTCL_INITIAL_ALLOC: usize = 1024;
|
const PTCL_INITIAL_ALLOC: usize = 1024;
|
||||||
|
|
||||||
|
const N_GRADIENT_SAMPLES: usize = 512;
|
||||||
|
// TODO: make this dynamic
|
||||||
|
const N_GRADIENTS: usize = 256;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn dump_scene(buf: &[u8]) {
|
fn dump_scene(buf: &[u8]) {
|
||||||
for i in 0..(buf.len() / 4) {
|
for i in 0..(buf.len() / 4) {
|
||||||
|
@ -157,6 +156,9 @@ pub struct Renderer {
|
||||||
|
|
||||||
gradient_bufs: Vec<Buffer>,
|
gradient_bufs: Vec<Buffer>,
|
||||||
gradients: Image,
|
gradients: Image,
|
||||||
|
|
||||||
|
ramps: ramp::RampCache,
|
||||||
|
drawdata_patches: Vec<(usize, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderConfig {
|
impl RenderConfig {
|
||||||
|
@ -222,7 +224,7 @@ impl Renderer {
|
||||||
|
|
||||||
let image_format = match config.format {
|
let image_format = match config.format {
|
||||||
PixelFormat::A8 => piet_gpu_hal::ImageFormat::A8,
|
PixelFormat::A8 => piet_gpu_hal::ImageFormat::A8,
|
||||||
PixelFormat::Rgba8 => piet_gpu_hal::ImageFormat::Rgba8,
|
PixelFormat::Rgba8 => piet_gpu_hal::ImageFormat::Surface,
|
||||||
};
|
};
|
||||||
let image_dev = session.create_image2d(width as u32, height as u32, image_format)?;
|
let image_dev = session.create_image2d(width as u32, height as u32, image_format)?;
|
||||||
|
|
||||||
|
@ -333,8 +335,8 @@ impl Renderer {
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let bg_image = Self::make_test_bg_image(&session);
|
let bg_image = Self::make_test_bg_image(&session);
|
||||||
|
|
||||||
const GRADIENT_BUF_SIZE: usize =
|
const GRADIENT_BUF_SIZE: usize = N_GRADIENTS * N_GRADIENT_SAMPLES * 4;
|
||||||
crate::gradient::N_GRADIENTS * crate::gradient::N_SAMPLES * 4;
|
|
||||||
let gradient_bufs = (0..n_bufs)
|
let gradient_bufs = (0..n_bufs)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
session
|
session
|
||||||
|
@ -367,6 +369,9 @@ impl Renderer {
|
||||||
.build(&session, &k4_pipeline)?;
|
.build(&session, &k4_pipeline)?;
|
||||||
|
|
||||||
let scene_stats = Default::default();
|
let scene_stats = Default::default();
|
||||||
|
let ramps = ramp::RampCache::default();
|
||||||
|
let drawdata_patches = vec![];
|
||||||
|
|
||||||
Ok(Renderer {
|
Ok(Renderer {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -406,62 +411,40 @@ impl Renderer {
|
||||||
_bg_image: bg_image,
|
_bg_image: bg_image,
|
||||||
gradient_bufs,
|
gradient_bufs,
|
||||||
gradients,
|
gradients,
|
||||||
|
ramps,
|
||||||
|
drawdata_patches,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the scene in the render context to GPU resources.
|
pub fn upload_scene(&mut self, scene: &Scene, buf_ix: usize) -> Result<(), Error> {
|
||||||
///
|
self.drawdata_patches.clear();
|
||||||
/// At present, this requires that any command buffer submission has completed.
|
self.scene_stats = SceneStats::from_scene(scene);
|
||||||
/// A future evolution will handle staging of the next frame's scene while the
|
self.ramps.advance();
|
||||||
/// rendering of the current frame is in flight.
|
let data = scene.data();
|
||||||
pub fn upload_render_ctx(
|
let stop_data = &data.resources.stops;
|
||||||
&mut self,
|
for patch in &data.resources.patches {
|
||||||
render_ctx: &mut PietGpuRenderContext,
|
match patch {
|
||||||
buf_ix: usize,
|
ResourcePatch::Ramp { offset, stops } => {
|
||||||
) -> Result<(), Error> {
|
let ramp_id = self.ramps.add(&stop_data[stops.clone()]);
|
||||||
self.scene_stats = render_ctx.stats();
|
self.drawdata_patches.push((*offset, ramp_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
self.upload_config(buf_ix)?;
|
self.upload_config(buf_ix)?;
|
||||||
{
|
{
|
||||||
let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?;
|
let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?;
|
||||||
render_ctx.write_scene(&mut mapped_scene);
|
write_scene(scene, &self.drawdata_patches, &mut mapped_scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload gradient data.
|
// Upload gradient data.
|
||||||
let ramp_data = render_ctx.get_ramp_data();
|
let ramp_data = self.ramps.data();
|
||||||
if !ramp_data.is_empty() {
|
if !ramp_data.is_empty() {
|
||||||
assert!(
|
assert!(
|
||||||
self.gradient_bufs[buf_ix].size() as usize
|
self.gradient_bufs[buf_ix].size() as usize
|
||||||
>= std::mem::size_of_val(&*ramp_data)
|
>= std::mem::size_of_val(&*ramp_data)
|
||||||
);
|
);
|
||||||
self.gradient_bufs[buf_ix].write(&ramp_data)?;
|
self.gradient_bufs[buf_ix].write(ramp_data)?;
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn upload_scene<T: Copy + Pod>(
|
|
||||||
&mut self,
|
|
||||||
scene: &EncodedSceneRef<T>,
|
|
||||||
buf_ix: usize,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.scene_stats = scene.stats();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.upload_config(buf_ix)?;
|
|
||||||
{
|
|
||||||
let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?;
|
|
||||||
scene.write_scene(&mut mapped_scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload gradient data.
|
|
||||||
if !scene.ramp_data.is_empty() {
|
|
||||||
assert!(
|
|
||||||
self.gradient_bufs[buf_ix].size() as usize
|
|
||||||
>= std::mem::size_of_val(&*scene.ramp_data)
|
|
||||||
);
|
|
||||||
self.gradient_bufs[buf_ix].write(scene.ramp_data)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -643,12 +626,8 @@ impl Renderer {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
format: ImageFormat,
|
|
||||||
) -> Result<Image, Error> {
|
) -> Result<Image, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if format != ImageFormat::RgbaPremul {
|
|
||||||
return Err("unsupported image format".into());
|
|
||||||
}
|
|
||||||
let buffer = session.create_buffer_init(&buf, BufferUsage::COPY_SRC)?;
|
let buffer = session.create_buffer_init(&buf, BufferUsage::COPY_SRC)?;
|
||||||
const RGBA: piet_gpu_hal::ImageFormat = piet_gpu_hal::ImageFormat::Rgba8;
|
const RGBA: piet_gpu_hal::ImageFormat = piet_gpu_hal::ImageFormat::Rgba8;
|
||||||
let image = session.create_image2d(width.try_into()?, height.try_into()?, RGBA)?;
|
let image = session.create_image2d(width.try_into()?, height.try_into()?, RGBA)?;
|
||||||
|
@ -682,18 +661,14 @@ impl Renderer {
|
||||||
buf[(y * WIDTH + x) * 4 + 2] = b;
|
buf[(y * WIDTH + x) * 4 + 2] = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::make_image(session, WIDTH, HEIGHT, &buf, ImageFormat::RgbaPremul).unwrap()
|
Self::make_image(session, WIDTH, HEIGHT, &buf).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_gradient_image(session: &Session) -> Image {
|
fn make_gradient_image(session: &Session) -> Image {
|
||||||
unsafe {
|
unsafe {
|
||||||
const RGBA: piet_gpu_hal::ImageFormat = piet_gpu_hal::ImageFormat::Rgba8;
|
const RGBA: piet_gpu_hal::ImageFormat = piet_gpu_hal::ImageFormat::Rgba8;
|
||||||
session
|
session
|
||||||
.create_image2d(
|
.create_image2d(N_GRADIENT_SAMPLES as u32, N_GRADIENTS as u32, RGBA)
|
||||||
gradient::N_SAMPLES as u32,
|
|
||||||
gradient::N_GRADIENTS as u32,
|
|
||||||
RGBA,
|
|
||||||
)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -792,6 +767,21 @@ const DRAWTAG_SIZE: usize = 4;
|
||||||
const ANNOTATED_SIZE: usize = 40;
|
const ANNOTATED_SIZE: usize = 40;
|
||||||
|
|
||||||
impl SceneStats {
|
impl SceneStats {
|
||||||
|
pub fn from_scene(scene: &piet_scene::Scene) -> Self {
|
||||||
|
let data = scene.data();
|
||||||
|
Self {
|
||||||
|
n_drawobj: data.drawtag_stream.len(),
|
||||||
|
drawdata_len: data.drawdata_stream.len(),
|
||||||
|
n_transform: data.transform_stream.len(),
|
||||||
|
linewidth_len: std::mem::size_of_val(&*data.linewidth_stream),
|
||||||
|
pathseg_len: data.pathseg_stream.len(),
|
||||||
|
n_pathtag: data.tag_stream.len(),
|
||||||
|
n_path: data.n_path,
|
||||||
|
n_pathseg: data.n_pathseg,
|
||||||
|
n_clip: data.n_clip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn scene_size(&self) -> usize {
|
pub(crate) fn scene_size(&self) -> usize {
|
||||||
align_up(self.n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE
|
align_up(self.n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE
|
||||||
+ self.drawdata_len
|
+ self.drawdata_len
|
||||||
|
@ -896,6 +886,40 @@ impl SceneStats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_scene(scene: &Scene, drawdata_patches: &[(usize, u32)], buf: &mut BufWrite) {
|
||||||
|
let data = scene.data();
|
||||||
|
buf.extend_slice(&data.drawtag_stream);
|
||||||
|
let n_drawobj = data.drawtag_stream.len();
|
||||||
|
buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE);
|
||||||
|
if !drawdata_patches.is_empty() {
|
||||||
|
let mut pos = 0;
|
||||||
|
for patch in drawdata_patches {
|
||||||
|
let offset = patch.0;
|
||||||
|
let value = patch.1;
|
||||||
|
if pos < offset {
|
||||||
|
buf.extend_slice(&data.drawdata_stream[pos..offset]);
|
||||||
|
}
|
||||||
|
buf.push(value);
|
||||||
|
pos = offset + 4;
|
||||||
|
}
|
||||||
|
if pos < data.drawdata_stream.len() {
|
||||||
|
buf.extend_slice(&data.drawdata_stream[pos..])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf.extend_slice(&data.drawdata_stream);
|
||||||
|
}
|
||||||
|
buf.extend_slice(&data.transform_stream);
|
||||||
|
buf.extend_slice(&data.linewidth_stream);
|
||||||
|
buf.extend_slice(&data.tag_stream);
|
||||||
|
let n_pathtag = data.tag_stream.len();
|
||||||
|
buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize));
|
||||||
|
buf.extend_slice(&data.pathseg_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn padding(x: usize, align: usize) -> usize {
|
||||||
|
x.wrapping_neg() & (align - 1)
|
||||||
|
}
|
||||||
|
|
||||||
fn align_up(x: usize, align: usize) -> usize {
|
fn align_up(x: usize, align: usize) -> usize {
|
||||||
debug_assert!(align.is_power_of_two());
|
debug_assert!(align.is_power_of_two());
|
||||||
(x + align - 1) & !(align - 1)
|
(x + align - 1) & !(align - 1)
|
||||||
|
|
|
@ -4,12 +4,12 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use roxmltree::{Document, Node};
|
use roxmltree::{Document, Node};
|
||||||
|
|
||||||
use piet::kurbo::{Affine, BezPath};
|
use kurbo::{Affine, BezPath};
|
||||||
|
|
||||||
use piet::{Color, RenderContext};
|
use piet_scene::Color;
|
||||||
|
|
||||||
pub struct PicoSvg {
|
pub struct PicoSvg {
|
||||||
items: Vec<Item>,
|
pub items: Vec<Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Item {
|
pub enum Item {
|
||||||
|
@ -18,14 +18,14 @@ pub enum Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StrokeItem {
|
pub struct StrokeItem {
|
||||||
width: f64,
|
pub width: f64,
|
||||||
color: Color,
|
pub color: Color,
|
||||||
path: BezPath,
|
pub path: BezPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FillItem {
|
pub struct FillItem {
|
||||||
color: Color,
|
pub color: Color,
|
||||||
path: BezPath,
|
pub path: BezPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Parser<'a> {
|
struct Parser<'a> {
|
||||||
|
@ -44,20 +44,6 @@ impl PicoSvg {
|
||||||
}
|
}
|
||||||
Ok(PicoSvg { items })
|
Ok(PicoSvg { items })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, rc: &mut impl RenderContext) {
|
|
||||||
for item in &self.items {
|
|
||||||
match item {
|
|
||||||
Item::Fill(fill_item) => {
|
|
||||||
rc.fill(&fill_item.path, &fill_item.color);
|
|
||||||
//rc.stroke(&fill_item.path, &fill_item.color, 1.0);
|
|
||||||
}
|
|
||||||
Item::Stroke(stroke_item) => {
|
|
||||||
rc.stroke(&stroke_item.path, &stroke_item.color, stroke_item.width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
|
@ -119,7 +105,14 @@ fn parse_color(color: &str) -> Color {
|
||||||
if color.len() == 4 {
|
if color.len() == 4 {
|
||||||
hex = (hex >> 8) * 0x110000 + ((hex >> 4) & 0xf) * 0x1100 + (hex & 0xf) * 0x11;
|
hex = (hex >> 8) * 0x110000 + ((hex >> 4) & 0xf) * 0x1100 + (hex & 0xf) * 0x11;
|
||||||
}
|
}
|
||||||
Color::from_rgba32_u32((hex << 8) + 0xff)
|
let rgba = (hex << 8) + 0xff;
|
||||||
|
let (r, g, b, a) = (
|
||||||
|
(rgba >> 24 & 255) as u8,
|
||||||
|
((rgba >> 16) & 255) as u8,
|
||||||
|
((rgba >> 8) & 255) as u8,
|
||||||
|
(rgba & 255) as u8,
|
||||||
|
);
|
||||||
|
Color::rgba8(r, g, b, a)
|
||||||
} else if color.starts_with("rgb(") {
|
} else if color.starts_with("rgb(") {
|
||||||
let mut iter = color[4..color.len() - 1].split(',');
|
let mut iter = color[4..color.len() - 1].split(',');
|
||||||
let r = u8::from_str(iter.next().unwrap()).unwrap();
|
let r = u8::from_str(iter.next().unwrap()).unwrap();
|
||||||
|
@ -127,19 +120,20 @@ fn parse_color(color: &str) -> Color {
|
||||||
let b = u8::from_str(iter.next().unwrap()).unwrap();
|
let b = u8::from_str(iter.next().unwrap()).unwrap();
|
||||||
Color::rgb8(r, g, b)
|
Color::rgb8(r, g, b)
|
||||||
} else {
|
} else {
|
||||||
Color::from_rgba32_u32(0xff00ff80)
|
Color::rgba8(255, 0, 255, 0x80)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modify_opacity(color: Color, attr_name: &str, node: Node) -> Color {
|
fn modify_opacity(mut color: Color, attr_name: &str, node: Node) -> Color {
|
||||||
if let Some(opacity) = node.attribute(attr_name) {
|
if let Some(opacity) = node.attribute(attr_name) {
|
||||||
let alpha = if opacity.ends_with("%") {
|
let alpha = if opacity.ends_with("%") {
|
||||||
let pctg = opacity[..opacity.len() - 1].parse().unwrap_or(100.0);
|
let pctg = opacity[..opacity.len() - 1].parse().unwrap_or(100.0);
|
||||||
pctg * 0.01
|
pctg * 0.01
|
||||||
} else {
|
} else {
|
||||||
opacity.parse().unwrap_or(1.0)
|
opacity.parse().unwrap_or(1.0)
|
||||||
};
|
} as f64;
|
||||||
color.with_alpha(alpha)
|
color.a = (alpha.min(1.0).max(0.0) * 255.0).round() as u8;
|
||||||
|
color
|
||||||
} else {
|
} else {
|
||||||
color
|
color
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::brush::{Color, Stop, StopVec};
|
use piet_scene::{Color, GradientStop, GradientStops};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const N_SAMPLES: usize = 512;
|
const N_SAMPLES: usize = 512;
|
||||||
|
@ -7,15 +8,11 @@ const RETAINED_COUNT: usize = 64;
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RampCache {
|
pub struct RampCache {
|
||||||
epoch: u64,
|
epoch: u64,
|
||||||
map: HashMap<StopVec, (u32, u64)>,
|
map: HashMap<GradientStops, (u32, u64)>,
|
||||||
data: Vec<u32>,
|
data: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RampCache {
|
impl RampCache {
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn advance(&mut self) {
|
pub fn advance(&mut self) {
|
||||||
self.epoch += 1;
|
self.epoch += 1;
|
||||||
if self.map.len() > RETAINED_COUNT {
|
if self.map.len() > RETAINED_COUNT {
|
||||||
|
@ -25,13 +22,7 @@ impl RampCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn add(&mut self, stops: &[GradientStop]) -> u32 {
|
||||||
self.epoch = 0;
|
|
||||||
self.map.clear();
|
|
||||||
self.data.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(&mut self, stops: &[Stop]) -> u32 {
|
|
||||||
if let Some(entry) = self.map.get_mut(stops) {
|
if let Some(entry) = self.map.get_mut(stops) {
|
||||||
entry.1 = self.epoch;
|
entry.1 = self.epoch;
|
||||||
entry.0
|
entry.0
|
||||||
|
@ -73,7 +64,7 @@ impl RampCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_ramp<'a>(stops: &'a [Stop]) -> impl Iterator<Item = u32> + 'a {
|
fn make_ramp<'a>(stops: &'a [GradientStop]) -> impl Iterator<Item = u32> + 'a {
|
||||||
let mut last_u = 0.0;
|
let mut last_u = 0.0;
|
||||||
let mut last_c = ColorF64::from_color(stops[0].color);
|
let mut last_c = ColorF64::from_color(stops[0].color);
|
||||||
let mut this_u = last_u;
|
let mut this_u = last_u;
|
||||||
|
@ -133,6 +124,6 @@ impl ColorF64 {
|
||||||
let g = ((self.0[1] * a).min(1.0).max(0.0) * 255.0) as u32;
|
let g = ((self.0[1] * a).min(1.0).max(0.0) * 255.0) as u32;
|
||||||
let b = ((self.0[2] * a).min(1.0).max(0.0) * 255.0) as u32;
|
let b = ((self.0[2] * a).min(1.0).max(0.0) * 255.0) as u32;
|
||||||
let a = (a * 255.0) as u32;
|
let a = (a * 255.0) as u32;
|
||||||
b | (g << 8) | (r << 16) | (a << 24)
|
r | (g << 8) | (b << 16) | (a << 24)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,452 +0,0 @@
|
||||||
// This should match the value in kernel4.comp for correct rendering.
|
|
||||||
const DO_SRGB_CONVERSION: bool = false;
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use crate::encoder::GlyphEncoder;
|
|
||||||
use crate::stages::Transform;
|
|
||||||
use piet::kurbo::{Affine, PathEl, Point, Rect, Shape};
|
|
||||||
use piet::{
|
|
||||||
Color, Error, FixedGradient, ImageFormat, InterpolationMode, IntoBrush, RenderContext,
|
|
||||||
StrokeStyle,
|
|
||||||
};
|
|
||||||
|
|
||||||
use piet_gpu_hal::BufWrite;
|
|
||||||
use piet_gpu_types::encoder::{Encode, Encoder};
|
|
||||||
use piet_gpu_types::scene::Element;
|
|
||||||
|
|
||||||
use crate::gradient::{Colrv1RadialGradient, LinearGradient, RadialGradient, RampCache};
|
|
||||||
use crate::text::Font;
|
|
||||||
pub use crate::text::{PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder};
|
|
||||||
use crate::{Blend, SceneStats};
|
|
||||||
|
|
||||||
pub struct PietGpuImage;
|
|
||||||
|
|
||||||
pub struct PietGpuRenderContext {
|
|
||||||
encoder: Encoder,
|
|
||||||
elements: Vec<Element>,
|
|
||||||
// Will probably need direct accesss to hal Device to create images etc.
|
|
||||||
inner_text: PietGpuText,
|
|
||||||
stroke_width: f32,
|
|
||||||
// We're tallying these cpu-side for expedience, but will probably
|
|
||||||
// move this to some kind of readback from element processing.
|
|
||||||
/// The count of elements that make it through to coarse rasterization.
|
|
||||||
path_count: usize,
|
|
||||||
/// The count of path segment elements.
|
|
||||||
pathseg_count: usize,
|
|
||||||
/// The count of transform elements.
|
|
||||||
trans_count: usize,
|
|
||||||
|
|
||||||
cur_transform: Affine,
|
|
||||||
state_stack: Vec<State>,
|
|
||||||
clip_stack: Vec<ClipElement>,
|
|
||||||
|
|
||||||
ramp_cache: RampCache,
|
|
||||||
|
|
||||||
// Fields for new element processing pipeline below
|
|
||||||
// TODO: delete old encoder, rename
|
|
||||||
new_encoder: crate::encoder::Encoder,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum PietGpuBrush {
|
|
||||||
Solid(u32),
|
|
||||||
LinGradient(LinearGradient),
|
|
||||||
RadGradient(RadialGradient),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct State {
|
|
||||||
/// The transform at the parent state.
|
|
||||||
transform: Affine,
|
|
||||||
n_clip: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ClipElement {
|
|
||||||
blend: Option<Blend>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const TOLERANCE: f64 = 0.25;
|
|
||||||
|
|
||||||
impl PietGpuRenderContext {
|
|
||||||
pub fn new() -> PietGpuRenderContext {
|
|
||||||
let encoder = Encoder::new();
|
|
||||||
let elements = Vec::new();
|
|
||||||
let font = Font::new();
|
|
||||||
let inner_text = PietGpuText::new(font);
|
|
||||||
let stroke_width = -1.0;
|
|
||||||
PietGpuRenderContext {
|
|
||||||
encoder,
|
|
||||||
elements,
|
|
||||||
inner_text,
|
|
||||||
stroke_width,
|
|
||||||
path_count: 0,
|
|
||||||
pathseg_count: 0,
|
|
||||||
trans_count: 0,
|
|
||||||
cur_transform: Affine::default(),
|
|
||||||
state_stack: Vec::new(),
|
|
||||||
clip_stack: Vec::new(),
|
|
||||||
ramp_cache: RampCache::default(),
|
|
||||||
new_encoder: crate::encoder::Encoder::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn stats(&self) -> SceneStats {
|
|
||||||
self.new_encoder.stats()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_scene(&self, buf: &mut BufWrite) {
|
|
||||||
self.new_encoder.write_scene(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: delete
|
|
||||||
pub fn get_scene_buf(&mut self) -> &[u8] {
|
|
||||||
const ALIGN: usize = 128;
|
|
||||||
let padded_size = (self.elements.len() + (ALIGN - 1)) & ALIGN.wrapping_neg();
|
|
||||||
self.elements.resize(padded_size, Element::Nop());
|
|
||||||
self.elements.encode(&mut self.encoder);
|
|
||||||
self.encoder.buf()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path_count(&self) -> usize {
|
|
||||||
self.path_count
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pathseg_count(&self) -> usize {
|
|
||||||
self.pathseg_count
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trans_count(&self) -> usize {
|
|
||||||
self.trans_count
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_ramp_data(&self) -> Vec<u32> {
|
|
||||||
self.ramp_cache.get_ramp_data()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderContext for PietGpuRenderContext {
|
|
||||||
type Brush = PietGpuBrush;
|
|
||||||
type Image = PietGpuImage;
|
|
||||||
type Text = PietGpuText;
|
|
||||||
type TextLayout = PietGpuTextLayout;
|
|
||||||
|
|
||||||
fn status(&mut self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn solid_brush(&mut self, color: Color) -> Self::Brush {
|
|
||||||
// kernel4 expects colors encoded in alpha-premultiplied sRGB:
|
|
||||||
//
|
|
||||||
// [α,sRGB(α⋅R),sRGB(α⋅G),sRGB(α⋅B)]
|
|
||||||
//
|
|
||||||
// See also http://ssp.impulsetrain.com/gamma-premult.html.
|
|
||||||
let (r, g, b, a) = color.as_rgba();
|
|
||||||
let premul = Color::rgba(
|
|
||||||
to_srgb(from_srgb(r) * a),
|
|
||||||
to_srgb(from_srgb(g) * a),
|
|
||||||
to_srgb(from_srgb(b) * a),
|
|
||||||
a,
|
|
||||||
);
|
|
||||||
PietGpuBrush::Solid(premul.as_rgba_u32())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gradient(&mut self, gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Error> {
|
|
||||||
match gradient.into() {
|
|
||||||
FixedGradient::Linear(lin) => {
|
|
||||||
let lin = self.ramp_cache.add_linear_gradient(&lin);
|
|
||||||
Ok(PietGpuBrush::LinGradient(lin))
|
|
||||||
}
|
|
||||||
FixedGradient::Radial(rad) => {
|
|
||||||
let rad = self.ramp_cache.add_radial_gradient(&rad);
|
|
||||||
Ok(PietGpuBrush::RadGradient(rad))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&mut self, _color: Color) {}
|
|
||||||
|
|
||||||
fn stroke(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>, width: f64) {
|
|
||||||
self.encode_linewidth(width.abs() as f32);
|
|
||||||
let brush = brush.make_brush(self, || shape.bounding_box()).into_owned();
|
|
||||||
let path = shape.path_elements(TOLERANCE);
|
|
||||||
self.encode_path(path, false);
|
|
||||||
self.encode_brush(&brush);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stroke_styled(
|
|
||||||
&mut self,
|
|
||||||
_shape: impl Shape,
|
|
||||||
_brush: &impl IntoBrush<Self>,
|
|
||||||
_width: f64,
|
|
||||||
_style: &StrokeStyle,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>) {
|
|
||||||
let brush = brush.make_brush(self, || shape.bounding_box()).into_owned();
|
|
||||||
let path = shape.path_elements(TOLERANCE);
|
|
||||||
self.encode_linewidth(-1.0);
|
|
||||||
self.encode_path(path, true);
|
|
||||||
self.encode_brush(&brush);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_even_odd(&mut self, _shape: impl Shape, _brush: &impl IntoBrush<Self>) {}
|
|
||||||
|
|
||||||
fn clip(&mut self, shape: impl Shape) {
|
|
||||||
self.encode_linewidth(-1.0);
|
|
||||||
let path = shape.path_elements(TOLERANCE);
|
|
||||||
self.encode_path(path, true);
|
|
||||||
self.new_encoder.begin_clip(None);
|
|
||||||
self.clip_stack.push(ClipElement { blend: None });
|
|
||||||
if let Some(tos) = self.state_stack.last_mut() {
|
|
||||||
tos.n_clip += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text(&mut self) -> &mut Self::Text {
|
|
||||||
&mut self.inner_text
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<Point>) {
|
|
||||||
self.encode_linewidth(-1.0);
|
|
||||||
layout.draw_text(self, pos.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save(&mut self) -> Result<(), Error> {
|
|
||||||
self.state_stack.push(State {
|
|
||||||
transform: self.cur_transform,
|
|
||||||
n_clip: 0,
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self) -> Result<(), Error> {
|
|
||||||
if let Some(state) = self.state_stack.pop() {
|
|
||||||
self.encode_transform(Transform::from_kurbo(state.transform));
|
|
||||||
self.cur_transform = state.transform;
|
|
||||||
for _ in 0..state.n_clip {
|
|
||||||
self.pop_clip();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::StackUnbalance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) -> Result<(), Error> {
|
|
||||||
for _ in 0..self.clip_stack.len() {
|
|
||||||
self.pop_clip();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform(&mut self, transform: Affine) {
|
|
||||||
self.cur_transform *= transform;
|
|
||||||
self.encode_transform(Transform::from_kurbo(self.cur_transform));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_image(
|
|
||||||
&mut self,
|
|
||||||
_width: usize,
|
|
||||||
_height: usize,
|
|
||||||
_buf: &[u8],
|
|
||||||
_format: ImageFormat,
|
|
||||||
) -> Result<Self::Image, Error> {
|
|
||||||
Ok(PietGpuImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_image(
|
|
||||||
&mut self,
|
|
||||||
_image: &Self::Image,
|
|
||||||
_rect: impl Into<Rect>,
|
|
||||||
_interp: InterpolationMode,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_image_area(
|
|
||||||
&mut self,
|
|
||||||
_image: &Self::Image,
|
|
||||||
_src_rect: impl Into<Rect>,
|
|
||||||
_dst_rect: impl Into<Rect>,
|
|
||||||
_interp: InterpolationMode,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blurred_rect(&mut self, _rect: Rect, _blur_radius: f64, _brush: &impl IntoBrush<Self>) {}
|
|
||||||
|
|
||||||
fn current_transform(&self) -> Affine {
|
|
||||||
self.cur_transform
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_save(&mut self, f: impl FnOnce(&mut Self) -> Result<(), Error>) -> Result<(), Error> {
|
|
||||||
self.save()?;
|
|
||||||
// Always try to restore the stack, even if `f` errored.
|
|
||||||
f(self).and(self.restore())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PietGpuRenderContext {
|
|
||||||
pub fn blend(&mut self, shape: impl Shape, blend: Blend) {
|
|
||||||
self.encode_linewidth(-1.0);
|
|
||||||
let path = shape.path_elements(TOLERANCE);
|
|
||||||
self.encode_path(path, true);
|
|
||||||
self.new_encoder.begin_clip(Some(blend));
|
|
||||||
self.clip_stack.push(ClipElement { blend: Some(blend) });
|
|
||||||
if let Some(tos) = self.state_stack.last_mut() {
|
|
||||||
tos.n_clip += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn radial_gradient_colrv1(&mut self, rad: &Colrv1RadialGradient) -> PietGpuBrush {
|
|
||||||
PietGpuBrush::RadGradient(self.ramp_cache.add_radial_gradient_colrv1(rad))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill_transform(&mut self, shape: impl Shape, brush: &PietGpuBrush, transform: Affine) {
|
|
||||||
let path = shape.path_elements(TOLERANCE);
|
|
||||||
self.encode_linewidth(-1.0);
|
|
||||||
self.encode_path(path, true);
|
|
||||||
self.encode_transform(Transform::from_kurbo(transform));
|
|
||||||
self.new_encoder.swap_last_tags();
|
|
||||||
self.encode_brush(&brush);
|
|
||||||
self.encode_transform(Transform::from_kurbo(transform.inverse()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_path(&mut self, path: impl Iterator<Item = PathEl>, is_fill: bool) {
|
|
||||||
if is_fill {
|
|
||||||
self.encode_path_inner(
|
|
||||||
path.flat_map(|el| {
|
|
||||||
match el {
|
|
||||||
PathEl::MoveTo(..) => Some(PathEl::ClosePath),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
.into_iter()
|
|
||||||
.chain(Some(el))
|
|
||||||
})
|
|
||||||
.chain(Some(PathEl::ClosePath)),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
self.encode_path_inner(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_path_inner(&mut self, path: impl Iterator<Item = PathEl>) {
|
|
||||||
let mut pe = self.new_encoder.path_encoder();
|
|
||||||
for el in path {
|
|
||||||
match el {
|
|
||||||
PathEl::MoveTo(p) => {
|
|
||||||
let p = to_f32_2(p);
|
|
||||||
pe.move_to(p[0], p[1]);
|
|
||||||
}
|
|
||||||
PathEl::LineTo(p) => {
|
|
||||||
let p = to_f32_2(p);
|
|
||||||
pe.line_to(p[0], p[1]);
|
|
||||||
}
|
|
||||||
PathEl::QuadTo(p1, p2) => {
|
|
||||||
let p1 = to_f32_2(p1);
|
|
||||||
let p2 = to_f32_2(p2);
|
|
||||||
pe.quad_to(p1[0], p1[1], p2[0], p2[1]);
|
|
||||||
}
|
|
||||||
PathEl::CurveTo(p1, p2, p3) => {
|
|
||||||
let p1 = to_f32_2(p1);
|
|
||||||
let p2 = to_f32_2(p2);
|
|
||||||
let p3 = to_f32_2(p3);
|
|
||||||
pe.cubic_to(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
|
|
||||||
}
|
|
||||||
PathEl::ClosePath => pe.close_path(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pe.path();
|
|
||||||
let n_pathseg = pe.n_pathseg();
|
|
||||||
self.new_encoder.finish_path(n_pathseg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_clip(&mut self) {
|
|
||||||
let tos = self.clip_stack.pop().unwrap();
|
|
||||||
self.new_encoder.end_clip(tos.blend);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) {
|
|
||||||
self.new_encoder.encode_glyph(glyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn fill_glyph(&mut self, rgba_color: u32) {
|
|
||||||
self.new_encoder.fill_color(rgba_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn encode_transform(&mut self, transform: Transform) {
|
|
||||||
self.new_encoder.transform(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_linewidth(&mut self, linewidth: f32) {
|
|
||||||
if self.stroke_width != linewidth {
|
|
||||||
self.new_encoder.linewidth(linewidth);
|
|
||||||
self.stroke_width = linewidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_brush(&mut self, brush: &PietGpuBrush) {
|
|
||||||
match brush {
|
|
||||||
PietGpuBrush::Solid(rgba_color) => {
|
|
||||||
self.new_encoder.fill_color(*rgba_color);
|
|
||||||
}
|
|
||||||
PietGpuBrush::LinGradient(lin) => {
|
|
||||||
self.new_encoder
|
|
||||||
.fill_lin_gradient(lin.ramp_id, lin.start, lin.end);
|
|
||||||
}
|
|
||||||
PietGpuBrush::RadGradient(rad) => {
|
|
||||||
self.new_encoder
|
|
||||||
.fill_rad_gradient(rad.ramp_id, rad.start, rad.end, rad.r0, rad.r1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoBrush<PietGpuRenderContext> for PietGpuBrush {
|
|
||||||
fn make_brush<'b>(
|
|
||||||
&'b self,
|
|
||||||
_piet: &mut PietGpuRenderContext,
|
|
||||||
_bbox: impl FnOnce() -> Rect,
|
|
||||||
) -> std::borrow::Cow<'b, PietGpuBrush> {
|
|
||||||
Cow::Borrowed(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn to_f32_2(point: Point) -> [f32; 2] {
|
|
||||||
[point.x as f32, point.y as f32]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rect_to_f32_4(rect: Rect) -> [f32; 4] {
|
|
||||||
[
|
|
||||||
rect.x0 as f32,
|
|
||||||
rect.y0 as f32,
|
|
||||||
rect.x1 as f32,
|
|
||||||
rect.y1 as f32,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_srgb(f: f64) -> f64 {
|
|
||||||
if DO_SRGB_CONVERSION {
|
|
||||||
if f <= 0.0031308 {
|
|
||||||
f * 12.92
|
|
||||||
} else {
|
|
||||||
let a = 0.055;
|
|
||||||
(1. + a) * f64::powf(f, f64::recip(2.4)) - a
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_srgb(f: f64) -> f64 {
|
|
||||||
if DO_SRGB_CONVERSION {
|
|
||||||
if f <= 0.04045 {
|
|
||||||
f / 12.92
|
|
||||||
} else {
|
|
||||||
let a = 0.055;
|
|
||||||
f64::powf((f + a) * f64::recip(1. + a), 2.4)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,10 +14,10 @@
|
||||||
//
|
//
|
||||||
// Also licensed under MIT license, at your choice.
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
use bytemuck::Pod;
|
|
||||||
use piet_gpu_hal::{CmdBuf, Error, Image, QueryPool, Semaphore, Session, SubmittedCmdBuf};
|
use piet_gpu_hal::{CmdBuf, Error, Image, QueryPool, Semaphore, Session, SubmittedCmdBuf};
|
||||||
|
use piet_scene::Scene;
|
||||||
|
|
||||||
use crate::{EncodedSceneRef, MemoryHeader, PietGpuRenderContext, Renderer, SceneStats};
|
use crate::{MemoryHeader, Renderer, SceneStats};
|
||||||
|
|
||||||
/// Additional logic for sequencing rendering operations, specifically
|
/// Additional logic for sequencing rendering operations, specifically
|
||||||
/// for handling failure and reallocation.
|
/// for handling failure and reallocation.
|
||||||
|
@ -86,22 +86,8 @@ impl RenderDriver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upload_render_ctx(
|
pub fn upload_scene(&mut self, session: &Session, scene: &Scene) -> Result<(), Error> {
|
||||||
&mut self,
|
let stats = SceneStats::from_scene(scene);
|
||||||
session: &Session,
|
|
||||||
render_ctx: &mut PietGpuRenderContext,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let stats = render_ctx.stats();
|
|
||||||
self.ensure_scene_buffers(session, &stats)?;
|
|
||||||
self.renderer.upload_render_ctx(render_ctx, self.buf_ix)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn upload_scene<T: Copy + Pod>(
|
|
||||||
&mut self,
|
|
||||||
session: &Session,
|
|
||||||
scene: &EncodedSceneRef<T>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let stats = scene.stats();
|
|
||||||
self.ensure_scene_buffers(session, &stats)?;
|
self.ensure_scene_buffers(session, &stats)?;
|
||||||
self.renderer.upload_scene(scene, self.buf_ix)
|
self.renderer.upload_scene(scene, self.buf_ix)
|
||||||
}
|
}
|
||||||
|
|
428
piet-gpu/src/samples.rs
Normal file
428
piet-gpu/src/samples.rs
Normal file
|
@ -0,0 +1,428 @@
|
||||||
|
use crate::PicoSvg;
|
||||||
|
use kurbo::BezPath;
|
||||||
|
use piet_scene::*;
|
||||||
|
|
||||||
|
use crate::SimpleText;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
const N_CIRCLES: usize = 0;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
||||||
|
use crate::pico_svg::*;
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
for item in &svg.items {
|
||||||
|
match item {
|
||||||
|
Item::Fill(fill) => {
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&fill.color.into(),
|
||||||
|
None,
|
||||||
|
convert_bez_path(&fill.path),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Item::Stroke(stroke) => {
|
||||||
|
sb.stroke(
|
||||||
|
&simple_stroke(stroke.width as f32),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&stroke.color.into(),
|
||||||
|
None,
|
||||||
|
convert_bez_path(&stroke.path),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if print_stats {
|
||||||
|
println!("flattening and encoding time: {:?}", start.elapsed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_tiger(sb: &mut SceneBuilder, print_stats: bool) {
|
||||||
|
use super::pico_svg::*;
|
||||||
|
let xml_str = std::str::from_utf8(include_bytes!("../Ghostscript_Tiger.svg")).unwrap();
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
let svg = PicoSvg::load(xml_str, 8.0).unwrap();
|
||||||
|
if print_stats {
|
||||||
|
println!("parsing time: {:?}", start.elapsed());
|
||||||
|
}
|
||||||
|
render_svg(sb, &svg, print_stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_scene(sb: &mut SceneBuilder) {
|
||||||
|
render_cardioid(sb);
|
||||||
|
render_clip_test(sb);
|
||||||
|
render_alpha_test(sb);
|
||||||
|
//render_tiger(sb, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render_cardioid(sb: &mut SceneBuilder) {
|
||||||
|
let n = 601;
|
||||||
|
let dth = std::f32::consts::PI * 2.0 / (n as f32);
|
||||||
|
let center = Point::new(1024.0, 768.0);
|
||||||
|
let r = 750.0;
|
||||||
|
let mut path = vec![];
|
||||||
|
for i in 1..n {
|
||||||
|
let mut p0 = center;
|
||||||
|
let a0 = i as f32 * dth;
|
||||||
|
p0.x += a0.cos() * r;
|
||||||
|
p0.y += a0.sin() * r;
|
||||||
|
let mut p1 = center;
|
||||||
|
let a1 = ((i * 2) % n) as f32 * dth;
|
||||||
|
p1.x += a1.cos() * r;
|
||||||
|
p1.y += a1.sin() * r;
|
||||||
|
path.push(PathElement::MoveTo(p0));
|
||||||
|
path.push(PathElement::LineTo(p1));
|
||||||
|
}
|
||||||
|
sb.stroke(
|
||||||
|
&simple_stroke(2.0),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgb8(0, 0, 0)),
|
||||||
|
None,
|
||||||
|
&path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render_clip_test(sb: &mut SceneBuilder) {
|
||||||
|
const N: usize = 16;
|
||||||
|
const X0: f32 = 50.0;
|
||||||
|
const Y0: f32 = 450.0;
|
||||||
|
// Note: if it gets much larger, it will exceed the 1MB scratch buffer.
|
||||||
|
// But this is a pretty demanding test.
|
||||||
|
const X1: f32 = 550.0;
|
||||||
|
const Y1: f32 = 950.0;
|
||||||
|
let step = 1.0 / ((N + 1) as f32);
|
||||||
|
for i in 0..N {
|
||||||
|
let t = ((i + 1) as f32) * step;
|
||||||
|
let path = &[
|
||||||
|
PathElement::MoveTo((X0, Y0).into()),
|
||||||
|
PathElement::LineTo((X1, Y0).into()),
|
||||||
|
PathElement::LineTo((X1, Y0 + t * (Y1 - Y0)).into()),
|
||||||
|
PathElement::LineTo((X1 + t * (X0 - X1), Y1).into()),
|
||||||
|
PathElement::LineTo((X0, Y1).into()),
|
||||||
|
PathElement::Close,
|
||||||
|
];
|
||||||
|
sb.push_layer(Mix::Clip.into(), Affine::IDENTITY, path);
|
||||||
|
}
|
||||||
|
let rect = Rect {
|
||||||
|
min: Point::new(X0, Y0),
|
||||||
|
max: Point::new(X1, Y1),
|
||||||
|
};
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgb8(0, 0, 0)),
|
||||||
|
None,
|
||||||
|
rect.elements(),
|
||||||
|
);
|
||||||
|
for _ in 0..N {
|
||||||
|
sb.pop_layer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render_alpha_test(sb: &mut SceneBuilder) {
|
||||||
|
// Alpha compositing tests.
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Color::rgb8(255, 0, 0).into(),
|
||||||
|
None,
|
||||||
|
make_diamond(1024.0, 100.0),
|
||||||
|
);
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Color::rgba8(0, 255, 0, 0x80).into(),
|
||||||
|
None,
|
||||||
|
make_diamond(1024.0, 125.0),
|
||||||
|
);
|
||||||
|
sb.push_layer(
|
||||||
|
Mix::Clip.into(),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
make_diamond(1024.0, 150.0),
|
||||||
|
);
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Color::rgba8(0, 0, 255, 0x80).into(),
|
||||||
|
None,
|
||||||
|
make_diamond(1024.0, 175.0),
|
||||||
|
);
|
||||||
|
sb.pop_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
||||||
|
const BLEND_MODES: &[Mix] = &[
|
||||||
|
Mix::Normal,
|
||||||
|
Mix::Multiply,
|
||||||
|
Mix::Darken,
|
||||||
|
Mix::Screen,
|
||||||
|
Mix::Lighten,
|
||||||
|
Mix::Overlay,
|
||||||
|
Mix::ColorDodge,
|
||||||
|
Mix::ColorBurn,
|
||||||
|
Mix::HardLight,
|
||||||
|
Mix::SoftLight,
|
||||||
|
Mix::Difference,
|
||||||
|
Mix::Exclusion,
|
||||||
|
Mix::Hue,
|
||||||
|
Mix::Saturation,
|
||||||
|
Mix::Color,
|
||||||
|
Mix::Luminosity,
|
||||||
|
];
|
||||||
|
for (ix, &blend) in BLEND_MODES.iter().enumerate() {
|
||||||
|
let i = ix % 4;
|
||||||
|
let j = ix / 4;
|
||||||
|
let transform = Affine::translate(i as f32 * 225., j as f32 * 225.);
|
||||||
|
let square = blend_square(blend.into());
|
||||||
|
sb.append(&square, Some(transform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affine) {
|
||||||
|
// Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
|
||||||
|
let rect = Rect::from_origin_size(Point::new(0., 0.), 200., 200.);
|
||||||
|
let stops = &[
|
||||||
|
GradientStop {
|
||||||
|
color: Color::rgb8(0, 0, 0),
|
||||||
|
offset: 0.0,
|
||||||
|
},
|
||||||
|
GradientStop {
|
||||||
|
color: Color::rgb8(255, 255, 255),
|
||||||
|
offset: 1.0,
|
||||||
|
},
|
||||||
|
][..];
|
||||||
|
let linear = Brush::LinearGradient(LinearGradient {
|
||||||
|
start: Point::new(0.0, 0.0),
|
||||||
|
end: Point::new(200.0, 0.0),
|
||||||
|
stops: stops.into(),
|
||||||
|
extend: ExtendMode::Pad,
|
||||||
|
});
|
||||||
|
sb.fill(Fill::NonZero, transform, &linear, None, rect.elements());
|
||||||
|
const GRADIENTS: &[(f32, f32, Color)] = &[
|
||||||
|
(150., 0., Color::rgb8(255, 240, 64)),
|
||||||
|
(175., 100., Color::rgb8(255, 96, 240)),
|
||||||
|
(125., 200., Color::rgb8(64, 192, 255)),
|
||||||
|
];
|
||||||
|
for (x, y, c) in GRADIENTS {
|
||||||
|
let mut color2 = c.clone();
|
||||||
|
color2.a = 0;
|
||||||
|
let stops = &[
|
||||||
|
GradientStop {
|
||||||
|
color: c.clone(),
|
||||||
|
offset: 0.0,
|
||||||
|
},
|
||||||
|
GradientStop {
|
||||||
|
color: color2,
|
||||||
|
offset: 1.0,
|
||||||
|
},
|
||||||
|
][..];
|
||||||
|
let rad = Brush::RadialGradient(RadialGradient {
|
||||||
|
center0: Point::new(*x, *y),
|
||||||
|
center1: Point::new(*x, *y),
|
||||||
|
radius0: 0.0,
|
||||||
|
radius1: 100.0,
|
||||||
|
stops: stops.into(),
|
||||||
|
extend: ExtendMode::Pad,
|
||||||
|
});
|
||||||
|
sb.fill(Fill::NonZero, transform, &rad, None, rect.elements());
|
||||||
|
}
|
||||||
|
const COLORS: &[Color] = &[
|
||||||
|
Color::rgb8(255, 0, 0),
|
||||||
|
Color::rgb8(0, 255, 0),
|
||||||
|
Color::rgb8(0, 0, 255),
|
||||||
|
];
|
||||||
|
sb.push_layer(Mix::Normal.into(), transform, rect.elements());
|
||||||
|
for (i, c) in COLORS.iter().enumerate() {
|
||||||
|
let stops = &[
|
||||||
|
GradientStop {
|
||||||
|
color: Color::rgb8(255, 255, 255),
|
||||||
|
offset: 0.0,
|
||||||
|
},
|
||||||
|
GradientStop {
|
||||||
|
color: c.clone(),
|
||||||
|
offset: 1.0,
|
||||||
|
},
|
||||||
|
][..];
|
||||||
|
let linear = Brush::LinearGradient(LinearGradient {
|
||||||
|
start: Point::new(0.0, 0.0),
|
||||||
|
end: Point::new(0.0, 200.0),
|
||||||
|
stops: stops.into(),
|
||||||
|
extend: ExtendMode::Pad,
|
||||||
|
});
|
||||||
|
sb.push_layer(blend, transform, rect.elements());
|
||||||
|
// squash the ellipse
|
||||||
|
let a = transform
|
||||||
|
* Affine::translate(100., 100.)
|
||||||
|
* Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32)
|
||||||
|
* Affine::scale(1.0, 0.357)
|
||||||
|
* Affine::translate(-100., -100.);
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
a,
|
||||||
|
&linear,
|
||||||
|
None,
|
||||||
|
make_ellipse(100., 100., 90., 90.),
|
||||||
|
);
|
||||||
|
sb.pop_layer();
|
||||||
|
}
|
||||||
|
sb.pop_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn blend_square(blend: BlendMode) -> SceneFragment {
|
||||||
|
let mut fragment = SceneFragment::default();
|
||||||
|
let mut sb = SceneBuilder::for_fragment(&mut fragment);
|
||||||
|
render_blend_square(&mut sb, blend, Affine::IDENTITY);
|
||||||
|
sb.finish();
|
||||||
|
fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) {
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgb8(128, 128, 128)),
|
||||||
|
None,
|
||||||
|
Rect::from_origin_size(Point::new(0.0, 0.0), 1000.0, 1000.0).elements(),
|
||||||
|
);
|
||||||
|
let text_size = 60.0 + 40.0 * (0.01 * i as f32).sin();
|
||||||
|
let s = "\u{1f600}hello piet-gpu text!";
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
text_size,
|
||||||
|
None,
|
||||||
|
Affine::translate(110.0, 600.0),
|
||||||
|
s,
|
||||||
|
);
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
text_size,
|
||||||
|
None,
|
||||||
|
Affine::translate(110.0, 700.0),
|
||||||
|
s,
|
||||||
|
);
|
||||||
|
let th = (std::f32::consts::PI / 180.0) * (i as f32);
|
||||||
|
let center = Point::new(500.0, 500.0);
|
||||||
|
let mut p1 = center;
|
||||||
|
p1.x += 400.0 * th.cos();
|
||||||
|
p1.y += 400.0 * th.sin();
|
||||||
|
sb.stroke(
|
||||||
|
&simple_stroke(5.0),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgb8(128, 0, 0)),
|
||||||
|
None,
|
||||||
|
&[PathElement::MoveTo(center), PathElement::LineTo(p1)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_brush_transform(sb: &mut SceneBuilder, i: usize) {
|
||||||
|
let th = (std::f32::consts::PI / 180.0) * (i as f32);
|
||||||
|
let stops = &[
|
||||||
|
GradientStop {
|
||||||
|
color: Color::rgb8(255, 0, 0),
|
||||||
|
offset: 0.0,
|
||||||
|
},
|
||||||
|
GradientStop {
|
||||||
|
color: Color::rgb8(0, 255, 0),
|
||||||
|
offset: 0.5,
|
||||||
|
},
|
||||||
|
GradientStop {
|
||||||
|
color: Color::rgb8(0, 0, 255),
|
||||||
|
offset: 1.0,
|
||||||
|
},
|
||||||
|
][..];
|
||||||
|
let linear = LinearGradient {
|
||||||
|
start: Point::new(0.0, 0.0),
|
||||||
|
end: Point::new(0.0, 200.0),
|
||||||
|
stops: stops.into(),
|
||||||
|
extend: ExtendMode::Pad,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::translate(200.0, 200.0),
|
||||||
|
&linear,
|
||||||
|
Some(Affine::rotate(th).around_center(200.0, 100.0)),
|
||||||
|
Rect::from_origin_size(Point::default(), 400.0, 200.0).elements(),
|
||||||
|
);
|
||||||
|
sb.stroke(
|
||||||
|
&simple_stroke(40.0),
|
||||||
|
Affine::translate(800.0, 200.0),
|
||||||
|
&linear,
|
||||||
|
Some(Affine::rotate(th).around_center(200.0, 100.0)),
|
||||||
|
Rect::from_origin_size(Point::default(), 400.0, 200.0).elements(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_bez_path<'a>(path: &'a BezPath) -> impl Iterator<Item = PathElement> + 'a + Clone {
|
||||||
|
path.elements()
|
||||||
|
.iter()
|
||||||
|
.map(|el| PathElement::from_kurbo(*el))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_ellipse(cx: f32, cy: f32, rx: f32, ry: f32) -> impl Iterator<Item = PathElement> + Clone {
|
||||||
|
let a = 0.551915024494;
|
||||||
|
let arx = a * rx;
|
||||||
|
let ary = a * ry;
|
||||||
|
let elements = [
|
||||||
|
PathElement::MoveTo(Point::new(cx + rx, cy)),
|
||||||
|
PathElement::CurveTo(
|
||||||
|
Point::new(cx + rx, cy + ary),
|
||||||
|
Point::new(cx + arx, cy + ry),
|
||||||
|
Point::new(cx, cy + ry),
|
||||||
|
),
|
||||||
|
PathElement::CurveTo(
|
||||||
|
Point::new(cx - arx, cy + ry),
|
||||||
|
Point::new(cx - rx, cy + ary),
|
||||||
|
Point::new(cx - rx, cy),
|
||||||
|
),
|
||||||
|
PathElement::CurveTo(
|
||||||
|
Point::new(cx - rx, cy - ary),
|
||||||
|
Point::new(cx - arx, cy - ry),
|
||||||
|
Point::new(cx, cy - ry),
|
||||||
|
),
|
||||||
|
PathElement::CurveTo(
|
||||||
|
Point::new(cx + arx, cy - ry),
|
||||||
|
Point::new(cx + rx, cy - ary),
|
||||||
|
Point::new(cx + rx, cy),
|
||||||
|
),
|
||||||
|
PathElement::Close,
|
||||||
|
];
|
||||||
|
(0..elements.len()).map(move |i| elements[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_diamond(cx: f32, cy: f32) -> impl Iterator<Item = PathElement> + Clone {
|
||||||
|
const SIZE: f32 = 50.0;
|
||||||
|
let elements = [
|
||||||
|
PathElement::MoveTo(Point::new(cx, cy - SIZE)),
|
||||||
|
PathElement::LineTo(Point::new(cx + SIZE, cy)),
|
||||||
|
PathElement::LineTo(Point::new(cx, cy + SIZE)),
|
||||||
|
PathElement::LineTo(Point::new(cx - SIZE, cy)),
|
||||||
|
PathElement::Close,
|
||||||
|
];
|
||||||
|
(0..elements.len()).map(move |i| elements[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simple_stroke(width: f32) -> Stroke<[f32; 0]> {
|
||||||
|
Stroke {
|
||||||
|
width,
|
||||||
|
join: Join::Round,
|
||||||
|
miter_limit: 1.4,
|
||||||
|
start_cap: Cap::Round,
|
||||||
|
end_cap: Cap::Round,
|
||||||
|
dash_pattern: [],
|
||||||
|
dash_offset: 0.0,
|
||||||
|
scale: true,
|
||||||
|
}
|
||||||
|
}
|
82
piet-gpu/src/simple_text.rs
Normal file
82
piet-gpu/src/simple_text.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2022 The piet-gpu authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
|
use piet_scene::glyph::{pinot, pinot::TableProvider, GlyphContext};
|
||||||
|
use piet_scene::{Affine, Brush, SceneBuilder};
|
||||||
|
|
||||||
|
pub use pinot::FontRef;
|
||||||
|
|
||||||
|
// This is very much a hack to get things working.
|
||||||
|
// On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji
|
||||||
|
const FONT_DATA: &[u8] = include_bytes!("../third-party/Roboto-Regular.ttf");
|
||||||
|
|
||||||
|
pub struct SimpleText {
|
||||||
|
gcx: GlyphContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleText {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
gcx: GlyphContext::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(
|
||||||
|
&mut self,
|
||||||
|
builder: &mut SceneBuilder,
|
||||||
|
font: Option<&FontRef>,
|
||||||
|
size: f32,
|
||||||
|
brush: Option<&Brush>,
|
||||||
|
transform: Affine,
|
||||||
|
text: &str,
|
||||||
|
) {
|
||||||
|
let font = font.unwrap_or(&FontRef {
|
||||||
|
data: FONT_DATA,
|
||||||
|
offset: 0,
|
||||||
|
});
|
||||||
|
if let Some(cmap) = font.cmap() {
|
||||||
|
if let Some(hmtx) = font.hmtx() {
|
||||||
|
let upem = font.head().map(|head| head.units_per_em()).unwrap_or(1000) as f32;
|
||||||
|
let scale = size / upem;
|
||||||
|
let vars: [(pinot::types::Tag, f32); 0] = [];
|
||||||
|
let mut provider = self.gcx.new_provider(font, None, size, false, vars);
|
||||||
|
let hmetrics = hmtx.hmetrics();
|
||||||
|
let default_advance = hmetrics
|
||||||
|
.get(hmetrics.len().saturating_sub(1))
|
||||||
|
.map(|h| h.advance_width)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let mut pen_x = 0f32;
|
||||||
|
for ch in text.chars() {
|
||||||
|
let gid = cmap.map(ch as u32).unwrap_or(0);
|
||||||
|
let advance = hmetrics
|
||||||
|
.get(gid as usize)
|
||||||
|
.map(|h| h.advance_width)
|
||||||
|
.unwrap_or(default_advance) as f32
|
||||||
|
* scale;
|
||||||
|
if let Some(glyph) = provider.get(gid, brush) {
|
||||||
|
if !glyph.is_empty() {
|
||||||
|
let xform = transform
|
||||||
|
* Affine::translate(pen_x, 0.0)
|
||||||
|
* Affine::scale(1.0, -1.0);
|
||||||
|
builder.append(&glyph, Some(xform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pen_x += advance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,6 @@
|
||||||
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
use piet::kurbo::Affine;
|
|
||||||
|
|
||||||
/// An affine transform.
|
/// An affine transform.
|
||||||
// This is equivalent to the version in piet-gpu-types, but the bytemuck
|
// This is equivalent to the version in piet-gpu-types, but the bytemuck
|
||||||
// representation will likely be faster.
|
// representation will likely be faster.
|
||||||
|
@ -35,23 +33,4 @@ impl Transform {
|
||||||
mat: [1.0, 0.0, 0.0, 1.0],
|
mat: [1.0, 0.0, 0.0, 1.0],
|
||||||
translate: [0.0, 0.0],
|
translate: [0.0, 0.0],
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from_kurbo(a: Affine) -> Transform {
|
|
||||||
let c = a.as_coeffs();
|
|
||||||
Transform {
|
|
||||||
mat: [c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32],
|
|
||||||
translate: [c[4] as f32, c[5] as f32],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_kurbo(self) -> Affine {
|
|
||||||
Affine::new([
|
|
||||||
self.mat[0] as f64,
|
|
||||||
self.mat[1] as f64,
|
|
||||||
self.mat[2] as f64,
|
|
||||||
self.mat[3] as f64,
|
|
||||||
self.translate[0] as f64,
|
|
||||||
self.translate[1] as f64,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,332 +0,0 @@
|
||||||
//! Various synthetic scenes for exercising the renderer.
|
|
||||||
|
|
||||||
use rand::{Rng, RngCore};
|
|
||||||
|
|
||||||
use crate::{Blend, BlendMode, Colrv1RadialGradient, PietGpuRenderContext};
|
|
||||||
use piet::kurbo::{Affine, BezPath, Circle, Line, Point, Rect, Shape};
|
|
||||||
use piet::{
|
|
||||||
Color, GradientStop, LinearGradient, Text, TextAttribute, TextLayoutBuilder, UnitPoint,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{PicoSvg, RenderContext, Vec2};
|
|
||||||
|
|
||||||
const N_CIRCLES: usize = 0;
|
|
||||||
|
|
||||||
pub fn render_blend_test(rc: &mut PietGpuRenderContext, i: usize, blend: Blend) {
|
|
||||||
rc.fill(Rect::new(400., 400., 800., 800.), &Color::rgb8(0, 0, 200));
|
|
||||||
rc.save().unwrap();
|
|
||||||
rc.blend(Rect::new(0., 0., 1000., 1000.), blend);
|
|
||||||
rc.transform(Affine::translate(Vec2::new(600., 600.)) * Affine::rotate(0.01 * i as f64));
|
|
||||||
rc.fill(Rect::new(0., 0., 400., 400.), &Color::rgba8(255, 0, 0, 255));
|
|
||||||
rc.restore().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_svg(rc: &mut impl RenderContext, svg: &PicoSvg) {
|
|
||||||
let start = std::time::Instant::now();
|
|
||||||
svg.render(rc);
|
|
||||||
println!("flattening and encoding time: {:?}", start.elapsed());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_scene(rc: &mut PietGpuRenderContext) {
|
|
||||||
const WIDTH: usize = 2048;
|
|
||||||
const HEIGHT: usize = 1536;
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
for _ in 0..N_CIRCLES {
|
|
||||||
let color = Color::from_rgba32_u32(rng.next_u32());
|
|
||||||
let center = Point::new(
|
|
||||||
rng.gen_range(0.0..WIDTH as f64),
|
|
||||||
rng.gen_range(0.0..HEIGHT as f64),
|
|
||||||
);
|
|
||||||
let radius = rng.gen_range(0.0..50.0);
|
|
||||||
let circle = Circle::new(center, radius);
|
|
||||||
rc.fill(circle, &color);
|
|
||||||
}
|
|
||||||
let _ = rc.save();
|
|
||||||
let mut path = BezPath::new();
|
|
||||||
path.move_to((200.0, 150.0));
|
|
||||||
path.line_to((100.0, 200.0));
|
|
||||||
path.line_to((150.0, 250.0));
|
|
||||||
path.close_path();
|
|
||||||
rc.clip(path);
|
|
||||||
|
|
||||||
let mut path = BezPath::new();
|
|
||||||
path.move_to((100.0, 150.0));
|
|
||||||
path.line_to((200.0, 200.0));
|
|
||||||
path.line_to((150.0, 250.0));
|
|
||||||
path.close_path();
|
|
||||||
rc.fill(path, &Color::rgb8(128, 0, 128));
|
|
||||||
let _ = rc.restore();
|
|
||||||
rc.stroke(
|
|
||||||
piet::kurbo::Line::new((100.0, 100.0), (200.0, 150.0)),
|
|
||||||
&Color::WHITE,
|
|
||||||
5.0,
|
|
||||||
);
|
|
||||||
//render_cardioid(rc);
|
|
||||||
render_clip_test(rc);
|
|
||||||
render_alpha_test(rc);
|
|
||||||
render_gradient_test(rc);
|
|
||||||
render_text_test(rc);
|
|
||||||
//render_tiger(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn render_cardioid(rc: &mut impl RenderContext) {
|
|
||||||
let n = 601;
|
|
||||||
let dth = std::f64::consts::PI * 2.0 / (n as f64);
|
|
||||||
let center = Point::new(1024.0, 768.0);
|
|
||||||
let r = 750.0;
|
|
||||||
let mut path = BezPath::new();
|
|
||||||
for i in 1..n {
|
|
||||||
let p0 = center + Vec2::from_angle(i as f64 * dth) * r;
|
|
||||||
let p1 = center + Vec2::from_angle(((i * 2) % n) as f64 * dth) * r;
|
|
||||||
//rc.fill(&Circle::new(p0, 8.0), &Color::WHITE);
|
|
||||||
path.move_to(p0);
|
|
||||||
path.line_to(p1);
|
|
||||||
//rc.stroke(Line::new(p0, p1), &Color::BLACK, 2.0);
|
|
||||||
}
|
|
||||||
rc.stroke(&path, &Color::BLACK, 2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn render_clip_test(rc: &mut impl RenderContext) {
|
|
||||||
const N: usize = 16;
|
|
||||||
const X0: f64 = 50.0;
|
|
||||||
const Y0: f64 = 450.0;
|
|
||||||
// Note: if it gets much larger, it will exceed the 1MB scratch buffer.
|
|
||||||
// But this is a pretty demanding test.
|
|
||||||
const X1: f64 = 550.0;
|
|
||||||
const Y1: f64 = 950.0;
|
|
||||||
let step = 1.0 / ((N + 1) as f64);
|
|
||||||
for i in 0..N {
|
|
||||||
let t = ((i + 1) as f64) * step;
|
|
||||||
rc.save();
|
|
||||||
let mut path = BezPath::new();
|
|
||||||
path.move_to((X0, Y0));
|
|
||||||
path.line_to((X1, Y0));
|
|
||||||
path.line_to((X1, Y0 + t * (Y1 - Y0)));
|
|
||||||
path.line_to((X1 + t * (X0 - X1), Y1));
|
|
||||||
path.line_to((X0, Y1));
|
|
||||||
path.close_path();
|
|
||||||
rc.clip(path);
|
|
||||||
}
|
|
||||||
let rect = piet::kurbo::Rect::new(X0, Y0, X1, Y1);
|
|
||||||
rc.fill(rect, &Color::BLACK);
|
|
||||||
for _ in 0..N {
|
|
||||||
rc.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn render_alpha_test(rc: &mut impl RenderContext) {
|
|
||||||
// Alpha compositing tests.
|
|
||||||
rc.fill(
|
|
||||||
diamond(Point::new(1024.0, 100.0)),
|
|
||||||
&Color::Rgba32(0xff0000ff),
|
|
||||||
);
|
|
||||||
rc.fill(
|
|
||||||
diamond(Point::new(1024.0, 125.0)),
|
|
||||||
&Color::Rgba32(0x00ff0080),
|
|
||||||
);
|
|
||||||
rc.save();
|
|
||||||
rc.clip(diamond(Point::new(1024.0, 150.0)));
|
|
||||||
rc.fill(
|
|
||||||
diamond(Point::new(1024.0, 175.0)),
|
|
||||||
&Color::Rgba32(0x0000ff80),
|
|
||||||
);
|
|
||||||
rc.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn render_gradient_test(rc: &mut PietGpuRenderContext) {
|
|
||||||
let stops = vec![
|
|
||||||
GradientStop {
|
|
||||||
color: Color::rgb8(0, 255, 0),
|
|
||||||
pos: 0.0,
|
|
||||||
},
|
|
||||||
GradientStop {
|
|
||||||
color: Color::BLACK,
|
|
||||||
pos: 1.0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let rad = Colrv1RadialGradient {
|
|
||||||
center0: Point::new(200.0, 200.0),
|
|
||||||
center1: Point::new(250.0, 200.0),
|
|
||||||
radius0: 50.0,
|
|
||||||
radius1: 100.0,
|
|
||||||
stops,
|
|
||||||
};
|
|
||||||
let brush = rc.radial_gradient_colrv1(&rad);
|
|
||||||
//let brush = FixedGradient::Radial(rad);
|
|
||||||
//let brush = Color::rgb8(0, 128, 0);
|
|
||||||
let transform = Affine::new([1.0, 0.0, 0.0, 0.5, 0.0, 100.0]);
|
|
||||||
rc.fill_transform(Rect::new(100.0, 100.0, 300.0, 300.0), &brush, transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diamond(origin: Point) -> impl Shape {
|
|
||||||
let mut path = BezPath::new();
|
|
||||||
const SIZE: f64 = 50.0;
|
|
||||||
path.move_to((origin.x, origin.y - SIZE));
|
|
||||||
path.line_to((origin.x + SIZE, origin.y));
|
|
||||||
path.line_to((origin.x, origin.y + SIZE));
|
|
||||||
path.line_to((origin.x - SIZE, origin.y));
|
|
||||||
path.close_path();
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn render_text_test(rc: &mut impl RenderContext) {
|
|
||||||
rc.save();
|
|
||||||
//rc.transform(Affine::new([0.2, 0.0, 0.0, -0.2, 200.0, 800.0]));
|
|
||||||
let layout = rc
|
|
||||||
.text()
|
|
||||||
.new_text_layout("\u{1f600}hello piet-gpu text!")
|
|
||||||
.default_attribute(TextAttribute::FontSize(100.0))
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
rc.draw_text(&layout, Point::new(110.0, 600.0));
|
|
||||||
rc.draw_text(&layout, Point::new(110.0, 700.0));
|
|
||||||
rc.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn render_tiger(rc: &mut impl RenderContext) {
|
|
||||||
let xml_str = std::str::from_utf8(include_bytes!("../Ghostscript_Tiger.svg")).unwrap();
|
|
||||||
let start = std::time::Instant::now();
|
|
||||||
let svg = PicoSvg::load(xml_str, 8.0).unwrap();
|
|
||||||
println!("parsing time: {:?}", start.elapsed());
|
|
||||||
|
|
||||||
let start = std::time::Instant::now();
|
|
||||||
svg.render(rc);
|
|
||||||
println!("flattening and encoding time: {:?}", start.elapsed());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_blend_square(rc: &mut PietGpuRenderContext, blend: Blend) {
|
|
||||||
// Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
|
|
||||||
let rect = Rect::new(0., 0., 200., 200.);
|
|
||||||
let stops = vec![
|
|
||||||
GradientStop {
|
|
||||||
color: Color::BLACK,
|
|
||||||
pos: 0.0,
|
|
||||||
},
|
|
||||||
GradientStop {
|
|
||||||
color: Color::WHITE,
|
|
||||||
pos: 1.0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let linear = LinearGradient::new(UnitPoint::LEFT, UnitPoint::RIGHT, stops);
|
|
||||||
rc.fill(rect, &linear);
|
|
||||||
const GRADIENTS: &[(f64, f64, Color)] = &[
|
|
||||||
(150., 0., Color::rgb8(255, 240, 64)),
|
|
||||||
(175., 100., Color::rgb8(255, 96, 240)),
|
|
||||||
(125., 200., Color::rgb8(64, 192, 255)),
|
|
||||||
];
|
|
||||||
for (x, y, c) in GRADIENTS {
|
|
||||||
let stops = vec![
|
|
||||||
GradientStop {
|
|
||||||
color: c.clone(),
|
|
||||||
pos: 0.0,
|
|
||||||
},
|
|
||||||
GradientStop {
|
|
||||||
color: Color::rgba8(0, 0, 0, 0),
|
|
||||||
pos: 1.0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let rad = Colrv1RadialGradient {
|
|
||||||
center0: Point::new(*x, *y),
|
|
||||||
center1: Point::new(*x, *y),
|
|
||||||
radius0: 0.0,
|
|
||||||
radius1: 100.0,
|
|
||||||
stops,
|
|
||||||
};
|
|
||||||
let brush = rc.radial_gradient_colrv1(&rad);
|
|
||||||
rc.fill(Rect::new(0., 0., 200., 200.), &brush);
|
|
||||||
}
|
|
||||||
const COLORS: &[Color] = &[
|
|
||||||
Color::rgb8(255, 0, 0),
|
|
||||||
Color::rgb8(0, 255, 0),
|
|
||||||
Color::rgb8(0, 0, 255),
|
|
||||||
];
|
|
||||||
let _ = rc.with_save(|rc| {
|
|
||||||
// Isolation (this can be removed for non-isolated version)
|
|
||||||
rc.blend(rect, BlendMode::Normal.into());
|
|
||||||
for (i, c) in COLORS.iter().enumerate() {
|
|
||||||
let stops = vec![
|
|
||||||
GradientStop {
|
|
||||||
color: Color::WHITE,
|
|
||||||
pos: 0.0,
|
|
||||||
},
|
|
||||||
GradientStop {
|
|
||||||
color: c.clone(),
|
|
||||||
pos: 1.0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// squash the ellipse
|
|
||||||
let a = Affine::translate((100., 100.))
|
|
||||||
* Affine::rotate(std::f64::consts::FRAC_PI_3 * (i * 2 + 1) as f64)
|
|
||||||
* Affine::scale_non_uniform(1.0, 0.357)
|
|
||||||
* Affine::translate((-100., -100.));
|
|
||||||
let linear = LinearGradient::new(UnitPoint::TOP, UnitPoint::BOTTOM, stops);
|
|
||||||
let _ = rc.with_save(|rc| {
|
|
||||||
rc.blend(rect, blend);
|
|
||||||
rc.transform(a);
|
|
||||||
rc.fill(Circle::new((100., 100.), 90.), &linear);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_blend_grid(rc: &mut PietGpuRenderContext) {
|
|
||||||
const BLEND_MODES: &[BlendMode] = &[
|
|
||||||
BlendMode::Normal,
|
|
||||||
BlendMode::Multiply,
|
|
||||||
BlendMode::Darken,
|
|
||||||
BlendMode::Screen,
|
|
||||||
BlendMode::Lighten,
|
|
||||||
BlendMode::Overlay,
|
|
||||||
BlendMode::ColorDodge,
|
|
||||||
BlendMode::ColorBurn,
|
|
||||||
BlendMode::HardLight,
|
|
||||||
BlendMode::SoftLight,
|
|
||||||
BlendMode::Difference,
|
|
||||||
BlendMode::Exclusion,
|
|
||||||
BlendMode::Hue,
|
|
||||||
BlendMode::Saturation,
|
|
||||||
BlendMode::Color,
|
|
||||||
BlendMode::Luminosity,
|
|
||||||
];
|
|
||||||
for (ix, &blend) in BLEND_MODES.iter().enumerate() {
|
|
||||||
let _ = rc.with_save(|rc| {
|
|
||||||
let i = ix % 4;
|
|
||||||
let j = ix / 4;
|
|
||||||
rc.transform(Affine::translate((i as f64 * 225., j as f64 * 225.)));
|
|
||||||
render_blend_square(rc, blend.into());
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_anim_frame(rc: &mut impl RenderContext, i: usize) {
|
|
||||||
rc.fill(
|
|
||||||
Rect::new(0.0, 0.0, 1000.0, 1000.0),
|
|
||||||
&Color::rgb8(128, 128, 128),
|
|
||||||
);
|
|
||||||
let text_size = 60.0 + 40.0 * (0.01 * i as f64).sin();
|
|
||||||
rc.save().unwrap();
|
|
||||||
//rc.transform(Affine::new([0.2, 0.0, 0.0, -0.2, 200.0, 800.0]));
|
|
||||||
let layout = rc
|
|
||||||
.text()
|
|
||||||
.new_text_layout("\u{1f600}hello piet-gpu text!")
|
|
||||||
.default_attribute(TextAttribute::FontSize(text_size))
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
rc.draw_text(&layout, Point::new(110.0, 600.0));
|
|
||||||
rc.draw_text(&layout, Point::new(110.0, 700.0));
|
|
||||||
rc.restore().unwrap();
|
|
||||||
let th = (std::f64::consts::PI / 180.0) * (i as f64);
|
|
||||||
let center = Point::new(500.0, 500.0);
|
|
||||||
let p1 = center + 400.0 * Vec2::from_angle(th);
|
|
||||||
let line = Line::new(center, p1);
|
|
||||||
rc.stroke(line, &Color::rgb8(128, 0, 0), 5.0);
|
|
||||||
}
|
|
|
@ -1,271 +0,0 @@
|
||||||
use std::ops::RangeBounds;
|
|
||||||
|
|
||||||
use swash::scale::{ScaleContext, Scaler};
|
|
||||||
use swash::zeno::{Vector, Verb};
|
|
||||||
use swash::{FontRef, GlyphId};
|
|
||||||
|
|
||||||
use piet::kurbo::{Point, Rect, Size};
|
|
||||||
use piet::{
|
|
||||||
Error, FontFamily, HitTestPoint, HitTestPosition, LineMetric, RenderContext, Text,
|
|
||||||
TextAttribute, TextLayout, TextLayoutBuilder, TextStorage,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::encoder::GlyphEncoder;
|
|
||||||
use crate::render_ctx;
|
|
||||||
use crate::stages::Transform;
|
|
||||||
use crate::PietGpuRenderContext;
|
|
||||||
|
|
||||||
// This is very much a hack to get things working.
|
|
||||||
// On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji
|
|
||||||
const FONT_DATA: &[u8] = include_bytes!("../third-party/Roboto-Regular.ttf");
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Font {
|
|
||||||
// Storing the font_ref is ok for static font data, but the better way to do
|
|
||||||
// this is to store the CacheKey.
|
|
||||||
font_ref: FontRef<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PietGpuText {
|
|
||||||
font: Font,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PietGpuTextLayout {
|
|
||||||
font: Font,
|
|
||||||
size: f64,
|
|
||||||
glyphs: Vec<Glyph>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PietGpuTextLayoutBuilder {
|
|
||||||
font: Font,
|
|
||||||
text: String,
|
|
||||||
size: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct Glyph {
|
|
||||||
glyph_id: GlyphId,
|
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TextRenderCtx<'a> {
|
|
||||||
scaler: Scaler<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PietGpuText {
|
|
||||||
pub(crate) fn new(font: Font) -> PietGpuText {
|
|
||||||
PietGpuText { font }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Text for PietGpuText {
|
|
||||||
type TextLayout = PietGpuTextLayout;
|
|
||||||
type TextLayoutBuilder = PietGpuTextLayoutBuilder;
|
|
||||||
|
|
||||||
fn load_font(&mut self, _data: &[u8]) -> Result<FontFamily, Error> {
|
|
||||||
Ok(FontFamily::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_text_layout(&mut self, text: impl TextStorage) -> Self::TextLayoutBuilder {
|
|
||||||
PietGpuTextLayoutBuilder::new(&self.font, &text.as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn font_family(&mut self, _family_name: &str) -> Option<FontFamily> {
|
|
||||||
Some(FontFamily::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextLayout for PietGpuTextLayout {
|
|
||||||
fn size(&self) -> Size {
|
|
||||||
Size::ZERO
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_bounds(&self) -> Rect {
|
|
||||||
Rect::ZERO
|
|
||||||
}
|
|
||||||
|
|
||||||
fn line_text(&self, _line_number: usize) -> Option<&str> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn line_metric(&self, _line_number: usize) -> Option<LineMetric> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn line_count(&self) -> usize {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hit_test_point(&self, _point: Point) -> HitTestPoint {
|
|
||||||
HitTestPoint::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hit_test_text_position(&self, _text_position: usize) -> HitTestPosition {
|
|
||||||
HitTestPosition::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text(&self) -> &str {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Font {
|
|
||||||
pub fn new() -> Font {
|
|
||||||
let font_ref = FontRef::from_index(FONT_DATA, 0).expect("error parsing font");
|
|
||||||
Font { font_ref }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_path<'a>(&self, glyph_id: GlyphId, tc: &mut TextRenderCtx<'a>) -> GlyphEncoder {
|
|
||||||
let mut encoder = GlyphEncoder::default();
|
|
||||||
if tc.scaler.has_color_outlines() {
|
|
||||||
if let Some(outline) = tc.scaler.scale_color_outline(glyph_id) {
|
|
||||||
// TODO: be more sophisticated choosing a palette
|
|
||||||
let palette = self.font_ref.color_palettes().next().unwrap();
|
|
||||||
let mut i = 0;
|
|
||||||
while let Some(layer) = outline.get(i) {
|
|
||||||
if let Some(color_ix) = layer.color_index() {
|
|
||||||
let color = palette.get(color_ix);
|
|
||||||
append_outline(&mut encoder, layer.verbs(), layer.points());
|
|
||||||
encoder.fill_color(*bytemuck::from_bytes(&color));
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
return encoder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(outline) = tc.scaler.scale_outline(glyph_id) {
|
|
||||||
append_outline(&mut encoder, outline.verbs(), outline.points());
|
|
||||||
}
|
|
||||||
encoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PietGpuTextLayout {
|
|
||||||
pub(crate) fn make_layout(font: &Font, text: &str, size: f64) -> PietGpuTextLayout {
|
|
||||||
let mut glyphs = Vec::new();
|
|
||||||
let mut x = 0.0;
|
|
||||||
let y = 0.0;
|
|
||||||
for c in text.chars() {
|
|
||||||
let glyph_id = font.font_ref.charmap().map(c);
|
|
||||||
let glyph = Glyph { glyph_id, x, y };
|
|
||||||
glyphs.push(glyph);
|
|
||||||
let adv = font.font_ref.glyph_metrics(&[]).advance_width(glyph_id);
|
|
||||||
x += adv;
|
|
||||||
}
|
|
||||||
PietGpuTextLayout {
|
|
||||||
glyphs,
|
|
||||||
font: font.clone(),
|
|
||||||
size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn draw_text(&self, ctx: &mut PietGpuRenderContext, pos: Point) {
|
|
||||||
let mut scale_ctx = ScaleContext::new();
|
|
||||||
let scaler = scale_ctx.builder(self.font.font_ref).size(2048.).build();
|
|
||||||
let mut tc = TextRenderCtx { scaler };
|
|
||||||
// Should we use ppem from font, or let swash scale?
|
|
||||||
const DEFAULT_UPEM: u16 = 2048;
|
|
||||||
let scale = self.size as f32 / DEFAULT_UPEM as f32;
|
|
||||||
ctx.save().unwrap();
|
|
||||||
// TODO: handle y offsets also
|
|
||||||
for glyph in &self.glyphs {
|
|
||||||
let tpos = render_ctx::to_f32_2(pos);
|
|
||||||
let transform = Transform {
|
|
||||||
mat: [scale, 0.0, 0.0, -scale],
|
|
||||||
translate: [tpos[0] + scale * glyph.x, tpos[1]],
|
|
||||||
};
|
|
||||||
//println!("{:?}, {:?}", transform.mat, transform.translate);
|
|
||||||
ctx.encode_transform(transform);
|
|
||||||
let glyph = self.font.make_path(glyph.glyph_id, &mut tc);
|
|
||||||
ctx.encode_glyph(&glyph);
|
|
||||||
if !glyph.is_color() {
|
|
||||||
ctx.fill_glyph(0xff_ff_ff_ff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.restore().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PietGpuTextLayoutBuilder {
|
|
||||||
pub(crate) fn new(font: &Font, text: &str) -> PietGpuTextLayoutBuilder {
|
|
||||||
PietGpuTextLayoutBuilder {
|
|
||||||
font: font.clone(),
|
|
||||||
text: text.to_owned(),
|
|
||||||
size: 12.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextLayoutBuilder for PietGpuTextLayoutBuilder {
|
|
||||||
type Out = PietGpuTextLayout;
|
|
||||||
|
|
||||||
fn max_width(self, _width: f64) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alignment(self, _alignment: piet::TextAlignment) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_attribute(mut self, attribute: impl Into<TextAttribute>) -> Self {
|
|
||||||
let attribute = attribute.into();
|
|
||||||
match attribute {
|
|
||||||
TextAttribute::FontSize(size) => self.size = size,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn range_attribute(
|
|
||||||
self,
|
|
||||||
_range: impl RangeBounds<usize>,
|
|
||||||
_attribute: impl Into<TextAttribute>,
|
|
||||||
) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build(self) -> Result<Self::Out, Error> {
|
|
||||||
Ok(PietGpuTextLayout::make_layout(
|
|
||||||
&self.font, &self.text, self.size,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn append_outline(encoder: &mut GlyphEncoder, verbs: &[Verb], points: &[Vector]) {
|
|
||||||
let mut path_encoder = encoder.path_encoder();
|
|
||||||
let mut i = 0;
|
|
||||||
for verb in verbs {
|
|
||||||
match verb {
|
|
||||||
Verb::MoveTo => {
|
|
||||||
let p = points[i];
|
|
||||||
path_encoder.move_to(p.x, p.y);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
Verb::LineTo => {
|
|
||||||
let p = points[i];
|
|
||||||
path_encoder.line_to(p.x, p.y);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
Verb::QuadTo => {
|
|
||||||
let p1 = points[i];
|
|
||||||
let p2 = points[i + 1];
|
|
||||||
path_encoder.quad_to(p1.x, p1.y, p2.x, p2.y);
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
Verb::CurveTo => {
|
|
||||||
let p1 = points[i];
|
|
||||||
let p2 = points[i + 1];
|
|
||||||
let p3 = points[i + 2];
|
|
||||||
path_encoder.cubic_to(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
i += 3;
|
|
||||||
}
|
|
||||||
Verb::Close => path_encoder.close_path(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
path_encoder.path();
|
|
||||||
let n_pathseg = path_encoder.n_pathseg();
|
|
||||||
encoder.finish_path(n_pathseg);
|
|
||||||
}
|
|
|
@ -14,6 +14,7 @@
|
||||||
//
|
//
|
||||||
// Also licensed under MIT license, at your choice.
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
|
/// 32-bit RGBA color.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
pub r: u8,
|
pub r: u8,
|
||||||
|
|
|
@ -15,17 +15,19 @@
|
||||||
// Also licensed under MIT license, at your choice.
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
use super::color::Color;
|
use super::color::Color;
|
||||||
|
use super::ExtendMode;
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
/// Offset and color of a transition point in a gradient.
|
||||||
#[derive(Copy, Clone, PartialOrd, Default, Debug)]
|
#[derive(Copy, Clone, PartialOrd, Default, Debug)]
|
||||||
pub struct Stop {
|
pub struct GradientStop {
|
||||||
pub offset: f32,
|
pub offset: f32,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Stop {
|
impl Hash for GradientStop {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.offset.to_bits().hash(state);
|
self.offset.to_bits().hash(state);
|
||||||
self.color.hash(state);
|
self.color.hash(state);
|
||||||
|
@ -33,46 +35,46 @@ impl Hash for Stop {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override PartialEq to use to_bits for the offset to match with the Hash impl
|
// Override PartialEq to use to_bits for the offset to match with the Hash impl
|
||||||
impl std::cmp::PartialEq for Stop {
|
impl std::cmp::PartialEq for GradientStop {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.offset.to_bits() == other.offset.to_bits() && self.color == other.color
|
self.offset.to_bits() == other.offset.to_bits() && self.color == other.color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::Eq for Stop {}
|
impl std::cmp::Eq for GradientStop {}
|
||||||
|
|
||||||
pub type StopVec = SmallVec<[Stop; 4]>;
|
/// Collection of gradient stops.
|
||||||
|
pub type GradientStops = SmallVec<[GradientStop; 4]>;
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
pub enum Extend {
|
|
||||||
Pad,
|
|
||||||
Repeat,
|
|
||||||
Reflect,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Definition of a gradient that transitions between two or more colors along
|
||||||
|
/// a line.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct LinearGradient {
|
pub struct LinearGradient {
|
||||||
pub start: Point,
|
pub start: Point,
|
||||||
pub end: Point,
|
pub end: Point,
|
||||||
pub stops: StopVec,
|
pub stops: GradientStops,
|
||||||
pub extend: Extend,
|
pub extend: ExtendMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Definition of a gradient that transitions between two or more colors that
|
||||||
|
/// radiate from an origin.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RadialGradient {
|
pub struct RadialGradient {
|
||||||
pub center0: Point,
|
pub center0: Point,
|
||||||
pub radius0: f32,
|
pub radius0: f32,
|
||||||
pub center1: Point,
|
pub center1: Point,
|
||||||
pub radius1: f32,
|
pub radius1: f32,
|
||||||
pub stops: StopVec,
|
pub stops: GradientStops,
|
||||||
pub extend: Extend,
|
pub extend: ExtendMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Definition gradient that transitions between two or more colors that rotate
|
||||||
|
/// around a center point.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SweepGradient {
|
pub struct SweepGradient {
|
||||||
pub center: Point,
|
pub center: Point,
|
||||||
pub start_angle: f32,
|
pub start_angle: f32,
|
||||||
pub end_angle: f32,
|
pub end_angle: f32,
|
||||||
pub stops: StopVec,
|
pub stops: GradientStops,
|
||||||
pub extend: Extend,
|
pub extend: ExtendMode,
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,53 +18,35 @@ use std::result::Result;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
/// Image data resource.
|
||||||
pub enum Format {
|
|
||||||
A8,
|
|
||||||
Rgba8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Format {
|
|
||||||
pub fn data_size(self, width: u32, height: u32) -> Option<usize> {
|
|
||||||
(width as usize)
|
|
||||||
.checked_mul(height as usize)
|
|
||||||
.and_then(|size| {
|
|
||||||
size.checked_mul(match self {
|
|
||||||
Self::A8 => 1,
|
|
||||||
Self::Rgba8 => 4,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Image(Arc<Inner>);
|
pub struct Image(Arc<Inner>);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
id: u64,
|
id: u64,
|
||||||
format: Format,
|
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
data: Vec<u8>,
|
data: Arc<[u8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
format: Format,
|
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
mut data: Vec<u8>,
|
data: impl Into<Arc<[u8]>>,
|
||||||
) -> Result<Self, DataSizeError> {
|
) -> Result<Self, ImageDataSizeError> {
|
||||||
let data_size = format.data_size(width, height).ok_or(DataSizeError)?;
|
let data_size = width
|
||||||
|
.checked_mul(height)
|
||||||
|
.and_then(|x| x.checked_mul(4))
|
||||||
|
.ok_or(ImageDataSizeError)? as usize;
|
||||||
|
let data = data.into();
|
||||||
if data.len() < data_size {
|
if data.len() < data_size {
|
||||||
return Err(DataSizeError);
|
return Err(ImageDataSizeError);
|
||||||
}
|
}
|
||||||
data.truncate(data_size);
|
|
||||||
static ID: AtomicU64 = AtomicU64::new(1);
|
static ID: AtomicU64 = AtomicU64::new(1);
|
||||||
Ok(Self(Arc::new(Inner {
|
Ok(Self(Arc::new(Inner {
|
||||||
id: ID.fetch_add(1, Ordering::Relaxed),
|
id: ID.fetch_add(1, Ordering::Relaxed),
|
||||||
format,
|
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
data,
|
data,
|
||||||
|
@ -75,10 +57,6 @@ impl Image {
|
||||||
self.0.id
|
self.0.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(&self) -> Format {
|
|
||||||
self.0.format
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn width(&self) -> u32 {
|
pub fn width(&self) -> u32 {
|
||||||
self.0.width
|
self.0.width
|
||||||
}
|
}
|
||||||
|
@ -92,5 +70,7 @@ impl Image {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error returned when image data size is not sufficient for the specified
|
||||||
|
/// dimensions.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DataSizeError;
|
pub struct ImageDataSizeError;
|
||||||
|
|
|
@ -22,8 +22,7 @@ pub use color::Color;
|
||||||
pub use gradient::*;
|
pub use gradient::*;
|
||||||
pub use image::*;
|
pub use image::*;
|
||||||
|
|
||||||
use crate::resource::PersistentBrush;
|
/// Describes the content of a filled or stroked shape.
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Brush {
|
pub enum Brush {
|
||||||
Solid(Color),
|
Solid(Color),
|
||||||
|
@ -31,5 +30,31 @@ pub enum Brush {
|
||||||
RadialGradient(RadialGradient),
|
RadialGradient(RadialGradient),
|
||||||
SweepGradient(SweepGradient),
|
SweepGradient(SweepGradient),
|
||||||
Image(Image),
|
Image(Image),
|
||||||
Persistent(PersistentBrush),
|
}
|
||||||
|
|
||||||
|
/// Defines how a brush is extended when the content does not
|
||||||
|
/// completely fill a shape.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub enum ExtendMode {
|
||||||
|
Pad,
|
||||||
|
Repeat,
|
||||||
|
Reflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Color> for Brush {
|
||||||
|
fn from(c: Color) -> Self {
|
||||||
|
Self::Solid(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LinearGradient> for Brush {
|
||||||
|
fn from(g: LinearGradient) -> Self {
|
||||||
|
Self::LinearGradient(g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadialGradient> for Brush {
|
||||||
|
fn from(g: RadialGradient) -> Self {
|
||||||
|
Self::RadialGradient(g)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,14 @@ impl Rect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new rectangle from an origin point and dimensions.
|
||||||
|
pub fn from_origin_size(origin: Point, width: f32, height: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
min: origin,
|
||||||
|
max: Point::new(origin.x + width, origin.y + height),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the width of the rectangle.
|
/// Returns the width of the rectangle.
|
||||||
pub fn width(&self) -> f32 {
|
pub fn width(&self) -> f32 {
|
||||||
self.max.x - self.min.x
|
self.max.x - self.min.x
|
||||||
|
|
|
@ -14,12 +14,14 @@
|
||||||
//
|
//
|
||||||
// Also licensed under MIT license, at your choice.
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
|
//! Support for glyph rendering.
|
||||||
|
|
||||||
pub use moscato::pinot;
|
pub use moscato::pinot;
|
||||||
|
|
||||||
use crate::brush::{Brush, Color};
|
use crate::brush::{Brush, Color};
|
||||||
use crate::geometry::Affine;
|
use crate::geometry::Affine;
|
||||||
use crate::path::Element;
|
use crate::path::PathElement;
|
||||||
use crate::scene::{build_fragment, Fill, Fragment};
|
use crate::scene::{Fill, SceneBuilder, SceneFragment};
|
||||||
|
|
||||||
use moscato::{Context, Scaler};
|
use moscato::{Context, Scaler};
|
||||||
use pinot::{types::Tag, FontRef};
|
use pinot::{types::Tag, FontRef};
|
||||||
|
@ -81,28 +83,30 @@ pub struct GlyphProvider<'a> {
|
||||||
impl<'a> GlyphProvider<'a> {
|
impl<'a> GlyphProvider<'a> {
|
||||||
/// Returns a scene fragment containing the commands to render the
|
/// Returns a scene fragment containing the commands to render the
|
||||||
/// specified glyph.
|
/// specified glyph.
|
||||||
pub fn get(&mut self, gid: u16) -> Option<Fragment> {
|
pub fn get(&mut self, gid: u16, brush: Option<&Brush>) -> Option<SceneFragment> {
|
||||||
let glyph = self.scaler.glyph(gid)?;
|
let glyph = self.scaler.glyph(gid)?;
|
||||||
let path = glyph.path(0)?;
|
let path = glyph.path(0)?;
|
||||||
let mut fragment = Fragment::default();
|
let mut fragment = SceneFragment::default();
|
||||||
let mut builder = build_fragment(&mut fragment);
|
let mut builder = SceneBuilder::for_fragment(&mut fragment);
|
||||||
builder.fill(
|
builder.fill(
|
||||||
Fill::NonZero,
|
Fill::NonZero,
|
||||||
&Brush::Solid(Color::rgb8(255, 255, 255)),
|
Affine::IDENTITY,
|
||||||
|
brush.unwrap_or(&Brush::Solid(Color::rgb8(255, 255, 255))),
|
||||||
None,
|
None,
|
||||||
convert_path(path.elements()),
|
convert_path(path.elements()),
|
||||||
);
|
);
|
||||||
|
builder.finish();
|
||||||
Some(fragment)
|
Some(fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a scene fragment containing the commands and resources to
|
/// Returns a scene fragment containing the commands and resources to
|
||||||
/// render the specified color glyph.
|
/// render the specified color glyph.
|
||||||
pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option<Fragment> {
|
pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option<SceneFragment> {
|
||||||
use crate::geometry::*;
|
use crate::geometry::*;
|
||||||
use moscato::Command;
|
use moscato::Command;
|
||||||
let glyph = self.scaler.color_glyph(palette_index, gid)?;
|
let glyph = self.scaler.color_glyph(palette_index, gid)?;
|
||||||
let mut fragment = Fragment::default();
|
let mut fragment = SceneFragment::default();
|
||||||
let mut builder = build_fragment(&mut fragment);
|
let mut builder = SceneBuilder::for_fragment(&mut fragment);
|
||||||
let mut xform_stack: SmallVec<[Affine; 8]> = SmallVec::new();
|
let mut xform_stack: SmallVec<[Affine; 8]> = SmallVec::new();
|
||||||
for command in glyph.commands() {
|
for command in glyph.commands() {
|
||||||
match command {
|
match command {
|
||||||
|
@ -122,10 +126,15 @@ impl<'a> GlyphProvider<'a> {
|
||||||
if let Some(xform) = xform_stack.last() {
|
if let Some(xform) = xform_stack.last() {
|
||||||
builder.push_layer(
|
builder.push_layer(
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
Affine::IDENTITY,
|
||||||
convert_transformed_path(path.elements(), xform),
|
convert_transformed_path(path.elements(), xform),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
builder.push_layer(Default::default(), convert_path(path.elements()));
|
builder.push_layer(
|
||||||
|
Default::default(),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
convert_path(path.elements()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::PopClip => builder.pop_layer(),
|
Command::PopClip => builder.pop_layer(),
|
||||||
|
@ -137,7 +146,7 @@ impl<'a> GlyphProvider<'a> {
|
||||||
if let Some(xform) = xform_stack.last() {
|
if let Some(xform) = xform_stack.last() {
|
||||||
rect = rect.transform(xform);
|
rect = rect.transform(xform);
|
||||||
}
|
}
|
||||||
builder.push_layer(Default::default(), rect.elements());
|
builder.push_layer(Default::default(), Affine::IDENTITY, rect.elements());
|
||||||
}
|
}
|
||||||
Command::PopLayer => builder.pop_layer(),
|
Command::PopLayer => builder.pop_layer(),
|
||||||
Command::BeginBlend(bounds, mode) => {
|
Command::BeginBlend(bounds, mode) => {
|
||||||
|
@ -148,7 +157,7 @@ impl<'a> GlyphProvider<'a> {
|
||||||
if let Some(xform) = xform_stack.last() {
|
if let Some(xform) = xform_stack.last() {
|
||||||
rect = rect.transform(xform);
|
rect = rect.transform(xform);
|
||||||
}
|
}
|
||||||
builder.push_layer(convert_blend(*mode), rect.elements())
|
builder.push_layer(convert_blend(*mode), Affine::IDENTITY, rect.elements())
|
||||||
}
|
}
|
||||||
Command::EndBlend => builder.pop_layer(),
|
Command::EndBlend => builder.pop_layer(),
|
||||||
Command::SimpleFill(path_index, brush, brush_xform) => {
|
Command::SimpleFill(path_index, brush, brush_xform) => {
|
||||||
|
@ -158,6 +167,7 @@ impl<'a> GlyphProvider<'a> {
|
||||||
if let Some(xform) = xform_stack.last() {
|
if let Some(xform) = xform_stack.last() {
|
||||||
builder.fill(
|
builder.fill(
|
||||||
Fill::NonZero,
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
&brush,
|
&brush,
|
||||||
brush_xform.map(|x| x * *xform),
|
brush_xform.map(|x| x * *xform),
|
||||||
convert_transformed_path(path.elements(), xform),
|
convert_transformed_path(path.elements(), xform),
|
||||||
|
@ -165,65 +175,71 @@ impl<'a> GlyphProvider<'a> {
|
||||||
} else {
|
} else {
|
||||||
builder.fill(
|
builder.fill(
|
||||||
Fill::NonZero,
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
&brush,
|
&brush,
|
||||||
brush_xform,
|
brush_xform,
|
||||||
convert_path(path.elements()),
|
convert_path(path.elements()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Fill(brush, brush_xform) => {
|
Command::Fill(_brush, _brush_xform) => {
|
||||||
// TODO: this needs to compute a bounding box for
|
// TODO: this needs to compute a bounding box for
|
||||||
// the parent clips
|
// the parent clips
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
builder.finish();
|
||||||
Some(fragment)
|
Some(fragment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_path(
|
fn convert_path(
|
||||||
path: impl Iterator<Item = moscato::Element> + Clone,
|
path: impl Iterator<Item = moscato::Element> + Clone,
|
||||||
) -> impl Iterator<Item = Element> + Clone {
|
) -> impl Iterator<Item = PathElement> + Clone {
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
path.map(|el| match el {
|
path.map(|el| match el {
|
||||||
moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y)),
|
moscato::Element::MoveTo(p0) => PathElement::MoveTo(Point::new(p0.x, p0.y)),
|
||||||
moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y)),
|
moscato::Element::LineTo(p0) => PathElement::LineTo(Point::new(p0.x, p0.y)),
|
||||||
moscato::Element::QuadTo(p0, p1) => {
|
moscato::Element::QuadTo(p0, p1) => {
|
||||||
Element::QuadTo(Point::new(p0.x, p0.y), Point::new(p1.x, p1.y))
|
PathElement::QuadTo(Point::new(p0.x, p0.y), Point::new(p1.x, p1.y))
|
||||||
}
|
}
|
||||||
moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo(
|
moscato::Element::CurveTo(p0, p1, p2) => PathElement::CurveTo(
|
||||||
Point::new(p0.x, p0.y),
|
Point::new(p0.x, p0.y),
|
||||||
Point::new(p1.x, p1.y),
|
Point::new(p1.x, p1.y),
|
||||||
Point::new(p2.x, p2.y),
|
Point::new(p2.x, p2.y),
|
||||||
),
|
),
|
||||||
moscato::Element::Close => Element::Close,
|
moscato::Element::Close => PathElement::Close,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_transformed_path(
|
fn convert_transformed_path(
|
||||||
path: impl Iterator<Item = moscato::Element> + Clone,
|
path: impl Iterator<Item = moscato::Element> + Clone,
|
||||||
xform: &Affine,
|
xform: &Affine,
|
||||||
) -> impl Iterator<Item = Element> + Clone {
|
) -> impl Iterator<Item = PathElement> + Clone {
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
let xform = *xform;
|
let xform = *xform;
|
||||||
path.map(move |el| match el {
|
path.map(move |el| match el {
|
||||||
moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y).transform(&xform)),
|
moscato::Element::MoveTo(p0) => {
|
||||||
moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y).transform(&xform)),
|
PathElement::MoveTo(Point::new(p0.x, p0.y).transform(&xform))
|
||||||
moscato::Element::QuadTo(p0, p1) => Element::QuadTo(
|
}
|
||||||
|
moscato::Element::LineTo(p0) => {
|
||||||
|
PathElement::LineTo(Point::new(p0.x, p0.y).transform(&xform))
|
||||||
|
}
|
||||||
|
moscato::Element::QuadTo(p0, p1) => PathElement::QuadTo(
|
||||||
Point::new(p0.x, p0.y).transform(&xform),
|
Point::new(p0.x, p0.y).transform(&xform),
|
||||||
Point::new(p1.x, p1.y).transform(&xform),
|
Point::new(p1.x, p1.y).transform(&xform),
|
||||||
),
|
),
|
||||||
moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo(
|
moscato::Element::CurveTo(p0, p1, p2) => PathElement::CurveTo(
|
||||||
Point::new(p0.x, p0.y).transform(&xform),
|
Point::new(p0.x, p0.y).transform(&xform),
|
||||||
Point::new(p1.x, p1.y).transform(&xform),
|
Point::new(p1.x, p1.y).transform(&xform),
|
||||||
Point::new(p2.x, p2.y).transform(&xform),
|
Point::new(p2.x, p2.y).transform(&xform),
|
||||||
),
|
),
|
||||||
moscato::Element::Close => Element::Close,
|
moscato::Element::Close => PathElement::Close,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend {
|
fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::BlendMode {
|
||||||
use crate::scene::{Blend, Compose, Mix};
|
use crate::scene::{BlendMode, Compose, Mix};
|
||||||
use moscato::CompositeMode;
|
use moscato::CompositeMode;
|
||||||
let mut mix = Mix::Normal;
|
let mut mix = Mix::Normal;
|
||||||
let mut compose = Compose::SrcOver;
|
let mut compose = Compose::SrcOver;
|
||||||
|
@ -257,7 +273,7 @@ fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend {
|
||||||
CompositeMode::HslColor => mix = Mix::Color,
|
CompositeMode::HslColor => mix = Mix::Color,
|
||||||
CompositeMode::HslLuminosity => mix = Mix::Luminosity,
|
CompositeMode::HslLuminosity => mix = Mix::Luminosity,
|
||||||
}
|
}
|
||||||
Blend { mix, compose }
|
BlendMode { mix, compose }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_transform(xform: &moscato::Transform) -> crate::geometry::Affine {
|
fn convert_transform(xform: &moscato::Transform) -> crate::geometry::Affine {
|
||||||
|
@ -298,11 +314,11 @@ fn convert_brush(brush: &moscato::Brush) -> crate::brush::Brush {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::StopVec {
|
fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::GradientStops {
|
||||||
use crate::brush::Stop;
|
use crate::brush::GradientStop;
|
||||||
stops
|
stops
|
||||||
.iter()
|
.iter()
|
||||||
.map(|stop| Stop {
|
.map(|stop| GradientStop {
|
||||||
offset: stop.offset,
|
offset: stop.offset,
|
||||||
color: Color {
|
color: Color {
|
||||||
r: stop.color.r,
|
r: stop.color.r,
|
||||||
|
@ -314,8 +330,8 @@ fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::StopVec {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_extend(extend: moscato::ExtendMode) -> crate::brush::Extend {
|
fn convert_extend(extend: moscato::ExtendMode) -> crate::brush::ExtendMode {
|
||||||
use crate::brush::Extend::*;
|
use crate::brush::ExtendMode::*;
|
||||||
match extend {
|
match extend {
|
||||||
moscato::ExtendMode::Pad => Pad,
|
moscato::ExtendMode::Pad => Pad,
|
||||||
moscato::ExtendMode::Repeat => Repeat,
|
moscato::ExtendMode::Repeat => Repeat,
|
|
@ -14,19 +14,26 @@
|
||||||
//
|
//
|
||||||
// Also licensed under MIT license, at your choice.
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
pub mod brush;
|
mod brush;
|
||||||
pub mod geometry;
|
mod geometry;
|
||||||
|
mod path;
|
||||||
|
mod resource;
|
||||||
|
mod scene;
|
||||||
|
|
||||||
pub mod glyph;
|
pub mod glyph;
|
||||||
pub mod path;
|
|
||||||
pub mod resource;
|
pub use brush::*;
|
||||||
pub mod scene;
|
pub use geometry::*;
|
||||||
|
pub use path::*;
|
||||||
|
pub use resource::*;
|
||||||
|
pub use scene::*;
|
||||||
|
|
||||||
/// Implement conversions to and from Kurbo types when the `kurbo` feature is
|
/// Implement conversions to and from Kurbo types when the `kurbo` feature is
|
||||||
/// enabled.
|
/// enabled.
|
||||||
#[cfg(feature = "kurbo")]
|
#[cfg(feature = "kurbo")]
|
||||||
mod kurbo_conv {
|
mod kurbo_conv {
|
||||||
use super::geometry::{Affine, Point, Rect};
|
use super::geometry::{Affine, Point, Rect};
|
||||||
use super::path::Element;
|
use super::path::PathElement;
|
||||||
|
|
||||||
impl Point {
|
impl Point {
|
||||||
/// Creates a new point from the equivalent kurbo type.
|
/// Creates a new point from the equivalent kurbo type.
|
||||||
|
@ -90,26 +97,27 @@ mod kurbo_conv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element {
|
impl PathElement {
|
||||||
/// Creates a new path element from the equivalent kurbo type.
|
/// Creates a new path element from the equivalent kurbo type.
|
||||||
pub fn from_kurbo(el: kurbo::PathEl) -> Self {
|
pub fn from_kurbo(el: kurbo::PathEl) -> Self {
|
||||||
use kurbo::PathEl::*;
|
use kurbo::PathEl::*;
|
||||||
use Point::from_kurbo;
|
match el {
|
||||||
match e {
|
MoveTo(p0) => Self::MoveTo(Point::from_kurbo(p0)),
|
||||||
MoveTo(p0) => Self::MoveTo(from_kurbo(p0)),
|
LineTo(p0) => Self::LineTo(Point::from_kurbo(p0)),
|
||||||
LineTo(p0) => Self::LineTo(from_kurbo(p0)),
|
QuadTo(p0, p1) => Self::QuadTo(Point::from_kurbo(p0), Point::from_kurbo(p1)),
|
||||||
QuadTo(p0, p1) => Self::QuadTo(from_kurbo(p0), from_kurbo(p1)),
|
CurveTo(p0, p1, p2) => Self::CurveTo(
|
||||||
CurveTo(p0, p1, p2) => {
|
Point::from_kurbo(p0),
|
||||||
Self::CurveTo(from_kurbo(p0), from_kurbo(p1), from_kurbo(p2))
|
Point::from_kurbo(p1),
|
||||||
}
|
Point::from_kurbo(p2),
|
||||||
|
),
|
||||||
ClosePath => Self::Close,
|
ClosePath => Self::Close,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Element> for kurbo::PathEl {
|
impl From<PathElement> for kurbo::PathEl {
|
||||||
fn from(e: Element) -> Self {
|
fn from(e: PathElement) -> Self {
|
||||||
use Element::*;
|
use PathElement::*;
|
||||||
match e {
|
match e {
|
||||||
MoveTo(p0) => Self::MoveTo(p0.into()),
|
MoveTo(p0) => Self::MoveTo(p0.into()),
|
||||||
LineTo(p0) => Self::LineTo(p0.into()),
|
LineTo(p0) => Self::LineTo(p0.into()),
|
||||||
|
|
|
@ -18,7 +18,7 @@ use super::geometry::{Point, Rect};
|
||||||
|
|
||||||
/// Action of a path element.
|
/// Action of a path element.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Verb {
|
pub enum PathVerb {
|
||||||
MoveTo,
|
MoveTo,
|
||||||
LineTo,
|
LineTo,
|
||||||
QuadTo,
|
QuadTo,
|
||||||
|
@ -28,7 +28,7 @@ pub enum Verb {
|
||||||
|
|
||||||
/// Element of a path represented by a verb and its associated points.
|
/// Element of a path represented by a verb and its associated points.
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum Element {
|
pub enum PathElement {
|
||||||
MoveTo(Point),
|
MoveTo(Point),
|
||||||
LineTo(Point),
|
LineTo(Point),
|
||||||
QuadTo(Point, Point),
|
QuadTo(Point, Point),
|
||||||
|
@ -36,27 +36,27 @@ pub enum Element {
|
||||||
Close,
|
Close,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element {
|
impl PathElement {
|
||||||
/// Returns the verb that describes the action of the path element.
|
/// Returns the verb that describes the action of the path element.
|
||||||
pub fn verb(&self) -> Verb {
|
pub fn verb(&self) -> PathVerb {
|
||||||
match self {
|
match self {
|
||||||
Self::MoveTo(..) => Verb::MoveTo,
|
Self::MoveTo(..) => PathVerb::MoveTo,
|
||||||
Self::LineTo(..) => Verb::LineTo,
|
Self::LineTo(..) => PathVerb::LineTo,
|
||||||
Self::QuadTo(..) => Verb::QuadTo,
|
Self::QuadTo(..) => PathVerb::QuadTo,
|
||||||
Self::CurveTo(..) => Verb::CurveTo,
|
Self::CurveTo(..) => PathVerb::CurveTo,
|
||||||
Self::Close => Verb::Close,
|
Self::Close => PathVerb::Close,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
pub fn elements(&self) -> impl Iterator<Item = Element> + Clone {
|
pub fn elements(&self) -> impl Iterator<Item = PathElement> + Clone {
|
||||||
let elements = [
|
let elements = [
|
||||||
Element::MoveTo((self.min.x, self.min.y).into()),
|
PathElement::MoveTo((self.min.x, self.min.y).into()),
|
||||||
Element::LineTo((self.max.x, self.min.y).into()),
|
PathElement::LineTo((self.max.x, self.min.y).into()),
|
||||||
Element::LineTo((self.max.x, self.max.y).into()),
|
PathElement::LineTo((self.max.x, self.max.y).into()),
|
||||||
Element::LineTo((self.min.x, self.max.y).into()),
|
PathElement::LineTo((self.min.x, self.max.y).into()),
|
||||||
Element::Close,
|
PathElement::Close,
|
||||||
];
|
];
|
||||||
(0..5).map(move |i| elements[i])
|
(0..5).map(move |i| elements[i])
|
||||||
}
|
}
|
||||||
|
|
31
piet-scene/src/resource.rs
Normal file
31
piet-scene/src/resource.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::brush::GradientStop;
|
||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
/// Collection of late bound resources for a scene or scene fragment.
|
||||||
|
pub struct ResourceBundle {
|
||||||
|
/// Sequence of resource patches.
|
||||||
|
pub patches: Vec<ResourcePatch>,
|
||||||
|
/// Cache of gradient stops, referenced by range from the patches.
|
||||||
|
pub stops: Vec<GradientStop>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceBundle {
|
||||||
|
/// Clears the resource set.
|
||||||
|
pub(crate) fn clear(&mut self) {
|
||||||
|
self.patches.clear();
|
||||||
|
self.stops.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
/// Description of a late bound resource.
|
||||||
|
pub enum ResourcePatch {
|
||||||
|
/// Gradient ramp resource.
|
||||||
|
Ramp {
|
||||||
|
/// Byte offset to the ramp id in the draw data stream.
|
||||||
|
offset: usize,
|
||||||
|
/// Range of the gradient stops in the resource set.
|
||||||
|
stops: Range<usize>,
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
mod gradient;
|
|
||||||
|
|
||||||
use crate::brush::{Brush, Stop};
|
|
||||||
use gradient::RampCache;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
/// Context for caching resources across rendering operations.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ResourceContext {
|
|
||||||
ramps: RampCache,
|
|
||||||
persistent_map: HashMap<u64, PersistentBrushData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResourceContext {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn advance(&mut self) {
|
|
||||||
self.ramps.advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.ramps.clear();
|
|
||||||
self.persistent_map.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_ramp(&mut self, stops: &[Stop]) -> u32 {
|
|
||||||
self.ramps.add(stops)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush {
|
|
||||||
match brush {
|
|
||||||
Brush::Persistent(dup) => return *dup,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
PersistentBrush { kind: 0, id: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy_brush(&mut self, brush: PersistentBrush) {}
|
|
||||||
|
|
||||||
pub fn ramp_data(&self) -> &[u32] {
|
|
||||||
&self.ramps.data()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle for a brush that is managed by the resource context.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub struct PersistentBrush {
|
|
||||||
kind: u8,
|
|
||||||
id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PersistentBrushData {
|
|
||||||
brush: Brush,
|
|
||||||
}
|
|
|
@ -60,12 +60,12 @@ pub enum Compose {
|
||||||
|
|
||||||
/// Blend mode consisting of mixing and composition functions.
|
/// Blend mode consisting of mixing and composition functions.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Blend {
|
pub struct BlendMode {
|
||||||
pub mix: Mix,
|
pub mix: Mix,
|
||||||
pub compose: Compose,
|
pub compose: Compose,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blend {
|
impl BlendMode {
|
||||||
pub fn new(mix: Mix, compose: Compose) -> Self {
|
pub fn new(mix: Mix, compose: Compose) -> Self {
|
||||||
Self { mix, compose }
|
Self { mix, compose }
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ impl Blend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Blend {
|
impl Default for BlendMode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
mix: Mix::Clip,
|
mix: Mix::Clip,
|
||||||
|
@ -84,7 +84,7 @@ impl Default for Blend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Mix> for Blend {
|
impl From<Mix> for BlendMode {
|
||||||
fn from(mix: Mix) -> Self {
|
fn from(mix: Mix) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mix,
|
mix,
|
||||||
|
@ -93,7 +93,7 @@ impl From<Mix> for Blend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Compose> for Blend {
|
impl From<Compose> for BlendMode {
|
||||||
fn from(compose: Compose) -> Self {
|
fn from(compose: Compose) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mix: Mix::Normal,
|
mix: Mix::Normal,
|
||||||
|
|
|
@ -15,72 +15,53 @@
|
||||||
// Also licensed under MIT license, at your choice.
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
use super::style::{Fill, Stroke};
|
use super::style::{Fill, Stroke};
|
||||||
use super::{Affine, Blend, Element, Fragment, FragmentResources, ResourcePatch, Scene, SceneData};
|
use super::{Affine, BlendMode, PathElement, Scene, SceneData, SceneFragment};
|
||||||
use crate::brush::*;
|
use crate::{brush::*, ResourcePatch};
|
||||||
use crate::resource::ResourceContext;
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
const MAX_BLEND_STACK: usize = 256;
|
/// Builder for constructing a scene or scene fragment.
|
||||||
|
pub struct SceneBuilder<'a> {
|
||||||
|
scene: &'a mut SceneData,
|
||||||
|
layers: SmallVec<[BlendMode; 8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SceneBuilder<'a> {
|
||||||
/// Creates a new builder for filling a scene. Any current content in the scene
|
/// Creates a new builder for filling a scene. Any current content in the scene
|
||||||
/// will be cleared.
|
/// will be cleared.
|
||||||
pub fn build_scene<'a>(scene: &'a mut Scene, rcx: &'a mut ResourceContext) -> Builder<'a> {
|
pub fn for_scene(scene: &'a mut Scene) -> Self {
|
||||||
Builder::new(&mut scene.data, ResourceData::Scene(rcx))
|
Self::new(&mut scene.data, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new builder for filling a scene fragment. Any current content in
|
/// Creates a new builder for filling a scene fragment. Any current content in
|
||||||
/// the fragment will be cleared.
|
/// the fragment will be cleared.
|
||||||
pub fn build_fragment<'a>(fragment: &'a mut Fragment) -> Builder<'a> {
|
pub fn for_fragment(fragment: &'a mut SceneFragment) -> Self {
|
||||||
Builder::new(
|
Self::new(&mut fragment.data, true)
|
||||||
&mut fragment.data,
|
|
||||||
ResourceData::Fragment(&mut fragment.resources),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for constructing a scene or scene fragment.
|
|
||||||
pub struct Builder<'a> {
|
|
||||||
scene: &'a mut SceneData,
|
|
||||||
resources: ResourceData<'a>,
|
|
||||||
layers: Vec<Blend>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
|
||||||
/// Creates a new builder for constructing a scene.
|
/// Creates a new builder for constructing a scene.
|
||||||
fn new(scene: &'a mut SceneData, mut resources: ResourceData<'a>) -> Self {
|
fn new(scene: &'a mut SceneData, is_fragment: bool) -> Self {
|
||||||
let is_fragment = match resources {
|
|
||||||
ResourceData::Fragment(_) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
scene.reset(is_fragment);
|
scene.reset(is_fragment);
|
||||||
resources.clear();
|
|
||||||
Self {
|
Self {
|
||||||
scene,
|
scene,
|
||||||
resources,
|
layers: Default::default(),
|
||||||
layers: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the current transformation.
|
|
||||||
pub fn transform(&mut self, transform: Affine) {
|
|
||||||
self.encode_transform(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes a new layer bound by the specifed shape and composed with
|
/// Pushes a new layer bound by the specifed shape and composed with
|
||||||
/// previous layers using the specified blend mode.
|
/// previous layers using the specified blend mode.
|
||||||
pub fn push_layer<'s, E>(&mut self, blend: Blend, elements: E)
|
pub fn push_layer<'s, E>(&mut self, blend: BlendMode, transform: Affine, elements: E)
|
||||||
where
|
where
|
||||||
E: IntoIterator,
|
E: IntoIterator,
|
||||||
E::IntoIter: Clone,
|
E::IntoIter: Clone,
|
||||||
E::Item: Borrow<Element>,
|
E::Item: Borrow<PathElement>,
|
||||||
{
|
{
|
||||||
|
self.maybe_encode_transform(transform);
|
||||||
self.linewidth(-1.0);
|
self.linewidth(-1.0);
|
||||||
let elements = elements.into_iter();
|
let elements = elements.into_iter();
|
||||||
self.encode_path(elements, true);
|
self.encode_path(elements, true);
|
||||||
self.begin_clip(Some(blend));
|
self.begin_clip(Some(blend));
|
||||||
if self.layers.len() >= MAX_BLEND_STACK {
|
|
||||||
panic!("Maximum clip/blend stack size {} exceeded", MAX_BLEND_STACK);
|
|
||||||
}
|
|
||||||
self.layers.push(blend);
|
self.layers.push(blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,32 +75,27 @@ impl<'a> Builder<'a> {
|
||||||
/// Fills a shape using the specified style and brush.
|
/// Fills a shape using the specified style and brush.
|
||||||
pub fn fill<'s, E>(
|
pub fn fill<'s, E>(
|
||||||
&mut self,
|
&mut self,
|
||||||
style: Fill,
|
_style: Fill,
|
||||||
|
transform: Affine,
|
||||||
brush: &Brush,
|
brush: &Brush,
|
||||||
brush_transform: Option<Affine>,
|
brush_transform: Option<Affine>,
|
||||||
elements: E,
|
elements: E,
|
||||||
) where
|
) where
|
||||||
E: IntoIterator,
|
E: IntoIterator,
|
||||||
E::IntoIter: Clone,
|
E::IntoIter: Clone,
|
||||||
E::Item: Borrow<Element>,
|
E::Item: Borrow<PathElement>,
|
||||||
{
|
{
|
||||||
|
self.maybe_encode_transform(transform);
|
||||||
self.linewidth(-1.0);
|
self.linewidth(-1.0);
|
||||||
let elements = elements.into_iter();
|
let elements = elements.into_iter();
|
||||||
self.encode_path(elements, true);
|
if self.encode_path(elements, true) {
|
||||||
if let Some(brush_transform) = brush_transform {
|
if let Some(brush_transform) = brush_transform {
|
||||||
if let Some(last_transform) = self.scene.transform_stream.last().copied() {
|
self.encode_transform(transform * brush_transform);
|
||||||
self.encode_transform(brush_transform * last_transform);
|
|
||||||
self.swap_last_tags();
|
self.swap_last_tags();
|
||||||
self.encode_brush(brush);
|
self.encode_brush(brush);
|
||||||
self.encode_transform(last_transform);
|
|
||||||
} else {
|
} else {
|
||||||
self.encode_transform(brush_transform);
|
|
||||||
self.swap_last_tags();
|
|
||||||
self.encode_brush(brush);
|
self.encode_brush(brush);
|
||||||
self.encode_transform(Affine::IDENTITY);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.encode_brush(brush);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +103,7 @@ impl<'a> Builder<'a> {
|
||||||
pub fn stroke<'s, D, E>(
|
pub fn stroke<'s, D, E>(
|
||||||
&mut self,
|
&mut self,
|
||||||
style: &Stroke<D>,
|
style: &Stroke<D>,
|
||||||
|
transform: Affine,
|
||||||
brush: &Brush,
|
brush: &Brush,
|
||||||
brush_transform: Option<Affine>,
|
brush_transform: Option<Affine>,
|
||||||
elements: E,
|
elements: E,
|
||||||
|
@ -134,78 +111,25 @@ impl<'a> Builder<'a> {
|
||||||
D: Borrow<[f32]>,
|
D: Borrow<[f32]>,
|
||||||
E: IntoIterator,
|
E: IntoIterator,
|
||||||
E::IntoIter: Clone,
|
E::IntoIter: Clone,
|
||||||
E::Item: Borrow<Element>,
|
E::Item: Borrow<PathElement>,
|
||||||
{
|
{
|
||||||
|
self.maybe_encode_transform(transform);
|
||||||
self.linewidth(style.width);
|
self.linewidth(style.width);
|
||||||
let elements = elements.into_iter();
|
let elements = elements.into_iter();
|
||||||
self.encode_path(elements, false);
|
if self.encode_path(elements, false) {
|
||||||
if let Some(brush_transform) = brush_transform {
|
if let Some(brush_transform) = brush_transform {
|
||||||
if let Some(last_transform) = self.scene.transform_stream.last().copied() {
|
self.encode_transform(transform * brush_transform);
|
||||||
self.encode_transform(brush_transform * last_transform);
|
|
||||||
self.swap_last_tags();
|
self.swap_last_tags();
|
||||||
self.encode_brush(brush);
|
self.encode_brush(brush);
|
||||||
self.encode_transform(last_transform);
|
|
||||||
} else {
|
} else {
|
||||||
self.encode_transform(brush_transform);
|
|
||||||
self.swap_last_tags();
|
|
||||||
self.encode_brush(brush);
|
self.encode_brush(brush);
|
||||||
self.encode_transform(Affine::IDENTITY);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.encode_brush(brush);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends a fragment to the scene.
|
/// Appends a fragment to the scene.
|
||||||
pub fn append(&mut self, fragment: &Fragment, transform: Option<Affine>) {
|
pub fn append(&mut self, fragment: &SceneFragment, transform: Option<Affine>) {
|
||||||
let drawdata_base = self.scene.drawdata_stream.len();
|
|
||||||
let mut cur_transform = self.scene.transform_stream.last().copied();
|
|
||||||
if let Some(transform) = transform {
|
|
||||||
if cur_transform.is_none() {
|
|
||||||
cur_transform = Some(Affine::IDENTITY);
|
|
||||||
}
|
|
||||||
self.encode_transform(transform);
|
|
||||||
} else if cur_transform != Some(Affine::IDENTITY) {
|
|
||||||
self.encode_transform(Affine::IDENTITY);
|
|
||||||
}
|
|
||||||
self.scene.append(&fragment.data, &transform);
|
self.scene.append(&fragment.data, &transform);
|
||||||
match &mut self.resources {
|
|
||||||
ResourceData::Scene(res) => {
|
|
||||||
for patch in &fragment.resources.patches {
|
|
||||||
match patch {
|
|
||||||
ResourcePatch::Ramp {
|
|
||||||
drawdata_offset,
|
|
||||||
stops,
|
|
||||||
} => {
|
|
||||||
let stops = &fragment.resources.stops[stops.clone()];
|
|
||||||
let ramp_id = res.add_ramp(stops);
|
|
||||||
let patch_base = *drawdata_offset + drawdata_base;
|
|
||||||
(&mut self.scene.drawdata_stream[patch_base..patch_base + 4])
|
|
||||||
.copy_from_slice(bytemuck::bytes_of(&ramp_id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResourceData::Fragment(res) => {
|
|
||||||
let stops_base = res.stops.len();
|
|
||||||
res.stops.extend_from_slice(&fragment.resources.stops);
|
|
||||||
res.patches.extend(fragment.resources.patches.iter().map(
|
|
||||||
|pending| match pending {
|
|
||||||
ResourcePatch::Ramp {
|
|
||||||
drawdata_offset,
|
|
||||||
stops,
|
|
||||||
} => ResourcePatch::Ramp {
|
|
||||||
drawdata_offset: drawdata_offset + drawdata_base,
|
|
||||||
stops: stops.start + stops_base..stops.end + stops_base,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Prevent fragments from affecting transform state. Should we allow this?
|
|
||||||
if let Some(transform) = cur_transform {
|
|
||||||
self.encode_transform(transform);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Completes construction and finalizes the underlying scene.
|
/// Completes construction and finalizes the underlying scene.
|
||||||
|
@ -216,11 +140,11 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a> SceneBuilder<'a> {
|
||||||
fn encode_path<E>(&mut self, elements: E, is_fill: bool)
|
fn encode_path<E>(&mut self, elements: E, is_fill: bool) -> bool
|
||||||
where
|
where
|
||||||
E: Iterator,
|
E: Iterator,
|
||||||
E::Item: Borrow<Element>,
|
E::Item: Borrow<PathElement>,
|
||||||
{
|
{
|
||||||
if is_fill {
|
if is_fill {
|
||||||
self.encode_path_inner(
|
self.encode_path_inner(
|
||||||
|
@ -228,35 +152,46 @@ impl<'a> Builder<'a> {
|
||||||
.map(|el| *el.borrow())
|
.map(|el| *el.borrow())
|
||||||
.flat_map(|el| {
|
.flat_map(|el| {
|
||||||
match el {
|
match el {
|
||||||
Element::MoveTo(..) => Some(Element::Close),
|
PathElement::MoveTo(..) => Some(PathElement::Close),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(Some(el))
|
.chain(Some(el))
|
||||||
})
|
})
|
||||||
.chain(Some(Element::Close)),
|
.chain(Some(PathElement::Close)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
self.encode_path_inner(elements.map(|el| *el.borrow()))
|
self.encode_path_inner(elements.map(|el| *el.borrow()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_path_inner(&mut self, elements: impl Iterator<Item = Element>) {
|
fn encode_path_inner(&mut self, elements: impl Iterator<Item = PathElement>) -> bool {
|
||||||
let mut b = PathBuilder::new(&mut self.scene.tag_stream, &mut self.scene.pathseg_stream);
|
let mut b = PathBuilder::new(&mut self.scene.tag_stream, &mut self.scene.pathseg_stream);
|
||||||
|
let mut has_els = false;
|
||||||
for el in elements {
|
for el in elements {
|
||||||
match el {
|
match el {
|
||||||
Element::MoveTo(p0) => b.move_to(p0.x, p0.y),
|
PathElement::MoveTo(p0) => b.move_to(p0.x, p0.y),
|
||||||
Element::LineTo(p0) => b.line_to(p0.x, p0.y),
|
PathElement::LineTo(p0) => b.line_to(p0.x, p0.y),
|
||||||
Element::QuadTo(p0, p1) => b.quad_to(p0.x, p0.y, p1.x, p1.y),
|
PathElement::QuadTo(p0, p1) => b.quad_to(p0.x, p0.y, p1.x, p1.y),
|
||||||
Element::CurveTo(p0, p1, p2) => b.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y),
|
PathElement::CurveTo(p0, p1, p2) => b.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y),
|
||||||
Element::Close => b.close_path(),
|
PathElement::Close => b.close_path(),
|
||||||
}
|
}
|
||||||
|
has_els = true;
|
||||||
}
|
}
|
||||||
|
if has_els {
|
||||||
b.path();
|
b.path();
|
||||||
let n_pathseg = b.n_pathseg();
|
let n_pathseg = b.n_pathseg();
|
||||||
self.scene.n_path += 1;
|
self.scene.n_path += 1;
|
||||||
self.scene.n_pathseg += n_pathseg;
|
self.scene.n_pathseg += n_pathseg;
|
||||||
}
|
}
|
||||||
|
has_els
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_encode_transform(&mut self, transform: Affine) {
|
||||||
|
if self.scene.transform_stream.last() != Some(&transform) {
|
||||||
|
self.encode_transform(transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn encode_transform(&mut self, transform: Affine) {
|
fn encode_transform(&mut self, transform: Affine) {
|
||||||
self.scene.tag_stream.push(0x20);
|
self.scene.tag_stream.push(0x20);
|
||||||
|
@ -272,9 +207,11 @@ impl<'a> Builder<'a> {
|
||||||
|
|
||||||
// -1.0 means "fill"
|
// -1.0 means "fill"
|
||||||
fn linewidth(&mut self, linewidth: f32) {
|
fn linewidth(&mut self, linewidth: f32) {
|
||||||
|
if self.scene.linewidth_stream.last() != Some(&linewidth) {
|
||||||
self.scene.tag_stream.push(0x40);
|
self.scene.tag_stream.push(0x40);
|
||||||
self.scene.linewidth_stream.push(linewidth);
|
self.scene.linewidth_stream.push(linewidth);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn encode_brush(&mut self, brush: &Brush) {
|
fn encode_brush(&mut self, brush: &Brush) {
|
||||||
match brush {
|
match brush {
|
||||||
|
@ -311,31 +248,26 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
Brush::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"),
|
Brush::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"),
|
||||||
Brush::Image(_image) => todo!("images aren't done yet!"),
|
Brush::Image(_image) => todo!("images aren't done yet!"),
|
||||||
Brush::Persistent(_) => todo!("persistent brushes aren't done yet!"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_ramp(&mut self, stops: &[Stop]) -> u32 {
|
fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 {
|
||||||
match &mut self.resources {
|
let offset = self.scene.drawdata_stream.len();
|
||||||
ResourceData::Scene(res) => res.add_ramp(stops),
|
let resources = &mut self.scene.resources;
|
||||||
ResourceData::Fragment(res) => {
|
let stops_start = resources.stops.len();
|
||||||
let stops_start = res.stops.len();
|
resources.stops.extend_from_slice(stops);
|
||||||
res.stops.extend_from_slice(stops);
|
resources.patches.push(ResourcePatch::Ramp {
|
||||||
let id = res.patches.len() as u32;
|
offset,
|
||||||
res.patches.push(ResourcePatch::Ramp {
|
|
||||||
drawdata_offset: self.scene.drawdata_stream.len(),
|
|
||||||
stops: stops_start..stops_start + stops.len(),
|
stops: stops_start..stops_start + stops.len(),
|
||||||
});
|
});
|
||||||
id
|
0
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a clip.
|
/// Start a clip.
|
||||||
fn begin_clip(&mut self, blend: Option<Blend>) {
|
fn begin_clip(&mut self, blend: Option<BlendMode>) {
|
||||||
self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP);
|
self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP);
|
||||||
let element = Clip {
|
let element = Clip {
|
||||||
blend: blend.unwrap_or(Blend::default()).pack(),
|
blend: blend.unwrap_or(BlendMode::default()).pack(),
|
||||||
};
|
};
|
||||||
self.scene
|
self.scene
|
||||||
.drawdata_stream
|
.drawdata_stream
|
||||||
|
@ -343,10 +275,10 @@ impl<'a> Builder<'a> {
|
||||||
self.scene.n_clip += 1;
|
self.scene.n_clip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_clip(&mut self, blend: Option<Blend>) {
|
fn end_clip(&mut self, blend: Option<BlendMode>) {
|
||||||
self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP);
|
self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP);
|
||||||
let element = Clip {
|
let element = Clip {
|
||||||
blend: blend.unwrap_or(Blend::default()).pack(),
|
blend: blend.unwrap_or(BlendMode::default()).pack(),
|
||||||
};
|
};
|
||||||
self.scene
|
self.scene
|
||||||
.drawdata_stream
|
.drawdata_stream
|
||||||
|
@ -358,23 +290,6 @@ impl<'a> Builder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ResourceData<'a> {
|
|
||||||
Fragment(&'a mut FragmentResources),
|
|
||||||
Scene(&'a mut ResourceContext),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResourceData<'_> {
|
|
||||||
fn clear(&mut self) {
|
|
||||||
match self {
|
|
||||||
Self::Fragment(res) => {
|
|
||||||
res.patches.clear();
|
|
||||||
res.stops.clear();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tags for draw objects. See shader/drawtag.h for the authoritative source.
|
// Tags for draw objects. See shader/drawtag.h for the authoritative source.
|
||||||
const DRAWTAG_FILLCOLOR: u32 = 0x44;
|
const DRAWTAG_FILLCOLOR: u32 = 0x44;
|
||||||
const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;
|
const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;
|
||||||
|
|
|
@ -18,15 +18,13 @@ mod blend;
|
||||||
mod builder;
|
mod builder;
|
||||||
mod style;
|
mod style;
|
||||||
|
|
||||||
pub use blend::{Blend, Compose, Mix};
|
pub use blend::{BlendMode, Compose, Mix};
|
||||||
pub use builder::{build_fragment, build_scene, Builder};
|
pub use builder::SceneBuilder;
|
||||||
pub use style::*;
|
pub use style::*;
|
||||||
|
|
||||||
use super::brush::*;
|
|
||||||
use super::geometry::{Affine, Point};
|
use super::geometry::{Affine, Point};
|
||||||
use super::path::Element;
|
use super::path::PathElement;
|
||||||
|
use super::resource::{ResourceBundle, ResourcePatch};
|
||||||
use core::ops::Range;
|
|
||||||
|
|
||||||
/// Raw data streams describing an encoded scene.
|
/// Raw data streams describing an encoded scene.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -40,9 +38,14 @@ pub struct SceneData {
|
||||||
pub n_path: u32,
|
pub n_path: u32,
|
||||||
pub n_pathseg: u32,
|
pub n_pathseg: u32,
|
||||||
pub n_clip: u32,
|
pub n_clip: u32,
|
||||||
|
pub resources: ResourceBundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SceneData {
|
impl SceneData {
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.pathseg_stream.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
fn reset(&mut self, is_fragment: bool) {
|
fn reset(&mut self, is_fragment: bool) {
|
||||||
self.transform_stream.clear();
|
self.transform_stream.clear();
|
||||||
self.tag_stream.clear();
|
self.tag_stream.clear();
|
||||||
|
@ -53,6 +56,7 @@ impl SceneData {
|
||||||
self.n_path = 0;
|
self.n_path = 0;
|
||||||
self.n_pathseg = 0;
|
self.n_pathseg = 0;
|
||||||
self.n_clip = 0;
|
self.n_clip = 0;
|
||||||
|
self.resources.clear();
|
||||||
if !is_fragment {
|
if !is_fragment {
|
||||||
self.transform_stream
|
self.transform_stream
|
||||||
.push(Affine::new(&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0]));
|
.push(Affine::new(&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0]));
|
||||||
|
@ -61,9 +65,11 @@ impl SceneData {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append(&mut self, other: &SceneData, transform: &Option<Affine>) {
|
fn append(&mut self, other: &SceneData, transform: &Option<Affine>) {
|
||||||
|
let stops_base = self.resources.stops.len();
|
||||||
|
let drawdata_base = self.drawdata_stream.len();
|
||||||
if let Some(transform) = *transform {
|
if let Some(transform) = *transform {
|
||||||
self.transform_stream
|
self.transform_stream
|
||||||
.extend(other.transform_stream.iter().map(|x| *x * transform));
|
.extend(other.transform_stream.iter().map(|x| transform * *x));
|
||||||
} else {
|
} else {
|
||||||
self.transform_stream
|
self.transform_stream
|
||||||
.extend_from_slice(&other.transform_stream);
|
.extend_from_slice(&other.transform_stream);
|
||||||
|
@ -78,6 +84,20 @@ impl SceneData {
|
||||||
self.n_path += other.n_path;
|
self.n_path += other.n_path;
|
||||||
self.n_pathseg += other.n_pathseg;
|
self.n_pathseg += other.n_pathseg;
|
||||||
self.n_clip += other.n_clip;
|
self.n_clip += other.n_clip;
|
||||||
|
self.resources
|
||||||
|
.stops
|
||||||
|
.extend_from_slice(&other.resources.stops);
|
||||||
|
self.resources
|
||||||
|
.patches
|
||||||
|
.extend(other.resources.patches.iter().map(|patch| match patch {
|
||||||
|
ResourcePatch::Ramp { offset, stops } => {
|
||||||
|
let stops = stops.start + stops_base..stops.end + stops_base;
|
||||||
|
ResourcePatch::Ramp {
|
||||||
|
offset: drawdata_base + offset,
|
||||||
|
stops,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,28 +117,23 @@ impl Scene {
|
||||||
|
|
||||||
/// Encoded definition of a scene fragment and associated resources.
|
/// Encoded definition of a scene fragment and associated resources.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Fragment {
|
pub struct SceneFragment {
|
||||||
data: SceneData,
|
data: SceneData,
|
||||||
resources: FragmentResources,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fragment {
|
impl SceneFragment {
|
||||||
|
/// Returns true if the fragment does not contain any paths.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.data.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the underlying stream of points that defined all encoded path
|
/// Returns the underlying stream of points that defined all encoded path
|
||||||
/// segments.
|
/// segments.
|
||||||
pub fn points(&self) -> &[Point] {
|
pub fn points(&self) -> &[Point] {
|
||||||
|
if self.is_empty() {
|
||||||
|
&[]
|
||||||
|
} else {
|
||||||
bytemuck::cast_slice(&self.data.pathseg_stream)
|
bytemuck::cast_slice(&self.data.pathseg_stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct FragmentResources {
|
|
||||||
patches: Vec<ResourcePatch>,
|
|
||||||
stops: Vec<Stop>,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ResourcePatch {
|
|
||||||
Ramp {
|
|
||||||
drawdata_offset: usize,
|
|
||||||
stops: Range<usize>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue