setOpacity before a call to draw a single channel Image doesn't display on new Apple laptops

I just came across this odd behaviour:

an image with Image::PixelFormat::SingleChannel and calling g.setOpacity(0.26) before drawing the image with g.drawImage(...) didn’t display anything on a 2018 MacBook Pro (Intel UHD 630) but does work on older hardware.

I don’t have the hardware here, so it’s not possible to create a minimal example, but my code was taking a full colour (with alpha) image, creating a copy with convertedToFormat(Image::PixelFormat::SingleChannel) and then drawing it with the lower opacity (to achieve a silhouette greyed out version of the colour version).

I’ve worked around it by taking a copy of the colour image, calling desaturate() and then multiplyAllAlphas() to get the same shade, but it seems like a bug that the original code shouldn’t work only on newer Apple hardware.

Did anyone from @JUCE team see this?

edit: @ed95 @t0m @jules?

Yes, we’ve seen it, but not yet gotten around to investigating any further.

Was this just using the standard CoreGraphics renderer, not OpenGL? I haven’t got a 2018 MB to test with but running the following on a 2017 model works as expected:

/*******************************************************************************
 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:             ImageOpacityTest

  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 Component
{
public:
    //==============================================================================
    MyComponent()
    {
        for (int x = 0; x < 250; ++x)
            for (int y = 0; y < 250; ++y)
                argbImage.setPixelAt (x, y, Colours::white);
            
        setSize (600, 400);
    }

    ~MyComponent()
    {
    }

    //==============================================================================
    void paint (Graphics& g) override
    {
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
        
        auto singleChannelImage = argbImage.convertedToFormat (Image::SingleChannel);
        g.setOpacity (0.1f);
        g.drawImage (singleChannelImage, 20, 20, 250, 250, 0, 0, 250, 250);
    }

private:
    //==============================================================================
    // Your private member variables go here...
    Image argbImage { Image::ARGB, 250, 250, true };

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyComponent)
};

Are you able to run this example and see if the problem is there?

I changed the code slightly to be more an exact replication of my experience.

/*******************************************************************************
 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:             MyComponentPIP

  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 Component
{
public:
    //==============================================================================
    MyComponent()
    {
        for (int x = 0; x < 250; ++x)
            for (int y = 0; y < 250; ++y)
                argbImage.setPixelAt (x, y, chooseColour(x, y));

        setSize (600, 400);
    }

    ~MyComponent()
    {
    }

    inline Colour chooseColour(const int x, const int y)
    {
        if ( y < 125 && x > y || y > 126 && x < y ) return transparentBlack;
        const auto position = x * 250 + y;
        if ( position < 250 * 250 * 0.333 ) return Colours::red;
        if ( position < 250 * 250 * 0.666 ) return Colours::green;
        return Colours::blue;
    }

    //==============================================================================
    void paint (Graphics& g) override
    {
        g.fillAll (Colours::black);

        g.drawImage (argbImage, 20, 20, 250, 250, 0, 0, 250, 250);

        auto singleChannelImage = argbImage.convertedToFormat (Image::SingleChannel);
        g.setOpacity (0.1f);
        g.drawImage (singleChannelImage, 290, 20, 250, 250, 0, 0, 250, 250);
    }

private:
    //==============================================================================
    // Your private member variables go here...

    Image argbImage { Image::ARGB, 250, 250, true };

    Colour transparentBlack { uint8(0), uint8(0), uint8(0), uint8(0) };

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyComponent)
};

my output (expected) :

2018 MBP output :

edit : 2018 MBP is running 10.13.6 if that makes any difference

That’s concerning. If you remove the g.setOpacity (0.1f); call does the single channel image draw correctly or is it still invisible? Which version of the macOS SDK are you building against? If it’s the 10.14 SDK (ie you are using Xcode 10) can you try building against an earlier version like 10.13? It’d be good to narrow down whether this is purely a hardware issue or whether a recent change in the SDK has something to do with it.

If you remove the g.setOpacity (0.1f); call does the single channel image draw correctly or is it still invisible?

It’s visible in full white when removing the setOpacity call.

Which version of the macOS SDK are you building against?

OSX Base SDK Version I have left at default.
image

If it’s the 10.14 SDK (ie you are using Xcode 10) can you try building against an earlier version like 10.13?

I’m using Xcode 9.2 on 10.12.6

It’d be good to narrow down whether this is purely a hardware issue or whether a recent change in the SDK has something to do with it.

Yeah it’s difficult for me to help much as I don’t have the hardware either.

I can reproduce it on my 2018 MBP running 10.13.6 too. I set the base SDK to 10.7 with no change.