color.lua 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. --- Color utilities
  2. -- @module color
  3. local modules = (...):gsub('%.[^%.]+$', '') .. "."
  4. local utils = require(modules .. "utils")
  5. local color = {}
  6. local color_mt = {}
  7. local function new(r, g, b, a)
  8. local c = { r, g, b, a }
  9. c._c = c
  10. return setmetatable(c, color)
  11. end
  12. -- HSV utilities (adapted from http://www.cs.rit.edu/~ncs/color/t_convert.html)
  13. -- hsv_to_color(hsv)
  14. -- Converts a set of HSV values to a color. hsv is a table.
  15. -- See also: hsv(h, s, v)
  16. local function hsv_to_color(hsv)
  17. local i
  18. local f, q, p, t
  19. local h, s, v
  20. local a = hsv[4] or 255
  21. s = hsv[2]
  22. v = hsv[3]
  23. if s == 0 then
  24. return new(v, v, v, a)
  25. end
  26. h = hsv[1] / 60
  27. i = math.floor(h)
  28. f = h - i
  29. p = v * (1-s)
  30. q = v * (1-s*f)
  31. t = v * (1-s*(1-f))
  32. if i == 0 then return new(v, t, p, a)
  33. elseif i == 1 then return new(q, v, p, a)
  34. elseif i == 2 then return new(p, v, t, a)
  35. elseif i == 3 then return new(p, q, v, a)
  36. elseif i == 4 then return new(t, p, v, a)
  37. else return new(v, p, q, a)
  38. end
  39. end
  40. -- color_to_hsv(c)
  41. -- Takes in a normal color and returns a table with the HSV values.
  42. local function color_to_hsv(c)
  43. local r = c[1]
  44. local g = c[2]
  45. local b = c[3]
  46. local a = c[4] or 255
  47. local h, s, v
  48. local min = math.min(r, g, b)
  49. local max = math.max(r, g, b)
  50. v = max
  51. local delta = max - min
  52. -- black, nothing else is really possible here.
  53. if min == 0 and max == 0 then
  54. return { 0, 0, 0, a }
  55. end
  56. if max ~= 0 then
  57. s = delta / max
  58. else
  59. -- r = g = b = 0 s = 0, v is undefined
  60. s = 0
  61. h = -1
  62. return { h, s, v, 255 }
  63. end
  64. if r == max then
  65. h = ( g - b ) / delta -- yellow/magenta
  66. elseif g == max then
  67. h = 2 + ( b - r ) / delta -- cyan/yellow
  68. else
  69. h = 4 + ( r - g ) / delta -- magenta/cyan
  70. end
  71. h = h * 60 -- degrees
  72. if h < 0 then
  73. h = h + 360
  74. end
  75. return { h, s, v, a }
  76. end
  77. --- The public constructor.
  78. -- @param x Can be of three types: </br>
  79. -- number red component 0-255
  80. -- table {r, g, b, a}
  81. -- nil for {0,0,0,0}
  82. -- @tparam number g Green component 0-255
  83. -- @tparam number b Blue component 0-255
  84. -- @tparam number a Alpha component 0-255
  85. -- @treturn color out
  86. function color.new(r, g, b, a)
  87. -- number, number, number, number
  88. if r and g and b and a then
  89. assert(type(r) == "number", "new: Wrong argument type for r (<number> expected)")
  90. assert(type(g) == "number", "new: Wrong argument type for g (<number> expected)")
  91. assert(type(b) == "number", "new: Wrong argument type for b (<number> expected)")
  92. assert(type(a) == "number", "new: Wrong argument type for a (<number> expected)")
  93. return new(r, g, b, a)
  94. -- {r, g, b, a}
  95. elseif type(r) == "table" then
  96. local rr, gg, bb, aa = r[1], r[2], r[3], r[4]
  97. assert(type(rr) == "number", "new: Wrong argument type for r (<number> expected)")
  98. assert(type(gg) == "number", "new: Wrong argument type for g (<number> expected)")
  99. assert(type(bb) == "number", "new: Wrong argument type for b (<number> expected)")
  100. assert(type(aa) == "number", "new: Wrong argument type for a (<number> expected)")
  101. return new(rr, gg, bb, aa)
  102. end
  103. return new(0, 0, 0, 0)
  104. end
  105. --- Convert hue,saturation,value table to color object.
  106. -- @tparam table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-255}
  107. -- @treturn color out
  108. color.hsv_to_color_table = hsv_to_color
  109. --- Convert color to hue,saturation,value table
  110. -- @tparam color in
  111. -- @treturn table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-255}
  112. color.color_to_hsv_table = color_to_hsv
  113. --- Convert hue,saturation,value to color object.
  114. -- @tparam number h hue 0-359
  115. -- @tparam number s saturation 0-1
  116. -- @tparam number v value 0-1
  117. -- @treturn color out
  118. function color.from_hsv(h, s, v)
  119. return hsv_to_color { h, s, v }
  120. end
  121. --- Convert hue,saturation,value to color object.
  122. -- @tparam number h hue 0-359
  123. -- @tparam number s saturation 0-1
  124. -- @tparam number v value 0-1
  125. -- @tparam number a alpha 0-255
  126. -- @treturn color out
  127. function color.from_hsva(h, s, v, a)
  128. return hsv_to_color { h, s, v, a }
  129. end
  130. --- Invert a color.
  131. -- @tparam color to invert
  132. -- @treturn color out
  133. function color.invert(c)
  134. return new(255 - c[1], 255 - c[2], 255 - c[3], c[4])
  135. end
  136. --- Lighten a color by a component-wise fixed amount (alpha unchanged)
  137. -- @tparam color to lighten
  138. -- @tparam number amount to increase each component by, 0-255 scale
  139. -- @treturn color out
  140. function color.lighten(c, v)
  141. return new(
  142. utils.clamp(c[1] + v * 255, 0, 255),
  143. utils.clamp(c[2] + v * 255, 0, 255),
  144. utils.clamp(c[3] + v * 255, 0, 255),
  145. c[4]
  146. )
  147. end
  148. function color.lerp(a, b, s)
  149. return a + s * (b - a)
  150. end
  151. --- Darken a color by a component-wise fixed amount (alpha unchanged)
  152. -- @tparam color to darken
  153. -- @tparam number amount to decrease each component by, 0-255 scale
  154. -- @treturn color out
  155. function color.darken(c, v)
  156. return new(
  157. utils.clamp(c[1] - v * 255, 0, 255),
  158. utils.clamp(c[2] - v * 255, 0, 255),
  159. utils.clamp(c[3] - v * 255, 0, 255),
  160. c[4]
  161. )
  162. end
  163. --- Multiply a color's components by a value (alpha unchanged)
  164. -- @tparam color to multiply
  165. -- @tparam number to multiply each component by
  166. -- @treturn color out
  167. function color.multiply(c, v)
  168. local t = color.new()
  169. for i = 1, 3 do
  170. t[i] = c[i] * v
  171. end
  172. t[4] = c[4]
  173. return t
  174. end
  175. -- directly set alpha channel
  176. -- @tparam color to alter
  177. -- @tparam number new alpha 0-255
  178. -- @treturn color out
  179. function color.alpha(c, v)
  180. local t = color.new()
  181. for i = 1, 3 do
  182. t[i] = c[i]
  183. end
  184. t[4] = v * 255
  185. return t
  186. end
  187. --- Multiply a color's alpha by a value
  188. -- @tparam color to multiply
  189. -- @tparam number to multiply alpha by
  190. -- @treturn color out
  191. function color.opacity(c, v)
  192. local t = color.new()
  193. for i = 1, 3 do
  194. t[i] = c[i]
  195. end
  196. t[4] = c[4] * v
  197. return t
  198. end
  199. --- Set a color's hue (saturation, value, alpha unchanged)
  200. -- @tparam color to alter
  201. -- @tparam hue to set 0-359
  202. -- @treturn color out
  203. function color.hue(col, hue)
  204. local c = color_to_hsv(col)
  205. c[1] = (hue + 360) % 360
  206. return hsv_to_color(c)
  207. end
  208. --- Set a color's saturation (hue, value, alpha unchanged)
  209. -- @tparam color to alter
  210. -- @tparam hue to set 0-359
  211. -- @treturn color out
  212. function color.saturation(col, percent)
  213. local c = color_to_hsv(col)
  214. c[2] = utils.clamp(percent, 0, 1)
  215. return hsv_to_color(c)
  216. end
  217. --- Set a color's value (saturation, hue, alpha unchanged)
  218. -- @tparam color to alter
  219. -- @tparam hue to set 0-359
  220. -- @treturn color out
  221. function color.value(col, percent)
  222. local c = color_to_hsv(col)
  223. c[3] = utils.clamp(percent, 0, 1)
  224. return hsv_to_color(c)
  225. end
  226. -- http://en.wikipedia.org/wiki/SRGB#The_reverse_transformation
  227. function color.gamma_to_linear(r, g, b, a)
  228. local function convert(c)
  229. if c > 1.0 then
  230. return 1.0
  231. elseif c < 0.0 then
  232. return 0.0
  233. elseif c <= 0.04045 then
  234. return c / 12.92
  235. else
  236. return math.pow((c + 0.055) / 1.055, 2.4)
  237. end
  238. end
  239. if type(r) == "table" then
  240. local c = {}
  241. for i = 1, 3 do
  242. c[i] = convert(r[i] / 255) * 255
  243. end
  244. c[4] = convert(r[4] / 255) * 255
  245. return c
  246. else
  247. return convert(r / 255) * 255, convert(g / 255) * 255, convert(b / 255) * 255, a or 255
  248. end
  249. end
  250. -- http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29
  251. function color.linear_to_gamma(r, g, b, a)
  252. local function convert(c)
  253. if c > 1.0 then
  254. return 1.0
  255. elseif c < 0.0 then
  256. return 0.0
  257. elseif c < 0.0031308 then
  258. return c * 12.92
  259. else
  260. return 1.055 * math.pow(c, 0.41666) - 0.055
  261. end
  262. end
  263. if type(r) == "table" then
  264. local c = {}
  265. for i = 1, 3 do
  266. c[i] = convert(r[i] / 255) * 255
  267. end
  268. c[4] = convert(r[4] / 255) * 255
  269. return c
  270. else
  271. return convert(r / 255) * 255, convert(g / 255) * 255, convert(b / 255) * 255, a or 255
  272. end
  273. end
  274. --- Check if color is valid
  275. -- @tparam color to test
  276. -- @treturn boolean is color
  277. function color.is_color(a)
  278. if type(a) ~= "table" then
  279. return false
  280. end
  281. for i = 1, 4 do
  282. if type(a[i]) ~= "number" then
  283. return false
  284. end
  285. end
  286. return true
  287. end
  288. --- Return a formatted string.
  289. -- @tparam color a color to be turned into a string
  290. -- @treturn string formatted
  291. function color.to_string(a)
  292. return string.format("[ %3.0f, %3.0f, %3.0f, %3.0f ]", a[1], a[2], a[3], a[4])
  293. end
  294. function color_mt.__index(t, k)
  295. if type(t) == "cdata" then
  296. if type(k) == "number" then
  297. return t._c[k-1]
  298. end
  299. end
  300. return rawget(color, k)
  301. end
  302. function color_mt.__newindex(t, k, v)
  303. if type(t) == "cdata" then
  304. if type(k) == "number" then
  305. t._c[k-1] = v
  306. end
  307. end
  308. end
  309. color_mt.__tostring = color.to_string
  310. function color_mt.__call(_, r, g, b, a)
  311. return color.new(r, g, b, a)
  312. end
  313. function color_mt.__add(a, b)
  314. return new(a[1] + b[1], a[2] + b[2], a[3] + b[3], a[4] + b[4])
  315. end
  316. function color_mt.__sub(a, b)
  317. return new(a[1] - b[1], a[2] - b[2], a[3] - b[3], a[4] - b[4])
  318. end
  319. function color_mt.__mul(a, b)
  320. if type(a) == "number" then
  321. return new(a * b[1], a * b[2], a * b[3], a * b[4])
  322. elseif type(b) == "number" then
  323. return new(b * a[1], b * a[2], b * a[3], b * a[4])
  324. else
  325. return new(a[1] * b[1], a[2] * b[2], a[3] * b[3], a[4] * b[4])
  326. end
  327. end
  328. return setmetatable({}, color_mt)