implement
[rainbowstream.git] / rainbowstream / image.py
CommitLineData
25e5e7f4 1from PIL import Image
2b502606 2import sys,os
1c50e2d1
O
3
4""" Convert values between RGB hex codes and xterm-256 color codes.
5
6Nice long listing of all 256 colors and their codes. Useful for
7developing console color themes, or even script output schemes.
8
9Resources:
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
14I'm not sure where this script was inspired from. I think I must have
15written 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
25import sys, re
26
27CLUT = [ # 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
295def _strip_hash(rgb):
296 # Strip leading `#` if exists.
297 if rgb.startswith('#'):
298 rgb = rgb.lstrip('#')
299 return rgb
300
301def _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
308def short2rgb(short):
309 return SHORT2RGB_DICT[short]
310
2b502606 311def pixel_print(ansicolor):
312 sys.stdout.write('\033[48;5;%sm \033[0m' % (ansicolor))
313
1c50e2d1
O
314def 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 325def 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
330def rgb_to_hex(rgb):
331 return '#%02x%02x%02x' % rgb
332
1c50e2d1
O
333def 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
373RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts()
374
2b502606 375def 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
397if __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")