Rendering ColourGradient to an Image gives incorrect results

I have a problem with ColourGradient, whereby gradients are not rendering correctly when the Graphics context points at an Image.

My Component draws a graph background, and ordinarily looks like this:

Because it uses some gradients and fonts to draw the grid/scale, I want to cache the background to an Image. However, calling setBufferedToImage(true) on the Component results in it looking like this:

There are four linear gradients, one at each edge of the graph area, which are supposed to fade between the background colour with high alpha at the outer edge, and the same colour with zero alpha at the inner edge.

On the buffered version, the gradients seem to be both drawing a darker colour than the background, and
are also darker in the middle of the shaded rectangle.

I get exactly the same problem if I disable setBufferedToImage, but create an Image and render to it using a new Graphics context, then copy that image to the screen in paint().

This is on Mac, by the way. I’ve not had a chance to test to see if this happens on PC yet.

In case it’s hard to see on the above screen grabs, here’s a version with the brightness/contrast boosted:

I’m struggling to repro this at the moment. It would be helpful to know:

  • How are your components arranged? Are you calling setBufferedToImage directly on the component that contains the gradients, or on an enclosing component?
  • How are you specifying the colours for the gradients?
  • Which JUCE version are you using? Please provide the git hash of the commit.

Ideally it would be helpful if you could provide a minimal code sample that demonstrates the problem.

Thanks for getting back to me on this.

The Component calls setBufferedToImage(true) in its constructor, and has a paint() function which goes something like this:

        // Fill with solid panel colour
        auto bg = Colour( 0xFF1F1F1F );

        // (Stuff for drawing grid lines etc. here)

        // Fade the grid into the background colour towards the edge
        auto r = getLocalBounds().removeFromBottom( 60 );
        auto cg = ColourGradient::vertical ( Colours::transparentBlack, bg.withAlpha ( 0.8f ), r )
        g.setGradientFill (cg);
        g.fillRect (r);

Paraphrasing my code here, I’ve just realised my error in the following line:

auto cg = ColourGradient::vertical ( Colours::transparentBlack, bg.withAlpha ( 0.8f ), r );

Because the first colour is transparentBlack, the RGB component of the colour fades towards black as the alpha is fading out, which explains why the shading gets darker before fading out completely. But this darkening doesn’t seem to happen when the component is not buffered.

Replacing the first colour with the background colour with zero alpha solves the problem, and produces a consistent result whether buffered or not:

auto cg = ColourGradient::vertical ( bg.withAlpha ( 0.0f ), bg.withAlpha ( 0.8f ), r );

Are you using OpenGL in your project?

We’re not using OpenGL. Juce 7.0.4, commit e49fb38d44622435f39f486530e84f84df20c8af

Are you disabling CoreGraphics rendering? I’m strugglng to repro the behaviour you’re seeing in a plain JUCE project on macOS (which uses CoreGraphics by default), but I see something similar when using the software renderer or OpenGL.

Please can you share any extra preprocessor definitions you’ve added to your project, along with any custom module options that you’ve set?

This change should improve consistency when rendering gradients using various renderers. Please try it out and let me know if you still see graphical differences when toggling image-buffering.

Yes, this fixed the issue! And apologies for the earlier red herring - it turns out that our project is based on a template that attaches an OpenGLContext to the UI, and I hadn’t realised. Sorry about that.