mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
7ff17453dd
Fixes a bug due to that f32.Rect.Intersect will not return the empty rectangle for non intersecting rectangles - but instead a swapped rectangle. By removing the .Canon() call in gpu.go we ensure that non overlapping clipping rects and paint rects will lead to no painting. The Canon() call is not needed since boundsForTransformedRect() was previously updated to always return a canonical rectangle. Test case added. Signed-off-by: Viktor <viktor.ogeman@gmail.com>
197 lines
4.6 KiB
Go
197 lines
4.6 KiB
Go
package rendertest
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"gioui.org/f32"
|
|
"gioui.org/op"
|
|
"gioui.org/op/clip"
|
|
"gioui.org/op/paint"
|
|
"golang.org/x/image/colornames"
|
|
)
|
|
|
|
func TestTransformMacro(t *testing.T) {
|
|
// testcase resulting from original bug when rendering layout.Stacked
|
|
|
|
// pre-build the text
|
|
c := constSqPath()
|
|
|
|
run(t, func(o *op.Ops) {
|
|
|
|
// render the first Stacked item
|
|
m1 := op.Record(o)
|
|
dr := f32.Rect(0, 0, 128, 50)
|
|
paint.ColorOp{Color: colornames.Black}.Add(o)
|
|
paint.PaintOp{Rect: dr}.Add(o)
|
|
c1 := m1.Stop()
|
|
|
|
// Render the second stacked item
|
|
m2 := op.Record(o)
|
|
paint.ColorOp{Color: colornames.Red}.Add(o)
|
|
// Simulate a draw text call
|
|
stack := op.Push(o)
|
|
op.Offset(f32.Pt(0, 10)).Add(o)
|
|
|
|
// Actually create the text clip-path
|
|
c.Add(o)
|
|
|
|
paint.PaintOp{Rect: f32.Rect(0, 0, 10, 10)}.Add(o)
|
|
stack.Pop()
|
|
|
|
c2 := m2.Stop()
|
|
|
|
// Call each of them in a transform
|
|
s1 := op.Push(o)
|
|
op.Offset(f32.Pt(0, 0)).Add(o)
|
|
c1.Add(o)
|
|
s1.Pop()
|
|
s2 := op.Push(o)
|
|
op.Offset(f32.Pt(0, 0)).Add(o)
|
|
c2.Add(o)
|
|
s2.Pop()
|
|
}, func(r result) {
|
|
r.expect(5, 15, colornames.Red)
|
|
r.expect(15, 15, colornames.Black)
|
|
r.expect(11, 51, colornames.White)
|
|
})
|
|
}
|
|
|
|
func TestRepeatedPaintsZ(t *testing.T) {
|
|
run(t, func(o *op.Ops) {
|
|
// Draw a rectangle
|
|
paint.ColorOp{Color: colornames.Black}.Add(o)
|
|
paint.PaintOp{Rect: f32.Rect(0, 0, 128, 50)}.Add(o)
|
|
|
|
builder := clip.Path{}
|
|
builder.Begin(o)
|
|
builder.Move(f32.Pt(0, 0))
|
|
builder.Line(f32.Pt(10, 0))
|
|
builder.Line(f32.Pt(0, 10))
|
|
builder.Line(f32.Pt(-10, 0))
|
|
builder.Line(f32.Pt(0, -10))
|
|
builder.End().Add(o)
|
|
paint.ColorOp{Color: colornames.Red}.Add(o)
|
|
paint.PaintOp{Rect: f32.Rect(0, 0, 10, 10)}.Add(o)
|
|
}, func(r result) {
|
|
r.expect(5, 5, colornames.Red)
|
|
r.expect(11, 15, colornames.Black)
|
|
r.expect(11, 51, colornames.White)
|
|
})
|
|
}
|
|
|
|
func TestNoClipFromPaint(t *testing.T) {
|
|
// ensure that a paint operation does not polute the state
|
|
// by leaving any clip paths i place.
|
|
run(t, func(o *op.Ops) {
|
|
a := f32.Affine2D{}.Rotate(f32.Pt(20, 20), math.Pi/4)
|
|
op.Affine(a).Add(o)
|
|
paint.ColorOp{Color: colornames.Red}.Add(o)
|
|
paint.PaintOp{Rect: f32.Rect(10, 10, 30, 30)}.Add(o)
|
|
a = f32.Affine2D{}.Rotate(f32.Pt(20, 20), -math.Pi/4)
|
|
op.Affine(a).Add(o)
|
|
|
|
paint.ColorOp{Color: colornames.Black}.Add(o)
|
|
paint.PaintOp{Rect: f32.Rect(0, 0, 50, 50)}.Add(o)
|
|
}, func(r result) {
|
|
r.expect(1, 1, colornames.Black)
|
|
r.expect(20, 20, colornames.Black)
|
|
r.expect(49, 49, colornames.Black)
|
|
r.expect(51, 51, colornames.White)
|
|
})
|
|
}
|
|
|
|
func constSqPath() op.CallOp {
|
|
innerOps := new(op.Ops)
|
|
m := op.Record(innerOps)
|
|
builder := clip.Path{}
|
|
builder.Begin(innerOps)
|
|
builder.Move(f32.Pt(0, 0))
|
|
builder.Line(f32.Pt(10, 0))
|
|
builder.Line(f32.Pt(0, 10))
|
|
builder.Line(f32.Pt(-10, 0))
|
|
builder.Line(f32.Pt(0, -10))
|
|
builder.End().Add(innerOps)
|
|
return m.Stop()
|
|
}
|
|
|
|
func constSqCirc() op.CallOp {
|
|
innerOps := new(op.Ops)
|
|
m := op.Record(innerOps)
|
|
clip.Rect{Rect: f32.Rect(0, 0, 40, 40),
|
|
NW: 20, NE: 20, SW: 20, SE: 20}.Add(innerOps)
|
|
return m.Stop()
|
|
}
|
|
|
|
func drawChild(ops *op.Ops, text op.CallOp) op.CallOp {
|
|
r1 := op.Record(ops)
|
|
text.Add(ops)
|
|
paint.PaintOp{Rect: f32.Rect(0, 0, 10, 10)}.Add(ops)
|
|
return r1.Stop()
|
|
}
|
|
|
|
func TestReuseStencil(t *testing.T) {
|
|
txt := constSqPath()
|
|
run(t, func(ops *op.Ops) {
|
|
c1 := drawChild(ops, txt)
|
|
c2 := drawChild(ops, txt)
|
|
|
|
// lay out the children
|
|
stack1 := op.Push(ops)
|
|
c1.Add(ops)
|
|
stack1.Pop()
|
|
|
|
stack2 := op.Push(ops)
|
|
op.Offset(f32.Pt(0, 50)).Add(ops)
|
|
c2.Add(ops)
|
|
stack2.Pop()
|
|
}, func(r result) {
|
|
r.expect(5, 5, colornames.Black)
|
|
r.expect(5, 55, colornames.Black)
|
|
})
|
|
}
|
|
|
|
func TestBuildOffscreen(t *testing.T) {
|
|
// Check that something we in one frame build outside the screen
|
|
// still is rendered correctly if moved into the screen in a later
|
|
// frame.
|
|
|
|
txt := constSqCirc()
|
|
draw := func(off float32, o *op.Ops) {
|
|
s := op.Push(o)
|
|
op.Offset(f32.Pt(0, off)).Add(o)
|
|
txt.Add(o)
|
|
paint.PaintOp{Rect: f32.Rect(0, 0, 40, 40)}.Add(o)
|
|
s.Pop()
|
|
}
|
|
|
|
multiRun(t,
|
|
frame(
|
|
func(ops *op.Ops) {
|
|
draw(-100, ops)
|
|
}, func(r result) {
|
|
r.expect(5, 5, colornames.White)
|
|
r.expect(20, 20, colornames.White)
|
|
}),
|
|
frame(
|
|
func(ops *op.Ops) {
|
|
draw(0, ops)
|
|
}, func(r result) {
|
|
r.expect(2, 2, colornames.White)
|
|
r.expect(20, 20, colornames.Black)
|
|
r.expect(38, 38, colornames.White)
|
|
}))
|
|
}
|
|
|
|
func TestNegativeOverlaps(t *testing.T) {
|
|
run(t, func(ops *op.Ops) {
|
|
clip.Rect{Rect: f32.Rect(50, 50, 100, 100)}.Add(ops)
|
|
paint.PaintOp{Rect: f32.Rect(0, 120, 100, 122)}.Add(ops)
|
|
}, func(r result) {
|
|
r.expect(60, 60, colornames.White)
|
|
r.expect(60, 110, colornames.White)
|
|
r.expect(60, 120, colornames.White)
|
|
r.expect(60, 122, colornames.White)
|
|
})
|
|
}
|