[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[pygame] Dirty Rectangle Processing
I worked up a small example to illustrate the dirtyrect
processing method I mentioned earlier.
If you can make this faster, post your results here!
Note that all sprites have an alpha channel that must be maintained.
A screenshot of the output of this sample program can be seen at:
http://www.kamilche.com/images/pygame/DirtyRectsExample.jpg
The original source can be found at:
http://www.kamilche.com/images/pygame/dirtyrects.py
It's a simple program that fills the screen with moving colored boxes,
and illustrates how to toggle between fullscreen and windowed mode in a
cross-platform-compatible way. It displays the current FPS in the top
left corner.
--Kamilche
'''
Pygame Dirtyrect Processing
An example of dirtyrect processing with Pygame.
Make it faster, if you can! My speed:
604 FPS on a 2.6 gHz Windows 2000 box w/1 gig memory.
'''
import pygame
import random
import time
import os
import sys
screen = None
sprites = []
class Sprite(object):
pos = (0, 0)
size = (30, 30)
rect = None
paint = 0
velocity = (3, 2)
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
self.rect = pygame.Rect((0, 0, 0, 0))
if type(self.pos) != list:
self.pos = [random.randint(0, screen.size[0]-1),
random.randint(0, screen.size[1]-1)]
self.size = list(self.size)
self.velocity = list(self.velocity)
sprites.append(self)
self.Paint()
def Paint(self):
raise Exception("You must override the paint method!")
def Draw(self, bg, dirtyrect):
' Draw the sprite'
# Repaint if necessary
if self.paint:
self.Paint()
self.paint = 0
# Clip picture against the dirtyrect
r = self.rect.clip(dirtyrect)
if r:
# Paint the clipped rect only
sourcerect = pygame.Rect(r[0]-self.rect[0],
r[1]-self.rect[1], r.w, r.h)
bg.blit(self.pic, r, sourcerect)
def Move(self):
' Move the sprite'
newx = self.pos[0] + self.velocity[0]
newy = self.pos[1] + self.velocity[1]
if 0 <= newx <= screen.size[0]:
self.pos[0] = newx
self.SetRect() # Not paint, because pic didn't change
else:
self.velocity[0] = -self.velocity[0]
if 0 <= newy <= screen.size[1]:
self.pos[1] = newy
self.SetRect() # Not paint, because pic didn't change
else:
self.velocity[1] = -self.velocity[1]
def Timer(self, now):
self.Move()
def SetRect(self):
" Dirty this item's current location, set its rectangle based
on its"
" current size and position, and dirty it again."
if self.size[0] == 0 or self.size[1] == 0:
return
screen.Dirty(self.rect)
self.rect.topleft = self.pos
self.rect.size = self.size
screen.Dirty(self.rect)
class Square(Sprite):
def Paint(self):
self.pic = pygame.Surface(self.size, pygame.SRCALPHA,
32).convert_alpha()
self.pic.fill(self.backcolor)
self.SetRect()
class Circle(Sprite):
def Paint(self):
self.pic = pygame.Surface(self.size, pygame.SRCALPHA,
32).convert_alpha()
self.pic.fill((0, 0, 0, 0))
radius = self.size[1]/2
center = self.size[0]/2, self.size[1]/2
pygame.draw.circle(self.pic, self.backcolor, center, radius, 0)
self.SetRect()
class Text(Sprite):
text = ''
fontcolor = (255, 255, 255)
font = None
def __init__(self, **kwargs):
self.font = pygame.font.Font(pygame.font.get_default_font(), 24)
super(Text, self).__init__(**kwargs)
self.SetText(self.text)
def Paint(self):
self.pic = self.font.render(self.text, 1,
self.fontcolor).convert_alpha()
self.size = self.pic.get_size()
self.SetRect()
def SetText(self, text):
self.text = text
self.paint = 1 # Paint, because pic DID change.
def Timer(self, now):
pass
class FPS(Text):
nexttime = 0
ctr = 0
def Timer(self, now):
self.ctr += 1
if now > self.nexttime:
self.SetText("%d FPS" % self.ctr)
self.ctr = 0
self.nexttime = now + 1
class Screen(object):
size = [800, 600]
pos = [0, 0]
dirtyrects = []
backcolor = (0, 64, 0)
fullscreen = 0
bitdepth = 16
def __init__(self):
self.CreateScreen()
def Dirty(self, rect):
" Mark a rectangle on the screen as needing to be redrawn
('Dirty' it)"
if rect == None:
# Redraw entire screen
del self.dirtyrects[:]
self.dirtyrects = [self.rect]
else:
# Redraw part of screen
R = rect.clip(self.rect)
i = R.collidelist(self.dirtyrects)
if i > -1:
# Add to existing dirty rectangle
R.union_ip(self.dirtyrects[i])
self.dirtyrects[i] = (R)
else:
# Add a new dirty rectangle
self.dirtyrects.append(R)
def Draw(self):
' Main render routine'
while len(self.dirtyrects) > 0:
r = self.dirtyrects[0]
del self.dirtyrects[0]
self.screen.fill(self.backcolor, r)
for sprite in sprites:
sprite.Draw(self.screen, r)
pygame.display.update(r)
def ToggleFullscreen(self):
' Toggle fullscreen mode'
if self.fullscreen:
self.fullscreen = 0
else:
self.fullscreen = 1
self.CreateScreen()
def CreateScreen(self):
' Create the main display screen'
if self.fullscreen:
flags = pygame.FULLSCREEN
else:
flags = 0
minbitdepth = 16
bitdepth = pygame.display.mode_ok(self.size, flags, self.bitdepth)
if bitdepth < minbitdepth:
raise Exception("Your system is unable to display %d bit
color in an %d x %d window!" % (minbitdepth, self.size[0], self.size[1]))
self.screen = pygame.display.set_mode(self.size, flags, bitdepth)
self.rect = pygame.Rect(0, 0, self.size[0], self.size[1])
self.Dirty(None)
def main():
global screen
pygame.init()
colors = [(255, 0, 0, 200),
( 0, 255, 0, 200),
( 0, 0, 255, 200),
(255, 255, 0, 200),
(0, 255, 255, 200),
(255, 0, 255, 200),
(255, 128, 0, 200),
(0, 255, 128, 200),
(128, 0, 255, 200),
(255, 255, 255, 200)]
screen = Screen()
for i in range(10):
Square(backcolor = colors[i])
Circle(backcolor = (255, 255, 0, 128), size = [100, 100])
FPS(pos = [0, 0])
Text(pos = [150, 0], text = 'Hit ESC to stop, ALT-ENTER to toggle
fullscreen mode.')
quit = 0
while not quit:
for sprite in sprites[:]:
sprite.Timer(time.time())
screen.Draw()
pygame.event.pump()
for e in pygame.event.get():
if e.type == pygame.QUIT:
quit = 1
elif e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
quit = 1
elif e.key == pygame.K_RETURN:
if pygame.key.get_mods() & pygame.KMOD_ALT:
screen.ToggleFullscreen()
pygame.quit()
if sys.platform == 'win32':
os.environ['SDL_VIDEO_WINDOW_POS'] = '3,23'
os.environ['SDL_VIDEODRIVER'] = 'windib'
main()