From 0bab5b4c8d158ce17a3d8733908f37bc7b07d84d Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Thu, 13 Aug 2026 05:44:30 +0000 Subject: [PATCH] feat(imaging): add pure-Rust JPEG 2000 backend (#109) --- Cargo.lock | 129 ++ README.md | 15 +- RUSTREWRITE.md | 1 + api/SEMVER-AUDIT.md | 4 +- api/SEMVER-BASELINE.json | 12 +- ci/ci-coverage.json | 9 +- ci/dependency-policy.json | 1 + ci/release-matrix.json | 22 +- crates/libremetaverse-imaging/Cargo.toml | 2 + crates/libremetaverse-imaging/src/jpeg2000.rs | 1207 ++++++++++++++++- crates/libremetaverse-imaging/src/lib.rs | 13 +- crates/libremetaverse/Cargo.toml | 1 + crates/libremetaverse/README.md | 5 +- crates/libremetaverse/src/appearance_baker.rs | 2 +- crates/libremetaverse/src/asset_models.rs | 22 +- .../src/asset_pipeline_semantics.rs | 6 +- crates/libremetaverse/src/model_workflow.rs | 10 +- docs/dependency-policy.md | 1 + docs/imaging-meshing.md | 6 +- docs/release-candidate.md | 1 + docs/rust-api-guide.md | 5 +- release/DEPENDENCY-LICENSES.json | 345 ++++- release/DISTRIBUTION-MANIFEST.json | 110 +- release/THIRD-PARTY-NOTICES.md | 912 ++++++++++++- tools/check_milestone_06.py | 2 + tools/ci-matrix/src/api_surface.rs | 5 + tools/ci-matrix/src/ci_gate.rs | 24 + tools/ci-matrix/src/lib.rs | 7 +- 28 files changed, 2770 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffba9c8..633c5e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -609,6 +609,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -882,6 +907,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +[[package]] +name = "fearless_simd" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693" + [[package]] name = "ff" version = "0.13.1" @@ -1397,6 +1428,61 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "j2k" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0371832fbd6d4e7c83ab85545bb41474f91f565dd1e5660b733de11b54a257eb" +dependencies = [ + "j2k-codec-math", + "j2k-core", + "j2k-native", + "j2k-types", + "moxcms", + "thiserror 2.0.20", +] + +[[package]] +name = "j2k-codec-math" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165366ed0d18b737c6f6cd2c3ffeb20c94dc40aac70e77c2f6dd1c8b518aec3e" + +[[package]] +name = "j2k-core" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058401c1957538158b1565d0b9453f8165ca0fc62e85197849364971d4be551a" +dependencies = [ + "thiserror 2.0.20", +] + +[[package]] +name = "j2k-native" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c8af5bf0762fdf381b2893a56a365c9a5916ec665716c981140afdbd0e9cb76" +dependencies = [ + "fearless_simd", + "j2k-codec-math", + "j2k-profile", + "j2k-types", + "libm", + "rayon", +] + +[[package]] +name = "j2k-profile" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be460f47625754ece6c087e869d80b119775d3b76b107ffaf2769b8ca5c1caca" + +[[package]] +name = "j2k-types" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae71c13c5e4c230bbc5dcf338faa3e5e45cc2906acef762e613b33110e9423d" + [[package]] name = "jni" version = "0.22.4" @@ -1498,6 +1584,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "libremetaverse" version = "0.0.1" @@ -1558,6 +1650,7 @@ dependencies = [ name = "libremetaverse-imaging" version = "0.0.1" dependencies = [ + "j2k", "libremetaverse-openjpeg", "libremetaverse-types", "stats_alloc", @@ -1851,6 +1944,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "ndk" version = "0.9.0" @@ -2409,6 +2512,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + [[package]] name = "quinn" version = "0.11.11" @@ -2551,6 +2660,26 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +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]] name = "rcgen" version = "0.14.9" diff --git a/README.md b/README.md index 46a0160..8042640 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ The currently available opt-in codec backends are: | Capability | Feature | Build command | External prerequisite | | --- | --- | --- | --- | | Raw J2K and JP2 decoding/encoding through OpenJPEG | `libremetaverse/jpeg2000` | `cargo build -p libremetaverse --features jpeg2000` | OpenJPEG 2.5.4 or newer | +| Raw J2K and JP2 decoding/encoding in pure Rust | `libremetaverse/rust-j2k` | `cargo build -p libremetaverse --no-default-features --features rust-j2k` | none | | BMP, GIF, ICO, JPEG, PNG, WBMP, and WebP decoding through Skia | `libremetaverse-imaging-skia/skia` | `cargo build -p libremetaverse-imaging-skia --features skia` | A matching rust-skia binary cache, or the documented native source-build tools | | PCM to Ogg Vorbis encoding | `libremetaverse/vorbis` | `cargo build -p libremetaverse --features vorbis` | `vorbis_rs` 0.5.6 and its native encoder build prerequisites | @@ -68,6 +69,7 @@ Run their focused test suites with: ```sh cargo test -p libremetaverse-imaging --features jpeg2000 +cargo test -p libremetaverse-imaging --no-default-features --features rust-j2k cargo test -p libremetaverse-imaging-skia --features skia cargo test -p libremetaverse --no-default-features --features vorbis ``` @@ -91,9 +93,10 @@ Deterministic task, socket, file, subscription, cancellation, and allocation baselines are documented in the [concurrency hardening guide](docs/concurrency-hardening.md). -An independent optional `rust-j2k` backend is planned but has not been -implemented yet. There is deliberately no enablement command for it today; -the existing `jpeg2000` feature always selects the OpenJPEG backend. +The independent `rust-j2k` backend is off by default and never enables the +native OpenJPEG adapter. Its explicit `RustJ2kCodec` preserves the existing +`J2kCodec` meaning when both features are enabled. Use the combined feature set +to run backend-interoperability tests without changing application selection. `tests/upstream-tests.json` is the machine-readable NUnit parity catalog. Translated tests live in hand-written Rust files with the `parity-case` marker @@ -212,6 +215,12 @@ including its alpha-only and opaque-alpha substitutions. See [`crates/libremetaverse-openjpeg/README.md`](crates/libremetaverse-openjpeg/README.md) for OpenJPEG prerequisites, licensing, and deployment details. Default builds do not discover or link OpenJPEG. +The independent opt-in `rust-j2k` feature provides the same bounded public +options through `RustJ2kCodec`, backed by the exact pure-Rust `j2k` 0.8.1 +release. It supports J2K and JP2, reversible and irreversible encoding, +native component precision/signedness/order, reduced-resolution and progressive +quality-layer decoding, and strict or explicitly permissive truncation handling. +It has no C/C++ codec, CMake, pkg-config, vcpkg, CLR, or platform-specific API. 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 diff --git a/RUSTREWRITE.md b/RUSTREWRITE.md index 08e5a75..2ab36e8 100644 --- a/RUSTREWRITE.md +++ b/RUSTREWRITE.md @@ -415,6 +415,7 @@ MSRV, features, and licenses at adoption time. | release artifact inspection | [`goblin` 0.10.5](https://crates.io/crates/goblin/0.10.5) | Parse ELF, PE, and Mach-O imports, symbols, and linked libraries without executing an artifact or relying on platform-only inspection tools. | | 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 pure-Rust extension | [`j2k`](https://crates.io/crates/j2k) 0.8.1 | Independent opt-in `rust-j2k` feature with no native library. The safe adapter provides J2K/JP2 encode/decode, native component preservation, bounded streams and allocations, reduced resolution, progressive quality layers, strict/permissive truncation, and OpenJPEG interoperability. | | 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. | | 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. | diff --git a/api/SEMVER-AUDIT.md b/api/SEMVER-AUDIT.md index 5adbf9c..9ff4ccc 100644 --- a/api/SEMVER-AUDIT.md +++ b/api/SEMVER-AUDIT.md @@ -20,10 +20,10 @@ The compatibility source is pinned at [`2aa70bb68513b39795da5d13c88f31b86e85a3ba | Crate | Mapped types | Mapped members | Rust declarations | Features | Declaration SHA-256 | | --- | ---: | ---: | ---: | --- | --- | -| `libremetaverse` | 2711 | 27281 | 24562 | dds-bc67, default, jpeg2000, vorbis | `b1c578bb84f352dc5c920fa87751b560eaba2b0d83db4ba8ff560535eeb51afa` | +| `libremetaverse` | 2711 | 27281 | 24562 | dds-bc67, default, jpeg2000, rust-j2k, vorbis | `b1c578bb84f352dc5c920fa87751b560eaba2b0d83db4ba8ff560535eeb51afa` | | `libremetaverse-types` | 45 | 942 | 887 | none | `532cdfc175a60cb6ab109b2c43cfdda1d89cf0aadffccbcd7c9b18e1a3215bee` | | `libremetaverse-structured-data` | 16 | 295 | 304 | none | `b1c2ca9a683efe92ea22293384189f9e7c9a1926860893ab78c41829e641a8c3` | -| `libremetaverse-imaging` | 3 | 20 | 63 | default, jpeg2000 | `bc3f5cf5a8f472b3d4092032be35202787f443f06a4998fe2533e21aadef2e86` | +| `libremetaverse-imaging` | 3 | 20 | 70 | default, jpeg2000, rust-j2k | `19423a5aec60e38d547b809ab947e7aff6366318f97f69cde824f57ae6194c0e` | | `libremetaverse-imaging-skia` | 1 | 3 | 13 | default, skia | `a6a1445725410a036204496ad4953c84dcfea1e6a00390ce4e10d12d36f2876b` | | `libremetaverse-prim-mesher` | 17 | 207 | 117 | none | `dde798e86c485948f1d67e093f8194a76082101416516df3f8dca4de04c2d415` | | `libremetaverse-rendering-simple` | 1 | 6 | 12 | none | `4dc284ea0abb16de63494435758c8b7c5eeb57a80d2e32a141e488e5861f2fad` | diff --git a/api/SEMVER-BASELINE.json b/api/SEMVER-BASELINE.json index 29f2866..99dfdfd 100644 --- a/api/SEMVER-BASELINE.json +++ b/api/SEMVER-BASELINE.json @@ -20,7 +20,7 @@ "type_mapping_sha256": "21025889c62b0f8666a2dda46a03ff4f28931f51156927822a1c04e97b49f221", "member_mapping_sha256": "cd99420643c665ea393dd59418c33fb00f97f7030e71540c00b2b27470abb1d4", "exported_mapping_sha256": "5b8a502ce229d26732744f2b44c029fc4a86b403966dc2f9b4a1c913a7b242b9", - "rust_declaration_sha256": "6f815653b4aa337ae6135f25e1961d1586e29f00405cd25d38462413c07091fa", + "rust_declaration_sha256": "687d3d29bb0e79651881ffdda962be21df8716a4d96417175611541d0fe27a8e", "crates": [ { "name": "libremetaverse", @@ -35,6 +35,9 @@ "jpeg2000": [ "libremetaverse-imaging/jpeg2000" ], + "rust-j2k": [ + "libremetaverse-imaging/rust-j2k" + ], "vorbis": [ "dep:vorbis_rs" ] @@ -69,12 +72,15 @@ "default": [], "jpeg2000": [ "dep:libremetaverse-openjpeg" + ], + "rust-j2k": [ + "dep:j2k" ] }, "mapped_types": 3, "mapped_members": 20, - "public_declarations": 63, - "declaration_sha256": "bc3f5cf5a8f472b3d4092032be35202787f443f06a4998fe2533e21aadef2e86" + "public_declarations": 70, + "declaration_sha256": "19423a5aec60e38d547b809ab947e7aff6366318f97f69cde824f57ae6194c0e" }, { "name": "libremetaverse-imaging-skia", diff --git a/ci/ci-coverage.json b/ci/ci-coverage.json index f1b3469..dc06193 100644 --- a/ci/ci-coverage.json +++ b/ci/ci-coverage.json @@ -12,6 +12,7 @@ "compatibility-parity", "minimal-features", "jpeg2000", + "rust-j2k", "skia-binary", "program-offline-smoke", "clippy", @@ -66,10 +67,10 @@ }, { "workflow": "jpeg2000.yml", - "responsibility": "Optional OpenJPEG/JPEG 2000 feature build and tests", + "responsibility": "Independent OpenJPEG and pure-Rust JPEG 2000 feature builds, tests, and interoperability", "destination": "required", - "checks": ["jpeg2000"], - "rationale": "The optional native codec is tested independently with an exact cached OpenJPEG 2.5.4 identity." + "checks": ["jpeg2000", "rust-j2k"], + "rationale": "The optional native codec is tested with exact OpenJPEG 2.5.4 and the pure-Rust codec is tested independently and together with it." }, { "workflow": "performance.yml", @@ -89,7 +90,7 @@ "workflow": "release-matrix.yml", "responsibility": "MSRV, default, minimal, features, release profiles, and portable cross-target checks", "destination": "split", - "checks": ["minimal-features", "jpeg2000", "skia-binary", "msrv", "portable-cross-targets", "release-profile"], + "checks": ["minimal-features", "jpeg2000", "rust-j2k", "skia-binary", "msrv", "portable-cross-targets", "release-profile"], "rationale": "Fast feature isolation remains required; clean MSRV, cross-target, and release-profile builds run non-routinely." }, { diff --git a/ci/dependency-policy.json b/ci/dependency-policy.json index fe57ffb..f859878 100644 --- a/ci/dependency-policy.json +++ b/ci/dependency-policy.json @@ -15,6 +15,7 @@ { "name": "getrandom", "versions": ["0.4.3"], "purpose": "Operating-system entropy for protocol identifiers", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`getrandom`" }, { "name": "goblin", "versions": ["0.10.5"], "purpose": "Cross-platform ELF, PE, and Mach-O release artifact inspection", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`goblin`" }, { "name": "hound", "versions": ["3.5.1"], "purpose": "Bounded PCM WAV parsing for WebRTC playback", "maintenance": "stable", "transitive_cost": "low", "native": false, "rewrite_anchor": "`hound`" }, + { "name": "j2k", "versions": ["0.8.1"], "purpose": "Opt-in pure-Rust bounded JPEG 2000 and JP2 encoding and decoding", "maintenance": "active", "transitive_cost": "medium", "native": false, "rewrite_anchor": "`j2k`" }, { "name": "mac_address2", "versions": ["2.0.2"], "purpose": "Cross-platform machine identity compatibility input", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`mac_address2`" }, { "name": "md-5", "versions": ["0.10.6"], "purpose": "Legacy protocol checksum compatibility", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`md-5`" }, { "name": "os_info", "versions": ["3.15.0"], "purpose": "Cross-platform login platform reporting", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`os_info`" }, diff --git a/ci/release-matrix.json b/ci/release-matrix.json index 9450166..08a4a67 100644 --- a/ci/release-matrix.json +++ b/ci/release-matrix.json @@ -42,12 +42,12 @@ "execution": "native", "toolchain": "1.96.0", "target": "x86_64-unknown-linux-gnu", - "feature_sets": ["no-default-features"], + "feature_sets": ["no-default-features", "rust-j2k"], "prerequisites": ["libopus-1.3"], "commands": [ { "label": "Compile the portable public crates on the declared MSRV", - "args": ["check", "--locked", "-j", "1", "--all-targets", "--no-default-features", "-p", "libremetaverse-types", "-p", "libremetaverse-structured-data", "-p", "libremetaverse-imaging", "-p", "libremetaverse-imaging-skia", "-p", "libremetaverse-prim-mesher", "-p", "libremetaverse-lsl-tools", "-p", "libremetaverse-rlv", "-p", "metacrate-ci-matrix"] + "args": ["check", "--locked", "-j", "1", "--all-targets", "--no-default-features", "--features", "libremetaverse-imaging/rust-j2k", "-p", "libremetaverse-types", "-p", "libremetaverse-structured-data", "-p", "libremetaverse-imaging", "-p", "libremetaverse-imaging-skia", "-p", "libremetaverse-prim-mesher", "-p", "libremetaverse-lsl-tools", "-p", "libremetaverse-rlv", "-p", "metacrate-ci-matrix"] }, { "label": "Compile the safe native Opus boundary and WebRTC voice on the declared MSRV", @@ -106,7 +106,7 @@ "execution": "native", "toolchain": "stable", "target": "x86_64-unknown-linux-gnu", - "feature_sets": ["all-features", "dds-bc67", "jpeg2000", "skia", "vorbis", "real-audio", "tests"], + "feature_sets": ["all-features", "dds-bc67", "jpeg2000", "rust-j2k", "skia", "vorbis", "real-audio", "tests"], "prerequisites": ["openjpeg-2.5.4", "skia-safe-0.99.0", "libopus-1.3", "alsa-1.2", "vorbis-rs-0.5.6"], "commands": [ { @@ -117,6 +117,14 @@ "label": "Test the OpenJPEG feature independently", "args": ["test", "-p", "libremetaverse-imaging", "--no-default-features", "--features", "jpeg2000", "--locked", "-j", "1"] }, + { + "label": "Test the pure-Rust JPEG 2000 feature independently", + "args": ["test", "-p", "libremetaverse-imaging", "--no-default-features", "--features", "rust-j2k", "--locked", "-j", "1"] + }, + { + "label": "Test OpenJPEG and pure-Rust JPEG 2000 interoperability", + "args": ["test", "-p", "libremetaverse-imaging", "--no-default-features", "--features", "jpeg2000,rust-j2k", "--locked", "-j", "1"] + }, { "label": "Test the Skia feature independently", "args": ["test", "-p", "libremetaverse-imaging-skia", "--no-default-features", "--features", "skia", "--locked", "-j", "1"] @@ -162,12 +170,12 @@ "execution": "cross", "toolchain": "stable", "target": "x86_64-pc-windows-gnu", - "feature_sets": ["no-default-features", "dds-bc67"], + "feature_sets": ["no-default-features", "dds-bc67", "rust-j2k"], "prerequisites": [], "commands": [ { "label": "Cross-check portable crates for Windows GNU", - "args": ["check", "--locked", "-j", "1", "--target", "x86_64-pc-windows-gnu", "--all-targets", "--no-default-features", "-p", "libremetaverse-types", "-p", "libremetaverse-structured-data", "-p", "libremetaverse-imaging", "-p", "libremetaverse-imaging-skia", "-p", "libremetaverse-prim-mesher", "-p", "libremetaverse-lsl-tools", "-p", "libremetaverse-rlv", "-p", "metacrate-ci-matrix"] + "args": ["check", "--locked", "-j", "1", "--target", "x86_64-pc-windows-gnu", "--all-targets", "--no-default-features", "--features", "libremetaverse-imaging/rust-j2k", "-p", "libremetaverse-types", "-p", "libremetaverse-structured-data", "-p", "libremetaverse-imaging", "-p", "libremetaverse-imaging-skia", "-p", "libremetaverse-prim-mesher", "-p", "libremetaverse-lsl-tools", "-p", "libremetaverse-rlv", "-p", "metacrate-ci-matrix"] } ] }, @@ -178,12 +186,12 @@ "execution": "cross", "toolchain": "stable", "target": "x86_64-apple-darwin", - "feature_sets": ["no-default-features", "dds-bc67"], + "feature_sets": ["no-default-features", "dds-bc67", "rust-j2k"], "prerequisites": [], "commands": [ { "label": "Cross-check portable crates for macOS", - "args": ["check", "--locked", "-j", "1", "--target", "x86_64-apple-darwin", "--all-targets", "--no-default-features", "-p", "libremetaverse-types", "-p", "libremetaverse-structured-data", "-p", "libremetaverse-imaging", "-p", "libremetaverse-imaging-skia", "-p", "libremetaverse-prim-mesher", "-p", "libremetaverse-lsl-tools", "-p", "libremetaverse-rlv", "-p", "metacrate-ci-matrix"] + "args": ["check", "--locked", "-j", "1", "--target", "x86_64-apple-darwin", "--all-targets", "--no-default-features", "--features", "libremetaverse-imaging/rust-j2k", "-p", "libremetaverse-types", "-p", "libremetaverse-structured-data", "-p", "libremetaverse-imaging", "-p", "libremetaverse-imaging-skia", "-p", "libremetaverse-prim-mesher", "-p", "libremetaverse-lsl-tools", "-p", "libremetaverse-rlv", "-p", "metacrate-ci-matrix"] } ] } diff --git a/crates/libremetaverse-imaging/Cargo.toml b/crates/libremetaverse-imaging/Cargo.toml index c2e8047..6e73cb3 100644 --- a/crates/libremetaverse-imaging/Cargo.toml +++ b/crates/libremetaverse-imaging/Cargo.toml @@ -10,10 +10,12 @@ description = "Texture codec abstractions for the MetaCrate LibreMetaverse rewri [features] default = [] jpeg2000 = ["dep:libremetaverse-openjpeg"] +rust-j2k = ["dep:j2k"] [dependencies] libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" } libremetaverse-openjpeg = { version = "0.0.1", path = "../libremetaverse-openjpeg", optional = true } +j2k = { version = "=0.8.1", optional = true } [dev-dependencies] stats_alloc = "0.1.10" diff --git a/crates/libremetaverse-imaging/src/jpeg2000.rs b/crates/libremetaverse-imaging/src/jpeg2000.rs index c106431..b49dbd3 100644 --- a/crates/libremetaverse-imaging/src/jpeg2000.rs +++ b/crates/libremetaverse-imaging/src/jpeg2000.rs @@ -1,12 +1,17 @@ -//! Optional JPEG 2000 codec backed by a bounded system `OpenJPEG` adapter. +//! Backend-neutral JPEG 2000 contracts with independently selectable codecs. use crate::codec::{InterleavedComponent, InterleavedImage}; use crate::{ DEFAULT_MAX_ENCODED_BYTES, DEFAULT_MAX_PIXELS, Error, ITextureCodec, ManagedImage, ManagedImageImageChannels, }; +#[cfg(feature = "rust-j2k")] +use j2k as rust_j2k; +#[cfg(feature = "jpeg2000")] use libremetaverse_openjpeg as openjpeg; use libremetaverse_types::compat::ReadWrite; +#[cfg(feature = "rust-j2k")] +use std::borrow::Cow; use std::io::Read; /// JPEG 2000 container selection. @@ -149,10 +154,12 @@ impl J2kEncodeOptions { /// vcpkg. Checked-in minimal bindings and all unsafe FFI are isolated in the private /// adapter crate and no native codec type crosses this boundary. #[derive(Clone, Copy, Debug, Default)] +#[cfg(feature = "jpeg2000")] pub struct J2kCodec { decode_options: J2kDecodeOptions, } +#[cfg(feature = "jpeg2000")] impl J2kCodec { /// Creates a codec with explicit bounded decode options. #[must_use] @@ -232,6 +239,7 @@ impl J2kCodec { } } +#[cfg(feature = "jpeg2000")] impl ITextureCodec for J2kCodec { fn decode(&self, mut stream: Box) -> Result { if self.decode_options.max_encoded_bytes == 0 { @@ -254,6 +262,145 @@ impl ITextureCodec for J2kCodec { } } +/// Cross-platform JPEG 2000 adapter implemented entirely in safe Rust. +/// +/// This backend is independent from [`J2kCodec`]: enabling `rust-j2k` does not +/// link `OpenJPEG` or change the meaning of the existing native codec type. +#[derive(Clone, Copy, Debug, Default)] +#[cfg(feature = "rust-j2k")] +pub struct RustJ2kCodec { + decode_options: J2kDecodeOptions, +} + +#[cfg(feature = "rust-j2k")] +impl RustJ2kCodec { + /// Creates a codec with explicit bounded decode options. + #[must_use] + pub const fn new(decode_options: J2kDecodeOptions) -> Self { + Self { decode_options } + } + + /// Decodes raw J2K/J2C or JP2 into native component planes. + /// + /// Header geometry, sampling, and component count are checked before the + /// backend is allowed to allocate decoded sample storage. + /// + /// # Errors + /// + /// Returns a typed argument or parse failure for invalid limits, + /// unsupported component geometry, malformed input, or codec failure. + pub fn decode_interleaved( + encoded: &[u8], + options: J2kDecodeOptions, + ) -> Result { + validate_decode_input(encoded, options)?; + detect_format(encoded)?; + let prepared = prepare_rust_decode_input(encoded, options)?; + let encoded = prepared.as_ref(); + + let settings = if options.strict { + rust_j2k::DecodeSettings::strict() + } else { + rust_j2k::DecodeSettings::lenient() + }; + let support = + rust_j2k::J2kDecoder::inspect_support(encoded).map_err(map_rust_decode_error)?; + let (width, height) = checked_dimensions( + support.info.dimensions.0, + support.info.dimensions.1, + options.max_pixels, + )?; + if !(1..=5).contains(&support.component_count()) || support.has_component_subsampling() { + return Err(Error::Argument); + } + if options.discard_levels == 0 { + let mut decoder = rust_j2k::J2kDecoder::new_with_settings(encoded, settings) + .map_err(map_rust_decode_error)?; + let native = decoder + .decode_native_components() + .map_err(map_rust_decode_error)?; + return rust_native_to_interleaved(&native, width, height, options.max_pixels); + } + + decode_rust_reduced(encoded, settings, &support, options) + } + + /// Decodes into the C#-compatible planar byte representation. + /// + /// # Errors + /// + /// Returns the failures documented by [`Self::decode_interleaved`] or a + /// typed component conversion/allocation failure. + pub fn decode_bytes(encoded: &[u8], options: J2kDecodeOptions) -> Result { + interleaved_to_managed(&Self::decode_interleaved(encoded, options)?) + } + + /// Encodes the four-component `CoreJ2K` compatibility view as J2K or JP2. + /// + /// # Errors + /// + /// Returns a typed validation/operation failure for invalid image layouts, + /// lossy settings, codec errors, allocation failure, or oversized output. + pub fn encode(image: &ManagedImage, options: J2kEncodeOptions) -> Result, Error> { + validate_encode_request(image, options)?; + encode_with_rust_j2k(image, options) + } +} + +#[cfg(feature = "rust-j2k")] +impl ITextureCodec for RustJ2kCodec { + fn decode(&self, mut stream: Box) -> Result { + read_bounded_stream(&mut stream, self.decode_options.max_encoded_bytes) + .and_then(|encoded| Self::decode_bytes(&encoded, self.decode_options)) + } +} + +#[cfg(feature = "rust-j2k")] +fn validate_decode_input(encoded: &[u8], options: J2kDecodeOptions) -> Result<(), Error> { + if options.max_encoded_bytes == 0 + || options.max_pixels == 0 + || encoded.is_empty() + || encoded.len() > options.max_encoded_bytes + { + return Err(Error::Argument); + } + Ok(()) +} + +#[cfg(feature = "rust-j2k")] +fn validate_encode_request(image: &ManagedImage, options: J2kEncodeOptions) -> Result<(), Error> { + image.validate()?; + if options.max_encoded_bytes == 0 { + return Err(Error::Argument); + } + if let J2kCompression::Lossy { compression_ratio } = options.compression + && (!compression_ratio.is_finite() || compression_ratio < 1.0) + { + return Err(Error::Argument); + } + Ok(()) +} + +#[cfg(feature = "rust-j2k")] +fn read_bounded_stream( + stream: &mut (dyn ReadWrite + Send), + max_encoded_bytes: usize, +) -> Result, Error> { + if max_encoded_bytes == 0 { + return Err(Error::Argument); + } + let limit = max_encoded_bytes.checked_add(1).ok_or(Error::Argument)?; + let mut encoded = Vec::new(); + Read::take(stream, u64::try_from(limit).map_err(|_| Error::Argument)?) + .read_to_end(&mut encoded) + .map_err(|_| parse("JPEG 2000 input stream"))?; + if encoded.len() > max_encoded_bytes { + return Err(Error::Argument); + } + Ok(encoded) +} + +#[cfg(feature = "jpeg2000")] fn backend_image_to_interleaved( image: openjpeg::Image, max_pixels: usize, @@ -277,6 +424,279 @@ fn backend_image_to_interleaved( InterleavedImage::new(width, height, decoded) } +#[cfg(feature = "rust-j2k")] +fn rust_native_to_interleaved( + image: &rust_j2k::J2kDecodedNativeComponents, + width: i32, + height: i32, + max_pixels: usize, +) -> Result { + let dimensions = image.dimensions(); + let checked = checked_dimensions(dimensions.0, dimensions.1, max_pixels)?; + if checked != (width, height) || !(1..=5).contains(&image.planes().len()) { + return Err(Error::Argument); + } + let has_alpha = image.has_alpha(); + let plane_count = image.planes().len(); + let mut components = Vec::new(); + components + .try_reserve_exact(plane_count) + .map_err(|_| Error::InvalidOperation)?; + for (index, plane) in image.planes().iter().enumerate() { + if plane.dimensions() != dimensions || plane.sampling() != (1, 1) { + return Err(parse("subsampled JPEG 2000 components")); + } + let samples = unpack_native_samples( + plane.data(), + plane.bytes_per_sample(), + plane.bit_depth(), + plane.signed(), + )?; + components.push(InterleavedComponent::new( + plane.bit_depth(), + plane.signed(), + has_alpha && index + 1 == plane_count, + samples, + )?); + } + InterleavedImage::new(width, height, components) +} + +#[cfg(feature = "rust-j2k")] +fn unpack_native_samples( + data: &[u8], + bytes_per_sample: u8, + precision: u8, + signed: bool, +) -> Result, Error> { + let sample_width = usize::from(bytes_per_sample); + if sample_width == 0 || sample_width > 4 || !data.len().is_multiple_of(sample_width) { + return Err(Error::Argument); + } + let mut samples = Vec::new(); + samples + .try_reserve_exact(data.len() / sample_width) + .map_err(|_| Error::InvalidOperation)?; + for bytes in data.chunks_exact(sample_width) { + let mut packed = [0_u8; 4]; + packed[..sample_width].copy_from_slice(bytes); + let raw = u32::from_le_bytes(packed); + let value = if signed { + let shift = 32_u32 + .checked_sub(u32::from(precision)) + .ok_or(Error::Argument)?; + i32::from_ne_bytes((raw << shift).to_ne_bytes()) >> shift + } else { + i32::try_from(raw).map_err(|_| Error::Argument)? + }; + samples.push(value); + } + Ok(samples) +} + +#[cfg(feature = "rust-j2k")] +fn decode_rust_reduced( + encoded: &[u8], + settings: rust_j2k::DecodeSettings, + support: &rust_j2k::J2kSupportInfo, + options: J2kDecodeOptions, +) -> Result { + if u32::from(support.info.resolution_levels) <= options.discard_levels { + return decode_rust_reduced_native(encoded, settings, support, options); + } + let scale = match options.discard_levels { + 1 => rust_j2k::Downscale::Half, + 2 => rust_j2k::Downscale::Quarter, + 3 => rust_j2k::Downscale::Eighth, + _ => return decode_rust_reduced_native(encoded, settings, support, options), + }; + let components = usize::from(support.component_count()); + if !matches!(components, 1 | 3 | 4) { + return decode_rust_reduced_native(encoded, settings, support, options); + } + let denominator = 1_u32 + .checked_shl(options.discard_levels) + .ok_or(Error::Argument)?; + let reduced_width = support.info.dimensions.0.div_ceil(denominator); + let reduced_height = support.info.dimensions.1.div_ceil(denominator); + let (width, height) = checked_dimensions(reduced_width, reduced_height, options.max_pixels)?; + let pixels = usize::try_from(reduced_width) + .ok() + .and_then(|value| { + usize::try_from(reduced_height) + .ok() + .and_then(|height| value.checked_mul(height)) + }) + .ok_or(Error::Argument)?; + let length = pixels.checked_mul(components).ok_or(Error::Argument)?; + let mut packed = Vec::new(); + packed + .try_reserve_exact(length) + .map_err(|_| Error::InvalidOperation)?; + packed.resize(length, 0); + let stride = usize::try_from(reduced_width) + .ok() + .and_then(|width| width.checked_mul(components)) + .ok_or(Error::Argument)?; + let format = match components { + 1 => rust_j2k::PixelFormat::Gray8, + 3 => rust_j2k::PixelFormat::Rgb8, + 4 => rust_j2k::PixelFormat::Rgba8, + _ => return Err(Error::Argument), + }; + let mut decoder = rust_j2k::J2kDecoder::new_with_settings(encoded, settings) + .map_err(map_rust_decode_error)?; + decoder + .decode_scaled_into( + &mut rust_j2k::J2kScratchPool::new(), + &mut packed, + stride, + format, + scale, + ) + .map_err(map_rust_decode_error)?; + packed_bytes_to_interleaved(width, height, components, &packed) +} + +#[cfg(feature = "rust-j2k")] +fn decode_rust_reduced_native( + encoded: &[u8], + settings: rust_j2k::DecodeSettings, + support: &rust_j2k::J2kSupportInfo, + options: J2kDecodeOptions, +) -> Result { + let denominator = 1_u32 + .checked_shl(options.discard_levels) + .ok_or(Error::Argument)?; + let reduced_width = support.info.dimensions.0.div_ceil(denominator); + let reduced_height = support.info.dimensions.1.div_ceil(denominator); + let (width, height) = checked_dimensions(reduced_width, reduced_height, options.max_pixels)?; + let mut decoder = rust_j2k::J2kDecoder::new_with_settings(encoded, settings) + .map_err(map_rust_decode_error)?; + let native = decoder + .decode_native_components() + .map_err(map_rust_decode_error)?; + let has_alpha = native.has_alpha(); + let count = native.planes().len(); + let mut components = Vec::new(); + components + .try_reserve_exact(count) + .map_err(|_| Error::InvalidOperation)?; + for (index, plane) in native.planes().iter().enumerate() { + if plane.dimensions() != support.info.dimensions || plane.sampling() != (1, 1) { + return Err(parse("subsampled JPEG 2000 components")); + } + let full = unpack_native_samples( + plane.data(), + plane.bytes_per_sample(), + plane.bit_depth(), + plane.signed(), + )?; + let reduced = subsample_plane( + &full, + support.info.dimensions, + (reduced_width, reduced_height), + denominator, + )?; + components.push(InterleavedComponent::new( + plane.bit_depth(), + plane.signed(), + has_alpha && index + 1 == count, + reduced, + )?); + } + InterleavedImage::new(width, height, components) +} + +#[cfg(feature = "rust-j2k")] +fn subsample_plane( + samples: &[i32], + source: (u32, u32), + target: (u32, u32), + denominator: u32, +) -> Result, Error> { + let source_width = usize::try_from(source.0).map_err(|_| Error::Argument)?; + let expected = source_width + .checked_mul(usize::try_from(source.1).map_err(|_| Error::Argument)?) + .ok_or(Error::Argument)?; + if samples.len() != expected { + return Err(Error::Argument); + } + let target_len = usize::try_from(target.0) + .ok() + .and_then(|width| { + usize::try_from(target.1) + .ok() + .and_then(|height| width.checked_mul(height)) + }) + .ok_or(Error::Argument)?; + let mut reduced = Vec::new(); + reduced + .try_reserve_exact(target_len) + .map_err(|_| Error::InvalidOperation)?; + for y in 0..target.1 { + let source_y = y.checked_mul(denominator).ok_or(Error::Argument)?; + for x in 0..target.0 { + let source_x = x.checked_mul(denominator).ok_or(Error::Argument)?; + let index = usize::try_from(source_y) + .ok() + .and_then(|y| y.checked_mul(source_width)) + .and_then(|row| { + usize::try_from(source_x) + .ok() + .and_then(|x| row.checked_add(x)) + }) + .ok_or(Error::Argument)?; + reduced.push(*samples.get(index).ok_or(Error::Argument)?); + } + } + Ok(reduced) +} + +#[cfg(feature = "rust-j2k")] +fn packed_bytes_to_interleaved( + width: i32, + height: i32, + component_count: usize, + packed: &[u8], +) -> Result { + let pixels = usize::try_from(width) + .ok() + .and_then(|width| { + usize::try_from(height) + .ok() + .and_then(|height| width.checked_mul(height)) + }) + .ok_or(Error::Argument)?; + if packed.len() != pixels.checked_mul(component_count).ok_or(Error::Argument)? { + return Err(Error::Argument); + } + let mut components = Vec::new(); + components + .try_reserve_exact(component_count) + .map_err(|_| Error::InvalidOperation)?; + for component in 0..component_count { + let mut samples = Vec::new(); + samples + .try_reserve_exact(pixels) + .map_err(|_| Error::InvalidOperation)?; + samples.extend( + packed + .iter() + .skip(component) + .step_by(component_count) + .map(|sample| i32::from(*sample)), + ); + components.push(InterleavedComponent::new( + 8, + false, + component + 1 == component_count && matches!(component_count, 2 | 4 | 5), + samples, + )?); + } + InterleavedImage::new(width, height, components) +} + fn interleaved_to_managed(image: &InterleavedImage) -> Result { let channels = channels_for_components(image.number_of_components())?; let pixels = usize::try_from(image.width()) @@ -305,6 +725,7 @@ fn interleaved_to_managed(image: &InterleavedImage) -> Result Result, Error> { let width = u32::try_from(image.width).map_err(|_| Error::Argument)?; let height = u32::try_from(image.height).map_err(|_| Error::Argument)?; @@ -352,6 +773,404 @@ fn encode_with_openjpeg(image: &ManagedImage, options: J2kEncodeOptions) -> Resu .map_err(map_encode_error) } +#[cfg(feature = "rust-j2k")] +fn encode_with_rust_j2k(image: &ManagedImage, options: J2kEncodeOptions) -> Result, Error> { + let width = u32::try_from(image.width).map_err(|_| Error::Argument)?; + let height = u32::try_from(image.height).map_err(|_| Error::Argument)?; + checked_dimensions(width, height, DEFAULT_MAX_PIXELS)?; + let planes = reference_encode_planes(image)?; + let pixels = usize::try_from(width) + .ok() + .and_then(|width| { + usize::try_from(height) + .ok() + .and_then(|height| width.checked_mul(height)) + }) + .ok_or(Error::Argument)?; + let length = pixels.checked_mul(4).ok_or(Error::Argument)?; + let mut interleaved = Vec::new(); + interleaved + .try_reserve_exact(length) + .map_err(|_| Error::InvalidOperation)?; + for pixel in 0..pixels { + for plane in &planes { + interleaved.push(u8::try_from(plane[pixel]).map_err(|_| Error::Argument)?); + } + } + + let codestream = match options.compression { + J2kCompression::Lossless => { + let samples = + rust_j2k::J2kLosslessSamples::new(&interleaved, width, height, 4, 8, false) + .map_err(map_rust_encode_error)?; + rust_j2k::encode_j2k_lossless( + samples, + &rust_j2k::J2kLosslessEncodeOptions::default() + .with_cpu_only_backend() + .with_max_decomposition_levels(Some(6)), + ) + .map_err(map_rust_encode_error)? + .codestream + } + J2kCompression::Lossy { compression_ratio } => { + let target_bytes = lossy_target_bytes(length, compression_ratio)?; + let layers = [ + rust_j2k::J2kQualityLayer::new(rust_j2k::J2kRateTarget::Bytes( + target_bytes.div_ceil(3), + )), + rust_j2k::J2kQualityLayer::new(rust_j2k::J2kRateTarget::Bytes( + target_bytes.saturating_mul(2).div_ceil(3), + )), + rust_j2k::J2kQualityLayer::new(rust_j2k::J2kRateTarget::Bytes(target_bytes)), + ]; + let samples = rust_j2k::J2kLossySamples::new(&interleaved, width, height, 4, 8, false) + .map_err(map_rust_encode_error)?; + rust_j2k::encode_j2k_lossy( + samples, + &rust_j2k::J2kLossyEncodeOptions::default() + .with_cpu_only_backend() + .with_max_decomposition_levels(Some(6)) + .with_quality_layers(layers.to_vec()) + .with_marker_segments(vec![rust_j2k::J2kMarkerSegment::Plt]), + ) + .map_err(map_rust_encode_error)? + .codestream + } + }; + + let encoded = match options.format { + J2kFormat::Codestream => codestream, + J2kFormat::Jp2 => { + rust_j2k::wrap_j2k_codestream(&codestream, rust_j2k::J2kFileWrapOptions::jp2()) + .map_err(map_rust_encode_error)? + } + }; + if encoded.len() > options.max_encoded_bytes { + return Err(Error::Argument); + } + Ok(encoded) +} + +#[cfg(feature = "rust-j2k")] +fn prepare_rust_decode_input( + encoded: &[u8], + options: J2kDecodeOptions, +) -> Result, Error> { + let format = detect_format(encoded)?; + let container = if !options.strict + && format == J2kFormat::Jp2 + && rust_j2k::extract_j2k_codestream_payload(encoded).is_err() + { + let mut repaired = Vec::new(); + repaired + .try_reserve_exact(encoded.len().saturating_add(2)) + .map_err(|_| Error::InvalidOperation)?; + repaired.extend_from_slice(encoded); + repaired.extend_from_slice(&[0xff, 0xd9]); + Cow::Owned(repaired) + } else { + Cow::Borrowed(encoded) + }; + let payload = rust_j2k::extract_j2k_codestream_payload(container.as_ref()) + .map_err(map_rust_decode_error)? + .codestream(); + let available = codestream_quality_layers(payload)?; + if options.quality_layers != 0 && options.quality_layers < u32::from(available) { + let requested = u16::try_from(options.quality_layers).map_err(|_| Error::Argument)?; + let limited = limit_codestream_quality_layers(payload, available, requested)?; + if format == J2kFormat::Jp2 { + return rust_j2k::wrap_j2k_codestream(&limited, rust_j2k::J2kFileWrapOptions::jp2()) + .map(Cow::Owned) + .map_err(map_rust_decode_error); + } + return Ok(Cow::Owned(limited)); + } + if !options.strict { + if payload.ends_with(&[0xff, 0xd9]) { + return Ok(container); + } + let mut repaired = Vec::new(); + repaired + .try_reserve_exact(payload.len().saturating_add(2)) + .map_err(|_| Error::InvalidOperation)?; + repaired.extend_from_slice(payload); + repaired.extend_from_slice(&[0xff, 0xd9]); + if format == J2kFormat::Jp2 { + return rust_j2k::wrap_j2k_codestream(&repaired, rust_j2k::J2kFileWrapOptions::jp2()) + .map(Cow::Owned) + .map_err(map_rust_decode_error); + } + return Ok(Cow::Owned(repaired)); + } + Ok(Cow::Borrowed(encoded)) +} + +#[cfg(feature = "rust-j2k")] +fn limit_codestream_quality_layers( + codestream: &[u8], + available: u16, + requested: u16, +) -> Result, Error> { + if requested == 0 || requested >= available { + return Err(Error::Argument); + } + let main = parse_main_header(codestream)?; + if main.progression != 0 || main.has_progression_changes { + return Err(Error::InvalidOperation); + } + let mut output = Vec::new(); + output + .try_reserve_exact(codestream.len()) + .map_err(|_| Error::InvalidOperation)?; + output.extend_from_slice(&codestream[..main.first_tile_part]); + output[main.cod_layers_offset..main.cod_layers_offset + 2] + .copy_from_slice(&requested.to_be_bytes()); + + let mut tile_offset = main.first_tile_part; + while tile_offset + .checked_add(2) + .is_some_and(|end| end <= codestream.len()) + && codestream[tile_offset..].starts_with(&[0xff, 0x90]) + { + let consumed = + append_limited_tile_part(codestream, tile_offset, available, requested, &mut output)?; + tile_offset = tile_offset.checked_add(consumed).ok_or(Error::Argument)?; + } + if tile_offset + .checked_add(2) + .is_none_or(|end| end > codestream.len()) + { + return Err(parse("JPEG 2000 EOC marker")); + } + output.extend_from_slice(&[0xff, 0xd9]); + Ok(output) +} + +#[cfg(feature = "rust-j2k")] +#[derive(Clone, Copy)] +struct MainHeader { + first_tile_part: usize, + cod_layers_offset: usize, + progression: u8, + has_progression_changes: bool, +} + +#[cfg(feature = "rust-j2k")] +fn parse_main_header(codestream: &[u8]) -> Result { + if !codestream.starts_with(&[0xff, 0x4f]) { + return Err(parse("JPEG 2000 codestream")); + } + let mut offset = 2_usize; + let mut cod = None; + let mut has_progression_changes = false; + while offset + .checked_add(2) + .is_some_and(|end| end <= codestream.len()) + { + if codestream[offset] != 0xff { + return Err(parse("JPEG 2000 main header marker")); + } + let marker = codestream[offset + 1]; + if marker == 0x90 { + let (cod_layers_offset, progression) = + cod.ok_or_else(|| parse("JPEG 2000 COD marker"))?; + return Ok(MainHeader { + first_tile_part: offset, + cod_layers_offset, + progression, + has_progression_changes, + }); + } + let (segment_length, end) = marker_segment_bounds(codestream, offset)?; + if marker == 0x52 { + if segment_length < 7 { + return Err(parse("JPEG 2000 COD marker")); + } + cod = Some((offset + 6, codestream[offset + 5])); + } else if marker == 0x5f { + has_progression_changes = true; + } + offset = end; + } + Err(parse("JPEG 2000 tile part")) +} + +#[cfg(feature = "rust-j2k")] +fn append_limited_tile_part( + codestream: &[u8], + tile_offset: usize, + available: u16, + requested: u16, + output: &mut Vec, +) -> Result { + if tile_offset + .checked_add(12) + .is_none_or(|end| end > codestream.len()) + { + return Err(parse("JPEG 2000 SOT marker")); + } + let tile_length = usize::try_from(u32::from_be_bytes([ + codestream[tile_offset + 6], + codestream[tile_offset + 7], + codestream[tile_offset + 8], + codestream[tile_offset + 9], + ])) + .map_err(|_| Error::Argument)?; + if tile_length < 14 + || tile_offset + .checked_add(tile_length) + .is_none_or(|end| end > codestream.len()) + { + return Err(parse("JPEG 2000 tile-part length")); + } + let tile_end = tile_offset + tile_length; + let mut header_offset = tile_offset + 12; + let output_start = output.len(); + output.extend_from_slice(&codestream[tile_offset..tile_offset + 12]); + let mut packet_lengths = Vec::new(); + loop { + if header_offset + .checked_add(2) + .is_none_or(|end| end > tile_end) + || codestream[header_offset] != 0xff + { + return Err(parse("JPEG 2000 tile header")); + } + let marker = codestream[header_offset + 1]; + if marker == 0x93 { + output.extend_from_slice(&[0xff, 0x93]); + header_offset += 2; + break; + } + let (_, marker_end) = marker_segment_bounds(codestream, header_offset)?; + if marker_end > tile_end { + return Err(parse("JPEG 2000 tile header")); + } + if marker == 0x58 { + decode_plt_lengths( + &codestream[header_offset + 5..marker_end], + &mut packet_lengths, + )?; + } else { + if marker == 0x5f { + return Err(Error::InvalidOperation); + } + output.extend_from_slice(&codestream[header_offset..marker_end]); + } + header_offset = marker_end; + } + if packet_lengths.is_empty() || packet_lengths.len() % usize::from(available) != 0 { + return Err(Error::InvalidOperation); + } + let packets_per_layer = packet_lengths.len() / usize::from(available); + let keep_packets = packets_per_layer + .checked_mul(usize::from(requested)) + .ok_or(Error::Argument)?; + let keep_bytes = packet_lengths[..keep_packets] + .iter() + .try_fold(0_usize, |total, length| { + total.checked_add(*length).ok_or(Error::Argument) + })?; + if header_offset + .checked_add(keep_bytes) + .is_none_or(|end| end > tile_end) + { + return Err(parse("JPEG 2000 packet lengths")); + } + output.extend_from_slice(&codestream[header_offset..header_offset + keep_bytes]); + let limited_length = output + .len() + .checked_sub(output_start) + .ok_or(Error::Argument)?; + let limited_length = u32::try_from(limited_length).map_err(|_| Error::Argument)?; + output[output_start + 6..output_start + 10].copy_from_slice(&limited_length.to_be_bytes()); + Ok(tile_length) +} + +#[cfg(feature = "rust-j2k")] +fn decode_plt_lengths(data: &[u8], lengths: &mut Vec) -> Result<(), Error> { + let mut value = 0_usize; + let mut pending = false; + for byte in data { + value = value + .checked_shl(7) + .and_then(|value| value.checked_add(usize::from(byte & 0x7f))) + .ok_or(Error::Argument)?; + pending = byte & 0x80 != 0; + if !pending { + lengths + .try_reserve(1) + .map_err(|_| Error::InvalidOperation)?; + lengths.push(value); + value = 0; + } + } + if pending { + return Err(parse("JPEG 2000 PLT packet length")); + } + Ok(()) +} + +#[cfg(feature = "rust-j2k")] +fn marker_segment_bounds(bytes: &[u8], offset: usize) -> Result<(usize, usize), Error> { + if offset.checked_add(4).is_none_or(|end| end > bytes.len()) { + return Err(parse("JPEG 2000 marker length")); + } + let segment_length = usize::from(u16::from_be_bytes([bytes[offset + 2], bytes[offset + 3]])); + if segment_length < 2 { + return Err(parse("JPEG 2000 marker length")); + } + let end = offset + .checked_add(2) + .and_then(|value| value.checked_add(segment_length)) + .filter(|end| *end <= bytes.len()) + .ok_or_else(|| parse("JPEG 2000 marker length"))?; + Ok((segment_length, end)) +} + +#[cfg(feature = "rust-j2k")] +fn codestream_quality_layers(codestream: &[u8]) -> Result { + if !codestream.starts_with(&[0xff, 0x4f]) { + return Err(parse("JPEG 2000 codestream")); + } + let mut offset = 2_usize; + while offset + .checked_add(4) + .is_some_and(|end| end <= codestream.len()) + { + if codestream[offset] != 0xff { + return Err(parse("JPEG 2000 marker")); + } + let marker = codestream[offset + 1]; + if matches!(marker, 0x90 | 0x93 | 0xd9) { + break; + } + let segment_length = usize::from(u16::from_be_bytes([ + codestream[offset + 2], + codestream[offset + 3], + ])); + if segment_length < 2 { + return Err(parse("JPEG 2000 marker length")); + } + let end = offset + .checked_add(2) + .and_then(|value| value.checked_add(segment_length)) + .filter(|end| *end <= codestream.len()) + .ok_or_else(|| parse("JPEG 2000 marker length"))?; + if marker == 0x52 { + if segment_length < 7 { + return Err(parse("JPEG 2000 COD marker")); + } + let layers = u16::from_be_bytes([codestream[offset + 6], codestream[offset + 7]]); + return (layers != 0) + .then_some(layers) + .ok_or_else(|| parse("JPEG 2000 quality layers")); + } + offset = end; + } + Err(parse("JPEG 2000 COD marker")) +} + fn reference_encode_planes(image: &ManagedImage) -> Result<[Vec; 4], Error> { let pixels = usize::try_from(image.width) .ok() @@ -489,6 +1308,7 @@ fn detect_format(encoded: &[u8]) -> Result { } } +#[cfg(feature = "jpeg2000")] const fn backend_format(format: J2kFormat) -> openjpeg::Format { match format { J2kFormat::Codestream => openjpeg::Format::J2k, @@ -496,6 +1316,7 @@ const fn backend_format(format: J2kFormat) -> openjpeg::Format { } } +#[cfg(feature = "jpeg2000")] const fn map_decode_error(error: openjpeg::Error) -> Error { match error { openjpeg::Error::LimitExceeded => Error::Argument, @@ -505,6 +1326,7 @@ const fn map_decode_error(error: openjpeg::Error) -> Error { } } +#[cfg(feature = "jpeg2000")] const fn map_encode_error(error: openjpeg::Error) -> Error { match error { openjpeg::Error::InvalidInput | openjpeg::Error::LimitExceeded => Error::Argument, @@ -512,6 +1334,49 @@ const fn map_encode_error(error: openjpeg::Error) -> Error { } } +#[cfg(feature = "rust-j2k")] +#[allow( + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + reason = "validated finite positive ratio and a u32-bounded byte count make this conversion exact enough for rate targeting" +)] +fn lossy_target_bytes(length: usize, compression_ratio: f32) -> Result { + let length = u32::try_from(length).map_err(|_| Error::Argument)?; + Ok((f64::from(length) / f64::from(compression_ratio)) + .round() + .max(1.0) as u64) +} + +#[cfg(feature = "rust-j2k")] +#[allow( + clippy::needless_pass_by_value, + reason = "map_err supplies owned non-Copy backend errors and the public error intentionally does not retain backend types" +)] +fn map_rust_decode_error(error: rust_j2k::J2kError) -> Error { + match error { + rust_j2k::J2kError::Buffer(_) + | rust_j2k::J2kError::InvalidSamples { .. } + | rust_j2k::J2kError::InvalidRegion { .. } + | rust_j2k::J2kError::DimensionOverflow { .. } => Error::Argument, + _ => parse("JPEG 2000 codestream"), + } +} + +#[cfg(feature = "rust-j2k")] +#[allow( + clippy::needless_pass_by_value, + reason = "map_err supplies owned non-Copy backend errors and the public error intentionally does not retain backend types" +)] +fn map_rust_encode_error(error: rust_j2k::J2kError) -> Error { + match error { + rust_j2k::J2kError::Buffer(_) + | rust_j2k::J2kError::InvalidSamples { .. } + | rust_j2k::J2kError::DimensionOverflow { .. } + | rust_j2k::J2kError::RateTargetUnreachable { .. } => Error::Argument, + _ => Error::InvalidOperation, + } +} + fn checked_dimensions(width: u32, height: u32, max_pixels: usize) -> Result<(i32, i32), Error> { let width_usize = usize::try_from(width).map_err(|_| Error::Argument)?; let height_usize = usize::try_from(height).map_err(|_| Error::Argument)?; @@ -534,7 +1399,7 @@ const fn parse(context: &'static str) -> Error { } } -#[cfg(test)] +#[cfg(all(test, feature = "jpeg2000"))] mod tests { use super::*; use std::io::Cursor; @@ -706,3 +1571,341 @@ mod tests { assert_eq!(component.samples(), expected); } } + +#[cfg(all(test, feature = "rust-j2k"))] +mod rust_tests { + use super::*; + use std::io::Cursor; + + fn rgba(width: i32, height: i32) -> ManagedImage { + let mut image = ManagedImage::new( + width, + height, + ManagedImageImageChannels::COLOR | ManagedImageImageChannels::ALPHA, + ) + .unwrap(); + for pixel in 0..image.red.len() { + image.red[pixel] = u8::try_from((pixel * 17) & 255).unwrap(); + image.green[pixel] = u8::try_from((pixel * 29 + 3) & 255).unwrap(); + image.blue[pixel] = u8::try_from((pixel * 43 + 7) & 255).unwrap(); + image.alpha[pixel] = u8::try_from((pixel * 11 + 101) & 255).unwrap(); + } + image + } + + #[test] + fn lossless_codestream_and_jp2_round_trip_all_channels() { + let source = rgba(17, 9); + for format in [J2kFormat::Codestream, J2kFormat::Jp2] { + let encoded = + RustJ2kCodec::encode(&source, J2kEncodeOptions::default().with_format(format)) + .expect("encode"); + let decoded = + RustJ2kCodec::decode_bytes(&encoded, J2kDecodeOptions::default()).expect("decode"); + assert_eq!(decoded, source); + } + } + + #[test] + fn alpha_only_substitution_matches_core_j2k() { + let mut source = ManagedImage::new(2, 1, ManagedImageImageChannels::ALPHA).unwrap(); + source.alpha.copy_from_slice(&[17, 231]); + let encoded = RustJ2kCodec::encode(&source, J2kEncodeOptions::default()).unwrap(); + let decoded = RustJ2kCodec::decode_bytes(&encoded, J2kDecodeOptions::default()).unwrap(); + assert_eq!(decoded.red, [17, 231]); + assert_eq!(decoded.green, [17, 231]); + assert_eq!(decoded.blue, [17, 231]); + assert_eq!(decoded.alpha, [255, 255]); + } + + #[test] + fn reduced_resolution_and_stream_limits_are_enforced() { + let source = rgba(64, 32); + let encoded = RustJ2kCodec::encode(&source, J2kEncodeOptions::default()).unwrap(); + let reduced = RustJ2kCodec::decode_bytes( + &encoded, + J2kDecodeOptions::default().with_discard_levels(1), + ) + .unwrap(); + assert_eq!((reduced.width, reduced.height), (32, 16)); + + let codec = RustJ2kCodec::new(J2kDecodeOptions::default().with_limits(8, 64)); + assert_eq!( + codec.decode(Box::new(Cursor::new(encoded))), + Err(Error::Argument) + ); + } + + #[test] + fn malformed_limits_and_output_caps_fail_without_panicking() { + assert!(matches!( + RustJ2kCodec::decode_bytes(b"not jpeg2000", J2kDecodeOptions::default()), + Err(Error::Parse { .. }) + )); + let source = rgba(2, 2); + assert_eq!( + RustJ2kCodec::encode( + &source, + J2kEncodeOptions::default().with_compression(J2kCompression::Lossy { + compression_ratio: f32::NAN, + }), + ), + Err(Error::Argument) + ); + assert_eq!( + RustJ2kCodec::encode( + &source, + J2kEncodeOptions::default().with_max_encoded_bytes(32), + ), + Err(Error::Argument) + ); + } + + #[test] + fn lossy_ratios_preserve_layout_and_reduce_size() { + let source = rgba(128, 128); + let low = RustJ2kCodec::encode( + &source, + J2kEncodeOptions::default().with_compression(J2kCompression::Lossy { + compression_ratio: 4.0, + }), + ) + .unwrap(); + let high = RustJ2kCodec::encode( + &source, + J2kEncodeOptions::default().with_compression(J2kCompression::Lossy { + compression_ratio: 12.0, + }), + ) + .unwrap(); + assert!(high.len() < low.len()); + let decoded = + RustJ2kCodec::decode_bytes(&high, J2kDecodeOptions::default().with_quality_layers(1)) + .unwrap(); + assert_eq!( + (decoded.width, decoded.height, decoded.channels), + (source.width, source.height, source.channels) + ); + assert_ne!(decoded, source); + } + + #[test] + fn progressive_quality_layers_are_applied_in_order() { + let source = rgba(128, 128); + let encoded = RustJ2kCodec::encode( + &source, + J2kEncodeOptions::default().with_compression(J2kCompression::Lossy { + compression_ratio: 8.0, + }), + ) + .unwrap(); + let first = RustJ2kCodec::decode_bytes( + &encoded, + J2kDecodeOptions::default().with_quality_layers(1), + ) + .unwrap(); + let second = RustJ2kCodec::decode_bytes( + &encoded, + J2kDecodeOptions::default().with_quality_layers(2), + ) + .unwrap(); + let complete = RustJ2kCodec::decode_bytes(&encoded, J2kDecodeOptions::default()).unwrap(); + + let error = |decoded: &ManagedImage| -> u64 { + source + .red + .iter() + .chain(&source.green) + .chain(&source.blue) + .chain(&source.alpha) + .zip( + decoded + .red + .iter() + .chain(&decoded.green) + .chain(&decoded.blue) + .chain(&decoded.alpha), + ) + .map(|(expected, actual)| u64::from(expected.abs_diff(*actual))) + .sum() + }; + assert!(error(&first) >= error(&second)); + assert!(error(&second) >= error(&complete)); + assert_ne!(first, complete); + } + + #[test] + fn strict_rejects_missing_eoc_and_permissive_repairs_it() { + let source = rgba(16, 16); + for format in [J2kFormat::Codestream, J2kFormat::Jp2] { + let mut encoded = + RustJ2kCodec::encode(&source, J2kEncodeOptions::default().with_format(format)) + .unwrap(); + assert!(encoded.ends_with(&[0xff, 0xd9])); + encoded.truncate(encoded.len() - 2); + assert!(RustJ2kCodec::decode_bytes(&encoded, J2kDecodeOptions::default()).is_err()); + assert_eq!( + RustJ2kCodec::decode_bytes( + &encoded, + J2kDecodeOptions::default().with_strict_mode(false), + ) + .unwrap(), + source + ); + } + } + + #[test] + fn native_planes_preserve_mixed_precision_signedness_and_five_component_order() { + let width = 8_u32; + let height = 4_u32; + let pixels = usize::try_from(width * height).unwrap(); + let specifications = [ + (8_u8, false), + (12_u8, true), + (10_u8, false), + (8_u8, false), + (16_u8, true), + ]; + let mut plane_bytes = Vec::new(); + let mut expected = Vec::new(); + for (component, (precision, signed)) in specifications.iter().copied().enumerate() { + let mut bytes = Vec::new(); + let mut values = Vec::new(); + for pixel in 0..pixels { + let value = if signed { + i32::try_from(pixel).unwrap() - 15 - i32::try_from(component).unwrap() + } else { + i32::try_from(pixel * (component + 1)).unwrap() + }; + values.push(value); + let width = usize::from(precision).div_ceil(8); + bytes.extend_from_slice(&value.to_le_bytes()[..width]); + } + plane_bytes.push(bytes); + expected.push(values); + } + let planes = plane_bytes + .iter() + .zip(specifications) + .map( + |(data, (bit_depth, signed))| rust_j2k::J2kLosslessTypedComponentPlane { + data, + x_rsiz: 1, + y_rsiz: 1, + bit_depth, + signed, + }, + ) + .collect::>(); + let samples = + rust_j2k::J2kLosslessTypedComponentSamples::new(&planes, width, height).unwrap(); + let encoded = rust_j2k::encode_j2k_lossless_typed_components( + samples, + &rust_j2k::J2kLosslessEncodeOptions::default() + .with_cpu_only_backend() + .with_reversible_transform(rust_j2k::ReversibleTransform::None53), + ) + .unwrap(); + let decoded = + RustJ2kCodec::decode_interleaved(&encoded.codestream, J2kDecodeOptions::default()) + .unwrap(); + assert_eq!(decoded.number_of_components(), 5); + for (index, component) in decoded.components().iter().enumerate() { + assert_eq!(component.precision(), specifications[index].0); + assert_eq!(component.is_signed(), specifications[index].1); + assert_eq!(component.samples(), expected[index]); + } + } + + #[test] + fn independent_gray_and_rgb_codestreams_decode_without_rgba_assumptions() { + for components in [1_u16, 3] { + let width = 11_u32; + let height = 7_u32; + let samples = (0..usize::try_from(width * height).unwrap() * usize::from(components)) + .map(|index| u8::try_from((index * 23 + 7) & 255).unwrap()) + .collect::>(); + let input = + rust_j2k::J2kLosslessSamples::new(&samples, width, height, components, 8, false) + .unwrap(); + let encoded = rust_j2k::encode_j2k_lossless( + input, + &rust_j2k::J2kLosslessEncodeOptions::default().with_cpu_only_backend(), + ) + .unwrap(); + let decoded = + RustJ2kCodec::decode_interleaved(&encoded.codestream, J2kDecodeOptions::default()) + .unwrap(); + assert_eq!(decoded.number_of_components(), usize::from(components)); + let managed = interleaved_to_managed(&decoded).unwrap(); + assert_eq!((managed.width, managed.height), (11, 7)); + } + } + + #[test] + fn unsupported_subsampling_and_excess_components_are_rejected() { + let width = 8_u32; + let height = 8_u32; + let full = vec![37_u8; usize::try_from(width * height).unwrap()]; + let half_width = vec![91_u8; usize::try_from(width / 2 * height).unwrap()]; + let planes = [ + rust_j2k::J2kLosslessTypedComponentPlane { + data: &full, + x_rsiz: 1, + y_rsiz: 1, + bit_depth: 8, + signed: false, + }, + rust_j2k::J2kLosslessTypedComponentPlane { + data: &half_width, + x_rsiz: 2, + y_rsiz: 1, + bit_depth: 8, + signed: false, + }, + ]; + let samples = + rust_j2k::J2kLosslessTypedComponentSamples::new(&planes, width, height).unwrap(); + let subsampled = rust_j2k::encode_j2k_lossless_typed_components( + samples, + &rust_j2k::J2kLosslessEncodeOptions::default() + .with_cpu_only_backend() + .with_reversible_transform(rust_j2k::ReversibleTransform::None53), + ) + .unwrap(); + assert!(matches!( + RustJ2kCodec::decode_interleaved(&subsampled.codestream, J2kDecodeOptions::default()), + Err(Error::Argument) + )); + + let six_components = vec![19_u8; usize::try_from(width * height).unwrap() * 6]; + let samples = + rust_j2k::J2kLosslessSamples::new(&six_components, width, height, 6, 8, false).unwrap(); + let encoded = rust_j2k::encode_j2k_lossless( + samples, + &rust_j2k::J2kLosslessEncodeOptions::default().with_cpu_only_backend(), + ) + .unwrap(); + assert_eq!( + RustJ2kCodec::decode_interleaved(&encoded.codestream, J2kDecodeOptions::default()), + Err(Error::Argument) + ); + } + + #[cfg(feature = "jpeg2000")] + #[test] + fn openjpeg_and_rust_backends_interoperate_both_directions() { + let source = rgba(32, 24); + let rust = RustJ2kCodec::encode(&source, J2kEncodeOptions::default()).unwrap(); + assert_eq!( + J2kCodec::decode_bytes(&rust, J2kDecodeOptions::default()).unwrap(), + source + ); + let native = J2kCodec::encode(&source, J2kEncodeOptions::default()).unwrap(); + assert_eq!( + RustJ2kCodec::decode_bytes(&native, J2kDecodeOptions::default()).unwrap(), + source + ); + } +} diff --git a/crates/libremetaverse-imaging/src/lib.rs b/crates/libremetaverse-imaging/src/lib.rs index 843d102..031249d 100644 --- a/crates/libremetaverse-imaging/src/lib.rs +++ b/crates/libremetaverse-imaging/src/lib.rs @@ -1,14 +1,15 @@ //! Image and texture abstractions corresponding to `LibreMetaverse.Imaging`. //! //! The default surface provides managed images plus native `TGA`/`DDS` handling. -//! Enable `jpeg2000` only when the audited system `OpenJPEG` adapter is available; -//! ordinary users can consume the traits without a native image dependency. +//! Enable `jpeg2000` for the audited system `OpenJPEG` adapter or `rust-j2k` +//! for the independently selectable pure-Rust JPEG 2000 backend. Ordinary +//! users can consume the traits without either codec dependency. extern crate self as libremetaverse_imaging; pub mod codec; -#[cfg(feature = "jpeg2000")] +#[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] mod jpeg2000; mod generated; @@ -16,6 +17,10 @@ mod managed_image; pub use generated::*; #[cfg(feature = "jpeg2000")] -pub use jpeg2000::{J2kCodec, J2kCompression, J2kDecodeOptions, J2kEncodeOptions, J2kFormat}; +pub use jpeg2000::J2kCodec; +#[cfg(feature = "rust-j2k")] +pub use jpeg2000::RustJ2kCodec; +#[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] +pub use jpeg2000::{J2kCompression, J2kDecodeOptions, J2kEncodeOptions, J2kFormat}; pub use libremetaverse_types::Error; pub use managed_image::{DEFAULT_MAX_ENCODED_BYTES, DEFAULT_MAX_PIXELS}; diff --git a/crates/libremetaverse/Cargo.toml b/crates/libremetaverse/Cargo.toml index a6a4897..3ff2dbb 100644 --- a/crates/libremetaverse/Cargo.toml +++ b/crates/libremetaverse/Cargo.toml @@ -11,6 +11,7 @@ description = "Rust rewrite shell for the LibreMetaverse client library" default = ["dds-bc67"] dds-bc67 = ["dep:bcdec_rs"] jpeg2000 = ["libremetaverse-imaging/jpeg2000"] +rust-j2k = ["libremetaverse-imaging/rust-j2k"] vorbis = ["dep:vorbis_rs"] [dependencies] diff --git a/crates/libremetaverse/README.md b/crates/libremetaverse/README.md index dd40756..28484d5 100644 --- a/crates/libremetaverse/README.md +++ b/crates/libremetaverse/README.md @@ -15,10 +15,10 @@ they use those APIs directly. | Crate | Choose it for | Features or system boundary | | --- | --- | --- | -| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7; optional `jpeg2000` and `vorbis` | +| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7; optional `jpeg2000`, `rust-j2k`, and `vorbis` | | `libremetaverse-types` | UUIDs, vectors, matrices, colors, cancellation, compatibility collections, and boundary types | Pure Rust | | `libremetaverse-structured-data` | LLSD/OSD XML, JSON, binary, and notation | Pure Rust | -| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Optional `jpeg2000` | +| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Optional `jpeg2000` or pure-Rust `rust-j2k` | | `libremetaverse-imaging-skia` | Common raster formats through Skia | Optional `skia`; native Skia build/cache | | `libremetaverse-prim-mesher` | Legacy prim and sculpt geometry | Pure Rust | | `libremetaverse-rendering-simple` | Deterministic reference geometry | Pure Rust | @@ -190,6 +190,7 @@ The default client build needs no image-codec system library. Optional native features require: - `OpenJPEG` 2.5.4 or newer for `jpeg2000`; +- no native prerequisite for the pure-Rust `rust-j2k` feature; - the rust-skia prerequisites/cache for `skia`; - system libopus for WebRTC voice and the `libremetaverse-opus` adapter; - ALSA development headers on Linux, `CoreAudio` on macOS, or WASAPI on Windows diff --git a/crates/libremetaverse/src/appearance_baker.rs b/crates/libremetaverse/src/appearance_baker.rs index 4335a7e..1f73ccd 100644 --- a/crates/libremetaverse/src/appearance_baker.rs +++ b/crates/libremetaverse/src/appearance_baker.rs @@ -150,7 +150,7 @@ impl Baker { let texture = crate::assets::AssetTexture::new_with_managed_image(output)?; // JPEG2000 is the protocol representation. Keep the decoded image even // when a build intentionally excludes that optional codec. - #[cfg(feature = "jpeg2000")] + #[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] texture.encode()?; self.baked_texture = Some(texture); Ok(()) diff --git a/crates/libremetaverse/src/asset_models.rs b/crates/libremetaverse/src/asset_models.rs index 690d76c..600da70 100644 --- a/crates/libremetaverse/src/asset_models.rs +++ b/crates/libremetaverse/src/asset_models.rs @@ -9,7 +9,11 @@ )] use crate::{Error, Permissions}; +#[cfg(feature = "jpeg2000")] +use libremetaverse_imaging::J2kCodec as TextureJ2kCodec; use libremetaverse_imaging::ManagedImage; +#[cfg(all(not(feature = "jpeg2000"), feature = "rust-j2k"))] +use libremetaverse_imaging::RustJ2kCodec as TextureJ2kCodec; use libremetaverse_structured_data::{OSD, OSDMap, OSDParser}; use libremetaverse_types::{AssetType, SaleType, UUID, Vector3, WearableType}; use std::collections::HashMap; @@ -526,9 +530,9 @@ impl AssetTexture { if self.image.is_some() { return Ok(true); } - #[cfg(feature = "jpeg2000")] + #[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] { - self.image = Some(libremetaverse_imaging::J2kCodec::decode_bytes( + self.image = Some(TextureJ2kCodec::decode_bytes( &self.raw.bytes(), libremetaverse_imaging::J2kDecodeOptions::default(), )?); @@ -543,7 +547,7 @@ impl AssetTexture { }; Ok(true) } - #[cfg(not(feature = "jpeg2000"))] + #[cfg(not(any(feature = "jpeg2000", feature = "rust-j2k")))] { Err(Error::InvalidOperation) } @@ -570,15 +574,15 @@ impl AssetTexture { }) } pub fn decode(&self) -> Result { - #[cfg(feature = "jpeg2000")] + #[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] { - Ok(libremetaverse_imaging::J2kCodec::decode_bytes( + Ok(TextureJ2kCodec::decode_bytes( &self.raw.bytes(), libremetaverse_imaging::J2kDecodeOptions::default(), ) .is_ok()) } - #[cfg(not(feature = "jpeg2000"))] + #[cfg(not(any(feature = "jpeg2000", feature = "rust-j2k")))] { validate_bytes(&self.raw.bytes())?; Err(Error::InvalidOperation) @@ -586,15 +590,15 @@ impl AssetTexture { } pub fn encode(&self) -> Result<(), Error> { let image = self.image.as_ref().ok_or(Error::InvalidOperation)?; - #[cfg(feature = "jpeg2000")] + #[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] { - let encoded = libremetaverse_imaging::J2kCodec::encode( + let encoded = TextureJ2kCodec::encode( image, libremetaverse_imaging::J2kEncodeOptions::default(), )?; self.raw.replace(encoded) } - #[cfg(not(feature = "jpeg2000"))] + #[cfg(not(any(feature = "jpeg2000", feature = "rust-j2k")))] { let _ = image; Err(Error::InvalidOperation) diff --git a/crates/libremetaverse/src/asset_pipeline_semantics.rs b/crates/libremetaverse/src/asset_pipeline_semantics.rs index 474fc1a..d39b499 100644 --- a/crates/libremetaverse/src/asset_pipeline_semantics.rs +++ b/crates/libremetaverse/src/asset_pipeline_semantics.rs @@ -1,10 +1,10 @@ -#[cfg(feature = "jpeg2000")] +#[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] use crate::assets::AssetTexture; use crate::assets::{AssetMesh, AssetMutable, AssetSound}; use crate::{ AssetCache, AssetCacheComputeAssetCacheFilenameDelegate, Error, GridClient, ImageCodec, }; -#[cfg(feature = "jpeg2000")] +#[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] use libremetaverse_imaging::{ManagedImage, ManagedImageImageChannels}; use libremetaverse_types::{AssetType, UUID}; use std::fs; @@ -58,7 +58,7 @@ fn sound_codec_is_explicitly_feature_gated() { } #[test] -#[cfg(feature = "jpeg2000")] +#[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] fn texture_codec_produces_real_valid_payload() { let mut image = ManagedImage::new(2, 2, ManagedImageImageChannels::COLOR).unwrap(); image.red.fill(255); diff --git a/crates/libremetaverse/src/model_workflow.rs b/crates/libremetaverse/src/model_workflow.rs index 7fee718..561bbb6 100644 --- a/crates/libremetaverse/src/model_workflow.rs +++ b/crates/libremetaverse/src/model_workflow.rs @@ -11,6 +11,10 @@ use std::path::{Component, Path, PathBuf}; use flate2::{Compression, write::ZlibEncoder}; use libremetaverse_imaging::ITextureCodec; +#[cfg(feature = "jpeg2000")] +use libremetaverse_imaging::J2kCodec as TextureJ2kCodec; +#[cfg(all(not(feature = "jpeg2000"), feature = "rust-j2k"))] +use libremetaverse_imaging::RustJ2kCodec as TextureJ2kCodec; use libremetaverse_structured_data::{OSD, OSDFormat, OSDParser}; use libremetaverse_types::compat::{CancellationToken, Uri}; use libremetaverse_types::{AssetType, Color4, Error, Quaternion, UUID, Vector2, Vector3}; @@ -956,14 +960,14 @@ fn load_texture( return Err(Error::InvalidOperation); }; let image = codec.decode(Box::new(std::io::Cursor::new(bytes)))?; - #[cfg(feature = "jpeg2000")] + #[cfg(any(feature = "jpeg2000", feature = "rust-j2k"))] { - return libremetaverse_imaging::J2kCodec::encode( + return TextureJ2kCodec::encode( &image, libremetaverse_imaging::J2kEncodeOptions::default(), ); } - #[cfg(not(feature = "jpeg2000"))] + #[cfg(not(any(feature = "jpeg2000", feature = "rust-j2k")))] { let _ = image; return Err(Error::InvalidOperation); diff --git a/docs/dependency-policy.md b/docs/dependency-policy.md index 3c57f8a..117957c 100644 --- a/docs/dependency-policy.md +++ b/docs/dependency-policy.md @@ -49,6 +49,7 @@ All native boundaries have Linux, Windows, and macOS strategies: |---|---|---|---| | libopus 1.3+ | `pkg-config` package `opus` | vcpkg `opus` for MSVC; `pkg-config` for GNU | WebRTC voice crate | | OpenJPEG 2.5.4+ | `pkg-config` package `libopenjp2` | vcpkg `openjpeg` for MSVC; `pkg-config` for GNU | opt-in `jpeg2000` | +| j2k 0.8.1 | pure Rust, no platform prerequisite | same pure-Rust graph | opt-in `rust-j2k` | | Skia 0.99.0 | target-specific official binary cache or source build | same target-specific strategy | opt-in `skia` | | Vorbis 0.5.6 stack | `vorbis_rs` builds its reviewed C codec stack | same crate strategy | opt-in `vorbis` | | Physical audio | ALSA development files on Linux; CoreAudio is system-provided on macOS | WASAPI is system-provided | opt-in `real-audio` | diff --git a/docs/imaging-meshing.md b/docs/imaging-meshing.md index 2b3a0fe..ccc8e40 100644 --- a/docs/imaging-meshing.md +++ b/docs/imaging-meshing.md @@ -17,12 +17,16 @@ and [`MeshFoundry` guide](../crates/libremetaverse-rendering-mesh-foundry/README | --- | --- | --- | --- | | Managed images and TGA | enabled | n/a | none | | JPEG 2000 | disabled | `libremetaverse-imaging/jpeg2000` | OpenJPEG 2.5.x | +| JPEG 2000 (pure Rust) | disabled | `libremetaverse-imaging/rust-j2k` | none (`j2k` 0.8.1) | | BMP/GIF/ICO/JPEG/PNG/WBMP/WebP | disabled | `libremetaverse-imaging-skia/skia` | Skia | | Prim and sculpt meshing, OBJ ingestion | enabled | n/a | none | OpenJPEG is a non-default workspace member so release tooling can package the adapter explicitly, and imaging reaches it only through the optional -dependency. Skia is likewise an optional, target-specific dependency. The +dependency. The independent `rust-j2k` feature reaches only the audited +pure-Rust `j2k` graph; enabling both features keeps both explicit codec types +available for interoperability testing. Skia is likewise an optional, +target-specific dependency. The consolidated required Gitea gate exercises both optional features on `ubuntu-latest` while its minimal-feature check proves that no native codec is needed for the owned Rust and translated compatibility tests. diff --git a/docs/release-candidate.md b/docs/release-candidate.md index 06d011c..73c4d8e 100644 --- a/docs/release-candidate.md +++ b/docs/release-candidate.md @@ -23,6 +23,7 @@ Optional boundaries are explicit: | Feature | Capability | Native/build prerequisite | | --- | --- | --- | | `jpeg2000` | J2K/JP2 through the safe OpenJPEG adapter | OpenJPEG 2.5.4 or newer | +| `rust-j2k` | J2K/JP2 through `RustJ2kCodec` | none; exact pure-Rust `j2k` 0.8.1 | | `skia` | PNG/JPEG/WebP and other Skia formats | Skia 0.99 target cache or documented source tools | | `vorbis` | PCM to Ogg Vorbis | the locked `vorbis_rs` native build stack | | default WebRTC | native ICE/DTLS/SRTP/RTP/SCTP and Opus | system libopus 1.3 or newer | diff --git a/docs/rust-api-guide.md b/docs/rust-api-guide.md index d5925fe..b4a8515 100644 --- a/docs/rust-api-guide.md +++ b/docs/rust-api-guide.md @@ -15,10 +15,10 @@ they use those APIs directly. | Crate | Choose it for | Features or system boundary | | --- | --- | --- | -| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7; optional `jpeg2000` and `vorbis` | +| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7; optional `jpeg2000`, `rust-j2k`, and `vorbis` | | `libremetaverse-types` | UUIDs, vectors, matrices, colors, cancellation, compatibility collections, and boundary types | Pure Rust | | `libremetaverse-structured-data` | LLSD/OSD XML, JSON, binary, and notation | Pure Rust | -| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Optional `jpeg2000` | +| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Optional `jpeg2000` or pure-Rust `rust-j2k` | | `libremetaverse-imaging-skia` | Common raster formats through Skia | Optional `skia`; native Skia build/cache | | `libremetaverse-prim-mesher` | Legacy prim and sculpt geometry | Pure Rust | | `libremetaverse-rendering-simple` | Deterministic reference geometry | Pure Rust | @@ -190,6 +190,7 @@ The default client build needs no image-codec system library. Optional native features require: - `OpenJPEG` 2.5.4 or newer for `jpeg2000`; +- no native prerequisite for the pure-Rust `rust-j2k` feature; - the rust-skia prerequisites/cache for `skia`; - system libopus for WebRTC voice and the `libremetaverse-opus` adapter; - ALSA development headers on Linux, `CoreAudio` on macOS, or WASAPI on Windows diff --git a/release/DEPENDENCY-LICENSES.json b/release/DEPENDENCY-LICENSES.json index 179b884..35148ab 100644 --- a/release/DEPENDENCY-LICENSES.json +++ b/release/DEPENDENCY-LICENSES.json @@ -1,6 +1,6 @@ { "schema": 1, - "cargo_lock_sha256": "4753d29e32012fcf82584ea9a5d73037ccf3e63114ad2d1b9295c41808862ddd", + "cargo_lock_sha256": "68988caa2c014bf2c67563df3dd0a8348d67eeb06559649798463fd8211fc0cb", "packages": [ { "name": "adler2", @@ -1065,6 +1065,57 @@ } ] }, + { + "name": "crossbeam-deque", + "version": "0.8.7", + "checksum": "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "notice_files": [ + { + "path": "LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2" + }, + { + "path": "LICENSE-MIT", + "sha256": "5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab" + } + ] + }, + { + "name": "crossbeam-epoch", + "version": "0.9.20", + "checksum": "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "notice_files": [ + { + "path": "LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2" + }, + { + "path": "LICENSE-MIT", + "sha256": "5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab" + } + ] + }, + { + "name": "crossbeam-utils", + "version": "0.8.22", + "checksum": "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "notice_files": [ + { + "path": "LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2" + }, + { + "path": "LICENSE-MIT", + "sha256": "5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab" + } + ] + }, { "name": "crypto-bigint", "version": "0.5.5", @@ -1453,6 +1504,23 @@ } ] }, + { + "name": "fearless_simd", + "version": "0.4.1", + "checksum": "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693", + "license": "Apache-2.0 OR MIT", + "repository": "https://github.com/linebender/fearless_simd", + "notice_files": [ + { + "path": "LICENSE-APACHE", + "sha256": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9" + }, + { + "path": "LICENSE-MIT", + "sha256": "23c23145f6eac25c28cdcc03d38506a5712a9a8cffb5bd5301457f6173ea696e" + } + ] + }, { "name": "ff", "version": "0.13.1", @@ -2293,6 +2361,200 @@ } ] }, + { + "name": "j2k", + "version": "0.8.1", + "checksum": "0371832fbd6d4e7c83ab85545bb41474f91f565dd1e5660b733de11b54a257eb", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/frames-sg/j2k", + "notice_files": [ + { + "path": "generated-package-license-notice.txt", + "sha256": "d21f134a7fcc8164e65f3f31ede9ab6c3bba8d1621845e4a5e92a7693d7bd73e" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-MIT", + "sha256": "c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe" + }, + { + "path": "repository-license/j2k-native-0.8.1/assets/LICENSE.txt", + "sha256": "286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenHTJ2K", + "sha256": "5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenJPH", + "sha256": "5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98" + } + ] + }, + { + "name": "j2k-codec-math", + "version": "0.8.1", + "checksum": "165366ed0d18b737c6f6cd2c3ffeb20c94dc40aac70e77c2f6dd1c8b518aec3e", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/frames-sg/j2k", + "notice_files": [ + { + "path": "generated-package-license-notice.txt", + "sha256": "5e8685f009d9fe56954cd78f1cd99dbedcebc527a65b17c4ab7f52ef00ab0d31" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-MIT", + "sha256": "c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe" + }, + { + "path": "repository-license/j2k-native-0.8.1/assets/LICENSE.txt", + "sha256": "286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenHTJ2K", + "sha256": "5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenJPH", + "sha256": "5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98" + } + ] + }, + { + "name": "j2k-core", + "version": "0.8.1", + "checksum": "058401c1957538158b1565d0b9453f8165ca0fc62e85197849364971d4be551a", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/frames-sg/j2k", + "notice_files": [ + { + "path": "generated-package-license-notice.txt", + "sha256": "62c91c3bde6a45ab5071c69a6f03db3d027119ddb192787d98a31e5cb42617e0" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-MIT", + "sha256": "c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe" + }, + { + "path": "repository-license/j2k-native-0.8.1/assets/LICENSE.txt", + "sha256": "286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenHTJ2K", + "sha256": "5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenJPH", + "sha256": "5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98" + } + ] + }, + { + "name": "j2k-native", + "version": "0.8.1", + "checksum": "7c8af5bf0762fdf381b2893a56a365c9a5916ec665716c981140afdbd0e9cb76", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/frames-sg/j2k", + "notice_files": [ + { + "path": "LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594" + }, + { + "path": "LICENSE-MIT", + "sha256": "c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe" + }, + { + "path": "assets/LICENSE.txt", + "sha256": "286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213" + }, + { + "path": "fixtures/htj2k/LICENSE.OpenHTJ2K", + "sha256": "5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63" + }, + { + "path": "fixtures/htj2k/LICENSE.OpenJPH", + "sha256": "5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98" + } + ] + }, + { + "name": "j2k-profile", + "version": "0.8.1", + "checksum": "be460f47625754ece6c087e869d80b119775d3b76b107ffaf2769b8ca5c1caca", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/frames-sg/j2k", + "notice_files": [ + { + "path": "generated-package-license-notice.txt", + "sha256": "72c509b36ef706c6d202cbfdb3fc0ab279b9d66e4b4f6df331048395ee4c75ef" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-MIT", + "sha256": "c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe" + }, + { + "path": "repository-license/j2k-native-0.8.1/assets/LICENSE.txt", + "sha256": "286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenHTJ2K", + "sha256": "5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenJPH", + "sha256": "5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98" + } + ] + }, + { + "name": "j2k-types", + "version": "0.8.1", + "checksum": "dae71c13c5e4c230bbc5dcf338faa3e5e45cc2906acef762e613b33110e9423d", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/frames-sg/j2k", + "notice_files": [ + { + "path": "generated-package-license-notice.txt", + "sha256": "113b9e80f5fbda8d6290f1bee47b019b54f0da3d43a609e18efc886e8e4c3129" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594" + }, + { + "path": "repository-license/j2k-native-0.8.1/LICENSE-MIT", + "sha256": "c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe" + }, + { + "path": "repository-license/j2k-native-0.8.1/assets/LICENSE.txt", + "sha256": "286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenHTJ2K", + "sha256": "5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63" + }, + { + "path": "repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenJPH", + "sha256": "5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98" + } + ] + }, { "name": "jni", "version": "0.22.4", @@ -2455,6 +2717,19 @@ } ] }, + { + "name": "libm", + "version": "0.2.16", + "checksum": "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981", + "license": "MIT", + "repository": "https://github.com/rust-lang/compiler-builtins", + "notice_files": [ + { + "path": "LICENSE.txt", + "sha256": "3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7" + } + ] + }, { "name": "linux-raw-sys", "version": "0.12.1", @@ -2679,6 +2954,23 @@ } ] }, + { + "name": "moxcms", + "version": "0.8.1", + "checksum": "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b", + "license": "BSD-3-Clause OR Apache-2.0", + "repository": "https://github.com/awxkee/moxcms.git", + "notice_files": [ + { + "path": "LICENSE-APACHE.md", + "sha256": "90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5" + }, + { + "path": "LICENSE.md", + "sha256": "4b6c5ac5c550bea22b1d6455684b21d94ca96f3bcbbc2840b793180707ed582a" + } + ] + }, { "name": "ndk", "version": "0.9.0", @@ -3555,6 +3847,23 @@ } ] }, + { + "name": "pxfm", + "version": "0.1.30", + "checksum": "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea", + "license": "BSD-3-Clause OR Apache-2.0", + "repository": "https://github.com/awxkee/pxfm", + "notice_files": [ + { + "path": "LICENSE-APACHE.md", + "sha256": "90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5" + }, + { + "path": "LICENSE.md", + "sha256": "4b6c5ac5c550bea22b1d6455684b21d94ca96f3bcbbc2840b793180707ed582a" + } + ] + }, { "name": "quinn", "version": "0.11.11", @@ -3796,6 +4105,40 @@ } ] }, + { + "name": "rayon", + "version": "1.12.0", + "checksum": "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/rayon-rs/rayon", + "notice_files": [ + { + "path": "LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2" + }, + { + "path": "LICENSE-MIT", + "sha256": "0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720" + } + ] + }, + { + "name": "rayon-core", + "version": "1.13.0", + "checksum": "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91", + "license": "MIT OR Apache-2.0", + "repository": "https://github.com/rayon-rs/rayon", + "notice_files": [ + { + "path": "LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2" + }, + { + "path": "LICENSE-MIT", + "sha256": "0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720" + } + ] + }, { "name": "rcgen", "version": "0.14.9", diff --git a/release/DISTRIBUTION-MANIFEST.json b/release/DISTRIBUTION-MANIFEST.json index 2b2bc77..992237a 100644 --- a/release/DISTRIBUTION-MANIFEST.json +++ b/release/DISTRIBUTION-MANIFEST.json @@ -1,6 +1,6 @@ { "schema": 1, - "cargo_lock_sha256": "4753d29e32012fcf82584ea9a5d73037ccf3e63114ad2d1b9295c41808862ddd", + "cargo_lock_sha256": "68988caa2c014bf2c67563df3dd0a8348d67eeb06559649798463fd8211fc0cb", "provenance_policy_sha256": "f95475a2284ab282e9432865d4a7ed103498a56ed5ec2393017bc312c77da890", "upstream_repository": "https://github.com/cinderblocks/libremetaverse", "upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba", @@ -42,8 +42,8 @@ }, { "path": "Cargo.lock", - "bytes": 100424, - "sha256": "4753d29e32012fcf82584ea9a5d73037ccf3e63114ad2d1b9295c41808862ddd" + "bytes": 103842, + "sha256": "68988caa2c014bf2c67563df3dd0a8348d67eeb06559649798463fd8211fc0cb" }, { "path": "Cargo.toml", @@ -62,13 +62,13 @@ }, { "path": "README.md", - "bytes": 35156, - "sha256": "d9e75ef87bbd9ab6dc87c1222b3eebf601cc0e088be0e8f2ccae912336a0a298" + "bytes": 35953, + "sha256": "dea4a52f12e5885e2c53356c56b122c9bbf00382cbff21ff179e05b1d8714f05" }, { "path": "RUSTREWRITE.md", - "bytes": 54108, - "sha256": "6da24d9e0c2ca89122b4daa494376097acd90820efa5668654f4742bf484f965" + "bytes": 54472, + "sha256": "b3bc471cb82e76cca0c3d251090634de3ed29e24ef91b01d3cc89723e31854be" }, { "path": "api/API-COVERAGE.md", @@ -102,13 +102,13 @@ }, { "path": "api/SEMVER-AUDIT.md", - "bytes": 5538, - "sha256": "f7b5e98facc572792d3a150915f220be978de637fb6b210d4bb9aa104dc3c47d" + "bytes": 5558, + "sha256": "3c44b4b0ea579d975492fd5f9395a29fc597e43b126f8cbae4a0c14792232dd0" }, { "path": "api/SEMVER-BASELINE.json", - "bytes": 5771, - "sha256": "7170e85dbab28a70ffcd392a0482ead23a1d7d2742fa1736a6f27a54148bc6d9" + "bytes": 5901, + "sha256": "cd422369af58c6f60dbdda202ae4bb331f68ca3aacb5171839512100c027432e" }, { "path": "api/SHIM-COVERAGE.md", @@ -212,8 +212,8 @@ }, { "path": "ci/ci-coverage.json", - "bytes": 5480, - "sha256": "4d1633c1cb396f934a8eff5f4e27362f6872b95ac66b95e9c4ac8e75ef833b29" + "bytes": 5593, + "sha256": "241e7bc687b1c2395885e4cd2a95c446e5d4847e3bf9a34d8cee3d76bcbc2903" }, { "path": "ci/concurrency-thresholds.json", @@ -222,8 +222,8 @@ }, { "path": "ci/dependency-policy.json", - "bytes": 10628, - "sha256": "2807ccae43c9f0cf011978e5d63217aa3ac827406b52db2b04945ce4064f988e" + "bytes": 10851, + "sha256": "781d497a7a0ddd53fb9158e74e1c5f967eaf3b97535596b42bf34fa38e59b482" }, { "path": "ci/evidence/api-audit.json", @@ -267,8 +267,8 @@ }, { "path": "ci/release-matrix.json", - "bytes": 7898, - "sha256": "4ba7d1dfb7ed7a8bb06d5702179af569ff52c2db33d10b0008389d2c13a40d93" + "bytes": 8560, + "sha256": "f6a641f66c023cd00d5d8b8692b5d6f4a8273fee8e8b5411cab4481de1b0f602" }, { "path": "codegen/README.md", @@ -362,8 +362,8 @@ }, { "path": "crates/libremetaverse-imaging/Cargo.toml", - "bytes": 644, - "sha256": "b28b70a09f4916624dfb1d54869a4084996615304b7e04449d331ccd067ff20b" + "bytes": 713, + "sha256": "b05533b2b63f1dc7c0228488b649dc5ee74e50b27a5260ebaf3c2cfa5b93f3f6" }, { "path": "crates/libremetaverse-imaging/benches/image_pipeline.rs", @@ -382,13 +382,13 @@ }, { "path": "crates/libremetaverse-imaging/src/jpeg2000.rs", - "bytes": 25288, - "sha256": "9c6458dd1312288fc639361b6d7b4c09da268ed9d68f74ec8df5299955cd3579" + "bytes": 66076, + "sha256": "59569f3c7324df38b48edbb7bae7ab29fb2ff5cc7094c03a30cff9abd2c50e8f" }, { "path": "crates/libremetaverse-imaging/src/lib.rs", - "bytes": 720, - "sha256": "1d90f7cecf5320914682e44109ab61d916ac617c6f6140c38a9dc57b8eb0d34b" + "bytes": 940, + "sha256": "b052dd1aab4a12f7eeb338473082819f5e453b0b127311d0ff6cb38e5da10111" }, { "path": "crates/libremetaverse-imaging/src/managed_image.rs", @@ -887,13 +887,13 @@ }, { "path": "crates/libremetaverse/Cargo.toml", - "bytes": 1403, - "sha256": "c1fa65e64affcf6d3422d31f2fdc13e5d67af7c84757e5abcda29c1927fd9131" + "bytes": 1450, + "sha256": "858687a6afd8d990eec7f7984d06ffde084971122c9106e8cba6e54fdf531330" }, { "path": "crates/libremetaverse/README.md", - "bytes": 12959, - "sha256": "22dc9242d755d6b81214081dc534c02bf84380c16d5f873729033dd04ec1d16c" + "bytes": 13059, + "sha256": "871b0b94073aafa46235bba7d150c13d4c4900da5e4e2d82201604365d2c5c40" }, { "path": "crates/libremetaverse/assets/avatar_lad.xml", @@ -947,8 +947,8 @@ }, { "path": "crates/libremetaverse/src/appearance_baker.rs", - "bytes": 22271, - "sha256": "6a6274c14114ce189df1057b1b2b49d686000dc2830f3f30912f5d56b12ff1dc" + "bytes": 22298, + "sha256": "ff49dc968d029d66965064303f65886be623c491a7bce74a39ecd5a4e4cc17c4" }, { "path": "crates/libremetaverse/src/appearance_baker_semantics.rs", @@ -987,13 +987,13 @@ }, { "path": "crates/libremetaverse/src/asset_models.rs", - "bytes": 28293, - "sha256": "38b127db078d6aad5279193bea45ca45681669633f1a614962585f162d1b79b1" + "bytes": 28612, + "sha256": "52f7df531c6e9c5d3fa21ee39e6ed258f7041a890e723cf7e534665ec9b5341a" }, { "path": "crates/libremetaverse/src/asset_pipeline_semantics.rs", - "bytes": 5191, - "sha256": "07318170717c3399707f8bf5c9fb2f90ce73b4da5278c9895637ca35dea0fb5a" + "bytes": 5272, + "sha256": "ea876f35d0c03abe85b1f2086312bbd5f4aa55c742dcc3ff136df21a376d911e" }, { "path": "crates/libremetaverse/src/asset_prim.rs", @@ -1262,8 +1262,8 @@ }, { "path": "crates/libremetaverse/src/model_workflow.rs", - "bytes": 42237, - "sha256": "2a00452d3c610b87aaae45731643d7a6ef0ab6bf5a384879c6c0cd87c21b748f" + "bytes": 42482, + "sha256": "363d4a07086130dceeb3bba70e6376f0541b3cc01bb251167d265430a0403408" }, { "path": "crates/libremetaverse/src/name_value.rs", @@ -1567,8 +1567,8 @@ }, { "path": "docs/dependency-policy.md", - "bytes": 4488, - "sha256": "2e822f2deefa949a4d2173ee6d993a7e3c5874c01cb4b90755d98d1b8805f3e8" + "bytes": 4583, + "sha256": "ec62e39b40f6070fb746f76f7ae934570de01caa4426d453e2db5c29ac3fa1ae" }, { "path": "docs/discovery.md", @@ -1582,8 +1582,8 @@ }, { "path": "docs/imaging-meshing.md", - "bytes": 3406, - "sha256": "51ae2dfb88f5a40a01ab9394ae0545806377c6cbb2cbd85da2b7947b5534859e" + "bytes": 3678, + "sha256": "c63e434d5181dc903c83f00c7649ee2040028c6d4bda698f57da8c1c3b1416c6" }, { "path": "docs/inventory.md", @@ -1612,8 +1612,8 @@ }, { "path": "docs/release-candidate.md", - "bytes": 4243, - "sha256": "ebddf2e4922ed7e8cea262501ede951b35b50a35ea19e17eaa317ae2af50199d" + "bytes": 4327, + "sha256": "20aa8c39de842ca1dee15808c44f1e71ee33441796b17d03feea8e14ef99165f" }, { "path": "docs/release-ci-matrix.md", @@ -1627,8 +1627,8 @@ }, { "path": "docs/rust-api-guide.md", - "bytes": 11958, - "sha256": "2ffcd84234a996ff97480dabda64fc815eb88d4b5fdf42e0836734d7277b046f" + "bytes": 12058, + "sha256": "db4d0ee7986eebe9434e5c51ca65a31ffc3b32323240d9dc62cbd931b21b21b1" }, { "path": "docs/social.md", @@ -2407,8 +2407,8 @@ }, { "path": "tools/check_milestone_06.py", - "bytes": 5598, - "sha256": "18f389b59020a6705595cfa18e6c374e09e4d81c8c3b1e397aa01da13c885e33" + "bytes": 5740, + "sha256": "a206b123b4202e81ef832c31bed10a1117e030c9927638727cc99ca7d5f5914d" }, { "path": "tools/check_milestone_08.py", @@ -2517,8 +2517,8 @@ }, { "path": "tools/ci-matrix/src/api_surface.rs", - "bytes": 48385, - "sha256": "b4a873a869a4d2af11ecda0c95e11f83e044e6002271a30b4fa424103ceacbc7" + "bytes": 48586, + "sha256": "a82ac15da446f2f393ba267f8c7537d43cb4595a6be3e08f27d2f718e2aad4e8" }, { "path": "tools/ci-matrix/src/artifact.rs", @@ -2527,8 +2527,8 @@ }, { "path": "tools/ci-matrix/src/ci_gate.rs", - "bytes": 45047, - "sha256": "73ebb225ef1128e82bd38e7d41d2a164e4bd82c89cbe241f547762f132c0c790" + "bytes": 45662, + "sha256": "5a3666a3c84993fb3c3ac8d920aed4ea6ecdd2bfa06b6c94ab84942ba7a33db5" }, { "path": "tools/ci-matrix/src/dependency.rs", @@ -2542,8 +2542,8 @@ }, { "path": "tools/ci-matrix/src/lib.rs", - "bytes": 21175, - "sha256": "096d055e5d295be43ae1db78acbc98557d27d63631c7699b6eb5d281fcc18424" + "bytes": 21271, + "sha256": "53f4286d600c78d7a3ea593e7d663854e722787573dc5bbf3e48d41e28ac8267" }, { "path": "tools/ci-matrix/src/main.rs", @@ -2664,13 +2664,13 @@ "generated_release_files": [ { "path": "release/DEPENDENCY-LICENSES.json", - "bytes": 217171, - "sha256": "a4095621c7975f6b01c5b1653b375c571be15657f7fa824d6b5a2e46f3434dcc" + "bytes": 230084, + "sha256": "64ee30790d4b3b643514098184bf15b2e9901fe177333dbd7950b4311a47292e" }, { "path": "release/THIRD-PARTY-NOTICES.md", - "bytes": 846570, - "sha256": "da051a965d39715d10da5c17fdfdafd8059b83d2491837d223607e9c5e16a5fa" + "bytes": 897381, + "sha256": "47d10a98d94bfe09124cbbfcf57f91a34231286aaec71a3265372ee9006d3eab" }, { "path": "release/NATIVE-LICENSES.md", diff --git a/release/THIRD-PARTY-NOTICES.md b/release/THIRD-PARTY-NOTICES.md index 987c3d1..7f6eff7 100644 --- a/release/THIRD-PARTY-NOTICES.md +++ b/release/THIRD-PARTY-NOTICES.md @@ -71,6 +71,9 @@ This document must accompany source and binary distributions. A package's presen | `crc 3.4.0` | `MIT OR Apache-2.0` | `5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d` | `470355a7eed93fcc4281ec2e0f82ca3b94e7af1e4d83629f91de8cfac34d750e`
`3488679340a49ecc34d342c4009d2dabf76f4a21f12aec2ca99b15805d656544` | | `crc-catalog 2.5.0` | `MIT OR Apache-2.0` | `217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853` | `f73ade7543b1def03053a62f9c1d187110568eed1426829448f324e477ce3e53` | | `crc32fast 1.5.0` | `MIT OR Apache-2.0` | `9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511` | `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
`61d383b05b87d78f94d2937e2580cce47226d17823c0430fbcad09596537efcf` | +| `crossbeam-deque 0.8.7` | `MIT OR Apache-2.0` | `5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab` | +| `crossbeam-epoch 0.9.20` | `MIT OR Apache-2.0` | `2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab` | +| `crossbeam-utils 0.8.22` | `MIT OR Apache-2.0` | `61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab` | | `crypto-bigint 0.5.5` | `Apache-2.0 OR MIT` | `0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76` | `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
`90c503b61dee04e1449c323ec34c229dfb68d7adcb96c7e140ee55f70fce2d8e` | | `crypto-common 0.1.7` | `MIT OR Apache-2.0` | `78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a` | `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
`3521672491a3479422d5fe1aca6645dd2984090f85da6e5205abfb18fb7a6897` | | `crypto-common 0.2.2` | `MIT OR Apache-2.0` | `ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453` | `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
`d2e7ec5355c96eeade56b09187ceb48a6a30299da3ce7531a66d3d11405ab963` | @@ -95,6 +98,7 @@ This document must accompany source and binary distributions. A package's presen | `equivalent 1.0.2` | `Apache-2.0 OR MIT` | `877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349` | | `errno 0.3.14` | `MIT OR Apache-2.0` | `39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`8764a597675778ddfd4e25f81b08a05dbcf089ac05662df7613fe67f150e3aa2` | | `fastrand 2.5.0` | `Apache-2.0 OR MIT` | `da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `fearless_simd 0.4.1` | `Apache-2.0 OR MIT` | `b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693` | `a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9`
`23c23145f6eac25c28cdcc03d38506a5712a9a8cffb5bd5301457f6173ea696e` | | `ff 0.13.1` | `MIT/Apache-2.0` | `c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393` | `3708458dee7f359ac6c9c5558023ed481be87e5372c43ebd7f2ca7ad23c12026`
`8d0c1d1d4b2bebf5b9211f0e883d5a482b7808f3ed31320da14ddee95811f4ef` | | `fiat-crypto 0.2.9` | `MIT OR Apache-2.0 OR BSD-1-Clause` | `28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d` | `6c935d087a2992d0f092665400a2fa99c98db58d96fbe29368f8f48182bed9aa`
`9eacbcb81be660840c714a560a9d65ba07913db98dd4baf969f78dd499fdd60f`
`0c1240e29b4a2c528bcc3ccc38a98b1dba5e3e6b465da910208935f082e2ced6`
`0034712d5e97a4f5b900a160a5c19d582b740d8e0638b4e44fd07e7b492dcd13` | | `filetime 0.2.29` | `MIT/Apache-2.0` | `5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397` | @@ -147,6 +151,12 @@ This document must accompany source and binary distributions. A package's presen | `is_terminal_polyfill 1.70.2` | `MIT OR Apache-2.0` | `a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695` | `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
`6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6` | | `itertools 0.13.0` | `MIT OR Apache-2.0` | `413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545` | | `itoa 1.0.18` | `MIT OR Apache-2.0` | `8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682` | `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
`23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `j2k 0.8.1` | `MIT OR Apache-2.0` | `0371832fbd6d4e7c83ab85545bb41474f91f565dd1e5660b733de11b54a257eb` | `d21f134a7fcc8164e65f3f31ede9ab6c3bba8d1621845e4a5e92a7693d7bd73e`
`0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
`c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe`
`286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213`
`5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63`
`5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98` | +| `j2k-codec-math 0.8.1` | `MIT OR Apache-2.0` | `165366ed0d18b737c6f6cd2c3ffeb20c94dc40aac70e77c2f6dd1c8b518aec3e` | `5e8685f009d9fe56954cd78f1cd99dbedcebc527a65b17c4ab7f52ef00ab0d31`
`0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
`c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe`
`286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213`
`5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63`
`5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98` | +| `j2k-core 0.8.1` | `MIT OR Apache-2.0` | `058401c1957538158b1565d0b9453f8165ca0fc62e85197849364971d4be551a` | `62c91c3bde6a45ab5071c69a6f03db3d027119ddb192787d98a31e5cb42617e0`
`0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
`c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe`
`286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213`
`5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63`
`5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98` | +| `j2k-native 0.8.1` | `MIT OR Apache-2.0` | `7c8af5bf0762fdf381b2893a56a365c9a5916ec665716c981140afdbd0e9cb76` | `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
`c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe`
`286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213`
`5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63`
`5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98` | +| `j2k-profile 0.8.1` | `MIT OR Apache-2.0` | `be460f47625754ece6c087e869d80b119775d3b76b107ffaf2769b8ca5c1caca` | `72c509b36ef706c6d202cbfdb3fc0ab279b9d66e4b4f6df331048395ee4c75ef`
`0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
`c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe`
`286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213`
`5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63`
`5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98` | +| `j2k-types 0.8.1` | `MIT OR Apache-2.0` | `dae71c13c5e4c230bbc5dcf338faa3e5e45cc2906acef762e613b33110e9423d` | `113b9e80f5fbda8d6290f1bee47b019b54f0da3d43a609e18efc886e8e4c3129`
`0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
`c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe`
`286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213`
`5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63`
`5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98` | | `jni 0.22.4` | `MIT OR Apache-2.0` | `5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498` | `7270e1bd9891568c2c9eb4707e1662f29ec7337147c2f4410e161d8f67a62412` | | `jni-macros 0.22.4` | `MIT OR Apache-2.0` | `a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3` | `180eec991459940405704345ffbdd7b3c0f1e1951b8e73bc6357259b02bbc4d4` | | `jni-sys 0.3.1` | `MIT OR Apache-2.0` | `41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258` | `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
`1d85bd754b04ceec93e98e890edd1fa3c6a22e81bcb32135806beeccefa51cd1` | @@ -157,6 +167,7 @@ This document must accompany source and binary distributions. A package's presen | `lazy_static 1.5.0` | `MIT OR Apache-2.0` | `bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720` | | `libc 0.2.189` | `MIT OR Apache-2.0` | `3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2` | `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
`123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e` | | `libloading 0.8.9` | `ISC` | `d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55` | `b29f8b01452350c20dd1af16ef83b598fea3053578ccc1c7a0ef40e57be2620f` | +| `libm 0.2.16` | `MIT` | `b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981` | `3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7` | | `linux-raw-sys 0.12.1` | `Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT` | `32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53` | `3290ae0fbc9ddb77d2239121d710f0bb9d31b3b4744e6d97fe01e652b4c1870b`
`a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5`
`23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | | `litemap 0.8.2` | `Unicode-3.0` | `92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0` | `f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2` | | `log 0.4.33` | `MIT OR Apache-2.0` | `0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | @@ -169,6 +180,7 @@ This document must accompany source and binary distributions. A package's presen | `minimal-lexical 0.2.1` | `MIT/Apache-2.0` | `68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a` | `8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90`
`23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3`
`dbe1fff0fb1314b6af94f161511406275cf01c5a32441fbf24528a57a051d599` | | `miniz_oxide 0.8.9` | `MIT OR Zlib OR Apache-2.0` | `1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316` | `4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b`
`0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
`799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d`
`0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39` | | `mio 1.2.2` | `MIT` | `30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427` | `07919255c7e04793d8ea760d6c2ce32d19f9ff02bdbdde3ce90b1e1880929a9b` | +| `moxcms 0.8.1` | `BSD-3-Clause OR Apache-2.0` | `bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b` | `90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5`
`4b6c5ac5c550bea22b1d6455684b21d94ca96f3bcbbc2840b793180707ed582a` | | `ndk 0.9.0` | `MIT OR Apache-2.0` | `c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4` | `1045ccac6de004bc43d922d1b7d4324f4f90c5ae006af8b318616e347bcd2be0` | | `ndk-context 0.1.1` | `MIT OR Apache-2.0` | `27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b` | `0671f66ca6d381f4794b036bdcf52664ff1d34ea8a7a8df620de6028bf5a9eaa` | | `ndk-sys 0.6.0+11769913` | `MIT OR Apache-2.0` | `ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873` | `e793fe782da312560decac8e8d8d98e1a62cf0d2dcdbd4c72ae4e3aba0b0f54e` | @@ -225,6 +237,7 @@ This document must accompany source and binary distributions. A package's presen | `primeorder 0.13.6` | `Apache-2.0 OR MIT` | `353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6` | `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
`233b95ccbf90dc67e32f3e8995c489f6312d9191ebd141a931c3b684f1e3be6d` | | `proc-macro-crate 3.5.0` | `MIT OR Apache-2.0` | `e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f` | `8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb`
`23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | | `proc-macro2 1.0.107` | `MIT OR Apache-2.0` | `985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9` | `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
`23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `pxfm 0.1.30` | `BSD-3-Clause OR Apache-2.0` | `d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea` | `90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5`
`4b6c5ac5c550bea22b1d6455684b21d94ca96f3bcbbc2840b793180707ed582a` | | `quinn 0.11.11` | `MIT OR Apache-2.0` | `0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8` | `c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4`
`4b2d0aca6789fa39e03d6738e869ea0988cceba210ca34ebb59c15c463e93a04` | | `quinn-proto 0.11.16` | `MIT OR Apache-2.0` | `2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560` | `c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4`
`4b2d0aca6789fa39e03d6738e869ea0988cceba210ca34ebb59c15c463e93a04` | | `quinn-udp 0.5.15` | `MIT OR Apache-2.0` | `35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694` | `c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4`
`4b2d0aca6789fa39e03d6738e869ea0988cceba210ca34ebb59c15c463e93a04` | @@ -238,6 +251,8 @@ This document must accompany source and binary distributions. A package's presen | `rand_core 0.6.4` | `MIT OR Apache-2.0` | `ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c` | `90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5`
`6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51`
`209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b` | | `rand_core 0.9.5` | `MIT OR Apache-2.0` | `76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c` | `90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5`
`6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51`
`209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b` | | `rand_pcg 0.10.2` | `MIT OR Apache-2.0` | `caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a` | `90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5`
`6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51`
`2234e3cefee876aeb686ad89e978bdb07bf118a1186ab1cf161bcdf69d4b4f57` | +| `rayon 1.12.0` | `MIT OR Apache-2.0` | `fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720` | +| `rayon-core 1.13.0` | `MIT OR Apache-2.0` | `22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720` | | `rcgen 0.14.9` | `MIT OR Apache-2.0` | `091e7a8e7d86e6feb87a27ce8e2cba29d49eff9507afeebefab7eeb2ca667fb4` | `debe6fed7ac143217c7f533759c54b14f18cd01d5fe195c51d83c2ae2d136cb4` | | `regex 1.13.1` | `MIT OR Apache-2.0` | `f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | | `regex-automata 0.4.18` | `MIT OR Apache-2.0` | `ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2` | `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
`6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | @@ -1053,7 +1068,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### `0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720` -Packages: lazy_static 1.5.0
+Packages: lazy_static 1.5.0, rayon 1.12.0, rayon-core 1.13.0
Source filenames: LICENSE-MIT
@@ -1418,8 +1433,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ### `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
 
-Packages: alsa 0.11.0, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, simdutf8 0.1.5, sync_wrapper 1.0.2, time 0.3.55, time-core 0.1.9, time-macros 0.2.32
-Source filenames: LICENSE, LICENSE-APACHE, LICENSE-APACHE.md, LICENSE-Apache +Packages: alsa 0.11.0, j2k 0.8.1, j2k-codec-math 0.8.1, j2k-core 0.8.1, j2k-native 0.8.1, j2k-profile 0.8.1, j2k-types 0.8.1, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, simdutf8 0.1.5, sync_wrapper 1.0.2, time 0.3.55, time-core 0.1.9, time-macros 0.2.32
+Source filenames: LICENSE, LICENSE-APACHE, LICENSE-APACHE.md, LICENSE-Apache, repository-license/j2k-native-0.8.1/LICENSE-APACHE
 
@@ -1680,6 +1695,24 @@ The published crate archive contains no discoverable top-level license/notice fi
 
 
+### `113b9e80f5fbda8d6290f1bee47b019b54f0da3d43a609e18efc886e8e4c3129` + +Packages: j2k-types 0.8.1
+Source filenames: generated-package-license-notice.txt + +
+Package: j2k-types 0.8.1
+Declared license: MIT OR Apache-2.0
+Authors/copyright attribution from package metadata: not supplied in Cargo package metadata
+Source: https://github.com/frames-sg/j2k
+SPDX license texts:
+https://spdx.org/licenses/Apache-2.0.html
+https://spdx.org/licenses/MIT.html
+
+The published crate archive contains no discoverable top-level license/notice file. This generated notice preserves all attribution and license metadata supplied by that archive instead of silently omitting the package.
+
+
+ ### `123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e` Packages: libc 0.2.189
@@ -2503,6 +2536,40 @@ DEALINGS IN THE SOFTWARE.
+### `23c23145f6eac25c28cdcc03d38506a5712a9a8cffb5bd5301457f6173ea696e` + +Packages: fearless_simd 0.4.1
+Source filenames: LICENSE-MIT + +
+Copyright (c) 2018 Raph Levien
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
+ ### `23d608079a47693a26dfc8a5cb01894ad0136a50714d46221d9380aa4a4b4423` Packages: block2 0.6.2, objc2 0.6.4, objc2-encode 4.1.0, objc2-foundation 0.3.2
@@ -3407,6 +3474,131 @@ DEALINGS IN THE SOFTWARE. +### `286e4fd7b447330b2c88e23890e3cd0a9d38cb398d4a59cb247f578ccbda3213` + +Packages: j2k 0.8.1, j2k-codec-math 0.8.1, j2k-core 0.8.1, j2k-native 0.8.1, j2k-profile 0.8.1, j2k-types 0.8.1
+Source filenames: assets/LICENSE.txt, repository-license/j2k-native-0.8.1/assets/LICENSE.txt + +
+CC0 1.0 Universal
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific
+works ("Commons") that the public can reliably and without fear of later
+claims of infringement build upon, modify, incorporate in other works, reuse
+and redistribute as freely as possible in any form whatsoever and for any
+purposes, including without limitation commercial purposes. These owners may
+contribute to the Commons to promote the ideal of a free culture and the
+further production of creative, cultural and scientific works, or to gain
+reputation or greater distribution for their Work in part through the use and
+efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation
+of additional consideration or compensation, the person associating CC0 with a
+Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
+and publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not limited
+to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display, communicate,
+  and translate a Work;
+
+  ii. moral rights retained by the original author(s) and/or performer(s);
+
+  iii. publicity and privacy rights pertaining to a person's image or likeness
+  depicted in a Work;
+
+  iv. rights protecting against unfair competition in regards to a Work,
+  subject to the limitations in paragraph 4(a), below;
+
+  v. rights protecting the extraction, dissemination, use and reuse of data in
+  a Work;
+
+  vi. database rights (such as those arising under Directive 96/9/EC of the
+  European Parliament and of the Council of 11 March 1996 on the legal
+  protection of databases, and under any national implementation thereof,
+  including any amended or successor version of such directive); and
+
+  vii. other similar, equivalent or corresponding rights throughout the world
+  based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of,
+applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
+and Related Rights and associated claims and causes of action, whether now
+known or unknown (including existing as well as future claims and causes of
+action), in the Work (i) in all territories worldwide, (ii) for the maximum
+duration provided by applicable law or treaty (including future time
+extensions), (iii) in any current or future medium and for any number of
+copies, and (iv) for any purpose whatsoever, including without limitation
+commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
+the Waiver for the benefit of each member of the public at large and to the
+detriment of Affirmer's heirs and successors, fully intending that such Waiver
+shall not be subject to revocation, rescission, cancellation, termination, or
+any other legal or equitable action to disrupt the quiet enjoyment of the Work
+by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be
+judged legally invalid or ineffective under applicable law, then the Waiver
+shall be preserved to the maximum extent permitted taking into account
+Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
+is so judged Affirmer hereby grants to each affected person a royalty-free,
+non transferable, non sublicensable, non exclusive, irrevocable and
+unconditional license to exercise Affirmer's Copyright and Related Rights in
+the Work (i) in all territories worldwide, (ii) for the maximum duration
+provided by applicable law or treaty (including future time extensions), (iii)
+in any current or future medium and for any number of copies, and (iv) for any
+purpose whatsoever, including without limitation commercial, advertising or
+promotional purposes (the "License"). The License shall be deemed effective as
+of the date CC0 was applied by Affirmer to the Work. Should any part of the
+License for any reason be judged legally invalid or ineffective under
+applicable law, such partial invalidity or ineffectiveness shall not
+invalidate the remainder of the License, and in such case Affirmer hereby
+affirms that he or she will not (i) exercise any of his or her remaining
+Copyright and Related Rights in the Work or (ii) assert any associated claims
+and causes of action with respect to the Work, in either case contrary to
+Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+  a. No trademark or patent rights held by Affirmer are waived, abandoned,
+  surrendered, licensed or otherwise affected by this document.
+
+  b. Affirmer offers the Work as-is and makes no representations or warranties
+  of any kind concerning the Work, express, implied, statutory or otherwise,
+  including without limitation warranties of title, merchantability, fitness
+  for a particular purpose, non infringement, or the absence of latent or
+  other defects, accuracy, or the present or absence of errors, whether or not
+  discoverable, all to the greatest extent permissible under applicable law.
+
+  c. Affirmer disclaims responsibility for clearing rights of other persons
+  that may apply to the Work or any use thereof, including without limitation
+  any person's Copyright and Related Rights in the Work. Further, Affirmer
+  disclaims responsibility for obtaining any necessary consents, permissions
+  or other rights required for any use of the Work.
+
+  d. Affirmer understands and acknowledges that Creative Commons is not a
+  party to this document and has no duty or obligation with respect to this
+  CC0 or use of the Work.
+
+For more information, please see
+http://creativecommons.org/publicdomain/zero/1.0/
+
+
+ ### `29e9fe5074bd27e0e5d5d110394fbbcd841baee2651a3c4b4560a632702cede4` Packages: getrandom 0.3.4
@@ -4172,6 +4364,273 @@ DEALINGS IN THE SOFTWARE. +### `3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7` + +Packages: libm 0.2.16
+Source filenames: LICENSE.txt + +
+rust-lang/libm as a whole is available for use under the MIT license:
+
+------------------------------------------------------------------------------
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+
+As a contributor, you agree that your code can be used under either the MIT
+license or the Apache-2.0 license:
+
+------------------------------------------------------------------------------
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+------------------------------------------------------------------------------
+
+This Rust library contains the following copyrights:
+
+    Copyright (c) 2018 Jorge Aparicio
+
+Portions of this software are derived from third-party works licensed under
+terms compatible with the above MIT license:
+
+* musl libc https://www.musl-libc.org/. This library contains the following
+  copyright:
+
+      Copyright © 2005-2020 Rich Felker, et al.
+
+* The CORE-MATH project https://core-math.gitlabpages.inria.fr/. CORE-MATH
+  routines are available under the MIT license on a per-file basis.
+
+The musl libc COPYRIGHT file also includes the following notice relevant to
+math portions of the library:
+
+------------------------------------------------------------------------------
+Much of the math library code (src/math/* and src/complex/*) is
+Copyright © 1993,2004 Sun Microsystems or
+Copyright © 2003-2011 David Schultz or
+Copyright © 2003-2009 Steven G. Kargl or
+Copyright © 2003-2009 Bruce D. Evans or
+Copyright © 2008 Stephen L. Moshier or
+Copyright © 2017-2018 Arm Limited
+and labelled as such in comments in the individual source files. All
+have been licensed under extremely permissive terms.
+------------------------------------------------------------------------------
+
+Copyright notices are retained in src/* files where relevant.
+
+
+ ### `391a5396cec6230bfabd4ef4eb2350eb895bc5efce377a2218f5702ed020d3e3` Packages: httparse 1.10.1
@@ -5464,6 +5923,41 @@ The published crate archive contains no discoverable top-level license/notice fi +### `4b6c5ac5c550bea22b1d6455684b21d94ca96f3bcbbc2840b793180707ed582a` + +Packages: moxcms 0.8.1, pxfm 0.1.30
+Source filenames: LICENSE.md + +
+Copyright (c) Radzivon Bartoshyk. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1.  Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+2.  Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+3.  Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ ### `4cada0bd02ea3692eee6f16400d86c6508bbd3bafb2b65fed0419f36d4f83e8f` Packages: ppv-lite86 0.2.21
@@ -5922,6 +6416,44 @@ DEALINGS IN THE SOFTWARE. +### `5369097362538b0a5b348476248240ab713674fa2fa4bdde511906d949985b63` + +Packages: j2k 0.8.1, j2k-codec-math 0.8.1, j2k-core 0.8.1, j2k-native 0.8.1, j2k-profile 0.8.1, j2k-types 0.8.1
+Source filenames: fixtures/htj2k/LICENSE.OpenHTJ2K, repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenHTJ2K + +
+BSD 3-Clause License
+
+Copyright (c) 2019 - 2021, Osamu Watanabe
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ ### `54a744942eb3fa63a74d8d359989ed6962c04f3f10ff20cfa08ff8aa7efbe4e6` Packages: web-time 1.1.0
@@ -6154,6 +6686,42 @@ limitations under the License. +### `5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab` + +Packages: crossbeam-deque 0.8.7, crossbeam-epoch 0.9.20, crossbeam-utils 0.8.22
+Source filenames: LICENSE-MIT + +
+The MIT License (MIT)
+
+Copyright (c) 2019 The Crossbeam Project Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
+ ### `58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79` Packages: tracing-core 0.1.36
@@ -6278,6 +6846,42 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I +### `5ddf5177863dfc9ab65fa129d587db651241f00e21ed2427b218bea997591f98` + +Packages: j2k 0.8.1, j2k-codec-math 0.8.1, j2k-core 0.8.1, j2k-native 0.8.1, j2k-profile 0.8.1, j2k-types 0.8.1
+Source filenames: fixtures/htj2k/LICENSE.OpenJPH, repository-license/j2k-native-0.8.1/fixtures/htj2k/LICENSE.OpenJPH + +
+BSD 2-Clause License
+
+Copyright (c) 2019, Aous Naman
+Copyright (c) 2019, Kakadu Software Pty Ltd, Australia
+Copyright (c) 2019, The University of New South Wales, Sydney, Australia
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ ### `5de9719e1ca3406135681f64fdd6dff12e602a9ba3adea72cf8df46e1a2596a8` Packages: ogg_next_sys 0.1.5
@@ -6295,6 +6899,24 @@ The published crate archive contains no discoverable top-level license/notice fi +### `5e8685f009d9fe56954cd78f1cd99dbedcebc527a65b17c4ab7f52ef00ab0d31` + +Packages: j2k-codec-math 0.8.1
+Source filenames: generated-package-license-notice.txt + +
+Package: j2k-codec-math 0.8.1
+Declared license: MIT OR Apache-2.0
+Authors/copyright attribution from package metadata: not supplied in Cargo package metadata
+Source: https://github.com/frames-sg/j2k
+SPDX license texts:
+https://spdx.org/licenses/Apache-2.0.html
+https://spdx.org/licenses/MIT.html
+
+The published crate archive contains no discoverable top-level license/notice file. This generated notice preserves all attribution and license metadata supplied by that archive instead of silently omitting the package.
+
+
+ ### `615f39d3be6ff90d956c6b490b1f967a8995e88c109384895740748cb197fd87` Packages: objc2-core-graphics 0.3.2
@@ -6634,6 +7256,24 @@ END OF TERMS AND CONDITIONS +### `62c91c3bde6a45ab5071c69a6f03db3d027119ddb192787d98a31e5cb42617e0` + +Packages: j2k-core 0.8.1
+Source filenames: generated-package-license-notice.txt + +
+Package: j2k-core 0.8.1
+Declared license: MIT OR Apache-2.0
+Authors/copyright attribution from package metadata: not supplied in Cargo package metadata
+Source: https://github.com/frames-sg/j2k
+SPDX license texts:
+https://spdx.org/licenses/Apache-2.0.html
+https://spdx.org/licenses/MIT.html
+
+The published crate archive contains no discoverable top-level license/notice file. This generated notice preserves all attribution and license metadata supplied by that archive instead of silently omitting the package.
+
+
+ ### `63af4bea227c94d021e99427f6ca3a4b8efddadcca93ab6130f708cf6138cf68` Packages: ctr 0.9.2
@@ -7763,6 +8403,24 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI +### `72c509b36ef706c6d202cbfdb3fc0ab279b9d66e4b4f6df331048395ee4c75ef` + +Packages: j2k-profile 0.8.1
+Source filenames: generated-package-license-notice.txt + +
+Package: j2k-profile 0.8.1
+Declared license: MIT OR Apache-2.0
+Authors/copyright attribution from package metadata: not supplied in Cargo package metadata
+Source: https://github.com/frames-sg/j2k
+SPDX license texts:
+https://spdx.org/licenses/Apache-2.0.html
+https://spdx.org/licenses/MIT.html
+
+The published crate archive contains no discoverable top-level license/notice file. This generated notice preserves all attribution and license metadata supplied by that archive instead of silently omitting the package.
+
+
+ ### `7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349` Packages: equivalent 1.0.2
@@ -10133,6 +10791,216 @@ DEALINGS IN THE SOFTWARE. +### `90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5` + +Packages: moxcms 0.8.1, pxfm 0.1.30
+Source filenames: LICENSE-APACHE.md + +
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2024 Radzivon Bartoshyk
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+ ### `90c503b61dee04e1449c323ec34c229dfb68d7adcb96c7e140ee55f70fce2d8e` Packages: crypto-bigint 0.5.5, pem-rfc7468 0.7.0, x509-cert 0.2.5
@@ -10940,7 +11808,7 @@ DEALINGS IN THE SOFTWARE. ### `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2` -Packages: arrayvec 0.7.8, asn1-rs 0.7.2, asn1-rs-derive 0.6.0, asn1-rs-impl 0.2.0, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 2.13.1, bumpalo 3.20.3, cc 1.4.2, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, core-foundation 0.10.1, core-foundation-sys 0.8.7, coreaudio-rs 0.14.2, curve25519-dalek-derive 0.1.1, der-parser 10.0.0, displaydoc 0.2.7, either 1.17.0, equivalent 1.0.2, errno 0.3.14, fastrand 2.5.0, filetime 0.2.29, find-msvc-tools 0.1.10, flate2 1.1.9, form_urlencoded 1.2.2, glob 0.3.4, group 0.13.0, hashbrown 0.17.1, heck 0.5.0, httparse 1.10.1, hyper-rustls 0.27.9, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, is 0.11.0, itertools 0.13.0, jobserver 0.1.35, js-sys 0.3.104, lazy_static 1.5.0, linux-raw-sys 0.12.1, log 0.4.33, num-bigint 0.4.8, num-derive 0.4.2, num-integer 0.1.46, num-traits 0.2.19, oid-registry 0.8.1, once_cell 1.21.4, openssl-probe 0.2.1, percent-encoding 2.3.2, pkg-config 0.3.33, plain 0.2.3, regex 1.13.1, regex-automata 0.4.18, regex-syntax 0.8.11, roxmltree 0.21.1, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.43, rustls-native-certs 0.8.4, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, simd_cesu8 1.2.0, smallvec 1.15.2, socket2 0.6.5, stable_deref_trait 1.2.1, str0m 0.22.0, str0m-proto 0.7.0, str0m-rust-crypto 0.6.0, tar 0.4.46, url 2.5.8, uuid 1.24.0, version_check 0.9.5, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.127, wasm-bindgen-futures 0.4.77, wasm-bindgen-macro 0.2.127, wasm-bindgen-macro-support 0.2.127, wasm-bindgen-shared 0.2.127, web-sys 0.3.104, wit-bindgen 0.57.1, x509-parser 0.18.1, xattr 1.6.1, yasna 0.6.0
+Packages: arrayvec 0.7.8, asn1-rs 0.7.2, asn1-rs-derive 0.6.0, asn1-rs-impl 0.2.0, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 2.13.1, bumpalo 3.20.3, cc 1.4.2, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, core-foundation 0.10.1, core-foundation-sys 0.8.7, coreaudio-rs 0.14.2, crossbeam-deque 0.8.7, crossbeam-epoch 0.9.20, crossbeam-utils 0.8.22, curve25519-dalek-derive 0.1.1, der-parser 10.0.0, displaydoc 0.2.7, either 1.17.0, equivalent 1.0.2, errno 0.3.14, fastrand 2.5.0, filetime 0.2.29, find-msvc-tools 0.1.10, flate2 1.1.9, form_urlencoded 1.2.2, glob 0.3.4, group 0.13.0, hashbrown 0.17.1, heck 0.5.0, httparse 1.10.1, hyper-rustls 0.27.9, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, is 0.11.0, itertools 0.13.0, jobserver 0.1.35, js-sys 0.3.104, lazy_static 1.5.0, linux-raw-sys 0.12.1, log 0.4.33, num-bigint 0.4.8, num-derive 0.4.2, num-integer 0.1.46, num-traits 0.2.19, oid-registry 0.8.1, once_cell 1.21.4, openssl-probe 0.2.1, percent-encoding 2.3.2, pkg-config 0.3.33, plain 0.2.3, rayon 1.12.0, rayon-core 1.13.0, regex 1.13.1, regex-automata 0.4.18, regex-syntax 0.8.11, roxmltree 0.21.1, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.43, rustls-native-certs 0.8.4, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, simd_cesu8 1.2.0, smallvec 1.15.2, socket2 0.6.5, stable_deref_trait 1.2.1, str0m 0.22.0, str0m-proto 0.7.0, str0m-rust-crypto 0.6.0, tar 0.4.46, url 2.5.8, uuid 1.24.0, version_check 0.9.5, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.127, wasm-bindgen-futures 0.4.77, wasm-bindgen-macro 0.2.127, wasm-bindgen-macro-support 0.2.127, wasm-bindgen-shared 0.2.127, web-sys 0.3.104, wit-bindgen 0.57.1, x509-parser 0.18.1, xattr 1.6.1, yasna 0.6.0
Source filenames: LICENSE-APACHE, repository-license/asn1-rs-0.7.2/LICENSE-APACHE, repository-license/str0m-0.22.0/src/packet/LICENSE-APACHE.txt, src/packet/LICENSE-APACHE.txt
@@ -11179,7 +12047,7 @@ THE SOFTWARE.
 
 ### `a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9`
 
-Packages: widestring 1.2.1
+Packages: fearless_simd 0.4.1, widestring 1.2.1
Source filenames: LICENSE-APACHE
@@ -14027,6 +14895,22 @@ Source filenames: LICENSE-APACHE2.0
 
 
+### `c945f59ea69b7c9eff45a54d9ebd9294524291399393d7bf86800507e4af7bfe` + +Packages: j2k 0.8.1, j2k-codec-math 0.8.1, j2k-core 0.8.1, j2k-native 0.8.1, j2k-profile 0.8.1, j2k-types 0.8.1
+Source filenames: LICENSE-MIT, repository-license/j2k-native-0.8.1/LICENSE-MIT + +
+Copyright (c) The Hayro Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+ ### `c995204cc6bad2ed67dd41f7d89bb9f1a9d48e0edd745732b30640d7912089a4` Packages: spki 0.7.3
@@ -14668,6 +15552,24 @@ The published crate archive contains no discoverable top-level license/notice fi
+### `d21f134a7fcc8164e65f3f31ede9ab6c3bba8d1621845e4a5e92a7693d7bd73e` + +Packages: j2k 0.8.1
+Source filenames: generated-package-license-notice.txt + +
+Package: j2k 0.8.1
+Declared license: MIT OR Apache-2.0
+Authors/copyright attribution from package metadata: not supplied in Cargo package metadata
+Source: https://github.com/frames-sg/j2k
+SPDX license texts:
+https://spdx.org/licenses/Apache-2.0.html
+https://spdx.org/licenses/MIT.html
+
+The published crate archive contains no discoverable top-level license/notice file. This generated notice preserves all attribution and license metadata supplied by that archive instead of silently omitting the package.
+
+
+ ### `d27687b51f2874822c1530976b7e837eac4f308d94bf3dd42047011b7d437b47` Packages: elliptic-curve 0.13.8
diff --git a/tools/check_milestone_06.py b/tools/check_milestone_06.py index 5010ef2..0d8999f 100644 --- a/tools/check_milestone_06.py +++ b/tools/check_milestone_06.py @@ -44,6 +44,8 @@ def check_features() -> None: raise SystemExit("imaging default features must stay empty") if imaging["features"].get("jpeg2000") != ["dep:libremetaverse-openjpeg"]: raise SystemExit("JPEG 2000 must stay isolated behind its optional feature") + if imaging["features"].get("rust-j2k") != ["dep:j2k"]: + raise SystemExit("pure-Rust JPEG 2000 must stay isolated behind rust-j2k") if not imaging["dependencies"]["libremetaverse-openjpeg"].get("optional"): raise SystemExit("the OpenJPEG adapter dependency must stay optional") diff --git a/tools/ci-matrix/src/api_surface.rs b/tools/ci-matrix/src/api_surface.rs index cf74f91..c333b63 100644 --- a/tools/ci-matrix/src/api_surface.rs +++ b/tools/ci-matrix/src/api_surface.rs @@ -916,6 +916,10 @@ fn validate_features(name: &str, actual: &BTreeMap>) -> Resu "jpeg2000".to_owned(), vec!["libremetaverse-imaging/jpeg2000".to_owned()], ), + ( + "rust-j2k".to_owned(), + vec!["libremetaverse-imaging/rust-j2k".to_owned()], + ), ("vorbis".to_owned(), vec!["dep:vorbis_rs".to_owned()]), ]), "libremetaverse-imaging" => BTreeMap::from([ @@ -924,6 +928,7 @@ fn validate_features(name: &str, actual: &BTreeMap>) -> Resu "jpeg2000".to_owned(), vec!["dep:libremetaverse-openjpeg".to_owned()], ), + ("rust-j2k".to_owned(), vec!["dep:j2k".to_owned()]), ]), "libremetaverse-imaging-skia" => BTreeMap::from([ ("default".to_owned(), Vec::new()), diff --git a/tools/ci-matrix/src/ci_gate.rs b/tools/ci-matrix/src/ci_gate.rs index 4f2a0a8..28be02f 100644 --- a/tools/ci-matrix/src/ci_gate.rs +++ b/tools/ci-matrix/src/ci_gate.rs @@ -637,6 +637,30 @@ fn test_commands() -> Vec { "--locked", ], ), + CommandSpec::new( + "cargo", + &[ + "test", + "-p", + "libremetaverse-imaging", + "--no-default-features", + "--features", + "rust-j2k", + "--locked", + ], + ), + CommandSpec::new( + "cargo", + &[ + "test", + "-p", + "libremetaverse-imaging", + "--no-default-features", + "--features", + "jpeg2000,rust-j2k", + "--locked", + ], + ), ] } diff --git a/tools/ci-matrix/src/lib.rs b/tools/ci-matrix/src/lib.rs index 5d3b686..4fc8427 100644 --- a/tools/ci-matrix/src/lib.rs +++ b/tools/ci-matrix/src/lib.rs @@ -38,7 +38,7 @@ const REQUIRED_PROFILES: [&str; 7] = [ "windows-stable-portable", "macos-stable-portable", ]; -const REQUIRED_FEATURE_SETS: [&str; 11] = [ +const REQUIRED_FEATURE_SETS: [&str; 12] = [ "all-features", "dds-bc67", "default", @@ -47,15 +47,18 @@ const REQUIRED_FEATURE_SETS: [&str; 11] = [ "jpeg2000", "no-default-features", "real-audio", + "rust-j2k", "skia", "tests", "vorbis", ]; -const REQUIRED_FEATURES: [(&str, &str); 6] = [ +const REQUIRED_FEATURES: [(&str, &str); 8] = [ ("libremetaverse", "dds-bc67"), ("libremetaverse", "jpeg2000"), + ("libremetaverse", "rust-j2k"), ("libremetaverse", "vorbis"), ("libremetaverse-imaging", "jpeg2000"), + ("libremetaverse-imaging", "rust-j2k"), ("libremetaverse-imaging-skia", "skia"), ("libremetaverse-voice-webrtc", "real-audio"), ];