Implement optional Skia codec adapter (#41)
Some checks failed
JPEG 2000 feature / linux (push) Has been cancelled
JPEG 2000 feature / macos (push) Has been cancelled
JPEG 2000 feature / windows (push) Has been cancelled
Skia feature / linux (push) Has been cancelled
Skia feature / macos (push) Has been cancelled
Skia feature / windows (push) Has been cancelled
Some checks failed
JPEG 2000 feature / linux (push) Has been cancelled
JPEG 2000 feature / macos (push) Has been cancelled
JPEG 2000 feature / windows (push) Has been cancelled
Skia feature / linux (push) Has been cancelled
Skia feature / macos (push) Has been cancelled
Skia feature / windows (push) Has been cancelled
This commit is contained in:
50
.gitea/workflows/skia.yml
Normal file
50
.gitea/workflows/skia.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
name: Skia feature
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
FORCE_SKIA_BINARIES_DOWNLOAD: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install native link prerequisites
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --yes curl pkg-config libfontconfig1-dev libfreetype6-dev
|
||||||
|
- name: Test default and Skia feature builds
|
||||||
|
run: |
|
||||||
|
cargo test -p libremetaverse-imaging-skia --no-default-features
|
||||||
|
cargo test -p libremetaverse-imaging-skia --features skia
|
||||||
|
cargo package -p libremetaverse-imaging-skia --list
|
||||||
|
|
||||||
|
macos:
|
||||||
|
runs-on: macos-latest
|
||||||
|
env:
|
||||||
|
FORCE_SKIA_BINARIES_DOWNLOAD: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Test default and Skia feature builds
|
||||||
|
run: |
|
||||||
|
cargo test -p libremetaverse-imaging-skia --no-default-features
|
||||||
|
cargo test -p libremetaverse-imaging-skia --features skia
|
||||||
|
cargo package -p libremetaverse-imaging-skia --list
|
||||||
|
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
env:
|
||||||
|
FORCE_SKIA_BINARIES_DOWNLOAD: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Test default and Skia feature builds
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
cargo test -p libremetaverse-imaging-skia --no-default-features
|
||||||
|
cargo test -p libremetaverse-imaging-skia --features skia
|
||||||
|
cargo package -p libremetaverse-imaging-skia --list
|
||||||
427
Cargo.lock
generated
427
Cargo.lock
generated
@@ -2,6 +2,21 @@
|
|||||||
# 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]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
@@ -14,6 +29,32 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f09c37bc0e9f0924b7dae9988265ef3c76c88538f41a3b06caf4bed07cee5226"
|
checksum = "f09c37bc0e9f0924b7dae9988265ef3c76c88538f41a3b06caf4bed07cee5226"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.72.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"itertools",
|
||||||
|
"log",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex 1.3.0",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@@ -38,12 +79,42 @@ version = "3.20.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex 2.0.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
@@ -68,6 +139,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -108,6 +188,54 @@ dependencies = [
|
|||||||
"crypto-common 0.2.2",
|
"crypto-common 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.2.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
|
||||||
|
|
||||||
|
[[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 = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.33"
|
version = "0.3.33"
|
||||||
@@ -153,6 +281,24 @@ dependencies = [
|
|||||||
"r-efi",
|
"r-efi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hybrid-array"
|
name = "hybrid-array"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
@@ -162,6 +308,25 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@@ -185,6 +350,16 @@ version = "0.2.189"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libremetaverse"
|
name = "libremetaverse"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
@@ -228,6 +403,7 @@ version = "0.0.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libremetaverse-imaging",
|
"libremetaverse-imaging",
|
||||||
"libremetaverse-types",
|
"libremetaverse-types",
|
||||||
|
"skia-safe",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -340,6 +516,18 @@ dependencies = [
|
|||||||
"libremetaverse-types",
|
"libremetaverse-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -356,12 +544,44 @@ version = "2.8.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[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 = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.4"
|
version = "1.21.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -374,6 +594,16 @@ version = "0.3.33"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.107"
|
version = "1.0.107"
|
||||||
@@ -398,6 +628,54 @@ version = "6.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
@@ -446,6 +724,15 @@ dependencies = [
|
|||||||
"zmij",
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -468,6 +755,65 @@ dependencies = [
|
|||||||
"digest 0.11.3",
|
"digest 0.11.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skia-bindings"
|
||||||
|
version = "0.99.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e2d1c3ebd697c0cbded0145e9204a38fa6b268446051b7196d0a096414ea7f3"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"cc",
|
||||||
|
"flate2",
|
||||||
|
"heck",
|
||||||
|
"pkg-config",
|
||||||
|
"regex",
|
||||||
|
"serde_json",
|
||||||
|
"tar",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skia-safe"
|
||||||
|
version = "0.99.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f512ac418a64194842dd05566320805dad1c957c521039db2486fd6368865bc"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bitflags",
|
||||||
|
"percent-encoding",
|
||||||
|
"skia-bindings",
|
||||||
|
"skia-svg-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skia-svg-macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "044dd2233c9717a74f75197f3e7f0a966db2127c0ffb5e05013b480a9b75b2c7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@@ -502,6 +848,56 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tar"
|
||||||
|
version = "0.4.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
|
||||||
|
dependencies = [
|
||||||
|
"filetime",
|
||||||
|
"libc",
|
||||||
|
"xattr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "1.1.4+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "1.1.1+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.1.3+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.1.2+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.20.1"
|
version = "1.20.1"
|
||||||
@@ -582,6 +978,37 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xattr"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
|||||||
@@ -150,5 +150,13 @@ including its alpha-only and opaque-alpha substitutions. See
|
|||||||
[`crates/libremetaverse-openjpeg/README.md`](crates/libremetaverse-openjpeg/README.md)
|
[`crates/libremetaverse-openjpeg/README.md`](crates/libremetaverse-openjpeg/README.md)
|
||||||
for OpenJPEG prerequisites, licensing, and deployment details. Default builds
|
for OpenJPEG prerequisites, licensing, and deployment details. Default builds
|
||||||
do not discover or link OpenJPEG.
|
do not discover or link OpenJPEG.
|
||||||
|
The optional Skia adapter decodes BMP, GIF, ICO, JPEG, PNG, WBMP, and WebP into
|
||||||
|
the same checked planar representation. Its `skia` feature uses pinned
|
||||||
|
rust-skia binary-cache configurations on macOS, Linux, and Windows, normalizes
|
||||||
|
premultiplied color at the imaging boundary, and keeps encoded input, decoded
|
||||||
|
dimensions, strides, and allocations bounded. See
|
||||||
|
[`crates/libremetaverse-imaging-skia/README.md`](crates/libremetaverse-imaging-skia/README.md)
|
||||||
|
for native prerequisites, cache/source-build controls, licenses, and packaging
|
||||||
|
details. Default workspace builds do not compile or link Skia.
|
||||||
The controlled audit aggregates every expected failure by standardized C#
|
The controlled audit aggregates every expected failure by standardized C#
|
||||||
member ID and rejects unrelated fixture, assertion, compile, or symbol errors.
|
member ID and rejects unrelated fixture, assertion, compile, or symbol errors.
|
||||||
|
|||||||
@@ -414,7 +414,7 @@ MSRV, features, and licenses at adoption time.
|
|||||||
| compression and tar archives | [`flate2` 1.1.9](https://crates.io/crates/flate2/1.1.9), [`tar` 0.4.46](https://crates.io/crates/tar/0.4.46) | Enforce decompressed-size, path traversal, and entry-count limits on untrusted OAR data. |
|
| compression and tar archives | [`flate2` 1.1.9](https://crates.io/crates/flate2/1.1.9), [`tar` 0.4.46](https://crates.io/crates/tar/0.4.46) | Enforce decompressed-size, path traversal, and entry-count limits on untrusted OAR data. |
|
||||||
| XML and URL | [`quick-xml` 0.41.0](https://crates.io/crates/quick-xml/0.41.0), [`url` 2.5.8](https://crates.io/crates/url/2.5.8), [`base64` 0.23.1](https://crates.io/crates/base64/0.23.1) | Streaming XML for LLSD/login; retain exact URL escaping behavior with fixtures. |
|
| XML and URL | [`quick-xml` 0.41.0](https://crates.io/crates/quick-xml/0.41.0), [`url` 2.5.8](https://crates.io/crates/url/2.5.8), [`base64` 0.23.1](https://crates.io/crates/base64/0.23.1) | Streaming XML for LLSD/login; retain exact URL escaping behavior with fixtures. |
|
||||||
| CoreJ2K 2.3.3.91 | system [OpenJPEG](https://www.openjpeg.org/) 2.5.4+ through the private `libremetaverse-openjpeg` adapter | Opt-in `jpeg2000` feature; checked-in minimal bindings avoid a libclang build dependency. Bounded memory streams and header validation isolate native code. Golden tests cover channel order, alpha, 16-bit precision, dimensions, discard levels, lossless/lossy modes, and malformed input. |
|
| CoreJ2K 2.3.3.91 | system [OpenJPEG](https://www.openjpeg.org/) 2.5.4+ through the private `libremetaverse-openjpeg` adapter | Opt-in `jpeg2000` feature; checked-in minimal bindings avoid a libclang build dependency. Bounded memory streams and header validation isolate native code. Golden tests cover channel order, alpha, 16-bit precision, dimensions, discard levels, lossless/lossy modes, and malformed input. |
|
||||||
| SkiaSharp 4.150.1 | [`skia-safe` 0.99.0](https://crates.io/crates/skia-safe/0.99.0) | MSRV 1.85 and native/binary-cache build. Optional adapter only; core image APIs must not leak Skia types. |
|
| SkiaSharp 4.150.1 | [`skia-safe` 0.99.0](https://crates.io/crates/skia-safe/0.99.0) | Adopted behind the opt-in `skia` feature. Target-specific official binary-cache feature sets provide BMP/GIF/ICO/JPEG/PNG/WBMP/WebP decoding on macOS, Linux, and Windows; checked owned buffers keep Skia types out of the core image API. |
|
||||||
| Pfim 0.11.4 | [`image` 0.25.10](https://crates.io/crates/image/0.25.10), [`ddsfile` 0.6.0](https://crates.io/crates/ddsfile/0.6.0) | `image` covers TGA and common DDS decoding; `ddsfile` exposes DDS container details. Golden files decide whether both are needed. |
|
| Pfim 0.11.4 | [`image` 0.25.10](https://crates.io/crates/image/0.25.10), [`ddsfile` 0.6.0](https://crates.io/crates/ddsfile/0.6.0) | `image` covers TGA and common DDS decoding; `ddsfile` exposes DDS container details. Golden files decide whether both are needed. |
|
||||||
| OggVorbisEncoder 1.2.2 | [`vorbis_rs` 0.5.6](https://crates.io/crates/vorbis_rs/0.5.6) | BSD-3-Clause, MSRV 1.82, backed by C libraries. Feature-gate native audio encoding. |
|
| OggVorbisEncoder 1.2.2 | [`vorbis_rs` 0.5.6](https://crates.io/crates/vorbis_rs/0.5.6) | BSD-3-Clause, MSRV 1.82, backed by C libraries. Feature-gate native audio encoding. |
|
||||||
| SIPSorcery 8.0.23 | [`webrtc` 0.20.0](https://crates.io/crates/webrtc/0.20.0), [`cpal` 0.18.1](https://crates.io/crates/cpal/0.18.1), [`opus` 0.3.1](https://crates.io/crates/opus/0.3.1) | Validate SDP, ICE, data-channel framing, audio formats, device hotplug, and native libopus deployment separately. Do not claim parity from successful compilation. |
|
| SIPSorcery 8.0.23 | [`webrtc` 0.20.0](https://crates.io/crates/webrtc/0.20.0), [`cpal` 0.18.1](https://crates.io/crates/cpal/0.18.1), [`opus` 0.3.1](https://crates.io/crates/opus/0.3.1) | Validate SDP, ICE, data-channel framing, audio formats, device hotplug, and native libopus deployment separately. Do not claim parity from successful compilation. |
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
|
|||||||
|---|---:|---:|---|
|
|---|---:|---:|---|
|
||||||
| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 3 types / 7 members; remaining surface is callable failure-only shims |
|
| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 3 types / 7 members; remaining surface is callable failure-only shims |
|
||||||
| `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain |
|
| `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain |
|
||||||
| `LibreMetaverse.Imaging.Skia` | 1 | 3 | callable failure-only shim |
|
| `LibreMetaverse.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain |
|
||||||
| `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim |
|
| `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim |
|
||||||
| `LibreMetaverse.PrimMesher` | 17 | 207 | callable failure-only shim |
|
| `LibreMetaverse.PrimMesher` | 17 | 207 | callable failure-only shim |
|
||||||
| `LibreMetaverse.RLV` | 28 | 499 | callable failure-only shim |
|
| `LibreMetaverse.RLV` | 28 | 499 | callable failure-only shim |
|
||||||
|
|||||||
@@ -5,11 +5,27 @@ edition.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
description = "Skia codec shims for the MetaCrate LibreMetaverse rewrite"
|
description = "Optional Skia image decoder for the MetaCrate LibreMetaverse rewrite"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
skia = ["dep:skia-safe"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libremetaverse-imaging = { path = "../libremetaverse-imaging" }
|
libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" }
|
||||||
libremetaverse-types = { path = "../libremetaverse-types" }
|
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
|
||||||
|
|
||||||
|
# rust-skia publishes WebP-capable binary caches with Vulkan on Linux/Windows
|
||||||
|
# and without Vulkan on macOS. Matching those sets avoids an hours-long Skia
|
||||||
|
# source build on every supported desktop target.
|
||||||
|
[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies]
|
||||||
|
skia-safe = { version = "0.99.0", default-features = false, features = ["binary-cache", "jpeg", "pdf", "svg", "textlayout", "vulkan", "webp"], optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
skia-safe = { version = "0.99.0", default-features = false, features = ["binary-cache", "jpeg", "pdf", "svg", "textlayout", "webp"], optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies]
|
||||||
|
skia-safe = { version = "0.99.0", default-features = false, features = ["binary-cache", "jpeg", "pdf", "webp"], optional = true }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
68
crates/libremetaverse-imaging-skia/README.md
Normal file
68
crates/libremetaverse-imaging-skia/README.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# MetaCrate Skia adapter
|
||||||
|
|
||||||
|
This crate implements the pinned `SkiaTextureCodec` behavior at a bounded,
|
||||||
|
project-owned image boundary. The core `libremetaverse-imaging` crate never
|
||||||
|
exposes a `skia-safe` type and never discovers or links Skia.
|
||||||
|
|
||||||
|
## Features and formats
|
||||||
|
|
||||||
|
The default feature set has no Skia dependency. `SkiaTextureCodec::decode`
|
||||||
|
returns a typed `InvalidOperation` error in that configuration, while conversion
|
||||||
|
from the checked project-owned `backend::SKBitmap` remains available for tests
|
||||||
|
and callers that already own decoded pixels.
|
||||||
|
|
||||||
|
Enable native decoding with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo test -p libremetaverse-imaging-skia --features skia
|
||||||
|
```
|
||||||
|
|
||||||
|
The `skia` feature decodes the CPU codec formats supported by the pinned
|
||||||
|
rust-skia release: BMP, GIF, ICO, JPEG, PNG, WBMP, and WebP. Input is buffered
|
||||||
|
to at most 64 MiB. Dimensions are rejected before native pixel allocation when
|
||||||
|
they exceed the core 16,777,216-pixel limit, and decoded storage uses checked
|
||||||
|
strides and owned Rust buffers.
|
||||||
|
|
||||||
|
The mapped C# adapter exposes decoding and `SKBitmap`-to-`ManagedImage`
|
||||||
|
conversion only; it has no encoding or resize method. Encoding selection stays
|
||||||
|
with the format-specific imaging APIs, and callers use `ManagedImage`'s checked
|
||||||
|
resize methods after decoding. Premultiplied pixels are converted to straight
|
||||||
|
alpha at this boundary. RGB565, BGRA8888, RGBA8888, RGBA/BGRA1010102, Gray8,
|
||||||
|
Alpha8, row padding, and the reference byte-width fallback retain the pinned
|
||||||
|
C# channel and rounding rules.
|
||||||
|
|
||||||
|
## Binary cache and source builds
|
||||||
|
|
||||||
|
`skia-safe` 0.99.0 downloads an official prebuilt Skia archive when the target
|
||||||
|
and Cargo feature set match a published archive. MetaCrate deliberately selects
|
||||||
|
the published WebP-capable feature sets:
|
||||||
|
|
||||||
|
- Linux and Windows: JPEG, PDF, SVG, text layout, Vulkan, and WebP;
|
||||||
|
- macOS: JPEG, PDF, SVG, text layout, and WebP.
|
||||||
|
|
||||||
|
Vulkan is selected on Linux and Windows only to match the published CPU/WebP
|
||||||
|
archive; this adapter does not create a GPU context or call a platform graphics
|
||||||
|
API. The same feature is therefore usable on macOS, Linux, and Windows without
|
||||||
|
leaking platform-specific behavior.
|
||||||
|
|
||||||
|
Build prerequisites for the supported desktop targets are:
|
||||||
|
|
||||||
|
- Linux: a C++ linker/runtime, `curl`, `pkg-config`, FreeType, and Fontconfig;
|
||||||
|
- macOS: Xcode command-line tools and `curl`;
|
||||||
|
- Windows MSVC: the Rust MSVC toolchain, Visual Studio C++ build tools, and
|
||||||
|
`curl`.
|
||||||
|
|
||||||
|
The build script downloads cache archives from the `rust-skia/skia-binaries`
|
||||||
|
GitHub releases. Pin or mirror `SKIA_BINARIES_URL` in offline/reproducible build
|
||||||
|
environments. `FORCE_SKIA_BINARIES_DOWNLOAD=1` makes a missing archive fail
|
||||||
|
instead of compiling Skia. If no archive matches and that variable is absent,
|
||||||
|
rust-skia falls back to a source build, which additionally requires Python 3,
|
||||||
|
Ninja, and an LLVM/Clang toolchain. `FORCE_SKIA_BUILD=1` selects that path
|
||||||
|
explicitly.
|
||||||
|
|
||||||
|
## Licensing and redistribution
|
||||||
|
|
||||||
|
`skia-safe` and rust-skia's bindings are MIT licensed; the linked Skia library
|
||||||
|
is BSD-3-Clause licensed. Official binary-cache archives contain compiled Skia.
|
||||||
|
Products that redistribute the resulting native artifacts must preserve the
|
||||||
|
applicable MIT and BSD notices and audit the exact archive they ship.
|
||||||
134
crates/libremetaverse-imaging-skia/src/backend.rs
Normal file
134
crates/libremetaverse-imaging-skia/src/backend.rs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
//! Project-owned representation of the mapped `SkiaSharp.SKBitmap` boundary.
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
use libremetaverse_imaging::DEFAULT_MAX_PIXELS;
|
||||||
|
|
||||||
|
/// Pixel formats used by the pinned `SkiaTextureCodec` implementation.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum SKColorType {
|
||||||
|
Rgb565,
|
||||||
|
Bgra8888,
|
||||||
|
Rgba8888,
|
||||||
|
Rgba1010102,
|
||||||
|
Bgra1010102,
|
||||||
|
Gray8,
|
||||||
|
Alpha8,
|
||||||
|
/// Another Skia format handled by the reference's byte-width fallback.
|
||||||
|
Other {
|
||||||
|
bytes_per_pixel: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SKColorType {
|
||||||
|
const fn storage_bytes(self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Rgb565 => 2,
|
||||||
|
Self::Bgra8888 | Self::Rgba8888 | Self::Rgba1010102 | Self::Bgra1010102 => 4,
|
||||||
|
Self::Gray8 | Self::Alpha8 => 1,
|
||||||
|
Self::Other { bytes_per_pixel } => bytes_per_pixel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alpha representation attached to bitmap samples.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum SKAlphaType {
|
||||||
|
Opaque,
|
||||||
|
Premul,
|
||||||
|
Unpremul,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Owned, checked bitmap used by the fixed mapped signature.
|
||||||
|
///
|
||||||
|
/// This deliberately does not expose a Skia allocation or pointer. Feature-
|
||||||
|
/// enabled decoding copies the native pixmap into this bounded Rust buffer
|
||||||
|
/// before conversion to the core imaging abstraction.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct SKBitmap {
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
row_bytes: usize,
|
||||||
|
color_type: SKColorType,
|
||||||
|
alpha_type: SKAlphaType,
|
||||||
|
pixels: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SKBitmap {
|
||||||
|
/// Creates a bitmap with an explicit row stride and owned pixels.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`Error::Argument`] for invalid dimensions, formats, strides,
|
||||||
|
/// lengths, or over-limit storage.
|
||||||
|
pub fn new(
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
row_bytes: usize,
|
||||||
|
color_type: SKColorType,
|
||||||
|
alpha_type: SKAlphaType,
|
||||||
|
pixels: Vec<u8>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let width_usize = usize::try_from(width).map_err(|_| Error::Argument)?;
|
||||||
|
let height_usize = usize::try_from(height).map_err(|_| Error::Argument)?;
|
||||||
|
let pixel_count = width_usize
|
||||||
|
.checked_mul(height_usize)
|
||||||
|
.filter(|count| *count > 0 && *count <= DEFAULT_MAX_PIXELS)
|
||||||
|
.ok_or(Error::Argument)?;
|
||||||
|
let storage_bytes = color_type.storage_bytes();
|
||||||
|
if storage_bytes == 0 || storage_bytes > 16 {
|
||||||
|
return Err(Error::Argument);
|
||||||
|
}
|
||||||
|
let packed_row = width_usize
|
||||||
|
.checked_mul(storage_bytes)
|
||||||
|
.ok_or(Error::Argument)?;
|
||||||
|
if row_bytes < packed_row {
|
||||||
|
return Err(Error::Argument);
|
||||||
|
}
|
||||||
|
let required = row_bytes
|
||||||
|
.checked_mul(height_usize.saturating_sub(1))
|
||||||
|
.and_then(|prefix| prefix.checked_add(packed_row))
|
||||||
|
.ok_or(Error::Argument)?;
|
||||||
|
if pixels.len() < required
|
||||||
|
|| pixels.len()
|
||||||
|
> pixel_count
|
||||||
|
.checked_mul(16)
|
||||||
|
.and_then(|size| size.checked_add(row_bytes))
|
||||||
|
.ok_or(Error::Argument)?
|
||||||
|
{
|
||||||
|
return Err(Error::Argument);
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
row_bytes,
|
||||||
|
color_type,
|
||||||
|
alpha_type,
|
||||||
|
pixels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn width(&self) -> i32 {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn height(&self) -> i32 {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn row_bytes(&self) -> usize {
|
||||||
|
self.row_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn color_type(&self) -> SKColorType {
|
||||||
|
self.color_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn alpha_type(&self) -> SKAlphaType {
|
||||||
|
self.alpha_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pixels(&self) -> &[u8] {
|
||||||
|
&self.pixels
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,29 +6,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Imaging.Skia.SkiaTextureCodec`.
|
/// C# type: `T:LibreMetaverse.Imaging.Skia.SkiaTextureCodec`.
|
||||||
pub struct SkiaTextureCodec;
|
|
||||||
impl SkiaTextureCodec {
|
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.#ctor`.
|
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.#ctor`.
|
||||||
pub fn new() -> Result<Self, crate::Error> {
|
|
||||||
libremetaverse_types::not_implemented(
|
|
||||||
"M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.#ctor",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.Decode(System.IO.Stream)`.
|
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.Decode(System.IO.Stream)`.
|
||||||
pub fn decode(
|
|
||||||
&self,
|
|
||||||
stream: Box<dyn libremetaverse_types::compat::ReadWrite + Send>,
|
|
||||||
) -> Result<libremetaverse_imaging::ManagedImage, crate::Error> {
|
|
||||||
libremetaverse_types::not_implemented(
|
|
||||||
"M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.Decode(System.IO.Stream)",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.ToManagedImage(SkiaSharp.SKBitmap)`.
|
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.ToManagedImage(SkiaSharp.SKBitmap)`.
|
||||||
pub fn to_managed_image(
|
pub use crate::skia_codec::SkiaTextureCodec;
|
||||||
bitmap: libremetaverse_imaging_skia::backend::SKBitmap,
|
|
||||||
) -> Result<libremetaverse_imaging::ManagedImage, crate::Error> {
|
|
||||||
libremetaverse_types::not_implemented(
|
|
||||||
"M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.ToManagedImage(SkiaSharp.SKBitmap)",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,20 +2,15 @@
|
|||||||
|
|
||||||
extern crate self as libremetaverse_imaging_skia;
|
extern crate self as libremetaverse_imaging_skia;
|
||||||
|
|
||||||
pub mod backend {
|
pub mod backend;
|
||||||
/// Project-owned bitmap boundary; no `SkiaSharp` API is copied.
|
|
||||||
pub struct SKBitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
mod generated;
|
mod generated;
|
||||||
|
mod skia_codec;
|
||||||
|
|
||||||
pub use generated::*;
|
pub use generated::*;
|
||||||
pub use libremetaverse_imaging as imaging;
|
pub use libremetaverse_imaging as imaging;
|
||||||
pub use libremetaverse_types::Error;
|
pub use libremetaverse_types::Error;
|
||||||
|
|
||||||
// The native codec abstraction requires an explicit implementation. Until the
|
|
||||||
// optional Skia adapter is ported, forward through its cataloged typed-failure
|
|
||||||
// method rather than manufacturing an image or silently accepting input.
|
|
||||||
impl libremetaverse_imaging::ITextureCodec for SkiaTextureCodec {
|
impl libremetaverse_imaging::ITextureCodec for SkiaTextureCodec {
|
||||||
fn decode(
|
fn decode(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
579
crates/libremetaverse-imaging-skia/src/skia_codec.rs
Normal file
579
crates/libremetaverse-imaging-skia/src/skia_codec.rs
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
//! Native implementation of the pinned `SkiaTextureCodec` behavior.
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
use crate::backend::{SKAlphaType, SKBitmap, SKColorType};
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
use libremetaverse_imaging::{DEFAULT_MAX_ENCODED_BYTES, DEFAULT_MAX_PIXELS};
|
||||||
|
use libremetaverse_imaging::{ManagedImage, ManagedImageImageChannels};
|
||||||
|
use libremetaverse_types::compat::ReadWrite;
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
/// Optional Skia-backed decoder and format-neutral bitmap converter.
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct SkiaTextureCodec;
|
||||||
|
|
||||||
|
impl SkiaTextureCodec {
|
||||||
|
/// Creates the stateless codec adapter.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This fixed compatibility constructor cannot fail.
|
||||||
|
pub const fn new() -> Result<Self, Error> {
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes a bounded compressed-image stream through Skia.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// With the `skia` feature, returns a typed argument, parse, or operation
|
||||||
|
/// error for oversized or malformed input and unsupported native output.
|
||||||
|
/// Without the feature it returns [`Error::InvalidOperation`].
|
||||||
|
pub fn decode(&self, mut stream: Box<dyn ReadWrite + Send>) -> Result<ManagedImage, Error> {
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
{
|
||||||
|
let mut encoded = Vec::new();
|
||||||
|
Read::by_ref(&mut stream)
|
||||||
|
.take((DEFAULT_MAX_ENCODED_BYTES + 1) as u64)
|
||||||
|
.read_to_end(&mut encoded)
|
||||||
|
.map_err(|_| Error::InvalidOperation)?;
|
||||||
|
if encoded.is_empty() {
|
||||||
|
return Err(Error::InvalidOperation);
|
||||||
|
}
|
||||||
|
if encoded.len() > DEFAULT_MAX_ENCODED_BYTES {
|
||||||
|
return Err(Error::Argument);
|
||||||
|
}
|
||||||
|
decode_with_skia(&encoded)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "skia"))]
|
||||||
|
{
|
||||||
|
let _ = &mut stream;
|
||||||
|
Err(Error::InvalidOperation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts an owned mapped bitmap using the exact reference channel rules.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns a typed error for invalid dimensions, storage, or an unsupported
|
||||||
|
/// fallback byte width.
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // fixed mapped C# signature
|
||||||
|
pub fn to_managed_image(bitmap: SKBitmap) -> Result<ManagedImage, Error> {
|
||||||
|
bitmap_to_managed(&bitmap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeping the format branches together makes comparison with the pinned C#
|
||||||
|
// color switch auditable and prevents subtly different indexing paths.
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
fn bitmap_to_managed(bitmap: &SKBitmap) -> Result<ManagedImage, Error> {
|
||||||
|
let width = usize::try_from(bitmap.width()).map_err(|_| Error::Argument)?;
|
||||||
|
let height = usize::try_from(bitmap.height()).map_err(|_| Error::Argument)?;
|
||||||
|
let reference_step = bitmap.row_bytes() / width.max(1);
|
||||||
|
let channels = match bitmap.color_type() {
|
||||||
|
SKColorType::Rgb565 => ManagedImageImageChannels::COLOR,
|
||||||
|
SKColorType::Bgra8888
|
||||||
|
| SKColorType::Rgba8888
|
||||||
|
| SKColorType::Rgba1010102
|
||||||
|
| SKColorType::Bgra1010102 => {
|
||||||
|
ManagedImageImageChannels::COLOR | ManagedImageImageChannels::ALPHA
|
||||||
|
}
|
||||||
|
SKColorType::Gray8 => ManagedImageImageChannels::GRAY,
|
||||||
|
SKColorType::Alpha8 => ManagedImageImageChannels::ALPHA,
|
||||||
|
SKColorType::Other { .. } if reference_step == 1 => ManagedImageImageChannels::GRAY,
|
||||||
|
SKColorType::Other { .. } if reference_step == 3 => ManagedImageImageChannels::COLOR,
|
||||||
|
SKColorType::Other { .. } if reference_step == 4 => {
|
||||||
|
ManagedImageImageChannels::COLOR | ManagedImageImageChannels::ALPHA
|
||||||
|
}
|
||||||
|
SKColorType::Other { .. } => return Err(Error::InvalidOperation),
|
||||||
|
};
|
||||||
|
let mut image = ManagedImage::new(bitmap.width(), bitmap.height(), channels)?;
|
||||||
|
for y in 0..height {
|
||||||
|
let row = y.checked_mul(bitmap.row_bytes()).ok_or(Error::Argument)?;
|
||||||
|
for x in 0..width {
|
||||||
|
let pixel = y
|
||||||
|
.checked_mul(width)
|
||||||
|
.and_then(|offset| offset.checked_add(x))
|
||||||
|
.ok_or(Error::Argument)?;
|
||||||
|
match bitmap.color_type() {
|
||||||
|
SKColorType::Rgb565 => {
|
||||||
|
let offset = checked_offset(row, x, 2, bitmap.pixels().len())?;
|
||||||
|
let packed =
|
||||||
|
u16::from_le_bytes([bitmap.pixels()[offset], bitmap.pixels()[offset + 1]]);
|
||||||
|
image.red[pixel] = scale_bits(u32::from((packed >> 11) & 0x1f), 31);
|
||||||
|
image.green[pixel] = scale_bits(u32::from((packed >> 5) & 0x3f), 63);
|
||||||
|
image.blue[pixel] = scale_bits(u32::from(packed & 0x1f), 31);
|
||||||
|
}
|
||||||
|
SKColorType::Bgra8888 | SKColorType::Rgba8888 => {
|
||||||
|
let offset = checked_offset(row, x, reference_step, bitmap.pixels().len())?;
|
||||||
|
let source = &bitmap.pixels()[offset..offset + 4];
|
||||||
|
let (red, green, blue, alpha) = if bitmap.color_type() == SKColorType::Rgba8888
|
||||||
|
{
|
||||||
|
(source[0], source[1], source[2], source[3])
|
||||||
|
} else {
|
||||||
|
(source[2], source[1], source[0], source[3])
|
||||||
|
};
|
||||||
|
let (red, green, blue) =
|
||||||
|
normalize_8bit_alpha(red, green, blue, alpha, bitmap.alpha_type());
|
||||||
|
image.red[pixel] = red;
|
||||||
|
image.green[pixel] = green;
|
||||||
|
image.blue[pixel] = blue;
|
||||||
|
image.alpha[pixel] = alpha;
|
||||||
|
}
|
||||||
|
SKColorType::Rgba1010102 | SKColorType::Bgra1010102 => {
|
||||||
|
let offset = checked_offset(row, x, 4, bitmap.pixels().len())?;
|
||||||
|
let packed = u32::from_le_bytes(
|
||||||
|
bitmap.pixels()[offset..offset + 4]
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::Argument)?,
|
||||||
|
);
|
||||||
|
let first = packed & 0x3ff;
|
||||||
|
let green = (packed >> 10) & 0x3ff;
|
||||||
|
let third = (packed >> 20) & 0x3ff;
|
||||||
|
let alpha = (packed >> 30) & 0x3;
|
||||||
|
let (red, blue) = if bitmap.color_type() == SKColorType::Rgba1010102 {
|
||||||
|
(first, third)
|
||||||
|
} else {
|
||||||
|
(third, first)
|
||||||
|
};
|
||||||
|
let (red, green, blue) =
|
||||||
|
normalize_10bit_alpha(red, green, blue, alpha, bitmap.alpha_type());
|
||||||
|
image.red[pixel] = scale_bits(red, 1023);
|
||||||
|
image.green[pixel] = scale_bits(green, 1023);
|
||||||
|
image.blue[pixel] = scale_bits(blue, 1023);
|
||||||
|
image.alpha[pixel] = u8::try_from(alpha * 85).map_err(|_| Error::Argument)?;
|
||||||
|
}
|
||||||
|
SKColorType::Gray8 => {
|
||||||
|
let offset = checked_offset(row, x, reference_step, bitmap.pixels().len())?;
|
||||||
|
image.red[pixel] = bitmap.pixels()[offset];
|
||||||
|
}
|
||||||
|
SKColorType::Alpha8 => {
|
||||||
|
let offset = checked_offset(row, x, reference_step, bitmap.pixels().len())?;
|
||||||
|
image.alpha[pixel] = bitmap.pixels()[offset];
|
||||||
|
}
|
||||||
|
SKColorType::Other { .. } => {
|
||||||
|
let offset = checked_offset(row, x, reference_step, bitmap.pixels().len())?;
|
||||||
|
match reference_step {
|
||||||
|
4 => {
|
||||||
|
let alpha = bitmap.pixels()[offset + 3];
|
||||||
|
let (red, green, blue) = normalize_8bit_alpha(
|
||||||
|
bitmap.pixels()[offset + 2],
|
||||||
|
bitmap.pixels()[offset + 1],
|
||||||
|
bitmap.pixels()[offset],
|
||||||
|
alpha,
|
||||||
|
bitmap.alpha_type(),
|
||||||
|
);
|
||||||
|
image.red[pixel] = red;
|
||||||
|
image.green[pixel] = green;
|
||||||
|
image.blue[pixel] = blue;
|
||||||
|
image.alpha[pixel] = alpha;
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
image.blue[pixel] = bitmap.pixels()[offset];
|
||||||
|
image.green[pixel] = bitmap.pixels()[offset + 1];
|
||||||
|
image.red[pixel] = bitmap.pixels()[offset + 2];
|
||||||
|
}
|
||||||
|
1 => image.red[pixel] = bitmap.pixels()[offset],
|
||||||
|
_ => return Err(Error::InvalidOperation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checked_offset(row: usize, x: usize, step: usize, length: usize) -> Result<usize, Error> {
|
||||||
|
let offset = x
|
||||||
|
.checked_mul(step)
|
||||||
|
.and_then(|offset| row.checked_add(offset))
|
||||||
|
.ok_or(Error::Argument)?;
|
||||||
|
offset
|
||||||
|
.checked_add(step)
|
||||||
|
.filter(|end| *end <= length)
|
||||||
|
.map(|_| offset)
|
||||||
|
.ok_or(Error::Argument)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scale_bits(value: u32, maximum: u32) -> u8 {
|
||||||
|
u8::try_from((value * 255 + maximum / 2) / maximum).unwrap_or(u8::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_8bit_alpha(
|
||||||
|
red: u8,
|
||||||
|
green: u8,
|
||||||
|
blue: u8,
|
||||||
|
alpha: u8,
|
||||||
|
alpha_type: SKAlphaType,
|
||||||
|
) -> (u8, u8, u8) {
|
||||||
|
if alpha_type != SKAlphaType::Premul || alpha == u8::MAX {
|
||||||
|
return (red, green, blue);
|
||||||
|
}
|
||||||
|
if alpha == 0 {
|
||||||
|
return (0, 0, 0);
|
||||||
|
}
|
||||||
|
let normalize = |value: u8| {
|
||||||
|
u8::try_from((u32::from(value) * 255 + u32::from(alpha) / 2) / u32::from(alpha))
|
||||||
|
.unwrap_or(u8::MAX)
|
||||||
|
};
|
||||||
|
(normalize(red), normalize(green), normalize(blue))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_10bit_alpha(
|
||||||
|
red: u32,
|
||||||
|
green: u32,
|
||||||
|
blue: u32,
|
||||||
|
alpha: u32,
|
||||||
|
alpha_type: SKAlphaType,
|
||||||
|
) -> (u32, u32, u32) {
|
||||||
|
if alpha_type != SKAlphaType::Premul || alpha == 3 {
|
||||||
|
return (red, green, blue);
|
||||||
|
}
|
||||||
|
if alpha == 0 {
|
||||||
|
return (0, 0, 0);
|
||||||
|
}
|
||||||
|
let normalize = |value: u32| (value * 3 + alpha / 2) / alpha;
|
||||||
|
(normalize(red), normalize(green), normalize(blue))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
fn decode_with_skia(encoded: &[u8]) -> Result<ManagedImage, Error> {
|
||||||
|
use skia_safe::Data;
|
||||||
|
use skia_safe::codec::{Codec, Options, Result as CodecResult};
|
||||||
|
|
||||||
|
let mut codec = Codec::from_data(Data::new_copy(encoded)).ok_or(Error::InvalidOperation)?;
|
||||||
|
let source_info = codec.info();
|
||||||
|
let width = usize::try_from(source_info.width()).map_err(|_| Error::Argument)?;
|
||||||
|
let height = usize::try_from(source_info.height()).map_err(|_| Error::Argument)?;
|
||||||
|
width
|
||||||
|
.checked_mul(height)
|
||||||
|
.filter(|pixels| *pixels > 0 && *pixels <= DEFAULT_MAX_PIXELS)
|
||||||
|
.ok_or(Error::Argument)?;
|
||||||
|
// Match SKBitmap.Decode: decode into Skia's native alpha representation,
|
||||||
|
// then normalize premultiplied channels at the abstraction boundary.
|
||||||
|
let target_info = source_info.clone();
|
||||||
|
let row_bytes = target_info.min_row_bytes();
|
||||||
|
let length = target_info.compute_byte_size(row_bytes);
|
||||||
|
if length == usize::MAX || length > DEFAULT_MAX_PIXELS * 16 {
|
||||||
|
return Err(Error::Argument);
|
||||||
|
}
|
||||||
|
let mut pixels = Vec::new();
|
||||||
|
pixels
|
||||||
|
.try_reserve_exact(length)
|
||||||
|
.map_err(|_| Error::InvalidOperation)?;
|
||||||
|
pixels.resize(length, 0);
|
||||||
|
let result = codec.get_pixels_with_options(
|
||||||
|
&target_info,
|
||||||
|
&mut pixels,
|
||||||
|
row_bytes,
|
||||||
|
Some(&Options {
|
||||||
|
max_decode_memory: Some(DEFAULT_MAX_PIXELS * 16),
|
||||||
|
..Options::default()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if result != CodecResult::Success {
|
||||||
|
return Err(Error::InvalidOperation);
|
||||||
|
}
|
||||||
|
let bitmap = SKBitmap::new(
|
||||||
|
source_info.width(),
|
||||||
|
source_info.height(),
|
||||||
|
row_bytes,
|
||||||
|
map_color_type(target_info.color_type(), target_info.bytes_per_pixel()),
|
||||||
|
map_alpha_type(target_info.alpha_type()),
|
||||||
|
pixels,
|
||||||
|
)?;
|
||||||
|
bitmap_to_managed(&bitmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
const fn map_color_type(color: skia_safe::ColorType, bytes_per_pixel: usize) -> SKColorType {
|
||||||
|
use skia_safe::ColorType;
|
||||||
|
match color {
|
||||||
|
ColorType::RGB565 => SKColorType::Rgb565,
|
||||||
|
ColorType::BGRA8888 => SKColorType::Bgra8888,
|
||||||
|
ColorType::RGBA8888 => SKColorType::Rgba8888,
|
||||||
|
ColorType::RGBA1010102 => SKColorType::Rgba1010102,
|
||||||
|
ColorType::BGRA1010102 => SKColorType::Bgra1010102,
|
||||||
|
ColorType::Gray8 => SKColorType::Gray8,
|
||||||
|
ColorType::Alpha8 => SKColorType::Alpha8,
|
||||||
|
_ => SKColorType::Other { bytes_per_pixel },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
const fn map_alpha_type(alpha: skia_safe::AlphaType) -> SKAlphaType {
|
||||||
|
use skia_safe::AlphaType;
|
||||||
|
match alpha {
|
||||||
|
AlphaType::Opaque => SKAlphaType::Opaque,
|
||||||
|
AlphaType::Premul => SKAlphaType::Premul,
|
||||||
|
AlphaType::Unpremul => SKAlphaType::Unpremul,
|
||||||
|
AlphaType::Unknown => SKAlphaType::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mapped_bitmap_formats_preserve_reference_channel_rules() {
|
||||||
|
let rgba = SKBitmap::new(
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
SKColorType::Rgba8888,
|
||||||
|
SKAlphaType::Unpremul,
|
||||||
|
vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let image = SkiaTextureCodec::to_managed_image(rgba).unwrap();
|
||||||
|
assert_eq!(image.red, [1, 5]);
|
||||||
|
assert_eq!(image.green, [2, 6]);
|
||||||
|
assert_eq!(image.blue, [3, 7]);
|
||||||
|
assert_eq!(image.alpha, [4, 8]);
|
||||||
|
|
||||||
|
let rgb565 = SKBitmap::new(
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
SKColorType::Rgb565,
|
||||||
|
SKAlphaType::Opaque,
|
||||||
|
vec![0x00, 0xf8, 0xe0, 0x07, 0x1f, 0x00],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let image = SkiaTextureCodec::to_managed_image(rgb565).unwrap();
|
||||||
|
assert_eq!(image.red, [255, 0, 0]);
|
||||||
|
assert_eq!(image.green, [0, 255, 0]);
|
||||||
|
assert_eq!(image.blue, [0, 0, 255]);
|
||||||
|
|
||||||
|
// The reference fallback derives its byte width from rowBytes / width,
|
||||||
|
// even when the declared native format has a different packed width.
|
||||||
|
let padded_unknown = SKBitmap::new(
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
SKColorType::Other { bytes_per_pixel: 3 },
|
||||||
|
SKAlphaType::Unpremul,
|
||||||
|
vec![3, 2, 1, 4, 7, 6, 5, 8],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let image = SkiaTextureCodec::to_managed_image(padded_unknown).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
image.channels,
|
||||||
|
ManagedImageImageChannels::COLOR | ManagedImageImageChannels::ALPHA
|
||||||
|
);
|
||||||
|
assert_eq!(image.red, [1, 5]);
|
||||||
|
assert_eq!(image.green, [2, 6]);
|
||||||
|
assert_eq!(image.blue, [3, 7]);
|
||||||
|
assert_eq!(image.alpha, [4, 8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grayscale_alpha_ten_bit_and_premul_are_converted() {
|
||||||
|
let gray = SKBitmap::new(
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
SKColorType::Gray8,
|
||||||
|
SKAlphaType::Opaque,
|
||||||
|
vec![11, 239],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let gray = SkiaTextureCodec::to_managed_image(gray).unwrap();
|
||||||
|
assert_eq!(gray.channels, ManagedImageImageChannels::GRAY);
|
||||||
|
assert_eq!(gray.red, [11, 239]);
|
||||||
|
|
||||||
|
let premul = SKBitmap::new(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
SKColorType::Bgra8888,
|
||||||
|
SKAlphaType::Premul,
|
||||||
|
vec![25, 50, 100, 128],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let premul = SkiaTextureCodec::to_managed_image(premul).unwrap();
|
||||||
|
assert_eq!(premul.red, [199]);
|
||||||
|
assert_eq!(premul.green, [100]);
|
||||||
|
assert_eq!(premul.blue, [50]);
|
||||||
|
assert_eq!(premul.alpha, [128]);
|
||||||
|
|
||||||
|
let transparent_premul = SKBitmap::new(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
SKColorType::Rgba8888,
|
||||||
|
SKAlphaType::Premul,
|
||||||
|
vec![100, 50, 25, 0],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let transparent_premul = SkiaTextureCodec::to_managed_image(transparent_premul).unwrap();
|
||||||
|
assert_eq!(transparent_premul.red, [0]);
|
||||||
|
assert_eq!(transparent_premul.green, [0]);
|
||||||
|
assert_eq!(transparent_premul.blue, [0]);
|
||||||
|
assert_eq!(transparent_premul.alpha, [0]);
|
||||||
|
|
||||||
|
let packed = 1023_u32 | (512 << 10) | (1 << 20) | (3 << 30);
|
||||||
|
let ten_bit = SKBitmap::new(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
SKColorType::Rgba1010102,
|
||||||
|
SKAlphaType::Unpremul,
|
||||||
|
packed.to_le_bytes().to_vec(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let ten_bit = SkiaTextureCodec::to_managed_image(ten_bit).unwrap();
|
||||||
|
assert_eq!(ten_bit.red, [255]);
|
||||||
|
assert_eq!(ten_bit.green, [128]);
|
||||||
|
assert_eq!(ten_bit.blue, [0]);
|
||||||
|
assert_eq!(ten_bit.alpha, [255]);
|
||||||
|
|
||||||
|
let packed = 1023_u32 | (512 << 10) | (1 << 20) | (2 << 30);
|
||||||
|
let ten_bit = SKBitmap::new(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
SKColorType::Bgra1010102,
|
||||||
|
SKAlphaType::Unpremul,
|
||||||
|
packed.to_le_bytes().to_vec(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let ten_bit = SkiaTextureCodec::to_managed_image(ten_bit).unwrap();
|
||||||
|
assert_eq!(ten_bit.red, [0]);
|
||||||
|
assert_eq!(ten_bit.green, [128]);
|
||||||
|
assert_eq!(ten_bit.blue, [255]);
|
||||||
|
assert_eq!(ten_bit.alpha, [170]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alpha_and_reference_fallback_layouts_are_converted_or_rejected() {
|
||||||
|
let alpha = SKBitmap::new(
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
SKColorType::Alpha8,
|
||||||
|
SKAlphaType::Unpremul,
|
||||||
|
vec![17, 241],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let alpha = SkiaTextureCodec::to_managed_image(alpha).unwrap();
|
||||||
|
assert_eq!(alpha.channels, ManagedImageImageChannels::ALPHA);
|
||||||
|
assert_eq!(alpha.alpha, [17, 241]);
|
||||||
|
|
||||||
|
let bgr = SKBitmap::new(
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
SKColorType::Other { bytes_per_pixel: 3 },
|
||||||
|
SKAlphaType::Opaque,
|
||||||
|
vec![3, 2, 1, 6, 5, 4],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let bgr = SkiaTextureCodec::to_managed_image(bgr).unwrap();
|
||||||
|
assert_eq!(bgr.channels, ManagedImageImageChannels::COLOR);
|
||||||
|
assert_eq!(bgr.red, [1, 4]);
|
||||||
|
assert_eq!(bgr.green, [2, 5]);
|
||||||
|
assert_eq!(bgr.blue, [3, 6]);
|
||||||
|
|
||||||
|
let gray = SKBitmap::new(
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
SKColorType::Other { bytes_per_pixel: 1 },
|
||||||
|
SKAlphaType::Opaque,
|
||||||
|
vec![23, 229],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let gray = SkiaTextureCodec::to_managed_image(gray).unwrap();
|
||||||
|
assert_eq!(gray.channels, ManagedImageImageChannels::GRAY);
|
||||||
|
assert_eq!(gray.red, [23, 229]);
|
||||||
|
|
||||||
|
let unsupported = SKBitmap::new(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
SKColorType::Other { bytes_per_pixel: 2 },
|
||||||
|
SKAlphaType::Opaque,
|
||||||
|
vec![0, 0],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
SkiaTextureCodec::to_managed_image(unsupported),
|
||||||
|
Err(Error::InvalidOperation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bitmap_bounds_and_feature_disabled_decode_fail_typed() {
|
||||||
|
assert_eq!(
|
||||||
|
SKBitmap::new(0, 1, 0, SKColorType::Gray8, SKAlphaType::Opaque, Vec::new(),),
|
||||||
|
Err(Error::Argument)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
SKBitmap::new(
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
SKColorType::Rgba8888,
|
||||||
|
SKAlphaType::Unpremul,
|
||||||
|
vec![0; 16],
|
||||||
|
),
|
||||||
|
Err(Error::Argument)
|
||||||
|
);
|
||||||
|
#[cfg(not(feature = "skia"))]
|
||||||
|
assert_eq!(
|
||||||
|
SkiaTextureCodec.decode(Box::new(std::io::Cursor::new(Vec::new()))),
|
||||||
|
Err(Error::InvalidOperation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
#[test]
|
||||||
|
fn feature_decodes_known_png_with_straight_alpha() {
|
||||||
|
const PNG: &[u8] = &[
|
||||||
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
|
||||||
|
0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00,
|
||||||
|
0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x44, 0x41, 0x54, 0x78,
|
||||||
|
0x9c, 0x63, 0xf8, 0xcf, 0xc0, 0xf0, 0x1f, 0x00, 0x05, 0x00, 0x01, 0xff, 0x89, 0x99,
|
||||||
|
0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
|
||||||
|
];
|
||||||
|
let image = SkiaTextureCodec
|
||||||
|
.decode(Box::new(std::io::Cursor::new(PNG.to_vec())))
|
||||||
|
.expect("decode PNG");
|
||||||
|
assert_eq!((image.width, image.height), (1, 1));
|
||||||
|
assert_eq!(image.red, [255]);
|
||||||
|
assert_eq!(image.green, [0]);
|
||||||
|
assert_eq!(image.blue, [0]);
|
||||||
|
assert_eq!(image.alpha, [255]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
SkiaTextureCodec.decode(Box::new(std::io::Cursor::new(Vec::new()))),
|
||||||
|
Err(Error::InvalidOperation)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
SkiaTextureCodec.decode(Box::new(std::io::Cursor::new(b"not an image".to_vec()))),
|
||||||
|
Err(Error::InvalidOperation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "skia")]
|
||||||
|
#[test]
|
||||||
|
fn feature_decodes_webp_from_the_cross_platform_cache_configuration() {
|
||||||
|
const WEBP: &[u8] = &[
|
||||||
|
0x52, 0x49, 0x46, 0x46, 0x1c, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50,
|
||||||
|
0x38, 0x4c, 0x0f, 0x00, 0x00, 0x00, 0x2f, 0x01, 0x40, 0x00, 0x00, 0x07, 0x10, 0xf5,
|
||||||
|
0x8f, 0xfe, 0x07, 0x22, 0xa2, 0xff, 0x01, 0x00,
|
||||||
|
];
|
||||||
|
let image = SkiaTextureCodec
|
||||||
|
.decode(Box::new(std::io::Cursor::new(WEBP.to_vec())))
|
||||||
|
.expect("decode WebP");
|
||||||
|
assert_eq!((image.width, image.height), (2, 2));
|
||||||
|
assert_eq!(image.red, [254; 4]);
|
||||||
|
assert_eq!(image.green, [0; 4]);
|
||||||
|
assert_eq!(image.blue, [0; 4]);
|
||||||
|
assert_eq!(image.alpha, [255; 4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,5 +101,5 @@
|
|||||||
"LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUUID::test",
|
"LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUUID::test",
|
||||||
"LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUndef::test"
|
"LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUndef::test"
|
||||||
],
|
],
|
||||||
"support_passes": 115
|
"support_passes": 119
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ TARGETS = {
|
|||||||
# implementations. The generated module keeps catalog markers and re-exports
|
# implementations. The generated module keeps catalog markers and re-exports
|
||||||
# the hand-written type so coverage remains deterministic.
|
# the hand-written type so coverage remains deterministic.
|
||||||
NATIVE_TYPES = {
|
NATIVE_TYPES = {
|
||||||
|
"T:LibreMetaverse.Imaging.Skia.SkiaTextureCodec": "crate::skia_codec::SkiaTextureCodec",
|
||||||
"T:LibreMetaverse.Imaging.ManagedImageCreator": "crate::j2k::ManagedImageCreator",
|
"T:LibreMetaverse.Imaging.ManagedImageCreator": "crate::j2k::ManagedImageCreator",
|
||||||
"T:LibreMetaverse.Imaging.ManagedImageInterleavedExtensions": "crate::j2k::ManagedImageInterleavedExtensions",
|
"T:LibreMetaverse.Imaging.ManagedImageInterleavedExtensions": "crate::j2k::ManagedImageInterleavedExtensions",
|
||||||
"T:LibreMetaverse.Imaging.Targa": "crate::targa::Targa",
|
"T:LibreMetaverse.Imaging.Targa": "crate::targa::Targa",
|
||||||
|
|||||||
Reference in New Issue
Block a user