package world import ( "image" "image/color" "testing" "github.com/stretchr/testify/require" ) type bgTheme struct { img image.Image } func (t bgTheme) ID() string { return "bg" } func (t bgTheme) Name() string { return "bg" } func (t bgTheme) BackgroundColor() color.Color { return color.RGBA{A: 255} } func (t bgTheme) BackgroundImage() image.Image { return t.img } func (t bgTheme) BackgroundTileMode() BackgroundTileMode { return BackgroundTileRepeat } func (t bgTheme) BackgroundScaleMode() BackgroundScaleMode { return BackgroundScaleNone } func (t bgTheme) BackgroundAnchorMode() BackgroundAnchorMode { return BackgroundAnchorViewport } func (t bgTheme) PointStyle() Style { return Style{FillColor: color.RGBA{A: 255}, PointRadiusPx: 2} } func (t bgTheme) LineStyle() Style { return Style{StrokeColor: color.RGBA{A: 255}, StrokeWidthPx: 1} } func (t bgTheme) CircleStyle() Style { return Style{FillColor: color.RGBA{A: 255}, StrokeColor: color.RGBA{A: 255}, StrokeWidthPx: 1} } func (t bgTheme) PointClassOverride(PointClassID) (StyleOverride, bool) { return StyleOverride{}, false } func (t bgTheme) LineClassOverride(LineClassID) (StyleOverride, bool) { return StyleOverride{}, false } func (t bgTheme) CircleClassOverride(CircleClassID) (StyleOverride, bool) { return StyleOverride{}, false } func TestRender_BackgroundImage_DrawsBeforePrimitives_FullRedraw(t *testing.T) { t.Parallel() w := NewWorld(10, 10) w.resetGrid(2 * SCALE) // 4x4 opaque image img := image.NewRGBA(image.Rect(0, 0, 4, 4)) w.SetTheme(bgTheme{img: img}) _, err := w.AddPoint(5, 5) require.NoError(t, err) for _, obj := range w.objects { w.indexObject(obj) } params := RenderParams{ ViewportWidthPx: 10, ViewportHeightPx: 10, MarginXPx: 2, MarginYPx: 2, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, } d := &fakePrimitiveDrawer{} require.NoError(t, w.Render(d, params)) cmds := d.Commands() iClear := indexOfFirstName(cmds, "ClearAllTo") iBg := indexOfFirstName(cmds, "DrawImage") iPrim := indexOfFirstName(cmds, "AddPoint") require.NotEqual(t, -1, iClear) require.NotEqual(t, -1, iBg) require.NotEqual(t, -1, iPrim) require.Less(t, iClear, iBg) require.Less(t, iBg, iPrim) } func TestRender_BackgroundImage_RedrawnInDirtyRects_OnIncrementalShift(t *testing.T) { t.Parallel() w := NewWorld(10, 10) w.resetGrid(2 * SCALE) img := image.NewRGBA(image.Rect(0, 0, 4, 4)) w.SetTheme(bgTheme{img: img}) // Ensure state: first full render commits. params := RenderParams{ ViewportWidthPx: 10, ViewportHeightPx: 10, MarginXPx: 2, MarginYPx: 2, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, Options: &RenderOptions{ Incremental: &IncrementalPolicy{ AllowShiftOnly: false, MaxCatchUpAreaPx: 0, RenderBudgetMs: 0, CoalesceUpdates: false, }, }, } d := &fakePrimitiveDrawer{} require.NoError(t, w.Render(d, params)) // Move camera by 1px right in world (zoom=1 => 1px == 1 unit). params2 := params params2.CameraXWorldFp += 1 * SCALE d2 := &fakePrimitiveDrawer{} require.NoError(t, w.Render(d2, params2)) // In incremental shift path we must see ClearRectTo and DrawImage. require.NotEmpty(t, d2.CommandsByName("CopyShift")) require.NotEmpty(t, d2.CommandsByName("ClearRectTo")) require.NotEmpty(t, d2.CommandsByName("DrawImage")) }