colors.nim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. #
  2. # Nim's Runtime Library
  3. # (c) Copyright 2010 Andreas Rumpf
  4. #
  5. # See the file "copying.txt", included in this
  6. # distribution, for details about the copyright.
  7. #
  8. ## This module implements color handling for Nim.
  9. import strutils
  10. from algorithm import binarySearch
  11. type
  12. Color* = distinct int ## A color stored as RGB, e.g. `0xff00cc`.
  13. proc `==` *(a, b: Color): bool {.borrow.}
  14. ## Compares two colors.
  15. ##
  16. ## .. code-block::
  17. ## var
  18. ## a = Color(0xff_00_ff)
  19. ## b = colFuchsia
  20. ## c = Color(0x00_ff_cc)
  21. ## assert a == b
  22. ## assert not a == c
  23. template extract(a: Color, r, g, b: untyped) =
  24. var r = a.int shr 16 and 0xff
  25. var g = a.int shr 8 and 0xff
  26. var b = a.int and 0xff
  27. template rawRGB(r, g, b: int): Color =
  28. Color(r shl 16 or g shl 8 or b)
  29. template colorOp(op): Color =
  30. extract(a, ar, ag, ab)
  31. extract(b, br, bg, bb)
  32. rawRGB(op(ar, br), op(ag, bg), op(ab, bb))
  33. proc satPlus(a, b: int): int {.inline.} =
  34. result = a +% b
  35. if result > 255: result = 255
  36. proc satMinus(a, b: int): int {.inline.} =
  37. result = a -% b
  38. if result < 0: result = 0
  39. proc `+`*(a, b: Color): Color =
  40. ## Adds two colors.
  41. ##
  42. ## This uses saturated arithmetic, so that each color
  43. ## component cannot overflow (255 is used as a maximum).
  44. ##
  45. ## .. code-block::
  46. ## var
  47. ## a = Color(0xaa_00_ff)
  48. ## b = Color(0x11_cc_cc)
  49. ## assert a + b == Color(0xbb_cc_ff)
  50. ##
  51. colorOp(satPlus)
  52. proc `-`*(a, b: Color): Color =
  53. ## Subtracts two colors.
  54. ##
  55. ## This uses saturated artithmetic, so that each color
  56. ## component cannot underflow (0 is used as a minimum).
  57. ##
  58. ## .. code-block::
  59. ## var
  60. ## a = Color(0xff_33_ff)
  61. ## b = Color(0x11_ff_cc)
  62. ## assert a - b == Color(0xee_00_33)
  63. ##
  64. colorOp(satMinus)
  65. proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
  66. ## Extracts the red/green/blue components of the color `a`.
  67. ##
  68. ## .. code-block::
  69. ## var
  70. ## a = Color(0xff_00_ff)
  71. ## b = Color(0x00_ff_cc)
  72. ## type
  73. ## Col = range[0..255]
  74. ## echo extractRGB(a) # => (r: 255, g: 0, b: 255)
  75. ## echo extractRGB(b) # => (r: 0, g: 255, b: 204)
  76. ##
  77. result.r = a.int shr 16 and 0xff
  78. result.g = a.int shr 8 and 0xff
  79. result.b = a.int and 0xff
  80. proc intensity*(a: Color, f: float): Color =
  81. ## Returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
  82. ## dark) to 1.0 (full color intensity).
  83. ##
  84. ## .. code-block::
  85. ## var
  86. ## a = Color(0xff_00_ff)
  87. ## b = Color(0x00_42_cc)
  88. ## assert a.intensity(0.5) == Color(0x80_00_80)
  89. ## assert b.intensity(0.5) == Color(0x00_21_66)
  90. ##
  91. var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
  92. var g = toInt(toFloat(a.int shr 8 and 0xff) * f)
  93. var b = toInt(toFloat(a.int and 0xff) * f)
  94. if r >% 255: r = 255
  95. if g >% 255: g = 255
  96. if b >% 255: b = 255
  97. result = rawRGB(r, g, b)
  98. template mix*(a, b: Color, fn: untyped): untyped =
  99. ## Uses `fn` to mix the colors `a` and `b`.
  100. ##
  101. ## `fn` is invoked for each component R, G, and B.
  102. ## If `fn`'s result is not in the `range[0..255]`,
  103. ## it will be saturated to be so.
  104. ##
  105. ## .. code-block::
  106. ## var
  107. ## a = Color(0x0a2814)
  108. ## b = Color(0x050a03)
  109. ##
  110. ## proc myMix(x, y: int): int =
  111. ## 2 * x - 3 * y
  112. ##
  113. ## assert mix(a, b, myMix) == Color(0x05_32_1f)
  114. ##
  115. template `><` (x: untyped): untyped =
  116. # keep it in the range 0..255
  117. block:
  118. var y = x # eval only once
  119. if y >% 255:
  120. y = if y < 0: 0 else: 255
  121. y
  122. extract(a, ar, ag, ab)
  123. extract(b, br, bg, bb)
  124. rawRGB(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb))
  125. const
  126. colAliceBlue* = Color(0xF0F8FF)
  127. colAntiqueWhite* = Color(0xFAEBD7)
  128. colAqua* = Color(0x00FFFF)
  129. colAquamarine* = Color(0x7FFFD4)
  130. colAzure* = Color(0xF0FFFF)
  131. colBeige* = Color(0xF5F5DC)
  132. colBisque* = Color(0xFFE4C4)
  133. colBlack* = Color(0x000000)
  134. colBlanchedAlmond* = Color(0xFFEBCD)
  135. colBlue* = Color(0x0000FF)
  136. colBlueViolet* = Color(0x8A2BE2)
  137. colBrown* = Color(0xA52A2A)
  138. colBurlyWood* = Color(0xDEB887)
  139. colCadetBlue* = Color(0x5F9EA0)
  140. colChartreuse* = Color(0x7FFF00)
  141. colChocolate* = Color(0xD2691E)
  142. colCoral* = Color(0xFF7F50)
  143. colCornflowerBlue* = Color(0x6495ED)
  144. colCornsilk* = Color(0xFFF8DC)
  145. colCrimson* = Color(0xDC143C)
  146. colCyan* = Color(0x00FFFF)
  147. colDarkBlue* = Color(0x00008B)
  148. colDarkCyan* = Color(0x008B8B)
  149. colDarkGoldenRod* = Color(0xB8860B)
  150. colDarkGray* = Color(0xA9A9A9)
  151. colDarkGreen* = Color(0x006400)
  152. colDarkKhaki* = Color(0xBDB76B)
  153. colDarkMagenta* = Color(0x8B008B)
  154. colDarkOliveGreen* = Color(0x556B2F)
  155. colDarkorange* = Color(0xFF8C00)
  156. colDarkOrchid* = Color(0x9932CC)
  157. colDarkRed* = Color(0x8B0000)
  158. colDarkSalmon* = Color(0xE9967A)
  159. colDarkSeaGreen* = Color(0x8FBC8F)
  160. colDarkSlateBlue* = Color(0x483D8B)
  161. colDarkSlateGray* = Color(0x2F4F4F)
  162. colDarkTurquoise* = Color(0x00CED1)
  163. colDarkViolet* = Color(0x9400D3)
  164. colDeepPink* = Color(0xFF1493)
  165. colDeepSkyBlue* = Color(0x00BFFF)
  166. colDimGray* = Color(0x696969)
  167. colDodgerBlue* = Color(0x1E90FF)
  168. colFireBrick* = Color(0xB22222)
  169. colFloralWhite* = Color(0xFFFAF0)
  170. colForestGreen* = Color(0x228B22)
  171. colFuchsia* = Color(0xFF00FF)
  172. colGainsboro* = Color(0xDCDCDC)
  173. colGhostWhite* = Color(0xF8F8FF)
  174. colGold* = Color(0xFFD700)
  175. colGoldenRod* = Color(0xDAA520)
  176. colGray* = Color(0x808080)
  177. colGreen* = Color(0x008000)
  178. colGreenYellow* = Color(0xADFF2F)
  179. colHoneyDew* = Color(0xF0FFF0)
  180. colHotPink* = Color(0xFF69B4)
  181. colIndianRed* = Color(0xCD5C5C)
  182. colIndigo* = Color(0x4B0082)
  183. colIvory* = Color(0xFFFFF0)
  184. colKhaki* = Color(0xF0E68C)
  185. colLavender* = Color(0xE6E6FA)
  186. colLavenderBlush* = Color(0xFFF0F5)
  187. colLawnGreen* = Color(0x7CFC00)
  188. colLemonChiffon* = Color(0xFFFACD)
  189. colLightBlue* = Color(0xADD8E6)
  190. colLightCoral* = Color(0xF08080)
  191. colLightCyan* = Color(0xE0FFFF)
  192. colLightGoldenRodYellow* = Color(0xFAFAD2)
  193. colLightGrey* = Color(0xD3D3D3)
  194. colLightGreen* = Color(0x90EE90)
  195. colLightPink* = Color(0xFFB6C1)
  196. colLightSalmon* = Color(0xFFA07A)
  197. colLightSeaGreen* = Color(0x20B2AA)
  198. colLightSkyBlue* = Color(0x87CEFA)
  199. colLightSlateGray* = Color(0x778899)
  200. colLightSteelBlue* = Color(0xB0C4DE)
  201. colLightYellow* = Color(0xFFFFE0)
  202. colLime* = Color(0x00FF00)
  203. colLimeGreen* = Color(0x32CD32)
  204. colLinen* = Color(0xFAF0E6)
  205. colMagenta* = Color(0xFF00FF)
  206. colMaroon* = Color(0x800000)
  207. colMediumAquaMarine* = Color(0x66CDAA)
  208. colMediumBlue* = Color(0x0000CD)
  209. colMediumOrchid* = Color(0xBA55D3)
  210. colMediumPurple* = Color(0x9370D8)
  211. colMediumSeaGreen* = Color(0x3CB371)
  212. colMediumSlateBlue* = Color(0x7B68EE)
  213. colMediumSpringGreen* = Color(0x00FA9A)
  214. colMediumTurquoise* = Color(0x48D1CC)
  215. colMediumVioletRed* = Color(0xC71585)
  216. colMidnightBlue* = Color(0x191970)
  217. colMintCream* = Color(0xF5FFFA)
  218. colMistyRose* = Color(0xFFE4E1)
  219. colMoccasin* = Color(0xFFE4B5)
  220. colNavajoWhite* = Color(0xFFDEAD)
  221. colNavy* = Color(0x000080)
  222. colOldLace* = Color(0xFDF5E6)
  223. colOlive* = Color(0x808000)
  224. colOliveDrab* = Color(0x6B8E23)
  225. colOrange* = Color(0xFFA500)
  226. colOrangeRed* = Color(0xFF4500)
  227. colOrchid* = Color(0xDA70D6)
  228. colPaleGoldenRod* = Color(0xEEE8AA)
  229. colPaleGreen* = Color(0x98FB98)
  230. colPaleTurquoise* = Color(0xAFEEEE)
  231. colPaleVioletRed* = Color(0xD87093)
  232. colPapayaWhip* = Color(0xFFEFD5)
  233. colPeachPuff* = Color(0xFFDAB9)
  234. colPeru* = Color(0xCD853F)
  235. colPink* = Color(0xFFC0CB)
  236. colPlum* = Color(0xDDA0DD)
  237. colPowderBlue* = Color(0xB0E0E6)
  238. colPurple* = Color(0x800080)
  239. colRed* = Color(0xFF0000)
  240. colRosyBrown* = Color(0xBC8F8F)
  241. colRoyalBlue* = Color(0x4169E1)
  242. colSaddleBrown* = Color(0x8B4513)
  243. colSalmon* = Color(0xFA8072)
  244. colSandyBrown* = Color(0xF4A460)
  245. colSeaGreen* = Color(0x2E8B57)
  246. colSeaShell* = Color(0xFFF5EE)
  247. colSienna* = Color(0xA0522D)
  248. colSilver* = Color(0xC0C0C0)
  249. colSkyBlue* = Color(0x87CEEB)
  250. colSlateBlue* = Color(0x6A5ACD)
  251. colSlateGray* = Color(0x708090)
  252. colSnow* = Color(0xFFFAFA)
  253. colSpringGreen* = Color(0x00FF7F)
  254. colSteelBlue* = Color(0x4682B4)
  255. colTan* = Color(0xD2B48C)
  256. colTeal* = Color(0x008080)
  257. colThistle* = Color(0xD8BFD8)
  258. colTomato* = Color(0xFF6347)
  259. colTurquoise* = Color(0x40E0D0)
  260. colViolet* = Color(0xEE82EE)
  261. colWheat* = Color(0xF5DEB3)
  262. colWhite* = Color(0xFFFFFF)
  263. colWhiteSmoke* = Color(0xF5F5F5)
  264. colYellow* = Color(0xFFFF00)
  265. colYellowGreen* = Color(0x9ACD32)
  266. colorNames = [
  267. ("aliceblue", colAliceBlue),
  268. ("antiquewhite", colAntiqueWhite),
  269. ("aqua", colAqua),
  270. ("aquamarine", colAquamarine),
  271. ("azure", colAzure),
  272. ("beige", colBeige),
  273. ("bisque", colBisque),
  274. ("black", colBlack),
  275. ("blanchedalmond", colBlanchedAlmond),
  276. ("blue", colBlue),
  277. ("blueviolet", colBlueViolet),
  278. ("brown", colBrown),
  279. ("burlywood", colBurlyWood),
  280. ("cadetblue", colCadetBlue),
  281. ("chartreuse", colChartreuse),
  282. ("chocolate", colChocolate),
  283. ("coral", colCoral),
  284. ("cornflowerblue", colCornflowerBlue),
  285. ("cornsilk", colCornsilk),
  286. ("crimson", colCrimson),
  287. ("cyan", colCyan),
  288. ("darkblue", colDarkBlue),
  289. ("darkcyan", colDarkCyan),
  290. ("darkgoldenrod", colDarkGoldenRod),
  291. ("darkgray", colDarkGray),
  292. ("darkgreen", colDarkGreen),
  293. ("darkkhaki", colDarkKhaki),
  294. ("darkmagenta", colDarkMagenta),
  295. ("darkolivegreen", colDarkOliveGreen),
  296. ("darkorange", colDarkorange),
  297. ("darkorchid", colDarkOrchid),
  298. ("darkred", colDarkRed),
  299. ("darksalmon", colDarkSalmon),
  300. ("darkseagreen", colDarkSeaGreen),
  301. ("darkslateblue", colDarkSlateBlue),
  302. ("darkslategray", colDarkSlateGray),
  303. ("darkturquoise", colDarkTurquoise),
  304. ("darkviolet", colDarkViolet),
  305. ("deeppink", colDeepPink),
  306. ("deepskyblue", colDeepSkyBlue),
  307. ("dimgray", colDimGray),
  308. ("dodgerblue", colDodgerBlue),
  309. ("firebrick", colFireBrick),
  310. ("floralwhite", colFloralWhite),
  311. ("forestgreen", colForestGreen),
  312. ("fuchsia", colFuchsia),
  313. ("gainsboro", colGainsboro),
  314. ("ghostwhite", colGhostWhite),
  315. ("gold", colGold),
  316. ("goldenrod", colGoldenRod),
  317. ("gray", colGray),
  318. ("green", colGreen),
  319. ("greenyellow", colGreenYellow),
  320. ("honeydew", colHoneyDew),
  321. ("hotpink", colHotPink),
  322. ("indianred", colIndianRed),
  323. ("indigo", colIndigo),
  324. ("ivory", colIvory),
  325. ("khaki", colKhaki),
  326. ("lavender", colLavender),
  327. ("lavenderblush", colLavenderBlush),
  328. ("lawngreen", colLawnGreen),
  329. ("lemonchiffon", colLemonChiffon),
  330. ("lightblue", colLightBlue),
  331. ("lightcoral", colLightCoral),
  332. ("lightcyan", colLightCyan),
  333. ("lightgoldenrodyellow", colLightGoldenRodYellow),
  334. ("lightgrey", colLightGrey),
  335. ("lightgreen", colLightGreen),
  336. ("lightpink", colLightPink),
  337. ("lightsalmon", colLightSalmon),
  338. ("lightseagreen", colLightSeaGreen),
  339. ("lightskyblue", colLightSkyBlue),
  340. ("lightslategray", colLightSlateGray),
  341. ("lightsteelblue", colLightSteelBlue),
  342. ("lightyellow", colLightYellow),
  343. ("lime", colLime),
  344. ("limegreen", colLimeGreen),
  345. ("linen", colLinen),
  346. ("magenta", colMagenta),
  347. ("maroon", colMaroon),
  348. ("mediumaquamarine", colMediumAquaMarine),
  349. ("mediumblue", colMediumBlue),
  350. ("mediumorchid", colMediumOrchid),
  351. ("mediumpurple", colMediumPurple),
  352. ("mediumseagreen", colMediumSeaGreen),
  353. ("mediumslateblue", colMediumSlateBlue),
  354. ("mediumspringgreen", colMediumSpringGreen),
  355. ("mediumturquoise", colMediumTurquoise),
  356. ("mediumvioletred", colMediumVioletRed),
  357. ("midnightblue", colMidnightBlue),
  358. ("mintcream", colMintCream),
  359. ("mistyrose", colMistyRose),
  360. ("moccasin", colMoccasin),
  361. ("navajowhite", colNavajoWhite),
  362. ("navy", colNavy),
  363. ("oldlace", colOldLace),
  364. ("olive", colOlive),
  365. ("olivedrab", colOliveDrab),
  366. ("orange", colOrange),
  367. ("orangered", colOrangeRed),
  368. ("orchid", colOrchid),
  369. ("palegoldenrod", colPaleGoldenRod),
  370. ("palegreen", colPaleGreen),
  371. ("paleturquoise", colPaleTurquoise),
  372. ("palevioletred", colPaleVioletRed),
  373. ("papayawhip", colPapayaWhip),
  374. ("peachpuff", colPeachPuff),
  375. ("peru", colPeru),
  376. ("pink", colPink),
  377. ("plum", colPlum),
  378. ("powderblue", colPowderBlue),
  379. ("purple", colPurple),
  380. ("red", colRed),
  381. ("rosybrown", colRosyBrown),
  382. ("royalblue", colRoyalBlue),
  383. ("saddlebrown", colSaddleBrown),
  384. ("salmon", colSalmon),
  385. ("sandybrown", colSandyBrown),
  386. ("seagreen", colSeaGreen),
  387. ("seashell", colSeaShell),
  388. ("sienna", colSienna),
  389. ("silver", colSilver),
  390. ("skyblue", colSkyBlue),
  391. ("slateblue", colSlateBlue),
  392. ("slategray", colSlateGray),
  393. ("snow", colSnow),
  394. ("springgreen", colSpringGreen),
  395. ("steelblue", colSteelBlue),
  396. ("tan", colTan),
  397. ("teal", colTeal),
  398. ("thistle", colThistle),
  399. ("tomato", colTomato),
  400. ("turquoise", colTurquoise),
  401. ("violet", colViolet),
  402. ("wheat", colWheat),
  403. ("white", colWhite),
  404. ("whitesmoke", colWhiteSmoke),
  405. ("yellow", colYellow),
  406. ("yellowgreen", colYellowGreen)]
  407. proc `$`*(c: Color): string =
  408. ## Converts a color into its textual representation.
  409. ##
  410. ## .. code-block::
  411. ## assert $colFuchsia == "#FF00FF"
  412. ##
  413. result = '#' & toHex(int(c), 6)
  414. proc colorNameCmp(x: tuple[name: string, col: Color], y: string): int =
  415. result = cmpIgnoreCase(x.name, y)
  416. proc parseColor*(name: string): Color =
  417. ## Parses `name` to a color value.
  418. ##
  419. ## If no valid color could be parsed ``ValueError`` is raised.
  420. ## Case insensitive.
  421. ##
  422. ## .. code-block::
  423. ## var
  424. ## a = "silver"
  425. ## b = "#0179fc"
  426. ## c = "#zzmmtt"
  427. ## assert parseColor(a) == Color(0xc0_c0_c0)
  428. ## assert parseColor(b) == Color(0x01_79_fc)
  429. ## doAssertRaises(ValueError): discard parseColor(c)
  430. ##
  431. if name[0] == '#':
  432. result = Color(parseHexInt(name))
  433. else:
  434. var idx = binarySearch(colorNames, name, colorNameCmp)
  435. if idx < 0: raise newException(ValueError, "unknown color: " & name)
  436. result = colorNames[idx][1]
  437. proc isColor*(name: string): bool =
  438. ## Returns true if `name` is a known color name or a hexadecimal color
  439. ## prefixed with ``#``. Case insensitive.
  440. ##
  441. ## .. code-block::
  442. ## var
  443. ## a = "silver"
  444. ## b = "#0179fc"
  445. ## c = "#zzmmtt"
  446. ## assert a.isColor
  447. ## assert b.isColor
  448. ## assert not c.isColor
  449. ##
  450. if name[0] == '#':
  451. for i in 1 .. name.len-1:
  452. if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false
  453. result = true
  454. else:
  455. result = binarySearch(colorNames, name, colorNameCmp) >= 0
  456. proc rgb*(r, g, b: range[0..255]): Color =
  457. ## Constructs a color from RGB values.
  458. ##
  459. ## .. code-block::
  460. ## assert rgb(0, 255, 128) == Color(0x00_ff_80)
  461. ##
  462. result = rawRGB(r, g, b)