summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/lcms2.h2
-rw-r--r--src/cmsvirt.c117
-rw-r--r--testbed/testcms2.c47
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);