mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +00:00
e3bb94ebb0
Uses app/headless to create a set of test cases for drawing operations, including clipping textures and transforms. This commit tests for approximate pixel matches, if future changes affect local drawing operations it will be easy to change the reference images, it thus becomes and should be an intentional operation if changes lead to local changes in drawn results. Ideally we should be able to make the tests check for exact pixel matches down the line to ensure consistent results between platforms. Signed-off-by: Viktor <viktor.ogeman@gmail.com>
145 lines
3.1 KiB
Go
145 lines
3.1 KiB
Go
package rendertest
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"image"
|
|
"image/color"
|
|
"image/draw"
|
|
"image/png"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"gioui.org/app/headless"
|
|
"gioui.org/op"
|
|
"gioui.org/op/paint"
|
|
"golang.org/x/image/colornames"
|
|
)
|
|
|
|
var (
|
|
dumpImages = flag.Bool("saveimages", false, "save test images")
|
|
squares paint.ImageOp
|
|
)
|
|
|
|
func init() {
|
|
// build the texture we use for testing
|
|
size := 512
|
|
sub := size / 4
|
|
im := image.NewNRGBA(image.Rect(0, 0, size, size))
|
|
c1, c2 := image.NewUniform(colornames.Green), image.NewUniform(colornames.Blue)
|
|
for r := 0; r < 4; r++ {
|
|
for c := 0; c < 4; c++ {
|
|
c1, c2 = c2, c1
|
|
draw.Draw(im, image.Rect(r*sub, c*sub, r*sub+sub, c*sub+sub), c1, image.Point{}, draw.Over)
|
|
}
|
|
c1, c2 = c2, c1
|
|
}
|
|
squares = paint.NewImageOp(im)
|
|
}
|
|
|
|
func drawImage(size int, ops *op.Ops, draw func(o *op.Ops)) (im *image.RGBA, err error) {
|
|
sz := image.Point{X: size, Y: size}
|
|
w, err := headless.NewWindow(sz.X, sz.Y)
|
|
if err != nil {
|
|
return im, err
|
|
}
|
|
draw(ops)
|
|
w.Frame(ops)
|
|
return w.Screenshot()
|
|
}
|
|
|
|
func run(t *testing.T, f func(o *op.Ops), c func(r result)) {
|
|
// draw a few times and check that it is correct each time, to
|
|
// ensure any caching effects still generate the correct images.
|
|
ok := true
|
|
var img *image.RGBA
|
|
var err error
|
|
ops := new(op.Ops)
|
|
for i := 0; i < 3; i++ {
|
|
ops.Reset()
|
|
img, err = drawImage(128, ops, f)
|
|
if err != nil {
|
|
t.Error("error rendering:", err)
|
|
}
|
|
// check for a reference image and make sure we are identical.
|
|
ok = ok && verifyRef(t, img)
|
|
c(result{t: t, img: img})
|
|
}
|
|
|
|
if *dumpImages || !ok {
|
|
if err := saveImage(t.Name()+".png", img); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func verifyRef(t *testing.T, img *image.RGBA) (ok bool) {
|
|
// ensure identical to ref data
|
|
path := filepath.Join("refs", t.Name()+".png")
|
|
b, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
t.Error("could not open ref:", err)
|
|
return
|
|
}
|
|
r, err := png.Decode(bytes.NewReader(b))
|
|
if err != nil {
|
|
t.Error("could not decode ref:", err)
|
|
return
|
|
}
|
|
ref, ok := r.(*image.RGBA)
|
|
if !ok {
|
|
t.Error("ref image note RGBA")
|
|
return
|
|
}
|
|
if len(ref.Pix) != len(img.Pix) {
|
|
t.Error("not equal to ref (len)")
|
|
return false
|
|
}
|
|
bnd := img.Bounds()
|
|
for x := bnd.Min.X; x < bnd.Max.X; x++ {
|
|
for y := bnd.Min.Y; y < bnd.Max.Y; y++ {
|
|
c1, c2 := ref.RGBAAt(x, y), img.RGBAAt(x, y)
|
|
if !colorsClose(c1, c2) {
|
|
t.Error("not equal to ref at", x, y, " ", c1, c2)
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func colorsClose(c1, c2 color.RGBA) bool {
|
|
return close(c1.A, c2.A) && close(c1.R, c2.R) && close(c1.G, c2.G) && close(c1.B, c2.B)
|
|
}
|
|
|
|
func close(b1, b2 uint8) bool {
|
|
if b1 > b2 {
|
|
b1, b2 = b2, b1
|
|
}
|
|
diff := b2 - b1
|
|
return diff < 20
|
|
}
|
|
|
|
func (r result) expect(x, y int, col color.RGBA) {
|
|
if r.img == nil {
|
|
return
|
|
}
|
|
c := r.img.RGBAAt(x, y)
|
|
if !colorsClose(c, col) {
|
|
r.t.Error("expected ", col, " at ", "(", x, ",", y, ") but got ", c)
|
|
}
|
|
}
|
|
|
|
type result struct {
|
|
t *testing.T
|
|
img *image.RGBA
|
|
}
|
|
|
|
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)
|
|
}
|