Introduction

The Pygame blit function for SRCALPHA to SRCALPHA surface blits has changed over the history of the package. For increased performance it is implemented as an integer arithmetic approximation of the floating point blend equation1 for alpha blitting. As such it has been refined to make its values more in line with what is expected. Unfortunately these revisions also made it prone to breakage2. So starting with Pygame 1.9 there are guarantees about the behavior of the alpha blit.

The Guarantees

For the the blit function blend(src, dst, src_alpha):

  1. 0 <= blend(c, d, a) <= 255, where c, d and a in x: 0 <= x <= 255
  2. blend(c, d, 255) = c, where c and d in x: 0 <= x <= 255
  3. blend(c, d, 0) = d, where c and d in x: 0 <= x <= 255
  4. blend(0, e, a) < e, where e and a in x: 1 <= x <= 255

The blit function for Pygame 1.9 is:
blend(s, d, a) = ((d << 8) + (s - d) * a + s) >> 8There are some special case performance enhancements, but they return the same values.

What this means

What do these mean in practice:

  1. All colors remain valid after a blit.
  2. A source pixel alpha of 255 is simply copies it to the destination pixel.
  3. A source pixel alpha of 0 is total transparency, leaving the destination pixel unchanged.
  4. Repeated blits of a black pixel will result in the destination pixel becoming black.

Notes

1 fblend(s, d, a) = (d * (255.0 - a) + s * a) / 255.0
This equation is adapted from the one used by the Python Image Library (PIL).

2 The earlier 1.8 function is:

blend(s, d, sA, dA) = (((s - d) * a) >> 8) + d, dA > 0
                    = s, dA == 0

This function has the unexpected behavior of:

blend(c, d, 255, dA) = c - 1, 1 <= c < 255, 1 <= dA <= 255

It also breaks on computer hardware where the right shift operator does not preserve the sign of its target (propagate the left most bit), as (s - d) can go negative, but the eight bit shift returns a positive.