Module:Hct/ColorUtils
跳转到导航
跳转到搜索
此模块的文档可以在Module:Hct/ColorUtils/doc创建
--[[
Copyright 2021 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]
--[[
This file has been modified. The original version is at
https://github.com/material-foundation/material-color-utilities
]]
local bit32 = require('bit32');
local mathUtils = require('Module:Hct/MathUtils');
--[[
Color science utilities.
Utility methods for color science constants and color space conversions that aren't HCT or
CAM16.
]]
local ColorUtils = {
SRGB_TO_XYZ = {
{0.41233895, 0.35762064, 0.18051042},
{0.2126, 0.7152, 0.0722},
{0.01932141, 0.11916382, 0.95034478},
},
XYZ_TO_SRGB = {
{3.2413774792388685, -1.5376652402851851, -0.49885366846268053},
{-0.9691452513005321, 1.8758853451067872, 0.04156585616912061},
{0.05562093689691305, -0.20395524564742123, 1.0571799111220335},
},
WHITE_POINT_D65 = {95.047, 100.0, 108.883},
};
--[[ Converts a color from RGB components to ARGB format. ]]
function ColorUtils.argbFromRgb(red, green, blue)
return bit32.bor(
0xff000000,
bit32.lshift(bit32.band(red, 255), 16),
bit32.lshift(bit32.band(green, 255), 8),
bit32.band(blue, 255));
end
--[[ Converts a color from linear RGB components to ARGB format. ]]
function ColorUtils.argbFromLinrgb(linrgb)
local r = ColorUtils.delinearized(linrgb[1]);
local g = ColorUtils.delinearized(linrgb[2]);
local b = ColorUtils.delinearized(linrgb[3]);
return ColorUtils.argbFromRgb(r, g, b);
end
--[[ Returns the alpha component of a color in ARGB format. ]]
function ColorUtils.alphaFromArgb(argb)
return bit32.band(bit32.rshift(argb, 24), 255);
end
--[[ Returns the red component of a color in ARGB format. ]]
function ColorUtils.redFromArgb(argb)
return bit32.band(bit32.rshift(argb, 16), 255);
end
--[[ Returns the green component of a color in ARGB format. ]]
function ColorUtils.greenFromArgb(argb)
return bit32.band(bit32.rshift(argb, 8), 255);
end
--[[ Returns the blue component of a color in ARGB format. ]]
function ColorUtils.blueFromArgb(argb)
return bit32.band(argb, 255);
end
--[[ Returns whether a color in ARGB format is opaque. ]]
function ColorUtils.isOpaque(argb)
return ColorUtils.alphaFromArgb(argb) >= 255;
end
--[[ Converts a color from ARGB to XYZ. ]]
function ColorUtils.argbFromXyz(x, y, z)
local matrix = ColorUtils.XYZ_TO_SRGB;
local linearR = matrix[1][1] * x + matrix[1][2] * y + matrix[1][3] * z;
local linearG = matrix[2][1] * x + matrix[2][2] * y + matrix[2][3] * z;
local linearB = matrix[3][1] * x + matrix[3][2] * y + matrix[3][3] * z;
local r = ColorUtils.delinearized(linearR);
local g = ColorUtils.delinearized(linearG);
local b = ColorUtils.delinearized(linearB);
return ColorUtils.argbFromRgb(r, g, b);
end
--[[ Converts a color from XYZ to ARGB. ]]
function ColorUtils.xyzFromArgb(argb)
local r = ColorUtils.linearized(ColorUtils.redFromArgb(argb));
local g = ColorUtils.linearized(ColorUtils.greenFromArgb(argb));
local b = ColorUtils.linearized(ColorUtils.blueFromArgb(argb));
return mathUtils.matrixMultiply({r, g, b}, ColorUtils.SRGB_TO_XYZ);
end
--[[ Converts a color represented in Lab color space into an ARGB integer. ]]
function ColorUtils.argbFromLab(l, a, b)
local whitePoint = ColorUtils.WHITE_POINT_D65;
local fy = (l + 16.0) / 116.0;
local fx = a / 500.0 + fy;
local fz = fy - b / 200.0;
local xNormalized = ColorUtils.labInvf(fx);
local yNormalized = ColorUtils.labInvf(fy);
local zNormalized = ColorUtils.labInvf(fz);
local x = xNormalized * whitePoint[1];
local y = yNormalized * whitePoint[2];
local z = zNormalized * whitePoint[3];
return ColorUtils.argbFromXyz(x, y, z);
end
--[[
Converts a color from ARGB representation to L*a*b* representation.
@param argb the ARGB representation of a color
@return a Lab object representing the color
]]
function ColorUtils.labFromArgb(argb)
local linearR = ColorUtils.linearized(ColorUtils.redFromArgb(argb));
local linearG = ColorUtils.linearized(ColorUtils.greenFromArgb(argb));
local linearB = ColorUtils.linearized(ColorUtils.blueFromArgb(argb));
local matrix = ColorUtils.SRGB_TO_XYZ;
local x = matrix[1][1] * linearR + matrix[1][2] * linearG + matrix[1][3] * linearB;
local y = matrix[2][1] * linearR + matrix[2][2] * linearG + matrix[2][3] * linearB;
local z = matrix[3][1] * linearR + matrix[3][2] * linearG + matrix[3][3] * linearB;
local whitePoint = ColorUtils.WHITE_POINT_D65;
local xNormalized = x / whitePoint[1];
local yNormalized = y / whitePoint[2];
local zNormalized = z / whitePoint[3];
local fx = ColorUtils.labF(xNormalized);
local fy = ColorUtils.labF(yNormalized);
local fz = ColorUtils.labF(zNormalized);
local l = 116.0 * fy - 16;
local a = 500.0 * (fx - fy);
local b = 200.0 * (fy - fz);
return {l, a, b};
end
--[[
Converts an L* value to an ARGB representation.
@param lstar L* in L*a*b*
@return ARGB representation of grayscale color with lightness matching L*
]]
function ColorUtils.argbFromLstar(lstar)
local y = ColorUtils.yFromLstar(lstar);
local component = ColorUtils.delinearized(y);
return ColorUtils.argbFromRgb(component, component, component);
end
--[[
Computes the L* value of a color in ARGB representation.
@param argb ARGB representation of a color
@return L*, from L*a*b*, coordinate of the color
]]
function ColorUtils.lstarFromArgb(argb)
local y = ColorUtils.xyzFromArgb(argb)[2];
return 116.0 * ColorUtils.labF(y / 100.0) - 16.0;
end
--[[
Converts an L* value to a Y value.
L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a
logarithmic scale.
@param lstar L* in L*a*b*
@return Y in XYZ
]]
function ColorUtils.yFromLstar(lstar)
return 100.0 * ColorUtils.labInvf((lstar + 16.0) / 116.0);
end
--[[
Linearizes an RGB component.
@param rgbComponent 0 <= rgb_component <= 255, represents R/G/B channel
@return 0.0 <= output <= 100.0, color channel converted to linear RGB space
]]
function ColorUtils.linearized(rgbComponent)
local normalized = rgbComponent / 255.0;
if normalized <= 0.040449936 then
return normalized / 12.92 * 100.0;
else
return math.pow((normalized + 0.055) / 1.055, 2.4) * 100.0;
end
end
--[[
Delinearizes an RGB component.
@param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel
@return 0 <= output <= 255, color channel converted to regular RGB space
]]
function ColorUtils.delinearized(rgbComponent)
local normalized = rgbComponent / 100.0;
local delinearized = 0.0;
if normalized <= 0.0031308 then
delinearized = normalized * 12.92;
else
delinearized = 1.055 * math.pow(normalized, 1.0 / 2.4) - 0.055;
end
return mathUtils.clampInt(0, 255, math.floor(delinearized * 255.0 + 0.5));
end
--[[
Returns the standard white point; white on a sunny day.
@return The white point
]]
function ColorUtils.whitePointD65()
return ColorUtils.WHITE_POINT_D65;
end
function ColorUtils.labF(t)
local e = 216.0 / 24389.0;
local kappa = 24389.0 / 27.0;
if t > e then
return math.pow(t, 1.0 / 3.0);
else
return (kappa * t + 16) / 116;
end
end
function ColorUtils.labInvf(ft)
local e = 216.0 / 24389.0;
local kappa = 24389.0 / 27.0;
local ft3 = ft * ft * ft;
if ft3 > e then
return ft3;
else
return (116 * ft - 16) / kappa;
end
end
return ColorUtils;