// // Little cms // Copyright (C) 1998-2007 Marti Maria // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Test Suite for Little cms // #define ICM_COMPARATIVE 1 // #define CHECK_SPEED 1 #ifdef __BORLANDC__ # include #endif #include "lcms.h" #include #include #include #ifndef NON_WINDOWS #include #endif #define PREC 20 #define TYPE_XYZA_16 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)) #define TYPE_LABA_16 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)) typedef struct {BYTE r, g, b, a;} Scanline_rgb1; typedef struct {WORD r, g, b, a;} Scanline_rgb2; typedef struct {BYTE r, g, b;} Scanline_rgb8; typedef struct {WORD r, g, b;} Scanline_rgb0; // Print a dot for gauging static void Dot(void) { fprintf(stdout, "."); fflush(stdout); } // #ifndef LCMS_DLL // Are we little or big endian? From Harbison&Steele. static int CheckEndianess(void) { int BigEndian, IsOk; union { long l; char c[sizeof (long)]; } u; u.l = 1; BigEndian = (u.c[sizeof (long) - 1] == 1); #ifdef USE_BIG_ENDIAN IsOk = BigEndian; #else IsOk = !BigEndian; #endif if (!IsOk) { printf("\nOOOPPSS! You have USE_BIG_ENDIAN toggle misconfigured!\n\n"); printf("Please, edit lcms.h and %s the USE_BIG_ENDIAN toggle.\n", BigEndian? "uncomment" : "comment"); return 0; } return 1; } static int CheckSwab(void) { unsigned char Test[] = { 1, 2, 3, 4, 5, 6}; #ifdef USE_CUSTOM_SWAB return 1; #endif #ifdef USE_BIG_ENDIAN return 1; #endif xswab((char*) Test, (char*) Test, 6); if (strncmp((char*) Test, "\x2\x1\x4\x3\x6\x5", 6) != 0) { printf("\nOOOPPSS! swab() does not work as expected in your machine!\n\n"); printf("Please, edit lcms.h and uncomment the USE_CUSTOM_SWAB toggle.\n"); return 0; } return 1; } static int CheckQuickFloor(void) { if ((_cmsQuickFloor(1.234) != 1) || (_cmsQuickFloor(32767.234) != 32767) || (_cmsQuickFloor(-1.234) != -2) || (_cmsQuickFloor(-32767.1) != -32768)) { printf("\nOOOPPSS! _cmsFloor() does not work as expected in your machine!\n\n"); printf("Please, edit lcms.h and uncomment the LCMS_DEFAULT_FLOOR_CONVERSION toggle.\n"); return 0; } return 1; } typedef struct _Stats { double n, x, y, x2, y2, xy; double Peak; } STATS, FAR* LPSTATS; static void ClearStats(LPSTATS p) { p -> n = p -> x = p -> y = p -> x2 = p -> y2 = p -> xy = p -> Peak = 0.0; } static double Std(LPSTATS p) { return sqrt((p->n*p->x2 - p->x * p->x) / (p->n*(p->n-1))); } static void PrintStatistics(clock_t atime, LPSTATS Stats) { clock_t diff; double a; diff = clock() - atime; a = (double) diff / CLOCKS_PER_SEC; // These are statistics of 16 bit, so divide // by 257 to get dE relative to 8 bits printf("\n"); if (Stats) printf("dE: mean=%g, SD=%g, max=%g ", (Stats->x / Stats -> n) / 257., (Std(Stats)) / 257., Stats -> Peak / 257.); if (atime > 0) printf("[%d tics, %g sec.]", (int) diff, a); } // Simpler fixed-point math static void TestFixedPoint(void) { Fixed32 a, b, c, d; double f; a = DOUBLE_TO_FIXED(1.1234); b = DOUBLE_TO_FIXED(2.5678); c = FixedMul(a, b); d = FIXED_REST_TO_INT(c); f = ((double) d / 0xffff) * 1000000.0; printf("Testing fixed point:\t%f = %d.%d\n", (1.1234 * 2.5678), FIXED_TO_INT(c), (int) f); } static int TestFixedScaling(void) { int i, j, nfl, nfx; double FloatFactor; Fixed32 FixedFactor; printf("Testing fixed scaling..."); for (j=5; j<100; j++) { FloatFactor = (double) j / 100.0 ; FloatFactor = FIXED_TO_DOUBLE(DOUBLE_TO_FIXED(FloatFactor)); FixedFactor = DOUBLE_TO_FIXED(FloatFactor); for (i=0; i < 0x10000L; i++) { nfl = (WORD) ((double) i * FloatFactor); nfx = FixedScale((WORD) i, FixedFactor); if (nfl != nfx) { printf("Failed!\ni=%x (%d), float=%x, fixed=%x", i, i, nfl, nfx); return 0; } } } printf ("pass.\n"); return 1; } // Curve joining test. Joining two high-gamma of 3.0 curves should // give something like linear static int TestJointCurves(void) { LPGAMMATABLE Forward, Reverse, Result; LCMSBOOL rc; printf("Testing curves join ..."); Forward = cmsBuildGamma(256, 3.0); Reverse = cmsBuildGamma(256, 3.0); Result = cmsJoinGammaEx(Forward, Reverse, 256); cmsFreeGamma(Forward); cmsFreeGamma(Reverse); rc = cmsIsLinear(Result->GammaTable, Result ->nEntries); cmsFreeGamma(Result); if (!rc) { printf("failed!\n"); return 0; } else { printf("pass.\n"); return 1; } } // Check reversing of gamma curves #define NPOINTS 1024 static int TestReversingOfCurves(void) { LPGAMMATABLE Gamma, Reverse, Computed; int i; double dE; STATS Stats; printf("Testing reversing of curves ..."); ClearStats(&Stats); Gamma = cmsBuildGamma(NPOINTS, 3.0); Reverse = cmsBuildGamma(NPOINTS, 1.0/3.0); Computed = cmsReverseGamma(NPOINTS, Gamma); for (i=0; i < NPOINTS; i++) { dE = fabs(Reverse->GammaTable[i] - Computed->GammaTable[i]); Stats.x += dE; Stats.x2 += (dE * dE); Stats.n += 1.0; if (dE > Stats.Peak) { Stats.Peak = dE; } if (dE > 0x0010) { printf("Coarse error! %x on entry %d: %X/%X", (int) dE, i, Reverse->GammaTable[i], Computed->GammaTable[i]); return 0; } } if (Stats.Peak > 0) PrintStatistics(0, &Stats); printf(" pass.\n"); cmsFreeGamma(Gamma); cmsFreeGamma(Reverse); cmsFreeGamma(Computed); return 1; } // Linear interpolation test. Here I check the cmsLinearInterpLUT16 // Tables are supposed to be monotonic, but the algorithm works on // non-monotonic as well. static int TestLinearInterpolation(int lExhaustive) { static WORD Tab[4098]; int j, i, k = 0; L16PARAMS p; int n; clock_t time; printf("Testing linear interpolation ..."); // First I will check exact values. Since prime factors of 65535 (FFFF) are, // // 0xFFFF = 1 * 3 * 5 * 17 * 257 // // I test tables of 2, 4, 6, and 18 points, that will be exact. // Then, a table of 3 elements are tested. Error must be < 1 // Since no floating point is involved, This will be a measure of speed. // Perform 10 times, so i can measure average times time = clock(); for (j=0; j < 10; j++) { // 2 points - exact Tab[0] = 0; Tab[1] = 0xffffU; cmsCalcL16Params(2, &p); for (i=0; i <= 0xffffL; i++) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if (n != i) { printf("Error in Linear interpolation (2p): Must be i=%x, But is n=%x\n", i, n); return 0; } } // 3 points - Here the error must be <= 1, since // 2 == (3 - 1) is not a factor of 0xffff Tab[0] = 0; Tab[1] = 0x7FFF; Tab[2] = 0xffffU; cmsCalcL16Params(3, &p); for (i=0; i <= 0xffffL; i++) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if (abs(n - i) > 1) { printf("Error in Linear interpolation (3p): Must be i=%x, But is n=%x\n", i, n); return 0; } } // 4 points - exact Tab[0] = 0; Tab[1] = 0x5555U; Tab[2] = 0xAAAAU; Tab[3] = 0xffffU; cmsCalcL16Params(4, &p); for (i=0; i <= 0xffffL; i++) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if (n != i) { printf("Error in Linear interpolation (4p): Must be i=%x, But is n=%x\n", i, n); return 0; } } // 6 - points Tab[0] = 0; Tab[1] = 0x3333U; Tab[2] = 0x6666U; Tab[3] = 0x9999U; Tab[4] = 0xCCCCU; Tab[5] = 0xFFFFU; cmsCalcL16Params(6, &p); for (i=0; i <= 0xffffL; i++) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if (n != i) { printf("Error in Linear interpolation (6p): Must be i=%x, But is n=%x\n", i, n); return 0; } } // 18 points for (i=0; i < 18; i++) Tab[i] = (WORD) (0x0f0fU*i); cmsCalcL16Params(18, &p); for (i=0; i <= 0xffffL; i++) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if (n != i) { printf("Error in Linear interpolation (18p): Must be i=%x, But is n=%x\n", i, n); return 0; } } } printf("pass. (%d tics)\n", (int) (clock() - time)); // Now test descending tables printf("Testing descending tables (linear interpolation)..."); // 2 points - exact Tab[1] = 0; Tab[0] = 0xffffU; cmsCalcL16Params(2, &p); for (i=0xffffL; i > 0; --i) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if ((0xffffL - n) != i) { printf("Error in Linear interpolation (descending) (2p): Must be i=%x, But is n=%x\n", i, 0xffff - n); return 0; } } // 3 points - Here the error must be <= 1, since // 2 = (3 - 1) is not a factor of 0xffff Tab[2] = 0; Tab[1] = 0x7FFF; Tab[0] = 0xffffU; cmsCalcL16Params(3, &p); for (i=0xffffL; i > 0; --i) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if (abs((0xffffL - n) - i) > 1) { printf("Error in Linear interpolation (descending) (3p): Must be i=%x, But is n=%x\n", i, n); return 0; } } // 4 points - exact Tab[3] = 0; Tab[2] = 0x5555U; Tab[1] = 0xAAAAU; Tab[0] = 0xffffU; cmsCalcL16Params(4, &p); for (i=0xffffL; i > 0; --i) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if ((0xffffL - n) != i) { printf("Error in Linear interpolation (descending) (4p): Must be i=%x, But is n=%x\n", i, n); return 0; } } // 6 - points Tab[5] = 0; Tab[4] = 0x3333U; Tab[3] = 0x6666U; Tab[2] = 0x9999U; Tab[1] = 0xCCCCU; Tab[0] = 0xFFFFU; cmsCalcL16Params(6, &p); for (i=0xffffL; i > 0; --i) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if ((0xffffL - n) != i) { printf("Error in Linear interpolation (descending) (6p): Must be i=%x, But is n=%x\n", i, n); return 0; } } // 18 points for (i=0; i < 18; i++) Tab[17-i] = (WORD) (0x0f0fU*i); cmsCalcL16Params(18, &p); for (i=0xffffL; i > 0; --i) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if ((0xffffL - n) != i) { printf("Error in Linear interpolation (descending) (18p): Must be i=%x, But is n=%x\n", i, n); return 0; } } printf("pass.\n"); if (!lExhaustive) return 1; printf("Now, testing interpolation errors for tables of n elements ...\n"); for (j=10; j < 4096; j ++) { if ((j % 10) == 0) printf("%d\r", j); for (i=0; i <= j; i++) { Tab[i] = (WORD) floor((((double) i / ((double) j-1)) * 65535.0) + .5); } k =0; cmsCalcL16Params(j, &p); for (i=0; i <= 0xffffL; i++) { n = cmsLinearInterpLUT16((WORD) i, Tab, &p); if (n != i) k++; } } printf("\n%d: %d errors\n\n", j, k); return 1; } static int IsGood(const char *frm, WORD in, WORD out) { // 1 for rounding if ((abs(in - out) > 1)) { printf("error %s %x - %x\n", frm, in, out); return 0; } return 1; } static LCMSBOOL TestReverseLinearInterpolation(void) { WORD Tab[20]; L16PARAMS p; int i, n, v; printf("Testing reverse linear interpolation\n"); cmsCalcL16Params(16, &p); for (i=0; i < 16; i++) Tab[i] = (WORD) i * 0x1111; printf("\ton normal monotonic curve..."); for (i=0; i < 16; i++) { v = (i * 0x1111); n = cmsReverseLinearInterpLUT16((WORD) v, Tab, &p); if (!IsGood("unexpected result", (WORD) v, (WORD) n)) return FALSE; } printf("pass.\n"); Tab[0] = 0; Tab[1] = 0; Tab[2] = 0; Tab[3] = 0; Tab[4] = 0; Tab[5] = 0x5555; Tab[6] = 0x6666; Tab[7] = 0x7777; Tab[8] = 0x8888; Tab[9] = 0x9999; Tab[10]= 0xffff; Tab[11]= 0xffff; Tab[12]= 0xffff; Tab[13]= 0xffff; Tab[14]= 0xffff; Tab[15]= 0xffff; printf("\ton degenerated curve ..."); for (i=0; i < 16; i++) { v = (i * 0x1111); n = cmsReverseLinearInterpLUT16((WORD) v, Tab, &p); if (i > 5 && i <= 9) { if (!IsGood("unexpected result", (WORD) v, (WORD) n)) return FALSE; } } printf("pass.\n"); return TRUE; } // 3D LUT test static int Test3D(void) { LPLUT MyLut; LPWORD Table; WORD In[3], Out[3]; int r, g, b, i; double *SampleTablePtr, SampleTable[] = { //R G B 0, 0, 0, // B=0,G=0,R=0 0, 0, .25, // B=1,G=0,R=0 0, .5, 0, // B=0,G=1,R=0 0, .5, .25, // B=1,G=1,R=0 1, 0, 0, // B=0,G=0,R=1 1, 0, .25, // B=1,G=0,R=1 1, .5, 0, // B=0,G=1,R=1 1, .5, .25 // B=1,G=1,R=1 }; printf("Testing 3D interpolation on LUT..."); // 1.- Allocate an empty LUT MyLut = cmsAllocLUT(); // 2.- In this LUT, allocate a 3D grid of 2 points, from 3 components (RGB) // to 3 components. First 3 is input dimension, last 3 is output one. // 2 is number of grid points. MyLut = cmsAlloc3DGrid(MyLut, 2, 3, 3); // 3.- Fill the LUT table with values. Table = MyLut -> T; SampleTablePtr = SampleTable; for (i= 0; i < 3; i++) for (r = 0; r < 2; r++) for (g = 0; g < 2; g++) for (b = 0; b < 2; b++) { WORD a = (WORD) floor(*SampleTablePtr++ * 65535. + .5); *Table++ = a; } // The sample table gives // // r = input, // g = input divided by 2 // b = input divided by 4 // // So, I should obtain on output r, g/2, g/4 for (i=0; i < 0xffff; i++) { In[0] = In[1] = In[2] = (WORD) i; cmsEvalLUT(MyLut, In, Out); // Check results, I will tolerate error <= 1 for rounding if (!IsGood("Channel 1", Out[0], In[0])) return 0; if (!IsGood("Channel 2", Out[1], (WORD) ((double) In[1] / 2))) return 0; if (!IsGood("Channel 3", Out[2], (WORD) ((double) In[2] / 4))) return 0; } // Last, remember free stuff cmsFreeLUT(MyLut); printf("pass.\n"); return 1; } static void PrintMatrix(LPMAT3 lpM) { int i, j; for (i=0; i < 3; i++) { printf ("[ "); for (j=0; j < 3; j++) { printf("%1.6f ", (*lpM).v[i].n[j]); } printf("]\n"); } printf("\n"); } static LCMSBOOL CmpMatrix(LPMAT3 lpM1, LPMAT3 lpM2, double tolerance) { int i, j; for (i=0; i < 3; i++) { for (j=0; j < 3; j++) { if (fabs(lpM1 -> v[i].n[j] - lpM2 -> v[i].n[j]) > tolerance) return FALSE; } } return TRUE; } static LCMSBOOL TestMatrixCreation(void) { MAT3 Mat; int rc; cmsCIExyY WhitePt = {0.3127, 0.3290, 1.0}; cmsCIExyYTRIPLE Primaries = { {0.6400, 0.3300, 1.0}, {0.3000, 0.6000, 1.0}, {0.1500, 0.0600, 1.0} }; MAT3 sRGB = {{ {{ 0.436066, 0.385147, 0.143066 }}, {{ 0.222488, 0.716873, 0.060608 }}, {{ 0.013916, 0.097076, 0.714096 }} }}; printf("Testing virtual profiles (Emulating sRGB)..."); rc = cmsBuildRGB2XYZtransferMatrix(&Mat, &WhitePt, &Primaries); cmsAdaptMatrixToD50(&Mat, &WhitePt); if (rc < 0) { printf("TestMatrixCreation failed, rc = %d\n", rc); return FALSE; } if (!CmpMatrix(&Mat, &sRGB, 0.001)) { printf("FAILED!\n"); printf("sRGB final matrix is:\n"); PrintMatrix(&sRGB); printf("\nlcms calculated matrix is:\n"); PrintMatrix(&Mat); return FALSE; } printf("pass.\n"); return TRUE; } /* Used for debug purposes */ #if 0 static void AdaptationMatrixTest(void) { cmsCIExyY D65 = {0.3127, 0.329001, 1.0}; // D65 MAT3 sRGB, TosRGB; VEC3init(&sRGB.v[0], 0.4124, 0.3576, 0.1805); VEC3init(&sRGB.v[1], 0.2126, 0.7152, 0.0722); VEC3init(&sRGB.v[2], 0.0193, 0.1192, 0.9505); cmsAdaptMatrixToD50(&sRGB, &D65); printf("Adaptation matrix D65 -> D50 (to PCS)\n"); PrintMatrix(&sRGB); MAT3inverse(&sRGB, &TosRGB); printf("inverse\n"); PrintMatrix(&TosRGB); cmsAdaptMatrixFromD50(&TosRGB, &D65); printf("adaptated to D65\n"); PrintMatrix(&TosRGB); } #endif // #endif static double VecDist(Scanline_rgb2 *bin, Scanline_rgb2 *bout) { double rdist, gdist, bdist; rdist = fabs(bout -> r - bin -> r); gdist = fabs(bout -> g - bin -> g); bdist = fabs(bout -> b - bin -> b); return (sqrt((rdist*rdist + gdist*gdist + bdist*bdist))); } // Perform sampling in the full spectrum & acotate error. // I choose red for the lowest incidence in eye. // Green is most lightful, eye is most accurate on blue. static int TestFullSpectrum(cmsHTRANSFORM xform, int nRedInterv, int MaxErr) { int r, g, b; double err; Scanline_rgb2 *bin, *bout; STATS Stats; clock_t t; bin = (Scanline_rgb2 *) _cmsMalloc(256*sizeof(Scanline_rgb2)); bout = (Scanline_rgb2 *) _cmsMalloc(256*sizeof(Scanline_rgb2)); ClearStats(&Stats); Stats.x = 0.0; Stats.n = 0.0; // GCC BUG HERE!!!! t = clock(); for (r=0; r < 256; r+= nRedInterv) { // printf("\r%02x:", r); Dot(); for (g=0; g < 256; g++) { for (b=0; b < 256; b++) { bin[b].r = (WORD) r << 8; // For L 0nly to 0xFF00 bin[b].g = RGB_8_TO_16(g); bin[b].b = RGB_8_TO_16(b); bin[b].a = 0; } cmsDoTransform(xform, bin, bout, 256); // I'm using b as index for (b=0; b < 256; b ++) { // I measure the error using vector distance err = VecDist(bin+b, bout+b); Stats.x += (double) err; Stats.x2 += (double) err * err; Stats.n += 1.0; if (err > Stats.Peak) Stats.Peak = err; if (err > MaxErr) { printf("Coarse error! : In=(%x,%x,%x) Out=(%x,%x,%x)\n", bin[b].r, bin[b].g, bin[b].b, bout[b].r, bout[b].g, bout[b].b); _cmsFree(bin); _cmsFree(bout); return 0; } } } } PrintStatistics(t, &Stats); _cmsFree(bin); _cmsFree(bout); return 1; } static int TestInducedError(DWORD Type) { cmsHPROFILE In, Out; cmsHTRANSFORM xform; int nMaxError; In = cmsCreateLabProfile(NULL); Out = cmsCreateLabProfile(NULL); printf("Error Induced by the CMM due to roundoff (dE) "); xform = cmsCreateTransform(In, Type, Out, Type, INTENT_RELATIVE_COLORIMETRIC, 0); nMaxError = TestFullSpectrum(xform, 31, 0x800); printf("\n"); cmsDeleteTransform(xform); cmsCloseProfile(In); cmsCloseProfile(Out); return nMaxError; } static double ConvertL(WORD v) { int fix32; fix32 = v; return (double)fix32/652.800; /* 0xff00/100.0 */ } static double Convertab(WORD v) { int fix32; fix32 = v; return ((double)fix32/256.0)-128.0; } #define BASE 255 static int CompareTransforms(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, int nRedInterv, int lWithStats, LCMSBOOL lIsLab) { int r, g, b; double err; Scanline_rgb2 *bin, *bout1, *bout2; STATS Stats; int OutOfGamut = 0; bin = (Scanline_rgb2 *) _cmsMalloc(256*sizeof(Scanline_rgb2)); bout1 = (Scanline_rgb2 *) _cmsMalloc(256*sizeof(Scanline_rgb2)); bout2 = (Scanline_rgb2 *) _cmsMalloc(256*sizeof(Scanline_rgb2)); ClearStats(&Stats); Stats.x = 0.0; Stats.n = 0.0; // GCC BUG HERE!!!! for (r=0; r <= BASE; r+= nRedInterv) { // printf("\r%02x:", r); Dot(); for (g=0; g <= BASE; g++) { // I will test random LSB for (b=0; b <= BASE; b++) // 256 { bin[b].r = RGB_8_TO_16(r); bin[b].g = RGB_8_TO_16(g); bin[b].b = RGB_8_TO_16(b); bin[b].a = 0; } cmsDoTransform(xform1, bin, bout1, 256); cmsDoTransform(xform2, bin, bout2, 256); // I'm using b as index for (b=0; b <= BASE; b ++) { // I measure the error using vector distance // Only if encodable values if (bout1[b].r != 0xffff && bout1[b].g != 0xffff && bout1[b].b != 0xffff) { err = VecDist(bout1+b, bout2+b); if (err > 0x1000L) { if (lIsLab) { printf("Coarse error: In=(%x,%x,%x) Out1=(%g,%g,%g) Out2=(%g,%g,%g)\n", bin[b].r, bin[b].g, bin[b].b, ConvertL(bout1[b].r), Convertab(bout1[b].g), Convertab(bout1[b].b), ConvertL(bout2[b].r), Convertab(bout2[b].g), Convertab(bout2[b].b)); } else { printf("Coarse error: In=(%x,%x,%x) Out1=(%x,%x,%x) Out2=(%x,%x,%x)\n", bin[b].r, bin[b].g, bin[b].b, bout1[b].r, bout1[b].g, bout1[b].b, bout2[b].r, bout2[b].g, bout2[b].b); } return 0; } else { Stats.x += (double) err; Stats.x2 += (double) err * err; Stats.n += 1.0; if (err > Stats.Peak) Stats.Peak = err; } } else OutOfGamut++; } } } if (lWithStats) { PrintStatistics(0, &Stats); printf(" pass.\n"); } if (OutOfGamut > 0) printf("Out of encodeable representation=%d\n\n", OutOfGamut); _cmsFree(bin); _cmsFree(bout1); _cmsFree(bout2); return 1; } static LCMSBOOL CheckXYZ(LPcmsCIEXYZ Check, double X, double Y, double Z) { return ((fabs(Check->X - X) < 0.001) && (fabs(Check->Y - Y) < 0.001) && (fabs(Check->Z - Z) < 0.001)); } static LPGAMMATABLE Build_sRGBGamma(void) { double Parameters[5]; Parameters[0] = 2.4; Parameters[1] = 1. / 1.055; Parameters[2] = 0.055 / 1.055; Parameters[3] = 1. / 12.92; Parameters[4] = 0.04045; // d return cmsBuildParametricGamma(1024, 4, Parameters); } static LCMSBOOL Check_sRGBGamma(LPGAMMATABLE Shape) { LPGAMMATABLE sRGB = Build_sRGBGamma(); int i; if (Shape ->nEntries != 1024) { printf("because wrong sizes (%d != 1024), ", Shape -> nEntries); return 0; } for (i=0; i < Shape -> nEntries; i++) { double nErr = Shape ->GammaTable[i] - sRGB ->GammaTable[i]; if (fabs(nErr) > 1.0) { int j; printf("because %x != %x on index %d\n", Shape ->GammaTable[i], sRGB ->GammaTable[i], i); printf("table dump follows:\n"); for (j=0; j < 10; j++) printf("%d) %X\n", j, Shape ->GammaTable[j]); printf("\nso, "); return 0; } } cmsFreeGamma(sRGB); return 1; } static int GetInfoTest(void) { cmsHPROFILE hProfile; cmsCIEXYZ WhitePoint; cmsCIEXYZTRIPLE Primaries; const char* Product; LPGAMMATABLE Shapes[3]; printf("Testing profile decoding (sRGB)"); hProfile = cmsOpenProfileFromFile("sRGB Color Space Profile.icm", "rb"); cmsTakeMediaWhitePoint(&WhitePoint, hProfile); Dot(); if (!CheckXYZ(&WhitePoint, 0.95045, 1.0, 1.08905)) { printf("White point read failed!\n"); return 0; } Dot(); cmsTakeColorants(&Primaries, hProfile); if (!CheckXYZ(&Primaries.Red, 0.43607, 0.22249, 0.01392)) { printf("Red colorant failed!\n"); return 0; } if (!CheckXYZ(&Primaries.Green, 0.38515,0.71617, 0.09708)) { printf("Green colorant failed!\n"); return 0; } if (!CheckXYZ(&Primaries.Blue, 0.14307, 0.06061, 0.71410)) { printf("Blue colorant failed!\n"); return 0; } Dot(); Product = cmsTakeProductName(hProfile); if (strcmp(Product, "IEC 61966-2.1 Default RGB colour space - sRGB") != 0) { printf("Product name mismatch!\n"); } Dot(); Shapes[0] = cmsReadICCGamma(hProfile, icSigRedTRCTag); Shapes[1] = cmsReadICCGamma(hProfile, icSigGreenTRCTag); Shapes[2] = cmsReadICCGamma(hProfile, icSigBlueTRCTag); if (!Check_sRGBGamma(Shapes[0]) || !Check_sRGBGamma(Shapes[1]) || !Check_sRGBGamma(Shapes[2])) { printf("Gamma curves mismatch!\n"); return 0; } cmsFreeGammaTriple(Shapes); Dot(); cmsCloseProfile(hProfile); printf("pass.\n"); return 1; } static int Test_sRGB(void) { cmsHPROFILE In1, In2, Out1, Out2; cmsHTRANSFORM xform1, xform2; int nMaxErr; printf("Testing sRGB built-in space"); In1 = cmsOpenProfileFromFile("sRGB Color Space Profile.icm", "rb"); Out1 = cmsCreateXYZProfile(); In2 = cmsCreate_sRGBProfile(); Out2 = cmsCreateXYZProfile(); xform1 = cmsCreateTransform(In1, TYPE_RGBA_16, Out1, TYPE_XYZA_16, 0, cmsFLAGS_NOTPRECALC); xform2 = cmsCreateTransform(In2, TYPE_RGBA_16, Out2, TYPE_XYZA_16, 0, cmsFLAGS_NOTPRECALC); nMaxErr = CompareTransforms(xform1, xform2, 31, TRUE, FALSE); cmsDeleteTransform(xform1); cmsCloseProfile(In1); cmsCloseProfile(Out1); cmsDeleteTransform(xform2); cmsCloseProfile(In2); cmsCloseProfile(Out2); return nMaxErr; } static int RealProfilesTest(void) { cmsHPROFILE In1, In2, Out1, Out2; cmsHTRANSFORM xform1, xform2; int nMaxErr; printf("Using two real profiles"); // sRGB is a simpler, public domain XYZ PCS profile // sRGBSpac comes with Win95 Platform SDK, in the public domain. // (not latest revisions) // Using LAB identity as output profile, I'm forcing an // implicit XYZ => L*a*b conversion in xform1. // xform2 is 8 bits - LUT based, and PCS is L*a*b In1 = cmsOpenProfileFromFile("sRGB Color Space Profile.icm", "rb"); Out1 = cmsCreateXYZProfile(); In2 = cmsOpenProfileFromFile("sRGBSpac.icm", "rb"); Out2 = cmsCreateXYZProfile(); // Since LUT is 8-bits width, xform1 = cmsCreateTransform(In1, TYPE_RGBA_16, Out1, TYPE_XYZA_16, 0, cmsFLAGS_NOTPRECALC|cmsFLAGS_MATRIXINPUT); xform2 = cmsCreateTransform(In2, TYPE_RGBA_16, Out2, TYPE_XYZA_16, 0, cmsFLAGS_NOTPRECALC); nMaxErr = CompareTransforms(xform1, xform2, 31, FALSE, FALSE); printf("pass\n"); cmsDeleteTransform(xform1); cmsCloseProfile(In1); cmsCloseProfile(Out1); cmsDeleteTransform(xform2); cmsCloseProfile(In2); cmsCloseProfile(Out2); return nMaxErr; } // --------------------------------------------------------- static int TestPreview(void) { cmsHPROFILE In, Out, Proof; cmsHTRANSFORM xform; int nMaxErr; printf("Testing preview"); In = cmsCreateLabProfile(NULL); Out = cmsCreateLabProfile(NULL); Proof = cmsCreateLabProfile(NULL); xform = cmsCreateProofingTransform(In, TYPE_LABA_16, Out, TYPE_LABA_16, Proof, 0, 0, cmsFLAGS_SOFTPROOFING); nMaxErr = TestFullSpectrum(xform, 31, 0x1000L); cmsDeleteTransform(xform); cmsCloseProfile(In); cmsCloseProfile(Out); cmsCloseProfile(Proof); printf("\n"); return nMaxErr; } // Check induced error on multiprofile transforms static int TestMultiprofile(void) { cmsHPROFILE hsRGB, hXYZ, hLab; cmsHTRANSFORM hXForm; cmsHPROFILE Profiles[10]; int nMaxErr; hsRGB = cmsCreate_sRGBProfile(); hLab = cmsCreateLabProfile(NULL); hXYZ = cmsCreateXYZProfile(); Profiles[0] = hsRGB; Profiles[1] = hLab; Profiles[2] = hsRGB; Profiles[3] = hsRGB; Profiles[4] = hLab; Profiles[5] = hXYZ; Profiles[6] = hsRGB; hXForm = cmsCreateMultiprofileTransform(Profiles, 7, TYPE_RGBA_16, TYPE_RGBA_16, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_HIGHRESPRECALC); printf("Testing multiprofile transforms (6 profiles)"); nMaxErr = TestFullSpectrum(hXForm, 31, 0x1000L); cmsDeleteTransform(hXForm); cmsCloseProfile(hsRGB); cmsCloseProfile(hXYZ); cmsCloseProfile(hLab); printf("\n"); return nMaxErr; } // Check linearization and other goodies static int TestLinearizationDevicelink() { LPGAMMATABLE Transfer[3]; cmsHPROFILE hLin1, hLin2; cmsHTRANSFORM hXForm; cmsHPROFILE Profiles[10]; int nMaxErr; printf("Testing linearization devicelink"); Transfer[0] = cmsBuildGamma(256, 1./2.2); Transfer[1] = cmsBuildGamma(256, 1./2.2); Transfer[2] = cmsBuildGamma(256, 1./2.2); hLin1 = cmsCreateLinearizationDeviceLink(icSigRgbData, Transfer); cmsFreeGammaTriple(Transfer); Transfer[0] = cmsBuildGamma(256, 2.2); Transfer[1] = cmsBuildGamma(256, 2.2); Transfer[2] = cmsBuildGamma(256, 2.2); hLin2 = cmsCreateLinearizationDeviceLink(icSigRgbData, Transfer); cmsFreeGammaTriple(Transfer); Profiles[0] = hLin1; Profiles[1] = hLin2; hXForm = cmsCreateMultiprofileTransform(Profiles, 2, TYPE_RGBA_16, TYPE_RGBA_16, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_HIGHRESPRECALC); if (!hXForm) { printf("Error!\n"); return 1; } nMaxErr = TestFullSpectrum(hXForm, 31, 0x1000L); cmsDeleteTransform(hXForm); cmsCloseProfile(hLin1); cmsCloseProfile(hLin2); printf("\n"); return nMaxErr; } static int TestLinearizationDevicelink2() { LPGAMMATABLE Transfer[3]; cmsHPROFILE hLin1; cmsHTRANSFORM hXForm; int nMaxErr; printf("Testing saved linearization devicelink"); Transfer[0] = cmsBuildGamma(256, 1); Transfer[1] = cmsBuildGamma(256, 1); Transfer[2] = cmsBuildGamma(256, 1); hLin1 = cmsCreateLinearizationDeviceLink(icSigRgbData, Transfer); _cmsSaveProfile(hLin1, "lin1.icc"); cmsFreeGammaTriple(Transfer); cmsCloseProfile(hLin1); hLin1 = cmsOpenProfileFromFile("lin1.icc", "r"); hXForm = cmsCreateTransform(hLin1, TYPE_RGBA_16, NULL, TYPE_RGBA_16, INTENT_ABSOLUTE_COLORIMETRIC, 0); if (!hXForm) { printf("Error!\n"); return 1; } nMaxErr = TestFullSpectrum(hXForm, 31, 1); cmsDeleteTransform(hXForm); cmsCloseProfile(hLin1); unlink("lin1.icc"); printf("\n"); return nMaxErr; } static int TestDeviceLinkGeneration() { cmsHTRANSFORM hXForm, hIdentity; cmsHPROFILE hDevLink, hsRGB; int nMaxErr; printf("Testing devicelink generation"); hsRGB = cmsOpenProfileFromFile("sRGB Color Space Profile.icm", "r"); hIdentity = cmsCreateTransform(hsRGB, TYPE_RGBA_16, hsRGB, TYPE_RGBA_16, INTENT_RELATIVE_COLORIMETRIC, 0); hDevLink = cmsTransform2DeviceLink(hIdentity, 0); _cmsSaveProfile(hDevLink, "devicelink.icm"); cmsCloseProfile(hDevLink); cmsCloseProfile(hsRGB); cmsDeleteTransform(hIdentity); hDevLink = cmsOpenProfileFromFile("devicelink.icm", "r"); hXForm = cmsCreateTransform(hDevLink, TYPE_RGBA_16, NULL, TYPE_RGBA_16, INTENT_RELATIVE_COLORIMETRIC, 0); nMaxErr = TestFullSpectrum(hXForm, 31, 0x1000L); cmsDeleteTransform(hXForm); cmsCloseProfile(hDevLink); printf("\n"); unlink("devicelink.icm"); return nMaxErr; } static int TestInkLimiting() { cmsHPROFILE hIL; cmsHTRANSFORM hXForm; BYTE In[4], Out[4]; int i, j, k, l, res; printf("Testing ink limiting "); hIL = cmsCreateInkLimitingDeviceLink(icSigCmykData, 100); hXForm = cmsCreateTransform(hIL, TYPE_CMYK_8, NULL, TYPE_CMYK_8, INTENT_RELATIVE_COLORIMETRIC, 0); if (!hXForm) { printf("Error!\n"); return 0; } for (l=0; l < 255; l += 8) { Dot(); for (k=0; k < 255; k += 8) for (j=0; j < 255; j += 8) for (i=0; i < 255; i += 8) { In[0] = (BYTE) i; In[1] = (BYTE) j; In[2] = (BYTE) k; In[3] = (BYTE) l; cmsDoTransform(hXForm, In, Out, 1); res = Out[0] + Out[1] + Out[2] + Out[3]; if (res > 0x100) { printf("Failed! (%d) \n", res); return 0; } } } cmsDeleteTransform(hXForm); cmsCloseProfile(hIL); printf(" pass.\n"); return 1; } static void CheckPlanar(void) { cmsHTRANSFORM xform; cmsHPROFILE hsRGB; int i; BYTE Out[12]; BYTE Bmp[] = { 0x00, 0x10, 0x20, 0x30, // R Plane 0x00, 0x10, 0x20, 0x30, // G Plane 0x00, 0x10, 0x20, 0x30 }; // B Plane hsRGB = cmsCreate_sRGBProfile(); xform = cmsCreateTransform(hsRGB, TYPE_RGB_8_PLANAR, hsRGB, TYPE_RGB_8_PLANAR, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC); cmsDoTransform(xform, Bmp, Out, 4); for (i=0; i < 12; i += 3) { printf("RGB=(%x, %x, %x)\n", Out[i+0], Out[i+1], Out[i+2]); } cmsDeleteTransform(xform); cmsCloseProfile(hsRGB); } #ifdef ICM_COMPARATIVE #ifndef NON_WINDOWS static void CompareWithICM_16bit(void) { HTRANSFORM hICMxform; HPROFILE hICMProfileFrom, hICMProfileTo; LOGCOLORSPACE LogColorSpace; COLOR In, Out; COLOR *InBlk, *OutBlk, *InPtr; size_t size; int r, g, b; PROFILE Profile; clock_t atime; double seconds, diff; cmsHPROFILE hlcmsProfileIn, hlcmsProfileOut; cmsHTRANSFORM hlcmsxform; printf("\n\nComparative with MS-Windows ICM (16 bits per sample):\n"); Profile.dwType = PROFILE_FILENAME; Profile.pProfileData = "sRGBSpac.icm"; Profile.cbDataSize = strlen("sRGBSpac.icm"); hICMProfileFrom = OpenColorProfile(&Profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING); Profile.pProfileData = "sRGBSpac.icm"; Profile.cbDataSize = strlen("sRGBSpac.icm"); hICMProfileTo = OpenColorProfile(&Profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING); ZeroMemory(&LogColorSpace, sizeof(LOGCOLORSPACE)); LogColorSpace.lcsSignature = LCS_SIGNATURE; LogColorSpace.lcsVersion = 0x400; LogColorSpace.lcsCSType = LCS_CALIBRATED_RGB; strcpy(LogColorSpace.lcsFilename, "sRGBSpac.icm"); hICMxform = CreateColorTransform(&LogColorSpace, hICMProfileTo, NULL, BEST_MODE); size = 256 * 256 * 256; InBlk = _cmsMalloc((size_t) size * sizeof(COLOR)); OutBlk = _cmsMalloc((size_t) size * sizeof(COLOR)); if (InBlk == NULL || OutBlk == NULL) { printf("Out of memory\n"); exit(2); } printf("Windows ICM is transforming full spectrum..."); InPtr = InBlk; for (r=0; r < 255; r++) for (g=0; g < 255; g++) for (b=0; b < 255; b++) { InPtr->rgb.red = (r << 8) | r; InPtr->rgb.green = (g << 8) | g; InPtr->rgb.blue = (b << 8) | b; InPtr++; } atime = clock(); TranslateColors( hICMxform, InBlk, size, COLOR_RGB, OutBlk, COLOR_RGB); diff = clock() - atime; seconds = (double) diff / CLOCKS_PER_SEC; printf("done. [%d tics, %g sec.]\n", (int) diff, seconds); CloseColorProfile(hICMProfileFrom); CloseColorProfile(hICMProfileTo); DeleteColorTransform(hICMxform); hlcmsProfileIn = cmsOpenProfileFromFile("sRGBSpac.icm", "r"); hlcmsProfileOut = cmsOpenProfileFromFile("sRGBSpac.icm", "r"); hlcmsxform = cmsCreateTransform(hlcmsProfileIn, TYPE_RGB_16, hlcmsProfileOut, TYPE_RGB_16, INTENT_PERCEPTUAL, 0); printf("lcms is transforming full spectrum..."); atime = clock(); cmsDoTransform(hlcmsxform, InBlk, OutBlk, size); diff = clock() - atime; seconds = (double) diff / CLOCKS_PER_SEC; printf("done. [%d tics, %g sec.]\n", (int) diff, seconds); cmsDeleteTransform(hlcmsxform); cmsCloseProfile(hlcmsProfileIn); cmsCloseProfile(hlcmsProfileOut); _cmsFree(InBlk); _cmsFree(OutBlk); } static void CompareWithICM_8bit(void) { HTRANSFORM hICMxform; HPROFILE hICMProfileFrom, hICMProfileTo; LOGCOLORSPACE LogColorSpace; RGBQUAD In, Out; int r, g, b; PROFILE Profile; clock_t atime; double seconds, diff; cmsHPROFILE hlcmsProfileIn, hlcmsProfileOut; cmsHTRANSFORM hlcmsxform; printf("\n\nComparative with MS-Windows ICM (8 bits per sample):\n"); Profile.dwType = PROFILE_FILENAME; Profile.pProfileData = "sRGBSpac.icm"; Profile.cbDataSize = strlen("sRGBSpac.icm"); hICMProfileFrom = OpenColorProfile(&Profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING); Profile.pProfileData = "sRGBSpac.icm"; Profile.cbDataSize = strlen("sRGBSpac.icm"); hICMProfileTo = OpenColorProfile(&Profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING); ZeroMemory(&LogColorSpace, sizeof(LOGCOLORSPACE)); LogColorSpace.lcsSignature = LCS_SIGNATURE; LogColorSpace.lcsVersion = 0x400; LogColorSpace.lcsCSType = LCS_CALIBRATED_RGB; strcpy(LogColorSpace.lcsFilename, "sRGBSpac.icm"); hICMxform = CreateColorTransform(&LogColorSpace, hICMProfileTo, NULL, BEST_MODE); printf("Windows ICM is transforming full spectrum..."); atime = clock(); for (r=0; r < 255; r++) for (g=0; g < 255; g++) for (b=0; b < 255; b++) { In.rgbRed = r; In.rgbGreen = g; In.rgbBlue = b; if (!TranslateBitmapBits(hICMxform, &In, BM_RGBTRIPLETS, 1, 1, 0, &Out, BM_RGBTRIPLETS, 0, NULL, 0)) exit(2); } diff = clock() - atime; seconds = (double) diff / CLOCKS_PER_SEC; printf("done. [%d tics, %g sec.]\n", (int) diff, seconds); CloseColorProfile(hICMProfileFrom); CloseColorProfile(hICMProfileTo); DeleteColorTransform(hICMxform); hlcmsProfileIn = cmsOpenProfileFromFile("sRGBSpac.icm", "r"); hlcmsProfileOut = cmsOpenProfileFromFile("sRGBSpac.icm", "r"); hlcmsxform = cmsCreateTransform(hlcmsProfileIn, TYPE_BGRA_8, hlcmsProfileOut, TYPE_BGRA_8, INTENT_PERCEPTUAL, 0); printf("lcms is transforming full spectrum..."); atime = clock(); for (r=0; r < 255; r++) for (g=0; g < 255; g++) for (b=0; b < 255; b++) { In.rgbRed = r; In.rgbGreen = g; In.rgbBlue = b; cmsDoTransform(hlcmsxform, &In, &Out, 1); } diff = clock() - atime; seconds = (double) diff / CLOCKS_PER_SEC; printf("done. [%d tics, %g sec.]\n", (int) diff, seconds); cmsDeleteTransform(hlcmsxform); cmsCloseProfile(hlcmsProfileIn); cmsCloseProfile(hlcmsProfileOut); } #endif #endif #ifdef CHECK_SPEED static void SpeedTest(void) { int r, g, b, j; clock_t atime; double seconds, diff; cmsHPROFILE hlcmsProfileIn, hlcmsProfileOut; cmsHTRANSFORM hlcmsxform; Scanline_rgb0 *In; size_t Mb; hlcmsProfileIn = cmsOpenProfileFromFile("sRGB Color Space Profile.icm", "r"); hlcmsProfileOut = cmsOpenProfileFromFile("sRGBSpac.icm", "r"); hlcmsxform = cmsCreateTransform(hlcmsProfileIn, TYPE_RGB_16, hlcmsProfileOut, TYPE_RGB_16, INTENT_PERCEPTUAL, cmsFLAGS_NOTCACHE); Mb = 256*256*256*sizeof(Scanline_rgb0); In = (Scanline_rgb0*) _cmsMalloc(Mb); j = 0; for (r=0; r < 256; r++) for (g=0; g < 256; g++) for (b=0; b < 256; b++) { In[j].r = (WORD) ((r << 8) | r); In[j].g = (WORD) ((g << 8) | g); In[j].b = (WORD) ((b << 8) | b); j++; } printf("lcms is transforming full spectrum..."); atime = clock(); cmsDoTransform(hlcmsxform, In, In, 256*256*256); diff = clock() - atime; seconds = (double) diff / CLOCKS_PER_SEC; _cmsFree(In); printf("done.\n[%d tics, %g sec, %g Mpixel/sec.]\n", (int) diff, seconds, Mb / (1024*1024*seconds*3*2) ); cmsDeleteTransform(hlcmsxform); cmsCloseProfile(hlcmsProfileIn); cmsCloseProfile(hlcmsProfileOut); } static void SpeedTest2(void) { int r, g, b, j; clock_t atime; double seconds, diff; cmsHPROFILE hlcmsProfileIn, hlcmsProfileOut; cmsHTRANSFORM hlcmsxform; Scanline_rgb8 *In; size_t Mb; hlcmsProfileIn = cmsOpenProfileFromFile("sRGB Color Space Profile.icm", "r"); hlcmsProfileOut = cmsOpenProfileFromFile("sRGBSpac.icm", "r"); hlcmsxform = cmsCreateTransform(hlcmsProfileIn, TYPE_RGB_8, hlcmsProfileOut, TYPE_RGB_8, INTENT_PERCEPTUAL, cmsFLAGS_NOTCACHE); Mb = 256*256*256*sizeof(Scanline_rgb8); In = (Scanline_rgb8*) _cmsMalloc(Mb); j = 0; for (r=0; r < 256; r++) for (g=0; g < 256; g++) for (b=0; b < 256; b++) { In[j].r = (BYTE) r; In[j].g = (BYTE) g; In[j].b = (BYTE) b; j++; } printf("lcms is transforming full spectrum..."); atime = clock(); cmsDoTransform(hlcmsxform, In, In, 256*256*256); diff = clock() - atime; seconds = (double) diff / CLOCKS_PER_SEC; _cmsFree(In); printf("done.\n[%d tics, %g sec, %g Mpixels/sec.]\n", (int) diff, seconds, Mb / (1024*1024*seconds*3) ); cmsDeleteTransform(hlcmsxform); cmsCloseProfile(hlcmsProfileIn); cmsCloseProfile(hlcmsProfileOut); } #endif static int TestSaveToMem(void) { void *memPtr=0; size_t bytesNeeded=0; int rc = FALSE; cmsHPROFILE hProfile = cmsCreate_sRGBProfile(); printf("Testing save to memory: "); // pass 1 - compute length if (!_cmsSaveProfileToMem(hProfile, memPtr, &bytesNeeded)) { printf("Failed!\n"); return FALSE; } // pass 2 - generate profile if(!bytesNeeded) { printf("Failed!\n"); return FALSE; } memPtr = _cmsMalloc(bytesNeeded); if (_cmsSaveProfileToMem(hProfile, memPtr, &bytesNeeded)) { cmsHPROFILE newProfile = cmsOpenProfileFromMem(memPtr, (DWORD) bytesNeeded); const char* s = cmsTakeProductName(newProfile); if (strncmp(s, "sRGB", 4) == 0) rc = TRUE; cmsCloseProfile(newProfile); _cmsFree(memPtr); } cmsCloseProfile(hProfile); printf (rc ? "pass.\n" : "failed!\n"); return rc; } static int TestNamedColor(void) { LPcmsNAMEDCOLORLIST nc2; cmsHPROFILE hProfile, hDevicelink, hsRGB, hLab; cmsHTRANSFORM xform, rgb2lab; int i; printf("Testing Named color profiles: "); hsRGB = cmsCreate_sRGBProfile(); hLab = cmsCreateLabProfile(NULL); rgb2lab = cmsCreateTransform(hsRGB, TYPE_RGB_16, hLab, TYPE_Lab_16, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC); nc2 = cmsAllocNamedColorList(64); nc2 ->ColorantCount = 3; strcpy(nc2 ->Prefix, "prefix"); strcpy(nc2 ->Suffix, "suffix"); for (i=0; i < 64; i++) { WORD vv = RGB_8_TO_16((i*4)); nc2 ->List[i].DeviceColorant[0] = vv; nc2 ->List[i].DeviceColorant[1] = vv; nc2 ->List[i].DeviceColorant[2] = vv; cmsDoTransform(rgb2lab, nc2 ->List[i].DeviceColorant, nc2 ->List[i].PCS, 1); sprintf(nc2 ->List[i].Name, "Color #%d", i); } hProfile = cmsOpenProfileFromFile("named.icc", "w"); cmsSetDeviceClass(hProfile, icSigNamedColorClass); cmsSetPCS(hProfile, icSigLabData); cmsSetColorSpace(hProfile, icSigRgbData); cmsAddTag(hProfile, icSigNamedColor2Tag, (void*) nc2); cmsAddTag(hProfile, icSigMediaWhitePointTag, cmsD50_XYZ()); cmsCloseProfile(hProfile); cmsFreeNamedColorList(nc2); hProfile = cmsOpenProfileFromFile("named.icc", "r"); xform = cmsCreateTransform(hProfile, TYPE_NAMED_COLOR_INDEX, NULL, TYPE_RGB_16, INTENT_PERCEPTUAL, 0); for (i=0; i < 64; i++) { WORD index; WORD Color[3]; index = (WORD) i; cmsDoTransform(xform, &index, Color, 1); if (Color[0] != RGB_8_TO_16((i*4)) || Color[1] != RGB_8_TO_16((i*4)) || Color[2] != RGB_8_TO_16((i*4))) { printf(" fail on spot color #%d\n", i); return 0; } } cmsDeleteTransform(xform); cmsCloseProfile(hProfile); cmsDeleteTransform(rgb2lab); cmsCloseProfile(hLab); hProfile = cmsOpenProfileFromFile("named.icc", "r"); xform = cmsCreateTransform(hProfile, TYPE_NAMED_COLOR_INDEX, hsRGB, TYPE_RGB_16, INTENT_PERCEPTUAL, 0); hDevicelink = cmsTransform2DeviceLink(xform, 0); _cmsSaveProfile(hDevicelink, "named2.icc"); cmsCloseProfile(hDevicelink); cmsDeleteTransform(xform); cmsCloseProfile(hProfile); cmsCloseProfile(hsRGB); unlink("named.icc"); unlink("named2.icc"); printf(" pass.\n"); return 1; } static int TestColorantTableTag() { LPcmsNAMEDCOLORLIST nc2; cmsHPROFILE hProfile = cmsOpenProfileFromFile("colTable.icc", "w"); nc2 = cmsAllocNamedColorList(3); strcpy(nc2 ->List[0].Name, "Red"); strcpy(nc2 ->List[1].Name, "Green"); strcpy(nc2 ->List[2].Name, "Blue"); cmsSetDeviceClass(hProfile, icSigOutputClass); cmsSetPCS(hProfile, icSigLabData); cmsSetColorSpace(hProfile, icSigRgbData); cmsAddTag(hProfile, icSigColorantTableTag, (void*) nc2); cmsAddTag(hProfile, icSigMediaWhitePointTag, cmsD50_XYZ()); cmsCloseProfile(hProfile); cmsFreeNamedColorList(nc2); hProfile = cmsOpenProfileFromFile("colTable.icc", "r"); nc2 = cmsReadColorantTable(hProfile, icSigColorantTableTag); cmsFreeNamedColorList(nc2); cmsCloseProfile(hProfile); unlink("colTable.icc"); return 1; } // New to 1.13 -- CGATS/IT8.7 #define NPOINTS_IT8 10 // (17*17*17*17) static int TestIT8(void) { LCMSHANDLE it8; int i; printf("Testing CGATS parser: "); it8 = cmsIT8Alloc(); cmsIT8SetSheetType(it8, "LCMS/TESTING"); cmsIT8SetPropertyStr(it8, "ORIGINATOR", "1 2 3 4"); cmsIT8SetPropertyUncooked(it8, "DESCRIPTOR", "1234"); cmsIT8SetPropertyStr(it8, "MANUFACTURER", "3"); cmsIT8SetPropertyDbl(it8, "CREATED", 4); cmsIT8SetPropertyDbl(it8, "SERIAL", 5); cmsIT8SetPropertyHex(it8, "MATERIAL", 0x123); cmsIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", NPOINTS_IT8); cmsIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", 4); cmsIT8SetDataFormat(it8, 0, "SAMPLE_ID"); cmsIT8SetDataFormat(it8, 1, "RGB_R"); cmsIT8SetDataFormat(it8, 2, "RGB_G"); cmsIT8SetDataFormat(it8, 3, "RGB_B"); for (i=0; i < NPOINTS_IT8; i++) { char Patch[20]; sprintf(Patch, "P%d", i); cmsIT8SetDataRowCol(it8, i, 0, Patch); cmsIT8SetDataRowColDbl(it8, i, 1, i); cmsIT8SetDataRowColDbl(it8, i, 2, i); cmsIT8SetDataRowColDbl(it8, i, 3, i); } cmsIT8SaveToFile(it8, "TEST.IT8"); cmsIT8Free(it8); it8 = cmsIT8LoadFromFile("TEST.IT8"); cmsIT8SaveToFile(it8, "TEST.IT8"); cmsIT8Free(it8); it8 = cmsIT8LoadFromFile("TEST.IT8"); if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 1234) { printf("fail!\n"); return 0; } cmsIT8SetPropertyDbl(it8, "DESCRIPTOR", 5678); if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 5678) { printf("fail!\n"); return 0; } if (cmsIT8GetDataDbl(it8, "P3", "RGB_G") != 3) { printf("fail!\n"); return 0; } cmsIT8Free(it8); unlink("TEST.IT8"); printf("pass.\n"); return 1; } // Create CSA/CRD static void GenerateCSA(const char* cInProf) { cmsHPROFILE hProfile; DWORD n; char* Buffer; if (cInProf == NULL) hProfile = cmsCreateLabProfile(NULL); else hProfile = cmsOpenProfileFromFile(cInProf, "r"); n = cmsGetPostScriptCSA(hProfile, 0, NULL, 0); if (n == 0) return; Buffer = (char*) _cmsMalloc(n + 1); cmsGetPostScriptCSA(hProfile, 0, Buffer, n); Buffer[n] = 0; _cmsFree(Buffer); cmsCloseProfile(hProfile); } static void GenerateCRD(const char* cOutProf) { cmsHPROFILE hProfile; DWORD n; char* Buffer; DWORD dwFlags = 0; if (cOutProf == NULL) hProfile = cmsCreateLabProfile(NULL); else hProfile = cmsOpenProfileFromFile(cOutProf, "r"); n = cmsGetPostScriptCRDEx(hProfile, 0, dwFlags, NULL, 0); if (n == 0) return; Buffer = (char*) _cmsMalloc(n + 1); cmsGetPostScriptCRDEx(hProfile, 0, dwFlags, Buffer, n); Buffer[n] = 0; _cmsFree(Buffer); cmsCloseProfile(hProfile); } static int TestPostScript() { GenerateCSA("sRGB Color Space Profile.icm"); GenerateCRD("sRGB Color Space Profile.icm"); GenerateCSA(NULL); GenerateCRD(NULL); return 1; } static void TestLabFloat() { #define TYPE_LabA_DBL (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0)|EXTRA_SH(1)|DOSWAP_SH(1)) struct { double L, a, b; double A; } a; cmsCIELab b; cmsHPROFILE hLab = cmsCreateLabProfile(NULL); cmsHTRANSFORM xform = cmsCreateTransform(hLab, TYPE_LabA_DBL, hLab, TYPE_Lab_DBL, 0, 0); a.L = 100; a.a = 0; a.b= 0; cmsDoTransform(xform, &a, &b, 1); cmsDeleteTransform(xform); cmsCloseProfile(hLab); } int main(int argc, char *argv[]) { int lExhaustive = 0; // #include "crtdbg.h" // _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); printf("little cms testbed. Ver %1.2f [build %s %s]\n\n", LCMS_VERSION / 100., __DATE__, __TIME__); #ifndef LCMS_DLL if (!CheckEndianess()) return 1; if (!CheckSwab()) return 1; if (!CheckQuickFloor()) return 1; TestFixedPoint(); if (!TestFixedScaling()) return 1; if (!TestJointCurves()) return 1; if (!TestReversingOfCurves()) return 1; if (!TestLinearInterpolation(lExhaustive)) return 1; if (!TestReverseLinearInterpolation()) return 1; if (!Test3D()) return 1; if (!TestMatrixCreation()) return 1; if (!GetInfoTest()) return 1; #endif if (!Test_sRGB()) return 1; if (!RealProfilesTest()) return 1; if (!TestInducedError(TYPE_LABA_16)) return 1; if (!TestPreview()) return 1; if (!TestMultiprofile()) return 1; if (!TestLinearizationDevicelink()) return 1; if (!TestDeviceLinkGeneration()) return 1; if (!TestLinearizationDevicelink2()) return 1; if (!TestInkLimiting()) return 1; if (!TestSaveToMem()) return 1; if (!TestNamedColor()) return 1; if (!TestIT8()) return 1; if (!TestPostScript()) return 1; if (!TestColorantTableTag()) return 1; #ifdef ICM_COMPARATIVE #ifndef NON_WINDOWS CompareWithICM_8bit(); CompareWithICM_16bit(); #endif #endif #ifdef CHECK_SPEED SpeedTest(); SpeedTest2(); #endif printf("\nSuccess.\n"); return 0; }