# [cairo] How to do non premultiplied alpha <=> premultiplied alpha conversion

Thu Aug 25 18:31:27 PDT 2005

```We were discussing this on IRC:

<biesi> what's the correct way to convert a double color to an int?
* 255 and round, or * 255 and truncate?
<ds> imo, *256, truncate, clamp
<int-e> at least that's what you get if you divide 0..1 into 256 equal
intervals
<int-e> *255 and truncate will only give you 255 for exact 1.0; *255 and
round will give you 0 and 255 half as often than the other values
(assuming uniformely distributed real numbers)

This lead to a discussion how to do alpha multiplication (for
premultiplied alpha) and its inverse correctly.

Here's an idea:

Let I={0,...,255}, D=[0.0,1.0]. I represents discrete color values; D
represents the ideal, continuous color spectrum.

Given transformations. d: I -> D and i: D -> I with desired rounding
properties (more on that below), we can define

ALPHA(x, a) = i(d(x)*d(a))

and

INV_ALPHA = i(d(x)/d(a)).

The best transformation from i to d seems to be to divide D into 256 equal
length intervals; the corresponding formula is

i(x) = clamp(floor(x*256.0))

where clamp clamps values to 0..255 (see above).

Note: Formulas in this mail are C expressions; if constants are real values,
that will be denoted by using a float constant (with a .). If all involved
terms are integers, division truncates.

There are (at least) two interesting choices for d:

1) d(x) = x/255.0

This transformation allows 0.0 and 1.0 to be represented exactly in the
discrete data, which is desirable for alpha values and probably for colors
as well. Note that i(d(x)) = x for all x.

Using this definition, the following formulas can be obtained:

ALPHA(x, a)     = i(d(x)*d(a))
= clamp(floor((x/255.0)*(a/255.0)*256.0))
= clamp(floor((x*a*256.0/255.0)/255.0)))
= clamp(floor(x*a*(255.0/254+1/254.0-1/255.0)/255.0))
= clamp(floor((x*a)/254.0 - (x*a)/(254.0*255.0*255.0)))

the attached program verifies that

ALPHA(x, a)     = clamp((x*a-1)/254)

INV_ALPHA(x, a) = i(d(x)/d(a))
= clamp(floor(color/255.0)/(alpha/255.0)*256)
= clamp(color*256/alpha)

2) d(x) = (x+0.5)/256.0

This transformation minimizes the average quantization error introduced
by d. Of course, i(d(x)) = x for all x.

ALPHA(x, a)     = i(d(x)*d(a))
= clamp(floor((x+0.5)/256.0*(a+0.5)/256.0)*256.0)
= clamp(floor((x*a+0.5*x+0.5*a+0.25)/256))
= (x*a+(x+a)/2)/256

INV_ALPHA(x, a) = i(d(x)/d(a))
= clamp(floor((x+0.5)/(a+0.5)*256.0))
= clamp((512*x+256)/(2*a+1))

Another possibility is to combine the two different formulas:

3) use 1) for alpha values, 2) for color values. While this seems strange
at first, being able to represent alpha=0 and alpha=1 exactly seems to
be much more important than being able to represent color values 0 and 1
exactly.

ALPHA(x, a)     = i(d2(x)*d1(a))
= clamp(floor((x+0.5)/256.0*a/255.0*256.0))
= clamp(floor((x+0.5)*a/255.0))
= (x*a+a/2)/255

(The clamp is not necessary because d2(x)<1.0 for all x.)

INV_ALPHA(x, a) = i(d2(x)/d1(a))
= clamp(floor((x+0.5) / 256.0 / a * 255.0) * 256.0))
= clamp(floor((255.0*x+127.5)/a))
= clamp((255*x+127)/a)

3) is my favorite choice at the moment.