prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

logo.go (2003B)


      1 package utils
      2 
      3 import (
      4 	"fmt"
      5 	"image"
      6 	"path/filepath"
      7 	"strings"
      8 	"sync"
      9 
     10 	aic "github.com/TheZoraiz/ascii-image-converter/aic_package"
     11 	"github.com/disintegration/imaging"
     12 )
     13 
     14 var logoMu sync.Mutex
     15 
     16 const logoPath = "static/me.jpg"
     17 
     18 // logoCrop frames the face: fractions of the source image (x0, y0, x1, y1).
     19 var logoCrop = [4]float64{0.12, 0.08, 0.88, 0.84}
     20 
     21 // LogoASCII renders static/me.jpg for terminals. Colored output uses
     22 // half-block cells (upper half = foreground, lower half = background), so each
     23 // cell carries two truecolor pixels and the portrait reads as a photo. Plain
     24 // output falls back to ascii art.
     25 func LogoASCII(width int, colored bool) string {
     26 	logoMu.Lock()
     27 	defer logoMu.Unlock()
     28 
     29 	if colored {
     30 		if s, err := halfBlocks(logoPath, width); err == nil {
     31 			return s
     32 		}
     33 	}
     34 	flags := aic.DefaultFlags()
     35 	flags.Width = width
     36 	flags.Dither = true
     37 	flags.Threshold = 160
     38 	ascii, err := aic.Convert(filepath.FromSlash(logoPath), flags)
     39 	if err != nil {
     40 		return ""
     41 	}
     42 	return strings.TrimRight(ascii, "\n")
     43 }
     44 
     45 func halfBlocks(path string, width int) (string, error) {
     46 	src, err := imaging.Open(path, imaging.AutoOrientation(true))
     47 	if err != nil {
     48 		return "", err
     49 	}
     50 	b := src.Bounds()
     51 	w, h := float64(b.Dx()), float64(b.Dy())
     52 	rect := image.Rect(
     53 		b.Min.X+int(logoCrop[0]*w), b.Min.Y+int(logoCrop[1]*h),
     54 		b.Min.X+int(logoCrop[2]*w), b.Min.Y+int(logoCrop[3]*h),
     55 	)
     56 	crop := imaging.Crop(src, rect)
     57 	// one cell is roughly twice as tall as wide, so two pixel rows per cell
     58 	// keep the pixels square
     59 	rows := int(float64(width)*float64(rect.Dy())/float64(rect.Dx())+0.5) &^ 1
     60 	img := imaging.Resize(crop, width, rows, imaging.Lanczos)
     61 
     62 	var sb strings.Builder
     63 	for y := 0; y < rows; y += 2 {
     64 		if y > 0 {
     65 			sb.WriteByte('\n')
     66 		}
     67 		for x := 0; x < width; x++ {
     68 			t := img.NRGBAAt(x, y)
     69 			u := img.NRGBAAt(x, y+1)
     70 			fmt.Fprintf(&sb, "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dmâ–€", t.R, t.G, t.B, u.R, u.G, u.B)
     71 		}
     72 		sb.WriteString("\x1b[0m")
     73 	}
     74 	return sb.String(), nil
     75 }