diff options
author | Marti Maria <marti.maria@littlecms.com> | 2023-05-15 18:37:34 +0200 |
---|---|---|
committer | Marti Maria <marti.maria@littlecms.com> | 2023-05-15 18:37:34 +0200 |
commit | f4e9f9122a001b71219d9b2d2a6ec5b8c784d785 (patch) | |
tree | b6b5921d0a713b41f436861e7c3d2bdf9f15f560 /src | |
parent | cdf9635b69c3f270684b3e6a0cdeda9b03ce920c (diff) | |
download | lcms2-f4e9f9122a001b71219d9b2d2a6ec5b8c784d785.tar.gz |
a preliminar implementation of OkLab color space as built-in profile
Oklab is an alternative to CIE Lab. It works with the same logic, but claims to be more perceptually uniform than CIELab.
Thanks to Lukas Sommer for the idea and Björn Ottosson for the documentation
Diffstat (limited to 'src')
-rw-r--r-- | src/cmsvirt.c | 117 |
1 files changed, 114 insertions, 3 deletions
diff --git a/src/cmsvirt.c b/src/cmsvirt.c index 951a8ee..d3b6ab2 100644 --- a/src/cmsvirt.c +++ b/src/cmsvirt.c @@ -672,6 +672,117 @@ cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void) return cmsCreate_sRGBProfileTHR(NULL); } +/** +* Oklab colorspace profile (experimental) +* +* This virtual profile cannot be saved as an ICC file +*/ +cmsHPROFILE cmsCreate_OkLabProfile(cmsContext ctx) +{ + cmsStage* XYZPCS = _cmsStageNormalizeFromXyzFloat(ctx); + cmsStage* PCSXYZ = _cmsStageNormalizeToXyzFloat(ctx); + + const double M_D65_D50[] = + { + 1.047886, 0.022919, -0.050216, + 0.029582, 0.990484, -0.017079, + -0.009252, 0.015073, 0.751678 + }; + + const double M_D50_D65[] = + { + 0.955513, -0.0230732, 0.063309, + -0.0283249, 1.00994, 0.0210548, + 0.0123289, -0.0205358, 1.33071 + }; + + cmsStage* D65toD50 = cmsStageAllocMatrix(ctx, 3, 3, M_D65_D50, NULL); + cmsStage* D50toD65 = cmsStageAllocMatrix(ctx, 3, 3, M_D50_D65, NULL); + + const double M_D65_LMS[] = + { + 0.8189330101, 0.3618667424, -0.1288597137, + 0.0329845436, 0.9293118715, 0.0361456387, + 0.0482003018, 0.2643662691, 0.6338517070 + }; + + const double M_LMS_D65[] = + { + 1.22701, -0.5578, 0.281256, + -0.0405802, 1.11226, -0.0716767, + -0.0763813, -0.421482, 1.58616 + }; + + cmsStage* D65toLMS = cmsStageAllocMatrix(ctx, 3, 3, M_D65_LMS, NULL); + cmsStage* LMStoD65 = cmsStageAllocMatrix(ctx, 3, 3, M_LMS_D65, NULL); + + cmsToneCurve* CubeRoot = cmsBuildGamma(ctx, 1.0 / 3.0); + cmsToneCurve* Cube = cmsBuildGamma(ctx, 3.0); + + cmsToneCurve* Roots[3] = { CubeRoot, CubeRoot, CubeRoot }; + cmsToneCurve* Cubes[3] = { Cube, Cube, Cube }; + + cmsStage* NonLinearityFw = cmsStageAllocToneCurves(ctx, 3, Roots); + cmsStage* NonLinearityRv = cmsStageAllocToneCurves(ctx, 3, Cubes); + + const double M_LMSprime_OkLab[] = + { + 0.2104542553, 0.7936177850, -0.0040720468, + 1.9779984951, -2.4285922050, 0.4505937099, + 0.0259040371, 0.7827717662, -0.8086757660 + }; + + const double M_OkLab_LMSprime[] = + { + 1.0, 0.396338, 0.215804, + 1.0, -0.105561, -0.0638542, + 1.0, -0.0894842, -1.29149 + }; + + cmsStage* LMSprime_OkLab = cmsStageAllocMatrix(ctx, 3, 3, M_LMSprime_OkLab, NULL); + cmsStage* OkLab_LMSprime = cmsStageAllocMatrix(ctx, 3, 3, M_OkLab_LMSprime, NULL); + + cmsPipeline* AToB = cmsPipelineAlloc(ctx, 3, 3); + cmsPipeline* BToA = cmsPipelineAlloc(ctx, 3, 3); + + cmsHPROFILE hProfile = cmsCreateProfilePlaceholder(ctx); + + cmsSetProfileVersion(hProfile, 4.4); + + cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass); + cmsSetColorSpace(hProfile, cmsSig3colorData); + cmsSetPCS(hProfile, cmsSigXYZData); + + cmsSetHeaderRenderingIntent(hProfile, INTENT_RELATIVE_COLORIMETRIC); + + /** + * Conversion PCS (XYZ/D50) to OkLab + */ + cmsPipelineInsertStage(BToA, cmsAT_END, PCSXYZ); + cmsPipelineInsertStage(BToA, cmsAT_END, D50toD65); + cmsPipelineInsertStage(BToA, cmsAT_END, D65toLMS); + cmsPipelineInsertStage(BToA, cmsAT_END, NonLinearityFw); + cmsPipelineInsertStage(BToA, cmsAT_END, LMSprime_OkLab); + + cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA); + + cmsPipelineInsertStage(AToB, cmsAT_END, OkLab_LMSprime); + cmsPipelineInsertStage(AToB, cmsAT_END, NonLinearityRv); + cmsPipelineInsertStage(AToB, cmsAT_END, LMStoD65); + cmsPipelineInsertStage(AToB, cmsAT_END, D65toD50); + cmsPipelineInsertStage(AToB, cmsAT_END, XYZPCS); + + cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB); + + cmsPipelineFree(BToA); + cmsPipelineFree(AToB); + + cmsFreeToneCurve(CubeRoot); + cmsFreeToneCurve(Cube); + + return hProfile; +} + typedef struct { @@ -1062,9 +1173,9 @@ const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTa cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) { cmsHPROFILE hProfile = NULL; - cmsUInt32Number FrmIn, FrmOut; - cmsInt32Number ChansIn, ChansOut; - int ColorSpaceBitsIn, ColorSpaceBitsOut; + cmsUInt32Number FrmIn, FrmOut; + cmsInt32Number ChansIn, ChansOut; + int ColorSpaceBitsIn, ColorSpaceBitsOut; _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; cmsPipeline* LUT = NULL; cmsStage* mpe; |