简体   繁体   English

如何在pysdl2中嵌入skia python表面

[英]How to embed skia python surface inside pysdl2

I am trying to embed skia-python 's surface inside a window rather than output to a image file.我正在尝试将skia-python的表面嵌入 window 而不是 output 到图像文件中。 I am using pysdl2 to create the window using the following code from the documentation:我正在使用pysdl2使用文档中的以下代码创建 window:

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

And this code to create the surface from skia's documentation:这段代码从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)

Now what I want to achieve is to take the image (or surface whichever applicable) object from the skia portion and plug it into pysdl2 so that I can draw with skia but handle window's event loop with pysdl2 and I'd like to avoid ctypes right now because I am not so familiar with it.现在我想要实现的是从skia部分获取image (或适用的surface )object并将其插入pysdl2,以便我可以使用skia绘制但使用pysdl2处理窗口的事件循环,我想避免ctypes正确现在因为我对它不太熟悉。

I gave up on creating it without ctypes as all we need from it is ctypes.byref and I am now importing sdl2 instead of sdl2.ext which was more pythonic, but also restricted a bit of functionality that is required here.我放弃了在没有ctypes的情况下创建它,因为我们需要的只是ctypes.byref并且我现在正在导入sdl2而不是sdl2.ext ,后者更加 Pythonic,但也限制了这里所需的一些功能。

Now to answer the question, I followed this guide here (if you are not building a browser it might go a little off topic for you)现在回答这个问题,我在这里遵循了本指南(如果您不构建浏览器,它可能 go 对您来说有点离题)

So I have also implement a general enough version from the above guide, you can draw to Window.skia_surface and then call Window.update to copy skia surface to the window screen:所以我也从上面的指南中实现了一个足够通用的版本,你可以绘制到Window.skia_surface然后调用Window.update将skia表面复制到 Z05B8C74CBD96FBF2DE4C1A352702FFF4

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, 0x000ff00, 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)

Explanation: Primarily the above code does four things create a skia surface that you will draw on, create an SDL window, convert the skia surface to a SDL surface and at last copy the data in the newly created surface to the SDL surface associated with the window and update it.说明:上面的代码主要做了四件事来创建一个你将在上面绘制的滑雪表面,创建一个 SDL window,将滑雪表面转换为 SDL 表面,最后将新创建的表面中的数据复制到与window 并更新它。 For a bit more explanation I recommend you look into the above guide and also check out skia-python docs and SDL2's API Reference .如需更多解释,我建议您查看上述指南并查看skia-python 文档SDL2 的 API 参考

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM