【问题标题】:How to embed skia python surface inside pysdl2如何在pysdl2中嵌入skia python表面
【发布时间】:2022-01-23 13:42:01
【问题描述】:

我正在尝试将skia-python 的表面嵌入到窗口中,而不是输出到图像文件。我正在使用pysdl2 使用文档中的以下代码创建窗口:

import sys
import sdl2.ext

RESOURCES = sdl2.ext.Resources(__file__, "resources")

sdl2.ext.init()

window = sdl2.ext.Window("Hello World!", size=(640, 480))
window.show()

factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE)
sprite = factory.from_image(RESOURCES.get_path("hello.bmp"))

spriterenderer = factory.create_sprite_render_system(window)
spriterenderer.render(sprite)

processor = sdl2.ext.TestEventProcessor()
processor.run(window)

sdl2.ext.quit()

这段代码是从skia的文档中创建表面的:

import skia

surface = skia.Surface(128, 128)

with surface as canvas:
    rect = skia.Rect(32, 32, 96, 96)
    paint = skia.Paint(
        Color=skia.ColorBLUE,
        Style=skia.Paint.kFill_Style)
    canvas.drawRect(rect, paint)

image = surface.makeImageSnapshot()
image.save('output.png', skia.kPNG)

现在我想要实现的是从skia部分获取image(或surface,无论哪个适用)对象并将其插入pysdl2,这样我就可以使用skia进行绘制,但我可以使用pysdl2处理窗口的事件循环'现在想避免使用 ctypes,因为我对它不太熟悉。

【问题讨论】:

    标签: python python-3.x python-3.9 skia pysdl2


    【解决方案1】:

    我放弃了在没有 ctypes 的情况下创建它,因为我们需要的只是 ctypes.byref,我现在正在导入 sdl2 而不是 sdl2.ext,后者更加 Pythonic,但也限制了一些功能这里需要。

    现在回答这个问题,我按照本指南here 操作(如果您不构建浏览器,它可能对您来说有点离题)

    所以我也从上面的指南中实现了一个足够通用的版本,你可以绘制到Window.skia_surface然后调用Window.update将skia表面复制到窗口屏幕:

    import skia
    import sdl2 as sdl
    from ctypes import byref
    
    
    def sdl_event_loop():
        event = sdl.SDL_Event()
        running = True
    
        while running:
            pending_events = sdl.SDL_PollEvent(byref(event))
            while pending_events != 0:
    
                # QUIT HANDLER
                if event.type == sdl.SDL_QUIT:
                    running = False
                    sdl.SDL_Quit()
                    break
    
                # UPDATE PENDING EVENTS
                pending_events = sdl.SDL_PollEvent(byref(event))
    
    
    class Window:
        DEFAULT_FLAGS = sdl.SDL_WINDOW_SHOWN
        BYTE_ORDER = {
            # ---------- ->   RED        GREEN       BLUE        ALPHA
            "BIG_ENDIAN": (0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff),
            "LIL_ENDIAN": (0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)
        }
    
        PIXEL_DEPTH = 32  # BITS PER PIXEL
        PIXEL_PITCH_FACTOR = 4  # Multiplied by Width to get BYTES PER ROW
    
        def __init__(self, title, width, height, x=None, y=None, flags=None):
            self.title = bytes(title, "utf8")
            self.width = width
            self.height = height
    
            # Center Window By default
            self.x, self.y = x, y
            if x is None:
                self.x = sdl.SDL_WINDOWPOS_CENTERED
            if y is None:
                self.y = sdl.SDL_WINDOWPOS_CENTERED
    
            # Override flags
            self.flags = flags
            if flags is None:
                self.flags = self.DEFAULT_FLAGS
    
            # SET RGBA MASKS BASED ON BYTE_ORDER
            is_big_endian = sdl.SDL_BYTEORDER == sdl.SDL_BIG_ENDIAN
            if is_big_endian:
                self.RGBA_MASKS = self.BYTE_ORDER["BIG_ENDIAN"]
            else:
                self.RGBA_MASKS = self.BYTE_ORDER["LIL_ENDIAN"]
    
            # CALCULATE PIXEL PITCH
            self.PIXEL_PITCH = self.PIXEL_PITCH_FACTOR * self.width
    
            # SKIA INIT
            self.skia_surface = self.__create_skia_surface()
    
            # SDL INIT
            sdl.SDL_Init(sdl.SDL_INIT_EVENTS)  # INITIALIZE SDL EVENTS
            self.sdl_window = self.__create_SDL_Window()
            sdl_event_loop()
    
        def __create_SDL_Window(self):
            window = sdl.SDL_CreateWindow(
                self.title,
                self.x, self.y,
                self.width, self.height,
                self.flags
            )
            return window
    
        def __create_skia_surface(self):
            surface_blueprint = skia.ImageInfo.Make(
                self.width, self.height,
                ct=skia.kRGBA_8888_ColorType,
                at=skia.kUnpremul_AlphaType
            )
    
            surface = skia.Surface.MakeRaster(surface_blueprint)
            return surface
    
        def __pixels_from_skia_surface(self):
            image = self.skia_surface.makeImageSnapshot()
            pixels = image.tobytes()
            return pixels
    
        def __transform_skia_surface_to_SDL_surface(self):
            pixels = self.__pixels_from_skia_surface()
            sdl_surface = sdl.SDL_CreateRGBSurfaceFrom(
                pixels,
                self.width, self.height,
                self.PIXEL_DEPTH, self.PIXEL_PITCH,
                *self.RGBA_MASKS
            )
            return sdl_surface
    
        def update(self):
            rect = sdl.SDL_Rect(0, 0, self.width, self.height)
            window_surface = sdl.SDL_GetWindowSurface(self.sdl_window)  # the SDL surface associated with the window
            transformed_skia_surface = self.__transform_skia_surface_to_SDL_surface()
            
            # Transfer skia surface to SDL window's surface
            sdl.SDL_BlitSurface(
                transformed_skia_surface, rect,
                window_surface, rect
            )
    
            # Update window with new copied data
            sdl.SDL_UpdateWindowSurface(self.sdl_window)
    
    
    if __name__ == "__main__":
        window = Window("Browser Test", 500, 500, flags=sdl.SDL_WINDOW_SHOWN | sdl.SDL_WINDOW_RESIZABLE)
    

    说明:上面的代码主要做了四件事来创建一个您将在其上绘制的skia表面,创建一个SDL窗口,将skia表面转换为SDL表面,最后将新创建的表面中的数据复制到SDL表面与窗口关联并更新它。如需更多解释,我建议您查看上述指南并查看skia-python docsSDL2's API Reference

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-08
      • 2020-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-13
      相关资源
      最近更新 更多