mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +00:00
app/headless: re-implement Screenshot on top of Backend
The new Framebuffer.ReadPixels method is enough to implement Window.Screenshot. Use that instead of the OpenGL-specific implementation. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -14,13 +14,15 @@ import (
|
|||||||
|
|
||||||
// Window is a headless window.
|
// Window is a headless window.
|
||||||
type Window struct {
|
type Window struct {
|
||||||
size image.Point
|
size image.Point
|
||||||
ctx backend
|
ctx backend
|
||||||
gpu *gpu.GPU
|
backend gpu.Backend
|
||||||
|
gpu *gpu.GPU
|
||||||
|
fboTex gpu.Texture
|
||||||
|
fbo gpu.Framebuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
type backend interface {
|
type backend interface {
|
||||||
Screenshot(width, height int, pixels []byte) error
|
|
||||||
Backend() (gpu.Backend, error)
|
Backend() (gpu.Backend, error)
|
||||||
MakeCurrent() error
|
MakeCurrent() error
|
||||||
ReleaseCurrent()
|
ReleaseCurrent()
|
||||||
@@ -40,18 +42,38 @@ func NewWindow(width, height int) (*Window, error) {
|
|||||||
err = contextDo(ctx, func() error {
|
err = contextDo(ctx, func() error {
|
||||||
backend, err := ctx.Backend()
|
backend, err := ctx.Backend()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Release()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gpu, err := gpu.New(backend)
|
fboTex, err := backend.NewTexture(
|
||||||
|
gpu.TextureFormatSRGB,
|
||||||
|
width, height,
|
||||||
|
gpu.FilterNearest, gpu.FilterNearest,
|
||||||
|
gpu.BufferBindingFramebuffer,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Release()
|
return nil
|
||||||
|
}
|
||||||
|
const depthBits = 16
|
||||||
|
fbo, err := backend.NewFramebuffer(fboTex, depthBits)
|
||||||
|
if err != nil {
|
||||||
|
fboTex.Release()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.gpu = gpu
|
fbo.Bind()
|
||||||
|
gp, err := gpu.New(backend)
|
||||||
|
if err != nil {
|
||||||
|
fbo.Release()
|
||||||
|
fboTex.Release()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.fboTex = fboTex
|
||||||
|
w.fbo = fbo
|
||||||
|
w.gpu = gp
|
||||||
|
w.backend = backend
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
ctx.Release()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return w, nil
|
return w, nil
|
||||||
@@ -60,6 +82,14 @@ func NewWindow(width, height int) (*Window, error) {
|
|||||||
// Release resources associated with the window.
|
// Release resources associated with the window.
|
||||||
func (w *Window) Release() {
|
func (w *Window) Release() {
|
||||||
contextDo(w.ctx, func() error {
|
contextDo(w.ctx, func() error {
|
||||||
|
if w.fbo != nil {
|
||||||
|
w.fbo.Release()
|
||||||
|
w.fbo = nil
|
||||||
|
}
|
||||||
|
if w.fboTex != nil {
|
||||||
|
w.fboTex.Release()
|
||||||
|
w.fboTex = nil
|
||||||
|
}
|
||||||
if w.gpu != nil {
|
if w.gpu != nil {
|
||||||
w.gpu.Release()
|
w.gpu.Release()
|
||||||
w.gpu = nil
|
w.gpu = nil
|
||||||
@@ -87,7 +117,10 @@ func (w *Window) Frame(frame *op.Ops) {
|
|||||||
func (w *Window) Screenshot() (*image.RGBA, error) {
|
func (w *Window) Screenshot() (*image.RGBA, error) {
|
||||||
img := image.NewRGBA(image.Rectangle{Max: w.size})
|
img := image.NewRGBA(image.Rectangle{Max: w.size})
|
||||||
contextDo(w.ctx, func() error {
|
contextDo(w.ctx, func() error {
|
||||||
return w.ctx.Screenshot(w.size.X, w.size.Y, img.Pix)
|
return w.fbo.ReadPixels(
|
||||||
|
image.Rectangle{
|
||||||
|
Max: image.Point{X: w.size.X, Y: w.size.Y},
|
||||||
|
}, img.Pix)
|
||||||
})
|
})
|
||||||
// Flip image in y-direction. OpenGL's origin is in the lower
|
// Flip image in y-direction. OpenGL's origin is in the lower
|
||||||
// left corner.
|
// left corner.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type nsContext struct {
|
|||||||
prepared bool
|
prepared bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGLContext() (glContext, error) {
|
func newGLContext() (backend, error) {
|
||||||
ctx := C.gio_headless_newContext()
|
ctx := C.gio_headless_newContext()
|
||||||
return &nsContext{ctx: ctx, c: new(glimpl.Functions)}, nil
|
return &nsContext{ctx: ctx, c: new(glimpl.Functions)}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ import (
|
|||||||
"gioui.org/app/internal/egl"
|
"gioui.org/app/internal/egl"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newGLContext() (glContext, error) {
|
func newGLContext() (backend, error) {
|
||||||
return egl.NewContext(egl.EGL_DEFAULT_DISPLAY)
|
return egl.NewContext(egl.EGL_DEFAULT_DISPLAY)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,72 +2,6 @@
|
|||||||
|
|
||||||
package headless
|
package headless
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"gioui.org/app/internal/glimpl"
|
|
||||||
"gioui.org/app/internal/srgb"
|
|
||||||
"gioui.org/gpu"
|
|
||||||
"gioui.org/gpu/gl"
|
|
||||||
)
|
|
||||||
|
|
||||||
type glContext interface {
|
|
||||||
Functions() *glimpl.Functions
|
|
||||||
Backend() (gpu.Backend, error)
|
|
||||||
MakeCurrent() error
|
|
||||||
ReleaseCurrent()
|
|
||||||
Release()
|
|
||||||
}
|
|
||||||
|
|
||||||
type glBackend struct {
|
|
||||||
glContext
|
|
||||||
srgb *srgb.FBO
|
|
||||||
}
|
|
||||||
|
|
||||||
func newContext(width, height int) (backend, error) {
|
func newContext(width, height int) (backend, error) {
|
||||||
glctx, err := newGLContext()
|
return newGLContext()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Create the back buffer FBO after locking the thread and making
|
|
||||||
// the context current.
|
|
||||||
runtime.LockOSThread()
|
|
||||||
defer runtime.UnlockOSThread()
|
|
||||||
if err := glctx.MakeCurrent(); err != nil {
|
|
||||||
glctx.Release()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer glctx.ReleaseCurrent()
|
|
||||||
fbo, err := srgb.New(glctx.Functions())
|
|
||||||
if err != nil {
|
|
||||||
glctx.Release()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := fbo.Refresh(width, height); err != nil {
|
|
||||||
fbo.Release()
|
|
||||||
glctx.Release()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &glBackend{glctx, fbo}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *glBackend) Screenshot(width, height int, pixels []byte) error {
|
|
||||||
if len(pixels) != width*height*4 {
|
|
||||||
panic("unexpected RGBA size")
|
|
||||||
}
|
|
||||||
f := b.Functions()
|
|
||||||
f.ReadPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
|
|
||||||
if glErr := f.GetError(); glErr != gl.NO_ERROR {
|
|
||||||
return fmt.Errorf("glReadPixels failed: %d", glErr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *glBackend) Release() {
|
|
||||||
if b.srgb != nil {
|
|
||||||
b.srgb.Release()
|
|
||||||
b.srgb = nil
|
|
||||||
}
|
|
||||||
b.glContext.Release()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type jsContext struct {
|
|||||||
f *glimpl.Functions
|
f *glimpl.Functions
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGLContext() (glContext, error) {
|
func newGLContext() (backend, error) {
|
||||||
version := 2
|
version := 2
|
||||||
doc := js.Global().Get("document")
|
doc := js.Global().Get("document")
|
||||||
cnv := doc.Call("createElement", "canvas")
|
cnv := doc.Call("createElement", "canvas")
|
||||||
|
|||||||
Reference in New Issue
Block a user