@trishchuk/coolors-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/.claude/settings.local.json +39 -0
  2. package/.env +2 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +73 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +71 -0
  5. package/.github/pull_request_template.md +97 -0
  6. package/.github/workflows/ci.yml +127 -0
  7. package/.github/workflows/deploy-docs.yml +56 -0
  8. package/.github/workflows/release.yml +99 -0
  9. package/.mcp.json +12 -0
  10. package/.prettierignore +1 -0
  11. package/CLAUDE.md +201 -0
  12. package/DOCUMENTATION.md +274 -0
  13. package/GEMINI.md +54 -0
  14. package/LICENSE +21 -0
  15. package/README.md +401 -0
  16. package/demo/content_based_color.png +0 -0
  17. package/demo/music-player.html +621 -0
  18. package/demo/podcast-player.html +903 -0
  19. package/dist/bin/coolors-mcp.d.ts +1 -0
  20. package/dist/bin/coolors-mcp.js +154 -0
  21. package/dist/bin/coolors-mcp.js.map +1 -0
  22. package/dist/bin/server.d.ts +1 -0
  23. package/dist/bin/server.js +3292 -0
  24. package/dist/bin/server.js.map +1 -0
  25. package/dist/chunk-IQ7NN26V.js +114 -0
  26. package/dist/chunk-IQ7NN26V.js.map +1 -0
  27. package/dist/chunk-P3ARRKLS.js +1214 -0
  28. package/dist/chunk-P3ARRKLS.js.map +1 -0
  29. package/dist/color/index.d.ts +716 -0
  30. package/dist/color/index.js +153 -0
  31. package/dist/color/index.js.map +1 -0
  32. package/dist/coolors-mcp.d.ts +136 -0
  33. package/dist/coolors-mcp.js +7 -0
  34. package/dist/coolors-mcp.js.map +1 -0
  35. package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
  36. package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
  37. package/docs/.vitepress/cache/deps/_metadata.json +127 -0
  38. package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
  39. package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
  40. package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js +12683 -0
  41. package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js.map +7 -0
  42. package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js +9719 -0
  43. package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js.map +7 -0
  44. package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
  45. package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
  46. package/docs/.vitepress/cache/deps/cytoscape.js +30278 -0
  47. package/docs/.vitepress/cache/deps/cytoscape.js.map +7 -0
  48. package/docs/.vitepress/cache/deps/dayjs.js +285 -0
  49. package/docs/.vitepress/cache/deps/dayjs.js.map +7 -0
  50. package/docs/.vitepress/cache/deps/debug.js +468 -0
  51. package/docs/.vitepress/cache/deps/debug.js.map +7 -0
  52. package/docs/.vitepress/cache/deps/package.json +3 -0
  53. package/docs/.vitepress/cache/deps/prismjs.js +1466 -0
  54. package/docs/.vitepress/cache/deps/prismjs.js.map +7 -0
  55. package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js +228 -0
  56. package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js.map +7 -0
  57. package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js +142 -0
  58. package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js.map +7 -0
  59. package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js +27 -0
  60. package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js.map +7 -0
  61. package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js +65 -0
  62. package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js.map +7 -0
  63. package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js +53 -0
  64. package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js.map +7 -0
  65. package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js +73 -0
  66. package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js.map +7 -0
  67. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
  68. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  69. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
  70. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  71. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1146 -0
  72. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  73. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
  74. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  75. package/docs/.vitepress/cache/deps/vitepress___minisearch.js +1814 -0
  76. package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
  77. package/docs/.vitepress/cache/deps/vue.js +344 -0
  78. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  79. package/docs/.vitepress/components/ClientGrid.vue +125 -0
  80. package/docs/.vitepress/components/CodeBlock.vue +231 -0
  81. package/docs/.vitepress/components/ConfigModal.vue +477 -0
  82. package/docs/.vitepress/components/DiagramModal.vue +528 -0
  83. package/docs/.vitepress/components/TroubleshootingModal.vue +472 -0
  84. package/docs/.vitepress/config.js +162 -0
  85. package/docs/.vitepress/theme/FundingLayout.vue +251 -0
  86. package/docs/.vitepress/theme/Layout.vue +134 -0
  87. package/docs/.vitepress/theme/components/AdBanner.vue +317 -0
  88. package/docs/.vitepress/theme/components/AdPlaceholder.vue +78 -0
  89. package/docs/.vitepress/theme/components/FundingEffects.vue +322 -0
  90. package/docs/.vitepress/theme/components/FundingHero.vue +345 -0
  91. package/docs/.vitepress/theme/components/SupportSection.vue +511 -0
  92. package/docs/.vitepress/theme/custom-app.css +339 -0
  93. package/docs/.vitepress/theme/custom.css +699 -0
  94. package/docs/.vitepress/theme/index.js +25 -0
  95. package/docs/README.md +198 -0
  96. package/docs/concepts/accessibility.md +473 -0
  97. package/docs/concepts/color-spaces.md +222 -0
  98. package/docs/concepts/distance-metrics.md +384 -0
  99. package/docs/concepts/hct.md +261 -0
  100. package/docs/concepts/image-analysis.md +396 -0
  101. package/docs/concepts/material-design.md +306 -0
  102. package/docs/concepts/theme-matching.md +399 -0
  103. package/docs/examples/basic-colors.md +490 -0
  104. package/docs/examples/creating-themes.md +898 -0
  105. package/docs/examples/css-refactoring.md +824 -0
  106. package/docs/examples/image-extraction.md +882 -0
  107. package/docs/getting-started.md +366 -0
  108. package/docs/index.md +190 -0
  109. package/docs/installation.md +157 -0
  110. package/docs/tools/README.md +234 -0
  111. package/docs/tools/accessibility.md +614 -0
  112. package/docs/tools/color-operations.md +374 -0
  113. package/docs/tools/image-extraction.md +624 -0
  114. package/docs/tools/material-design.md +347 -0
  115. package/docs/tools/theme-matching.md +552 -0
  116. package/eslint.config.ts +14 -0
  117. package/examples/theme-matching.md +113 -0
  118. package/jsr.json +7 -0
  119. package/mcp-config.json +8 -0
  120. package/note.md +35 -0
  121. package/package.json +122 -0
  122. package/research_results.md +53 -0
  123. package/src/bin/coolors-mcp.ts +194 -0
  124. package/src/bin/server.ts +61 -0
  125. package/src/color/__tests__/conversions-argb.test.ts +198 -0
  126. package/src/color/__tests__/extract-colors.test.ts +360 -0
  127. package/src/color/__tests__/image-utils.test.ts +242 -0
  128. package/src/color/__tests__/reference-colors.test.ts +278 -0
  129. package/src/color/__tests__/round-trip.test.ts +197 -0
  130. package/src/color/conversions.test.ts +402 -0
  131. package/src/color/conversions.ts +393 -0
  132. package/src/color/dislike/__tests__/dislike-analyzer.test.ts +248 -0
  133. package/src/color/dislike/dislike-analyzer.ts +114 -0
  134. package/src/color/extract-colors.ts +228 -0
  135. package/src/color/hct/__tests__/hct-class.test.ts +232 -0
  136. package/src/color/hct/harmonization.ts +204 -0
  137. package/src/color/hct/hct-class.ts +109 -0
  138. package/src/color/hct/hct-solver.ts +168 -0
  139. package/src/color/hct/index.ts +39 -0
  140. package/src/color/hct/tonal-palette.ts +211 -0
  141. package/src/color/hct/types.ts +88 -0
  142. package/src/color/image-utils.ts +79 -0
  143. package/src/color/index.ts +87 -0
  144. package/src/color/material-theme.ts +157 -0
  145. package/src/color/metrics.test.ts +276 -0
  146. package/src/color/metrics.ts +281 -0
  147. package/src/color/quantize/__tests__/quantizer_celebi.test.ts +195 -0
  148. package/src/color/quantize/lab_point_provider.ts +55 -0
  149. package/src/color/quantize/point_provider.ts +27 -0
  150. package/src/color/quantize/quantizer_celebi.ts +51 -0
  151. package/src/color/quantize/quantizer_celebi_test.ts +71 -0
  152. package/src/color/quantize/quantizer_map.ts +47 -0
  153. package/src/color/quantize/quantizer_wsmeans.ts +232 -0
  154. package/src/color/quantize/quantizer_wu.ts +472 -0
  155. package/src/color/score/__tests__/score.test.ts +224 -0
  156. package/src/color/score/score.ts +175 -0
  157. package/src/color/types.ts +151 -0
  158. package/src/color/utils/color_utils.ts +292 -0
  159. package/src/color/utils/math_utils.ts +145 -0
  160. package/src/color/utils.test.ts +403 -0
  161. package/src/color/utils.ts +315 -0
  162. package/src/constants.ts +5 -0
  163. package/src/coolors-mcp.ts +37 -0
  164. package/src/examples/addition.ts +333 -0
  165. package/src/examples/color-demo.ts +125 -0
  166. package/src/examples/custom-logger.ts +201 -0
  167. package/src/examples/oauth-server.ts +113 -0
  168. package/src/examples/session-context.ts +269 -0
  169. package/src/session.ts +116 -0
  170. package/src/theme/__tests__/matcher.test.ts +180 -0
  171. package/src/theme/__tests__/parser.test.ts +148 -0
  172. package/src/theme/__tests__/refactor.test.ts +224 -0
  173. package/src/theme/index.ts +34 -0
  174. package/src/theme/matcher.ts +395 -0
  175. package/src/theme/parser.ts +392 -0
  176. package/src/theme/refactor.ts +360 -0
  177. package/src/theme/types.ts +152 -0
  178. package/src/tools/__tests__/gradient-generator.test.ts +206 -0
  179. package/src/tools/__tests__/palette-with-locks.test.ts +109 -0
  180. package/src/tools/color-conversion.tool.ts +54 -0
  181. package/src/tools/color-distance.tool.ts +41 -0
  182. package/src/tools/colors.ts +31 -0
  183. package/src/tools/contrast-checker.tool.ts +37 -0
  184. package/src/tools/dislike-analyzer.tool.ts +247 -0
  185. package/src/tools/gradient-generator.tool.ts +250 -0
  186. package/src/tools/image-extraction.tools.ts +289 -0
  187. package/src/tools/index.ts +39 -0
  188. package/src/tools/material-theme.tools.ts +250 -0
  189. package/src/tools/palette-generator.tool.ts +135 -0
  190. package/src/tools/palette-with-locks.tool.ts +221 -0
  191. package/src/tools/registry.ts +142 -0
  192. package/src/tools/simple-tools.ts +37 -0
  193. package/src/tools/theme-matching.tools.ts +334 -0
  194. package/src/types.ts +182 -0
  195. package/src/utils.ts +22 -0
  196. package/tsconfig.json +8 -0
  197. package/vitest.config.js +15 -0
