pygame is
Python
Simple DirectMedia Layer
 
 
pygame.org is
Site Swing
Wiki

ShadowEffects

      
Search:  
 
 

This module requires Pygame 1.8 and either Numeric or NumPy.

# shadows.py module
"""Tools for adding a shadow effect to pygame images
 
Exports make_shadow, make_shadow_opaque, place_shadow, add_shadow.
 
pygame.display.set_mode() must be called before this module's functions
can be used.
"""
import pygame
_arraytype = pygame.surfarray.get_arraytype()
if _arraytype == 'numeric':
    from Numeric import UInt8 as uint8, minimum, array, Float32 as float32, Int32 as int32
elif _arraytype == 'numpy':
    from numpy import uint8, minimum, array, float32, int32
else:
    raise TypeError("Unrecognized surfarray array type %s" % _arraytype)
 
def make_shadow(image, ambience=None):
    """Return a shadow representation of image for a given ambient lighting
 
    image - foreground image.
    ambience (optional) - 0.0 to 1.0: Ambient light ratio. 0.0 gives a
                          totally black shadow while 1.0 gives no shadow.
                          Defaults to 0.0 .
    """
    if ambience is None:
        ambience = 0.0
    elif not (0.0 <= ambience <= 1.0):
        raise ValueError("ambience must be between 0.0 and 1.0 inclusive")
    if image.get_masks()[3] != 0:
        image_alpha = pygame.surfarray.pixels_alpha(image)
        if ambience > 0.0:
            shadow_alpha = (image_alpha *
                            (1.0 - ambience)).astype(uint8)
        else:
            shadow_alpha = image_alpha
    elif image.get_colorkey() is not None:
        image_alpha = pygame.surfarray.array_colorkey(image)
        image.unlock(); image.unlock()  # pygame 1.7 bug (fixed in 1.8).
        surface_alpha = image.get_alpha()
        if surface_alpha is not None:
            # Do what array_colorkey should have done: use surface alpha!
            minimum(image_alpha, surface_alpha, image_alpha)
        if ambience > 0.0:
            shadow_alpha = (image_alpha *
                            (1.0 - ambience)).astype(uint8)
        else:
            shadow_alpha = image_alpha
    else:
        image_alpha = image.get_alpha()
        if image_alpha is None:
            image_alpha = 255
        shadow_alpha = int(image_alpha * (1.0 - ambience))
    shadow = image.convert_alpha()
    shading = pygame.Surface(shadow.get_size(), pygame.SRCALPHA, 32)
    pygame.surfarray.pixels_alpha(shading)[...] = image_alpha
    shadow.blit(shading, (0, 0))
    pygame.surfarray.pixels_alpha(shadow)[...] = shadow_alpha
    return shadow
 
def make_shadow_opaque(size, ambience=None, pixel_size=None):
    """Return a retangular shadow for the given ambient lighting
 
    size - (width, height): shadow dimensions.
    ambience (optional) - 0.0 to 1.0: Ambient light ratio. 0.0 gives a
                          totally black shadow while 1.0 gives no shadow.
                          Defaults to 0.0 .
    pixel_size (optional) - bits per pixel - defaults to screen value.
 
    This version is provided for performance.
    """
    if ambience is None:
        ambience = 0.0
    if pixel_size is None:
        rest = ()
    else:
        rest = (pixel_size,)
    shadow = pygame.Surface(size, 0, rest)
    shadow.set_alpha(255 * (1.0 - ambience))
    return shadow
    
def place_shadow(image, shadow, shadow_offset):
    """Return a surface that combines the image and shadow
 
    image - foreground image.
    shadow - image shadow.
    shadow_offset - (dx, dy) amount, in pixel, to shift
                    shadow center relative to image center.
    """
    image_rect = image.get_rect()
    shadow_rect = shadow.get_rect()
    shadow_rect.center = image_rect.center
    shadow_rect.move_ip(shadow_offset)
    rect = image_rect.union(shadow_rect)
    result = pygame.Surface(rect.size, pygame.SRCALPHA, 32)
    result.blit(shadow, (shadow_rect.left - rect.left,
                         shadow_rect.top - rect.top))
    result.blit(image, (image_rect.left - rect.left,
                        image_rect.top - rect.top))
    return result
 
