From 25fdffbd3be0ff651171a2a519d32255ff0b0028 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Thu, 13 Aug 2026 11:24:38 +0000 Subject: [PATCH] feat(imaging): default to pure-Rust codecs --- README.md | 54 ++++++++++--------- api/SEMVER-AUDIT.md | 2 +- api/SEMVER-BASELINE.json | 11 ++-- ci/dependency-policy.json | 6 +-- crates/libremetaverse-imaging-skia/Cargo.toml | 4 +- crates/libremetaverse-imaging-skia/README.md | 5 +- crates/libremetaverse-imaging-skia/src/lib.rs | 8 +-- crates/libremetaverse-imaging/Cargo.toml | 2 +- crates/libremetaverse-imaging/src/lib.rs | 8 +-- crates/libremetaverse-prim-mesher/Cargo.toml | 2 +- crates/libremetaverse/Cargo.toml | 4 +- crates/libremetaverse/README.md | 12 ++--- docs/assets.md | 10 ++-- docs/dependency-policy.md | 3 +- docs/imaging-meshing.md | 15 +++--- docs/release-candidate.md | 8 +-- docs/rust-api-guide.md | 12 ++--- programs/Cargo.toml | 6 +-- programs/src/test_client/inventory.rs | 14 ++--- programs/src/test_client/world.rs | 4 +- programs/tests/test_client_inventory_cli.rs | 8 +-- programs/tests/test_client_world_cli.rs | 6 ++- tools/check_milestone_06.py | 8 +-- tools/ci-matrix/src/api_surface.rs | 11 ++-- tools/ci-matrix/src/lib.rs | 4 +- 25 files changed, 124 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index ad0399d..05ec700 100644 --- a/README.md +++ b/README.md @@ -49,20 +49,22 @@ python3 tools/check_test_parity.py python3 tools/audit_red_suite.py ``` -### Optional codec features +### Image codec features The default build needs no system image-codec library. It includes the -project-owned TGA and DDS implementations; the default `dds-bc67` feature uses -the pure-Rust `bcdec_rs` dependency for BC6H and BC7. Build without BC6H/BC7 -support with `cargo build -p libremetaverse --no-default-features`. +project-owned TGA and DDS implementations, pure-Rust J2K/JP2 through +`rust-j2k`, and pure-Rust common-raster decoding through the default +`libremetaverse-imaging-skia` `rust-skia` feature. The default `dds-bc67` +feature uses the pure-Rust `bcdec_rs` dependency for BC6H and BC7. Use +`--no-default-features` only for a deliberately minimal codec-free build. -The currently available opt-in codec backends are: +The available default and optional 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 in pure Rust | `libremetaverse-imaging-skia/rust-skia` | `cargo build -p libremetaverse-imaging-skia --features rust-skia` | None | +| Raw J2K and JP2 decoding/encoding in pure Rust (default) | `libremetaverse/rust-j2k` | `cargo build -p libremetaverse` | none | +| BMP, GIF, ICO, JPEG, PNG, WBMP, and WebP decoding in pure Rust (default) | `libremetaverse-imaging-skia/rust-skia` | `cargo build -p libremetaverse-imaging-skia` | none | +| Raw J2K and JP2 decoding/encoding through OpenJPEG (optional) | `libremetaverse/jpeg2000` | `cargo build -p libremetaverse --features jpeg2000` | OpenJPEG 2.5.4 or newer | | BMP, GIF, ICO, JPEG, PNG, WBMP, and WebP decoding through native 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 | @@ -70,9 +72,9 @@ 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 cargo test -p libremetaverse-imaging-skia --features skia -cargo test -p libremetaverse-imaging-skia --features rust-skia +cargo test -p libremetaverse-imaging-skia cargo test -p libremetaverse --no-default-features --features vorbis ``` @@ -95,10 +97,11 @@ Deterministic task, socket, file, subscription, cancellation, and allocation baselines are documented in the [concurrency hardening guide](docs/concurrency-hardening.md). -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. +The default `rust-j2k` backend never enables the native OpenJPEG adapter. Its +explicit `RustJ2kCodec` preserves the existing `J2kCodec` meaning when both +features are enabled. Likewise, default `RustSkiaTextureCodec` selection does +not rename or enable native `SkiaTextureCodec`. The native features remain +available for backend-interoperability testing or explicit application use. `tests/upstream-tests.json` is the machine-readable NUnit parity catalog. Translated tests live in hand-written Rust files with the `parity-case` marker @@ -208,8 +211,14 @@ Legacy and DX10 BC1 through BC5 decoding is built in. The default `dds-bc67` feature adds BC6H/BC7 decoding through the safe, pure-Rust `bcdec_rs` crate; disabling default features removes that optional dependency and makes those two formats return a typed unsupported-format error. -JPEG 2000 is available through the opt-in `jpeg2000` feature. It provides raw -J2K and JP2 lossless/lossy encoding and decoding, preserves one through five +The default `rust-j2k` feature provides raw J2K and JP2 lossless/lossy encoding +and decoding through `RustJ2kCodec`, backed by the exact pure-Rust `j2k` 0.8.1 +release. It supports 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 `jpeg2000` feature provides the native OpenJPEG alternative. It +provides raw J2K and JP2 lossless/lossy encoding and decoding, preserves one through five component order, precision, signedness, and alpha metadata until explicit byte conversion, and bounds encoded input, output, dimensions, and decoded samples. The compatibility encoder reproduces CoreJ2K's four-plane RGB/alpha view, @@ -217,20 +226,15 @@ 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 +The default pure-Rust raster adapter decodes BMP, GIF, ICO, JPEG, PNG, WBMP, +and WebP into the same checked planar representation through +`RustSkiaTextureCodec`. Its optional native `skia` feature uses pinned rust-skia binary-cache configurations on macOS, Linux, and Windows, normalizes premultiplied color at the imaging boundary, and keeps encoded input, decoded dimensions, strides, and allocations bounded. See [`crates/libremetaverse-imaging-skia/README.md`](crates/libremetaverse-imaging-skia/README.md) for native prerequisites, cache/source-build controls, licenses, and packaging -details. Default workspace builds do not compile or link Skia. +details. Default workspace builds compile neither OpenJPEG nor native Skia. Primitive profile and path meshing is implemented in native Rust from the pinned `PrimMesher.cs` reference. It covers checked linear, flexible, and circular extrusion; profile cuts and hollows; twist, taper, shear, skew, diff --git a/api/SEMVER-AUDIT.md b/api/SEMVER-AUDIT.md index ce9db4d..df1a014 100644 --- a/api/SEMVER-AUDIT.md +++ b/api/SEMVER-AUDIT.md @@ -46,7 +46,7 @@ The exact mapping, exported-marker, feature, and normalized Rust declaration has - .NET events become `subscribe_*` calls returning an owned `Subscription`; callbacks are `Send + Sync` and dispatch outside internal locks. - `IDisposable` becomes explicit idempotent shutdown plus `Drop`; cancellation remains a typed `Error::Cancelled`. - Closed C# enums retain fixed discriminants and are SemVer-breaking to extend; flags retain unknown bits, while public Rust error enums are `non_exhaustive`. -- Optional native and pure-Rust backends remain feature-gated (`jpeg2000`, `rust-j2k`, `skia`, `rust-skia`, `vorbis`, `real-audio`); default and empty-default sets are part of the baseline. +- Pure-Rust image backends are enabled by default (`rust-j2k`, `rust-skia`); native alternatives remain feature-gated (`jpeg2000`, `skia`, `vorbis`, `real-audio`), and empty-default sets remain part of the baseline. - Capability HTTP injection uses project-owned `HttpMessageHandler`, `CapsHttpLimits`, and `with_native_transport`; the internal `reqwest` client is not part of the public surface. - External C# signature types map to core/std, adopted cross-platform crates, or project-owned boundary types; no third-party crate path leaks through mapped signatures or public Rust declarations. diff --git a/api/SEMVER-BASELINE.json b/api/SEMVER-BASELINE.json index 8993de3..0388f63 100644 --- a/api/SEMVER-BASELINE.json +++ b/api/SEMVER-BASELINE.json @@ -30,7 +30,8 @@ "dep:bcdec_rs" ], "default": [ - "dds-bc67" + "dds-bc67", + "rust-j2k" ], "jpeg2000": [ "libremetaverse-imaging/jpeg2000" @@ -69,7 +70,9 @@ "name": "libremetaverse-imaging", "version": "0.0.1", "features": { - "default": [], + "default": [ + "rust-j2k" + ], "jpeg2000": [ "dep:libremetaverse-openjpeg" ], @@ -86,7 +89,9 @@ "name": "libremetaverse-imaging-skia", "version": "0.0.1", "features": { - "default": [], + "default": [ + "rust-skia" + ], "rust-skia": [ "dep:image-webp", "dep:skia-rs-codec", diff --git a/ci/dependency-policy.json b/ci/dependency-policy.json index b1ee9dc..9559b07 100644 --- a/ci/dependency-policy.json +++ b/ci/dependency-policy.json @@ -15,8 +15,8 @@ { "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": "image-webp", "versions": ["0.2.4"], "purpose": "Opt-in pure-Rust bounded WebP image decoding", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`image-webp`" }, - { "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": "image-webp", "versions": ["0.2.4"], "purpose": "Default pure-Rust bounded WebP image decoding", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`image-webp`" }, + { "name": "j2k", "versions": ["0.8.1"], "purpose": "Default 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`" }, @@ -30,7 +30,7 @@ { "name": "sha1", "versions": ["0.10.7"], "purpose": "Legacy protocol hash compatibility", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`sha1`" }, { "name": "sha2", "versions": ["0.11.0"], "purpose": "Manifest integrity and protocol hashing", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`sha2`" }, { "name": "skia-safe", "versions": ["0.99.0"], "purpose": "Opt-in cross-platform Skia image decoding", "maintenance": "monitored-native", "transitive_cost": "high", "native": true, "rewrite_anchor": "`skia-safe`" }, - { "name": "skia-rs-codec", "versions": ["0.3.0"], "purpose": "Opt-in pure-Rust BMP, GIF, ICO, JPEG, PNG, and WBMP image decoding", "maintenance": "active", "transitive_cost": "high", "native": false, "rewrite_anchor": "`skia-rs-codec`" }, + { "name": "skia-rs-codec", "versions": ["0.3.0"], "purpose": "Default pure-Rust BMP, GIF, ICO, JPEG, PNG, and WBMP image decoding", "maintenance": "active", "transitive_cost": "high", "native": false, "rewrite_anchor": "`skia-rs-codec`" }, { "name": "stats_alloc", "versions": ["0.1.10"], "purpose": "Allocation-budget compatibility tests, concurrency leak audits, and reproducible performance evidence", "maintenance": "stable", "transitive_cost": "low", "native": false, "rewrite_anchor": "`stats_alloc`" }, { "name": "str0m", "versions": ["0.22.0"], "purpose": "Native Rust ICE, DTLS, SRTP, RTP, and SCTP WebRTC transport", "maintenance": "active", "transitive_cost": "high", "native": false, "rewrite_anchor": "`str0m`" }, { "name": "syn", "versions": ["2.0.119"], "purpose": "Syntax validation for generated Rust sources", "maintenance": "active", "transitive_cost": "medium", "native": false, "rewrite_anchor": "`syn`" }, diff --git a/crates/libremetaverse-imaging-skia/Cargo.toml b/crates/libremetaverse-imaging-skia/Cargo.toml index 524fa98..f9a193a 100644 --- a/crates/libremetaverse-imaging-skia/Cargo.toml +++ b/crates/libremetaverse-imaging-skia/Cargo.toml @@ -8,12 +8,12 @@ repository.workspace = true description = "Optional Skia image decoder for the MetaCrate LibreMetaverse rewrite" [features] -default = [] +default = ["rust-skia"] skia = ["dep:skia-safe"] rust-skia = ["dep:image-webp", "dep:skia-rs-codec", "skia-rs-codec/gif", "skia-rs-codec/jpeg", "skia-rs-codec/png"] [dependencies] -libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" } +libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging", default-features = false } libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" } image-webp = { version = "=0.2.4", optional = true } skia-rs-codec = { version = "=0.3.0", default-features = false, optional = true } diff --git a/crates/libremetaverse-imaging-skia/README.md b/crates/libremetaverse-imaging-skia/README.md index e478049..18ebad3 100644 --- a/crates/libremetaverse-imaging-skia/README.md +++ b/crates/libremetaverse-imaging-skia/README.md @@ -6,7 +6,8 @@ exposes a `skia-safe` type and never discovers or links Skia. ## Features and formats -The default feature set has no Skia dependency. `SkiaTextureCodec::decode` and +The default feature set enables the pure-Rust `rust-skia` backend and has no +native Google Skia dependency. `SkiaTextureCodec::decode` and `RustSkiaTextureCodec::decode` return a typed `InvalidOperation` error when their respective feature is disabled, while conversion from the checked project-owned `backend::SKBitmap` remains available for tests and callers that @@ -15,7 +16,7 @@ already own decoded pixels. For a fully Rust implementation, enable: ```sh -cargo test -p libremetaverse-imaging-skia --no-default-features --features rust-skia +cargo test -p libremetaverse-imaging-skia ``` `rust-skia` pins [`skia-rs-codec` 0.3.0](https://github.com/rust-skia/skia-rs) diff --git a/crates/libremetaverse-imaging-skia/src/lib.rs b/crates/libremetaverse-imaging-skia/src/lib.rs index 3cff104..e30891e 100644 --- a/crates/libremetaverse-imaging-skia/src/lib.rs +++ b/crates/libremetaverse-imaging-skia/src/lib.rs @@ -1,9 +1,9 @@ //! Optional Skia imaging adapter corresponding to `LibreMetaverse.Imaging.Skia`. //! -//! Enable `skia` or the fully native-Rust `rust-skia` alternative for bounded -//! `BMP`, `GIF`, `ICO`, `JPEG`, `PNG`, `WBMP`, and `WebP` decoding. -//! The default build remains inert. See the crate README for native cache, -//! source-build, licensing, and cross-platform prerequisite details. +//! The default `rust-skia` backend provides bounded pure-Rust `BMP`, `GIF`, +//! `ICO`, `JPEG`, `PNG`, `WBMP`, and `WebP` decoding. Enable the optional +//! `skia` feature when native Google Skia is specifically required. See the +//! crate README for native cache, source-build, licensing, and deployment details. extern crate self as libremetaverse_imaging_skia; diff --git a/crates/libremetaverse-imaging/Cargo.toml b/crates/libremetaverse-imaging/Cargo.toml index 6e73cb3..a4b7c97 100644 --- a/crates/libremetaverse-imaging/Cargo.toml +++ b/crates/libremetaverse-imaging/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true description = "Texture codec abstractions for the MetaCrate LibreMetaverse rewrite" [features] -default = [] +default = ["rust-j2k"] jpeg2000 = ["dep:libremetaverse-openjpeg"] rust-j2k = ["dep:j2k"] diff --git a/crates/libremetaverse-imaging/src/lib.rs b/crates/libremetaverse-imaging/src/lib.rs index 031249d..f405130 100644 --- a/crates/libremetaverse-imaging/src/lib.rs +++ b/crates/libremetaverse-imaging/src/lib.rs @@ -1,9 +1,9 @@ //! Image and texture abstractions corresponding to `LibreMetaverse.Imaging`. //! -//! The default surface provides managed images plus native `TGA`/`DDS` handling. -//! 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. +//! The default surface provides managed images, native `TGA`/`DDS` handling, +//! and the pure-Rust `rust-j2k` JPEG 2000 backend. Enable `jpeg2000` for the +//! optional audited system `OpenJPEG` adapter. Consumers that only need the +//! format-neutral image types can disable default features. extern crate self as libremetaverse_imaging; diff --git a/crates/libremetaverse-prim-mesher/Cargo.toml b/crates/libremetaverse-prim-mesher/Cargo.toml index cbed1b7..d134513 100644 --- a/crates/libremetaverse-prim-mesher/Cargo.toml +++ b/crates/libremetaverse-prim-mesher/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true description = "Primitive meshing shims for the MetaCrate LibreMetaverse rewrite" [dependencies] -libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" } +libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging", default-features = false } libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" } [dev-dependencies] diff --git a/crates/libremetaverse/Cargo.toml b/crates/libremetaverse/Cargo.toml index 3ff2dbb..923a114 100644 --- a/crates/libremetaverse/Cargo.toml +++ b/crates/libremetaverse/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true description = "Rust rewrite shell for the LibreMetaverse client library" [features] -default = ["dds-bc67"] +default = ["dds-bc67", "rust-j2k"] dds-bc67 = ["dep:bcdec_rs"] jpeg2000 = ["libremetaverse-imaging/jpeg2000"] rust-j2k = ["libremetaverse-imaging/rust-j2k"] @@ -20,7 +20,7 @@ bcdec_rs = { version = "0.2.0", optional = true } flate2 = "1.1.2" futures-channel = "0.3.31" futures-util = "0.3.31" -libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" } +libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging", default-features = false } libremetaverse-prim-mesher = { version = "0.0.1", path = "../libremetaverse-prim-mesher" } libremetaverse-structured-data = { version = "0.0.1", path = "../libremetaverse-structured-data" } libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" } diff --git a/crates/libremetaverse/README.md b/crates/libremetaverse/README.md index d1b51e0..c5d152d 100644 --- a/crates/libremetaverse/README.md +++ b/crates/libremetaverse/README.md @@ -15,11 +15,11 @@ 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`, `rust-j2k`, and `vorbis` | +| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7 and J2K/JP2; optional native `jpeg2000` 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` or pure-Rust `rust-j2k` | -| `libremetaverse-imaging-skia` | Common raster formats through native Skia or pure Rust | Optional `skia` native build/cache; optional `rust-skia` has no native prerequisites | +| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Default pure-Rust `rust-j2k`; optional native `jpeg2000` | +| `libremetaverse-imaging-skia` | Common raster formats through native Skia or pure Rust | Default pure-Rust `rust-skia`; optional `skia` native build/cache | | `libremetaverse-prim-mesher` | Legacy prim and sculpt geometry | Pure Rust | | `libremetaverse-rendering-simple` | Deterministic reference geometry | Pure Rust | | `libremetaverse-rendering-mesh-foundry` | Prim, terrain, sculpt, and mesh-asset rendering | Pure Rust | @@ -31,8 +31,8 @@ they use those APIs directly. | `libremetaverse-openjpeg` | Audited `OpenJPEG` adapter | System `OpenJPEG` 2.5.4 or newer | | `libremetaverse-opus` | Audited Opus encoder/decoder adapter | System libopus | -Features are additive. Keep defaults unless you need a codec, and enable one -native adapter at a time while diagnosing installation problems. The +Features are additive. Keep the pure-Rust defaults for normal applications, +and enable a native adapter only when it is specifically needed. The [release CI matrix](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/docs/release-ci-matrix.md) records every validated combination. ## Naming and overload migration @@ -190,7 +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; +- no native prerequisite for the default pure-Rust `rust-j2k` and `rust-skia` features; - 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/docs/assets.md b/docs/assets.md index 62dcb0f..6d21f68 100644 --- a/docs/assets.md +++ b/docs/assets.md @@ -6,11 +6,11 @@ and mutable raw payload. Concrete animation, sound, script, calling-card, landmark, settings, mesh, texture, wearable, and notecard models enforce a 64 MiB input ceiling and reject malformed structured formats with typed errors. Mesh and settings parsing use StructuredData. Texture encode/decode uses the imaging -crate's bounded `jpeg2000` adapter, while the opt-in `vorbis` feature streams -PCM conversion through bounded planar Ogg Vorbis blocks; neither API exposes -codec implementation types. With either native codec feature disabled, its -public API remains present and returns a typed `InvalidOperation` for otherwise -valid input instead of discovering or linking a system codec implicitly. +crate's default bounded pure-Rust JPEG 2000 adapter, while the opt-in `vorbis` +feature streams PCM conversion through bounded planar Ogg Vorbis blocks; +neither API exposes codec implementation types. The optional native OpenJPEG +path is selected only by its explicit feature and never discovered or linked +by a default build. `AssetManager` is client-owned and shares one `DownloadManager` and `AssetCache`. ViewerAsset, GetMesh, GetTexture, and server-bake requests check the cache before diff --git a/docs/dependency-policy.md b/docs/dependency-policy.md index 117957c..6f9f496 100644 --- a/docs/dependency-policy.md +++ b/docs/dependency-policy.md @@ -49,7 +49,8 @@ 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` | +| j2k 0.8.1 | pure Rust, no platform prerequisite | same pure-Rust graph | default `rust-j2k` | +| skia-rs-codec 0.3.0 and image-webp 0.2.4 | pure Rust, no platform prerequisite | same pure-Rust graph | default `rust-skia` | | 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 5dc2f07..202c4d5 100644 --- a/docs/imaging-meshing.md +++ b/docs/imaging-meshing.md @@ -17,18 +17,19 @@ 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 (pure Rust) | disabled | `libremetaverse-imaging-skia/rust-skia` | none (`skia-rs-codec` 0.3.0 plus `image-webp` 0.2.4) | +| JPEG 2000 (pure Rust) | enabled | `libremetaverse-imaging/rust-j2k` | none (`j2k` 0.8.1) | +| BMP/GIF/ICO/JPEG/PNG/WBMP/WebP (pure Rust) | enabled | `libremetaverse-imaging-skia/rust-skia` | none (`skia-rs-codec` 0.3.0 plus `image-webp` 0.2.4) | | BMP/GIF/ICO/JPEG/PNG/WBMP/WebP (native) | 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. 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 the -native feature, while a dedicated `ubuntu-latest` job tests `rust-skia`, audits +dependency. The default `rust-j2k` feature reaches only the audited pure-Rust +`j2k` graph; enabling both features keeps both explicit codec types available +for interoperability testing. Native Skia is likewise an optional, +target-specific dependency, while `rust-skia` is the raster crate default. The +consolidated required Gitea gate exercises the native feature, while a dedicated +`ubuntu-latest` job tests `rust-skia`, audits its normal/build graph without installing native codec tools, and compiles the same code for WASM. Minimal-feature checks still prove no codec is needed for the owned Rust and translated compatibility tests. diff --git a/docs/release-candidate.md b/docs/release-candidate.md index c00a9ca..af0e837 100644 --- a/docs/release-candidate.md +++ b/docs/release-candidate.md @@ -17,14 +17,14 @@ is evidence of remaining work, never permission to waive it. ## Features and prerequisites -The default `libremetaverse` build uses the pure-Rust `dds-bc67` image path. -Optional boundaries are explicit: +The default MetaCrate build uses the pure-Rust `dds-bc67`, `rust-j2k`, and +`rust-skia` image paths. Native alternatives remain explicit opt-ins: | 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 | -| `rust-skia` | BMP/GIF/ICO/JPEG/PNG/WBMP/WebP through `RustSkiaTextureCodec` | none; exact pure-Rust `skia-rs-codec` 0.3.0 and `image-webp` 0.2.4 | +| `rust-j2k` (default) | J2K/JP2 through `RustJ2kCodec` | none; exact pure-Rust `j2k` 0.8.1 | +| `rust-skia` (default) | BMP/GIF/ICO/JPEG/PNG/WBMP/WebP through `RustSkiaTextureCodec` | none; exact pure-Rust `skia-rs-codec` 0.3.0 and `image-webp` 0.2.4 | | `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 85d968e..b707c2a 100644 --- a/docs/rust-api-guide.md +++ b/docs/rust-api-guide.md @@ -15,11 +15,11 @@ 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`, `rust-j2k`, and `vorbis` | +| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7 and J2K/JP2; optional native `jpeg2000` 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` or pure-Rust `rust-j2k` | -| `libremetaverse-imaging-skia` | Common raster formats through native Skia or pure Rust | Optional `skia` native build/cache; optional `rust-skia` has no native prerequisites | +| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Default pure-Rust `rust-j2k`; optional native `jpeg2000` | +| `libremetaverse-imaging-skia` | Common raster formats through native Skia or pure Rust | Default pure-Rust `rust-skia`; optional `skia` native build/cache | | `libremetaverse-prim-mesher` | Legacy prim and sculpt geometry | Pure Rust | | `libremetaverse-rendering-simple` | Deterministic reference geometry | Pure Rust | | `libremetaverse-rendering-mesh-foundry` | Prim, terrain, sculpt, and mesh-asset rendering | Pure Rust | @@ -31,8 +31,8 @@ they use those APIs directly. | `libremetaverse-openjpeg` | Audited `OpenJPEG` adapter | System `OpenJPEG` 2.5.4 or newer | | `libremetaverse-opus` | Audited Opus encoder/decoder adapter | System libopus | -Features are additive. Keep defaults unless you need a codec, and enable one -native adapter at a time while diagnosing installation problems. The +Features are additive. Keep the pure-Rust defaults for normal applications, +and enable a native adapter only when it is specifically needed. The [release CI matrix](release-ci-matrix.md) records every validated combination. ## Naming and overload migration @@ -190,7 +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; +- no native prerequisite for the default pure-Rust `rust-j2k` and `rust-skia` features; - 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/programs/Cargo.toml b/programs/Cargo.toml index 913c774..776aa07 100644 --- a/programs/Cargo.toml +++ b/programs/Cargo.toml @@ -10,9 +10,9 @@ publish = false [dependencies] clap = { version = "4.5", features = ["derive"] } -libremetaverse = { version = "0.0.1", path = "../crates/libremetaverse", default-features = false } -libremetaverse-imaging = { version = "0.0.1", path = "../crates/libremetaverse-imaging", features = ["jpeg2000"] } -libremetaverse-imaging-skia = { version = "0.0.1", path = "../crates/libremetaverse-imaging-skia", features = ["skia"] } +libremetaverse = { version = "0.0.1", path = "../crates/libremetaverse" } +libremetaverse-imaging = { version = "0.0.1", path = "../crates/libremetaverse-imaging" } +libremetaverse-imaging-skia = { version = "0.0.1", path = "../crates/libremetaverse-imaging-skia" } libremetaverse-structured-data = { version = "0.0.1", path = "../crates/libremetaverse-structured-data" } libremetaverse-voice-vivox = { version = "0.0.1", path = "../crates/libremetaverse-voice-vivox" } libremetaverse-voice-webrtc = { version = "0.0.1", path = "../crates/libremetaverse-voice-webrtc" } diff --git a/programs/src/test_client/inventory.rs b/programs/src/test_client/inventory.rs index 2081769..00d68ee 100644 --- a/programs/src/test_client/inventory.rs +++ b/programs/src/test_client/inventory.rs @@ -14,10 +14,10 @@ use libremetaverse::types::compat::CancellationToken; use libremetaverse::types::{AssetType, FolderType, InventoryType, UUID}; use libremetaverse::{PermissionMask, Permissions}; use libremetaverse_imaging::{ - J2kCodec, J2kCompression, J2kDecodeOptions, J2kEncodeOptions, ManagedImage, - ManagedImageImageChannels, + J2kCompression, J2kDecodeOptions, J2kEncodeOptions, ManagedImage, ManagedImageImageChannels, + RustJ2kCodec, }; -use libremetaverse_imaging_skia::SkiaTextureCodec; +use libremetaverse_imaging_skia::RustSkiaTextureCodec; use std::collections::{HashMap, HashSet}; use std::fs::{self, File}; use std::io::{Cursor, Read, Write}; @@ -1465,14 +1465,14 @@ fn image_to_j2k(path: &Path) -> Result, String> { .unwrap_or("") .to_ascii_lowercase(); if matches!(extension.as_str(), "jp2" | "j2c" | "j2k") { - J2kCodec::decode_bytes(&bytes, J2kDecodeOptions::default()) + RustJ2kCodec::decode_bytes(&bytes, J2kDecodeOptions::default()) .map_err(|_| "Invalid JPEG 2000 image".to_owned())?; return Ok(bytes); } let mut image = if matches!(extension.as_str(), "tga" | "targa") { decode_tga(&bytes)? } else { - SkiaTextureCodec::new() + RustSkiaTextureCodec::new() .map_err(|_| "Could not initialize the image decoder".to_owned())? .decode(Box::new(Cursor::new(bytes))) .map_err(|_| "Failed to decode image".to_owned())? @@ -1491,7 +1491,7 @@ fn image_to_j2k(path: &Path) -> Result, String> { .resize_bilinear(width.min(1024), height.min(1024)) .map_err(|_| "Failed to limit image dimensions".to_owned())?; } - J2kCodec::encode( + RustJ2kCodec::encode( &image, J2kEncodeOptions::default().with_compression(J2kCompression::Lossy { compression_ratio: 10.0, @@ -1583,7 +1583,7 @@ async fn dump_outfit( .await?; let jp2 = directory.join(format!("{texture_id}.jp2")); write_bounded(&jp2, &bytes)?; - let image = J2kCodec::decode_bytes(&bytes, J2kDecodeOptions::default()) + let image = RustJ2kCodec::decode_bytes(&bytes, J2kDecodeOptions::default()) .map_err(|_| format!("Failed to decode outfit texture {texture_id}"))?; let tga = directory.join(format!("{texture_id}.tga")); write_bounded(&tga, &encode_tga(&image)?)?; diff --git a/programs/src/test_client/world.rs b/programs/src/test_client/world.rs index 9329d91..7c57c28 100644 --- a/programs/src/test_client/world.rs +++ b/programs/src/test_client/world.rs @@ -1466,7 +1466,7 @@ async fn download_texture( .as_str(), )?; write_bounded(&path, &data)?; - let dimensions = libremetaverse_imaging::J2kCodec::decode_bytes( + let dimensions = libremetaverse_imaging::RustJ2kCodec::decode_bytes( &data, libremetaverse_imaging::J2kDecodeOptions::default(), ) @@ -1574,7 +1574,7 @@ async fn export( .await?; let path = texture_directory.join(format!("{texture}.jp2")); write_bounded(&path, &data)?; - let image = libremetaverse_imaging::J2kCodec::decode_bytes( + let image = libremetaverse_imaging::RustJ2kCodec::decode_bytes( &data, libremetaverse_imaging::J2kDecodeOptions::default(), ) diff --git a/programs/tests/test_client_inventory_cli.rs b/programs/tests/test_client_inventory_cli.rs index c9448c3..bfddfcb 100644 --- a/programs/tests/test_client_inventory_cli.rs +++ b/programs/tests/test_client_inventory_cli.rs @@ -1,4 +1,6 @@ -use libremetaverse_imaging::{J2kCodec, J2kEncodeOptions, ManagedImage, ManagedImageImageChannels}; +use libremetaverse_imaging::{ + J2kEncodeOptions, ManagedImage, ManagedImageImageChannels, RustJ2kCodec, +}; use std::fs; use std::path::{Path, PathBuf}; use std::process::{Command, Output, Stdio}; @@ -111,7 +113,7 @@ fn one_pixel_jp2() -> Vec { image.red[0] = 10; image.green[0] = 20; image.blue[0] = 30; - J2kCodec::encode(&image, J2kEncodeOptions::default()).expect("encode JPEG2000 fixture") + RustJ2kCodec::encode(&image, J2kEncodeOptions::default()).expect("encode JPEG2000 fixture") } fn one_pixel_tga() -> Vec { @@ -277,7 +279,7 @@ fn fake_grid_exercises_every_owned_inventory_appearance_and_asset_command() { b"default { state_entry() {} }" ); assert_eq!( - J2kCodec::decode_bytes( + RustJ2kCodec::decode_bytes( &fs::read(outfit.join(format!("{UPLOADED_TEXTURE_ASSET}.jp2"))) .expect("uploaded outfit jp2"), libremetaverse_imaging::J2kDecodeOptions::default(), diff --git a/programs/tests/test_client_world_cli.rs b/programs/tests/test_client_world_cli.rs index 1a200cf..0918ce7 100644 --- a/programs/tests/test_client_world_cli.rs +++ b/programs/tests/test_client_world_cli.rs @@ -1,4 +1,6 @@ -use libremetaverse_imaging::{J2kCodec, J2kEncodeOptions, ManagedImage, ManagedImageImageChannels}; +use libremetaverse_imaging::{ + J2kEncodeOptions, ManagedImage, ManagedImageImageChannels, RustJ2kCodec, +}; use std::fs; use std::path::{Path, PathBuf}; use std::process::{Command, Output, Stdio}; @@ -85,7 +87,7 @@ fn one_pixel_jp2() -> Vec { image.red[0] = 10; image.green[0] = 20; image.blue[0] = 30; - J2kCodec::encode(&image, J2kEncodeOptions::default()).expect("encode JPEG2000") + RustJ2kCodec::encode(&image, J2kEncodeOptions::default()).expect("encode JPEG2000") } #[test] diff --git a/tools/check_milestone_06.py b/tools/check_milestone_06.py index 0d8999f..212fbd5 100644 --- a/tools/check_milestone_06.py +++ b/tools/check_milestone_06.py @@ -40,8 +40,8 @@ def check_owned_rust() -> None: def check_features() -> None: imaging = load_toml(OWNED_CRATES[0] / "Cargo.toml") - if imaging["features"].get("default") != []: - raise SystemExit("imaging default features must stay empty") + if imaging["features"].get("default") != ["rust-j2k"]: + raise SystemExit("imaging defaults must select only pure-Rust JPEG 2000") 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"]: @@ -50,8 +50,8 @@ def check_features() -> None: raise SystemExit("the OpenJPEG adapter dependency must stay optional") skia = load_toml(OWNED_CRATES[1] / "Cargo.toml") - if skia["features"].get("default") != []: - raise SystemExit("Skia adapter default features must stay empty") + if skia["features"].get("default") != ["rust-skia"]: + raise SystemExit("raster adapter defaults must select only pure-Rust codecs") if skia["features"].get("skia") != ["dep:skia-safe"]: raise SystemExit("Skia must stay isolated behind its optional feature") targets = skia.get("target", {}).values() diff --git a/tools/ci-matrix/src/api_surface.rs b/tools/ci-matrix/src/api_surface.rs index 0491c07..262e355 100644 --- a/tools/ci-matrix/src/api_surface.rs +++ b/tools/ci-matrix/src/api_surface.rs @@ -911,7 +911,10 @@ fn validate_features(name: &str, actual: &BTreeMap>) -> Resu let expected = match name { "libremetaverse" => BTreeMap::from([ ("dds-bc67".to_owned(), vec!["dep:bcdec_rs".to_owned()]), - ("default".to_owned(), vec!["dds-bc67".to_owned()]), + ( + "default".to_owned(), + vec!["dds-bc67".to_owned(), "rust-j2k".to_owned()], + ), ( "jpeg2000".to_owned(), vec!["libremetaverse-imaging/jpeg2000".to_owned()], @@ -923,7 +926,7 @@ fn validate_features(name: &str, actual: &BTreeMap>) -> Resu ("vorbis".to_owned(), vec!["dep:vorbis_rs".to_owned()]), ]), "libremetaverse-imaging" => BTreeMap::from([ - ("default".to_owned(), Vec::new()), + ("default".to_owned(), vec!["rust-j2k".to_owned()]), ( "jpeg2000".to_owned(), vec!["dep:libremetaverse-openjpeg".to_owned()], @@ -931,7 +934,7 @@ fn validate_features(name: &str, actual: &BTreeMap>) -> Resu ("rust-j2k".to_owned(), vec!["dep:j2k".to_owned()]), ]), "libremetaverse-imaging-skia" => BTreeMap::from([ - ("default".to_owned(), Vec::new()), + ("default".to_owned(), vec!["rust-skia".to_owned()]), ( "rust-skia".to_owned(), vec![ @@ -1242,7 +1245,7 @@ The exact mapping, exported-marker, feature, and normalized Rust declaration has - .NET events become `subscribe_*` calls returning an owned `Subscription`; callbacks are `Send + Sync` and dispatch outside internal locks.\n\ - `IDisposable` becomes explicit idempotent shutdown plus `Drop`; cancellation remains a typed `Error::Cancelled`.\n\ - Closed C# enums retain fixed discriminants and are SemVer-breaking to extend; flags retain unknown bits, while public Rust error enums are `non_exhaustive`.\n\ -- Optional native and pure-Rust backends remain feature-gated (`jpeg2000`, `rust-j2k`, `skia`, `rust-skia`, `vorbis`, `real-audio`); default and empty-default sets are part of the baseline.\n\ +- Pure-Rust image backends are enabled by default (`rust-j2k`, `rust-skia`); native alternatives remain feature-gated (`jpeg2000`, `skia`, `vorbis`, `real-audio`), and empty-default sets remain part of the baseline.\n\ - Capability HTTP injection uses project-owned `HttpMessageHandler`, `CapsHttpLimits`, and `with_native_transport`; the internal `reqwest` client is not part of the public surface.\n\ - External C# signature types map to core/std, adopted cross-platform crates, or project-owned boundary types; no third-party crate path leaks through mapped signatures or public Rust declarations.\n\n\ ## Compile consumers\n\n\ diff --git a/tools/ci-matrix/src/lib.rs b/tools/ci-matrix/src/lib.rs index 846cf1a..6c0451f 100644 --- a/tools/ci-matrix/src/lib.rs +++ b/tools/ci-matrix/src/lib.rs @@ -422,7 +422,7 @@ fn audit_cargo_features(root: &Path) -> Result<()> { if defaults.iter().any(|feature| { matches!( feature.as_str(), - Some("jpeg2000" | "vorbis" | "dep:vorbis_rs") + Some("jpeg2000" | "skia" | "vorbis" | "dep:vorbis_rs") ) }) { return Err(MatrixError::new( @@ -450,6 +450,8 @@ fn audit_cargo_features(root: &Path) -> Result<()> { let default_tree = String::from_utf8_lossy(&default_tree.stdout); for forbidden in [ "libremetaverse-openjpeg ", + "skia-safe ", + "skia-bindings ", "vorbis_rs ", "ogg_next_sys ", "aotuv_lancer_vorbis_sys ",