Repository navigation
Fix conversion issues and enhance alpha preservation in color models - #77
Merged
Merged
Conversation
hsv_to_rgb and hwb_to_rgb were normalized through wrap_unit_hue in #66, but hsl_to_rgb still fed the raw unit hue into hue2rgb, whose single ±1 adjustment cannot recover hues needing two wraps: hsl(780°) rendered black instead of yellow, hsl(700°) rendered a wrong sector. Negative and multi-wrap inputs now land on the [0,1) ring exactly like the other two polar spaces, symmetric with #66. Co-Authored-By: Claude Code <noreply@anthropic.com>
cone_scale_adapt hopped color -> xyz_t -> color_cast back; xyz_t has no alpha, so the cast defaulted it to 1.0 and silently discarded the caller's value (bradford_adapt(rgbaf(1,0,0,0.5)) returned alpha 1.0). The transform now captures alpha via get_src_alpha before the hop and restores it on the result for alpha-carrying models, covering all six transforms that share the construction (Bradford, Von Kries, CAT02, CAT16, CMCCAT2000, XYZ scaling). 3-channel inputs are unaffected. Co-Authored-By: Claude Code <noreply@anthropic.com>
…#73) The wide-gamut forward matrices were built from chromaticity-rounded D65/D50 (column sums 0.950456/1.089058) while Lab's Xn/Yn/Zn are the CIE tabulated whites (0.95047/1.08883), so white (1,1,1) in Display P3, Rec.2020, ProPhoto and Adobe RGB landed at a*≈-0.002…-0.012, b*≈0.008…0.014 instead of the achromatic origin. Row-normalize each forward matrix so column sums equal the tabulated white (D65 for P3/Rec2020/Adobe, D50 for ProPhoto), and re-derive every inverse via Gauss-Jordan on the exact normalized matrix (same discipline as #68) so pairs stay mutually inverse. Sync all copies: functions/{xyz, rec2020,adobe_rgb,prophoto_rgb}.hpp, gamut/details.hpp inverses, io/css/{color_function,relative_color}.hpp. ProPhoto's D50 primaries now sum to WHITEPOINT_D50 exactly, so the Bradford D50→D65 step maps white onto D65 and OkLab L = 1.0 (the old 1.0000031 above-white symptom is gone; above-one clipping stays covered by SuperWhiteXyzLightnessAboveOne). New ConversionWhitePointTest suite: white→Lab a*=b*=0 for all four spaces plus Lab round-trip. sRGB matrices untouched (already exact). ctest 1415/1415. Co-Authored-By: Claude Code <noreply@anthropic.com>
#75) std::pow/std::cbrt are not constexpr in C++17/20 (P0533R3 = C++26). GCC folds them as builtins during constant evaluation; Clang/MSVC do not, so constexpr color_cast through the Adobe gamma and CIELAB/CIELUV/OKLab cbrt paths failed to compile there while CI stayed green. - Add conversion/math.hpp: pure double-precision Newton shims (cbrt, Adobe linearize/encode) plus portable_* wrappers that branch on is_constant_evaluated() — runtime keeps std::pow/std::cbrt (unchanged), constant evaluation uses the shims (float-accurate via double internals). - Wire call sites: adobe_rgb (linearize/encode), lab (2x cbrt), luv (cbrt), oklab (safe_cbrt). - New test_constexpr.cpp: libm-equivalence sweeps (mutation anchors) plus unguarded static_asserts proving constant evaluation on any compiler with __builtin_is_constant_evaluated (GCC 9+/Clang 8+/MSVC 16.4+). - README: document the constexpr portability guarantee. ctest 1420/1420. Co-Authored-By: Claude Code <noreply@anthropic.com>
#76) from_unit/from_value clamped every conversion hop to [0,1]/[min,max], so rgbf(-0.1, 0.5, 1.2) lost its over/under range at the first hop to linear RGB — scRGB/HDR intermediates were impossible and Rec.2020's sign-preserving transfer curve never saw its input. - Add traits::model_extended_range_v: per-model opt-in member (static constexpr bool extended_range = true) on model_traits, detected via void_t (same shape as model_has_alpha_v), default false. - Opt in the linear RGB float families (sRGB/Adobe/P3/Rec.2020/ProPhoto, 3- and 4-channel) and XYZ. - from_unit/from_value skip the clamp only for float channels of extended-range models; integral channels and display-encoded models keep clamping at the encode boundary (issue expectation: "keep display-encoded spaces clamping as today"). - Nominal channel ranges stay [0,1]: widening them would re-anchor the to_unit/from_unit affine map away from the transfer functions' 0=black/1=white contract (white would store as 2.0 under a [-1,2] range), breaking every raw-value consumer (blend, luminance, IO). Storage still accepts out-of-range floats; clamped()/make_checked() remain the opt-in boundary factories. - graph DI comment updated for accuracy; README documents the story. New tests/operations/test_extended_range.cpp: 9 tests — trait opt-in asserts, the issue's rgb→linear→xyz→linear→rgb chain, Rec.2020 signed curve, XYZ beyond [-1,2] round trip, display/integral clamp guards, alpha policy. RED observed at both layers (missing trait, then clamped hops); mutation: from_unit gate removed → 6 tests fail. ctest 1429/1429. Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.