<languageVersion : 1.0>
// Based on “Color-Defective Vision and Computer Graphics Displays” written by Gary W. Meyer and Donald P. Greenberg
// http://ieeexplore.ieee.org/iel1/38/408/00007759.pdf?arnumber=7759
kernel ColorBlindness
<
namespace : "Color Blindness";
vendor : "http://www.colorjack.com/";
version : 1;
description : "This filter provides simulation of Protanopia, Deuteranopia, Tritanopia, and Achromatopsia.";
>
{
input image4 src;
output pixel4 dst;
parameter int type
<
minValue: 0;
maxValue: 3;
defaultValue: 4;
>;
parameter float amount
<
minValue: 0.0;
maxValue: 1.0;
defaultValue: 1.0;
>;
const float3x3 RGB_XYZ = float3x3( // [M] in sRGB (D65)
0.412424, 0.212656, 0.0193324,
0.357579, 0.715158, 0.119193,
0.180464, 0.0721856, 0.950444
);
const float3x3 XYZ_RGB = float3x3( // -[M] in sRGB (D65)
3.24071, -0.969258, 0.0556352,
-1.53726, 1.87599, -0.203996,
-0.498571, 0.0415557, 1.05707
);
void evaluatePixel() {
float4 o = sampleNearest(src, outCoord()); // source pixel
float3 z = float3(o.r, o.g, o.b); // destination pixel
float _x, _y, _m, _yi;
if(type == 4) { // no changes (normal vision)
dst.r = o.r * (1.0 - amount) + o.r * amount;
dst.g = o.g * (1.0 - amount) + o.g * amount;
dst.b = o.b * (1.0 - amount) + o.b * amount;
dst.a = o.a;
} else if(type == 3) { // convert to Monochrome using sRGB WhitePoint
// monochrome
z.r = z.g = z.b = (z.r * 0.212656 + z.g * 0.715158 + z.b * 0.072186);
// anomylize colors
z.r = o.r * (1.0 - amount) + z.r * amount;
z.g = o.g * (1.0 - amount) + z.g * amount;
z.b = o.b * (1.0 - amount) + z.b * amount;
// record values
dst.r = z.r;
dst.g = z.g;
dst.b = z.b;
dst.a = o.a;
} else {
if(type == 0) {
_x = 0.7465;
_y = 0.2535;
_m = 1.273463;
_yi = -0.073894;
} else if(type == 1) {
_x = 1.4;
_y = -0.4;
_m = 0.968437;
_yi = 0.003331;
} else if(type == 2) {
_x = 0.1748;
_y = 0.0;
_m = 0.062921;
_yi = 0.292119;
}
// Convert source color into XYZ color space
float3 XYZ = RGB_XYZ * float3(pow(o.r, 2.2), pow(o.g, 2.2), pow(o.b, 2.2));
// Convert XYZ into xyY — breaks down to Chromacity Coordinates (xy) and Luminance (L)
float o_x = XYZ[0] / (XYZ[0] + XYZ[1] + XYZ[2]);
float o_y = XYZ[1] / (XYZ[0] + XYZ[1] + XYZ[2]);
float o_Y = XYZ[1];
// Generate the “Confusion Line” between the source color and the Confusion Point
float m = (o_y - _y) / (o_x - _x); // slope of Confusion Line
float yi = o_y - o_x * m; // y-intercept of Confusion Line (x-intercept = 0.0)
// How far do the xy coords deviate from the selected type of Color Blindness?
float change_x = (_yi - yi) / (m - _m);
float change_y = (m * change_x) + yi;
float change_Y = 0.0;
// Compute the simulated color’s XYZ coords
float z_X = change_x * o_Y / change_y;
float z_Y = o_Y;
float z_Z = (1.0 - (change_x + change_y)) * o_Y / change_y;
// Difference between simulated color and neutral grey (in D65)
float grey_X = 0.312713 * o_Y / 0.329016;
float grey_Z = 0.358271 * o_Y / 0.329016;
float3 diff = XYZ_RGB * float3(grey_X - z_X, 0.0, grey_Z - z_Z);
// Compensate simulated color towards a neutral fit in RGB space
z = XYZ_RGB * float3(z_X, z_Y, z_Z); // convert to RGB color space
float _r = ((z.r < 0.0 ? 0.0 : 1.0) - z.r) / diff.r;
float _g = ((z.g < 0.0 ? 0.0 : 1.0) - z.g) / diff.g;
float _b = ((z.b < 0.0 ? 0.0 : 1.0) - z.b) / diff.b;
float adjust = max((_r > 1.0 || _r < 0.0) ? 0.0 : _r, (_g > 1.0 || _g < 0.0) ? 0.0 : _g);
adjust = max(adjust, (_b > 1.0 || _b < 0.0) ? 0.0 : _b);
// now shift * all * three proportional to the greatest shift...
z.r = z.r + (adjust * diff.r);
z.g = z.g + (adjust * diff.g);
z.b = z.b + (adjust * diff.b);
// and then save the resulting simulated color...
z.r = pow(z.r, 1.0 / 2.2);
z.g = pow(z.g, 1.0 / 2.2);
z.b = pow(z.b, 1.0 / 2.2);
// anomylize colors
z.r = o.r * (1.0 - amount) + z.r * amount;
z.g = o.g * (1.0 - amount) + z.g * amount;
z.b = o.b * (1.0 - amount) + z.b * amount;
// return values
dst.r = z.r;
dst.g = z.g;
dst.b = z.b;
dst.a = o.a;
}
}
}
Required-plugin: