mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
af63c089f6
When extracting headless.Window's content via screenshots, it can be useful to keep reusing the same image for output, as well as specify which area of the Window is to be extracted. The updated Screenshot method does this by using the supplied image. API change: users must pass an existing image to Window.Screenshot. Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
208 lines
5.0 KiB
Go
208 lines
5.0 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package headless
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"image"
|
|
"image/color"
|
|
"image/png"
|
|
"io/ioutil"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"gioui.org/gpu/internal/driver"
|
|
"gioui.org/internal/byteslice"
|
|
"gioui.org/internal/f32color"
|
|
"gioui.org/shader"
|
|
"gioui.org/shader/gio"
|
|
)
|
|
|
|
var dumpImages = flag.Bool("saveimages", false, "save test images")
|
|
|
|
var clearCol = color.NRGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe}
|
|
var clearColExpect = f32color.NRGBAToRGBA(clearCol)
|
|
|
|
func TestFramebufferClear(t *testing.T) {
|
|
b := newDriver(t)
|
|
sz := image.Point{X: 800, Y: 600}
|
|
fbo := newFBO(t, b, sz)
|
|
d := driver.LoadDesc{
|
|
// ClearColor accepts linear RGBA colors, while 8-bit colors
|
|
// are in the sRGB color space.
|
|
ClearColor: f32color.LinearFromSRGB(clearCol),
|
|
Action: driver.LoadActionClear,
|
|
}
|
|
b.BeginRenderPass(fbo, d)
|
|
b.EndRenderPass()
|
|
img := screenshot(t, b, fbo, sz)
|
|
if got := img.RGBAAt(0, 0); got != clearColExpect {
|
|
t.Errorf("got color %v, expected %v", got, clearColExpect)
|
|
}
|
|
}
|
|
|
|
func TestInputShader(t *testing.T) {
|
|
b := newDriver(t)
|
|
sz := image.Point{X: 800, Y: 600}
|
|
vsh, fsh, err := newShaders(b, gio.Shader_input_vert, gio.Shader_simple_frag)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer vsh.Release()
|
|
defer fsh.Release()
|
|
layout := driver.VertexLayout{
|
|
Inputs: []driver.InputDesc{
|
|
{
|
|
Type: shader.DataTypeFloat,
|
|
Size: 4,
|
|
Offset: 0,
|
|
},
|
|
},
|
|
Stride: 4 * 4,
|
|
}
|
|
fbo := newFBO(t, b, sz)
|
|
pipe, err := b.NewPipeline(driver.PipelineDesc{
|
|
VertexShader: vsh,
|
|
FragmentShader: fsh,
|
|
VertexLayout: layout,
|
|
PixelFormat: driver.TextureFormatSRGBA,
|
|
Topology: driver.TopologyTriangles,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer pipe.Release()
|
|
buf, err := b.NewImmutableBuffer(driver.BufferBindingVertices,
|
|
byteslice.Slice([]float32{
|
|
0, -.5, .5, 1,
|
|
-.5, +.5, .5, 1,
|
|
.5, +.5, .5, 1,
|
|
}),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer buf.Release()
|
|
d := driver.LoadDesc{
|
|
ClearColor: f32color.LinearFromSRGB(clearCol),
|
|
Action: driver.LoadActionClear,
|
|
}
|
|
b.BeginRenderPass(fbo, d)
|
|
b.Viewport(0, 0, sz.X, sz.Y)
|
|
b.BindPipeline(pipe)
|
|
b.BindVertexBuffer(buf, 0)
|
|
b.DrawArrays(0, 3)
|
|
b.EndRenderPass()
|
|
img := screenshot(t, b, fbo, sz)
|
|
if got := img.RGBAAt(0, 0); got != clearColExpect {
|
|
t.Errorf("got color %v, expected %v", got, clearColExpect)
|
|
}
|
|
cx, cy := 300, 400
|
|
shaderCol := f32color.RGBA{R: .25, G: .55, B: .75, A: 1.0}
|
|
if got, exp := img.RGBAAt(cx, cy), shaderCol.SRGB(); got != f32color.NRGBAToRGBA(exp) {
|
|
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(exp))
|
|
}
|
|
}
|
|
|
|
func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) {
|
|
vert, err = ctx.NewVertexShader(vsrc)
|
|
if err != nil {
|
|
return
|
|
}
|
|
frag, err = ctx.NewFragmentShader(fsrc)
|
|
if err != nil {
|
|
vert.Release()
|
|
}
|
|
return
|
|
}
|
|
|
|
func TestFramebuffers(t *testing.T) {
|
|
b := newDriver(t)
|
|
sz := image.Point{X: 800, Y: 600}
|
|
var (
|
|
col1 = color.NRGBA{R: 0xac, G: 0xbd, B: 0xef, A: 0xde}
|
|
col2 = color.NRGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca}
|
|
)
|
|
fbo1 := newFBO(t, b, sz)
|
|
fbo2 := newFBO(t, b, sz)
|
|
fcol1, fcol2 := f32color.LinearFromSRGB(col1), f32color.LinearFromSRGB(col2)
|
|
d := driver.LoadDesc{Action: driver.LoadActionClear}
|
|
d.ClearColor = fcol1
|
|
b.BeginRenderPass(fbo1, d)
|
|
b.EndRenderPass()
|
|
d.ClearColor = fcol2
|
|
b.BeginRenderPass(fbo2, d)
|
|
b.EndRenderPass()
|
|
img := screenshot(t, b, fbo1, sz)
|
|
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) {
|
|
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col1))
|
|
}
|
|
img = screenshot(t, b, fbo2, sz)
|
|
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col2) {
|
|
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col2))
|
|
}
|
|
}
|
|
|
|
func newFBO(t *testing.T, b driver.Device, size image.Point) driver.Texture {
|
|
fboTex, err := b.NewTexture(
|
|
driver.TextureFormatSRGBA,
|
|
size.X, size.Y,
|
|
driver.FilterNearest, driver.FilterNearest,
|
|
driver.BufferBindingFramebuffer,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
fboTex.Release()
|
|
})
|
|
return fboTex
|
|
}
|
|
|
|
func newDriver(t *testing.T) driver.Device {
|
|
ctx, err := newContext()
|
|
if err != nil {
|
|
t.Skipf("no context available: %v", err)
|
|
}
|
|
runtime.LockOSThread()
|
|
if err := ctx.MakeCurrent(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, err := driver.NewDevice(ctx.API())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b.BeginFrame(nil, true, image.Pt(1, 1))
|
|
t.Cleanup(func() {
|
|
b.EndFrame()
|
|
b.Release()
|
|
ctx.ReleaseCurrent()
|
|
runtime.UnlockOSThread()
|
|
ctx.Release()
|
|
})
|
|
return b
|
|
}
|
|
|
|
func screenshot(t *testing.T, d driver.Device, fbo driver.Texture, size image.Point) *image.RGBA {
|
|
img := image.NewRGBA(image.Rectangle{Max: size})
|
|
err := driver.DownloadImage(d, fbo, img)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if *dumpImages {
|
|
if err := saveImage(t.Name()+".png", img); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
return img
|
|
}
|
|
|
|
func saveImage(file string, img image.Image) error {
|
|
var buf bytes.Buffer
|
|
if err := png.Encode(&buf, img); err != nil {
|
|
return err
|
|
}
|
|
return ioutil.WriteFile(file, buf.Bytes(), 0666)
|
|
}
|