├── README.md ├── main.go └── output.go /README.md: -------------------------------------------------------------------------------- 1 | Terminal Colorscheme Generator 2 | ============================== 3 | 4 | # Please note: 5 | 6 | If you have no strong reason to use this version specifically, please instead use [schemer2](https://github.com/thefryscorer/schemer2). It has many more features, and a smarter interface. Any new features added will be added to schemer2. 7 | 8 | The reason schemer2 is a separate project is to support the AUR package (Now removed), and not break the existing workflow of anybody who has incorporated schemer into scripts or other programs. Additionally, schemer2 does not feature the SDL rendered preview. 9 | 10 | ## Screenshot 11 | ![Screenshot](http://i.imgur.com/TSLluID.png) 12 | 13 | ## Installation 14 | 15 | ### Short version 16 | 17 | > go get github.com/thefryscorer/schemer 18 | 19 | ### Long Version 20 | 21 | #### Installing and configuring Go 22 | To build this program, you will need to have Go installed and properly configured. After installing the Go package, you will need to configure a GOPATH. This is a directory in which Go will keep its programs and source files. I recommend making the GOPATH directory in your home folder. If your GOPATH is in your root directory a kitten will die. 23 | 24 | > mkdir ~/Go 25 | 26 | You will also need to set the GOPATH variable so that Go knows where to put things. You can do this by running: 27 | 28 | > export GOPATH=$HOME/Go 29 | 30 | NOTE: You don't need to (and shouldn't) set the $GOROOT variable. This is handled for you and you shouldn't mess with it. 31 | 32 | #### Installing SDL1.2 33 | This program also makes use of SDL1.2 for the color preview window (which you can use by adding the "-d" flag in schemer). As such, SDL1.2 will need to be installed on your system for you to build and run schemer. 34 | 35 | **To install SDL1.2 in ArchLinux:** 36 | 37 | > sudo pacman -S sdl sdl_image sdl_ttf sdl_mixer 38 | 39 | **To install SDL1.2 in Mac OS:** 40 | 41 | > brew install sdl sdl_image sdl_ttf sdl_mixer 42 | 43 | If you haven't installed HomeBrew, you can find it at http://brew.sh 44 | 45 | 46 | **To install SDL1.2 on another system:** 47 | 48 | Learn how to use Google and your package manager. Both are very useful. 49 | 50 | #### Installing schemer 51 | You should now be able to install schemer using the command: 52 | 53 | > go get github.com/thefryscorer/schemer 54 | 55 | And it will be built in your GOPATH directory, in a subdirectory named 'bin'. To run it, you can either add $HOME/Go/bin to your system path and run it as you would any other command. Or cd into the bin directory and run it with: 56 | 57 | > ./schemer 58 | 59 | ## Usage 60 | 61 | > schemer -term="xfce" Image.png 62 | 63 | Then copy the generated config lines into your terminal config file. 64 | 65 | ## Features 66 | 67 | - Outputs configuration in several different formats for different terminals. 68 | - Configurable color difference threshold 69 | - Configurable minimum and maximum brightness value 70 | - Can preview colorscheme in SDL window 71 | 72 | ## Supported output formats 73 | 74 | - Colours in just plain text (default) 75 | - Konsole 76 | - xterm/rxvt/aterm 77 | - urxvt 78 | - iTerm2 79 | - XFCE Terminal 80 | - Roxterm 81 | - LilyTerm 82 | - Terminator 83 | - Chrome Shell 84 | - OS X Terminal 85 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "image" 7 | "image/color" 8 | _ "image/jpeg" 9 | _ "image/png" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | "strings" 14 | 15 | "github.com/jqln-0/colorshow" 16 | ) 17 | 18 | func loadImage(filepath string) image.Image { 19 | infile, err := os.Open(filepath) 20 | if err != nil { 21 | log.Panic(err) 22 | } 23 | defer infile.Close() 24 | 25 | src, _, err := image.Decode(infile) 26 | if err != nil { 27 | log.Panic(err) 28 | } 29 | return src 30 | } 31 | 32 | func abs(n int) int { 33 | if n >= 0 { 34 | return n 35 | } 36 | return -n 37 | } 38 | 39 | func colorDifference(col1 color.Color, col2 color.Color, threshold int) bool { 40 | c1 := col1.(color.NRGBA) 41 | c2 := col2.(color.NRGBA) 42 | 43 | rDiff := abs(int(c1.R) - int(c2.R)) 44 | gDiff := abs(int(c1.G) - int(c2.G)) 45 | bDiff := abs(int(c1.B) - int(c2.B)) 46 | 47 | total := rDiff + gDiff + bDiff 48 | return total >= threshold 49 | } 50 | 51 | func getDistinctColors(colors []color.Color, threshold int, minBrightness, maxBrightness int) []color.Color { 52 | distinctColors := make([]color.Color, 0) 53 | for _, c := range colors { 54 | same := false 55 | if !colorDifference(c, color.NRGBAModel.Convert(color.Black), minBrightness*3) { 56 | continue 57 | } 58 | if !colorDifference(c, color.NRGBAModel.Convert(color.White), (255-maxBrightness)*3) { 59 | continue 60 | } 61 | for _, k := range distinctColors { 62 | if !colorDifference(c, k, threshold) { 63 | same = true 64 | break 65 | } 66 | } 67 | if !same { 68 | distinctColors = append(distinctColors, c) 69 | } 70 | } 71 | return distinctColors 72 | } 73 | func usage() { 74 | fmt.Fprintln(os.Stderr, "Usage: colorscheme [flags] -term=\"terminal format\" imagepath") 75 | flag.PrintDefaults() 76 | os.Exit(2) 77 | } 78 | 79 | func main() { 80 | terminalSupport := "Terminal format to output colors as. Currently supported: \n" 81 | for _, t := range terminals { 82 | terminalSupport += strings.Join([]string{" ", t.friendlyName, ":", t.flagName, "\n"}, " ") 83 | } 84 | var ( 85 | threshold = flag.Int("t", 50, "Threshold for minimum color difference") 86 | display = flag.Bool("d", false, "Display colors in SDL window") 87 | terminal = flag.String("term", "default", terminalSupport) 88 | minBrightness = flag.Int("minBright", 50, "Minimum brightness for colors") 89 | maxBrightness = flag.Int("maxBright", 200, "Maximum brightness for colors") 90 | debug = flag.Bool("debug", false, "Show debugging messages") 91 | ) 92 | flag.Usage = usage 93 | flag.Parse() 94 | if len(flag.Args()) < 1 { 95 | usage() 96 | os.Exit(2) 97 | } 98 | if *minBrightness > 255 || *maxBrightness > 255 { 99 | fmt.Print("Minimum and maximum brightness must be an integer between 0 and 255.\n") 100 | os.Exit(2) 101 | } 102 | if *threshold > 255 { 103 | fmt.Print("Threshold should be an integer between 0 and 255.\n") 104 | os.Exit(2) 105 | } 106 | if !*debug { 107 | log.SetOutput(ioutil.Discard) 108 | } 109 | 110 | // Load the image and create array of colors 111 | fuzzyness := 5 112 | img := loadImage(flag.Args()[0]) 113 | w, h := img.Bounds().Max.X, img.Bounds().Max.Y 114 | colors := make([]color.Color, 0, w*h) 115 | for x := 0; x < w; x += fuzzyness { 116 | for y := 0; y < h; y += fuzzyness { 117 | col := color.NRGBAModel.Convert(img.At(x, y)) 118 | colors = append(colors, col) 119 | } 120 | } 121 | // Get the distinct colors from the array by comparing differences with a threshold 122 | distinctColors := getDistinctColors(colors, *threshold, *minBrightness, *maxBrightness) 123 | 124 | // Ensure there are 16 colors 125 | count := 0 126 | for len(distinctColors) < 16 { 127 | count++ 128 | distinctColors = append(distinctColors, getDistinctColors(colors, *threshold-count, *minBrightness, *maxBrightness)...) 129 | if count == *threshold { 130 | fmt.Print("Could not get colors from image with settings specified. Aborting.\n") 131 | os.Exit(1) 132 | } 133 | } 134 | 135 | if len(distinctColors) > 16 { 136 | distinctColors = distinctColors[:16] 137 | } 138 | 139 | if *display { 140 | colorshow.DisplaySwatches(distinctColors) 141 | } 142 | 143 | // Output the configuration specified 144 | terminalMatch := false 145 | for _, t := range terminals { 146 | if *terminal == t.flagName { 147 | fmt.Print(t.output(distinctColors)) 148 | terminalMatch = true 149 | break 150 | } 151 | } 152 | if !terminalMatch { 153 | fmt.Printf("Did not recognise format %v. \n", *terminal) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /output.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/hex" 6 | "fmt" 7 | "image/color" 8 | "strconv" 9 | ) 10 | 11 | type outputFunction (func([]color.Color) string) 12 | 13 | type Terminal struct { 14 | friendlyName string 15 | flagName string 16 | output outputFunction 17 | } 18 | 19 | // Terminals are defined here 20 | var terminals = []Terminal{ 21 | { 22 | friendlyName: "Default output (colors only)", 23 | flagName: "default", 24 | output: printColors, 25 | }, 26 | { 27 | friendlyName: "XFCE4Terminal", 28 | flagName: "xfce", 29 | output: printXfce, 30 | }, 31 | { 32 | friendlyName: "LilyTerm", 33 | flagName: "lilyterm", 34 | output: printLilyTerm, 35 | }, 36 | { 37 | friendlyName: "Termite", 38 | flagName: "termite", 39 | output: printTermite, 40 | }, 41 | { 42 | friendlyName: "Terminator", 43 | flagName: "terminator", 44 | output: printTerminator, 45 | }, 46 | { 47 | friendlyName: "ROXTerm", 48 | flagName: "roxterm", 49 | output: printRoxTerm, 50 | }, 51 | { 52 | friendlyName: "rxvt/xterm/aterm", 53 | flagName: "xterm", 54 | output: printXterm, 55 | }, 56 | { 57 | friendlyName: "Konsole", 58 | flagName: "konsole", 59 | output: printKonsole, 60 | }, 61 | { 62 | friendlyName: "iTerm2", 63 | flagName: "iterm2", 64 | output: printITerm2, 65 | }, 66 | { 67 | friendlyName: "urxvt", 68 | flagName: "urxvt", 69 | output: printURxvt, 70 | }, 71 | { 72 | friendlyName: "Chrome Shell", 73 | flagName: "chrome", 74 | output: printChrome, 75 | }, 76 | { 77 | friendlyName: "OS X Terminal", 78 | flagName: "osxterminal", 79 | output: printOSXTerminal, 80 | }, 81 | } 82 | 83 | func printXfce(colors []color.Color) string { 84 | output := "" 85 | output += "ColorPalette=" 86 | for _, c := range colors { 87 | bytes := []byte{byte(c.(color.NRGBA).R), byte(c.(color.NRGBA).R), byte(c.(color.NRGBA).G), byte(c.(color.NRGBA).G), byte(c.(color.NRGBA).B), byte(c.(color.NRGBA).B)} 88 | output += "#" 89 | output += hex.EncodeToString(bytes) 90 | output += ";" 91 | } 92 | output += "\n" 93 | 94 | return output 95 | } 96 | 97 | func printLilyTerm(colors []color.Color) string { 98 | output := "" 99 | for i, c := range colors { 100 | cc := c.(color.NRGBA) 101 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 102 | output += "Color" 103 | output += strconv.Itoa(i) 104 | output += " = " 105 | output += "#" 106 | output += hex.EncodeToString(bytes) 107 | output += "\n" 108 | } 109 | return output 110 | } 111 | 112 | func printTermite(colors []color.Color) string { 113 | output := "" 114 | for i, c := range colors { 115 | cc := c.(color.NRGBA) 116 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 117 | output += "color" 118 | output += strconv.Itoa(i) 119 | output += " = " 120 | output += "#" 121 | output += hex.EncodeToString(bytes) 122 | output += "\n" 123 | } 124 | return output 125 | } 126 | 127 | func printTerminator(colors []color.Color) string { 128 | output := "palette = \"" 129 | for i, c := range colors { 130 | cc := c.(color.NRGBA) 131 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 132 | if i < len(colors)-1 { 133 | output += "#" 134 | output += hex.EncodeToString(bytes) 135 | output += ":" 136 | } else if i == len(colors)-1 { 137 | output += "#" 138 | output += hex.EncodeToString(bytes) 139 | output += "\"\n" 140 | } 141 | } 142 | return output 143 | } 144 | 145 | func printXterm(colors []color.Color) string { 146 | output := "" 147 | output += "! Terminal colors" 148 | output += "\n" 149 | for i, c := range colors { 150 | cc := c.(color.NRGBA) 151 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 152 | output += "*color" 153 | output += strconv.Itoa(i) 154 | output += ": #" 155 | output += hex.EncodeToString(bytes) 156 | output += "\n" 157 | } 158 | 159 | return output 160 | } 161 | 162 | func printKonsole(colors []color.Color) string { 163 | output := "" 164 | for i, c := range colors { 165 | cc := c.(color.NRGBA) 166 | output += "[Color" 167 | if i > 7 { 168 | output += strconv.Itoa(i - 8) 169 | output += "Intense" 170 | } else { 171 | output += strconv.Itoa(i) 172 | } 173 | output += "]\n" 174 | output += "Color=" 175 | output += strconv.Itoa(int(cc.R)) + "," 176 | output += strconv.Itoa(int(cc.G)) + "," 177 | output += strconv.Itoa(int(cc.B)) + "\n" 178 | output += "Transparency=false\n\n" 179 | } 180 | 181 | return output 182 | } 183 | 184 | func printRoxTerm(colors []color.Color) string { 185 | output := "[roxterm colour scheme]\n" 186 | output += "pallete_size=16\n" 187 | 188 | for i, c := range colors { 189 | cc := c.(color.NRGBA) 190 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 191 | output += "color" 192 | output += strconv.Itoa(i) 193 | output += " = " 194 | output += "#" 195 | output += hex.EncodeToString(bytes) 196 | output += "\n" 197 | } 198 | 199 | return output 200 | } 201 | 202 | func printITerm2(colors []color.Color) string { 203 | output := "\n" 204 | output += "\n" 205 | output += "\n" 206 | output += "\n" 207 | for i, c := range colors { 208 | cc := c.(color.NRGBA) 209 | output += "\tAnsi " 210 | output += strconv.Itoa(i) 211 | output += " Color\n" 212 | output += "\t\n" 213 | output += "\t\tBlue Component\n" 214 | output += "\t\t" 215 | output += strconv.FormatFloat(float64(cc.B)/255, 'f', 17, 64) 216 | output += "\n" 217 | output += "\t\tGreen Component\n" 218 | output += "\t\t" 219 | output += strconv.FormatFloat(float64(cc.G)/255, 'f', 17, 64) 220 | output += "\n" 221 | output += "\t\tRed Component\n" 222 | output += "\t\t" 223 | output += strconv.FormatFloat(float64(cc.R)/255, 'f', 17, 64) 224 | output += "\n" 225 | output += "\t\n" 226 | } 227 | output += "\n" 228 | output += "\n" 229 | return output 230 | } 231 | 232 | func printURxvt(colors []color.Color) string { 233 | output := "" 234 | for i, c := range colors { 235 | cc := c.(color.NRGBA) 236 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 237 | output += "URxvt*color" 238 | output += strconv.Itoa(i) 239 | output += ": " 240 | output += "#" 241 | output += hex.EncodeToString(bytes) 242 | output += "\n" 243 | } 244 | return output 245 | } 246 | 247 | func printColors(colors []color.Color) string { 248 | output := "" 249 | for _, c := range colors { 250 | cc := c.(color.NRGBA) 251 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 252 | output += "#" 253 | output += hex.EncodeToString(bytes) 254 | output += "\n" 255 | } 256 | return output 257 | } 258 | func printChrome(colors []color.Color) string { 259 | output := "{" 260 | for i, c := range colors { 261 | cc := c.(color.NRGBA) 262 | bytes := []byte{byte(cc.R), byte(cc.G), byte(cc.B)} 263 | output += " \"" 264 | output += strconv.Itoa(i) 265 | output += "\"" 266 | output += ": " 267 | output += " \"" 268 | output += "#" 269 | output += hex.EncodeToString(bytes) 270 | output += "\" " 271 | if i != len(colors)-1 { 272 | output += ", " 273 | } 274 | } 275 | output += "}\n" 276 | return output 277 | } 278 | 279 | func printOSXTerminal(colors []color.Color) string { 280 | // The plist that is used by OS X's Terminal to store colours. Normally, 281 | // Terminal stores the colours in a base64 encoded binary plist but it'll 282 | // happily read base64 encoded xml plists which makes things easier. 283 | const OSXSerializedNSColorTemplate = `$archiverNSKeyedArchiver$objects$null$classCF$UID2NSColorSpace1NSRGB%s$classesNSColorNSObject$classnameNSColor$toprootCF$UID1$version100000` 284 | OSXColorNames := map[int]string{ 285 | 0: "Black", 286 | 1: "Red", 287 | 2: "Green", 288 | 3: "Yellow", 289 | 4: "Blue", 290 | 5: "Magenta", 291 | 6: "Cyan", 292 | 7: "White", 293 | } 294 | 295 | output := "\n" 296 | output += "\n" 297 | output += "\n" 298 | output += "\n" 299 | for i, c := range colors { 300 | cc := c.(color.NRGBA) 301 | output += "\tANSI" 302 | if i > 7 { 303 | output += "Bright" + OSXColorNames[i-8] 304 | } else { 305 | output += OSXColorNames[i] 306 | } 307 | output += "Color\n" 308 | output += "\t\n" 309 | rgbColorString := fmt.Sprintf("%.10f %.10f %.10f", float64(cc.R)/255, float64(cc.G)/255, float64(cc.B)/255) 310 | serializedColor := fmt.Sprintf(OSXSerializedNSColorTemplate, base64.StdEncoding.EncodeToString([]byte(rgbColorString))) 311 | output += "\t" + base64.StdEncoding.EncodeToString([]byte(serializedColor)) 312 | output += "\n\t\n" 313 | } 314 | 315 | output += "\ttype\n" // Need this key or Terminal says the file is corrupt 316 | output += "\tWindow Settings\n" 317 | output += "\n" 318 | output += "\n" 319 | return output 320 | } 321 | --------------------------------------------------------------------------------