Documentation for this module may be created at Module:Color contrast/doc

  1. --
  2. -- This module implements
  3. -- {{Color contrast ratio}}
  4. -- {{Greater color contrast ratio}}
  5. -- {{ColorToLum}}
  6. -- {{RGBColorToLum}}
  7. --
  8. local p = {}
  9. local HTMLcolor = mw.loadData( 'Module:Color contrast/colors' )
  10.  
  11. local function sRGB ( v )
  12. if (v <= 0.03928) then
  13. v = v / 12.92
  14. else
  15. v = math.pow((v+0.055)/1.055, 2.4)
  16. end
  17. return v
  18. end
  19.  
  20. local function rgbdec2lum( R, G, B )
  21. if ( 0 <= R and R < 256 and 0 <= G and G < 256 and 0 <= B and B < 256 ) then
  22. return 0.2126 * sRGB(R/255) + 0.7152 * sRGB(G/255) + 0.0722 * sRGB(B/255)
  23. else
  24. return ''
  25. end
  26. end
  27.  
  28. local function hsl2lum( h, s, l )
  29. if ( 0 <= h and h < 360 and 0 <= s and s <= 1 and 0 <= l and l <= 1 ) then
  30. local c = (1 - math.abs(2*l - 1))*s
  31. local x = c*(1 - math.abs( math.fmod(h/60, 2) - 1) )
  32. local m = l - c/2
  33.  
  34. local r, g, b = m, m, m
  35. if( 0 <= h and h < 60 ) then
  36. r = r + c
  37. g = g + x
  38. elseif( 60 <= h and h < 120 ) then
  39. r = r + x
  40. g = g + c
  41. elseif( 120 <= h and h < 180 ) then
  42. g = g + c
  43. b = b + x
  44. elseif( 180 <= h and h < 240 ) then
  45. g = g + x
  46. b = b + c
  47. elseif( 240 <= h and h < 300 ) then
  48. r = r + x
  49. b = b + c
  50. elseif( 300 <= h and h < 360 ) then
  51. r = r + c
  52. b = b + x
  53. end
  54. return rgbdec2lum(255*r, 255*g, 255*b)
  55. else
  56. return ''
  57. end
  58. end
  59.  
  60. local function color2lum( c )
  61.  
  62. if (c == nil) then
  63. return ''
  64. end
  65. -- whitespace
  66. c = c:match( '^%s*(.-)[%s;]*$' )
  67.  
  68. -- lowercase
  69. c = c:lower()
  70.  
  71. -- first try to look it up
  72. local L = HTMLcolor[c]
  73. if (L ~= nil) then
  74. return L
  75. end
  76.  
  77. -- convert from hsl
  78. if mw.ustring.match(c,'^hsl%([%s]*[0-9][0-9%.]*[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then
  79. local h, s, l = mw.ustring.match(c,'^hsl%([%s]*([0-9][0-9%.]*)[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')
  80. return hsl2lum(tonumber(h), tonumber(s)/100, tonumber(l)/100)
  81. end
  82.  
  83. -- convert from rgb
  84. if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*%)$') then
  85. local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*%)$')
  86. return rgbdec2lum(tonumber(R), tonumber(G), tonumber(B))
  87. end
  88.  
  89. -- convert from rgb percent
  90. if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then
  91. local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')
  92. return rgbdec2lum(255*tonumber(R)/100, 255*tonumber(G)/100, 255*tonumber(B)/100)
  93. end
  94.  
  95. -- remove leading # (if there is one) and whitespace
  96. c = mw.ustring.match(c, '^[%s#]*([a-f0-9]*)[%s]*$')
  97.  
  98. -- split into rgb
  99. local cs = mw.text.split(c or '', '')
  100. if( #cs == 6 ) then
  101. local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[2])
  102. local G = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[4])
  103. local B = 16*tonumber('0x' .. cs[5]) + tonumber('0x' .. cs[6])
  104.  
  105. return rgbdec2lum(R, G, B)
  106. elseif ( #cs == 3 ) then
  107. local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[1])
  108. local G = 16*tonumber('0x' .. cs[2]) + tonumber('0x' .. cs[2])
  109. local B = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[3])
  110.  
  111. return rgbdec2lum(R, G, B)
  112. end
  113.  
  114. -- failure, return blank
  115. return ''
  116. end
  117.  
  118. function p._greatercontrast(args)
  119. local bias = tonumber(args['bias'] or '0') or 0
  120. local v1 = color2lum(args[1] or '')
  121. local c2 = args[2] or '#FFFFFF'
  122. local v2 = color2lum(c2)
  123. local c3 = args[3] or '#000000'
  124. local v3 = color2lum(c3)
  125. local ratio1 = 0;
  126. local ratio2 = 0;
  127. if (type(v1) == 'number' and type(v2) == 'number') then
  128. ratio1 = (v2 + 0.05)/(v1 + 0.05)
  129. ratio1 = (ratio1 < 1) and 1/ratio1 or ratio1
  130. end
  131. if (type(v1) == 'number' and type(v3) == 'number') then
  132. ratio2 = (v3 + 0.05)/(v1 + 0.05)
  133. ratio2 = (ratio2 < 1) and 1/ratio2 or ratio2
  134. end
  135. return (ratio1 + bias > ratio2) and c2 or c3
  136. end
  137.  
  138. function p._ratio(args)
  139. local v1 = color2lum(args[1])
  140. local v2 = color2lum(args[2])
  141. if (type(v1) == 'number' and type(v2) == 'number') then
  142. -- v1 should be the brighter of the two.
  143. if v2 > v1 then
  144. v1, v2 = v2, v1
  145. end
  146. return (v1 + 0.05)/(v2 + 0.05)
  147. else
  148. return args['error'] or '?'
  149. end
  150. end
  151.  
  152. function p._styleratio(args)
  153. local style = (args[1] or ''):lower()
  154. local bg, fg = 'white', 'black'
  155. local lum_bg, lum_fg = 1, 0
  156.  
  157. if args[2] then
  158. local lum = color2lum(args[2])
  159. if lum ~= '' then bg, lum_bg = args[2], lum end
  160. end
  161. if args[3] then
  162. local lum = color2lum(args[3])
  163. if lum ~= '' then fg, lum_fg = args[3], lum end
  164. end
  165.  
  166. local slist = mw.text.split(style or '', ';')
  167. for k = 1,#slist do
  168. s = slist[k]
  169. local k,v = s:match( '^[%s]*([^:]-):([^:]-)[%s;]*$' )
  170. k = k or ''
  171. v = v or ''
  172. if (k:match('^[%s]*(background)[%s]*$') or k:match('^[%s]*(background%-color)[%s]*$')) then
  173. local lum = color2lum(v)
  174. if( lum ~= '' ) then bg, lum_bg = v, lum end
  175. elseif (k:match('^[%s]*(color)[%s]*$')) then
  176. local lum = color2lum(v)
  177. if( lum ~= '' ) then bg, lum_fg = v, lum end
  178. end
  179. end
  180. if lum_bg > lum_fg then
  181. return (lum_bg + 0.05)/(lum_fg + 0.05)
  182. else
  183. return (lum_fg + 0.05)/(lum_bg + 0.05)
  184. end
  185. end
  186.  
  187. function p.lum(frame)
  188. return color2lum(frame.args[1] or frame:getParent().args[1])
  189. end
  190.  
  191. function p.ratio(frame)
  192. local args = frame.args[1] and frame.args or frame:getParent().args
  193. return p._ratio(args)
  194. end
  195.  
  196. function p.styleratio(frame)
  197. local args = frame.args[1] and frame.args or frame:getParent().args
  198. return p._styleratio(args)
  199. end
  200.  
  201. function p.greatercontrast(frame)
  202. local args = frame.args[1] and frame.args or frame:getParent().args
  203. return p._greatercontrast(args)
  204. end
  205.  
  206. return p