неправильные цветовые каналы, рисунок pygame cairo rsvg

У меня есть cairo+rsvg, отображающий файл .svg в pygame. Но цветовые каналы неверны.

тестирование с помощью 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()    

person ninMonkey    schedule 15.04.2012    source источник


Ответы (3)


Действительно - порядок байтов для каждого канала отличается от cairo до pygame. Вы можете либо жонглировать массивом, прежде чем преобразовать его в строку, чтобы опубликовать данные в правильном порядке для pygame (вам нужно будет поменять местами зеленые и синие данные для каждого пикселя), или использовать дополнительную зависимость: PIL Python, для правильной обработки различных данных на исходных скоростях.

Для этого на сайте 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, а не из каирского контекста)

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")
person jsbueno    schedule 16.04.2012
comment
1] Я запустил ваше решение без PIL. Но ничего не меняется, если я включаю/выключаю блок ломтиков. (Я не сохранял в формате .png в качестве промежуточного шага.) 2] Будет ли blue = data[1::4] ссылаться на красный цвет, поскольку он находится в формате ARGB? Если я не ошибаюсь. - person ninMonkey; 22.04.2012

Если нарезка массива не работает, могу ли я предложить подход PIL?

Он был вдохновлен тем же рецептом Pygame, который упоминал jsbueno, но я очистил его и упорядочил -содержащая функция. Он принимает имя файла в качестве аргумента и возвращает pygame.Surface так же, как pygame.image.load().

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")
person MestreLion    schedule 15.08.2014

Если вы не хотите использовать 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")
person Simon Lopez    schedule 16.09.2015
comment
Привет @Саймон Лопес. Добро пожаловать в СО. Спасибо за Ваш ответ. Кстати, не могли бы вы добавить подробнее о том, что вы изменили и почему, пожалуйста? - person Gustavo Straube; 17.09.2015
comment
Привет @GustavoStraube, я исправил замену цвета. В предыдущем ответе не использовались хорошие индексы. - person Simon Lopez; 22.09.2015