Module:Hct/ColorUtils
跳到导航
跳到搜索
- --[[
- 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;