Home > database >  Cross-platform way to draw buffer of pixels to frame in Golang?
Cross-platform way to draw buffer of pixels to frame in Golang?

Time:06-10

Say I have an in memory bitmap/grid of pixels and I want to write them all out to the screen in some sort of window. For example:

[
 [255,0,0],[200,100,100]...
 [255,0,100],[155,200,100]...
 ...
]

Is there a simple and performant way of doing this? I am happy to use OpenGL or Vulkan, but they seem a bit overkill here. Bonus points for anything that would still allow me to capture mouse/keyboard input, but I'm happy to manage that part on my own if it complicates things. I am personally working on Linux and am ok if it is not portable to Windows, but that would be nice.

Edit: Just an update on this as per Ben Hoyt's comments - my goal is to provide refreshing frames, imagine the use case as writing a GIF player. Given a set of these frames, I would like to display them sequentially and with a reasonably low display between them.

CodePudding user response:

If your goal is only to load the image to the screen, here's a direct way to do it with OpenGL and GLFW. You can use a library like Fyne to do this, but it already uses OpenGL and GLFW internally (along with lots of other dependencies), so you might as well cut out the middle man.

Here's my boilerplate program that blits an image to the screen with an OpenGL texture and framebuffer.

package main

import (
    "image"
    "runtime"

    "github.com/go-gl/gl/all-core/gl"
    "github.com/go-gl/glfw/v3.3/glfw"
)

func init() {
    // GLFW: This is needed to arrange that main() runs on main thread.
    // See documentation for functions that are only allowed to be called from the main thread.
    runtime.LockOSThread()
}

func main() {
    err := glfw.Init()
    if err != nil {
        panic(err)
    }
    defer glfw.Terminate()

    window, err := glfw.CreateWindow(640, 480, "My Window", nil, nil)
    if err != nil {
        panic(err)
    }

    window.MakeContextCurrent()

    err = gl.Init()
    if err != nil {
        panic(err)
    }

    var texture uint32
    {
        gl.GenTextures(1, &texture)

        gl.BindTexture(gl.TEXTURE_2D, texture)
        gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
        gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
        gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
        gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)

        gl.BindImageTexture(0, texture, 0, false, 0, gl.WRITE_ONLY, gl.RGBA8)
    }

    var framebuffer uint32
    {
        gl.GenFramebuffers(1, &framebuffer)
        gl.BindFramebuffer(gl.FRAMEBUFFER, framebuffer)
        gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)

        gl.BindFramebuffer(gl.READ_FRAMEBUFFER, framebuffer)
        gl.BindFramebuffer(gl.DRAW_FRAMEBUFFER, 0)
    }

    for !window.ShouldClose() {

        var w, h = window.GetSize()

        var img = image.NewRGBA(image.Rect(0, 0, w, h))

        // -------------------------
        // MODIFY OR LOAD IMAGE HERE
        // -------------------------

        gl.BindTexture(gl.TEXTURE_2D, texture)
        gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, int32(w), int32(h), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(img.Pix))

        gl.BlitFramebuffer(0, 0, int32(w), int32(h), 0, 0, int32(w), int32(h), gl.COLOR_BUFFER_BIT, gl.LINEAR)

        window.SwapBuffers()
        glfw.PollEvents()
    }
}

CodePudding user response:

I ended up using Fyne for this, to support the goal of going cross-platform and supporting fast refresh. If anyone knows of a native way to put a buffer of pixels on the screen, feel free to comment it here, but this seems pretty lightweight and natively works for Go. My GHI on how to do this correctly can be seen here: Playing a lot of sequential raster frames at 60 FPS

  • Related