Is there a way to create radial gradients for ellipses?

Right now juce colour gradients only allow you to specify two points, which will always create a circle with identical width and height. Is there a way to create elliptical gradients?
This would be especially great for converting figma designs into juce code.
Is there maybe a third party module Im not aware of?

Maybe you could create a circle in an offsecreen context, apply the gradient to that, then render it with different x and y mappings into the actual graphical context?

1 Like

Try applying a transform to the fill type.

    void paint(juce::Graphics& g) override
    {
        auto localBounds = getLocalBounds().toFloat();
        
        juce::ColourGradient gradient{ juce::Colours::magenta, 
            localBounds.getCentre(), 
            juce::Colours::cyan, 
            localBounds.getTopLeft(), true };
        auto transform = juce::AffineTransform::scale(0.5f, 1.5f).
            translated(getWidth() * 0.25f, getHeight() * -0.25f);
        auto fill = juce::FillType{ gradient }.transformed(transform);

        g.setFillType(fill);
        g.fillRect(localBounds);
    }

image

Matt

6 Likes

Nice solution! Looks very Georgia O’Keefe :sweat_smile:

I would never have thought of that, nice!

Didn’t even know about juce::FillType::transformed :eyes:

2 Likes

:grin: thanks.

Never even occurred to me that you could do this until I saw the original question and started thinking about what happens in the low-level graphics context.

You could do more; here’s a rotated, translated, and scaled gradient:

image

Gradient meshes would be even more fun.

Matt

1 Like

I also found Graphics::addTransform after seeing your solution, incase your method should be independent of the current fill :slight_smile:

Sure; this way you can transform the gradient fill separately from the painted image.

        juce::ColourGradient gradient{ juce::Colours::magenta, 
            localBounds.getCentre(), 
            juce::Colours::cyan, 
            localBounds.getTopLeft(), true };
        auto transform = juce::AffineTransform{}.
            scaled(0.5f, 1.5f).
            translated(getWidth() * 0.25f, getHeight() * -0.25f).
            rotated(juce::MathConstants<float>::pi * 0.25f, localBounds.getCentreX(), localBounds.getCentreY());
        auto fill = juce::FillType{ gradient }.transformed(transform);

        g.setFillType(fill);
        g.addTransform(AffineTransform::shear(0.2f, 0.0f).translated(-getWidth() * 0.1f, 0.0f));
        g.fillEllipse(localBounds.reduced(30.0f));

image

1 Like