@@ -0,0 +1,175 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2021 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import { Hct } from "../hct/index.js";
19
+ import * as math from "../utils/math_utils.js";
20
+
21
+ /**
22
+ * Default options for ranking colors based on usage counts.
23
+ * desired: is the max count of the colors returned.
24
+ * fallbackColorARGB: Is the default color that should be used if no
25
+ * other colors are suitable.
26
+ * filter: controls if the resulting colors should be filtered to not include
27
+ * hues that are not used often enough, and colors that are effectively
28
+ * grayscale.
29
+ */
30
+ declare interface ScoreOptions {
31
+ desired?: number;
32
+ fallbackColorARGB?: number;
33
+ filter?: boolean;
34
+ }
35
+
36
+ const SCORE_OPTION_DEFAULTS = {
37
+ desired: 4, // 4 colors matches what Android wallpaper picker.
38
+ fallbackColorARGB: 0xff4285f4, // Google Blue.
39
+ filter: true, // Avoid unsuitable colors.
40
+ };
41
+
42
+ function compare(
43
+ a: { hct: Hct; score: number },
44
+ b: { hct: Hct; score: number },
45
+ ): number {
46
+ if (a.score > b.score) {
47
+ return -1;
48
+ } else if (a.score < b.score) {
49
+ return 1;
50
+ }
51
+ return 0;
52
+ }
53
+
54
+ /**
55
+ * Given a large set of colors, remove colors that are unsuitable for a UI
56
+ * theme, and rank the rest based on suitability.
57
+ *
58
+ * Enables use of a high cluster count for image quantization, thus ensuring
59
+ * colors aren't muddied, while curating the high cluster count to a much
60
+ * smaller number of appropriate choices.
61
+ */
62
+ export class Score {
63
+ private static readonly CUTOFF_CHROMA = 5.0;
64
+ private static readonly CUTOFF_EXCITED_PROPORTION = 0.01;
65
+ private static readonly TARGET_CHROMA = 48.0; // A1 Chroma
66
+ private static readonly WEIGHT_CHROMA_ABOVE = 0.3;
67
+ private static readonly WEIGHT_CHROMA_BELOW = 0.1;
68
+ private static readonly WEIGHT_PROPORTION = 0.7;
69
+
70
+ private constructor() {}
71
+
72
+ /**
73
+ * Given a map with keys of colors and values of how often the color appears,
74
+ * rank the colors based on suitability for being used for a UI theme.
75
+ *
76
+ * @param colorsToPopulation map with keys of colors and values of how often
77
+ * the color appears, usually from a source image.
78
+ * @param {ScoreOptions} options optional parameters.
79
+ * @return Colors sorted by suitability for a UI theme. The most suitable
80
+ * color is the first item, the least suitable is the last. There will
81
+ * always be at least one color returned. If all the input colors
82
+ * were not suitable for a theme, a default fallback color will be
83
+ * provided, Google Blue.
84
+ */
85
+ static score(
86
+ colorsToPopulation: Map<number, number>,
87
+ options?: ScoreOptions,
88
+ ): number[] {
89
+ const { desired, fallbackColorARGB, filter } = {
90
+ ...SCORE_OPTION_DEFAULTS,
91
+ ...options,
92
+ };
93
+ // Get the HCT color for each Argb value, while finding the per hue count and
94
+ // total count.
95
+ const colorsHct: Hct[] = [];
96
+ const huePopulation = new Array<number>(360).fill(0);
97
+ let populationSum = 0;
98
+ for (const [argb, population] of colorsToPopulation.entries()) {
99
+ const hct = Hct.fromInt(argb);
100
+ colorsHct.push(hct);
101
+ const hue = Math.floor(hct.hue);
102
+ huePopulation[hue] += population;
103
+ populationSum += population;
104
+ }
105
+
106
+ // Hues with more usage in neighboring 30 degree slice get a larger number.
107
+ const hueExcitedProportions = new Array<number>(360).fill(0.0);
108
+ for (let hue = 0; hue < 360; hue++) {
109
+ const proportion = huePopulation[hue] / populationSum;
110
+ for (let i = hue - 14; i < hue + 16; i++) {
111
+ const neighborHue = math.sanitizeDegreesInt(i);
112
+ hueExcitedProportions[neighborHue] += proportion;
113
+ }
114
+ }
115
+
116
+ // Scores each HCT color based on usage and chroma, while optionally
117
+ // filtering out values that do not have enough chroma or usage.
118
+ const scoredHct = new Array<{ hct: Hct; score: number }>();
119
+ for (const hct of colorsHct) {
120
+ const hue = math.sanitizeDegreesInt(Math.round(hct.hue));
121
+ const proportion = hueExcitedProportions[hue];
122
+ if (
123
+ filter &&
124
+ (hct.chroma < Score.CUTOFF_CHROMA ||
125
+ proportion <= Score.CUTOFF_EXCITED_PROPORTION)
126
+ ) {
127
+ continue;
128
+ }
129
+
130
+ const proportionScore = proportion * 100.0 * Score.WEIGHT_PROPORTION;
131
+ const chromaWeight =
132
+ hct.chroma < Score.TARGET_CHROMA
133
+ ? Score.WEIGHT_CHROMA_BELOW
134
+ : Score.WEIGHT_CHROMA_ABOVE;
135
+ const chromaScore = (hct.chroma - Score.TARGET_CHROMA) * chromaWeight;
136
+ const score = proportionScore + chromaScore;
137
+ scoredHct.push({ hct, score });
138
+ }
139
+ // Sorted so that colors with higher scores come first.
140
+ scoredHct.sort(compare);
141
+
142
+ // Iterates through potential hue differences in degrees in order to select
143
+ // the colors with the largest distribution of hues possible. Starting at
144
+ // 90 degrees(maximum difference for 4 colors) then decreasing down to a
145
+ // 15 degree minimum.
146
+ const chosenColors: Hct[] = [];
147
+ for (
148
+ let differenceDegrees = 90;
149
+ differenceDegrees >= 15;
150
+ differenceDegrees--
151
+ ) {
152
+ chosenColors.length = 0;
153
+ for (const { hct } of scoredHct) {
154
+ const duplicateHue = chosenColors.find((chosenHct) => {
155
+ return (
156
+ math.differenceDegrees(hct.hue, chosenHct.hue) < differenceDegrees
157
+ );
158
+ });
159
+ if (!duplicateHue) {
160
+ chosenColors.push(hct);
161
+ }
162
+ if (chosenColors.length >= desired) break;
163
+ }
164
+ if (chosenColors.length >= desired) break;
165
+ }
166
+ const colors: number[] = [];
167
+ if (chosenColors.length === 0) {
168
+ colors.push(fallbackColorARGB);
169
+ }
170
+ for (const chosenHct of chosenColors) {
171
+ colors.push(chosenHct.toInt());
172
+ }
173
+ return colors;
174
+ }
175
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Color type definitions for various color spaces
3
+ */
4
+
5
+ /**
6
+ * Supported color formats for parsing
7
+ */
8
+ export enum ColorFormat {
9
+ HEX = "hex",
10
+ HSL = "hsl",
11
+ HSV = "hsv",
12
+ LAB = "lab",
13
+ RGB = "rgb",
14
+ XYZ = "xyz",
15
+ }
16
+
17
+ /**
18
+ * Options for color distance calculations
19
+ */
20
+ export interface ColorDistanceOptions {
21
+ /**
22
+ * Parameters for Delta E 94
23
+ */
24
+ deltaE94?: {
25
+ kC?: number;
26
+ kH?: number;
27
+ kL?: number;
28
+ };
29
+
30
+ /**
31
+ * Type of distance metric to use
32
+ */
33
+ metric?: "deltaE2000" | "deltaE76" | "deltaE94" | "euclidean" | "weighted";
34
+
35
+ /**
36
+ * Weights for weighted RGB distance
37
+ */
38
+ weights?: {
39
+ b?: number;
40
+ g?: number;
41
+ r?: number;
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Color input type that can be parsed
47
+ */
48
+ export type ColorInput = HSL | HSV | LAB | RGB | string | XYZ;
49
+
50
+ /**
51
+ * HCT (Hue, Chroma, Tone) color representation
52
+ */
53
+ export interface HCT {
54
+ c: number; // Chroma [0, ~150]
55
+ h: number; // Hue [0, 360)
56
+ t: number; // Tone [0, 100]
57
+ }
58
+
59
+ /**
60
+ * Hexadecimal color representation
61
+ */
62
+ export type HEX = string;
63
+
64
+ /**
65
+ * HSL (Hue, Saturation, Lightness) color representation
66
+ * h: [0, 360] degrees
67
+ * s: [0, 100] percentage
68
+ * l: [0, 100] percentage
69
+ */
70
+ export interface HSL {
71
+ h: number;
72
+ l: number;
73
+ s: number;
74
+ }
75
+
76
+ /**
77
+ * HSV (Hue, Saturation, Value) color representation
78
+ * Also known as HSB (Hue, Saturation, Brightness)
79
+ * h: [0, 360] degrees
80
+ * s: [0, 100] percentage
81
+ * v: [0, 100] percentage
82
+ */
83
+ export interface HSV {
84
+ h: number;
85
+ s: number;
86
+ v: number;
87
+ }
88
+
89
+ /**
90
+ * LAB color space (CIE L*a*b*)
91
+ * L: [0, 100] lightness
92
+ * a: [-128, 127] green-red axis
93
+ * b: [-128, 127] blue-yellow axis
94
+ */
95
+ export interface LAB {
96
+ a: number;
97
+ b: number;
98
+ l: number;
99
+ }
100
+
101
+ /**
102
+ * Color parsing result
103
+ */
104
+ export interface ParsedColor {
105
+ format: ColorFormat;
106
+ value: HSL | HSV | LAB | RGB | XYZ;
107
+ }
108
+
109
+ /**
110
+ * RGB color representation
111
+ * Values should be in range [0, 255]
112
+ */
113
+ export interface RGB {
114
+ b: number;
115
+ g: number;
116
+ r: number;
117
+ }
118
+
119
+ /**
120
+ * XYZ color space (CIE 1931)
121
+ * Reference white: D65 illuminant
122
+ */
123
+ export interface XYZ {
124
+ x: number;
125
+ y: number;
126
+ z: number;
127
+ }
128
+
129
+ /**
130
+ * Constants for color space conversions
131
+ */
132
+ export const ColorConstants = {
133
+ /**
134
+ * D65 illuminant reference white point
135
+ */
136
+ D65: {
137
+ X: 95.047,
138
+ Y: 100.0,
139
+ Z: 108.883,
140
+ },
141
+
142
+ /**
143
+ * Epsilon for LAB conversion
144
+ */
145
+ EPSILON: 0.008856,
146
+
147
+ /**
148
+ * Kappa for LAB conversion
149
+ */
150
+ KAPPA: 903.3,
151
+ } as const;
@@ -0,0 +1,292 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2021 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ // This file is automatically generated. Do not modify it.
19
+
20
+ import * as mathUtils from "./math_utils.js";
21
+
22
+ /**
23
+ * Color science utilities.
24
+ *
25
+ * Utility methods for color science constants and color space
26
+ * conversions that aren't HCT or CAM16.
27
+ */
28
+
29
+ const SRGB_TO_XYZ = [
30
+ [0.41233895, 0.35762064, 0.18051042],
31
+ [0.2126, 0.7152, 0.0722],
32
+ [0.01932141, 0.11916382, 0.95034478],
33
+ ];
34
+
35
+ const XYZ_TO_SRGB = [
36
+ [3.2413774792388685, -1.5376652402851851, -0.49885366846268053],
37
+ [-0.9691452513005321, 1.8758853451067872, 0.04156585616912061],
38
+ [0.05562093689691305, -0.20395524564742123, 1.0571799111220335],
39
+ ];
40
+
41
+ const WHITE_POINT_D65 = [95.047, 100.0, 108.883];
42
+
43
+ /**
44
+ * Returns the alpha component of a color in ARGB format.
45
+ */
46
+ export function alphaFromArgb(argb: number): number {
47
+ return (argb >> 24) & 255;
48
+ }
49
+
50
+ /**
51
+ * Converts a color represented in Lab color space into an ARGB
52
+ * integer.
53
+ */
54
+ export function argbFromLab(l: number, a: number, b: number): number {
55
+ const whitePoint = WHITE_POINT_D65;
56
+ const fy = (l + 16.0) / 116.0;
57
+ const fx = a / 500.0 + fy;
58
+ const fz = fy - b / 200.0;
59
+ const xNormalized = labInvf(fx);
60
+ const yNormalized = labInvf(fy);
61
+ const zNormalized = labInvf(fz);
62
+ const x = xNormalized * whitePoint[0];
63
+ const y = yNormalized * whitePoint[1];
64
+ const z = zNormalized * whitePoint[2];
65
+ return argbFromXyz(x, y, z);
66
+ }
67
+
68
+ /**
69
+ * Converts a color from linear RGB components to ARGB format.
70
+ */
71
+ export function argbFromLinrgb(linrgb: number[]): number {
72
+ const r = delinearized(linrgb[0]);
73
+ const g = delinearized(linrgb[1]);
74
+ const b = delinearized(linrgb[2]);
75
+ return argbFromRgb(r, g, b);
76
+ }
77
+
78
+ /**
79
+ * Converts an L* value to an ARGB representation.
80
+ *
81
+ * @param lstar L* in L*a*b*
82
+ * @return ARGB representation of grayscale color with lightness
83
+ * matching L*
84
+ */
85
+ export function argbFromLstar(lstar: number): number {
86
+ const y = yFromLstar(lstar);
87
+ const component = delinearized(y);
88
+ return argbFromRgb(component, component, component);
89
+ }
90
+
91
+ /**
92
+ * Converts a color from RGB components to ARGB format.
93
+ */
94
+ export function argbFromRgb(red: number, green: number, blue: number): number {
95
+ return (
96
+ ((255 << 24) |
97
+ ((red & 255) << 16) |
98
+ ((green & 255) << 8) |
99
+ (blue & 255)) >>>
100
+ 0
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Converts a color from ARGB to XYZ.
106
+ */
107
+ export function argbFromXyz(x: number, y: number, z: number): number {
108
+ const matrix = XYZ_TO_SRGB;
109
+ const linearR = matrix[0][0] * x + matrix[0][1] * y + matrix[0][2] * z;
110
+ const linearG = matrix[1][0] * x + matrix[1][1] * y + matrix[1][2] * z;
111
+ const linearB = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2] * z;
112
+ const r = delinearized(linearR);
113
+ const g = delinearized(linearG);
114
+ const b = delinearized(linearB);
115
+ return argbFromRgb(r, g, b);
116
+ }
117
+
118
+ /**
119
+ * Returns the blue component of a color in ARGB format.
120
+ */
121
+ export function blueFromArgb(argb: number): number {
122
+ return argb & 255;
123
+ }
124
+
125
+ /**
126
+ * Delinearizes an RGB component.
127
+ *
128
+ * @param rgbComponent 0.0 <= rgb_component <= 100.0, represents
129
+ * linear R/G/B channel
130
+ * @return 0 <= output <= 255, color channel converted to regular
131
+ * RGB space
132
+ */
133
+ export function delinearized(rgbComponent: number): number {
134
+ const normalized = rgbComponent / 100.0;
135
+ let delinearized = 0.0;
136
+ if (normalized <= 0.0031308) {
137
+ delinearized = normalized * 12.92;
138
+ } else {
139
+ delinearized = 1.055 * Math.pow(normalized, 1.0 / 2.4) - 0.055;
140
+ }
141
+ return mathUtils.clampInt(0, 255, Math.round(delinearized * 255.0));
142
+ }
143
+
144
+ /**
145
+ * Returns the green component of a color in ARGB format.
146
+ */
147
+ export function greenFromArgb(argb: number): number {
148
+ return (argb >> 8) & 255;
149
+ }
150
+
151
+ /**
152
+ * Returns whether a color in ARGB format is opaque.
153
+ */
154
+ export function isOpaque(argb: number): boolean {
155
+ return alphaFromArgb(argb) >= 255;
156
+ }
157
+
158
+ /**
159
+ * Converts a color from ARGB representation to L*a*b*
160
+ * representation.
161
+ *
162
+ * @param argb the ARGB representation of a color
163
+ * @return a Lab object representing the color
164
+ */
165
+ export function labFromArgb(argb: number): number[] {
166
+ const linearR = linearized(redFromArgb(argb));
167
+ const linearG = linearized(greenFromArgb(argb));
168
+ const linearB = linearized(blueFromArgb(argb));
169
+ const matrix = SRGB_TO_XYZ;
170
+ const x =
171
+ matrix[0][0] * linearR + matrix[0][1] * linearG + matrix[0][2] * linearB;
172
+ const y =
173
+ matrix[1][0] * linearR + matrix[1][1] * linearG + matrix[1][2] * linearB;
174
+ const z =
175
+ matrix[2][0] * linearR + matrix[2][1] * linearG + matrix[2][2] * linearB;
176
+ const whitePoint = WHITE_POINT_D65;
177
+ const xNormalized = x / whitePoint[0];
178
+ const yNormalized = y / whitePoint[1];
179
+ const zNormalized = z / whitePoint[2];
180
+ const fx = labF(xNormalized);
181
+ const fy = labF(yNormalized);
182
+ const fz = labF(zNormalized);
183
+ const l = 116.0 * fy - 16;
184
+ const a = 500.0 * (fx - fy);
185
+ const b = 200.0 * (fy - fz);
186
+ return [l, a, b];
187
+ }
188
+
189
+ /**
190
+ * Linearizes an RGB component.
191
+ *
192
+ * @param rgbComponent 0 <= rgb_component <= 255, represents R/G/B
193
+ * channel
194
+ * @return 0.0 <= output <= 100.0, color channel converted to
195
+ * linear RGB space
196
+ */
197
+ export function linearized(rgbComponent: number): number {
198
+ const normalized = rgbComponent / 255.0;
199
+ if (normalized <= 0.040449936) {
200
+ return (normalized / 12.92) * 100.0;
201
+ } else {
202
+ return Math.pow((normalized + 0.055) / 1.055, 2.4) * 100.0;
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Computes the L* value of a color in ARGB representation.
208
+ *
209
+ * @param argb ARGB representation of a color
210
+ * @return L*, from L*a*b*, coordinate of the color
211
+ */
212
+ export function lstarFromArgb(argb: number): number {
213
+ const y = xyzFromArgb(argb)[1];
214
+ return 116.0 * labF(y / 100.0) - 16.0;
215
+ }
216
+
217
+ /**
218
+ * Converts a Y value to an L* value.
219
+ *
220
+ * L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
221
+ *
222
+ * L* measures perceptual luminance, a linear scale. Y in XYZ
223
+ * measures relative luminance, a logarithmic scale.
224
+ *
225
+ * @param y Y in XYZ
226
+ * @return L* in L*a*b*
227
+ */
228
+ export function lstarFromY(y: number): number {
229
+ return labF(y / 100.0) * 116.0 - 16.0;
230
+ }
231
+
232
+ /**
233
+ * Returns the red component of a color in ARGB format.
234
+ */
235
+ export function redFromArgb(argb: number): number {
236
+ return (argb >> 16) & 255;
237
+ }
238
+
239
+ /**
240
+ * Returns the standard white point; white on a sunny day.
241
+ *
242
+ * @return The white point
243
+ */
244
+ export function whitePointD65(): number[] {
245
+ return WHITE_POINT_D65;
246
+ }
247
+
248
+ /**
249
+ * Converts a color from XYZ to ARGB.
250
+ */
251
+ export function xyzFromArgb(argb: number): number[] {
252
+ const r = linearized(redFromArgb(argb));
253
+ const g = linearized(greenFromArgb(argb));
254
+ const b = linearized(blueFromArgb(argb));
255
+ return mathUtils.matrixMultiply([r, g, b], SRGB_TO_XYZ);
256
+ }
257
+
258
+ /**
259
+ * Converts an L* value to a Y value.
260
+ *
261
+ * L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
262
+ *
263
+ * L* measures perceptual luminance, a linear scale. Y in XYZ
264
+ * measures relative luminance, a logarithmic scale.
265
+ *
266
+ * @param lstar L* in L*a*b*
267
+ * @return Y in XYZ
268
+ */
269
+ export function yFromLstar(lstar: number): number {
270
+ return 100.0 * labInvf((lstar + 16.0) / 116.0);
271
+ }
272
+
273
+ function labF(t: number): number {
274
+ const e = 216.0 / 24389.0;
275
+ const kappa = 24389.0 / 27.0;
276
+ if (t > e) {
277
+ return Math.pow(t, 1.0 / 3.0);
278
+ } else {
279
+ return (kappa * t + 16) / 116;
280
+ }
281
+ }
282
+
283
+ function labInvf(ft: number): number {
284
+ const e = 216.0 / 24389.0;
285
+ const kappa = 24389.0 / 27.0;
286
+ const ft3 = ft * ft * ft;
287
+ if (ft3 > e) {
288
+ return ft3;
289
+ } else {
290
+ return (116 * ft - 16) / kappa;
291
+ }
292
+ }