1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2010 Marti Maria Saguer
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //---------------------------------------------------------------------------------
28 # define _CRT_SECURE_NO_WARNINGS 1
31 #include "lcms2_internal.h"
33 // On Visual Studio, use debug CRT
39 // A single check. Returns 1 if success, 0 if failed
40 typedef cmsInt32Number (*TestFn)(void);
42 // A parametric Tone curve test function
43 typedef cmsFloat32Number (* dblfnptr)(cmsFloat32Number x, const cmsFloat64Number Params[]);
45 // Some globals to keep track of error
46 #define TEXT_ERROR_BUFFER_SIZE 4096
48 static char ReasonToFailBuffer[TEXT_ERROR_BUFFER_SIZE];
49 static char SubTestBuffer[TEXT_ERROR_BUFFER_SIZE];
50 static cmsInt32Number TotalTests = 0, TotalFail = 0;
51 static cmsBool TrappedError;
52 static cmsInt32Number SimultaneousErrors;
55 #define cmsmin(a, b) (((a) < (b)) ? (a) : (b))
57 // Die, a fatal unexpected error is detected!
59 void Die(const char* Reason)
61 printf("\n\nArrrgggg!!: %s!\n\n", Reason);
66 // Memory management replacement -----------------------------------------------------------------------------
69 // This is just a simple plug-in for malloc, free and realloc to keep track of memory allocated,
70 // maximum requested as a single block and maximum allocated at a given time. Results are printed at the end
71 static cmsUInt32Number SingleHit, MaxAllocated=0, TotalMemory=0;
73 // I'm hidding the size before the block. This is a well-known technique and probably the blocks coming from
74 // malloc are built in a way similar to that, but I do on my own to be portable.
76 cmsUInt32Number KeepSize;
77 cmsContext WhoAllocated;
80 cmsUInt64Number HiSparc;
82 // '_cmsMemoryBlock' block is prepended by the
83 // allocator for any requested size. Thus, union holds
84 // "widest" type to guarantee proper '_cmsMemoryBlock'
85 // alignment for any requested size.
92 #define SIZE_OF_MEM_HEADER (sizeof(_cmsMemoryBlock))
94 // This is a fake thread descriptor used to check thread integrity.
95 // Basically it returns a different threadID each time it is called.
96 // Then the memory management replacement functions does check if each
97 // free() is being called with same ContextID used on malloc()
99 cmsContext DbgThread(void)
101 static cmsUInt32Number n = 1;
103 return (cmsContext) n++;
106 // The allocate routine
108 void* DebugMalloc(cmsContext ContextID, cmsUInt32Number size)
110 _cmsMemoryBlock* blk;
113 Die("malloc requested with zero bytes");
118 if (TotalMemory > MaxAllocated)
119 MaxAllocated = TotalMemory;
121 if (size > SingleHit)
124 blk = (_cmsMemoryBlock*) malloc(size + SIZE_OF_MEM_HEADER);
125 if (blk == NULL) return NULL;
127 blk ->KeepSize = size;
128 blk ->WhoAllocated = ContextID;
130 return (void*) ((cmsUInt8Number*) blk + SIZE_OF_MEM_HEADER);
135 void DebugFree(cmsContext ContextID, void *Ptr)
137 _cmsMemoryBlock* blk;
140 Die("NULL free (which is a no-op in C, but may be an clue of something going wrong)");
143 blk = (_cmsMemoryBlock*) (((cmsUInt8Number*) Ptr) - SIZE_OF_MEM_HEADER);
144 TotalMemory -= blk ->KeepSize;
146 if (blk ->WhoAllocated != ContextID) {
147 Die("Trying to free memory allocated by a different thread");
153 // Reallocate, just a malloc, a copy and a free in this case.
155 void * DebugRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize)
157 _cmsMemoryBlock* blk;
159 cmsUInt32Number max_sz;
161 NewPtr = DebugMalloc(ContextID, NewSize);
162 if (Ptr == NULL) return NewPtr;
164 blk = (_cmsMemoryBlock*) (((cmsUInt8Number*) Ptr) - SIZE_OF_MEM_HEADER);
165 max_sz = blk -> KeepSize > NewSize ? NewSize : blk ->KeepSize;
166 memmove(NewPtr, Ptr, max_sz);
167 DebugFree(ContextID, Ptr);
172 // Let's know the totals
174 void DebugMemPrintTotals(void)
176 printf("[Memory statistics]\n");
177 printf("Allocated = %d MaxAlloc = %d Single block hit = %d\n", TotalMemory, MaxAllocated, SingleHit);
180 // Here we go with the plug-in declaration
181 static cmsPluginMemHandler DebugMemHandler = {{ cmsPluginMagicNumber, 2000, cmsPluginMemHandlerSig, NULL },
182 DebugMalloc, DebugFree, DebugRealloc, NULL, NULL, NULL };
184 // Utils -------------------------------------------------------------------------------------
187 void FatalErrorQuit(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
192 // Print a dot for gauging
196 fprintf(stdout, "."); fflush(stdout);
199 // Keep track of the reason to fail
201 void Fail(const char* frm, ...)
205 vsprintf(ReasonToFailBuffer, frm, args);
209 // Keep track of subtest
211 void SubTest(const char* frm, ...)
217 vsprintf(SubTestBuffer, frm, args);
224 const char* MemStr(cmsUInt32Number size)
226 static char Buffer[1024];
228 if (size > 1024*1024) {
229 sprintf(Buffer, "%g Mb", (cmsFloat64Number) size / (1024.0*1024.0));
233 sprintf(Buffer, "%g Kb", (cmsFloat64Number) size / 1024.0);
236 sprintf(Buffer, "%g bytes", (cmsFloat64Number) size);
242 // The check framework
244 void Check(const char* Title, TestFn Fn)
246 printf("Checking %s ...", Title);
249 ReasonToFailBuffer[0] = 0;
250 SubTestBuffer[0] = 0;
251 TrappedError = FALSE;
252 SimultaneousErrors = 0;
255 if (Fn() && !TrappedError) {
257 // It is a good place to check memory
259 printf("Ok, but %s are left!\n", MemStr(TotalMemory));
266 if (SubTestBuffer[0])
267 printf("%s: [%s]\n\t%s\n", Title, SubTestBuffer, ReasonToFailBuffer);
269 printf("%s:\n\t%s\n", Title, ReasonToFailBuffer);
271 if (SimultaneousErrors > 1)
272 printf("\tMore than one (%d) errors were reported\n", SimultaneousErrors);
279 // Dump a tone curve, for easy diagnostic
280 void DumpToneCurve(cmsToneCurve* gamma, const char* FileName)
285 hIT8 = cmsIT8Alloc(gamma ->InterpParams->ContextID);
287 cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_FIELDS", 2);
288 cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_SETS", gamma ->nEntries);
290 cmsIT8SetDataFormat(hIT8, 0, "SAMPLE_ID");
291 cmsIT8SetDataFormat(hIT8, 1, "VALUE");
293 for (i=0; i < gamma ->nEntries; i++) {
296 sprintf(Val, "%d", i);
297 cmsIT8SetDataRowCol(hIT8, i, 0, Val);
298 sprintf(Val, "0x%x", gamma ->Table16[i]);
299 cmsIT8SetDataRowCol(hIT8, i, 1, Val);
302 cmsIT8SaveToFile(hIT8, FileName);
306 // -------------------------------------------------------------------------------------------------
309 // Used to perform several checks.
310 // The space used is a clone of a well-known commercial
311 // color space which I will name "Above RGB"
313 cmsHPROFILE Create_AboveRGB(void)
315 cmsToneCurve* Curve[3];
316 cmsHPROFILE hProfile;
318 cmsCIExyYTRIPLE Primaries = {{0.64, 0.33, 1 },
322 Curve[0] = Curve[1] = Curve[2] = cmsBuildGamma(DbgThread(), 2.19921875);
324 cmsWhitePointFromTemp(&D65, 6504);
325 hProfile = cmsCreateRGBProfileTHR(DbgThread(), &D65, &Primaries, Curve);
326 cmsFreeToneCurve(Curve[0]);
331 // A gamma-2.2 gray space
333 cmsHPROFILE Create_Gray22(void)
335 cmsHPROFILE hProfile;
336 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 2.2);
337 if (Curve == NULL) return NULL;
339 hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
340 cmsFreeToneCurve(Curve);
347 cmsHPROFILE Create_GrayLab(void)
349 cmsHPROFILE hProfile;
350 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 1.0);
351 if (Curve == NULL) return NULL;
353 hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
354 cmsFreeToneCurve(Curve);
356 cmsSetPCS(hProfile, cmsSigLabData);
360 // A CMYK devicelink that adds gamma 3.0 to each channel
362 cmsHPROFILE Create_CMYK_DeviceLink(void)
364 cmsHPROFILE hProfile;
365 cmsToneCurve* Tab[4];
366 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 3.0);
367 if (Curve == NULL) return NULL;
374 hProfile = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigCmykData, Tab);
375 if (hProfile == NULL) return NULL;
377 cmsFreeToneCurve(Curve);
383 // Create a fake CMYK profile, without any other requeriment that being coarse CMYK.
384 // DONT USE THIS PROFILE FOR ANYTHING, IT IS USELESS BUT FOR TESTING PURPOSES.
387 cmsHTRANSFORM hLab2sRGB;
388 cmsHTRANSFORM sRGB2Lab;
389 cmsHTRANSFORM hIlimit;
394 cmsFloat64Number Clip(cmsFloat64Number v)
403 cmsInt32Number ForwardSampler(register const cmsUInt16Number In[], cmsUInt16Number Out[], void* Cargo)
405 FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
406 cmsFloat64Number rgb[3], cmyk[4];
407 cmsFloat64Number c, m, y, k;
409 cmsDoTransform(p ->hLab2sRGB, In, rgb, 1);
415 k = (c < m ? cmsmin(c, y) : cmsmin(m, y));
417 // NONSENSE WARNING!: I'm doing this just because this is a test
418 // profile that may have ink limit up to 400%. There is no UCR here
419 // so the profile is basically useless for anything but testing.
426 cmsDoTransform(p ->hIlimit, cmyk, Out, 1);
433 cmsInt32Number ReverseSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
435 FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
436 cmsFloat64Number c, m, y, k, rgb[3];
445 rgb[0] = Clip(1 - c);
446 rgb[1] = Clip(1 - m);
447 rgb[2] = Clip(1 - y);
452 rgb[0] = rgb[1] = rgb[2] = 0;
456 rgb[0] = Clip((1 - c) * (1 - k));
457 rgb[1] = Clip((1 - m) * (1 - k));
458 rgb[2] = Clip((1 - y) * (1 - k));
461 cmsDoTransform(p ->sRGB2Lab, rgb, Out, 1);
468 cmsHPROFILE CreateFakeCMYK(cmsFloat64Number InkLimit, cmsBool lUseAboveRGB)
471 cmsPipeline* AToB0, *BToA0;
473 cmsContext ContextID;
475 cmsHPROFILE hLab, hsRGB, hLimit;
476 cmsUInt32Number cmykfrm;
480 hsRGB = Create_AboveRGB();
482 hsRGB = cmsCreate_sRGBProfile();
484 hLab = cmsCreateLab4Profile(NULL);
485 hLimit = cmsCreateInkLimitingDeviceLink(cmsSigCmykData, InkLimit);
487 cmykfrm = FLOAT_SH(1) | BYTES_SH(0)|CHANNELS_SH(4);
488 p.hLab2sRGB = cmsCreateTransform(hLab, TYPE_Lab_16, hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
489 p.sRGB2Lab = cmsCreateTransform(hsRGB, TYPE_RGB_DBL, hLab, TYPE_Lab_16, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
490 p.hIlimit = cmsCreateTransform(hLimit, cmykfrm, NULL, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
492 cmsCloseProfile(hLab); cmsCloseProfile(hsRGB); cmsCloseProfile(hLimit);
494 ContextID = DbgThread();
495 hICC = cmsCreateProfilePlaceholder(ContextID);
496 if (!hICC) return NULL;
498 cmsSetProfileVersion(hICC, 4.3);
500 cmsSetDeviceClass(hICC, cmsSigOutputClass);
501 cmsSetColorSpace(hICC, cmsSigCmykData);
502 cmsSetPCS(hICC, cmsSigLabData);
504 BToA0 = cmsPipelineAlloc(ContextID, 3, 4);
505 if (BToA0 == NULL) return 0;
506 CLUT = cmsStageAllocCLut16bit(ContextID, 17, 3, 4, NULL);
507 if (CLUT == NULL) return 0;
508 if (!cmsStageSampleCLut16bit(CLUT, ForwardSampler, &p, 0)) return 0;
510 cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3));
511 cmsPipelineInsertStage(BToA0, cmsAT_END, CLUT);
512 cmsPipelineInsertStage(BToA0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 4));
514 if (!cmsWriteTag(hICC, cmsSigBToA0Tag, (void*) BToA0)) return 0;
515 cmsPipelineFree(BToA0);
517 AToB0 = cmsPipelineAlloc(ContextID, 4, 3);
518 if (AToB0 == NULL) return 0;
519 CLUT = cmsStageAllocCLut16bit(ContextID, 17, 4, 3, NULL);
520 if (CLUT == NULL) return 0;
521 if (!cmsStageSampleCLut16bit(CLUT, ReverseSampler, &p, 0)) return 0;
523 cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 4));
524 cmsPipelineInsertStage(AToB0, cmsAT_END, CLUT);
525 cmsPipelineInsertStage(AToB0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 3));
527 if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) AToB0)) return 0;
528 cmsPipelineFree(AToB0);
530 cmsDeleteTransform(p.hLab2sRGB);
531 cmsDeleteTransform(p.sRGB2Lab);
532 cmsDeleteTransform(p.hIlimit);
534 cmsLinkTag(hICC, cmsSigAToB1Tag, cmsSigAToB0Tag);
535 cmsLinkTag(hICC, cmsSigAToB2Tag, cmsSigAToB0Tag);
536 cmsLinkTag(hICC, cmsSigBToA1Tag, cmsSigBToA0Tag);
537 cmsLinkTag(hICC, cmsSigBToA2Tag, cmsSigBToA0Tag);
543 // Does create several profiles for latter use------------------------------------------------------------------------------------------------
546 cmsInt32Number OneVirtual(cmsHPROFILE h, const char* SubTestTxt, const char* FileName)
549 if (h == NULL) return 0;
551 if (!cmsSaveProfileToFile(h, FileName)) return 0;
554 h = cmsOpenProfileFromFile(FileName, "r");
555 if (h == NULL) return 0;
566 // This test checks the ability of lcms2 to save its built-ins as valid profiles.
567 // It does not check the functionality of such profiles
569 cmsInt32Number CreateTestProfiles(void)
573 h = cmsCreate_sRGBProfileTHR(DbgThread());
574 if (!OneVirtual(h, "sRGB profile", "sRGBlcms2.icc")) return 0;
578 h = Create_AboveRGB();
579 if (!OneVirtual(h, "aRGB profile", "aRGBlcms2.icc")) return 0;
584 if (!OneVirtual(h, "Gray profile", "graylcms2.icc")) return 0;
588 h = Create_GrayLab();
589 if (!OneVirtual(h, "Gray Lab profile", "glablcms2.icc")) return 0;
593 h = Create_CMYK_DeviceLink();
594 if (!OneVirtual(h, "Linearization profile", "linlcms2.icc")) return 0;
597 h = cmsCreateInkLimitingDeviceLinkTHR(DbgThread(), cmsSigCmykData, 150);
598 if (h == NULL) return 0;
599 if (!OneVirtual(h, "Ink-limiting profile", "limitlcms2.icc")) return 0;
603 h = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
604 if (!OneVirtual(h, "Lab 2 identity profile", "labv2lcms2.icc")) return 0;
608 h = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
609 if (!OneVirtual(h, "Lab 4 identity profile", "labv4lcms2.icc")) return 0;
613 h = cmsCreateXYZProfileTHR(DbgThread());
614 if (!OneVirtual(h, "XYZ identity profile", "xyzlcms2.icc")) return 0;
618 h = cmsCreateNULLProfileTHR(DbgThread());
619 if (!OneVirtual(h, "NULL profile", "nullcms2.icc")) return 0;
623 h = cmsCreateBCHSWabstractProfileTHR(DbgThread(), 17, 0, 0, 0, 0, 5000, 6000);
624 if (!OneVirtual(h, "BCHS profile", "bchslcms2.icc")) return 0;
628 h = CreateFakeCMYK(300, FALSE);
629 if (!OneVirtual(h, "Fake CMYK profile", "lcms2cmyk.icc")) return 0;
635 void RemoveTestProfiles(void)
637 remove("sRGBlcms2.icc");
638 remove("aRGBlcms2.icc");
639 remove("graylcms2.icc");
640 remove("linlcms2.icc");
641 remove("limitlcms2.icc");
642 remove("labv2lcms2.icc");
643 remove("labv4lcms2.icc");
644 remove("xyzlcms2.icc");
645 remove("nullcms2.icc");
646 remove("bchslcms2.icc");
647 remove("lcms2cmyk.icc");
648 remove("glablcms2.icc");
651 // -------------------------------------------------------------------------------------------------
653 // Check the size of basic types. If this test fails, nothing is going to work anyway
655 cmsInt32Number CheckBaseTypes(void)
657 if (sizeof(cmsUInt8Number) != 1) return 0;
658 if (sizeof(cmsInt8Number) != 1) return 0;
659 if (sizeof(cmsUInt16Number) != 2) return 0;
660 if (sizeof(cmsInt16Number) != 2) return 0;
661 if (sizeof(cmsUInt32Number) != 4) return 0;
662 if (sizeof(cmsInt32Number) != 4) return 0;
663 if (sizeof(cmsUInt64Number) != 8) return 0;
664 if (sizeof(cmsInt64Number) != 8) return 0;
665 if (sizeof(cmsFloat32Number) != 4) return 0;
666 if (sizeof(cmsFloat64Number) != 8) return 0;
667 if (sizeof(cmsSignature) != 4) return 0;
668 if (sizeof(cmsU8Fixed8Number) != 2) return 0;
669 if (sizeof(cmsS15Fixed16Number) != 4) return 0;
670 if (sizeof(cmsU16Fixed16Number) != 4) return 0;
675 // -------------------------------------------------------------------------------------------------
678 // Are we little or big endian? From Harbison&Steele.
680 cmsInt32Number CheckEndianess(void)
682 cmsInt32Number BigEndian, IsOk;
685 char c[sizeof (long)];
689 BigEndian = (u.c[sizeof (long) - 1] == 1);
691 #ifdef CMS_USE_BIG_ENDIAN
698 Fail("\nOOOPPSS! You have CMS_USE_BIG_ENDIAN toggle misconfigured!\n\n"
699 "Please, edit lcms2.h and %s the CMS_USE_BIG_ENDIAN toggle.\n", BigEndian? "uncomment" : "comment");
708 cmsInt32Number CheckQuickFloor(void)
710 if ((_cmsQuickFloor(1.234) != 1) ||
711 (_cmsQuickFloor(32767.234) != 32767) ||
712 (_cmsQuickFloor(-1.234) != -2) ||
713 (_cmsQuickFloor(-32767.1) != -32768)) {
715 Fail("\nOOOPPSS! _cmsQuickFloor() does not work as expected in your machine!\n\n"
716 "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
724 // Quick floor restricted to word
726 cmsInt32Number CheckQuickFloorWord(void)
730 for (i=0; i < 65535; i++) {
732 if (_cmsQuickFloorWord((cmsFloat64Number) i + 0.1234) != i) {
734 Fail("\nOOOPPSS! _cmsQuickFloorWord() does not work as expected in your machine!\n\n"
735 "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
743 // -------------------------------------------------------------------------------------------------
747 // On 15.16 fixed point, this is the maximum we can obtain. Remember ICC profiles have storage limits on this number
748 #define FIXED_PRECISION_15_16 (1.0 / 65535.0)
750 // On 8.8 fixed point, that is the max we can obtain.
751 #define FIXED_PRECISION_8_8 (1.0 / 255.0)
753 // On cmsFloat32Number type, this is the precision we expect
754 #define FLOAT_PRECISSION (0.00001)
756 static cmsFloat64Number MaxErr;
757 static cmsFloat64Number AllowedErr = FIXED_PRECISION_15_16;
760 cmsBool IsGoodVal(const char *title, cmsFloat64Number in, cmsFloat64Number out, cmsFloat64Number max)
762 cmsFloat64Number Err = fabs(in - out);
764 if (Err > MaxErr) MaxErr = Err;
768 Fail("(%s): Must be %f, But is %f ", title, in, out);
776 cmsBool IsGoodFixed15_16(const char *title, cmsFloat64Number in, cmsFloat64Number out)
778 return IsGoodVal(title, in, out, FIXED_PRECISION_15_16);
782 cmsBool IsGoodFixed8_8(const char *title, cmsFloat64Number in, cmsFloat64Number out)
784 return IsGoodVal(title, in, out, FIXED_PRECISION_8_8);
788 cmsBool IsGoodWord(const char *title, cmsUInt16Number in, cmsUInt16Number out)
790 if ((abs(in - out) > 0 )) {
792 Fail("(%s): Must be %x, But is %x ", title, in, out);
800 cmsBool IsGoodWordPrec(const char *title, cmsUInt16Number in, cmsUInt16Number out, cmsUInt16Number maxErr)
802 if ((abs(in - out) > maxErr )) {
804 Fail("(%s): Must be %x, But is %x ", title, in, out);
811 // Fixed point ----------------------------------------------------------------------------------------------
814 cmsInt32Number TestSingleFixed15_16(cmsFloat64Number d)
816 cmsS15Fixed16Number f = _cmsDoubleTo15Fixed16(d);
817 cmsFloat64Number RoundTrip = _cms15Fixed16toDouble(f);
818 cmsFloat64Number Error = fabs(d - RoundTrip);
820 return ( Error <= FIXED_PRECISION_15_16);
824 cmsInt32Number CheckFixedPoint15_16(void)
826 if (!TestSingleFixed15_16(1.0)) return 0;
827 if (!TestSingleFixed15_16(2.0)) return 0;
828 if (!TestSingleFixed15_16(1.23456)) return 0;
829 if (!TestSingleFixed15_16(0.99999)) return 0;
830 if (!TestSingleFixed15_16(0.1234567890123456789099999)) return 0;
831 if (!TestSingleFixed15_16(-1.0)) return 0;
832 if (!TestSingleFixed15_16(-2.0)) return 0;
833 if (!TestSingleFixed15_16(-1.23456)) return 0;
834 if (!TestSingleFixed15_16(-1.1234567890123456789099999)) return 0;
835 if (!TestSingleFixed15_16(+32767.1234567890123456789099999)) return 0;
836 if (!TestSingleFixed15_16(-32767.1234567890123456789099999)) return 0;
841 cmsInt32Number TestSingleFixed8_8(cmsFloat64Number d)
843 cmsS15Fixed16Number f = _cmsDoubleTo8Fixed8(d);
844 cmsFloat64Number RoundTrip = _cms8Fixed8toDouble((cmsUInt16Number) f);
845 cmsFloat64Number Error = fabs(d - RoundTrip);
847 return ( Error <= FIXED_PRECISION_8_8);
851 cmsInt32Number CheckFixedPoint8_8(void)
853 if (!TestSingleFixed8_8(1.0)) return 0;
854 if (!TestSingleFixed8_8(2.0)) return 0;
855 if (!TestSingleFixed8_8(1.23456)) return 0;
856 if (!TestSingleFixed8_8(0.99999)) return 0;
857 if (!TestSingleFixed8_8(0.1234567890123456789099999)) return 0;
858 if (!TestSingleFixed8_8(+255.1234567890123456789099999)) return 0;
863 // Linear interpolation -----------------------------------------------------------------------------------------------
865 // Since prime factors of 65535 (FFFF) are,
867 // 0xFFFF = 3 * 5 * 17 * 257
869 // I test tables of 2, 4, 6, and 18 points, that will be exact.
872 void BuildTable(cmsInt32Number n, cmsUInt16Number Tab[], cmsBool Descending)
876 for (i=0; i < n; i++) {
877 cmsFloat64Number v = (cmsFloat64Number) ((cmsFloat64Number) 65535.0 * i ) / (n-1);
879 Tab[Descending ? (n - i - 1) : i ] = (cmsUInt16Number) floor(v + 0.5);
883 // A single function that does check 1D interpolation
884 // nNodesToCheck = number on nodes to check
885 // Down = Create decreasing tables
886 // Reverse = Check reverse interpolation
887 // max_err = max allowed error
890 cmsInt32Number Check1D(cmsInt32Number nNodesToCheck, cmsBool Down, cmsInt32Number max_err)
893 cmsUInt16Number in, out;
895 cmsUInt16Number* Tab;
897 Tab = (cmsUInt16Number*) malloc(sizeof(cmsUInt16Number)* nNodesToCheck);
898 if (Tab == NULL) return 0;
900 p = _cmsComputeInterpParams(DbgThread(), nNodesToCheck, 1, 1, Tab, CMS_LERP_FLAGS_16BITS);
901 if (p == NULL) return 0;
903 BuildTable(nNodesToCheck, Tab, Down);
905 for (i=0; i <= 0xffff; i++) {
907 in = (cmsUInt16Number) i;
910 p ->Interpolation.Lerp16(&in, &out, p);
912 if (Down) out = 0xffff - out;
914 if (abs(out - in) > max_err) {
916 Fail("(%dp): Must be %x, But is %x : ", nNodesToCheck, in, out);
917 _cmsFreeInterpParams(p);
923 _cmsFreeInterpParams(p);
930 cmsInt32Number Check1DLERP2(void)
932 return Check1D(2, FALSE, 0);
937 cmsInt32Number Check1DLERP3(void)
939 return Check1D(3, FALSE, 1);
944 cmsInt32Number Check1DLERP4(void)
946 return Check1D(4, FALSE, 0);
950 cmsInt32Number Check1DLERP6(void)
952 return Check1D(6, FALSE, 0);
956 cmsInt32Number Check1DLERP18(void)
958 return Check1D(18, FALSE, 0);
963 cmsInt32Number Check1DLERP2Down(void)
965 return Check1D(2, TRUE, 0);
970 cmsInt32Number Check1DLERP3Down(void)
972 return Check1D(3, TRUE, 1);
976 cmsInt32Number Check1DLERP6Down(void)
978 return Check1D(6, TRUE, 0);
982 cmsInt32Number Check1DLERP18Down(void)
984 return Check1D(18, TRUE, 0);
988 cmsInt32Number ExhaustiveCheck1DLERP(void)
993 for (j=10; j <= 4096; j++) {
995 if ((j % 10) == 0) printf("%d \r", j);
997 if (!Check1D(j, FALSE, 1)) return 0;
1000 printf("\rResult is ");
1005 cmsInt32Number ExhaustiveCheck1DLERPDown(void)
1010 for (j=10; j <= 4096; j++) {
1012 if ((j % 10) == 0) printf("%d \r", j);
1014 if (!Check1D(j, TRUE, 1)) return 0;
1018 printf("\rResult is ");
1024 // 3D interpolation -------------------------------------------------------------------------------------------------
1027 cmsInt32Number Check3DinterpolationFloatTetrahedral(void)
1031 cmsFloat32Number In[3], Out[3];
1032 cmsFloat32Number FloatTable[] = { //R G B
1034 0, 0, 0, // B=0,G=0,R=0
1035 0, 0, .25, // B=1,G=0,R=0
1037 0, .5, 0, // B=0,G=1,R=0
1038 0, .5, .25, // B=1,G=1,R=0
1040 1, 0, 0, // B=0,G=0,R=1
1041 1, 0, .25, // B=1,G=0,R=1
1043 1, .5, 0, // B=0,G=1,R=1
1044 1, .5, .25 // B=1,G=1,R=1
1048 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
1052 for (i=0; i < 0xffff; i++) {
1054 In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
1056 p ->Interpolation.LerpFloat(In, Out, p);
1058 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1059 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1060 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1063 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1064 _cmsFreeInterpParams(p);
1068 _cmsFreeInterpParams(p);
1073 cmsInt32Number Check3DinterpolationFloatTrilinear(void)
1077 cmsFloat32Number In[3], Out[3];
1078 cmsFloat32Number FloatTable[] = { //R G B
1080 0, 0, 0, // B=0,G=0,R=0
1081 0, 0, .25, // B=1,G=0,R=0
1083 0, .5, 0, // B=0,G=1,R=0
1084 0, .5, .25, // B=1,G=1,R=0
1086 1, 0, 0, // B=0,G=0,R=1
1087 1, 0, .25, // B=1,G=0,R=1
1089 1, .5, 0, // B=0,G=1,R=1
1090 1, .5, .25 // B=1,G=1,R=1
1094 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
1097 for (i=0; i < 0xffff; i++) {
1099 In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
1101 p ->Interpolation.LerpFloat(In, Out, p);
1103 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1104 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1105 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1108 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1109 _cmsFreeInterpParams(p);
1113 _cmsFreeInterpParams(p);
1119 cmsInt32Number Check3DinterpolationTetrahedral16(void)
1123 cmsUInt16Number In[3], Out[3];
1124 cmsUInt16Number Table[] = {
1136 0xffff, 0xffff, 0xffff
1139 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
1142 for (i=0; i < 0xffff; i++) {
1144 In[0] = In[1] = In[2] = (cmsUInt16Number) i;
1146 p ->Interpolation.Lerp16(In, Out, p);
1148 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1149 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1150 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1153 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1154 _cmsFreeInterpParams(p);
1158 _cmsFreeInterpParams(p);
1163 cmsInt32Number Check3DinterpolationTrilinear16(void)
1167 cmsUInt16Number In[3], Out[3];
1168 cmsUInt16Number Table[] = {
1180 0xffff, 0xffff, 0xffff
1183 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
1186 for (i=0; i < 0xffff; i++) {
1188 In[0] = In[1] = In[2] = (cmsUInt16Number) i;
1190 p ->Interpolation.Lerp16(In, Out, p);
1192 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1193 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1194 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1197 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1198 _cmsFreeInterpParams(p);
1202 _cmsFreeInterpParams(p);
1208 cmsInt32Number ExaustiveCheck3DinterpolationFloatTetrahedral(void)
1211 cmsInt32Number r, g, b;
1212 cmsFloat32Number In[3], Out[3];
1213 cmsFloat32Number FloatTable[] = { //R G B
1215 0, 0, 0, // B=0,G=0,R=0
1216 0, 0, .25, // B=1,G=0,R=0
1218 0, .5, 0, // B=0,G=1,R=0
1219 0, .5, .25, // B=1,G=1,R=0
1221 1, 0, 0, // B=0,G=0,R=1
1222 1, 0, .25, // B=1,G=0,R=1
1224 1, .5, 0, // B=0,G=1,R=1
1225 1, .5, .25 // B=1,G=1,R=1
1229 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
1232 for (r=0; r < 0xff; r++)
1233 for (g=0; g < 0xff; g++)
1234 for (b=0; b < 0xff; b++)
1237 In[0] = (cmsFloat32Number) r / 255.0F;
1238 In[1] = (cmsFloat32Number) g / 255.0F;
1239 In[2] = (cmsFloat32Number) b / 255.0F;
1242 p ->Interpolation.LerpFloat(In, Out, p);
1244 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1245 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1246 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1249 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1250 _cmsFreeInterpParams(p);
1254 _cmsFreeInterpParams(p);
1259 cmsInt32Number ExaustiveCheck3DinterpolationFloatTrilinear(void)
1262 cmsInt32Number r, g, b;
1263 cmsFloat32Number In[3], Out[3];
1264 cmsFloat32Number FloatTable[] = { //R G B
1266 0, 0, 0, // B=0,G=0,R=0
1267 0, 0, .25, // B=1,G=0,R=0
1269 0, .5, 0, // B=0,G=1,R=0
1270 0, .5, .25, // B=1,G=1,R=0
1272 1, 0, 0, // B=0,G=0,R=1
1273 1, 0, .25, // B=1,G=0,R=1
1275 1, .5, 0, // B=0,G=1,R=1
1276 1, .5, .25 // B=1,G=1,R=1
1280 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
1283 for (r=0; r < 0xff; r++)
1284 for (g=0; g < 0xff; g++)
1285 for (b=0; b < 0xff; b++)
1288 In[0] = (cmsFloat32Number) r / 255.0F;
1289 In[1] = (cmsFloat32Number) g / 255.0F;
1290 In[2] = (cmsFloat32Number) b / 255.0F;
1293 p ->Interpolation.LerpFloat(In, Out, p);
1295 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1296 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1297 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1300 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1301 _cmsFreeInterpParams(p);
1305 _cmsFreeInterpParams(p);
1311 cmsInt32Number ExhaustiveCheck3DinterpolationTetrahedral16(void)
1314 cmsInt32Number r, g, b;
1315 cmsUInt16Number In[3], Out[3];
1316 cmsUInt16Number Table[] = {
1328 0xffff, 0xffff, 0xffff
1331 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
1333 for (r=0; r < 0xff; r++)
1334 for (g=0; g < 0xff; g++)
1335 for (b=0; b < 0xff; b++)
1337 In[0] = (cmsUInt16Number) r ;
1338 In[1] = (cmsUInt16Number) g ;
1339 In[2] = (cmsUInt16Number) b ;
1342 p ->Interpolation.Lerp16(In, Out, p);
1344 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1345 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1346 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1349 _cmsFreeInterpParams(p);
1353 _cmsFreeInterpParams(p);
1358 cmsInt32Number ExhaustiveCheck3DinterpolationTrilinear16(void)
1361 cmsInt32Number r, g, b;
1362 cmsUInt16Number In[3], Out[3];
1363 cmsUInt16Number Table[] = {
1375 0xffff, 0xffff, 0xffff
1378 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
1380 for (r=0; r < 0xff; r++)
1381 for (g=0; g < 0xff; g++)
1382 for (b=0; b < 0xff; b++)
1384 In[0] = (cmsUInt16Number) r ;
1385 In[1] = (cmsUInt16Number)g ;
1386 In[2] = (cmsUInt16Number)b ;
1389 p ->Interpolation.Lerp16(In, Out, p);
1391 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1392 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1393 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1397 _cmsFreeInterpParams(p);
1401 _cmsFreeInterpParams(p);
1405 // Check reverse interpolation on LUTS. This is right now exclusively used by K preservation algorithm
1407 cmsInt32Number CheckReverseInterpolation3x3(void)
1411 cmsFloat32Number Target[3], Result[3], Hint[3];
1412 cmsFloat32Number err, max;
1414 cmsUInt16Number Table[] = {
1417 0, 0, 0xffff, // 0 0 1
1419 0, 0xffff, 0, // 0 1 0
1420 0, 0xffff, 0xffff, // 0 1 1
1422 0xffff, 0, 0, // 1 0 0
1423 0xffff, 0, 0xffff, // 1 0 1
1425 0xffff, 0xffff, 0, // 1 1 0
1426 0xffff, 0xffff, 0xffff, // 1 1 1
1431 Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1433 clut = cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table);
1434 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
1436 Target[0] = 0; Target[1] = 0; Target[2] = 0;
1437 Hint[0] = 0; Hint[1] = 0; Hint[2] = 0;
1438 cmsPipelineEvalReverseFloat(Target, Result, NULL, Lut);
1439 if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0){
1441 Fail("Reverse interpolation didn't find zero");
1445 // Transverse identity
1447 for (i=0; i <= 100; i++) {
1449 cmsFloat32Number in = i / 100.0F;
1451 Target[0] = in; Target[1] = 0; Target[2] = 0;
1452 cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1454 err = fabsf(in - Result[0]);
1455 if (err > max) max = err;
1457 memcpy(Hint, Result, sizeof(Hint));
1460 cmsPipelineFree(Lut);
1461 return (max <= FLOAT_PRECISSION);
1466 cmsInt32Number CheckReverseInterpolation4x3(void)
1470 cmsFloat32Number Target[4], Result[4], Hint[4];
1471 cmsFloat32Number err, max;
1474 // 4 -> 3, output gets 3 first channels copied
1475 cmsUInt16Number Table[] = {
1477 0, 0, 0, // 0 0 0 0 = ( 0, 0, 0)
1478 0, 0, 0, // 0 0 0 1 = ( 0, 0, 0)
1480 0, 0, 0xffff, // 0 0 1 0 = ( 0, 0, 1)
1481 0, 0, 0xffff, // 0 0 1 1 = ( 0, 0, 1)
1483 0, 0xffff, 0, // 0 1 0 0 = ( 0, 1, 0)
1484 0, 0xffff, 0, // 0 1 0 1 = ( 0, 1, 0)
1486 0, 0xffff, 0xffff, // 0 1 1 0 = ( 0, 1, 1)
1487 0, 0xffff, 0xffff, // 0 1 1 1 = ( 0, 1, 1)
1489 0xffff, 0, 0, // 1 0 0 0 = ( 1, 0, 0)
1490 0xffff, 0, 0, // 1 0 0 1 = ( 1, 0, 0)
1492 0xffff, 0, 0xffff, // 1 0 1 0 = ( 1, 0, 1)
1493 0xffff, 0, 0xffff, // 1 0 1 1 = ( 1, 0, 1)
1495 0xffff, 0xffff, 0, // 1 1 0 0 = ( 1, 1, 0)
1496 0xffff, 0xffff, 0, // 1 1 0 1 = ( 1, 1, 0)
1498 0xffff, 0xffff, 0xffff, // 1 1 1 0 = ( 1, 1, 1)
1499 0xffff, 0xffff, 0xffff, // 1 1 1 1 = ( 1, 1, 1)
1503 Lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1505 clut = cmsStageAllocCLut16bit(DbgThread(), 2, 4, 3, Table);
1506 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
1508 // Check if the LUT is behaving as expected
1509 SubTest("4->3 feasibility");
1510 for (i=0; i <= 100; i++) {
1512 Target[0] = i / 100.0F;
1513 Target[1] = Target[0];
1517 cmsPipelineEvalFloat(Target, Result, Lut);
1519 if (!IsGoodFixed15_16("0", Target[0], Result[0])) return 0;
1520 if (!IsGoodFixed15_16("1", Target[1], Result[1])) return 0;
1521 if (!IsGoodFixed15_16("2", Target[2], Result[2])) return 0;
1524 SubTest("4->3 zero");
1529 // This one holds the fixed K
1532 // This is our hint (which is a big lie in this case)
1533 Hint[0] = 0.1F; Hint[1] = 0.1F; Hint[2] = 0.1F;
1535 cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1537 if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0 || Result[3] != 0){
1539 Fail("Reverse interpolation didn't find zero");
1543 SubTest("4->3 find CMY");
1545 for (i=0; i <= 100; i++) {
1547 cmsFloat32Number in = i / 100.0F;
1549 Target[0] = in; Target[1] = 0; Target[2] = 0;
1550 cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1552 err = fabsf(in - Result[0]);
1553 if (err > max) max = err;
1555 memcpy(Hint, Result, sizeof(Hint));
1558 cmsPipelineFree(Lut);
1559 return (max <= FLOAT_PRECISSION);
1564 // Check all interpolation.
1567 cmsUInt16Number Fn8D1(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1568 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1571 return (cmsUInt16Number) ((a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8) / m);
1576 cmsUInt16Number Fn8D2(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1577 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1580 return (cmsUInt16Number) ((a1 + 3* a2 + 3* a3 + a4 + a5 + a6 + a7 + a8 ) / (m + 4));
1585 cmsUInt16Number Fn8D3(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1586 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1589 return (cmsUInt16Number) ((3*a1 + 2*a2 + 3*a3 + a4 + a5 + a6 + a7 + a8) / (m + 5));
1596 cmsInt32Number Sampler3D(register const cmsUInt16Number In[],
1597 register cmsUInt16Number Out[],
1598 register void * Cargo)
1601 Out[0] = Fn8D1(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1602 Out[1] = Fn8D2(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1603 Out[2] = Fn8D3(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1609 cmsInt32Number Sampler4D(register const cmsUInt16Number In[],
1610 register cmsUInt16Number Out[],
1611 register void * Cargo)
1614 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1615 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1616 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1622 cmsInt32Number Sampler5D(register const cmsUInt16Number In[],
1623 register cmsUInt16Number Out[],
1624 register void * Cargo)
1627 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1628 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1629 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1635 cmsInt32Number Sampler6D(register const cmsUInt16Number In[],
1636 register cmsUInt16Number Out[],
1637 register void * Cargo)
1640 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1641 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1642 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1648 cmsInt32Number Sampler7D(register const cmsUInt16Number In[],
1649 register cmsUInt16Number Out[],
1650 register void * Cargo)
1653 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1654 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1655 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1661 cmsInt32Number Sampler8D(register const cmsUInt16Number In[],
1662 register cmsUInt16Number Out[],
1663 register void * Cargo)
1666 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1667 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1668 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1674 cmsBool CheckOne3D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3)
1676 cmsUInt16Number In[3], Out1[3], Out2[3];
1678 In[0] = a1; In[1] = a2; In[2] = a3;
1680 // This is the interpolated value
1681 cmsPipelineEval16(In, Out1, lut);
1683 // This is the real value
1684 Sampler3D(In, Out2, NULL);
1686 // Let's see the difference
1688 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1689 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1690 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1696 cmsBool CheckOne4D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4)
1698 cmsUInt16Number In[4], Out1[3], Out2[3];
1700 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4;
1702 // This is the interpolated value
1703 cmsPipelineEval16(In, Out1, lut);
1705 // This is the real value
1706 Sampler4D(In, Out2, NULL);
1708 // Let's see the difference
1710 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1711 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1712 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1718 cmsBool CheckOne5D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1719 cmsUInt16Number a3, cmsUInt16Number a4, cmsUInt16Number a5)
1721 cmsUInt16Number In[5], Out1[3], Out2[3];
1723 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5;
1725 // This is the interpolated value
1726 cmsPipelineEval16(In, Out1, lut);
1728 // This is the real value
1729 Sampler5D(In, Out2, NULL);
1731 // Let's see the difference
1733 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1734 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1735 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1741 cmsBool CheckOne6D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1742 cmsUInt16Number a3, cmsUInt16Number a4,
1743 cmsUInt16Number a5, cmsUInt16Number a6)
1745 cmsUInt16Number In[6], Out1[3], Out2[3];
1747 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6;
1749 // This is the interpolated value
1750 cmsPipelineEval16(In, Out1, lut);
1752 // This is the real value
1753 Sampler6D(In, Out2, NULL);
1755 // Let's see the difference
1757 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1758 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1759 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1766 cmsBool CheckOne7D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1767 cmsUInt16Number a3, cmsUInt16Number a4,
1768 cmsUInt16Number a5, cmsUInt16Number a6,
1771 cmsUInt16Number In[7], Out1[3], Out2[3];
1773 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6; In[6] = a7;
1775 // This is the interpolated value
1776 cmsPipelineEval16(In, Out1, lut);
1778 // This is the real value
1779 Sampler7D(In, Out2, NULL);
1781 // Let's see the difference
1783 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1784 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1785 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1792 cmsBool CheckOne8D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1793 cmsUInt16Number a3, cmsUInt16Number a4,
1794 cmsUInt16Number a5, cmsUInt16Number a6,
1795 cmsUInt16Number a7, cmsUInt16Number a8)
1797 cmsUInt16Number In[8], Out1[3], Out2[3];
1799 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6; In[6] = a7; In[7] = a8;
1801 // This is the interpolated value
1802 cmsPipelineEval16(In, Out1, lut);
1804 // This is the real value
1805 Sampler8D(In, Out2, NULL);
1807 // Let's see the difference
1809 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1810 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1811 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1818 cmsInt32Number Check3Dinterp(void)
1823 lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1824 mpe = cmsStageAllocCLut16bit(DbgThread(), 9, 3, 3, NULL);
1825 cmsStageSampleCLut16bit(mpe, Sampler3D, NULL, 0);
1826 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1830 if (!CheckOne3D(lut, 0, 0, 0)) return 0;
1831 if (!CheckOne3D(lut, 0xffff, 0xffff, 0xffff)) return 0;
1833 if (!CheckOne3D(lut, 0x8080, 0x8080, 0x8080)) return 0;
1834 if (!CheckOne3D(lut, 0x0000, 0xFE00, 0x80FF)) return 0;
1835 if (!CheckOne3D(lut, 0x1111, 0x2222, 0x3333)) return 0;
1836 if (!CheckOne3D(lut, 0x0000, 0x0012, 0x0013)) return 0;
1837 if (!CheckOne3D(lut, 0x3141, 0x1415, 0x1592)) return 0;
1838 if (!CheckOne3D(lut, 0xFF00, 0xFF01, 0xFF12)) return 0;
1840 cmsPipelineFree(lut);
1846 cmsInt32Number Check3DinterpGranular(void)
1850 cmsUInt32Number Dimensions[] = { 7, 8, 9 };
1852 lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1853 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 3, 3, NULL);
1854 cmsStageSampleCLut16bit(mpe, Sampler3D, NULL, 0);
1855 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1859 if (!CheckOne3D(lut, 0, 0, 0)) return 0;
1860 if (!CheckOne3D(lut, 0xffff, 0xffff, 0xffff)) return 0;
1862 if (!CheckOne3D(lut, 0x8080, 0x8080, 0x8080)) return 0;
1863 if (!CheckOne3D(lut, 0x0000, 0xFE00, 0x80FF)) return 0;
1864 if (!CheckOne3D(lut, 0x1111, 0x2222, 0x3333)) return 0;
1865 if (!CheckOne3D(lut, 0x0000, 0x0012, 0x0013)) return 0;
1866 if (!CheckOne3D(lut, 0x3141, 0x1415, 0x1592)) return 0;
1867 if (!CheckOne3D(lut, 0xFF00, 0xFF01, 0xFF12)) return 0;
1869 cmsPipelineFree(lut);
1876 cmsInt32Number Check4Dinterp(void)
1881 lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1882 mpe = cmsStageAllocCLut16bit(DbgThread(), 9, 4, 3, NULL);
1883 cmsStageSampleCLut16bit(mpe, Sampler4D, NULL, 0);
1884 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1888 if (!CheckOne4D(lut, 0, 0, 0, 0)) return 0;
1889 if (!CheckOne4D(lut, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
1891 if (!CheckOne4D(lut, 0x8080, 0x8080, 0x8080, 0x8080)) return 0;
1892 if (!CheckOne4D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888)) return 0;
1893 if (!CheckOne4D(lut, 0x1111, 0x2222, 0x3333, 0x4444)) return 0;
1894 if (!CheckOne4D(lut, 0x0000, 0x0012, 0x0013, 0x0014)) return 0;
1895 if (!CheckOne4D(lut, 0x3141, 0x1415, 0x1592, 0x9261)) return 0;
1896 if (!CheckOne4D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13)) return 0;
1898 cmsPipelineFree(lut);
1906 cmsInt32Number Check4DinterpGranular(void)
1910 cmsUInt32Number Dimensions[] = { 9, 8, 7, 6 };
1912 lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1913 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 4, 3, NULL);
1914 cmsStageSampleCLut16bit(mpe, Sampler4D, NULL, 0);
1915 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1919 if (!CheckOne4D(lut, 0, 0, 0, 0)) return 0;
1920 if (!CheckOne4D(lut, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
1922 if (!CheckOne4D(lut, 0x8080, 0x8080, 0x8080, 0x8080)) return 0;
1923 if (!CheckOne4D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888)) return 0;
1924 if (!CheckOne4D(lut, 0x1111, 0x2222, 0x3333, 0x4444)) return 0;
1925 if (!CheckOne4D(lut, 0x0000, 0x0012, 0x0013, 0x0014)) return 0;
1926 if (!CheckOne4D(lut, 0x3141, 0x1415, 0x1592, 0x9261)) return 0;
1927 if (!CheckOne4D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13)) return 0;
1929 cmsPipelineFree(lut);
1936 cmsInt32Number Check5DinterpGranular(void)
1940 cmsUInt32Number Dimensions[] = { 3, 2, 2, 2, 2 };
1942 lut = cmsPipelineAlloc(DbgThread(), 5, 3);
1943 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 5, 3, NULL);
1944 cmsStageSampleCLut16bit(mpe, Sampler5D, NULL, 0);
1945 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1949 if (!CheckOne5D(lut, 0, 0, 0, 0, 0)) return 0;
1950 if (!CheckOne5D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
1952 if (!CheckOne5D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234)) return 0;
1953 if (!CheckOne5D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078)) return 0;
1954 if (!CheckOne5D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455)) return 0;
1955 if (!CheckOne5D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333)) return 0;
1956 if (!CheckOne5D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567)) return 0;
1957 if (!CheckOne5D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344)) return 0;
1959 cmsPipelineFree(lut);
1965 cmsInt32Number Check6DinterpGranular(void)
1969 cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2 };
1971 lut = cmsPipelineAlloc(DbgThread(), 6, 3);
1972 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 6, 3, NULL);
1973 cmsStageSampleCLut16bit(mpe, Sampler6D, NULL, 0);
1974 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1978 if (!CheckOne6D(lut, 0, 0, 0, 0, 0, 0)) return 0;
1979 if (!CheckOne6D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
1981 if (!CheckOne6D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122)) return 0;
1982 if (!CheckOne6D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233)) return 0;
1983 if (!CheckOne6D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344)) return 0;
1984 if (!CheckOne6D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455)) return 0;
1985 if (!CheckOne6D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566)) return 0;
1986 if (!CheckOne6D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677)) return 0;
1988 cmsPipelineFree(lut);
1994 cmsInt32Number Check7DinterpGranular(void)
1998 cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2, 2 };
2000 lut = cmsPipelineAlloc(DbgThread(), 7, 3);
2001 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 7, 3, NULL);
2002 cmsStageSampleCLut16bit(mpe, Sampler7D, NULL, 0);
2003 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
2007 if (!CheckOne7D(lut, 0, 0, 0, 0, 0, 0, 0)) return 0;
2008 if (!CheckOne7D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
2010 if (!CheckOne7D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122, 0x0056)) return 0;
2011 if (!CheckOne7D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233, 0x0088)) return 0;
2012 if (!CheckOne7D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344, 0x1987)) return 0;
2013 if (!CheckOne7D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455, 0x9988)) return 0;
2014 if (!CheckOne7D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566, 0xfe56)) return 0;
2015 if (!CheckOne7D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677, 0xbabe)) return 0;
2017 cmsPipelineFree(lut);
2024 cmsInt32Number Check8DinterpGranular(void)
2028 cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2, 2, 2 };
2030 lut = cmsPipelineAlloc(DbgThread(), 8, 3);
2031 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 8, 3, NULL);
2032 cmsStageSampleCLut16bit(mpe, Sampler8D, NULL, 0);
2033 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
2037 if (!CheckOne8D(lut, 0, 0, 0, 0, 0, 0, 0, 0)) return 0;
2038 if (!CheckOne8D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
2040 if (!CheckOne8D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122, 0x0056, 0x0011)) return 0;
2041 if (!CheckOne8D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233, 0x0088, 0x2020)) return 0;
2042 if (!CheckOne8D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344, 0x1987, 0x4532)) return 0;
2043 if (!CheckOne8D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455, 0x9988, 0x1200)) return 0;
2044 if (!CheckOne8D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566, 0xfe56, 0x6666)) return 0;
2045 if (!CheckOne8D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677, 0xbabe, 0xface)) return 0;
2047 cmsPipelineFree(lut);
2052 // Colorimetric conversions -------------------------------------------------------------------------------------------------
2054 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2056 cmsInt32Number CheckLab2LCh(void)
2058 cmsInt32Number l, a, b;
2059 cmsFloat64Number dist, Max = 0;
2060 cmsCIELab Lab, Lab2;
2063 for (l=0; l <= 100; l += 10) {
2065 for (a=-128; a <= +128; a += 8) {
2067 for (b=-128; b <= 128; b += 8) {
2073 cmsLab2LCh(&LCh, &Lab);
2074 cmsLCh2Lab(&Lab2, &LCh);
2076 dist = cmsDeltaE(&Lab, &Lab2);
2077 if (dist > Max) Max = dist;
2085 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2087 cmsInt32Number CheckLab2XYZ(void)
2089 cmsInt32Number l, a, b;
2090 cmsFloat64Number dist, Max = 0;
2091 cmsCIELab Lab, Lab2;
2094 for (l=0; l <= 100; l += 10) {
2096 for (a=-128; a <= +128; a += 8) {
2098 for (b=-128; b <= 128; b += 8) {
2104 cmsLab2XYZ(NULL, &XYZ, &Lab);
2105 cmsXYZ2Lab(NULL, &Lab2, &XYZ);
2107 dist = cmsDeltaE(&Lab, &Lab2);
2108 if (dist > Max) Max = dist;
2117 // Lab to xyY and back should be performed at 1E-12 accuracy at least
2119 cmsInt32Number CheckLab2xyY(void)
2121 cmsInt32Number l, a, b;
2122 cmsFloat64Number dist, Max = 0;
2123 cmsCIELab Lab, Lab2;
2127 for (l=0; l <= 100; l += 10) {
2129 for (a=-128; a <= +128; a += 8) {
2131 for (b=-128; b <= 128; b += 8) {
2137 cmsLab2XYZ(NULL, &XYZ, &Lab);
2138 cmsXYZ2xyY(&xyY, &XYZ);
2139 cmsxyY2XYZ(&XYZ, &xyY);
2140 cmsXYZ2Lab(NULL, &Lab2, &XYZ);
2142 dist = cmsDeltaE(&Lab, &Lab2);
2143 if (dist > Max) Max = dist;
2154 cmsInt32Number CheckLabV2encoding(void)
2156 cmsInt32Number n2, i, j;
2157 cmsUInt16Number Inw[3], aw[3];
2162 for (j=0; j < 65535; j++) {
2164 Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
2166 cmsLabEncoded2FloatV2(&Lab, Inw);
2167 cmsFloat2LabEncodedV2(aw, &Lab);
2169 for (i=0; i < 3; i++) {
2182 cmsInt32Number CheckLabV4encoding(void)
2184 cmsInt32Number n2, i, j;
2185 cmsUInt16Number Inw[3], aw[3];
2190 for (j=0; j < 65535; j++) {
2192 Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
2194 cmsLabEncoded2Float(&Lab, Inw);
2195 cmsFloat2LabEncoded(aw, &Lab);
2197 for (i=0; i < 3; i++) {
2210 // BlackBody -----------------------------------------------------------------------------------------------------
2213 cmsInt32Number CheckTemp2CHRM(void)
2216 cmsFloat64Number d, v, Max = 0;
2219 for (j=4000; j < 25000; j++) {
2221 cmsWhitePointFromTemp(&White, j);
2222 if (!cmsTempFromWhitePoint(&v, &White)) return 0;
2225 if (d > Max) Max = d;
2228 // 100 degree is the actual resolution
2234 // Tone curves -----------------------------------------------------------------------------------------------------
2237 cmsInt32Number CheckGammaEstimation(cmsToneCurve* c, cmsFloat64Number g)
2239 cmsFloat64Number est = cmsEstimateGamma(c, 0.001);
2241 SubTest("Gamma estimation");
2242 if (fabs(est - g) > 0.001) return 0;
2247 cmsInt32Number CheckGammaCreation16(void)
2249 cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2251 cmsUInt16Number in, out;
2253 for (i=0; i < 0xffff; i++) {
2255 in = (cmsUInt16Number) i;
2256 out = cmsEvalToneCurve16(LinGamma, in);
2258 Fail("(lin gamma): Must be %x, But is %x : ", in, out);
2259 cmsFreeToneCurve(LinGamma);
2264 if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2266 cmsFreeToneCurve(LinGamma);
2272 cmsInt32Number CheckGammaCreationFlt(void)
2274 cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2276 cmsFloat32Number in, out;
2278 for (i=0; i < 0xffff; i++) {
2280 in = (cmsFloat32Number) (i / 65535.0);
2281 out = cmsEvalToneCurveFloat(LinGamma, in);
2282 if (fabs(in - out) > (1/65535.0)) {
2283 Fail("(lin gamma): Must be %f, But is %f : ", in, out);
2284 cmsFreeToneCurve(LinGamma);
2289 if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2290 cmsFreeToneCurve(LinGamma);
2294 // Curve curves using a single power function
2295 // Error is given in 0..ffff counts
2297 cmsInt32Number CheckGammaFloat(cmsFloat64Number g)
2299 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), g);
2301 cmsFloat32Number in, out;
2302 cmsFloat64Number val, Err;
2305 for (i=0; i < 0xffff; i++) {
2307 in = (cmsFloat32Number) (i / 65535.0);
2308 out = cmsEvalToneCurveFloat(Curve, in);
2309 val = pow((cmsFloat64Number) in, g);
2311 Err = fabs( val - out);
2312 if (Err > MaxErr) MaxErr = Err;
2315 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2317 if (!CheckGammaEstimation(Curve, g)) return 0;
2319 cmsFreeToneCurve(Curve);
2323 static cmsInt32Number CheckGamma18(void)
2325 return CheckGammaFloat(1.8);
2328 static cmsInt32Number CheckGamma22(void)
2330 return CheckGammaFloat(2.2);
2333 static cmsInt32Number CheckGamma30(void)
2335 return CheckGammaFloat(3.0);
2339 // Check table-based gamma functions
2341 cmsInt32Number CheckGammaFloatTable(cmsFloat64Number g)
2343 cmsFloat32Number Values[1025];
2344 cmsToneCurve* Curve;
2346 cmsFloat32Number in, out;
2347 cmsFloat64Number val, Err;
2349 for (i=0; i <= 1024; i++) {
2351 in = (cmsFloat32Number) (i / 1024.0);
2352 Values[i] = powf(in, (float) g);
2355 Curve = cmsBuildTabulatedToneCurveFloat(DbgThread(), 1025, Values);
2358 for (i=0; i <= 0xffff; i++) {
2360 in = (cmsFloat32Number) (i / 65535.0);
2361 out = cmsEvalToneCurveFloat(Curve, in);
2364 Err = fabs(val - out);
2365 if (Err > MaxErr) MaxErr = Err;
2368 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2370 if (!CheckGammaEstimation(Curve, g)) return 0;
2372 cmsFreeToneCurve(Curve);
2377 static cmsInt32Number CheckGamma18Table(void)
2379 return CheckGammaFloatTable(1.8);
2382 static cmsInt32Number CheckGamma22Table(void)
2384 return CheckGammaFloatTable(2.2);
2387 static cmsInt32Number CheckGamma30Table(void)
2389 return CheckGammaFloatTable(3.0);
2392 // Create a curve from a table (which is a pure gamma function) and check it against the pow function.
2394 cmsInt32Number CheckGammaWordTable(cmsFloat64Number g)
2396 cmsUInt16Number Values[1025];
2397 cmsToneCurve* Curve;
2399 cmsFloat32Number in, out;
2400 cmsFloat64Number val, Err;
2402 for (i=0; i <= 1024; i++) {
2404 in = (cmsFloat32Number) (i / 1024.0);
2405 Values[i] = (cmsUInt16Number) floor(pow(in, g) * 65535.0 + 0.5);
2408 Curve = cmsBuildTabulatedToneCurve16(DbgThread(), 1025, Values);
2411 for (i=0; i <= 0xffff; i++) {
2413 in = (cmsFloat32Number) (i / 65535.0);
2414 out = cmsEvalToneCurveFloat(Curve, in);
2417 Err = fabs(val - out);
2418 if (Err > MaxErr) MaxErr = Err;
2421 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2423 if (!CheckGammaEstimation(Curve, g)) return 0;
2425 cmsFreeToneCurve(Curve);
2429 static cmsInt32Number CheckGamma18TableWord(void)
2431 return CheckGammaWordTable(1.8);
2434 static cmsInt32Number CheckGamma22TableWord(void)
2436 return CheckGammaWordTable(2.2);
2439 static cmsInt32Number CheckGamma30TableWord(void)
2441 return CheckGammaWordTable(3.0);
2445 // Curve joining test. Joining two high-gamma of 3.0 curves should
2446 // give something like linear
2448 cmsInt32Number CheckJointCurves(void)
2450 cmsToneCurve *Forward, *Reverse, *Result;
2453 Forward = cmsBuildGamma(DbgThread(), 3.0);
2454 Reverse = cmsBuildGamma(DbgThread(), 3.0);
2456 Result = cmsJoinToneCurve(DbgThread(), Forward, Reverse, 256);
2458 cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
2460 rc = cmsIsToneCurveLinear(Result);
2461 cmsFreeToneCurve(Result);
2464 Fail("Joining same curve twice does not result in a linear ramp");
2470 // Create a gamma curve by cheating the table
2472 cmsToneCurve* GammaTableLinear(cmsInt32Number nEntries, cmsBool Dir)
2475 cmsToneCurve* g = cmsBuildTabulatedToneCurve16(DbgThread(), nEntries, NULL);
2477 for (i=0; i < nEntries; i++) {
2479 cmsInt32Number v = _cmsQuantizeVal(i, nEntries);
2482 g->Table16[i] = (cmsUInt16Number) v;
2484 g->Table16[i] = (cmsUInt16Number) (0xFFFF - v);
2492 cmsInt32Number CheckJointCurvesDescending(void)
2494 cmsToneCurve *Forward, *Reverse, *Result;
2495 cmsInt32Number i, rc;
2497 Forward = cmsBuildGamma(DbgThread(), 2.2);
2499 // Fake the curve to be table-based
2501 for (i=0; i < 4096; i++)
2502 Forward ->Table16[i] = 0xffff - Forward->Table16[i];
2503 Forward ->Segments[0].Type = 0;
2505 Reverse = cmsReverseToneCurve(Forward);
2507 Result = cmsJoinToneCurve(DbgThread(), Reverse, Reverse, 256);
2509 cmsFreeToneCurve(Forward);
2510 cmsFreeToneCurve(Reverse);
2512 rc = cmsIsToneCurveLinear(Result);
2513 cmsFreeToneCurve(Result);
2520 cmsInt32Number CheckFToneCurvePoint(cmsToneCurve* c, cmsUInt16Number Point, cmsInt32Number Value)
2522 cmsInt32Number Result;
2524 Result = cmsEvalToneCurve16(c, Point);
2526 return (abs(Value - Result) < 2);
2530 cmsInt32Number CheckReverseDegenerated(void)
2532 cmsToneCurve* p, *g;
2533 cmsUInt16Number Tab[16];
2552 p = cmsBuildTabulatedToneCurve16(DbgThread(), 16, Tab);
2553 g = cmsReverseToneCurve(p);
2555 // Now let's check some points
2556 if (!CheckFToneCurvePoint(g, 0x5555, 0x5555)) return 0;
2557 if (!CheckFToneCurvePoint(g, 0x7777, 0x7777)) return 0;
2559 // First point for zero
2560 if (!CheckFToneCurvePoint(g, 0x0000, 0x4444)) return 0;
2563 if (!CheckFToneCurvePoint(g, 0xFFFF, 0xFFFF)) return 0;
2565 cmsFreeToneCurve(p);
2566 cmsFreeToneCurve(g);
2572 // Build a parametric sRGB-like curve
2574 cmsToneCurve* Build_sRGBGamma(void)
2576 cmsFloat64Number Parameters[5];
2578 Parameters[0] = 2.4;
2579 Parameters[1] = 1. / 1.055;
2580 Parameters[2] = 0.055 / 1.055;
2581 Parameters[3] = 1. / 12.92;
2582 Parameters[4] = 0.04045; // d
2584 return cmsBuildParametricToneCurve(DbgThread(), 4, Parameters);
2589 // Join two gamma tables in floting point format. Result should be a straight line
2591 cmsToneCurve* CombineGammaFloat(cmsToneCurve* g1, cmsToneCurve* g2)
2593 cmsUInt16Number Tab[256];
2597 for (i=0; i < 256; i++) {
2599 f = (cmsFloat32Number) i / 255.0F;
2600 f = cmsEvalToneCurveFloat(g2, cmsEvalToneCurveFloat(g1, f));
2602 Tab[i] = (cmsUInt16Number) floor(f * 65535.0 + 0.5);
2605 return cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
2608 // Same of anterior, but using quantized tables
2610 cmsToneCurve* CombineGamma16(cmsToneCurve* g1, cmsToneCurve* g2)
2612 cmsUInt16Number Tab[256];
2616 for (i=0; i < 256; i++) {
2618 cmsUInt16Number wValIn;
2620 wValIn = _cmsQuantizeVal(i, 256);
2621 Tab[i] = cmsEvalToneCurve16(g2, cmsEvalToneCurve16(g1, wValIn));
2624 return cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
2628 cmsInt32Number CheckJointFloatCurves_sRGB(void)
2630 cmsToneCurve *Forward, *Reverse, *Result;
2633 Forward = Build_sRGBGamma();
2634 Reverse = cmsReverseToneCurve(Forward);
2635 Result = CombineGammaFloat(Forward, Reverse);
2636 cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
2638 rc = cmsIsToneCurveLinear(Result);
2639 cmsFreeToneCurve(Result);
2645 cmsInt32Number CheckJoint16Curves_sRGB(void)
2647 cmsToneCurve *Forward, *Reverse, *Result;
2650 Forward = Build_sRGBGamma();
2651 Reverse = cmsReverseToneCurve(Forward);
2652 Result = CombineGamma16(Forward, Reverse);
2653 cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
2655 rc = cmsIsToneCurveLinear(Result);
2656 cmsFreeToneCurve(Result);
2661 // sigmoidal curve f(x) = (1-x^g) ^(1/g)
2664 cmsInt32Number CheckJointCurvesSShaped(void)
2666 cmsFloat64Number p = 3.2;
2667 cmsToneCurve *Forward, *Reverse, *Result;
2670 Forward = cmsBuildParametricToneCurve(DbgThread(), 108, &p);
2671 Reverse = cmsReverseToneCurve(Forward);
2672 Result = cmsJoinToneCurve(DbgThread(), Forward, Forward, 4096);
2674 cmsFreeToneCurve(Forward);
2675 cmsFreeToneCurve(Reverse);
2677 rc = cmsIsToneCurveLinear(Result);
2678 cmsFreeToneCurve(Result);
2683 // --------------------------------------------------------------------------------------------------------
2685 // Implementation of some tone curve functions
2687 cmsFloat32Number Gamma(cmsFloat32Number x, const cmsFloat64Number Params[])
2689 return (cmsFloat32Number) pow(x, Params[0]);
2693 cmsFloat32Number CIE122(cmsFloat32Number x, const cmsFloat64Number Params[])
2696 cmsFloat64Number e, Val;
2698 if (x >= -Params[2] / Params[1]) {
2700 e = Params[1]*x + Params[2];
2703 Val = pow(e, Params[0]);
2710 return (cmsFloat32Number) Val;
2714 cmsFloat32Number IEC61966_3(cmsFloat32Number x, const cmsFloat64Number Params[])
2716 cmsFloat64Number e, Val;
2718 if (x >= -Params[2] / Params[1]) {
2720 e = Params[1]*x + Params[2];
2723 Val = pow(e, Params[0]) + Params[3];
2730 return (cmsFloat32Number) Val;
2734 cmsFloat32Number IEC61966_21(cmsFloat32Number x, const cmsFloat64Number Params[])
2736 cmsFloat64Number e, Val;
2738 if (x >= Params[4]) {
2740 e = Params[1]*x + Params[2];
2743 Val = pow(e, Params[0]);
2748 Val = x * Params[3];
2750 return (cmsFloat32Number) Val;
2754 cmsFloat32Number param_5(cmsFloat32Number x, const cmsFloat64Number Params[])
2756 cmsFloat64Number e, Val;
2757 // Y = (aX + b)^Gamma + e | X >= d
2758 // Y = cX + f | else
2759 if (x >= Params[4]) {
2761 e = Params[1]*x + Params[2];
2763 Val = pow(e, Params[0]) + Params[5];
2768 Val = x*Params[3] + Params[6];
2770 return (cmsFloat32Number) Val;
2774 cmsFloat32Number param_6(cmsFloat32Number x, const cmsFloat64Number Params[])
2776 cmsFloat64Number e, Val;
2778 e = Params[1]*x + Params[2];
2780 Val = pow(e, Params[0]) + Params[3];
2784 return (cmsFloat32Number) Val;
2788 cmsFloat32Number param_7(cmsFloat32Number x, const cmsFloat64Number Params[])
2790 cmsFloat64Number Val;
2793 Val = Params[1]*log10(Params[2] * pow(x, Params[0]) + Params[3]) + Params[4];
2795 return (cmsFloat32Number) Val;
2800 cmsFloat32Number param_8(cmsFloat32Number x, const cmsFloat64Number Params[])
2802 cmsFloat64Number Val;
2804 Val = (Params[0] * pow(Params[1], Params[2] * x + Params[3]) + Params[4]);
2806 return (cmsFloat32Number) Val;
2811 cmsFloat32Number sigmoidal(cmsFloat32Number x, const cmsFloat64Number Params[])
2813 cmsFloat64Number Val;
2815 Val = pow(1.0 - pow(1 - x, 1/Params[0]), 1/Params[0]);
2817 return (cmsFloat32Number) Val;
2822 cmsBool CheckSingleParametric(const char* Name, dblfnptr fn, cmsInt32Number Type, const cmsFloat64Number Params[])
2827 char InverseText[256];
2829 tc = cmsBuildParametricToneCurve(DbgThread(), Type, Params);
2830 tc_1 = cmsBuildParametricToneCurve(DbgThread(), -Type, Params);
2832 for (i=0; i <= 1000; i++) {
2834 cmsFloat32Number x = (cmsFloat32Number) i / 1000;
2835 cmsFloat32Number y_fn, y_param, x_param, y_param2;
2837 y_fn = fn(x, Params);
2838 y_param = cmsEvalToneCurveFloat(tc, x);
2839 x_param = cmsEvalToneCurveFloat(tc_1, y_param);
2841 y_param2 = fn(x_param, Params);
2843 if (!IsGoodVal(Name, y_fn, y_param, FIXED_PRECISION_15_16))
2846 sprintf(InverseText, "Inverse %s", Name);
2847 if (!IsGoodVal(InverseText, y_fn, y_param2, FIXED_PRECISION_15_16))
2851 cmsFreeToneCurve(tc);
2852 cmsFreeToneCurve(tc_1);
2856 cmsFreeToneCurve(tc);
2857 cmsFreeToneCurve(tc_1);
2861 // Check against some known values
2863 cmsInt32Number CheckParametricToneCurves(void)
2865 cmsFloat64Number Params[10];
2871 if (!CheckSingleParametric("Gamma", Gamma, 1, Params)) return 0;
2874 // Y = (aX + b)^Gamma | X >= -b/a
2881 if (!CheckSingleParametric("CIE122-1966", CIE122, 2, Params)) return 0;
2884 // Y = (aX + b)^Gamma | X <= -b/a
2893 if (!CheckSingleParametric("IEC 61966-3", IEC61966_3, 3, Params)) return 0;
2895 // 4) IEC 61966-2.1 (sRGB)
2896 // Y = (aX + b)^Gamma | X >= d
2900 Params[1] = 1. / 1.055;
2901 Params[2] = 0.055 / 1.055;
2902 Params[3] = 1. / 12.92;
2903 Params[4] = 0.04045;
2905 if (!CheckSingleParametric("IEC 61966-2.1", IEC61966_21, 4, Params)) return 0;
2908 // 5) Y = (aX + b)^Gamma + e | X >= d
2909 // Y = cX + f | else
2919 if (!CheckSingleParametric("param_5", param_5, 5, Params)) return 0;
2921 // 6) Y = (aX + b) ^ Gamma + c
2928 if (!CheckSingleParametric("param_6", param_6, 6, Params)) return 0;
2930 // 7) Y = a * log (b * X^Gamma + c) + d
2938 if (!CheckSingleParametric("param_7", param_7, 7, Params)) return 0;
2940 // 8) Y = a * b ^ (c*X+d) + e
2948 if (!CheckSingleParametric("param_8", param_8, 8, Params)) return 0;
2950 // 108: S-Shaped: (1 - (1-x)^1/g)^1/g
2953 if (!CheckSingleParametric("sigmoidal", sigmoidal, 108, Params)) return 0;
2960 // LUT checks ------------------------------------------------------------------------------
2963 cmsInt32Number CheckLUTcreation(void)
2967 cmsInt32Number n1, n2;
2969 lut = cmsPipelineAlloc(DbgThread(), 1, 1);
2970 n1 = cmsPipelineStageCount(lut);
2971 lut2 = cmsPipelineDup(lut);
2972 n2 = cmsPipelineStageCount(lut2);
2974 cmsPipelineFree(lut);
2975 cmsPipelineFree(lut2);
2977 return (n1 == 0) && (n2 == 0);
2980 // Create a MPE for a identity matrix
2982 void AddIdentityMatrix(cmsPipeline* lut)
2984 const cmsFloat64Number Identity[] = { 1, 0, 0,
2989 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocMatrix(DbgThread(), 3, 3, Identity, NULL));
2992 // Create a MPE for identity cmsFloat32Number CLUT
2994 void AddIdentityCLUTfloat(cmsPipeline* lut)
2996 const cmsFloat32Number Table[] = {
3011 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLutFloat(DbgThread(), 2, 3, 3, Table));
3014 // Create a MPE for identity cmsFloat32Number CLUT
3016 void AddIdentityCLUT16(cmsPipeline* lut)
3018 const cmsUInt16Number Table[] = {
3030 0xffff, 0xffff, 0xffff
3034 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table));
3038 // Create a 3 fn identity curves
3041 void Add3GammaCurves(cmsPipeline* lut, cmsFloat64Number Curve)
3043 cmsToneCurve* id = cmsBuildGamma(DbgThread(), Curve);
3044 cmsToneCurve* id3[3];
3050 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, id3));
3052 cmsFreeToneCurve(id);
3057 cmsInt32Number CheckFloatLUT(cmsPipeline* lut)
3059 cmsInt32Number n1, i, j;
3060 cmsFloat32Number Inf[3], Outf[3];
3064 for (j=0; j < 65535; j++) {
3066 cmsInt32Number af[3];
3068 Inf[0] = Inf[1] = Inf[2] = (cmsFloat32Number) j / 65535.0F;
3069 cmsPipelineEvalFloat(Inf, Outf, lut);
3071 af[0] = (cmsInt32Number) floor(Outf[0]*65535.0 + 0.5);
3072 af[1] = (cmsInt32Number) floor(Outf[1]*65535.0 + 0.5);
3073 af[2] = (cmsInt32Number) floor(Outf[2]*65535.0 + 0.5);
3075 for (i=0; i < 3; i++) {
3089 cmsInt32Number Check16LUT(cmsPipeline* lut)
3091 cmsInt32Number n2, i, j;
3092 cmsUInt16Number Inw[3], Outw[3];
3096 for (j=0; j < 65535; j++) {
3098 cmsInt32Number aw[3];
3100 Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
3101 cmsPipelineEval16(Inw, Outw, lut);
3106 for (i=0; i < 3; i++) {
3119 // Check any LUT that is linear
3121 cmsInt32Number CheckStagesLUT(cmsPipeline* lut, cmsInt32Number ExpectedStages)
3124 cmsInt32Number nInpChans, nOutpChans, nStages;
3126 nInpChans = cmsPipelineInputChannels(lut);
3127 nOutpChans = cmsPipelineOutputChannels(lut);
3128 nStages = cmsPipelineStageCount(lut);
3130 return (nInpChans == 3) && (nOutpChans == 3) && (nStages == ExpectedStages);
3135 cmsInt32Number CheckFullLUT(cmsPipeline* lut, cmsInt32Number ExpectedStages)
3137 cmsInt32Number rc = CheckStagesLUT(lut, ExpectedStages) && Check16LUT(lut) && CheckFloatLUT(lut);
3139 cmsPipelineFree(lut);
3145 cmsInt32Number Check1StageLUT(void)
3147 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3149 AddIdentityMatrix(lut);
3150 return CheckFullLUT(lut, 1);
3156 cmsInt32Number Check2StageLUT(void)
3158 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3160 AddIdentityMatrix(lut);
3161 AddIdentityCLUTfloat(lut);
3163 return CheckFullLUT(lut, 2);
3167 cmsInt32Number Check2Stage16LUT(void)
3169 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3171 AddIdentityMatrix(lut);
3172 AddIdentityCLUT16(lut);
3174 return CheckFullLUT(lut, 2);
3180 cmsInt32Number Check3StageLUT(void)
3182 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3184 AddIdentityMatrix(lut);
3185 AddIdentityCLUTfloat(lut);
3186 Add3GammaCurves(lut, 1.0);
3188 return CheckFullLUT(lut, 3);
3192 cmsInt32Number Check3Stage16LUT(void)
3194 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3196 AddIdentityMatrix(lut);
3197 AddIdentityCLUT16(lut);
3198 Add3GammaCurves(lut, 1.0);
3200 return CheckFullLUT(lut, 3);
3206 cmsInt32Number Check4StageLUT(void)
3208 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3210 AddIdentityMatrix(lut);
3211 AddIdentityCLUTfloat(lut);
3212 Add3GammaCurves(lut, 1.0);
3213 AddIdentityMatrix(lut);
3215 return CheckFullLUT(lut, 4);
3219 cmsInt32Number Check4Stage16LUT(void)
3221 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3223 AddIdentityMatrix(lut);
3224 AddIdentityCLUT16(lut);
3225 Add3GammaCurves(lut, 1.0);
3226 AddIdentityMatrix(lut);
3228 return CheckFullLUT(lut, 4);
3232 cmsInt32Number Check5StageLUT(void)
3234 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3236 AddIdentityMatrix(lut);
3237 AddIdentityCLUTfloat(lut);
3238 Add3GammaCurves(lut, 1.0);
3239 AddIdentityMatrix(lut);
3240 Add3GammaCurves(lut, 1.0);
3242 return CheckFullLUT(lut, 5);
3247 cmsInt32Number Check5Stage16LUT(void)
3249 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3251 AddIdentityMatrix(lut);
3252 AddIdentityCLUT16(lut);
3253 Add3GammaCurves(lut, 1.0);
3254 AddIdentityMatrix(lut);
3255 Add3GammaCurves(lut, 1.0);
3257 return CheckFullLUT(lut, 5);
3261 cmsInt32Number Check6StageLUT(void)
3263 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3265 AddIdentityMatrix(lut);
3266 Add3GammaCurves(lut, 1.0);
3267 AddIdentityCLUTfloat(lut);
3268 Add3GammaCurves(lut, 1.0);
3269 AddIdentityMatrix(lut);
3270 Add3GammaCurves(lut, 1.0);
3272 return CheckFullLUT(lut, 6);
3276 cmsInt32Number Check6Stage16LUT(void)
3278 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3280 AddIdentityMatrix(lut);
3281 Add3GammaCurves(lut, 1.0);
3282 AddIdentityCLUT16(lut);
3283 Add3GammaCurves(lut, 1.0);
3284 AddIdentityMatrix(lut);
3285 Add3GammaCurves(lut, 1.0);
3287 return CheckFullLUT(lut, 6);
3292 cmsInt32Number CheckLab2LabLUT(void)
3294 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3297 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3298 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3300 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3302 cmsPipelineFree(lut);
3309 cmsInt32Number CheckXYZ2XYZLUT(void)
3311 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3314 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3315 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3317 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3319 cmsPipelineFree(lut);
3327 cmsInt32Number CheckLab2LabMatLUT(void)
3329 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3332 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3333 AddIdentityMatrix(lut);
3334 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3336 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 3);
3338 cmsPipelineFree(lut);
3344 cmsInt32Number CheckNamedColorLUT(void)
3346 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3347 cmsNAMEDCOLORLIST* nc;
3348 cmsInt32Number i,j, rc = 1, n2;
3349 cmsUInt16Number PCS[3];
3350 cmsUInt16Number Colorant[cmsMAXCHANNELS];
3352 cmsUInt16Number Inw[3], Outw[3];
3356 nc = cmsAllocNamedColorList(DbgThread(), 256, 3, "pre", "post");
3357 if (nc == NULL) return 0;
3359 for (i=0; i < 256; i++) {
3361 PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
3362 Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) i;
3364 sprintf(Name, "#%d", i);
3365 if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
3368 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocNamedColor(nc, FALSE));
3370 cmsFreeNamedColorList(nc);
3371 if (rc == 0) return 0;
3375 for (j=0; j < 256; j++) {
3377 Inw[0] = (cmsUInt16Number) j;
3379 cmsPipelineEval16(Inw, Outw, lut);
3380 for (i=0; i < 3; i++) {
3389 cmsPipelineFree(lut);
3395 // --------------------------------------------------------------------------------------------
3397 // A lightweight test of multilocalized unicode structures.
3400 cmsInt32Number CheckMLU(void)
3402 cmsMLU* mlu, *mlu2, *mlu3;
3403 char Buffer[256], Buffer2[256];
3404 cmsInt32Number rc = 1;
3406 cmsHPROFILE h= NULL;
3408 // Allocate a MLU structure, no preferred size
3409 mlu = cmsMLUalloc(DbgThread(), 0);
3411 // Add some localizations
3412 cmsMLUsetWide(mlu, "en", "US", L"Hello, world");
3413 cmsMLUsetWide(mlu, "es", "ES", L"Hola, mundo");
3414 cmsMLUsetWide(mlu, "fr", "FR", L"Bonjour, le monde");
3415 cmsMLUsetWide(mlu, "ca", "CA", L"Hola, mon");
3418 // Check the returned string for each language
3420 cmsMLUgetASCII(mlu, "en", "US", Buffer, 256);
3421 if (strcmp(Buffer, "Hello, world") != 0) rc = 0;
3424 cmsMLUgetASCII(mlu, "es", "ES", Buffer, 256);
3425 if (strcmp(Buffer, "Hola, mundo") != 0) rc = 0;
3428 cmsMLUgetASCII(mlu, "fr", "FR", Buffer, 256);
3429 if (strcmp(Buffer, "Bonjour, le monde") != 0) rc = 0;
3432 cmsMLUgetASCII(mlu, "ca", "CA", Buffer, 256);
3433 if (strcmp(Buffer, "Hola, mon") != 0) rc = 0;
3436 Fail("Unexpected string '%s'", Buffer);
3441 // Now for performance, allocate an empty struct
3442 mlu = cmsMLUalloc(DbgThread(), 0);
3444 // Fill it with several thousands of different lenguages
3445 for (i=0; i < 4096; i++) {
3449 Lang[0] = (char) (i % 255);
3450 Lang[1] = (char) (i / 255);
3453 sprintf(Buffer, "String #%i", i);
3454 cmsMLUsetASCII(mlu, Lang, Lang, Buffer);
3458 mlu2 = cmsMLUdup(mlu);
3460 // Get rid of original
3463 // Check all is still in place
3464 for (i=0; i < 4096; i++) {
3468 Lang[0] = (char)(i % 255);
3469 Lang[1] = (char)(i / 255);
3472 cmsMLUgetASCII(mlu2, Lang, Lang, Buffer2, 256);
3473 sprintf(Buffer, "String #%i", i);
3475 if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
3479 Fail("Unexpected string '%s'", Buffer2);
3483 h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "w");
3485 cmsSetProfileVersion(h, 4.3);
3487 cmsWriteTag(h, cmsSigProfileDescriptionTag, mlu2);
3492 h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "r");
3494 mlu3 = cmsReadTag(h, cmsSigProfileDescriptionTag);
3495 if (mlu3 == NULL) { Fail("Profile didn't get the MLU\n"); rc = 0; goto Error; }
3497 // Check all is still in place
3498 for (i=0; i < 4096; i++) {
3502 Lang[0] = (char) (i % 255);
3503 Lang[1] = (char) (i / 255);
3506 cmsMLUgetASCII(mlu3, Lang, Lang, Buffer2, 256);
3507 sprintf(Buffer, "String #%i", i);
3509 if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
3512 if (rc == 0) Fail("Unexpected string '%s'", Buffer2);
3516 if (h != NULL) cmsCloseProfile(h);
3517 remove("mlucheck.icc");
3523 // A lightweight test of named color structures.
3525 cmsInt32Number CheckNamedColorList(void)
3527 cmsNAMEDCOLORLIST* nc = NULL, *nc2;
3528 cmsInt32Number i, j, rc=1;
3530 cmsUInt16Number PCS[3];
3531 cmsUInt16Number Colorant[cmsMAXCHANNELS];
3532 char CheckName[255];
3533 cmsUInt16Number CheckPCS[3];
3534 cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
3537 nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
3538 if (nc == NULL) return 0;
3540 for (i=0; i < 4096; i++) {
3543 PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
3544 Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) (4096 - i);
3546 sprintf(Name, "#%d", i);
3547 if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
3550 for (i=0; i < 4096; i++) {
3552 CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
3553 CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (4096 - i);
3555 sprintf(CheckName, "#%d", i);
3556 if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
3559 for (j=0; j < 3; j++) {
3560 if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
3563 for (j=0; j < 4; j++) {
3564 if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
3567 if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
3570 h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "w");
3571 if (h == NULL) return 0;
3572 if (!cmsWriteTag(h, cmsSigNamedColor2Tag, nc)) return 0;
3574 cmsFreeNamedColorList(nc);
3577 h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "r");
3578 nc2 = cmsReadTag(h, cmsSigNamedColor2Tag);
3580 if (cmsNamedColorCount(nc2) != 4096) { rc = 0; Fail("Invalid count"); goto Error; }
3582 i = cmsNamedColorIndex(nc2, "#123");
3583 if (i != 123) { rc = 0; Fail("Invalid index"); goto Error; }
3586 for (i=0; i < 4096; i++) {
3588 CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
3589 CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (4096 - i);
3591 sprintf(CheckName, "#%d", i);
3592 if (!cmsNamedColorInfo(nc2, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
3595 for (j=0; j < 3; j++) {
3596 if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
3599 for (j=0; j < 4; j++) {
3600 if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
3603 if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
3607 remove("namedcol.icc");
3610 if (nc != NULL) cmsFreeNamedColorList(nc);
3616 // ----------------------------------------------------------------------------------------------------------
3620 static cmsBool FormatterFailed;
3623 void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text)
3625 cmsUInt16Number Values[cmsMAXCHANNELS];
3626 cmsUInt8Number Buffer[1024];
3628 cmsInt32Number i, j, nChannels, bytes;
3632 if (FormatterFailed) return;
3634 memset(&info, 0, sizeof(info));
3635 info.OutputFormat = info.InputFormat = Type;
3637 // Go forth and back
3638 f = _cmsGetFormatter(Type, cmsFormatterInput, 0);
3639 b = _cmsGetFormatter(Type, cmsFormatterOutput, 0);
3641 if (f.Fmt16 == NULL || b.Fmt16 == NULL) {
3642 Fail("no formatter for %s", Text);
3643 FormatterFailed = TRUE;
3646 f = _cmsGetFormatter(Type, cmsFormatterInput, 0);
3647 b = _cmsGetFormatter(Type, cmsFormatterOutput, 0);
3651 nChannels = T_CHANNELS(Type);
3652 bytes = T_BYTES(Type);
3654 for (j=0; j < 5; j++) {
3656 for (i=0; i < nChannels; i++) {
3657 Values[i] = (cmsUInt16Number) (i+j);
3663 b.Fmt16(&info, Values, Buffer, 1);
3664 memset(Values, 0, sizeof(Values));
3665 f.Fmt16(&info, Values, Buffer, 1);
3667 for (i=0; i < nChannels; i++) {
3671 if (Values[i] != i+j) {
3673 Fail("%s failed", Text);
3674 FormatterFailed = TRUE;
3677 for (i=0; i < nChannels; i++) {
3678 Values[i] = (cmsUInt16Number) (i+j);
3684 b.Fmt16(&info, Values, Buffer, 1);
3685 f.Fmt16(&info, Values, Buffer, 1);
3692 #define C(a) CheckSingleFormatter16(a, #a)
3695 // Check all formatters
3697 cmsInt32Number CheckFormatters16(void)
3699 FormatterFailed = FALSE;
3702 C( TYPE_GRAY_8_REV );
3704 C( TYPE_GRAY_16_REV );
3705 C( TYPE_GRAY_16_SE );
3708 C( TYPE_GRAYA_16_SE );
3709 C( TYPE_GRAYA_8_PLANAR );
3710 C( TYPE_GRAYA_16_PLANAR );
3712 C( TYPE_RGB_8_PLANAR );
3714 C( TYPE_BGR_8_PLANAR );
3716 C( TYPE_RGB_16_PLANAR );
3717 C( TYPE_RGB_16_SE );
3719 C( TYPE_BGR_16_PLANAR );
3720 C( TYPE_BGR_16_SE );
3722 C( TYPE_RGBA_8_PLANAR );
3724 C( TYPE_RGBA_16_PLANAR );
3725 C( TYPE_RGBA_16_SE );
3730 C( TYPE_ABGR_16_PLANAR );
3731 C( TYPE_ABGR_16_SE );
3734 C( TYPE_BGRA_16_SE );
3736 C( TYPE_CMY_8_PLANAR );
3738 C( TYPE_CMY_16_PLANAR );
3739 C( TYPE_CMY_16_SE );
3742 C( TYPE_CMYK_8_REV );
3744 C( TYPE_CMYK_8_PLANAR );
3746 C( TYPE_CMYK_16_REV );
3748 C( TYPE_CMYK_16_PLANAR );
3749 C( TYPE_CMYK_16_SE );
3752 C( TYPE_KYMC_16_SE );
3754 C( TYPE_KCMY_8_REV );
3756 C( TYPE_KCMY_16_REV );
3757 C( TYPE_KCMY_16_SE );
3760 C( TYPE_CMYK5_16_SE );
3763 C( TYPE_KYMC5_16_SE );
3765 C( TYPE_CMYK6_8_PLANAR );
3767 C( TYPE_CMYK6_16_PLANAR );
3768 C( TYPE_CMYK6_16_SE );
3771 C( TYPE_CMYK7_16_SE );
3774 C( TYPE_KYMC7_16_SE );
3777 C( TYPE_CMYK8_16_SE );
3780 C( TYPE_KYMC8_16_SE );
3783 C( TYPE_CMYK9_16_SE );
3786 C( TYPE_KYMC9_16_SE );
3788 C( TYPE_CMYK10_16 );
3789 C( TYPE_CMYK10_16_SE );
3791 C( TYPE_KYMC10_16 );
3792 C( TYPE_KYMC10_16_SE );
3794 C( TYPE_CMYK11_16 );
3795 C( TYPE_CMYK11_16_SE );
3797 C( TYPE_KYMC11_16 );
3798 C( TYPE_KYMC11_16_SE );
3800 C( TYPE_CMYK12_16 );
3801 C( TYPE_CMYK12_16_SE );
3803 C( TYPE_KYMC12_16 );
3804 C( TYPE_KYMC12_16_SE );
3811 C( TYPE_YCbCr_8_PLANAR );
3813 C( TYPE_YCbCr_16_PLANAR );
3814 C( TYPE_YCbCr_16_SE );
3816 C( TYPE_YUV_8_PLANAR );
3818 C( TYPE_YUV_16_PLANAR );
3819 C( TYPE_YUV_16_SE );
3821 C( TYPE_HLS_8_PLANAR );
3823 C( TYPE_HLS_16_PLANAR );
3824 C( TYPE_HLS_16_SE );
3826 C( TYPE_HSV_8_PLANAR );
3828 C( TYPE_HSV_16_PLANAR );
3829 C( TYPE_HSV_16_SE );
3850 return FormatterFailed == 0 ? 1 : 0;
3855 void CheckSingleFormatterFloat(cmsUInt32Number Type, const char* Text)
3857 cmsFloat32Number Values[cmsMAXCHANNELS];
3858 cmsUInt8Number Buffer[1024];
3860 cmsInt32Number i, j, nChannels;
3864 if (FormatterFailed) return;
3866 memset(&info, 0, sizeof(info));
3867 info.OutputFormat = info.InputFormat = Type;
3869 // Go forth and back
3870 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
3871 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
3873 if (f.FmtFloat == NULL || b.FmtFloat == NULL) {
3874 Fail("no formatter for %s", Text);
3875 FormatterFailed = TRUE;
3878 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
3879 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
3883 nChannels = T_CHANNELS(Type);
3885 for (j=0; j < 5; j++) {
3887 for (i=0; i < nChannels; i++) {
3888 Values[i] = (cmsFloat32Number) (i+j);
3891 b.FmtFloat(&info, Values, Buffer, 1);
3892 memset(Values, 0, sizeof(Values));
3893 f.FmtFloat(&info, Values, Buffer, 1);
3895 for (i=0; i < nChannels; i++) {
3897 cmsFloat64Number delta = fabs(Values[i] - ( i+j));
3899 if (delta > 0.000000001) {
3901 Fail("%s failed", Text);
3902 FormatterFailed = TRUE;
3905 for (i=0; i < nChannels; i++) {
3906 Values[i] = (cmsFloat32Number) (i+j);
3909 b.FmtFloat(&info, Values, Buffer, 1);
3910 f.FmtFloat(&info, Values, Buffer, 1);
3917 #define C(a) CheckSingleFormatterFloat(a, #a)
3920 cmsInt32Number CheckFormattersFloat(void)
3922 FormatterFailed = FALSE;
3941 return FormatterFailed == 0 ? 1 : 0;
3947 cmsInt32Number CheckOneRGB(cmsHTRANSFORM xform, cmsUInt32Number R, cmsUInt32Number G, cmsUInt32Number B, cmsUInt32Number Ro, cmsUInt32Number Go, cmsUInt32Number Bo)
3949 cmsUInt16Number RGB[3];
3950 cmsUInt16Number Out[3];
3956 cmsDoTransform(xform, RGB, Out, 1);
3958 return IsGoodWord("R", Ro , Out[0]) &&
3959 IsGoodWord("G", Go , Out[1]) &&
3960 IsGoodWord("B", Bo , Out[2]);
3963 // Check known values going from sRGB to XYZ
3965 cmsInt32Number CheckOneRGB_double(cmsHTRANSFORM xform, cmsFloat64Number R, cmsFloat64Number G, cmsFloat64Number B, cmsFloat64Number Ro, cmsFloat64Number Go, cmsFloat64Number Bo)
3967 cmsFloat64Number RGB[3];
3968 cmsFloat64Number Out[3];
3974 cmsDoTransform(xform, RGB, Out, 1);
3976 return IsGoodVal("R", Ro , Out[0], 0.01) &&
3977 IsGoodVal("G", Go , Out[1], 0.01) &&
3978 IsGoodVal("B", Bo , Out[2], 0.01);
3983 cmsInt32Number CheckChangeBufferFormat(void)
3985 cmsHPROFILE hsRGB = cmsCreate_sRGBProfile();
3986 cmsHTRANSFORM xform;
3989 xform = cmsCreateTransform(hsRGB, TYPE_RGB_16, hsRGB, TYPE_RGB_16, INTENT_PERCEPTUAL, 0);
3990 cmsCloseProfile(hsRGB);
3991 if (xform == NULL) return 0;
3994 if (!CheckOneRGB(xform, 0, 0, 0, 0, 0, 0)) return 0;
3995 if (!CheckOneRGB(xform, 120, 0, 0, 120, 0, 0)) return 0;
3996 if (!CheckOneRGB(xform, 0, 222, 255, 0, 222, 255)) return 0;
3999 if (!cmsChangeBuffersFormat(xform, TYPE_BGR_16, TYPE_RGB_16)) return 0;
4001 if (!CheckOneRGB(xform, 0, 0, 123, 123, 0, 0)) return 0;
4002 if (!CheckOneRGB(xform, 154, 234, 0, 0, 234, 154)) return 0;
4004 if (!cmsChangeBuffersFormat(xform, TYPE_RGB_DBL, TYPE_RGB_DBL)) return 0;
4006 if (!CheckOneRGB_double(xform, 0.20, 0, 0, 0.20, 0, 0)) return 0;
4007 if (!CheckOneRGB_double(xform, 0, 0.9, 1, 0, 0.9, 1)) return 0;
4009 cmsDeleteTransform(xform);
4015 // Write tag testbed ----------------------------------------------------------------------------------------
4018 cmsInt32Number CheckXYZ(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4027 XYZ.X = 1.0; XYZ.Y = 1.1; XYZ.Z = 1.2;
4028 return cmsWriteTag(hProfile, tag, &XYZ);
4031 Pt = cmsReadTag(hProfile, tag);
4032 if (Pt == NULL) return 0;
4033 return IsGoodFixed15_16("X", 1.0, Pt ->X) &&
4034 IsGoodFixed15_16("Y", 1.1, Pt->Y) &&
4035 IsGoodFixed15_16("Z", 1.2, Pt -> Z);
4044 cmsInt32Number CheckGamma(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4046 cmsToneCurve *g, *Pt;
4053 g = cmsBuildGamma(DbgThread(), 1.0);
4054 rc = cmsWriteTag(hProfile, tag, g);
4055 cmsFreeToneCurve(g);
4059 Pt = cmsReadTag(hProfile, tag);
4060 if (Pt == NULL) return 0;
4061 return cmsIsToneCurveLinear(Pt);
4069 cmsInt32Number CheckText(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4079 m = cmsMLUalloc(DbgThread(), 0);
4080 cmsMLUsetASCII(m, cmsNoLanguage, cmsNoCountry, "Test test");
4081 rc = cmsWriteTag(hProfile, tag, m);
4086 Pt = cmsReadTag(hProfile, tag);
4087 if (Pt == NULL) return 0;
4088 cmsMLUgetASCII(Pt, cmsNoLanguage, cmsNoCountry, Buffer, 256);
4089 return strcmp(Buffer, "Test test") == 0;
4097 cmsInt32Number CheckData(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4100 cmsICCData d = { 1, 0, { '?' }};
4107 rc = cmsWriteTag(hProfile, tag, &d);
4111 Pt = cmsReadTag(hProfile, tag);
4112 if (Pt == NULL) return 0;
4113 return (Pt ->data[0] == '?') && (Pt ->flag == 0) && (Pt ->len == 1);
4122 cmsInt32Number CheckSignature(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4124 cmsTagSignature *Pt, Holder;
4129 Holder = cmsSigPerceptualReferenceMediumGamut;
4130 return cmsWriteTag(hProfile, tag, &Holder);
4133 Pt = cmsReadTag(hProfile, tag);
4134 if (Pt == NULL) return 0;
4135 return *Pt == cmsSigPerceptualReferenceMediumGamut;
4144 cmsInt32Number CheckDateTime(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4146 struct tm *Pt, Holder;
4157 Holder.tm_year = 2009 - 1900;
4158 return cmsWriteTag(hProfile, tag, &Holder);
4161 Pt = cmsReadTag(hProfile, tag);
4162 if (Pt == NULL) return 0;
4164 return (Pt ->tm_hour == 1 &&
4167 Pt ->tm_mday == 4 &&
4169 Pt ->tm_year == 2009 - 1900);
4179 cmsInt32Number CheckNamedColor(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag, cmsInt32Number max_check, cmsBool colorant_check)
4181 cmsNAMEDCOLORLIST* nc;
4182 cmsInt32Number i, j, rc;
4184 cmsUInt16Number PCS[3];
4185 cmsUInt16Number Colorant[cmsMAXCHANNELS];
4186 char CheckName[255];
4187 cmsUInt16Number CheckPCS[3];
4188 cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
4194 nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
4195 if (nc == NULL) return 0;
4197 for (i=0; i < max_check; i++) {
4199 PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
4200 Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) (max_check - i);
4202 sprintf(Name, "#%d", i);
4203 if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { Fail("Couldn't append named color"); return 0; }
4206 rc = cmsWriteTag(hProfile, tag, nc);
4207 cmsFreeNamedColorList(nc);
4212 nc = cmsReadTag(hProfile, tag);
4213 if (nc == NULL) return 0;
4215 for (i=0; i < max_check; i++) {
4217 CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
4218 CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (max_check - i);
4220 sprintf(CheckName, "#%d", i);
4221 if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { Fail("Invalid string"); return 0; }
4224 for (j=0; j < 3; j++) {
4225 if (CheckPCS[j] != PCS[j]) { Fail("Invalid PCS"); return 0; }
4228 // This is only used on named color list
4229 if (colorant_check) {
4231 for (j=0; j < 4; j++) {
4232 if (CheckColorant[j] != Colorant[j]) { Fail("Invalid Colorant"); return 0; };
4236 if (strcmp(Name, CheckName) != 0) { Fail("Invalid Name"); return 0; };
4247 cmsInt32Number CheckLUT(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4249 cmsPipeline* Lut, *Pt;
4257 Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
4258 if (Lut == NULL) return 0;
4260 // Create an identity LUT
4261 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(DbgThread(), 3));
4262 cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCLut(DbgThread(), 3));
4263 cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCurves(DbgThread(), 3));
4265 rc = cmsWriteTag(hProfile, tag, Lut);
4266 cmsPipelineFree(Lut);
4270 Pt = cmsReadTag(hProfile, tag);
4271 if (Pt == NULL) return 0;
4273 // Transform values, check for identity
4274 return Check16LUT(Pt);
4282 cmsInt32Number CheckCHAD(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4284 cmsFloat64Number *Pt;
4285 cmsFloat64Number CHAD[] = { 0, .1, .2, .3, .4, .5, .6, .7, .8 };
4291 return cmsWriteTag(hProfile, tag, CHAD);
4295 Pt = cmsReadTag(hProfile, tag);
4296 if (Pt == NULL) return 0;
4298 for (i=0; i < 9; i++) {
4299 if (!IsGoodFixed15_16("CHAD", Pt[i], CHAD[i])) return 0;
4310 cmsInt32Number CheckChromaticity(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4312 cmsCIExyYTRIPLE *Pt, c = { {0, .1, 1 }, { .3, .4, 1 }, { .6, .7, 1 }};
4317 return cmsWriteTag(hProfile, tag, &c);
4321 Pt = cmsReadTag(hProfile, tag);
4322 if (Pt == NULL) return 0;
4324 if (!IsGoodFixed15_16("xyY", Pt ->Red.x, c.Red.x)) return 0;
4325 if (!IsGoodFixed15_16("xyY", Pt ->Red.y, c.Red.y)) return 0;
4326 if (!IsGoodFixed15_16("xyY", Pt ->Green.x, c.Green.x)) return 0;
4327 if (!IsGoodFixed15_16("xyY", Pt ->Green.y, c.Green.y)) return 0;
4328 if (!IsGoodFixed15_16("xyY", Pt ->Blue.x, c.Blue.x)) return 0;
4329 if (!IsGoodFixed15_16("xyY", Pt ->Blue.y, c.Blue.y)) return 0;
4339 cmsInt32Number CheckColorantOrder(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4341 cmsUInt8Number *Pt, c[cmsMAXCHANNELS];
4347 for (i=0; i < cmsMAXCHANNELS; i++) c[i] = (cmsUInt8Number) (cmsMAXCHANNELS - i - 1);
4348 return cmsWriteTag(hProfile, tag, c);
4352 Pt = cmsReadTag(hProfile, tag);
4353 if (Pt == NULL) return 0;
4355 for (i=0; i < cmsMAXCHANNELS; i++) {
4356 if (Pt[i] != ( cmsMAXCHANNELS - i - 1 )) return 0;
4366 cmsInt32Number CheckMeasurement(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4368 cmsICCMeasurementConditions *Pt, m;
4378 m.IlluminantType = cmsILLUMINANT_TYPE_D50;
4380 return cmsWriteTag(hProfile, tag, &m);
4384 Pt = cmsReadTag(hProfile, tag);
4385 if (Pt == NULL) return 0;
4387 if (!IsGoodFixed15_16("Backing", Pt ->Backing.X, 0.1)) return 0;
4388 if (!IsGoodFixed15_16("Backing", Pt ->Backing.Y, 0.2)) return 0;
4389 if (!IsGoodFixed15_16("Backing", Pt ->Backing.Z, 0.3)) return 0;
4390 if (!IsGoodFixed15_16("Flare", Pt ->Flare, 1.0)) return 0;
4392 if (Pt ->Geometry != 1) return 0;
4393 if (Pt ->IlluminantType != cmsILLUMINANT_TYPE_D50) return 0;
4394 if (Pt ->Observer != 1) return 0;
4404 cmsInt32Number CheckUcrBg(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4413 m.Ucr = cmsBuildGamma(DbgThread(), 2.4);
4414 m.Bg = cmsBuildGamma(DbgThread(), -2.2);
4415 m.Desc = cmsMLUalloc(DbgThread(), 1);
4416 cmsMLUsetASCII(m.Desc, cmsNoLanguage, cmsNoCountry, "test UCR/BG");
4417 rc = cmsWriteTag(hProfile, tag, &m);
4419 cmsFreeToneCurve(m.Bg);
4420 cmsFreeToneCurve(m.Ucr);
4425 Pt = cmsReadTag(hProfile, tag);
4426 if (Pt == NULL) return 0;
4428 cmsMLUgetASCII(Pt ->Desc, cmsNoLanguage, cmsNoCountry, Buffer, 256);
4429 if (strcmp(Buffer, "test UCR/BG") != 0) return 0;
4439 cmsInt32Number CheckCRDinfo(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4448 mlu = cmsMLUalloc(DbgThread(), 5);
4450 cmsMLUsetWide(mlu, "PS", "nm", L"test postscript");
4451 cmsMLUsetWide(mlu, "PS", "#0", L"perceptual");
4452 cmsMLUsetWide(mlu, "PS", "#1", L"relative_colorimetric");
4453 cmsMLUsetWide(mlu, "PS", "#2", L"saturation");
4454 cmsMLUsetWide(mlu, "PS", "#3", L"absolute_colorimetric");
4455 rc = cmsWriteTag(hProfile, tag, mlu);
4461 mlu = (cmsMLU*) cmsReadTag(hProfile, tag);
4462 if (mlu == NULL) return 0;
4466 cmsMLUgetASCII(mlu, "PS", "nm", Buffer, 256);
4467 if (strcmp(Buffer, "test postscript") != 0) return 0;
4470 cmsMLUgetASCII(mlu, "PS", "#0", Buffer, 256);
4471 if (strcmp(Buffer, "perceptual") != 0) return 0;
4474 cmsMLUgetASCII(mlu, "PS", "#1", Buffer, 256);
4475 if (strcmp(Buffer, "relative_colorimetric") != 0) return 0;
4478 cmsMLUgetASCII(mlu, "PS", "#2", Buffer, 256);
4479 if (strcmp(Buffer, "saturation") != 0) return 0;
4482 cmsMLUgetASCII(mlu, "PS", "#3", Buffer, 256);
4483 if (strcmp(Buffer, "absolute_colorimetric") != 0) return 0;
4493 cmsToneCurve *CreateSegmentedCurve(void)
4495 cmsCurveSegment Seg[3];
4496 cmsFloat32Number Sampled[2] = { 0, 1};
4499 Seg[0].Params[0] = 1;
4500 Seg[0].Params[1] = 0;
4501 Seg[0].Params[2] = 0;
4502 Seg[0].Params[3] = 0;
4507 Seg[1].nGridPoints = 2;
4508 Seg[1].SampledPoints = Sampled;
4513 Seg[2].Params[0] = 1;
4514 Seg[2].Params[1] = 0;
4515 Seg[2].Params[2] = 0;
4516 Seg[2].Params[3] = 0;
4520 return cmsBuildSegmentedToneCurve(DbgThread(), 3, Seg);
4525 cmsInt32Number CheckMPE(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4527 cmsPipeline* Lut, *Pt;
4535 Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
4537 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4(DbgThread()));
4538 cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV4ToV2(DbgThread()));
4539 AddIdentityCLUTfloat(Lut);
4541 G[0] = G[1] = G[2] = CreateSegmentedCurve();
4542 cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, G));
4543 cmsFreeToneCurve(G[0]);
4545 rc = cmsWriteTag(hProfile, tag, Lut);
4546 cmsPipelineFree(Lut);
4550 Pt = cmsReadTag(hProfile, tag);
4551 if (Pt == NULL) return 0;
4552 return CheckFloatLUT(Pt);
4561 cmsInt32Number CheckScreening(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4563 cmsScreening *Pt, sc;
4572 sc.Channels[0].Frequency = 2.0;
4573 sc.Channels[0].ScreenAngle = 3.0;
4574 sc.Channels[0].SpotShape = cmsSPOT_ELLIPSE;
4576 rc = cmsWriteTag(hProfile, tag, &sc);
4581 Pt = cmsReadTag(hProfile, tag);
4582 if (Pt == NULL) return 0;
4584 if (Pt ->nChannels != 1) return 0;
4585 if (Pt ->Flag != 0) return 0;
4586 if (!IsGoodFixed15_16("Freq", Pt ->Channels[0].Frequency, 2.0)) return 0;
4587 if (!IsGoodFixed15_16("Angle", Pt ->Channels[0].ScreenAngle, 3.0)) return 0;
4588 if (Pt ->Channels[0].SpotShape != cmsSPOT_ELLIPSE) return 0;
4598 cmsBool CheckOneStr(cmsMLU* mlu, cmsInt32Number n)
4600 char Buffer[256], Buffer2[256];
4603 cmsMLUgetASCII(mlu, "en", "US", Buffer, 255);
4604 sprintf(Buffer2, "Hello, world %d", n);
4605 if (strcmp(Buffer, Buffer2) != 0) return FALSE;
4608 cmsMLUgetASCII(mlu, "es", "ES", Buffer, 255);
4609 sprintf(Buffer2, "Hola, mundo %d", n);
4610 if (strcmp(Buffer, Buffer2) != 0) return FALSE;
4617 void SetOneStr(cmsMLU** mlu, wchar_t* s1, wchar_t* s2)
4619 *mlu = cmsMLUalloc(DbgThread(), 0);
4620 cmsMLUsetWide(*mlu, "en", "US", s1);
4621 cmsMLUsetWide(*mlu, "es", "ES", s2);
4626 cmsInt32Number CheckProfileSequenceTag(cmsInt32Number Pass, cmsHPROFILE hProfile)
4635 s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
4636 if (s == NULL) return 0;
4638 SetOneStr(&s -> seq[0].Manufacturer, L"Hello, world 0", L"Hola, mundo 0");
4639 SetOneStr(&s -> seq[0].Model, L"Hello, world 0", L"Hola, mundo 0");
4640 SetOneStr(&s -> seq[1].Manufacturer, L"Hello, world 1", L"Hola, mundo 1");
4641 SetOneStr(&s -> seq[1].Model, L"Hello, world 1", L"Hola, mundo 1");
4642 SetOneStr(&s -> seq[2].Manufacturer, L"Hello, world 2", L"Hola, mundo 2");
4643 SetOneStr(&s -> seq[2].Model, L"Hello, world 2", L"Hola, mundo 2");
4646 #ifdef CMS_DONT_USE_INT64
4647 s ->seq[0].attributes[0] = cmsTransparency|cmsMatte;
4648 s ->seq[0].attributes[1] = 0;
4650 s ->seq[0].attributes = cmsTransparency|cmsMatte;
4653 #ifdef CMS_DONT_USE_INT64
4654 s ->seq[1].attributes[0] = cmsReflective|cmsMatte;
4655 s ->seq[1].attributes[1] = 0;
4657 s ->seq[1].attributes = cmsReflective|cmsMatte;
4660 #ifdef CMS_DONT_USE_INT64
4661 s ->seq[2].attributes[0] = cmsTransparency|cmsGlossy;
4662 s ->seq[2].attributes[1] = 0;
4664 s ->seq[2].attributes = cmsTransparency|cmsGlossy;
4667 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, s)) return 0;
4668 cmsFreeProfileSequenceDescription(s);
4673 s = cmsReadTag(hProfile, cmsSigProfileSequenceDescTag);
4674 if (s == NULL) return 0;
4676 if (s ->n != 3) return 0;
4678 #ifdef CMS_DONT_USE_INT64
4679 if (s ->seq[0].attributes[0] != (cmsTransparency|cmsMatte)) return 0;
4680 if (s ->seq[0].attributes[1] != 0) return 0;
4682 if (s ->seq[0].attributes != (cmsTransparency|cmsMatte)) return 0;
4685 #ifdef CMS_DONT_USE_INT64
4686 if (s ->seq[1].attributes[0] != (cmsReflective|cmsMatte)) return 0;
4687 if (s ->seq[1].attributes[1] != 0) return 0;
4689 if (s ->seq[1].attributes != (cmsReflective|cmsMatte)) return 0;
4692 #ifdef CMS_DONT_USE_INT64
4693 if (s ->seq[2].attributes[0] != (cmsTransparency|cmsGlossy)) return 0;
4694 if (s ->seq[2].attributes[1] != 0) return 0;
4696 if (s ->seq[2].attributes != (cmsTransparency|cmsGlossy)) return 0;
4700 for (i=0; i < 3; i++) {
4702 if (!CheckOneStr(s -> seq[i].Manufacturer, i)) return 0;
4703 if (!CheckOneStr(s -> seq[i].Model, i)) return 0;
4714 cmsInt32Number CheckProfileSequenceIDTag(cmsInt32Number Pass, cmsHPROFILE hProfile)
4723 s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
4724 if (s == NULL) return 0;
4726 memcpy(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16);
4727 memcpy(s ->seq[1].ProfileID.ID8, "1111111111111111", 16);
4728 memcpy(s ->seq[2].ProfileID.ID8, "2222222222222222", 16);
4731 SetOneStr(&s -> seq[0].Description, L"Hello, world 0", L"Hola, mundo 0");
4732 SetOneStr(&s -> seq[1].Description, L"Hello, world 1", L"Hola, mundo 1");
4733 SetOneStr(&s -> seq[2].Description, L"Hello, world 2", L"Hola, mundo 2");
4735 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, s)) return 0;
4736 cmsFreeProfileSequenceDescription(s);
4741 s = cmsReadTag(hProfile, cmsSigProfileSequenceIdTag);
4742 if (s == NULL) return 0;
4744 if (s ->n != 3) return 0;
4746 if (memcmp(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16) != 0) return 0;
4747 if (memcmp(s ->seq[1].ProfileID.ID8, "1111111111111111", 16) != 0) return 0;
4748 if (memcmp(s ->seq[2].ProfileID.ID8, "2222222222222222", 16) != 0) return 0;
4750 for (i=0; i < 3; i++) {
4752 if (!CheckOneStr(s -> seq[i].Description, i)) return 0;
4764 cmsInt32Number CheckICCViewingConditions(cmsInt32Number Pass, cmsHPROFILE hProfile)
4766 cmsICCViewingConditions* v;
4767 cmsICCViewingConditions s;
4772 s.IlluminantType = 1;
4773 s.IlluminantXYZ.X = 0.1;
4774 s.IlluminantXYZ.Y = 0.2;
4775 s.IlluminantXYZ.Z = 0.3;
4776 s.SurroundXYZ.X = 0.4;
4777 s.SurroundXYZ.Y = 0.5;
4778 s.SurroundXYZ.Z = 0.6;
4780 if (!cmsWriteTag(hProfile, cmsSigViewingConditionsTag, &s)) return 0;
4784 v = cmsReadTag(hProfile, cmsSigViewingConditionsTag);
4785 if (v == NULL) return 0;
4787 if (v ->IlluminantType != 1) return 0;
4788 if (!IsGoodVal("IlluminantXYZ.X", v ->IlluminantXYZ.X, 0.1, 0.001)) return 0;
4789 if (!IsGoodVal("IlluminantXYZ.Y", v ->IlluminantXYZ.Y, 0.2, 0.001)) return 0;
4790 if (!IsGoodVal("IlluminantXYZ.Z", v ->IlluminantXYZ.Z, 0.3, 0.001)) return 0;
4792 if (!IsGoodVal("SurroundXYZ.X", v ->SurroundXYZ.X, 0.4, 0.001)) return 0;
4793 if (!IsGoodVal("SurroundXYZ.Y", v ->SurroundXYZ.Y, 0.5, 0.001)) return 0;
4794 if (!IsGoodVal("SurroundXYZ.Z", v ->SurroundXYZ.Z, 0.6, 0.001)) return 0;
4806 cmsInt32Number CheckVCGT(cmsInt32Number Pass, cmsHPROFILE hProfile)
4808 cmsToneCurve* Curves[3];
4809 cmsToneCurve** PtrCurve;
4814 Curves[0] = cmsBuildGamma(DbgThread(), 1.1);
4815 Curves[1] = cmsBuildGamma(DbgThread(), 2.2);
4816 Curves[2] = cmsBuildGamma(DbgThread(), 3.4);
4818 if (!cmsWriteTag(hProfile, cmsSigVcgtTag, Curves)) return 0;
4820 cmsFreeToneCurveTriple(Curves);
4826 PtrCurve = cmsReadTag(hProfile, cmsSigVcgtTag);
4827 if (PtrCurve == NULL) return 0;
4828 if (!IsGoodVal("VCGT R", cmsEstimateGamma(PtrCurve[0], 0.01), 1.1, 0.001)) return 0;
4829 if (!IsGoodVal("VCGT G", cmsEstimateGamma(PtrCurve[1], 0.01), 2.2, 0.001)) return 0;
4830 if (!IsGoodVal("VCGT B", cmsEstimateGamma(PtrCurve[2], 0.01), 3.4, 0.001)) return 0;
4840 // Only one of the two following may be used, as they share the same tag
4842 cmsInt32Number CheckDictionary16(cmsInt32Number Pass, cmsHPROFILE hProfile)
4845 const cmsDICTentry* e;
4849 hDict = cmsDictAlloc(DbgThread());
4850 cmsDictAddEntry(hDict, L"Name0", NULL, NULL, NULL);
4851 cmsDictAddEntry(hDict, L"Name1", L"", NULL, NULL);
4852 cmsDictAddEntry(hDict, L"Name", L"String", NULL, NULL);
4853 cmsDictAddEntry(hDict, L"Name2", L"12", NULL, NULL);
4854 if (!cmsWriteTag(hProfile, cmsSigMetaTag, hDict)) return 0;
4861 hDict = cmsReadTag(hProfile, cmsSigMetaTag);
4862 if (hDict == NULL) return 0;
4863 e = cmsDictGetEntryList(hDict);
4864 if (memcmp(e ->Name, L"Name2", sizeof(wchar_t) * 5) != 0) return 0;
4865 if (memcmp(e ->Value, L"12", sizeof(wchar_t) * 2) != 0) return 0;
4866 e = cmsDictNextEntry(e);
4867 if (memcmp(e ->Name, L"Name", sizeof(wchar_t) * 4) != 0) return 0;
4868 if (memcmp(e ->Value, L"String", sizeof(wchar_t) * 5) != 0) return 0;
4869 e = cmsDictNextEntry(e);
4870 if (memcmp(e ->Name, L"Name1", sizeof(wchar_t) *5) != 0) return 0;
4871 if (e ->Value == NULL) return 0;
4872 if (*e->Value != 0) return 0;
4873 e = cmsDictNextEntry(e);
4874 if (memcmp(e ->Name, L"Name0", sizeof(wchar_t) * 5) != 0) return 0;
4875 if (e ->Value != NULL) return 0;
4888 cmsInt32Number CheckDictionary24(cmsInt32Number Pass, cmsHPROFILE hProfile)
4891 const cmsDICTentry* e;
4892 cmsMLU* DisplayName;
4894 cmsInt32Number rc = 1;
4899 hDict = cmsDictAlloc(DbgThread());
4901 DisplayName = cmsMLUalloc(DbgThread(), 0);
4903 cmsMLUsetWide(DisplayName, "en", "US", L"Hello, world");
4904 cmsMLUsetWide(DisplayName, "es", "ES", L"Hola, mundo");
4905 cmsMLUsetWide(DisplayName, "fr", "FR", L"Bonjour, le monde");
4906 cmsMLUsetWide(DisplayName, "ca", "CA", L"Hola, mon");
4908 cmsDictAddEntry(hDict, L"Name", L"String", DisplayName, NULL);
4909 cmsMLUfree(DisplayName);
4911 cmsDictAddEntry(hDict, L"Name2", L"12", NULL, NULL);
4912 if (!cmsWriteTag(hProfile, cmsSigMetaTag, hDict)) return 0;
4920 hDict = cmsReadTag(hProfile, cmsSigMetaTag);
4921 if (hDict == NULL) return 0;
4923 e = cmsDictGetEntryList(hDict);
4924 if (memcmp(e ->Name, L"Name2", sizeof(wchar_t) * 5) != 0) return 0;
4925 if (memcmp(e ->Value, L"12", sizeof(wchar_t) * 2) != 0) return 0;
4926 e = cmsDictNextEntry(e);
4927 if (memcmp(e ->Name, L"Name", sizeof(wchar_t) * 4) != 0) return 0;
4928 if (memcmp(e ->Value, L"String", sizeof(wchar_t) * 5) != 0) return 0;
4930 cmsMLUgetASCII(e->DisplayName, "en", "US", Buffer, 256);
4931 if (strcmp(Buffer, "Hello, world") != 0) rc = 0;
4934 cmsMLUgetASCII(e->DisplayName, "es", "ES", Buffer, 256);
4935 if (strcmp(Buffer, "Hola, mundo") != 0) rc = 0;
4938 cmsMLUgetASCII(e->DisplayName, "fr", "FR", Buffer, 256);
4939 if (strcmp(Buffer, "Bonjour, le monde") != 0) rc = 0;
4942 cmsMLUgetASCII(e->DisplayName, "ca", "CA", Buffer, 256);
4943 if (strcmp(Buffer, "Hola, mon") != 0) rc = 0;
4946 Fail("Unexpected string '%s'", Buffer);
4956 cmsInt32Number CheckRAWtags(cmsInt32Number Pass, cmsHPROFILE hProfile)
4963 return cmsWriteRawTag(hProfile, 0x31323334, "data123", 7);
4966 if (!cmsReadRawTag(hProfile, 0x31323334, Buffer, 7)) return 0;
4968 if (strncmp(Buffer, "data123", 7) != 0) return 0;
4977 // This is a very big test that checks every single tag
4979 cmsInt32Number CheckProfileCreation(void)
4982 cmsInt32Number Pass;
4984 h = cmsCreateProfilePlaceholder(DbgThread());
4985 if (h == NULL) return 0;
4987 cmsSetProfileVersion(h, 4.3);
4988 if (cmsGetTagCount(h) != 0) { Fail("Empty profile with nonzero number of tags"); return 0; }
4989 if (cmsIsTag(h, cmsSigAToB0Tag)) { Fail("Found a tag in an empty profile"); return 0; }
4991 cmsSetColorSpace(h, cmsSigRgbData);
4992 if (cmsGetColorSpace(h) != cmsSigRgbData) { Fail("Unable to set colorspace"); return 0; }
4994 cmsSetPCS(h, cmsSigLabData);
4995 if (cmsGetPCS(h) != cmsSigLabData) { Fail("Unable to set colorspace"); return 0; }
4997 cmsSetDeviceClass(h, cmsSigDisplayClass);
4998 if (cmsGetDeviceClass(h) != cmsSigDisplayClass) { Fail("Unable to set deviceclass"); return 0; }
5000 cmsSetHeaderRenderingIntent(h, INTENT_SATURATION);
5001 if (cmsGetHeaderRenderingIntent(h) != INTENT_SATURATION) { Fail("Unable to set rendering intent"); return 0; }
5003 for (Pass = 1; Pass <= 2; Pass++) {
5005 SubTest("Tags holding XYZ");
5007 if (!CheckXYZ(Pass, h, cmsSigBlueColorantTag)) return 0;
5008 if (!CheckXYZ(Pass, h, cmsSigGreenColorantTag)) return 0;
5009 if (!CheckXYZ(Pass, h, cmsSigRedColorantTag)) return 0;
5010 if (!CheckXYZ(Pass, h, cmsSigMediaBlackPointTag)) return 0;
5011 if (!CheckXYZ(Pass, h, cmsSigMediaWhitePointTag)) return 0;
5012 if (!CheckXYZ(Pass, h, cmsSigLuminanceTag)) return 0;
5014 SubTest("Tags holding curves");
5016 if (!CheckGamma(Pass, h, cmsSigBlueTRCTag)) return 0;
5017 if (!CheckGamma(Pass, h, cmsSigGrayTRCTag)) return 0;
5018 if (!CheckGamma(Pass, h, cmsSigGreenTRCTag)) return 0;
5019 if (!CheckGamma(Pass, h, cmsSigRedTRCTag)) return 0;
5021 SubTest("Tags holding text");
5023 if (!CheckText(Pass, h, cmsSigCharTargetTag)) return 0;
5024 if (!CheckText(Pass, h, cmsSigCopyrightTag)) return 0;
5025 if (!CheckText(Pass, h, cmsSigProfileDescriptionTag)) return 0;
5026 if (!CheckText(Pass, h, cmsSigDeviceMfgDescTag)) return 0;
5027 if (!CheckText(Pass, h, cmsSigDeviceModelDescTag)) return 0;
5028 if (!CheckText(Pass, h, cmsSigViewingCondDescTag)) return 0;
5029 if (!CheckText(Pass, h, cmsSigScreeningDescTag)) return 0;
5031 SubTest("Tags holding cmsICCData");
5033 if (!CheckData(Pass, h, cmsSigPs2CRD0Tag)) return 0;
5034 if (!CheckData(Pass, h, cmsSigPs2CRD1Tag)) return 0;
5035 if (!CheckData(Pass, h, cmsSigPs2CRD2Tag)) return 0;
5036 if (!CheckData(Pass, h, cmsSigPs2CRD3Tag)) return 0;
5037 if (!CheckData(Pass, h, cmsSigPs2CSATag)) return 0;
5038 if (!CheckData(Pass, h, cmsSigPs2RenderingIntentTag)) return 0;
5040 SubTest("Tags holding signatures");
5042 if (!CheckSignature(Pass, h, cmsSigColorimetricIntentImageStateTag)) return 0;
5043 if (!CheckSignature(Pass, h, cmsSigPerceptualRenderingIntentGamutTag)) return 0;
5044 if (!CheckSignature(Pass, h, cmsSigSaturationRenderingIntentGamutTag)) return 0;
5045 if (!CheckSignature(Pass, h, cmsSigTechnologyTag)) return 0;
5047 SubTest("Tags holding date_time");
5049 if (!CheckDateTime(Pass, h, cmsSigCalibrationDateTimeTag)) return 0;
5050 if (!CheckDateTime(Pass, h, cmsSigDateTimeTag)) return 0;
5052 SubTest("Tags holding named color lists");
5054 if (!CheckNamedColor(Pass, h, cmsSigColorantTableTag, 15, FALSE)) return 0;
5055 if (!CheckNamedColor(Pass, h, cmsSigColorantTableOutTag, 15, FALSE)) return 0;
5056 if (!CheckNamedColor(Pass, h, cmsSigNamedColor2Tag, 4096, TRUE)) return 0;
5058 SubTest("Tags holding LUTs");
5060 if (!CheckLUT(Pass, h, cmsSigAToB0Tag)) return 0;
5061 if (!CheckLUT(Pass, h, cmsSigAToB1Tag)) return 0;
5062 if (!CheckLUT(Pass, h, cmsSigAToB2Tag)) return 0;
5063 if (!CheckLUT(Pass, h, cmsSigBToA0Tag)) return 0;
5064 if (!CheckLUT(Pass, h, cmsSigBToA1Tag)) return 0;
5065 if (!CheckLUT(Pass, h, cmsSigBToA2Tag)) return 0;
5066 if (!CheckLUT(Pass, h, cmsSigPreview0Tag)) return 0;
5067 if (!CheckLUT(Pass, h, cmsSigPreview1Tag)) return 0;
5068 if (!CheckLUT(Pass, h, cmsSigPreview2Tag)) return 0;
5069 if (!CheckLUT(Pass, h, cmsSigGamutTag)) return 0;
5071 SubTest("Tags holding CHAD");
5072 if (!CheckCHAD(Pass, h, cmsSigChromaticAdaptationTag)) return 0;
5074 SubTest("Tags holding Chromaticity");
5075 if (!CheckChromaticity(Pass, h, cmsSigChromaticityTag)) return 0;
5077 SubTest("Tags holding colorant order");
5078 if (!CheckColorantOrder(Pass, h, cmsSigColorantOrderTag)) return 0;
5080 SubTest("Tags holding measurement");
5081 if (!CheckMeasurement(Pass, h, cmsSigMeasurementTag)) return 0;
5083 SubTest("Tags holding CRD info");
5084 if (!CheckCRDinfo(Pass, h, cmsSigCrdInfoTag)) return 0;
5086 SubTest("Tags holding UCR/BG");
5087 if (!CheckUcrBg(Pass, h, cmsSigUcrBgTag)) return 0;
5089 SubTest("Tags holding MPE");
5090 if (!CheckMPE(Pass, h, cmsSigDToB0Tag)) return 0;
5091 if (!CheckMPE(Pass, h, cmsSigDToB1Tag)) return 0;
5092 if (!CheckMPE(Pass, h, cmsSigDToB2Tag)) return 0;
5093 if (!CheckMPE(Pass, h, cmsSigDToB3Tag)) return 0;
5094 if (!CheckMPE(Pass, h, cmsSigBToD0Tag)) return 0;
5095 if (!CheckMPE(Pass, h, cmsSigBToD1Tag)) return 0;
5096 if (!CheckMPE(Pass, h, cmsSigBToD2Tag)) return 0;
5097 if (!CheckMPE(Pass, h, cmsSigBToD3Tag)) return 0;
5099 SubTest("Tags using screening");
5100 if (!CheckScreening(Pass, h, cmsSigScreeningTag)) return 0;
5102 SubTest("Tags holding profile sequence description");
5103 if (!CheckProfileSequenceTag(Pass, h)) return 0;
5104 if (!CheckProfileSequenceIDTag(Pass, h)) return 0;
5106 SubTest("Tags holding ICC viewing conditions");
5107 if (!CheckICCViewingConditions(Pass, h)) return 0;
5110 SubTest("VCGT tags");
5111 if (!CheckVCGT(Pass, h)) return 0;
5113 SubTest("RAW tags");
5114 if (!CheckRAWtags(Pass, h)) return 0;
5116 SubTest("Dictionary meta tags");
5117 // if (!CheckDictionary16(Pass, h)) return 0;
5118 if (!CheckDictionary24(Pass, h)) return 0;
5121 cmsSaveProfileToFile(h, "alltags.icc");
5123 h = cmsOpenProfileFromFileTHR(DbgThread(), "alltags.icc", "r");
5129 Not implemented (by design):
5131 cmsSigDataTag = 0x64617461, // 'data' -- Unused
5132 cmsSigDeviceSettingsTag = 0x64657673, // 'devs' -- Unused
5133 cmsSigNamedColorTag = 0x6E636f6C, // 'ncol' -- Don't use this one, deprecated by ICC
5134 cmsSigOutputResponseTag = 0x72657370, // 'resp' -- Possible patent on this
5138 remove("alltags.icc");
5143 // Error reporting -------------------------------------------------------------------------------------------------------
5147 void ErrorReportingFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
5149 TrappedError = TRUE;
5150 SimultaneousErrors++;
5151 strncpy(ReasonToFailBuffer, Text, TEXT_ERROR_BUFFER_SIZE-1);
5156 cmsInt32Number CheckBadProfiles(void)
5160 h = cmsOpenProfileFromFileTHR(DbgThread(), "IDoNotExist.icc", "r");
5166 h = cmsOpenProfileFromFileTHR(DbgThread(), "IAmIllFormed*.icc", "r");
5172 // No profile name given
5173 h = cmsOpenProfileFromFileTHR(DbgThread(), "", "r");
5179 h = cmsOpenProfileFromFileTHR(DbgThread(), "..", "r");
5185 h = cmsOpenProfileFromFileTHR(DbgThread(), "IHaveBadAccessMode.icc", "@");
5191 h = cmsOpenProfileFromFileTHR(DbgThread(), "bad.icc", "r");
5197 h = cmsOpenProfileFromFileTHR(DbgThread(), "toosmall.icc", "r");
5203 h = cmsOpenProfileFromMemTHR(DbgThread(), NULL, 3);
5209 h = cmsOpenProfileFromMemTHR(DbgThread(), "123", 3);
5215 if (SimultaneousErrors != 9) return 0;
5222 cmsInt32Number CheckErrReportingOnBadProfiles(void)
5226 cmsSetLogErrorHandler(ErrorReportingFunction);
5227 rc = CheckBadProfiles();
5228 cmsSetLogErrorHandler(FatalErrorQuit);
5230 // Reset the error state
5231 TrappedError = FALSE;
5237 cmsInt32Number CheckBadTransforms(void)
5239 cmsHPROFILE h1 = cmsCreate_sRGBProfile();
5242 x1 = cmsCreateTransform(NULL, 0, NULL, 0, 0, 0);
5244 cmsDeleteTransform(x1);
5250 x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_RGB_8, 12345, 0);
5252 cmsDeleteTransform(x1);
5256 x1 = cmsCreateTransform(h1, TYPE_CMYK_8, h1, TYPE_RGB_8, 0, 0);
5258 cmsDeleteTransform(x1);
5262 x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_CMYK_8, 1, 0);
5264 cmsDeleteTransform(x1);
5268 // sRGB does its output as XYZ!
5269 x1 = cmsCreateTransform(h1, TYPE_RGB_8, NULL, TYPE_Lab_8, 1, 0);
5271 cmsDeleteTransform(x1);
5275 cmsCloseProfile(h1);
5280 cmsHPROFILE h1 = cmsOpenProfileFromFile("test1.icc", "r");
5281 cmsHPROFILE h2 = cmsCreate_sRGBProfile();
5283 x1 = cmsCreateTransform(h1, TYPE_BGR_8, h2, TYPE_BGR_8, INTENT_PERCEPTUAL, 0);
5285 cmsCloseProfile(h1); cmsCloseProfile(h2);
5287 cmsDeleteTransform(x1);
5297 cmsInt32Number CheckErrReportingOnBadTransforms(void)
5301 cmsSetLogErrorHandler(ErrorReportingFunction);
5302 rc = CheckBadTransforms();
5303 cmsSetLogErrorHandler(FatalErrorQuit);
5305 // Reset the error state
5306 TrappedError = FALSE;
5313 // ---------------------------------------------------------------------------------------------------------
5315 // Check a linear xform
5317 cmsInt32Number Check8linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5319 cmsInt32Number n2, i, j;
5320 cmsUInt8Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5324 for (j=0; j < 0xFF; j++) {
5326 memset(Inw, j, sizeof(Inw));
5327 cmsDoTransform(xform, Inw, Outw, 1);
5329 for (i=0; i < nChan; i++) {
5331 cmsInt32Number dif = abs(Outw[i] - j);
5332 if (dif > n2) n2 = dif;
5337 // We allow 2 contone of difference on 8 bits
5340 Fail("Differences too big (%x)", n2);
5348 cmsInt32Number Compare8bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5350 cmsInt32Number n2, i, j;
5351 cmsUInt8Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5355 for (j=0; j < 0xFF; j++) {
5357 memset(Inw, j, sizeof(Inw));
5358 cmsDoTransform(xform1, Inw, Outw1, 1);
5359 cmsDoTransform(xform2, Inw, Outw2, 1);
5361 for (i=0; i < nChan; i++) {
5363 cmsInt32Number dif = abs(Outw2[i] - Outw1[i]);
5364 if (dif > n2) n2 = dif;
5369 // We allow 2 contone of difference on 8 bits
5372 Fail("Differences too big (%x)", n2);
5381 // Check a linear xform
5383 cmsInt32Number Check16linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5385 cmsInt32Number n2, i, j;
5386 cmsUInt16Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5389 for (j=0; j < 0xFFFF; j++) {
5391 for (i=0; i < nChan; i++) Inw[i] = (cmsUInt16Number) j;
5393 cmsDoTransform(xform, Inw, Outw, 1);
5395 for (i=0; i < nChan; i++) {
5397 cmsInt32Number dif = abs(Outw[i] - j);
5398 if (dif > n2) n2 = dif;
5403 // We allow 2 contone of difference on 16 bits
5406 Fail("Differences too big (%x)", n2);
5415 cmsInt32Number Compare16bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5417 cmsInt32Number n2, i, j;
5418 cmsUInt16Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5422 for (j=0; j < 0xFFFF; j++) {
5424 for (i=0; i < nChan; i++) Inw[i] = (cmsUInt16Number) j;
5426 cmsDoTransform(xform1, Inw, Outw1, 1);
5427 cmsDoTransform(xform2, Inw, Outw2, 1);
5429 for (i=0; i < nChan; i++) {
5431 cmsInt32Number dif = abs(Outw2[i] - Outw1[i]);
5432 if (dif > n2) n2 = dif;
5437 // We allow 2 contone of difference on 16 bits
5440 Fail("Differences too big (%x)", n2);
5449 // Check a linear xform
5451 cmsInt32Number CheckFloatlinearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5453 cmsInt32Number n2, i, j;
5454 cmsFloat32Number In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS];
5458 for (j=0; j < 0xFFFF; j++) {
5460 for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5462 cmsDoTransform(xform, In, Out, 1);
5464 for (i=0; i < nChan; i++) {
5466 // We allow no difference in floating point
5467 if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out[i], (cmsFloat32Number) (j / 65535.0)))
5476 // Check a linear xform
5478 cmsInt32Number CompareFloatXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5480 cmsInt32Number n2, i, j;
5481 cmsFloat32Number In[cmsMAXCHANNELS], Out1[cmsMAXCHANNELS], Out2[cmsMAXCHANNELS];
5485 for (j=0; j < 0xFFFF; j++) {
5487 for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5489 cmsDoTransform(xform1, In, Out1, 1);
5490 cmsDoTransform(xform2, In, Out2, 1);
5492 for (i=0; i < nChan; i++) {
5494 // We allow no difference in floating point
5495 if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out1[i], Out2[i]))
5505 // Curves only transforms ----------------------------------------------------------------------------------------
5508 cmsInt32Number CheckCurvesOnlyTransforms(void)
5511 cmsHTRANSFORM xform1, xform2;
5512 cmsHPROFILE h1, h2, h3;
5513 cmsToneCurve* c1, *c2, *c3;
5514 cmsInt32Number rc = 1;
5517 c1 = cmsBuildGamma(DbgThread(), 2.2);
5518 c2 = cmsBuildGamma(DbgThread(), 1/2.2);
5519 c3 = cmsBuildGamma(DbgThread(), 4.84);
5521 h1 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c1);
5522 h2 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c2);
5523 h3 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c3);
5525 SubTest("Gray float optimizeable transform");
5526 xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h2, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5527 rc &= CheckFloatlinearXFORM(xform1, 1);
5528 cmsDeleteTransform(xform1);
5529 if (rc == 0) goto Error;
5531 SubTest("Gray 8 optimizeable transform");
5532 xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h2, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5533 rc &= Check8linearXFORM(xform1, 1);
5534 cmsDeleteTransform(xform1);
5535 if (rc == 0) goto Error;
5537 SubTest("Gray 16 optimizeable transform");
5538 xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h2, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5539 rc &= Check16linearXFORM(xform1, 1);
5540 cmsDeleteTransform(xform1);
5541 if (rc == 0) goto Error;
5543 SubTest("Gray float non-optimizeable transform");
5544 xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h1, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5545 xform2 = cmsCreateTransform(h3, TYPE_GRAY_FLT, NULL, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5547 rc &= CompareFloatXFORM(xform1, xform2, 1);
5548 cmsDeleteTransform(xform1);
5549 cmsDeleteTransform(xform2);
5550 if (rc == 0) goto Error;
5552 SubTest("Gray 8 non-optimizeable transform");
5553 xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h1, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5554 xform2 = cmsCreateTransform(h3, TYPE_GRAY_8, NULL, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5556 rc &= Compare8bitXFORM(xform1, xform2, 1);
5557 cmsDeleteTransform(xform1);
5558 cmsDeleteTransform(xform2);
5559 if (rc == 0) goto Error;
5562 SubTest("Gray 16 non-optimizeable transform");
5563 xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h1, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5564 xform2 = cmsCreateTransform(h3, TYPE_GRAY_16, NULL, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5566 rc &= Compare16bitXFORM(xform1, xform2, 1);
5567 cmsDeleteTransform(xform1);
5568 cmsDeleteTransform(xform2);
5569 if (rc == 0) goto Error;
5573 cmsCloseProfile(h1); cmsCloseProfile(h2); cmsCloseProfile(h3);
5574 cmsFreeToneCurve(c1); cmsFreeToneCurve(c2); cmsFreeToneCurve(c3);
5581 // Lab to Lab trivial transforms ----------------------------------------------------------------------------------------
5583 static cmsFloat64Number MaxDE;
5586 cmsInt32Number CheckOneLab(cmsHTRANSFORM xform, cmsFloat64Number L, cmsFloat64Number a, cmsFloat64Number b)
5589 cmsFloat64Number dE;
5591 In.L = L; In.a = a; In.b = b;
5592 cmsDoTransform(xform, &In, &Out, 1);
5594 dE = cmsDeltaE(&In, &Out);
5596 if (dE > MaxDE) MaxDE = dE;
5598 if (MaxDE > 0.003) {
5599 Fail("dE=%f Lab1=(%f, %f, %f)\n\tLab2=(%f %f %f)", MaxDE, In.L, In.a, In.b, Out.L, Out.a, Out.b);
5600 cmsDoTransform(xform, &In, &Out, 1);
5607 // Check several Lab, slicing at non-exact values. Precision should be 16 bits. 50x50x50 checks aprox.
5609 cmsInt32Number CheckSeveralLab(cmsHTRANSFORM xform)
5611 cmsInt32Number L, a, b;
5614 for (L=0; L < 65536; L += 1311) {
5616 for (a = 0; a < 65536; a += 1232) {
5618 for (b = 0; b < 65536; b += 1111) {
5620 if (!CheckOneLab(xform, (L * 100.0) / 65535.0,
5621 (a / 257.0) - 128, (b / 257.0) - 128))
5633 cmsInt32Number OneTrivialLab(cmsHPROFILE hLab1, cmsHPROFILE hLab2, const char* txt)
5635 cmsHTRANSFORM xform;
5639 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5640 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5642 rc = CheckSeveralLab(xform);
5643 cmsDeleteTransform(xform);
5649 cmsInt32Number CheckFloatLabTransforms(void)
5651 return OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab4/Lab4") &&
5652 OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab2/Lab2") &&
5653 OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab4/Lab2") &&
5654 OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab2/Lab4");
5659 cmsInt32Number CheckEncodedLabTransforms(void)
5661 cmsHTRANSFORM xform;
5662 cmsUInt16Number In[3];
5664 cmsCIELab White = { 100, 0, 0 };
5665 cmsHPROFILE hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5666 cmsHPROFILE hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5669 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5670 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5676 cmsDoTransform(xform, In, &Lab, 1);
5678 if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5679 cmsDeleteTransform(xform);
5681 hLab1 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5682 hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5684 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_LabV2_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5685 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5692 cmsDoTransform(xform, In, &Lab, 1);
5694 if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5696 cmsDeleteTransform(xform);
5698 hLab2 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5699 hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5701 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_LabV2_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5702 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5708 cmsDoTransform(xform, &Lab, In, 1);
5709 if (In[0] != 0xFF00 ||
5711 In[2] != 0x8000) return 0;
5713 cmsDeleteTransform(xform);
5715 hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5716 hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5718 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5719 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5725 cmsDoTransform(xform, &Lab, In, 1);
5727 if (In[0] != 0xFFFF ||
5729 In[2] != 0x8080) return 0;
5731 cmsDeleteTransform(xform);
5737 cmsInt32Number CheckStoredIdentities(void)
5739 cmsHPROFILE hLab, hLink, h4, h2;
5740 cmsHTRANSFORM xform;
5741 cmsInt32Number rc = 1;
5743 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5744 xform = cmsCreateTransformTHR(DbgThread(), hLab, TYPE_Lab_8, hLab, TYPE_Lab_8, 0, 0);
5746 hLink = cmsTransform2DeviceLink(xform, 3.4, 0);
5747 cmsSaveProfileToFile(hLink, "abstractv2.icc");
5748 cmsCloseProfile(hLink);
5750 hLink = cmsTransform2DeviceLink(xform, 4.3, 0);
5751 cmsSaveProfileToFile(hLink, "abstractv4.icc");
5752 cmsCloseProfile(hLink);
5754 cmsDeleteTransform(xform);
5755 cmsCloseProfile(hLab);
5757 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5759 xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5762 rc &= CheckSeveralLab(xform);
5764 cmsDeleteTransform(xform);
5765 cmsCloseProfile(h4);
5766 if (!rc) goto Error;
5770 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5772 xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5773 rc &= CheckSeveralLab(xform);
5774 cmsDeleteTransform(xform);
5775 cmsCloseProfile(h2);
5776 if (!rc) goto Error;
5779 SubTest("V2 -> V4");
5780 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5781 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5783 xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5784 rc &= CheckSeveralLab(xform);
5785 cmsDeleteTransform(xform);
5786 cmsCloseProfile(h2);
5787 cmsCloseProfile(h4);
5789 SubTest("V4 -> V2");
5790 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5791 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5793 xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5794 rc &= CheckSeveralLab(xform);
5795 cmsDeleteTransform(xform);
5796 cmsCloseProfile(h2);
5797 cmsCloseProfile(h4);
5800 remove("abstractv2.icc");
5801 remove("abstractv4.icc");
5808 // Check a simple xform from a matrix profile to itself. Test floating point accuracy.
5810 cmsInt32Number CheckMatrixShaperXFORMFloat(void)
5812 cmsHPROFILE hAbove, hSRGB;
5813 cmsHTRANSFORM xform;
5814 cmsInt32Number rc1, rc2;
5816 hAbove = Create_AboveRGB();
5817 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
5818 cmsCloseProfile(hAbove);
5819 rc1 = CheckFloatlinearXFORM(xform, 3);
5820 cmsDeleteTransform(xform);
5822 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5823 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_FLT, hSRGB, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
5824 cmsCloseProfile(hSRGB);
5825 rc2 = CheckFloatlinearXFORM(xform, 3);
5826 cmsDeleteTransform(xform);
5832 // Check a simple xform from a matrix profile to itself. Test 16 bits accuracy.
5834 cmsInt32Number CheckMatrixShaperXFORM16(void)
5836 cmsHPROFILE hAbove, hSRGB;
5837 cmsHTRANSFORM xform;
5838 cmsInt32Number rc1, rc2;
5840 hAbove = Create_AboveRGB();
5841 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5842 cmsCloseProfile(hAbove);
5844 rc1 = Check16linearXFORM(xform, 3);
5845 cmsDeleteTransform(xform);
5847 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5848 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_16, hSRGB, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5849 cmsCloseProfile(hSRGB);
5850 rc2 = Check16linearXFORM(xform, 3);
5851 cmsDeleteTransform(xform);
5858 // Check a simple xform from a matrix profile to itself. Test 8 bits accuracy.
5860 cmsInt32Number CheckMatrixShaperXFORM8(void)
5862 cmsHPROFILE hAbove, hSRGB;
5863 cmsHTRANSFORM xform;
5864 cmsInt32Number rc1, rc2;
5866 hAbove = Create_AboveRGB();
5867 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_8, hAbove, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
5868 cmsCloseProfile(hAbove);
5869 rc1 = Check8linearXFORM(xform, 3);
5870 cmsDeleteTransform(xform);
5872 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5873 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_8, hSRGB, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
5874 cmsCloseProfile(hSRGB);
5875 rc2 = Check8linearXFORM(xform, 3);
5876 cmsDeleteTransform(xform);
5883 // TODO: Check LUT based to LUT based transforms for CMYK
5890 // -----------------------------------------------------------------------------------------------------------------
5893 // Check known values going from sRGB to XYZ
5895 cmsInt32Number CheckOneRGB_f(cmsHTRANSFORM xform, cmsInt32Number R, cmsInt32Number G, cmsInt32Number B, cmsFloat64Number X, cmsFloat64Number Y, cmsFloat64Number Z, cmsFloat64Number err)
5897 cmsFloat32Number RGB[3];
5898 cmsFloat64Number Out[3];
5900 RGB[0] = (cmsFloat32Number) (R / 255.0);
5901 RGB[1] = (cmsFloat32Number) (G / 255.0);
5902 RGB[2] = (cmsFloat32Number) (B / 255.0);
5904 cmsDoTransform(xform, RGB, Out, 1);
5906 return IsGoodVal("X", X , Out[0], err) &&
5907 IsGoodVal("Y", Y , Out[1], err) &&
5908 IsGoodVal("Z", Z , Out[2], err);
5912 cmsInt32Number Chack_sRGB_Float(void)
5914 cmsHPROFILE hsRGB, hXYZ, hLab;
5915 cmsHTRANSFORM xform1, xform2;
5919 hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5920 hXYZ = cmsCreateXYZProfileTHR(DbgThread());
5921 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5923 xform1 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hXYZ, TYPE_XYZ_DBL,
5924 INTENT_RELATIVE_COLORIMETRIC, 0);
5926 xform2 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hLab, TYPE_Lab_DBL,
5927 INTENT_RELATIVE_COLORIMETRIC, 0);
5928 cmsCloseProfile(hsRGB);
5929 cmsCloseProfile(hXYZ);
5930 cmsCloseProfile(hLab);
5934 // Xform 1 goes from 8 bits to XYZ,
5935 rc = CheckOneRGB_f(xform1, 1, 1, 1, 0.0002926, 0.00030352, 0.00025037, 0.0001);
5936 rc &= CheckOneRGB_f(xform1, 127, 127, 127, 0.2046329, 0.212230, 0.175069, 0.0001);
5937 rc &= CheckOneRGB_f(xform1, 12, 13, 15, 0.0038364, 0.0039928, 0.00385212, 0.0001);
5938 rc &= CheckOneRGB_f(xform1, 128, 0, 0, 0.0940846, 0.0480030, 0.00300543, 0.0001);
5939 rc &= CheckOneRGB_f(xform1, 190, 25, 210, 0.3203491, 0.1605240, 0.46817115, 0.0001);
5941 // Xform 2 goes from 8 bits to Lab, we allow 0.01 error max
5942 rc &= CheckOneRGB_f(xform2, 1, 1, 1, 0.2741748, 0, 0, 0.01);
5943 rc &= CheckOneRGB_f(xform2, 127, 127, 127, 53.192776, 0, 0, 0.01);
5944 rc &= CheckOneRGB_f(xform2, 190, 25, 210, 47.043171, 74.564576, -56.89373, 0.01);
5945 rc &= CheckOneRGB_f(xform2, 128, 0, 0, 26.158100, 48.474477, 39.425916, 0.01);
5947 cmsDeleteTransform(xform1);
5948 cmsDeleteTransform(xform2);
5953 // ---------------------------------------------------
5956 cmsBool GetProfileRGBPrimaries(cmsHPROFILE hProfile,
5957 cmsCIEXYZTRIPLE *result,
5958 cmsUInt32Number intent)
5961 cmsHTRANSFORM hTransform;
5962 cmsFloat64Number rgb[3][3] = {{1., 0., 0.},
5966 hXYZ = cmsCreateXYZProfile();
5967 if (hXYZ == NULL) return FALSE;
5969 hTransform = cmsCreateTransform(hProfile, TYPE_RGB_DBL, hXYZ, TYPE_XYZ_DBL,
5970 intent, cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
5971 cmsCloseProfile(hXYZ);
5972 if (hTransform == NULL) return FALSE;
5974 cmsDoTransform(hTransform, rgb, result, 3);
5975 cmsDeleteTransform(hTransform);
5981 int CheckRGBPrimaries(void)
5984 cmsCIEXYZTRIPLE tripXYZ;
5985 cmsCIExyYTRIPLE tripxyY;
5988 cmsSetAdaptationState(0);
5989 hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5990 if (!hsRGB) return 0;
5992 result = GetProfileRGBPrimaries(hsRGB, &tripXYZ,
5993 INTENT_ABSOLUTE_COLORIMETRIC);
5995 cmsCloseProfile(hsRGB);
5996 if (!result) return 0;
5998 cmsXYZ2xyY(&tripxyY.Red, &tripXYZ.Red);
5999 cmsXYZ2xyY(&tripxyY.Green, &tripXYZ.Green);
6000 cmsXYZ2xyY(&tripxyY.Blue, &tripXYZ.Blue);
6002 /* valus were taken from
6003 http://en.wikipedia.org/wiki/RGB_color_spaces#Specifications */
6005 if (!IsGoodFixed15_16("xRed", tripxyY.Red.x, 0.64) ||
6006 !IsGoodFixed15_16("yRed", tripxyY.Red.y, 0.33) ||
6007 !IsGoodFixed15_16("xGreen", tripxyY.Green.x, 0.30) ||
6008 !IsGoodFixed15_16("yGreen", tripxyY.Green.y, 0.60) ||
6009 !IsGoodFixed15_16("xBlue", tripxyY.Blue.x, 0.15) ||
6010 !IsGoodFixed15_16("yBlue", tripxyY.Blue.y, 0.06)) {
6011 Fail("One or more primaries are wrong.");
6019 // -----------------------------------------------------------------------------------------------------------------
6021 // This function will check CMYK -> CMYK transforms. It uses FOGRA29 and SWOP ICC profiles
6024 cmsInt32Number CheckCMYK(cmsInt32Number Intent, const char *Profile1, const char* Profile2)
6026 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), Profile1, "r");
6027 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), Profile2, "r");
6028 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6029 cmsFloat32Number CMYK1[4], CMYK2[4];
6030 cmsCIELab Lab1, Lab2;
6032 cmsFloat64Number DeltaL, Max;
6035 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6037 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, Intent, 0);
6039 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6040 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6043 for (i=0; i <= 100; i++) {
6048 CMYK1[3] = (cmsFloat32Number) i;
6050 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6051 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6052 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6054 DeltaL = fabs(Lab1.L - Lab2.L);
6056 if (DeltaL > Max) Max = DeltaL;
6060 cmsDeleteTransform(xform);
6062 if (Max > 3.0) return 0;
6064 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, Intent, 0);
6068 for (i=0; i <= 100; i++) {
6072 CMYK1[3] = (cmsFloat32Number) i;
6074 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6075 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6076 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6078 DeltaL = fabs(Lab1.L - Lab2.L);
6080 if (DeltaL > Max) Max = DeltaL;
6084 cmsCloseProfile(hSWOP);
6085 cmsCloseProfile(hFOGRA);
6086 cmsCloseProfile(hLab);
6088 cmsDeleteTransform(xform);
6089 cmsDeleteTransform(swop_lab);
6090 cmsDeleteTransform(fogra_lab);
6096 cmsInt32Number CheckCMYKRoundtrip(void)
6098 return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test1.icc");
6103 cmsInt32Number CheckCMYKPerceptual(void)
6105 return CheckCMYK(INTENT_PERCEPTUAL, "test1.icc", "test2.icc");
6111 cmsInt32Number CheckCMYKRelCol(void)
6113 return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test2.icc");
6119 cmsInt32Number CheckKOnlyBlackPreserving(void)
6121 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6122 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6123 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6124 cmsFloat32Number CMYK1[4], CMYK2[4];
6125 cmsCIELab Lab1, Lab2;
6127 cmsFloat64Number DeltaL, Max;
6130 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6132 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6134 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6135 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6139 for (i=0; i <= 100; i++) {
6143 CMYK1[3] = (cmsFloat32Number) i;
6145 // SWOP CMYK to Lab1
6146 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6148 // SWOP To FOGRA using black preservation
6149 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6151 // Obtained FOGRA CMYK to Lab2
6152 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6154 // We care only on L*
6155 DeltaL = fabs(Lab1.L - Lab2.L);
6157 if (DeltaL > Max) Max = DeltaL;
6161 cmsDeleteTransform(xform);
6163 // dL should be below 3.0
6164 if (Max > 3.0) return 0;
6167 // Same, but FOGRA to SWOP
6168 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6172 for (i=0; i <= 100; i++) {
6176 CMYK1[3] = (cmsFloat32Number) i;
6178 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6179 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6180 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6182 DeltaL = fabs(Lab1.L - Lab2.L);
6184 if (DeltaL > Max) Max = DeltaL;
6188 cmsCloseProfile(hSWOP);
6189 cmsCloseProfile(hFOGRA);
6190 cmsCloseProfile(hLab);
6192 cmsDeleteTransform(xform);
6193 cmsDeleteTransform(swop_lab);
6194 cmsDeleteTransform(fogra_lab);
6200 cmsInt32Number CheckKPlaneBlackPreserving(void)
6202 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6203 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6204 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6205 cmsFloat32Number CMYK1[4], CMYK2[4];
6206 cmsCIELab Lab1, Lab2;
6208 cmsFloat64Number DeltaE, Max;
6211 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6213 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PERCEPTUAL, 0);
6215 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6216 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6220 for (i=0; i <= 100; i++) {
6224 CMYK1[3] = (cmsFloat32Number) i;
6226 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6227 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6228 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6230 DeltaE = cmsDeltaE(&Lab1, &Lab2);
6232 if (DeltaE > Max) Max = DeltaE;
6236 cmsDeleteTransform(xform);
6238 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_PLANE_PERCEPTUAL, 0);
6240 for (i=0; i <= 100; i++) {
6244 CMYK1[3] = (cmsFloat32Number) i;
6246 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6247 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6248 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6250 DeltaE = cmsDeltaE(&Lab1, &Lab2);
6252 if (DeltaE > Max) Max = DeltaE;
6255 cmsDeleteTransform(xform);
6259 cmsCloseProfile(hSWOP);
6260 cmsCloseProfile(hFOGRA);
6261 cmsCloseProfile(hLab);
6264 cmsDeleteTransform(swop_lab);
6265 cmsDeleteTransform(fogra_lab);
6271 // ------------------------------------------------------------------------------------------------------
6275 cmsInt32Number CheckProofingXFORMFloat(void)
6278 cmsHTRANSFORM xform;
6281 hAbove = Create_AboveRGB();
6282 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
6283 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING);
6284 cmsCloseProfile(hAbove);
6285 rc = CheckFloatlinearXFORM(xform, 3);
6286 cmsDeleteTransform(xform);
6291 cmsInt32Number CheckProofingXFORM16(void)
6294 cmsHTRANSFORM xform;
6297 hAbove = Create_AboveRGB();
6298 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
6299 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING|cmsFLAGS_NOCACHE);
6300 cmsCloseProfile(hAbove);
6301 rc = Check16linearXFORM(xform, 3);
6302 cmsDeleteTransform(xform);
6308 cmsInt32Number CheckGamutCheck(void)
6310 cmsHPROFILE hSRGB, hAbove;
6311 cmsHTRANSFORM xform;
6313 cmsUInt16Number Alarm[3] = { 0xDEAD, 0xBABE, 0xFACE };
6315 // Set alarm codes to fancy values so we could check the out of gamut condition
6316 cmsSetAlarmCodes(Alarm);
6318 // Create the profiles
6319 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
6320 hAbove = Create_AboveRGB();
6322 if (hSRGB == NULL || hAbove == NULL) return 0; // Failed
6324 SubTest("Gamut check on floating point");
6326 // Create a gamut checker in the same space. No value should be out of gamut
6327 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
6328 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6331 if (!CheckFloatlinearXFORM(xform, 3)) {
6332 cmsCloseProfile(hSRGB);
6333 cmsCloseProfile(hAbove);
6334 cmsDeleteTransform(xform);
6335 Fail("Gamut check on same profile failed");
6339 cmsDeleteTransform(xform);
6341 SubTest("Gamut check on 16 bits");
6343 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
6344 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6346 cmsCloseProfile(hSRGB);
6347 cmsCloseProfile(hAbove);
6349 rc = Check16linearXFORM(xform, 3);
6351 cmsDeleteTransform(xform);
6358 // -------------------------------------------------------------------------------------------------------------------
6361 cmsInt32Number CheckBlackPoint(void)
6363 cmsHPROFILE hProfile;
6367 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test5.icc", "r");
6368 cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6369 cmsCloseProfile(hProfile);
6372 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6373 cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6374 cmsXYZ2Lab(NULL, &Lab, &Black);
6375 cmsCloseProfile(hProfile);
6377 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "lcms2cmyk.icc", "r");
6378 cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6379 cmsXYZ2Lab(NULL, &Lab, &Black);
6380 cmsCloseProfile(hProfile);
6382 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6383 cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6384 cmsXYZ2Lab(NULL, &Lab, &Black);
6385 cmsCloseProfile(hProfile);
6387 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6388 cmsDetectBlackPoint(&Black, hProfile, INTENT_PERCEPTUAL, 0);
6389 cmsXYZ2Lab(NULL, &Lab, &Black);
6390 cmsCloseProfile(hProfile);
6397 cmsInt32Number CheckOneTAC(cmsFloat64Number InkLimit)
6402 h =CreateFakeCMYK(InkLimit, TRUE);
6403 cmsSaveProfileToFile(h, "lcmstac.icc");
6406 h = cmsOpenProfileFromFile("lcmstac.icc", "r");
6407 d = cmsDetectTAC(h);
6410 remove("lcmstac.icc");
6412 if (fabs(d - InkLimit) > 5) return 0;
6419 cmsInt32Number CheckTAC(void)
6421 if (!CheckOneTAC(180)) return 0;
6422 if (!CheckOneTAC(220)) return 0;
6423 if (!CheckOneTAC(286)) return 0;
6424 if (!CheckOneTAC(310)) return 0;
6425 if (!CheckOneTAC(330)) return 0;
6430 // -------------------------------------------------------------------------------------------------------
6433 #define NPOINTS_IT8 10 // (17*17*17*17)
6436 cmsInt32Number CheckCGATS(void)
6442 it8 = cmsIT8Alloc(DbgThread());
6443 if (it8 == NULL) return 0;
6445 cmsIT8SetSheetType(it8, "LCMS/TESTING");
6446 cmsIT8SetPropertyStr(it8, "ORIGINATOR", "1 2 3 4");
6447 cmsIT8SetPropertyUncooked(it8, "DESCRIPTOR", "1234");
6448 cmsIT8SetPropertyStr(it8, "MANUFACTURER", "3");
6449 cmsIT8SetPropertyDbl(it8, "CREATED", 4);
6450 cmsIT8SetPropertyDbl(it8, "SERIAL", 5);
6451 cmsIT8SetPropertyHex(it8, "MATERIAL", 0x123);
6453 cmsIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", NPOINTS_IT8);
6454 cmsIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", 4);
6456 cmsIT8SetDataFormat(it8, 0, "SAMPLE_ID");
6457 cmsIT8SetDataFormat(it8, 1, "RGB_R");
6458 cmsIT8SetDataFormat(it8, 2, "RGB_G");
6459 cmsIT8SetDataFormat(it8, 3, "RGB_B");
6461 for (i=0; i < NPOINTS_IT8; i++) {
6465 sprintf(Patch, "P%d", i);
6467 cmsIT8SetDataRowCol(it8, i, 0, Patch);
6468 cmsIT8SetDataRowColDbl(it8, i, 1, i);
6469 cmsIT8SetDataRowColDbl(it8, i, 2, i);
6470 cmsIT8SetDataRowColDbl(it8, i, 3, i);
6473 cmsIT8SaveToFile(it8, "TEST.IT8");
6477 it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6478 cmsIT8SaveToFile(it8, "TEST.IT8");
6483 it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6485 if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 1234) {
6491 cmsIT8SetPropertyDbl(it8, "DESCRIPTOR", 5678);
6493 if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 5678) {
6498 if (cmsIT8GetDataDbl(it8, "P3", "RGB_G") != 3) {
6514 void GenerateCSA(const char* cInProf, const char* FileName)
6516 cmsHPROFILE hProfile;
6519 cmsContext BuffThread = DbgThread();
6523 if (cInProf == NULL)
6524 hProfile = cmsCreateLab4Profile(NULL);
6526 hProfile = cmsOpenProfileFromFile(cInProf, "r");
6528 n = cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, NULL, 0);
6531 Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6532 cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, Buffer, n);
6535 if (FileName != NULL) {
6536 o = fopen(FileName, "wb");
6537 fwrite(Buffer, n, 1, o);
6541 _cmsFree(BuffThread, Buffer);
6542 cmsCloseProfile(hProfile);
6548 void GenerateCRD(const char* cOutProf, const char* FileName)
6550 cmsHPROFILE hProfile;
6553 cmsUInt32Number dwFlags = 0;
6554 cmsContext BuffThread = DbgThread();
6557 if (cOutProf == NULL)
6558 hProfile = cmsCreateLab4Profile(NULL);
6560 hProfile = cmsOpenProfileFromFile(cOutProf, "r");
6562 n = cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, NULL, 0);
6565 Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6566 cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, Buffer, n);
6569 if (FileName != NULL) {
6570 FILE* o = fopen(FileName, "wb");
6571 fwrite(Buffer, n, 1, o);
6575 _cmsFree(BuffThread, Buffer);
6576 cmsCloseProfile(hProfile);
6581 cmsInt32Number CheckPostScript(void)
6583 GenerateCSA("test5.icc", "sRGB_CSA.ps");
6584 GenerateCSA("aRGBlcms2.icc", "aRGB_CSA.ps");
6585 GenerateCSA("test4.icc", "sRGBV4_CSA.ps");
6586 GenerateCSA("test1.icc", "SWOP_CSA.ps");
6587 GenerateCSA(NULL, "Lab_CSA.ps");
6588 GenerateCSA("graylcms2.icc", "gray_CSA.ps");
6590 GenerateCRD("test5.icc", "sRGB_CRD.ps");
6591 GenerateCRD("aRGBlcms2.icc", "aRGB_CRD.ps");
6592 GenerateCRD(NULL, "Lab_CRD.ps");
6593 GenerateCRD("test1.icc", "SWOP_CRD.ps");
6594 GenerateCRD("test4.icc", "sRGBV4_CRD.ps");
6595 GenerateCRD("graylcms2.icc", "gray_CRD.ps");
6602 cmsInt32Number CheckGray(cmsHTRANSFORM xform, cmsUInt8Number g, double L)
6606 cmsDoTransform(xform, &g, &Lab, 1);
6608 if (!IsGoodVal("a axis on gray", 0, Lab.a, 0.001)) return 0;
6609 if (!IsGoodVal("b axis on gray", 0, Lab.b, 0.001)) return 0;
6611 return IsGoodVal("Gray value", L, Lab.L, 0.01);
6615 cmsInt32Number CheckInputGray(void)
6617 cmsHPROFILE hGray = Create_Gray22();
6618 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6619 cmsHTRANSFORM xform;
6621 if (hGray == NULL || hLab == NULL) return 0;
6623 xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
6624 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6626 if (!CheckGray(xform, 0, 0)) return 0;
6627 if (!CheckGray(xform, 125, 52.768)) return 0;
6628 if (!CheckGray(xform, 200, 81.069)) return 0;
6629 if (!CheckGray(xform, 255, 100.0)) return 0;
6631 cmsDeleteTransform(xform);
6636 cmsInt32Number CheckLabInputGray(void)
6638 cmsHPROFILE hGray = Create_GrayLab();
6639 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6640 cmsHTRANSFORM xform;
6642 if (hGray == NULL || hLab == NULL) return 0;
6644 xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
6645 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6647 if (!CheckGray(xform, 0, 0)) return 0;
6648 if (!CheckGray(xform, 125, 49.019)) return 0;
6649 if (!CheckGray(xform, 200, 78.431)) return 0;
6650 if (!CheckGray(xform, 255, 100.0)) return 0;
6652 cmsDeleteTransform(xform);
6658 cmsInt32Number CheckOutGray(cmsHTRANSFORM xform, double L, cmsUInt8Number g)
6661 cmsUInt8Number g_out;
6667 cmsDoTransform(xform, &Lab, &g_out, 1);
6669 return IsGoodVal("Gray value", g, (double) g_out, 0.01);
6673 cmsInt32Number CheckOutputGray(void)
6675 cmsHPROFILE hGray = Create_Gray22();
6676 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6677 cmsHTRANSFORM xform;
6679 if (hGray == NULL || hLab == NULL) return 0;
6681 xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0);
6682 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6684 if (!CheckOutGray(xform, 0, 0)) return 0;
6685 if (!CheckOutGray(xform, 100, 255)) return 0;
6687 if (!CheckOutGray(xform, 20, 52)) return 0;
6688 if (!CheckOutGray(xform, 50, 118)) return 0;
6691 cmsDeleteTransform(xform);
6697 cmsInt32Number CheckLabOutputGray(void)
6699 cmsHPROFILE hGray = Create_GrayLab();
6700 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6701 cmsHTRANSFORM xform;
6704 if (hGray == NULL || hLab == NULL) return 0;
6706 xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0);
6707 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6709 if (!CheckOutGray(xform, 0, 0)) return 0;
6710 if (!CheckOutGray(xform, 100, 255)) return 0;
6712 for (i=0; i < 100; i++) {
6716 g = (cmsUInt8Number) floor(i * 255.0 / 100.0 + 0.5);
6718 if (!CheckOutGray(xform, i, g)) return 0;
6722 cmsDeleteTransform(xform);
6728 cmsInt32Number CheckV4gamma(void)
6731 cmsUInt16Number Lin[] = {0, 0xffff};
6732 cmsToneCurve*g = cmsBuildTabulatedToneCurve16(DbgThread(), 2, Lin);
6734 h = cmsOpenProfileFromFileTHR(DbgThread(), "v4gamma.icc", "w");
6735 if (h == NULL) return 0;
6738 cmsSetProfileVersion(h, 4.3);
6740 if (!cmsWriteTag(h, cmsSigGrayTRCTag, g)) return 0;
6743 cmsFreeToneCurve(g);
6744 remove("v4gamma.icc");
6748 // cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname);
6750 // Gamut descriptor routines
6752 cmsInt32Number CheckGBD(void)
6756 cmsInt32Number L, a, b;
6757 cmsUInt32Number r1, g1, b1;
6758 cmsHPROFILE hLab, hsRGB;
6759 cmsHTRANSFORM xform;
6761 h = cmsGBDAlloc(DbgThread());
6762 if (h == NULL) return 0;
6764 // Fill all Lab gamut as valid
6765 SubTest("Filling RAW gamut");
6767 for (L=0; L <= 100; L += 10)
6768 for (a = -128; a <= 128; a += 5)
6769 for (b = -128; b <= 128; b += 5) {
6774 if (!cmsGDBAddPoint(h, &Lab)) return 0;
6777 // Complete boundaries
6778 SubTest("computing Lab gamut");
6779 if (!cmsGDBCompute(h, 0)) return 0;
6782 // All points should be inside gamut
6783 SubTest("checking Lab gamut");
6784 for (L=10; L <= 90; L += 25)
6785 for (a = -120; a <= 120; a += 25)
6786 for (b = -120; b <= 120; b += 25) {
6791 if (!cmsGDBCheckPoint(h, &Lab)) {
6799 SubTest("checking sRGB gamut");
6800 h = cmsGBDAlloc(DbgThread());
6801 hsRGB = cmsCreate_sRGBProfile();
6802 hLab = cmsCreateLab4Profile(NULL);
6804 xform = cmsCreateTransform(hsRGB, TYPE_RGB_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE);
6805 cmsCloseProfile(hsRGB); cmsCloseProfile(hLab);
6808 for (r1=0; r1 < 256; r1 += 5) {
6809 for (g1=0; g1 < 256; g1 += 5)
6810 for (b1=0; b1 < 256; b1 += 5) {
6813 cmsUInt8Number rgb[3];
6815 rgb[0] = (cmsUInt8Number) r1;
6816 rgb[1] = (cmsUInt8Number) g1;
6817 rgb[2] = (cmsUInt8Number) b1;
6819 cmsDoTransform(xform, rgb, &Lab, 1);
6821 // if (fabs(Lab.b) < 20 && Lab.a > 0) continue;
6823 if (!cmsGDBAddPoint(h, &Lab)) {
6833 if (!cmsGDBCompute(h, 0)) return 0;
6834 // cmsGBDdumpVRML(h, "c:\\colormaps\\lab.wrl");
6836 for (r1=10; r1 < 200; r1 += 10) {
6837 for (g1=10; g1 < 200; g1 += 10)
6838 for (b1=10; b1 < 200; b1 += 10) {
6841 cmsUInt8Number rgb[3];
6843 rgb[0] = (cmsUInt8Number) r1;
6844 rgb[1] = (cmsUInt8Number) g1;
6845 rgb[2] = (cmsUInt8Number) b1;
6847 cmsDoTransform(xform, rgb, &Lab, 1);
6848 if (!cmsGDBCheckPoint(h, &Lab)) {
6850 cmsDeleteTransform(xform);
6858 cmsDeleteTransform(xform);
6861 SubTest("checking LCh chroma ring");
6862 h = cmsGBDAlloc(DbgThread());
6865 for (r1=0; r1 < 360; r1++) {
6873 cmsLCh2Lab(&Lab, &LCh);
6874 if (!cmsGDBAddPoint(h, &Lab)) {
6881 if (!cmsGDBCompute(h, 0)) return 0;
6893 cmsHPROFILE pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r");
6894 cmsProfileID ProfileID1, ProfileID2, ProfileID3, ProfileID4;
6896 h =(_cmsICCPROFILE*) pProfile;
6897 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID1.ID8);
6898 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID2.ID8);
6900 cmsCloseProfile(pProfile);
6903 pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r");
6905 h =(_cmsICCPROFILE*) pProfile;
6906 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID3.ID8);
6907 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID4.ID8);
6909 cmsCloseProfile(pProfile);
6911 return ((memcmp(ProfileID1.ID8, ProfileID3.ID8, sizeof(ProfileID1)) == 0) &&
6912 (memcmp(ProfileID2.ID8, ProfileID4.ID8, sizeof(ProfileID2)) == 0));
6916 // --------------------------------------------------------------------------------------------------
6917 // P E R F O R M A N C E C H E C K S
6918 // --------------------------------------------------------------------------------------------------
6921 typedef struct {cmsUInt8Number r, g, b, a;} Scanline_rgb1;
6922 typedef struct {cmsUInt16Number r, g, b, a;} Scanline_rgb2;
6923 typedef struct {cmsUInt8Number r, g, b;} Scanline_rgb8;
6924 typedef struct {cmsUInt16Number r, g, b;} Scanline_rgb0;
6928 void TitlePerformance(const char* Txt)
6930 printf("%-45s: ", Txt); fflush(stdout);
6934 void PrintPerformance(cmsUInt32Number Bytes, cmsUInt32Number SizeOfPixel, cmsFloat64Number diff)
6936 cmsFloat64Number seconds = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
6937 cmsFloat64Number mpix_sec = Bytes / (1024.0*1024.0*seconds*SizeOfPixel);
6939 printf("%g MPixel/sec.\n", mpix_sec);
6945 void SpeedTest16bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
6948 cmsInt32Number r, g, b, j;
6950 cmsFloat64Number diff;
6951 cmsHTRANSFORM hlcmsxform;
6955 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
6956 Die("Unable to open profiles");
6958 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_16,
6959 hlcmsProfileOut, TYPE_RGB_16, Intent, cmsFLAGS_NOCACHE);
6960 cmsCloseProfile(hlcmsProfileIn);
6961 cmsCloseProfile(hlcmsProfileOut);
6963 Mb = 256*256*256*sizeof(Scanline_rgb0);
6964 In = (Scanline_rgb0*) malloc(Mb);
6967 for (r=0; r < 256; r++)
6968 for (g=0; g < 256; g++)
6969 for (b=0; b < 256; b++) {
6971 In[j].r = (cmsUInt16Number) ((r << 8) | r);
6972 In[j].g = (cmsUInt16Number) ((g << 8) | g);
6973 In[j].b = (cmsUInt16Number) ((b << 8) | b);
6979 TitlePerformance(Title);
6983 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
6985 diff = clock() - atime;
6988 PrintPerformance(Mb, sizeof(Scanline_rgb0), diff);
6989 cmsDeleteTransform(hlcmsxform);
6995 void SpeedTest16bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
6997 cmsInt32Number r, g, b, j;
6999 cmsFloat64Number diff;
7000 cmsHTRANSFORM hlcmsxform;
7004 if (hlcmsProfileOut == NULL || hlcmsProfileOut == NULL)
7005 Die("Unable to open profiles");
7007 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_16,
7008 hlcmsProfileOut, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
7009 cmsCloseProfile(hlcmsProfileIn);
7010 cmsCloseProfile(hlcmsProfileOut);
7012 Mb = 256*256*256*sizeof(Scanline_rgb2);
7014 In = (Scanline_rgb2*) malloc(Mb);
7017 for (r=0; r < 256; r++)
7018 for (g=0; g < 256; g++)
7019 for (b=0; b < 256; b++) {
7021 In[j].r = (cmsUInt16Number) ((r << 8) | r);
7022 In[j].g = (cmsUInt16Number) ((g << 8) | g);
7023 In[j].b = (cmsUInt16Number) ((b << 8) | b);
7030 TitlePerformance(Title);
7034 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7036 diff = clock() - atime;
7040 PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7042 cmsDeleteTransform(hlcmsxform);
7048 void SpeedTest8bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7050 cmsInt32Number r, g, b, j;
7052 cmsFloat64Number diff;
7053 cmsHTRANSFORM hlcmsxform;
7057 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7058 Die("Unable to open profiles");
7060 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_8,
7061 hlcmsProfileOut, TYPE_RGB_8, Intent, cmsFLAGS_NOCACHE);
7062 cmsCloseProfile(hlcmsProfileIn);
7063 cmsCloseProfile(hlcmsProfileOut);
7065 Mb = 256*256*256*sizeof(Scanline_rgb8);
7067 In = (Scanline_rgb8*) malloc(Mb);
7070 for (r=0; r < 256; r++)
7071 for (g=0; g < 256; g++)
7072 for (b=0; b < 256; b++) {
7074 In[j].r = (cmsUInt8Number) r;
7075 In[j].g = (cmsUInt8Number) g;
7076 In[j].b = (cmsUInt8Number) b;
7081 TitlePerformance(Title);
7085 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7087 diff = clock() - atime;
7091 PrintPerformance(Mb, sizeof(Scanline_rgb8), diff);
7093 cmsDeleteTransform(hlcmsxform);
7099 void SpeedTest8bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
7101 cmsInt32Number r, g, b, j;
7103 cmsFloat64Number diff;
7104 cmsHTRANSFORM hlcmsxform;
7108 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7109 Die("Unable to open profiles");
7111 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_8,
7112 hlcmsProfileOut, TYPE_CMYK_8, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
7113 cmsCloseProfile(hlcmsProfileIn);
7114 cmsCloseProfile(hlcmsProfileOut);
7116 Mb = 256*256*256*sizeof(Scanline_rgb2);
7118 In = (Scanline_rgb2*) malloc(Mb);
7121 for (r=0; r < 256; r++)
7122 for (g=0; g < 256; g++)
7123 for (b=0; b < 256; b++) {
7125 In[j].r = (cmsUInt8Number) r;
7126 In[j].g = (cmsUInt8Number) g;
7127 In[j].b = (cmsUInt8Number) b;
7128 In[j].a = (cmsUInt8Number) 0;
7133 TitlePerformance(Title);
7137 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7139 diff = clock() - atime;
7143 PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7146 cmsDeleteTransform(hlcmsxform);
7152 void SpeedTest8bitsGray(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7154 cmsInt32Number r, g, b, j;
7156 cmsFloat64Number diff;
7157 cmsHTRANSFORM hlcmsxform;
7162 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7163 Die("Unable to open profiles");
7165 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn,
7166 TYPE_GRAY_8, hlcmsProfileOut, TYPE_GRAY_8, Intent, cmsFLAGS_NOCACHE);
7167 cmsCloseProfile(hlcmsProfileIn);
7168 cmsCloseProfile(hlcmsProfileOut);
7171 In = (cmsUInt8Number*) malloc(Mb);
7174 for (r=0; r < 256; r++)
7175 for (g=0; g < 256; g++)
7176 for (b=0; b < 256; b++) {
7178 In[j] = (cmsUInt8Number) r;
7183 TitlePerformance(Title);
7187 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7189 diff = clock() - atime;
7192 PrintPerformance(Mb, sizeof(cmsUInt8Number), diff);
7193 cmsDeleteTransform(hlcmsxform);
7198 cmsHPROFILE CreateCurves(void)
7200 cmsToneCurve* Gamma = cmsBuildGamma(DbgThread(), 1.1);
7201 cmsToneCurve* Transfer[3];
7204 Transfer[0] = Transfer[1] = Transfer[2] = Gamma;
7205 h = cmsCreateLinearizationDeviceLink(cmsSigRgbData, Transfer);
7207 cmsFreeToneCurve(Gamma);
7214 void SpeedTest(void)
7217 printf("\n\nP E R F O R M A N C E T E S T S\n");
7218 printf( "=================================\n\n");
7221 SpeedTest16bits("16 bits on CLUT profiles",
7222 cmsOpenProfileFromFile("test5.icc", "r"),
7223 cmsOpenProfileFromFile("test3.icc", "r"), INTENT_PERCEPTUAL);
7225 SpeedTest8bits("8 bits on CLUT profiles",
7226 cmsOpenProfileFromFile("test5.icc", "r"),
7227 cmsOpenProfileFromFile("test3.icc", "r"),
7230 SpeedTest8bits("8 bits on Matrix-Shaper profiles",
7231 cmsOpenProfileFromFile("test5.icc", "r"),
7232 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7235 SpeedTest8bits("8 bits on SAME Matrix-Shaper profiles",
7236 cmsOpenProfileFromFile("test5.icc", "r"),
7237 cmsOpenProfileFromFile("test5.icc", "r"),
7240 SpeedTest8bits("8 bits on Matrix-Shaper profiles (AbsCol)",
7241 cmsOpenProfileFromFile("test5.icc", "r"),
7242 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7243 INTENT_ABSOLUTE_COLORIMETRIC);
7245 SpeedTest16bits("16 bits on Matrix-Shaper profiles",
7246 cmsOpenProfileFromFile("test5.icc", "r"),
7247 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7250 SpeedTest16bits("16 bits on SAME Matrix-Shaper profiles",
7251 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7252 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7255 SpeedTest16bits("16 bits on Matrix-Shaper profiles (AbsCol)",
7256 cmsOpenProfileFromFile("test5.icc", "r"),
7257 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7258 INTENT_ABSOLUTE_COLORIMETRIC);
7260 SpeedTest8bits("8 bits on curves",
7265 SpeedTest16bits("16 bits on curves",
7270 SpeedTest8bitsCMYK("8 bits on CMYK profiles",
7271 cmsOpenProfileFromFile("test1.icc", "r"),
7272 cmsOpenProfileFromFile("test2.icc", "r"));
7274 SpeedTest16bitsCMYK("16 bits on CMYK profiles",
7275 cmsOpenProfileFromFile("test1.icc", "r"),
7276 cmsOpenProfileFromFile("test2.icc", "r"));
7278 SpeedTest8bitsGray("8 bits on gray-to-gray",
7279 cmsOpenProfileFromFile("graylcms2.icc", "r"),
7280 cmsOpenProfileFromFile("glablcms2.icc", "r"), INTENT_RELATIVE_COLORIMETRIC);
7282 SpeedTest8bitsGray("8 bits on SAME gray-to-gray",
7283 cmsOpenProfileFromFile("graylcms2.icc", "r"),
7284 cmsOpenProfileFromFile("graylcms2.icc", "r"), INTENT_PERCEPTUAL);
7288 // -----------------------------------------------------------------------------------------------------
7291 // Print the supported intents
7293 void PrintSupportedIntents(void)
7295 cmsUInt32Number n, i;
7296 cmsUInt32Number Codes[200];
7297 char* Descriptions[200];
7299 n = cmsGetSupportedIntents(200, Codes, Descriptions);
7301 printf("Supported intents:\n");
7302 for (i=0; i < n; i++) {
7303 printf("\t%d - %s\n", Codes[i], Descriptions[i]);
7308 // ZOO checks ------------------------------------------------------------------------------------------------------------
7311 #ifdef CMS_IS_WINDOWS_
7313 static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\";
7314 static char ZOOwrite[cmsMAX_PATH] = "c:\\colormaps\\write\\";
7315 static char ZOORawWrite[cmsMAX_PATH] = "c:\\colormaps\\rawwrite\\";
7318 // Read all tags on a profile given by its handle
7320 void ReadAllTags(cmsHPROFILE h)
7322 cmsInt32Number i, n;
7323 cmsTagSignature sig;
7325 n = cmsGetTagCount(h);
7326 for (i=0; i < n; i++) {
7328 sig = cmsGetTagSignature(h, i);
7329 if (cmsReadTag(h, sig) == NULL) return;
7334 // Read all tags on a profile given by its handle
7336 void ReadAllRAWTags(cmsHPROFILE h)
7338 cmsInt32Number i, n;
7339 cmsTagSignature sig;
7342 n = cmsGetTagCount(h);
7343 for (i=0; i < n; i++) {
7345 sig = cmsGetTagSignature(h, i);
7346 len = cmsReadRawTag(h, sig, NULL, 0);
7352 void PrintInfo(cmsHPROFILE h, cmsInfoType Info)
7356 cmsContext id = DbgThread();
7358 len = cmsGetProfileInfo(h, Info, "en", "US", NULL, 0);
7359 if (len == 0) return;
7361 text = _cmsMalloc(id, len);
7362 cmsGetProfileInfo(h, Info, "en", "US", text, len);
7364 wprintf(L"%s\n", text);
7370 void PrintAllInfos(cmsHPROFILE h)
7372 PrintInfo(h, cmsInfoDescription);
7373 PrintInfo(h, cmsInfoManufacturer);
7374 PrintInfo(h, cmsInfoModel);
7375 PrintInfo(h, cmsInfoCopyright);
7380 void ReadAllLUTS(cmsHPROFILE h)
7385 a = _cmsReadInputLUT(h, INTENT_PERCEPTUAL);
7386 if (a) cmsPipelineFree(a);
7388 a = _cmsReadInputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7389 if (a) cmsPipelineFree(a);
7391 a = _cmsReadInputLUT(h, INTENT_SATURATION);
7392 if (a) cmsPipelineFree(a);
7394 a = _cmsReadInputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7395 if (a) cmsPipelineFree(a);
7398 a = _cmsReadOutputLUT(h, INTENT_PERCEPTUAL);
7399 if (a) cmsPipelineFree(a);
7401 a = _cmsReadOutputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7402 if (a) cmsPipelineFree(a);
7404 a = _cmsReadOutputLUT(h, INTENT_SATURATION);
7405 if (a) cmsPipelineFree(a);
7407 a = _cmsReadOutputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7408 if (a) cmsPipelineFree(a);
7411 a = _cmsReadDevicelinkLUT(h, INTENT_PERCEPTUAL);
7412 if (a) cmsPipelineFree(a);
7414 a = _cmsReadDevicelinkLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7415 if (a) cmsPipelineFree(a);
7417 a = _cmsReadDevicelinkLUT(h, INTENT_SATURATION);
7418 if (a) cmsPipelineFree(a);
7420 a = _cmsReadDevicelinkLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7421 if (a) cmsPipelineFree(a);
7424 cmsDetectBlackPoint(&Black, h, INTENT_PERCEPTUAL, 0);
7425 cmsDetectBlackPoint(&Black, h, INTENT_RELATIVE_COLORIMETRIC, 0);
7426 cmsDetectBlackPoint(&Black, h, INTENT_SATURATION, 0);
7427 cmsDetectBlackPoint(&Black, h, INTENT_ABSOLUTE_COLORIMETRIC, 0);
7431 // Check one specimen in the ZOO
7434 cmsInt32Number CheckSingleSpecimen(const char* Profile)
7440 sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
7441 sprintf(BuffDst, "%s%s", ZOOwrite, Profile);
7443 h = cmsOpenProfileFromFile(BuffSrc, "r");
7444 if (h == NULL) return 0;
7446 printf("%s\n", Profile);
7449 // ReadAllRAWTags(h);
7452 cmsSaveProfileToFile(h, BuffDst);
7455 h = cmsOpenProfileFromFile(BuffDst, "r");
7456 if (h == NULL) return 0;
7466 cmsInt32Number CheckRAWSpecimen(const char* Profile)
7472 sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
7473 sprintf(BuffDst, "%s%s", ZOORawWrite, Profile);
7475 h = cmsOpenProfileFromFile(BuffSrc, "r");
7476 if (h == NULL) return 0;
7480 cmsSaveProfileToFile(h, BuffDst);
7483 h = cmsOpenProfileFromFile(BuffDst, "r");
7484 if (h == NULL) return 0;
7493 void CheckProfileZOO(void)
7496 struct _finddata_t c_file;
7499 cmsSetLogErrorHandler(NULL);
7501 if ( (hFile = _findfirst("c:\\colormaps\\*.*", &c_file)) == -1L )
7502 printf("No files in current directory");
7508 printf("%s\n", c_file.name);
7509 if (strcmp(c_file.name, ".") != 0 &&
7510 strcmp(c_file.name, "..") != 0) {
7512 CheckSingleSpecimen( c_file.name);
7513 CheckRAWSpecimen( c_file.name);
7515 if (TotalMemory > 0)
7516 printf("Ok, but %s are left!\n", MemStr(TotalMemory));
7522 } while ( _findnext(hFile, &c_file) == 0 );
7527 cmsSetLogErrorHandler(FatalErrorQuit);
7534 #define TYPE_709 709
7535 static double Rec709Math(int Type, const double Params[], double R)
7542 if (R <= (Params[3]*Params[4])) Fun = R / Params[3];
7543 else Fun = pow(((R - Params[2])/Params[1]), Params[0]);
7548 if (R <= Params[4]) Fun = R * Params[3];
7549 else Fun = Params[1] * pow(R, (1/Params[0])) + Params[2];
7556 // Add nonstandard TRC curves -> Rec709
7557 cmsPluginParametricCurves NewCurvePlugin = {
7558 { cmsPluginMagicNumber, 2000, cmsPluginParametricCurveSig, NULL },
7559 1, {TYPE_709}, {5}, Rec709Math};
7565 // ---------------------------------------------------------------------------------------
7567 int main(int argc, char* argv[])
7569 cmsInt32Number Exhaustive = 0;
7570 cmsInt32Number DoSpeedTests = 1;
7574 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
7577 printf("LittleCMS %2.2f test bed %s %s\n\n", LCMS_VERSION / 1000.0, __DATE__, __TIME__);
7579 if ((argc == 2) && strcmp(argv[1], "--exhaustive") == 0) {
7582 printf("Running exhaustive tests (will take a while...)\n\n");
7586 printf("Installing debug memory plug-in ... ");
7587 cmsPlugin(&DebugMemHandler);
7590 printf("Installing error logger ... ");
7591 cmsSetLogErrorHandler(FatalErrorQuit);
7594 #ifdef CMS_IS_WINDOWS_
7595 // CheckProfileZOO();
7598 PrintSupportedIntents();
7600 // Create utility profiles
7601 Check("Creation of test profiles", CreateTestProfiles);
7603 Check("Base types", CheckBaseTypes);
7604 Check("endianess", CheckEndianess);
7605 Check("quick floor", CheckQuickFloor);
7606 Check("quick floor word", CheckQuickFloorWord);
7607 Check("Fixed point 15.16 representation", CheckFixedPoint15_16);
7608 Check("Fixed point 8.8 representation", CheckFixedPoint8_8);
7610 // Forward 1D interpolation
7611 Check("1D interpolation in 2pt tables", Check1DLERP2);
7612 Check("1D interpolation in 3pt tables", Check1DLERP3);
7613 Check("1D interpolation in 4pt tables", Check1DLERP4);
7614 Check("1D interpolation in 6pt tables", Check1DLERP6);
7615 Check("1D interpolation in 18pt tables", Check1DLERP18);
7616 Check("1D interpolation in descending 2pt tables", Check1DLERP2Down);
7617 Check("1D interpolation in descending 3pt tables", Check1DLERP3Down);
7618 Check("1D interpolation in descending 6pt tables", Check1DLERP6Down);
7619 Check("1D interpolation in descending 18pt tables", Check1DLERP18Down);
7623 Check("1D interpolation in n tables", ExhaustiveCheck1DLERP);
7624 Check("1D interpolation in descending tables", ExhaustiveCheck1DLERPDown);
7627 // Forward 3D interpolation
7628 Check("3D interpolation Tetrahedral (float) ", Check3DinterpolationFloatTetrahedral);
7629 Check("3D interpolation Trilinear (float) ", Check3DinterpolationFloatTrilinear);
7630 Check("3D interpolation Tetrahedral (16) ", Check3DinterpolationTetrahedral16);
7631 Check("3D interpolation Trilinear (16) ", Check3DinterpolationTrilinear16);
7635 Check("Exhaustive 3D interpolation Tetrahedral (float) ", ExaustiveCheck3DinterpolationFloatTetrahedral);
7636 Check("Exhaustive 3D interpolation Trilinear (float) ", ExaustiveCheck3DinterpolationFloatTrilinear);
7637 Check("Exhaustive 3D interpolation Tetrahedral (16) ", ExhaustiveCheck3DinterpolationTetrahedral16);
7638 Check("Exhaustive 3D interpolation Trilinear (16) ", ExhaustiveCheck3DinterpolationTrilinear16);
7641 Check("Reverse interpolation 3 -> 3", CheckReverseInterpolation3x3);
7642 Check("Reverse interpolation 4 -> 3", CheckReverseInterpolation4x3);
7645 // High dimensionality interpolation
7647 Check("3D interpolation", Check3Dinterp);
7648 Check("3D interpolation with granularity", Check3DinterpGranular);
7649 Check("4D interpolation", Check4Dinterp);
7650 Check("4D interpolation with granularity", Check4DinterpGranular);
7651 Check("5D interpolation with granularity", Check5DinterpGranular);
7652 Check("6D interpolation with granularity", Check6DinterpGranular);
7653 Check("7D interpolation with granularity", Check7DinterpGranular);
7654 Check("8D interpolation with granularity", Check8DinterpGranular);
7656 // Encoding of colorspaces
7657 Check("Lab to LCh and back (float only) ", CheckLab2LCh);
7658 Check("Lab to XYZ and back (float only) ", CheckLab2XYZ);
7659 Check("Lab to xyY and back (float only) ", CheckLab2xyY);
7660 Check("Lab V2 encoding", CheckLabV2encoding);
7661 Check("Lab V4 encoding", CheckLabV4encoding);
7664 Check("Blackbody radiator", CheckTemp2CHRM);
7667 Check("Linear gamma curves (16 bits)", CheckGammaCreation16);
7668 Check("Linear gamma curves (float)", CheckGammaCreationFlt);
7670 Check("Curve 1.8 (float)", CheckGamma18);
7671 Check("Curve 2.2 (float)", CheckGamma22);
7672 Check("Curve 3.0 (float)", CheckGamma30);
7674 Check("Curve 1.8 (table)", CheckGamma18Table);
7675 Check("Curve 2.2 (table)", CheckGamma22Table);
7676 Check("Curve 3.0 (table)", CheckGamma30Table);
7678 Check("Curve 1.8 (word table)", CheckGamma18TableWord);
7679 Check("Curve 2.2 (word table)", CheckGamma22TableWord);
7680 Check("Curve 3.0 (word table)", CheckGamma30TableWord);
7682 Check("Parametric curves", CheckParametricToneCurves);
7684 Check("Join curves", CheckJointCurves);
7685 Check("Join curves descending", CheckJointCurvesDescending);
7686 Check("Join curves degenerated", CheckReverseDegenerated);
7687 Check("Join curves sRGB (Float)", CheckJointFloatCurves_sRGB);
7688 Check("Join curves sRGB (16 bits)", CheckJoint16Curves_sRGB);
7689 Check("Join curves sigmoidal", CheckJointCurvesSShaped);
7692 Check("LUT creation & dup", CheckLUTcreation);
7693 Check("1 Stage LUT ", Check1StageLUT);
7694 Check("2 Stage LUT ", Check2StageLUT);
7695 Check("2 Stage LUT (16 bits)", Check2Stage16LUT);
7696 Check("3 Stage LUT ", Check3StageLUT);
7697 Check("3 Stage LUT (16 bits)", Check3Stage16LUT);
7698 Check("4 Stage LUT ", Check4StageLUT);
7699 Check("4 Stage LUT (16 bits)", Check4Stage16LUT);
7700 Check("5 Stage LUT ", Check5StageLUT);
7701 Check("5 Stage LUT (16 bits) ", Check5Stage16LUT);
7702 Check("6 Stage LUT ", Check6StageLUT);
7703 Check("6 Stage LUT (16 bits) ", Check6Stage16LUT);
7706 Check("Lab to Lab LUT (float only) ", CheckLab2LabLUT);
7707 Check("XYZ to XYZ LUT (float only) ", CheckXYZ2XYZLUT);
7708 Check("Lab to Lab MAT LUT (float only) ", CheckLab2LabMatLUT);
7709 Check("Named Color LUT", CheckNamedColorLUT);
7710 Check("Usual formatters", CheckFormatters16);
7711 Check("Floating point formatters", CheckFormattersFloat);
7713 // ChangeBuffersFormat
7714 Check("ChangeBuffersFormat", CheckChangeBufferFormat);
7717 Check("Multilocalized Unicode", CheckMLU);
7720 Check("Named color lists", CheckNamedColorList);
7722 // Profile I/O (this one is huge!)
7723 Check("Profile creation", CheckProfileCreation);
7727 Check("Error reporting on bad profiles", CheckErrReportingOnBadProfiles);
7728 Check("Error reporting on bad transforms", CheckErrReportingOnBadTransforms);
7731 Check("Curves only transforms", CheckCurvesOnlyTransforms);
7732 Check("Float Lab->Lab transforms", CheckFloatLabTransforms);
7733 Check("Encoded Lab->Lab transforms", CheckEncodedLabTransforms);
7734 Check("Stored identities", CheckStoredIdentities);
7736 Check("Matrix-shaper transform (float)", CheckMatrixShaperXFORMFloat);
7737 Check("Matrix-shaper transform (16 bits)", CheckMatrixShaperXFORM16);
7738 Check("Matrix-shaper transform (8 bits)", CheckMatrixShaperXFORM8);
7740 Check("Primaries of sRGB", CheckRGBPrimaries);
7743 Check("Known values across matrix-shaper", Chack_sRGB_Float);
7744 Check("Gray input profile", CheckInputGray);
7745 Check("Gray Lab input profile", CheckLabInputGray);
7746 Check("Gray output profile", CheckOutputGray);
7747 Check("Gray Lab output profile", CheckLabOutputGray);
7749 Check("Matrix-shaper proofing transform (float)", CheckProofingXFORMFloat);
7750 Check("Matrix-shaper proofing transform (16 bits)", CheckProofingXFORM16);
7752 Check("Gamut check", CheckGamutCheck);
7754 Check("CMYK roundtrip on perceptual transform", CheckCMYKRoundtrip);
7756 Check("CMYK perceptual transform", CheckCMYKPerceptual);
7757 // Check("CMYK rel.col. transform", CheckCMYKRelCol);
7759 Check("Black ink only preservation", CheckKOnlyBlackPreserving);
7760 Check("Black plane preservation", CheckKPlaneBlackPreserving);
7763 Check("Deciding curve types", CheckV4gamma);
7765 Check("Black point detection", CheckBlackPoint);
7766 Check("TAC detection", CheckTAC);
7768 Check("CGATS parser", CheckCGATS);
7769 Check("PostScript generator", CheckPostScript);
7770 Check("Segment maxima GBD", CheckGBD);
7771 Check("MD5 digest", CheckMD5);
7777 DebugMemPrintTotals();
7779 cmsUnregisterPlugins();
7782 RemoveTestProfiles();