I haven’t tried it or looked at the code, but I can imagine that using PixelRGB or PixelARGB instead of Colour could make things easier when mixing. It also seems that you have access to the individual bytes using the enums, maybe the blend methods already do what you’d like to do.
Edit: After looking a little bit at the code: blend won’t do the job
The matter is, what would be the result of a colour multiplication?
You can multiply the RGBA components, you can multiply the HSV components, you can multiply only individual components.
I assume, multiplying or summing the brightness comes closest to what you want to achieve.
Also, if you work pixel by pixel, I would advise against using Colour per pixel, since the ColourHelpers::HSB does already more maths in the constructor alone, than you would expect it to:
Ideally you can work with the individual bytes yourself and try to use only trivial operations.
Here is how JUCE applies convolution to an Image, maybe use that class instead?
Isn’t it clear? Multiply the colour components of a Colour object? But yeah, I thought about using .getBrightness, but then I’d have to migrate to HSV.
Right, this sounds like what I want to do, but where do I learn how to do this. Are the bytes laid out in some kind standard defined by RGB, RGBA, HSV, CMYK etc.? I.e. if I research how these formats hold their data, it will be true for Image::BitmapData::data?
And it looks simple enough to use. My question about working with the individual bytes still stands though, because in the future there may be things I want to do, that I would need to access the bytes for!
Luckily, in C++ you can define custom operators, so you can do something like this:
namespace custom_ops
{
template <typename Combine>
inline juce::Colour zip (juce::Colour a, juce::Colour b, Combine&& combine)
{
return juce::Colour::fromFloatRGBA (combine (a.getFloatRed(), b.getFloatRed()),
combine (a.getFloatGreen(), b.getFloatGreen()),
combine (a.getFloatBlue(), b.getFloatBlue()),
combine (a.getFloatAlpha(), b.getFloatAlpha()));
}
inline juce::Colour operator+ (juce::Colour a, juce::Colour b)
{
return zip (a, b, std::plus<>{});
}
inline juce::Colour operator* (juce::Colour a, juce::Colour b)
{
return zip (a, b, std::multiplies<>{});
}
// etc...
} // namespace custom_ops
int main()
{
using custom_ops::operator+, custom_ops::operator*;
juce::Colour a { 1.0f, 0.0f, 0.0f, 1.0f };
juce::Colour b { 0.0f, 1.0f, 0.0f, 1.0f };
auto c = a + b;
auto d = c * b * a;
}
Note that:
It’s a good idea to put the custom ops in their own namespace, and to switch the namespace on explicitly when you want to use the new operators.
It’s rarely a good idea to supply operators unless it’s extremely obvious how they should behave. In the case of Colour I think it’s fairly clear - in GLSL the vec4 type has similar elementwise operators and they’re pretty handy there.