-
Notifications
You must be signed in to change notification settings - Fork 1
Color spaces implementation #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
WalkthroughThe changes refactor the color handling system by replacing the monolithic Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant RGBAColor
participant OKLABColor
User->>RGBAColor: Construct RGBAColor (from floats, ints, or hex)
User->>RGBAColor: Access or modify color components
User->>RGBAColor: Convert to other formats (argb, bgra, abgr)
User->>OKLABColor: convert<RGBAColor, OKLABColor>(rgba)
OKLABColor-->>User: Return OKLABColor instance
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🔭 Outside diff range comments (2)
mr-math.natvis (1)
107-116
: 🧹 Nitpick (assertive)Spurious closing brace in the display string
R={r()}, G={g()}, B={b()}, A={a()}}
has one brace too many, so Visual Studio will show an Invalid Expression error for the whole visualiser.-<DisplayString>R={r()}, G={g()}, B={b()}, A={a()}}</DisplayString> +<DisplayString>R={r()}, G={g()}, B={b()}, A={a()}</DisplayString>USAGE.md (1)
220-220
: 🧹 Nitpick (assertive)Typo:
epsion
→epsilon
-float eps = mr::epsion<float>(); +float eps = mr::epsilon<float>();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
USAGE.md
(2 hunks)include/mr-math/color.hpp
(3 hunks)mr-math.natvis
(1 hunks)tests/misc.cpp
(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
include/mr-math/color.hpp (3)
include/mr-math/matr.hpp (18)
other
(117-126)other
(117-117)other
(128-133)other
(128-128)other
(135-140)other
(135-135)other
(142-150)other
(142-142)other
(152-158)other
(152-152)other
(160-166)other
(160-160)nodiscard
(169-171)nodiscard
(173-175)nodiscard
(178-191)_data
(321-324)_data
(383-386)_data
(455-458)include/mr-math/operators.hpp (2)
s
(133-141)s
(134-134)include/mr-math/def.hpp (2)
epsilon
(35-37)epsilon
(35-35)
tests/misc.cpp (1)
include/mr-math/color.hpp (6)
RGBAColor
(76-76)RGBAColor
(79-80)RGBAColor
(82-83)RGBAColor
(86-93)RGBAColor
(95-101)RGBAColor
(95-95)
🪛 markdownlint-cli2 (0.17.2)
USAGE.md
190-190: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
192-192: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
197-197: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
199-199: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
199-199: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
200-200: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
205-205: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
206-206: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
206-206: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
207-207: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
🪛 GitHub Actions: Benchmarks
include/mr-math/color.hpp
[error] 29-54: Syntax errors due to invalid use of 'this' keyword in method parameters and undeclared identifiers 'self', 'other', and 'eps'.
[error] 213-213: No matching function for call to 'mr::math::Vec<float, 3>::Vec(mr::math::RGBAColor&)'. Invalid static_cast from RGBAColor to Vec3f.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: build (windows-latest, Release, cl)
- GitHub Check: build (windows-latest, Debug, cl)
- GitHub Check: build (ubuntu-24.04, Debug, clang)
🔇 Additional comments (5)
include/mr-math/color.hpp (1)
213-230
:convert<RGBAColor, OKLABColor>
still fails without the implicit Vec3f conversion
After replacing the “deducing this” operators you must adjust this line:auto v = static_cast<Vec3f>(rgba) * Matr3f{ … };Because
static_cast<Vec3f>(rgba)
now works (see earlier fix) this part will compile, but note:
- You need
<cmath>
forcbrtf
.Vec3f * Matr3f
looks backwards – shouldn’t it beMatr3f * Vec3f
? Double-check the intended order.No code suggestion here, only a heads-up to verify the math once the build is green.
🧰 Tools
🪛 GitHub Actions: Benchmarks
[error] 213-213: No matching function for call to 'mr::math::Vec<float, 3>::Vec(mr::math::RGBAColor&)'. Invalid static_cast from RGBAColor to Vec3f.
tests/misc.cpp (4)
34-45
: Tests updated to usemr::RGBAColor
All constructor tests have been correctly updated to reference the new
RGBAColor
type, including float, integer, and packed‐integer overloads and the user‐defined literal. The assertions align with the implementations ininclude/mr-math/color.hpp
.
35-35
: Verify default‐constructor behaviorThe test assumes
RGBAColor()
yields(0,0,0,0)
. Please confirm that the default constructor zero‐initializes all channels (including alpha) rather than defaulting alpha to1.0
or leaving uninitialized memory.
77-77
: Setters test updated toRGBAColor
The setter test now correctly asserts the updated RGBA channel values against the
RGBAColor
instance. Good use ofset(index, value)
followingr(…)
.
94-95
: Clarify addition semantics and potential clampingThe test allows channel values to exceed
1.0
(e.g., alpha sums to2.0
). Please verify whetherRGBAColor::operator+
is intended to produce unclamped results or should clamp channels to[0,1]
. If clamping is desired, consider adding a saturating‐add function or adjust operator behavior.
template <typename InputT, typename OutputT> | ||
OutputT convert(InputT input) { | ||
static_assert(false, "You have to specify this function for your particular types"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
convert
is currently unusable – the unconditional static_assert(false)
aborts every build
With the present implementation the primary template can never be instantiated, so even a valid call (one that has a full specialization) will trigger the assertion before the compiler has a chance to pick the specialization. Make the assertion dependent on the template parameters:
-template <typename InputT, typename OutputT>
- OutputT convert(InputT input) {
- static_assert(false, "You have to specify this function for your particular types");
- }
+template <typename...>
+inline constexpr bool _always_false_v = false;
+
+template <typename InputT, typename OutputT>
+constexpr OutputT convert(InputT /*unused*/) {
+ static_assert(_always_false_v<InputT, OutputT>,
+ "No `convert` specialization found for the supplied types");
+}
This stops the hard error, allows the compiler to look for specializations, and still provides a clear diagnostic when none are found.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
template <typename InputT, typename OutputT> | |
OutputT convert(InputT input) { | |
static_assert(false, "You have to specify this function for your particular types"); | |
} | |
template <typename...> | |
inline constexpr bool _always_false_v = false; | |
template <typename InputT, typename OutputT> | |
constexpr OutputT convert(InputT /*unused*/) { | |
static_assert(_always_false_v<InputT, OutputT>, | |
"No `convert` specialization found for the supplied types"); | |
} |
bool operator==(this const ColorDerived &self, const ColorDerived &other) noexcept { | ||
return self._data == other._data; | ||
} | ||
|
||
bool equal(this const ColorDerived &self, const ColorDerived &other, ValueT eps = epsilon<ValueT>()) noexcept { | ||
return self._data.equal(other._data, eps); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
operator==
/ equal
suffer from the same “deducing this” issue
Rewrite using the conventional form and preserve const
-correctness:
-bool operator==(this const ColorDerived &self, const ColorDerived &other) noexcept {
- return self._data == other._data;
-}
-
-bool equal(this const ColorDerived &self, const ColorDerived &other, ValueT eps = epsilon<ValueT>()) noexcept {
- return self._data.equal(other._data, eps);
-}
+constexpr bool operator==(const ColorDerived &other) const noexcept {
+ return static_cast<const ColorDerived &>(*this)._data == other._data;
+}
+
+constexpr bool equal(const ColorDerived &other,
+ ValueT eps = epsilon<ValueT>()) const noexcept {
+ return static_cast<const ColorDerived &>(*this)._data.equal(other._data, eps);
+}
This removes the compilation errors and keeps behaviour identical.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
bool operator==(this const ColorDerived &self, const ColorDerived &other) noexcept { | |
return self._data == other._data; | |
} | |
bool equal(this const ColorDerived &self, const ColorDerived &other, ValueT eps = epsilon<ValueT>()) noexcept { | |
return self._data.equal(other._data, eps); | |
} | |
constexpr bool operator==(const ColorDerived &other) const noexcept { | |
return static_cast<const ColorDerived &>(*this)._data == other._data; | |
} | |
constexpr bool equal(const ColorDerived &other, | |
ValueT eps = epsilon<ValueT>()) const noexcept { | |
return static_cast<const ColorDerived &>(*this)._data.equal(other._data, eps); | |
} |
// structured binding support | ||
template <size_t I> requires (I < 4) | ||
ValueT get(this const ColorDerived &self) { return self._data[I]; } | ||
|
||
operator Vec3f(this ColorDerived &self) noexcept { | ||
return (Vec3f)self._data; | ||
} | ||
|
||
operator Vec4f(this ColorDerived &self) noexcept { | ||
return self._data; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deducing this
syntax isn’t yet supported by our tool-chain – replace it with ordinary methods
All the functions that use
operator Vec3f(this ColorDerived &self)
/ ValueT get(this const ColorDerived &self)
etc. are rejected by both GCC 13 and Clang 17 (see CI errors 29-54). A drop-in, C++20-compatible rewrite is straightforward:
-// structured binding support
-template <size_t I> requires (I < 4)
- ValueT get(this const ColorDerived &self) { return self._data[I]; }
-
-operator Vec3f(this ColorDerived &self) noexcept {
- return (Vec3f)self._data;
-}
-
-operator Vec4f(this ColorDerived &self) noexcept {
- return self._data;
-}
+// structured binding support
+template <size_t I> requires (I < 4)
+constexpr ValueT get() const noexcept {
+ static_assert(I < 4);
+ return static_cast<const ColorDerived &>(*this)._data[I];
+}
+
+/* implicit conversions */
+constexpr operator Vec3f() const noexcept {
+ const auto &d = static_cast<const ColorDerived &>(*this)._data;
+ return Vec3f{d.x(), d.y(), d.z()}; // avoid the “no Vec3f(Vec4f) ctor” error
+}
+constexpr operator Vec4f() const noexcept {
+ return static_cast<const ColorDerived &>(*this)._data;
+}
Apply the same pattern to every other member that currently relies on the P0847 “deducing this” feature.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// structured binding support | |
template <size_t I> requires (I < 4) | |
ValueT get(this const ColorDerived &self) { return self._data[I]; } | |
operator Vec3f(this ColorDerived &self) noexcept { | |
return (Vec3f)self._data; | |
} | |
operator Vec4f(this ColorDerived &self) noexcept { | |
return self._data; | |
} | |
// structured binding support | |
template <size_t I> requires (I < 4) | |
constexpr ValueT get() const noexcept { | |
static_assert(I < 4); | |
return static_cast<const ColorDerived &>(*this)._data[I]; | |
} | |
/* implicit conversions */ | |
constexpr operator Vec3f() const noexcept { | |
const auto &d = static_cast<const ColorDerived &>(*this)._data; | |
return Vec3f{d.x(), d.y(), d.z()}; // avoid the “no Vec3f(Vec4f) ctor” error | |
} | |
constexpr operator Vec4f() const noexcept { | |
return static_cast<const ColorDerived &>(*this)._data; | |
} |
friend std::ostream & operator<<(std::ostream &s, const ColorDerived &color) noexcept { | ||
Vec4u comps = color._data * 255; | ||
comps.clamp(0, 256); | ||
s << '#' << std::hex << std::uppercase << comps[0] << comps[1] << comps[2] << comps[3] << std::nouppercase << std::dec; | ||
return s; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
operator<<
prints incorrect hex and risks narrowing
- Multiplying a
Vec4f
by255
gives floats; converting them to aVec4u
via implicit ctor may silently floor or truncate. << std::hex
streams the decimal value of every component, not two-digit padded bytes, so#3F3F3F3F
might appear as#3F3F3F3F
or#3f3f3f3f
depending on the values ‑ but e.g. component10
becomes justA
, losing the leading zero.
Consider formatting each channel explicitly:
-Vec4u comps = color._data * 255;
-comps.clamp(0, 256);
-s << '#' << std::hex << std::uppercase << comps[0] << comps[1] << comps[2] << comps[3] << std::nouppercase << std::dec;
+const auto comps = (color._data * 255.0f).clamped(0.0f, 255.0f).round(); // or explicit cast
+s << '#' << std::uppercase << std::hex
+ << std::setw(2) << std::setfill('0') << uint32_t(comps[0])
+ << std::setw(2) << std::setfill('0') << uint32_t(comps[1])
+ << std::setw(2) << std::setfill('0') << uint32_t(comps[2])
+ << std::setw(2) << std::setfill('0') << uint32_t(comps[3])
+ << std::dec << std::nouppercase;
Requires <iomanip>
and a round()
/clamped
helper, but produces predictable 8-digit ARGB output.
Committable suggestion skipped: line range outside the PR's diff.
ColorDerived &operator+=(const ColorDerived &other) noexcept { | ||
static_cast<ColorDerived*>(this)->_data += other._data; | ||
return *(ColorDerived*)this; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Unsafe pointer casts & missing const
qualification in operator+=
static_cast<ColorDerived*>(this)->_data += other._data;
forces a mutable pointer even when *this
is const
.
Use a reference, keep qualifiers, and avoid C-style casts:
-ColorDerived &operator+=(const ColorDerived &other) noexcept {
- static_cast<ColorDerived*>(this)->_data += other._data;
- return *(ColorDerived*)this;
-}
+ColorDerived &operator+=(const ColorDerived &other) noexcept {
+ auto &self = static_cast<ColorDerived &>(*this);
+ self._data += other._data;
+ return self;
+}
This addresses UB and removes the noisy casts.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
ColorDerived &operator+=(const ColorDerived &other) noexcept { | |
static_cast<ColorDerived*>(this)->_data += other._data; | |
return *(ColorDerived*)this; | |
} | |
ColorDerived &operator+=(const ColorDerived &other) noexcept { | |
auto &self = static_cast<ColorDerived &>(*this); | |
self._data += other._data; | |
return self; | |
} |
mr::RGBAColor c1; // same as Vec4f(0, 0, 0, 0) | ||
mr::RGBAColor c2 = mr::Color(0.3, 0.47, 0.8); // == (0.3, 0.47, 0.8, 1.0) | ||
mr::RGBAColor c3 = mr::Color(76, 119, 204, 255); // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | ||
mr::RGBAColor c4 = mr::Color(0x4C'77'CC'FF); // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | ||
mr::RGBAColor c5 = 0x4C'77'CC'FF_rgba; // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Examples still reference the removed mr::Color
type
The refactor renamed the type to mr::RGBAColor
, yet the docs mix both names:
-mr::RGBAColor c2 = mr::Color(0.3, 0.47, 0.8);
-mr::RGBAColor c3 = mr::Color(76, 119, 204, 255);
-mr::RGBAColor c4 = mr::Color(0x4C'77'CC'FF);
+mr::RGBAColor c2{0.3, 0.47, 0.8};
+mr::RGBAColor c3{76, 119, 204, 255};
+mr::RGBAColor c4{0x4C'77'CC'FF};
Do the same for the arithmetic example further down (mr::Color(1.0, …)
➜ mr::RGBAColor{…}
).
Keeping the docs in sync prevents confusion for consumers.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
mr::RGBAColor c1; // same as Vec4f(0, 0, 0, 0) | |
mr::RGBAColor c2 = mr::Color(0.3, 0.47, 0.8); // == (0.3, 0.47, 0.8, 1.0) | |
mr::RGBAColor c3 = mr::Color(76, 119, 204, 255); // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | |
mr::RGBAColor c4 = mr::Color(0x4C'77'CC'FF); // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | |
mr::RGBAColor c5 = 0x4C'77'CC'FF_rgba; // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | |
``` | |
mr::RGBAColor c1; // same as Vec4f(0, 0, 0, 0) | |
mr::RGBAColor c2{0.3, 0.47, 0.8}; // == (0.3, 0.47, 0.8, 1.0) | |
mr::RGBAColor c3{76, 119, 204, 255}; // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | |
mr::RGBAColor c4{0x4C'77'CC'FF}; // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) | |
mr::RGBAColor c5 = 0x4C'77'CC'FF_rgba; // == (0.2980392156862745, 0.4666666666666667, 0.8, 1.0) |
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
190-190: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
const mr::RGBAColor expected1{0.3, 0.47, 0.8, 1.0}; | ||
EXPECT_EQ(mr::RGBAColor(0.3, 0.47, 0.8), expected1); | ||
EXPECT_EQ(mr::RGBAColor(mr::Vec4f(0.3, 0.47, 0.8, 1)), expected1); | ||
|
||
const mr::Color expected2{0.2980392156862745, 0.4666666666666667, 0.8, 1.0}; | ||
EXPECT_EQ(mr::Color(76, 119, 204, 255), expected2); | ||
EXPECT_EQ(mr::Color(0x4C'77'CC'FF), expected2); | ||
const mr::RGBAColor expected2{0.2980392156862745, 0.4666666666666667, 0.8, 1.0}; | ||
EXPECT_EQ(mr::RGBAColor(76, 119, 204, 255), expected2); | ||
EXPECT_EQ(mr::RGBAColor(0x4C'77'CC'FF), expected2); | ||
EXPECT_EQ(0x4C'77'CC'FF_rgba, expected2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider using EXPECT_FLOAT_EQ
for floating‐point constructor tests
The constructors accepting floats and vectors compare RGBAColor
instances via EXPECT_EQ
, which relies on operator==
. If operator==
uses exact float comparison, tiny rounding differences could cause spurious failures. Consider switching to EXPECT_TRUE(equal(...))
or EXPECT_FLOAT_EQ
on each component for these tests to tolerate floating‐point precision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (5)
include/mr-math/color.hpp (5)
17-20
: Unconditionalstatic_assert
still prevents all instantiationsThis exact issue was raised in the previous review: the primary template aborts every build even when a matching specialization exists. Make the assertion dependent on the template parameters.
27-38
:deducing this
is the cause of the current CI syntax errorsGCC 13 / Clang 17 (our tool-chain) do not support the P0847 “deducing-this” syntax yet. Every use (
get
, the two conversion operators) fails to compile (self
/other
/eps
undeclared).
44-48
:operator+=
still does UB-prone pointer castsCasting away
const
and going through a pointer was already flagged; the unsafe pattern is unchanged.
49-55
: Equality helpers repeat the unsupported “deducing-this” styleAgain identical to the previously reported issue; the build stops here.
57-62
:operator<<
prints wrong hex & silently truncates ‑ nothing changedThe earlier suggestions (use
std::setw
, explicit rounding, avoid<< std::hex
onfloat
s) are still applicable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
include/mr-math/color.hpp
(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
include/mr-math/color.hpp (4)
include/mr-math/matr.hpp (8)
nodiscard
(169-171)nodiscard
(173-175)nodiscard
(178-191)_data
(321-324)_data
(383-386)_data
(455-458)i
(169-169)i
(173-173)include/mr-math/operators.hpp (2)
s
(133-141)s
(134-134)include/mr-math/def.hpp (6)
epsilon
(35-37)epsilon
(35-35)value
(93-93)value
(93-93)value
(116-116)value
(116-116)include/mr-math/vec.hpp (7)
l
(165-169)l
(172-175)i
(84-84)i
(84-84)i
(95-95)y
(86-86)y
(86-86)
🪛 GitHub Actions: Benchmarks
include/mr-math/color.hpp
[error] 29-54: Syntax errors due to incorrect use of 'this' keyword in method parameters and undeclared identifiers 'self', 'other', and 'eps'.
[error] 371-371: No matching function for call to 'mr::math::Vec<float, 3>::Vec(mr::math::RGBAColor&)'. Invalid static_cast from RGBAColor to Vec3f.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: build (windows-latest, Release, cl)
- GitHub Check: build (windows-latest, Debug, cl)
- GitHub Check: build (ubuntu-24.04, Release, clang)
struct HSLAColor { | ||
friend class ColorBase<HSLAColor>; | ||
|
||
private: | ||
Vec4f _data; | ||
|
||
public: | ||
using ValueT = float; | ||
|
||
HSLAColor() = default; | ||
|
||
template <std::floating_point T> | ||
HSLAColor(T h, T s, T l, T a = 1) noexcept | ||
: _data(h, s, l, a) {} | ||
|
||
HSLAColor(Vec4f hsla) noexcept | ||
: _data(hsla) {} | ||
|
||
template <std::integral T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All new colour structs forget to inherit from ColorBase
HSLAColor
, HSVAColor
, CMYKColor
, and OKLABColor
only friend
the base but do not derive from it, so they do not get:
operator+
,operator+=
- equality helpers
- structured-binding support
- stream output
…and any future generic utilities.
This is a functional break (e.g. HSLAColor{}
can’t be streamed, added, or compared).
Apply the pattern already used for RGBAColor
:
-struct HSLAColor {
- friend class ColorBase<HSLAColor>;
+struct HSLAColor : ColorBase<HSLAColor> {
+ friend class ColorBase<HSLAColor>;
Repeat the same one-liner change for HSVAColor
, CMYKColor
, and OKLABColor
.
Also applies to: 196-214, 254-272, 312-330
inline OKLABColor convert<RGBAColor, OKLABColor>(RGBAColor rgba) { | ||
auto v = static_cast<Vec3f>(rgba) * Matr3f { | ||
Matr3f::RowT {0.4122214708f, 0.2119034982f, 0.0883024619f}, | ||
Matr3f::RowT {0.5363325363f, 0.6806995451f, 0.2817188376f}, | ||
Matr3f::RowT {0.0514459929f, 0.1073969566f, 0.6299787005f}, | ||
}; | ||
|
||
// TODO: rewrite using mr-math adaptation of <cmath> | ||
float l_ = cbrtf(v[0]); | ||
float m_ = cbrtf(v[1]); | ||
float s_ = cbrtf(v[2]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
❓ Verification inconclusive
convert<RGBAColor, OKLABColor>
currently fails to compile
static_cast<Vec3f>(rgba)
relies on the (unsupported) conversion operator discussed above, causing the pipeline error at line 371.- The multiplication order
Vec3f * Matr3f
may be wrong depending on how the library definesoperator*
. Most math libraries expectMatr * Vec
, not the other way around.
- auto v = static_cast<Vec3f>(rgba) * Matr3f{
+ Vec3f rgb = {rgba.r(), rgba.g(), rgba.b()};
+ auto v = Matr3f{
{0.4122214708f, 0.2119034982f, 0.0883024619f},
{0.5363325363f, 0.6806995451f, 0.2817188376f},
{0.0514459929f, 0.1073969566f, 0.6299787005f},
- };
+ } * rgb; // matrix-vector product
After the earlier conversion-operator rewrite, you could switch back to static_cast<Vec3f>(rgba)
if desired.
Please confirm the actual operator*
signature; run:
🏁 Script executed:
#!/bin/bash
# Show all operator* overloads involving Matr3f and Vec3f
rg -A2 -e 'operator\*.*Matr3f' -t cpp
rg -A2 -e 'operator\*.*Vec3f' -t cpp
Length of output: 78
I couldn’t find any operator*
overloads involving Matr3f
or Vec3f
with the previous search. Let’s run a broader grep to catch definitions in both headers and sources:
🏁 Script executed:
#!/bin/bash
# Look for any operator* overloads that mention Matr3f or Vec3f
rg -n 'operator\*' -t cpp -t hpp | grep -E 'Matr3f|Vec3f'
Length of output: 87
Let’s locate Matr3f
’s definition and then search for any operator*
there and globally:
🏁 Script executed:
#!/bin/bash
# 1) Find the file that defines Matr3f
def_file=$(rg -l 'struct Matr3f' | head -n1)
echo "Matr3f defined in: $def_file"
# 2) Search for operator* in that file
if [ -n "$def_file" ]; then
echo "—— operator* in $def_file ——"
grep -n 'operator\*' "$def_file" || echo "none"
else
echo "Matr3f definition file not found"
fi
# 3) Search globally for any operator* involving Matr3f or Vec3f
echo "—— global operator* occurrences involving Matr3f/Vec3f ——"
rg -n 'operator\*' | grep -E 'Matr3f|Vec3f' || echo "none"
Length of output: 376
Please verify the RGBA→Vec3f conversion and the matrix‐vector multiplication signature
- The use of
static_cast<Vec3f>(rgba)
fails because there’s no supported conversion operator fromRGBAColor
toVec3f
. - We could not locate any
operator*
overload forMatr3f
orVec3f
in the codebase. Confirm whether your math library definesMatr3f operator*(Matr3f, Vec3f)
Vec3f operator*(Vec3f, Matr3f)
and adjust the multiplication order accordingly.
- auto v = static_cast<Vec3f>(rgba) * Matr3f{
+ Vec3f rgb{ rgba.r(), rgba.g(), rgba.b() };
+ auto v = Matr3f{
{0.4122214708f, 0.2119034982f, 0.0883024619f},
{0.5363325363f, 0.6806995451f, 0.2817188376f},
{0.0514459929f, 0.1073969566f, 0.6299787005f},
- };
+ } * rgb; // matrix * vector
Once you’ve confirmed or added the proper operator*
, you may revert to static_cast<Vec3f>(rgba)
if you prefer.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
inline OKLABColor convert<RGBAColor, OKLABColor>(RGBAColor rgba) { | |
auto v = static_cast<Vec3f>(rgba) * Matr3f { | |
Matr3f::RowT {0.4122214708f, 0.2119034982f, 0.0883024619f}, | |
Matr3f::RowT {0.5363325363f, 0.6806995451f, 0.2817188376f}, | |
Matr3f::RowT {0.0514459929f, 0.1073969566f, 0.6299787005f}, | |
}; | |
// TODO: rewrite using mr-math adaptation of <cmath> | |
float l_ = cbrtf(v[0]); | |
float m_ = cbrtf(v[1]); | |
float s_ = cbrtf(v[2]); | |
inline OKLABColor convert<RGBAColor, OKLABColor>(RGBAColor rgba) { | |
// extract RGB components explicitly | |
Vec3f rgb{ rgba.r(), rgba.g(), rgba.b() }; | |
// perform matrix–vector multiply (ensure Matr3f * Vec3f exists) | |
auto v = Matr3f{ | |
{0.4122214708f, 0.2119034982f, 0.0883024619f}, | |
{0.5363325363f, 0.6806995451f, 0.2817188376f}, | |
{0.0514459929f, 0.1073969566f, 0.6299787005f}, | |
} * rgb; | |
// TODO: rewrite using mr-math adaptation of <cmath> | |
float l_ = cbrtf(v[0]); | |
float m_ = cbrtf(v[1]); | |
float s_ = cbrtf(v[2]); | |
… |
🧰 Tools
🪛 GitHub Actions: Benchmarks
[error] 371-371: No matching function for call to 'mr::math::Vec<float, 3>::Vec(mr::math::RGBAColor&)'. Invalid static_cast from RGBAColor to Vec3f.
Summary by CodeRabbit
New Features
Documentation
Style
Tests