Reputation: 14906
Given a pygame Surface filled with some pixels, I want to programmatically make an alpha-channel "window" in that image (such that the background shows through).
Obviously I realise that this can be trivially done with a bitmap editor, the point of the question is to do this on-the-fly as part of a different, more-complex operation.
So I made a sprite that switches between the "full-red" and "red-with-hole" images, and tried to dynamically make the bitmap-window by blitting a fully-transparent image onto the base-bitmap.
I think what is happening, is that PyGame is not painting the fully-transparent pixels because, well they're transparent! Is there a way to blit them such that the window is made?
I tried adding pygame.BLEND_RGBA_MIN
and others (as per the suggestion in Need to blit transparency on a surface in Pygame ), but failed to achieve the desired result.
All I ever get is a black square, centred in the red, rather than a window to the background.
import pygame
# Window size
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
FPS = 60
# background colours
INKY_BLACK = (128, 128, 128)
class HoleSprite( pygame.sprite.Sprite ):
def __init__( self ):
pygame.sprite.Sprite.__init__( self )
# Make a full-image (no hole)
self.base_image = pygame.Surface( ( 32, 32 ), pygame.SRCALPHA )
self.base_image.fill( ( 255,0,0 ) )
# Make an image with a see-through window
self.hole_image = pygame.Surface( ( 32, 32 ), pygame.SRCALPHA )
self.hole_image.fill( ( 255,0,0 ) )
self.hole = pygame.Surface( ( 10, 10 ), pygame.SRCALPHA )
self.hole.fill( ( 0, 0, 0, 255 ) ) # transparent
self.hole_image.blit( self.hole, [ 10,10, 10,10 ] ) # punch the middle out?
# sprite housekeeping
self.image = self.base_image
self.rect = self.image.get_rect()
self.rect.x = WINDOW_WIDTH // 2 # centred
self.rect.y = WINDOW_HEIGHT // 2
self.last = 0
def update( self ):
time_ms = pygame.time.get_ticks()
# FLip the images each second
if ( time_ms - self.last > 1000 ):
if ( self.image == self.hole_image ):
self.image = self.base_image
else:
self.image = self.hole_image
self.last = time_ms
### MAIN
pygame.init()
pygame.font.init()
SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )
pygame.display.set_caption("Hole Srite Test")
anims = pygame.sprite.GroupSingle()
holey = HoleSprite( )
anims.add( holey )
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
# Repaint the screen
anims.update()
WINDOW.fill( INKY_BLACK )
anims.draw( WINDOW )
pygame.display.flip()
# Update the window, but not more than 60fps
clock.tick_busy_loop( FPS )
pygame.quit()
Upvotes: 1
Views: 862
Reputation: 142631
First: you need (0,0,0,0)
instead of (0,0,0,255)
Second: you have to use fill(color, rect)
to create transparent hole directly in red rectangle.
self.hole_image.fill( (0, 0, 0, 0), (10, 10, 10, 10) )
Or you can draw transparent rectangle directly in red surface
pygame.draw.rect(self.hole_image, (0, 0, 0, 0), (10, 10, 10, 10) )
When you blit transparent image on full color image then it doesn't replace pixels but it mix both colors (transparent and full) and you get full color.
blit()
has flags BLEND_ADD
, BLEND_SUB
, BLEND_MULT
, etc. and maybe using one of this flag you could replace pixels but I never tried it.
Other information: pygame transparency
Full code:
import pygame
# Window size
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
FPS = 60
# background colours
INKY_BLACK = (128, 128, 128)
class HoleSprite( pygame.sprite.Sprite ):
def __init__( self ):
pygame.sprite.Sprite.__init__( self )
# Make a full-image (no hole)
self.base_image = pygame.Surface( ( 32, 32 ), pygame.SRCALPHA )
self.base_image.fill( ( 255,0,0 ) )
# Make an image with a see-through window
self.hole_image = pygame.Surface( ( 32, 32 ), pygame.SRCALPHA )
self.hole_image.fill( ( 255,0,0 ) )
self.hole_image.fill( (0, 0, 0, 0), (10, 10, 10, 10) )
# OR
#pygame.draw.rect(self.hole_image, (0, 0, 0, 0), (10, 10, 10, 10) )
# sprite housekeeping
self.image = self.base_image
self.rect = self.image.get_rect()
self.rect.x = WINDOW_WIDTH // 2 # centred
self.rect.y = WINDOW_HEIGHT // 2
self.last = 0
def update( self ):
time_ms = pygame.time.get_ticks()
# FLip the images each second
if ( time_ms - self.last > 1000 ):
if ( self.image == self.hole_image ):
self.image = self.base_image
else:
self.image = self.hole_image
self.last = time_ms
### MAIN
pygame.init()
pygame.font.init()
SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )
pygame.display.set_caption("Hole Srite Test")
anims = pygame.sprite.GroupSingle()
holey = HoleSprite( )
anims.add( holey )
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
# Repaint the screen
anims.update()
WINDOW.fill( INKY_BLACK )
anims.draw( WINDOW )
pygame.display.flip()
# Update the window, but not more than 60fps
clock.tick_busy_loop( FPS )
pygame.quit()
Upvotes: 3