Image
[rainbowstream.git] / rainbowstream / image.py
... / ...
CommitLineData
1#! /usr/bin/env python
2
3""" Convert values between RGB hex codes and xterm-256 color codes.
4
5Nice long listing of all 256 colors and their codes. Useful for
6developing console color themes, or even script output schemes.
7
8Resources:
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
13I'm not sure where this script was inspired from. I think I must have
14written 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
24import sys, re
25
26CLUT = [ # 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
294def _str2hex(hexstr):
295 return int(hexstr, 16)
296
297def _strip_hash(rgb):
298 # Strip leading `#` if exists.
299 if rgb.startswith('#'):
300 rgb = rgb.lstrip('#')
301 return rgb
302
303def _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
310def short2rgb(short):
311 return SHORT2RGB_DICT[short]
312
313def 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
324def 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
358RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts()
359
360#---------------------------------------------------------------------
361
362if __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")