github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/internal/rendertest/clip_test.go (about) 1 package rendertest 2 3 import ( 4 "image" 5 "math" 6 "testing" 7 8 "golang.org/x/image/colornames" 9 10 "github.com/cybriq/giocore/f32" 11 "github.com/cybriq/giocore/op" 12 "github.com/cybriq/giocore/op/clip" 13 "github.com/cybriq/giocore/op/paint" 14 ) 15 16 func TestPaintRect(t *testing.T) { 17 run(t, func(o *op.Ops) { 18 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) 19 }, func(r result) { 20 r.expect(0, 0, colornames.Red) 21 r.expect(49, 0, colornames.Red) 22 r.expect(50, 0, transparent) 23 r.expect(10, 50, transparent) 24 }) 25 } 26 27 func TestPaintClippedRect(t *testing.T) { 28 run(t, func(o *op.Ops) { 29 clip.RRect{Rect: f32.Rect(25, 25, 60, 60)}.Add(o) 30 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) 31 }, func(r result) { 32 r.expect(0, 0, transparent) 33 r.expect(24, 35, transparent) 34 r.expect(25, 35, colornames.Red) 35 r.expect(50, 0, transparent) 36 r.expect(10, 50, transparent) 37 }) 38 } 39 40 func TestPaintClippedCircle(t *testing.T) { 41 run(t, func(o *op.Ops) { 42 r := float32(10) 43 clip.RRect{Rect: f32.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Add(o) 44 clip.Rect(image.Rect(0, 0, 30, 50)).Add(o) 45 paint.Fill(o, red) 46 }, func(r result) { 47 r.expect(21, 21, transparent) 48 r.expect(25, 30, colornames.Red) 49 r.expect(31, 30, transparent) 50 }) 51 } 52 53 func TestPaintArc(t *testing.T) { 54 run(t, func(o *op.Ops) { 55 p := new(clip.Path) 56 p.Begin(o) 57 p.Move(f32.Pt(0, 20)) 58 p.Line(f32.Pt(10, 0)) 59 p.Arc(f32.Pt(10, 0), f32.Pt(40, 0), math.Pi) 60 p.Line(f32.Pt(30, 0)) 61 p.Line(f32.Pt(0, 25)) 62 p.Arc(f32.Pt(-10, 5), f32.Pt(10, 15), -math.Pi) 63 p.Line(f32.Pt(0, 25)) 64 p.Arc(f32.Pt(10, 10), f32.Pt(10, 10), 2*math.Pi) 65 p.Line(f32.Pt(-10, 0)) 66 p.Arc(f32.Pt(-10, 0), f32.Pt(-40, 0), -math.Pi) 67 p.Line(f32.Pt(-10, 0)) 68 p.Line(f32.Pt(0, -10)) 69 p.Arc(f32.Pt(-10, -20), f32.Pt(10, -5), math.Pi) 70 p.Line(f32.Pt(0, -10)) 71 p.Line(f32.Pt(-50, 0)) 72 p.Close() 73 clip.Outline{ 74 Path: p.End(), 75 }.Op().Add(o) 76 77 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op()) 78 }, func(r result) { 79 r.expect(0, 0, transparent) 80 r.expect(0, 25, colornames.Red) 81 r.expect(0, 15, transparent) 82 }) 83 } 84 85 func TestPaintAbsolute(t *testing.T) { 86 run(t, func(o *op.Ops) { 87 p := new(clip.Path) 88 p.Begin(o) 89 p.Move(f32.Pt(100, 100)) // offset the initial pen position to test "MoveTo" 90 91 p.MoveTo(f32.Pt(20, 20)) 92 p.LineTo(f32.Pt(80, 20)) 93 p.QuadTo(f32.Pt(80, 80), f32.Pt(20, 80)) 94 p.Close() 95 clip.Outline{ 96 Path: p.End(), 97 }.Op().Add(o) 98 99 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op()) 100 }, func(r result) { 101 r.expect(0, 0, transparent) 102 r.expect(30, 30, colornames.Red) 103 r.expect(79, 79, transparent) 104 r.expect(90, 90, transparent) 105 }) 106 } 107 108 func TestPaintTexture(t *testing.T) { 109 run(t, func(o *op.Ops) { 110 squares.Add(o) 111 scale(80.0/512, 80.0/512).Add(o) 112 paint.PaintOp{}.Add(o) 113 }, func(r result) { 114 r.expect(0, 0, colornames.Blue) 115 r.expect(79, 10, colornames.Green) 116 r.expect(80, 0, transparent) 117 r.expect(10, 80, transparent) 118 }) 119 } 120 121 func TestTexturedStrokeClipped(t *testing.T) { 122 run(t, func(o *op.Ops) { 123 smallSquares.Add(o) 124 op.Offset(f32.Pt(50, 50)).Add(o) 125 clip.Stroke{ 126 Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o), 127 Style: clip.StrokeStyle{ 128 Width: 10, 129 }, 130 }.Op().Add(o) 131 clip.RRect{Rect: f32.Rect(-30, -30, 60, 60)}.Add(o) 132 op.Offset(f32.Pt(-10, -10)).Add(o) 133 paint.PaintOp{}.Add(o) 134 }, func(r result) { 135 }) 136 } 137 138 func TestTexturedStroke(t *testing.T) { 139 run(t, func(o *op.Ops) { 140 smallSquares.Add(o) 141 op.Offset(f32.Pt(50, 50)).Add(o) 142 clip.Stroke{ 143 Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o), 144 Style: clip.StrokeStyle{ 145 Width: 10, 146 }, 147 }.Op().Add(o) 148 op.Offset(f32.Pt(-10, -10)).Add(o) 149 paint.PaintOp{}.Add(o) 150 }, func(r result) { 151 }) 152 } 153 154 func TestPaintClippedTexture(t *testing.T) { 155 run(t, func(o *op.Ops) { 156 squares.Add(o) 157 clip.RRect{Rect: f32.Rect(0, 0, 40, 40)}.Add(o) 158 scale(80.0/512, 80.0/512).Add(o) 159 paint.PaintOp{}.Add(o) 160 }, func(r result) { 161 r.expect(40, 40, transparent) 162 r.expect(25, 35, colornames.Blue) 163 }) 164 } 165 166 func TestStrokedPathBevelFlat(t *testing.T) { 167 run(t, func(o *op.Ops) { 168 p := newStrokedPath(o) 169 clip.Stroke{ 170 Path: p, 171 Style: clip.StrokeStyle{ 172 Width: 2.5, 173 Cap: clip.FlatCap, 174 Join: clip.BevelJoin, 175 }, 176 }.Op().Add(o) 177 178 paint.Fill(o, red) 179 }, func(r result) { 180 r.expect(0, 0, transparent) 181 r.expect(10, 50, colornames.Red) 182 }) 183 } 184 185 func TestStrokedPathBevelRound(t *testing.T) { 186 run(t, func(o *op.Ops) { 187 p := newStrokedPath(o) 188 clip.Stroke{ 189 Path: p, 190 Style: clip.StrokeStyle{ 191 Width: 2.5, 192 Cap: clip.RoundCap, 193 Join: clip.BevelJoin, 194 }, 195 }.Op().Add(o) 196 197 paint.Fill(o, red) 198 }, func(r result) { 199 r.expect(0, 0, transparent) 200 r.expect(10, 50, colornames.Red) 201 }) 202 } 203 204 func TestStrokedPathBevelSquare(t *testing.T) { 205 run(t, func(o *op.Ops) { 206 p := newStrokedPath(o) 207 clip.Stroke{ 208 Path: p, 209 Style: clip.StrokeStyle{ 210 Width: 2.5, 211 Cap: clip.SquareCap, 212 Join: clip.BevelJoin, 213 }, 214 }.Op().Add(o) 215 216 paint.Fill(o, red) 217 }, func(r result) { 218 r.expect(0, 0, transparent) 219 r.expect(10, 50, colornames.Red) 220 }) 221 } 222 223 func TestStrokedPathRoundRound(t *testing.T) { 224 run(t, func(o *op.Ops) { 225 p := newStrokedPath(o) 226 clip.Stroke{ 227 Path: p, 228 Style: clip.StrokeStyle{ 229 Width: 2.5, 230 Cap: clip.RoundCap, 231 Join: clip.RoundJoin, 232 }, 233 }.Op().Add(o) 234 235 paint.Fill(o, red) 236 }, func(r result) { 237 r.expect(0, 0, transparent) 238 r.expect(10, 50, colornames.Red) 239 }) 240 } 241 242 func TestStrokedPathFlatMiter(t *testing.T) { 243 run(t, func(o *op.Ops) { 244 { 245 stk := op.Save(o) 246 p := newZigZagPath(o) 247 clip.Stroke{ 248 Path: p, 249 Style: clip.StrokeStyle{ 250 Width: 10, 251 Cap: clip.FlatCap, 252 Join: clip.BevelJoin, 253 Miter: 5, 254 }, 255 }.Op().Add(o) 256 paint.Fill(o, red) 257 stk.Load() 258 } 259 { 260 stk := op.Save(o) 261 p := newZigZagPath(o) 262 clip.Stroke{ 263 Path: p, 264 Style: clip.StrokeStyle{ 265 Width: 2, 266 Cap: clip.FlatCap, 267 Join: clip.BevelJoin, 268 }, 269 }.Op().Add(o) 270 paint.Fill(o, black) 271 stk.Load() 272 } 273 274 }, func(r result) { 275 r.expect(0, 0, transparent) 276 r.expect(40, 10, colornames.Black) 277 r.expect(40, 12, colornames.Red) 278 }) 279 } 280 281 func TestStrokedPathFlatMiterInf(t *testing.T) { 282 run(t, func(o *op.Ops) { 283 { 284 stk := op.Save(o) 285 p := newZigZagPath(o) 286 clip.Stroke{ 287 Path: p, 288 Style: clip.StrokeStyle{ 289 Width: 10, 290 Cap: clip.FlatCap, 291 Join: clip.BevelJoin, 292 Miter: float32(math.Inf(+1)), 293 }, 294 }.Op().Add(o) 295 paint.Fill(o, red) 296 stk.Load() 297 } 298 { 299 stk := op.Save(o) 300 p := newZigZagPath(o) 301 clip.Stroke{ 302 Path: p, 303 Style: clip.StrokeStyle{ 304 Width: 2, 305 Cap: clip.FlatCap, 306 Join: clip.BevelJoin, 307 }, 308 }.Op().Add(o) 309 paint.Fill(o, black) 310 stk.Load() 311 } 312 313 }, func(r result) { 314 r.expect(0, 0, transparent) 315 r.expect(40, 10, colornames.Black) 316 r.expect(40, 12, colornames.Red) 317 }) 318 } 319 320 func TestStrokedPathZeroWidth(t *testing.T) { 321 run(t, func(o *op.Ops) { 322 { 323 stk := op.Save(o) 324 p := new(clip.Path) 325 p.Begin(o) 326 p.Move(f32.Pt(10, 50)) 327 p.Line(f32.Pt(50, 0)) 328 clip.Stroke{ 329 Path: p.End(), 330 Style: clip.StrokeStyle{ 331 Width: 2, 332 Cap: clip.FlatCap, 333 Join: clip.BevelJoin, 334 }, 335 }.Op().Add(o) 336 337 paint.Fill(o, black) 338 stk.Load() 339 } 340 341 { 342 stk := op.Save(o) 343 p := new(clip.Path) 344 p.Begin(o) 345 p.Move(f32.Pt(10, 50)) 346 p.Line(f32.Pt(30, 0)) 347 clip.Stroke{ 348 Path: p.End(), 349 }.Op().Add(o) // width=0, disable stroke 350 351 paint.Fill(o, red) 352 stk.Load() 353 } 354 355 }, func(r result) { 356 r.expect(0, 0, transparent) 357 r.expect(10, 50, colornames.Black) 358 r.expect(30, 50, colornames.Black) 359 r.expect(65, 50, transparent) 360 }) 361 } 362 363 func TestDashedPathFlatCapEllipse(t *testing.T) { 364 run(t, func(o *op.Ops) { 365 { 366 stk := op.Save(o) 367 p := newEllipsePath(o) 368 369 var dash clip.Dash 370 dash.Begin(o) 371 dash.Dash(5) 372 dash.Dash(3) 373 374 clip.Stroke{ 375 Path: p, 376 Style: clip.StrokeStyle{ 377 Width: 10, 378 Cap: clip.FlatCap, 379 Join: clip.BevelJoin, 380 Miter: float32(math.Inf(+1)), 381 }, 382 Dashes: dash.End(), 383 }.Op().Add(o) 384 385 paint.Fill( 386 o, 387 red, 388 ) 389 stk.Load() 390 } 391 { 392 stk := op.Save(o) 393 p := newEllipsePath(o) 394 clip.Stroke{ 395 Path: p, 396 Style: clip.StrokeStyle{ 397 Width: 2, 398 }, 399 }.Op().Add(o) 400 401 paint.Fill( 402 o, 403 black, 404 ) 405 stk.Load() 406 } 407 408 }, func(r result) { 409 r.expect(0, 0, transparent) 410 r.expect(0, 62, colornames.Red) 411 r.expect(0, 65, colornames.Black) 412 }) 413 } 414 415 func TestDashedPathFlatCapZ(t *testing.T) { 416 run(t, func(o *op.Ops) { 417 { 418 stk := op.Save(o) 419 p := newZigZagPath(o) 420 var dash clip.Dash 421 dash.Begin(o) 422 dash.Dash(5) 423 dash.Dash(3) 424 425 clip.Stroke{ 426 Path: p, 427 Style: clip.StrokeStyle{ 428 Width: 10, 429 Cap: clip.FlatCap, 430 Join: clip.BevelJoin, 431 Miter: float32(math.Inf(+1)), 432 }, 433 Dashes: dash.End(), 434 }.Op().Add(o) 435 paint.Fill(o, red) 436 stk.Load() 437 } 438 439 { 440 stk := op.Save(o) 441 p := newZigZagPath(o) 442 clip.Stroke{ 443 Path: p, 444 Style: clip.StrokeStyle{ 445 Width: 2, 446 Cap: clip.FlatCap, 447 Join: clip.BevelJoin, 448 }, 449 }.Op().Add(o) 450 paint.Fill(o, black) 451 stk.Load() 452 } 453 }, func(r result) { 454 r.expect(0, 0, transparent) 455 r.expect(40, 10, colornames.Black) 456 r.expect(40, 12, colornames.Red) 457 r.expect(46, 12, transparent) 458 }) 459 } 460 461 func TestDashedPathFlatCapZNoDash(t *testing.T) { 462 run(t, func(o *op.Ops) { 463 { 464 stk := op.Save(o) 465 p := newZigZagPath(o) 466 var dash clip.Dash 467 dash.Begin(o) 468 dash.Phase(1) 469 470 clip.Stroke{ 471 Path: p, 472 Style: clip.StrokeStyle{ 473 Width: 10, 474 Cap: clip.FlatCap, 475 Join: clip.BevelJoin, 476 Miter: float32(math.Inf(+1)), 477 }, 478 Dashes: dash.End(), 479 }.Op().Add(o) 480 paint.Fill(o, red) 481 stk.Load() 482 } 483 { 484 stk := op.Save(o) 485 clip.Stroke{ 486 Path: newZigZagPath(o), 487 Style: clip.StrokeStyle{ 488 Width: 2, 489 Cap: clip.FlatCap, 490 Join: clip.BevelJoin, 491 }, 492 }.Op().Add(o) 493 paint.Fill(o, black) 494 stk.Load() 495 } 496 }, func(r result) { 497 r.expect(0, 0, transparent) 498 r.expect(40, 10, colornames.Black) 499 r.expect(40, 12, colornames.Red) 500 r.expect(46, 12, colornames.Red) 501 }) 502 } 503 504 func TestDashedPathFlatCapZNoPath(t *testing.T) { 505 run(t, func(o *op.Ops) { 506 { 507 stk := op.Save(o) 508 var dash clip.Dash 509 dash.Begin(o) 510 dash.Dash(0) 511 clip.Stroke{ 512 Path: newZigZagPath(o), 513 Style: clip.StrokeStyle{ 514 Width: 10, 515 Cap: clip.FlatCap, 516 Join: clip.BevelJoin, 517 Miter: float32(math.Inf(+1)), 518 }, 519 Dashes: dash.End(), 520 }.Op().Add(o) 521 paint.Fill(o, red) 522 stk.Load() 523 } 524 { 525 stk := op.Save(o) 526 p := newZigZagPath(o) 527 clip.Stroke{ 528 Path: p, 529 Style: clip.StrokeStyle{ 530 Width: 2, 531 Cap: clip.FlatCap, 532 Join: clip.BevelJoin, 533 }, 534 }.Op().Add(o) 535 paint.Fill(o, black) 536 stk.Load() 537 } 538 }, func(r result) { 539 r.expect(0, 0, transparent) 540 r.expect(40, 10, colornames.Black) 541 r.expect(40, 12, transparent) 542 r.expect(46, 12, transparent) 543 }) 544 } 545 546 func newStrokedPath(o *op.Ops) clip.PathSpec { 547 p := new(clip.Path) 548 p.Begin(o) 549 p.Move(f32.Pt(10, 50)) 550 p.Line(f32.Pt(10, 0)) 551 p.Arc(f32.Pt(10, 0), f32.Pt(20, 0), math.Pi) 552 p.Line(f32.Pt(10, 0)) 553 p.Line(f32.Pt(10, 10)) 554 p.Arc(f32.Pt(0, 30), f32.Pt(0, 30), 2*math.Pi) 555 p.Line(f32.Pt(-20, 0)) 556 p.Quad(f32.Pt(-10, -10), f32.Pt(-30, 30)) 557 return p.End() 558 } 559 560 func newZigZagPath(o *op.Ops) clip.PathSpec { 561 p := new(clip.Path) 562 p.Begin(o) 563 p.Move(f32.Pt(40, 10)) 564 p.Line(f32.Pt(50, 0)) 565 p.Line(f32.Pt(-50, 50)) 566 p.Line(f32.Pt(50, 0)) 567 p.Quad(f32.Pt(-50, 20), f32.Pt(-50, 50)) 568 p.Line(f32.Pt(50, 0)) 569 return p.End() 570 } 571 572 func newEllipsePath(o *op.Ops) clip.PathSpec { 573 p := new(clip.Path) 574 p.Begin(o) 575 p.Move(f32.Pt(0, 65)) 576 p.Line(f32.Pt(20, 0)) 577 p.Arc(f32.Pt(20, 0), f32.Pt(70, 0), 2*math.Pi) 578 return p.End() 579 }