Commit | Line | Data |
---|---|---|
1c50e2d1 O |
1 | #! /usr/bin/env python |
2 | ||
3 | """ Convert values between RGB hex codes and xterm-256 color codes. | |
4 | ||
5 | Nice long listing of all 256 colors and their codes. Useful for | |
6 | developing console color themes, or even script output schemes. | |
7 | ||
8 | Resources: | |
9 | * http://en.wikipedia.org/wiki/8-bit_color | |
10 | * http://en.wikipedia.org/wiki/ANSI_escape_code | |
11 | * /usr/share/X11/rgb.txt | |
12 | ||
13 | I'm not sure where this script was inspired from. I think I must have | |
14 | written it from scratch, though it's been several years now. | |
15 | """ | |
16 | ||
17 | __author__ = 'Micah Elliott http://MicahElliott.com' | |
18 | __version__ = '0.1' | |
19 | __copyright__ = 'Copyright (C) 2011 Micah Elliott. All rights reserved.' | |
20 | __license__ = 'WTFPL http://sam.zoy.org/wtfpl/' | |
21 | ||
22 | #--------------------------------------------------------------------- | |
23 | ||
24 | import sys, re | |
25 | ||
26 | CLUT = [ # color look-up table | |
27 | # 8-bit, RGB hex | |
28 | ||
29 | # Primary 3-bit (8 colors). Unique representation! | |
30 | ('00', '000000'), | |
31 | ('01', '800000'), | |
32 | ('02', '008000'), | |
33 | ('03', '808000'), | |
34 | ('04', '000080'), | |
35 | ('05', '800080'), | |
36 | ('06', '008080'), | |
37 | ('07', 'c0c0c0'), | |
38 | ||
39 | # Equivalent "bright" versions of original 8 colors. | |
40 | ('08', '808080'), | |
41 | ('09', 'ff0000'), | |
42 | ('10', '00ff00'), | |
43 | ('11', 'ffff00'), | |
44 | ('12', '0000ff'), | |
45 | ('13', 'ff00ff'), | |
46 | ('14', '00ffff'), | |
47 | ('15', 'ffffff'), | |
48 | ||
49 | # Strictly ascending. | |
50 | ('16', '000000'), | |
51 | ('17', '00005f'), | |
52 | ('18', '000087'), | |
53 | ('19', '0000af'), | |
54 | ('20', '0000d7'), | |
55 | ('21', '0000ff'), | |
56 | ('22', '005f00'), | |
57 | ('23', '005f5f'), | |
58 | ('24', '005f87'), | |
59 | ('25', '005faf'), | |
60 | ('26', '005fd7'), | |
61 | ('27', '005fff'), | |
62 | ('28', '008700'), | |
63 | ('29', '00875f'), | |
64 | ('30', '008787'), | |
65 | ('31', '0087af'), | |
66 | ('32', '0087d7'), | |
67 | ('33', '0087ff'), | |
68 | ('34', '00af00'), | |
69 | ('35', '00af5f'), | |
70 | ('36', '00af87'), | |
71 | ('37', '00afaf'), | |
72 | ('38', '00afd7'), | |
73 | ('39', '00afff'), | |
74 | ('40', '00d700'), | |
75 | ('41', '00d75f'), | |
76 | ('42', '00d787'), | |
77 | ('43', '00d7af'), | |
78 | ('44', '00d7d7'), | |
79 | ('45', '00d7ff'), | |
80 | ('46', '00ff00'), | |
81 | ('47', '00ff5f'), | |
82 | ('48', '00ff87'), | |
83 | ('49', '00ffaf'), | |
84 | ('50', '00ffd7'), | |
85 | ('51', '00ffff'), | |
86 | ('52', '5f0000'), | |
87 | ('53', '5f005f'), | |
88 | ('54', '5f0087'), | |
89 | ('55', '5f00af'), | |
90 | ('56', '5f00d7'), | |
91 | ('57', '5f00ff'), | |
92 | ('58', '5f5f00'), | |
93 | ('59', '5f5f5f'), | |
94 | ('60', '5f5f87'), | |
95 | ('61', '5f5faf'), | |
96 | ('62', '5f5fd7'), | |
97 | ('63', '5f5fff'), | |
98 | ('64', '5f8700'), | |
99 | ('65', '5f875f'), | |
100 | ('66', '5f8787'), | |
101 | ('67', '5f87af'), | |
102 | ('68', '5f87d7'), | |
103 | ('69', '5f87ff'), | |
104 | ('70', '5faf00'), | |
105 | ('71', '5faf5f'), | |
106 | ('72', '5faf87'), | |
107 | ('73', '5fafaf'), | |
108 | ('74', '5fafd7'), | |
109 | ('75', '5fafff'), | |
110 | ('76', '5fd700'), | |
111 | ('77', '5fd75f'), | |
112 | ('78', '5fd787'), | |
113 | ('79', '5fd7af'), | |
114 | ('80', '5fd7d7'), | |
115 | ('81', '5fd7ff'), | |
116 | ('82', '5fff00'), | |
117 | ('83', '5fff5f'), | |
118 | ('84', '5fff87'), | |
119 | ('85', '5fffaf'), | |
120 | ('86', '5fffd7'), | |
121 | ('87', '5fffff'), | |
122 | ('88', '870000'), | |
123 | ('89', '87005f'), | |
124 | ('90', '870087'), | |
125 | ('91', '8700af'), | |
126 | ('92', '8700d7'), | |
127 | ('93', '8700ff'), | |
128 | ('94', '875f00'), | |
129 | ('95', '875f5f'), | |
130 | ('96', '875f87'), | |
131 | ('97', '875faf'), | |
132 | ('98', '875fd7'), | |
133 | ('99', '875fff'), | |
134 | ('100', '878700'), | |
135 | ('101', '87875f'), | |
136 | ('102', '878787'), | |
137 | ('103', '8787af'), | |
138 | ('104', '8787d7'), | |
139 | ('105', '8787ff'), | |
140 | ('106', '87af00'), | |
141 | ('107', '87af5f'), | |
142 | ('108', '87af87'), | |
143 | ('109', '87afaf'), | |
144 | ('110', '87afd7'), | |
145 | ('111', '87afff'), | |
146 | ('112', '87d700'), | |
147 | ('113', '87d75f'), | |
148 | ('114', '87d787'), | |
149 | ('115', '87d7af'), | |
150 | ('116', '87d7d7'), | |
151 | ('117', '87d7ff'), | |
152 | ('118', '87ff00'), | |
153 | ('119', '87ff5f'), | |
154 | ('120', '87ff87'), | |
155 | ('121', '87ffaf'), | |
156 | ('122', '87ffd7'), | |
157 | ('123', '87ffff'), | |
158 | ('124', 'af0000'), | |
159 | ('125', 'af005f'), | |
160 | ('126', 'af0087'), | |
161 | ('127', 'af00af'), | |
162 | ('128', 'af00d7'), | |
163 | ('129', 'af00ff'), | |
164 | ('130', 'af5f00'), | |
165 | ('131', 'af5f5f'), | |
166 | ('132', 'af5f87'), | |
167 | ('133', 'af5faf'), | |
168 | ('134', 'af5fd7'), | |
169 | ('135', 'af5fff'), | |
170 | ('136', 'af8700'), | |
171 | ('137', 'af875f'), | |
172 | ('138', 'af8787'), | |
173 | ('139', 'af87af'), | |
174 | ('140', 'af87d7'), | |
175 | ('141', 'af87ff'), | |
176 | ('142', 'afaf00'), | |
177 | ('143', 'afaf5f'), | |
178 | ('144', 'afaf87'), | |
179 | ('145', 'afafaf'), | |
180 | ('146', 'afafd7'), | |
181 | ('147', 'afafff'), | |
182 | ('148', 'afd700'), | |
183 | ('149', 'afd75f'), | |
184 | ('150', 'afd787'), | |
185 | ('151', 'afd7af'), | |
186 | ('152', 'afd7d7'), | |
187 | ('153', 'afd7ff'), | |
188 | ('154', 'afff00'), | |
189 | ('155', 'afff5f'), | |
190 | ('156', 'afff87'), | |
191 | ('157', 'afffaf'), | |
192 | ('158', 'afffd7'), | |
193 | ('159', 'afffff'), | |
194 | ('160', 'd70000'), | |
195 | ('161', 'd7005f'), | |
196 | ('162', 'd70087'), | |
197 | ('163', 'd700af'), | |
198 | ('164', 'd700d7'), | |
199 | ('165', 'd700ff'), | |
200 | ('166', 'd75f00'), | |
201 | ('167', 'd75f5f'), | |
202 | ('168', 'd75f87'), | |
203 | ('169', 'd75faf'), | |
204 | ('170', 'd75fd7'), | |
205 | ('171', 'd75fff'), | |
206 | ('172', 'd78700'), | |
207 | ('173', 'd7875f'), | |
208 | ('174', 'd78787'), | |
209 | ('175', 'd787af'), | |
210 | ('176', 'd787d7'), | |
211 | ('177', 'd787ff'), | |
212 | ('178', 'd7af00'), | |
213 | ('179', 'd7af5f'), | |
214 | ('180', 'd7af87'), | |
215 | ('181', 'd7afaf'), | |
216 | ('182', 'd7afd7'), | |
217 | ('183', 'd7afff'), | |
218 | ('184', 'd7d700'), | |
219 | ('185', 'd7d75f'), | |
220 | ('186', 'd7d787'), | |
221 | ('187', 'd7d7af'), | |
222 | ('188', 'd7d7d7'), | |
223 | ('189', 'd7d7ff'), | |
224 | ('190', 'd7ff00'), | |
225 | ('191', 'd7ff5f'), | |
226 | ('192', 'd7ff87'), | |
227 | ('193', 'd7ffaf'), | |
228 | ('194', 'd7ffd7'), | |
229 | ('195', 'd7ffff'), | |
230 | ('196', 'ff0000'), | |
231 | ('197', 'ff005f'), | |
232 | ('198', 'ff0087'), | |
233 | ('199', 'ff00af'), | |
234 | ('200', 'ff00d7'), | |
235 | ('201', 'ff00ff'), | |
236 | ('202', 'ff5f00'), | |
237 | ('203', 'ff5f5f'), | |
238 | ('204', 'ff5f87'), | |
239 | ('205', 'ff5faf'), | |
240 | ('206', 'ff5fd7'), | |
241 | ('207', 'ff5fff'), | |
242 | ('208', 'ff8700'), | |
243 | ('209', 'ff875f'), | |
244 | ('210', 'ff8787'), | |
245 | ('211', 'ff87af'), | |
246 | ('212', 'ff87d7'), | |
247 | ('213', 'ff87ff'), | |
248 | ('214', 'ffaf00'), | |
249 | ('215', 'ffaf5f'), | |
250 | ('216', 'ffaf87'), | |
251 | ('217', 'ffafaf'), | |
252 | ('218', 'ffafd7'), | |
253 | ('219', 'ffafff'), | |
254 | ('220', 'ffd700'), | |
255 | ('221', 'ffd75f'), | |
256 | ('222', 'ffd787'), | |
257 | ('223', 'ffd7af'), | |
258 | ('224', 'ffd7d7'), | |
259 | ('225', 'ffd7ff'), | |
260 | ('226', 'ffff00'), | |
261 | ('227', 'ffff5f'), | |
262 | ('228', 'ffff87'), | |
263 | ('229', 'ffffaf'), | |
264 | ('230', 'ffffd7'), | |
265 | ('231', 'ffffff'), | |
266 | ||
267 | # Gray-scale range. | |
268 | ('232', '080808'), | |
269 | ('233', '121212'), | |
270 | ('234', '1c1c1c'), | |
271 | ('235', '262626'), | |
272 | ('236', '303030'), | |
273 | ('237', '3a3a3a'), | |
274 | ('238', '444444'), | |
275 | ('239', '4e4e4e'), | |
276 | ('240', '585858'), | |
277 | ('241', '626262'), | |
278 | ('242', '6c6c6c'), | |
279 | ('243', '767676'), | |
280 | ('244', '808080'), | |
281 | ('245', '8a8a8a'), | |
282 | ('246', '949494'), | |
283 | ('247', '9e9e9e'), | |
284 | ('248', 'a8a8a8'), | |
285 | ('249', 'b2b2b2'), | |
286 | ('250', 'bcbcbc'), | |
287 | ('251', 'c6c6c6'), | |
288 | ('252', 'd0d0d0'), | |
289 | ('253', 'dadada'), | |
290 | ('254', 'e4e4e4'), | |
291 | ('255', 'eeeeee'), | |
292 | ] | |
293 | ||
294 | def _str2hex(hexstr): | |
295 | return int(hexstr, 16) | |
296 | ||
297 | def _strip_hash(rgb): | |
298 | # Strip leading `#` if exists. | |
299 | if rgb.startswith('#'): | |
300 | rgb = rgb.lstrip('#') | |
301 | return rgb | |
302 | ||
303 | def _create_dicts(): | |
304 | short2rgb_dict = dict(CLUT) | |
305 | rgb2short_dict = {} | |
306 | for k, v in short2rgb_dict.items(): | |
307 | rgb2short_dict[v] = k | |
308 | return rgb2short_dict, short2rgb_dict | |
309 | ||
310 | def short2rgb(short): | |
311 | return SHORT2RGB_DICT[short] | |
312 | ||
313 | def print_all(): | |
314 | """ Print all 256 xterm color codes. | |
315 | """ | |
316 | for short, rgb in CLUT: | |
317 | sys.stdout.write('\033[48;5;%sm%s:%s' % (short, short, rgb)) | |
318 | sys.stdout.write("\033[0m ") | |
319 | sys.stdout.write('\033[38;5;%sm%s:%s' % (short, short, rgb)) | |
320 | sys.stdout.write("\033[0m\n") | |
321 | print "Printed all codes." | |
322 | print "You can translate a hex or 0-255 code by providing an argument." | |
323 | ||
324 | def rgb2short(rgb): | |
325 | """ Find the closest xterm-256 approximation to the given RGB value. | |
326 | @param rgb: Hex code representing an RGB value, eg, 'abcdef' | |
327 | @returns: String between 0 and 255, compatible with xterm. | |
328 | >>> rgb2short('123456') | |
329 | ('23', '005f5f') | |
330 | >>> rgb2short('ffffff') | |
331 | ('231', 'ffffff') | |
332 | >>> rgb2short('0DADD6') # vimeo logo | |
333 | ('38', '00afd7') | |
334 | """ | |
335 | rgb = _strip_hash(rgb) | |
336 | incs = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff) | |
337 | # Break 6-char RGB code into 3 integer vals. | |
338 | parts = [ int(h, 16) for h in re.split(r'(..)(..)(..)', rgb)[1:4] ] | |
339 | res = [] | |
340 | for part in parts: | |
341 | i = 0 | |
342 | while i < len(incs)-1: | |
343 | s, b = incs[i], incs[i+1] # smaller, bigger | |
344 | if s <= part <= b: | |
345 | s1 = abs(s - part) | |
346 | b1 = abs(b - part) | |
347 | if s1 < b1: closest = s | |
348 | else: closest = b | |
349 | res.append(closest) | |
350 | break | |
351 | i += 1 | |
352 | #print '***', res | |
353 | res = ''.join([ ('%02.x' % i) for i in res ]) | |
354 | equiv = RGB2SHORT_DICT[ res ] | |
355 | #print '***', res, equiv | |
356 | return equiv, res | |
357 | ||
358 | RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts() | |
359 | ||
360 | #--------------------------------------------------------------------- | |
361 | ||
362 | if __name__ == '__main__': | |
363 | import doctest | |
364 | doctest.testmod() | |
365 | if len(sys.argv) == 1: | |
366 | print_all() | |
367 | raise SystemExit | |
368 | arg = sys.argv[1] | |
369 | if len(arg) < 4 and int(arg) < 256: | |
370 | rgb = short2rgb(arg) | |
371 | sys.stdout.write('xterm color \033[38;5;%sm%s\033[0m -> RGB exact \033[38;5;%sm%s\033[0m' % (arg, arg, arg, rgb)) | |
372 | sys.stdout.write("\033[0m\n") | |
373 | else: | |
374 | short, rgb = rgb2short(arg) | |
375 | sys.stdout.write('RGB %s -> xterm color approx \033[38;5;%sm%s (%s)' % (arg, short, short, rgb)) | |
376 | sys.stdout.write("\033[0m\n") |