#!/usr/bin/env python # -*- coding: utf-8 -*- """(Un-)Stretch an image to all four sides. A basic 2D animation effect. Requires Pygame_ and Numeric_. That the latter is no longer actively developed, but Pygame doesn't support the newer NumPy yet. If you are using Windows and Python 2.5, you might want to try out VPython_. Its installer for that constellation includes Numeric, but a separate Numeric Windows installer seems not to be available for Python 2.5. .. _Pygame: http://www.pygame.org/ .. _Numeric: http://numpy.scipy.org/ .. _VPython: http://www.vpython.org/ Known issues ------------ - The image moves a little after the first half of an animation when stretching from the left or the top. :Copyright: 2007 Jochen Kupperschmidt :Date: 21-Sep-2007 :License: MIT """ from optparse import OptionParser from sys import exit from time import sleep import Numeric as num import pygame from pygame import surfarray class Stretcher(object): """(Un-)Stretch an image.""" # The pause between each animation step. interval = 0.0025 def __init__(self, screen, image): self.screen = screen self.image_array = num.array(surfarray.array2d(image)) # ``xrange`` objects don't need to be recreated since iterating over # them starts at the beginning like lists, and unlike generators. self.width_range = xrange(image.get_rect().width) self.height_range = xrange(image.get_rect().height) self.clock = pygame.time.Clock() def get_new_image_array(self): return self.image_array.copy() def display(self, img_arr): self.clock.tick(100) check_events() surfarray.blit_array(self.screen, img_arr) pygame.display.update() sleep(self.interval) # horizontal animation def _from_left(self, range_): img_arr = self.get_new_image_array() for border in range_: img_arr[:border] = self.image_array[border] self.display(img_arr) def from_left(self): # Fade in and out. self._from_left(reversed(self.width_range)) self._from_left(self.width_range) def _from_right(self, range_): img_arr = self.get_new_image_array() for border in range_: img_arr[border:] = self.image_array[border] self.display(img_arr) def from_right(self): # Fade in and out. self._from_right(self.width_range) self._from_right(reversed(self.width_range)) # vertical animation def _from_top(self, outer, inner): img_arr = self.get_new_image_array() for border in outer: for col in inner: img_arr[col][:border] = self.image_array[col][border] self.display(img_arr) def from_top(self): # Fade in and out. self._from_top(reversed(self.height_range), self.width_range) self._from_top(self.height_range, self.width_range) def _from_bottom(self, outer, inner): img_arr = self.get_new_image_array() for border in outer: for col in inner: img_arr[col][border:] = self.image_array[col][border] self.display(img_arr) def from_bottom(self): # Fade in and out. self._from_bottom(self.height_range, self.width_range) self._from_bottom(reversed(self.height_range), self.width_range) def check_events(): """Process events and react on exit triggers.""" for event in pygame.event.get(): if (event.type == pygame.QUIT) or \ ((event.type == pygame.KEYDOWN) and (event.key == pygame.K_ESCAPE)): exit() if __name__ == '__main__': # Take image filename as sole argument. parser = OptionParser(usage='%prog <image filename>') args = parser.parse_args()[1] if len(args) != 1: parser.print_help() parser.exit() filename = args[0] pygame.init() image = pygame.image.load(filename) # Prepare display. screen = pygame.display.set_mode(image.get_rect().size) screen.fill((0, 0, 0)) # Prepare stretcher and stretch forwards and backwards in all directions. stretcher = Stretcher(screen, image.convert()) for method_name in ('from_left', 'from_right', 'from_top', 'from_bottom'): getattr(stretcher, method_name)() sleep(0.1)