Rendu SVG dans une application PyGame
Question
Dans une application pyGame , je souhaite effectuer le rendu des widgets d'interface graphique sans résolution décrits dans SVG.
Quel outil et / ou bibliothèque puis-je utiliser pour atteindre cet objectif?
(J'aime le kit d'outils de l'interface graphique OCEMP , mais il semble dépendre de l'image pour son rendu.)
La solution
Ceci est un exemple complet qui combine les conseils d’autres personnes ici. Il devrait rendre un fichier appelé test.svg à partir du répertoire en cours. Il a été testé sur Ubuntu 10.10, python-cairo 1.8.8, python-pygame 1.9.1, python-rsvg 2.30.0.
#!/usr/bin/python
import array
import math
import cairo
import pygame
import rsvg
WIDTH = 512
HEIGHT = 512
data = array.array('c', chr(0) * WIDTH * HEIGHT * 4)
surface = cairo.ImageSurface.create_for_data(
data, cairo.FORMAT_ARGB32, WIDTH, HEIGHT, WIDTH * 4)
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
svg = rsvg.Handle(file="test.svg")
ctx = cairo.Context(surface)
svg.render_cairo(ctx)
screen = pygame.display.get_surface()
image = pygame.image.frombuffer(data.tostring(), (WIDTH, HEIGHT),"ARGB")
screen.blit(image, (0, 0))
pygame.display.flip()
clock = pygame.time.Clock()
while True:
clock.tick(15)
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
Autres conseils
La question est assez ancienne, mais 10 ans se sont écoulés et il existe une nouvelle possibilité qui fonctionne et ne nécessite plus librsvg
. Il existe un enveloppe Cython sur la bibliothèque nanosvg et cela fonctionne:
from svg import Parser, Rasterizer
def load_svg(filename, surface, position, size=None):
if size is None:
w = surface.get_width()
h = surface.get_height()
else:
w, h = size
svg = Parser.parse_file(filename)
rast = Rasterizer()
buff = rast.rasterize(svg, w, h)
image = pygame.image.frombuffer(buff, (w, h), 'ARGB')
surface.blit(image, position)
J'ai trouvé la solution Cairo / rsvg trop compliquée à installer car les dépendances sont difficiles à installer.
Je réalise que cela ne répond pas exactement à votre question, mais il existe une bibliothèque appelée Squirtle qui rendra les fichiers SVG en utilisant soit Pyglet, soit PyOpenGL.
pygamesvg semble faire ce que vous voulez (bien que je ne l'aie pas essayé).
Cairo ne peut pas rendre SVG immédiatement. Il semble que nous devions utiliser librsvg.
Je viens de trouver ces deux pages:
Quelque chose comme cela devrait probablement fonctionner (convertissez test.svg en test.png ):
import cairo
import rsvg
WIDTH, HEIGHT = 256, 256
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
ctx = cairo.Context (surface)
svg = rsvg.Handle(file="test.svg")
svg.render_cairo(ctx)
surface.write_to_png("test.png")
Le dernier commentaire a été bloqué lorsque je l'ai exécuté car svg.render_cairo () attend un contexte cairo et non une surface cairo. J'ai créé et testé la fonction suivante et elle semble fonctionner correctement sur mon système.
import array,cairo, pygame,rsvg
def loadsvg(filename,surface,position):
WIDTH = surface.get_width()
HEIGHT = surface.get_height()
data = array.array('c', chr(0) * WIDTH * HEIGHT * 4)
cairosurface = cairo.ImageSurface.create_for_data(data, cairo.FORMAT_ARGB32, WIDTH, HEIGHT, WIDTH * 4)
svg = rsvg.Handle(filename)
svg.render_cairo(cairo.Context(cairosurface))
image = pygame.image.frombuffer(data.tostring(), (WIDTH, HEIGHT),"ARGB")
surface.blit(image, position)
WIDTH = 800
HEIGHT = 600
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
screen = pygame.display.get_surface()
loadsvg("test.svg",screen,(0,0))
pygame.display.flip()
clock = pygame.time.Clock()
while True:
clock.tick(15)
event = pygame.event.get()
for e in event:
if e.type == 12:
raise SystemExit
Sur la base d'autres réponses, voici une fonction permettant de lire un fichier SVG dans une image pygame - y compris la correction de l'ordre des canaux de couleur et la mise à l'échelle:
def pygame_svg( svg_file, scale=1 ):
svg = rsvg.Handle(file=svg_file)
width, height= map(svg.get_property, ("width", "height"))
width*=scale; height*=scale
data = array.array('c', chr(0) * width * height * 4)
surface = cairo.ImageSurface.create_for_data( data, cairo.FORMAT_ARGB32, width, height, width*4)
ctx = cairo.Context(surface)
ctx.scale(scale, scale)
svg.render_cairo(ctx)
#seemingly, cairo and pygame expect channels in a different order...
#if colors/alpha are funny, mess with the next lines
import numpy
data= numpy.fromstring(data, dtype='uint8')
data.shape= (height, width, 4)
c= data.copy()
data[::,::,0]=c[::,::,1]
data[::,::,1]=c[::,::,0]
data[::,::,2]=c[::,::,3]
data[::,::,3]=c[::,::,2]
image = pygame.image.frombuffer(data.tostring(), (width, height),"ARGB")
return image