Skip to main content

CairoPygame — wiki

This piece of code allows you to convert a Cairo surface to a PyGame surface. It also includes a small example on how to make SVG loading work. It works with Python 2.5+ and requires relatively recent versions of pygame, pycairo and NumPy to work. For the SVG example to work, you must also have rsvg installed.

#!/usr/bin/env python
# Copyleft 2010 Niels Serup, WTFPL 2.0. Free software.

### Imports ###
import math
import pygame
import cairo
import numpy
import Image

### Constants ###
width, height = 640, 480


### Functions ###
def draw(ctx):
    ctx.set_line_width(15)
    ctx.arc(320, 240, 200, 0, 2 * math.pi)

    #                   r    g  b    a
    ctx.set_source_rgba(0.6, 0, 0.4, 1)
    ctx.fill_preserve()

    #                   r  g     b    a
    ctx.set_source_rgba(0, 0.84, 0.2, 0.5)
    ctx.stroke()

def bgra_surf_to_rgba_string(cairo_surface):
    # We use PIL to do this
    img = Image.frombuffer(
        'RGBA', (cairo_surface.get_width(),
                 cairo_surface.get_height()),
        cairo_surface.get_data(), 'raw', 'BGRA', 0, 1)

    return img.tostring('raw', 'RGBA', 0, 1)


### Body ###
# Init PyGame
pygame.display.init()
screen = pygame.display.set_mode((width, height), 0, 32)

# Create raw surface data (could also be done with something else than
# NumPy)
data = numpy.empty(width * height * 4, dtype=numpy.int8)

# Create Cairo surface
cairo_surface = cairo.ImageSurface.create_for_data(
    data, cairo.FORMAT_ARGB32, width, height, width * 4)

# Draw with Cairo on the surface
ctx = cairo.Context(cairo_surface)
draw(ctx)


##### SVG LOADING EXAMPLE #####
# Using rsvg it is possible to render an SVG file onto a Cairo
# surface. Uncomment the following lines to make it work.
#import rsvg # This will probably not work in Windows. As far as I
# know, only GNU/Linux distibutions package this Python
# module. Nevertheless, it should be easy to create a small wrapper;
# see http://www.cairographics.org/cairo_rsvg_and_python_in_windows/

# Load the file
#svg_graphics = rsvg.Handle('path/to/file.svg')

# Render the graphics onto your Cairo context
#svg_graphics.render_cairo(ctx)

# To get the SVG file's dimensions before you create a Cairo surface,
# use the following function:
#print svg_graphics.get_dimension_data()
###############################

# On little-endian machines (and perhaps big-endian, who knows?),
# Cairo's ARGB format becomes a BGRA format. PyGame does not accept
# BGRA, but it does accept RGBA, which is why we have to convert the
# surface data. You can check what endian-type you have by printing
# out sys.byteorder
data_string = bgra_surf_to_rgba_string(cairo_surface)

# Create PyGame surface
pygame_surface = pygame.image.frombuffer(
    data_string, (width, height), 'RGBA')

# Show PyGame surface
screen.blit(pygame_surface, (0,0)) 
pygame.display.flip()

clock = pygame.time.Clock()
while not pygame.QUIT in [e.type for e in pygame.event.get()]:
    clock.tick(30)
This method allows Cairo to use the same block of memory for the surface as pygame does. This is most likely the fastest method of the ones listed here, since it doesn't use an extra buffer. (Tested on Python 2.6.4, pycairo 1.8.6, Cairo 1.8.8, pygame 1.8.1, numpy 1.3.0.) (note: does not work, if numeric is installed)
#!/usr/bin/env python
import cairo
import pygame
import math

size = 400, 400

# Initialize pygame with 32-bit colors. This setting stores the pixels
# in the format 0x00rrggbb.
pygame.init()
screen = pygame.display.set_mode(size, 0, 32)

# Get a reference to the memory block storing the pixel data.
pixels = pygame.surfarray.pixels2d(screen)

# Set up a Cairo surface using the same memory block and the same pixel
# format (Cairo's RGB24 format means that the pixels are stored as
# 0x00rrggbb; i.e. only 24 bits are used and the upper 16 are 0).
cairo_surface = cairo.ImageSurface.create_for_data(
	pixels.data, cairo.FORMAT_RGB24, size[0], size[1])

# Draw a white circle to the screen using pygame.
radius = int(min(size)/2*0.7)
pos = [int(a/2) for a in size]
pygame.draw.circle(screen, (255,255,255), pos, radius)

