Images!
This commit is contained in:
743
Cargo.lock
generated
743
Cargo.lock
generated
@@ -2,6 +2,12 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
@@ -11,6 +17,24 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aligned"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
|
||||||
|
dependencies = [
|
||||||
|
"as-slice",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aligned-vec"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
|
||||||
|
dependencies = [
|
||||||
|
"equator",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@@ -26,12 +50,44 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arg_enum_proc_macro"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "as-slice"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
|
||||||
|
dependencies = [
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ash"
|
name = "ash"
|
||||||
version = "0.38.0+1.3.281"
|
version = "0.38.0+1.3.281"
|
||||||
@@ -53,6 +109,49 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "av-scenechange"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
|
||||||
|
dependencies = [
|
||||||
|
"aligned",
|
||||||
|
"anyhow",
|
||||||
|
"arg_enum_proc_macro",
|
||||||
|
"arrayvec",
|
||||||
|
"log",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"pastey",
|
||||||
|
"rayon",
|
||||||
|
"thiserror",
|
||||||
|
"v_frame",
|
||||||
|
"y4m",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "av1-grain"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"arrayvec",
|
||||||
|
"log",
|
||||||
|
"nom",
|
||||||
|
"num-rational",
|
||||||
|
"v_frame",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "avif-serialize"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@@ -68,12 +167,27 @@ version = "0.9.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitstream-io"
|
||||||
|
version = "4.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
|
||||||
|
dependencies = [
|
||||||
|
"core2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block2"
|
name = "block2"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@@ -83,6 +197,12 @@ dependencies = [
|
|||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "built"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.2"
|
version = "3.20.2"
|
||||||
@@ -109,6 +229,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder-lite"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.11.1"
|
version = "1.11.1"
|
||||||
@@ -122,6 +248,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -148,6 +276,21 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core2"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core_maths"
|
name = "core_maths"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -181,6 +324,40 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@@ -221,6 +398,32 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equator"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
|
||||||
|
dependencies = [
|
||||||
|
"equator-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equator-macro"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -237,12 +440,66 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "exr"
|
||||||
|
version = "1.74.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
|
||||||
|
dependencies = [
|
||||||
|
"bit_field",
|
||||||
|
"half",
|
||||||
|
"lebe",
|
||||||
|
"miniz_oxide",
|
||||||
|
"rayon-core",
|
||||||
|
"smallvec",
|
||||||
|
"zune-inflate",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fax"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
|
||||||
|
dependencies = [
|
||||||
|
"fax_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fax_derive"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fdeflate"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||||
|
dependencies = [
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -338,6 +595,28 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
|
||||||
|
dependencies = [
|
||||||
|
"color_quant",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gl_generator"
|
name = "gl_generator"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -515,6 +794,46 @@ dependencies = [
|
|||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.25.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder-lite",
|
||||||
|
"color_quant",
|
||||||
|
"exr",
|
||||||
|
"gif",
|
||||||
|
"image-webp",
|
||||||
|
"moxcms",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
"qoi",
|
||||||
|
"ravif",
|
||||||
|
"rayon",
|
||||||
|
"rgb",
|
||||||
|
"tiff",
|
||||||
|
"zune-core",
|
||||||
|
"zune-jpeg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image-webp"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder-lite",
|
||||||
|
"quick-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imgref"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.13.0"
|
version = "2.13.0"
|
||||||
@@ -525,6 +844,26 @@ dependencies = [
|
|||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "interpolate_name"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
@@ -537,6 +876,16 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.91"
|
version = "0.3.91"
|
||||||
@@ -570,12 +919,28 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lebe"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.183"
|
version = "0.2.183"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -625,6 +990,15 @@ version = "0.4.29"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loop9"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
|
||||||
|
dependencies = [
|
||||||
|
"imgref",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -634,6 +1008,16 @@ dependencies = [
|
|||||||
"regex-automata",
|
"regex-automata",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maybe-rayon"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
@@ -649,6 +1033,26 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "moxcms"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"pxfm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "29.0.0"
|
version = "29.0.0"
|
||||||
@@ -684,6 +1088,68 @@ dependencies = [
|
|||||||
"jni-sys",
|
"jni-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "new_debug_unreachable"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "8.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "noop_proc_macro"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-derive"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -794,6 +1260,18 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pastey"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -812,6 +1290,19 @@ version = "0.3.32"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crc32fast",
|
||||||
|
"fdeflate",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pollster"
|
name = "pollster"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -833,6 +1324,15 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "presser"
|
name = "presser"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -853,6 +1353,40 @@ name = "profiling"
|
|||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
|
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
|
||||||
|
dependencies = [
|
||||||
|
"profiling-procmacros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "profiling-procmacros"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pxfm"
|
||||||
|
version = "0.1.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "qoi"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
@@ -872,6 +1406,41 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "range-alloc"
|
name = "range-alloc"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -884,6 +1453,56 @@ version = "1.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
|
checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rav1e"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
|
||||||
|
dependencies = [
|
||||||
|
"aligned-vec",
|
||||||
|
"arbitrary",
|
||||||
|
"arg_enum_proc_macro",
|
||||||
|
"arrayvec",
|
||||||
|
"av-scenechange",
|
||||||
|
"av1-grain",
|
||||||
|
"bitstream-io",
|
||||||
|
"built",
|
||||||
|
"cfg-if",
|
||||||
|
"interpolate_name",
|
||||||
|
"itertools",
|
||||||
|
"libc",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
"log",
|
||||||
|
"maybe-rayon",
|
||||||
|
"new_debug_unreachable",
|
||||||
|
"noop_proc_macro",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
|
"paste",
|
||||||
|
"profiling",
|
||||||
|
"rand",
|
||||||
|
"rand_chacha",
|
||||||
|
"simd_helpers",
|
||||||
|
"thiserror",
|
||||||
|
"v_frame",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ravif"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45"
|
||||||
|
dependencies = [
|
||||||
|
"avif-serialize",
|
||||||
|
"imgref",
|
||||||
|
"loop9",
|
||||||
|
"quick-error",
|
||||||
|
"rav1e",
|
||||||
|
"rayon",
|
||||||
|
"rgb",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@@ -902,6 +1521,26 @@ dependencies = [
|
|||||||
"objc2-quartz-core",
|
"objc2-quartz-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "read-fonts"
|
name = "read-fonts"
|
||||||
version = "0.35.0"
|
version = "0.35.0"
|
||||||
@@ -955,6 +1594,12 @@ 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 = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rgb"
|
||||||
|
version = "0.8.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roxmltree"
|
name = "roxmltree"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
@@ -997,6 +1642,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-text",
|
"cosmic-text",
|
||||||
"fontconfig",
|
"fontconfig",
|
||||||
|
"image",
|
||||||
"ruin-runtime",
|
"ruin-runtime",
|
||||||
"ruin_reactivity",
|
"ruin_reactivity",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -1159,6 +1805,21 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd_helpers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "skrifa"
|
name = "skrifa"
|
||||||
version = "0.37.0"
|
version = "0.37.0"
|
||||||
@@ -1215,6 +1876,12 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -1290,6 +1957,20 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiff"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
|
||||||
|
dependencies = [
|
||||||
|
"fax",
|
||||||
|
"flate2",
|
||||||
|
"half",
|
||||||
|
"quick-error",
|
||||||
|
"weezl",
|
||||||
|
"zune-jpeg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@@ -1411,6 +2092,17 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "v_frame"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
|
||||||
|
dependencies = [
|
||||||
|
"aligned-vec",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -1426,6 +2118,15 @@ dependencies = [
|
|||||||
"try-lock",
|
"try-lock",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.2+wasi-0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.114"
|
version = "0.2.114"
|
||||||
@@ -1556,6 +2257,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu"
|
name = "wgpu"
|
||||||
version = "29.0.0"
|
version = "29.0.0"
|
||||||
@@ -1851,6 +2558,12 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xkbcommon"
|
name = "xkbcommon"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -1874,6 +2587,12 @@ version = "0.8.28"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "y4m"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yazi"
|
name = "yazi"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1916,3 +2635,27 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-core"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-inflate"
|
||||||
|
version = "0.2.54"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||||
|
dependencies = [
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-jpeg"
|
||||||
|
version = "0.5.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec5f41c76397b7da451efd19915684f727d7e1d516384ca6bd0ec43ec94de23c"
|
||||||
|
dependencies = [
|
||||||
|
"zune-core",
|
||||||
|
]
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ use std::time::{Duration, Instant};
|
|||||||
|
|
||||||
use ruin_runtime::{TimeoutHandle, clear_timeout, set_timeout};
|
use ruin_runtime::{TimeoutHandle, clear_timeout, set_timeout};
|
||||||
use ruin_ui::{
|
use ruin_ui::{
|
||||||
Color, CursorIcon, DisplayItem, Edges, Element, ElementId, InteractionTree, KeyboardEvent,
|
Color, CursorIcon, DisplayItem, Edges, Element, ElementId, ImageFit, ImageResource,
|
||||||
KeyboardEventKind, KeyboardKey, LayoutSnapshot, PlatformEvent, PointerButton, PointerEvent,
|
InteractionTree, KeyboardEvent, KeyboardEventKind, KeyboardKey, LayoutSnapshot, PlatformEvent,
|
||||||
PointerEventKind, PointerRouter, PreparedText, Quad, RoutedPointerEventKind, SceneSnapshot,
|
PointerButton, PointerEvent, PointerEventKind, PointerRouter, PreparedText, Quad,
|
||||||
TextAlign, TextFontFamily, TextSelectionStyle, TextSpan, TextSpanSlant, TextSpanWeight,
|
RoutedPointerEventKind, SceneSnapshot, TextAlign, TextFontFamily, TextSelectionStyle, TextSpan,
|
||||||
TextStyle, TextSystem, TextWrap, UiSize, WindowController, WindowSpec, WindowUpdate,
|
TextSpanSlant, TextSpanWeight, TextStyle, TextSystem, TextWrap, UiSize, WindowController,
|
||||||
layout_snapshot_with_text_system,
|
WindowSpec, WindowUpdate, layout_snapshot_with_text_system,
|
||||||
};
|
};
|
||||||
use ruin_ui_platform_wayland::start_wayland_ui;
|
use ruin_ui_platform_wayland::start_wayland_ui;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
@@ -34,6 +34,7 @@ const SECOND_INPUT_TEXT_ID: ElementId = ElementId::new(107);
|
|||||||
const INPUT_CARET_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
const INPUT_CARET_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
const MULTI_CLICK_INTERVAL: Duration = Duration::from_millis(350);
|
const MULTI_CLICK_INTERVAL: Duration = Duration::from_millis(350);
|
||||||
const MULTI_CLICK_DISTANCE_SQUARED: f32 = 36.0;
|
const MULTI_CLICK_DISTANCE_SQUARED: f32 = 36.0;
|
||||||
|
const HERO_IMAGE_ASSET: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/ruin.png");
|
||||||
const DEMO_SELECTION_STYLE: TextSelectionStyle =
|
const DEMO_SELECTION_STYLE: TextSelectionStyle =
|
||||||
TextSelectionStyle::new(Color::rgba(0x6C, 0x8E, 0xFF, 0xB8))
|
TextSelectionStyle::new(Color::rgba(0x6C, 0x8E, 0xFF, 0xB8))
|
||||||
.with_text_color(Color::rgb(0x0D, 0x14, 0x25));
|
.with_text_color(Color::rgb(0x0D, 0x14, 0x25));
|
||||||
@@ -192,7 +193,10 @@ fn active_input_selection_bounds(
|
|||||||
Some(selection_bounds(selection))
|
Some(selection_bounds(selection))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_active_input_selection(selection: Option<TextSelection>, input_field: &InputFieldState) -> bool {
|
fn has_active_input_selection(
|
||||||
|
selection: Option<TextSelection>,
|
||||||
|
input_field: &InputFieldState,
|
||||||
|
) -> bool {
|
||||||
active_input_selection_bounds(selection, input_field).is_some()
|
active_input_selection_bounds(selection, input_field).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,6 +352,7 @@ fn install_tracing() {
|
|||||||
#[ruin_runtime::async_main]
|
#[ruin_runtime::async_main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
install_tracing();
|
install_tracing();
|
||||||
|
let hero_image = ImageResource::load_path(HERO_IMAGE_ASSET).await?;
|
||||||
let mut ui = start_wayland_ui();
|
let mut ui = start_wayland_ui();
|
||||||
let window = ui.create_window(
|
let window = ui.create_window(
|
||||||
WindowSpec::new("RUIN paragraph demo")
|
WindowSpec::new("RUIN paragraph demo")
|
||||||
@@ -522,6 +527,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
viewport,
|
viewport,
|
||||||
version,
|
version,
|
||||||
hovered_card,
|
hovered_card,
|
||||||
|
&hero_image,
|
||||||
&input_fields,
|
&input_fields,
|
||||||
focused_element,
|
focused_element,
|
||||||
&mut text_system,
|
&mut text_system,
|
||||||
@@ -656,8 +662,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
&text,
|
&text,
|
||||||
);
|
);
|
||||||
needs_input_rebuild |= input_outcome.text_changed;
|
needs_input_rebuild |= input_outcome.text_changed;
|
||||||
needs_overlay_present |=
|
needs_overlay_present |= input_outcome.caret_changed || input_outcome.selection_changed;
|
||||||
input_outcome.caret_changed || input_outcome.selection_changed;
|
|
||||||
}
|
}
|
||||||
if let Some(text) = pending_primary_selection_text {
|
if let Some(text) = pending_primary_selection_text {
|
||||||
let input_outcome = insert_text_into_focused_input(
|
let input_outcome = insert_text_into_focused_input(
|
||||||
@@ -715,6 +720,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
viewport,
|
viewport,
|
||||||
version,
|
version,
|
||||||
hovered_card,
|
hovered_card,
|
||||||
|
&hero_image,
|
||||||
&input_fields,
|
&input_fields,
|
||||||
focused_element,
|
focused_element,
|
||||||
&mut text_system,
|
&mut text_system,
|
||||||
@@ -771,11 +777,18 @@ fn build_snapshot(
|
|||||||
viewport: UiSize,
|
viewport: UiSize,
|
||||||
version: u64,
|
version: u64,
|
||||||
hovered_card: Option<ElementId>,
|
hovered_card: Option<ElementId>,
|
||||||
|
hero_image: &ImageResource,
|
||||||
input_fields: &[InputFieldState],
|
input_fields: &[InputFieldState],
|
||||||
focused_element: Option<ElementId>,
|
focused_element: Option<ElementId>,
|
||||||
text_system: &mut TextSystem,
|
text_system: &mut TextSystem,
|
||||||
) -> LayoutSnapshot {
|
) -> LayoutSnapshot {
|
||||||
let tree = build_document_tree(viewport, hovered_card, input_fields, focused_element);
|
let tree = build_document_tree(
|
||||||
|
viewport,
|
||||||
|
hovered_card,
|
||||||
|
hero_image,
|
||||||
|
input_fields,
|
||||||
|
focused_element,
|
||||||
|
);
|
||||||
layout_snapshot_with_text_system(version, viewport, &tree, text_system)
|
layout_snapshot_with_text_system(version, viewport, &tree, text_system)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1051,14 +1064,11 @@ fn handle_keyboard_input_event(
|
|||||||
let prepared_text = interaction_tree.text_for_element(text_id);
|
let prepared_text = interaction_tree.text_for_element(text_id);
|
||||||
if event.modifiers.shift
|
if event.modifiers.shift
|
||||||
&& let Some(prepared_text) = prepared_text
|
&& let Some(prepared_text) = prepared_text
|
||||||
&& let Some(next_caret) =
|
&& let Some(next_caret) = navigation_target_offset(
|
||||||
navigation_target_offset(
|
|
||||||
prepared_text,
|
prepared_text,
|
||||||
selection
|
selection
|
||||||
.filter(|current| current.element_id == text_id)
|
.filter(|current| current.element_id == text_id)
|
||||||
.map(|current| {
|
.map(|current| selection_navigation_anchor_and_focus(current, &event.key).1)
|
||||||
selection_navigation_anchor_and_focus(current, &event.key).1
|
|
||||||
})
|
|
||||||
.unwrap_or(input_fields[input_index].caret),
|
.unwrap_or(input_fields[input_index].caret),
|
||||||
&event.key,
|
&event.key,
|
||||||
)
|
)
|
||||||
@@ -1312,10 +1322,6 @@ fn demo_body_style(font_size: f32, color: Color) -> TextStyle {
|
|||||||
TextStyle::new(font_size, color).with_selection_style(DEMO_SELECTION_STYLE)
|
TextStyle::new(font_size, color).with_selection_style(DEMO_SELECTION_STYLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demo_unselectable_title(text: impl Into<String>, id: ElementId, style: TextStyle) -> Element {
|
|
||||||
Element::paragraph(text, style.with_selectable(false)).id(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_rust_website() {
|
fn open_rust_website() {
|
||||||
if let Err(error) = Command::new("xdg-open")
|
if let Err(error) = Command::new("xdg-open")
|
||||||
.arg("https://www.rust-lang.org")
|
.arg("https://www.rust-lang.org")
|
||||||
@@ -1374,29 +1380,32 @@ fn input_field_element(input_field: &InputFieldState, focused: bool) -> Element
|
|||||||
fn build_document_tree(
|
fn build_document_tree(
|
||||||
viewport: UiSize,
|
viewport: UiSize,
|
||||||
hovered_card: Option<ElementId>,
|
hovered_card: Option<ElementId>,
|
||||||
|
hero_image: &ImageResource,
|
||||||
input_fields: &[InputFieldState],
|
input_fields: &[InputFieldState],
|
||||||
focused_element: Option<ElementId>,
|
focused_element: Option<ElementId>,
|
||||||
) -> Element {
|
) -> Element {
|
||||||
let gutter = (viewport.width * 0.025).clamp(18.0, 30.0);
|
let gutter = (viewport.width * 0.025).clamp(18.0, 30.0);
|
||||||
let sidebar_width = (viewport.width * 0.28).clamp(220.0, 320.0);
|
let sidebar_width = (viewport.width * 0.28).clamp(220.0, 320.0);
|
||||||
|
let hero_image_width = (viewport.width * 0.32).clamp(200.0, 320.0);
|
||||||
|
let hero_image_height = (hero_image_width * 0.34).clamp(72.0, 128.0);
|
||||||
|
|
||||||
Element::column()
|
Element::column()
|
||||||
.background(Color::rgb(0x0F, 0x13, 0x1E))
|
.background(Color::rgb(0x0F, 0x13, 0x1E))
|
||||||
.padding(Edges::all(gutter))
|
.padding(Edges::all(gutter))
|
||||||
.gap(gutter)
|
.gap(gutter)
|
||||||
.children([
|
.children([
|
||||||
Element::column()
|
Element::row()
|
||||||
.padding(Edges::all(gutter))
|
.padding(Edges::all(gutter))
|
||||||
.gap(gutter * 0.45)
|
.gap(gutter)
|
||||||
.background(Color::rgb(0x16, 0x1D, 0x2B))
|
.background(Color::rgb(0x16, 0x1D, 0x2B))
|
||||||
.children([
|
.children([
|
||||||
demo_unselectable_title(
|
Element::image(hero_image.clone())
|
||||||
"RUIN paragraph demo",
|
.id(HERO_TITLE_ID)
|
||||||
HERO_TITLE_ID,
|
.width(hero_image_width)
|
||||||
TextStyle::new(34.0, Color::rgb(0xF5, 0xF7, 0xFB))
|
.height(hero_image_height)
|
||||||
.with_line_height(40.0)
|
.image_fit(ImageFit::Contain)
|
||||||
.with_align(TextAlign::Center),
|
.pointer_events(false),
|
||||||
),
|
Element::column().flex(1.0).gap(gutter * 0.45).children([
|
||||||
Element::rich_paragraph(
|
Element::rich_paragraph(
|
||||||
[
|
[
|
||||||
TextSpan::new("RUIN is exploring a "),
|
TextSpan::new("RUIN is exploring a "),
|
||||||
@@ -1441,6 +1450,7 @@ fn build_document_tree(
|
|||||||
focused_element == Some(input_fields[1].field_id),
|
focused_element == Some(input_fields[1].field_id),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
]),
|
||||||
Element::row().flex(1.0).gap(gutter).children([
|
Element::row().flex(1.0).gap(gutter).children([
|
||||||
Element::column()
|
Element::column()
|
||||||
.flex(1.0)
|
.flex(1.0)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmic-text = "0.18.2"
|
cosmic-text = "0.18.2"
|
||||||
|
image = { version = "0.25", default-features = false, features = ["rayon", "avif", "bmp", "dds", "exr", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp"] }
|
||||||
ruin_reactivity = { path = "../reactivity" }
|
ruin_reactivity = { path = "../reactivity" }
|
||||||
ruin_runtime = { package = "ruin-runtime", path = "../runtime" }
|
ruin_runtime = { package = "ruin-runtime", path = "../runtime" }
|
||||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
|
|||||||
161
lib/ui/src/image.rs
Normal file
161
lib/ui/src/image.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
|
use image::ImageReader;
|
||||||
|
use ruin_runtime::channel::oneshot;
|
||||||
|
|
||||||
|
use crate::UiSize;
|
||||||
|
|
||||||
|
static NEXT_IMAGE_RESOURCE_ID: AtomicU64 = AtomicU64::new(1);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum ImageFit {
|
||||||
|
Contain,
|
||||||
|
Cover,
|
||||||
|
Fill,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ImageResource {
|
||||||
|
inner: Arc<ImageResourceInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImageResourceInner {
|
||||||
|
id: u64,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
pixels: Arc<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ImageResourceError {
|
||||||
|
Io(std::io::Error),
|
||||||
|
Decode(image::ImageError),
|
||||||
|
DecodeTaskClosed,
|
||||||
|
InvalidRgbaPixels { width: u32, height: u32, len: usize },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageResource {
|
||||||
|
pub async fn load_path(path: impl AsRef<Path>) -> Result<Self, ImageResourceError> {
|
||||||
|
let bytes = ruin_runtime::fs::read(path.as_ref())
|
||||||
|
.await
|
||||||
|
.map_err(ImageResourceError::Io)?;
|
||||||
|
Self::decode_encoded_bytes(bytes).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decode_encoded_bytes(
|
||||||
|
bytes: impl Into<Vec<u8>>,
|
||||||
|
) -> Result<Self, ImageResourceError> {
|
||||||
|
let bytes = bytes.into();
|
||||||
|
let (tx, mut rx) = oneshot::channel();
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("ruin-ui-image-decode".to_owned())
|
||||||
|
.spawn(move || {
|
||||||
|
let result = Self::decode_encoded_bytes_sync(&bytes);
|
||||||
|
let _ = tx.send(result);
|
||||||
|
})
|
||||||
|
.map_err(ImageResourceError::Io)?;
|
||||||
|
rx.recv()
|
||||||
|
.await
|
||||||
|
.map_err(|_| ImageResourceError::DecodeTaskClosed)?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_rgba8(
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
pixels: impl Into<Vec<u8>>,
|
||||||
|
) -> Result<Self, ImageResourceError> {
|
||||||
|
let pixels = pixels.into();
|
||||||
|
let expected_len = width as usize * height as usize * 4;
|
||||||
|
if pixels.len() != expected_len {
|
||||||
|
return Err(ImageResourceError::InvalidRgbaPixels {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
len: pixels.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
inner: Arc::new(ImageResourceInner {
|
||||||
|
id: NEXT_IMAGE_RESOURCE_ID.fetch_add(1, Ordering::Relaxed),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
pixels: pixels.into(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_encoded_bytes_sync(bytes: &[u8]) -> Result<Self, ImageResourceError> {
|
||||||
|
let image = ImageReader::new(std::io::Cursor::new(bytes))
|
||||||
|
.with_guessed_format()
|
||||||
|
.map_err(ImageResourceError::Io)?
|
||||||
|
.decode()
|
||||||
|
.map_err(ImageResourceError::Decode)?;
|
||||||
|
let rgba = image.to_rgba8();
|
||||||
|
Self::from_rgba8(rgba.width(), rgba.height(), rgba.into_raw())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> u64 {
|
||||||
|
self.inner.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> u32 {
|
||||||
|
self.inner.width
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> u32 {
|
||||||
|
self.inner.height
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> UiSize {
|
||||||
|
UiSize::new(self.inner.width as f32, self.inner.height as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pixels(&self) -> &[u8] {
|
||||||
|
&self.inner.pixels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ImageResource {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("ImageResource")
|
||||||
|
.field("id", &self.inner.id)
|
||||||
|
.field("width", &self.inner.width)
|
||||||
|
.field("height", &self.inner.height)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ImageResource {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.inner.id == other.inner.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ImageResource {}
|
||||||
|
|
||||||
|
impl std::hash::Hash for ImageResource {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.inner.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ImageResourceError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Io(error) => write!(f, "{error}"),
|
||||||
|
Self::Decode(error) => write!(f, "{error}"),
|
||||||
|
Self::DecodeTaskClosed => {
|
||||||
|
f.write_str("image decode task closed before producing a result")
|
||||||
|
}
|
||||||
|
Self::InvalidRgbaPixels { width, height, len } => write!(
|
||||||
|
f,
|
||||||
|
"expected {} RGBA bytes for {width}x{height} image, got {len}",
|
||||||
|
*width as usize * *height as usize * 4
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ImageResourceError {}
|
||||||
@@ -167,6 +167,7 @@ mod tests {
|
|||||||
pointer_events: false,
|
pointer_events: false,
|
||||||
focusable: false,
|
focusable: false,
|
||||||
cursor: CursorIcon::Default,
|
cursor: CursorIcon::Default,
|
||||||
|
prepared_image: None,
|
||||||
prepared_text: None,
|
prepared_text: None,
|
||||||
children: vec![
|
children: vec![
|
||||||
LayoutNode {
|
LayoutNode {
|
||||||
@@ -176,6 +177,7 @@ mod tests {
|
|||||||
pointer_events: true,
|
pointer_events: true,
|
||||||
focusable: false,
|
focusable: false,
|
||||||
cursor: CursorIcon::Default,
|
cursor: CursorIcon::Default,
|
||||||
|
prepared_image: None,
|
||||||
prepared_text: None,
|
prepared_text: None,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
},
|
},
|
||||||
@@ -186,6 +188,7 @@ mod tests {
|
|||||||
pointer_events: true,
|
pointer_events: true,
|
||||||
focusable: false,
|
focusable: false,
|
||||||
cursor: CursorIcon::Default,
|
cursor: CursorIcon::Default,
|
||||||
|
prepared_image: None,
|
||||||
prepared_text: None,
|
prepared_text: None,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
},
|
},
|
||||||
@@ -203,6 +206,7 @@ mod tests {
|
|||||||
pointer_events: false,
|
pointer_events: false,
|
||||||
focusable: false,
|
focusable: false,
|
||||||
cursor: CursorIcon::Default,
|
cursor: CursorIcon::Default,
|
||||||
|
prepared_image: None,
|
||||||
prepared_text: None,
|
prepared_text: None,
|
||||||
children: vec![LayoutNode {
|
children: vec![LayoutNode {
|
||||||
path: LayoutPath::root().child(0),
|
path: LayoutPath::root().child(0),
|
||||||
@@ -211,6 +215,7 @@ mod tests {
|
|||||||
pointer_events: true,
|
pointer_events: true,
|
||||||
focusable: false,
|
focusable: false,
|
||||||
cursor: CursorIcon::Default,
|
cursor: CursorIcon::Default,
|
||||||
|
prepared_image: None,
|
||||||
prepared_text: None,
|
prepared_text: None,
|
||||||
children: vec![LayoutNode {
|
children: vec![LayoutNode {
|
||||||
path: LayoutPath::root().child(0).child(0),
|
path: LayoutPath::root().child(0).child(0),
|
||||||
@@ -219,6 +224,7 @@ mod tests {
|
|||||||
pointer_events: true,
|
pointer_events: true,
|
||||||
focusable: false,
|
focusable: false,
|
||||||
cursor: CursorIcon::Default,
|
cursor: CursorIcon::Default,
|
||||||
|
prepared_image: None,
|
||||||
prepared_text: None,
|
prepared_text: None,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}],
|
}],
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::scene::{PreparedText, Rect, SceneSnapshot, UiSize};
|
use crate::ImageFit;
|
||||||
|
use crate::scene::{PreparedImage, PreparedText, Rect, SceneSnapshot, UiSize};
|
||||||
use crate::text::TextSystem;
|
use crate::text::TextSystem;
|
||||||
use crate::tree::{CursorIcon, Edges, Element, ElementId, FlexDirection};
|
use crate::tree::{CursorIcon, Edges, Element, ElementId, FlexDirection, ImageNode};
|
||||||
|
|
||||||
pub fn layout_scene(version: u64, logical_size: UiSize, root: &Element) -> SceneSnapshot {
|
pub fn layout_scene(version: u64, logical_size: UiSize, root: &Element) -> SceneSnapshot {
|
||||||
let mut text_system = TextSystem::new();
|
let mut text_system = TextSystem::new();
|
||||||
@@ -60,6 +61,7 @@ pub struct LayoutNode {
|
|||||||
pub pointer_events: bool,
|
pub pointer_events: bool,
|
||||||
pub focusable: bool,
|
pub focusable: bool,
|
||||||
pub cursor: CursorIcon,
|
pub cursor: CursorIcon,
|
||||||
|
pub prepared_image: Option<PreparedImage>,
|
||||||
pub prepared_text: Option<PreparedText>,
|
pub prepared_text: Option<PreparedText>,
|
||||||
pub children: Vec<LayoutNode>,
|
pub children: Vec<LayoutNode>,
|
||||||
}
|
}
|
||||||
@@ -188,6 +190,7 @@ fn layout_element(
|
|||||||
pointer_events: element.style.pointer_events,
|
pointer_events: element.style.pointer_events,
|
||||||
focusable: element.style.focusable,
|
focusable: element.style.focusable,
|
||||||
cursor,
|
cursor,
|
||||||
|
prepared_image: None,
|
||||||
prepared_text: None,
|
prepared_text: None,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
};
|
};
|
||||||
@@ -223,6 +226,16 @@ fn layout_element(
|
|||||||
return interaction;
|
return interaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(image) = element.image_node() {
|
||||||
|
let content = inset_rect(rect, element.style.padding);
|
||||||
|
if content.size.width > 0.0 && content.size.height > 0.0 {
|
||||||
|
let prepared = prepare_image(image, content, element.id);
|
||||||
|
scene.push_image(prepared.clone());
|
||||||
|
interaction.prepared_image = Some(prepared);
|
||||||
|
}
|
||||||
|
return interaction;
|
||||||
|
}
|
||||||
|
|
||||||
perf_stats.container_nodes += 1;
|
perf_stats.container_nodes += 1;
|
||||||
|
|
||||||
if element.children.is_empty() {
|
if element.children.is_empty() {
|
||||||
@@ -427,6 +440,11 @@ fn intrinsic_main_size(
|
|||||||
return main_axis_size(content, direction) + padding;
|
return main_axis_size(content, direction) + padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(image) = child.image_node() {
|
||||||
|
let resolved = resolve_image_element_size(child, image.resource.size());
|
||||||
|
return main_axis_size(resolved, direction);
|
||||||
|
}
|
||||||
|
|
||||||
let available_size = match direction {
|
let available_size = match direction {
|
||||||
FlexDirection::Row => UiSize::new(available_main.max(0.0), cross_size.max(0.0)),
|
FlexDirection::Row => UiSize::new(available_main.max(0.0), cross_size.max(0.0)),
|
||||||
FlexDirection::Column => UiSize::new(cross_size.max(0.0), available_main.max(0.0)),
|
FlexDirection::Column => UiSize::new(cross_size.max(0.0), available_main.max(0.0)),
|
||||||
@@ -461,6 +479,14 @@ fn intrinsic_size(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(image) = element.image_node() {
|
||||||
|
let resolved = resolve_image_element_size(element, image.resource.size());
|
||||||
|
return UiSize::new(
|
||||||
|
resolved.width + element.style.padding.left + element.style.padding.right,
|
||||||
|
resolved.height + element.style.padding.top + element.style.padding.bottom,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let explicit_width = element.style.width;
|
let explicit_width = element.style.width;
|
||||||
let explicit_height = element.style.height;
|
let explicit_height = element.style.height;
|
||||||
let content_width = explicit_width.unwrap_or(available_size.width).max(0.0)
|
let content_width = explicit_width.unwrap_or(available_size.width).max(0.0)
|
||||||
@@ -484,9 +510,7 @@ fn intrinsic_size(
|
|||||||
let mut width: f32 = 0.0;
|
let mut width: f32 = 0.0;
|
||||||
let mut height = gap_total;
|
let mut height = gap_total;
|
||||||
for child in &element.children {
|
for child in &element.children {
|
||||||
if child.style.flex_grow > 0.0 && child.style.height.is_none() {
|
let skip_main = child.style.flex_grow > 0.0 && child.style.height.is_none();
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let child_size = intrinsic_size(
|
let child_size = intrinsic_size(
|
||||||
child,
|
child,
|
||||||
UiSize::new(
|
UiSize::new(
|
||||||
@@ -497,15 +521,23 @@ fn intrinsic_size(
|
|||||||
perf_stats,
|
perf_stats,
|
||||||
);
|
);
|
||||||
width = width.max(child.style.width.unwrap_or(child_size.width));
|
width = width.max(child.style.width.unwrap_or(child_size.width));
|
||||||
|
if !skip_main {
|
||||||
height += child.style.height.unwrap_or(child_size.height);
|
height += child.style.height.unwrap_or(child_size.height);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
(width, height)
|
(width, height)
|
||||||
}
|
}
|
||||||
FlexDirection::Row => {
|
FlexDirection::Row => {
|
||||||
let mut width = gap_total;
|
let mut width = gap_total;
|
||||||
let mut height: f32 = 0.0;
|
let mut height: f32 = 0.0;
|
||||||
|
let mut fixed_main = 0.0;
|
||||||
|
let mut flex_total = 0.0;
|
||||||
|
let mut child_main_sizes = Vec::with_capacity(element.children.len());
|
||||||
for child in &element.children {
|
for child in &element.children {
|
||||||
if child.style.flex_grow > 0.0 && child.style.width.is_none() {
|
let is_flex = child.style.flex_grow > 0.0 && child.style.width.is_none();
|
||||||
|
if is_flex {
|
||||||
|
flex_total += child_flex_weight(child);
|
||||||
|
child_main_sizes.push(None);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let child_size = intrinsic_size(
|
let child_size = intrinsic_size(
|
||||||
@@ -517,7 +549,32 @@ fn intrinsic_size(
|
|||||||
text_system,
|
text_system,
|
||||||
perf_stats,
|
perf_stats,
|
||||||
);
|
);
|
||||||
width += child.style.width.unwrap_or(child_size.width);
|
let child_main = child.style.width.unwrap_or(child_size.width);
|
||||||
|
fixed_main += child_main;
|
||||||
|
child_main_sizes.push(Some(child_main));
|
||||||
|
}
|
||||||
|
let remaining_main = (content_size.width - gap_total - fixed_main).max(0.0);
|
||||||
|
for (child, measured_main) in element.children.iter().zip(child_main_sizes.iter()) {
|
||||||
|
let skip_main = child.style.flex_grow > 0.0 && child.style.width.is_none();
|
||||||
|
let child_main = measured_main.unwrap_or_else(|| {
|
||||||
|
if flex_total <= 0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
remaining_main * (child_flex_weight(child) / flex_total)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let child_size = intrinsic_size(
|
||||||
|
child,
|
||||||
|
UiSize::new(
|
||||||
|
child_main,
|
||||||
|
child.style.height.unwrap_or(content_size.height),
|
||||||
|
),
|
||||||
|
text_system,
|
||||||
|
perf_stats,
|
||||||
|
);
|
||||||
|
if !skip_main {
|
||||||
|
width += child_main;
|
||||||
|
}
|
||||||
height = height.max(child.style.height.unwrap_or(child_size.height));
|
height = height.max(child.style.height.unwrap_or(child_size.height));
|
||||||
}
|
}
|
||||||
(width, height)
|
(width, height)
|
||||||
@@ -569,6 +626,67 @@ impl LayoutPerfStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_image(image: &ImageNode, rect: Rect, element_id: Option<ElementId>) -> PreparedImage {
|
||||||
|
let source_size = image.resource.size();
|
||||||
|
let source_aspect = if source_size.height > 0.0 {
|
||||||
|
source_size.width / source_size.height
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
let rect_aspect = if rect.size.height > 0.0 {
|
||||||
|
rect.size.width / rect.size.height
|
||||||
|
} else {
|
||||||
|
source_aspect
|
||||||
|
};
|
||||||
|
|
||||||
|
let (draw_rect, uv_rect) = match image.fit {
|
||||||
|
ImageFit::Fill => (rect, (0.0, 0.0, 1.0, 1.0)),
|
||||||
|
ImageFit::Contain => {
|
||||||
|
let scale = (rect.size.width / source_size.width)
|
||||||
|
.min(rect.size.height / source_size.height)
|
||||||
|
.max(0.0);
|
||||||
|
let width = source_size.width * scale;
|
||||||
|
let height = source_size.height * scale;
|
||||||
|
let x = rect.origin.x + (rect.size.width - width) * 0.5;
|
||||||
|
let y = rect.origin.y + (rect.size.height - height) * 0.5;
|
||||||
|
(Rect::new(x, y, width, height), (0.0, 0.0, 1.0, 1.0))
|
||||||
|
}
|
||||||
|
ImageFit::Cover => {
|
||||||
|
if rect_aspect > source_aspect {
|
||||||
|
let visible_height = (source_aspect / rect_aspect).clamp(0.0, 1.0);
|
||||||
|
let inset = (1.0 - visible_height) * 0.5;
|
||||||
|
(rect, (0.0, inset, 1.0, 1.0 - inset))
|
||||||
|
} else {
|
||||||
|
let visible_width = (rect_aspect / source_aspect).clamp(0.0, 1.0);
|
||||||
|
let inset = (1.0 - visible_width) * 0.5;
|
||||||
|
(rect, (inset, 0.0, 1.0 - inset, 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PreparedImage {
|
||||||
|
element_id,
|
||||||
|
resource: image.resource.clone(),
|
||||||
|
rect: draw_rect,
|
||||||
|
uv_rect,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_image_element_size(element: &Element, intrinsic: UiSize) -> UiSize {
|
||||||
|
match (element.style.width, element.style.height) {
|
||||||
|
(Some(width), Some(height)) => UiSize::new(width.max(0.0), height.max(0.0)),
|
||||||
|
(Some(width), None) if intrinsic.width > 0.0 => UiSize::new(
|
||||||
|
width.max(0.0),
|
||||||
|
(width * intrinsic.height / intrinsic.width).max(0.0),
|
||||||
|
),
|
||||||
|
(None, Some(height)) if intrinsic.height > 0.0 => UiSize::new(
|
||||||
|
(height * intrinsic.width / intrinsic.height).max(0.0),
|
||||||
|
height.max(0.0),
|
||||||
|
),
|
||||||
|
_ => intrinsic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn inset_rect(rect: Rect, edges: Edges) -> Rect {
|
fn inset_rect(rect: Rect, edges: Edges) -> Rect {
|
||||||
let width = (rect.size.width - edges.left - edges.right).max(0.0);
|
let width = (rect.size.width - edges.left - edges.right).max(0.0);
|
||||||
let height = (rect.size.height - edges.top - edges.bottom).max(0.0);
|
let height = (rect.size.height - edges.top - edges.bottom).max(0.0);
|
||||||
@@ -773,6 +891,60 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn row_container_counts_flex_child_height_in_intrinsic_size() {
|
||||||
|
let row_color = Color::rgb(0x12, 0x24, 0x36);
|
||||||
|
let root = Element::column().child(
|
||||||
|
Element::row()
|
||||||
|
.padding(Edges::all(12.0))
|
||||||
|
.gap(16.0)
|
||||||
|
.background(row_color)
|
||||||
|
.children([
|
||||||
|
Element::new()
|
||||||
|
.width(120.0)
|
||||||
|
.height(60.0)
|
||||||
|
.background(Color::rgb(0x44, 0x55, 0x66)),
|
||||||
|
Element::column().flex(1.0).child(Element::paragraph(
|
||||||
|
"A flex child with wrapped text should make the row grow tall enough to contain it.",
|
||||||
|
TextStyle::new(18.0, Color::rgb(0xFF, 0xFF, 0xFF))
|
||||||
|
.with_line_height(24.0)
|
||||||
|
.with_wrap(TextWrap::Word),
|
||||||
|
)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
let scene = layout_scene(1, UiSize::new(360.0, 320.0), &root);
|
||||||
|
let row_quad = scene
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|item| match item {
|
||||||
|
DisplayItem::Quad(quad) if quad.color == row_color => Some(*quad),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.expect("row container should emit a background quad");
|
||||||
|
let text = scene
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.find_map(|item| match item {
|
||||||
|
DisplayItem::Text(text) => Some(text),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("row should emit a text display item");
|
||||||
|
let text_bounds = text.bounds.expect("text layout should provide bounds");
|
||||||
|
let row_bottom = row_quad.rect.origin.y + row_quad.rect.size.height;
|
||||||
|
let text_bottom = text.origin.y + text_bounds.height;
|
||||||
|
|
||||||
|
assert!(row_quad.rect.size.height > 84.0);
|
||||||
|
assert!(row_bottom >= text_bottom);
|
||||||
|
assert!(
|
||||||
|
scene
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.any(|item| matches!(item, DisplayItem::Text(_)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interaction_tree_hit_test_returns_deepest_pointer_target() {
|
fn interaction_tree_hit_test_returns_deepest_pointer_target() {
|
||||||
let root = Element::column()
|
let root = Element::column()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub(crate) mod trace_targets {
|
|||||||
pub const SCENE: &str = "ruin_ui::scene";
|
pub const SCENE: &str = "ruin_ui::scene";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod image;
|
||||||
mod interaction;
|
mod interaction;
|
||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod layout;
|
mod layout;
|
||||||
@@ -20,6 +21,7 @@ mod text;
|
|||||||
mod tree;
|
mod tree;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
|
pub use image::{ImageFit, ImageResource, ImageResourceError};
|
||||||
pub use interaction::{
|
pub use interaction::{
|
||||||
PointerButton, PointerEvent, PointerEventKind, PointerRouter, RoutedPointerEvent,
|
PointerButton, PointerEvent, PointerEventKind, PointerRouter, RoutedPointerEvent,
|
||||||
RoutedPointerEventKind,
|
RoutedPointerEventKind,
|
||||||
@@ -36,8 +38,8 @@ pub use platform::{
|
|||||||
};
|
};
|
||||||
pub use runtime::{EventStreamClosed, UiRuntime, WindowController};
|
pub use runtime::{EventStreamClosed, UiRuntime, WindowController};
|
||||||
pub use scene::{
|
pub use scene::{
|
||||||
Color, DisplayItem, GlyphInstance, Point, PreparedText, PreparedTextLine, Quad, Rect,
|
Color, DisplayItem, GlyphInstance, Point, PreparedImage, PreparedText, PreparedTextLine, Quad,
|
||||||
SceneSnapshot, Translation, UiSize,
|
Rect, SceneSnapshot, Translation, UiSize,
|
||||||
};
|
};
|
||||||
pub use text::{
|
pub use text::{
|
||||||
TextAlign, TextFontFamily, TextSelectionStyle, TextSpan, TextSpanSlant, TextSpanWeight,
|
TextAlign, TextFontFamily, TextSelectionStyle, TextSpan, TextSpanSlant, TextSpanWeight,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::ops::Range;
|
|||||||
use cosmic_text::CacheKey;
|
use cosmic_text::CacheKey;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
use crate::ImageResource;
|
||||||
use crate::text::TextSelectionStyle;
|
use crate::text::TextSelectionStyle;
|
||||||
use crate::trace_targets;
|
use crate::trace_targets;
|
||||||
use crate::tree::ElementId;
|
use crate::tree::ElementId;
|
||||||
@@ -133,6 +134,14 @@ pub struct PreparedText {
|
|||||||
pub glyphs: Vec<GlyphInstance>,
|
pub glyphs: Vec<GlyphInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct PreparedImage {
|
||||||
|
pub element_id: Option<ElementId>,
|
||||||
|
pub resource: ImageResource,
|
||||||
|
pub rect: Rect,
|
||||||
|
pub uv_rect: (f32, f32, f32, f32),
|
||||||
|
}
|
||||||
|
|
||||||
impl PreparedText {
|
impl PreparedText {
|
||||||
pub fn monospace(
|
pub fn monospace(
|
||||||
text: impl Into<String>,
|
text: impl Into<String>,
|
||||||
@@ -456,6 +465,7 @@ fn classify_word_char(ch: char) -> WordClass {
|
|||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum DisplayItem {
|
pub enum DisplayItem {
|
||||||
Quad(Quad),
|
Quad(Quad),
|
||||||
|
Image(PreparedImage),
|
||||||
Text(PreparedText),
|
Text(PreparedText),
|
||||||
PushClip(Rect),
|
PushClip(Rect),
|
||||||
PopClip,
|
PopClip,
|
||||||
@@ -506,6 +516,10 @@ impl SceneSnapshot {
|
|||||||
self.push_item(DisplayItem::Text(text))
|
self.push_item(DisplayItem::Text(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_image(&mut self, image: PreparedImage) -> &mut Self {
|
||||||
|
self.push_item(DisplayItem::Image(image))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push_clip(&mut self, rect: Rect) -> &mut Self {
|
pub fn push_clip(&mut self, rect: Rect) -> &mut Self {
|
||||||
self.push_item(DisplayItem::PushClip(rect))
|
self.push_item(DisplayItem::PushClip(rect))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::scene::Color;
|
use crate::scene::Color;
|
||||||
use crate::text::{TextSpan, TextStyle, TextWrap};
|
use crate::text::{TextSpan, TextStyle, TextWrap};
|
||||||
|
use crate::{ImageFit, ImageResource};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct ElementId(u64);
|
pub struct ElementId(u64);
|
||||||
@@ -96,6 +97,7 @@ impl Default for Style {
|
|||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum ElementContent {
|
enum ElementContent {
|
||||||
Container,
|
Container,
|
||||||
|
Image(ImageNode),
|
||||||
Text(TextNode),
|
Text(TextNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +107,12 @@ pub(crate) struct TextNode {
|
|||||||
pub style: TextStyle,
|
pub style: TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub(crate) struct ImageNode {
|
||||||
|
pub resource: ImageResource,
|
||||||
|
pub fit: ImageFit,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Element {
|
pub struct Element {
|
||||||
pub id: Option<ElementId>,
|
pub id: Option<ElementId>,
|
||||||
@@ -143,6 +151,18 @@ impl Element {
|
|||||||
Self::text(text, style.with_wrap(TextWrap::Word))
|
Self::text(text, style.with_wrap(TextWrap::Word))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn image(resource: ImageResource) -> Self {
|
||||||
|
Self {
|
||||||
|
id: None,
|
||||||
|
style: Style::default(),
|
||||||
|
children: Vec::new(),
|
||||||
|
content: ElementContent::Image(ImageNode {
|
||||||
|
resource,
|
||||||
|
fit: ImageFit::Contain,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rich_paragraph(spans: impl IntoIterator<Item = TextSpan>, style: TextStyle) -> Self {
|
pub fn rich_paragraph(spans: impl IntoIterator<Item = TextSpan>, style: TextStyle) -> Self {
|
||||||
Self::spans(spans, style.with_wrap(TextWrap::Word))
|
Self::spans(spans, style.with_wrap(TextWrap::Word))
|
||||||
}
|
}
|
||||||
@@ -210,6 +230,14 @@ impl Element {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn image_fit(mut self, fit: ImageFit) -> Self {
|
||||||
|
let ElementContent::Image(image) = &mut self.content else {
|
||||||
|
panic!("only image elements can set image_fit");
|
||||||
|
};
|
||||||
|
image.fit = fit;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn child(mut self, child: Element) -> Self {
|
pub fn child(mut self, child: Element) -> Self {
|
||||||
self.assert_container();
|
self.assert_container();
|
||||||
self.children.push(child);
|
self.children.push(child);
|
||||||
@@ -225,14 +253,21 @@ impl Element {
|
|||||||
pub(crate) fn text_node(&self) -> Option<&TextNode> {
|
pub(crate) fn text_node(&self) -> Option<&TextNode> {
|
||||||
match &self.content {
|
match &self.content {
|
||||||
ElementContent::Text(text) => Some(text),
|
ElementContent::Text(text) => Some(text),
|
||||||
ElementContent::Container => None,
|
ElementContent::Container | ElementContent::Image(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn image_node(&self) -> Option<&ImageNode> {
|
||||||
|
match &self.content {
|
||||||
|
ElementContent::Image(image) => Some(image),
|
||||||
|
ElementContent::Container | ElementContent::Text(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_container(&self) {
|
fn assert_container(&self) {
|
||||||
assert!(
|
assert!(
|
||||||
matches!(self.content, ElementContent::Container),
|
matches!(self.content, ElementContent::Container),
|
||||||
"text elements cannot contain children"
|
"non-container elements cannot contain children"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,11 +294,11 @@ impl WaylandWindow {
|
|||||||
let seat: wl_seat::WlSeat = globals.bind(&qh, 1..=9, ())?;
|
let seat: wl_seat::WlSeat = globals.bind(&qh, 1..=9, ())?;
|
||||||
let wm_base: xdg_wm_base::XdgWmBase = globals.bind(&qh, 1..=6, ())?;
|
let wm_base: xdg_wm_base::XdgWmBase = globals.bind(&qh, 1..=6, ())?;
|
||||||
let clipboard_manager = globals.bind(&qh, 1..=3, ()).ok();
|
let clipboard_manager = globals.bind(&qh, 1..=3, ()).ok();
|
||||||
let clipboard_device = clipboard_manager
|
let clipboard_device = clipboard_manager.as_ref().map(
|
||||||
.as_ref()
|
|manager: &wl_data_device_manager::WlDataDeviceManager| {
|
||||||
.map(|manager: &wl_data_device_manager::WlDataDeviceManager| {
|
|
||||||
manager.get_data_device(&seat, &qh, ())
|
manager.get_data_device(&seat, &qh, ())
|
||||||
});
|
},
|
||||||
|
);
|
||||||
let cursor_shape_manager = globals.bind(&qh, 1..=2, ()).ok();
|
let cursor_shape_manager = globals.bind(&qh, 1..=2, ()).ok();
|
||||||
let primary_selection_manager = globals.bind(&qh, 1..=1, ()).ok();
|
let primary_selection_manager = globals.bind(&qh, 1..=1, ()).ok();
|
||||||
let primary_selection_device = primary_selection_manager.as_ref().map(
|
let primary_selection_device = primary_selection_manager.as_ref().map(
|
||||||
@@ -619,7 +619,8 @@ impl WaylandWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_primary_selection_text(&mut self) -> Result<Option<String>, Box<dyn Error>> {
|
pub fn read_primary_selection_text(&mut self) -> Result<Option<String>, Box<dyn Error>> {
|
||||||
let preferred_mime = preferred_plain_text_mime(&self.state.primary_selection_offer_mime_types);
|
let preferred_mime =
|
||||||
|
preferred_plain_text_mime(&self.state.primary_selection_offer_mime_types);
|
||||||
let Some(mime_type) = preferred_mime else {
|
let Some(mime_type) = preferred_mime else {
|
||||||
return Ok(self.state.primary_selection_text.clone());
|
return Ok(self.state.primary_selection_text.clone());
|
||||||
};
|
};
|
||||||
@@ -1097,7 +1098,8 @@ fn spawn_window_worker(
|
|||||||
let mut state_ref = state.borrow_mut();
|
let mut state_ref = state.borrow_mut();
|
||||||
match state_ref.window.read_clipboard_text() {
|
match state_ref.window.read_clipboard_text() {
|
||||||
Ok(Some(text)) => {
|
Ok(Some(text)) => {
|
||||||
let _ = state_ref.event_tx.send(PlatformEvent::ClipboardText {
|
let _ =
|
||||||
|
state_ref.event_tx.send(PlatformEvent::ClipboardText {
|
||||||
window_id: state_ref.window_id,
|
window_id: state_ref.window_id,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use cosmic_text::{
|
|||||||
Attrs, Buffer, CacheKey, FontSystem, Metrics, Shaping, SwashCache, SwashContent, SwashImage,
|
Attrs, Buffer, CacheKey, FontSystem, Metrics, Shaping, SwashCache, SwashContent, SwashImage,
|
||||||
};
|
};
|
||||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||||
use ruin_ui::{Color, DisplayItem, Point, PreparedText, Rect, SceneSnapshot, UiSize};
|
use ruin_ui::{
|
||||||
|
Color, DisplayItem, Point, PreparedImage, PreparedText, Rect, SceneSnapshot, UiSize,
|
||||||
|
};
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
@@ -70,6 +72,11 @@ struct CachedTextTexture {
|
|||||||
size: UiSize,
|
size: UiSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CachedImageTexture {
|
||||||
|
_texture: wgpu::Texture,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
struct GlyphAtlas {
|
struct GlyphAtlas {
|
||||||
texture: wgpu::Texture,
|
texture: wgpu::Texture,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
@@ -106,6 +113,12 @@ struct UploadedText {
|
|||||||
vertex_count: u32,
|
vertex_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UploadedImage {
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
vertex_buffer: wgpu::Buffer,
|
||||||
|
vertex_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
struct UploadedAtlasText {
|
struct UploadedAtlasText {
|
||||||
vertex_buffer: wgpu::Buffer,
|
vertex_buffer: wgpu::Buffer,
|
||||||
vertex_count: u32,
|
vertex_count: u32,
|
||||||
@@ -181,10 +194,13 @@ pub struct WgpuSceneRenderer {
|
|||||||
swash_cache: SwashCache,
|
swash_cache: SwashCache,
|
||||||
text_cache: HashMap<TextTextureKey, CachedTextTexture>,
|
text_cache: HashMap<TextTextureKey, CachedTextTexture>,
|
||||||
text_cache_order: VecDeque<TextTextureKey>,
|
text_cache_order: VecDeque<TextTextureKey>,
|
||||||
|
image_cache: HashMap<u64, CachedImageTexture>,
|
||||||
|
image_cache_order: VecDeque<u64>,
|
||||||
glyph_atlas: GlyphAtlas,
|
glyph_atlas: GlyphAtlas,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_TEXT_CACHE_ENTRIES: usize = 64;
|
const MAX_TEXT_CACHE_ENTRIES: usize = 64;
|
||||||
|
const MAX_IMAGE_CACHE_ENTRIES: usize = 64;
|
||||||
const GLYPH_ATLAS_SIZE: u32 = 2048;
|
const GLYPH_ATLAS_SIZE: u32 = 2048;
|
||||||
const GLYPH_ATLAS_PADDING: u32 = 1;
|
const GLYPH_ATLAS_PADDING: u32 = 1;
|
||||||
|
|
||||||
@@ -397,6 +413,8 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
swash_cache: SwashCache::new(),
|
swash_cache: SwashCache::new(),
|
||||||
text_cache: HashMap::new(),
|
text_cache: HashMap::new(),
|
||||||
text_cache_order: VecDeque::new(),
|
text_cache_order: VecDeque::new(),
|
||||||
|
image_cache: HashMap::new(),
|
||||||
|
image_cache_order: VecDeque::new(),
|
||||||
glyph_atlas,
|
glyph_atlas,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -434,6 +452,14 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
usage: wgpu::BufferUsages::VERTEX,
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
});
|
});
|
||||||
let text_prepare_start = std::time::Instant::now();
|
let text_prepare_start = std::time::Instant::now();
|
||||||
|
let uploaded_images: Vec<_> = scene
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|item| match item {
|
||||||
|
DisplayItem::Image(image) => self.prepare_uploaded_image(image, scene.logical_size),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
let uploaded_atlas_text = self.prepare_uploaded_atlas_text(scene);
|
let uploaded_atlas_text = self.prepare_uploaded_atlas_text(scene);
|
||||||
let uploaded_texts: Vec<_> = scene
|
let uploaded_texts: Vec<_> = scene
|
||||||
.items
|
.items
|
||||||
@@ -480,6 +506,14 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
pass.set_vertex_buffer(0, vertex_buffer.slice(..));
|
pass.set_vertex_buffer(0, vertex_buffer.slice(..));
|
||||||
pass.draw(0..vertices.len() as u32, 0..1);
|
pass.draw(0..vertices.len() as u32, 0..1);
|
||||||
}
|
}
|
||||||
|
if !uploaded_images.is_empty() {
|
||||||
|
pass.set_pipeline(&self.text_pipeline);
|
||||||
|
for image in &uploaded_images {
|
||||||
|
pass.set_bind_group(0, &image.bind_group, &[]);
|
||||||
|
pass.set_vertex_buffer(0, image.vertex_buffer.slice(..));
|
||||||
|
pass.draw(0..image.vertex_count, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(atlas_text) = uploaded_atlas_text.as_ref() {
|
if let Some(atlas_text) = uploaded_atlas_text.as_ref() {
|
||||||
pass.set_pipeline(&self.text_pipeline);
|
pass.set_pipeline(&self.text_pipeline);
|
||||||
pass.set_bind_group(0, &self.glyph_atlas.bind_group, &[]);
|
pass.set_bind_group(0, &self.glyph_atlas.bind_group, &[]);
|
||||||
@@ -501,6 +535,7 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
target: "ruin_ui_renderer_wgpu::perf",
|
target: "ruin_ui_renderer_wgpu::perf",
|
||||||
scene_version = scene.version,
|
scene_version = scene.version,
|
||||||
quad_vertices = vertices.len(),
|
quad_vertices = vertices.len(),
|
||||||
|
image_batches = uploaded_images.len(),
|
||||||
atlas_text_vertices = uploaded_atlas_text
|
atlas_text_vertices = uploaded_atlas_text
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(0_u32, |text| text.vertex_count),
|
.map_or(0_u32, |text| text.vertex_count),
|
||||||
@@ -825,6 +860,36 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
Some(rect)
|
Some(rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_uploaded_image(
|
||||||
|
&mut self,
|
||||||
|
image: &PreparedImage,
|
||||||
|
logical_size: UiSize,
|
||||||
|
) -> Option<UploadedImage> {
|
||||||
|
let key = image.resource.id();
|
||||||
|
if !self.image_cache.contains_key(&key) {
|
||||||
|
let cached = self.create_cached_image_texture(image);
|
||||||
|
self.image_cache.insert(key, cached);
|
||||||
|
}
|
||||||
|
self.touch_image_cache_entry(key);
|
||||||
|
let cached = self
|
||||||
|
.image_cache
|
||||||
|
.get(&key)
|
||||||
|
.expect("image cache entry should exist after insertion");
|
||||||
|
let vertices = build_image_vertices(image.rect, image.uv_rect, logical_size);
|
||||||
|
let vertex_buffer = self
|
||||||
|
.device
|
||||||
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("ruin-ui-renderer-wgpu-image-vertices"),
|
||||||
|
contents: bytemuck::cast_slice(&vertices),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
Some(UploadedImage {
|
||||||
|
bind_group: cached.bind_group.clone(),
|
||||||
|
vertex_buffer,
|
||||||
|
vertex_count: vertices.len() as u32,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn prepare_uploaded_text(
|
fn prepare_uploaded_text(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: &PreparedText,
|
text: &PreparedText,
|
||||||
@@ -879,6 +944,23 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn touch_image_cache_entry(&mut self, key: u64) {
|
||||||
|
if let Some(position) = self
|
||||||
|
.image_cache_order
|
||||||
|
.iter()
|
||||||
|
.position(|existing| *existing == key)
|
||||||
|
{
|
||||||
|
self.image_cache_order.remove(position);
|
||||||
|
}
|
||||||
|
self.image_cache_order.push_back(key);
|
||||||
|
while self.image_cache_order.len() > MAX_IMAGE_CACHE_ENTRIES {
|
||||||
|
let Some(evicted) = self.image_cache_order.pop_front() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
self.image_cache.remove(&evicted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_cached_text_texture(&self, text: &RasterizedText) -> CachedTextTexture {
|
fn create_cached_text_texture(&self, text: &RasterizedText) -> CachedTextTexture {
|
||||||
let texture = self.device.create_texture(&wgpu::TextureDescriptor {
|
let texture = self.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: Some("ruin-ui-renderer-wgpu-texture"),
|
label: Some("ruin-ui-renderer-wgpu-texture"),
|
||||||
@@ -935,6 +1017,61 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
size: text.size,
|
size: text.size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_cached_image_texture(&self, image: &PreparedImage) -> CachedImageTexture {
|
||||||
|
let texture = self.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: Some("ruin-ui-renderer-wgpu-image-texture"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: image.resource.width(),
|
||||||
|
height: image.resource.height(),
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
self.queue.write_texture(
|
||||||
|
wgpu::TexelCopyTextureInfo {
|
||||||
|
texture: &texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
},
|
||||||
|
image.resource.pixels(),
|
||||||
|
wgpu::TexelCopyBufferLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(image.resource.width() * 4),
|
||||||
|
rows_per_image: Some(image.resource.height()),
|
||||||
|
},
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: image.resource.width(),
|
||||||
|
height: image.resource.height(),
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("ruin-ui-renderer-wgpu-image-bind-group"),
|
||||||
|
layout: &self.text_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&texture_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&self.text_sampler),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
CachedImageTexture {
|
||||||
|
_texture: texture,
|
||||||
|
bind_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_glyph_atlas(
|
fn create_glyph_atlas(
|
||||||
@@ -1234,6 +1371,55 @@ fn build_text_vertices(origin: Point, size: UiSize, logical_size: UiSize) -> [Te
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_image_vertices(
|
||||||
|
rect: Rect,
|
||||||
|
uv_rect: (f32, f32, f32, f32),
|
||||||
|
logical_size: UiSize,
|
||||||
|
) -> [TextVertex; 6] {
|
||||||
|
let left = to_ndc_x(rect.origin.x, logical_size.width.max(1.0));
|
||||||
|
let right = to_ndc_x(rect.origin.x + rect.size.width, logical_size.width.max(1.0));
|
||||||
|
let top = to_ndc_y(rect.origin.y, logical_size.height.max(1.0));
|
||||||
|
let bottom = to_ndc_y(
|
||||||
|
rect.origin.y + rect.size.height,
|
||||||
|
logical_size.height.max(1.0),
|
||||||
|
);
|
||||||
|
let (u0, v0, u1, v1) = uv_rect;
|
||||||
|
let color = [1.0, 1.0, 1.0, 1.0];
|
||||||
|
|
||||||
|
[
|
||||||
|
TextVertex {
|
||||||
|
position: [left, top],
|
||||||
|
uv: [u0, v0],
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
TextVertex {
|
||||||
|
position: [left, bottom],
|
||||||
|
uv: [u0, v1],
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
TextVertex {
|
||||||
|
position: [right, top],
|
||||||
|
uv: [u1, v0],
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
TextVertex {
|
||||||
|
position: [right, top],
|
||||||
|
uv: [u1, v0],
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
TextVertex {
|
||||||
|
position: [left, bottom],
|
||||||
|
uv: [u0, v1],
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
TextVertex {
|
||||||
|
position: [right, bottom],
|
||||||
|
uv: [u1, v1],
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
fn push_glyph_vertices(
|
fn push_glyph_vertices(
|
||||||
vertices: &mut Vec<TextVertex>,
|
vertices: &mut Vec<TextVertex>,
|
||||||
glyph_rect: PixelRect,
|
glyph_rect: PixelRect,
|
||||||
|
|||||||
Reference in New Issue
Block a user