Search code examples
pythonpygameblending

Why is alpha blending not working properly?


I'm trying to make a simple Pygame application where some colors are blended with colors under them. Here is my code:

code-listing 1:

import pygame, sys, time
from pygame.locals import *

#define constants
WINDOW_WIDTH = 600
WINDOW_HEIGHT = 600
FPS = 60
    
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), 0, 32)
    
alpha = 0
increasing = True
    
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

    #fill screen with red
    screen.fill(pygame.Color(247, 25, 0,255))
    alpha_surface = pygame.Surface((screen.get_rect().width, screen.get_rect().height))
    alpha_surface = alpha_surface.convert_alpha()
    surfaceRect = alpha_surface.get_rect()

    #fill surface with orange
    pygame.draw.rect(alpha_surface, (247, 137, 0, 255), (0, 0, 480, 480))
    #fill surface with translucent yellow
    pygame.draw.rect(alpha_surface, (220, 247, 0, alpha), (120, 120, 360, 360))
    #fill surface with green
    pygame.draw.rect(alpha_surface, (0, 247, 4), (240, 240, 240, 240))
    #fill surface with translucent blue
    pygame.draw.rect(alpha_surface, (0, 78, 247, alpha), (360, 360, 120, 120))
    screen.blit(alpha_surface, (120,120))
    
    pygame.display.update()
    clock.tick(FPS)

    if increasing:
        alpha += 1
        if alpha > 255:
            alpha = 255
            increasing = False
    else:
        alpha -= 1
        if alpha < 0:
            alpha = 0
            increasing = True

the code is supposed to make it so the yellow rectangle blends with the orange rectangle and the blue rectangle with the green rectangle. Instead I am getting something that goes from this:

figure 1: original state with yellow and blue opacity set to 0%

to this:

figure 2: final state with yellow and blue opacity set to 100%

As you can see the yellow and blue rectangles not only blend with the red rectangle (screen surface) but they also make a hole to the orange and green rectangle so that we can see the red rectangle through them.


Solution

  • If you want to blend different layers, then you have to create different pygame.Surfaces or images. A Surface can be genrated by loading an image (pygame.image) or by constructing a pygame.Surface object.

    Create a surface completely transparent Surface with per pixel alpha. Use pygame.Surface.convert_alpha to change the pixel format of an image including per pixel alphas. Fill the Surface with a transparent color (e.g pygame.Color(0, 0, 0, 0)):


    Minimal example:

    import pygame
    
    pygame.init()
    clock = pygame.time.Clock()
    screen = pygame.display.set_mode((600, 600), 0, 32)
    
    def transparentSurface(size):
        surface = pygame.Surface(size).convert_alpha()
        surface.fill((0, 0, 0, 0))
        return surface
    
    alpha, increase = 0, 1
    run = True
    while run:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        screen.fill(pygame.Color(247, 25, 0,255))
        
        alpha_surface1 = transparentSurface(screen.get_size())
        pygame.draw.rect(alpha_surface1, (247, 137, 0, 255), (120, 120, 480, 480))
    
        alpha_surface2 =  transparentSurface(screen.get_size())
        pygame.draw.rect(alpha_surface2, (220, 247, 0, alpha), (240, 240, 360, 360))
    
        alpha_surface3 =  transparentSurface(screen.get_size())
        pygame.draw.rect(alpha_surface3, (0, 247, 4), (360, 360, 240, 240) )
    
        alpha_surface4 =  transparentSurface(screen.get_size())
        pygame.draw.rect(alpha_surface4, (0, 78, 247, alpha), (480, 480, 120, 120) )
        
        screen.blit(alpha_surface1, (0,0))
        screen.blit(alpha_surface2, (0,0))
        screen.blit(alpha_surface3, (0,0))
        screen.blit(alpha_surface4, (0,0))
    
        pygame.display.update()
        
        alpha += increase
        if alpha < 0 or alpha > 255:
            increase *= -1
            alpha = max(0, min(255, alpha))
    
    pygame.quit()