44#include < fstream>
55
66#include " ../../debug/log/Logger.hpp"
7+ #include " ../../render/Texture.hpp"
8+ #include " ../../render/Renderer.hpp"
79
810#include < hyprutils/utils/ScopeGuard.hpp>
911using namespace Hyprutils ::Utils;
@@ -12,6 +14,9 @@ using namespace Hyprutils::Utils;
1214
1315using namespace NColorManagement ;
1416
17+ // IMPORTANT: this needs to match LUT_SIZE in CM.glsl
18+ constexpr const size_t LUT_SIZE = 2048 ;
19+
1520static std::vector<uint8_t > readBinary (const std::filesystem::path& file) {
1621 std::ifstream ifs (file, std::ios::binary);
1722 if (!ifs.good ())
@@ -118,21 +123,6 @@ static std::optional<Mat3x3> invertMat3(const Mat3x3& m) {
118123 return inv;
119124}
120125
121- static bool toneCurvesSimilar (cmsToneCurve* a, cmsToneCurve* b) {
122- if (!a || !b)
123- return false ;
124-
125- for (int i = 0 ; i <= 256 ; ++i) {
126- double x = (double )i / 256.0 ;
127- double ya = cmsEvalToneCurveFloat (a, x);
128- double yb = cmsEvalToneCurveFloat (b, x);
129- if (std::abs (ya - yb) > 0.001 ) // close enough
130- return false ;
131- }
132-
133- return true ;
134- }
135-
136126static std::string dumpToneCurve (cmsToneCurve* c) {
137127 if (!c)
138128 return " " ;
@@ -152,6 +142,25 @@ static std::string dumpToneCurve(cmsToneCurve* c) {
152142 return res;
153143}
154144
145+ static SP<CTexture> uploadRamp (std::span<const float > data) {
146+ g_pHyprRenderer->makeEGLCurrent ();
147+
148+ SP<CTexture> tex = makeShared<CTexture>(data);
149+
150+ return tex;
151+ }
152+
153+ static std::vector<float > sampleToneCurve (cmsToneCurve* curve) {
154+ std::vector<float > lut;
155+ lut.reserve (LUT_SIZE);
156+ for (size_t i = 0 ; i < LUT_SIZE; ++i) {
157+ float x = sc<float >(i) / sc<float >(LUT_SIZE - 1 );
158+ float y = sc<float >(cmsEvalToneCurveFloat (curve, x));
159+ lut.emplace_back (std::clamp (y, 0 .F , 1 .F ));
160+ }
161+ return lut;
162+ }
163+
155164std::expected<SImageDescription, std::string> SImageDescription::fromICC (const std::filesystem::path& file) {
156165 std::error_code ec;
157166 if (!std::filesystem::exists (file, ec) || ec)
@@ -228,37 +237,30 @@ std::expected<SImageDescription, std::string> SImageDescription::fromICC(const s
228237 Log::logger->log (Log::DEBUG, " TRC G: {}" , dumpToneCurve (trcG));
229238 Log::logger->log (Log::DEBUG, " TRC B: {}" , dumpToneCurve (trcB));
230239
231- // FIXME: make this representable...
232- cmsToneCurve* trc = trcR;
233- if (trcR && trcG && trcB && (!toneCurvesSimilar (trcR, trcG) || !toneCurvesSimilar (trcR, trcB))) {
234- Log::logger->log (Log::DEBUG, " ============= End ICC load =============" );
235- return std::unexpected (" Hyprland cannot represent split-trc profiles yet" );
236- }
240+ if (trcR && trcG && trcB) {
241+ Log::logger->log (Log::DEBUG, " All TRC present, uploading LUTs" , dumpToneCurve (trcR));
237242
238- if (trc) {
239- if (cmsIsToneCurveLinear (trc)) {
240- Log::logger->log (Log::DEBUG, " TRC: Linear" );
241- image.transferFunction = CM_TRANSFER_FUNCTION_EXT_LINEAR;
242- image.transferFunctionPower = 1 .F ;
243- } else {
244- // gamma fit as a fallback
245- Log::logger->log (Log::DEBUG, " TRC: Gamma fit" );
246- const double g = cmsEstimateGamma (trc, 0.000001 );
247- if (g > 1.0 && g < 4.0 ) {
248- Log::logger->log (Log::DEBUG, " TRC: Gamma fit at {}" , g);
249- image.transferFunctionPower = (float )g;
250-
251- if (std::abs (g - 2.2 ) < 0.05 )
252- image.transferFunction = CM_TRANSFER_FUNCTION_GAMMA22;
253- else if (std::abs (g - 2.8 ) < 0.05 )
254- image.transferFunction = CM_TRANSFER_FUNCTION_GAMMA28;
255- else
256- image.transferFunction = CM_TRANSFER_FUNCTION_EXT_SRGB;
257- } else {
258- Log::logger->log (Log::DEBUG, " TRC: Gamma fit failed, falling back to srgb" );
259- image.transferFunction = CM_TRANSFER_FUNCTION_SRGB;
260- }
261- }
243+ image.lutRamps .present = true ;
244+
245+ cmsToneCurve* invR = cmsReverseToneCurve (trcR);
246+ cmsToneCurve* invG = cmsReverseToneCurve (trcG);
247+ cmsToneCurve* invB = cmsReverseToneCurve (trcB);
248+
249+ CScopeGuard x2 ([&] {
250+ if (invR)
251+ cmsFreeToneCurve (invR);
252+ if (invG)
253+ cmsFreeToneCurve (invG);
254+ if (invB)
255+ cmsFreeToneCurve (invB);
256+ });
257+
258+ if (!invR || !invG || !invB)
259+ return std::unexpected (" Failed to invert TRC, likely invalid/unsupported data" );
260+
261+ image.lutRamps .r = uploadRamp (sampleToneCurve (invR));
262+ image.lutRamps .g = uploadRamp (sampleToneCurve (invG));
263+ image.lutRamps .b = uploadRamp (sampleToneCurve (invB));
262264 }
263265
264266 Log::logger->log (Log::DEBUG, " ============= End ICC load =============" );
0 commit comments