diff options
-rw-r--r-- | include/lcms2.h | 2 | ||||
-rw-r--r-- | src/cmsvirt.c | 117 | ||||
-rw-r--r-- | testbed/testcms2.c | 47 |
3 files changed, 163 insertions, 3 deletions
diff --git a/include/lcms2.h b/include/lcms2.h index c361520..8769267 100644 --- a/include/lcms2.h +++ b/include/lcms2.h @@ -1634,6 +1634,8 @@ CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void); CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID); CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_OkLabProfile(cmsContext ctx); + CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, cmsUInt32Number nLUTPoints, cmsFloat64Number Bright, 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; diff --git a/testbed/testcms2.c b/testbed/testcms2.c index 48230ea..e5a2032 100644 --- a/testbed/testcms2.c +++ b/testbed/testcms2.c @@ -8374,6 +8374,52 @@ int Check_sRGB_Rountrips(void) return 1; } +/** +* Check OKLab colorspace +*/ +static +int Check_OkLab(void) +{ + cmsHPROFILE hOkLab = cmsCreate_OkLabProfile(NULL); + cmsHPROFILE hXYZ = cmsCreateXYZProfile(); + cmsCIEXYZ xyz, xyz2; + cmsCIELab okLab; + +#define TYPE_OKLAB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_MCH3)|CHANNELS_SH(3)|BYTES_SH(0)) + + cmsHTRANSFORM xform = cmsCreateTransform(hXYZ, TYPE_XYZ_DBL, hOkLab, TYPE_OKLAB_DBL, INTENT_RELATIVE_COLORIMETRIC, 0); + cmsHTRANSFORM xform2 = cmsCreateTransform(hOkLab, TYPE_OKLAB_DBL, hXYZ, TYPE_XYZ_DBL, INTENT_RELATIVE_COLORIMETRIC, 0); + + /** + * D50 should be converted to white by PCS definition + */ + xyz.X = 0.9642; xyz.Y = 1.0000; xyz.Z = 0.8249; + cmsDoTransform(xform, &xyz, &okLab, 1); + cmsDoTransform(xform2, &okLab, &xyz2, 1); + + + xyz.X = 1.0; xyz.Y = 0.0; xyz.Z = 0.0; + cmsDoTransform(xform, &xyz, &okLab, 1); + cmsDoTransform(xform2, &okLab, &xyz2, 1); + + + xyz.X = 0.0; xyz.Y = 1.0; xyz.Z = 0.0; + cmsDoTransform(xform, &xyz, &okLab, 1); + cmsDoTransform(xform2, &okLab, &xyz2, 1); + + xyz.X = 0.0; xyz.Y = 0.0; xyz.Z = 1.0; + cmsDoTransform(xform, &xyz, &okLab, 1); + cmsDoTransform(xform2, &okLab, &xyz2, 1); + + + cmsDeleteTransform(xform); + cmsDeleteTransform(xform2); + cmsCloseProfile(hOkLab); + cmsCloseProfile(hXYZ); + + return 0; +} + static cmsHPROFILE createRgbGamma(cmsFloat64Number g) { @@ -9492,6 +9538,7 @@ int main(int argc, char* argv[]) Check("Proofing intersection", CheckProofingIntersection); Check("Empty MLUC", CheckEmptyMLUC); Check("sRGB round-trips", Check_sRGB_Rountrips); + Check("OkLab color space", Check_OkLab); Check("Gamma space detection", CheckGammaSpaceDetection); Check("Unbounded mode w/ integer output", CheckIntToFloatTransform); Check("Corrupted built-in by using cmsWriteRawTag", CheckInducedCorruption); |