Maxim Shemanarev | 8 Sep 22:18 2004

Re: [AGG] agg_span_image_filter_rgba32

> This simple function...
> pixel combine(pixel a, pixel b)
> {
> 	pixel result;
> 	result.color = (a.color + b.color) / 2;
> 	result.alpha = (a.alpha + b.alpha) / 2;
> 	return result;
> }
> becoming more complex in the non-premultiplied case:
> pixel combine(pixel a, pixel b)
> {
> 	pixel result;
> 	result.color = (a.color * a.alpha + b.color * b.alpha)
> 							/ (a.alpha + b.alpha);
> 	result.alpha = (a.alpha + b.alpha) / 2;
> 	return result;
> }
> (well, you'll have to watch out for division by zero...)

You are right. Thanks for the explanations. Can I ask you co conduct some
experiments? I'm kinda busy with another project.

If you take agg_span_image_filter_rgba32.h and the bilinear filter, you will
see that fg[i] accumulates values with weight:

   weight = (image_subpixel_size - x_hr) * 
            (image_subpixel_size - y_hr);
   fg[0] += weight * *fg_ptr++;
   fg[1] += weight * *fg_ptr++;
   fg[2] += weight * *fg_ptr++;
   fg[3] += weight * *fg_ptr++;

It perfectly works for the premultiplied color space. 
If color1=1, alpha1=1, color2=0, alpha2=0, and weights=0.5, the result will be
color=0.5 and alpha=0.5. In the premultiplied colorspace it means "white,
semitransparent". In the plain one it's "gray, semitransparent", after the
final blending it'll be 0.75.

One way (questionable) is to try to multiply the weight by the respective
alpha, that is, 

   weight = (image_subpixel_size - x_hr) * 
            (image_subpixel_size - y_hr) * fg_ptr[alpha];

And then to shift the accumulated result to 8 bits more:

   fg[0] >>= image_subpixel_shift * 2 + 8;
   fg[1] >>= image_subpixel_shift * 2 + 8;
   fg[2] >>= image_subpixel_shift * 2 + 8;
   fg[3] >>= image_subpixel_shift * 2 + 8;

Another way is obvious, and maybe even more efficient, that is, "demultiply"
the values afterwards:

  *span++ = rgba8().demultiply(fg[Order::R], 
Instead of existing

                span->r = (int8u)fg[Order::R];
                span->g = (int8u)fg[Order::G];
                span->b = (int8u)fg[Order::B];
                span->a = (int8u)fg[Order::A];

Well, sorry, it's all too confusing for me right now :)

> I know, I could be using premultiplied colors everywhere in my 
> application, but I think this will really complicate things in terms of 
> gamma correction and color management that I might decide to add later. 
> On the other hand, the premultiplied color space does have a speed 
> advantage. But as you saw yourself in the case of the coverage alpha 
> values, mixing them in is an additional step. Therefor I think that 
> premultiplied colors are good for games and such, where there is *only* 
> alpha-blending, but no actual image editing or other types of 
> blending. Maybe I'm wrong though. At least in my application, I have 
> tons of cases where I need the "real" color of a pixel. For example 
> when layers are blended in other blending modes, like "multiply" or 
> "luminance" and such.

Right, There's definitely a lack of high quality color conversion methods. But
again, getting back to gamma correction, it might make sense (people from convinced me) to work in the premultiplied and 
*physically* uniform color space (luminance uniform), and only before output
convert it into plain and *perceptually* uniform space (that is, sRGB).
Converting on the fly becomes too complex, confusing and slow. Still, I hope
some day we'll arrange all of it properly.

To convert from sRGB to linear, we apply gamma=2.2, to get back to sRGB, we
apply gamma=1/2.2. But the problem is 8 bits per component become too rough,
we'll need to use 16bit components (64 bit colors), like in photoshop.


This SF.Net email is sponsored by BEA Weblogic Workshop
FREE Java Enterprise J2EE developer tools!
Get your free copy of BEA WebLogic Workshop 8.1 today.