render: normalize high precision texture inputs (#1267)
Adds a bounded normalization path for PNG16 and ICC-bearing JPEG texture inputs, producing float RGBA with recorded color and orientation decisions. Co-authored-by: 0xlillith <0xlillith@users.noreply.github.com>
This commit is contained in:
1 parent
a47e82ba94
commit
1e984c8e46
13 files changed
+1616
-17
No files matched your search
@@ -0,0 +1,132 @@
|
||||
# Explicit texture normalization (#1218)
|
||||
|
||||
`texture_input::normalization` is a pure-Rust, in-memory input utility, separate
|
||||
from the strict RGBA8 [texture admission API](render-texture-input.md). It does not
|
||||
install a renderer, command, resource format, document property or GPU texture.
|
||||
The old API still refuses 16-bit samples, ICC and nonidentity EXIF orientation.
|
||||
|
||||
## Input, policy and completed output
|
||||
|
||||
`NormalizationSource::new(bytes, role, options)` retains immutable encoded storage
|
||||
and admits format/storage/budget only. `decode(cancelled)` admits metadata and
|
||||
completes normalization before returning an immutable `NormalizedTexture`.
|
||||
Header admission is not successful decoding. No partial result is returned.
|
||||
|
||||
- PNG channels expand to RGB/gray with alpha at their original 8/16-bit sample
|
||||
depth; palette/sub-byte expansion uses the existing PNG codec. Output is RGBA
|
||||
f32, without an implicit RGBA8 preview. This preserves distinguishable adjacent
|
||||
16-bit raw samples; ICC conversion and floating-point arithmetic are not lossless.
|
||||
- JPEG is initially 8-bit grayscale or three-component sequential/progressive DCT;
|
||||
four-component/CMYK, arithmetic and lossless JPEG are outside this policy.
|
||||
JPEG decoding is lossy irrespective of the output precision.
|
||||
- Base color returns **linear sRGB** RGB and unassociated normalized alpha. A
|
||||
renderer must not apply sRGB decoding again. Alpha never receives a color transform.
|
||||
- Roughness, metallic, AO and OpenGL normal channels remain normalized **data**.
|
||||
They receive no gamma correction, ICC conversion, normal renormalization, channel
|
||||
convention conversion or filename inference. ICC on a data map refuses.
|
||||
- Untagged color refuses by default. `DeclareSrgbPrimaries` is an explicit caller
|
||||
declaration, not source certification: it uses PNG gAMA when present (encoding
|
||||
gamma in [0.1, 10]), otherwise the sRGB transfer function. Non-sRGB cHRM refuses.
|
||||
An explicit sRGB chunk selects sRGB transfer; ICC plus sRGB refuses.
|
||||
- PNG HDR/CICP metadata and animation refuse for base color; HDR environments use
|
||||
the separate [Radiance input utility](render-hdr-input.md). Data roles record
|
||||
ignored PNG color metadata, rather than applying it to data channels.
|
||||
- EXIF uses classic TIFF IFD0 only: either byte order, at most 128 entries,
|
||||
one SHORT Orientation in 1..8. Missing Orientation uses TIFF's top-left default,
|
||||
with distinct receipt flags for absent EXIF and absent Orientation. Other EXIF
|
||||
fields/thumbnail IFDs are not interpreted or certified. Malformed admission refuses.
|
||||
Nonidentity orientation requires `Apply`; all eight operations are exact pixel
|
||||
permutations, without interpolation. Oriented normal maps refuse until a vector
|
||||
and tangent-basis conversion contract exists.
|
||||
|
||||
## ICC scope and measured limitations
|
||||
|
||||
The already locked moxcms 0.8.1 is now a direct dependency (BSD-3-Clause OR
|
||||
Apache-2.0). PNG 0.18.1 and SHA-256 were also already in the lockfile. No dependency
|
||||
version, copied source, external image/profile, commercial asset or material dataset
|
||||
is added. Fixtures are generated from original synthetic values in test code.
|
||||
|
||||
Initial ICC admission supports version 2/4 RGB input/display matrix/TRC profiles
|
||||
with XYZ PCS and **identical RGB tone curves**. Required colorant/white-point/curve
|
||||
tags are checked; CLUT and unknown conversion tags refuse. Limits: 256 KiB profile,
|
||||
64 tags, 64 KiB per tag and 4096 sampled curve values. Offsets are four-byte aligned;
|
||||
exact shared ranges are allowed, partial overlaps and duplicate signatures refuse.
|
||||
The optional informational `chrm` tag initially admits three explicit device xy
|
||||
channels (type 0, bounded xy values); PCS colorants still define the conversion.
|
||||
This is bounded supported-profile admission, not a complete ICC conformance checker.
|
||||
|
||||
Conversion uses relative colorimetric intent, no source CICP override or fixed-point
|
||||
preference, and **extended range**. Testing found the default destination LUT clips
|
||||
before loss can be counted. Extended range preserves excursions until our policy.
|
||||
f64 rows retain the CMS's 16-bit source LUT where analytic evaluation is unavailable;
|
||||
the library still uses f32 matrix/curve arithmetic internally. No universal accuracy
|
||||
or full-f64 colorimetry is claimed. Unequal RGB curves refuse because the pinned
|
||||
same-curve optimization can treat two matching curves as three matching curves.
|
||||
|
||||
`GamutPolicy::Refuse` rejects encoded destination RGB outside [0, 1], allowing an
|
||||
explicit edge tolerance of 3/65535 for finite-precision roundoff. `Clip` is an
|
||||
explicit lossy preview policy. Every clamped RGB channel, including edge-tolerance
|
||||
clamps, is counted. Nonfinite values always refuse. Output RGB is then linearized.
|
||||
|
||||
## Resource and provenance boundaries
|
||||
|
||||
- Encoded length **and capacity**: at most 16 MiB. Dimensions: at most 2048 per edge.
|
||||
Raw decoded storage: at most 32 MiB. Completed float RGBA: at most 64 MiB.
|
||||
- Maximum working admission: 256 MiB, charging retained encoded capacity, 24 bytes
|
||||
per pixel for raw/output allowances, a 64 MiB codec reservation, 16 MiB conservative
|
||||
CMS workspace, two 256 KiB profile allowances and two f64 RGB rows. The constructor
|
||||
requires encoded capacity plus codec reservation before header work. Codec defaults
|
||||
outside our control, allocator overhead and other jobs mean this is **not an RSS,
|
||||
CPU-time or combined GPU/CPU ceiling**. Owned large buffers use fallible allocation.
|
||||
- Framing/presence checks cap PNG chunks and JPEG marker segments at 4096, refuse
|
||||
duplicate/late semantic metadata and require the codec to retain declared ICC/EXIF
|
||||
information. JPEG scan stuffing/restarts are skipped without entropy decoding.
|
||||
Existing codecs remain responsible for pixels and format validation; no second
|
||||
compression decoder is introduced. PNG is fully finished before publication.
|
||||
- Cancellation is checked before/after whole-image decode/profile setup, per
|
||||
normalization row and during hashing. It cannot interrupt codec/profile construction.
|
||||
The host must schedule this work away from the interactive UI thread. Worker ownership,
|
||||
document generations, stale-result rejection, pooling and browser scheduling remain
|
||||
mandatory consumer work; an immutable completed object does not implement those jobs.
|
||||
- A read-only serializable receipt records recipe version 1, source and normalized
|
||||
SHA-256 (little-endian f32 sample bytes), role, precision, dimensions, color conversion,
|
||||
ICC identity, orientation operations/presence, chosen policies, clamp count, storage
|
||||
and working estimate. It includes no paths or license approval. Hashes identify bytes,
|
||||
not publishers. The receipt is diagnostic, not a persistent resource-pack schema or
|
||||
permission to redistribute images/profiles. Per-file provenance and licensing remain
|
||||
the host/library's responsibility.
|
||||
|
||||
Tests use independent analytic sRGB/linear expectations, adjacent 16-bit samples,
|
||||
alpha/palette data, original ICC stress profiles, all eight orientations, malformed
|
||||
and ignored metadata, budgets and cancellation. This utility alone cannot demonstrate
|
||||
wood shading, material assignment, PBR/HDRI, GPU behavior or solver properties.
|
||||
|
||||
## Individually licensed file probe (2026-10-15)
|
||||
|
||||
The same [Black Oak Veneer](https://polyhaven.com/a/black_oak_veneer) 1K diffuse
|
||||
files that the legacy utility refused now complete normalization with their actual
|
||||
embedded ICC profile. Author: Jenelle van Heerden; asset license:
|
||||
[Poly Haven CC0](https://polyhaven.com/license), verified at retrieval. Provider API
|
||||
calls identified SolidCraft; this is an offline validation probe, not live API integration.
|
||||
Files, generated output and probe harness are not bundled or committed.
|
||||
|
||||
| Input | Source SHA-256 | Completed result |
|
||||
|---|---|---|
|
||||
| PNG, 5,262,864 bytes | `3303ebadba1277488e43b0936b27743525711985db0da62ac9f989f695f6065c` | 16-bit source, 1024x1024 linear-sRGB RGBA f32, 16,777,216 output bytes, zero clamped channels |
|
||||
| JPEG, 580,236 bytes | `7893f9bfeda626364f0a803dea6327c9f3a7b2f2fe330f6b4f326f6b06c0c265` | 8-bit source, same dimensions/encoding/storage, zero clamped channels |
|
||||
|
||||
Both receipts identify ICC SHA-256
|
||||
`500fa2f01de5a0e051ce3b4111b0c94aa223cda0f4ccfbc1c631adc30a050616`.
|
||||
The first probe refused its optional `chrm` tag. Support was added from ICC.1
|
||||
sections 9.2.16/10.2, with an original valid/malformed fixture; no profile was stripped
|
||||
or source primaries guessed. EXIF is present without Orientation, so TIFF's identity
|
||||
default applies. PNG/JPEG output hashes differ as expected for different input bytes
|
||||
and JPEG loss; these are completed input results, not wood rendering evidence.
|
||||
Local receipts retain download URL/license/retrieval metadata and provider MD5 checks.
|
||||
Any future shipped file needs its own ATTRIBUTION.md entry and reviewed provenance.
|
||||
|
||||
Public format references: [PNG third edition](https://www.w3.org/TR/png-3/),
|
||||
[TIFF 6](https://www.itu.int/itudoc/itu-t/com16/tiff-fx/docs/tiff6.pdf),
|
||||
[ICC.1](https://www.color.org/specification/ICC1v43_2010-12.pdf),
|
||||
[ICC embedding note](https://www.color.org/technotes/ICC-Technote-ProfileEmbedding.pdf)
|
||||
and [JPEG T.81](https://www.w3.org/Graphics/JPEG/itu-t81.pdf).
|
||||
Reference in new issue
Block a user