tags:

views:

77

answers:

2

I have two images:

Mask Texture

I'd like to essentially 'cut out' the black shape from the texture tile so that I end up with something along these lines:

Cutout

Except transparent around the shape. Is this possible using pygame? This example I had to create in GIMP.

Additionally, would it be too performance-heavy to do this for every frame for a few sprites in a real-time environment? (30+ frames per second)

+1  A: 

Hi,

I made a solution, however it is not the best either for speed either for beauty. You can use double blitting with setting colorkeys for transparency. In that way the mask should have only two colors: black and white. Note that you can't use this for images with per pixel alpha (RGBA) only for RGB images. Other restriction is that it is recommended that the size of the texture and the mask image is the same (if not you should use areas for blitting).

In words, step by step:

  • create a surface with the mask. background should be white (255,255,255), the masked parts should be black (0,0,0)
  • create or load the texture into a surface
  • set a transparency colorkey for the mask for the black color
  • blit the mask onto the texture (or onto a copy of the texture). at this point the black parts of the mask haven't blitted to the texture because we set the black color to transparent with the set_colorkey method
  • now set the colorkey of the texture (or the copy of the texture) to white. remember that our current texture surface has white and textured parts.
  • blit the texture to the screen. the white parts won't be blitted due to we have set it to transparent with the colorkey

Code sample:

#!/usr/bin/python
# -*- coding:utf8 -*-

import pygame, sys

#init pygame
pygame.init()

#init screen
screen=pygame.display.set_mode((800,600))
screen.fill((255,0,255))

#loading the images
texture=pygame.image.load("texture.jpg").convert()
texture_rect=texture.get_rect()
texture_rect.center=(200,300)
mask=pygame.Surface((texture_rect.width,texture_rect.height)) # mask should have only 2 colors: black and white
mask.fill((255,255,255))
pygame.draw.circle(mask,(0,0,0),(texture_rect.width/2,texture_rect.height/2),int(texture_rect.width*0.3))
mask_rect=mask.get_rect()
mask_rect.center=(600,300)

tmp_image=texture.copy() # make a copy of the texture to keep it unchanged for future usage
mask.set_colorkey((0,0,0)) # we want the black colored parts of the mask to be transparent
tmp_image.blit(mask,(0,0)) # blit the mask to the texture. the black parts are transparent so we see the pixels of the texture there

tmp_rect=tmp_image.get_rect()
tmp_rect.center=(400,300)
tmp_image.set_colorkey((255,255,255))
screen.blit(texture,texture_rect)
screen.blit(mask,mask_rect)
screen.blit(tmp_image,tmp_rect)

pygame.display.flip()

while 1:
    event=pygame.event.wait()
    if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_q]):
        sys.exit()

I recommend not to use jpg as mask because of its lossy format, I recommend bmp or png (bmp is better). Remember it not uses alpha so the edges won't be anti-aliased so it is not a nice solution in 2010 :)

Here is the screenshot of the result: masked blitting

Edit: Hi again,

I made some tests with the blitting with BLEND_ADD and the results was promising. Here is the code:

import pygame, sys

#init pygame
pygame.init()

#init screen
screen=pygame.display.set_mode((800,600))
screen.fill((255,0,255))

#loading the images
texture=pygame.image.load("texture.jpg").convert_alpha()
texture_rect=texture.get_rect()
texture_rect.center=(200,300)
mask=pygame.image.load("mask2.png").convert_alpha()
mask_rect=mask.get_rect()
mask_rect.center=(600,300)

textured_mask=mask.copy()
textured_rect=textured_mask.get_rect()
textured_rect.center=400,300

textured_mask.blit(texture,(0,0),None,pygame.BLEND_ADD)

screen.blit(texture,texture_rect)
screen.blit(mask,mask_rect)
screen.blit(textured_mask,textured_rect)

pygame.display.flip()

while 1:
    event=pygame.event.wait()
    if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_q]):
        sys.exit()

And the result: result

With this solution you can get per pixel alpha texturing. Please note that the mask should be an image with alpha channel so jpg could not be used. The best to use is png.

Texture image (texture.jpg): texture

Mask image (mask2.png): mask

sipiatti
Thanks for this!
agscala
+2  A: 

If you're using images with a pre-rendered alpha and additive blending, you can get alpha-blended masks:

import pygame
import sys

screen = pygame.display.set_mode( (800, 600) )
pygame.init()

background = pygame.image.load( "background.png" )
mask = pygame.image.load( "mask.png" )

# Create a surface to hold the masked image
masked_image = pygame.surface.Surface( background.get_size(), 0, mask )
# Blit the texture normally
masked_image.blit( background, (0,0) )
# Multiply by the pre-rendered, inverted mask
masked_image.blit( mask, (0,0), None, pygame.BLEND_MULT )

# masked_image now holds the 'cutout' of your texture blended onto a black
# background, so we need to blit it as such, i.e., using additive blending.
screen.fill( (0, 0, 0) )
screen.blit( masked_image, (10, 10), None, pygame.BLEND_ADD )

pygame.display.flip()

while True:
  event=pygame.event.wait()
  if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_q]):
    sys.exit()

Where background.png and mask.png are:

background.png + mask.png = result

Doches
Hey thanks. I'm going to give this a try tonight.
agscala