[cairo] Blend modes take 3

Soeren Sandmann sandmann at daimi.au.dk
Tue Dec 2 13:51:32 PST 2008


"Benjamin Otte" <otte at gnome.org> writes:

> On Tue, Nov 25, 2008 at 10:10 PM, Soeren Sandmann <sandmann at daimi.au.dk> wrote:
> >    - When mask is 0, there is no reason to read the source.
> >
> >    - There is no reason to read the destination if the inverse
> >      combined src/mask is 0
> >
> Don't compilers take care of that?
> The code looks better to me if the variable assignments all occur in one place.
> 
> >    - The continue is incorrect; it needs to write out 0 in that case,
> >      since the intermediate buffer is not zero-filled.
> >
> Oh, the code is supposed to modify the _source_ parameter, too?
> Someone really needs to document what component alpha does properly so
> I really grok it.
> Fixed.

No, what I meant was that the destination buffer would need to be
modified in that case too, but that was just a thinko on my part.

However, I don't think the new code is correct either. In order to
skip the computation, it needs to ensure that both m and s are 0. So
let's not do this optimization for now. 

Another problem I forgot to mention: fbByteMulAddC() produces a result
in its first argument, not its last.

> >>  if (min < 0) {
> >>    tmp[0] = l + (tmp[0] - l) / 4 * l / (l - min) * 4;
> >>    tmp[1] = l + (tmp[1] - l) / 4 * l / (l - min) * 4;
> >>    tmp[2] = l + (tmp[2] - l) / 4 * l / (l - min) * 4;
> >>  }
> >>  if (max > a) {
> >>    tmp[0] = l + (tmp[0] - l) / 4 * (a - l) / (max - l) * 4;
> >>    tmp[1] = l + (tmp[1] - l) / 4 * (a - l) / (max - l) * 4;
> >>    tmp[2] = l + (tmp[2] - l) / 4 * (a - l) / (max - l) * 4;
> >>  }
> >
> > Where do those 4s come from?
> >
> Overflow protection. The tmp value can range from -COMP2_T_MAX .. 2 *
> COMP2_T_MAX (I think), so I took the shortcut to just divide by a high
> enough number to avoid it. Also, this code will break on 64bit cases
> as I'm using ints there for lack of a signed comp4_t type.
> It's one of the cases I asked about previously on IRC I think as I was
> unsure if this is a case for doubles or how it should best be
> handled.

I would be more comfortable with floating points, yes. In the
'blendmode' branch here:

        git://freedesktop.org/~sandmann/pixman/

I fixed some of the above things and added a long comment about the
linearity of the HSL functions. Here is the comment:

/* For premultiplied colors, we need to know what happens when C is
 * multiplied by a real number. Lum and Sat are linear:
 *
 *    Lum (r * C) = r * Lum (C)          Sat (r * C) = r * Sat (C)
 *
 * If we extend ClipColor with an extra argument a and change
 *
 *        if x >= 1.0
 *
 * into
 *
 *        if x >= a
 *
 * then ClipColor is also linear:
 *
 *    r * ClipColor (C, a) = ClipColor (rC, ra);
 *
 * for positive r.
 *
 * Similarly, we can extend SetLum with an extra argument that is just passed
 * on to ClipColor:
 *
 *   r * SetLum ( C, l, a)
 *
 *   = r * ClipColor ( C + l - Lum (C), a)
 *
 *   = ClipColor ( r * C + r * l - r * Lum (C), r * a)
 *
 *   = SetLum ( r * C, r * l, r * a)
 *
 * Finally, SetSat:
 *
 *    r * SetSat (C, s) = SetSat (x * C, r * s)
 *
 * The above holds for all non-zero x, because they x'es in the fraction for
 * C_mid cancel out. Specifically, it holds for x = r:
 *
 *    r * SetSat (C, s) = SetSat (rC, rs)
 *  
 */

/* So, for the non-separable PDF blend modes, we have (using s, d for non-premultiplied
 * colors, and S, D for premultiplied:
 *
 *   Color:
 *
 *     a_s * a_d * B(s, d)
 *   = a_s * a_d * SetLum (S/a_s, Lum (D/a_d), 1)
 *   = SetLum (S * a_d, a_s * Lum (D), a_s * a_d)
 *
 *
 *   Luminosity:
 *
 *     a_s * a_d * B(s, d)
 *   = a_s * a_d * SetLum (D/a_d, Lum(S/a_s), 1)
 *   = SetLum (a_s * D, a_d * Lum(S), a_s * a_d)
 *
 *
 *   Saturation:
 *
 *     a_s * a_d * B(s, d)
 *   = a_s * a_d * SetLum (SetSat (D/a_d, Sat (S/a_s)), Lum (D/a_d), 1)
 *   = SetLum (a_s * a_d * SetSat (D/a_d, Sat (S/a_s)), a_s * Lum (D), a_s * a_d)
 *   = SetLum (SetSat (a_s * D, a_d * Sat (S), a_s * Lum (D), a_s * a_d))
 *
 *   Hue:
 *
 *     a_s * a_d * B(s, d)
 *   = a_s * a_d * SetLum (SetSat (S/a_s, Sat (D/a_d)), Lum (D/a_d), 1)
 *   = a_s * a_d * SetLum (SetSat (a_d * S, a_s * Sat (D)), a_s * Lum (D), a_s * a_d)
 *
 */

A careful review of both the code in the branch and the math in the
comment would be appreciated.

The things remaining to be done on top of that branch:

        - Review that what I did makes sense

        - Use floating point in the HSL modes

        - Getting rid of component alpha versions of HSL and
          return_if_fail()ing if you to use them with a component
          alpha mask. [1]

        - There is an "#undef Set" which should be "#undef Sat"

Soren


[1] They could be done in ways that would be mathematically consistent
with the other blend modes, but I don't know that it would make any
visual sense. I could be convinced otherwise, though.


More information about the cairo mailing list