gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/gpu/internal/rendertest/clip_test.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package rendertest 4 5 import ( 6 "image" 7 "image/color" 8 "math" 9 "testing" 10 11 "golang.org/x/image/colornames" 12 13 "gioui.org/f32" 14 "gioui.org/op" 15 "gioui.org/op/clip" 16 "gioui.org/op/paint" 17 ) 18 19 func TestPaintRect(t *testing.T) { 20 run(t, func(o *op.Ops) { 21 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) 22 }, func(r result) { 23 r.expect(0, 0, colornames.Red) 24 r.expect(49, 0, colornames.Red) 25 r.expect(50, 0, transparent) 26 r.expect(10, 50, transparent) 27 }) 28 } 29 30 func TestPaintClippedRect(t *testing.T) { 31 run(t, func(o *op.Ops) { 32 defer clip.RRect{Rect: image.Rect(25, 25, 60, 60)}.Push(o).Pop() 33 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) 34 }, func(r result) { 35 r.expect(0, 0, transparent) 36 r.expect(24, 35, transparent) 37 r.expect(25, 35, colornames.Red) 38 r.expect(50, 0, transparent) 39 r.expect(10, 50, transparent) 40 }) 41 } 42 43 func TestPaintClippedCircle(t *testing.T) { 44 run(t, func(o *op.Ops) { 45 const r = 10 46 defer clip.RRect{Rect: image.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Push(o).Pop() 47 defer clip.Rect(image.Rect(0, 0, 30, 50)).Push(o).Pop() 48 paint.Fill(o, red) 49 }, func(r result) { 50 r.expect(21, 21, transparent) 51 r.expect(25, 30, colornames.Red) 52 r.expect(31, 30, transparent) 53 }) 54 } 55 56 func TestPaintArc(t *testing.T) { 57 run(t, func(o *op.Ops) { 58 p := new(clip.Path) 59 p.Begin(o) 60 p.Move(f32.Pt(0, 20)) 61 p.Line(f32.Pt(10, 0)) 62 p.Arc(f32.Pt(10, 0), f32.Pt(40, 0), math.Pi) 63 p.Line(f32.Pt(30, 0)) 64 p.Line(f32.Pt(0, 25)) 65 p.Arc(f32.Pt(-10, 5), f32.Pt(10, 15), -math.Pi) 66 p.Line(f32.Pt(0, 25)) 67 p.Arc(f32.Pt(10, 10), f32.Pt(10, 10), 2*math.Pi) 68 p.Line(f32.Pt(-10, 0)) 69 p.Arc(f32.Pt(-10, 0), f32.Pt(-40, 0), -math.Pi) 70 p.Line(f32.Pt(-10, 0)) 71 p.Line(f32.Pt(0, -10)) 72 p.Arc(f32.Pt(-10, -20), f32.Pt(10, -5), math.Pi) 73 p.Line(f32.Pt(0, -10)) 74 p.Line(f32.Pt(-50, 0)) 75 p.Close() 76 defer clip.Outline{ 77 Path: p.End(), 78 }.Op().Push(o).Pop() 79 80 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op()) 81 }, func(r result) { 82 r.expect(0, 0, transparent) 83 r.expect(0, 25, colornames.Red) 84 r.expect(0, 15, transparent) 85 }) 86 } 87 88 func TestPaintAbsolute(t *testing.T) { 89 run(t, func(o *op.Ops) { 90 p := new(clip.Path) 91 p.Begin(o) 92 p.Move(f32.Pt(100, 100)) // offset the initial pen position to test "MoveTo" 93 94 p.MoveTo(f32.Pt(20, 20)) 95 p.LineTo(f32.Pt(80, 20)) 96 p.QuadTo(f32.Pt(80, 80), f32.Pt(20, 80)) 97 p.Close() 98 defer clip.Outline{ 99 Path: p.End(), 100 }.Op().Push(o).Pop() 101 102 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op()) 103 }, func(r result) { 104 r.expect(0, 0, transparent) 105 r.expect(30, 30, colornames.Red) 106 r.expect(79, 79, transparent) 107 r.expect(90, 90, transparent) 108 }) 109 } 110 111 func TestPaintTexture(t *testing.T) { 112 run(t, func(o *op.Ops) { 113 squares.Add(o) 114 defer scale(80.0/512, 80.0/512).Push(o).Pop() 115 paint.PaintOp{}.Add(o) 116 }, func(r result) { 117 r.expect(0, 0, colornames.Blue) 118 r.expect(79, 10, colornames.Green) 119 r.expect(80, 0, transparent) 120 r.expect(10, 80, transparent) 121 }) 122 } 123 124 func TestTexturedStrokeClipped(t *testing.T) { 125 run(t, func(o *op.Ops) { 126 smallSquares.Add(o) 127 defer op.Offset(image.Pt(50, 50)).Push(o).Pop() 128 defer clip.Stroke{ 129 Path: clip.RRect{Rect: image.Rect(0, 0, 30, 30)}.Path(o), 130 Width: 10, 131 }.Op().Push(o).Pop() 132 defer clip.RRect{Rect: image.Rect(-30, -30, 60, 60)}.Push(o).Pop() 133 defer op.Offset(image.Pt(-10, -10)).Push(o).Pop() 134 paint.PaintOp{}.Add(o) 135 }, func(r result) { 136 }) 137 } 138 139 func TestTexturedStroke(t *testing.T) { 140 run(t, func(o *op.Ops) { 141 smallSquares.Add(o) 142 defer op.Offset(image.Pt(50, 50)).Push(o).Pop() 143 defer clip.Stroke{ 144 Path: clip.RRect{Rect: image.Rect(0, 0, 30, 30)}.Path(o), 145 Width: 10, 146 }.Op().Push(o).Pop() 147 defer op.Offset(image.Pt(-10, -10)).Push(o).Pop() 148 paint.PaintOp{}.Add(o) 149 }, func(r result) { 150 }) 151 } 152 153 func TestPaintClippedTexture(t *testing.T) { 154 run(t, func(o *op.Ops) { 155 squares.Add(o) 156 defer clip.RRect{Rect: image.Rect(0, 0, 40, 40)}.Push(o).Pop() 157 defer scale(80.0/512, 80.0/512).Push(o).Pop() 158 paint.PaintOp{}.Add(o) 159 }, func(r result) { 160 r.expect(40, 40, transparent) 161 r.expect(25, 35, colornames.Blue) 162 }) 163 } 164 165 func TestStrokedPathZeroWidth(t *testing.T) { 166 t.Skip("test broken, see issue 479") 167 168 run(t, func(o *op.Ops) { 169 { 170 p := new(clip.Path) 171 p.Begin(o) 172 p.Move(f32.Pt(10, 50)) 173 p.Line(f32.Pt(30, 0)) 174 cl := clip.Stroke{ 175 Path: p.End(), 176 }.Op().Push(o) // width=0, disable stroke 177 178 paint.Fill(o, red) 179 cl.Pop() 180 } 181 182 }, func(r result) { 183 r.expect(0, 0, transparent) 184 r.expect(10, 50, colornames.Black) 185 r.expect(30, 50, colornames.Black) 186 r.expect(65, 50, transparent) 187 }) 188 } 189 190 func TestStrokedPathCoincidentControlPoint(t *testing.T) { 191 run(t, func(o *op.Ops) { 192 p := new(clip.Path) 193 p.Begin(o) 194 p.MoveTo(f32.Pt(70, 20)) 195 p.CubeTo(f32.Pt(70, 20), f32.Pt(70, 110), f32.Pt(120, 120)) 196 p.LineTo(f32.Pt(20, 120)) 197 p.LineTo(f32.Pt(70, 20)) 198 cl := clip.Stroke{ 199 Path: p.End(), 200 Width: 20, 201 }.Op().Push(o) 202 203 paint.Fill(o, black) 204 cl.Pop() 205 }, func(r result) { 206 r.expect(0, 0, transparent) 207 r.expect(70, 20, colornames.Black) 208 r.expect(70, 90, transparent) 209 }) 210 } 211 212 func TestStrokedPathBalloon(t *testing.T) { 213 run(t, func(o *op.Ops) { 214 // This shape is based on the one drawn by the Bubble function in 215 // github.com/llgcode/draw2d/samples/geometry/geometry.go. 216 p := new(clip.Path) 217 p.Begin(o) 218 p.MoveTo(f32.Pt(42.69375, 10.5)) 219 p.CubeTo(f32.Pt(42.69375, 10.5), f32.Pt(14.85, 10.5), f32.Pt(14.85, 31.5)) 220 p.CubeTo(f32.Pt(14.85, 31.5), f32.Pt(14.85, 52.5), f32.Pt(28.771875, 52.5)) 221 p.CubeTo(f32.Pt(28.771875, 52.5), f32.Pt(28.771875, 63.7), f32.Pt(17.634375, 66.5)) 222 p.CubeTo(f32.Pt(17.634375, 66.5), f32.Pt(34.340626, 63.7), f32.Pt(37.125, 52.5)) 223 p.CubeTo(f32.Pt(37.125, 52.5), f32.Pt(70.5375, 52.5), f32.Pt(70.5375, 31.5)) 224 p.CubeTo(f32.Pt(70.5375, 31.5), f32.Pt(70.5375, 10.5), f32.Pt(42.69375, 10.5)) 225 cl := clip.Stroke{ 226 Path: p.End(), 227 Width: 2.83, 228 }.Op().Push(o) 229 paint.Fill(o, black) 230 cl.Pop() 231 }, func(r result) { 232 r.expect(0, 0, transparent) 233 r.expect(70, 52, colornames.Black) 234 r.expect(70, 90, transparent) 235 }) 236 } 237 238 func TestPathReuse(t *testing.T) { 239 run(t, func(o *op.Ops) { 240 var path clip.Path 241 path.Begin(o) 242 path.MoveTo(f32.Pt(60, 10)) 243 path.LineTo(f32.Pt(110, 75)) 244 path.LineTo(f32.Pt(10, 75)) 245 path.Close() 246 spec := path.End() 247 248 outline := clip.Outline{Path: spec}.Op().Push(o) 249 paint.Fill(o, color.NRGBA{R: 0xFF, A: 0xFF}) 250 outline.Pop() 251 252 stroke := clip.Stroke{Path: spec, Width: 3}.Op().Push(o) 253 paint.Fill(o, color.NRGBA{B: 0xFF, A: 0xFF}) 254 stroke.Pop() 255 }, func(r result) { 256 }) 257 } 258 259 func TestPathInterleave(t *testing.T) { 260 t.Run("interleave op in clip.Path", func(t *testing.T) { 261 defer func() { 262 if err := recover(); err == nil { 263 t.Error("expected panic did not occur") 264 } 265 }() 266 ops := new(op.Ops) 267 var path clip.Path 268 path.Begin(ops) 269 path.LineTo(f32.Point{X: 123, Y: 456}) 270 paint.ColorOp{}.Add(ops) 271 path.End() 272 }) 273 t.Run("use ops after clip.Path", func(t *testing.T) { 274 ops := new(op.Ops) 275 var path clip.Path 276 path.Begin(ops) 277 path.LineTo(f32.Point{X: 123, Y: 456}) 278 path.End() 279 paint.ColorOp{}.Add(ops) 280 }) 281 } 282 283 func TestStrokedRect(t *testing.T) { 284 run(t, func(o *op.Ops) { 285 r := clip.Rect{Min: image.Pt(50, 50), Max: image.Pt(100, 100)} 286 paint.FillShape(o, 287 color.NRGBA{R: 0xff, A: 0xFF}, 288 clip.Stroke{ 289 Path: r.Path(), 290 Width: 5, 291 }.Op(), 292 ) 293 }, func(r result) { 294 }) 295 }