color.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. #!/usr/bin/env python
  2. import math, os, sys
  3. webcolors = {
  4. "indianred": "#cd5c5c",
  5. "lightcoral": "#f08080",
  6. "salmon": "#fa8072",
  7. "darksalmon": "#e9967a",
  8. "lightsalmon": "#ffa07a",
  9. "red": "#ff0000",
  10. "crimson": "#dc143c",
  11. "firebrick": "#b22222",
  12. "darkred": "#8b0000",
  13. "pink": "#ffc0cb",
  14. "lightpink": "#ffb6c1",
  15. "hotpink": "#ff69b4",
  16. "deeppink": "#ff1493",
  17. "mediumvioletred": "#c71585",
  18. "palevioletred": "#db7093",
  19. "lightsalmon": "#ffa07a",
  20. "coral": "#ff7f50",
  21. "tomato": "#ff6347",
  22. "orangered": "#ff4500",
  23. "darkorange": "#ff8c00",
  24. "orange": "#ffa500",
  25. "gold": "#ffd700",
  26. "yellow": "#ffff00",
  27. "lightyellow": "#ffffe0",
  28. "lemonchiffon": "#fffacd",
  29. "lightgoldenrodyellow": "#fafad2",
  30. "papayawhip": "#ffefd5",
  31. "moccasin": "#ffe4b5",
  32. "peachpuff": "#ffdab9",
  33. "palegoldenrod": "#eee8aa",
  34. "khaki": "#f0e68c",
  35. "darkkhaki": "#bdb76b",
  36. "lavender": "#e6e6fa",
  37. "thistle": "#d8bfd8",
  38. "plum": "#dda0dd",
  39. "violet": "#ee82ee",
  40. "orchid": "#da70d6",
  41. "fuchsia": "#ff00ff",
  42. "magenta": "#ff00ff",
  43. "mediumorchid": "#ba55d3",
  44. "mediumpurple": "#9370db",
  45. "blueviolet": "#8a2be2",
  46. "darkviolet": "#9400d3",
  47. "darkorchid": "#9932cc",
  48. "darkmagenta": "#8b008b",
  49. "purple": "#800080",
  50. "indigo": "#4b0082",
  51. "darkslateblue": "#483d8b",
  52. "slateblue": "#6a5acd",
  53. "mediumslateblue": "#7b68ee",
  54. "greenyellow": "#adff2f",
  55. "chartreuse": "#7fff00",
  56. "lawngreen": "#7cfc00",
  57. "lime": "#00ff00",
  58. "limegreen": "#32cd32",
  59. "palegreen": "#98fb98",
  60. "lightgreen": "#90ee90",
  61. "mediumspringgreen": "#00fa9a",
  62. "springgreen": "#00ff7f",
  63. "mediumseagreen": "#3cb371",
  64. "seagreen": "#2e8b57",
  65. "forestgreen": "#228b22",
  66. "green": "#008000",
  67. "darkgreen": "#006400",
  68. "yellowgreen": "#9acd32",
  69. "olivedrab": "#6b8e23",
  70. "olive": "#808000",
  71. "darkolivegreen": "#556b2f",
  72. "mediumaquamarine": "#66cdaa",
  73. "darkseagreen": "#8fbc8f",
  74. "lightseagreen": "#20b2aa",
  75. "darkcyan": "#008b8b",
  76. "teal": "#008080",
  77. "aqua": "#00ffff",
  78. "cyan": "#00ffff",
  79. "lightcyan": "#e0ffff",
  80. "paleturquoise": "#afeeee",
  81. "aquamarine": "#7fffd4",
  82. "turquoise": "#40e0d0",
  83. "mediumturquoise": "#48d1cc",
  84. "darkturquoise": "#00ced1",
  85. "cadetblue": "#5f9ea0",
  86. "steelblue": "#4682b4",
  87. "lightsteelblue": "#b0c4de",
  88. "powderblue": "#b0e0e6",
  89. "lightblue": "#add8e6",
  90. "skyblue": "#87ceeb",
  91. "lightskyblue": "#87cefa",
  92. "deepskyblue": "#00bfff",
  93. "dodgerblue": "#1e90ff",
  94. "cornflowerblue": "#6495ed",
  95. "royalblue": "#4169e1",
  96. "blue": "#0000ff",
  97. "mediumblue": "#0000cd",
  98. "darkblue": "#00008b",
  99. "navy": "#000080",
  100. "midnightblue": "#191970",
  101. "cornsilk": "#fff8dc",
  102. "blanchedalmond": "#ffebcd",
  103. "bisque": "#ffe4c4",
  104. "navajowhite": "#ffdead",
  105. "wheat": "#f5deb3",
  106. "burlywood": "#deb887",
  107. "tan": "#d2b48c",
  108. "rosybrown": "#bc8f8f",
  109. "sandybrown": "#f4a460",
  110. "goldenrod": "#daa520",
  111. "darkgoldenrod": "#b8860b",
  112. "peru": "#cd853f",
  113. "chocolate": "#d2691e",
  114. "saddlebrown": "#8b4513",
  115. "sienna": "#a0522d",
  116. "brown": "#a52a2a",
  117. "maroon": "#800000",
  118. "white": "#ffffff",
  119. "snow": "#fffafa",
  120. "honeydew": "#f0fff0",
  121. "mintcream": "#f5fffa",
  122. "azure": "#f0ffff",
  123. "aliceblue": "#f0f8ff",
  124. "ghostwhite": "#f8f8ff",
  125. "whitesmoke": "#f5f5f5",
  126. "seashell": "#fff5ee",
  127. "beige": "#f5f5dc",
  128. "oldlace": "#fdf5e6",
  129. "floralwhite": "#fffaf0",
  130. "ivory": "#fffff0",
  131. "antiquewhite": "#faebd7",
  132. "linen": "#faf0e6",
  133. "lavenderblush": "#fff0f5",
  134. "mistyrose": "#ffe4e1",
  135. "gainsboro": "#dcdcdc",
  136. "lightgrey": "#d3d3d3",
  137. "silver": "#c0c0c0",
  138. "darkgray": "#a9a9a9",
  139. "gray": "#808080",
  140. "dimgray": "#696969",
  141. "lightslategray": "#778899",
  142. "slategray": "#708090",
  143. "darkslategray": "#2f4f4f",
  144. "black": "#000000",
  145. }
  146. if os.name == "nt":
  147. consoleColors = [
  148. "#000000", #{ 0, 0, 0 },//0 - black
  149. "#000080", #{ 0, 0, 128 },//1 - navy
  150. "#008000", #{ 0, 128, 0 },//2 - green
  151. "#008080", #{ 0, 128, 128 },//3 - teal
  152. "#800000", #{ 128, 0, 0 },//4 - maroon
  153. "#800080", #{ 128, 0, 128 },//5 - purple
  154. "#808000", #{ 128, 128, 0 },//6 - olive
  155. "#C0C0C0", #{ 192, 192, 192 },//7 - silver
  156. "#808080", #{ 128, 128, 128 },//8 - gray
  157. "#0000FF", #{ 0, 0, 255 },//9 - blue
  158. "#00FF00", #{ 0, 255, 0 },//a - lime
  159. "#00FFFF", #{ 0, 255, 255 },//b - cyan
  160. "#FF0000", #{ 255, 0, 0 },//c - red
  161. "#FF00FF", #{ 255, 0, 255 },//d - magenta
  162. "#FFFF00", #{ 255, 255, 0 },//e - yellow
  163. "#FFFFFF", #{ 255, 255, 255 } //f - white
  164. ]
  165. else:
  166. consoleColors = [
  167. "#2e3436",
  168. "#cc0000",
  169. "#4e9a06",
  170. "#c4a000",
  171. "#3465a4",
  172. "#75507b",
  173. "#06989a",
  174. "#d3d7cf",
  175. "#ffffff",
  176. "#555753",
  177. "#ef2929",
  178. "#8ae234",
  179. "#fce94f",
  180. "#729fcf",
  181. "#ad7fa8",
  182. "#34e2e2",
  183. "#eeeeec",
  184. ]
  185. def RGB2LAB(r,g,b):
  186. if max(r,g,b):
  187. r /= 255.
  188. g /= 255.
  189. b /= 255.
  190. X = (0.412453 * r + 0.357580 * g + 0.180423 * b) / 0.950456
  191. Y = (0.212671 * r + 0.715160 * g + 0.072169 * b)
  192. Z = (0.019334 * r + 0.119193 * g + 0.950227 * b) / 1.088754
  193. #[X * 0.950456] [0.412453 0.357580 0.180423] [R]
  194. #[Y ] = [0.212671 0.715160 0.072169] * [G]
  195. #[Z * 1.088754] [0.019334 0.119193 0.950227] [B]
  196. T = 0.008856 #threshold
  197. if X > T:
  198. fX = math.pow(X, 1./3.)
  199. else:
  200. fX = 7.787 * X + 16./116.
  201. # Compute L
  202. if Y > T:
  203. Y3 = math.pow(Y, 1./3.)
  204. fY = Y3
  205. L = 116. * Y3 - 16.0
  206. else:
  207. fY = 7.787 * Y + 16./116.
  208. L = 903.3 * Y
  209. if Z > T:
  210. fZ = math.pow(Z, 1./3.)
  211. else:
  212. fZ = 7.787 * Z + 16./116.
  213. # Compute a and b
  214. a = 500. * (fX - fY)
  215. b = 200. * (fY - fZ)
  216. return (L,a,b)
  217. def colorDistance(r1,g1,b1 = None, r2 = None, g2 = None,b2 = None):
  218. if type(r1) == tuple and type(g1) == tuple and b1 is None and r2 is None and g2 is None and b2 is None:
  219. (l1,a1,b1) = RGB2LAB(*r1)
  220. (l2,a2,b2) = RGB2LAB(*g1)
  221. else:
  222. (l1,a1,b1) = RGB2LAB(r1,g1,b1)
  223. (l2,a2,b2) = RGB2LAB(r2,g2,b2)
  224. #CIE94
  225. dl = l1-l2
  226. C1 = math.sqrt(a1*a1 + b1*b1)
  227. C2 = math.sqrt(a2*a2 + b2*b2)
  228. dC = C1 - C2
  229. da = a1-a2
  230. db = b1-b2
  231. dH = math.sqrt(max(0, da*da + db*db - dC*dC))
  232. Kl = 1
  233. K1 = 0.045
  234. K2 = 0.015
  235. s1 = dl/Kl
  236. s2 = dC/(1. + K1 * C1)
  237. s3 = dH/(1. + K2 * C1)
  238. return math.sqrt(s1*s1 + s2*s2 + s3*s3)
  239. def parseHexColor(col):
  240. if len(col) != 4 and len(col) != 7 and not col.startswith("#"):
  241. return (0,0,0)
  242. if len(col) == 4:
  243. r = col[1]*2
  244. g = col[2]*2
  245. b = col[3]*2
  246. else:
  247. r = col[1:3]
  248. g = col[3:5]
  249. b = col[5:7]
  250. return (int(r,16), int(g,16), int(b,16))
  251. def getColor(col):
  252. if isinstance(col, str):
  253. if col.lower() in webcolors:
  254. return parseHexColor(webcolors[col.lower()])
  255. else:
  256. return parseHexColor(col)
  257. else:
  258. return col
  259. def getNearestConsoleColor(col):
  260. color = getColor(col)
  261. minidx = 0
  262. mindist = colorDistance(color, getColor(consoleColors[0]))
  263. for i in range(len(consoleColors)):
  264. dist = colorDistance(color, getColor(consoleColors[i]))
  265. if dist < mindist:
  266. mindist = dist
  267. minidx = i
  268. return minidx
  269. if os.name == 'nt':
  270. import msvcrt
  271. from ctypes import windll, Structure, c_short, c_ushort, byref
  272. SHORT = c_short
  273. WORD = c_ushort
  274. class COORD(Structure):
  275. _fields_ = [
  276. ("X", SHORT),
  277. ("Y", SHORT)]
  278. class SMALL_RECT(Structure):
  279. _fields_ = [
  280. ("Left", SHORT),
  281. ("Top", SHORT),
  282. ("Right", SHORT),
  283. ("Bottom", SHORT)]
  284. class CONSOLE_SCREEN_BUFFER_INFO(Structure):
  285. _fields_ = [
  286. ("dwSize", COORD),
  287. ("dwCursorPosition", COORD),
  288. ("wAttributes", WORD),
  289. ("srWindow", SMALL_RECT),
  290. ("dwMaximumWindowSize", COORD)]
  291. class winConsoleColorizer(object):
  292. def __init__(self, stream):
  293. self.handle = msvcrt.get_osfhandle(stream.fileno())
  294. self.default_attrs = 7#self.get_text_attr()
  295. self.stream = stream
  296. def get_text_attr(self):
  297. csbi = CONSOLE_SCREEN_BUFFER_INFO()
  298. windll.kernel32.GetConsoleScreenBufferInfo(self.handle, byref(csbi))
  299. return csbi.wAttributes
  300. def set_text_attr(self, color):
  301. windll.kernel32.SetConsoleTextAttribute(self.handle, color)
  302. def write(self, *text, **attrs):
  303. if not text:
  304. return
  305. color = attrs.get("color", None)
  306. if color:
  307. col = getNearestConsoleColor(color)
  308. self.stream.flush()
  309. self.set_text_attr(col)
  310. self.stream.write(" ".join([str(t) for t in text]))
  311. if color:
  312. self.stream.flush()
  313. self.set_text_attr(self.default_attrs)
  314. class dummyColorizer(object):
  315. def __init__(self, stream):
  316. self.stream = stream
  317. def write(self, *text, **attrs):
  318. if text:
  319. self.stream.write(" ".join([str(t) for t in text]))
  320. class asciiSeqColorizer(object):
  321. RESET_SEQ = "\033[0m"
  322. #BOLD_SEQ = "\033[1m"
  323. ITALIC_SEQ = "\033[3m"
  324. UNDERLINE_SEQ = "\033[4m"
  325. STRIKEOUT_SEQ = "\033[9m"
  326. COLOR_SEQ0 = "\033[00;%dm" #dark
  327. COLOR_SEQ1 = "\033[01;%dm" #bold and light
  328. def __init__(self, stream):
  329. self.stream = stream
  330. def get_seq(self, code):
  331. if code > 8:
  332. return self.__class__.COLOR_SEQ1 % (30 + code - 9)
  333. else:
  334. return self.__class__.COLOR_SEQ0 % (30 + code)
  335. def write(self, *text, **attrs):
  336. if not text:
  337. return
  338. color = attrs.get("color", None)
  339. if color:
  340. col = getNearestConsoleColor(color)
  341. self.stream.write(self.get_seq(col))
  342. self.stream.write(" ".join([str(t) for t in text]))
  343. if color:
  344. self.stream.write(self.__class__.RESET_SEQ)
  345. def getColorizer(stream):
  346. if stream.isatty():
  347. if os.name == "nt":
  348. return winConsoleColorizer(stream)
  349. else:
  350. return asciiSeqColorizer(stream)
  351. else:
  352. return dummyColorizer(stream)