def add_shadow(image, shadow_offset, shadow_scale=None, ambience=None):
    """Return a copy of image with a shadow added
 
    image - a surface with or without alpha or colorkey.
    shadow_offset - (dx, dy) amount, in pixel, to shift
                    shadow center relative to image center.
    shadow_scale (optional) - amount by which to scale the shadow.
                              defaults to 1.0 (no scaling).
    ambience (optional) - 0.0 to 1.0: Ambient light ratio. 0.0 gives a
                          totally black shadow while 1.0 gives no shadow.
                          Defaults to 0.0 .
    """
    if (image.get_flags() & pygame.SRCALPHA or
        image.get_colorkey() is not None):
        shadow = make_shadow(image, ambience)
        if shadow_scale is not None:
            size = (array(shadow.get_size(), float32) * shadow_scale).astype(int32)
            shadow = pygame.transform.smoothscale(shadow, size)
    else:
        size = (array(image.get_size(), float32) * shadow_scale).astype(int32)
        if shadow_scale is None:
            shadow_scale = 1.0
        shadow = make_shadow_opaque(size, ambience, image.get_bitsize())
    return place_shadow(image, shadow, shadow_offset)
 
__all__ = ['make_shadow', 'make_shadow_opaque', 'place_shadow', 'add_shadow']

Manipulating a surface's alpha values is one use for Pygame's surfarray module and an array package like Numeric or NumPy. The alpha values of a surface can be isolated as a two dimensional array of bytes.

image_alpha = pygame.surfarray.pixels_alpha(image)
Array operations can be performed on the alpha bytes.
shadow_alpha = (image_alpha *
                            (1.0 - ambience)).astype(uint8)
This multiplies all alpha bytes in the array by a floating point value. The intermediate floating point array is then cast into the final array of single byte integers. Finally, a surface's alpha values can be replaced with new values using an assignment to an array slice.
pygame.surfarray.pixels_alpha(shadow)[...] = shadow_alpha
 
Below is a demonstration program that adds shadows to text.
 
<code class="python">
import pygame, sys
try:
    pygame.surfarray.use_arraytype(sys.argv[1])
except IndexError:
    pass
except ValueError:
    print ("Unknown array type %s. Valid types are %s." %
           (sys.argv[1], ", ".join(pygame.surfarray.get_arraytypes())))
    sys.exit()
from pygame.locals import *
from shadows import *
 
def main():
    screen_size = (400, 200)
    pygame.init()
    screen = pygame.display.set_mode(screen_size)
    screen.fill((255, 255, 200, 255))
    rect = screen.get_rect()
    for i in range(3):
        rect.inflate_ip(-60, -60)
        pygame.draw.rect(screen, Color('gray'), rect, 1)
    pygame.display.flip()
 
    font = pygame.font.SysFont([], 24)
    ambience = 0.4
    textA = font.render("Per-pixel alpha, ambience %.1f" % ambience,
                        True,
                        Color('blue'))
    labelA = add_shadow(textA, (20, 10), shadow_scale=0.8, ambience=ambience)
 
    ambience = 0.2
    alpha = 180
    textB = font.render("Surface alpha %i, ambience %.1f" % (alpha, ambience),
                        True,
                        Color('red'), Color('white'))
    textB.set_alpha(alpha)
    labelB = add_shadow(textB, (20, 10), shadow_scale=0.8, ambience=ambience)
 
    ambience = 0.2
    textC = font.render("No alpha, ambience %.1f" % ambience,
                        True,
                        Color('white'), Color('black'))
    labelC = add_shadow(textC, (20, 10), shadow_scale=0.8, ambience=ambience)
 
    ambience = 0.8
    textD = font.render("Colorkey, ambience %.1f" % ambience,
                        True,
                        Color('white'), Color('black'))
    textD.set_colorkey(Color('white'))
    labelD = add_shadow(textD, (20, 10), shadow_scale=0.8, ambience=ambience)
 
    screen.blit(labelA, (50, 20))
    screen.blit(labelB, (50, 67))
    screen.blit(labelC, (50, 114))
    screen.blit(labelD, (50, 160))
 
    pygame.event.set_blocked(MOUSEMOTION)
    repeat = 1
    while repeat:
        for e in pygame.event.get():
            if e.type in [pygame.QUIT, pygame.MOUSEBUTTONDOWN]:
            	repeat = 0
                break
            elif e.type == pygame.KEYDOWN:
                key = e.key
                if key == K_q or key == K_ESCAPE:
                    repeat = 0
                    break
    
        pygame.display.flip()
 
if __name__ == '__main__':
    main()

This is the sampling of shadow effects it displays.

spotlight

 
our projects
pygame.org welcomes all python game, art, music, sound, video and multimedia projects. If they use pygame or not.
 
recent releases
Apr 16, 2014


Apr 13, 2014

Apr 9, 2014

Mar 18, 2014


Mar 15, 2014


Mar 14, 2014

Mar 13, 2014

Mar 11, 2014

Mar 9, 2014

... more!
 
for pygame related questions, comments, and suggestions, please see help (lists, irc)