Files
gio/gpu/internal/rendertest/clip_test.go
T
Elias Naur a63e0cb44a all: [API] change op.Offset to take integer coordinates
op.Offset is a convenience function most often used by layouts. Layouts
usually operate in integer coordinates, and the float32 version of op.Offset
needlessly force conversions from int to float32. This change makes op.Offset
take integer coordinates, to better match its intended use.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-05-31 10:24:09 +02:00

294 lines
7.3 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package rendertest
import (
"image"
"image/color"
"math"
"testing"
"golang.org/x/image/colornames"
"gioui.org/f32"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
)
func TestPaintRect(t *testing.T) {
run(t, func(o *op.Ops) {
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
}, func(r result) {
r.expect(0, 0, colornames.Red)
r.expect(49, 0, colornames.Red)
r.expect(50, 0, transparent)
r.expect(10, 50, transparent)
})
}
func TestPaintClippedRect(t *testing.T) {
run(t, func(o *op.Ops) {
defer clip.RRect{Rect: f32.Rect(25, 25, 60, 60)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(24, 35, transparent)
r.expect(25, 35, colornames.Red)
r.expect(50, 0, transparent)
r.expect(10, 50, transparent)
})
}
func TestPaintClippedCircle(t *testing.T) {
run(t, func(o *op.Ops) {
r := float32(10)
defer clip.RRect{Rect: f32.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Push(o).Pop()
defer clip.Rect(image.Rect(0, 0, 30, 50)).Push(o).Pop()
paint.Fill(o, red)
}, func(r result) {
r.expect(21, 21, transparent)
r.expect(25, 30, colornames.Red)
r.expect(31, 30, transparent)
})
}
func TestPaintArc(t *testing.T) {
run(t, func(o *op.Ops) {
p := new(clip.Path)
p.Begin(o)
p.Move(f32.Pt(0, 20))
p.Line(f32.Pt(10, 0))
p.Arc(f32.Pt(10, 0), f32.Pt(40, 0), math.Pi)
p.Line(f32.Pt(30, 0))
p.Line(f32.Pt(0, 25))
p.Arc(f32.Pt(-10, 5), f32.Pt(10, 15), -math.Pi)
p.Line(f32.Pt(0, 25))
p.Arc(f32.Pt(10, 10), f32.Pt(10, 10), 2*math.Pi)
p.Line(f32.Pt(-10, 0))
p.Arc(f32.Pt(-10, 0), f32.Pt(-40, 0), -math.Pi)
p.Line(f32.Pt(-10, 0))
p.Line(f32.Pt(0, -10))
p.Arc(f32.Pt(-10, -20), f32.Pt(10, -5), math.Pi)
p.Line(f32.Pt(0, -10))
p.Line(f32.Pt(-50, 0))
p.Close()
defer clip.Outline{
Path: p.End(),
}.Op().Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op())
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(0, 25, colornames.Red)
r.expect(0, 15, transparent)
})
}
func TestPaintAbsolute(t *testing.T) {
run(t, func(o *op.Ops) {
p := new(clip.Path)
p.Begin(o)
p.Move(f32.Pt(100, 100)) // offset the initial pen position to test "MoveTo"
p.MoveTo(f32.Pt(20, 20))
p.LineTo(f32.Pt(80, 20))
p.QuadTo(f32.Pt(80, 80), f32.Pt(20, 80))
p.Close()
defer clip.Outline{
Path: p.End(),
}.Op().Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op())
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(30, 30, colornames.Red)
r.expect(79, 79, transparent)
r.expect(90, 90, transparent)
})
}
func TestPaintTexture(t *testing.T) {
run(t, func(o *op.Ops) {
squares.Add(o)
defer scale(80.0/512, 80.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o)
}, func(r result) {
r.expect(0, 0, colornames.Blue)
r.expect(79, 10, colornames.Green)
r.expect(80, 0, transparent)
r.expect(10, 80, transparent)
})
}
func TestTexturedStrokeClipped(t *testing.T) {
run(t, func(o *op.Ops) {
smallSquares.Add(o)
defer op.Offset(image.Pt(50, 50)).Push(o).Pop()
defer clip.Stroke{
Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o),
Width: 10,
}.Op().Push(o).Pop()
defer clip.RRect{Rect: f32.Rect(-30, -30, 60, 60)}.Push(o).Pop()
defer op.Offset(image.Pt(-10, -10)).Push(o).Pop()
paint.PaintOp{}.Add(o)
}, func(r result) {
})
}
func TestTexturedStroke(t *testing.T) {
run(t, func(o *op.Ops) {
smallSquares.Add(o)
defer op.Offset(image.Pt(50, 50)).Push(o).Pop()
defer clip.Stroke{
Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o),
Width: 10,
}.Op().Push(o).Pop()
defer op.Offset(image.Pt(-10, -10)).Push(o).Pop()
paint.PaintOp{}.Add(o)
}, func(r result) {
})
}
func TestPaintClippedTexture(t *testing.T) {
run(t, func(o *op.Ops) {
squares.Add(o)
defer clip.RRect{Rect: f32.Rect(0, 0, 40, 40)}.Push(o).Pop()
defer scale(80.0/512, 80.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o)
}, func(r result) {
r.expect(40, 40, transparent)
r.expect(25, 35, colornames.Blue)
})
}
func TestStrokedPathZeroWidth(t *testing.T) {
run(t, func(o *op.Ops) {
{
p := new(clip.Path)
p.Begin(o)
p.Move(f32.Pt(10, 50))
p.Line(f32.Pt(30, 0))
cl := clip.Stroke{
Path: p.End(),
}.Op().Push(o) // width=0, disable stroke
paint.Fill(o, red)
cl.Pop()
}
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(10, 50, colornames.Black)
r.expect(30, 50, colornames.Black)
r.expect(65, 50, transparent)
})
}
func TestStrokedPathCoincidentControlPoint(t *testing.T) {
run(t, func(o *op.Ops) {
p := new(clip.Path)
p.Begin(o)
p.MoveTo(f32.Pt(70, 20))
p.CubeTo(f32.Pt(70, 20), f32.Pt(70, 110), f32.Pt(120, 120))
p.LineTo(f32.Pt(20, 120))
p.LineTo(f32.Pt(70, 20))
cl := clip.Stroke{
Path: p.End(),
Width: 20,
}.Op().Push(o)
paint.Fill(o, black)
cl.Pop()
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(70, 20, colornames.Black)
r.expect(70, 90, transparent)
})
}
func TestStrokedPathBalloon(t *testing.T) {
run(t, func(o *op.Ops) {
// This shape is based on the one drawn by the Bubble function in
// github.com/llgcode/draw2d/samples/geometry/geometry.go.
p := new(clip.Path)
p.Begin(o)
p.MoveTo(f32.Pt(42.69375, 10.5))
p.CubeTo(f32.Pt(42.69375, 10.5), f32.Pt(14.85, 10.5), f32.Pt(14.85, 31.5))
p.CubeTo(f32.Pt(14.85, 31.5), f32.Pt(14.85, 52.5), f32.Pt(28.771875, 52.5))
p.CubeTo(f32.Pt(28.771875, 52.5), f32.Pt(28.771875, 63.7), f32.Pt(17.634375, 66.5))
p.CubeTo(f32.Pt(17.634375, 66.5), f32.Pt(34.340626, 63.7), f32.Pt(37.125, 52.5))
p.CubeTo(f32.Pt(37.125, 52.5), f32.Pt(70.5375, 52.5), f32.Pt(70.5375, 31.5))
p.CubeTo(f32.Pt(70.5375, 31.5), f32.Pt(70.5375, 10.5), f32.Pt(42.69375, 10.5))
cl := clip.Stroke{
Path: p.End(),
Width: 2.83,
}.Op().Push(o)
paint.Fill(o, black)
cl.Pop()
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(70, 52, colornames.Black)
r.expect(70, 90, transparent)
})
}
func TestPathReuse(t *testing.T) {
run(t, func(o *op.Ops) {
var path clip.Path
path.Begin(o)
path.MoveTo(f32.Pt(60, 10))
path.LineTo(f32.Pt(110, 75))
path.LineTo(f32.Pt(10, 75))
path.Close()
spec := path.End()
outline := clip.Outline{Path: spec}.Op().Push(o)
paint.Fill(o, color.NRGBA{R: 0xFF, A: 0xFF})
outline.Pop()
stroke := clip.Stroke{Path: spec, Width: 3}.Op().Push(o)
paint.Fill(o, color.NRGBA{B: 0xFF, A: 0xFF})
stroke.Pop()
}, func(r result) {
})
}
func TestPathInterleave(t *testing.T) {
t.Run("interleave op in clip.Path", func(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Error("expected panic did not occur")
}
}()
ops := new(op.Ops)
var path clip.Path
path.Begin(ops)
path.LineTo(f32.Point{X: 123, Y: 456})
paint.ColorOp{}.Add(ops)
path.End()
})
t.Run("use ops after clip.Path", func(t *testing.T) {
ops := new(op.Ops)
var path clip.Path
path.Begin(ops)
path.LineTo(f32.Point{X: 123, Y: 456})
path.End()
paint.ColorOp{}.Add(ops)
})
}
func TestStrokedRect(t *testing.T) {
run(t, func(o *op.Ops) {
r := clip.Rect{Min: image.Pt(50, 50), Max: image.Pt(100, 100)}
paint.FillShape(o,
color.NRGBA{R: 0xff, A: 0xFF},
clip.Stroke{
Path: r.Path(),
Width: 5,
}.Op(),
)
}, func(r result) {
})
}