1// Based on the implementation by @trashhalo at:
2// https://github.com/trashhalo/imgcat
16 tea "charm.land/bubbletea/v2"
17 "github.com/disintegration/imageorient"
18 "github.com/lucasb-eyer/go-colorful"
19 "github.com/muesli/termenv"
20 "github.com/nfnt/resize"
21 "github.com/srwiley/oksvg"
22 "github.com/srwiley/rasterx"
29func loadURL(url string) tea.Cmd {
33 if strings.HasPrefix(url, "http") {
34 var resp *http.Request
35 resp, err = http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
42 return func() tea.Msg {
50func load(r io.ReadCloser) tea.Cmd {
51 return func() tea.Msg {
56func handleLoadMsg(m Model, msg loadMsg) (Model, tea.Cmd) {
59 img, err := readerToImage(m.width, m.height, m.url, msg)
61 return m, func() tea.Msg { return errMsg{err} }
67func imageToString(width, height uint, img image.Image) (string, error) {
68 img = resize.Thumbnail(width, height*2-4, img, resize.Lanczos3)
72 p := termenv.ColorProfile()
73 str := strings.Builder{}
74 for y := 0; y < h; y += 2 {
75 for x := w; x < int(width); x = x + 2 {
79 c1, _ := colorful.MakeColor(img.At(x, y))
80 color1 := p.Color(c1.Hex())
81 c2, _ := colorful.MakeColor(img.At(x, y+1))
82 color2 := p.Color(c2.Hex())
83 str.WriteString(termenv.String("▀").
90 return str.String(), nil
93func readerToImage(width uint, height uint, url string, r io.Reader) (string, error) {
94 if strings.HasSuffix(strings.ToLower(url), ".svg") {
95 return svgToImage(width, height, r)
98 img, _, err := imageorient.Decode(r)
103 return imageToString(width, height, img)
106func svgToImage(width uint, height uint, r io.Reader) (string, error) {
107 // Original author: https://stackoverflow.com/users/10826783/usual-human
108 // https://stackoverflow.com/questions/42993407/how-to-create-and-export-svg-to-png-jpeg-in-golang
109 // Adapted to use size from SVG, and to use temp file.
111 tmpPngFile, err := os.CreateTemp("", "img.*.png")
115 tmpPngPath := tmpPngFile.Name()
116 defer os.Remove(tmpPngPath)
117 defer tmpPngFile.Close()
119 // Rasterize the SVG:
120 icon, err := oksvg.ReadIconStream(r)
124 w := int(icon.ViewBox.W)
125 h := int(icon.ViewBox.H)
126 icon.SetTarget(0, 0, float64(w), float64(h))
127 rgba := image.NewRGBA(image.Rect(0, 0, w, h))
128 icon.Draw(rasterx.NewDasher(w, h, rasterx.NewScannerGV(w, h, rgba, rgba.Bounds())), 1)
129 // Write rasterized image as PNG:
130 err = png.Encode(tmpPngFile, rgba)
137 rPng, err := os.Open(tmpPngPath)
143 img, _, err := imageorient.Decode(rPng)
147 return imageToString(width, height, img)
150// ImageFromBase64 renders an image from base64-encoded data.
151func ImageFromBase64(width, height uint, data, mediaType string) (string, error) {
152 decoded, err := base64.StdEncoding.DecodeString(data)
157 r := bytes.NewReader(decoded)
159 if strings.Contains(mediaType, "svg") {
160 return svgToImage(width, height, r)
163 img, _, err := imageorient.Decode(r)
168 return imageToString(width, height, img)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list