TelegramPWA/src/util/colors.ts

206 lines
5.1 KiB
TypeScript

/* eslint-disable eqeqeq */
/* eslint-disable prefer-template */
/* eslint-disable prefer-const */
/* eslint-disable prefer-destructuring */
/* eslint-disable one-var */
/* eslint-disable one-var-declaration-per-line */
import { preloadImage } from './files';
/**
* HEX > RGB
* input: 'xxxxxx' (ex. 'ed15fa') case-insensitive
* output: [r, g, b] ([0-255, 0-255, 0-255])
*/
export function hex2rgb(param: string): [number, number, number] {
return [
parseInt(param.substring(0, 2), 16),
parseInt(param.substring(2, 4), 16),
parseInt(param.substring(4, 6), 16),
];
}
/**
* RGB > HEX
* input: [r, g, b] ([0-255, 0-255, 0-255])
* output: 'xxxxxx' (ex. 'ff0000')
*/
export function rgb2hex(param: [number, number, number]) {
const p0 = param[0].toString(16);
const p1 = param[1].toString(16);
const p2 = param[2].toString(16);
return (p0.length == 1 ? '0' + p0 : p0) + (p1.length == 1 ? '0' + p1 : p1) + (p2.length == 1 ? '0' + p2 : p2);
}
/**
* Converts an RGB color value to HSV. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and v in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSV representation
*/
export function rgb2hsb([r, g, b]: [number, number, number]): [number, number, number] {
r /= 255;
g /= 255;
b /= 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let h!: number, s: number, v: number = max;
let d = max - min;
s = max == 0 ? 0 : d / max;
if (max == min) {
h = 0; // achromatic
} else {
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;
break;
}
h /= 6;
}
return [h, s, v];
}
/**
* Converts an HSV color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes h, s, and v 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 v The value
* @return Array The RGB representation
*/
export function hsb2rgb([h, s, v]: [number, number, number]): [number, number, number] {
let r!: number, g!: number, b!: number;
let i = Math.floor(h * 6);
let f = h * 6 - i;
let p = v * (1 - s);
let q = v * (1 - f * s);
let 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 [
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255),
];
}
export async function getAverageColor(url: string): Promise<[number, number, number]> {
// Only visit every 5 pixels
const blockSize = 5;
const defaultRGB: [number, number, number] = [0, 0, 0];
let data;
let width;
let height;
let i = -4;
let length;
let rgb: [number, number, number] = [0, 0, 0];
let count = 0;
const canvas = document.createElement('canvas');
const context = canvas.getContext && canvas.getContext('2d');
if (!context) {
return defaultRGB;
}
const image = await preloadImage(url);
height = image.naturalHeight || image.offsetHeight || image.height;
width = image.naturalWidth || image.offsetWidth || image.width;
canvas.height = height;
canvas.width = width;
context.drawImage(image, 0, 0);
try {
data = context.getImageData(0, 0, width, height);
} catch (e) {
return defaultRGB;
}
length = data.data.length;
// eslint-disable-next-line no-cond-assign
while ((i += blockSize * 4) < length) {
if (data.data[i + 3] === 0) continue; // Ignore fully transparent pixels
++count;
rgb[0] += data.data[i];
rgb[1] += data.data[i + 1];
rgb[2] += data.data[i + 2];
}
rgb[0] = Math.floor(rgb[0] / count);
rgb[1] = Math.floor(rgb[1] / count);
rgb[2] = Math.floor(rgb[2] / count);
return rgb;
}
export function isDarkColor(rgbColor: [number, number, number]) {
const [r, g, b] = rgbColor;
const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
return luma < 128;
}
// eslint-disable-next-line max-len
// Function was adapted from https://github.com/telegramdesktop/tdesktop/blob/35ff621b5b52f7e3553fb0f990ea13ade7101b8e/Telegram/SourceFiles/data/data_wall_paper.cpp#L518
export function getPatternColor(rgbColor: [number, number, number]) {
let [hue, saturation, value] = rgb2hsb(rgbColor);
saturation = Math.min(1, saturation + 0.05 + 0.1 * (1 - saturation));
value = value > 0.5
? Math.max(0, value * 0.65)
: Math.max(0, Math.min(1, 1 - value * 0.65));
return `hsla(${hue * 360}, ${saturation * 100}%, ${value * 100}%, .4)`;
}