UNPKG

color-blind

Version:

Simulate color blindness by converting RGB hex codes

207 lines (197 loc) 6.4 kB
/* * color-blind * https://github.com/skratchdot/color-blind * * This source was copied from http://mudcu.be/sphere/js/Color.Blind.js * * It contains modifications for use in node.js. * * The original copyright is included below. * * Here is a license note copied/edited from (http://colorlab.wickline.org/colorblind/colorlab/engine.js) * * 20221013 UPDATE * HCIRN appears to no longer exist. This makes it impractical * for users to obtain permission from HCIRN in order to use * this file for commercial works. Instead: * * This work is licensed under a * Creative Commons Attribution-ShareAlike 4.0 International License. * http://creativecommons.org/licenses/by-sa/4.0/ */ /* The Color Blindness Simulation function is copyright (c) 2000-2001 by Matthew Wickline and the Human-Computer Interaction Resource Network ( http://hcirn.com/ ). It is used with the permission of Matthew Wickline and HCIRN, and is freely available for non-commercial use. For commercial use, please contact the Human-Computer Interaction Resource Network ( http://hcirn.com/ ). ------------------------ blind.protan = cpu = 0.735; // confusion point, u coord cpv = 0.265; // confusion point, v coord abu = 0.115807; // color axis begining point (473nm), u coord abv = 0.073581; // color axis begining point (473nm), v coord aeu = 0.471899; // color axis ending point (574nm), u coord aev = 0.527051; // color axis ending point (574nm), v coord blind.deutan = cpu = 1.14; // confusion point, u coord cpv = -0.14; // confusion point, v coord abu = 0.102776; // color axis begining point (477nm), u coord abv = 0.102864; // color axis begining point (477nm), v coord aeu = 0.505845; // color axis ending point (579nm), u coord aev = 0.493211; // color axis ending point (579nm), v coord blind.tritan = cpu = 0.171; // confusion point, u coord cpv = -0.003; // confusion point, v coord abu = 0.045391; // color axis begining point (490nm), u coord abv = 0.294976; // color axis begining point (490nm), v coord aeu = 0.665764; // color axis ending point (610nm), u coord aev = 0.334011; // color axis ending point (610nm), v coord m = (aev - abv) / (aeu - abu); // slope of color axis yi = blind[t].abv - blind[t].abu * blind[t].m; // "y-intercept" of axis (on the "v" axis at u=0) */ 'use strict'; var colorProfile = 'sRGB'; var gammaCorrection = 2.2; var matrixXyzRgb = [ 3.240712470389558, -0.969259258688888, 0.05563600315398933, -1.5372626602963142, 1.875996969313966, -0.2039948802843549, -0.49857440415943116, 0.041556132211625726, 1.0570636917433989 ]; var matrixRgbXyz = [ 0.41242371206635076, 0.21265606784927693, 0.019331987577444885, 0.3575793401363035, 0.715157818248362, 0.11919267420354762, 0.1804662232369621, 0.0721864539171564, 0.9504491124870351 ]; // xy: coordinates, m: slope, yi: y-intercept var blinder = { protan: { x: 0.7465, y: 0.2535, m: 1.273463, yi: -0.073894 }, deutan: { x: 1.4, y: -0.4, m: 0.968437, yi: 0.003331 }, tritan: { x: 0.1748, y: 0, m: 0.062921, yi: 0.292119 }, custom: { x: 0.735, y: 0.265, m: -1.059259, yi: 1.026914 } }; var convertRgbToXyz = function (o) { var M = matrixRgbXyz; var z = {}; var R = o.R / 255; var G = o.G / 255; var B = o.B / 255; if (colorProfile === 'sRGB') { R = (R > 0.04045) ? Math.pow(((R + 0.055) / 1.055), 2.4) : R / 12.92; G = (G > 0.04045) ? Math.pow(((G + 0.055) / 1.055), 2.4) : G / 12.92; B = (B > 0.04045) ? Math.pow(((B + 0.055) / 1.055), 2.4) : B / 12.92; } else { R = Math.pow(R, gammaCorrection); G = Math.pow(G, gammaCorrection); B = Math.pow(B, gammaCorrection); } z.X = R * M[0] + G * M[3] + B * M[6]; z.Y = R * M[1] + G * M[4] + B * M[7]; z.Z = R * M[2] + G * M[5] + B * M[8]; return z; }; var convertXyzToXyy = function (o) { var n = o.X + o.Y + o.Z; if (n === 0) { return {x: 0, y: 0, Y: o.Y}; } return {x: o.X / n, y: o.Y / n, Y: o.Y}; }; exports.Blind = function (rgb, type, anomalize) { var z, v, n, line, c, slope, yi, dx, dy, dX, dY, dZ, dR, dG, dB, _r, _g, _b, ngx, ngz, M, adjust; if (type === "achroma") { // D65 in sRGB z = rgb.R * 0.212656 + rgb.G * 0.715158 + rgb.B * 0.072186; z = {R: z, G: z, B: z}; if (anomalize) { v = 1.75; n = v + 1; z.R = (v * z.R + rgb.R) / n; z.G = (v * z.G + rgb.G) / n; z.B = (v * z.B + rgb.B) / n; } return z; } line = blinder[type]; c = convertXyzToXyy(convertRgbToXyz(rgb)); // The confusion line is between the source color and the confusion point slope = (c.y - line.y) / (c.x - line.x); yi = c.y - c.x * slope; // slope, and y-intercept (at x=0) // Find the change in the x and y dimensions (no Y change) dx = (line.yi - yi) / (slope - line.m); dy = (slope * dx) + yi; dY = 0; // Find the simulated colors XYZ coords z = {}; z.X = dx * c.Y / dy; z.Y = c.Y; z.Z = (1 - (dx + dy)) * c.Y / dy; // Calculate difference between sim color and neutral color ngx = 0.312713 * c.Y / 0.329016; // find neutral grey using D65 white-point ngz = 0.358271 * c.Y / 0.329016; dX = ngx - z.X; dZ = ngz - z.Z; // find out how much to shift sim color toward neutral to fit in RGB space M = matrixXyzRgb; dR = dX * M[0] + dY * M[3] + dZ * M[6]; // convert d to linear RGB dG = dX * M[1] + dY * M[4] + dZ * M[7]; dB = dX * M[2] + dY * M[5] + dZ * M[8]; z.R = z.X * M[0] + z.Y * M[3] + z.Z * M[6]; // convert z to linear RGB z.G = z.X * M[1] + z.Y * M[4] + z.Z * M[7]; z.B = z.X * M[2] + z.Y * M[5] + z.Z * M[8]; _r = ((z.R < 0 ? 0 : 1) - z.R) / dR; _g = ((z.G < 0 ? 0 : 1) - z.G) / dG; _b = ((z.B < 0 ? 0 : 1) - z.B) / dB; _r = (_r > 1 || _r < 0) ? 0 : _r; _g = (_g > 1 || _g < 0) ? 0 : _g; _b = (_b > 1 || _b < 0) ? 0 : _b; adjust = _r > _g ? _r : _g; if (_b > adjust) { adjust = _b; } // shift proportionally... z.R += adjust * dR; z.G += adjust * dG; z.B += adjust * dB; // apply gamma and clamp simulated color... z.R = 255 * (z.R <= 0 ? 0 : z.R >= 1 ? 1 : Math.pow(z.R, 1 / gammaCorrection)); z.G = 255 * (z.G <= 0 ? 0 : z.G >= 1 ? 1 : Math.pow(z.G, 1 / gammaCorrection)); z.B = 255 * (z.B <= 0 ? 0 : z.B >= 1 ? 1 : Math.pow(z.B, 1 / gammaCorrection)); // if (anomalize) { v = 1.75; n = v + 1; z.R = (v * z.R + rgb.R) / n; z.G = (v * z.G + rgb.G) / n; z.B = (v * z.B + rgb.B) / n; } // return z; };