Update mpv config:

This commit is contained in:
aria 2025-05-26 00:03:04 +10:00
parent 64e685695e
commit d1e0b97d62
Signed by: aria
SSH key fingerprint: SHA256:WqtcVnDMrv1lnUlNah5k31iywFUI/DV+5yHzCTO4Vds
36 changed files with 5555 additions and 10 deletions

View file

@ -0,0 +1,350 @@
// https://bottosson.github.io/posts/gamutclipping/
// https://www.shadertoy.com/view/7sXcWn
//!PARAM softness_scale
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.3
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (bottosson, soft)
float cbrt(float x) {
return sign(x) * pow(abs(x), 1.0 / 3.0);
}
vec3 cbrt(vec3 color) {
return vec3(
cbrt(color.x),
cbrt(color.y),
cbrt(color.z)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.8190224379967030, 0.3619062600528904, -0.1288737815209879,
0.0329836539323885, 0.9292868615863434, 0.0361446663506424,
0.0481771893596242, 0.2642395317527308, 0.6335478284694309
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
1.2268798758459243, -0.5578149944602171, 0.2813910456659647,
-0.0405757452148008, 1.1122868032803170, -0.0717110580655164,
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816
);
}
vec3 LMS_to_Lab(vec3 LMS) {
return LMS * mat3(
0.2104542683093140, 0.7936177747023054, -0.0040720430116193,
1.9779985324311684, -2.4285922420485799, 0.4505937096174110,
0.0259040424655478, 0.7827717124575296, -0.8086757549230774
);
}
vec3 Lab_to_LMS(vec3 Lab) {
return Lab * mat3(
1.0000000000000000, 0.3963377773761749, 0.2158037573099136,
1.0000000000000000, -0.1055613458156586, -0.0638541728258133,
1.0000000000000000, -0.0894841775298119, -1.2914855480194092
);
}
vec3 RGB_to_Lab(vec3 color) {
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = cbrt(color);
color = LMS_to_Lab(color);
return color;
}
vec3 Lab_to_RGB(vec3 color) {
color = Lab_to_LMS(color);
color = pow(color, vec3(3.0));
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
return color;
}
const float epsilon = 1e-6;
vec3 Lab_to_LCh(vec3 Lab) {
float L = Lab.x;
float a = Lab.y;
float b = Lab.z;
float C = length(vec2(a, b));
float h = (abs(a) < epsilon && abs(b) < epsilon) ? 0.0 : atan(b, a);
return vec3(L, C, h);
}
vec3 LCh_to_Lab(vec3 LCh) {
float L = LCh.x;
float C = LCh.y;
float h = LCh.z;
C = max(C, 0.0);
float a = C * cos(h);
float b = C * sin(h);
return vec3(L, a, b);
}
vec3 output_RGB_to_XYZ(vec3 RGB) {
mat3 M = mat3(
0.41239079926595934, 0.357584339383878, 0.1804807884018343,
0.21263900587151027, 0.715168678767756, 0.07219231536073371,
0.01933081871559182, 0.11919477979462598, 0.9505321522496607);
return RGB * M;
}
vec3 output_XYZ_to_RGB(vec3 XYZ) {
mat3 M = mat3(
3.2409699419045226, -1.537383177570094, -0.4986107602930034,
-0.9692436362808796, 1.8759675015077202, 0.04155505740717559,
0.05563007969699366, -0.20397695888897652, 1.0569715142428786);
return XYZ * M;
}
float findCenter(vec3 x) {
float a = 1.9779985324311684 * x.x - 2.4285922420485799 * x.y + 0.4505937096174110 * x.z;
float b = 0.0259040424655478 * x.x + 0.7827717124575296 * x.y - 0.8086757549230774 * x.z;
float C = sqrt(a*a+b*b);
// Matrix derived for max(l,m,s) to be as close to macadam limit as possible
// this makes it some kind of g0-like estimate
mat3 M = mat3(
2.26923008, -1.43594808, 0.166718,
-0.98545265, 2.12616699, -0.14071434,
-0.02985871, -0.25753239, 1.2873911);
x = x*M;
float x_min = min(x.r,min(x.g,x.b));
float x_max = max(x.r,max(x.g,x.b));
float c = 0.5*(x_max+x_min);
float s = (x_max-x_min);
// math trickery to create values close to c and s, but without producing hard edges
vec3 y = (x-c)/s;
float c_smooth = c + dot(y*y*y, vec3(1.0/3.0))*s;
float s_smooth = sqrt(dot(x-c,x-c)/2.0);
return c_smooth;
}
vec2 findCenterAndPurity(vec3 x) {
// Matrix derived for (c_smooth+s_smooth) to be an approximation of the macadam limit
// this makes it some kind of g0-like estimate
mat3 M = mat3(
2.26775149, -1.43293879, 0.1651873,
-0.98535505, 2.1260072, -0.14065215,
-0.02501605, -0.26349465, 1.2885107);
x = x*M;
float x_min = min(x.r,min(x.g,x.b));
float x_max = max(x.r,max(x.g,x.b));
float c = 0.5*(x_max+x_min);
float s = (x_max-x_min);
// math trickery to create values close to c and s, but without producing hard edges
vec3 y = (x-c)/s;
float c_smooth = c + dot(y*y*y, vec3(1.0/3.0))*s;
float s_smooth = sqrt(dot(x-c,x-c)/2.0);
return vec2(c_smooth, s_smooth);
}
vec3 toLms(vec3 c) {
vec3 lms_ = XYZ_to_LMS(output_RGB_to_XYZ(c));
return sign(lms_)*pow(abs(lms_), vec3(1.0/3.0));
}
float calculateC(vec3 lms) {
// Most of this could be precomputed
// Creating a transform that maps R,G,B in the target gamut to have same distance from grey axis
vec3 lmsR = toLms(vec3(1.0,0.0,0.0));
vec3 lmsG = toLms(vec3(0.0,1.0,0.0));
vec3 lmsB = toLms(vec3(0.0,0.0,1.0));
vec3 uDir = (lmsR - lmsG)/sqrt(2.0);
vec3 vDir = (lmsR + lmsG - 2.0*lmsB)/sqrt(6.0);
mat3 to_uv = inverse(mat3(
1.0, uDir.x, vDir.x,
1.0, uDir.y, vDir.y,
1.0, uDir.z, vDir.z
));
vec3 _uv = lms * to_uv;
return sqrt(_uv.y*_uv.y + _uv.z*_uv.z);
}
vec3 calculateLCh(vec3 c) {
vec3 lms = toLms(c);
float maxLms = findCenter(lms);
float a = 1.9779985324311684 * lms.x - 2.4285922420485799 * lms.y + 0.4505937096174110 * lms.z;
float b = 0.0259040424655478 * lms.x + 0.7827717124575296 * lms.y - 0.8086757549230774 * lms.z;
float C = sqrt(a*a+b*b);
return vec3(maxLms, C, atan(-b, -a));
}
vec2 expandShape(vec3 rgb, vec2 ST) {
vec3 LCh = calculateLCh(rgb);
vec2 STnew = vec2(LCh.x/LCh.y, (1.0-LCh.x)/LCh.y);
STnew = (STnew + 3.0*STnew*STnew*LCh.y);
return vec2(min(ST.x, STnew.x), min(ST.y, STnew.y));
}
float expandScale(vec3 rgb, vec2 ST, float scale) {
vec3 LCh = calculateLCh(rgb);
float Cnew = (1.0/((ST.x/LCh.x) + (ST.y/(1.0-LCh.x))));
return max(LCh.y/Cnew, scale);
}
vec2 approximateShape() {
float m = -softness_scale*0.2;
float s = 1.0 + (softness_scale*0.2+softness_scale*0.8);
vec2 ST = vec2(1000.0,1000.0);
ST = expandShape(m+s*vec3(1.0,0.0,0.0), ST);
ST = expandShape(m+s*vec3(1.0,1.0,0.0), ST);
ST = expandShape(m+s*vec3(0.0,1.0,0.0), ST);
ST = expandShape(m+s*vec3(0.0,1.0,1.0), ST);
ST = expandShape(m+s*vec3(0.0,0.0,1.0), ST);
ST = expandShape(m+s*vec3(1.0,0.0,1.0), ST);
float scale = 0.0;
scale = expandScale(m+s*vec3(1.0,0.0,0.0), ST, scale);
scale = expandScale(m+s*vec3(1.0,1.0,0.0), ST, scale);
scale = expandScale(m+s*vec3(0.0,1.0,0.0), ST, scale);
scale = expandScale(m+s*vec3(0.0,1.0,1.0), ST, scale);
scale = expandScale(m+s*vec3(0.0,0.0,1.0), ST, scale);
scale = expandScale(m+s*vec3(1.0,0.0,1.0), ST, scale);
return ST/scale;
}
vec3 compute(float L, float hue, float sat) {
vec3 c = vec3(L, cos(hue), sin(hue));
float l_ = + 0.3963377773761749 * c.y + 0.2158037573099136 * c.z;
float m_ = - 0.1055613458156586 * c.y - 0.0638541728258133 * c.z;
float s_ = - 0.0894841775298119 * c.y - 1.2914855480194092 * c.z;
vec3 lms = vec3(l_,m_,s_);
vec2 MC = findCenterAndPurity(lms);
lms -= MC.x;
lms *= sat;
lms += c.x;
lms = lms*lms*lms;
vec3 rgb = output_XYZ_to_RGB(LMS_to_XYZ(lms));
return rgb;
}
vec3 softSaturate(vec3 x, vec3 a) {
a = clamp(a, 0.0,softness_scale);
a = 1.0+a;
x = min(x, a);
vec3 b = (a-1.0)*sqrt(a/(2.0-a));
return 1.0 - (sqrt((x-a)*(x-a) + b*b) - b)/(sqrt(a*a+b*b)-b);
}
vec3 softClipColor(vec3 color) {
// soft clip of rgb values to avoid artifacts of hard clipping
// causes hues distortions, but is a smooth mapping
float maxRGB = max(max(color.r, color.g), color.b);
float minRGB = min(min(color.r, color.g), color.b);
float grey = 0.2;
vec3 x = color-grey;
vec3 xsgn = sign(x);
vec3 xscale = 0.5 + xsgn*(0.5-grey);
x /= xscale;
float softness_0 = maxRGB/(1.0+softness_scale)*softness_scale;
float softness_1 = (1.0-minRGB)/(1.0+softness_scale)*softness_scale;
vec3 softness = vec3(0.5)*(softness_0+softness_1 + xsgn*(softness_1 - softness_0));
return grey + xscale*xsgn*softSaturate(abs(x), softness);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
vec3 lch = Lab_to_LCh(RGB_to_Lab(color.rgb));
float L = lch.x;
float C = lch.y;
float h = lch.z;
// if (L >= 1.0) {
// return vec4(vec3(1.0, 1.0, 1.0), color.a);
// }
// if (L <= 0.0) {
// return vec4(vec3(0.0, 0.0, 0.0), color.a);
// }
if (C <= 1e-6) {
return color;
}
vec2 ST = approximateShape();
float C_smooth = (1.0 / ((ST.x / L) + (ST.y / max(1.0 - L, 1e-6))));
color.rgb = compute(L, h, C / sqrt(C * C / C_smooth / C_smooth + 1.0));
color.rgb = softClipColor(color.rgb);
return color;
}

View file

@ -0,0 +1,512 @@
// RGB to RGB conversion, includes chromatic adaptation transform
// All coordinates are based on the CIE 1931 2° chromaticity diagram
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (clip)
// You can use custom chromaticity here.
// Example: BT.709 with a D93 white point: Chromaticity(BT709.r, BT709.g, BT709.b, D93)
// You can also define custom coordinates: Chromaticity(vec2(0.7347, 0.2653), BT709.g, BT709.b, D65)
#define from BT2020
#define to BT709
// White points of standard illuminants
// https://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants
const vec2 A = vec2(0.44757, 0.40745);
const vec2 B = vec2(0.34842, 0.35161);
const vec2 C = vec2(0.31006, 0.31616);
const vec2 D50 = vec2(0.34567, 0.35850);
const vec2 D55 = vec2(0.33242, 0.34743);
const vec2 D65 = vec2(0.31271, 0.32902);
const vec2 D75 = vec2(0.29902, 0.31485);
const vec2 D93 = vec2(0.28315, 0.29711);
const vec2 E = vec2(1.0/3.0, 1.0/3.0);
const vec2 F2 = vec2(0.37208, 0.37529);
const vec2 F7 = vec2(0.31292, 0.32933);
const vec2 F11 = vec2(0.38052, 0.37713);
const vec2 DCI = vec2(0.31400, 0.35100);
// It is also known as D60
const vec2 ACES = vec2(0.32168, 0.33767);
// Colour Matching Between OLED and CRT
// https://www.sony.jp/products/catalog/FUN_WhitePaper_OLED_ColorMatching_V1_00.pdf
const vec2 BRAVIA = vec2(0.3067, 0.318);
// https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
vec2 CIE_D(float T) {
// Compensate for the loss caused by the accuracy difference between the old and new standards
// c2 = 1.4387768775039337
// https://en.wikipedia.org/wiki/Planckian_locus#Planckian_locus_in_the_XYZ_color_space
T = (T * 1.4388) / 1.438;
// This formula is applicable to temperatures ranging from 4000K to 25000K
T = clamp(T, 4000.0, 25000.0);
float t1 = 1000.0 / T;
float t2 = t1 * t1;
float t3 = t1 * t2;
float x =
T <= 7000.0
? 0.244063 + 0.09911 * t1 + 2.9678 * t2 - 4.607 * t3
: 0.23704 + 0.24748 * t1 + 1.9018 * t2 - 2.0064 * t3;
// Daylight locus
float y = -0.275 + 2.87 * x - 3.0 * x * x;
return vec2(x, y);
}
// https://en.wikipedia.org/wiki/Planckian_locus#Approximation
vec2 Kang(float T) {
// This formula is applicable to temperatures ranging from 1667K to 25000K
T = clamp(T, 1667.0, 25000.0);
float t1 = 1000.0 / T;
float t2 = t1 * t1;
float t3 = t1 * t2;
float x =
T <= 4000.0
? -0.2661239 * t3 - 0.234358 * t2 + 0.8776956 * t1 + 0.17991
: -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t1 + 0.24039;
float x2 = x * x;
float x3 = x2 * x;
float y =
T <= 2222.0
? -1.1063814 * x3 - 1.3481102 * x2 - 2.18555832 * x - 0.20219683
: T <= 4000.0
? -0.9549476 * x3 - 1.37418593 * x2 - 2.09137015 * x - 0.16748867
: 3.081758 * x3 - 5.8733867 * x2 + 3.75112997 * x - 0.37001483;
return vec2(x, y);
}
// Chromaticities
// https://www.itu.int/rec/T-REC-H.273
// https://github.com/colour-science/colour/tree/develop/colour/models/rgb/datasets
struct Chromaticity {
vec2 r, g, b, w;
};
// ITU-R BT.2020, ITU-R BT.2100
const Chromaticity BT2020 = Chromaticity(
vec2(0.708, 0.292),
vec2(0.170, 0.797),
vec2(0.131, 0.046),
D65
);
// ITU-R BT.709, IEC 61966-2-1 (sRGB)
const Chromaticity BT709 = Chromaticity(
vec2(0.64, 0.33),
vec2(0.30, 0.60),
vec2(0.15, 0.06),
D65
);
// ITU-R BT.601 (525 lines), SMPTE ST 240
const Chromaticity BT601_525 = Chromaticity(
vec2(0.630, 0.340),
vec2(0.310, 0.595),
vec2(0.155, 0.070),
D65
);
// ITU-R BT.601 (625 lines), BT.470 (B/G), EBU 3213-E
const Chromaticity BT601_625 = Chromaticity(
vec2(0.64, 0.33),
vec2(0.29, 0.60),
vec2(0.15, 0.06),
D65
);
// ITU-R BT.470 (M)
const Chromaticity BT470m = Chromaticity(
vec2(0.67, 0.33),
vec2(0.21, 0.71),
vec2(0.14, 0.08),
C
);
// P3-DCI (Theater)
const Chromaticity P3DCI = Chromaticity(
vec2(0.680, 0.320),
vec2(0.265, 0.690),
vec2(0.150, 0.060),
DCI
);
// P3-D65 (Display)
const Chromaticity P3D65 = Chromaticity(
P3DCI.r,
P3DCI.g,
P3DCI.b,
D65
);
// P3-D60 (ACES Cinema)
const Chromaticity P3D60 = Chromaticity(
P3DCI.r,
P3DCI.g,
P3DCI.b,
ACES
);
// ITU-T H.273 (Generic film)
const Chromaticity H273_8 = Chromaticity(
vec2(0.681, 0.319),
vec2(0.243, 0.692),
vec2(0.145, 0.049),
C
);
// ITU-T H.273 (No corresponding industry specification identified)
const Chromaticity H273_22 = Chromaticity(
vec2(0.630, 0.340),
vec2(0.295, 0.605),
vec2(0.155, 0.077),
D65
);
// CIE RGB (CIE 1931 color space)
const Chromaticity CIERGB = Chromaticity(
vec2(0.73474284, 0.26525716),
vec2(0.27377903, 0.7174777),
vec2(0.16655563, 0.00891073),
E
);
// CIE XYZ (CIE 1931 color space)
const Chromaticity XYZ = Chromaticity(
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(0.0, 0.0),
E
);
// CIE XYZ (CIE 1931 color space, D65 whitepoint)
const Chromaticity XYZD65 = Chromaticity(
XYZ.r,
XYZ.g,
XYZ.b,
D65
);
// CIE XYZ (CIE 1931 color space, D50 whitepoint)
const Chromaticity XYZD50 = Chromaticity(
XYZ.r,
XYZ.g,
XYZ.b,
D50
);
// Grayscale, Monochrome
const Chromaticity GRAY = Chromaticity(
vec2(0.0, 1.0),
vec2(0.0, 1.0),
vec2(0.0, 1.0),
E
);
// Adobe RGB (1998)
const Chromaticity AdobeRGB = Chromaticity(
vec2(0.64, 0.33),
vec2(0.21, 0.71),
vec2(0.15, 0.06),
D65
);
// Adobe Wide Gamut RGB
const Chromaticity AdobeWideGamutRGB = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.1152, 0.8264),
vec2(0.1566, 0.0177),
D50
);
// ROMM (ProPhoto RGB)
const Chromaticity ROMM = Chromaticity(
vec2(0.734699, 0.265301),
vec2(0.159597, 0.840403),
vec2(0.036598, 0.000105),
D50
);
// AP0 (ACES 2065-1)
const Chromaticity AP0 = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.0000, 1.0000),
vec2(0.0001, -0.0770),
ACES
);
// AP1 (ACEScg, cc, cct, proxy)
const Chromaticity AP1 = Chromaticity(
vec2(0.713, 0.293),
vec2(0.165, 0.830),
vec2(0.128, 0.044),
ACES
);
// ARRI Wide Gamut 3
const Chromaticity AWG3 = Chromaticity(
vec2(0.684, 0.313),
vec2(0.221, 0.848),
vec2(0.0861, -0.102),
D65
);
// ARRI Wide Gamut 4
const Chromaticity AWG4 = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.1424, 0.8576),
vec2(0.0991, -0.0308),
D65
);
// RED Wide Gamut RGB
const Chromaticity RWG = Chromaticity(
vec2(0.780308, 0.304253),
vec2(0.121595, 1.493994),
vec2(0.095612, -0.084589),
D65
);
// DaVinci Wide Gamut
const Chromaticity DWG = Chromaticity(
vec2(0.8000, 0.3130),
vec2(0.1682, 0.9877),
vec2(0.0790, -0.1155),
D65
);
// FilmLight E-Gamut
const Chromaticity EGAMUT = Chromaticity(
vec2(0.8000, 0.3177),
vec2(0.1800, 0.9000),
vec2(0.0650, -0.0805),
D65
);
// FilmLight E-Gamut 2
const Chromaticity EGAMUT2 = Chromaticity(
vec2(0.8300, 0.3100),
vec2(0.1500, 0.9500),
vec2(0.0650, -0.0805),
D65
);
// FUJIFILM F-Gamut C
const Chromaticity FGAMUTC = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.0263, 0.9737),
vec2(0.1173, -0.0224),
D65
);
// Sony S-Gamut3/S-Gamut
const Chromaticity SGAMUT = Chromaticity(
vec2(0.73, 0.280),
vec2(0.14, 0.855),
vec2(0.10, -0.050),
D65
);
// Sony S-Gamut.Cine
const Chromaticity SGAMUTCINE = Chromaticity(
vec2(0.766, 0.275),
vec2(0.225, 0.800),
vec2(0.089, -0.087),
D65
);
// Canon Cinema Gamut
const Chromaticity CINEMA_GAMUT = Chromaticity(
vec2(0.74, 0.27),
vec2(0.17, 1.14),
vec2(0.08, -0.10),
D65
);
// Panasonic V-Gamut
const Chromaticity VGAMUT = Chromaticity(
vec2(0.730, 0.28),
vec2(0.165, 0.84),
vec2(0.100, -0.03),
D65
);
// DJI D-Gamut
const Chromaticity DGAMUT = Chromaticity(
vec2(0.71, 0.31),
vec2(0.21, 0.88),
vec2(0.09, -0.08),
D65
);
// Chromatic adaptation transform
// https://en.wikipedia.org/wiki/LMS_color_space
// It is also known as von Kries
const mat3 HPE = mat3(
0.40024, 0.70760, -0.08081,
-0.22630, 1.16532, 0.04570,
0.00000, 0.00000, 0.91822
);
const mat3 Bradford = mat3(
0.8951, 0.2664, -0.1614,
-0.7502, 1.7135, 0.0367,
0.0389, -0.0685, 1.0296
);
const mat3 CAT97 = mat3(
0.8562, 0.3372, -0.1934,
-0.8360, 1.8327, 0.0033,
0.0357, -0.0469, 1.0112
);
const mat3 CAT02 = mat3(
0.7328, 0.4296, -0.1624,
-0.7036, 1.6975, 0.0061,
0.0030, 0.0136, 0.9834
);
const mat3 CAT16 = mat3(
0.401288, 0.650173, -0.051461,
-0.250268, 1.204414, 0.045854,
-0.002079, 0.048952, 0.953127
);
// Other constants
const mat3 Identity3 = mat3(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
const mat3 SingularY3 = mat3(
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0
);
// http://www.brucelindbloom.com/Eqn_xyY_to_XYZ.html
vec3 xyY_to_XYZ(vec3 xyY) {
float x = xyY.x;
float y = xyY.y;
float Y = xyY.z;
float multiplier = Y / max(y, 1e-6);
float z = 1.0 - x - y;
float X = x * multiplier;
float Z = z * multiplier;
return vec3(X, Y, Z);
}
// http://www.brucelindbloom.com/Eqn_XYZ_to_xyY.html
vec3 XYZ_to_xyY(vec3 XYZ) {
float X = XYZ.x;
float Y = XYZ.y;
float Z = XYZ.z;
float divisor = X + Y + Z;
if (divisor == 0.0) divisor = 1e-6;
float x = X / divisor;
float y = Y / divisor;
return vec3(x, y, Y);
}
// http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
mat3 RGB_to_XYZ(Chromaticity C) {
if (C == GRAY)
return Identity3;
vec3 r = xyY_to_XYZ(vec3(C.r, 1.0));
vec3 g = xyY_to_XYZ(vec3(C.g, 1.0));
vec3 b = xyY_to_XYZ(vec3(C.b, 1.0));
vec3 w = xyY_to_XYZ(vec3(C.w, 1.0));
mat3 xyz = transpose(mat3(r, g, b));
vec3 scale = w * inverse(xyz);
mat3 scale_diag = mat3(
scale.x, 0.0, 0.0,
0.0, scale.y, 0.0,
0.0, 0.0, scale.z
);
return scale_diag * xyz;
}
mat3 XYZ_to_RGB(Chromaticity C) {
if (C == GRAY)
return SingularY3;
return inverse(RGB_to_XYZ(C));
}
// http://www.brucelindbloom.com/Eqn_ChromAdapt.html
mat3 adaptation(vec2 w1, vec2 w2, mat3 cat) {
vec3 src_xyz = xyY_to_XYZ(vec3(w1, 1.0));
vec3 dst_xyz = xyY_to_XYZ(vec3(w2, 1.0));
vec3 src_lms = src_xyz * cat;
vec3 dst_lms = dst_xyz * cat;
vec3 scale = dst_lms / src_lms;
mat3 scale_diag = mat3(
scale.x, 0.0, 0.0,
0.0, scale.y, 0.0,
0.0, 0.0, scale.z
);
return cat * scale_diag * inverse(cat);
}
// CAM16 uses CAT16 as cat and equal-energy illuminant (E) as wt.
// https://www.researchgate.net/publication/318152296_Comprehensive_color_solutions_CAM16_CAT16_and_CAM16-UCS
// Android uses Bradford as cat and D50 as wt.
// https://android.googlesource.com/platform/frameworks/base/+/master/graphics/java/android/graphics/ColorSpace.java
mat3 adaptation_two_step(vec2 w1, vec2 w2, vec2 wt, mat3 cat) {
return adaptation(w1, wt, cat) * adaptation(wt, w2, cat);
}
mat3 adaptation_two_step(vec2 w1, vec2 w2) {
return adaptation_two_step(w1, w2, E, CAT16);
}
mat3 RGB_to_RGB(Chromaticity c1, Chromaticity c2) {
mat3 m = Identity3;
if (c1 != c2) {
m *= RGB_to_XYZ(c1);
if (c1.w != c2.w) {
m *= adaptation_two_step(c1.w, c2.w);
}
m *= XYZ_to_RGB(c2);
}
return m;
}
vec3 RGB_to_RGB(vec3 c, Chromaticity c1, Chromaticity c2) {
return c * RGB_to_RGB(c1, c2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_RGB(color.rgb, from, to);
return color;
}

View file

@ -0,0 +1,104 @@
// Visualizes the out of gamut colors using false color.
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (false color)
float cbrt(float x) {
return sign(x) * pow(abs(x), 1.0 / 3.0);
}
vec3 cbrt(vec3 color) {
return vec3(
cbrt(color.x),
cbrt(color.y),
cbrt(color.z)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.8190224379967030, 0.3619062600528904, -0.1288737815209879,
0.0329836539323885, 0.9292868615863434, 0.0361446663506424,
0.0481771893596242, 0.2642395317527308, 0.6335478284694309
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
1.2268798758459243, -0.5578149944602171, 0.2813910456659647,
-0.0405757452148008, 1.1122868032803170, -0.0717110580655164,
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816
);
}
vec3 LMS_to_Lab(vec3 LMS) {
return LMS * mat3(
0.2104542683093140, 0.7936177747023054, -0.0040720430116193,
1.9779985324311684, -2.4285922420485799, 0.4505937096174110,
0.0259040424655478, 0.7827717124575296, -0.8086757549230774
);
}
vec3 Lab_to_LMS(vec3 Lab) {
return Lab * mat3(
1.0000000000000000, 0.3963377773761749, 0.2158037573099136,
1.0000000000000000, -0.1055613458156586, -0.0638541728258133,
1.0000000000000000, -0.0894841775298119, -1.2914855480194092
);
}
vec3 RGB_to_Lab(vec3 color) {
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = cbrt(color);
color = LMS_to_Lab(color);
return color;
}
vec3 Lab_to_RGB(vec3 color) {
color = Lab_to_LMS(color);
color = pow(color, vec3(3.0));
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
return color;
}
vec3 BT2020_to_BT709(vec3 color) {
return color * mat3(
1.66049100210843540, -0.58764113878854950, -0.072849863319884740,
-0.12455047452159074, 1.13289989712595960, -0.008349422604369515,
-0.01815076335490526, -0.10057889800800737, 1.118729661362913000
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
vec3 color_dst = BT2020_to_BT709(color.rgb);
vec3 color_dst_cliped = clamp(color_dst, 0.0, 1.0);
bool is_in_gamut = color_dst == color_dst_cliped;
color.rgb = RGB_to_Lab(color.rgb);
color.rgb = is_in_gamut ? vec3(color.x, vec2(0.0)) : vec3(0.5, color.yz);
color.rgb = Lab_to_RGB(color.rgb);
color.rgb = BT2020_to_BT709(color.rgb);
return color;
}

View file

@ -0,0 +1,88 @@
// https://github.com/jedypod/gamut-compress
// https://github.com/ampas/aces-dev/blob/dev/transforms/ctl/lmt/LMT.Academy.ReferenceGamutCompress.ctl
//!PARAM cyan_limit
//!TYPE float
//!MINIMUM 1.01
//!MAXIMUM 2.0
1.216
//!PARAM magenta_limit
//!TYPE float
//!MINIMUM 1.01
//!MAXIMUM 2.0
1.035
//!PARAM yellow_limit
//!TYPE float
//!MINIMUM 1.01
//!MAXIMUM 2.0
1.076
//!PARAM cyan_threshold
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.998
//!PARAM magenta_threshold
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.940
//!PARAM yellow_threshold
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.977
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (jedypod)
// Parabolic compression function
// https://www.desmos.com/calculator/khowxlu6xh
float parabolic(float x, float t0, float x0, float y0) {
float s = (y0 - t0) / sqrt(x0 - y0);
float ox = t0 - s * s / 4.0;
float oy = t0 - s * sqrt(s * s / 4.0);
return (x < t0 ? x : s * sqrt(x - ox) + oy);
}
vec3 gamut_compress(vec3 rgb) {
// Achromatic axis
float ac = max(max(rgb.r, rgb.g), rgb.b);
// Inverse RGB Ratios: distance from achromatic axis
vec3 d = ac == 0.0 ? vec3(0.0) : (ac - rgb) / abs(ac);
// Compressed distance
vec3 cd = vec3(
parabolic(d.x, cyan_threshold, cyan_limit, 1.0),
parabolic(d.y, magenta_threshold, magenta_limit, 1.0),
parabolic(d.z, yellow_threshold, yellow_limit, 1.0)
);
// Inverse RGB Ratios to RGB
vec3 crgb = ac - cd * abs(ac);
return crgb;
}
vec3 BT2020_to_BT709(vec3 color) {
return color * mat3(
1.66049100210843540, -0.58764113878854950, -0.072849863319884740,
-0.12455047452159074, 1.13289989712595960, -0.008349422604369515,
-0.01815076335490526, -0.10057889800800737, 1.118729661362913000
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = BT2020_to_BT709(color.rgb);
color.rgb = gamut_compress(color.rgb);
return color;
}