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