diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | Makefile.in | 2 | ||||
-rwxr-xr-x | configure | 3 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/cmsio1.c | 151 | ||||
-rw-r--r-- | src/cmsxform.c | 36 | ||||
-rwxr-xr-x | testbed/testcms2.c | 76 |
7 files changed, 157 insertions, 114 deletions
diff --git a/Makefile.am b/Makefile.am index 3fe7946..188e819 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,7 @@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ # Directories containing Makefiles to 'make' -SUBDIRS = src utils/tificc utils/transicc utils/linkicc utils/jpgicc utils/psicc testbed +SUBDIRS = src include utils/tificc utils/transicc utils/linkicc utils/jpgicc utils/psicc testbed # Additional files to distribute EXTRA_DIST = AUTHORS COPYING ChangeLog doc Projects include bin Lib INSTALL NEWS README.1ST lcms2.pc.in diff --git a/Makefile.in b/Makefile.in index 5522c93..85a9790 100644 --- a/Makefile.in +++ b/Makefile.in @@ -229,7 +229,7 @@ to_host_path_cmd = @to_host_path_cmd@ AUTOMAKE_OPTIONS = 1.7.2 dist-zip foreign # Directories containing Makefiles to 'make' -SUBDIRS = src utils/tificc utils/transicc utils/linkicc utils/jpgicc utils/psicc testbed +SUBDIRS = src include utils/tificc utils/transicc utils/linkicc utils/jpgicc utils/psicc testbed # Additional files to distribute EXTRA_DIST = AUTHORS COPYING ChangeLog doc Projects include bin Lib INSTALL NEWS README.1ST lcms2.pc.in @@ -15952,6 +15952,8 @@ ac_config_files="$ac_config_files Makefile" ac_config_files="$ac_config_files lcms2.pc" +ac_config_files="$ac_config_files include/Makefile" + ac_config_files="$ac_config_files src/Makefile" ac_config_files="$ac_config_files utils/tificc/Makefile" @@ -17075,6 +17077,7 @@ do "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "lcms2.pc") CONFIG_FILES="$CONFIG_FILES lcms2.pc" ;; + "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "utils/tificc/Makefile") CONFIG_FILES="$CONFIG_FILES utils/tificc/Makefile" ;; "utils/transicc/Makefile") CONFIG_FILES="$CONFIG_FILES utils/transicc/Makefile" ;; diff --git a/configure.ac b/configure.ac index 9b833da..dfda505 100644 --- a/configure.ac +++ b/configure.ac @@ -274,6 +274,7 @@ LIBS='' # AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([lcms2.pc]) +AC_CONFIG_FILES([include/Makefile]) AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([utils/tificc/Makefile]) AC_CONFIG_FILES([utils/transicc/Makefile]) diff --git a/src/cmsio1.c b/src/cmsio1.c index 2c09549..f86a9cf 100644 --- a/src/cmsio1.c +++ b/src/cmsio1.c @@ -54,19 +54,11 @@ static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Percept #define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0)) #define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0) -// Equal power for Gray. This is just to obtain XYZ of D50 scaled down. Note that gray spaces are implemented -// as RGB spaces, and R=G=B is forced in input. -static const cmsFloat64Number - GrayInputEqupower[] = { (InpAdj*cmsD50X)/3.0, (InpAdj*cmsD50X)/3.0, (InpAdj*cmsD50X)/3.0, - (InpAdj*cmsD50Y)/3.0, (InpAdj*cmsD50Y)/3.0, (InpAdj*cmsD50Y)/3.0, - (InpAdj*cmsD50Z)/3.0, (InpAdj*cmsD50Z)/3.0, (InpAdj*cmsD50Z)/3.0, - 0, 0, 0 }; - -static const cmsFloat64Number - GrayOutputEqupower[] = {(OutpAdj*cmsD50X)/3.0, (OutpAdj*cmsD50X)/3.0, (OutpAdj*cmsD50X)/3.0, - (OutpAdj*cmsD50Y)/3.0, (OutpAdj*cmsD50Y)/3.0, (OutpAdj*cmsD50Y)/3.0, - (OutpAdj*cmsD50Z)/3.0, (OutpAdj*cmsD50Z)/3.0, (OutpAdj*cmsD50Z)/3.0, - 0, 0, 0 }; +// Several resources for gray conversions. +static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) }; +static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 }; +static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 }; +static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; // Get a media white point fixing some issues found in certain old profiles cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile) @@ -150,77 +142,51 @@ cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile) return TRUE; } -// Some gray profiles are using kTCR as L*, this function converts the curve to XYZ PCS. -static -void FromLstarToXYZ(cmsToneCurve* g, cmsToneCurve* gxyz[3]) -{ - int i; - const int nPoints = 4096; - cmsCIELab Lab; - cmsCIEXYZ XYZ; - - // Allocate curves - gxyz[0] = cmsBuildTabulatedToneCurve16(g ->InterpParams->ContextID, nPoints, NULL); - gxyz[1] = cmsBuildTabulatedToneCurve16(g ->InterpParams->ContextID, nPoints, NULL); - gxyz[2] = cmsBuildTabulatedToneCurve16(g ->InterpParams->ContextID, nPoints, NULL); - - // Resample curve, converting from Lab to XYZ - for (i=0; i < nPoints; i++) { - - cmsUInt16Number val = _cmsQuantizeVal(i, nPoints); - cmsUInt16Number w; - - w = cmsEvalToneCurve16(g, val); - - Lab.L = ((cmsFloat64Number) 100.0 * w ) / 65535.0; - Lab.a = Lab.b = 0; - - cmsLab2XYZ(NULL, &XYZ, &Lab); - - // Store XYX data - gxyz[0] ->Table16[i] = _cmsQuickSaturateWord((65535.0 * XYZ.X) / cmsD50X); - gxyz[1] ->Table16[i] = _cmsQuickSaturateWord((65535.0 * XYZ.Y) / cmsD50Y); - gxyz[2] ->Table16[i] = _cmsQuickSaturateWord((65535.0 * XYZ.Z) / cmsD50Z); - } -} - -// Gray matrix shaper +// Gray input pipeline static -cmsPipeline* BuildGrayInputMatrixShaper(cmsHPROFILE hProfile) +cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile) { cmsToneCurve *GrayTRC; - cmsToneCurve *Shapes[3]; cmsPipeline* Lut; cmsContext ContextID = cmsGetProfileContextID(hProfile); - GrayTRC = cmsReadTag(hProfile, cmsSigGrayTRCTag); // This is Y + GrayTRC = cmsReadTag(hProfile, cmsSigGrayTRCTag); if (GrayTRC == NULL) return NULL; + Lut = cmsPipelineAlloc(ContextID, 1, 3); + if (Lut == NULL) return NULL; + if (cmsGetPCS(hProfile) == cmsSigLabData) { - // Fixup for Lab monochrome - FromLstarToXYZ(GrayTRC, Shapes); - } - else { - Shapes[0] = cmsDupToneCurve(GrayTRC); - Shapes[1] = cmsDupToneCurve(GrayTRC); - Shapes[2] = cmsDupToneCurve(GrayTRC); - } + // In this case we implement the profile as an identity matrix plus 3 tone curves + cmsUInt16Number Zero[2] = { 0x8080, 0x8080 }; + cmsToneCurve* EmptyTab; + cmsToneCurve* LabCurves[3]; + + EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + + if (EmptyTab == NULL) { - if (!Shapes[0] || !Shapes[1] || !Shapes[2]) { + cmsPipelineFree(Lut); return NULL; } + LabCurves[0] = GrayTRC; + LabCurves[1] = EmptyTab; + LabCurves[2] = EmptyTab; - Lut = cmsPipelineAlloc(ContextID, 3, 3); - if (Lut != NULL) { + cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)); + cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves)); + + cmsFreeToneCurve(EmptyTab); - cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)); - cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, GrayInputEqupower, NULL)); + } + else { + cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)); + cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL)); } - cmsFreeToneCurveTriple(Shapes); return Lut; } @@ -307,7 +273,7 @@ cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent) // if so, build appropiate conversion tables. // The tables are the PCS iluminant, scaled across GrayTRC - return BuildGrayInputMatrixShaper(hProfile); + return BuildGrayInputMatrixPipeline(hProfile); } // Not gray, create a normal matrix-shaper @@ -316,51 +282,46 @@ cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent) // --------------------------------------------------------------------------------------------------------------- -// Gray matrix shaper +// Gray output pipeline. +// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be +// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve. +// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well. + static -cmsPipeline* BuildGrayOutputMatrixShaper(cmsHPROFILE hProfile) +cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile) { - cmsToneCurve *GrayTRC, *Shapes[3], *RevShapes[3]; + cmsToneCurve *GrayTRC, *RevGrayTRC; cmsPipeline* Lut; cmsContext ContextID = cmsGetProfileContextID(hProfile); - GrayTRC = cmsReadTag(hProfile, cmsSigGrayTRCTag); // Y + GrayTRC = cmsReadTag(hProfile, cmsSigGrayTRCTag); if (GrayTRC == NULL) return NULL; + RevGrayTRC = cmsReverseToneCurve(GrayTRC); + if (RevGrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 1); + if (Lut == NULL) { + cmsFreeToneCurve(RevGrayTRC); + return NULL; + } + if (cmsGetPCS(hProfile) == cmsSigLabData) { - // Fixup for Lab monochrome - FromLstarToXYZ(GrayTRC, Shapes); + cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)); } else { - Shapes[0] = cmsDupToneCurve(GrayTRC); - Shapes[1] = cmsDupToneCurve(GrayTRC); - Shapes[2] = cmsDupToneCurve(GrayTRC); + cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL)); } + cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC)); + cmsFreeToneCurve(RevGrayTRC); - if (!Shapes[0] || !Shapes[1] || !Shapes[2]) - return NULL; - - // This is output matrix-shaper, so we need to reverse curves - RevShapes[0] = cmsReverseToneCurve(Shapes[0]); - RevShapes[1] = cmsReverseToneCurve(Shapes[1]); - RevShapes[2] = cmsReverseToneCurve(Shapes[2]); + return Lut; +} - if (!RevShapes[0] || !RevShapes[1] || !RevShapes[2]) - return NULL; - Lut = cmsPipelineAlloc(ContextID, 3, 3); - if (Lut != NULL) { - cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, RevShapes)); - cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, GrayOutputEqupower, NULL)); - } - - cmsFreeToneCurveTriple(Shapes); - cmsFreeToneCurveTriple(RevShapes); - return Lut; -} static cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) @@ -455,7 +416,7 @@ cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent) // if so, build appropiate conversion tables. // The tables are the PCS iluminant, scaled across GrayTRC - return BuildGrayOutputMatrixShaper(hProfile); + return BuildGrayOutputPipeline(hProfile); } // Not gray, create a normal matrix-shaper diff --git a/src/cmsxform.c b/src/cmsxform.c index 27defeb..9d670dd 100644 --- a/src/cmsxform.c +++ b/src/cmsxform.c @@ -281,22 +281,24 @@ void CachedXFORM(_cmsTRANSFORM* p, LCMS_READ_LOCK(&p ->rwlock); - memmove(CacheIn, p ->CacheIn, sizeof(cmsUInt16Number) * MAXCHANNELS); - memmove(CacheOut, p ->CacheOut, sizeof(cmsUInt16Number) * MAXCHANNELS); + memmove(CacheIn, p ->CacheIn, sizeof(CacheIn)); + memmove(CacheOut, p ->CacheOut, sizeof(CacheOut)); LCMS_UNLOCK(&p ->rwlock); for (i=0; i < n; i++) { accum = p -> FromInput(p, wIn, accum); - if (memcmp(wIn, CacheIn, sizeof(cmsUInt16Number) * MAXCHANNELS) == 0) { + if (memcmp(wIn, CacheIn, sizeof(CacheIn)) == 0) { - memmove(wOut, CacheOut, sizeof(cmsUInt16Number) * MAXCHANNELS); + memmove(wOut, CacheOut, sizeof(CacheOut)); } else { + p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); - memmove(CacheIn, wIn, sizeof(cmsUInt16Number) * MAXCHANNELS); - memmove(CacheOut, wOut, sizeof(cmsUInt16Number) * MAXCHANNELS); + + memmove(CacheIn, wIn, sizeof(CacheIn)); + memmove(CacheOut, wOut, sizeof(CacheOut)); } output = p -> ToOutput(p, wOut, output); @@ -304,8 +306,8 @@ void CachedXFORM(_cmsTRANSFORM* p, LCMS_WRITE_LOCK(&p ->rwlock); - memmove(p->CacheIn, CacheIn, sizeof(cmsUInt16Number) * MAXCHANNELS); - memmove(p->CacheOut, CacheOut, sizeof(cmsUInt16Number) * MAXCHANNELS); + memmove(p->CacheIn, CacheIn, sizeof(CacheIn)); + memmove(p->CacheOut, CacheOut, sizeof(CacheOut)); LCMS_UNLOCK(&p ->rwlock); } @@ -329,7 +331,6 @@ void CachedXFORMGamutCheck(_cmsTRANSFORM* p, n = Size; // Buffer len // Empty buffers for quick memcmp - memset(wIn, 0, sizeof(cmsUInt16Number) * MAXCHANNELS); memset(wOut, 0, sizeof(cmsUInt16Number) * MAXCHANNELS); @@ -498,6 +499,9 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, // On floating point transforms, inhibit optimizations FloatTransform = (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat)); + if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) + dwFlags |= cmsFLAGS_NOCACHE; + // Mark entry/exit spaces GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace); @@ -578,14 +582,18 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, xform ->Sequence = NULL; // If this is a cached transform, init first value, which is zero (16 bits only) - if (!FloatTransform && !(dwFlags & cmsFLAGS_NOCACHE)) { + if (!(dwFlags & cmsFLAGS_NOCACHE)) { memset(&xform ->CacheIn, 0, sizeof(xform ->CacheIn)); - if (xform ->GamutCheck != NULL) - PrecalculatedXFORMGamutCheck(xform, &xform ->CacheIn, &xform->CacheOut, 1); - else - PrecalculatedXFORM(xform, &xform ->CacheIn, &xform->CacheOut, 1); + if (xform ->GamutCheck != NULL) { + TransformOnePixelWithGamutCheck(xform, xform ->CacheIn, xform->CacheOut); + } + else { + + xform ->Lut ->Eval16Fn(xform ->CacheIn, xform->CacheOut, xform -> Lut->Data); + } + } return (cmsHTRANSFORM) xform; diff --git a/testbed/testcms2.c b/testbed/testcms2.c index 689b35a..6176d8e 100755 --- a/testbed/testcms2.c +++ b/testbed/testcms2.c @@ -294,6 +294,7 @@ void DumpToneCurve(cmsToneCurve* gamma, const char* FileName) // ------------------------------------------------------------------------------------------------- + // Used to perform several checks. // The space used is a clone of a well-known commercial // color space which I will name "Above RGB" @@ -330,6 +331,21 @@ cmsHPROFILE Create_Gray22(void) return hProfile; } + +static +cmsHPROFILE Create_GrayLab(void) +{ + cmsHPROFILE hProfile; + cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 1.0); + if (Curve == NULL) return NULL; + + hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve); + cmsFreeToneCurve(Curve); + + cmsSetPCS(hProfile, cmsSigLabData); + return hProfile; +} + // A CMYK devicelink that adds gamma 3.0 to each channel static cmsHPROFILE Create_CMYK_DeviceLink(void) @@ -5726,8 +5742,60 @@ int CheckPostScript(void) } +static +int CheckGray(cmsHTRANSFORM xform, int g, double L) +{ + cmsCIELab Lab; + + cmsDoTransform(xform, &g, &Lab, 1); + + if (!IsGoodVal("a axis on gray", 0, Lab.a, 0.001)) return 0; + if (!IsGoodVal("b axis on gray", 0, Lab.b, 0.001)) return 0; + + return IsGoodVal("Gray value", L, Lab.L, 0.01); +} + +static +int CheckInputGray(void) +{ + cmsHPROFILE hGray = Create_Gray22(); + cmsHPROFILE hLab = cmsCreateLab4Profile(NULL); + cmsHTRANSFORM xform; + + if (hGray == NULL || hLab == NULL) return 0; + + xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0); + cmsCloseProfile(hGray); cmsCloseProfile(hLab); + + if (!CheckGray(xform, 0, 0)) return 0; + if (!CheckGray(xform, 125, 52.768)) return 0; + if (!CheckGray(xform, 200, 81.069)) return 0; + if (!CheckGray(xform, 255, 100.0)) return 0; + + cmsDeleteTransform(xform); + return 1; +} + +static +int CheckLabInputGray(void) +{ + cmsHPROFILE hGray = Create_GrayLab(); + cmsHPROFILE hLab = cmsCreateLab4Profile(NULL); + cmsHTRANSFORM xform; + + if (hGray == NULL || hLab == NULL) return 0; + + xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0); + cmsCloseProfile(hGray); cmsCloseProfile(hLab); + if (!CheckGray(xform, 0, 0)) return 0; + if (!CheckGray(xform, 125, 49.019)) return 0; + if (!CheckGray(xform, 200, 78.431)) return 0; + if (!CheckGray(xform, 255, 100.0)) return 0; + cmsDeleteTransform(xform); + return 1; +} @@ -6100,7 +6168,7 @@ void PrintSupportedIntents(void) #ifdef _CMS_IS_WINDOWS -static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\bpc\\"; +static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\"; static char ZOOwrite[cmsMAX_PATH] = "c:\\colormaps\\write\\"; static char ZOORawWrite[cmsMAX_PATH] = "c:\\colormaps\\rawwrite\\"; @@ -6288,7 +6356,7 @@ void CheckProfileZOO(void) cmsSetLogErrorHandler(NULL); - if ( (hFile = _findfirst("c:\\colormaps\\bpc\\*.*", &c_file)) == -1L ) + if ( (hFile = _findfirst("c:\\colormaps\\*.*", &c_file)) == -1L ) printf("No files in current directory"); else { @@ -6300,7 +6368,7 @@ void CheckProfileZOO(void) strcmp(c_file.name, "..") != 0) { CheckSingleSpecimen( c_file.name); - // CheckRAWSpecimen( c_file.name); + CheckRAWSpecimen( c_file.name); if (TotalMemory > 0) printf("Ok, but %s are left!\n", MemStr(TotalMemory)); @@ -6660,6 +6728,8 @@ int main(int argc, char* argv[]) // Known values Check("Known values across matrix-shaper", Chack_sRGB_Float); + Check("Gray input profile", CheckInputGray); + Check("Gray lab input profile", CheckLabInputGray); Check("Matrix-shaper proofing transform (float)", CheckProofingXFORMFloat); Check("Matrix-shaper proofing transform (16 bits)", CheckProofingXFORM16); |