Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/02/2026 in all areas

  1. Hi! I just stumbled upon this thread and thought I'd share an OKLab conversion DCTL I wrote about a year ago. It;s written as a header file to be included in any other DCTL you may need it for. It supports conversion from ACES, Davinci Wide and Rec.709/sRGB. OKLab_Transform.h: #line 2 #ifndef ENCODING_ENUMS_DEFINED_IN_UI enum Encoding { gAcc, gAcct, gDWI, gLIN, g709, gSRGB }; #endif #ifndef COLORSPACE_ENUMS_DEFINED_IN_UI enum ColorSpace { cACES0, cACES1, cDWG, c709 }; #endif // ============================================================= // Util // ============================================================= // ============================================================= __DEVICE__ float powCf(float base, float exp) { return _copysignf(_powf(_fabs(base), exp), base); } __DEVICE__ float3 VecMatMul3x3(const float3 m[3], float3 v) { float3 r; r.x = m[0].x * v.x + m[0].y * v.y + m[0].z * v.z; r.y = m[1].x * v.x + m[1].y * v.y + m[1].z * v.z; r.z = m[2].x * v.x + m[2].y * v.y + m[2].z * v.z; return r; } // ============================================================= // Matrices // ============================================================= // ============================================================= // These matrices are the concatenated forms of (colorspace -> XYZ -> OKlms) // or, XYZToLMS @ ColorspaceToXYZ and, XYZToColorspace @ LMSToXYZ // original matrices are included as comments at the bottom of this script // ACES (AP0) // ============================== __CONSTANT__ float3 mat_ACES0_LMS[3] = { {0.90454662f, 0.26349909f, -0.15602258f }, {0.35107161f, 0.6766934f, -0.03056591f }, {0.13684644f, 0.19250255f, 0.62038067f } }; __CONSTANT__ float3 mat_LMS_ACES0[3] = { {1.2881401f, -0.58554348f, 0.29511118f }, {-0.67171287f, 1.76268516f, -0.08208556f }, {-0.0757131f, -0.41779486f, 1.57228749f } }; // ACES (AP1) // ============================== __CONSTANT__ float3 mat_ACES1_LMS[3] = { {0.64173446f, 0.35314498f, 0.0171437f }, {0.27463463f, 0.63099904f, 0.09156544f }, {0.10036508f, 0.18723743f, 0.66212716f } }; __CONSTANT__ float3 mat_LMS_ACES1[3] = { {2.04479741f, -1.17697875f, 0.10982058f }, {-0.88115384f, 2.15979229f, -0.27586256f }, {-0.06077576f, -0.43234352f, 1.57164623f } }; // ITU BT.709 // ============================== __CONSTANT__ float3 mat_709_LMS[3] = { { 0.4122214708f, 0.5363325363f, 0.0514459929f }, { 0.2119034982f, 0.6806995451f, 0.1073969566f }, { 0.0883024619f, 0.2817188376f, 0.6299787005f } }; __CONSTANT__ float3 mat_LMS_709[3] = { { 4.0767416621f, -3.3077115913f, 0.2309699292f }, { -1.2684380046f, 2.6097574011f, -0.3413193965f }, { -0.0041960863f, -0.7034186147f, 1.7076147010f } }; // Davinci Wide // ============================== __CONSTANT__ float3 mat_DWG_LMS[3] = { { 0.68570951f, 0.45574409f, -0.14156279f }, { 0.27427422f, 0.81179945f, -0.08604675f }, { 0.04351009f, 0.15072461f, 0.80624495f } }; __CONSTANT__ float3 mat_LMS_DWG[3] = { { 1.8836253f, -1.09713301f, 0.21364045f }, { -0.63460063f, 1.57752473f, 0.05693684f }, { 0.01698397f, -0.23570436f, 1.21814431f } }; // OKLab <-> Cone Response // ============================== __CONSTANT__ float3 mat_LMS_LAB[3] = { { 0.2104542553f, 0.7936177850f, -0.0040720468f }, { 1.9779984951f, -2.4285922050f, 0.4505937099f }, { 0.0259040371f, 0.7827717662f, -0.8086757660f } }; __CONSTANT__ float3 mat_LAB_LMS[3] = { { 1.0f, 0.3963377774f, 0.2158037573f }, { 1.0f, -0.1055613458f, -0.0638541728f }, { 1.0f, -0.0894841775f, -1.2914855480f } }; // ============================================================= // Transfer Functions // ============================================================= // ============================================================= // ACEScc // ============================== __DEVICE__ float ACEScc_DecodeBase(float v, float a, float b, float upperClampThreshold, float lowerDecodeThreshold, float two_m16) { float out = v; if (v >= upperClampThreshold) out = 65504.0f; else if (v < lowerDecodeThreshold) out = (_exp2f(v * b - a) - two_m16) * 2.0f; else out = _exp2f(v * b - a); return out; } __DEVICE__ float3 ACEScc_Decode(float3 in) { const float two_m16 = _exp2f(-16.0f); const float a = 9.72f; const float b = 17.52f; const float lowerDecodeThreshold = (a - 15.0f) / b; const float upperClampThreshold = (_log2f(65504.0f) + a) / b; float3 out = in; out.x = ACEScc_DecodeBase(out.x, a, b, upperClampThreshold, lowerDecodeThreshold, two_m16); out.y = ACEScc_DecodeBase(out.y, a, b, upperClampThreshold, lowerDecodeThreshold, two_m16); out.z = ACEScc_DecodeBase(out.z, a, b, upperClampThreshold, lowerDecodeThreshold, two_m16); return out; } __DEVICE__ float ACEScc_EncodeBase(float v, float a, float b, float negConstant, float two_m15, float two_m16) { float out; if (v < 0.0f) out = negConstant; else if (v < two_m15) out = (_log2f(two_m16 + v * 0.5f) + a) / b; else out = (_log2f(v) + a) / b; return out; } __DEVICE__ float3 ACEScc_Encode(float3 in) { const float two_m16 = _exp2f(-16.0f); const float two_m15 = _exp2f(-15.0f); const float a = 9.72f; const float b = 17.52f; const float negConstant = (_log2f(two_m16) + a) / b; float3 out = in; out.x = ACEScc_EncodeBase(out.x, a, b, negConstant, two_m15, two_m16); out.y = ACEScc_EncodeBase(out.y, a, b, negConstant, two_m15, two_m16); out.z = ACEScc_EncodeBase(out.z, a, b, negConstant, two_m15, two_m16); return out; } // ACEScct // ============================== __DEVICE__ float3 ACEScct_Encode(float3 in) { const float a = 9.72f; const float b = 17.52f; const float X_BRK = 0.0078125f; const float A = 10.5402377416545f; const float B = 0.0729055341958355f; float3 out; out.x = (in.x <= X_BRK) ? (A * in.x + B) : ((_log2f(in.x) + a) / b); out.y = (in.y <= X_BRK) ? (A * in.y + B) : ((_log2f(in.y) + a) / b); out.z = (in.z <= X_BRK) ? (A * in.z + B) : ((_log2f(in.z) + a) / b); return out; } __DEVICE__ float3 ACEScct_Decode(float3 in) { const float a = 9.72f; const float b = 17.52f; const float Y_BRK = 0.155251141552511f; const float A = 10.5402377416545f; const float B = 0.0729055341958355f; float3 out = in; out.x = (in.x > Y_BRK) ? _exp2f(in.x * b - a) : ((in.x - B) / A); out.y = (in.y > Y_BRK) ? _exp2f(in.y * b - a) : ((in.y - B) / A); out.z = (in.z > Y_BRK) ? _exp2f(in.z * b - a) : ((in.z - B) / A); return out; } // Davinci Intermediate // ============================== __DEVICE__ float3 DWI_Decode(float3 in) { float3 out = in; float a = 0.0075; float b = 7.0; float c = 0.07329248; float m = 10.44426855; float log_cut = 0.02740668; out.x = in.x > log_cut ? powCf(2.0f, (in.x / c) - b) - a : in.x / m; out.y = in.y > log_cut ? powCf(2.0f, (in.y / c) - b) - a : in.y / m; out.z = in.z > log_cut ? powCf(2.0f, (in.z / c) - b) - a : in.z / m; return out; } __DEVICE__ float3 DWI_Encode(float3 in) { float3 out = in; float a = 0.0075; float b = 7.0; float c = 0.07329248; float m = 10.44426855; float lin_cut = 0.00262409; out.x = in.x > lin_cut ? (_log2f(in.x + a) + b) * c : in.x * m; out.y = in.y > lin_cut ? (_log2f(in.y + a) + b) * c : in.y * m; out.z = in.z > lin_cut ? (_log2f(in.z + a) + b) * c : in.z * m; return out; } // ITU BT.709 // ============================== __DEVICE__ float3 BT709_Decode(float3 in) { float3 out = in; out.x = out.x < 0.081f ? out.x / 4.5f : powCf((out.x + 0.099f) / 1.099f, 1.0f / 0.45f); out.y = out.y < 0.081f ? out.y / 4.5f : powCf((out.y + 0.099f) / 1.099f, 1.0f / 0.45f); out.z = out.z < 0.081f ? out.z / 4.5f : powCf((out.z + 0.099f) / 1.099f, 1.0f / 0.45f); return out; } __DEVICE__ float3 BT709_Encode(float3 in) { float3 out = in; out.x = out.x < 0.018 ? out.x * 4.5f : 1.099f * powCf(out.x, 0.45f) - 0.099f; out.y = out.y < 0.018 ? out.y * 4.5f : 1.099f * powCf(out.y, 0.45f) - 0.099f; out.z = out.z < 0.018 ? out.z * 4.5f : 1.099f * powCf(out.z, 0.45f) - 0.099f; return out; } // sRGB // ============================== __DEVICE__ float3 sRGB_Decode(float3 in) { float3 out = in; out.x = out.x < 0.04045 ? out.x / 12.92f : powCf((out.x + 0.055f) / 1.055f, 2.4f); out.y = out.y < 0.04045 ? out.y / 12.92f : powCf((out.y + 0.055f) / 1.055f, 2.4f); out.z = out.z < 0.04045 ? out.z / 12.92f : powCf((out.z + 0.055f) / 1.055f, 2.4f); return out; } __DEVICE__ float3 sRGB_Encode(float3 in) { float3 out = in; out.x = out.x < 0.0031308 ? out.x * 12.92f : 1.055f * powCf(out.x, 1.0f / 2.4f) - 0.055f; out.y = out.y < 0.0031308 ? out.y * 12.92f : 1.055f * powCf(out.y, 1.0f / 2.4f) - 0.055f; out.z = out.z < 0.0031308 ? out.z * 12.92f : 1.055f * powCf(out.z, 1.0f / 2.4f) - 0.055f; return out; } // ============================================================= // Convert // ============================================================= // ============================================================= __DEVICE__ float3 Decode(float3 in, int tFunction) { float3 out = in; switch (tFunction) { case gAcc: out = ACEScc_Decode(in); break; case gAcct: out = ACEScct_Decode(in); break; case gDWI: out = DWI_Decode(in); break; case g709: out = BT709_Decode(in); break; case gSRGB: out = sRGB_Decode(in); break; } return out; } __DEVICE__ float3 Encode(float3 in, int tFunction) { float3 out = in; switch (tFunction) { case gAcc: out = ACEScc_Encode(in); break; case gAcct: out = ACEScct_Encode(in); break; case gDWI: out = DWI_Encode(in); break; case g709: out = BT709_Encode(in); break; case gSRGB: out = sRGB_Encode(in); break; } return out; } __DEVICE__ float3 OKLab_OKLCh(float3 lab) { float C = _hypotf(lab.y, lab.z); float h = _atan2f(lab.z, lab.y); return make_float3(lab.x, C, h); } __DEVICE__ float3 OKLCh_OKLab(float3 lch) { float a = lch.y * cosf(lch.z); float b = lch.y * sinf(lch.z); return make_float3(lch.x, a, b); } __DEVICE__ float3 RGB_OKLab(float3 rgb, int colorspace) { const float3* mat; switch (colorspace) { case cACES0: mat = mat_ACES0_LMS; break; case cACES1: mat = mat_ACES1_LMS; break; case cDWG: mat = mat_DWG_LMS; break; case c709: mat = mat_709_LMS; break; } float3 lms = VecMatMul3x3(mat, rgb); float3 lms_; lms_.x = cbrt(lms.x); lms_.y = cbrt(lms.y); lms_.z = cbrt(lms.z); return VecMatMul3x3(mat_LMS_LAB, lms_); } __DEVICE__ float3 OKLab_RGB(float3 lab, int colorspace) { const float3* mat; switch (colorspace) { case cACES0: mat = mat_LMS_ACES0; break; case cACES1: mat = mat_LMS_ACES1; break; case cDWG: mat = mat_LMS_DWG; break; case c709: mat = mat_LMS_709; break; } float3 lms = VecMatMul3x3(mat_LAB_LMS, lab); lms.x = powCf(lms.x, 3.0f); lms.y = powCf(lms.y, 3.0f); lms.z = powCf(lms.z, 3.0f); return VecMatMul3x3(mat, lms); } __DEVICE__ float3 RGB_OkLCh(float3 rgb, int colorspace) { float3 lab = RGB_OKLab(rgb, colorspace); return OKLab_OKLCh(lab); } __DEVICE__ float3 OKLCh_RGB(float3 lch, int colorspace) { float3 lab = OKLCh_OKLab(lch); return OKLab_RGB(lab, colorspace); } // ============================================================= // Ref Matrices // ============================================================= // ============================================================= // DWG -> XYZ // 0.70062239, 0.14877482, 0.10105872 // 0.27411851, 0.87363190, -0.14775041 // -0.09896291, -0.13789533, 1.32591599 // XYZ -> DWG // 1.51667204, -0.28147805, -0.14696363 // -0.46491710, 1.25142378, 0.17488461 // 0.06484905, 0.10913934, 0.76141462 // 709 -> XYZ // 0.4123908, 0.35758434, 0.18048079 // 0.21263901, 0.71516868, 0.07219232 // 0.01933082, 0.11919478, 0.95053215 // XYZ -> 709 // 3.24096994, -1.53738318, -0.49861076 // -0.96924364, 1.8759675, 0.04155506 // 0.05563008, -0.20397696 , 1.05697151 // XYZ -> LMS // 0.8189330101, 0.3618667424, -0.1288597137 // 0.0329845436, 0.9293118715, 0.0361456387 // 0.0482003018, 0.2643662691, 0.6338517070 // LMS -> XYZ // 1.22701385, -0.55779998, 0.28125615 // -0.04058018, 1.11225687, -0.07167668 // -0.07638128, -0.42148198, 1.58616322 Using it another DCTL looks something like this: #line 2 #define ENCODING_ENUMS_DEFINED_IN_UI #define COLORSPACE_ENUMS_DEFINED_IN_UI #include "OKLab_Transforms.h" DEFINE_UI_PARAMS(p_InCSpace, Input Color Space, DCTLUI_COMBO_BOX, 2, {cACES0, cACES1, cDWG, c709}, {ACES (AP0), ACES (AP1), Davinci Wide Gamut, Rec.709 / sRGB / BT.1886}); DEFINE_UI_PARAMS(p_InGamma, Input Gamma, DCTLUI_COMBO_BOX, 2, {gAcc, gAcct, gDWI, gLIN, g709, gSRGB}, {ACEScc, ACEScct, Davinci Intermediate, Linear, Rec.709, sRGB}); __DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B) { float3 in = make_float3(p_R, p_G, p_B); float3 out = in; // Convert to OKLCh: float3 linear = Decode(in, p_InGamma); float3 oklch = RGB_OkLCh(linear, p_InCSpace); // Convert back: linear = OKLCh_RGB(oklch, p_InCSpace); out = Encode(linear, p_InGamma); return out; }
    1 point
×
×
  • Create New...