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  }