colors.nim 15 KB

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