BR: repeated calls to Graphics::setOpacity will not totally fade out image

Hi,

In a plugin i work on, i fade out a complex image using juce::Graphics::setOpacity before drawing the image onto the context. However it seems the image does not entirely disappear and i end up with a blotch stuck as in the screenshot of the PIP i made to repro (i faded out a white circle).

However if i use juce::Image::multiplyAllAlphas (which i want to avoid in my plugin) the image ends up totally fading out which is what i expect…

Here’s the PIP to reproduce the issue, clicking on the window retriggers the fade out. Am i missing something?

Cheers

(I have the issue with both juce 7 and 8)

/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

  name:             BugTestPIP

  dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics, juce_gui_basics
  exporters:        XCODE_MAC

  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

  type:             Component
  mainClass:        MyComponent

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once


//==============================================================================
class MyComponent : public juce::Component
                  , public juce::Timer
{
public:
    //==============================================================================
    MyComponent()
    {
        setSize (600, 400);
        startTimer (timerInterval);
    }

    //==============================================================================
    void paint (juce::Graphics& g) override
    {
        imageToRender.clear (imageToRender.getBounds());
        juce::Graphics imageContext { imageToRender };
        imageContext.setOpacity (0.9f);
//        previousImage.multiplyAllAlphas (0.9f);
        imageContext.drawImageAt (previousImage, 0, 0);
        
        g.drawImageAt (imageToRender, 0, 0);
        std::swap (imageToRender, previousImage);
    }

    void resized() override
    {
        initImage();
    }
    
    void mouseDown (const MouseEvent&) override
    {
        initImage();
    }
    
    //==============================================================================
    void timerCallback() override
    {
        repaint();
    }
    
private:
    void initImage()
    {
        imageToRender = juce::Image (juce::Image::ARGB, getWidth(), getHeight(), true);
        previousImage = juce::Image (juce::Image::ARGB, getWidth(), getHeight(), true);
        
        juce::Graphics imageContext { previousImage };
        imageContext.setOpacity (1.f);
        
        const auto area = getLocalBounds();
        const auto spotRadius = std::min (area.proportionOfWidth (0.5f), area.proportionOfHeight (0.5f));
        const auto spotArea = juce::Rectangle<float> (0, 0, spotRadius, spotRadius).withCentre (area.getCentre().toFloat());
        imageContext.setColour (Colours::white);
        imageContext.fillRoundedRectangle (spotArea, spotArea.proportionOfHeight (0.5f));
    }


private:
    //==============================================================================
    juce::Image imageToRender;
    juce::Image previousImage;
    static constexpr int timerInterval = 30;


    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyComponent)
};