Efficient way to change gamma on component?


#1

I’d like to add an option in my plugin to brighten/darken the entire interface. My UI is a combination of images and drawing code, but the background is mostly a big Image.

I thought about looking into ImageEffectFilter on the top level Component, but that just gives me an image and the context to draw it into, but I don’t see any blending modes or any kind of brightness, contrast or gamma functions in JUCE. Of course, I could be missing something.

Any ideas?


#2

There may be a better way, but you could create a second copy of your background which is brighter and/or has more contrast and render it with variable alpha over the top of your existing image.


#3

not really answering your question I’m affraid, but here is a quick trick that should darken your whole interface if that can help :

void paintOverChildren (Graphics& g) override
{
    g.fillAll (Colours::black.withAlpha (0.2f));
}

#4

Both of those methods are reasonable, but not ideal.

Another way would be for me to make a copy of each image on the fly and run a gamma function on each pixel. Is there a faster way than using getPixelAt()/setPixelAt() for each pixel?

Has anyone tried something like this as an ImageFilterEffect on the top level Component? If there was a way for fast pixel access that could be interesting.


#5

Ok, so I put the following ImageEffectFilter on my plugin’s top most component, which obviously affects every component down the line. My UI is 1024 x 680.

It looks good and seemed fast on my Mac, but on Windows (in Parallels), the UI lags a bit. Not sure how much of that is due to the emulation.

So, my question is…Is this crazy to run on my entire UI? @jules ?

class GammaFilter : public ImageEffectFilter
{
public:
    GammaFilter()
    {
        setGamma(1.0f);
    }
    ~GammaFilter(){}
    
    void setGamma(float gamma)
    {
        const float ogamma = 1.0f / gamma;
        for (int i = 0; i < 256; i++) {
            const float v = std::powf( (float)i / 255.0f, ogamma) * 255.0f;
            gammaTable[i] = (uint8)v;
        }
    }
    
    void applyEffect(Image& sourceImage, Graphics& 	destContext,
                     float scaleFactor, float alpha) override
    {
        Rectangle<float>r(0.0f, 0.0f, (float)sourceImage.getWidth(), (float)sourceImage.getHeight());
        
        int width = sourceImage.getWidth();
        int height = sourceImage.getHeight();
        Image::PixelFormat format = sourceImage.getFormat();
        jassert (format == Image::ARGB);
        
        Image::BitmapData inPic(sourceImage, Image::BitmapData::readOnly);
        
        int width4 = width * 4;
        
        for(int y = 0; y < height; y++)
        {
            uint8* ptr = inPic.getLinePointer(y);
            for(int x = 0; x < width4; x += 4)
            {
                ptr[x + 0] = gammaTable[ptr[x + 0]];
                ptr[x + 1] = gammaTable[ptr[x + 1]];
                ptr[x + 2] = gammaTable[ptr[x + 2]];
                // this is an opaque image so leave alpha alone
            }
        }
        destContext.drawImage(sourceImage, r);
        
    }
private:
    uint8 gammaTable[256];
};

And in my Editor:

GammaFilter gammaEffect;

// at the end of the the constructor
setComponentEffect(&gammaEffect);

// called from a menu
gammaEffect.setGamma(newGamma); // 0.8 - 1.2
container.repaint();

#6

Yeah, that’s really going to put an unsustainable load on the CPU! Really not something you’d attempt to do with a live UI, the only way you could do live gamma correction would be using the GPU, but we don’t have any mechanism for that.

Can’t you just gamma-correct the images that you’re using? All your other vector graphics shouldn’t need its gamma to be changed, right?


#7

I had a feeling it wouldn’t be the right thing to do. It would be great if there was a GPU way to do it.

Sure, I can gamma correct each image I use (which is definitely more than a few), but I’ll also have to modify the look and feel for the vector stuff, as I’m basically trying to brighten or darken the entire UI.

I was just looking for a clever/fast way to get this done.