置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

Module:Hct/ColorUtils

贴贴♀百科,万娘皆可贴的百科全书!转载请标注来源页面的网页链接,并声明引自贴贴百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [创建] [刷新]
  1. --[[
  2. Copyright 2021 Google LLC
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ]]
  13. --[[
  14. This file has been modified. The original version is at
  15. https://github.com/material-foundation/material-color-utilities
  16. ]]
  17. local bit32 = require('bit32');
  18. local mathUtils = require('Module:Hct/MathUtils');
  19. --[[
  20. Color science utilities.
  21. Utility methods for color science constants and color space conversions that aren't HCT or
  22. CAM16.
  23. ]]
  24. local ColorUtils = {
  25. SRGB_TO_XYZ = {
  26. {0.41233895, 0.35762064, 0.18051042},
  27. {0.2126, 0.7152, 0.0722},
  28. {0.01932141, 0.11916382, 0.95034478},
  29. },
  30. XYZ_TO_SRGB = {
  31. {3.2413774792388685, -1.5376652402851851, -0.49885366846268053},
  32. {-0.9691452513005321, 1.8758853451067872, 0.04156585616912061},
  33. {0.05562093689691305, -0.20395524564742123, 1.0571799111220335},
  34. },
  35. WHITE_POINT_D65 = {95.047, 100.0, 108.883},
  36. };
  37. --[[ Converts a color from RGB components to ARGB format. ]]
  38. function ColorUtils.argbFromRgb(red, green, blue)
  39. return bit32.bor(
  40. 0xff000000,
  41. bit32.lshift(bit32.band(red, 255), 16),
  42. bit32.lshift(bit32.band(green, 255), 8),
  43. bit32.band(blue, 255));
  44. end
  45. --[[ Converts a color from linear RGB components to ARGB format. ]]
  46. function ColorUtils.argbFromLinrgb(linrgb)
  47. local r = ColorUtils.delinearized(linrgb[1]);
  48. local g = ColorUtils.delinearized(linrgb[2]);
  49. local b = ColorUtils.delinearized(linrgb[3]);
  50. return ColorUtils.argbFromRgb(r, g, b);
  51. end
  52. --[[ Returns the alpha component of a color in ARGB format. ]]
  53. function ColorUtils.alphaFromArgb(argb)
  54. return bit32.band(bit32.rshift(argb, 24), 255);
  55. end
  56. --[[ Returns the red component of a color in ARGB format. ]]
  57. function ColorUtils.redFromArgb(argb)
  58. return bit32.band(bit32.rshift(argb, 16), 255);
  59. end
  60. --[[ Returns the green component of a color in ARGB format. ]]
  61. function ColorUtils.greenFromArgb(argb)
  62. return bit32.band(bit32.rshift(argb, 8), 255);
  63. end
  64. --[[ Returns the blue component of a color in ARGB format. ]]
  65. function ColorUtils.blueFromArgb(argb)
  66. return bit32.band(argb, 255);
  67. end
  68. --[[ Returns whether a color in ARGB format is opaque. ]]
  69. function ColorUtils.isOpaque(argb)
  70. return ColorUtils.alphaFromArgb(argb) >= 255;
  71. end
  72. --[[ Converts a color from ARGB to XYZ. ]]
  73. function ColorUtils.argbFromXyz(x, y, z)
  74. local matrix = ColorUtils.XYZ_TO_SRGB;
  75. local linearR = matrix[1][1] * x + matrix[1][2] * y + matrix[1][3] * z;
  76. local linearG = matrix[2][1] * x + matrix[2][2] * y + matrix[2][3] * z;
  77. local linearB = matrix[3][1] * x + matrix[3][2] * y + matrix[3][3] * z;
  78. local r = ColorUtils.delinearized(linearR);
  79. local g = ColorUtils.delinearized(linearG);
  80. local b = ColorUtils.delinearized(linearB);
  81. return ColorUtils.argbFromRgb(r, g, b);
  82. end
  83. --[[ Converts a color from XYZ to ARGB. ]]
  84. function ColorUtils.xyzFromArgb(argb)
  85. local r = ColorUtils.linearized(ColorUtils.redFromArgb(argb));
  86. local g = ColorUtils.linearized(ColorUtils.greenFromArgb(argb));
  87. local b = ColorUtils.linearized(ColorUtils.blueFromArgb(argb));
  88. return mathUtils.matrixMultiply({r, g, b}, ColorUtils.SRGB_TO_XYZ);
  89. end
  90. --[[ Converts a color represented in Lab color space into an ARGB integer. ]]
  91. function ColorUtils.argbFromLab(l, a, b)
  92. local whitePoint = ColorUtils.WHITE_POINT_D65;
  93. local fy = (l + 16.0) / 116.0;
  94. local fx = a / 500.0 + fy;
  95. local fz = fy - b / 200.0;
  96. local xNormalized = ColorUtils.labInvf(fx);
  97. local yNormalized = ColorUtils.labInvf(fy);
  98. local zNormalized = ColorUtils.labInvf(fz);
  99. local x = xNormalized * whitePoint[1];
  100. local y = yNormalized * whitePoint[2];
  101. local z = zNormalized * whitePoint[3];
  102. return ColorUtils.argbFromXyz(x, y, z);
  103. end
  104. --[[
  105. Converts a color from ARGB representation to L*a*b* representation.
  106. @param argb the ARGB representation of a color
  107. @return a Lab object representing the color
  108. ]]
  109. function ColorUtils.labFromArgb(argb)
  110. local linearR = ColorUtils.linearized(ColorUtils.redFromArgb(argb));
  111. local linearG = ColorUtils.linearized(ColorUtils.greenFromArgb(argb));
  112. local linearB = ColorUtils.linearized(ColorUtils.blueFromArgb(argb));
  113. local matrix = ColorUtils.SRGB_TO_XYZ;
  114. local x = matrix[1][1] * linearR + matrix[1][2] * linearG + matrix[1][3] * linearB;
  115. local y = matrix[2][1] * linearR + matrix[2][2] * linearG + matrix[2][3] * linearB;
  116. local z = matrix[3][1] * linearR + matrix[3][2] * linearG + matrix[3][3] * linearB;
  117. local whitePoint = ColorUtils.WHITE_POINT_D65;
  118. local xNormalized = x / whitePoint[1];
  119. local yNormalized = y / whitePoint[2];
  120. local zNormalized = z / whitePoint[3];
  121. local fx = ColorUtils.labF(xNormalized);
  122. local fy = ColorUtils.labF(yNormalized);
  123. local fz = ColorUtils.labF(zNormalized);
  124. local l = 116.0 * fy - 16;
  125. local a = 500.0 * (fx - fy);
  126. local b = 200.0 * (fy - fz);
  127. return {l, a, b};
  128. end
  129. --[[
  130. Converts an L* value to an ARGB representation.
  131. @param lstar L* in L*a*b*
  132. @return ARGB representation of grayscale color with lightness matching L*
  133. ]]
  134. function ColorUtils.argbFromLstar(lstar)
  135. local y = ColorUtils.yFromLstar(lstar);
  136. local component = ColorUtils.delinearized(y);
  137. return ColorUtils.argbFromRgb(component, component, component);
  138. end
  139. --[[
  140. Computes the L* value of a color in ARGB representation.
  141. @param argb ARGB representation of a color
  142. @return L*, from L*a*b*, coordinate of the color
  143. ]]
  144. function ColorUtils.lstarFromArgb(argb)
  145. local y = ColorUtils.xyzFromArgb(argb)[2];
  146. return 116.0 * ColorUtils.labF(y / 100.0) - 16.0;
  147. end
  148. --[[
  149. Converts an L* value to a Y value.
  150. L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
  151. L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a
  152. logarithmic scale.
  153. @param lstar L* in L*a*b*
  154. @return Y in XYZ
  155. ]]
  156. function ColorUtils.yFromLstar(lstar)
  157. return 100.0 * ColorUtils.labInvf((lstar + 16.0) / 116.0);
  158. end
  159. --[[
  160. Linearizes an RGB component.
  161. @param rgbComponent 0 <= rgb_component <= 255, represents R/G/B channel
  162. @return 0.0 <= output <= 100.0, color channel converted to linear RGB space
  163. ]]
  164. function ColorUtils.linearized(rgbComponent)
  165. local normalized = rgbComponent / 255.0;
  166. if normalized <= 0.040449936 then
  167. return normalized / 12.92 * 100.0;
  168. else
  169. return math.pow((normalized + 0.055) / 1.055, 2.4) * 100.0;
  170. end
  171. end
  172. --[[
  173. Delinearizes an RGB component.
  174. @param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel
  175. @return 0 <= output <= 255, color channel converted to regular RGB space
  176. ]]
  177. function ColorUtils.delinearized(rgbComponent)
  178. local normalized = rgbComponent / 100.0;
  179. local delinearized = 0.0;
  180. if normalized <= 0.0031308 then
  181. delinearized = normalized * 12.92;
  182. else
  183. delinearized = 1.055 * math.pow(normalized, 1.0 / 2.4) - 0.055;
  184. end
  185. return mathUtils.clampInt(0, 255, math.floor(delinearized * 255.0 + 0.5));
  186. end
  187. --[[
  188. Returns the standard white point; white on a sunny day.
  189. @return The white point
  190. ]]
  191. function ColorUtils.whitePointD65()
  192. return ColorUtils.WHITE_POINT_D65;
  193. end
  194. function ColorUtils.labF(t)
  195. local e = 216.0 / 24389.0;
  196. local kappa = 24389.0 / 27.0;
  197. if t > e then
  198. return math.pow(t, 1.0 / 3.0);
  199. else
  200. return (kappa * t + 16) / 116;
  201. end
  202. end
  203. function ColorUtils.labInvf(ft)
  204. local e = 216.0 / 24389.0;
  205. local kappa = 24389.0 / 27.0;
  206. local ft3 = ft * ft * ft;
  207. if ft3 > e then
  208. return ft3;
  209. else
  210. return (116 * ft - 16) / kappa;
  211. end
  212. end
  213. return ColorUtils;