# Draw a smaller black circle to the screen using Cairo.
context = cairo.Context(cairo_surface)
context.set_source_rgb(0, 0, 0)
context.arc(size[0]/2, size[1]/2, min(size)/2*0.5, 0, 2*math.pi)
context.fill()

# Flip the changes into view.
pygame.display.flip()

# Wait for the user to quit.
while pygame.QUIT not in [e.type for e in pygame.event.get()]:
	pass
Following works with Python 2.5, pycairo-1.4.12-1, pygame 1.8.0: Found at: http://www.pjblog.net/index.php?2006/06/23/144-using-pycairo-with-pygame-surface Written by Pierre-Jean Coudert
#!/usr/bin/env python
import cairo
import pygame
import array
import math
import sys

def draw(surface):
    x,y, radius = (250,250, 200)
    ctx = cairo.Context(surface)
    ctx.set_line_width(15)
    ctx.arc(x, y, radius, 0, 2.0 * math.pi)
    ctx.set_source_rgb(0.8, 0.8, 0.8)
    ctx.fill_preserve()
    ctx.set_source_rgb(1, 1, 1)
    ctx.stroke()
    
def input(events): 
   for event in events: 
      if event.type == pygame.QUIT: 
         sys.exit(0) 
      else: 
         print event 

#Create Cairo Surface
Width, Height = 512, 512
data = array.array('c', chr(0) * Width * Height * 4)
stride = Width * 4
surface = cairo.ImageSurface.create_for_data (data, cairo.FORMAT_ARGB32,Width, Height, stride)
#init PyGame
pygame.init()
window = pygame.display.set_mode( (Width,Height) ) 
screen = pygame.display.get_surface()
#Draw with Cairo
draw(surface)
#Create PyGame surface from Cairo Surface
image = pygame.image.frombuffer(data.tostring(),(Width,Height),"ARGB",)
#Tranfer to Screen
screen.blit(image, (0,0)) 
pygame.display.flip() 

while True: 
   input(pygame.event.get())
Note: this does *not* work with Python 2.5: http://thread.gmane.org/gmane.linux.debian.devel.bugs.general/437547/focus=14467
#!/usr/bin/env python

import cairo

w, h = 128, 128

# Setup Cairo
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
ctx = cairo.Context(surface)

# Set thickness of brush
ctx.set_line_width(15)

# Draw out the triangle using absolute coordinates
ctx.move_to(w/2, h/3)
ctx.line_to(2*w/3, 2*h/3)
ctx.rel_line_to(-1*w/3, 0)
ctx.close_path()

# Apply the ink
ctx.stroke()

# Output a PNG file
surface.write_to_png("triangle.png")

# Alias the image as a numpy array
import numpy

# This needs better than pycairo-1.2.2, eg. pycairo CVS:
# cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/cairo co pycairo
buf = surface.get_data()

a = numpy.frombuffer(buf, numpy.uint8)
a.shape = (w, h, 4)

a[:,:,2] = 255
surface.write_to_png("triangle1.png") # red triangle..

# Alias the image as a pygame surface
import pygame
from time import sleep

imsurf = pygame.image.frombuffer(buf, (w,h), "RGBA")
depth = 4*8

pygame.display.init()
surface = pygame.display.set_mode((w,h), pygame.DOUBLEBUF, depth)

done = False
while not done:
    surface.blit(imsurf, (0,0)) # blue triangle..
    sleep(0.1)
    events = pygame.event.get()
    for event in events:
        if event.type == pygame.QUIT:
            done = True
    pygame.display.flip()
If you don't have a super new version of python-cairo bindings then use this instead:
#!/usr/bin/env python
 
import cairo
 
w, h = 128, 128
 
# Setup Cairo
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
ctx = cairo.Context(surface)
 
# Set thickness of brush
ctx.set_line_width(15)
 
# Draw out the triangle using absolute coordinates
ctx.move_to(w/2, h/3)
ctx.line_to(2*w/3, 2*h/3)
ctx.rel_line_to(-1*w/3, 0)
ctx.close_path()
 
# Apply the ink
ctx.stroke()
 
 #!/usr/bin/env python
import sys, cStringIO, pygame, pygame.locals

pygame.init()

f = cStringIO.StringIO()
surface.write_to_png(f)

screen = pygame.display.set_mode((w, h))
screen.fill((255, 255, 255))

f.seek(0)
pic = pygame.image.load(f,'temp.png').convert_alpha()
screen.blit(pic, (0, 0))

pygame.display.flip()

clock = pygame.time.Clock()

while True:
    clock.tick(15)

    for event in pygame.event.get():
        if event.type == pygame.locals.QUIT:
            raise SystemExit