rearrage_stuff
This commit is contained in:
21
node_modules/colorjs.io/LICENSE
generated
vendored
Normal file
21
node_modules/colorjs.io/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Lea Verou, Chris Lilley
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
3
node_modules/colorjs.io/README.json
generated
vendored
Normal file
3
node_modules/colorjs.io/README.json
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"layout": "home"
|
||||
}
|
||||
257
node_modules/colorjs.io/README.md
generated
vendored
Normal file
257
node_modules/colorjs.io/README.md
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
<header class="readme-only">
|
||||
|
||||
# Color.js: Let’s get serious about color
|
||||
|
||||
[](https://app.netlify.com/sites/colorjs/deploys)
|
||||
[](https://npmjs.com/package/colorjs.io)
|
||||
|
||||
[Official website](https://colorjs.io) • [Contribution guide](CONTRIBUTING.md)
|
||||
|
||||
Color.js is a color conversion and modification library originally created by two of the editors of the CSS Color specifications: Lea Verou and Chris Lilley.
|
||||
They continue to work on it, but are also joined by an exceptional small grassroots team of co-maintainers.
|
||||
|
||||
## Features
|
||||
|
||||
- **Color space agnostic**: Each color object is basically a list of coords and a color space reference. Operations are color space agnostic.
|
||||
Modules for <a href="https://colorjs.io/docs/spaces.html">a wide variety of color spaces</a>,
|
||||
including Lab/LCh, OKLab/OKLCh,
|
||||
sRGB and friends (HSL/HSV/HWB), Display P3,
|
||||
J<sub>z</sub>a<sub>z</sub>b<sub>z</sub>, REC.2100 and many <a href="https://colorjs.io/docs/spaces.html">more</a>.
|
||||
- **Doesn't gloss over color science**: Actual <a href="docs/gamut-mapping.html">gamut mapping</a> instead of naïve clipping,
|
||||
multiple <a href="https://colorjs.io/docs/color-difference.html">DeltaE</a> methods (76, CMC, 2000, J<sub>z</sub>),
|
||||
multiple <a href="https://colorjs.io/docs/adaptation.html">chromatic adaptation</a> methods (von Kries, Bradford, CAT02, CAT16),
|
||||
all with sensible defaults
|
||||
- **Up to date with CSS Color 4**: Every <a href="https://drafts.csswg.org/css-color-4/">CSS Color 4</a> format & color space supported for both <a href="docs/the-color-object.html">input</a> and <a href="https://colorjs.io/docs/output.html">output</a>, whether your browser supports it or not.
|
||||
- **Readable, object-oriented API**: Color objects for multiple operations on the same color, and static `Color.something()` functions for one-off calculations
|
||||
- **Modular & Extensible**: Use only what you need, or a bundle. Client-side or Node. Deep extensibility with <a href="https://colorjs.io/api/#Hooks-hooks.js">hooks</a>.
|
||||
- **Fast & efficient**: <a href="https://colorjs.io/docs/procedural.html">Procedural, tree-shakeable API</a> available for performance sensitive tasks and reduced bundle size
|
||||
|
||||
</header>
|
||||
|
||||
<section>
|
||||
|
||||
## Impact
|
||||
|
||||
- Has been used to create demos for several W3C specifications
|
||||
- Has been used by browsers to test their CSS Color 4/5 implementations
|
||||
- Over [2 million total npm downloads](https://limonte.dev/total-npm-downloads/?package=colorjs.io)!
|
||||
- Used by several [high impact projects](https://www.npmjs.com/browse/depended/colorjs.io), including [Sass](https://sass-lang.com/), [Open Props](https://open-props.style/), [axe](https://www.deque.com/axe/) accessibility testing engine, and [OddContrast](https://www.oddcontrast.com/) and [CSS HD Gradients](https://gradient.style/) color tools
|
||||
- Parts of Color.js’s API are used as a testing ground for the design of a [native `Color` object for the Web platform](https://github.com/wicg/color-api).
|
||||
|
||||
</section>
|
||||
|
||||
<section class="cn-ignore">
|
||||
|
||||
## Installation
|
||||
|
||||
Color.js is designed make simple things easy, and complex things possible, and that extends to installation as well.
|
||||
|
||||
For quick experiments, you can just import Color.js directly from the CDN (kindly provided by the awesome folks at [Netlify](https://netlify.com)) with all modules included:
|
||||
|
||||
```js
|
||||
import Color from "https://colorjs.io/dist/color.js";
|
||||
```
|
||||
|
||||
You can also install via npm if you’d prefer:
|
||||
|
||||
```
|
||||
npm install colorjs.io
|
||||
```
|
||||
|
||||
Whether you’re using NPM, the CDN, or local files, Color.js allows you to also import specific modules by directly importing from `src`:
|
||||
- `https://colorjs.io/src/` for the CDN
|
||||
- `node_modules/colorjs.io/src/ for NPM
|
||||
|
||||
For example:
|
||||
```js
|
||||
import Color from "https://colorjs.io/src/color.js";
|
||||
import p3 from "https://colorjs.io/src/spaces/p3.js";
|
||||
import rec2020 from "https://colorjs.io/src/spaces/rec2020.js";
|
||||
import deltaE200 from "https://colorjs.io/src/deltaE/deltaE2000.js";
|
||||
```
|
||||
|
||||
Warning: To use `import` statements in a browser, your `<script>` needs `type="module"`
|
||||
|
||||
Are you old school and prefer to simply have a global `Color` variable?
|
||||
We’ve got you covered!
|
||||
Just include the following script in your HTML:
|
||||
|
||||
```html
|
||||
<script src="https://colorjs.io/dist/color.global.js"></script>
|
||||
```
|
||||
|
||||
<p class="read-more"><a href="https://colorjs.io/get">Read more about installation</a></p>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
## Reading colors
|
||||
|
||||
Any color from CSS Color Level 4 should work:
|
||||
|
||||
```js
|
||||
let color = new Color("slategray");
|
||||
let color2 = new Color("hwb(60 30% 40% / .5)");
|
||||
let color3 = new Color("color(display-p3 0 1 0 / .9)");
|
||||
let color4 = new Color("lch(50% 80 30)");
|
||||
```
|
||||
|
||||
You can also create `Color` objects manually:
|
||||
|
||||
```js
|
||||
let color2 = new Color("hwb", [60, 30, 40], .5);
|
||||
let color3 = new Color({space: "p3", coords: [0, 1, 0], alpha: .9});
|
||||
```
|
||||
|
||||
<p class="read-more"><a href="https://colorjs.io/docs/the-color-object.html">Read more about color objects</a>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Manipulating colors</h2>
|
||||
|
||||
You can use properties to modify coordinates
|
||||
of any color space and convert back
|
||||
|
||||
```js
|
||||
let color = new Color("slategray");
|
||||
color.lch.l = 80; // Set coord directly in any color space
|
||||
color.lch.c *= 1.2; // saturate by increasing LCH chroma by 20%
|
||||
color.hwb.w += 10; // any other color space also available
|
||||
```
|
||||
|
||||
To modify coordinates in any color space you use `color.set()` and `color.setAll()`:
|
||||
|
||||
```js
|
||||
let color = new Color("slategray");
|
||||
|
||||
// Multiple coordinates
|
||||
color.set({
|
||||
"lch.l": 80, // set lightness to 80
|
||||
"lch.c": c => c * 1.2 // Relative manipulation
|
||||
});
|
||||
|
||||
// Set single coordinate
|
||||
color.set("hwb.w", w => w + 10);
|
||||
```
|
||||
|
||||
Coordinates of the color's color space are available without a prefix:
|
||||
|
||||
```js
|
||||
let color = new Color("slategray").to("lch");
|
||||
|
||||
// Multiple coordinates
|
||||
color.set({
|
||||
l: 80, // set lightness to 80
|
||||
c: c => c * 1.2 // Relative manipulation
|
||||
});
|
||||
|
||||
// Set single coordinate
|
||||
color.set("h", 30);
|
||||
```
|
||||
|
||||
Chaining-style modifications are also supported:
|
||||
```js
|
||||
let color = new Color("lch(50% 50 10)");
|
||||
color = color.set({
|
||||
h: h => h + 180,
|
||||
c: 60
|
||||
}).lighten();
|
||||
```
|
||||
|
||||
You can also use properties:
|
||||
|
||||
```js
|
||||
let color = new Color("slategray");
|
||||
color.lch.l = 80; // Set coord directly in any color space
|
||||
color.lch.c *= 1.2; // saturate by increasing LCH chroma by 20%
|
||||
color.hwb.w += 10; // any other color space also available
|
||||
```
|
||||
|
||||
Coordinates of the color's color space are available without a prefix:
|
||||
|
||||
```js
|
||||
let color = new Color("slategray").to("lch");
|
||||
color.l = 80; // Set LCH lightness
|
||||
color.c *= 1.2; // saturate by increasing LCH chroma
|
||||
```
|
||||
|
||||
<p class="read-more"><a href="https://colorjs.io/docs/manipulation.html">Read more about color manipulation</a></p>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
## Converting between color spaces & stringifying
|
||||
|
||||
Convert to any color space:
|
||||
|
||||
```js
|
||||
let color = new Color("slategray");
|
||||
color.to("lch") // Convert to LCH
|
||||
```
|
||||
|
||||
Output in any color space
|
||||
|
||||
```js
|
||||
let color = new Color("slategray");
|
||||
color + ""; // default stringification
|
||||
color.to("p3").toString({precision: 3});
|
||||
```
|
||||
|
||||
Clip to gamut or don't
|
||||
```js
|
||||
let color = new Color("p3", [0, 1, 0]);
|
||||
color.to("srgb") + ""; // Default toString()
|
||||
color.to("srgb").toString({inGamut: false});
|
||||
```
|
||||
|
||||
<p class="read-more"><a href="https://colorjs.io/docs/output.html">Read more about output</a></p>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
## Interpolation
|
||||
|
||||
Get a function that accepts a percentage:
|
||||
|
||||
```js
|
||||
let color = new Color("p3", [0, 1, 0]);
|
||||
let redgreen = color.range("red", {
|
||||
space: "lch", // interpolation space
|
||||
outputSpace: "srgb"
|
||||
});
|
||||
redgreen(.5); // midpoint
|
||||
```
|
||||
|
||||
Interpolation by discrete steps:
|
||||
|
||||
```js
|
||||
let color = new Color("p3", [0, 1, 0]);
|
||||
color.steps("red", {
|
||||
space: "lch",
|
||||
outputSpace: "srgb",
|
||||
maxDeltaE: 3, // max deltaE between consecutive steps
|
||||
steps: 10 // min number of steps
|
||||
});
|
||||
```
|
||||
|
||||
Shortcut for specific points in the range:
|
||||
|
||||
```js
|
||||
let color = new Color("p3", [0, 1, 0]);
|
||||
let redgreen = color.mix("red", .5, {space: "lch", outputSpace: "srgb"});
|
||||
let reddishGreen = color.mix("red", .25, {space: "lch", outputSpace: "srgb"});
|
||||
```
|
||||
|
||||
Static syntax (every color method has a static one too):
|
||||
|
||||
```js
|
||||
Color.mix("color(display-p3 0 1 0)", "red", .5);
|
||||
```
|
||||
|
||||
<p class="read-more"><a href="https://colorjs.io/docs/interpolation.html">Read more about interpolation</a></p>
|
||||
|
||||
</section>
|
||||
5437
node_modules/colorjs.io/dist/color-fn.cjs
generated
vendored
Normal file
5437
node_modules/colorjs.io/dist/color-fn.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color-fn.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color-fn.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
7048
node_modules/colorjs.io/dist/color-fn.legacy.cjs
generated
vendored
Normal file
7048
node_modules/colorjs.io/dist/color-fn.legacy.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color-fn.legacy.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color-fn.legacy.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color-fn.legacy.min.cjs
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color-fn.legacy.min.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color-fn.legacy.min.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color-fn.legacy.min.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color-fn.min.cjs
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color-fn.min.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color-fn.min.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color-fn.min.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5759
node_modules/colorjs.io/dist/color.cjs
generated
vendored
Normal file
5759
node_modules/colorjs.io/dist/color.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5760
node_modules/colorjs.io/dist/color.global.js
generated
vendored
Normal file
5760
node_modules/colorjs.io/dist/color.global.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color.global.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.global.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
7400
node_modules/colorjs.io/dist/color.global.legacy.js
generated
vendored
Normal file
7400
node_modules/colorjs.io/dist/color.global.legacy.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color.global.legacy.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.global.legacy.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color.global.legacy.min.js
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color.global.legacy.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color.global.legacy.min.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.global.legacy.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color.global.min.js
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color.global.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color.global.min.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.global.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5755
node_modules/colorjs.io/dist/color.js
generated
vendored
Normal file
5755
node_modules/colorjs.io/dist/color.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
7399
node_modules/colorjs.io/dist/color.legacy.cjs
generated
vendored
Normal file
7399
node_modules/colorjs.io/dist/color.legacy.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color.legacy.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.legacy.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
7395
node_modules/colorjs.io/dist/color.legacy.js
generated
vendored
Normal file
7395
node_modules/colorjs.io/dist/color.legacy.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/colorjs.io/dist/color.legacy.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.legacy.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color.legacy.min.cjs
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color.legacy.min.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color.legacy.min.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.legacy.min.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color.legacy.min.js
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color.legacy.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color.legacy.min.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.legacy.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color.min.cjs
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color.min.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color.min.cjs.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.min.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/colorjs.io/dist/color.min.js
generated
vendored
Normal file
2
node_modules/colorjs.io/dist/color.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/colorjs.io/dist/color.min.js.map
generated
vendored
Normal file
1
node_modules/colorjs.io/dist/color.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
113
node_modules/colorjs.io/package.json
generated
vendored
Normal file
113
node_modules/colorjs.io/package.json
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"name": "colorjs.io",
|
||||
"version": "0.5.2",
|
||||
"description": "Let’s get serious about color",
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/",
|
||||
"types/src/",
|
||||
"types/index.d.ts",
|
||||
"types/index.d.cts"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./types/index.d.ts",
|
||||
"default": "./dist/color.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/index.d.cts",
|
||||
"default": "./dist/color.cjs"
|
||||
}
|
||||
},
|
||||
"./fn": {
|
||||
"import": {
|
||||
"types": "./types/src/index-fn.d.ts",
|
||||
"default": "./src/index-fn.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/src/index-fn.d.cts",
|
||||
"default": "./dist/color-fn.cjs"
|
||||
}
|
||||
},
|
||||
"./dist/*": "./dist/*"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"fn": [
|
||||
"./types/src/index-fn.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "module",
|
||||
"main": "./dist/color.cjs",
|
||||
"module": "./dist/color.js",
|
||||
"types": "./types",
|
||||
"directories": {
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npx htest ./test",
|
||||
"dtslint": "dtslint --expectOnly types",
|
||||
"eslint": "eslint . --ext .js --ext .ts --ext .cjs",
|
||||
"lint": "run-s build:space-accessors \"eslint -- --fix\" dtslint",
|
||||
"lint:ci": "run-s build:space-accessors \"eslint -- --max-warnings 0\" dtslint",
|
||||
"build:css": "npx postcss \"**/*.postcss\" --base . --dir . --ext .css --config postcss.config.cjs",
|
||||
"build:html": "npx @11ty/eleventy --config=.eleventy.cjs",
|
||||
"build:js": "rollup -c",
|
||||
"build:js:legacy": "rollup -c rollup.legacy.config.js",
|
||||
"build:space-accessors": "node ./scripts/generate-space-accessor-types.js",
|
||||
"build": "run-s build:html build:css build:js build:js:legacy build:space-accessors",
|
||||
"watch:css": "npx postcss \"**/*.postcss\" --base . --dir . --ext .css --config postcss.config.cjs --watch",
|
||||
"watch:html": "npx @11ty/eleventy --config=.eleventy.cjs --watch",
|
||||
"watch:js": "rollup -c --watch",
|
||||
"watch": "run-p watch:*",
|
||||
"prepack": "npm run build",
|
||||
"release": "release-it"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/LeaVerou/color.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"color"
|
||||
],
|
||||
"contributors": [
|
||||
"Lea Verou",
|
||||
"Chris Lilley"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/LeaVerou/color.js/issues"
|
||||
},
|
||||
"homepage": "https://colorjs.io",
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^2.0",
|
||||
"@babel/core": "^7.24.0",
|
||||
"@babel/preset-env": "^7.24.0",
|
||||
"@definitelytyped/dtslint": "0.0.182",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@stylistic/eslint-plugin": "latest",
|
||||
"@typescript-eslint/eslint-plugin": "latest",
|
||||
"@typescript-eslint/parser": "latest",
|
||||
"acorn": "latest",
|
||||
"core-js": "^3.36.0",
|
||||
"eslint": "latest",
|
||||
"htest.dev": "^0.0.9",
|
||||
"mathjs": "^12.4.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "latest",
|
||||
"postcss-cli": "latest",
|
||||
"postcss-nesting": "latest",
|
||||
"release-it": "^17.1.1",
|
||||
"rollup": "^4.12.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"sideEffects": [
|
||||
"./src/index.js",
|
||||
"./src/spaces/index.js"
|
||||
]
|
||||
}
|
||||
131
node_modules/colorjs.io/src/CATs.js
generated
vendored
Normal file
131
node_modules/colorjs.io/src/CATs.js
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
import hooks from "./hooks.js";
|
||||
import {multiplyMatrices} from "./util.js";
|
||||
import {WHITES} from "./adapt.js";
|
||||
|
||||
export const CATs = {};
|
||||
|
||||
hooks.add("chromatic-adaptation-start", env => {
|
||||
if (env.options.method) {
|
||||
env.M = adapt(env.W1, env.W2, env.options.method);
|
||||
}
|
||||
});
|
||||
|
||||
hooks.add("chromatic-adaptation-end", env => {
|
||||
if (!env.M) {
|
||||
env.M = adapt(env.W1, env.W2, env.options.method);
|
||||
}
|
||||
});
|
||||
|
||||
export function defineCAT ({id, toCone_M, fromCone_M}) {
|
||||
// Use id, toCone_M, fromCone_M like variables
|
||||
CATs[id] = arguments[0];
|
||||
}
|
||||
|
||||
export function adapt (W1, W2, id = "Bradford") {
|
||||
// adapt from a source whitepoint or illuminant W1
|
||||
// to a destination whitepoint or illuminant W2,
|
||||
// using the given chromatic adaptation transform (CAT)
|
||||
// debugger;
|
||||
let method = CATs[id];
|
||||
|
||||
let [ρs, γs, βs] = multiplyMatrices(method.toCone_M, W1);
|
||||
let [ρd, γd, βd] = multiplyMatrices(method.toCone_M, W2);
|
||||
|
||||
// all practical illuminants have non-zero XYZ so no division by zero can occur below
|
||||
let scale = [
|
||||
[ρd / ρs, 0, 0 ],
|
||||
[0, γd / γs, 0 ],
|
||||
[0, 0, βd / βs],
|
||||
];
|
||||
// console.log({scale});
|
||||
|
||||
let scaled_cone_M = multiplyMatrices(scale, method.toCone_M);
|
||||
let adapt_M = multiplyMatrices(method.fromCone_M, scaled_cone_M);
|
||||
// console.log({scaled_cone_M, adapt_M});
|
||||
return adapt_M;
|
||||
}
|
||||
|
||||
defineCAT({
|
||||
id: "von Kries",
|
||||
toCone_M: [
|
||||
[ 0.4002400, 0.7076000, -0.0808100 ],
|
||||
[ -0.2263000, 1.1653200, 0.0457000 ],
|
||||
[ 0.0000000, 0.0000000, 0.9182200 ],
|
||||
],
|
||||
fromCone_M: [
|
||||
[ 1.8599363874558397, -1.1293816185800916, 0.21989740959619328 ],
|
||||
[ 0.3611914362417676, 0.6388124632850422, -0.000006370596838649899 ],
|
||||
[ 0, 0, 1.0890636230968613 ],
|
||||
],
|
||||
});
|
||||
|
||||
defineCAT({
|
||||
id: "Bradford",
|
||||
// Convert an array of XYZ values in the range 0.0 - 1.0
|
||||
// to cone fundamentals
|
||||
toCone_M: [
|
||||
[ 0.8951000, 0.2664000, -0.1614000 ],
|
||||
[ -0.7502000, 1.7135000, 0.0367000 ],
|
||||
[ 0.0389000, -0.0685000, 1.0296000 ],
|
||||
],
|
||||
// and back
|
||||
fromCone_M: [
|
||||
[ 0.9869929054667121, -0.14705425642099013, 0.15996265166373122 ],
|
||||
[ 0.4323052697233945, 0.5183602715367774, 0.049291228212855594 ],
|
||||
[ -0.00852866457517732, 0.04004282165408486, 0.96848669578755 ],
|
||||
],
|
||||
});
|
||||
|
||||
defineCAT({
|
||||
id: "CAT02",
|
||||
// with complete chromatic adaptation to W2, so D = 1.0
|
||||
toCone_M: [
|
||||
[ 0.7328000, 0.4296000, -0.1624000 ],
|
||||
[ -0.7036000, 1.6975000, 0.0061000 ],
|
||||
[ 0.0030000, 0.0136000, 0.9834000 ],
|
||||
],
|
||||
fromCone_M: [
|
||||
[ 1.0961238208355142, -0.27886900021828726, 0.18274517938277307 ],
|
||||
[ 0.4543690419753592, 0.4735331543074117, 0.07209780371722911 ],
|
||||
[ -0.009627608738429355, -0.00569803121611342, 1.0153256399545427 ],
|
||||
],
|
||||
});
|
||||
|
||||
defineCAT({
|
||||
id: "CAT16",
|
||||
toCone_M: [
|
||||
[ 0.401288, 0.650173, -0.051461 ],
|
||||
[ -0.250268, 1.204414, 0.045854 ],
|
||||
[ -0.002079, 0.048952, 0.953127 ],
|
||||
],
|
||||
// the extra precision is needed to avoid roundtripping errors
|
||||
fromCone_M: [
|
||||
[ 1.862067855087233, -1.0112546305316845, 0.14918677544445172 ],
|
||||
[ 0.3875265432361372, 0.6214474419314753, -0.008973985167612521 ],
|
||||
[ -0.01584149884933386, -0.03412293802851557, 1.0499644368778496 ],
|
||||
],
|
||||
});
|
||||
|
||||
Object.assign(WHITES, {
|
||||
// whitepoint values from ASTM E308-01 with 10nm spacing, 1931 2 degree observer
|
||||
// all normalized to Y (luminance) = 1.00000
|
||||
// Illuminant A is a tungsten electric light, giving a very warm, orange light.
|
||||
A: [1.09850, 1.00000, 0.35585],
|
||||
|
||||
// Illuminant C was an early approximation to daylight: illuminant A with a blue filter.
|
||||
C: [0.98074, 1.000000, 1.18232],
|
||||
|
||||
// The daylight series of illuminants simulate natural daylight.
|
||||
// The color temperature (in degrees Kelvin/100) ranges from
|
||||
// cool, overcast daylight (D50) to bright, direct sunlight (D65).
|
||||
D55: [0.95682, 1.00000, 0.92149],
|
||||
D75: [0.94972, 1.00000, 1.22638],
|
||||
|
||||
// Equal-energy illuminant, used in two-stage CAT16
|
||||
E: [1.00000, 1.00000, 1.00000],
|
||||
|
||||
// The F series of illuminants represent fluorescent lights
|
||||
F2: [0.99186, 1.00000, 0.67393],
|
||||
F7: [0.95041, 1.00000, 1.08747],
|
||||
F11: [1.00962, 1.00000, 0.64350],
|
||||
});
|
||||
62
node_modules/colorjs.io/src/adapt.js
generated
vendored
Normal file
62
node_modules/colorjs.io/src/adapt.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
import hooks from "./hooks.js";
|
||||
import {multiplyMatrices} from "./util.js";
|
||||
|
||||
export const WHITES = {
|
||||
// for compatibility, the four-digit chromaticity-derived ones everyone else uses
|
||||
D50: [0.3457 / 0.3585, 1.00000, (1.0 - 0.3457 - 0.3585) / 0.3585],
|
||||
D65: [0.3127 / 0.3290, 1.00000, (1.0 - 0.3127 - 0.3290) / 0.3290],
|
||||
};
|
||||
|
||||
export function getWhite (name) {
|
||||
if (Array.isArray(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return WHITES[name];
|
||||
}
|
||||
|
||||
// Adapt XYZ from white point W1 to W2
|
||||
export default function adapt (W1, W2, XYZ, options = {}) {
|
||||
W1 = getWhite(W1);
|
||||
W2 = getWhite(W2);
|
||||
|
||||
if (!W1 || !W2) {
|
||||
throw new TypeError(`Missing white point to convert ${!W1 ? "from" : ""}${!W1 && !W2 ? "/" : ""}${!W2 ? "to" : ""}`);
|
||||
}
|
||||
|
||||
if (W1 === W2) {
|
||||
// Same whitepoints, no conversion needed
|
||||
return XYZ;
|
||||
}
|
||||
|
||||
let env = {W1, W2, XYZ, options};
|
||||
|
||||
hooks.run("chromatic-adaptation-start", env);
|
||||
|
||||
if (!env.M) {
|
||||
if (env.W1 === WHITES.D65 && env.W2 === WHITES.D50) {
|
||||
env.M = [
|
||||
[ 1.0479297925449969, 0.022946870601609652, -0.05019226628920524 ],
|
||||
[ 0.02962780877005599, 0.9904344267538799, -0.017073799063418826 ],
|
||||
[ -0.009243040646204504, 0.015055191490298152, 0.7518742814281371 ],
|
||||
];
|
||||
}
|
||||
else if (env.W1 === WHITES.D50 && env.W2 === WHITES.D65) {
|
||||
|
||||
env.M = [
|
||||
[ 0.955473421488075, -0.02309845494876471, 0.06325924320057072 ],
|
||||
[ -0.0283697093338637, 1.0099953980813041, 0.021041441191917323 ],
|
||||
[ 0.012314014864481998, -0.020507649298898964, 1.330365926242124 ],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
hooks.run("chromatic-adaptation-end", env);
|
||||
|
||||
if (env.M) {
|
||||
return multiplyMatrices(env.M, env.XYZ);
|
||||
}
|
||||
else {
|
||||
throw new TypeError("Only Bradford CAT with white points D50 and D65 supported for now.");
|
||||
}
|
||||
}
|
||||
44
node_modules/colorjs.io/src/angles.js
generated
vendored
Normal file
44
node_modules/colorjs.io/src/angles.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
export function constrain (angle) {
|
||||
return ((angle % 360) + 360) % 360;
|
||||
}
|
||||
|
||||
export function adjust (arc, angles) {
|
||||
if (arc === "raw") {
|
||||
return angles;
|
||||
}
|
||||
|
||||
let [a1, a2] = angles.map(constrain);
|
||||
|
||||
let angleDiff = a2 - a1;
|
||||
|
||||
if (arc === "increasing") {
|
||||
if (angleDiff < 0) {
|
||||
a2 += 360;
|
||||
}
|
||||
}
|
||||
else if (arc === "decreasing") {
|
||||
if (angleDiff > 0) {
|
||||
a1 += 360;
|
||||
}
|
||||
}
|
||||
else if (arc === "longer") {
|
||||
if (-180 < angleDiff && angleDiff < 180) {
|
||||
if (angleDiff > 0) {
|
||||
a1 += 360;
|
||||
}
|
||||
else {
|
||||
a2 += 360;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (arc === "shorter") {
|
||||
if (angleDiff > 180) {
|
||||
a1 += 360;
|
||||
}
|
||||
else if (angleDiff < -180) {
|
||||
a2 += 360;
|
||||
}
|
||||
}
|
||||
|
||||
return [a1, a2];
|
||||
}
|
||||
33
node_modules/colorjs.io/src/chromaticity.js
generated
vendored
Normal file
33
node_modules/colorjs.io/src/chromaticity.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import xyz_d65 from "./spaces/xyz-d65.js";
|
||||
import getAll from "./getAll.js";
|
||||
|
||||
// Chromaticity coordinates
|
||||
export function uv (color) {
|
||||
// Assumes getAll() calls getColor() on color
|
||||
let [X, Y, Z] = getAll(color, xyz_d65);
|
||||
let denom = X + 15 * Y + 3 * Z;
|
||||
return [4 * X / denom, 9 * Y / denom];
|
||||
}
|
||||
|
||||
export function xy (color) {
|
||||
// Assumes getAll() calls getColor() on color
|
||||
let [X, Y, Z] = getAll(color, xyz_d65);
|
||||
let sum = X + Y + Z;
|
||||
return [X / sum, Y / sum];
|
||||
}
|
||||
|
||||
export function register (Color) {
|
||||
// no setters, as lightness information is lost
|
||||
// when converting color to chromaticity
|
||||
Object.defineProperty(Color.prototype, "uv", {
|
||||
get () {
|
||||
return uv(this);
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(Color.prototype, "xy", {
|
||||
get () {
|
||||
return xy(this);
|
||||
},
|
||||
});
|
||||
}
|
||||
7
node_modules/colorjs.io/src/clone.js
generated
vendored
Normal file
7
node_modules/colorjs.io/src/clone.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function clone (color) {
|
||||
return {
|
||||
space: color.space,
|
||||
coords: color.coords.slice(),
|
||||
alpha: color.alpha,
|
||||
};
|
||||
}
|
||||
201
node_modules/colorjs.io/src/color.js
generated
vendored
Normal file
201
node_modules/colorjs.io/src/color.js
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
import * as util from "./util.js";
|
||||
import hooks from "./hooks.js";
|
||||
import defaults from "./defaults.js";
|
||||
import ColorSpace from "./space.js";
|
||||
import {WHITES} from "./adapt.js";
|
||||
import {
|
||||
getColor,
|
||||
parse,
|
||||
to,
|
||||
serialize,
|
||||
inGamut,
|
||||
toGamut,
|
||||
distance,
|
||||
equals,
|
||||
get,
|
||||
getAll,
|
||||
set,
|
||||
setAll,
|
||||
display,
|
||||
} from "./index-fn.js";
|
||||
|
||||
|
||||
import "./spaces/xyz-d50.js";
|
||||
import "./spaces/srgb.js";
|
||||
|
||||
/**
|
||||
* Class that represents a color
|
||||
*/
|
||||
export default class Color {
|
||||
/**
|
||||
* Creates an instance of Color.
|
||||
* Signatures:
|
||||
* - `new Color(stringToParse)`
|
||||
* - `new Color(otherColor)`
|
||||
* - `new Color({space, coords, alpha})`
|
||||
* - `new Color(space, coords, alpha)`
|
||||
* - `new Color(spaceId, coords, alpha)`
|
||||
*/
|
||||
constructor (...args) {
|
||||
let color;
|
||||
|
||||
if (args.length === 1) {
|
||||
color = getColor(args[0]);
|
||||
}
|
||||
|
||||
let space, coords, alpha;
|
||||
|
||||
if (color) {
|
||||
space = color.space || color.spaceId;
|
||||
coords = color.coords;
|
||||
alpha = color.alpha;
|
||||
}
|
||||
else {
|
||||
// default signature new Color(ColorSpace, array [, alpha])
|
||||
[space, coords, alpha] = args;
|
||||
}
|
||||
|
||||
Object.defineProperty(this, "space", {
|
||||
value: ColorSpace.get(space),
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: true, // see note in https://262.ecma-international.org/8.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
|
||||
});
|
||||
|
||||
this.coords = coords ? coords.slice() : [0, 0, 0];
|
||||
|
||||
// Clamp alpha to [0, 1]
|
||||
this.alpha = alpha > 1 || alpha === undefined ? 1 : (alpha < 0 ? 0 : alpha);
|
||||
|
||||
// Convert "NaN" to NaN
|
||||
for (let i = 0; i < this.coords.length; i++) {
|
||||
if (this.coords[i] === "NaN") {
|
||||
this.coords[i] = NaN;
|
||||
}
|
||||
}
|
||||
|
||||
// Define getters and setters for each coordinate
|
||||
for (let id in this.space.coords) {
|
||||
Object.defineProperty(this, id, {
|
||||
get: () => this.get(id),
|
||||
set: value => this.set(id, value),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get spaceId () {
|
||||
return this.space.id;
|
||||
}
|
||||
|
||||
clone () {
|
||||
return new Color(this.space, this.coords, this.alpha);
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
spaceId: this.spaceId,
|
||||
coords: this.coords,
|
||||
alpha: this.alpha,
|
||||
};
|
||||
}
|
||||
|
||||
display (...args) {
|
||||
let ret = display(this, ...args);
|
||||
|
||||
// Convert color object to Color instance
|
||||
ret.color = new Color(ret.color);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a color from the argument passed
|
||||
* Basically gets us the same result as new Color(color) but doesn't clone an existing color object
|
||||
*/
|
||||
static get (color, ...args) {
|
||||
if (color instanceof Color) {
|
||||
return color;
|
||||
}
|
||||
|
||||
return new Color(color, ...args);
|
||||
}
|
||||
|
||||
static defineFunction (name, code, o = code) {
|
||||
let {instance = true, returns} = o;
|
||||
|
||||
let func = function (...args) {
|
||||
let ret = code(...args);
|
||||
|
||||
if (returns === "color") {
|
||||
ret = Color.get(ret);
|
||||
}
|
||||
else if (returns === "function<color>") {
|
||||
let f = ret;
|
||||
ret = function (...args) {
|
||||
let ret = f(...args);
|
||||
return Color.get(ret);
|
||||
};
|
||||
// Copy any function metadata
|
||||
Object.assign(ret, f);
|
||||
}
|
||||
else if (returns === "array<color>") {
|
||||
ret = ret.map(c => Color.get(c));
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
if (!(name in Color)) {
|
||||
Color[name] = func;
|
||||
}
|
||||
|
||||
if (instance) {
|
||||
Color.prototype[name] = function (...args) {
|
||||
return func(this, ...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static defineFunctions (o) {
|
||||
for (let name in o) {
|
||||
Color.defineFunction(name, o[name], o[name]);
|
||||
}
|
||||
}
|
||||
|
||||
static extend (exports) {
|
||||
if (exports.register) {
|
||||
exports.register(Color);
|
||||
}
|
||||
else {
|
||||
// No register method, just add the module's functions
|
||||
for (let name in exports) {
|
||||
Color.defineFunction(name, exports[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Color.defineFunctions({
|
||||
get,
|
||||
getAll,
|
||||
set,
|
||||
setAll,
|
||||
to,
|
||||
equals,
|
||||
inGamut,
|
||||
toGamut,
|
||||
distance,
|
||||
toString: serialize,
|
||||
});
|
||||
|
||||
Object.assign(Color, {
|
||||
util,
|
||||
hooks,
|
||||
WHITES,
|
||||
Space: ColorSpace,
|
||||
spaces: ColorSpace.registry,
|
||||
parse,
|
||||
|
||||
// Global defaults one may want to configure
|
||||
defaults,
|
||||
});
|
||||
28
node_modules/colorjs.io/src/contrast.js
generated
vendored
Normal file
28
node_modules/colorjs.io/src/contrast.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import getColor from "./getColor.js";
|
||||
// import defaults from "./defaults.js";
|
||||
import {isString} from "./util.js";
|
||||
import * as contrastAlgorithms from "./contrast/index.js";
|
||||
|
||||
export default function contrast (background, foreground, o = {}) {
|
||||
if (isString(o)) {
|
||||
o = {algorithm: o};
|
||||
}
|
||||
|
||||
let {algorithm, ...rest} = o;
|
||||
|
||||
if (!algorithm) {
|
||||
let algorithms = Object.keys(contrastAlgorithms).map(a => a.replace(/^contrast/, "")).join(", ");
|
||||
throw new TypeError(`contrast() function needs a contrast algorithm. Please specify one of: ${algorithms}`);
|
||||
}
|
||||
|
||||
background = getColor(background);
|
||||
foreground = getColor(foreground);
|
||||
|
||||
for (let a in contrastAlgorithms) {
|
||||
if ("contrast" + algorithm.toLowerCase() === a.toLowerCase()) {
|
||||
return contrastAlgorithms[a](background, foreground, rest);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(`Unknown contrast algorithm: ${algorithm}`);
|
||||
}
|
||||
102
node_modules/colorjs.io/src/contrast/APCA.js
generated
vendored
Normal file
102
node_modules/colorjs.io/src/contrast/APCA.js
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
// APCA 0.0.98G
|
||||
// https://github.com/Myndex/apca-w3
|
||||
// see also https://github.com/w3c/silver/issues/643
|
||||
|
||||
import getColor from "../getColor.js";
|
||||
import to from "../to.js";
|
||||
|
||||
// exponents
|
||||
const normBG = 0.56;
|
||||
const normTXT = 0.57;
|
||||
const revTXT = 0.62;
|
||||
const revBG = 0.65;
|
||||
|
||||
// clamps
|
||||
const blkThrs = 0.022;
|
||||
const blkClmp = 1.414;
|
||||
const loClip = 0.1;
|
||||
const deltaYmin = 0.0005;
|
||||
|
||||
// scalers
|
||||
// see https://github.com/w3c/silver/issues/645
|
||||
const scaleBoW = 1.14;
|
||||
const loBoWoffset = 0.027;
|
||||
const scaleWoB = 1.14;
|
||||
const loWoBoffset = 0.027;
|
||||
|
||||
function fclamp (Y) {
|
||||
if (Y >= blkThrs) {
|
||||
return Y;
|
||||
}
|
||||
return Y + (blkThrs - Y) ** blkClmp;
|
||||
}
|
||||
|
||||
function linearize (val) {
|
||||
let sign = val < 0 ? -1 : 1;
|
||||
let abs = Math.abs(val);
|
||||
return sign * Math.pow(abs, 2.4);
|
||||
}
|
||||
|
||||
// Not symmetric, requires a foreground (text) color, and a background color
|
||||
export default function contrastAPCA (background, foreground) {
|
||||
foreground = getColor(foreground);
|
||||
background = getColor(background);
|
||||
|
||||
let S;
|
||||
let C;
|
||||
let Sapc;
|
||||
|
||||
// Myndex as-published, assumes sRGB inputs
|
||||
let R, G, B;
|
||||
|
||||
foreground = to(foreground, "srgb");
|
||||
// Should these be clamped to in-gamut values?
|
||||
|
||||
// Calculates "screen luminance" with non-standard simple gamma EOTF
|
||||
// weights should be from CSS Color 4, not the ones here which are via Myndex and copied from Lindbloom
|
||||
[R, G, B] = foreground.coords;
|
||||
let lumTxt = linearize(R) * 0.2126729 + linearize(G) * 0.7151522 + linearize(B) * 0.0721750;
|
||||
|
||||
background = to(background, "srgb");
|
||||
[R, G, B] = background.coords;
|
||||
let lumBg = linearize(R) * 0.2126729 + linearize(G) * 0.7151522 + linearize(B) * 0.0721750;
|
||||
|
||||
// toe clamping of very dark values to account for flare
|
||||
let Ytxt = fclamp(lumTxt);
|
||||
let Ybg = fclamp(lumBg);
|
||||
|
||||
// are we "Black on White" (dark on light), or light on dark?
|
||||
let BoW = Ybg > Ytxt;
|
||||
|
||||
// why is this a delta, when Y is not perceptually uniform?
|
||||
// Answer: it is a noise gate, see
|
||||
// https://github.com/LeaVerou/color.js/issues/208
|
||||
if (Math.abs(Ybg - Ytxt) < deltaYmin) {
|
||||
C = 0;
|
||||
}
|
||||
else {
|
||||
if (BoW) {
|
||||
// dark text on light background
|
||||
S = Ybg ** normBG - Ytxt ** normTXT;
|
||||
C = S * scaleBoW;
|
||||
}
|
||||
else {
|
||||
// light text on dark background
|
||||
S = Ybg ** revBG - Ytxt ** revTXT;
|
||||
C = S * scaleWoB;
|
||||
}
|
||||
}
|
||||
if (Math.abs(C) < loClip) {
|
||||
Sapc = 0;
|
||||
}
|
||||
else if (C > 0) {
|
||||
// not clear whether Woffset is loBoWoffset or loWoBoffset
|
||||
// but they have the same value
|
||||
Sapc = C - loBoWoffset;
|
||||
}
|
||||
else {
|
||||
Sapc = C + loBoWoffset;
|
||||
}
|
||||
|
||||
return Sapc * 100;
|
||||
}
|
||||
17
node_modules/colorjs.io/src/contrast/Lstar.js
generated
vendored
Normal file
17
node_modules/colorjs.io/src/contrast/Lstar.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// CIE Lightness difference, as used by Google Material Design
|
||||
// Google HCT Tone is the same as CIE Lightness
|
||||
// https://material.io/blog/science-of-color-design
|
||||
|
||||
import getColor from "../getColor.js";
|
||||
import get from "../get.js";
|
||||
import lab from "../spaces/lab.js";
|
||||
|
||||
export default function contrastLstar (color1, color2) {
|
||||
color1 = getColor(color1);
|
||||
color2 = getColor(color2);
|
||||
|
||||
let L1 = get(color1, [lab, "l"]);
|
||||
let L2 = get(color2, [lab, "l"]);
|
||||
|
||||
return Math.abs(L1 - L2);
|
||||
}
|
||||
22
node_modules/colorjs.io/src/contrast/Michelson.js
generated
vendored
Normal file
22
node_modules/colorjs.io/src/contrast/Michelson.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Michelson luminance contrast
|
||||
// the relation between the spread and the sum of the two luminances
|
||||
// Symmetric, does not matter which is foreground and which is background
|
||||
// No black level compensation for flare.
|
||||
|
||||
import getColor from "../getColor.js";
|
||||
import {getLuminance} from "../luminance.js";
|
||||
|
||||
export default function contrastMichelson (color1, color2) {
|
||||
color1 = getColor(color1);
|
||||
color2 = getColor(color2);
|
||||
|
||||
let Y1 = Math.max(getLuminance(color1), 0);
|
||||
let Y2 = Math.max(getLuminance(color2), 0);
|
||||
|
||||
if (Y2 > Y1) {
|
||||
[Y1, Y2] = [Y2, Y1];
|
||||
}
|
||||
|
||||
let denom = (Y1 + Y2);
|
||||
return denom === 0 ? 0 : (Y1 - Y2) / denom;
|
||||
}
|
||||
20
node_modules/colorjs.io/src/contrast/WCAG21.js
generated
vendored
Normal file
20
node_modules/colorjs.io/src/contrast/WCAG21.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// WCAG 2.0 contrast https://www.w3.org/TR/WCAG20-TECHS/G18.html
|
||||
// Simple contrast, with fixed 5% viewing flare contribution
|
||||
// Symmetric, does not matter which is foreground and which is background
|
||||
|
||||
import getColor from "../getColor.js";
|
||||
import {getLuminance} from "../luminance.js";
|
||||
|
||||
export default function contrastWCAG21 (color1, color2) {
|
||||
color1 = getColor(color1);
|
||||
color2 = getColor(color2);
|
||||
|
||||
let Y1 = Math.max(getLuminance(color1), 0);
|
||||
let Y2 = Math.max(getLuminance(color2), 0);
|
||||
|
||||
if (Y2 > Y1) {
|
||||
[Y1, Y2] = [Y2, Y1];
|
||||
}
|
||||
|
||||
return (Y1 + .05) / (Y2 + .05);
|
||||
}
|
||||
27
node_modules/colorjs.io/src/contrast/Weber.js
generated
vendored
Normal file
27
node_modules/colorjs.io/src/contrast/Weber.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Weber luminance contrast
|
||||
// The difference between the two luminances divided by the lower luminance
|
||||
// Symmetric, does not matter which is foreground and which is background
|
||||
// No black level compensation for flare.
|
||||
|
||||
import getColor from "../getColor.js";
|
||||
import {getLuminance} from "../luminance.js";
|
||||
|
||||
// the darkest sRGB color above black is #000001 and this produces
|
||||
// a plain Weber contrast of ~45647.
|
||||
// So, setting the divide-by-zero result at 50000 is a reasonable
|
||||
// max clamp for the plain Weber
|
||||
const max = 50000;
|
||||
|
||||
export default function contrastWeber (color1, color2) {
|
||||
color1 = getColor(color1);
|
||||
color2 = getColor(color2);
|
||||
|
||||
let Y1 = Math.max(getLuminance(color1), 0);
|
||||
let Y2 = Math.max(getLuminance(color2), 0);
|
||||
|
||||
if (Y2 > Y1) {
|
||||
[Y1, Y2] = [Y2, Y1];
|
||||
}
|
||||
|
||||
return Y2 === 0 ? max : (Y1 - Y2) / Y2;
|
||||
}
|
||||
25
node_modules/colorjs.io/src/contrast/deltaPhi.js
generated
vendored
Normal file
25
node_modules/colorjs.io/src/contrast/deltaPhi.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Delta Phi Star perceptual lightness contrast
|
||||
// See https://github.com/Myndex/deltaphistar
|
||||
// The (difference between two Lstars each raised to phi) raised to (1/phi)
|
||||
// Symmetric, does not matter which is foreground and which is background
|
||||
|
||||
|
||||
import getColor from "../getColor.js";
|
||||
import get from "../get.js";
|
||||
import lab_d65 from "../spaces/lab-d65.js";
|
||||
|
||||
const phi = Math.pow(5, 0.5) * 0.5 + 0.5; // Math.phi can be used if Math.js
|
||||
|
||||
export default function contrastDeltaPhi (color1, color2) {
|
||||
color1 = getColor(color1);
|
||||
color2 = getColor(color2);
|
||||
|
||||
let Lstr1 = get(color1, [lab_d65, "l"]);
|
||||
let Lstr2 = get(color2, [lab_d65, "l"]);
|
||||
|
||||
let deltaPhiStar = Math.abs(Math.pow(Lstr1, phi) - Math.pow(Lstr2, phi));
|
||||
|
||||
let contrast = Math.pow(deltaPhiStar, (1 / phi)) * Math.SQRT2 - 40;
|
||||
|
||||
return (contrast < 7.5) ? 0.0 : contrast ;
|
||||
}
|
||||
6
node_modules/colorjs.io/src/contrast/index.js
generated
vendored
Normal file
6
node_modules/colorjs.io/src/contrast/index.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export {default as contrastWCAG21} from "./WCAG21.js";
|
||||
export {default as contrastAPCA} from "./APCA.js";
|
||||
export {default as contrastMichelson} from "./Michelson.js";
|
||||
export {default as contrastWeber} from "./Weber.js";
|
||||
export {default as contrastLstar} from "./Lstar.js";
|
||||
export {default as contrastDeltaPhi} from "./deltaPhi.js";
|
||||
12
node_modules/colorjs.io/src/defaults.js
generated
vendored
Normal file
12
node_modules/colorjs.io/src/defaults.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Global defaults one may want to configure
|
||||
export default {
|
||||
gamut_mapping: "css",
|
||||
precision: 5,
|
||||
deltaE: "76", // Default deltaE method
|
||||
verbose: globalThis?.process?.env?.NODE_ENV?.toLowerCase() !== "test",
|
||||
warn: function warn (msg) {
|
||||
if (this.verbose) {
|
||||
globalThis?.console?.warn?.(msg);
|
||||
}
|
||||
},
|
||||
};
|
||||
19
node_modules/colorjs.io/src/deltaE.js
generated
vendored
Normal file
19
node_modules/colorjs.io/src/deltaE.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import defaults from "./defaults.js";
|
||||
import {isString} from "./util.js";
|
||||
import deltaEMethods from "./deltaE/index.js";
|
||||
|
||||
export default function deltaE (c1, c2, o = {}) {
|
||||
if (isString(o)) {
|
||||
o = {method: o};
|
||||
}
|
||||
|
||||
let {method = defaults.deltaE, ...rest} = o;
|
||||
|
||||
for (let m in deltaEMethods) {
|
||||
if ("deltae" + method.toLowerCase() === m.toLowerCase()) {
|
||||
return deltaEMethods[m](c1, c2, rest);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(`Unknown deltaE method: ${method}`);
|
||||
}
|
||||
179
node_modules/colorjs.io/src/deltaE/deltaE2000.js
generated
vendored
Normal file
179
node_modules/colorjs.io/src/deltaE/deltaE2000.js
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
import defaults from "../defaults.js";
|
||||
import lab from "../spaces/lab.js";
|
||||
import lch from "../spaces/lch.js";
|
||||
import getColor from "../getColor.js";
|
||||
|
||||
// deltaE2000 is a statistically significant improvement
|
||||
// and is recommended by the CIE and Idealliance
|
||||
// especially for color differences less than 10 deltaE76
|
||||
// but is wicked complicated
|
||||
// and many implementations have small errors!
|
||||
// DeltaE2000 is also discontinuous; in case this
|
||||
// matters to you, use deltaECMC instead.
|
||||
|
||||
const Gfactor = 25 ** 7;
|
||||
const π = Math.PI;
|
||||
const r2d = 180 / π;
|
||||
const d2r = π / 180;
|
||||
|
||||
function pow7 (x) {
|
||||
// Faster than x ** 7 or Math.pow(x, 7)
|
||||
|
||||
const x2 = x * x;
|
||||
const x7 = x2 * x2 * x2 * x;
|
||||
|
||||
return x7;
|
||||
}
|
||||
|
||||
export default function (color, sample, {kL = 1, kC = 1, kH = 1} = {}) {
|
||||
[color, sample] = getColor([color, sample]);
|
||||
|
||||
// Given this color as the reference
|
||||
// and the function parameter as the sample,
|
||||
// calculate deltaE 2000.
|
||||
|
||||
// This implementation assumes the parametric
|
||||
// weighting factors kL, kC and kH
|
||||
// for the influence of viewing conditions
|
||||
// are all 1, as sadly seems typical.
|
||||
// kL should be increased for lightness texture or noise
|
||||
// and kC increased for chroma noise
|
||||
|
||||
let [L1, a1, b1] = lab.from(color);
|
||||
let C1 = lch.from(lab, [L1, a1, b1])[1];
|
||||
let [L2, a2, b2] = lab.from(sample);
|
||||
let C2 = lch.from(lab, [L2, a2, b2])[1];
|
||||
|
||||
// Check for negative Chroma,
|
||||
// which might happen through
|
||||
// direct user input of LCH values
|
||||
|
||||
if (C1 < 0) {
|
||||
C1 = 0;
|
||||
}
|
||||
if (C2 < 0) {
|
||||
C2 = 0;
|
||||
}
|
||||
|
||||
let Cbar = (C1 + C2) / 2; // mean Chroma
|
||||
|
||||
// calculate a-axis asymmetry factor from mean Chroma
|
||||
// this turns JND ellipses for near-neutral colors back into circles
|
||||
let C7 = pow7(Cbar);
|
||||
|
||||
let G = 0.5 * (1 - Math.sqrt(C7 / (C7 + Gfactor)));
|
||||
|
||||
// scale a axes by asymmetry factor
|
||||
// this by the way is why there is no Lab2000 colorspace
|
||||
let adash1 = (1 + G) * a1;
|
||||
let adash2 = (1 + G) * a2;
|
||||
|
||||
// calculate new Chroma from scaled a and original b axes
|
||||
let Cdash1 = Math.sqrt(adash1 ** 2 + b1 ** 2);
|
||||
let Cdash2 = Math.sqrt(adash2 ** 2 + b2 ** 2);
|
||||
|
||||
// calculate new hues, with zero hue for true neutrals
|
||||
// and in degrees, not radians
|
||||
|
||||
let h1 = (adash1 === 0 && b1 === 0) ? 0 : Math.atan2(b1, adash1);
|
||||
let h2 = (adash2 === 0 && b2 === 0) ? 0 : Math.atan2(b2, adash2);
|
||||
|
||||
if (h1 < 0) {
|
||||
h1 += 2 * π;
|
||||
}
|
||||
if (h2 < 0) {
|
||||
h2 += 2 * π;
|
||||
}
|
||||
|
||||
h1 *= r2d;
|
||||
h2 *= r2d;
|
||||
|
||||
// Lightness and Chroma differences; sign matters
|
||||
let ΔL = L2 - L1;
|
||||
let ΔC = Cdash2 - Cdash1;
|
||||
|
||||
// Hue difference, getting the sign correct
|
||||
let hdiff = h2 - h1;
|
||||
let hsum = h1 + h2;
|
||||
let habs = Math.abs(hdiff);
|
||||
let Δh;
|
||||
|
||||
if (Cdash1 * Cdash2 === 0) {
|
||||
Δh = 0;
|
||||
}
|
||||
else if (habs <= 180) {
|
||||
Δh = hdiff;
|
||||
}
|
||||
else if (hdiff > 180) {
|
||||
Δh = hdiff - 360;
|
||||
}
|
||||
else if (hdiff < -180) {
|
||||
Δh = hdiff + 360;
|
||||
}
|
||||
else {
|
||||
defaults.warn("the unthinkable has happened");
|
||||
}
|
||||
|
||||
// weighted Hue difference, more for larger Chroma
|
||||
let ΔH = 2 * Math.sqrt(Cdash2 * Cdash1) * Math.sin(Δh * d2r / 2);
|
||||
|
||||
// calculate mean Lightness and Chroma
|
||||
let Ldash = (L1 + L2) / 2;
|
||||
let Cdash = (Cdash1 + Cdash2) / 2;
|
||||
let Cdash7 = pow7(Cdash);
|
||||
|
||||
// Compensate for non-linearity in the blue region of Lab.
|
||||
// Four possibilities for hue weighting factor,
|
||||
// depending on the angles, to get the correct sign
|
||||
let hdash;
|
||||
if (Cdash1 * Cdash2 === 0) {
|
||||
hdash = hsum; // which should be zero
|
||||
}
|
||||
else if (habs <= 180) {
|
||||
hdash = hsum / 2;
|
||||
}
|
||||
else if (hsum < 360) {
|
||||
hdash = (hsum + 360) / 2;
|
||||
}
|
||||
else {
|
||||
hdash = (hsum - 360) / 2;
|
||||
}
|
||||
|
||||
// positional corrections to the lack of uniformity of CIELAB
|
||||
// These are all trying to make JND ellipsoids more like spheres
|
||||
|
||||
// SL Lightness crispening factor
|
||||
// a background with L=50 is assumed
|
||||
let lsq = (Ldash - 50) ** 2;
|
||||
let SL = 1 + ((0.015 * lsq) / Math.sqrt(20 + lsq));
|
||||
|
||||
// SC Chroma factor, similar to those in CMC and deltaE 94 formulae
|
||||
let SC = 1 + 0.045 * Cdash;
|
||||
|
||||
// Cross term T for blue non-linearity
|
||||
let T = 1;
|
||||
T -= (0.17 * Math.cos(( hdash - 30) * d2r));
|
||||
T += (0.24 * Math.cos( 2 * hdash * d2r));
|
||||
T += (0.32 * Math.cos(((3 * hdash) + 6) * d2r));
|
||||
T -= (0.20 * Math.cos(((4 * hdash) - 63) * d2r));
|
||||
|
||||
// SH Hue factor depends on Chroma,
|
||||
// as well as adjusted hue angle like deltaE94.
|
||||
let SH = 1 + 0.015 * Cdash * T;
|
||||
|
||||
// RT Hue rotation term compensates for rotation of JND ellipses
|
||||
// and Munsell constant hue lines
|
||||
// in the medium-high Chroma blue region
|
||||
// (Hue 225 to 315)
|
||||
let Δθ = 30 * Math.exp(-1 * (((hdash - 275) / 25) ** 2));
|
||||
let RC = 2 * Math.sqrt(Cdash7 / (Cdash7 + Gfactor));
|
||||
let RT = -1 * Math.sin(2 * Δθ * d2r) * RC;
|
||||
|
||||
// Finally calculate the deltaE, term by term as root sume of squares
|
||||
let dE = (ΔL / (kL * SL)) ** 2;
|
||||
dE += (ΔC / (kC * SC)) ** 2;
|
||||
dE += (ΔH / (kH * SH)) ** 2;
|
||||
dE += RT * (ΔC / (kC * SC)) * (ΔH / (kH * SH));
|
||||
return Math.sqrt(dE);
|
||||
// Yay!!!
|
||||
}
|
||||
7
node_modules/colorjs.io/src/deltaE/deltaE76.js
generated
vendored
Normal file
7
node_modules/colorjs.io/src/deltaE/deltaE76.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import distance from "../distance.js";
|
||||
import getColor from "../getColor.js";
|
||||
|
||||
export default function deltaE76 (color, sample) {
|
||||
// Assume getColor() is called in the distance function
|
||||
return distance(color, sample, "lab");
|
||||
}
|
||||
114
node_modules/colorjs.io/src/deltaE/deltaECMC.js
generated
vendored
Normal file
114
node_modules/colorjs.io/src/deltaE/deltaECMC.js
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
import lab from "../spaces/lab.js";
|
||||
import lch from "../spaces/lch.js";
|
||||
import getColor from "../getColor.js";
|
||||
|
||||
// More accurate color-difference formulae
|
||||
// than the simple 1976 Euclidean distance in Lab
|
||||
|
||||
// CMC by the Color Measurement Committee of the
|
||||
// Bradford Society of Dyeists and Colorsts, 1994.
|
||||
// Uses LCH rather than Lab,
|
||||
// with different weights for L, C and H differences
|
||||
// A nice increase in accuracy for modest increase in complexity
|
||||
const π = Math.PI;
|
||||
const d2r = π / 180;
|
||||
|
||||
export default function (color, sample, {l = 2, c = 1} = {}) {
|
||||
[color, sample] = getColor([color, sample]);
|
||||
|
||||
// Given this color as the reference
|
||||
// and a sample,
|
||||
// calculate deltaE CMC.
|
||||
|
||||
// This implementation assumes the parametric
|
||||
// weighting factors l:c are 2:1
|
||||
// which is typical for non-textile uses.
|
||||
|
||||
let [L1, a1, b1] = lab.from(color);
|
||||
let [, C1, H1] = lch.from(lab, [L1, a1, b1]);
|
||||
let [L2, a2, b2] = lab.from(sample);
|
||||
let C2 = lch.from(lab, [L2, a2, b2])[1];
|
||||
|
||||
// let [L1, a1, b1] = color.getAll(lab);
|
||||
// let C1 = color.get("lch.c");
|
||||
// let H1 = color.get("lch.h");
|
||||
// let [L2, a2, b2] = sample.getAll(lab);
|
||||
// let C2 = sample.get("lch.c");
|
||||
|
||||
// Check for negative Chroma,
|
||||
// which might happen through
|
||||
// direct user input of LCH values
|
||||
|
||||
if (C1 < 0) {
|
||||
C1 = 0;
|
||||
}
|
||||
if (C2 < 0) {
|
||||
C2 = 0;
|
||||
}
|
||||
|
||||
// we don't need H2 as ΔH is calculated from Δa, Δb and ΔC
|
||||
|
||||
// Lightness and Chroma differences
|
||||
// These are (color - sample), unlike deltaE2000
|
||||
let ΔL = L1 - L2;
|
||||
let ΔC = C1 - C2;
|
||||
|
||||
let Δa = a1 - a2;
|
||||
let Δb = b1 - b2;
|
||||
|
||||
// weighted Hue difference, less for larger Chroma difference
|
||||
|
||||
let H2 = (Δa ** 2) + (Δb ** 2) - (ΔC ** 2);
|
||||
// due to roundoff error it is possible that, for zero a and b,
|
||||
// ΔC > Δa + Δb is 0, resulting in attempting
|
||||
// to take the square root of a negative number
|
||||
|
||||
// trying instead the equation from Industrial Color Physics
|
||||
// By Georg A. Klein
|
||||
|
||||
// let ΔH = ((a1 * b2) - (a2 * b1)) / Math.sqrt(0.5 * ((C2 * C1) + (a2 * a1) + (b2 * b1)));
|
||||
// console.log({ΔH});
|
||||
// This gives the same result to 12 decimal places
|
||||
// except it sometimes NaNs when trying to root a negative number
|
||||
|
||||
// let ΔH = Math.sqrt(H2); we never actually use the root, it gets squared again!!
|
||||
|
||||
// positional corrections to the lack of uniformity of CIELAB
|
||||
// These are all trying to make JND ellipsoids more like spheres
|
||||
|
||||
// SL Lightness crispening factor, depends entirely on L1 not L2
|
||||
let SL = 0.511; // linear portion of the Y to L transfer function
|
||||
if (L1 >= 16) { // cubic portion
|
||||
SL = (0.040975 * L1) / (1 + 0.01765 * L1);
|
||||
}
|
||||
|
||||
// SC Chroma factor
|
||||
let SC = ((0.0638 * C1) / (1 + 0.0131 * C1)) + 0.638;
|
||||
|
||||
// Cross term T for blue non-linearity
|
||||
let T;
|
||||
if (Number.isNaN(H1)) {
|
||||
H1 = 0;
|
||||
}
|
||||
|
||||
if (H1 >= 164 && H1 <= 345) {
|
||||
T = 0.56 + Math.abs(0.2 * Math.cos((H1 + 168) * d2r));
|
||||
}
|
||||
else {
|
||||
T = 0.36 + Math.abs(0.4 * Math.cos((H1 + 35) * d2r));
|
||||
}
|
||||
// console.log({T});
|
||||
|
||||
// SH Hue factor also depends on C1,
|
||||
let C4 = Math.pow(C1, 4);
|
||||
let F = Math.sqrt(C4 / (C4 + 1900));
|
||||
let SH = SC * ((F * T) + 1 - F);
|
||||
|
||||
// Finally calculate the deltaE, term by term as root sume of squares
|
||||
let dE = (ΔL / (l * SL)) ** 2;
|
||||
dE += (ΔC / (c * SC)) ** 2;
|
||||
dE += (H2 / (SH ** 2));
|
||||
// dE += (ΔH / SH) ** 2;
|
||||
return Math.sqrt(dE);
|
||||
// Yay!!!
|
||||
}
|
||||
51
node_modules/colorjs.io/src/deltaE/deltaEHCT.js
generated
vendored
Normal file
51
node_modules/colorjs.io/src/deltaE/deltaEHCT.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
import hct from "../spaces/hct.js";
|
||||
import {viewingConditions} from "../spaces/hct.js";
|
||||
import getColor from "../getColor.js";
|
||||
|
||||
const rad2deg = 180 / Math.PI;
|
||||
const deg2rad = Math.PI / 180;
|
||||
const ucsCoeff = [1.00, 0.007, 0.0228];
|
||||
|
||||
/**
|
||||
* Convert HCT chroma and hue (CAM16 JMh colorfulness and hue) using UCS logic for a and b.
|
||||
* @param {number[]} coords - HCT coordinates.
|
||||
* @return {number[]}
|
||||
*/
|
||||
function convertUcsAb (coords) {
|
||||
// We want the distance between the actual color.
|
||||
// If chroma is negative, it will throw off our calculations.
|
||||
// Normally, converting back to the base and forward will correct it.
|
||||
// If we have a negative chroma after this, then we have a color that
|
||||
// cannot resolve to positive chroma.
|
||||
if (coords[1] < 0) {
|
||||
coords = hct.fromBase(hct.toBase(coords));
|
||||
}
|
||||
|
||||
// Only in extreme cases (usually outside the visible spectrum)
|
||||
// can the input value for log become negative.
|
||||
// Avoid domain error by forcing a zero result via "max" if necessary.
|
||||
const M = Math.log(Math.max(1 + ucsCoeff[2] * coords[1] * viewingConditions.flRoot, 1.0)) / ucsCoeff[2];
|
||||
const hrad = coords[0] * deg2rad;
|
||||
const a = M * Math.cos(hrad);
|
||||
const b = M * Math.sin(hrad);
|
||||
|
||||
return [coords[2], a, b];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Color distance using HCT.
|
||||
* @param {Color} color - Color to compare.
|
||||
* @param {Color} sample - Color to compare.
|
||||
* @return {number[]}
|
||||
*/
|
||||
export default function (color, sample) {
|
||||
[color, sample] = getColor([color, sample]);
|
||||
|
||||
let [ t1, a1, b1 ] = convertUcsAb(hct.from(color));
|
||||
let [ t2, a2, b2 ] = convertUcsAb(hct.from(sample));
|
||||
|
||||
// Use simple euclidean distance with a and b using UCS conversion
|
||||
// and LCh lightness (HCT tone).
|
||||
return Math.sqrt((t1 - t2) ** 2 + (a1 - a2) ** 2 + (b1 - b2) ** 2);
|
||||
}
|
||||
24
node_modules/colorjs.io/src/deltaE/deltaEITP.js
generated
vendored
Normal file
24
node_modules/colorjs.io/src/deltaE/deltaEITP.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import ictcp from "../spaces/ictcp.js";
|
||||
import getColor from "../getColor.js";
|
||||
|
||||
// Delta E in ICtCp space,
|
||||
// which the ITU calls Delta E ITP, which is shorter
|
||||
// formulae from ITU Rec. ITU-R BT.2124-0
|
||||
|
||||
export default function (color, sample) {
|
||||
[color, sample] = getColor([color, sample]);
|
||||
|
||||
// Given this color as the reference
|
||||
// and a sample,
|
||||
// calculate deltaE in ICtCp
|
||||
// which is simply the Euclidean distance
|
||||
|
||||
let [ I1, T1, P1 ] = ictcp.from(color);
|
||||
let [ I2, T2, P2 ] = ictcp.from(sample);
|
||||
|
||||
// the 0.25 factor is to undo the encoding scaling in Ct
|
||||
// the 720 is so that 1 deltaE = 1 JND
|
||||
// per ITU-R BT.2124-0 p.3
|
||||
|
||||
return 720 * Math.sqrt((I1 - I2) ** 2 + (0.25 * (T1 - T2) ** 2) + (P1 - P2) ** 2);
|
||||
}
|
||||
43
node_modules/colorjs.io/src/deltaE/deltaEJz.js
generated
vendored
Normal file
43
node_modules/colorjs.io/src/deltaE/deltaEJz.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import jzczhz from "../spaces/jzczhz.js";
|
||||
import getColor from "../getColor.js";
|
||||
|
||||
// More accurate color-difference formulae
|
||||
// than the simple 1976 Euclidean distance in Lab
|
||||
|
||||
// Uses JzCzHz, which has improved perceptual uniformity
|
||||
// and thus a simple Euclidean root-sum of ΔL² ΔC² ΔH²
|
||||
// gives good results.
|
||||
|
||||
export default function (color, sample) {
|
||||
[color, sample] = getColor([color, sample]);
|
||||
|
||||
// Given this color as the reference
|
||||
// and a sample,
|
||||
// calculate deltaE in JzCzHz.
|
||||
let [Jz1, Cz1, Hz1] = jzczhz.from(color);
|
||||
let [Jz2, Cz2, Hz2] = jzczhz.from(sample);
|
||||
|
||||
// Lightness and Chroma differences
|
||||
// sign does not matter as they are squared.
|
||||
let ΔJ = Jz1 - Jz2;
|
||||
let ΔC = Cz1 - Cz2;
|
||||
|
||||
// length of chord for ΔH
|
||||
if ((Number.isNaN(Hz1)) && (Number.isNaN(Hz2))) {
|
||||
// both undefined hues
|
||||
Hz1 = 0;
|
||||
Hz2 = 0;
|
||||
}
|
||||
else if (Number.isNaN(Hz1)) {
|
||||
// one undefined, set to the defined hue
|
||||
Hz1 = Hz2;
|
||||
}
|
||||
else if (Number.isNaN(Hz2)) {
|
||||
Hz2 = Hz1;
|
||||
}
|
||||
|
||||
let Δh = Hz1 - Hz2;
|
||||
let ΔH = 2 * Math.sqrt(Cz1 * Cz2) * Math.sin((Δh / 2) * (Math.PI / 180));
|
||||
|
||||
return Math.sqrt(ΔJ ** 2 + ΔC ** 2 + ΔH ** 2);
|
||||
}
|
||||
19
node_modules/colorjs.io/src/deltaE/deltaEOK.js
generated
vendored
Normal file
19
node_modules/colorjs.io/src/deltaE/deltaEOK.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// More accurate color-difference formulae
|
||||
// than the simple 1976 Euclidean distance in CIE Lab
|
||||
|
||||
import oklab from "../spaces/oklab.js";
|
||||
import getColor from "../getColor.js";
|
||||
|
||||
export default function (color, sample) {
|
||||
[color, sample] = getColor([color, sample]);
|
||||
|
||||
// Given this color as the reference
|
||||
// and a sample,
|
||||
// calculate deltaEOK, term by term as root sum of squares
|
||||
let [L1, a1, b1] = oklab.from(color);
|
||||
let [L2, a2, b2] = oklab.from(sample);
|
||||
let ΔL = L1 - L2;
|
||||
let Δa = a1 - a2;
|
||||
let Δb = b1 - b2;
|
||||
return Math.sqrt(ΔL ** 2 + Δa ** 2 + Δb ** 2);
|
||||
}
|
||||
27
node_modules/colorjs.io/src/deltaE/index.js
generated
vendored
Normal file
27
node_modules/colorjs.io/src/deltaE/index.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import deltaE76 from "./deltaE76.js";
|
||||
import deltaECMC from "./deltaECMC.js";
|
||||
import deltaE2000 from "./deltaE2000.js";
|
||||
import deltaEJz from "./deltaEJz.js";
|
||||
import deltaEITP from "./deltaEITP.js";
|
||||
import deltaEOK from "./deltaEOK.js";
|
||||
import deltaEHCT from "./deltaEHCT.js";
|
||||
|
||||
export {
|
||||
deltaE76,
|
||||
deltaECMC,
|
||||
deltaE2000,
|
||||
deltaEJz,
|
||||
deltaEITP,
|
||||
deltaEOK,
|
||||
deltaEHCT,
|
||||
};
|
||||
|
||||
export default {
|
||||
deltaE76,
|
||||
deltaECMC,
|
||||
deltaE2000,
|
||||
deltaEJz,
|
||||
deltaEITP,
|
||||
deltaEOK,
|
||||
deltaEHCT,
|
||||
};
|
||||
83
node_modules/colorjs.io/src/display.js
generated
vendored
Normal file
83
node_modules/colorjs.io/src/display.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
import { isNone, skipNone } from "./util.js";
|
||||
import defaults from "./defaults.js";
|
||||
import to from "./to.js";
|
||||
import serialize from "./serialize.js";
|
||||
import clone from "./clone.js";
|
||||
import REC2020 from "./spaces/rec2020.js";
|
||||
import P3 from "./spaces/p3.js";
|
||||
import Lab from "./spaces/lab.js";
|
||||
import sRGB from "./spaces/srgb.js";
|
||||
|
||||
// Default space for CSS output. Code in Color.js makes this wider if there's a DOM available
|
||||
defaults.display_space = sRGB;
|
||||
|
||||
let supportsNone;
|
||||
|
||||
if (typeof CSS !== "undefined" && CSS.supports) {
|
||||
// Find widest supported color space for CSS
|
||||
for (let space of [Lab, REC2020, P3]) {
|
||||
let coords = space.getMinCoords();
|
||||
let color = {space, coords, alpha: 1};
|
||||
let str = serialize(color);
|
||||
|
||||
if (CSS.supports("color", str)) {
|
||||
defaults.display_space = space;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a serialization of the color that can actually be displayed in the browser.
|
||||
* If the default serialization can be displayed, it is returned.
|
||||
* Otherwise, the color is converted to Lab, REC2020, or P3, whichever is the widest supported.
|
||||
* In Node.js, this is basically equivalent to `serialize()` but returns a `String` object instead.
|
||||
*
|
||||
* @export
|
||||
* @param {{space, coords} | Color | string} color
|
||||
* @param {*} [options={}] Options to be passed to serialize()
|
||||
* @param {ColorSpace | string} [options.space = defaults.display_space] Color space to use for serialization if default is not supported
|
||||
* @returns {String} String object containing the serialized color with a color property containing the converted color (or the original, if no conversion was necessary)
|
||||
*/
|
||||
export default function display (color, {space = defaults.display_space, ...options} = {}) {
|
||||
let ret = serialize(color, options);
|
||||
|
||||
if (typeof CSS === "undefined" || CSS.supports("color", ret) || !defaults.display_space) {
|
||||
ret = new String(ret);
|
||||
ret.color = color;
|
||||
}
|
||||
else {
|
||||
// If we're here, what we were about to output is not supported
|
||||
let fallbackColor = color;
|
||||
|
||||
// First, check if the culprit is none values
|
||||
let hasNone = color.coords.some(isNone) || isNone(color.alpha);
|
||||
|
||||
if (hasNone) {
|
||||
// Does the browser support none values?
|
||||
if (!(supportsNone ??= CSS.supports("color", "hsl(none 50% 50%)"))) {
|
||||
// Nope, try again without none
|
||||
fallbackColor = clone(color);
|
||||
fallbackColor.coords = fallbackColor.coords.map(skipNone);
|
||||
fallbackColor.alpha = skipNone(fallbackColor.alpha);
|
||||
|
||||
ret = serialize(fallbackColor, options);
|
||||
|
||||
if (CSS.supports("color", ret)) {
|
||||
// We're done, now it's supported
|
||||
ret = new String(ret);
|
||||
ret.color = fallbackColor;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're here, the color function is not supported
|
||||
// Fall back to fallback space
|
||||
fallbackColor = to(fallbackColor, space);
|
||||
ret = new String(serialize(fallbackColor, options));
|
||||
ret.color = fallbackColor;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
21
node_modules/colorjs.io/src/distance.js
generated
vendored
Normal file
21
node_modules/colorjs.io/src/distance.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import ColorSpace from "./space.js";
|
||||
|
||||
/**
|
||||
* Euclidean distance of colors in an arbitrary color space
|
||||
*/
|
||||
export default function distance (color1, color2, space = "lab") {
|
||||
space = ColorSpace.get(space);
|
||||
|
||||
// Assume getColor() is called on color in space.from()
|
||||
let coords1 = space.from(color1);
|
||||
let coords2 = space.from(color2);
|
||||
|
||||
return Math.sqrt(coords1.reduce((acc, c1, i) => {
|
||||
let c2 = coords2[i];
|
||||
if (isNaN(c1) || isNaN(c2)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
return acc + (c2 - c1) ** 2;
|
||||
}, 0));
|
||||
}
|
||||
10
node_modules/colorjs.io/src/equals.js
generated
vendored
Normal file
10
node_modules/colorjs.io/src/equals.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import getColor from "./getColor.js";
|
||||
|
||||
export default function equals (color1, color2) {
|
||||
color1 = getColor(color1);
|
||||
color2 = getColor(color2);
|
||||
|
||||
return color1.space === color2.space
|
||||
&& color1.alpha === color2.alpha
|
||||
&& color1.coords.every((c, i) => c === color2.coords[i]);
|
||||
}
|
||||
11
node_modules/colorjs.io/src/get.js
generated
vendored
Normal file
11
node_modules/colorjs.io/src/get.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import ColorSpace from "./space.js";
|
||||
import getAll from "./getAll.js";
|
||||
import getColor from "./getColor.js";
|
||||
|
||||
export default function get (color, prop) {
|
||||
color = getColor(color);
|
||||
|
||||
let {space, index} = ColorSpace.resolveCoord(prop, color.space);
|
||||
let coords = getAll(color, space);
|
||||
return coords[index];
|
||||
}
|
||||
20
node_modules/colorjs.io/src/getAll.js
generated
vendored
Normal file
20
node_modules/colorjs.io/src/getAll.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import ColorSpace from "./space.js";
|
||||
import getColor from "./getColor.js";
|
||||
|
||||
/**
|
||||
* Get the coordinates of a color in any color space
|
||||
* @param {Color} color
|
||||
* @param {string | ColorSpace} [space = color.space] The color space to convert to. Defaults to the color's current space
|
||||
* @returns {number[]} The color coordinates in the given color space
|
||||
*/
|
||||
export default function getAll (color, space) {
|
||||
color = getColor(color);
|
||||
|
||||
if (!space || color.space.equals(space)) {
|
||||
// No conversion needed
|
||||
return color.coords.slice();
|
||||
}
|
||||
|
||||
space = ColorSpace.get(space);
|
||||
return space.from(color);
|
||||
}
|
||||
36
node_modules/colorjs.io/src/getColor.js
generated
vendored
Normal file
36
node_modules/colorjs.io/src/getColor.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import ColorSpace from "./space.js";
|
||||
import {isString} from "./util.js";
|
||||
import parse from "./parse.js";
|
||||
|
||||
/**
|
||||
* Resolves a color reference (object or string) to a plain color object
|
||||
* @param {Color | {space, coords, alpha} | string | Array<Color | {space, coords, alpha} | string> } color
|
||||
* @returns {{space, coords, alpha} | Array<{space, coords, alpha}}>
|
||||
*/
|
||||
export default function getColor (color) {
|
||||
if (Array.isArray(color)) {
|
||||
return color.map(getColor);
|
||||
}
|
||||
|
||||
if (!color) {
|
||||
throw new TypeError("Empty color reference");
|
||||
}
|
||||
|
||||
if (isString(color)) {
|
||||
color = parse(color);
|
||||
}
|
||||
|
||||
// Object fixup
|
||||
let space = color.space || color.spaceId;
|
||||
|
||||
if (!(space instanceof ColorSpace)) {
|
||||
// Convert string id to color space object
|
||||
color.space = ColorSpace.get(space);
|
||||
}
|
||||
|
||||
if (color.alpha === undefined) {
|
||||
color.alpha = 1;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
37
node_modules/colorjs.io/src/hooks.js
generated
vendored
Normal file
37
node_modules/colorjs.io/src/hooks.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* A class for adding deep extensibility to any piece of JS code
|
||||
*/
|
||||
export class Hooks {
|
||||
add (name, callback, first) {
|
||||
if (typeof arguments[0] != "string") {
|
||||
// Multiple hooks
|
||||
for (var name in arguments[0]) {
|
||||
this.add(name, arguments[0][name], arguments[1]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
(Array.isArray(name) ? name : [name]).forEach(function (name) {
|
||||
this[name] = this[name] || [];
|
||||
|
||||
if (callback) {
|
||||
this[name][first ? "unshift" : "push"](callback);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
run (name, env) {
|
||||
this[name] = this[name] || [];
|
||||
this[name].forEach(function (callback) {
|
||||
callback.call(env && env.context ? env.context : env, env);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The instance of {@link Hooks} used throughout Color.js
|
||||
*/
|
||||
const hooks = new Hooks();
|
||||
|
||||
export default hooks;
|
||||
25
node_modules/colorjs.io/src/inGamut.js
generated
vendored
Normal file
25
node_modules/colorjs.io/src/inGamut.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import ColorSpace from "./space.js";
|
||||
import getColor from "./getColor.js";
|
||||
|
||||
const ε = .000075;
|
||||
|
||||
/**
|
||||
* Check if a color is in gamut of either its own or another color space
|
||||
* @return {Boolean} Is the color in gamut?
|
||||
*/
|
||||
export default function inGamut (color, space, {epsilon = ε} = {}) {
|
||||
color = getColor(color);
|
||||
|
||||
if (!space) {
|
||||
space = color.space;
|
||||
}
|
||||
|
||||
space = ColorSpace.get(space);
|
||||
let coords = color.coords;
|
||||
|
||||
if (space !== color.space) {
|
||||
coords = space.from(color);
|
||||
}
|
||||
|
||||
return space.inGamut(coords, {epsilon});
|
||||
}
|
||||
28
node_modules/colorjs.io/src/index-fn.js
generated
vendored
Normal file
28
node_modules/colorjs.io/src/index-fn.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
export {default as ColorSpace} from "./space.js";
|
||||
export {default as RGBColorSpace} from "./rgbspace.js";
|
||||
export {default as hooks, Hooks} from "./hooks.js";
|
||||
export {default as defaults} from "./defaults.js";
|
||||
export {default as getColor} from "./getColor.js";
|
||||
export {default as get} from "./get.js";
|
||||
export {default as getAll} from "./getAll.js";
|
||||
export {default as set} from "./set.js";
|
||||
export {default as setAll} from "./setAll.js";
|
||||
export {default as parse} from "./parse.js";
|
||||
export {default as to} from "./to.js";
|
||||
export {default as serialize} from "./serialize.js";
|
||||
export {default as display} from "./display.js";
|
||||
export {default as inGamut} from "./inGamut.js";
|
||||
export {default as toGamut, toGamutCSS} from "./toGamut.js";
|
||||
export {default as distance} from "./distance.js";
|
||||
export {default as equals} from "./equals.js";
|
||||
export {default as contrast} from "./contrast.js";
|
||||
export {default as clone} from "./clone.js";
|
||||
export { getLuminance, setLuminance } from "./luminance.js";
|
||||
export {uv, xy} from "./chromaticity.js";
|
||||
export * from "./contrast/index.js";
|
||||
export {default as deltaE} from "./deltaE.js";
|
||||
export * from "./deltaE/index.js";
|
||||
export {default as deltaEMethods} from "./deltaE/index.js";
|
||||
export * from "./variations.js";
|
||||
export { mix, steps, range, isRange } from "./interpolation.js";
|
||||
export * from "./spaces/index-fn.js";
|
||||
38
node_modules/colorjs.io/src/index.js
generated
vendored
Normal file
38
node_modules/colorjs.io/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Import all modules of Color.js
|
||||
import Color from "./color.js";
|
||||
|
||||
// Import all color spaces
|
||||
import "./spaces/index.js";
|
||||
|
||||
// Import all DeltaE methods
|
||||
import deltaE from "./deltaE.js";
|
||||
import deltaEMethods from "./deltaE/index.js";
|
||||
|
||||
Color.extend(deltaEMethods);
|
||||
Color.extend({deltaE});
|
||||
Object.assign(Color, {deltaEMethods});
|
||||
|
||||
// Import optional modules
|
||||
import * as variations from "./variations.js";
|
||||
Color.extend(variations);
|
||||
|
||||
import contrast from "./contrast.js";
|
||||
Color.extend({contrast});
|
||||
|
||||
import * as chromaticity from "./chromaticity.js";
|
||||
Color.extend(chromaticity);
|
||||
|
||||
import * as luminance from "./luminance.js";
|
||||
Color.extend(luminance);
|
||||
|
||||
import * as interpolation from "./interpolation.js";
|
||||
Color.extend(interpolation);
|
||||
|
||||
import * as contrastMethods from "./contrast/index.js";
|
||||
Color.extend(contrastMethods);
|
||||
|
||||
import "./CATs.js";
|
||||
import "./space-accessors.js";
|
||||
|
||||
// Re-export everything
|
||||
export default Color;
|
||||
222
node_modules/colorjs.io/src/interpolation.js
generated
vendored
Normal file
222
node_modules/colorjs.io/src/interpolation.js
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* Functions related to color interpolation
|
||||
*/
|
||||
import ColorSpace from "./space.js";
|
||||
import {type, interpolate} from "./util.js";
|
||||
import getColor from "./getColor.js";
|
||||
import clone from "./clone.js";
|
||||
import to from "./to.js";
|
||||
import toGamut from "./toGamut.js";
|
||||
import get from "./get.js";
|
||||
import set from "./set.js";
|
||||
import defaults from "./defaults.js";
|
||||
import * as angles from "./angles.js";
|
||||
import deltaE from "./deltaE.js";
|
||||
|
||||
/**
|
||||
* Return an intermediate color between two colors
|
||||
* Signatures: mix(c1, c2, p, options)
|
||||
* mix(c1, c2, options)
|
||||
* mix(color)
|
||||
* @param {Color | string} c1 The first color
|
||||
* @param {Color | string} [c2] The second color
|
||||
* @param {number} [p=.5] A 0-1 percentage where 0 is c1 and 1 is c2
|
||||
* @param {Object} [o={}]
|
||||
* @return {Color}
|
||||
*/
|
||||
export function mix (c1, c2, p = .5, o = {}) {
|
||||
[c1, c2] = [getColor(c1), getColor(c2)];
|
||||
|
||||
if (type(p) === "object") {
|
||||
[p, o] = [.5, p];
|
||||
}
|
||||
|
||||
let r = range(c1, c2, o);
|
||||
return r(p);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Color | string | Function} c1 The first color or a range
|
||||
* @param {Color | string} [c2] The second color if c1 is not a range
|
||||
* @param {Object} [options={}]
|
||||
* @return {Color[]}
|
||||
*/
|
||||
export function steps (c1, c2, options = {}) {
|
||||
let colorRange;
|
||||
|
||||
if (isRange(c1)) {
|
||||
// Tweaking existing range
|
||||
[colorRange, options] = [c1, c2];
|
||||
[c1, c2] = colorRange.rangeArgs.colors;
|
||||
}
|
||||
|
||||
let {
|
||||
maxDeltaE, deltaEMethod,
|
||||
steps = 2, maxSteps = 1000,
|
||||
...rangeOptions
|
||||
} = options;
|
||||
|
||||
if (!colorRange) {
|
||||
[c1, c2] = [getColor(c1), getColor(c2)];
|
||||
colorRange = range(c1, c2, rangeOptions);
|
||||
}
|
||||
|
||||
let totalDelta = deltaE(c1, c2);
|
||||
let actualSteps = maxDeltaE > 0 ? Math.max(steps, Math.ceil(totalDelta / maxDeltaE) + 1) : steps;
|
||||
let ret = [];
|
||||
|
||||
if (maxSteps !== undefined) {
|
||||
actualSteps = Math.min(actualSteps, maxSteps);
|
||||
}
|
||||
|
||||
if (actualSteps === 1) {
|
||||
ret = [{p: .5, color: colorRange(.5)}];
|
||||
}
|
||||
else {
|
||||
let step = 1 / (actualSteps - 1);
|
||||
ret = Array.from({length: actualSteps}, (_, i) => {
|
||||
let p = i * step;
|
||||
return {p, color: colorRange(p)};
|
||||
});
|
||||
}
|
||||
|
||||
if (maxDeltaE > 0) {
|
||||
// Iterate over all stops and find max deltaE
|
||||
let maxDelta = ret.reduce((acc, cur, i) => {
|
||||
if (i === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let ΔΕ = deltaE(cur.color, ret[i - 1].color, deltaEMethod);
|
||||
return Math.max(acc, ΔΕ);
|
||||
}, 0);
|
||||
|
||||
while (maxDelta > maxDeltaE) {
|
||||
// Insert intermediate stops and measure maxDelta again
|
||||
// We need to do this for all pairs, otherwise the midpoint shifts
|
||||
maxDelta = 0;
|
||||
|
||||
for (let i = 1; (i < ret.length) && (ret.length < maxSteps); i++) {
|
||||
let prev = ret[i - 1];
|
||||
let cur = ret[i];
|
||||
|
||||
let p = (cur.p + prev.p) / 2;
|
||||
let color = colorRange(p);
|
||||
maxDelta = Math.max(maxDelta, deltaE(color, prev.color), deltaE(color, cur.color));
|
||||
ret.splice(i, 0, {p, color: colorRange(p)});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = ret.map(a => a.color);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate to color2 and return a function that takes a 0-1 percentage
|
||||
* @param {Color | string | Function} color1 The first color or an existing range
|
||||
* @param {Color | string} [color2] If color1 is a color, this is the second color
|
||||
* @param {Object} [options={}]
|
||||
* @returns {Function} A function that takes a 0-1 percentage and returns a color
|
||||
*/
|
||||
export function range (color1, color2, options = {}) {
|
||||
if (isRange(color1)) {
|
||||
// Tweaking existing range
|
||||
let [r, options] = [color1, color2];
|
||||
|
||||
return range(...r.rangeArgs.colors, {...r.rangeArgs.options, ...options});
|
||||
}
|
||||
|
||||
let {space, outputSpace, progression, premultiplied} = options;
|
||||
|
||||
color1 = getColor(color1);
|
||||
color2 = getColor(color2);
|
||||
|
||||
// Make sure we're working on copies of these colors
|
||||
color1 = clone(color1);
|
||||
color2 = clone(color2);
|
||||
|
||||
let rangeArgs = {colors: [color1, color2], options};
|
||||
|
||||
if (space) {
|
||||
space = ColorSpace.get(space);
|
||||
}
|
||||
else {
|
||||
space = ColorSpace.registry[defaults.interpolationSpace] || color1.space;
|
||||
}
|
||||
|
||||
outputSpace = outputSpace ? ColorSpace.get(outputSpace) : space;
|
||||
|
||||
color1 = to(color1, space);
|
||||
color2 = to(color2, space);
|
||||
|
||||
// Gamut map to avoid areas of flat color
|
||||
color1 = toGamut(color1);
|
||||
color2 = toGamut(color2);
|
||||
|
||||
// Handle hue interpolation
|
||||
// See https://github.com/w3c/csswg-drafts/issues/4735#issuecomment-635741840
|
||||
if (space.coords.h && space.coords.h.type === "angle") {
|
||||
let arc = options.hue = options.hue || "shorter";
|
||||
|
||||
let hue = [space, "h"];
|
||||
let [θ1, θ2] = [get(color1, hue), get(color2, hue)];
|
||||
// Undefined hues must be evaluated before hue fix-up to properly
|
||||
// calculate hue arcs between undefined and defined hues.
|
||||
// See https://github.com/w3c/csswg-drafts/issues/9436#issuecomment-1746957545
|
||||
if (isNaN(θ1) && !isNaN(θ2)) {
|
||||
θ1 = θ2;
|
||||
}
|
||||
else if (isNaN(θ2) && !isNaN(θ1)) {
|
||||
θ2 = θ1;
|
||||
}
|
||||
[θ1, θ2] = angles.adjust(arc, [θ1, θ2]);
|
||||
set(color1, hue, θ1);
|
||||
set(color2, hue, θ2);
|
||||
}
|
||||
|
||||
if (premultiplied) {
|
||||
// not coping with polar spaces yet
|
||||
color1.coords = color1.coords.map(c => c * color1.alpha);
|
||||
color2.coords = color2.coords.map(c => c * color2.alpha);
|
||||
}
|
||||
|
||||
return Object.assign(p => {
|
||||
p = progression ? progression(p) : p;
|
||||
let coords = color1.coords.map((start, i) => {
|
||||
let end = color2.coords[i];
|
||||
return interpolate(start, end, p);
|
||||
});
|
||||
|
||||
let alpha = interpolate(color1.alpha, color2.alpha, p);
|
||||
let ret = {space, coords, alpha};
|
||||
|
||||
if (premultiplied) {
|
||||
// undo premultiplication
|
||||
ret.coords = ret.coords.map(c => c / alpha);
|
||||
}
|
||||
|
||||
if (outputSpace !== space) {
|
||||
ret = to(ret, outputSpace);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}, {
|
||||
rangeArgs,
|
||||
});
|
||||
}
|
||||
|
||||
export function isRange (val) {
|
||||
return type(val) === "function" && !!val.rangeArgs;
|
||||
}
|
||||
|
||||
defaults.interpolationSpace = "lab";
|
||||
|
||||
export function register (Color) {
|
||||
Color.defineFunction("mix", mix, {returns: "color"});
|
||||
Color.defineFunction("range", range, {returns: "function<color>"});
|
||||
Color.defineFunction("steps", steps, {returns: "array<color>"});
|
||||
}
|
||||
158
node_modules/colorjs.io/src/keywords.js
generated
vendored
Normal file
158
node_modules/colorjs.io/src/keywords.js
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
/* List of CSS color keywords
|
||||
* Note that this does not include currentColor, transparent,
|
||||
* or system colors
|
||||
*/
|
||||
|
||||
// To produce: Visit https://www.w3.org/TR/css-color-4/#named-colors
|
||||
// and run in the console:
|
||||
// copy($$("tr", $(".named-color-table tbody")).map(tr => `"${tr.cells[2].textContent.trim()}": [${tr.cells[4].textContent.trim().split(/\s+/).map(c => c === "0"? "0" : c === "255"? "1" : c + " / 255").join(", ")}]`).join(",\n"))
|
||||
export default {
|
||||
"aliceblue": [240 / 255, 248 / 255, 1],
|
||||
"antiquewhite": [250 / 255, 235 / 255, 215 / 255],
|
||||
"aqua": [0, 1, 1],
|
||||
"aquamarine": [127 / 255, 1, 212 / 255],
|
||||
"azure": [240 / 255, 1, 1],
|
||||
"beige": [245 / 255, 245 / 255, 220 / 255],
|
||||
"bisque": [1, 228 / 255, 196 / 255],
|
||||
"black": [0, 0, 0],
|
||||
"blanchedalmond": [1, 235 / 255, 205 / 255],
|
||||
"blue": [0, 0, 1],
|
||||
"blueviolet": [138 / 255, 43 / 255, 226 / 255],
|
||||
"brown": [165 / 255, 42 / 255, 42 / 255],
|
||||
"burlywood": [222 / 255, 184 / 255, 135 / 255],
|
||||
"cadetblue": [95 / 255, 158 / 255, 160 / 255],
|
||||
"chartreuse": [127 / 255, 1, 0],
|
||||
"chocolate": [210 / 255, 105 / 255, 30 / 255],
|
||||
"coral": [1, 127 / 255, 80 / 255],
|
||||
"cornflowerblue": [100 / 255, 149 / 255, 237 / 255],
|
||||
"cornsilk": [1, 248 / 255, 220 / 255],
|
||||
"crimson": [220 / 255, 20 / 255, 60 / 255],
|
||||
"cyan": [0, 1, 1],
|
||||
"darkblue": [0, 0, 139 / 255],
|
||||
"darkcyan": [0, 139 / 255, 139 / 255],
|
||||
"darkgoldenrod": [184 / 255, 134 / 255, 11 / 255],
|
||||
"darkgray": [169 / 255, 169 / 255, 169 / 255],
|
||||
"darkgreen": [0, 100 / 255, 0],
|
||||
"darkgrey": [169 / 255, 169 / 255, 169 / 255],
|
||||
"darkkhaki": [189 / 255, 183 / 255, 107 / 255],
|
||||
"darkmagenta": [139 / 255, 0, 139 / 255],
|
||||
"darkolivegreen": [85 / 255, 107 / 255, 47 / 255],
|
||||
"darkorange": [1, 140 / 255, 0],
|
||||
"darkorchid": [153 / 255, 50 / 255, 204 / 255],
|
||||
"darkred": [139 / 255, 0, 0],
|
||||
"darksalmon": [233 / 255, 150 / 255, 122 / 255],
|
||||
"darkseagreen": [143 / 255, 188 / 255, 143 / 255],
|
||||
"darkslateblue": [72 / 255, 61 / 255, 139 / 255],
|
||||
"darkslategray": [47 / 255, 79 / 255, 79 / 255],
|
||||
"darkslategrey": [47 / 255, 79 / 255, 79 / 255],
|
||||
"darkturquoise": [0, 206 / 255, 209 / 255],
|
||||
"darkviolet": [148 / 255, 0, 211 / 255],
|
||||
"deeppink": [1, 20 / 255, 147 / 255],
|
||||
"deepskyblue": [0, 191 / 255, 1],
|
||||
"dimgray": [105 / 255, 105 / 255, 105 / 255],
|
||||
"dimgrey": [105 / 255, 105 / 255, 105 / 255],
|
||||
"dodgerblue": [30 / 255, 144 / 255, 1],
|
||||
"firebrick": [178 / 255, 34 / 255, 34 / 255],
|
||||
"floralwhite": [1, 250 / 255, 240 / 255],
|
||||
"forestgreen": [34 / 255, 139 / 255, 34 / 255],
|
||||
"fuchsia": [1, 0, 1],
|
||||
"gainsboro": [220 / 255, 220 / 255, 220 / 255],
|
||||
"ghostwhite": [248 / 255, 248 / 255, 1],
|
||||
"gold": [1, 215 / 255, 0],
|
||||
"goldenrod": [218 / 255, 165 / 255, 32 / 255],
|
||||
"gray": [128 / 255, 128 / 255, 128 / 255],
|
||||
"green": [0, 128 / 255, 0],
|
||||
"greenyellow": [173 / 255, 1, 47 / 255],
|
||||
"grey": [128 / 255, 128 / 255, 128 / 255],
|
||||
"honeydew": [240 / 255, 1, 240 / 255],
|
||||
"hotpink": [1, 105 / 255, 180 / 255],
|
||||
"indianred": [205 / 255, 92 / 255, 92 / 255],
|
||||
"indigo": [75 / 255, 0, 130 / 255],
|
||||
"ivory": [1, 1, 240 / 255],
|
||||
"khaki": [240 / 255, 230 / 255, 140 / 255],
|
||||
"lavender": [230 / 255, 230 / 255, 250 / 255],
|
||||
"lavenderblush": [1, 240 / 255, 245 / 255],
|
||||
"lawngreen": [124 / 255, 252 / 255, 0],
|
||||
"lemonchiffon": [1, 250 / 255, 205 / 255],
|
||||
"lightblue": [173 / 255, 216 / 255, 230 / 255],
|
||||
"lightcoral": [240 / 255, 128 / 255, 128 / 255],
|
||||
"lightcyan": [224 / 255, 1, 1],
|
||||
"lightgoldenrodyellow": [250 / 255, 250 / 255, 210 / 255],
|
||||
"lightgray": [211 / 255, 211 / 255, 211 / 255],
|
||||
"lightgreen": [144 / 255, 238 / 255, 144 / 255],
|
||||
"lightgrey": [211 / 255, 211 / 255, 211 / 255],
|
||||
"lightpink": [1, 182 / 255, 193 / 255],
|
||||
"lightsalmon": [1, 160 / 255, 122 / 255],
|
||||
"lightseagreen": [32 / 255, 178 / 255, 170 / 255],
|
||||
"lightskyblue": [135 / 255, 206 / 255, 250 / 255],
|
||||
"lightslategray": [119 / 255, 136 / 255, 153 / 255],
|
||||
"lightslategrey": [119 / 255, 136 / 255, 153 / 255],
|
||||
"lightsteelblue": [176 / 255, 196 / 255, 222 / 255],
|
||||
"lightyellow": [1, 1, 224 / 255],
|
||||
"lime": [0, 1, 0],
|
||||
"limegreen": [50 / 255, 205 / 255, 50 / 255],
|
||||
"linen": [250 / 255, 240 / 255, 230 / 255],
|
||||
"magenta": [1, 0, 1],
|
||||
"maroon": [128 / 255, 0, 0],
|
||||
"mediumaquamarine": [102 / 255, 205 / 255, 170 / 255],
|
||||
"mediumblue": [0, 0, 205 / 255],
|
||||
"mediumorchid": [186 / 255, 85 / 255, 211 / 255],
|
||||
"mediumpurple": [147 / 255, 112 / 255, 219 / 255],
|
||||
"mediumseagreen": [60 / 255, 179 / 255, 113 / 255],
|
||||
"mediumslateblue": [123 / 255, 104 / 255, 238 / 255],
|
||||
"mediumspringgreen": [0, 250 / 255, 154 / 255],
|
||||
"mediumturquoise": [72 / 255, 209 / 255, 204 / 255],
|
||||
"mediumvioletred": [199 / 255, 21 / 255, 133 / 255],
|
||||
"midnightblue": [25 / 255, 25 / 255, 112 / 255],
|
||||
"mintcream": [245 / 255, 1, 250 / 255],
|
||||
"mistyrose": [1, 228 / 255, 225 / 255],
|
||||
"moccasin": [1, 228 / 255, 181 / 255],
|
||||
"navajowhite": [1, 222 / 255, 173 / 255],
|
||||
"navy": [0, 0, 128 / 255],
|
||||
"oldlace": [253 / 255, 245 / 255, 230 / 255],
|
||||
"olive": [128 / 255, 128 / 255, 0],
|
||||
"olivedrab": [107 / 255, 142 / 255, 35 / 255],
|
||||
"orange": [1, 165 / 255, 0],
|
||||
"orangered": [1, 69 / 255, 0],
|
||||
"orchid": [218 / 255, 112 / 255, 214 / 255],
|
||||
"palegoldenrod": [238 / 255, 232 / 255, 170 / 255],
|
||||
"palegreen": [152 / 255, 251 / 255, 152 / 255],
|
||||
"paleturquoise": [175 / 255, 238 / 255, 238 / 255],
|
||||
"palevioletred": [219 / 255, 112 / 255, 147 / 255],
|
||||
"papayawhip": [1, 239 / 255, 213 / 255],
|
||||
"peachpuff": [1, 218 / 255, 185 / 255],
|
||||
"peru": [205 / 255, 133 / 255, 63 / 255],
|
||||
"pink": [1, 192 / 255, 203 / 255],
|
||||
"plum": [221 / 255, 160 / 255, 221 / 255],
|
||||
"powderblue": [176 / 255, 224 / 255, 230 / 255],
|
||||
"purple": [128 / 255, 0, 128 / 255],
|
||||
"rebeccapurple": [102 / 255, 51 / 255, 153 / 255],
|
||||
"red": [1, 0, 0],
|
||||
"rosybrown": [188 / 255, 143 / 255, 143 / 255],
|
||||
"royalblue": [65 / 255, 105 / 255, 225 / 255],
|
||||
"saddlebrown": [139 / 255, 69 / 255, 19 / 255],
|
||||
"salmon": [250 / 255, 128 / 255, 114 / 255],
|
||||
"sandybrown": [244 / 255, 164 / 255, 96 / 255],
|
||||
"seagreen": [46 / 255, 139 / 255, 87 / 255],
|
||||
"seashell": [1, 245 / 255, 238 / 255],
|
||||
"sienna": [160 / 255, 82 / 255, 45 / 255],
|
||||
"silver": [192 / 255, 192 / 255, 192 / 255],
|
||||
"skyblue": [135 / 255, 206 / 255, 235 / 255],
|
||||
"slateblue": [106 / 255, 90 / 255, 205 / 255],
|
||||
"slategray": [112 / 255, 128 / 255, 144 / 255],
|
||||
"slategrey": [112 / 255, 128 / 255, 144 / 255],
|
||||
"snow": [1, 250 / 255, 250 / 255],
|
||||
"springgreen": [0, 1, 127 / 255],
|
||||
"steelblue": [70 / 255, 130 / 255, 180 / 255],
|
||||
"tan": [210 / 255, 180 / 255, 140 / 255],
|
||||
"teal": [0, 128 / 255, 128 / 255],
|
||||
"thistle": [216 / 255, 191 / 255, 216 / 255],
|
||||
"tomato": [1, 99 / 255, 71 / 255],
|
||||
"turquoise": [64 / 255, 224 / 255, 208 / 255],
|
||||
"violet": [238 / 255, 130 / 255, 238 / 255],
|
||||
"wheat": [245 / 255, 222 / 255, 179 / 255],
|
||||
"white": [1, 1, 1],
|
||||
"whitesmoke": [245 / 255, 245 / 255, 245 / 255],
|
||||
"yellow": [1, 1, 0],
|
||||
"yellowgreen": [154 / 255, 205 / 255, 50 / 255],
|
||||
};
|
||||
27
node_modules/colorjs.io/src/luminance.js
generated
vendored
Normal file
27
node_modules/colorjs.io/src/luminance.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Relative luminance
|
||||
*/
|
||||
import get from "./get.js";
|
||||
import set from "./set.js";
|
||||
import xyz_d65 from "./spaces/xyz-d65.js";
|
||||
|
||||
export function getLuminance (color) {
|
||||
// Assume getColor() is called on color in get()
|
||||
return get(color, [xyz_d65, "y"]);
|
||||
}
|
||||
|
||||
export function setLuminance (color, value) {
|
||||
// Assume getColor() is called on color in set()
|
||||
set(color, [xyz_d65, "y"], value);
|
||||
}
|
||||
|
||||
export function register (Color) {
|
||||
Object.defineProperty(Color.prototype, "luminance", {
|
||||
get () {
|
||||
return getLuminance(this);
|
||||
},
|
||||
set (value) {
|
||||
setLuminance(this, value);
|
||||
},
|
||||
});
|
||||
}
|
||||
44
node_modules/colorjs.io/src/multiply-matrices.js
generated
vendored
Normal file
44
node_modules/colorjs.io/src/multiply-matrices.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// A is m x n. B is n x p. product is m x p.
|
||||
export default function multiplyMatrices (A, B) {
|
||||
let m = A.length;
|
||||
|
||||
if (!Array.isArray(A[0])) {
|
||||
// A is vector, convert to [[a, b, c, ...]]
|
||||
A = [A];
|
||||
}
|
||||
|
||||
if (!Array.isArray(B[0])) {
|
||||
// B is vector, convert to [[a], [b], [c], ...]]
|
||||
B = B.map(x => [x]);
|
||||
}
|
||||
|
||||
let p = B[0].length;
|
||||
let B_cols = B[0].map((_, i) => B.map(x => x[i])); // transpose B
|
||||
let product = A.map(row => B_cols.map(col => {
|
||||
let ret = 0;
|
||||
|
||||
if (!Array.isArray(row)) {
|
||||
for (let c of col) {
|
||||
ret += row * c;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
ret += row[i] * (col[i] || 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}));
|
||||
|
||||
if (m === 1) {
|
||||
product = product[0]; // Avoid [[a, b, c, ...]]
|
||||
}
|
||||
|
||||
if (p === 1) {
|
||||
return product.map(x => x[0]); // Avoid [[a], [b], [c], ...]]
|
||||
}
|
||||
|
||||
return product;
|
||||
}
|
||||
198
node_modules/colorjs.io/src/parse.js
generated
vendored
Normal file
198
node_modules/colorjs.io/src/parse.js
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
import * as util from "./util.js";
|
||||
import hooks from "./hooks.js";
|
||||
import ColorSpace from "./space.js";
|
||||
import defaults from "./defaults.js";
|
||||
|
||||
const noneTypes = new Set(["<number>", "<percentage>", "<angle>"]);
|
||||
|
||||
/**
|
||||
* Validates the coordinates of a color against a format's coord grammar and
|
||||
* maps the coordinates to the range or refRange of the coordinates.
|
||||
* @param {ColorSpace} space - Colorspace the coords are in
|
||||
* @param {object} format - the format object to validate against
|
||||
* @param {string} name - the name of the color function. e.g. "oklab" or "color"
|
||||
* @returns {object[]} - an array of type metadata for each coordinate
|
||||
*/
|
||||
function coerceCoords (space, format, name, coords) {
|
||||
let types = Object.entries(space.coords).map(([id, coordMeta], i) => {
|
||||
let coordGrammar = format.coordGrammar[i];
|
||||
let arg = coords[i];
|
||||
let providedType = arg?.type;
|
||||
|
||||
// Find grammar alternative that matches the provided type
|
||||
// Non-strict equals is intentional because we are comparing w/ string objects
|
||||
let type;
|
||||
if (arg.none) {
|
||||
type = coordGrammar.find(c => noneTypes.has(c));
|
||||
}
|
||||
else {
|
||||
type = coordGrammar.find(c => c == providedType);
|
||||
}
|
||||
|
||||
// Check that each coord conforms to its grammar
|
||||
if (!type) {
|
||||
// Type does not exist in the grammar, throw
|
||||
let coordName = coordMeta.name || id;
|
||||
throw new TypeError(`${providedType ?? arg.raw} not allowed for ${coordName} in ${name}()`);
|
||||
}
|
||||
|
||||
let fromRange = type.range;
|
||||
|
||||
if (providedType === "<percentage>") {
|
||||
fromRange ||= [0, 1];
|
||||
}
|
||||
|
||||
let toRange = coordMeta.range || coordMeta.refRange;
|
||||
|
||||
if (fromRange && toRange) {
|
||||
coords[i] = util.mapRange(fromRange, toRange, coords[i]);
|
||||
}
|
||||
|
||||
return type;
|
||||
});
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a CSS Color string to a color object
|
||||
* @param {string} str
|
||||
* @param {object} [options]
|
||||
* @param {object} [options.meta] - Object for additional information about the parsing
|
||||
* @returns {Color}
|
||||
*/
|
||||
export default function parse (str, {meta} = {}) {
|
||||
let env = {"str": String(str)?.trim()};
|
||||
hooks.run("parse-start", env);
|
||||
|
||||
if (env.color) {
|
||||
return env.color;
|
||||
}
|
||||
|
||||
env.parsed = util.parseFunction(env.str);
|
||||
|
||||
if (env.parsed) {
|
||||
// Is a functional syntax
|
||||
let name = env.parsed.name;
|
||||
|
||||
if (name === "color") {
|
||||
// color() function
|
||||
let id = env.parsed.args.shift();
|
||||
// Check against both <dashed-ident> and <ident> versions
|
||||
let alternateId = id.startsWith("--") ? id.substring(2) : `--${id}`;
|
||||
let ids = [id, alternateId];
|
||||
let alpha = env.parsed.rawArgs.indexOf("/") > 0 ? env.parsed.args.pop() : 1;
|
||||
|
||||
for (let space of ColorSpace.all) {
|
||||
let colorSpec = space.getFormat("color");
|
||||
|
||||
if (colorSpec) {
|
||||
if (ids.includes(colorSpec.id) || colorSpec.ids?.filter((specId) => ids.includes(specId)).length) {
|
||||
// From https://drafts.csswg.org/css-color-4/#color-function
|
||||
// If more <number>s or <percentage>s are provided than parameters that the colorspace takes, the excess <number>s at the end are ignored.
|
||||
// If less <number>s or <percentage>s are provided than parameters that the colorspace takes, the missing parameters default to 0. (This is particularly convenient for multichannel printers where the additional inks are spot colors or varnishes that most colors on the page won’t use.)
|
||||
const coords = Object.keys(space.coords).map((_, i) => env.parsed.args[i] || 0);
|
||||
|
||||
let types;
|
||||
|
||||
if (colorSpec.coordGrammar) {
|
||||
types = coerceCoords(space, colorSpec, "color", coords);
|
||||
}
|
||||
|
||||
if (meta) {
|
||||
Object.assign(meta, {formatId: "color", types});
|
||||
}
|
||||
|
||||
if (colorSpec.id.startsWith("--") && !id.startsWith("--")) {
|
||||
defaults.warn(`${space.name} is a non-standard space and not currently supported in the CSS spec. ` +
|
||||
`Use prefixed color(${colorSpec.id}) instead of color(${id}).`);
|
||||
}
|
||||
if (id.startsWith("--") && !colorSpec.id.startsWith("--")) {
|
||||
defaults.warn(`${space.name} is a standard space and supported in the CSS spec. ` +
|
||||
`Use color(${colorSpec.id}) instead of prefixed color(${id}).`);
|
||||
}
|
||||
|
||||
return {spaceId: space.id, coords, alpha};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found
|
||||
let didYouMean = "";
|
||||
let registryId = id in ColorSpace.registry ? id : alternateId;
|
||||
if (registryId in ColorSpace.registry) {
|
||||
// Used color space id instead of color() id, these are often different
|
||||
let cssId = ColorSpace.registry[registryId].formats?.color?.id;
|
||||
|
||||
if (cssId) {
|
||||
didYouMean = `Did you mean color(${cssId})?`;
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(`Cannot parse color(${id}). ` + (didYouMean || "Missing a plugin?"));
|
||||
}
|
||||
else {
|
||||
for (let space of ColorSpace.all) {
|
||||
// color space specific function
|
||||
let format = space.getFormat(name);
|
||||
if (format && format.type === "function") {
|
||||
let alpha = 1;
|
||||
|
||||
if (format.lastAlpha || util.last(env.parsed.args).alpha) {
|
||||
alpha = env.parsed.args.pop();
|
||||
}
|
||||
|
||||
let coords = env.parsed.args;
|
||||
|
||||
let types;
|
||||
|
||||
if (format.coordGrammar) {
|
||||
types = coerceCoords(space, format, name, coords);
|
||||
}
|
||||
|
||||
if (meta) {
|
||||
Object.assign(meta, {formatId: format.name, types});
|
||||
}
|
||||
|
||||
return {
|
||||
spaceId: space.id,
|
||||
coords, alpha,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Custom, colorspace-specific format
|
||||
for (let space of ColorSpace.all) {
|
||||
for (let formatId in space.formats) {
|
||||
let format = space.formats[formatId];
|
||||
|
||||
if (format.type !== "custom") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (format.test && !format.test(env.str)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let color = format.parse(env.str);
|
||||
|
||||
if (color) {
|
||||
color.alpha ??= 1;
|
||||
|
||||
if (meta) {
|
||||
meta.formatId = formatId;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we're here, we couldn't parse
|
||||
throw new TypeError(`Could not parse ${str} as a color. Missing a plugin?`);
|
||||
}
|
||||
64
node_modules/colorjs.io/src/rgbspace.js
generated
vendored
Normal file
64
node_modules/colorjs.io/src/rgbspace.js
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import ColorSpace from "./space.js";
|
||||
import {multiplyMatrices} from "./util.js";
|
||||
import adapt from "./adapt.js";
|
||||
import XYZ_D65 from "./spaces/xyz-d65.js";
|
||||
|
||||
/**
|
||||
* Convenience class for RGB color spaces
|
||||
* @extends {ColorSpace}
|
||||
*/
|
||||
export default class RGBColorSpace extends ColorSpace {
|
||||
/**
|
||||
* Creates a new RGB ColorSpace.
|
||||
* If coords are not specified, they will use the default RGB coords.
|
||||
* Instead of `fromBase()` and `toBase()` functions,
|
||||
* you can specify to/from XYZ matrices and have `toBase()` and `fromBase()` automatically generated.
|
||||
* @param {*} options - Same options as {@link ColorSpace} plus:
|
||||
* @param {number[][]} options.toXYZ_M - Matrix to convert to XYZ
|
||||
* @param {number[][]} options.fromXYZ_M - Matrix to convert from XYZ
|
||||
*/
|
||||
constructor (options) {
|
||||
if (!options.coords) {
|
||||
options.coords = {
|
||||
r: {
|
||||
range: [0, 1],
|
||||
name: "Red",
|
||||
},
|
||||
g: {
|
||||
range: [0, 1],
|
||||
name: "Green",
|
||||
},
|
||||
b: {
|
||||
range: [0, 1],
|
||||
name: "Blue",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!options.base) {
|
||||
options.base = XYZ_D65;
|
||||
}
|
||||
|
||||
if (options.toXYZ_M && options.fromXYZ_M) {
|
||||
options.toBase ??= rgb => {
|
||||
let xyz = multiplyMatrices(options.toXYZ_M, rgb);
|
||||
|
||||
if (this.white !== this.base.white) {
|
||||
// Perform chromatic adaptation
|
||||
xyz = adapt(this.white, this.base.white, xyz);
|
||||
}
|
||||
|
||||
return xyz;
|
||||
};
|
||||
|
||||
options.fromBase ??= xyz => {
|
||||
xyz = adapt(this.base.white, this.white, xyz);
|
||||
return multiplyMatrices(options.fromXYZ_M, xyz);
|
||||
};
|
||||
}
|
||||
|
||||
options.referred ??= "display";
|
||||
|
||||
super(options);
|
||||
}
|
||||
}
|
||||
86
node_modules/colorjs.io/src/serialize.js
generated
vendored
Normal file
86
node_modules/colorjs.io/src/serialize.js
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import * as util from "./util.js";
|
||||
import ColorSpace from "./space.js";
|
||||
import defaults from "./defaults.js";
|
||||
import getColor from "./getColor.js";
|
||||
import checkInGamut from "./inGamut.js";
|
||||
import toGamut from "./toGamut.js";
|
||||
import clone from "./clone.js";
|
||||
|
||||
/**
|
||||
* Generic toString() method, outputs a color(spaceId ...coords) function, a functional syntax, or custom formats defined by the color space
|
||||
* @param {Object} options
|
||||
* @param {number} options.precision - Significant digits
|
||||
* @param {boolean} options.inGamut - Adjust coordinates to fit in gamut first? [default: false]
|
||||
*/
|
||||
export default function serialize (color, {
|
||||
precision = defaults.precision,
|
||||
format = "default",
|
||||
inGamut = true,
|
||||
...customOptions
|
||||
} = {}) {
|
||||
let ret;
|
||||
|
||||
color = getColor(color);
|
||||
|
||||
let formatId = format;
|
||||
format = color.space.getFormat(format)
|
||||
?? color.space.getFormat("default")
|
||||
?? ColorSpace.DEFAULT_FORMAT;
|
||||
|
||||
// The assignment to coords and inGamut needs to stay in the order they are now
|
||||
// The order of the assignment was changed as a workaround for a bug in Next.js
|
||||
// See this issue for details: https://github.com/color-js/color.js/issues/260
|
||||
|
||||
let coords = color.coords.slice(); // clone so we can manipulate it
|
||||
|
||||
inGamut ||= format.toGamut;
|
||||
|
||||
if (inGamut && !checkInGamut(color)) {
|
||||
// FIXME what happens if the color contains NaNs?
|
||||
coords = toGamut(clone(color), inGamut === true ? undefined : inGamut).coords;
|
||||
}
|
||||
|
||||
if (format.type === "custom") {
|
||||
customOptions.precision = precision;
|
||||
|
||||
if (format.serialize) {
|
||||
ret = format.serialize(coords, color.alpha, customOptions);
|
||||
}
|
||||
else {
|
||||
throw new TypeError(`format ${formatId} can only be used to parse colors, not for serialization`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Functional syntax
|
||||
let name = format.name || "color";
|
||||
|
||||
if (format.serializeCoords) {
|
||||
coords = format.serializeCoords(coords, precision);
|
||||
}
|
||||
else {
|
||||
if (precision !== null) {
|
||||
coords = coords.map(c => {
|
||||
return util.serializeNumber(c, {precision});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let args = [...coords];
|
||||
|
||||
if (name === "color") {
|
||||
// If output is a color() function, add colorspace id as first argument
|
||||
let cssId = format.id || format.ids?.[0] || color.space.id;
|
||||
args.unshift(cssId);
|
||||
}
|
||||
|
||||
let alpha = color.alpha;
|
||||
if (precision !== null) {
|
||||
alpha = util.serializeNumber(alpha, {precision});
|
||||
}
|
||||
|
||||
let strAlpha = color.alpha >= 1 || format.noAlpha ? "" : `${format.commas ? "," : " /"} ${alpha}`;
|
||||
ret = `${name}(${args.join(format.commas ? ", " : " ")}${strAlpha})`;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
33
node_modules/colorjs.io/src/set.js
generated
vendored
Normal file
33
node_modules/colorjs.io/src/set.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import ColorSpace from "./space.js";
|
||||
import getColor from "./getColor.js";
|
||||
import get from "./get.js";
|
||||
import getAll from "./getAll.js";
|
||||
import setAll from "./setAll.js";
|
||||
import {type} from "./util.js";
|
||||
|
||||
// Set properties and return current instance
|
||||
export default function set (color, prop, value) {
|
||||
color = getColor(color);
|
||||
|
||||
if (arguments.length === 2 && type(arguments[1]) === "object") {
|
||||
// Argument is an object literal
|
||||
let object = arguments[1];
|
||||
for (let p in object) {
|
||||
set(color, p, object[p]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (typeof value === "function") {
|
||||
value = value(get(color, prop));
|
||||
}
|
||||
|
||||
let {space, index} = ColorSpace.resolveCoord(prop, color.space);
|
||||
let coords = getAll(color, space);
|
||||
coords[index] = value;
|
||||
setAll(color, space, coords);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
set.returns = "color";
|
||||
12
node_modules/colorjs.io/src/setAll.js
generated
vendored
Normal file
12
node_modules/colorjs.io/src/setAll.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import ColorSpace from "./space.js";
|
||||
import getColor from "./getColor.js";
|
||||
|
||||
export default function setAll (color, space, coords) {
|
||||
color = getColor(color);
|
||||
|
||||
space = ColorSpace.get(space);
|
||||
color.coords = space.to(color.space, coords);
|
||||
return color;
|
||||
}
|
||||
|
||||
setAll.returns = "color";
|
||||
86
node_modules/colorjs.io/src/space-accessors.js
generated
vendored
Normal file
86
node_modules/colorjs.io/src/space-accessors.js
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* This plugin defines getters and setters for color[spaceId]
|
||||
* e.g. color.lch on *any* color gives us the lch coords
|
||||
*/
|
||||
import ColorSpace from "./space.js";
|
||||
import Color from "./color.js";
|
||||
import hooks from "./hooks.js";
|
||||
|
||||
// Add space accessors to existing color spaces
|
||||
for (let id in ColorSpace.registry) {
|
||||
addSpaceAccessors(id, ColorSpace.registry[id]);
|
||||
}
|
||||
|
||||
// Add space accessors to color spaces not yet created
|
||||
hooks.add("colorspace-init-end", space => {
|
||||
addSpaceAccessors(space.id, space);
|
||||
space.aliases?.forEach(alias => {
|
||||
addSpaceAccessors(alias, space);
|
||||
});
|
||||
});
|
||||
|
||||
function addSpaceAccessors (id, space) {
|
||||
let propId = id.replace(/-/g, "_");
|
||||
|
||||
Object.defineProperty(Color.prototype, propId, {
|
||||
// Convert coords to coords in another colorspace and return them
|
||||
// Source colorspace: this.spaceId
|
||||
// Target colorspace: id
|
||||
get () {
|
||||
let ret = this.getAll(id);
|
||||
|
||||
if (typeof Proxy === "undefined") {
|
||||
// If proxies are not supported, just return a static array
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Enable color.spaceId.coordName syntax
|
||||
return new Proxy(ret, {
|
||||
has: (obj, property) => {
|
||||
try {
|
||||
ColorSpace.resolveCoord([space, property]);
|
||||
return true;
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
return Reflect.has(obj, property);
|
||||
},
|
||||
get: (obj, property, receiver) => {
|
||||
if (property && typeof property !== "symbol" && !(property in obj)) {
|
||||
let {index} = ColorSpace.resolveCoord([space, property]);
|
||||
|
||||
if (index >= 0) {
|
||||
return obj[index];
|
||||
}
|
||||
}
|
||||
|
||||
return Reflect.get(obj, property, receiver);
|
||||
},
|
||||
set: (obj, property, value, receiver) => {
|
||||
if (property && typeof property !== "symbol" && !(property in obj) || property >= 0) {
|
||||
let {index} = ColorSpace.resolveCoord([space, property]);
|
||||
|
||||
if (index >= 0) {
|
||||
obj[index] = value;
|
||||
|
||||
// Update color.coords
|
||||
this.setAll(id, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Reflect.set(obj, property, value, receiver);
|
||||
},
|
||||
});
|
||||
},
|
||||
// Convert coords in another colorspace to internal coords and set them
|
||||
// Target colorspace: this.spaceId
|
||||
// Source colorspace: id
|
||||
set (coords) {
|
||||
this.setAll(id, coords);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
440
node_modules/colorjs.io/src/space.js
generated
vendored
Normal file
440
node_modules/colorjs.io/src/space.js
generated
vendored
Normal file
@@ -0,0 +1,440 @@
|
||||
import {type, parseCoordGrammar, serializeNumber, mapRange} from "./util.js";
|
||||
import {getWhite} from "./adapt.js";
|
||||
import hooks from "./hooks.js";
|
||||
import getColor from "./getColor.js";
|
||||
|
||||
const ε = .000075;
|
||||
|
||||
/**
|
||||
* Class to represent a color space
|
||||
*/
|
||||
export default class ColorSpace {
|
||||
constructor (options) {
|
||||
this.id = options.id;
|
||||
this.name = options.name;
|
||||
this.base = options.base ? ColorSpace.get(options.base) : null;
|
||||
this.aliases = options.aliases;
|
||||
|
||||
if (this.base) {
|
||||
this.fromBase = options.fromBase;
|
||||
this.toBase = options.toBase;
|
||||
}
|
||||
|
||||
// Coordinate metadata
|
||||
|
||||
let coords = options.coords ?? this.base.coords;
|
||||
|
||||
for (let name in coords) {
|
||||
if (!("name" in coords[name])) {
|
||||
coords[name].name = name;
|
||||
}
|
||||
}
|
||||
this.coords = coords;
|
||||
|
||||
// White point
|
||||
|
||||
let white = options.white ?? this.base.white ?? "D65";
|
||||
this.white = getWhite(white);
|
||||
|
||||
// Sort out formats
|
||||
|
||||
this.formats = options.formats ?? {};
|
||||
|
||||
for (let name in this.formats) {
|
||||
let format = this.formats[name];
|
||||
format.type ||= "function";
|
||||
format.name ||= name;
|
||||
}
|
||||
|
||||
if (!this.formats.color?.id) {
|
||||
this.formats.color = {
|
||||
...this.formats.color ?? {},
|
||||
id: options.cssId || this.id,
|
||||
};
|
||||
}
|
||||
|
||||
// Gamut space
|
||||
|
||||
if (options.gamutSpace) {
|
||||
// Gamut space explicitly specified
|
||||
this.gamutSpace = options.gamutSpace === "self" ? this : ColorSpace.get(options.gamutSpace);
|
||||
}
|
||||
else {
|
||||
// No gamut space specified, calculate a sensible default
|
||||
if (this.isPolar) {
|
||||
// Do not check gamut through polar coordinates
|
||||
this.gamutSpace = this.base;
|
||||
}
|
||||
else {
|
||||
this.gamutSpace = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Optimize inGamut for unbounded spaces
|
||||
if (this.gamutSpace.isUnbounded) {
|
||||
this.inGamut = (coords, options) => {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// Other stuff
|
||||
this.referred = options.referred;
|
||||
|
||||
// Compute ancestors and store them, since they will never change
|
||||
Object.defineProperty(this, "path", {
|
||||
value: getPath(this).reverse(),
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
hooks.run("colorspace-init-end", this);
|
||||
}
|
||||
|
||||
inGamut (coords, {epsilon = ε} = {}) {
|
||||
if (!this.equals(this.gamutSpace)) {
|
||||
coords = this.to(this.gamutSpace, coords);
|
||||
return this.gamutSpace.inGamut(coords, {epsilon});
|
||||
}
|
||||
|
||||
let coordMeta = Object.values(this.coords);
|
||||
|
||||
return coords.every((c, i) => {
|
||||
let meta = coordMeta[i];
|
||||
|
||||
if (meta.type !== "angle" && meta.range) {
|
||||
if (Number.isNaN(c)) {
|
||||
// NaN is always in gamut
|
||||
return true;
|
||||
}
|
||||
|
||||
let [min, max] = meta.range;
|
||||
return (min === undefined || c >= min - epsilon)
|
||||
&& (max === undefined || c <= max + epsilon);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
get isUnbounded () {
|
||||
return Object.values(this.coords).every(coord => !("range" in coord));
|
||||
}
|
||||
|
||||
get cssId () {
|
||||
return this.formats?.color?.id || this.id;
|
||||
}
|
||||
|
||||
get isPolar () {
|
||||
for (let id in this.coords) {
|
||||
if (this.coords[id].type === "angle") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getFormat (format) {
|
||||
if (typeof format === "object") {
|
||||
format = processFormat(format, this);
|
||||
return format;
|
||||
}
|
||||
|
||||
let ret;
|
||||
if (format === "default") {
|
||||
// Get first format
|
||||
ret = Object.values(this.formats)[0];
|
||||
}
|
||||
else {
|
||||
ret = this.formats[format];
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
ret = processFormat(ret, this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this color space is the same as another color space reference.
|
||||
* Allows proxying color space objects and comparing color spaces with ids.
|
||||
* @param {string | ColorSpace} space ColorSpace object or id to compare to
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals (space) {
|
||||
if (!space) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this === space || this.id === space || this.id === space.id;
|
||||
}
|
||||
|
||||
to (space, coords) {
|
||||
if (arguments.length === 1) {
|
||||
const color = getColor(space);
|
||||
[space, coords] = [color.space, color.coords];
|
||||
}
|
||||
|
||||
space = ColorSpace.get(space);
|
||||
|
||||
if (this.equals(space)) {
|
||||
// Same space, no change needed
|
||||
return coords;
|
||||
}
|
||||
|
||||
// Convert NaN to 0, which seems to be valid in every coordinate of every color space
|
||||
coords = coords.map(c => Number.isNaN(c) ? 0 : c);
|
||||
|
||||
// Find connection space = lowest common ancestor in the base tree
|
||||
let myPath = this.path;
|
||||
let otherPath = space.path;
|
||||
|
||||
let connectionSpace, connectionSpaceIndex;
|
||||
|
||||
for (let i = 0; i < myPath.length; i++) {
|
||||
if (myPath[i].equals(otherPath[i])) {
|
||||
connectionSpace = myPath[i];
|
||||
connectionSpaceIndex = i;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connectionSpace) {
|
||||
// This should never happen
|
||||
throw new Error(`Cannot convert between color spaces ${this} and ${space}: no connection space was found`);
|
||||
}
|
||||
|
||||
// Go up from current space to connection space
|
||||
for (let i = myPath.length - 1; i > connectionSpaceIndex; i--) {
|
||||
coords = myPath[i].toBase(coords);
|
||||
}
|
||||
|
||||
// Go down from connection space to target space
|
||||
for (let i = connectionSpaceIndex + 1; i < otherPath.length; i++) {
|
||||
coords = otherPath[i].fromBase(coords);
|
||||
}
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
from (space, coords) {
|
||||
if (arguments.length === 1) {
|
||||
const color = getColor(space);
|
||||
[space, coords] = [color.space, color.coords];
|
||||
}
|
||||
|
||||
space = ColorSpace.get(space);
|
||||
|
||||
return space.to(this, coords);
|
||||
}
|
||||
|
||||
toString () {
|
||||
return `${this.name} (${this.id})`;
|
||||
}
|
||||
|
||||
getMinCoords () {
|
||||
let ret = [];
|
||||
|
||||
for (let id in this.coords) {
|
||||
let meta = this.coords[id];
|
||||
let range = meta.range || meta.refRange;
|
||||
ret.push(range?.min ?? 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static registry = {};
|
||||
|
||||
// Returns array of unique color spaces
|
||||
static get all () {
|
||||
return [...new Set(Object.values(ColorSpace.registry))];
|
||||
}
|
||||
|
||||
static register (id, space) {
|
||||
if (arguments.length === 1) {
|
||||
space = arguments[0];
|
||||
id = space.id;
|
||||
}
|
||||
|
||||
space = this.get(space);
|
||||
|
||||
if (this.registry[id] && this.registry[id] !== space) {
|
||||
throw new Error(`Duplicate color space registration: '${id}'`);
|
||||
}
|
||||
this.registry[id] = space;
|
||||
|
||||
// Register aliases when called without an explicit ID.
|
||||
if (arguments.length === 1 && space.aliases) {
|
||||
for (let alias of space.aliases) {
|
||||
this.register(alias, space);
|
||||
}
|
||||
}
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup ColorSpace object by name
|
||||
* @param {ColorSpace | string} name
|
||||
*/
|
||||
static get (space, ...alternatives) {
|
||||
if (!space || space instanceof ColorSpace) {
|
||||
return space;
|
||||
}
|
||||
|
||||
let argType = type(space);
|
||||
|
||||
if (argType === "string") {
|
||||
// It's a color space id
|
||||
let ret = ColorSpace.registry[space.toLowerCase()];
|
||||
|
||||
if (!ret) {
|
||||
throw new TypeError(`No color space found with id = "${space}"`);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (alternatives.length) {
|
||||
return ColorSpace.get(...alternatives);
|
||||
}
|
||||
|
||||
throw new TypeError(`${space} is not a valid color space`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata about a coordinate of a color space
|
||||
*
|
||||
* @static
|
||||
* @param {Array | string} ref
|
||||
* @param {ColorSpace | string} [workingSpace]
|
||||
* @return {Object}
|
||||
*/
|
||||
static resolveCoord (ref, workingSpace) {
|
||||
let coordType = type(ref);
|
||||
let space, coord;
|
||||
|
||||
if (coordType === "string") {
|
||||
if (ref.includes(".")) {
|
||||
// Absolute coordinate
|
||||
[space, coord] = ref.split(".");
|
||||
}
|
||||
else {
|
||||
// Relative coordinate
|
||||
[space, coord] = [, ref];
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(ref)) {
|
||||
[space, coord] = ref;
|
||||
}
|
||||
else {
|
||||
// Object
|
||||
space = ref.space;
|
||||
coord = ref.coordId;
|
||||
}
|
||||
|
||||
space = ColorSpace.get(space);
|
||||
|
||||
if (!space) {
|
||||
space = workingSpace;
|
||||
}
|
||||
|
||||
if (!space) {
|
||||
throw new TypeError(`Cannot resolve coordinate reference ${ref}: No color space specified and relative references are not allowed here`);
|
||||
}
|
||||
|
||||
coordType = type(coord);
|
||||
|
||||
if (coordType === "number" || coordType === "string" && coord >= 0) {
|
||||
// Resolve numerical coord
|
||||
let meta = Object.entries(space.coords)[coord];
|
||||
|
||||
if (meta) {
|
||||
return {space, id: meta[0], index: coord, ...meta[1]};
|
||||
}
|
||||
}
|
||||
|
||||
space = ColorSpace.get(space);
|
||||
|
||||
let normalizedCoord = coord.toLowerCase();
|
||||
|
||||
let i = 0;
|
||||
for (let id in space.coords) {
|
||||
let meta = space.coords[id];
|
||||
|
||||
if (id.toLowerCase() === normalizedCoord || meta.name?.toLowerCase() === normalizedCoord) {
|
||||
return {space, id, index: i, ...meta};
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
throw new TypeError(`No "${coord}" coordinate found in ${space.name}. Its coordinates are: ${Object.keys(space.coords).join(", ")}`);
|
||||
}
|
||||
|
||||
static DEFAULT_FORMAT = {
|
||||
type: "functions",
|
||||
name: "color",
|
||||
};
|
||||
}
|
||||
|
||||
function getPath (space) {
|
||||
let ret = [space];
|
||||
|
||||
for (let s = space; s = s.base;) {
|
||||
ret.push(s);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function processFormat (format, {coords} = {}) {
|
||||
if (format.coords && !format.coordGrammar) {
|
||||
format.type ||= "function";
|
||||
format.name ||= "color";
|
||||
|
||||
// Format has not been processed
|
||||
format.coordGrammar = parseCoordGrammar(format.coords);
|
||||
|
||||
let coordFormats = Object.entries(coords).map(([id, coordMeta], i) => {
|
||||
// Preferred format for each coord is the first one
|
||||
let outputType = format.coordGrammar[i][0];
|
||||
|
||||
let fromRange = coordMeta.range || coordMeta.refRange;
|
||||
let toRange = outputType.range, suffix = "";
|
||||
|
||||
// Non-strict equals intentional since outputType could be a string object
|
||||
if (outputType == "<percentage>") {
|
||||
toRange = [0, 100];
|
||||
suffix = "%";
|
||||
}
|
||||
else if (outputType == "<angle>") {
|
||||
suffix = "deg";
|
||||
}
|
||||
|
||||
return {fromRange, toRange, suffix};
|
||||
});
|
||||
|
||||
format.serializeCoords = (coords, precision) => {
|
||||
return coords.map((c, i) => {
|
||||
let {fromRange, toRange, suffix} = coordFormats[i];
|
||||
|
||||
if (fromRange && toRange) {
|
||||
c = mapRange(fromRange, toRange, c);
|
||||
}
|
||||
|
||||
c = serializeNumber(c, {precision, unit: suffix});
|
||||
|
||||
return c;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
28
node_modules/colorjs.io/src/spaces/a98rgb-linear.js
generated
vendored
Normal file
28
node_modules/colorjs.io/src/spaces/a98rgb-linear.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import RGBColorSpace from "../rgbspace.js";
|
||||
|
||||
// convert an array of linear-light a98-rgb values to CIE XYZ
|
||||
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
// has greater numerical precision than section 4.3.5.3 of
|
||||
// https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
|
||||
// but the values below were calculated from first principles
|
||||
// from the chromaticity coordinates of R G B W
|
||||
const toXYZ_M = [
|
||||
[ 0.5766690429101305, 0.1855582379065463, 0.1882286462349947 ],
|
||||
[ 0.29734497525053605, 0.6273635662554661, 0.07529145849399788 ],
|
||||
[ 0.02703136138641234, 0.07068885253582723, 0.9913375368376388 ],
|
||||
];
|
||||
|
||||
const fromXYZ_M = [
|
||||
[ 2.0415879038107465, -0.5650069742788596, -0.34473135077832956 ],
|
||||
[ -0.9692436362808795, 1.8759675015077202, 0.04155505740717557 ],
|
||||
[ 0.013444280632031142, -0.11836239223101838, 1.0151749943912054 ],
|
||||
];
|
||||
|
||||
export default new RGBColorSpace({
|
||||
id: "a98rgb-linear",
|
||||
cssId: "--a98-rgb-linear",
|
||||
name: "Linear Adobe® 98 RGB compatible",
|
||||
white: "D65",
|
||||
toXYZ_M,
|
||||
fromXYZ_M,
|
||||
});
|
||||
11
node_modules/colorjs.io/src/spaces/a98rgb.js
generated
vendored
Normal file
11
node_modules/colorjs.io/src/spaces/a98rgb.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import RGBColorSpace from "../rgbspace.js";
|
||||
import A98Linear from "./a98rgb-linear.js";
|
||||
|
||||
export default new RGBColorSpace({
|
||||
id: "a98rgb",
|
||||
cssId: "a98-rgb",
|
||||
name: "Adobe® 98 RGB compatible",
|
||||
base: A98Linear,
|
||||
toBase: RGB => RGB.map(val => Math.pow(Math.abs(val), 563 / 256) * Math.sign(val)),
|
||||
fromBase: RGB => RGB.map(val => Math.pow(Math.abs(val), 256 / 563) * Math.sign(val)),
|
||||
});
|
||||
76
node_modules/colorjs.io/src/spaces/acescc.js
generated
vendored
Normal file
76
node_modules/colorjs.io/src/spaces/acescc.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import RGBColorSpace from "../rgbspace.js";
|
||||
import "../CATs.js"; // because of the funky whitepoint
|
||||
import ACEScg from "./acescg.js";
|
||||
|
||||
const ε = 2 ** -16;
|
||||
|
||||
// the smallest value which, in the 32bit IEEE 754 float encoding,
|
||||
// decodes as a non-negative value
|
||||
const ACES_min_nonzero = -0.35828683;
|
||||
|
||||
// brightest encoded value, decodes to 65504
|
||||
const ACES_cc_max = (Math.log2(65504) + 9.72) / 17.52; // 1.468
|
||||
|
||||
export default new RGBColorSpace({
|
||||
id: "acescc",
|
||||
cssId: "--acescc",
|
||||
name: "ACEScc",
|
||||
// see S-2014-003 ACEScc – A Logarithmic Encoding of ACES Data
|
||||
// https://docs.acescentral.com/specifications/acescc/
|
||||
// uses the AP1 primaries, see section 4.3.1 Color primaries
|
||||
|
||||
// Appendix A: "Very small ACES scene referred values below 7 1/4 stops
|
||||
// below 18% middle gray are encoded as negative ACEScc values.
|
||||
// These values should be preserved per the encoding in Section 4.4
|
||||
// so that all positive ACES values are maintained."
|
||||
coords: {
|
||||
r: {
|
||||
range: [ACES_min_nonzero, ACES_cc_max],
|
||||
name: "Red",
|
||||
},
|
||||
g: {
|
||||
range: [ACES_min_nonzero, ACES_cc_max],
|
||||
name: "Green",
|
||||
},
|
||||
b: {
|
||||
range: [ACES_min_nonzero, ACES_cc_max],
|
||||
name: "Blue",
|
||||
},
|
||||
},
|
||||
referred: "scene",
|
||||
|
||||
base: ACEScg,
|
||||
// from section 4.4.2 Decoding Function
|
||||
toBase (RGB) {
|
||||
const low = (9.72 - 15) / 17.52; // -0.3014
|
||||
|
||||
return RGB.map(function (val) {
|
||||
if (val <= low) {
|
||||
return (2 ** ((val * 17.52) - 9.72) - ε) * 2; // very low values, below -0.3014
|
||||
}
|
||||
else if (val < ACES_cc_max) {
|
||||
return 2 ** ((val * 17.52) - 9.72);
|
||||
}
|
||||
else { // val >= ACES_cc_max
|
||||
return 65504;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Non-linear encoding function from S-2014-003, section 4.4.1 Encoding Function
|
||||
fromBase (RGB) {
|
||||
return RGB.map(function (val) {
|
||||
if (val <= 0) {
|
||||
return (Math.log2(ε) + 9.72) / 17.52; // -0.3584
|
||||
}
|
||||
else if (val < ε) {
|
||||
return (Math.log2(ε + val * 0.5) + 9.72) / 17.52;
|
||||
}
|
||||
else { // val >= ε
|
||||
return (Math.log2(val) + 9.72) / 17.52;
|
||||
}
|
||||
});
|
||||
},
|
||||
// encoded media white (rgb 1,1,1) => linear [ 222.861, 222.861, 222.861 ]
|
||||
// encoded media black (rgb 0,0,0) => linear [ 0.0011857, 0.0011857, 0.0011857]
|
||||
});
|
||||
54
node_modules/colorjs.io/src/spaces/acescg.js
generated
vendored
Normal file
54
node_modules/colorjs.io/src/spaces/acescg.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import RGBColorSpace from "../rgbspace.js";
|
||||
import {WHITES} from "../adapt.js";
|
||||
import "../CATs.js"; // because of the funky whitepoint
|
||||
|
||||
// The ACES whitepoint
|
||||
// see TB-2018-001 Derivation of the ACES White Point CIE Chromaticity Coordinates
|
||||
// also https://github.com/ampas/aces-dev/blob/master/documents/python/TB-2018-001/aces_wp.py
|
||||
// Similar to D60
|
||||
WHITES.ACES = [0.32168 / 0.33767, 1.00000, (1.00000 - 0.32168 - 0.33767) / 0.33767];
|
||||
|
||||
// convert an array of linear-light ACEScc values to CIE XYZ
|
||||
const toXYZ_M = [
|
||||
[ 0.6624541811085053, 0.13400420645643313, 0.1561876870049078 ],
|
||||
[ 0.27222871678091454, 0.6740817658111484, 0.05368951740793705 ],
|
||||
[ -0.005574649490394108, 0.004060733528982826, 1.0103391003129971 ],
|
||||
];
|
||||
const fromXYZ_M = [
|
||||
[ 1.6410233796943257, -0.32480329418479, -0.23642469523761225 ],
|
||||
[ -0.6636628587229829, 1.6153315916573379, 0.016756347685530137 ],
|
||||
[ 0.011721894328375376, -0.008284441996237409, 0.9883948585390215 ],
|
||||
];
|
||||
|
||||
export default new RGBColorSpace({
|
||||
id: "acescg",
|
||||
cssId: "--acescg",
|
||||
name: "ACEScg",
|
||||
|
||||
// ACEScg – A scene-referred, linear-light encoding of ACES Data
|
||||
// https://docs.acescentral.com/specifications/acescg/
|
||||
// uses the AP1 primaries, see section 4.3.1 Color primaries
|
||||
coords: {
|
||||
r: {
|
||||
range: [0, 65504],
|
||||
name: "Red",
|
||||
},
|
||||
g: {
|
||||
range: [0, 65504],
|
||||
name: "Green",
|
||||
},
|
||||
b: {
|
||||
range: [0, 65504],
|
||||
name: "Blue",
|
||||
},
|
||||
},
|
||||
|
||||
referred: "scene",
|
||||
|
||||
white: WHITES.ACES,
|
||||
|
||||
toXYZ_M,
|
||||
fromXYZ_M,
|
||||
});
|
||||
|
||||
// export default Color;
|
||||
362
node_modules/colorjs.io/src/spaces/cam16.js
generated
vendored
Normal file
362
node_modules/colorjs.io/src/spaces/cam16.js
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import {multiplyMatrices, interpolate, copySign, spow, zdiv, bisectLeft} from "../util.js";
|
||||
import {constrain} from "../angles.js";
|
||||
import xyz_d65 from "./xyz-d65.js";
|
||||
import {WHITES} from "../adapt.js";
|
||||
|
||||
const white = WHITES.D65;
|
||||
const adaptedCoef = 0.42;
|
||||
const adaptedCoefInv = 1 / adaptedCoef;
|
||||
const tau = 2 * Math.PI;
|
||||
|
||||
const cat16 = [
|
||||
[ 0.401288, 0.650173, -0.051461 ],
|
||||
[ -0.250268, 1.204414, 0.045854 ],
|
||||
[ -0.002079, 0.048952, 0.953127 ],
|
||||
];
|
||||
|
||||
const cat16Inv = [
|
||||
[1.8620678550872327, -1.0112546305316843, 0.14918677544445175],
|
||||
[0.38752654323613717, 0.6214474419314753, -0.008973985167612518],
|
||||
[-0.015841498849333856, -0.03412293802851557, 1.0499644368778496],
|
||||
];
|
||||
|
||||
const m1 = [
|
||||
[460.0, 451.0, 288.0],
|
||||
[460.0, -891.0, -261.0],
|
||||
[460.0, -220.0, -6300.0],
|
||||
];
|
||||
|
||||
const surroundMap = {
|
||||
dark: [0.8, 0.525, 0.8],
|
||||
dim: [0.9, 0.59, 0.9],
|
||||
average: [1, 0.69, 1],
|
||||
};
|
||||
|
||||
const hueQuadMap = {
|
||||
// Red, Yellow, Green, Blue, Red
|
||||
h: [20.14, 90.00, 164.25, 237.53, 380.14],
|
||||
e: [0.8, 0.7, 1.0, 1.2, 0.8],
|
||||
H: [0.0, 100.0, 200.0, 300.0, 400.0],
|
||||
};
|
||||
|
||||
const rad2deg = 180 / Math.PI;
|
||||
const deg2rad = Math.PI / 180;
|
||||
|
||||
export function adapt (coords, fl) {
|
||||
const temp = coords.map(c => {
|
||||
const x = spow(fl * Math.abs(c) * 0.01, adaptedCoef);
|
||||
return 400 * copySign(x, c) / (x + 27.13);
|
||||
});
|
||||
return temp;
|
||||
}
|
||||
|
||||
export function unadapt (adapted, fl) {
|
||||
const constant = 100 / fl * (27.13 ** adaptedCoefInv);
|
||||
return adapted.map(c => {
|
||||
const cabs = Math.abs(c);
|
||||
return copySign(constant * spow(cabs / (400 - cabs), adaptedCoefInv), c);
|
||||
});
|
||||
}
|
||||
|
||||
export function hueQuadrature (h) {
|
||||
let hp = constrain(h);
|
||||
if (hp <= hueQuadMap.h[0]) {
|
||||
hp += 360;
|
||||
}
|
||||
|
||||
const i = bisectLeft(hueQuadMap.h, hp) - 1;
|
||||
const [hi, hii] = hueQuadMap.h.slice(i, i + 2);
|
||||
const [ei, eii] = hueQuadMap.e.slice(i, i + 2);
|
||||
const Hi = hueQuadMap.H[i];
|
||||
|
||||
const t = (hp - hi) / ei;
|
||||
return Hi + (100 * t) / (t + (hii - hp) / eii);
|
||||
}
|
||||
|
||||
export function invHueQuadrature (H) {
|
||||
let Hp = ((H % 400 + 400) % 400);
|
||||
const i = Math.floor(0.01 * Hp);
|
||||
Hp = Hp % 100;
|
||||
const [hi, hii] = hueQuadMap.h.slice(i, i + 2);
|
||||
const [ei, eii] = hueQuadMap.e.slice(i, i + 2);
|
||||
|
||||
return constrain(
|
||||
(Hp * (eii * hi - ei * hii) - 100 * hi * eii) /
|
||||
(Hp * (eii - ei) - 100 * eii),
|
||||
);
|
||||
}
|
||||
|
||||
export function environment (
|
||||
refWhite,
|
||||
adaptingLuminance,
|
||||
backgroundLuminance,
|
||||
surround,
|
||||
discounting,
|
||||
) {
|
||||
|
||||
const env = {};
|
||||
|
||||
env.discounting = discounting;
|
||||
env.refWhite = refWhite;
|
||||
env.surround = surround;
|
||||
const xyzW = refWhite.map(c => {
|
||||
return c * 100;
|
||||
});
|
||||
|
||||
// The average luminance of the environment in `cd/m^2cd/m` (a.k.a. nits)
|
||||
env.la = adaptingLuminance;
|
||||
// The relative luminance of the nearby background
|
||||
env.yb = backgroundLuminance;
|
||||
// Absolute luminance of the reference white.
|
||||
const yw = xyzW[1];
|
||||
|
||||
// Cone response for reference white
|
||||
const rgbW = multiplyMatrices(cat16, xyzW);
|
||||
|
||||
// Surround: dark, dim, and average
|
||||
surround = surroundMap[env.surround];
|
||||
const f = surround[0];
|
||||
env.c = surround[1];
|
||||
env.nc = surround[2];
|
||||
|
||||
const k = 1 / (5 * env.la + 1);
|
||||
const k4 = k ** 4;
|
||||
|
||||
// Factor of luminance level adaptation
|
||||
env.fl = (k4 * env.la + 0.1 * (1 - k4) * (1 - k4) * Math.cbrt(5 * env.la));
|
||||
env.flRoot = env.fl ** 0.25;
|
||||
|
||||
env.n = env.yb / yw;
|
||||
env.z = 1.48 + Math.sqrt(env.n);
|
||||
env.nbb = 0.725 * (env.n ** -0.2);
|
||||
env.ncb = env.nbb;
|
||||
|
||||
// Degree of adaptation calculating if not discounting
|
||||
// illuminant (assumed eye is fully adapted)
|
||||
const d = (discounting) ?
|
||||
1 :
|
||||
Math.max(
|
||||
Math.min(f * (1 - 1 / 3.6 * Math.exp((-env.la - 42) / 92)), 1),
|
||||
0,
|
||||
);
|
||||
env.dRgb = rgbW.map(c => {
|
||||
return interpolate(1, yw / c, d);
|
||||
});
|
||||
env.dRgbInv = env.dRgb.map(c => {
|
||||
return 1 / c;
|
||||
});
|
||||
|
||||
// Achromatic response
|
||||
const rgbCW = rgbW.map((c, i) => {
|
||||
return c * env.dRgb[i];
|
||||
});
|
||||
const rgbAW = adapt(rgbCW, env.fl);
|
||||
env.aW = env.nbb * (2 * rgbAW[0] + rgbAW[1] + 0.05 * rgbAW[2]);
|
||||
|
||||
// console.log(env);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
// Pre-calculate everything we can with the viewing conditions
|
||||
const viewingConditions = environment(
|
||||
white,
|
||||
64 / Math.PI * 0.2, 20,
|
||||
"average",
|
||||
false,
|
||||
);
|
||||
|
||||
export function fromCam16 (cam16, env) {
|
||||
|
||||
// These check ensure one, and only one attribute for a
|
||||
// given category is provided.
|
||||
if (!((cam16.J !== undefined) ^ (cam16.Q !== undefined))) {
|
||||
throw new Error("Conversion requires one and only one: 'J' or 'Q'");
|
||||
}
|
||||
|
||||
if (!((cam16.C !== undefined) ^ (cam16.M !== undefined) ^ (cam16.s !== undefined))) {
|
||||
throw new Error("Conversion requires one and only one: 'C', 'M' or 's'");
|
||||
}
|
||||
|
||||
// Hue is absolutely required
|
||||
if (!((cam16.h !== undefined) ^ (cam16.H !== undefined))) {
|
||||
throw new Error("Conversion requires one and only one: 'h' or 'H'");
|
||||
}
|
||||
|
||||
// Black
|
||||
if (cam16.J === 0.0 || cam16.Q === 0.0) {
|
||||
return [0.0, 0.0, 0.0];
|
||||
}
|
||||
|
||||
// Break hue into Cartesian components
|
||||
let hRad = 0.0;
|
||||
if (cam16.h !== undefined) {
|
||||
hRad = constrain(cam16.h) * deg2rad;
|
||||
}
|
||||
else {
|
||||
hRad = invHueQuadrature(cam16.H) * deg2rad;
|
||||
}
|
||||
|
||||
const cosh = Math.cos(hRad);
|
||||
const sinh = Math.sin(hRad);
|
||||
|
||||
// Calculate `Jroot` from one of the lightness derived coordinates.
|
||||
let Jroot = 0.0;
|
||||
if (cam16.J !== undefined) {
|
||||
Jroot = spow(cam16.J, 1 / 2) * 0.1;
|
||||
}
|
||||
else if (cam16.Q !== undefined) {
|
||||
Jroot = 0.25 * env.c * cam16.Q / ((env.aW + 4) * env.flRoot);
|
||||
}
|
||||
|
||||
// Calculate the `t` value from one of the chroma derived coordinates
|
||||
let alpha = 0.0;
|
||||
if (cam16.C !== undefined) {
|
||||
alpha = cam16.C / Jroot;
|
||||
}
|
||||
else if (cam16.M !== undefined) {
|
||||
alpha = (cam16.M / env.flRoot) / Jroot;
|
||||
}
|
||||
else if (cam16.s !== undefined) {
|
||||
alpha = 0.0004 * (cam16.s ** 2) * (env.aW + 4) / env.c;
|
||||
}
|
||||
const t = spow(
|
||||
alpha * Math.pow(1.64 - Math.pow(0.29, env.n), -0.73),
|
||||
10 / 9,
|
||||
);
|
||||
|
||||
// Eccentricity
|
||||
const et = 0.25 * (Math.cos(hRad + 2) + 3.8);
|
||||
|
||||
// Achromatic response
|
||||
const A = env.aW * spow(Jroot, 2 / env.c / env.z);
|
||||
|
||||
// Calculate red-green and yellow-blue components
|
||||
const p1 = 5e4 / 13 * env.nc * env.ncb * et;
|
||||
const p2 = A / env.nbb;
|
||||
const r = (
|
||||
23 * (p2 + 0.305) *
|
||||
zdiv(t, 23 * p1 + t * (11 * cosh + 108 * sinh))
|
||||
);
|
||||
const a = r * cosh;
|
||||
const b = r * sinh;
|
||||
|
||||
// Calculate back from cone response to XYZ
|
||||
const rgb_c = unadapt(
|
||||
multiplyMatrices(m1, [p2, a, b]).map(c => {
|
||||
return c * 1 / 1403;
|
||||
}),
|
||||
env.fl,
|
||||
);
|
||||
return multiplyMatrices(
|
||||
cat16Inv,
|
||||
rgb_c.map((c, i) => {
|
||||
return c * env.dRgbInv[i];
|
||||
}),
|
||||
).map(c => {
|
||||
return c / 100;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function toCam16 (xyzd65, env) {
|
||||
// Cone response
|
||||
const xyz100 = xyzd65.map(c => {
|
||||
return c * 100;
|
||||
});
|
||||
const rgbA = adapt(
|
||||
multiplyMatrices(cat16, xyz100).map((c, i) => {
|
||||
return c * env.dRgb[i];
|
||||
}),
|
||||
env.fl,
|
||||
);
|
||||
|
||||
// Calculate hue from red-green and yellow-blue components
|
||||
const a = rgbA[0] + (-12 * rgbA[1] + rgbA[2]) / 11;
|
||||
const b = (rgbA[0] + rgbA[1] - 2 * rgbA[2]) / 9;
|
||||
const hRad = ((Math.atan2(b, a) % tau) + tau) % tau;
|
||||
|
||||
// Eccentricity
|
||||
const et = 0.25 * (Math.cos(hRad + 2) + 3.8);
|
||||
|
||||
const t = (
|
||||
5e4 / 13 * env.nc * env.ncb *
|
||||
zdiv(
|
||||
et * Math.sqrt(a ** 2 + b ** 2),
|
||||
rgbA[0] + rgbA[1] + 1.05 * rgbA[2] + 0.305,
|
||||
)
|
||||
);
|
||||
const alpha = spow(t, 0.9) * Math.pow(1.64 - Math.pow(0.29, env.n), 0.73);
|
||||
|
||||
// Achromatic response
|
||||
const A = env.nbb * (2 * rgbA[0] + rgbA[1] + 0.05 * rgbA[2]);
|
||||
|
||||
const Jroot = spow(A / env.aW, 0.5 * env.c * env.z);
|
||||
|
||||
// Lightness
|
||||
const J = 100 * spow(Jroot, 2);
|
||||
|
||||
// Brightness
|
||||
const Q = (4 / env.c * Jroot * (env.aW + 4) * env.flRoot);
|
||||
|
||||
// Chroma
|
||||
const C = alpha * Jroot;
|
||||
|
||||
// Colorfulness
|
||||
const M = C * env.flRoot;
|
||||
|
||||
// Hue
|
||||
const h = constrain(hRad * rad2deg);
|
||||
|
||||
// Hue quadrature
|
||||
const H = hueQuadrature(h);
|
||||
|
||||
// Saturation
|
||||
const s = 50 * spow(env.c * alpha / (env.aW + 4), 1 / 2);
|
||||
|
||||
// console.log({J: J, C: C, h: h, s: s, Q: Q, M: M, H: H});
|
||||
|
||||
return {J: J, C: C, h: h, s: s, Q: Q, M: M, H: H};
|
||||
}
|
||||
|
||||
|
||||
// Provided as a way to directly evaluate the CAM16 model
|
||||
// https://observablehq.com/@jrus/cam16: reference implementation
|
||||
// https://arxiv.org/pdf/1802.06067.pdf: Nico Schlömer
|
||||
// https://onlinelibrary.wiley.com/doi/pdf/10.1002/col.22324: hue quadrature
|
||||
// https://www.researchgate.net/publication/318152296_Comprehensive_color_solutions_CAM16_CAT16_and_CAM16-UCS
|
||||
// Results compared against: https://github.com/colour-science/colour
|
||||
export default new ColorSpace({
|
||||
id: "cam16-jmh",
|
||||
cssId: "--cam16-jmh",
|
||||
name: "CAM16-JMh",
|
||||
coords: {
|
||||
j: {
|
||||
refRange: [0, 100],
|
||||
name: "J",
|
||||
},
|
||||
m: {
|
||||
refRange: [0, 105.0],
|
||||
name: "Colorfulness",
|
||||
},
|
||||
h: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
},
|
||||
|
||||
base: xyz_d65,
|
||||
|
||||
fromBase (xyz) {
|
||||
const cam16 = toCam16(xyz, viewingConditions);
|
||||
return [cam16.J, cam16.M, cam16.h];
|
||||
},
|
||||
toBase (cam16) {
|
||||
return fromCam16(
|
||||
{J: cam16[0], M: cam16[1], h: cam16[2]},
|
||||
viewingConditions,
|
||||
);
|
||||
},
|
||||
});
|
||||
157
node_modules/colorjs.io/src/spaces/hct.js
generated
vendored
Normal file
157
node_modules/colorjs.io/src/spaces/hct.js
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import {constrain} from "../angles.js";
|
||||
import xyz_d65 from "./xyz-d65.js";
|
||||
import {fromCam16, toCam16, environment} from "./cam16.js";
|
||||
import {WHITES} from "../adapt.js";
|
||||
|
||||
const white = WHITES.D65;
|
||||
const ε = 216 / 24389; // 6^3/29^3 == (24/116)^3
|
||||
const κ = 24389 / 27; // 29^3/3^3
|
||||
|
||||
function toLstar (y) {
|
||||
// Convert XYZ Y to L*
|
||||
|
||||
const fy = (y > ε) ? Math.cbrt(y) : (κ * y + 16) / 116;
|
||||
return (116.0 * fy) - 16.0;
|
||||
}
|
||||
|
||||
function fromLstar (lstar) {
|
||||
// Convert L* back to XYZ Y
|
||||
|
||||
return (lstar > 8) ? Math.pow((lstar + 16) / 116, 3) : lstar / κ;
|
||||
}
|
||||
|
||||
function fromHct (coords, env) {
|
||||
// Use Newton's method to try and converge as quick as possible or
|
||||
// converge as close as we can. While the requested precision is achieved
|
||||
// most of the time, it may not always be achievable. Especially past the
|
||||
// visible spectrum, the algorithm will likely struggle to get the same
|
||||
// precision. If, for whatever reason, we cannot achieve the accuracy we
|
||||
// seek in the allotted iterations, just return the closest we were able to
|
||||
// get.
|
||||
|
||||
let [h, c, t] = coords;
|
||||
let xyz = [];
|
||||
let j = 0;
|
||||
|
||||
// Shortcut out for black
|
||||
if (t === 0) {
|
||||
return [0.0, 0.0, 0.0];
|
||||
}
|
||||
|
||||
// Calculate the Y we need to target
|
||||
let y = fromLstar(t);
|
||||
|
||||
// A better initial guess yields better results. Polynomials come from
|
||||
// curve fitting the T vs J response.
|
||||
if (t > 0) {
|
||||
j = 0.00379058511492914 * t ** 2 + 0.608983189401032 * t + 0.9155088574762233;
|
||||
}
|
||||
else {
|
||||
j = 9.514440756550361e-06 * t ** 2 + 0.08693057439788597 * t - 21.928975842194614;
|
||||
}
|
||||
|
||||
// Threshold of how close is close enough, and max number of attempts.
|
||||
// More precision and more attempts means more time spent iterating. Higher
|
||||
// required precision gives more accuracy but also increases the chance of
|
||||
// not hitting the goal. 2e-12 allows us to convert round trip with
|
||||
// reasonable accuracy of six decimal places or more.
|
||||
const threshold = 2e-12;
|
||||
const max_attempts = 15;
|
||||
|
||||
let attempt = 0;
|
||||
let last = Infinity;
|
||||
let best = j;
|
||||
|
||||
// Try to find a J such that the returned y matches the returned y of the L*
|
||||
while (attempt <= max_attempts) {
|
||||
xyz = fromCam16({J: j, C: c, h: h}, env);
|
||||
|
||||
// If we are within range, return XYZ
|
||||
// If we are closer than last time, save the values
|
||||
const delta = Math.abs(xyz[1] - y);
|
||||
if (delta < last) {
|
||||
if (delta <= threshold) {
|
||||
return xyz;
|
||||
}
|
||||
best = j;
|
||||
last = delta;
|
||||
}
|
||||
|
||||
// f(j_root) = (j ** (1 / 2)) * 0.1
|
||||
// f(j) = ((f(j_root) * 100) ** 2) / j - 1 = 0
|
||||
// f(j_root) = Y = y / 100
|
||||
// f(j) = (y ** 2) / j - 1
|
||||
// f'(j) = (2 * y) / j
|
||||
j = j - (xyz[1] - y) * j / (2 * xyz[1]);
|
||||
|
||||
attempt += 1;
|
||||
}
|
||||
|
||||
// We could not acquire the precision we desired,
|
||||
// return our closest attempt.
|
||||
return fromCam16({J: j, C: c, h: h}, env);
|
||||
}
|
||||
|
||||
function toHct (xyz, env) {
|
||||
// Calculate HCT by taking the L* of CIE LCh D65 and CAM16 chroma and hue.
|
||||
|
||||
const t = toLstar(xyz[1]);
|
||||
if (t === 0.0) {
|
||||
return [0.0, 0.0, 0.0];
|
||||
}
|
||||
const cam16 = toCam16(xyz, viewingConditions);
|
||||
return [constrain(cam16.h), cam16.C, t];
|
||||
}
|
||||
|
||||
// Pre-calculate everything we can with the viewing conditions
|
||||
export const viewingConditions = environment(
|
||||
white, 200 / Math.PI * fromLstar(50.0),
|
||||
fromLstar(50.0) * 100,
|
||||
"average",
|
||||
false,
|
||||
);
|
||||
|
||||
// https://material.io/blog/science-of-color-design
|
||||
// This is not a port of the material-color-utilities,
|
||||
// but instead implements the full color space as described,
|
||||
// combining CAM16 JCh and Lab D65. This does not clamp conversion
|
||||
// to HCT to specific chroma bands and provides support for wider
|
||||
// gamuts than Google currently supports and does so at a greater
|
||||
// precision (> 8 bits back to sRGB).
|
||||
// This implementation comes from https://github.com/facelessuser/coloraide
|
||||
// which is licensed under MIT.
|
||||
export default new ColorSpace({
|
||||
id: "hct",
|
||||
name: "HCT",
|
||||
coords: {
|
||||
h: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
c: {
|
||||
refRange: [0, 145],
|
||||
name: "Colorfulness",
|
||||
},
|
||||
t: {
|
||||
refRange: [0, 100],
|
||||
name: "Tone",
|
||||
},
|
||||
},
|
||||
|
||||
base: xyz_d65,
|
||||
|
||||
fromBase (xyz) {
|
||||
return toHct(xyz, viewingConditions);
|
||||
},
|
||||
toBase (hct) {
|
||||
return fromHct(hct, viewingConditions);
|
||||
},
|
||||
formats: {
|
||||
color: {
|
||||
id: "--hct",
|
||||
coords: ["<number> | <angle>", "<percentage> | <number>", "<percentage> | <number>"],
|
||||
},
|
||||
},
|
||||
});
|
||||
130
node_modules/colorjs.io/src/spaces/hpluv.js
generated
vendored
Normal file
130
node_modules/colorjs.io/src/spaces/hpluv.js
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Adapted from: https://github.com/hsluv/hsluv-javascript/blob/14b49e6cf9a9137916096b8487a5372626b57ba4/src/hsluv.ts
|
||||
|
||||
Copyright (c) 2012-2022 Alexei Boronine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import ColorSpace from "../space.js";
|
||||
import LCHuv from "./lchuv.js";
|
||||
import {fromXYZ_M} from "./srgb-linear.js";
|
||||
import {skipNone} from "../util.js";
|
||||
import {calculateBoundingLines} from "./hsluv.js";
|
||||
|
||||
const ε = 216 / 24389; // 6^3/29^3 == (24/116)^3
|
||||
const κ = 24389 / 27; // 29^3/3^3
|
||||
|
||||
const m_r0 = fromXYZ_M[0][0];
|
||||
const m_r1 = fromXYZ_M[0][1];
|
||||
const m_r2 = fromXYZ_M[0][2];
|
||||
const m_g0 = fromXYZ_M[1][0];
|
||||
const m_g1 = fromXYZ_M[1][1];
|
||||
const m_g2 = fromXYZ_M[1][2];
|
||||
const m_b0 = fromXYZ_M[2][0];
|
||||
const m_b1 = fromXYZ_M[2][1];
|
||||
const m_b2 = fromXYZ_M[2][2];
|
||||
|
||||
function distanceFromOrigin (slope, intercept) {
|
||||
return Math.abs(intercept) / Math.sqrt(Math.pow(slope, 2) + 1);
|
||||
}
|
||||
|
||||
function calcMaxChromaHpluv (lines) {
|
||||
let r0 = distanceFromOrigin(lines.r0s, lines.r0i);
|
||||
let r1 = distanceFromOrigin(lines.r1s, lines.r1i);
|
||||
let g0 = distanceFromOrigin(lines.g0s, lines.g0i);
|
||||
let g1 = distanceFromOrigin(lines.g1s, lines.g1i);
|
||||
let b0 = distanceFromOrigin(lines.b0s, lines.b0i);
|
||||
let b1 = distanceFromOrigin(lines.b1s, lines.b1i);
|
||||
|
||||
return Math.min(r0, r1, g0, g1, b0, b1);
|
||||
}
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "hpluv",
|
||||
name: "HPLuv",
|
||||
coords: {
|
||||
h: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
s: {
|
||||
range: [0, 100],
|
||||
name: "Saturation",
|
||||
},
|
||||
l: {
|
||||
range: [0, 100],
|
||||
name: "Lightness",
|
||||
},
|
||||
},
|
||||
|
||||
base: LCHuv,
|
||||
gamutSpace: "self",
|
||||
|
||||
// Convert LCHuv to HPLuv
|
||||
fromBase (lch) {
|
||||
let [l, c, h] = [skipNone(lch[0]), skipNone(lch[1]), skipNone(lch[2])];
|
||||
let s;
|
||||
|
||||
if (l > 99.9999999) {
|
||||
s = 0;
|
||||
l = 100;
|
||||
}
|
||||
else if (l < 0.00000001) {
|
||||
s = 0;
|
||||
l = 0;
|
||||
}
|
||||
else {
|
||||
let lines = calculateBoundingLines(l);
|
||||
let max = calcMaxChromaHpluv(lines);
|
||||
s = c / max * 100;
|
||||
}
|
||||
return [h, s, l];
|
||||
},
|
||||
|
||||
// Convert HPLuv to LCHuv
|
||||
toBase (hsl) {
|
||||
let [h, s, l] = [skipNone(hsl[0]), skipNone(hsl[1]), skipNone(hsl[2])];
|
||||
let c;
|
||||
|
||||
if (l > 99.9999999) {
|
||||
l = 100;
|
||||
c = 0;
|
||||
}
|
||||
else if (l < 0.00000001) {
|
||||
l = 0;
|
||||
c = 0;
|
||||
}
|
||||
else {
|
||||
let lines = calculateBoundingLines(l);
|
||||
let max = calcMaxChromaHpluv(lines, h);
|
||||
c = max / 100 * s;
|
||||
}
|
||||
|
||||
return [l, c, h];
|
||||
},
|
||||
|
||||
formats: {
|
||||
color: {
|
||||
id: "--hpluv",
|
||||
coords: ["<number> | <angle>", "<percentage> | <number>", "<percentage> | <number>"],
|
||||
},
|
||||
},
|
||||
});
|
||||
91
node_modules/colorjs.io/src/spaces/hsl.js
generated
vendored
Normal file
91
node_modules/colorjs.io/src/spaces/hsl.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import sRGB from "./srgb.js";
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "hsl",
|
||||
name: "HSL",
|
||||
coords: {
|
||||
h: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
s: {
|
||||
range: [0, 100],
|
||||
name: "Saturation",
|
||||
},
|
||||
l: {
|
||||
range: [0, 100],
|
||||
name: "Lightness",
|
||||
},
|
||||
},
|
||||
|
||||
base: sRGB,
|
||||
|
||||
// Adapted from https://drafts.csswg.org/css-color-4/better-rgbToHsl.js
|
||||
fromBase: rgb => {
|
||||
let max = Math.max(...rgb);
|
||||
let min = Math.min(...rgb);
|
||||
let [r, g, b] = rgb;
|
||||
let [h, s, l] = [NaN, 0, (min + max) / 2];
|
||||
let d = max - min;
|
||||
|
||||
if (d !== 0) {
|
||||
s = (l === 0 || l === 1) ? 0 : (max - l) / Math.min(l, 1 - l);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4;
|
||||
}
|
||||
|
||||
h = h * 60;
|
||||
}
|
||||
|
||||
// Very out of gamut colors can produce negative saturation
|
||||
// If so, just rotate the hue by 180 and use a positive saturation
|
||||
// see https://github.com/w3c/csswg-drafts/issues/9222
|
||||
if (s < 0) {
|
||||
h += 180;
|
||||
s = Math.abs(s);
|
||||
}
|
||||
|
||||
if (h >= 360) {
|
||||
h -= 360;
|
||||
}
|
||||
|
||||
return [h, s * 100, l * 100];
|
||||
},
|
||||
|
||||
// Adapted from https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative
|
||||
toBase: hsl => {
|
||||
let [h, s, l] = hsl;
|
||||
h = h % 360;
|
||||
|
||||
if (h < 0) {
|
||||
h += 360;
|
||||
}
|
||||
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
|
||||
function f (n) {
|
||||
let k = (n + h / 30) % 12;
|
||||
let a = s * Math.min(l, 1 - l);
|
||||
return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
|
||||
}
|
||||
|
||||
return [f(0), f(8), f(4)];
|
||||
},
|
||||
|
||||
formats: {
|
||||
"hsl": {
|
||||
coords: ["<number> | <angle>", "<percentage>", "<percentage>"],
|
||||
},
|
||||
"hsla": {
|
||||
coords: ["<number> | <angle>", "<percentage>", "<percentage>"],
|
||||
commas: true,
|
||||
lastAlpha: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
162
node_modules/colorjs.io/src/spaces/hsluv.js
generated
vendored
Normal file
162
node_modules/colorjs.io/src/spaces/hsluv.js
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
Adapted from: https://github.com/hsluv/hsluv-javascript/blob/14b49e6cf9a9137916096b8487a5372626b57ba4/src/hsluv.ts
|
||||
|
||||
Copyright (c) 2012-2022 Alexei Boronine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import ColorSpace from "../space.js";
|
||||
import LCHuv from "./lchuv.js";
|
||||
import sRGB from "./srgb.js";
|
||||
import {fromXYZ_M} from "./srgb-linear.js";
|
||||
import {skipNone} from "../util.js";
|
||||
|
||||
const ε = 216 / 24389; // 6^3/29^3 == (24/116)^3
|
||||
const κ = 24389 / 27; // 29^3/3^3
|
||||
|
||||
const m_r0 = fromXYZ_M[0][0];
|
||||
const m_r1 = fromXYZ_M[0][1];
|
||||
const m_r2 = fromXYZ_M[0][2];
|
||||
const m_g0 = fromXYZ_M[1][0];
|
||||
const m_g1 = fromXYZ_M[1][1];
|
||||
const m_g2 = fromXYZ_M[1][2];
|
||||
const m_b0 = fromXYZ_M[2][0];
|
||||
const m_b1 = fromXYZ_M[2][1];
|
||||
const m_b2 = fromXYZ_M[2][2];
|
||||
|
||||
function distanceFromOriginAngle (slope, intercept, angle) {
|
||||
const d = intercept / (Math.sin(angle) - slope * Math.cos(angle));
|
||||
return d < 0 ? Infinity : d;
|
||||
}
|
||||
|
||||
export function calculateBoundingLines (l) {
|
||||
const sub1 = Math.pow(l + 16, 3) / 1560896;
|
||||
const sub2 = sub1 > ε ? sub1 : l / κ;
|
||||
const s1r = sub2 * (284517 * m_r0 - 94839 * m_r2);
|
||||
const s2r = sub2 * (838422 * m_r2 + 769860 * m_r1 + 731718 * m_r0);
|
||||
const s3r = sub2 * (632260 * m_r2 - 126452 * m_r1);
|
||||
const s1g = sub2 * (284517 * m_g0 - 94839 * m_g2);
|
||||
const s2g = sub2 * (838422 * m_g2 + 769860 * m_g1 + 731718 * m_g0);
|
||||
const s3g = sub2 * (632260 * m_g2 - 126452 * m_g1);
|
||||
const s1b = sub2 * (284517 * m_b0 - 94839 * m_b2);
|
||||
const s2b = sub2 * (838422 * m_b2 + 769860 * m_b1 + 731718 * m_b0);
|
||||
const s3b = sub2 * (632260 * m_b2 - 126452 * m_b1);
|
||||
|
||||
return {
|
||||
r0s: s1r / s3r,
|
||||
r0i: s2r * l / s3r,
|
||||
r1s: s1r / (s3r + 126452),
|
||||
r1i: (s2r - 769860) * l / (s3r + 126452),
|
||||
g0s: s1g / s3g,
|
||||
g0i: s2g * l / s3g,
|
||||
g1s: s1g / (s3g + 126452),
|
||||
g1i: (s2g - 769860) * l / (s3g + 126452),
|
||||
b0s: s1b / s3b,
|
||||
b0i: s2b * l / s3b,
|
||||
b1s: s1b / (s3b + 126452),
|
||||
b1i: (s2b - 769860) * l / (s3b + 126452),
|
||||
};
|
||||
}
|
||||
|
||||
function calcMaxChromaHsluv (lines, h) {
|
||||
const hueRad = h / 360 * Math.PI * 2;
|
||||
const r0 = distanceFromOriginAngle(lines.r0s, lines.r0i, hueRad);
|
||||
const r1 = distanceFromOriginAngle(lines.r1s, lines.r1i, hueRad);
|
||||
const g0 = distanceFromOriginAngle(lines.g0s, lines.g0i, hueRad);
|
||||
const g1 = distanceFromOriginAngle(lines.g1s, lines.g1i, hueRad);
|
||||
const b0 = distanceFromOriginAngle(lines.b0s, lines.b0i, hueRad);
|
||||
const b1 = distanceFromOriginAngle(lines.b1s, lines.b1i, hueRad);
|
||||
|
||||
return Math.min(r0, r1, g0, g1, b0, b1);
|
||||
}
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "hsluv",
|
||||
name: "HSLuv",
|
||||
coords: {
|
||||
h: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
s: {
|
||||
range: [0, 100],
|
||||
name: "Saturation",
|
||||
},
|
||||
l: {
|
||||
range: [0, 100],
|
||||
name: "Lightness",
|
||||
},
|
||||
},
|
||||
|
||||
base: LCHuv,
|
||||
gamutSpace: sRGB,
|
||||
|
||||
// Convert LCHuv to HSLuv
|
||||
fromBase (lch) {
|
||||
let [l, c, h] = [skipNone(lch[0]), skipNone(lch[1]), skipNone(lch[2])];
|
||||
let s;
|
||||
|
||||
if (l > 99.9999999) {
|
||||
s = 0;
|
||||
l = 100;
|
||||
}
|
||||
else if (l < 0.00000001) {
|
||||
s = 0;
|
||||
l = 0;
|
||||
}
|
||||
else {
|
||||
let lines = calculateBoundingLines(l);
|
||||
let max = calcMaxChromaHsluv(lines, h);
|
||||
s = c / max * 100;
|
||||
}
|
||||
|
||||
return [h, s, l];
|
||||
},
|
||||
|
||||
// Convert HSLuv to LCHuv
|
||||
toBase (hsl) {
|
||||
let [h, s, l] = [skipNone(hsl[0]), skipNone(hsl[1]), skipNone(hsl[2])];
|
||||
let c;
|
||||
|
||||
if (l > 99.9999999) {
|
||||
l = 100;
|
||||
c = 0;
|
||||
}
|
||||
else if (l < 0.00000001) {
|
||||
l = 0;
|
||||
c = 0;
|
||||
}
|
||||
else {
|
||||
let lines = calculateBoundingLines(l);
|
||||
let max = calcMaxChromaHsluv(lines, h);
|
||||
c = max / 100 * s;
|
||||
}
|
||||
|
||||
return [l, c, h];
|
||||
},
|
||||
|
||||
formats: {
|
||||
color: {
|
||||
id: "--hsluv",
|
||||
coords: ["<number> | <angle>", "<percentage> | <number>", "<percentage> | <number>"],
|
||||
},
|
||||
},
|
||||
});
|
||||
65
node_modules/colorjs.io/src/spaces/hsv.js
generated
vendored
Normal file
65
node_modules/colorjs.io/src/spaces/hsv.js
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import HSL from "./hsl.js";
|
||||
|
||||
// The Hue, Whiteness Blackness (HWB) colorspace
|
||||
// See https://drafts.csswg.org/css-color-4/#the-hwb-notation
|
||||
// Note that, like HSL, calculations are done directly on
|
||||
// gamma-corrected sRGB values rather than linearising them first.
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "hsv",
|
||||
name: "HSV",
|
||||
coords: {
|
||||
h: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
s: {
|
||||
range: [0, 100],
|
||||
name: "Saturation",
|
||||
},
|
||||
v: {
|
||||
range: [0, 100],
|
||||
name: "Value",
|
||||
},
|
||||
},
|
||||
|
||||
base: HSL,
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#Interconversion
|
||||
fromBase (hsl) {
|
||||
let [h, s, l] = hsl;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
|
||||
let v = l + s * Math.min(l, 1 - l);
|
||||
|
||||
return [
|
||||
h, // h is the same
|
||||
v === 0 ? 0 : 200 * (1 - l / v), // s
|
||||
100 * v,
|
||||
];
|
||||
},
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#Interconversion
|
||||
toBase (hsv) {
|
||||
let [h, s, v] = hsv;
|
||||
|
||||
s /= 100;
|
||||
v /= 100;
|
||||
|
||||
let l = v * (1 - s / 2);
|
||||
|
||||
return [
|
||||
h, // h is the same
|
||||
(l === 0 || l === 1) ? 0 : ((v - l) / Math.min(l, 1 - l)) * 100,
|
||||
l * 100,
|
||||
];
|
||||
},
|
||||
|
||||
formats: {
|
||||
color: {
|
||||
id: "--hsv",
|
||||
coords: ["<number> | <angle>", "<percentage> | <number>", "<percentage> | <number>"],
|
||||
},
|
||||
},
|
||||
});
|
||||
58
node_modules/colorjs.io/src/spaces/hwb.js
generated
vendored
Normal file
58
node_modules/colorjs.io/src/spaces/hwb.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import HSV from "./hsv.js";
|
||||
|
||||
// The Hue, Whiteness Blackness (HWB) colorspace
|
||||
// See https://drafts.csswg.org/css-color-4/#the-hwb-notation
|
||||
// Note that, like HSL, calculations are done directly on
|
||||
// gamma-corrected sRGB values rather than linearising them first.
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "hwb",
|
||||
name: "HWB",
|
||||
coords: {
|
||||
h: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
w: {
|
||||
range: [0, 100],
|
||||
name: "Whiteness",
|
||||
},
|
||||
b: {
|
||||
range: [0, 100],
|
||||
name: "Blackness",
|
||||
},
|
||||
},
|
||||
|
||||
base: HSV,
|
||||
fromBase (hsv) {
|
||||
let [h, s, v] = hsv;
|
||||
|
||||
return [h, v * (100 - s) / 100, 100 - v];
|
||||
},
|
||||
toBase (hwb) {
|
||||
let [h, w, b] = hwb;
|
||||
|
||||
// Now convert percentages to [0..1]
|
||||
w /= 100;
|
||||
b /= 100;
|
||||
|
||||
// Achromatic check (white plus black >= 1)
|
||||
let sum = w + b;
|
||||
if (sum >= 1) {
|
||||
let gray = w / sum;
|
||||
return [h, 0, gray * 100];
|
||||
}
|
||||
|
||||
let v = (1 - b);
|
||||
let s = (v === 0) ? 0 : 1 - w / v;
|
||||
return [h, s * 100, v * 100];
|
||||
},
|
||||
|
||||
formats: {
|
||||
"hwb": {
|
||||
coords: ["<number> | <angle>", "<percentage> | <number>", "<percentage> | <number>"],
|
||||
},
|
||||
},
|
||||
});
|
||||
133
node_modules/colorjs.io/src/spaces/ictcp.js
generated
vendored
Normal file
133
node_modules/colorjs.io/src/spaces/ictcp.js
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import {multiplyMatrices} from "../util.js";
|
||||
import XYZ_Abs_D65 from "./xyz-abs-d65.js";
|
||||
|
||||
const c1 = 3424 / 4096;
|
||||
const c2 = 2413 / 128;
|
||||
const c3 = 2392 / 128;
|
||||
const m1 = 2610 / 16384;
|
||||
const m2 = 2523 / 32;
|
||||
const im1 = 16384 / 2610;
|
||||
const im2 = 32 / 2523;
|
||||
|
||||
// The matrix below includes the 4% crosstalk components
|
||||
// and is from the Dolby "What is ICtCp" paper"
|
||||
const XYZtoLMS_M = [
|
||||
[ 0.3592832590121217, 0.6976051147779502, -0.0358915932320290 ],
|
||||
[ -0.1920808463704993, 1.1004767970374321, 0.0753748658519118 ],
|
||||
[ 0.0070797844607479, 0.0748396662186362, 0.8433265453898765 ],
|
||||
];
|
||||
// linear-light Rec.2020 to LMS, again with crosstalk
|
||||
// rational terms from Jan Fröhlich,
|
||||
// Encoding High Dynamic Range andWide Color Gamut Imagery, p.97
|
||||
// and ITU-R BT.2124-0 p.2
|
||||
/*
|
||||
const Rec2020toLMS_M = [
|
||||
[ 1688 / 4096, 2146 / 4096, 262 / 4096 ],
|
||||
[ 683 / 4096, 2951 / 4096, 462 / 4096 ],
|
||||
[ 99 / 4096, 309 / 4096, 3688 / 4096 ]
|
||||
];
|
||||
*/
|
||||
// this includes the Ebner LMS coefficients,
|
||||
// the rotation, and the scaling to [-0.5,0.5] range
|
||||
// rational terms from Fröhlich p.97
|
||||
// and ITU-R BT.2124-0 pp.2-3
|
||||
const LMStoIPT_M = [
|
||||
[ 2048 / 4096, 2048 / 4096, 0 ],
|
||||
[ 6610 / 4096, -13613 / 4096, 7003 / 4096 ],
|
||||
[ 17933 / 4096, -17390 / 4096, -543 / 4096 ],
|
||||
];
|
||||
|
||||
// inverted matrices, calculated from the above
|
||||
const IPTtoLMS_M = [
|
||||
[ 0.9999999999999998, 0.0086090370379328, 0.1110296250030260 ],
|
||||
[ 0.9999999999999998, -0.0086090370379328, -0.1110296250030259 ],
|
||||
[ 0.9999999999999998, 0.5600313357106791, -0.3206271749873188 ],
|
||||
];
|
||||
/*
|
||||
const LMStoRec2020_M = [
|
||||
[ 3.4375568932814012112, -2.5072112125095058195, 0.069654319228104608382],
|
||||
[-0.79142868665644156125, 1.9838372198740089874, -0.19240853321756742626 ],
|
||||
[-0.025646662911506476363, -0.099240248643945566751, 1.1248869115554520431 ]
|
||||
];
|
||||
*/
|
||||
const LMStoXYZ_M = [
|
||||
[ 2.0701522183894223, -1.3263473389671563, 0.2066510476294053 ],
|
||||
[ 0.3647385209748072, 0.6805660249472273, -0.0453045459220347 ],
|
||||
[ -0.0497472075358123, -0.0492609666966131, 1.1880659249923042 ],
|
||||
];
|
||||
|
||||
// Only the PQ form of ICtCp is implemented here. There is also an HLG form.
|
||||
// from Dolby, "WHAT IS ICTCP?"
|
||||
// https://professional.dolby.com/siteassets/pdfs/ictcp_dolbywhitepaper_v071.pdf
|
||||
// and
|
||||
// Dolby, "Perceptual Color Volume
|
||||
// Measuring the Distinguishable Colors of HDR and WCG Displays"
|
||||
// https://professional.dolby.com/siteassets/pdfs/dolby-vision-measuring-perceptual-color-volume-v7.1.pdf
|
||||
export default new ColorSpace({
|
||||
id: "ictcp",
|
||||
name: "ICTCP",
|
||||
// From BT.2100-2 page 7:
|
||||
// During production, signal values are expected to exceed the
|
||||
// range E′ = [0.0 : 1.0]. This provides processing headroom and avoids
|
||||
// signal degradation during cascaded processing. Such values of E′,
|
||||
// below 0.0 or exceeding 1.0, should not be clipped during production
|
||||
// and exchange.
|
||||
// Values below 0.0 should not be clipped in reference displays (even
|
||||
// though they represent “negative” light) to allow the black level of
|
||||
// the signal (LB) to be properly set using test signals known as “PLUGE”
|
||||
coords: {
|
||||
i: {
|
||||
refRange: [0, 1], // Constant luminance,
|
||||
name: "I",
|
||||
},
|
||||
ct: {
|
||||
refRange: [-0.5, 0.5], // Full BT.2020 gamut in range [-0.5, 0.5]
|
||||
name: "CT",
|
||||
},
|
||||
cp: {
|
||||
refRange: [-0.5, 0.5],
|
||||
name: "CP",
|
||||
},
|
||||
},
|
||||
|
||||
base: XYZ_Abs_D65,
|
||||
fromBase (XYZ) {
|
||||
// move to LMS cone domain
|
||||
let LMS = multiplyMatrices(XYZtoLMS_M, XYZ);
|
||||
|
||||
return LMStoICtCp(LMS);
|
||||
},
|
||||
toBase (ICtCp) {
|
||||
let LMS = ICtCptoLMS(ICtCp);
|
||||
|
||||
return multiplyMatrices(LMStoXYZ_M, LMS);
|
||||
},
|
||||
});
|
||||
|
||||
function LMStoICtCp (LMS) {
|
||||
// apply the PQ EOTF
|
||||
// we can't ever be dividing by zero because of the "1 +" in the denominator
|
||||
let PQLMS = LMS.map (function (val) {
|
||||
let num = c1 + (c2 * ((val / 10000) ** m1));
|
||||
let denom = 1 + (c3 * ((val / 10000) ** m1));
|
||||
|
||||
return (num / denom) ** m2;
|
||||
});
|
||||
|
||||
// LMS to IPT, with rotation for Y'C'bC'r compatibility
|
||||
return multiplyMatrices(LMStoIPT_M, PQLMS);
|
||||
}
|
||||
|
||||
function ICtCptoLMS (ICtCp) {
|
||||
let PQLMS = multiplyMatrices(IPTtoLMS_M, ICtCp);
|
||||
|
||||
// From BT.2124-0 Annex 2 Conversion 3
|
||||
let LMS = PQLMS.map (function (val) {
|
||||
let num = Math.max((val ** im2) - c1, 0);
|
||||
let denom = (c2 - (c3 * (val ** im2)));
|
||||
return 10000 * ((num / denom) ** im1);
|
||||
});
|
||||
|
||||
return LMS;
|
||||
}
|
||||
7
node_modules/colorjs.io/src/spaces/index-fn-hdr.js
generated
vendored
Normal file
7
node_modules/colorjs.io/src/spaces/index-fn-hdr.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export {default as Jzazbz} from "./jzazbz.js";
|
||||
export {default as JzCzHz} from "./jzczhz.js";
|
||||
export {default as ICTCP} from "./ictcp.js";
|
||||
export {default as REC_2100_PQ} from "./rec2100-pq.js";
|
||||
export {default as REC_2100_HLG} from "./rec2100-hlg.js";
|
||||
export {default as ACEScg} from "./acescg.js";
|
||||
export {default as ACEScc} from "./acescc.js";
|
||||
29
node_modules/colorjs.io/src/spaces/index-fn.js
generated
vendored
Normal file
29
node_modules/colorjs.io/src/spaces/index-fn.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
export {default as XYZ_D65} from "./xyz-d65.js";
|
||||
export {default as XYZ_D50} from "./xyz-d50.js";
|
||||
export {default as XYZ_ABS_D65} from "./xyz-abs-d65.js";
|
||||
export {default as Lab_D65} from "./lab-d65.js";
|
||||
export {default as Lab} from "./lab.js";
|
||||
export {default as LCH} from "./lch.js";
|
||||
export {default as sRGB_Linear} from "./srgb-linear.js";
|
||||
export {default as sRGB} from "./srgb.js";
|
||||
export {default as HSL} from "./hsl.js";
|
||||
export {default as HWB} from "./hwb.js";
|
||||
export {default as HSV} from "./hsv.js";
|
||||
export {default as P3_Linear} from "./p3-linear.js";
|
||||
export {default as P3} from "./p3.js";
|
||||
export {default as A98RGB_Linear} from "./a98rgb-linear.js";
|
||||
export {default as A98RGB} from "./a98rgb.js";
|
||||
export {default as ProPhoto_Linear} from "./prophoto-linear.js";
|
||||
export {default as ProPhoto} from "./prophoto.js";
|
||||
export {default as REC_2020_Linear} from "./rec2020-linear.js";
|
||||
export {default as REC_2020} from "./rec2020.js";
|
||||
export {default as OKLab} from "./oklab.js";
|
||||
export {default as OKLCH} from "./oklch.js";
|
||||
export {default as CAM16_JMh} from "./cam16.js";
|
||||
export {default as HCT} from "./hct.js";
|
||||
export {default as Luv} from "./luv.js";
|
||||
export {default as LCHuv} from "./lchuv.js";
|
||||
export {default as HSLuv} from "./hsluv.js";
|
||||
export {default as HPLuv} from "./hpluv.js";
|
||||
|
||||
export * from "./index-fn-hdr.js";
|
||||
8
node_modules/colorjs.io/src/spaces/index.js
generated
vendored
Normal file
8
node_modules/colorjs.io/src/spaces/index.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import * as spaces from "./index-fn.js";
|
||||
|
||||
export * as spaces from "./index-fn.js";
|
||||
|
||||
for (let key of Object.keys(spaces)) {
|
||||
ColorSpace.register(spaces[key]);
|
||||
}
|
||||
118
node_modules/colorjs.io/src/spaces/jzazbz.js
generated
vendored
Normal file
118
node_modules/colorjs.io/src/spaces/jzazbz.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import {multiplyMatrices} from "../util.js";
|
||||
import XYZ_Abs_D65 from "./xyz-abs-d65.js";
|
||||
|
||||
const b = 1.15;
|
||||
const g = 0.66;
|
||||
const n = 2610 / (2 ** 14);
|
||||
const ninv = (2 ** 14) / 2610;
|
||||
const c1 = 3424 / (2 ** 12);
|
||||
const c2 = 2413 / (2 ** 7);
|
||||
const c3 = 2392 / (2 ** 7);
|
||||
const p = 1.7 * 2523 / (2 ** 5);
|
||||
const pinv = (2 ** 5) / (1.7 * 2523);
|
||||
const d = -0.56;
|
||||
const d0 = 1.6295499532821566E-11;
|
||||
|
||||
const XYZtoCone_M = [
|
||||
[ 0.41478972, 0.579999, 0.0146480 ],
|
||||
[ -0.2015100, 1.120649, 0.0531008 ],
|
||||
[ -0.0166008, 0.264800, 0.6684799 ],
|
||||
];
|
||||
// XYZtoCone_M inverted
|
||||
const ConetoXYZ_M = [
|
||||
[ 1.9242264357876067, -1.0047923125953657, 0.037651404030618 ],
|
||||
[ 0.35031676209499907, 0.7264811939316552, -0.06538442294808501 ],
|
||||
[ -0.09098281098284752, -0.3127282905230739, 1.5227665613052603 ],
|
||||
];
|
||||
const ConetoIab_M = [
|
||||
[ 0.5, 0.5, 0 ],
|
||||
[ 3.524000, -4.066708, 0.542708 ],
|
||||
[ 0.199076, 1.096799, -1.295875 ],
|
||||
];
|
||||
// ConetoIab_M inverted
|
||||
const IabtoCone_M = [
|
||||
[ 1, 0.1386050432715393, 0.05804731615611886 ],
|
||||
[ 0.9999999999999999, -0.1386050432715393, -0.05804731615611886 ],
|
||||
[ 0.9999999999999998, -0.09601924202631895, -0.8118918960560388 ],
|
||||
];
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "jzazbz",
|
||||
name: "Jzazbz",
|
||||
coords: {
|
||||
jz: {
|
||||
refRange: [0, 1],
|
||||
name: "Jz",
|
||||
},
|
||||
az: {
|
||||
refRange: [-0.5, 0.5],
|
||||
},
|
||||
bz: {
|
||||
refRange: [-0.5, 0.5],
|
||||
},
|
||||
},
|
||||
|
||||
base: XYZ_Abs_D65,
|
||||
fromBase (XYZ) {
|
||||
// First make XYZ absolute, not relative to media white
|
||||
// Maximum luminance in PQ is 10,000 cd/m²
|
||||
// Relative XYZ has Y=1 for media white
|
||||
// BT.2048 says media white Y=203 at PQ 58
|
||||
|
||||
let [ Xa, Ya, Za ] = XYZ;
|
||||
|
||||
// modify X and Y
|
||||
let Xm = (b * Xa) - ((b - 1) * Za);
|
||||
let Ym = (g * Ya) - ((g - 1) * Xa);
|
||||
|
||||
// move to LMS cone domain
|
||||
let LMS = multiplyMatrices(XYZtoCone_M, [ Xm, Ym, Za ]);
|
||||
|
||||
// PQ-encode LMS
|
||||
let PQLMS = LMS.map (function (val) {
|
||||
let num = c1 + (c2 * ((val / 10000) ** n));
|
||||
let denom = 1 + (c3 * ((val / 10000) ** n));
|
||||
|
||||
return (num / denom) ** p;
|
||||
});
|
||||
|
||||
// almost there, calculate Iz az bz
|
||||
let [ Iz, az, bz] = multiplyMatrices(ConetoIab_M, PQLMS);
|
||||
// console.log({Iz, az, bz});
|
||||
|
||||
let Jz = ((1 + d) * Iz) / (1 + (d * Iz)) - d0;
|
||||
return [Jz, az, bz];
|
||||
},
|
||||
toBase (Jzazbz) {
|
||||
let [Jz, az, bz] = Jzazbz;
|
||||
let Iz = (Jz + d0) / (1 + d - d * (Jz + d0));
|
||||
|
||||
// bring into LMS cone domain
|
||||
let PQLMS = multiplyMatrices(IabtoCone_M, [ Iz, az, bz ]);
|
||||
|
||||
// convert from PQ-coded to linear-light
|
||||
let LMS = PQLMS.map(function (val) {
|
||||
let num = (c1 - (val ** pinv));
|
||||
let denom = (c3 * (val ** pinv)) - c2;
|
||||
let x = 10000 * ((num / denom) ** ninv);
|
||||
|
||||
return (x); // luminance relative to diffuse white, [0, 70 or so].
|
||||
});
|
||||
|
||||
// modified abs XYZ
|
||||
let [ Xm, Ym, Za ] = multiplyMatrices(ConetoXYZ_M, LMS);
|
||||
|
||||
// restore standard D50 relative XYZ, relative to media white
|
||||
let Xa = (Xm + ((b - 1) * Za)) / b;
|
||||
let Ya = (Ym + ((g - 1) * Xa)) / g;
|
||||
return [ Xa, Ya, Za ];
|
||||
},
|
||||
|
||||
formats: {
|
||||
// https://drafts.csswg.org/css-color-hdr/#Jzazbz
|
||||
"color": {
|
||||
coords: ["<number> | <percentage>", "<number> | <percentage>[-1,1]", "<number> | <percentage>[-1,1]"],
|
||||
},
|
||||
},
|
||||
});
|
||||
53
node_modules/colorjs.io/src/spaces/jzczhz.js
generated
vendored
Normal file
53
node_modules/colorjs.io/src/spaces/jzczhz.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import Jzazbz from "./jzazbz.js";
|
||||
import {constrain as constrainAngle} from "../angles.js";
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "jzczhz",
|
||||
name: "JzCzHz",
|
||||
coords: {
|
||||
jz: {
|
||||
refRange: [0, 1],
|
||||
name: "Jz",
|
||||
},
|
||||
cz: {
|
||||
refRange: [0, 1],
|
||||
name: "Chroma",
|
||||
},
|
||||
hz: {
|
||||
refRange: [0, 360],
|
||||
type: "angle",
|
||||
name: "Hue",
|
||||
},
|
||||
},
|
||||
|
||||
base: Jzazbz,
|
||||
fromBase (jzazbz) {
|
||||
// Convert to polar form
|
||||
let [Jz, az, bz] = jzazbz;
|
||||
let hue;
|
||||
const ε = 0.0002; // chromatic components much smaller than a,b
|
||||
|
||||
if (Math.abs(az) < ε && Math.abs(bz) < ε) {
|
||||
hue = NaN;
|
||||
}
|
||||
else {
|
||||
hue = Math.atan2(bz, az) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
return [
|
||||
Jz, // Jz is still Jz
|
||||
Math.sqrt(az ** 2 + bz ** 2), // Chroma
|
||||
constrainAngle(hue), // Hue, in degrees [0 to 360)
|
||||
];
|
||||
},
|
||||
toBase (jzczhz) {
|
||||
// Convert from polar form
|
||||
// debugger;
|
||||
return [
|
||||
jzczhz[0], // Jz is still Jz
|
||||
jzczhz[1] * Math.cos(jzczhz[2] * Math.PI / 180), // az
|
||||
jzczhz[1] * Math.sin(jzczhz[2] * Math.PI / 180), // bz
|
||||
];
|
||||
},
|
||||
});
|
||||
74
node_modules/colorjs.io/src/spaces/lab-d65.js
generated
vendored
Normal file
74
node_modules/colorjs.io/src/spaces/lab-d65.js
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import {WHITES} from "../adapt.js";
|
||||
import xyz_d65 from "./xyz-d65.js";
|
||||
|
||||
// κ * ε = 2^3 = 8
|
||||
const ε = 216 / 24389; // 6^3/29^3 == (24/116)^3
|
||||
const ε3 = 24 / 116;
|
||||
const κ = 24389 / 27; // 29^3/3^3
|
||||
|
||||
let white = WHITES.D65;
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "lab-d65",
|
||||
name: "Lab D65",
|
||||
coords: {
|
||||
l: {
|
||||
refRange: [0, 100],
|
||||
name: "Lightness",
|
||||
},
|
||||
a: {
|
||||
refRange: [-125, 125],
|
||||
},
|
||||
b: {
|
||||
refRange: [-125, 125],
|
||||
},
|
||||
},
|
||||
|
||||
// Assuming XYZ is relative to D65, convert to CIE Lab
|
||||
// from CIE standard, which now defines these as a rational fraction
|
||||
white,
|
||||
|
||||
base: xyz_d65,
|
||||
// Convert D65-adapted XYZ to Lab
|
||||
// CIE 15.3:2004 section 8.2.1.1
|
||||
fromBase (XYZ) {
|
||||
// compute xyz, which is XYZ scaled relative to reference white
|
||||
let xyz = XYZ.map((value, i) => value / white[i]);
|
||||
|
||||
// now compute f
|
||||
let f = xyz.map(value => value > ε ? Math.cbrt(value) : (κ * value + 16) / 116);
|
||||
|
||||
return [
|
||||
(116 * f[1]) - 16, // L
|
||||
500 * (f[0] - f[1]), // a
|
||||
200 * (f[1] - f[2]), // b
|
||||
];
|
||||
},
|
||||
// Convert Lab to D65-adapted XYZ
|
||||
// Same result as CIE 15.3:2004 Appendix D although the derivation is different
|
||||
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
toBase (Lab) {
|
||||
// compute f, starting with the luminance-related term
|
||||
let f = [];
|
||||
f[1] = (Lab[0] + 16) / 116;
|
||||
f[0] = Lab[1] / 500 + f[1];
|
||||
f[2] = f[1] - Lab[2] / 200;
|
||||
|
||||
// compute xyz
|
||||
let xyz = [
|
||||
f[0] > ε3 ? Math.pow(f[0], 3) : (116 * f[0] - 16) / κ,
|
||||
Lab[0] > 8 ? Math.pow((Lab[0] + 16) / 116, 3) : Lab[0] / κ,
|
||||
f[2] > ε3 ? Math.pow(f[2], 3) : (116 * f[2] - 16) / κ,
|
||||
];
|
||||
|
||||
// Compute XYZ by scaling xyz by reference white
|
||||
return xyz.map((value, i) => value * white[i]);
|
||||
},
|
||||
|
||||
formats: {
|
||||
"lab-d65": {
|
||||
coords: ["<number> | <percentage>", "<number> | <percentage>[-1,1]", "<number> | <percentage>[-1,1]"],
|
||||
},
|
||||
},
|
||||
});
|
||||
74
node_modules/colorjs.io/src/spaces/lab.js
generated
vendored
Normal file
74
node_modules/colorjs.io/src/spaces/lab.js
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import ColorSpace from "../space.js";
|
||||
import {WHITES} from "../adapt.js";
|
||||
import xyz_d50 from "./xyz-d50.js";
|
||||
|
||||
// κ * ε = 2^3 = 8
|
||||
const ε = 216 / 24389; // 6^3/29^3 == (24/116)^3
|
||||
const ε3 = 24 / 116;
|
||||
const κ = 24389 / 27; // 29^3/3^3
|
||||
|
||||
let white = WHITES.D50;
|
||||
|
||||
export default new ColorSpace({
|
||||
id: "lab",
|
||||
name: "Lab",
|
||||
coords: {
|
||||
l: {
|
||||
refRange: [0, 100],
|
||||
name: "Lightness",
|
||||
},
|
||||
a: {
|
||||
refRange: [-125, 125],
|
||||
},
|
||||
b: {
|
||||
refRange: [-125, 125],
|
||||
},
|
||||
},
|
||||
|
||||
// Assuming XYZ is relative to D50, convert to CIE Lab
|
||||
// from CIE standard, which now defines these as a rational fraction
|
||||
white,
|
||||
|
||||
base: xyz_d50,
|
||||
// Convert D50-adapted XYX to Lab
|
||||
// CIE 15.3:2004 section 8.2.1.1
|
||||
fromBase (XYZ) {
|
||||
// compute xyz, which is XYZ scaled relative to reference white
|
||||
let xyz = XYZ.map((value, i) => value / white[i]);
|
||||
|
||||
// now compute f
|
||||
let f = xyz.map(value => value > ε ? Math.cbrt(value) : (κ * value + 16) / 116);
|
||||
|
||||
return [
|
||||
(116 * f[1]) - 16, // L
|
||||
500 * (f[0] - f[1]), // a
|
||||
200 * (f[1] - f[2]), // b
|
||||
];
|
||||
},
|
||||
// Convert Lab to D50-adapted XYZ
|
||||
// Same result as CIE 15.3:2004 Appendix D although the derivation is different
|
||||
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
toBase (Lab) {
|
||||
// compute f, starting with the luminance-related term
|
||||
let f = [];
|
||||
f[1] = (Lab[0] + 16) / 116;
|
||||
f[0] = Lab[1] / 500 + f[1];
|
||||
f[2] = f[1] - Lab[2] / 200;
|
||||
|
||||
// compute xyz
|
||||
let xyz = [
|
||||
f[0] > ε3 ? Math.pow(f[0], 3) : (116 * f[0] - 16) / κ,
|
||||
Lab[0] > 8 ? Math.pow((Lab[0] + 16) / 116, 3) : Lab[0] / κ,
|
||||
f[2] > ε3 ? Math.pow(f[2], 3) : (116 * f[2] - 16) / κ,
|
||||
];
|
||||
|
||||
// Compute XYZ by scaling xyz by reference white
|
||||
return xyz.map((value, i) => value * white[i]);
|
||||
},
|
||||
|
||||
formats: {
|
||||
"lab": {
|
||||
coords: ["<number> | <percentage>", "<number> | <percentage>[-1,1]", "<number> | <percentage>[-1,1]"],
|
||||
},
|
||||
},
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user