Module:Color
Jump to navigation
Jump to search
| Module Documentation [edit] |
|---|
|
A utility module for dealing with colors. Usagelocal color = require('Module:Color')
local p = {}
function p.main(frame)
local newColor = color.darken(frame.args[1])
-- Main module code goes here.
end
return p
Hex ConversionConvert RGB values to hex. {{#invoke:Color|toHex|255|255|255}}
DarkenDarken the color. {{#invoke:Color|darken|#ffffff}}
LightenLighten the color. {{#invoke:Color|lighten|#000000}}
AtReturns color at a given Bootstrap/Tailwind-like brightness level, where 0 is the brightest and 1000 is the darkest. {{#invoke:Color|at|#ffffff|500}}
InvertInvert the brightness level. {{#invoke:Color|invert|#ffffff}}
|
-- ------------------------------------------------------------------
-- Main routine
--
local function rgbFromHex(hex)
hex = hex:gsub("#", "")
local r = tonumber(hex:sub(1,2), 16) / 255
local g = tonumber(hex:sub(3,4), 16) / 255
local b = tonumber(hex:sub(5,6), 16) / 255
return r, g, b
end
local function hexFromRGB(r, g, b)
r = math.floor(r * 255)
g = math.floor(g * 255)
b = math.floor(b * 255)
return string.format("#%02X%02X%02X", r, g, b)
end
local function clamp(val, min, max)
return math.max(min, math.min(max, val))
end
-- ------------------------------------------------------------------
-- HSL routine
--
local function hslFromRGB(r, g, b)
local max = math.max(r, g, b)
local min = math.min(r, g, b)
local h, s, l
l = (max + min) / 2
if max == min then
h = 0
s = 0
else
local d = max - min
s = l > 0.5 and d / (2 - max - min) or d / (max + min)
if max == r then
h = (g - b) / d + (g < b and 6 or 0)
elseif max == g then
h = (b - r) / d + 2
else
h = (r - g) / d + 4
end
h = h / 6
end
return h * 360, s * 100, l * 100
end
local function rgbFromHSL(h, s, l)
local h = h / 360
local s = s / 100
local l = l / 100
local function hue2rgb(p, q, t)
if t < 0 then t = t + 1 end
if t > 1 then t = t - 1 end
if t < 1/6 then return p + (q - p) * 6 * t end
if t < 1/2 then return q end
if t < 2/3 then return p + (q - p) * (2/3 - t) * 6 end
return p
end
local r, g, b
if s == 0 then
r, g, b = l, l, l
else
local q = l < 0.5 and l * (1 + s) or l + s - l * s
local p = 2 * l - q
r = hue2rgb(p, q, h + 1/3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1/3)
end
return r, g, b
end
-- ------------------------------------------------------------------
-- Oklab routine
-- Oklab constant taken from https://bottosson.github.io/posts/oklab/
--
local function oklabFromRGB(r, g, b)
local function srgbToLinear(c)
return (c > 0.04045) and (((c + 0.055) / 1.055) ^ 2.4) or (c / 12.92)
end
local lr = srgbToLinear(r)
local lg = srgbToLinear(g)
local lb = srgbToLinear(b)
local l = 0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb
local m = 0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb
local s = 0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb
local function cbrt(v)
return (v < 0) and -((-v) ^ (1 / 3)) or (v ^ (1 / 3))
end
local l_ = cbrt(l)
local m_ = cbrt(m)
local s_ = cbrt(s)
local L = 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_
local a = 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_
local b_val = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_
return L, a, b_val
end
local function rgbFromOklab(L, a, b_val)
local l_ = L + 0.3963377774 * a + 0.2158037573 * b_val
local m_ = L - 0.1055613458 * a - 0.0638541728 * b_val
local s_ = L - 0.0894841775 * a - 1.2914855480 * b_val
local l = l_ ^ 3
local m = m_ ^ 3
local s = s_ ^ 3
local lr = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s
local lg = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s
local lb = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s
local function linearToSrgb(c)
c = clamp(c, 0, 1)
local companded = (c > 0.0031308) and (1.055 * (c ^ (1 / 2.4)) - 0.055) or (12.92 * c)
return clamp(companded, 0, 1)
end
return linearToSrgb(lr), linearToSrgb(lg), linearToSrgb(lb)
end
-- ------------------------------------------------------------------
-- Utility
local function lighten(hex, intensity)
local r, g, b = rgbFromHex(hex)
local L, a, b_val = oklabFromRGB(r, g, b)
if intensity == nil then intensity = 8 end
intensity = clamp(intensity, 0, 100) / 100 -- 1-100
L = clamp(L + intensity, 0, 1)
r, g, b = rgbFromOklab(L, a, b_val)
return hexFromRGB(r, g, b)
end
local function darken(hex, intensity)
local r, g, b = rgbFromHex(hex)
local L, a, b_val = oklabFromRGB(r, g, b)
if intensity == nil then intensity = 8 end
intensity = clamp(intensity, 0, 100) / 100 -- 1-100
L = clamp(L - intensity, 0, 1)
r, g, b = rgbFromOklab(L, a, b_val)
return hexFromRGB(r, g, b)
end
-- Invert remains HSL since uniform color ramp with Oklab can cause
-- inverted colors to become too dark, e.g. #d6f2ff has a color 0.94
-- but inverse value of 0.06 is near black.
local function invert(hex)
local r, g, b = rgbFromHex(hex)
local h, s, l = hslFromRGB(r, g, b)
l = math.min(100, math.max(0, 100 - l))
r, g, b = rgbFromHSL(h, s, l)
return hexFromRGB(r, g, b)
end
local function colorAt(hex, brightness)
local r, g, b = rgbFromHex(hex)
local L, a, b_val = oklabFromRGB(r, g, b)
brightness = clamp(brightness, 0, 1000)
L = (1000 - brightness) / 1000
r, g, b = rgbFromOklab(L, a, b_val)
return hexFromRGB(r, g, b)
end
-- ------------------------------------------------------------------
-- MediaWiki
--
local getArgs = require('Module:Arguments').getArgs
local p = {}
-- Encode
function p.toHex(r, g, b)
return hexFromRGB(r / 255, g / 255, b / 255)
end
-- Color manipulation
function p.darken(...)
local args = getArgs(...)
return darken(unpack(args))
end
function p.lighten(...)
local args = getArgs(...)
return lighten(unpack(args))
end
function p.invert(...)
local args = getArgs(...)
return invert(unpack(args))
end
function p.at(...)
local args = getArgs(...)
return colorAt(unpack(args))
end
return p