Module:Hct/ColorUtils:修订间差异

来自Vocawiki
跳转到导航 跳转到搜索
无编辑摘要
 
星剑生留言 | 贡献
导入1个版本:​搬运自萌娘百科,依CC BY-NC-SA 3.0 CN导入
 
(没有差异)

2025年9月12日 (五) 13:45的最新版本

此模块的文档可以在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;