SoftwareImageRenderer produces premultiplied ARGB?


#1

If I construct a Graphics () with an Image of type ARGB, is the resulting image premultiplied format?

If yes, then I have a problem with my compositing operators…there’s no way to calculate them from premultiplied RGB.


#2

I’m afraid the answer is yes: all the rendering uses premultiplied images.


#3

Hmm…what should I do? There is no obvious solution. It would be straightforward to change FillType to support a flag for premultiplied solid colours, make FillType::colour private and go through an accessor that either premultiplies, or doesn’t.

This would fail for gradients and image fills though…Help!!


#4

FillType? The Colour and Gradient classes aren’t premultiplied, it’s just images that are.


#5

Every call to getPixelARGB() ends up putting premultiplied colour values into the Image associated with a Graphics context:

juce_Colour.cpp

const PixelARGB Colour::getPixelARGB() const noexcept
{
    PixelARGB p (argb);
    p.premultiply();
    return p;
}

ColourGradient::createLookupTable() calls getPixelARGB(). Also, SoftwareRendererSavedState::fillRect calls getPixelARGB(). These are the places where solid colour fills and gradients sneak premultiplied values into the Image of a Graphics.

If you are interested in helping me work through this and support non-premultiplied drawing all you have to do is a global search for “getPixelARGB” and you’ll see the places, there are ~30 of them. Only a small subset of these need attention (I don’t think OpenGLGraphicsContext wants or needs non premultiplied rendering support).

Thinking about it some more, getPixelARGB() could take a bool parameter indicating whether or not premultiplication should take place. The LowLevelGraphicsContext (or some other related class) could have a setting for premultiplication and then just pass that through getPixelARGB.

We would probably need a new function in the software renderer for compositing images that are not premultiplied but that shouldn’t be too hard.

Thoughts?


#6

glad to see that there’s ongoing effort to bring photoshop compositing modes to juce

see these old thread:
http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=2429
http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=4761


#7

There’s a Colour::getARGB() method that returns you the raw numbers - you can construct a PixelARGB from that value.


#8

Getting non-premultiplied values from a Colour is not helpful, there is no way to prevent premultiplied values from going into the Image attached to a Graphics without changing JUCE in some way.


#9

I don’t think it’s that simple - there’d need to be a new NonPremultipliedPixelARGB class, not a bool, and every level of the software renderer would need to handle that class as well as the existing ones… Sorry, all the effort and extra code bloat is too scary for me.


#10

Can premultiplication be perfectly reversed? i.e. does unpremultiply() put the colour components back exactly as they were?


#11

No, the precision decreases as the alpha value decreases, and at alpha=0 it can’t be reversed at all.


#12

It seems that for doing blend modes, the loss of accuracy when undoing the premultiply doesn’t matter (since it gets multiplied back in after). Unfortunately there is a lot of work per-pixel but I did manage to get it sort of working:

This is calculated per channel:

int const fa = *srcAlpha;
int const f  = fa ? jmin (255, (*src * 255) / fa) : 0;
int const ba = *destAlpha;
int const b  = ba ? jmin (255, (*dest * 255) / ba) : 0;
int const v  = (op (f, b) * fa * alpha) / 65025;

*dest = static_cast <uint8> (((v + *dest) * 65025 - ba * alpha * *dest) / 65025);

#13

I’m not saying anything has to be changed yet, Jules, but these Layer Effects open up a whole world of possibilities. Imagine a designer providing a user interface control presented as a Photoshop document with layers containing effects and being able to turn that into procedural vectorized drawing code which scales and transforms perfectly, retaining all of the effects.

Some point after I have finished implementing all the effects it would be nice to get some support from JUCE for making these things work fast; although I’m not sure exactly what needs to be done yet since I’m not finished.


#14

It is extremely cool, Vinnie. If it’s possible to get it working without completely rewriting the renderer (agh!) then I hope we can make it happen.


#15

Rewriting the renderer is not necessary. In the past the thinking for implementing the blending modes is to make it a property of the Graphics context, much like the fill colour. This is not the right approach. First of all its not possible to implement some of the blending modes this way because they are not associative. Second, it is not possible to get a correct alpha channel for drawn content in all cases. By blending modes I mean the Photoshop blending modes (normal, screen, multiply, overlay, et. al.) and not the commonly referenced Porter and Duff compositing operators which in my opinion are not useful for artistic purposes. Witness that Photoshop does not offer any of the Porter and Duff operators except “over.”

The path that I have taken is to perform all rendering into a separate layer image, much like using Graphics::beginTransparencyLayer. When all the drawing is done the layer effects are applied one by one to the background layer using the alpha channel and colour channels of the transparency layer.

There are currently two problems with this method:

  1. Colours have to be unpremultiplied in the innermost loop of the blending function. This adds a lot of needless multiplies and divides. If there was a way for the software renderer to support non premultiplied colours it would solve this problem.

  2. The bottommost image cannot be ARGB (i.e. the Component that uses these layer effects must have setOpaque (true)). This is easily fixed by adopting the changes for Exchanging the Font and Software Renderer. Specifically, it is necessary for JUCE to allow a ComponentPeer’s attributes to indicate that a custom software renderer should be used for all drawing. This custom renderer would be identical to the existing one (ideally just derive from the appropriate building blocks), but also allow Getting the Pixels from a Graphics.