277 lines
5.8 KiB
Go
277 lines
5.8 KiB
Go
package world
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
"testing"
|
|
|
|
"github.com/fogleman/gg"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func hasAnyNonTransparentPixel(img image.Image) bool {
|
|
b := img.Bounds()
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
|
_, _, _, a := img.At(x, y).RGBA()
|
|
if a != 0 {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func pixelHasAlpha(img image.Image, x, y int) bool {
|
|
_, _, _, a := img.At(x, y).RGBA()
|
|
return a != 0
|
|
}
|
|
|
|
func TestGGDrawerStrokeSequenceProducesPixels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(32, 32)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
drawer.SetStrokeColor(color.RGBA{R: 255, A: 255})
|
|
drawer.SetLineWidth(2)
|
|
drawer.SetDash(4, 2)
|
|
drawer.SetDashOffset(1)
|
|
drawer.AddLine(4, 16, 28, 16)
|
|
drawer.Stroke()
|
|
|
|
require.True(t, hasAnyNonTransparentPixel(dc.Image()))
|
|
}
|
|
|
|
func TestGGDrawerFillSequenceProducesPixels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(32, 32)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
drawer.SetFillColor(color.RGBA{G: 255, A: 255})
|
|
drawer.AddCircle(16, 16, 6)
|
|
drawer.Fill()
|
|
|
|
require.True(t, pixelHasAlpha(dc.Image(), 16, 16))
|
|
}
|
|
|
|
func TestGGDrawerPointSequenceProducesPixels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(32, 32)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
drawer.SetFillColor(color.RGBA{B: 255, A: 255})
|
|
drawer.AddPoint(16, 16, 3)
|
|
drawer.Fill()
|
|
|
|
require.True(t, pixelHasAlpha(dc.Image(), 16, 16))
|
|
}
|
|
|
|
func TestGGDrawerClipRectLimitsDrawing(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(32, 32)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
drawer.Save()
|
|
drawer.ClipRect(0, 0, 10, 32)
|
|
drawer.SetFillColor(color.RGBA{B: 255, A: 255})
|
|
drawer.AddCircle(15, 16, 10)
|
|
drawer.Fill()
|
|
drawer.Restore()
|
|
|
|
img := dc.Image()
|
|
|
|
require.True(t, pixelHasAlpha(img, 5, 16))
|
|
require.False(t, pixelHasAlpha(img, 15, 16))
|
|
}
|
|
|
|
func TestGGDrawerResetClipClearsClip(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(32, 32)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
drawer.ClipRect(0, 0, 10, 32)
|
|
drawer.ResetClip()
|
|
drawer.SetFillColor(color.RGBA{R: 255, G: 255, A: 255})
|
|
drawer.AddCircle(15, 16, 10)
|
|
drawer.Fill()
|
|
|
|
require.True(t, pixelHasAlpha(dc.Image(), 15, 16))
|
|
}
|
|
|
|
func TestGGDrawerClearRect_ClearsPixels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(10, 10)
|
|
dr := &GGDrawer{DC: dc}
|
|
|
|
// Draw something everywhere.
|
|
dr.SetFillColor(color.RGBA{R: 255, A: 255})
|
|
dr.ClipRect(0, 0, 10, 10)
|
|
dr.AddCircle(5, 5, 5)
|
|
dr.Fill()
|
|
dr.ResetClip()
|
|
|
|
// Clear a 2x2 rect at (1,1)
|
|
dr.ClearRect(1, 1, 2, 2)
|
|
|
|
img := dc.Image()
|
|
_, _, _, a := img.At(1, 1).RGBA()
|
|
require.Equal(t, uint32(0), a)
|
|
|
|
// Pixel outside should remain non-zero alpha.
|
|
_, _, _, a2 := img.At(5, 5).RGBA()
|
|
require.NotEqual(t, uint32(0), a2)
|
|
}
|
|
|
|
func TestGGDrawerSaveRestoreRestoresClipState(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(32, 32)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
drawer.Save()
|
|
drawer.ClipRect(0, 0, 10, 32)
|
|
drawer.Restore()
|
|
|
|
drawer.SetFillColor(color.RGBA{R: 255, A: 255})
|
|
drawer.AddCircle(15, 16, 10)
|
|
drawer.Fill()
|
|
|
|
require.True(t, pixelHasAlpha(dc.Image(), 15, 16))
|
|
}
|
|
|
|
func TestGGDrawerNestedSaveRestoreRestoresOuterClip(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(32, 32)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
drawer.ClipRect(0, 0, 20, 32)
|
|
|
|
drawer.Save()
|
|
drawer.ClipRect(0, 0, 10, 32)
|
|
drawer.Restore()
|
|
|
|
drawer.SetFillColor(color.RGBA{R: 255, G: 255, A: 255})
|
|
drawer.AddCircle(15, 16, 10)
|
|
drawer.Fill()
|
|
|
|
img := dc.Image()
|
|
|
|
require.True(t, pixelHasAlpha(img, 15, 16))
|
|
require.False(t, pixelHasAlpha(img, 25, 16))
|
|
}
|
|
|
|
func TestFakePrimitiveDrawerRecordsCommandsAndState(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d := &fakePrimitiveDrawer{}
|
|
|
|
d.Save()
|
|
d.ClipRect(1, 2, 30, 40)
|
|
d.SetStrokeColor(color.RGBA{R: 10, G: 20, B: 30, A: 255})
|
|
d.SetFillColor(color.RGBA{R: 40, G: 50, B: 60, A: 255})
|
|
d.SetLineWidth(3)
|
|
d.SetDash(5, 6)
|
|
d.SetDashOffset(7)
|
|
d.AddLine(10, 11, 12, 13)
|
|
d.Stroke()
|
|
d.Restore()
|
|
|
|
requireDrawerCommandNames(t, d,
|
|
"Save",
|
|
"ClipRect",
|
|
"SetStrokeColor",
|
|
"SetFillColor",
|
|
"SetLineWidth",
|
|
"SetDash",
|
|
"SetDashOffset",
|
|
"AddLine",
|
|
"Stroke",
|
|
"Restore",
|
|
)
|
|
|
|
cmd := requireDrawerSingleCommand(t, d, "AddLine")
|
|
requireCommandArgs(t, cmd, 10, 11, 12, 13)
|
|
requireCommandLineWidth(t, cmd, 3)
|
|
requireCommandDashes(t, cmd, 5, 6)
|
|
requireCommandDashOffset(t, cmd, 7)
|
|
requireCommandClipRects(t, cmd, fakeClipRect{X: 1, Y: 2, W: 30, H: 40})
|
|
require.Equal(t, color.RGBA{R: 10, G: 20, B: 30, A: 255}, cmd.StrokeColor)
|
|
require.Equal(t, color.RGBA{R: 40, G: 50, B: 60, A: 255}, cmd.FillColor)
|
|
}
|
|
|
|
func TestFakePrimitiveDrawerRestoreWithoutSavePanics(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d := &fakePrimitiveDrawer{}
|
|
|
|
require.Panics(t, func() {
|
|
d.Restore()
|
|
})
|
|
}
|
|
|
|
func TestFakePrimitiveDrawerSaveRestoreRestoresState(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d := &fakePrimitiveDrawer{}
|
|
|
|
d.SetLineWidth(1)
|
|
d.Save()
|
|
d.SetLineWidth(9)
|
|
d.ClipRect(1, 2, 3, 4)
|
|
d.Restore()
|
|
|
|
state := d.CurrentState()
|
|
|
|
require.Equal(t, 1.0, state.LineWidth)
|
|
require.Empty(t, state.Clips)
|
|
require.Equal(t, 0, d.SaveDepth())
|
|
}
|
|
|
|
func TestFakePrimitiveDrawerResetClipClearsOnlyClipState(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d := &fakePrimitiveDrawer{}
|
|
|
|
d.SetLineWidth(4)
|
|
d.ClipRect(1, 2, 3, 4)
|
|
d.ResetClip()
|
|
|
|
state := d.CurrentState()
|
|
|
|
require.Equal(t, 4.0, state.LineWidth)
|
|
require.Empty(t, state.Clips)
|
|
}
|
|
|
|
func TestGGDrawerCopyShift_ShiftsPixels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dc := gg.NewContext(10, 10)
|
|
drawer := &GGDrawer{DC: dc}
|
|
|
|
// Draw a single filled point at (1,1).
|
|
drawer.SetFillColor(color.RGBA{R: 255, A: 255})
|
|
drawer.AddPoint(1, 1, 1)
|
|
drawer.Fill()
|
|
|
|
// Shift image right by 2 and down by 3.
|
|
drawer.CopyShift(2, 3)
|
|
|
|
img := dc.Image()
|
|
|
|
// The old pixel near (1,1) should now be present near (3,4).
|
|
// We check alpha only to avoid depending on exact blending.
|
|
_, _, _, a := img.At(3, 4).RGBA()
|
|
require.NotEqual(t, uint32(0), a)
|
|
|
|
// A pixel in the newly exposed top-left area should be transparent.
|
|
_, _, _, a2 := img.At(0, 0).RGBA()
|
|
require.Equal(t, uint32(0), a2)
|
|
}
|