Jump to content
Toggle menu
  • 8 articles
  • 75 files
  • 7 users
  • 37.8K edits
Neodyland Wiki
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.
Revision as of 21:56, 10 January 2022 by w>Ftrebien
Module documentation[ view · edit · history · purge ]
This documentation is transcluded from Module:Color/doc.

This template is being used in the wrong namespace. Use {{Heavily used module}} instead! This module is used primarily by {{Infobox color}}, eliminating the need for external color converters and preventing mismatch between color coordinates.

Usage

To use this module, you may use one of the above listed templates or invoke the module directly. All functions that accept hexadecimal triplets also handle the shorthand three-digit format.

To convert a hexadecimal triplet to an RGB triplet as comma-separated values:

{{#invoke:Color|hexToRgbTriplet|color}}

To convert a hexadecimal triplet to the CMYK color model without a color profile (which is a very bad idea!):

{{#invoke:Color|hexToCmyk|color|precision=?|pctsign=?}}

To convert a hexadecimal triplet to HSL or HSV:

{{#invoke:Color|hexToHsl|color|precision=?}}
{{#invoke:Color|hexToHsv|color|precision=?}}

To convert a hexadecimal triplet to the perceptual CIELChuv color space:

{{#invoke:Color|hexToCielch|color|precision=?}}

To mix two colors in the more physically correct linear RGB space:

{{#invoke:Color|hexMix|color1|color2|proportion|min=?|max=?}}

To convert an RGB triplet to a hex code:

{{#invoke:Color|rgbTripletToHex|r|g|b}}

The following parameters are optional:

  • precision: defaults to 0 (zero)
  • pctsign: set to 0 (zero) to suppress percent signs in the generated output
  • proportion: proportion of color2, defaults to 50
  • min: minimum value of proportion range, defaults to 0
  • max: maximum value of proportion range, defaults to 100
local p = {}

local function hexToRgb(color)
	if (#color == 6) then
		return {
			r = tonumber(string.sub(color, 1, 2), 16),
			g = tonumber(string.sub(color, 3, 4), 16),
			b = tonumber(string.sub(color, 5, 6), 16)
		}
	elseif (#color == 3) then
		return {
			r = 17 * tonumber(string.sub(color, 1, 1), 16),
			g = 17 * tonumber(string.sub(color, 2, 2), 16),
			b = 17 * tonumber(string.sub(color, 3, 3), 16)
		}
	end
	error("Invalid hexadecimal color " .. color, 1)
end

local function rgbToHsl(r, g, b)
	if (r > 255 or g > 255 or b > 255 or r < 0 or g < 0 or b < 0) then
		error("Color level out of bounds")
	end
	channelMax = math.max(r, g, b)
	channelMin = math.min(r, g, b)
	range = channelMax - channelMin
	if (range == 0) then
		h = 0
	elseif (channelMax == r) then
		h = 60 * ((g - b) / range)
	elseif (channelMax == g) then
		h = 60 * (2 + (b - r) / range)
	else
		h = 60 * (4 + (r - g) / range)
	end
	if (h < 0) then
		h = 360 + h
	end
	L = channelMax + channelMin
	if (L == 0 or L == 510) then
		s = 0
	else
		s = 100 * range / math.min(L, 510 - L)
	end
	return { h = h, s = s, l = L * 50 / 255 }
end

local function rgbToHsv(r, g, b)
	if (r > 255 or g > 255 or b > 255 or r < 0 or g < 0 or b < 0) then
		error("Color level out of bounds")
	end
	channelMax = math.max(r, g, b)
	channelMin = math.min(r, g, b)
	range = channelMax - channelMin
	if (range == 0) then
		h = 0
	elseif (channelMax == r) then
		h = 60 * ((g - b) / range)
	elseif (channelMax == g) then
		h = 60 * (2 + (b - r) / range)
	else
		h = 60 * (4 + (r - g) / range)
	end
	if (h < 0) then
		h = 360 + h
	end
	if (channelMax == 0) then
		s = 0
	else
		s = 100 * range / channelMax
	end
	return { h = h, s = s, v = channelMax * 100 / 255 }
end

-- c in [0, 255], condition tweaked for no discontinuity
-- http://entropymine.com/imageworsener/srgbformula/
local function inverseCompanding(c)
	if (c > 10.314300250662591) then
		return math.pow((c + 14.025) / 269.025, 2.4)
	else
		return c / 3294.6
	end
end

local function srgbToCielchuvD65o2deg(r, g, b)
	if (r > 255 or g > 255 or b > 255 or r < 0 or g < 0 or b < 0) then
		error("Color level out of bounds")
	end
	R = inverseCompanding(r)
	G = inverseCompanding(g)
	B = inverseCompanding(b)
	-- https://github.com/w3c/csswg-drafts/issues/5922
	X = 0.1804807884018343 * B + 0.357584339383878 * G + 0.41239079926595934 * R
	Y = 0.07219231536073371 * B + 0.21263900587151027 * R + 0.715168678767756 * G
	Z = 0.01933081871559182 * R + 0.11919477979462598 * G + 0.9505321522496607 * B
	if (Y > 0.00885645167903563082) then
		L = 116 * math.pow(Y, 1/3) - 16
	else
		L = Y * 903.2962962962962962963
	end
	if (r == g and g == b) then
		C = 0
		h = 0
	else
		d = X + 3 * Z + 15 * Y
		if (d == 0) then
			C = 0
			h = 0
		else
			-- 0.19783... and 0.4631... computed with extra precision from (X,Y,Z) when (R,G,B) = (1,1,1),
			-- in which case (u,v) ≈ (0,0)
			u = 13 * L * (4 * X / d - 0.19783000664283678994)
			v = 13 * L * (9 * Y / d - 0.46831999493879099801)
			h = math.atan2(v, u) * 57.2957795130823208768
			if (h < 0) then
				h = h + 360
			elseif (h == 0) then
				h = 0 -- ensure zero is positive
			end
			C = math.sqrt(u * u + v * v)
			if (C == 0) then
				C = 0
				h = 0
			end
		end
	end
	return { L = L, C = C, h = h }
end

local function formatAngle(value, p)
	local vs = string.format("%." .. p .. "f", value)
	local zeros
	if (p > 0) then
		zeros = "." .. string.rep("0", p)
	else
		zeros = ""
	end
	if (vs == "360" .. zeros) then
		vs = "0" .. zeros -- handle rounding to 360
	end
	return vs .. "°"
end

function p.hexToRgbTriplet(frame)
	local args = frame.args or frame:getParent().args
	local hex = args[1]
	if (hex) then
		local rgb = hexToRgb(hex)
		return rgb.r .. ', ' .. rgb.g .. ', ' .. rgb.b
	else
		return ""
	end
end

function p.hexToHsl(frame)
	local args = frame.args or frame:getParent().args
	local hex = args[1]
	local p = tonumber(args.precision) or 0
	if (hex) then
		local rgb = hexToRgb(hex)
		local hsl = rgbToHsl(rgb.r, rgb.g, rgb.b)
		return formatAngle(hsl.h, p) .. string.format(", %." .. p .. "f%%, %." .. p .. "f%%", hsl.s, hsl.l)
	else
		return ""
	end
end

function p.hexToHsv(frame)
	local args = frame.args or frame:getParent().args
	local hex = args[1]
	local p = tonumber(args.precision) or 0
	if (hex) then
		local rgb = hexToRgb(hex)
		local hsv = rgbToHsv(rgb.r, rgb.g, rgb.b)
		return formatAngle(hsv.h, p) .. string.format(", %." .. p .. "f%%, %." .. p .. "f%%", hsv.s, hsv.v)
	else
		return ""
	end
end

function p.hexToCielch(frame)
	local args = frame.args or frame:getParent().args
	local hex = args[1]
	local p = tonumber(args.precision) or 0
	if (hex) then
		local rgb = hexToRgb(hex)
		local LCh = srgbToCielchuvD65o2deg(rgb.r, rgb.g, rgb.b)
		return string.format("%." .. p .. "f, %." .. p .. "f, ", LCh.L, LCh.C) .. formatAngle(LCh.h, p)
	else
		return ""
	end
end

return p
Contents
Cookies help us deliver our services. By using our services, you agree to our use of cookies.