@@ -5,186 +5,45 @@ kernel GamutCompression : public ImageComputationKernel<ePixelWise> {
55 param:
66 float3 threshold;
77 float p;
8- float shd_rolloff;
98 float cyan;
109 float magenta;
1110 float yellow;
1211 float p_Height;
13- int method;
14- bool hexagonal;
1512 bool invert;
1613 bool overlay;
1714
1815 local:
1916 float3 thr;
2017 float3 lim;
21- float pi;
2218
2319 void init() {
24- pi = 3.14159265359;
25-
2620 // thr is the percentage of the core gamut to preserve
2721 thr = float3(min(0.9999f, threshold.x), min(0.9999f, threshold.y), min(0.9999f, threshold.z));
2822
2923 // lim is the max distance from the gamut boundary that will be compressed
3024 // 0 is a no-op, 1 will compress colors from a distance of 2.0 from achromatic to the gamut boundary
31- // if method is Reinhard or Power, use the limit as-is
32- if (method == 1 || method == 2) {
33- lim = float3(cyan+1.0f, magenta+1.0f, yellow+1.0f);
34- } else {
35- // otherwise, we have to bruteforce the value of limit
36- // such that lim is the value of x where y=1.0f
37- // importantly, this runs once at the beginning of evaluation, NOT per-pixel!!!
38- lim = float3(
39- bisect(max(0.0001f, cyan)+1.0f, thr.x),
40- bisect(max(0.0001f, magenta)+1.0f, thr.y),
41- bisect(max(0.0001f, yellow)+1.0f, thr.z));
42- }
43- }
44-
45- // calculate hyperbolic tangent
46- float tanh( float in) {
47- float f = exp(2.0f*in);
48- return (f-1.0f) / (f+1.0f);
49- }
50-
51- // calculate inverse hyperbolic tangent
52- float atanh( float in) {
53- return log((1.0f+in)/(1.0f-in))/2.0f;
54- }
55-
56- // compression function which gives the y=1 x intersect at y=0
57- float f(float x, float k, float t) {
58- if (method == 0) {
59- // natural logarithm compression method
60- return (exp((1.0f-t+t*log(1.0f-x)-x*t*log(1.0f-x))/(t*(1.0f-x))))*t+x*t-k;
61- } else if (method == 1 || method == 2) {
62- return k;
63- } else if (method == 3) {
64- // natural exponent compression method
65- return -log((-x+1.0f)/(t-x))*(-t+x)+t-k;
66- } else if (method == 4) {
67- // arctangent compression method
68- return (2*tan( (pi*(1.0f-t))/(2.0f*(x-t)))*(x-t))/pi+t-k;
69- } else if (method == 5) {
70- // hyperbolic tangent compression method
71- return atanh((1.0f-t)/(x-t))*(x-t)+t-k;
72- }
73- }
74-
75- int _sign(float x) {
76- return x == 0.0f ? 0.0f : x > 0.0f ? 1.0f : 0.0f;
25+ lim = float3(cyan+1.0f, magenta+1.0f, yellow+1.0f);
7726 }
7827
79- float bisect(float k, float t) {
80- // use a simple bisection algorithm to bruteforce the root of f
81- // returns an approximation of the value of limit
82- // such that the compression function intersects y=1 at desired value k
83- // this allows us to specify the max distance we will compress to the gamut boundary
84-
85- float a, b, c, y;
86- float tol = 0.0001f; // accuracy of estimate
87- int nmax = 100; // max iterations
88-
89- // set up reasonable initial guesses for each method given output ranges of each function
90- if (method == 0) {
91- // natural logarithm needs a limit between -inf (linear), and 1 (clip)
92- a = -15.0f;
93- b = 0.98f;
94- } else if (method == 5) {
95- // tanh needs more precision
96- a = 1.000001f;
97- b = 5.0f;
98- } else {
99- a = 1.0001f;
100- b = 5.0f;
101- }
102-
103- if (_sign(f(a, k, t)) == _sign(f(b, k, t))) {
104- // bad estimate. return something close to linear
105- if ((method == 0) || (method == 2)) {
106- return -100.0f;
107- } else {
108- return 1.999999f;
109- }
110- }
111- c = (a+b)/2.0f;
112- y = f(c, k, t);
113- if (fabs(y) <= tol) {
114- return c; // lucky guess
115- }
116-
117- int n = 1;
118- while ((fabs(y) > tol) && (n <= nmax)) {
119- if (_sign(y) == _sign(f(a, k, t))) {
120- a = c;
121- } else {
122- b = c;
123- }
124- c = (a+b)/2.0f;
125- y = f(c, k, t);
126- n += 1;
127- }
128- return c;
129- }
130-
131-
13228 // calculate compressed distance
13329 float compress(float x, float l, float t) {
13430 float cx, s;
13531 if (x < t) {
13632 cx = x;
13733 } else {
138- if (method == 0) {
139- // natural logarithm compression method: https://www.desmos.com/calculator/hmzirlw7tj
140- // inspired by ITU-R BT.2446 http://www.itu.int/pub/R-REP-BT.2446-2019
141- if (invert == 0) {
142- cx = t*log(x/t-l)-l*t*log(x/t-l)+t-t*log(1.0f-l)+l*t*log(1.0f-l);
143- } else {
144- cx = exp((x-t+t*log(1.0f-l)-l*t*log(1.0f-l))/(t*(1.0f-l)))*t+l*t;
145- }
146- } else if (method == 1) {
147- // simple Reinhard type compression method: https://www.desmos.com/calculator/lkhdtjbodx
148- if (invert == 0) {
149- cx = t+1.0f/(1.0f/(x-t)+1.0f/(1.0f-t)-1.0f/(l-t));
150- } else {
151- cx = t+1.0f/(1.0f/(x-t)-1.0f/(1.0f-t)+1.0f/(l-t));
152- }
153- } else if (method == 2) {
154- // power(p) compression function plot https://www.desmos.com/calculator/54aytu7hek
155- if (l < 1.0001) {
156- return x; // disable compression, avoid nan
157- }
158- s = (l-t)/pow(pow((1.0f-t)/(l-t),-p)-1.0f,1.0f/p); // calc y=1 intersect
159- if (invert == 0) {
160- cx = t+s*((x-t)/s)/(pow(1.0f+pow((x-t)/s,p),1.0f/p)); // compress
34+ // power(p) compression function plot https://www.desmos.com/calculator/54aytu7hek
35+ if (l < 1.0001) {
36+ return x; // disable compression, avoid nan
37+ }
38+ s = (l-t)/pow(pow((1.0f-t)/(l-t),-p)-1.0f,1.0f/p); // calc y=1 intersect
39+ if (invert == 0) {
40+ cx = t+s*((x-t)/s)/(pow(1.0f+pow((x-t)/s,p),1.0f/p)); // compress
41+ } else {
42+ if (x > (t + s)) {
43+ cx = x; // avoid singularity
16144 } else {
162- if (x > (t + s)) {
163- cx = x; // avoid singularity
164- }
16545 cx = t+s*pow(-(pow((x-t)/s,p)/(pow((x-t)/s,p)-1.0f)),1.0f/p); // uncompress
16646 }
167- } else if (method == 3) {
168- // natural exponent compression method: https://www.desmos.com/calculator/s2adnicmmr
169- if (invert == 0) {
170- cx = l-(l-t)*exp(-(((x-t)*((1.0f*l)/(l-t))/l)));
171- } else {
172- cx = -log((x-l)/(t-l))*(-t+l)/1.0f+t;
173- }
174- } else if (method == 4) {
175- // arctangent compression method: https://www.desmos.com/calculator/h96qmnozpo
176- if (invert == 0) {
177- cx = t+(l-t)*2/pi*atan(pi/2*(x-t)/(l-t));
178- } else {
179- cx = t+(l-t)*2/pi*tan(pi/2*(x-t)/(l-t));
180- }
181- } else if (method == 5) {
182- // hyperbolic tangent compression method: https://www.desmos.com/calculator/xiwliws24x
183- if (invert == 0) {
184- cx = t+(l-t)*tanh(((x-t)/(l-t)));
185- } else {
186- cx = t+(l-t)*atanh(x/(l-t)-t/(l-t));
187- }
18847 }
18948 }
19049 return cx;
@@ -202,36 +61,21 @@ kernel GamutCompression : public ImageComputationKernel<ePixelWise> {
20261 // achromatic axis
20362 float ach = max(rgb.x, max(rgb.y, rgb.z));
20463
205- // achromatic with shadow rolloff below shd_rolloff threshold
206- float ach_shd = 1.0f-( (1.0f-ach)<(1.0f-shd_rolloff)?(1.0f-ach):(1.0f-shd_rolloff)+shd_rolloff*tanh((((1.0f-ach)-(1.0f-shd_rolloff))/shd_rolloff)));
207-
20864 // distance from the achromatic axis for each color component aka inverse rgb ratios
20965 // distance is normalized by achromatic, so that 1.0f is at gamut boundary, avoid 0 div
210- float3 dist = ach_shd == 0 ? float3(0.0f, 0.0f, 0.0f) : (ach-rgb)/ach_shd ;
66+ float3 dist = ach == 0 ? float3(0.0f, 0.0f, 0.0f) : (ach-rgb)/fabs(ach) ;
21167
21268 // compress distance with user controlled parameterized shaper function
21369 float sat;
21470 float3 csat, cdist;
215- if (hexagonal) {
216- sat = max(dist.x, max(dist.y, dist.z));
217- csat = float3(
218- compress(sat, lim.x, thr.x),
219- compress(sat, lim.y, thr.y),
220- compress(sat, lim.z, thr.z));
221- cdist = sat == 0 ? dist : float3(
222- dist.x * csat.x / sat,
223- dist.y * csat.y / sat,
224- dist.z * csat.z / sat);
225- } else {
226- cdist = float3(
227- compress(dist.x, lim.x, thr.x),
228- compress(dist.y, lim.y, thr.y),
229- compress(dist.z, lim.z, thr.z));
230- }
71+ cdist = float3(
72+ compress(dist.x, lim.x, thr.x),
73+ compress(dist.y, lim.y, thr.y),
74+ compress(dist.z, lim.z, thr.z));
23175
23276 // recalculate rgb from compressed distance and achromatic
23377 // effectively this scales each color component relative to achromatic axis by the compressed distance
234- float3 crgb = ach-cdist*ach_shd ;
78+ float3 crgb = ach-cdist*fabs(ach) ;
23579
23680 // Graph overlay method based on one by Paul Dore
23781 // https://github.com/baldavenger/DCTLs/tree/master/ACES%20TOOLS
0 commit comments