diff options
-rw-r--r-- | include/lcms2.h | 2 | ||||
-rw-r--r-- | plugins/fast_float/src/fast_16_tethra.c | 10 | ||||
-rw-r--r-- | plugins/fast_float/src/fast_8_tethra.c | 10 | ||||
-rw-r--r-- | plugins/fast_float/src/fast_float_cmyk.c | 10 | ||||
-rw-r--r-- | plugins/fast_float/src/fast_float_lab.c | 10 | ||||
-rw-r--r-- | plugins/fast_float/src/fast_float_tethra.c | 10 | ||||
-rw-r--r-- | src/cmsgmt.c | 66 | ||||
-rw-r--r-- | src/cmsopt.c | 28 | ||||
-rw-r--r-- | src/cmsxform.c | 9 | ||||
-rw-r--r-- | src/lcms2.def | 1 | ||||
-rw-r--r-- | testbed/testcms2.c | 112 | ||||
-rw-r--r-- | utils/delphi/lcms2dll.pas | 7 |
12 files changed, 207 insertions, 68 deletions
diff --git a/include/lcms2.h b/include/lcms2.h index c748e51..61e2ab3 100644 --- a/include/lcms2.h +++ b/include/lcms2.h @@ -1907,6 +1907,8 @@ CMSAPI cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* Blac // Estimate total area coverage CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile); +// Estimate gamma space, alwasys positive. Returns -1 on error. +CMSAPI cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number thereshold); // Poor man's gamut mapping CMSAPI cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, diff --git a/plugins/fast_float/src/fast_16_tethra.c b/plugins/fast_float/src/fast_16_tethra.c index aad207f..d76c65a 100644 --- a/plugins/fast_float/src/fast_16_tethra.c +++ b/plugins/fast_float/src/fast_16_tethra.c @@ -317,7 +317,6 @@ cmsBool Optimize16BitRGBTransform(_cmsTransform2Fn* TransformFn, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) { - cmsStage* mpe; Performance16Data* p16; cmsContext ContextID; _cmsStageCLutData* data; @@ -353,14 +352,7 @@ cmsBool Optimize16BitRGBTransform(_cmsTransform2Fn* TransformFn, cmsSigCurveSetElemType, cmsSigCurveSetElemType, NULL, NULL)) return FALSE; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(*Lut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - + ContextID = cmsGetPipelineContextID(*Lut); newFlags = *dwFlags | cmsFLAGS_FORCE_CLUT; diff --git a/plugins/fast_float/src/fast_8_tethra.c b/plugins/fast_float/src/fast_8_tethra.c index 26a5a5d..9a724a6 100644 --- a/plugins/fast_float/src/fast_8_tethra.c +++ b/plugins/fast_float/src/fast_8_tethra.c @@ -346,7 +346,6 @@ cmsBool Optimize8BitRGBTransform(_cmsTransform2Fn* TransformFn, cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL; cmsStage* OptimizedCLUTmpe; cmsStage* OptimizedPrelinMpe; - cmsStage* mpe; Performance8Data* p8; cmsUInt16Number* MyTable[3]; cmsContext ContextID; @@ -365,14 +364,7 @@ cmsBool Optimize8BitRGBTransform(_cmsTransform2Fn* TransformFn, if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; OriginalLut = *Lut; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - + ContextID = cmsGetPipelineContextID(OriginalLut); nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigRgbData, *dwFlags); diff --git a/plugins/fast_float/src/fast_float_cmyk.c b/plugins/fast_float/src/fast_float_cmyk.c index d4a8261..a0a23de 100644 --- a/plugins/fast_float/src/fast_float_cmyk.c +++ b/plugins/fast_float/src/fast_float_cmyk.c @@ -331,7 +331,6 @@ cmsBool OptimizeCLUTCMYKTransform(_cmsTransform2Fn* TransformFn, int nGridPoints; cmsPipeline* OptimizedLUT = NULL; cmsStage* OptimizedCLUTmpe; - cmsStage* mpe; FloatCMYKData* pcmyk; cmsContext ContextID; _cmsStageCLutData* data; @@ -349,14 +348,7 @@ cmsBool OptimizeCLUTCMYKTransform(_cmsTransform2Fn* TransformFn, if (T_COLORSPACE(*InputFormat) != PT_CMYK) return FALSE; OriginalLut = *Lut; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - + ContextID = cmsGetPipelineContextID(OriginalLut); nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigRgbData, *dwFlags); diff --git a/plugins/fast_float/src/fast_float_lab.c b/plugins/fast_float/src/fast_float_lab.c index d392a84..2f31d7d 100644 --- a/plugins/fast_float/src/fast_float_lab.c +++ b/plugins/fast_float/src/fast_float_lab.c @@ -369,7 +369,6 @@ cmsBool OptimizeCLUTLabTransform(_cmsTransform2Fn* TransformFn, int nGridPoints; cmsPipeline* OptimizedLUT = NULL; cmsStage* OptimizedCLUTmpe; - cmsStage* mpe; LabCLUTdata* pfloat; cmsContext ContextID; _cmsStageCLutData* data; @@ -389,14 +388,7 @@ cmsBool OptimizeCLUTLabTransform(_cmsTransform2Fn* TransformFn, if (T_COLORSPACE(*InputFormat) != PT_Lab) return FALSE; OriginalLut = *Lut; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - + ContextID = cmsGetPipelineContextID(OriginalLut); nGridPoints = GetGridpoints(*dwFlags); diff --git a/plugins/fast_float/src/fast_float_tethra.c b/plugins/fast_float/src/fast_float_tethra.c index 0e3015a..29ebde5 100644 --- a/plugins/fast_float/src/fast_float_tethra.c +++ b/plugins/fast_float/src/fast_float_tethra.c @@ -239,7 +239,6 @@ cmsBool OptimizeCLUTRGBTransform(_cmsTransform2Fn* TransformFn, int nGridPoints; cmsPipeline* OptimizedLUT = NULL; cmsStage* OptimizedCLUTmpe; - cmsStage* mpe; FloatCLUTData* pfloat; cmsContext ContextID; _cmsStageCLutData* data; @@ -258,14 +257,7 @@ cmsBool OptimizeCLUTRGBTransform(_cmsTransform2Fn* TransformFn, if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; OriginalLut = *Lut; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - + ContextID = cmsGetPipelineContextID(OriginalLut); nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigRgbData, *dwFlags); diff --git a/src/cmsgmt.c b/src/cmsgmt.c index 75904e4..f28e251 100644 --- a/src/cmsgmt.c +++ b/src/cmsgmt.c @@ -1,7 +1,7 @@ //---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2020 Marti Maria Saguer
+// Copyright (c) 1998-2021 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -588,3 +588,67 @@ cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, return TRUE;
}
+
+// Detect whatever a given ICC profile works in linear (gamma 1.0) space
+// Actually, doing that "well" is quite hard, since every component may behave completely different.
+// Since the true point of this function is to detect suitable optimizations, I am imposing some requirements
+// that simplifies things: only RGB, and only profiles that can got in both directions.
+// The algorith obtains Y from a syntetical gray R=G=B. Then least squares fitting is used to estimate gamma.
+// For gamma close to 1.0, RGB is linear. On profiles not supported, -1 is returned.
+
+cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number thereshold)
+{
+ cmsContext ContextID;
+ cmsHPROFILE hXYZ;
+ cmsHTRANSFORM xform;
+ cmsToneCurve* Y_curve;
+ cmsUInt16Number rgb[256][3];
+ cmsCIEXYZ XYZ[256];
+ cmsFloat32Number Y_normalized[256];
+ cmsFloat64Number gamma;
+ cmsProfileClassSignature cl;
+ int i;
+
+ if (cmsGetColorSpace(hProfile) != cmsSigRgbData)
+ return -1;
+
+ cl = cmsGetDeviceClass(hProfile);
+ if (cl != cmsSigInputClass && cl != cmsSigDisplayClass &&
+ cl != cmsSigOutputClass && cl != cmsSigColorSpaceClass)
+ return -1;
+
+ ContextID = cmsGetProfileContextID(hProfile);
+ hXYZ = cmsCreateXYZProfileTHR(ContextID);
+ xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_RGB_16, hXYZ, TYPE_XYZ_DBL,
+ INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE);
+
+ if (xform == NULL) { // If not RGB or forward direction is not supported, regret with the previous error
+
+ cmsCloseProfile(hXYZ);
+ return -1;
+ }
+
+ for (i = 0; i < 256; i++) {
+ rgb[i][0] = rgb[i][1] = rgb[i][2] = FROM_8_TO_16(i);
+ }
+
+ cmsDoTransform(xform, rgb, XYZ, 256);
+
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(hXYZ);
+
+ for (i = 0; i < 256; i++) {
+ Y_normalized[i] = (cmsFloat32Number) XYZ[i].Y;
+ }
+
+ Y_curve = cmsBuildTabulatedToneCurveFloat(ContextID, 256, Y_normalized);
+ if (Y_curve == NULL)
+ return -1;
+
+ gamma = cmsEstimateGamma(Y_curve, thereshold);
+
+ cmsFreeToneCurve(Y_curve);
+
+ return gamma;
+}
+
diff --git a/src/cmsopt.c b/src/cmsopt.c index c760551..56cdf29 100644 --- a/src/cmsopt.c +++ b/src/cmsopt.c @@ -647,7 +647,6 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 { cmsPipeline* Src = NULL; cmsPipeline* Dest = NULL; - cmsStage* mpe; cmsStage* CLUT; cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; cmsUInt32Number nGridPoints; @@ -669,7 +668,7 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 if (ColorSpace == (cmsColorSpaceSignature)0 || OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; - nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); // For empty LUTs, 2 points are enough if (cmsPipelineStageCount(*Lut) == 0) @@ -677,13 +676,6 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 Src = *Lut; - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(Src); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - // Allocate an empty LUT Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); if (!Dest) return FALSE; @@ -1051,7 +1043,6 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte cmsStage* OptimizedCLUTmpe; cmsColorSpaceSignature ColorSpace, OutputColorSpace; cmsStage* OptimizedPrelinMpe; - cmsStage* mpe; cmsToneCurve** OptimizedPrelinCurves; _cmsStageCLutData* OptimizedPrelinCLUT; @@ -1072,14 +1063,7 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte } OriginalLut = *Lut; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - + ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); @@ -1918,6 +1902,7 @@ cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID, _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); _cmsOptimizationCollection* Opts; cmsBool AnySuccess = FALSE; + cmsStage* mpe; // A CLUT is being asked, so force this specific optimization if (*dwFlags & cmsFLAGS_FORCE_CLUT) { @@ -1932,6 +1917,13 @@ cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID, return TRUE; } + // Named color pipelines cannot be optimized + for (mpe = cmsPipelineGetPtrToFirstStage(*PtrLut); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + // Try to get rid of identities and trivial conversions. AnySuccess = PreOptimize(*PtrLut); diff --git a/src/cmsxform.c b/src/cmsxform.c index 9d182e1..ac7792a 100644 --- a/src/cmsxform.c +++ b/src/cmsxform.c @@ -1081,6 +1081,15 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, return NULL; } + // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations + if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE)) + { + cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1); + + if (gamma > 0 && gamma < 1.6) + dwFlags |= cmsFLAGS_NOOPTIMIZE; + } + // Create a pipeline with all transformations Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); if (Lut == NULL) { diff --git a/src/lcms2.def b/src/lcms2.def index a60f1b0..b03943c 100644 --- a/src/lcms2.def +++ b/src/lcms2.def @@ -364,4 +364,5 @@ cmsMD5alloc = cmsMD5alloc cmsMD5finish = cmsMD5finish
_cmsComputeInterpParams = _cmsComputeInterpParams
cmsGetToneCurveParams = cmsGetToneCurveParams
+cmsDetectRGBProfileGamma = cmsDetectRGBProfileGamma
diff --git a/testbed/testcms2.c b/testbed/testcms2.c index aad8656..0355fd5 100644 --- a/testbed/testcms2.c +++ b/testbed/testcms2.c @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 Marti Maria Saguer +// Copyright (c) 1998-2021 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -8318,6 +8318,113 @@ int Check_sRGB_Rountrips(void) return 1; } +static +cmsHPROFILE createRgbGamma(cmsFloat64Number g) +{ + cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 }; + cmsCIExyYTRIPLE Rec709Primaries = { + {0.6400, 0.3300, 1.0}, + {0.3000, 0.6000, 1.0}, + {0.1500, 0.0600, 1.0} + }; + cmsToneCurve* Gamma[3]; + cmsHPROFILE hRGB; + + Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildGamma(0, g); + if (Gamma[0] == NULL) return NULL; + + hRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma); + cmsFreeToneCurve(Gamma[0]); + return hRGB; +} + + +static +int CheckGammaSpaceDetection(void) +{ + cmsFloat64Number i; + + for (i = 0.5; i < 3; i += 0.1) + { + cmsHPROFILE hProfile = createRgbGamma(i); + + cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfile, 0.01); + + cmsCloseProfile(hProfile); + + if (fabs(gamma - i) > 0.1) + { + Fail("Failed profile gamma detection of %f (got %f)", i, gamma); + return 0; + } + } + + return 1; +} + + +#if 0 + +// You need to download folowing profilies to execute this test: sRGB-elle-V4-srgbtrc.icc, sRGB-elle-V4-g10.icc +// The include this line in the checks list: Check("KInear spaces detection", CheckLinearSpacesOptimization); +static +void uint16toFloat(cmsUInt16Number* src, cmsFloat32Number* dst) +{ + for (int i = 0; i < 3; i++) { + dst[i] = src[i] / 65535.f; + } +} + +static +int CheckLinearSpacesOptimization(void) +{ + cmsHPROFILE lcms_sRGB = cmsCreate_sRGBProfile(); + cmsHPROFILE elle_sRGB = cmsOpenProfileFromFile("sRGB-elle-V4-srgbtrc.icc", "r"); + cmsHPROFILE elle_linear = cmsOpenProfileFromFile("sRGB-elle-V4-g10.icc", "r"); + cmsHTRANSFORM transform1 = cmsCreateTransform(elle_sRGB, TYPE_RGB_16, elle_linear, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0); + cmsHTRANSFORM transform2 = cmsCreateTransform(elle_linear, TYPE_RGB_16, lcms_sRGB, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0); + cmsHTRANSFORM transform2a = cmsCreateTransform(elle_linear, TYPE_RGB_FLT, lcms_sRGB, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0); + + cmsUInt16Number sourceCol[3] = { 43 * 257, 27 * 257, 6 * 257 }; + cmsUInt16Number linearCol[3] = { 0 }; + float linearColF[3] = { 0 }; + cmsUInt16Number finalCol[3] = { 0 }; + int difR, difG, difB; + int difR2, difG2, difB2; + + cmsDoTransform(transform1, sourceCol, linearCol, 1); + cmsDoTransform(transform2, linearCol, finalCol, 1); + + cmsCloseProfile(lcms_sRGB); cmsCloseProfile(elle_sRGB); cmsCloseProfile(elle_linear); + + + difR = (int)sourceCol[0] - finalCol[0]; + difG = (int)sourceCol[1] - finalCol[1]; + difB = (int)sourceCol[2] - finalCol[2]; + + + uint16toFloat(linearCol, linearColF); + cmsDoTransform(transform2a, linearColF, finalCol, 1); + + difR2 = (int)sourceCol[0] - finalCol[0]; + difG2 = (int)sourceCol[1] - finalCol[1]; + difB2 = (int)sourceCol[2] - finalCol[2]; + + cmsDeleteTransform(transform1); + cmsDeleteTransform(transform2); + cmsDeleteTransform(transform2a); + + if (abs(difR2 - difR) > 5 || abs(difG2 - difG) > 5 || abs(difB2 - difB) > 5) + { + Fail("Linear detection failed"); + return 0; + } + + return 1; +} +#endif + + // -------------------------------------------------------------------------------------------------- // P E R F O R M A N C E C H E C K S // -------------------------------------------------------------------------------------------------- @@ -9049,7 +9156,7 @@ int main(int argc, char* argv[]) printf("Installing error logger ... "); cmsSetLogErrorHandler(FatalErrorQuit); printf("done.\n"); - + PrintSupportedIntents(); Check("Base types", CheckBaseTypes); @@ -9254,6 +9361,7 @@ int main(int argc, char* argv[]) Check("Proofing intersection", CheckProofingIntersection); Check("Empty MLUC", CheckEmptyMLUC); Check("sRGB round-trips", Check_sRGB_Rountrips); + Check("Gamma space detection", CheckGammaSpaceDetection); } if (DoPluginTests) diff --git a/utils/delphi/lcms2dll.pas b/utils/delphi/lcms2dll.pas index 9af7a0d..9368fc9 100644 --- a/utils/delphi/lcms2dll.pas +++ b/utils/delphi/lcms2dll.pas @@ -3,7 +3,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2014 Marti Maria Saguer +// Copyright (c) 1998-2021 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -25,7 +25,7 @@ // //--------------------------------------------------------------------------------- // -// Version 2.6 +// Version 2.13 // UNIT lcms2dll; @@ -1659,6 +1659,9 @@ FUNCTION cmsDetectDestinationBlackPoint( BlackPoint: LPcmsCIEXYZ; hProfile: cmsH // Estimate total area coverage FUNCTION cmsDetectTAC(hProfile: cmsHPROFILE): cmsFloat64Number; StdCall; +// Estimate profile gamma +FUNCTION cmsDetectRGBProfileGamma(hProfile: cmsHPROFILE): cmsFloat64Number; StdCall; + // Poor man's gamut mapping FUNCTION cmsDesaturateLab(Lab: LPcmsCIELab; amax, amin, bmax, bmin: cmsFloat64Number): cmsBool; StdCall; |