mirror of
https://github.com/lospec/pixel-editor.git
synced 2023-08-10 21:12:51 +03:00
185 lines
4.7 KiB
JavaScript
185 lines
4.7 KiB
JavaScript
|
// CONSTS
|
||
|
|
||
|
// Degrees to radiants
|
||
|
let degreesToRad = Math.PI / 180;
|
||
|
// I'm pretty sure that precision is necessary
|
||
|
let referenceWhite = {x: 95.05, y: 100, z: 108.89999999999999};
|
||
|
|
||
|
/**********************SECTION: COLOUR CONVERSIONS****************************** */
|
||
|
|
||
|
/**
|
||
|
* Converts an HSL color value to RGB. Conversion formula
|
||
|
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||
|
* Assumes h, s, and l are contained in the set [0, 1] and
|
||
|
* returns r, g, and b in the set [0, 255].
|
||
|
*
|
||
|
* @param {number} h The hue
|
||
|
* @param {number} s The saturation
|
||
|
* @param {number} l The lightness
|
||
|
* @return {Array} The RGB representation
|
||
|
*/
|
||
|
function cpHslToRgb(h, s, l){
|
||
|
var r, g, b;
|
||
|
|
||
|
h /= 360;
|
||
|
s /= 100;
|
||
|
l /= 100;
|
||
|
|
||
|
if(s == 0){
|
||
|
r = g = b = l; // achromatic
|
||
|
}else{
|
||
|
var hue2rgb = function hue2rgb(p, q, t){
|
||
|
if(t < 0) t += 1;
|
||
|
if(t > 1) t -= 1;
|
||
|
if(t < 1/6) return p + (q - p) * 6 * t;
|
||
|
if(t < 1/2) return q;
|
||
|
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||
|
var p = 2 * l - q;
|
||
|
r = hue2rgb(p, q, h + 1/3);
|
||
|
g = hue2rgb(p, q, h);
|
||
|
b = hue2rgb(p, q, h - 1/3);
|
||
|
}
|
||
|
|
||
|
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||
|
}
|
||
|
|
||
|
function hsvToRgb(h, s, v) {
|
||
|
var r, g, b;
|
||
|
|
||
|
h /= 360;
|
||
|
s /= 100;
|
||
|
v /= 100;
|
||
|
var i = Math.floor(h * 6);
|
||
|
var f = h * 6 - i;
|
||
|
var p = v * (1 - s);
|
||
|
var q = v * (1 - f * s);
|
||
|
var t = v * (1 - (1 - f) * s);
|
||
|
|
||
|
switch (i % 6) {
|
||
|
case 0: r = v, g = t, b = p; break;
|
||
|
case 1: r = q, g = v, b = p; break;
|
||
|
case 2: r = p, g = v, b = t; break;
|
||
|
case 3: r = p, g = q, b = v; break;
|
||
|
case 4: r = t, g = p, b = v; break;
|
||
|
case 5: r = v, g = p, b = q; break;
|
||
|
}
|
||
|
|
||
|
return [ r * 255, g * 255, b * 255 ];
|
||
|
}
|
||
|
|
||
|
function hslToHex(h, s, l) {
|
||
|
h /= 360;
|
||
|
s /= 100;
|
||
|
l /= 100;
|
||
|
let r, g, b;
|
||
|
if (s === 0) {
|
||
|
r = g = b = l; // achromatic
|
||
|
} else {
|
||
|
const hue2rgb = (p, q, t) => {
|
||
|
if (t < 0) t += 1;
|
||
|
if (t > 1) t -= 1;
|
||
|
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||
|
if (t < 1 / 2) return q;
|
||
|
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||
|
return p;
|
||
|
};
|
||
|
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||
|
const p = 2 * l - q;
|
||
|
r = hue2rgb(p, q, h + 1 / 3);
|
||
|
g = hue2rgb(p, q, h);
|
||
|
b = hue2rgb(p, q, h - 1 / 3);
|
||
|
}
|
||
|
const toHex = x => {
|
||
|
const hex = Math.round(x * 255).toString(16);
|
||
|
return hex.length === 1 ? '0' + hex : hex;
|
||
|
};
|
||
|
|
||
|
return `${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||
|
}
|
||
|
|
||
|
function rgbToHsl(col) {
|
||
|
let r = col.r;
|
||
|
let g = col.g;
|
||
|
let b = col.b;
|
||
|
|
||
|
r /= 255, g /= 255, b /= 255;
|
||
|
|
||
|
let max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||
|
let myH, myS, myL = (max + min) / 2;
|
||
|
|
||
|
if (max == min) {
|
||
|
myH = myS = 0; // achromatic
|
||
|
}
|
||
|
else {
|
||
|
let d = max - min;
|
||
|
myS = myL > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||
|
|
||
|
switch (max) {
|
||
|
case r: myH = (g - b) / d + (g < b ? 6 : 0); break;
|
||
|
case g: myH = (b - r) / d + 2; break;
|
||
|
case b: myH = (r - g) / d + 4; break;
|
||
|
}
|
||
|
|
||
|
myH /= 6;
|
||
|
}
|
||
|
|
||
|
return {h: myH, s: myS, l: myL };
|
||
|
}
|
||
|
|
||
|
function rgbToHsv(col) {
|
||
|
let r = col.r;
|
||
|
let g = col.g;
|
||
|
let b = col.b;
|
||
|
|
||
|
r /= 255, g /= 255, b /= 255;
|
||
|
|
||
|
let max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||
|
let myH, myS, myV = max;
|
||
|
|
||
|
let d = max - min;
|
||
|
myS = max == 0 ? 0 : d / max;
|
||
|
|
||
|
if (max == min) {
|
||
|
myH = 0; // achromatic
|
||
|
}
|
||
|
else {
|
||
|
switch (max) {
|
||
|
case r: myH = (g - b) / d + (g < b ? 6 : 0); break;
|
||
|
case g: myH = (b - r) / d + 2; break;
|
||
|
case b: myH = (r - g) / d + 4; break;
|
||
|
}
|
||
|
|
||
|
myH /= 6;
|
||
|
}
|
||
|
|
||
|
return {h: myH, s: myS, v: myV};
|
||
|
}
|
||
|
|
||
|
function RGBtoCIELAB(rgbColour) {
|
||
|
// Convert to XYZ first via matrix transformation
|
||
|
let x = 0.412453 * rgbColour.r + 0.357580 * rgbColour.g + 0.180423 * rgbColour.b;
|
||
|
let y = 0.212671 * rgbColour.r + 0.715160 * rgbColour.g + 0.072169 * rgbColour.b;
|
||
|
let z = 0.019334 * rgbColour.r + 0.119193 * rgbColour.g + 0.950227 * rgbColour.b;
|
||
|
|
||
|
let xFunc = CIELABconvF(x / referenceWhite.x);
|
||
|
let yFunc = CIELABconvF(y / referenceWhite.y);
|
||
|
let zFunc = CIELABconvF(z / referenceWhite.z);
|
||
|
|
||
|
let myL = 116 * yFunc - 16;
|
||
|
let myA = 500 * (xFunc - yFunc);
|
||
|
let myB = 200 * (yFunc - zFunc);
|
||
|
|
||
|
return {l: myL, a: myA, b: myB};
|
||
|
|
||
|
}
|
||
|
function CIELABconvF(value) {
|
||
|
if (value > Math.pow(6/29, 3)) {
|
||
|
return Math.cbrt(value);
|
||
|
}
|
||
|
|
||
|
return 1/3 * Math.pow(6/29, 2) * value + 4/29;
|
||
|
}
|