Go bindings for libghostty-vt.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #9 from mitchellh/push-suyvllymsktt

sys/png: move SysDecodePng to separate subpackage

authored by

Mitchell Hashimoto and committed by
GitHub
e65c1153 5367d47a

+107 -69
+27 -1
kitty_graphics_test.go
··· 1 1 package libghostty 2 2 3 3 import ( 4 + "bytes" 5 + "fmt" 6 + "image" 7 + "image/png" 4 8 "testing" 5 9 ) 6 10 11 + // testDecodePng is a minimal SysDecodePngFn for tests. It avoids 12 + // importing the syspng subpackage (which would create an import cycle 13 + // in internal tests) by inlining the same logic. 14 + func testDecodePng(data []byte) (*SysImage, error) { 15 + img, err := png.Decode(bytes.NewReader(data)) 16 + if err != nil { 17 + return nil, fmt.Errorf("png decode: %w", err) 18 + } 19 + bounds := img.Bounds() 20 + w, h := bounds.Dx(), bounds.Dy() 21 + if nrgba, ok := img.(*image.NRGBA); ok { 22 + return &SysImage{Width: uint32(w), Height: uint32(h), Data: nrgba.Pix}, nil 23 + } 24 + dst := image.NewNRGBA(bounds) 25 + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 26 + for x := bounds.Min.X; x < bounds.Max.X; x++ { 27 + dst.Set(x, y, img.At(x, y)) 28 + } 29 + } 30 + return &SysImage{Width: uint32(w), Height: uint32(h), Data: dst.Pix}, nil 31 + } 32 + 7 33 // newKittyTerminal creates a terminal with Kitty graphics enabled 8 34 // (PNG decode callback, WritePty handler, storage limit, and cell 9 35 // pixel dimensions), ready for Kitty graphics protocol testing. ··· 11 37 t.Helper() 12 38 13 39 // Install the PNG decoder. 14 - if err := SysSetDecodePng(SysDecodePng); err != nil { 40 + if err := SysSetDecodePng(testDecodePng); err != nil { 15 41 t.Fatal(err) 16 42 } 17 43
+67
sys/png/decode.go
··· 1 + // Package png provides a ready-to-use PNG decoder for libghostty 2 + // using Go's standard [image/png] package. 3 + // 4 + // This package is separate from the root libghostty package so that 5 + // importing libghostty does not unconditionally pull in image/png 6 + // (and its init-time image format registration). Import this package 7 + // only when you need PNG decoding: 8 + // 9 + // import ( 10 + // libghostty "github.com/mitchellh/go-libghostty" 11 + // syspng "github.com/mitchellh/go-libghostty/sys/png" 12 + // ) 13 + // 14 + // libghostty.SysSetDecodePng(syspng.Decode) 15 + package png 16 + 17 + import ( 18 + "bytes" 19 + "fmt" 20 + "image" 21 + goimg "image/png" 22 + 23 + libghostty "github.com/mitchellh/go-libghostty" 24 + ) 25 + 26 + // Decode is a ready-to-use [libghostty.SysDecodePngFn] implementation 27 + // that decodes PNG data using Go's standard [image/png] package. It 28 + // converts any decoded image format to NRGBA (non-premultiplied alpha) 29 + // before returning the raw pixel bytes. 30 + // 31 + // Usage: 32 + // 33 + // libghostty.SysSetDecodePng(syspng.Decode) 34 + func Decode(data []byte) (*libghostty.SysImage, error) { 35 + img, err := goimg.Decode(bytes.NewReader(data)) 36 + if err != nil { 37 + return nil, fmt.Errorf("png decode: %w", err) 38 + } 39 + 40 + bounds := img.Bounds() 41 + w := bounds.Dx() 42 + h := bounds.Dy() 43 + 44 + // Fast path: if the image is already NRGBA we can use the pixels 45 + // directly without a per-pixel conversion. 46 + if nrgba, ok := img.(*image.NRGBA); ok { 47 + return &libghostty.SysImage{ 48 + Width: uint32(w), 49 + Height: uint32(h), 50 + Data: nrgba.Pix, 51 + }, nil 52 + } 53 + 54 + // Slow path: convert arbitrary image types to NRGBA. 55 + dst := image.NewNRGBA(bounds) 56 + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 57 + for x := bounds.Min.X; x < bounds.Max.X; x++ { 58 + dst.Set(x, y, img.At(x, y)) 59 + } 60 + } 61 + 62 + return &libghostty.SysImage{ 63 + Width: uint32(w), 64 + Height: uint32(h), 65 + Data: dst.Pix, 66 + }, nil 67 + }
-55
sys_builtin.go
··· 1 - package libghostty 2 - 3 - // Built-in implementations for system callbacks using Go standard library 4 - // packages. These are optional convenience functions that can be passed 5 - // directly to their corresponding SysSet* installers. 6 - 7 - import ( 8 - "bytes" 9 - "fmt" 10 - "image" 11 - "image/png" 12 - ) 13 - 14 - // SysDecodePng is a ready-to-use [SysDecodePngFn] implementation that 15 - // decodes PNG data using Go's standard [image/png] package. It converts 16 - // any decoded image format to NRGBA (non-premultiplied alpha) before 17 - // returning the raw pixel bytes. 18 - // 19 - // Usage: 20 - // 21 - // libghostty.SysSetDecodePng(libghostty.SysDecodePng) 22 - func SysDecodePng(data []byte) (*SysImage, error) { 23 - img, err := png.Decode(bytes.NewReader(data)) 24 - if err != nil { 25 - return nil, fmt.Errorf("png decode: %w", err) 26 - } 27 - 28 - bounds := img.Bounds() 29 - w := bounds.Dx() 30 - h := bounds.Dy() 31 - 32 - // Fast path: if the image is already NRGBA we can use the pixels 33 - // directly without a per-pixel conversion. 34 - if nrgba, ok := img.(*image.NRGBA); ok { 35 - return &SysImage{ 36 - Width: uint32(w), 37 - Height: uint32(h), 38 - Data: nrgba.Pix, 39 - }, nil 40 - } 41 - 42 - // Slow path: convert arbitrary image types to NRGBA. 43 - dst := image.NewNRGBA(bounds) 44 - for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 45 - for x := bounds.Min.X; x < bounds.Max.X; x++ { 46 - dst.Set(x, y, img.At(x, y)) 47 - } 48 - } 49 - 50 - return &SysImage{ 51 - Width: uint32(w), 52 - Height: uint32(h), 53 - Data: dst.Pix, 54 - }, nil 55 - }
+13 -13
sys_builtin_test.go sys/png/decode_test.go
··· 1 - package libghostty 1 + package png 2 2 3 3 import ( 4 4 "bytes" 5 5 "image" 6 6 "image/color" 7 - "image/png" 7 + goimg "image/png" 8 8 "testing" 9 9 ) 10 10 11 - func TestSysDecodePng(t *testing.T) { 11 + func TestDecode(t *testing.T) { 12 12 // Encode a small 2x2 NRGBA PNG in-memory. 13 13 src := image.NewNRGBA(image.Rect(0, 0, 2, 2)) 14 14 src.SetNRGBA(0, 0, color.NRGBA{R: 255, A: 255}) ··· 17 17 src.SetNRGBA(1, 1, color.NRGBA{R: 255, G: 255, B: 255, A: 255}) 18 18 19 19 var buf bytes.Buffer 20 - if err := png.Encode(&buf, src); err != nil { 20 + if err := goimg.Encode(&buf, src); err != nil { 21 21 t.Fatalf("png.Encode: %v", err) 22 22 } 23 23 24 - img, err := SysDecodePng(buf.Bytes()) 24 + img, err := Decode(buf.Bytes()) 25 25 if err != nil { 26 - t.Fatalf("SysDecodePng: %v", err) 26 + t.Fatalf("Decode: %v", err) 27 27 } 28 28 29 29 if img.Width != 2 || img.Height != 2 { ··· 41 41 } 42 42 } 43 43 44 - func TestSysDecodePngInvalid(t *testing.T) { 45 - _, err := SysDecodePng([]byte("not a png")) 44 + func TestDecodeInvalid(t *testing.T) { 45 + _, err := Decode([]byte("not a png")) 46 46 if err == nil { 47 - t.Fatal("SysDecodePng(invalid) = nil error, want error") 47 + t.Fatal("Decode(invalid) = nil error, want error") 48 48 } 49 49 } 50 50 51 - func TestSysDecodePngRGBA(t *testing.T) { 51 + func TestDecodeRGBA(t *testing.T) { 52 52 // Use an RGBA image (premultiplied alpha) to exercise the slow path 53 53 // conversion to NRGBA. 54 54 src := image.NewRGBA(image.Rect(0, 0, 1, 1)) 55 55 src.SetRGBA(0, 0, color.RGBA{R: 128, G: 0, B: 0, A: 128}) 56 56 57 57 var buf bytes.Buffer 58 - if err := png.Encode(&buf, src); err != nil { 58 + if err := goimg.Encode(&buf, src); err != nil { 59 59 t.Fatalf("png.Encode: %v", err) 60 60 } 61 61 62 - img, err := SysDecodePng(buf.Bytes()) 62 + img, err := Decode(buf.Bytes()) 63 63 if err != nil { 64 - t.Fatalf("SysDecodePng: %v", err) 64 + t.Fatalf("Decode: %v", err) 65 65 } 66 66 67 67 if img.Width != 1 || img.Height != 1 {