针对讨论:
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.