我让cairo+rsvg在pygame中渲染一个.svg文件。但是颜色通道是错误的。
使用lion.svg进行测试
但图片是:

我想我的RGBA频道顺序被调换了(他是粉色的,不是黄色的)。但我不清楚它是如何工作的。下面是我的代码(否则就是正确的渲染)。
也许pygame.display.set_mode(...)或pygame.image.frombuffer(...)是相关的问题?
import pygame
from pygame.locals import *
import os
import cairo
import rsvg
import array
WIDTH, HEIGHT = 60,60
class Lion(object):
"""load+draw lion.svg"""
def __init__(self, file=None):
"""create surface"""
# Sprite.__init__(self)
self.screen = pygame.display.get_surface()
self.image = None
self.filename = 'lion.svg'
self.width, self.height = WIDTH, HEIGHT
def draw_svg(self):
"""draw .svg to pygame Surface"""
svg = rsvg.Handle(file= os.path.join('data', self.filename))
dim = svg.get_dimension_data()
self.width , self.height = dim[0], dim[1]
data = array.array('c', chr(0) * self.width * self.height * 4 )
cairo_surf= cairo.ImageSurface.create_for_data( data,
cairo.FORMAT_ARGB32, self.width, self.height, self.width * 4 )
ctx = cairo.Context(cairo_surf)
svg.render_cairo(ctx)
self.image = pygame.image.frombuffer(data.tostring(), (self.width,self.height), "ARGB")
def draw(self):
"""draw to screen"""
if self.image is None: self.draw_svg()
self.screen.blit(self.image, Rect(200,200,0,0))
class GameMain(object):
"""game Main entry point. handles intialization of game and graphics, as well as game loop"""
done = False
color_bg = Color('black') # or also: Color(50,50,50) , or: Color('#fefefe')
def __init__(self, width=800, height=600):
pygame.init()
self.width, self.height = width, height
self.screen = pygame.display.set_mode(( self.width, self.height ))
# self.screen = pygame.display.set_mode(( self.width, self.height ),0,32) # 32bpp for format 0x00rrggbb
# fps clock, limits max fps
self.clock = pygame.time.Clock()
self.limit_fps = True
self.fps_max = 40
self.lion = Lion()
def main_loop(self):
while not self.done:
# get input
self.handle_events()
self.draw()
# cap FPS if: limit_fps == True
if self.limit_fps: self.clock.tick( self.fps_max )
else: self.clock.tick()
def draw(self):
"""draw screen"""
self.screen.fill( self.color_bg )
self.lion.draw()
pygame.display.flip()
def handle_events(self):
"""handle events: keyboard, mouse, etc."""
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT: self.done = True
# event: keydown
elif event.type == KEYDOWN:
if event.key == K_ESCAPE: self.done = True
if __name__ == "__main__":
print """Keys:
ESC = quit
"""
game = GameMain()
game.main_loop() 发布于 2012-04-16 12:44:59
事实上,每个通道的字节顺序从cairo到pygame都是不同的。您可以在将数组转换为字符串之前处理数组,以便按照pygame的正确顺序发布数据(您必须为每个像素交换绿色和蓝色数据)-或者使用额外的依赖项:Python的PIL,以便以原生速度正确处理不同的数据。
在pygame的网站上有一个这样的代码片段:
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)
...
# 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')请访问:http://www.pygame.org/wiki/CairoPygame查看整个食谱
如果你不想添加额外的依赖(本例中是PIL),Python的原生数组允许切片分配-使用那些可以以原生速度交换蓝色和绿色通道数据的数组-尝试这种交换(可能需要一些调整:-)这是在将上面的图像加载为png文件后对我起作用的方法,而不是从cairo上下文)
def draw_svg(self):
"""draw .svg to pygame Surface"""
svg = rsvg.Handle(file= os.path.join('data', self.filename))
dim = svg.get_dimension_data()
self.width , self.height = dim[0], dim[1]
data = array.array('c', chr(0) * self.width * self.height * 4 )
cairo_surf= cairo.ImageSurface.create_for_data( data,
cairo.FORMAT_ARGB32, self.width, self.height, self.width * 4 )
ctx = cairo.Context(cairo_surf)
blue = data[1::4]
green = data[3::4]
data[1::4] = green
data[3::4] = blue
svg.render_cairo(ctx)
self.image = pygame.image.frombuffer(data.tostring(), (self.width,self.height), "ARGB")发布于 2014-08-15 15:39:29
如果数组切片不起作用,我可以提供PIL方法吗?
它的灵感来自于前面提到的Pygame recipe jsbueno,但我将其清理并组织成一个自包含的函数。它接受文件名作为参数,并像pygame.image.load()一样返回一个pygame.Surface。
import pygame # python-pygame
import rsvg # python-rsvg
import cairo # python-cairo
import PIL.Image # python-imaging
def load_svg(filename):
''' Load an SVG file and return a pygame.Surface '''
def bgra_rgba(surface):
''' Convert a Cairo surface in BGRA format to a RBGA string '''
img = PIL.Image.frombuffer(
'RGBA', (surface.get_width(), surface.get_height()),
surface.get_data(), 'raw', 'BGRA', 0, 1)
return img.tostring('raw', 'RGBA', 0, 1)
svg = rsvg.Handle(filename)
width, height = svg.props.width, svg.props.height
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
svg.render_cairo(cairo.Context(surface))
return pygame.image.frombuffer(bgra_rgba(surface), (width,height), "RGBA")发布于 2015-09-17 06:20:35
如果不想使用PIL,正确的数组切片是:
def draw_svg(self):
"""draw .svg to pygame Surface"""
svg = rsvg.Handle(file= os.path.join('data', self.filename))
dim = svg.get_dimension_data()
self.width , self.height = dim[0], dim[1]
data = array.array('c', chr(0) * self.width * self.height * 4 )
cairo_surf= cairo.ImageSurface.create_for_data( data,
cairo.FORMAT_ARGB32, self.width, self.height, self.width * 4 )
ctx = cairo.Context(cairo_surf)
svg.render_cairo(ctx)
blue = data[0::4]
red = data[2::4]
data[0::4] = red
data[2::4] = blue
self.image = pygame.image.frombuffer(data.tostring(), (self.width,self.height), "ARGB")https://stackoverflow.com/questions/10163870
复制相似问题