aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--thumbnailer.go132
1 files changed, 132 insertions, 0 deletions
diff --git a/thumbnailer.go b/thumbnailer.go
index a593550..78d393d 100644
--- a/thumbnailer.go
+++ b/thumbnailer.go
@@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"image"
+ "image/color"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
@@ -27,8 +28,54 @@ import (
var (
x = flag.Int("x", 15, "set image width in columns")
y = flag.Int("y", 0, "set image height in rows")
+ m = flagPrintMode(flag.CommandLine, "m", modeBlock24, "one of 'block', 'block24', or 'sixel'")
)
+type printMode int
+
+const (
+ modeInvalid printMode = iota
+ modeBlock
+ modeBlock24
+ modeSixel
+)
+
+type printModeValue printMode
+
+func (m *printModeValue) String() string {
+ switch printMode(*m) {
+ case modeBlock:
+ return "block"
+ case modeBlock24:
+ return "block24"
+ case modeSixel:
+ return "sixel"
+ default:
+ return "invalid"
+ }
+}
+
+func (m *printModeValue) Set(s string) error {
+ var mode printMode
+ switch s {
+ case "block":
+ mode = modeBlock
+ case "block24":
+ mode = modeBlock24
+ case "sixel":
+ mode = modeSixel
+ default:
+ return fmt.Errorf("bad mode type %q, should be one of 'block', 'block24', or 'sixel'", s)
+ }
+ *m = printModeValue(mode)
+ return nil
+}
+
+func flagPrintMode(fs *flag.FlagSet, name string, value printMode, usage string) *printMode {
+ fs.Var((*printModeValue)(&value), name, usage)
+ return &value
+}
+
func isDir(filename string) (bool, error) {
stat, err := os.Stat(filename)
if err != nil {
@@ -79,6 +126,87 @@ func load(filename string) (image.Image, error) {
return img, nil
}
+var (
+ xtermBlack = color.RGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff}
+ xtermRed = color.RGBA{R: 0xcd, G: 0x00, B: 0x00, A: 0xff}
+ xtermGreen = color.RGBA{R: 0x00, G: 0xcd, B: 0x00, A: 0xff}
+ xtermYellow = color.RGBA{R: 0xcd, G: 0xcd, B: 0x00, A: 0xff}
+ xtermBlue = color.RGBA{R: 0x00, G: 0x00, B: 0xee, A: 0xff}
+ xtermMagenta = color.RGBA{R: 0xcd, G: 0x00, B: 0xcd, A: 0xff}
+ xtermCyan = color.RGBA{R: 0x00, G: 0xcd, B: 0xcd, A: 0xff}
+ xtermWhite = color.RGBA{R: 0xe5, G: 0xe5, B: 0xe5, A: 0xff}
+ xtermGray = color.RGBA{R: 0x7f, G: 0x7f, B: 0x7f, A: 0xff}
+ xtermBrightRed = color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff}
+ xtermBrightGreen = color.RGBA{R: 0x00, G: 0xff, B: 0x00, A: 0xff}
+ xtermBrightYellow = color.RGBA{R: 0xff, G: 0xff, B: 0x00, A: 0xff}
+ xtermBrightBlue = color.RGBA{R: 0x5c, G: 0x5c, B: 0xff, A: 0xff}
+ xtermBrightMagenta = color.RGBA{R: 0xff, G: 0x00, B: 0xff, A: 0xff}
+ xtermBrightCyan = color.RGBA{R: 0x00, G: 0xff, B: 0xff, A: 0xff}
+ xtermBrightWhite = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
+)
+
+var xtermPalette = color.Palette{xtermBlack, xtermRed, xtermGreen, xtermYellow, xtermBlue, xtermMagenta, xtermCyan, xtermWhite, xtermGray, xtermBrightRed, xtermBrightGreen, xtermBrightYellow, xtermBrightBlue, xtermBrightMagenta, xtermBrightCyan, xtermBrightWhite}
+
+func xtermColorCode(c color.Color) int {
+ switch c {
+ case xtermBlack:
+ return 0
+ case xtermRed:
+ return 1
+ case xtermGreen:
+ return 2
+ case xtermYellow:
+ return 3
+ case xtermBlue:
+ return 4
+ case xtermMagenta:
+ return 5
+ case xtermCyan:
+ return 6
+ case xtermWhite:
+ return 7
+ case xtermGray:
+ return 60
+ case xtermBrightRed:
+ return 61
+ case xtermBrightGreen:
+ return 62
+ case xtermBrightYellow:
+ return 63
+ case xtermBrightBlue:
+ return 64
+ case xtermBrightMagenta:
+ return 65
+ case xtermBrightCyan:
+ return 66
+ case xtermBrightWhite:
+ return 67
+ default:
+ panic("not an xterm color")
+ }
+}
+
+func printImgXterm(img image.Image, cols, lines int) (string, int) {
+ scaled := image.NewRGBA(image.Rect(0, 0, cols, 2*lines))
+ draw.BiLinear.Scale(scaled, scaled.Bounds(), img, img.Bounds(), draw.Over, nil)
+ paletted := image.NewPaletted(scaled.Bounds(), xtermPalette)
+ draw.FloydSteinberg.Draw(paletted, paletted.Bounds(), scaled, image.Point{})
+
+ s := new(strings.Builder)
+ for y := 0; y < 2*lines; y += 2 {
+ if y > 0 {
+ fmt.Fprintln(s)
+ }
+ for x := 0; x < cols; x++ {
+ hi := xtermColorCode(paletted.At(x, y))
+ lo := xtermColorCode(paletted.At(x, y+1))
+ fmt.Fprintf(s, "\033[%dm\033[%dmâ–€", 30+hi, 40+lo)
+ }
+ fmt.Fprint(s, "\033[39m\033[49m")
+ }
+ return s.String(), cols
+}
+
func printImg(img image.Image, cols, lines int) (string, int) {
// Try not to stretch the image.
if lines <= 0 || img.Bounds().Dy()*cols <= img.Bounds().Dx()*lines*5/2 {
@@ -90,6 +218,10 @@ func printImg(img image.Image, cols, lines int) (string, int) {
return "", 0
}
+ if *m == modeBlock {
+ return printImgXterm(img, cols, lines)
+ }
+
dst := image.NewRGBA(image.Rect(0, 0, cols, 2*lines))
draw.BiLinear.Scale(dst, dst.Bounds(), img, img.Bounds(), draw.Over, nil)