-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprint_image.go
143 lines (117 loc) · 3.24 KB
/
print_image.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package icat
import (
"bytes"
"fmt"
"image"
"image/color/palette"
"image/draw"
"io"
"net/http"
"os"
"time"
"github.com/BourgeoisBear/rasterm"
"github.com/hilli/icat/util"
"github.com/nfnt/resize"
ascii "github.com/qeesung/image2ascii/convert"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
_ "golang.org/x/image/bmp"
_ "golang.org/x/image/riff"
_ "golang.org/x/image/tiff"
_ "golang.org/x/image/vp8"
_ "golang.org/x/image/vp8l"
_ "golang.org/x/image/webp"
)
func PrintImageFile(imageFileName string, forceASCII bool) error {
imageFile, imageSize, err := util.FileAndStat(imageFileName)
if err != nil {
return err
}
defer imageFile.Close()
imageData, err := os.ReadFile(imageFileName)
if err != nil {
return err
}
img, err := DecodeImage(imageData)
if err != nil {
return err
}
return PrintImage(img, imageFileName, imageSize, forceASCII)
}
func PrintImageURL(imageURL string, forceASCII bool) error {
client := http.Client{
Timeout: 5 * time.Second,
}
resp, err := client.Get(imageURL)
if err != nil {
return err
}
defer resp.Body.Close()
imageData, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
img, err := DecodeImage(imageData)
if err != nil {
return err
}
return PrintImage(img, imageURL, resp.ContentLength, forceASCII)
}
func PrintImage(img *image.Image, filename string, imageSize int64, forceASCII bool) error {
var img2 image.Image = *img
if forceASCII {
fmt.Print("\n", ConvertToASCII(img2))
return nil
}
sixelCapable, _ := rasterm.IsSixelCapable()
_, _, pw, ph := TermSize() // Get terminal height and width in pixels
size, resizeOption := resizeConstraints(img2.Bounds(), int(pw), int(ph))
switch resizeOption {
case 'x':
img2 = resize.Resize(uint(size), 0, img2, resize.NearestNeighbor)
case 'y':
img2 = resize.Resize(0, uint(size), img2, resize.NearestNeighbor)
case '0':
// Do nothing
}
newWidth := img2.Bounds().Max.X
newHeight := img2.Bounds().Max.Y
kittyOpts := rasterm.KittyImgOpts{SrcWidth: uint32(newWidth), SrcHeight: uint32(newHeight)}
switch {
case rasterm.IsKittyCapable():
return rasterm.KittyWriteImage(os.Stdout, img2, kittyOpts)
case rasterm.IsItermCapable():
return rasterm.ItermWriteImage(os.Stdout, img2)
case sixelCapable:
// Convert image to a paletted format
palettedImg := ConvertToPaletted(img2)
return rasterm.SixelWriteImage(os.Stdout, palettedImg)
default:
// Ascii art fallback
fmt.Print("\n", ConvertToASCII(img2)) // Align image at the initial position instead of \n first?
}
return nil
}
func DecodeImage(imageData []byte) (*image.Image, error) {
imageDataReader := bytes.NewReader(imageData)
image, _, err := image.Decode(imageDataReader)
if err != nil {
return nil, err
}
return &image, nil
}
// ConvertToPaletted converts an image.Image to an image.Paletted
// Needed for Sixel conversion
func ConvertToPaletted(img image.Image) *image.Paletted {
bounds := img.Bounds()
palettedImg := image.NewPaletted(bounds, palette.Plan9)
draw.Draw(palettedImg, bounds, img, bounds.Min, draw.Over)
return palettedImg
}
// ASCII art conversion
func ConvertToASCII(img image.Image) string {
converter := ascii.NewImageConverter()
convertOptions := ascii.DefaultOptions
return converter.Image2ASCIIString(img, &convertOptions)
}