2012-04-15 7 views
0

Ich habe kairo + RSVG eine SVG-Datei in pygame Rendering. Aber die Farbkanäle sind falsch.falsche Farbkanäle, pygame kairo RSVG Zeichnung

mit lion.svg Prüfung

Aber Bild ist: enter image description here

Ich glaube, ich meine RGBA-Kanal, um getauscht haben, (Er ist rosa, nicht gelb). aber mir ist nicht klar, wie es funktioniert. Hier ist mein Code, (der sonst richtig Rendering ist.)

Vielleicht pygame.display.set_mode(...) oder pygame.image.frombuffer(...) ist das relevante Problem?

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()  

Antwort

1

In der Tat - die Byte-Reihenfolge für jeden Kanal unterscheidet sich von Kairo zu Pygame. Sie können entweder mit dem Array jonglieren, bevor es in einen String umzuwandeln, um die Daten in der richtigen Reihenfolge für pygame zu schreiben (Sie würden die grünen und blauen Daten für jedes Pixel tauschen müssen) - oder eine aditional Abhängigkeit verwenden: Python PIL, um die verschiedenen Daten bei nativen Geschwindigkeiten richtig zu verarbeiten.

Es ist ein Snippet für das in-Website 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') 

das ganze Rezept prüfen auf: http://www.pygame.org/wiki/CairoPygame

Wenn Sie eine aditional Abhängigkeit (PIL in diesem Fall) nicht hinzufügen möchten, Pythons native Arrays erlauben Slice-Zuweisung - mit denen ist es möglich, die Daten Ihrer blauen und grünen Kanäle bei nativen Geschwindigkeiten zu tauschen - versuchen Sie dieses Swapping (vielleicht einige tunning :-) das ist, was für mich funktionierte nach dem Laden Ihres Bildes oben als PNG-Datei, nicht aus einem Cairo-Kontext)

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") 
+0

1] ich Ihre Nicht-PIL Lösung lief. Aber es macht nichts anderes, wenn ich den Scheibenblock ein-/ausschalten. (Ich habe nicht als .png als Zwischenschritt gespeichert.) 2] Würde 'blue = data [1 :: 4]' das Rot referenzieren, da es in ARGB ist? Es sei denn, ich missverstehe es. – ninMonkey

1

Wenn das Array Slicing nicht funktioniert, kann ich den PIL Ansatz anbieten?

Es wurde inspiriert von der gleichen Pygame recipe jsbueno erwähnt, aber ich räumte auf und organisiert zu einer in sich geschlossenen Funktion. Es nimmt einen Dateinamen als Argument und gibt pygame.Surface genau wie pygame.image.load() zurück.

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") 
1

Wenn Sie PIL nicht verwenden möchten, die richtige Array Slicing ist:

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") 
+1

Hallo @ Simon Lopez. Willkommen bei SO. Danke für deine Antwort. Übrigens, können Sie bitte weitere Details darüber, was Sie geändert haben und warum, bitte hinzufügen? –

+0

Hallo @GustavoStraube, ich habe den Farbwechsel behoben. Die vorherige Antwort verwendete die guten Indizes nicht. –