From 4cb5012ca2bdac8451cd2d9f27e06fc5e222fddd Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Thu, 28 May 2026 12:50:46 -0700 Subject: Better palette support --- thumbnailer.go | 115 ++++++++++++++++++++++----------------------------------- 1 file changed, 45 insertions(+), 70 deletions(-) diff --git a/thumbnailer.go b/thumbnailer.go index 89f9fed..6c69fc1 100644 --- a/thumbnailer.go +++ b/thumbnailer.go @@ -126,66 +126,51 @@ 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} -) +type xtermColor int8 -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 { +func (c xtermColor) RGBA() (r, g, b, a uint32) { + var col color.RGBA 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 + case 0: + col = color.RGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff} + case 1: + col = color.RGBA{R: 0xcd, G: 0x00, B: 0x00, A: 0xff} + case 2: + col = color.RGBA{R: 0x00, G: 0xcd, B: 0x00, A: 0xff} + case 3: + col = color.RGBA{R: 0xcd, G: 0xcd, B: 0x00, A: 0xff} + case 4: + col = color.RGBA{R: 0x00, G: 0x00, B: 0xee, A: 0xff} + case 5: + col = color.RGBA{R: 0xcd, G: 0x00, B: 0xcd, A: 0xff} + case 6: + col = color.RGBA{R: 0x00, G: 0xcd, B: 0xcd, A: 0xff} + case 7: + col = color.RGBA{R: 0xe5, G: 0xe5, B: 0xe5, A: 0xff} + case 60: + col = color.RGBA{R: 0x7f, G: 0x7f, B: 0x7f, A: 0xff} + case 61: + col = color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff} + case 62: + col = color.RGBA{R: 0x00, G: 0xff, B: 0x00, A: 0xff} + case 63: + col = color.RGBA{R: 0xff, G: 0xff, B: 0x00, A: 0xff} + case 64: + col = color.RGBA{R: 0x5c, G: 0x5c, B: 0xff, A: 0xff} + case 65: + col = color.RGBA{R: 0xff, G: 0x00, B: 0xff, A: 0xff} + case 66: + col = color.RGBA{R: 0x00, G: 0xff, B: 0xff, A: 0xff} + case 67: + col = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} default: panic("not an xterm color") } + return col.RGBA() } +var xtermPalette = color.Palette{xtermColor(0), xtermColor(1), xtermColor(2), xtermColor(3), xtermColor(4), xtermColor(5), xtermColor(6), xtermColor(7), xtermColor(60), xtermColor(61), xtermColor(62), xtermColor(63), xtermColor(64), xtermColor(65), xtermColor(66), xtermColor(67)} + func printImgXterm(w io.Writer, img image.Image) { for y := 0; y < img.Bounds().Dy(); y += 2 { if y > 0 { @@ -201,9 +186,9 @@ func printImgXterm(w io.Writer, img image.Image) { } else { lo := 9 if y+1 < img.Bounds().Dy() && img.At(x, y+1) != color.Transparent { - lo = xtermColorCode(img.At(x, y+1)) + lo = int(img.At(x, y+1).(xtermColor)) } - fmt.Fprintf(w, "\033[%dm\033[%dm▀", 30+xtermColorCode(hi), 40+lo) + fmt.Fprintf(w, "\033[%dm\033[%dm▀", 30+hi.(xtermColor), 40+lo) } } fmt.Fprint(w, "\033[39m\033[49m") @@ -237,23 +222,13 @@ func printImgBlock24(w io.Writer, img image.Image) { } } -func makePlan9ColorMap() map[color.Color]int { - m := make(map[color.Color]int) - for i, c := range palette.Plan9 { - m[c] = i - } - return m -} - -var plan9ColorMap = makePlan9ColorMap() - func scale100(c uint32) int { return int(c) * 100 / 0xffff } -func printImgSixel(w io.Writer, img image.Image) { +func printImgSixel(w io.Writer, img image.PalettedImage) { fmt.Fprint(w, "\033P7;1q") - for i, c := range palette.Plan9 { + for i, c := range img.ColorModel().(color.Palette)[1:] { r, g, b, _ := c.RGBA() fmt.Fprintf(w, "#%d;2;%d;%d;%d", i, scale100(r), scale100(g), scale100(b)) } @@ -270,7 +245,7 @@ func printImgSixel(w io.Writer, img image.Image) { fmt.Fprint(w, "?") } else { char := '?' + 1<<(y%6) - fmt.Fprintf(w, "#%d%c", plan9ColorMap[img.At(x, y)], char) + fmt.Fprintf(w, "#%d%c", img.ColorIndexAt(x, y)-1, char) } } } @@ -284,7 +259,7 @@ func printImg(w io.Writer, img image.Image) { case modeBlock24: printImgBlock24(w, img) case modeSixel: - printImgSixel(w, img) + printImgSixel(w, img.(image.PalettedImage)) default: panic("invalid mode") } @@ -342,7 +317,7 @@ func compositeImages(w io.Writer, images []img, width, imagesPerRow, cellX, cell case modeSixel: imageWidth = width * cellX padding = cellX - colorPalette = palette.Plan9 + colorPalette = palette.WebSafe } for row := range slices.Chunk(images, imagesPerRow) { images := make([]image.Image, len(row)) -- cgit v1.3.1