Shouldn't Colour have mathematical operator overloads? [SOLVED]

So I’m a super noob and possibly out of my depth here, but…

I’m trying to apply a convolution matrix on the pixels around a current index.

I want to create a new Colour from existing Colour values

The psuedo code in my head goes as follows:

Image::BitmapData buffer1Data(buffer1, Image::BitmapData::writeOnly);

for (int i = 1; i < rows-1; ++i)
    for (int j = 1; j < cols-1; ++j)
    {
         Colour newColour = (    buffer1Data.getPixelColour(i-1,j) +
                         buffer1Data.getPixelColour(i+1,j) +
                         buffer1Data.getPixelColour(i,j-1) +
                         buffer1Data.getPixelColour(i,j+1) ) / 4;
    }

But the Colour class doesn’t know how to do addition or division. (It does have operator=, operator==, operator!=)

So instead I had to get around it by writing this:

Image::BitmapData buffer1Data(buffer1, Image::BitmapData::writeOnly);

Colour c1 = buffer1Data.getPixelColour(i-1,j);
Colour c2 = buffer1Data.getPixelColour(i+1,j);
Colour c3 = buffer1Data.getPixelColour(i,j-1);
Colour c4 = buffer1Data.getPixelColour(i,j+1);

uint8 newR = (c1.getRed() + c2.getRed() + c3.getRed() + c4.getRed()) / 4;
uint8 newG = (c1.getGreen() + c2.getGreen() + c3.getGreen() + c4.getGreen()) / 4;
uint8 newB = (c1.getBlue() + c2.getBlue() + c3.getBlue() + c4.getBlue()) / 4;

Mixing colours is not such a common problem in JUCE? Or am I going about my problem the wrong way?

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?

2 Likes

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?

:relieved: 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!

Thanks as always

It’s not clear, it’s ambiguous…

1 Like

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.
1 Like

Yes! I completely forgot that you can actually define class methods outside of the class declaration!

(for better or worse, I’m a header only kind of guy!)

Great tip. Thank you!