install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
package ascii
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
"net/http"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func NewWriter(w io.Writer, foreground, background, text string) io.Writer {
|
||||
// once clear screen
|
||||
_, _ = w.Write([]byte(csiClear))
|
||||
|
||||
// every frame - move to home
|
||||
a := &writer{wr: w, buf: []byte(csiHome)}
|
||||
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
switch foreground {
|
||||
case "":
|
||||
case "8":
|
||||
a.color = func(r, g, b uint8) {
|
||||
idx := xterm256color(r, g, b, 8)
|
||||
a.appendEsc(fmt.Sprintf("\033[%dm", 30+idx))
|
||||
|
||||
}
|
||||
case "256":
|
||||
a.color = func(r, g, b uint8) {
|
||||
idx := xterm256color(r, g, b, 255)
|
||||
a.appendEsc(fmt.Sprintf("\033[38;5;%dm", idx))
|
||||
}
|
||||
case "rgb":
|
||||
a.color = func(r, g, b uint8) {
|
||||
a.appendEsc(fmt.Sprintf("\033[38;2;%d;%d;%dm", r, g, b))
|
||||
}
|
||||
default:
|
||||
a.buf = append(a.buf, "\033["+foreground+"m"...)
|
||||
}
|
||||
|
||||
switch background {
|
||||
case "":
|
||||
case "8":
|
||||
a.color = func(r, g, b uint8) {
|
||||
idx := xterm256color(r, g, b, 8)
|
||||
a.appendEsc(fmt.Sprintf("\033[%dm", 40+idx))
|
||||
}
|
||||
case "256":
|
||||
a.color = func(r, g, b uint8) {
|
||||
idx := xterm256color(r, g, b, 255)
|
||||
a.appendEsc(fmt.Sprintf("\033[48;5;%dm", idx))
|
||||
}
|
||||
case "rgb":
|
||||
a.color = func(r, g, b uint8) {
|
||||
a.appendEsc(fmt.Sprintf("\033[48;2;%d;%d;%dm", r, g, b))
|
||||
}
|
||||
default:
|
||||
a.buf = append(a.buf, "\033["+background+"m"...)
|
||||
}
|
||||
|
||||
a.pre = len(a.buf) // save prefix size
|
||||
|
||||
if len(text) == 1 {
|
||||
// fast 1 symbol version
|
||||
a.text = func(_, _, _ uint32) {
|
||||
a.buf = append(a.buf, text[0])
|
||||
}
|
||||
} else {
|
||||
switch text {
|
||||
case "":
|
||||
text = ` .::--~~==++**##%%$@` // default for empty text
|
||||
case "block":
|
||||
text = " ░░▒▒▓▓█" // https://en.wikipedia.org/wiki/Block_Elements
|
||||
}
|
||||
|
||||
if runes := []rune(text); len(runes) != len(text) {
|
||||
k := float32(len(runes)-1) / 255
|
||||
a.text = func(r, g, b uint32) {
|
||||
i := gray(r, g, b, k)
|
||||
a.buf = utf8.AppendRune(a.buf, runes[i])
|
||||
}
|
||||
} else {
|
||||
k := float32(len(text)-1) / 255
|
||||
a.text = func(r, g, b uint32) {
|
||||
i := gray(r, g, b, k)
|
||||
a.buf = append(a.buf, text[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
wr io.Writer
|
||||
buf []byte
|
||||
pre int
|
||||
esc string
|
||||
color func(r, g, b uint8)
|
||||
text func(r, g, b uint32)
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/37774983/clearing-the-screen-by-printing-a-character
|
||||
const csiClear = "\033[2J"
|
||||
const csiHome = "\033[H"
|
||||
|
||||
func (a *writer) Write(p []byte) (n int, err error) {
|
||||
img, err := jpeg.Decode(bytes.NewReader(p))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
a.buf = a.buf[:a.pre] // restore prefix
|
||||
|
||||
w := img.Bounds().Dx()
|
||||
h := img.Bounds().Dy()
|
||||
for y := 0; y < h; y++ {
|
||||
for x := 0; x < w; x++ {
|
||||
r, g, b, _ := img.At(x, y).RGBA()
|
||||
if a.color != nil {
|
||||
a.color(uint8(r>>8), uint8(g>>8), uint8(b>>8))
|
||||
}
|
||||
a.text(r, g, b)
|
||||
}
|
||||
a.buf = append(a.buf, '\n')
|
||||
}
|
||||
|
||||
a.appendEsc("\033[0m")
|
||||
|
||||
if _, err = a.wr.Write(a.buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
a.wr.(http.Flusher).Flush()
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// appendEsc - append ESC code to buffer, and skip duplicates
|
||||
func (a *writer) appendEsc(s string) {
|
||||
if a.esc != s {
|
||||
a.esc = s
|
||||
a.buf = append(a.buf, s...)
|
||||
}
|
||||
}
|
||||
|
||||
func gray(r, g, b uint32, k float32) uint8 {
|
||||
gr := (19595*r + 38470*g + 7471*b + 1<<15) >> 24 // uint8
|
||||
return uint8(float32(gr) * k)
|
||||
}
|
||||
|
||||
const x256r = "\x00\x80\x00\x80\x00\x80\x00\xc0\x80\xff\x00\xff\x00\xff\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x08\x12\x1c\x26\x30\x3a\x44\x4e\x58\x60\x66\x76\x80\x8a\x94\x9e\xa8\xb2\xbc\xc6\xd0\xda\xe4\xee"
|
||||
const x256g = "\x00\x00\x80\x80\x00\x00\x80\xc0\x80\x00\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x5f\x5f\x5f\x5f\x5f\x5f\x87\x87\x87\x87\x87\x87\xaf\xaf\xaf\xaf\xaf\xaf\xd7\xd7\xd7\xd7\xd7\xd7\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x5f\x5f\x5f\x5f\x5f\x5f\x87\x87\x87\x87\x87\x87\xaf\xaf\xaf\xaf\xaf\xaf\xd7\xd7\xd7\xd7\xd7\xd7\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x5f\x5f\x5f\x5f\x5f\x5f\x87\x87\x87\x87\x87\x87\xaf\xaf\xaf\xaf\xaf\xaf\xd7\xd7\xd7\xd7\xd7\xd7\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x5f\x5f\x5f\x5f\x5f\x5f\x87\x87\x87\x87\x87\x87\xaf\xaf\xaf\xaf\xaf\xaf\xd7\xd7\xd7\xd7\xd7\xd7\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x5f\x5f\x5f\x5f\x5f\x5f\x87\x87\x87\x87\x87\x87\xaf\xaf\xaf\xaf\xaf\xaf\xd7\xd7\xd7\xd7\xd7\xd7\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x5f\x5f\x5f\x5f\x5f\x5f\x87\x87\x87\x87\x87\x87\xaf\xaf\xaf\xaf\xaf\xaf\xd7\xd7\xd7\xd7\xd7\xd7\xff\xff\xff\xff\xff\xff\x08\x12\x1c\x26\x30\x3a\x44\x4e\x58\x60\x66\x76\x80\x8a\x94\x9e\xa8\xb2\xbc\xc6\xd0\xda\xe4\xee"
|
||||
const x256b = "\x00\x00\x00\x00\x80\x80\x80\xc0\x80\x00\x00\x00\xff\xff\xff\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x00\x5f\x87\xaf\xd7\xff\x08\x12\x1c\x26\x30\x3a\x44\x4e\x58\x60\x66\x76\x80\x8a\x94\x9e\xa8\xb2\xbc\xc6\xd0\xda\xe4\xee"
|
||||
|
||||
func xterm256color(r, g, b uint8, n int) (index uint8) {
|
||||
best := uint16(0xFFFF)
|
||||
for i := 0; i < n; i++ {
|
||||
diff := sqDiff(r, x256r[i]) + sqDiff(g, x256g[i]) + sqDiff(b, x256b[i])
|
||||
if diff < best {
|
||||
best = diff
|
||||
index = uint8(i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// sqDiff - just like from image/color/color.go
|
||||
func sqDiff(x, y uint8) uint16 {
|
||||
d := uint16(x - y)
|
||||
//return d
|
||||
return (d * d) >> 2
|
||||
}
|
||||
Reference in New Issue
Block a user