Created
November 29, 2023 02:56
-
-
Save damywise/a8131a6c300953f2ee7a6483b2b8aa72 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const m1 = 0.1593017578125; | |
const m2 = 78.84375; | |
const c1 = 0.8359375; | |
const c2 = 18.851525; | |
const c3 = 18.6875; | |
double inversePq(double n) { | |
final top = max( | |
pow(n, 1 / m2) - c1, | |
0, | |
); | |
final bottom = c2 - c3 * pow(n, 1 / m2); | |
final result = pow(top / bottom, 1 / m1).toDouble(); | |
return result * 10000 / 203; | |
} | |
void applyInversePq(Float32List data) { | |
for (var i = 0; i < data.length; i += 3) { | |
data[i] = inversePq(data[i]); | |
data[i + 1] = inversePq(data[i + 1]); | |
data[i + 2] = inversePq(data[i + 2]); | |
} | |
} | |
// srgb | |
final srgbXyzMatrix = Matrix3( | |
0.4124564, | |
0.3575761, | |
0.1804375, | |
0.2126729, | |
0.7151522, | |
0.0721750, | |
0.0193339, | |
0.1191920, | |
0.9503041, | |
); | |
// prophoto rgb | |
final prophotoRgbToXyzMatrix = Matrix3( | |
0.7976749, | |
0.1351917, | |
0.0313534, | |
0.2880402, | |
0.7118741, | |
0.0000857, | |
0.0000000, | |
0.0000000, | |
0.8252100, | |
); | |
Matrix3 get rgbToXyzMatrix => prophotoRgbToXyzMatrix.clone(); | |
Matrix3 get inverseRgbToXyzMatrix => prophotoRgbToXyzMatrix.clone()..invert(); | |
Vector3 rgbToXyz(Vector3 rgb) { | |
return rgbToXyzMatrix.clone() * rgb; | |
} | |
Vector3 xyzToRgb(Vector3 xyz) { | |
return inverseRgbToXyzMatrix.clone() * xyz; | |
} | |
Vector3 XYZ_to_xyY(Vector3 XYZ) { | |
double X = XYZ.x; | |
double Y = XYZ.y; | |
double Z = XYZ.z; | |
double divisor = X + Y + Z; | |
if (divisor == 0.0) divisor = 1e-6; | |
double x = X / divisor; | |
double y = Y / divisor; | |
return Vector3(x, y, Y); | |
} | |
Vector3 xyY_to_XYZ(Vector3 xyY) { | |
double x = xyY.x; | |
double y = xyY.y; | |
double Y = xyY.z; | |
double multiplo = Y / max(y, 1e-6); | |
double z = 1.0 - x - y; | |
double X = x * multiplo; | |
double Z = z * multiplo; | |
return Vector3(X, Y, Z); | |
} | |
const double ip = 0.58535; // linear length | |
const double k1 = 0.83802; // linear strength | |
const double k3 = 0.74204; // shoulder strength | |
double f2(double Y, double k1, double k3, double ip) { | |
ip /= k1; | |
double k2 = (k1 * ip) * (1.0 - k3); | |
double k4 = (k1 * ip) - (k2 * log(1.0 - k3)); | |
return Y < ip ? Y * k1 : log((Y / ip) - k3) * k2 + k4; | |
} | |
double tonemapping(double x) { | |
const double over_white = 1019.0 / 940.0; // 109% range (super-whites) | |
return f2(x, k1, k3, ip) / over_white; | |
} | |
double gamma2_2(double value) { | |
return value > 0 ? pow(value, 2.2).toDouble() : 0; | |
} | |
void applyGamma(Float32List data) { | |
for (var i = 0; i < data.length; i += 3) { | |
data[i] = gamma2_2(data[i]); | |
data[i + 1] = gamma2_2(data[i + 1]); | |
data[i + 2] = gamma2_2(data[i + 2]); | |
} | |
} | |
void applyHdrToSdrTonemap(Float32List data) { | |
applyInversePq(data); | |
for (var i = 0; i < data.length; i += 3) { | |
final r = data[i]; | |
final g = data[i + 1]; | |
final b = data[i + 2]; | |
var color = Vector3(r, g, b); | |
color = rgbToXyz(color); | |
color = XYZ_to_xyY(color); | |
color = Vector3(color.x, color.y, tonemapping(color.z)); | |
color = xyY_to_XYZ(color); | |
color = xyzToRgb(color); | |
data[i] = color.x; | |
data[i + 1] = color.y; | |
data[i + 2] = color.z; | |
} | |
applyGamma(data); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment