package main import ( "image" "image/color" _ "image/gif" _ "image/jpeg" _ "image/png" "log" "math" "os" termbox "github.com/nsf/termbox-go" ) var palette color.Palette = []color.Color{ attrColor(termbox.ColorBlack), attrColor(termbox.ColorRed), attrColor(termbox.ColorGreen), attrColor(termbox.ColorYellow), attrColor(termbox.ColorBlue), attrColor(termbox.ColorMagenta), attrColor(termbox.ColorCyan), attrColor(termbox.ColorWhite), } func upgrade(palette color.Palette) color.Palette { for i := 16; i <= 255; i++ { palette = append(palette, attrColor(i)) } return palette } type attrColor termbox.Attribute var base = [...]uint8{0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff} func (c attrColor) RGBA() (r, g, b, a uint32) { switch termbox.Attribute(c) { case termbox.ColorBlack: return 0, 0, 0, math.MaxUint16 case termbox.ColorRed: return math.MaxUint16, 0, 0, math.MaxUint16 case termbox.ColorGreen: return 0, math.MaxUint16, 0, math.MaxUint16 case termbox.ColorYellow: return math.MaxUint16, math.MaxUint16, 0, math.MaxUint16 case termbox.ColorBlue: return 0, 0, math.MaxUint16, math.MaxUint16 case termbox.ColorMagenta: return math.MaxUint16, 0, math.MaxUint16, math.MaxUint16 case termbox.ColorCyan: return 0, math.MaxUint16, math.MaxUint16, math.MaxUint16 case termbox.ColorWhite: return math.MaxUint16, math.MaxUint16, math.MaxUint16, math.MaxUint16 } switch { case c >= 16 && c <= 231: c -= 16 rgba := color.RGBA{R: base[(c/36)%6], G: base[(c/6)%6], B: base[c%6], A: 0xff} return rgba.RGBA() case c >= 232 && c <= 255: x := uint8(0x08 + 0xa*(c-232)) rgba := color.RGBA{R: x, G: x, B: x, A: 0xff} return rgba.RGBA() } panic("not found") } func fit(w, h, iw, ih int) (sw, sh int) { if w >= iw && h >= ih { // image is smaller than screen return iw, ih } else if w >= iw { // image is taller than the screen return iw * h / ih, h } else if h >= ih { // image is skinnier than the screen return w, ih * w / iw } else { sw, sh = iw*h/ih, h if sw <= w { return sw, sh } return w, ih * w / iw } } func draw(img image.Image) { w, h := termbox.Size() iw, ih := img.Bounds().Max.X, img.Bounds().Max.Y sw, sh := fit(w, h, iw*2, ih) termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) for y := 0; y < h; y++ { for x := 0; x < w; x++ { if x >= sw || y >= sh { termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorDefault) continue } xi := x * img.Bounds().Max.X / sw yi := y * img.Bounds().Max.Y / sh a := palette.Convert(img.At(xi, yi)) if at, ok := a.(attrColor); ok { termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.Attribute(at)) } } } termbox.Flush() } func main() { fname := os.Args[1] f, err := os.Open(fname) if err != nil { log.Fatal(err) } defer f.Close() img, _, err := image.Decode(f) if err != nil { log.Fatal(err) } err = termbox.Init() if err != nil { panic(err) } defer termbox.Close() if termbox.SetOutputMode(termbox.Output256) == termbox.Output256 { palette = upgrade(palette) } lw, lh := termbox.Size() draw(img) termbox.PollEvent() termbox.Flush() if w, h := termbox.Size(); lw != w || lh != h { lw, lh = w, h draw(img) } }