diff options
| -rw-r--r-- | thumbnailer.go | 132 |
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) |
