1package graphicsharness
2
3import (
4 "bytes"
5 _ "embed"
6 "encoding/base64"
7 "image/color"
8 "strings"
9 "testing"
10
11 "github.com/stretchr/testify/require"
12
13 vt "github.com/bnema/vev-vt"
14 "github.com/bnema/vev-vt/graphics"
15)
16
17//go:embed testdata/pngsuite-exif2c08.png.b64
18var pngSuiteEXIFBase64 string
19
20//go:embed testdata/demo.apc
21var demoAPC []byte
22
23func TestRenderTerminalDemo(t *testing.T) {
24 geometry := vt.Geometry{Cols: 4, Rows: 4, PixelWidth: 4, PixelHeight: 4}
25 screen := vt.NewScreen(geometry.Cols, geometry.Rows)
26 screen.SetGeometry(geometry)
27 screen.Write(demoAPC)
28 require.Equal(t, uint64(2), screen.GraphicsSnapshot().Usage().Assets)
29 require.Equal(t, uint64(2), screen.GraphicsSnapshot().Usage().Placements)
30 placements := screen.GraphicsSnapshot().Placements()
31 require.Equal(t, graphics.PixelRect{X: 1, Y: 1, Width: 2, Height: 2}, placements[1].Destination())
32 require.Equal(t, graphics.PixelRect{Width: 2, Height: 2}, placements[1].Source())
33 require.Equal(t, int64(1), placements[1].Layer())
34
35 frame, err := RenderTerminal(demoAPC, geometry)
36 require.NoError(t, err)
37 require.Equal(t, color.RGBA{22, 62, 143, 255}, frame.RGBAAt(0, 0))
38 require.Equal(t, color.RGBA{168, 67, 106, 255}, frame.RGBAAt(1, 1))
39}
40
41func TestRenderTerminalAPC(t *testing.T) {
42 frame, err := RenderTerminal([]byte("\x1b_Ga=T,i=1,f=32,s=1,v=1;/wAA/w\x1b\\"), vt.Geometry{Cols: 1, Rows: 1, PixelWidth: 1, PixelHeight: 1})
43 require.NoError(t, err)
44 require.Equal(t, color.RGBA{255, 0, 0, 255}, frame.RGBAAt(0, 0))
45}
46
47func TestRenderDecodesMetadataBearingPNG(t *testing.T) {
48 encoded, err := base64.StdEncoding.DecodeString(pngSuiteEXIFBase64)
49 require.NoError(t, err)
50 require.True(t, bytes.Contains(encoded, []byte("eXIf")), "fixture must retain its eXIf metadata chunk")
51
52 input := []byte("\x1b_Ga=T,i=1,f=100;" + strings.ReplaceAll(pngSuiteEXIFBase64, "\n", "") + "\x1b\\")
53 frame, err := RenderTerminal(input, vt.Geometry{Cols: 32, Rows: 32, PixelWidth: 32, PixelHeight: 32})
54 require.NoError(t, err)
55 require.Equal(t, 32, frame.Bounds().Dx())
56 require.Equal(t, 32, frame.Bounds().Dy())
57}
58
59func TestRenderRejectsPNGDimensionsDifferentFromSnapshot(t *testing.T) {
60 encoded, err := base64.StdEncoding.DecodeString(pngSuiteEXIFBase64)
61 require.NoError(t, err)
62 scene := graphics.NewScene(graphics.Limits{})
63 assetID, err := scene.AddAsset(graphics.AssetBlob{Format: graphics.AssetFormatPNG, Width: 31, Height: 32, Encoded: encoded})
64 require.NoError(t, err)
65 _, err = scene.Place(graphics.PlacementSpec{Asset: assetID, Destination: graphics.PixelRect{Width: 31, Height: 32}})
66 require.NoError(t, err)
67
68 _, err = Render(scene.Snapshot(), graphics.PixelRect{Width: 31, Height: 32})
69 require.EqualError(t, err, "decode asset a1: PNG dimensions do not match snapshot")
70}
71
72func TestRenderScalesCropsAndClips(t *testing.T) {
73 scene := graphics.NewScene(graphics.Limits{})
74 assetID, err := scene.AddAsset(graphics.AssetBlob{
75 Format: graphics.AssetFormatRGBA,
76 Width: 3,
77 Height: 2,
78 Encoded: []byte{
79 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255,
80 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255,
81 },
82 })
83 require.NoError(t, err)
84 _, err = scene.Place(graphics.PlacementSpec{
85 Asset: assetID,
86 Source: graphics.PixelRect{X: 1, Width: 2, Height: 2},
87 Destination: graphics.PixelRect{X: -1, Y: 1, Width: 5, Height: 2},
88 })
89 require.NoError(t, err)
90
91 frame, err := Render(scene.Snapshot(), graphics.PixelRect{Width: 4, Height: 4})
92 require.NoError(t, err)
93
94 for x, want := range []color.RGBA{{0, 255, 0, 255}, {0, 255, 0, 255}, {0, 0, 255, 255}, {0, 0, 255, 255}} {
95 require.Equal(t, want, frame.RGBAAt(x, 1))
96 }
97 for x, want := range []color.RGBA{{255, 0, 255, 255}, {255, 0, 255, 255}, {255, 255, 0, 255}, {255, 255, 0, 255}} {
98 require.Equal(t, want, frame.RGBAAt(x, 2))
99 }
100}
101
102func TestRenderCompositesPlacementsInLayerOrder(t *testing.T) {
103 scene := graphics.NewScene(graphics.Limits{})
104 background, err := scene.AddAsset(graphics.AssetBlob{Format: graphics.AssetFormatRGBA, Width: 2, Height: 1, Encoded: []byte{0, 0, 255, 255, 0, 0, 255, 255}})
105 require.NoError(t, err)
106 foreground, err := scene.AddAsset(graphics.AssetBlob{Format: graphics.AssetFormatRGBA, Width: 1, Height: 1, Encoded: []byte{255, 0, 0, 128}})
107 require.NoError(t, err)
108 _, err = scene.Place(graphics.PlacementSpec{Asset: background, Destination: graphics.PixelRect{Width: 2, Height: 1}, HasLayer: true})
109 require.NoError(t, err)
110 _, err = scene.Place(graphics.PlacementSpec{Asset: foreground, Destination: graphics.PixelRect{X: 1, Width: 1, Height: 1}, Layer: 1, HasLayer: true})
111 require.NoError(t, err)
112
113 frame, err := Render(scene.Snapshot(), graphics.PixelRect{Width: 2, Height: 1})
114 require.NoError(t, err)
115 require.Equal(t, color.RGBA{0, 0, 255, 255}, frame.RGBAAt(0, 0))
116 require.Equal(t, color.RGBA{128, 0, 127, 255}, frame.RGBAAt(1, 0))
117}