aboutsummaryrefslogtreecommitdiff
path: root/lib/image_renderer.go
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-09-08 18:34:58 -0400
committerBobby <[email protected]>2024-09-08 18:34:58 -0400
commite25611bde49fe2db28a006aca3ea49eece046c5f (patch)
treedae68f8784010ff9002fe0e3c5d228879c15a25f /lib/image_renderer.go
parent61bc5b38044bc52442415f83390ba72ed3b27491 (diff)
downloadyato-main.tar.xz
yato-main.zip
Image Rendering. Recommendations Thingy. Basic HomeHEADmain
Diffstat (limited to 'lib/image_renderer.go')
-rw-r--r--lib/image_renderer.go158
1 files changed, 158 insertions, 0 deletions
diff --git a/lib/image_renderer.go b/lib/image_renderer.go
new file mode 100644
index 0000000..03a8059
--- /dev/null
+++ b/lib/image_renderer.go
@@ -0,0 +1,158 @@
+package lib
+
+import (
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "image"
+ "image/color"
+ "image/jpeg"
+ "image/png"
+ "os"
+ "strings"
+
+ "golang.org/x/image/draw"
+)
+
+type ImageRenderer struct {
+ method string
+}
+
+func NewImageRenderer() *ImageRenderer {
+ method := determineRenderMethod()
+ return &ImageRenderer{method: method}
+}
+
+func determineRenderMethod() string {
+ if os.Getenv("TERM") == "xterm-kitty" {
+ return "kitty"
+ } else if os.Getenv("TERM_PROGRAM") == "iTerm.app" {
+ return "iterm2"
+ } else if os.Getenv("TERM") == "xterm-256color" && os.Getenv("VTE_VERSION") != "" {
+ return "sixel"
+ }
+ return "none"
+}
+
+func (r *ImageRenderer) RenderImage(img image.Image, width, height int) string {
+ switch r.method {
+ case "kitty":
+ return r.renderKitty(img, width, height)
+ case "iterm2":
+ return r.renderITerm2(img, width, height)
+ case "sixel":
+ return r.renderSixel(img, width, height)
+ case "ascii":
+ return r.renderASCII(img, width, height)
+ default:
+ return ""
+ }
+}
+
+func (r *ImageRenderer) renderKitty(img image.Image, width, height int) string {
+ resized := image.NewRGBA(image.Rect(0, 0, width, height))
+ draw.NearestNeighbor.Scale(resized, resized.Rect, img, img.Bounds(), draw.Over, nil)
+
+ var buf bytes.Buffer
+ png.Encode(&buf, resized)
+ encoded := base64.StdEncoding.EncodeToString(buf.Bytes())
+
+ // Split the encoded data into chunks
+ const chunkSize = 4096
+ chunks := make([]string, 0, (len(encoded)+chunkSize-1)/chunkSize)
+ for i := 0; i < len(encoded); i += chunkSize {
+ end := i + chunkSize
+ if end > len(encoded) {
+ end = len(encoded)
+ }
+ chunks = append(chunks, encoded[i:end])
+ }
+
+ // Build the Kitty graphics protocol command
+ var result strings.Builder
+ for i, chunk := range chunks {
+ if i == 0 {
+ result.WriteString(fmt.Sprintf("\033_Ga=T,f=100,s=%d,v=%d,m=1;", width, height))
+ } else {
+ result.WriteString("\033_Gm=1;")
+ }
+ result.WriteString(chunk)
+ result.WriteString("\033\\")
+ }
+
+ // Final chunk
+ result.WriteString("\033_Gm=0;\033\\")
+
+ return result.String()
+}
+
+func (r *ImageRenderer) renderITerm2(img image.Image, width, height int) string {
+ // Implement iTerm2 inline image protocol
+ var buf bytes.Buffer
+ jpeg.Encode(&buf, img, nil)
+ encoded := base64.StdEncoding.EncodeToString(buf.Bytes())
+ return fmt.Sprintf("\033]1337;File=inline=1;width=%dpx;height=%dpx:%s\a", width, height, encoded)
+}
+
+func (r *ImageRenderer) renderSixel(img image.Image, width, height int) string {
+ resized := image.NewRGBA(image.Rect(0, 0, width, height))
+ draw.NearestNeighbor.Scale(resized, resized.Rect, img, img.Bounds(), draw.Over, nil)
+
+ // Convert to Sixel
+ var sb strings.Builder
+ sb.WriteString("\033Pq") // Start Sixel sequence
+ sb.WriteString("\"1;1;") // Set color mode and aspect ratio
+ sb.WriteString(fmt.Sprintf("%d;%d", width, height))
+ sb.WriteString("\n")
+
+ // Simple color quantization (this can be improved)
+ palette := make(map[color.Color]int)
+ colorIndex := 0
+
+ for y := 0; y < height; y++ {
+ sixelRow := make([]int, width)
+ for x := 0; x < width; x++ {
+ c := resized.At(x, y)
+ if _, exists := palette[c]; !exists {
+ palette[c] = colorIndex
+ colorIndex++
+ r, g, b, _ := c.RGBA()
+ sb.WriteString(fmt.Sprintf("#%d;2;%d;%d;%d", palette[c], r>>8, g>>8, b>>8))
+ }
+ sixelRow[x] = palette[c]
+ }
+
+ // Encode sixel data
+ for i := 0; i < 6; i++ {
+ for _, colorIdx := range sixelRow {
+ sb.WriteByte(byte('?' + ((colorIdx >> i) & 1)))
+ }
+ sb.WriteByte('-')
+ }
+ sb.WriteByte('\n')
+ }
+
+ sb.WriteString("\033\\") // End Sixel sequence
+ return sb.String()
+}
+
+func (r *ImageRenderer) renderASCII(img image.Image, width, height int) string {
+ // Implement a simple ASCII art renderer
+ // This is a very basic implementation and can be improved
+ bounds := img.Bounds()
+ ascii := ""
+ for y := bounds.Min.Y; y < bounds.Max.Y; y += height / 10 {
+ for x := bounds.Min.X; x < bounds.Max.X; x += width / 20 {
+ c := img.At(x, y)
+ r, g, b, _ := c.RGBA()
+ avg := (r + g + b) / 3
+ if avg > 32768 {
+ ascii += " "
+ } else {
+ ascii += "#"
+ }
+ }
+ ascii += "\n"
+ }
+ return ascii
+}