Hydra
Hydra

Reputation: 373

Setting an alpha on a pygame surface

I'm creating a GUI library with pygame. I'd like to support transparent surfaces as well as transparent rects. The transparent rects work when I use self.original_image.set_alpha(100). I'm converting the pygame surface so it can accept an alpha with surface.convert(), however the image ends up with a black background and some distortion when I do this. surface.convert_alpha() doesn't seem to have any effect.

class UIComponent(pg.sprite.Sprite):

    def __init__(self, width=10, height=10, visible=True):
        pg.sprite.Sprite.__init__(self)
        self.width_percent, self.height_percent = 0, 0
        self.x_offset, self.y_offset = 0, 0
        self.alignments = []
        self.original_image = pg.Surface((width, height))
        self.image = self.original_image
        self.original_image.set_alpha(100)
        self.rect = self.image.get_rect()

    def set_image(self, surface):
        self.original_image = surface.convert()
        self.image = self.original_image

What the surface looks like after surface.convert() This is what the image looks like when I use surface.convert()

What the actual image looks like. This is what the image actually looks like.

EDIT: Request for the code that interacts with UIComponent to create the sword.

self.slot_icon = UILib.UIComponent()
self.slot_icon.set_image(win.icon)
self.slot_icon.add_alignment(self.slot_icon.relative_width)
self.slot_icon.add_alignment(self.slot_icon.height_ratio_constraint)
self.slot_icon.width_percent = 0.03
self.slot_icon.add_alignment(self.slot_icon.center_x)
self.slot_icon.add_alignment(self.slot_icon.center_y)

In win:

def __init__(self):
    self.s_width = 0
    self.s_height = 0
    self.screen = pg.display.set_mode()
    self.icon = pg.image.load("icon.png")

Upvotes: 0

Views: 2173

Answers (1)

sloth
sloth

Reputation: 101162

There are three different kinds of transparency supported in pygame: colorkeys, surface alphas, and pixel alphas; and you can't easily mix surface alphas and pixel alphas

If you want to use set_alpha to set the transparency of the entire Surface, you use surface alpha (that means setting, well, the alpha value of the entire surface).

But the image you want to use is a PNG which uses pixel alpha (that means each pixel has its own alpha value); so you should call convert_alpha on the Surface after loading so the image.

If you want to change the surface alpha of such an image, there are basically two ways:

Option 1: use a colorkey

import pygame
import itertools

class UIComponent(pygame.sprite.Sprite):

    def __init__(self, width=10, height=10, visible=True):
        pygame.sprite.Sprite.__init__(self)
        self.width_percent, self.height_percent = 0, 0
        self.x_offset, self.y_offset = 0, 0
        self.alignments = []
        self.original_image = pygame.Surface((width, height))
        self.image = self.original_image
        self.rect = self.image.get_rect()

    def set_image(self, surface):
        self.original_image = surface
        self.image = pygame.Surface(surface.get_size())
        self.image.fill((1,2,3))
        self.image.set_colorkey((1,2,3))
        self.image.blit(self.original_image.copy(), (0, 0))
        self.rect = self.image.get_rect(center=self.rect.center)
        
def main():
    pygame.init()
    screen = pygame.display.set_mode((200, 200))
    
    comp = UIComponent()
    comp.set_image(pygame.transform.scale2x(pygame.image.load('sword.png').convert_alpha()))
    comp.rect.center = screen.get_rect().center
    
    sprites = pygame.sprite.Group(comp)
    E = pygame.USEREVENT + 1
    alphas = itertools.cycle((10, 100, 200, 255))
    pygame.time.set_timer(E, 1000)
    while True:
        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                return
            if e.type == E:
                comp.image.set_alpha(next(alphas))
        screen.fill((30, 30, 30))
        sprites.update()
        sprites.draw(screen)
        pygame.display.flip()
        
main()

Option 2: use a temporary surface and BLEND_RGBA_MULT to "apply" the transparency:

import pygame
import itertools

class UIComponent(pygame.sprite.Sprite):

    def __init__(self, width=10, height=10, visible=True):
        pygame.sprite.Sprite.__init__(self)
        self.width_percent, self.height_percent = 0, 0
        self.x_offset, self.y_offset = 0, 0
        self.alignments = []
        self.original_image = pygame.Surface((width, height))
        self.image = self.original_image
        self.rect = self.image.get_rect()

    def set_image(self, surface):
        self.original_image = surface
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect(center=self.rect.center)

    def set_alpha(self, alpha_value):
        tmp = pygame.Surface(self.rect.size, pygame.SRCALPHA)
        tmp.fill((255, 255, 255, alpha_value))
        self.image = self.original_image.copy()
        self.image.blit(tmp, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
        
def main():
    pygame.init()
    screen = pygame.display.set_mode((200, 200))
    
    comp = UIComponent()
    comp.set_image(pygame.transform.scale2x(pygame.image.load('sword.png').convert_alpha()))
    comp.rect.center = screen.get_rect().center
    
    sprites = pygame.sprite.Group(comp)
    E = pygame.USEREVENT + 1
    alphas = itertools.cycle((10, 100, 200, 255))
    pygame.time.set_timer(E, 1000)
    while True:
        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                return
            if e.type == E:
                comp.set_alpha(next(alphas))
        screen.fill((30, 30, 30))
        sprites.update()
        sprites.draw(screen)
        pygame.display.flip()
        
main()

The result is the same:

enter image description here

Upvotes: 1

Related Questions