agg_pixfmt_rgba32.h blending formula

针对讨论:

I have played with the blending functions found in agg_pixfmt_rgba32.h
and have come to the conclusion that the formulae used to compute the
output alpha in all blend_... functions is wrong.

Here is a snippet of what blend_solid_hspan does:

    int alpha = (*covers++) * c.a;
    ...
    int r = p[Order::R];
    ...
    int a = p[Order::A];
    p[Order::R] = (((c.r - r) * alpha) + (r << 16)) >> 16;
    ...
    p[Order::A] = (((c.a - a) * alpha) + (a << 16)) >> 16;

The computation for R, G and B is OK: the destination color is the
result of destination color added to the difference multiplied by
alpha.

However, for alpha, this is wrong. You don't want to compute the
destination alpha based on (c.a - a) * (c.a * coverage), but
instead, I would write :

    p[Order::A] = ((alpha + (a << 8)) - ((alpha * a) >> 8)) >> 8;

实际问题说明:

I was experimenting with RGBA images long ago and used WinAPI function
AlphaBlend(). I'm not sure if I used exactly yours formulae for the
Alpha-channel, but something like that. The result of alpha-blending was
strange. Honestly, it's a bit confusing to me, because I tried to use regular
and premultiplied color spaces, but end up with complete mess. Then I tried to
use use the same formula for the alpha-channel and got some appropriate result:http://www.antigrain.com/agg_alpha_blend.gif (I reproduced it a couple of
minutes ago). Here I clear the frame buffer with rgba8(0,0,0,0) and use the
following code to display:

         // Alpha-blending the buffer
         BLENDFUNCTION blend;
         blend.BlendOp = AC_SRC_OVER;
         blend.BlendFlags = 0;

#if defined(AC_SRC_ALPHA)
         blend.AlphaFormat = AC_SRC_ALPHA;
#elif defined(AC_SRC_NO_PREMULT_ALPHA)
         // this is only for old version of wingdi.h that exists in Visual-C
6.x
         blend.AlphaFormat = AC_SRC_NO_PREMULT_ALPHA;
#else 
#error "No appropriate constant for alpha format. Check version of wingdi.h,
There must be AC_SRC_ALPHA or AC_SRC_NO_PREMULT_ALPHA"
         blend.AlphaFormat = 1; // just in case if the compiler treats "#error"
as a warning
#endif

邮件回复:

        blend.SourceConstantAlpha = 255;
         ::AlphaBlend(
            m_dc,      
            m_devRect.x1,
            m_devRect.y1,
            m_rbuf.width(),
            m_rbuf.height(),
            m_memDC,
            0,
            0,     
            m_rbuf.width(), 
            m_rbuf.height(),
            blend
         );

Although, if you clear the buffer with rgba8(255,255,255,0) the result will be
wrong. I'll try to experiment with your formula later and see what will happen.
Maybe I do something wrong when calling AlphaBlend too?

But in general, there're many ways to blend pixels in RGBA, you always can
write your own :-)
> I was experimenting with RGBA images long ago and used WinAPI function
> AlphaBlend(). I'm not sure if I used exactly yours formulae for the
> Alpha-channel, but something like that. The result of alpha-blending was
> strange. Honestly, it's a bit confusing to me, because I tried to use
> regular and premultiplied color spaces, but end up with complete mess.

Same for me. I finally stopped messing with AlphaBlend since it did not
do what I expected. What I did was to paint using AGG into an RGBA buffer
initialised to rgba8(0,0,0,0) and that was OK. The differences between
your original blending code and my modified version usually only apply
to the areas where anti-aliasing is used, and thus don't show significant
differences, unless you paint surfaces with a given alpha value.

With your code, setting alpha to 50% and painting a pure red color over
a transparent (0,0,0,0) background results in the premultiplied red and
somehow-premultiplied alpha :

  r = 50%
  g = 0%
  b = 0%
  alpha = 25%


My code produces alpha = 50% in this case, which makes more sense. I
checked with the formulae used in Intel IPPI imaging library, and their
definition of premultiplied RGBA produces indeed 50%,0%,0%,50%.

> [...]
> Although, if you clear the buffer with rgba8(255,255,255,0) the result
> will be wrong. I'll try to experiment with your formula later and see
> what will happen. Maybe I do something wrong when calling AlphaBlend too?

I tried using AlphaBlend too. Here was my scenario:

- Paint into a transparent RGBA buffer (0,0,0,0) with AGG
- Set up an empty pixmap in .NET (premultiplied RGBA or plain RGBA, it
  does not seem to matter) and use AlphaBlend to copy the buffer into it.
  Somehow, .NET allows me to create an HDC from a Bitmap object, and that
  is what I needed to set up layered windows.
- Use that pixmap as the layered window background.

As soon as I paint with alpha!=0, AlphaBlend sets the alpha value of the
pixmap to 1.0, which is nonsense. Compositing a buffer onto another with
AlphaBlend does not preserve transparency, from what I have experimented.

> But in general, there're many ways to blend pixels in RGBA, you always can
> write your own :-)
I finally replaced the call of AlphaBlend with a simple byte-by-byte
buffer manipulation and now I get semi-transparency working quite well
in .NET windows. But I hate to have to give up because of lack of
understanding/documentation related to AlphaBlend.


你可能感兴趣的:(Blend,Alpha,agg)