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)
191 cmsUNUSED_PARAMETER(ContextID);
192 cmsUNUSED_PARAMETER(ErrorCode);
196 // Print a dot for gauging
200 fprintf(stdout, "."); fflush(stdout);
203 // Keep track of the reason to fail
205 void Fail(const char* frm, ...)
209 vsprintf(ReasonToFailBuffer, frm, args);
213 // Keep track of subtest
215 void SubTest(const char* frm, ...)
221 vsprintf(SubTestBuffer, frm, args);
228 const char* MemStr(cmsUInt32Number size)
230 static char Buffer[1024];
232 if (size > 1024*1024) {
233 sprintf(Buffer, "%g Mb", (cmsFloat64Number) size / (1024.0*1024.0));
237 sprintf(Buffer, "%g Kb", (cmsFloat64Number) size / 1024.0);
240 sprintf(Buffer, "%g bytes", (cmsFloat64Number) size);
246 // The check framework
248 void Check(const char* Title, TestFn Fn)
250 printf("Checking %s ...", Title);
253 ReasonToFailBuffer[0] = 0;
254 SubTestBuffer[0] = 0;
255 TrappedError = FALSE;
256 SimultaneousErrors = 0;
259 if (Fn() && !TrappedError) {
261 // It is a good place to check memory
263 printf("Ok, but %s are left!\n", MemStr(TotalMemory));
270 if (SubTestBuffer[0])
271 printf("%s: [%s]\n\t%s\n", Title, SubTestBuffer, ReasonToFailBuffer);
273 printf("%s:\n\t%s\n", Title, ReasonToFailBuffer);
275 if (SimultaneousErrors > 1)
276 printf("\tMore than one (%d) errors were reported\n", SimultaneousErrors);
283 // Dump a tone curve, for easy diagnostic
284 void DumpToneCurve(cmsToneCurve* gamma, const char* FileName)
289 hIT8 = cmsIT8Alloc(gamma ->InterpParams->ContextID);
291 cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_FIELDS", 2);
292 cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_SETS", gamma ->nEntries);
294 cmsIT8SetDataFormat(hIT8, 0, "SAMPLE_ID");
295 cmsIT8SetDataFormat(hIT8, 1, "VALUE");
297 for (i=0; i < gamma ->nEntries; i++) {
300 sprintf(Val, "%d", i);
301 cmsIT8SetDataRowCol(hIT8, i, 0, Val);
302 sprintf(Val, "0x%x", gamma ->Table16[i]);
303 cmsIT8SetDataRowCol(hIT8, i, 1, Val);
306 cmsIT8SaveToFile(hIT8, FileName);
310 // -------------------------------------------------------------------------------------------------
313 // Used to perform several checks.
314 // The space used is a clone of a well-known commercial
315 // color space which I will name "Above RGB"
317 cmsHPROFILE Create_AboveRGB(void)
319 cmsToneCurve* Curve[3];
320 cmsHPROFILE hProfile;
322 cmsCIExyYTRIPLE Primaries = {{0.64, 0.33, 1 },
326 Curve[0] = Curve[1] = Curve[2] = cmsBuildGamma(DbgThread(), 2.19921875);
328 cmsWhitePointFromTemp(&D65, 6504);
329 hProfile = cmsCreateRGBProfileTHR(DbgThread(), &D65, &Primaries, Curve);
330 cmsFreeToneCurve(Curve[0]);
335 // A gamma-2.2 gray space
337 cmsHPROFILE Create_Gray22(void)
339 cmsHPROFILE hProfile;
340 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 2.2);
341 if (Curve == NULL) return NULL;
343 hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
344 cmsFreeToneCurve(Curve);
349 // A gamma-3.0 gray space
351 cmsHPROFILE Create_Gray30(void)
353 cmsHPROFILE hProfile;
354 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 3.0);
355 if (Curve == NULL) return NULL;
357 hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
358 cmsFreeToneCurve(Curve);
365 cmsHPROFILE Create_GrayLab(void)
367 cmsHPROFILE hProfile;
368 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 1.0);
369 if (Curve == NULL) return NULL;
371 hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
372 cmsFreeToneCurve(Curve);
374 cmsSetPCS(hProfile, cmsSigLabData);
378 // A CMYK devicelink that adds gamma 3.0 to each channel
380 cmsHPROFILE Create_CMYK_DeviceLink(void)
382 cmsHPROFILE hProfile;
383 cmsToneCurve* Tab[4];
384 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 3.0);
385 if (Curve == NULL) return NULL;
392 hProfile = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigCmykData, Tab);
393 if (hProfile == NULL) return NULL;
395 cmsFreeToneCurve(Curve);
401 // Create a fake CMYK profile, without any other requeriment that being coarse CMYK.
402 // DONT USE THIS PROFILE FOR ANYTHING, IT IS USELESS BUT FOR TESTING PURPOSES.
405 cmsHTRANSFORM hLab2sRGB;
406 cmsHTRANSFORM sRGB2Lab;
407 cmsHTRANSFORM hIlimit;
412 cmsFloat64Number Clip(cmsFloat64Number v)
421 cmsInt32Number ForwardSampler(register const cmsUInt16Number In[], cmsUInt16Number Out[], void* Cargo)
423 FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
424 cmsFloat64Number rgb[3], cmyk[4];
425 cmsFloat64Number c, m, y, k;
427 cmsDoTransform(p ->hLab2sRGB, In, rgb, 1);
433 k = (c < m ? cmsmin(c, y) : cmsmin(m, y));
435 // NONSENSE WARNING!: I'm doing this just because this is a test
436 // profile that may have ink limit up to 400%. There is no UCR here
437 // so the profile is basically useless for anything but testing.
444 cmsDoTransform(p ->hIlimit, cmyk, Out, 1);
451 cmsInt32Number ReverseSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
453 FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
454 cmsFloat64Number c, m, y, k, rgb[3];
463 rgb[0] = Clip(1 - c);
464 rgb[1] = Clip(1 - m);
465 rgb[2] = Clip(1 - y);
470 rgb[0] = rgb[1] = rgb[2] = 0;
474 rgb[0] = Clip((1 - c) * (1 - k));
475 rgb[1] = Clip((1 - m) * (1 - k));
476 rgb[2] = Clip((1 - y) * (1 - k));
479 cmsDoTransform(p ->sRGB2Lab, rgb, Out, 1);
486 cmsHPROFILE CreateFakeCMYK(cmsFloat64Number InkLimit, cmsBool lUseAboveRGB)
489 cmsPipeline* AToB0, *BToA0;
491 cmsContext ContextID;
493 cmsHPROFILE hLab, hsRGB, hLimit;
494 cmsUInt32Number cmykfrm;
498 hsRGB = Create_AboveRGB();
500 hsRGB = cmsCreate_sRGBProfile();
502 hLab = cmsCreateLab4Profile(NULL);
503 hLimit = cmsCreateInkLimitingDeviceLink(cmsSigCmykData, InkLimit);
505 cmykfrm = FLOAT_SH(1) | BYTES_SH(0)|CHANNELS_SH(4);
506 p.hLab2sRGB = cmsCreateTransform(hLab, TYPE_Lab_16, hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
507 p.sRGB2Lab = cmsCreateTransform(hsRGB, TYPE_RGB_DBL, hLab, TYPE_Lab_16, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
508 p.hIlimit = cmsCreateTransform(hLimit, cmykfrm, NULL, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
510 cmsCloseProfile(hLab); cmsCloseProfile(hsRGB); cmsCloseProfile(hLimit);
512 ContextID = DbgThread();
513 hICC = cmsCreateProfilePlaceholder(ContextID);
514 if (!hICC) return NULL;
516 cmsSetProfileVersion(hICC, 4.3);
518 cmsSetDeviceClass(hICC, cmsSigOutputClass);
519 cmsSetColorSpace(hICC, cmsSigCmykData);
520 cmsSetPCS(hICC, cmsSigLabData);
522 BToA0 = cmsPipelineAlloc(ContextID, 3, 4);
523 if (BToA0 == NULL) return 0;
524 CLUT = cmsStageAllocCLut16bit(ContextID, 17, 3, 4, NULL);
525 if (CLUT == NULL) return 0;
526 if (!cmsStageSampleCLut16bit(CLUT, ForwardSampler, &p, 0)) return 0;
528 cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3));
529 cmsPipelineInsertStage(BToA0, cmsAT_END, CLUT);
530 cmsPipelineInsertStage(BToA0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 4));
532 if (!cmsWriteTag(hICC, cmsSigBToA0Tag, (void*) BToA0)) return 0;
533 cmsPipelineFree(BToA0);
535 AToB0 = cmsPipelineAlloc(ContextID, 4, 3);
536 if (AToB0 == NULL) return 0;
537 CLUT = cmsStageAllocCLut16bit(ContextID, 17, 4, 3, NULL);
538 if (CLUT == NULL) return 0;
539 if (!cmsStageSampleCLut16bit(CLUT, ReverseSampler, &p, 0)) return 0;
541 cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 4));
542 cmsPipelineInsertStage(AToB0, cmsAT_END, CLUT);
543 cmsPipelineInsertStage(AToB0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 3));
545 if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) AToB0)) return 0;
546 cmsPipelineFree(AToB0);
548 cmsDeleteTransform(p.hLab2sRGB);
549 cmsDeleteTransform(p.sRGB2Lab);
550 cmsDeleteTransform(p.hIlimit);
552 cmsLinkTag(hICC, cmsSigAToB1Tag, cmsSigAToB0Tag);
553 cmsLinkTag(hICC, cmsSigAToB2Tag, cmsSigAToB0Tag);
554 cmsLinkTag(hICC, cmsSigBToA1Tag, cmsSigBToA0Tag);
555 cmsLinkTag(hICC, cmsSigBToA2Tag, cmsSigBToA0Tag);
561 // Does create several profiles for latter use------------------------------------------------------------------------------------------------
564 cmsInt32Number OneVirtual(cmsHPROFILE h, const char* SubTestTxt, const char* FileName)
567 if (h == NULL) return 0;
569 if (!cmsSaveProfileToFile(h, FileName)) return 0;
572 h = cmsOpenProfileFromFile(FileName, "r");
573 if (h == NULL) return 0;
584 // This test checks the ability of lcms2 to save its built-ins as valid profiles.
585 // It does not check the functionality of such profiles
587 cmsInt32Number CreateTestProfiles(void)
591 h = cmsCreate_sRGBProfileTHR(DbgThread());
592 if (!OneVirtual(h, "sRGB profile", "sRGBlcms2.icc")) return 0;
596 h = Create_AboveRGB();
597 if (!OneVirtual(h, "aRGB profile", "aRGBlcms2.icc")) return 0;
602 if (!OneVirtual(h, "Gray profile", "graylcms2.icc")) return 0;
607 if (!OneVirtual(h, "Gray 3.0 profile", "gray3lcms2.icc")) return 0;
611 h = Create_GrayLab();
612 if (!OneVirtual(h, "Gray Lab profile", "glablcms2.icc")) return 0;
616 h = Create_CMYK_DeviceLink();
617 if (!OneVirtual(h, "Linearization profile", "linlcms2.icc")) return 0;
620 h = cmsCreateInkLimitingDeviceLinkTHR(DbgThread(), cmsSigCmykData, 150);
621 if (h == NULL) return 0;
622 if (!OneVirtual(h, "Ink-limiting profile", "limitlcms2.icc")) return 0;
626 h = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
627 if (!OneVirtual(h, "Lab 2 identity profile", "labv2lcms2.icc")) return 0;
631 h = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
632 if (!OneVirtual(h, "Lab 4 identity profile", "labv4lcms2.icc")) return 0;
636 h = cmsCreateXYZProfileTHR(DbgThread());
637 if (!OneVirtual(h, "XYZ identity profile", "xyzlcms2.icc")) return 0;
641 h = cmsCreateNULLProfileTHR(DbgThread());
642 if (!OneVirtual(h, "NULL profile", "nullcms2.icc")) return 0;
646 h = cmsCreateBCHSWabstractProfileTHR(DbgThread(), 17, 0, 0, 0, 0, 5000, 6000);
647 if (!OneVirtual(h, "BCHS profile", "bchslcms2.icc")) return 0;
651 h = CreateFakeCMYK(300, FALSE);
652 if (!OneVirtual(h, "Fake CMYK profile", "lcms2cmyk.icc")) return 0;
658 void RemoveTestProfiles(void)
660 remove("sRGBlcms2.icc");
661 remove("aRGBlcms2.icc");
662 remove("graylcms2.icc");
663 remove("gray3lcms2.icc");
664 remove("linlcms2.icc");
665 remove("limitlcms2.icc");
666 remove("labv2lcms2.icc");
667 remove("labv4lcms2.icc");
668 remove("xyzlcms2.icc");
669 remove("nullcms2.icc");
670 remove("bchslcms2.icc");
671 remove("lcms2cmyk.icc");
672 remove("glablcms2.icc");
673 remove("lcms2link.icc");
674 remove("lcms2link2.icc");
677 // -------------------------------------------------------------------------------------------------
679 // Check the size of basic types. If this test fails, nothing is going to work anyway
681 cmsInt32Number CheckBaseTypes(void)
683 // Ignore warnings about conditional expression
685 #pragma warning(disable: 4127)
688 if (sizeof(cmsUInt8Number) != 1) return 0;
689 if (sizeof(cmsInt8Number) != 1) return 0;
690 if (sizeof(cmsUInt16Number) != 2) return 0;
691 if (sizeof(cmsInt16Number) != 2) return 0;
692 if (sizeof(cmsUInt32Number) != 4) return 0;
693 if (sizeof(cmsInt32Number) != 4) return 0;
694 if (sizeof(cmsUInt64Number) != 8) return 0;
695 if (sizeof(cmsInt64Number) != 8) return 0;
696 if (sizeof(cmsFloat32Number) != 4) return 0;
697 if (sizeof(cmsFloat64Number) != 8) return 0;
698 if (sizeof(cmsSignature) != 4) return 0;
699 if (sizeof(cmsU8Fixed8Number) != 2) return 0;
700 if (sizeof(cmsS15Fixed16Number) != 4) return 0;
701 if (sizeof(cmsU16Fixed16Number) != 4) return 0;
706 // -------------------------------------------------------------------------------------------------
709 // Are we little or big endian? From Harbison&Steele.
711 cmsInt32Number CheckEndianess(void)
713 cmsInt32Number BigEndian, IsOk;
716 char c[sizeof (long)];
720 BigEndian = (u.c[sizeof (long) - 1] == 1);
722 #ifdef CMS_USE_BIG_ENDIAN
729 Fail("\nOOOPPSS! You have CMS_USE_BIG_ENDIAN toggle misconfigured!\n\n"
730 "Please, edit lcms2.h and %s the CMS_USE_BIG_ENDIAN toggle.\n", BigEndian? "uncomment" : "comment");
739 cmsInt32Number CheckQuickFloor(void)
741 if ((_cmsQuickFloor(1.234) != 1) ||
742 (_cmsQuickFloor(32767.234) != 32767) ||
743 (_cmsQuickFloor(-1.234) != -2) ||
744 (_cmsQuickFloor(-32767.1) != -32768)) {
746 Fail("\nOOOPPSS! _cmsQuickFloor() does not work as expected in your machine!\n\n"
747 "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
755 // Quick floor restricted to word
757 cmsInt32Number CheckQuickFloorWord(void)
761 for (i=0; i < 65535; i++) {
763 if (_cmsQuickFloorWord((cmsFloat64Number) i + 0.1234) != i) {
765 Fail("\nOOOPPSS! _cmsQuickFloorWord() does not work as expected in your machine!\n\n"
766 "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
774 // -------------------------------------------------------------------------------------------------
778 // On 15.16 fixed point, this is the maximum we can obtain. Remember ICC profiles have storage limits on this number
779 #define FIXED_PRECISION_15_16 (1.0 / 65535.0)
781 // On 8.8 fixed point, that is the max we can obtain.
782 #define FIXED_PRECISION_8_8 (1.0 / 255.0)
784 // On cmsFloat32Number type, this is the precision we expect
785 #define FLOAT_PRECISSION (0.00001)
787 static cmsFloat64Number MaxErr;
788 static cmsFloat64Number AllowedErr = FIXED_PRECISION_15_16;
791 cmsBool IsGoodVal(const char *title, cmsFloat64Number in, cmsFloat64Number out, cmsFloat64Number max)
793 cmsFloat64Number Err = fabs(in - out);
795 if (Err > MaxErr) MaxErr = Err;
799 Fail("(%s): Must be %f, But is %f ", title, in, out);
807 cmsBool IsGoodFixed15_16(const char *title, cmsFloat64Number in, cmsFloat64Number out)
809 return IsGoodVal(title, in, out, FIXED_PRECISION_15_16);
813 cmsBool IsGoodFixed8_8(const char *title, cmsFloat64Number in, cmsFloat64Number out)
815 return IsGoodVal(title, in, out, FIXED_PRECISION_8_8);
819 cmsBool IsGoodWord(const char *title, cmsUInt16Number in, cmsUInt16Number out)
821 if ((abs(in - out) > 0 )) {
823 Fail("(%s): Must be %x, But is %x ", title, in, out);
831 cmsBool IsGoodWordPrec(const char *title, cmsUInt16Number in, cmsUInt16Number out, cmsUInt16Number maxErr)
833 if ((abs(in - out) > maxErr )) {
835 Fail("(%s): Must be %x, But is %x ", title, in, out);
842 // Fixed point ----------------------------------------------------------------------------------------------
845 cmsInt32Number TestSingleFixed15_16(cmsFloat64Number d)
847 cmsS15Fixed16Number f = _cmsDoubleTo15Fixed16(d);
848 cmsFloat64Number RoundTrip = _cms15Fixed16toDouble(f);
849 cmsFloat64Number Error = fabs(d - RoundTrip);
851 return ( Error <= FIXED_PRECISION_15_16);
855 cmsInt32Number CheckFixedPoint15_16(void)
857 if (!TestSingleFixed15_16(1.0)) return 0;
858 if (!TestSingleFixed15_16(2.0)) return 0;
859 if (!TestSingleFixed15_16(1.23456)) return 0;
860 if (!TestSingleFixed15_16(0.99999)) return 0;
861 if (!TestSingleFixed15_16(0.1234567890123456789099999)) return 0;
862 if (!TestSingleFixed15_16(-1.0)) return 0;
863 if (!TestSingleFixed15_16(-2.0)) return 0;
864 if (!TestSingleFixed15_16(-1.23456)) return 0;
865 if (!TestSingleFixed15_16(-1.1234567890123456789099999)) return 0;
866 if (!TestSingleFixed15_16(+32767.1234567890123456789099999)) return 0;
867 if (!TestSingleFixed15_16(-32767.1234567890123456789099999)) return 0;
872 cmsInt32Number TestSingleFixed8_8(cmsFloat64Number d)
874 cmsS15Fixed16Number f = _cmsDoubleTo8Fixed8(d);
875 cmsFloat64Number RoundTrip = _cms8Fixed8toDouble((cmsUInt16Number) f);
876 cmsFloat64Number Error = fabs(d - RoundTrip);
878 return ( Error <= FIXED_PRECISION_8_8);
882 cmsInt32Number CheckFixedPoint8_8(void)
884 if (!TestSingleFixed8_8(1.0)) return 0;
885 if (!TestSingleFixed8_8(2.0)) return 0;
886 if (!TestSingleFixed8_8(1.23456)) return 0;
887 if (!TestSingleFixed8_8(0.99999)) return 0;
888 if (!TestSingleFixed8_8(0.1234567890123456789099999)) return 0;
889 if (!TestSingleFixed8_8(+255.1234567890123456789099999)) return 0;
894 // Linear interpolation -----------------------------------------------------------------------------------------------
896 // Since prime factors of 65535 (FFFF) are,
898 // 0xFFFF = 3 * 5 * 17 * 257
900 // I test tables of 2, 4, 6, and 18 points, that will be exact.
903 void BuildTable(cmsInt32Number n, cmsUInt16Number Tab[], cmsBool Descending)
907 for (i=0; i < n; i++) {
908 cmsFloat64Number v = (cmsFloat64Number) ((cmsFloat64Number) 65535.0 * i ) / (n-1);
910 Tab[Descending ? (n - i - 1) : i ] = (cmsUInt16Number) floor(v + 0.5);
914 // A single function that does check 1D interpolation
915 // nNodesToCheck = number on nodes to check
916 // Down = Create decreasing tables
917 // Reverse = Check reverse interpolation
918 // max_err = max allowed error
921 cmsInt32Number Check1D(cmsInt32Number nNodesToCheck, cmsBool Down, cmsInt32Number max_err)
924 cmsUInt16Number in, out;
926 cmsUInt16Number* Tab;
928 Tab = (cmsUInt16Number*) malloc(sizeof(cmsUInt16Number)* nNodesToCheck);
929 if (Tab == NULL) return 0;
931 p = _cmsComputeInterpParams(DbgThread(), nNodesToCheck, 1, 1, Tab, CMS_LERP_FLAGS_16BITS);
932 if (p == NULL) return 0;
934 BuildTable(nNodesToCheck, Tab, Down);
936 for (i=0; i <= 0xffff; i++) {
938 in = (cmsUInt16Number) i;
941 p ->Interpolation.Lerp16(&in, &out, p);
943 if (Down) out = 0xffff - out;
945 if (abs(out - in) > max_err) {
947 Fail("(%dp): Must be %x, But is %x : ", nNodesToCheck, in, out);
948 _cmsFreeInterpParams(p);
954 _cmsFreeInterpParams(p);
961 cmsInt32Number Check1DLERP2(void)
963 return Check1D(2, FALSE, 0);
968 cmsInt32Number Check1DLERP3(void)
970 return Check1D(3, FALSE, 1);
975 cmsInt32Number Check1DLERP4(void)
977 return Check1D(4, FALSE, 0);
981 cmsInt32Number Check1DLERP6(void)
983 return Check1D(6, FALSE, 0);
987 cmsInt32Number Check1DLERP18(void)
989 return Check1D(18, FALSE, 0);
994 cmsInt32Number Check1DLERP2Down(void)
996 return Check1D(2, TRUE, 0);
1001 cmsInt32Number Check1DLERP3Down(void)
1003 return Check1D(3, TRUE, 1);
1007 cmsInt32Number Check1DLERP6Down(void)
1009 return Check1D(6, TRUE, 0);
1013 cmsInt32Number Check1DLERP18Down(void)
1015 return Check1D(18, TRUE, 0);
1019 cmsInt32Number ExhaustiveCheck1DLERP(void)
1024 for (j=10; j <= 4096; j++) {
1026 if ((j % 10) == 0) printf("%d \r", j);
1028 if (!Check1D(j, FALSE, 1)) return 0;
1031 printf("\rResult is ");
1036 cmsInt32Number ExhaustiveCheck1DLERPDown(void)
1041 for (j=10; j <= 4096; j++) {
1043 if ((j % 10) == 0) printf("%d \r", j);
1045 if (!Check1D(j, TRUE, 1)) return 0;
1049 printf("\rResult is ");
1055 // 3D interpolation -------------------------------------------------------------------------------------------------
1058 cmsInt32Number Check3DinterpolationFloatTetrahedral(void)
1062 cmsFloat32Number In[3], Out[3];
1063 cmsFloat32Number FloatTable[] = { //R G B
1065 0, 0, 0, // B=0,G=0,R=0
1066 0, 0, .25, // B=1,G=0,R=0
1068 0, .5, 0, // B=0,G=1,R=0
1069 0, .5, .25, // B=1,G=1,R=0
1071 1, 0, 0, // B=0,G=0,R=1
1072 1, 0, .25, // B=1,G=0,R=1
1074 1, .5, 0, // B=0,G=1,R=1
1075 1, .5, .25 // B=1,G=1,R=1
1079 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
1083 for (i=0; i < 0xffff; i++) {
1085 In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
1087 p ->Interpolation.LerpFloat(In, Out, p);
1089 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1090 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1091 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1094 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1095 _cmsFreeInterpParams(p);
1099 _cmsFreeInterpParams(p);
1104 cmsInt32Number Check3DinterpolationFloatTrilinear(void)
1108 cmsFloat32Number In[3], Out[3];
1109 cmsFloat32Number FloatTable[] = { //R G B
1111 0, 0, 0, // B=0,G=0,R=0
1112 0, 0, .25, // B=1,G=0,R=0
1114 0, .5, 0, // B=0,G=1,R=0
1115 0, .5, .25, // B=1,G=1,R=0
1117 1, 0, 0, // B=0,G=0,R=1
1118 1, 0, .25, // B=1,G=0,R=1
1120 1, .5, 0, // B=0,G=1,R=1
1121 1, .5, .25 // B=1,G=1,R=1
1125 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
1128 for (i=0; i < 0xffff; i++) {
1130 In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
1132 p ->Interpolation.LerpFloat(In, Out, p);
1134 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1135 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1136 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1139 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1140 _cmsFreeInterpParams(p);
1144 _cmsFreeInterpParams(p);
1150 cmsInt32Number Check3DinterpolationTetrahedral16(void)
1154 cmsUInt16Number In[3], Out[3];
1155 cmsUInt16Number Table[] = {
1167 0xffff, 0xffff, 0xffff
1170 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
1173 for (i=0; i < 0xffff; i++) {
1175 In[0] = In[1] = In[2] = (cmsUInt16Number) i;
1177 p ->Interpolation.Lerp16(In, Out, p);
1179 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1180 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1181 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1184 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1185 _cmsFreeInterpParams(p);
1189 _cmsFreeInterpParams(p);
1194 cmsInt32Number Check3DinterpolationTrilinear16(void)
1198 cmsUInt16Number In[3], Out[3];
1199 cmsUInt16Number Table[] = {
1211 0xffff, 0xffff, 0xffff
1214 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
1217 for (i=0; i < 0xffff; i++) {
1219 In[0] = In[1] = In[2] = (cmsUInt16Number) i;
1221 p ->Interpolation.Lerp16(In, Out, p);
1223 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1224 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1225 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1228 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1229 _cmsFreeInterpParams(p);
1233 _cmsFreeInterpParams(p);
1239 cmsInt32Number ExaustiveCheck3DinterpolationFloatTetrahedral(void)
1242 cmsInt32Number r, g, b;
1243 cmsFloat32Number In[3], Out[3];
1244 cmsFloat32Number FloatTable[] = { //R G B
1246 0, 0, 0, // B=0,G=0,R=0
1247 0, 0, .25, // B=1,G=0,R=0
1249 0, .5, 0, // B=0,G=1,R=0
1250 0, .5, .25, // B=1,G=1,R=0
1252 1, 0, 0, // B=0,G=0,R=1
1253 1, 0, .25, // B=1,G=0,R=1
1255 1, .5, 0, // B=0,G=1,R=1
1256 1, .5, .25 // B=1,G=1,R=1
1260 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
1263 for (r=0; r < 0xff; r++)
1264 for (g=0; g < 0xff; g++)
1265 for (b=0; b < 0xff; b++)
1268 In[0] = (cmsFloat32Number) r / 255.0F;
1269 In[1] = (cmsFloat32Number) g / 255.0F;
1270 In[2] = (cmsFloat32Number) b / 255.0F;
1273 p ->Interpolation.LerpFloat(In, Out, p);
1275 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1276 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1277 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1280 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1281 _cmsFreeInterpParams(p);
1285 _cmsFreeInterpParams(p);
1290 cmsInt32Number ExaustiveCheck3DinterpolationFloatTrilinear(void)
1293 cmsInt32Number r, g, b;
1294 cmsFloat32Number In[3], Out[3];
1295 cmsFloat32Number FloatTable[] = { //R G B
1297 0, 0, 0, // B=0,G=0,R=0
1298 0, 0, .25, // B=1,G=0,R=0
1300 0, .5, 0, // B=0,G=1,R=0
1301 0, .5, .25, // B=1,G=1,R=0
1303 1, 0, 0, // B=0,G=0,R=1
1304 1, 0, .25, // B=1,G=0,R=1
1306 1, .5, 0, // B=0,G=1,R=1
1307 1, .5, .25 // B=1,G=1,R=1
1311 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
1314 for (r=0; r < 0xff; r++)
1315 for (g=0; g < 0xff; g++)
1316 for (b=0; b < 0xff; b++)
1319 In[0] = (cmsFloat32Number) r / 255.0F;
1320 In[1] = (cmsFloat32Number) g / 255.0F;
1321 In[2] = (cmsFloat32Number) b / 255.0F;
1324 p ->Interpolation.LerpFloat(In, Out, p);
1326 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1327 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1328 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1331 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1332 _cmsFreeInterpParams(p);
1336 _cmsFreeInterpParams(p);
1342 cmsInt32Number ExhaustiveCheck3DinterpolationTetrahedral16(void)
1345 cmsInt32Number r, g, b;
1346 cmsUInt16Number In[3], Out[3];
1347 cmsUInt16Number Table[] = {
1359 0xffff, 0xffff, 0xffff
1362 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
1364 for (r=0; r < 0xff; r++)
1365 for (g=0; g < 0xff; g++)
1366 for (b=0; b < 0xff; b++)
1368 In[0] = (cmsUInt16Number) r ;
1369 In[1] = (cmsUInt16Number) g ;
1370 In[2] = (cmsUInt16Number) b ;
1373 p ->Interpolation.Lerp16(In, Out, p);
1375 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1376 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1377 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1380 _cmsFreeInterpParams(p);
1384 _cmsFreeInterpParams(p);
1389 cmsInt32Number ExhaustiveCheck3DinterpolationTrilinear16(void)
1392 cmsInt32Number r, g, b;
1393 cmsUInt16Number In[3], Out[3];
1394 cmsUInt16Number Table[] = {
1406 0xffff, 0xffff, 0xffff
1409 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
1411 for (r=0; r < 0xff; r++)
1412 for (g=0; g < 0xff; g++)
1413 for (b=0; b < 0xff; b++)
1415 In[0] = (cmsUInt16Number) r ;
1416 In[1] = (cmsUInt16Number)g ;
1417 In[2] = (cmsUInt16Number)b ;
1420 p ->Interpolation.Lerp16(In, Out, p);
1422 if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1423 if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1424 if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1428 _cmsFreeInterpParams(p);
1432 _cmsFreeInterpParams(p);
1436 // Check reverse interpolation on LUTS. This is right now exclusively used by K preservation algorithm
1438 cmsInt32Number CheckReverseInterpolation3x3(void)
1442 cmsFloat32Number Target[3], Result[3], Hint[3];
1443 cmsFloat32Number err, max;
1445 cmsUInt16Number Table[] = {
1448 0, 0, 0xffff, // 0 0 1
1450 0, 0xffff, 0, // 0 1 0
1451 0, 0xffff, 0xffff, // 0 1 1
1453 0xffff, 0, 0, // 1 0 0
1454 0xffff, 0, 0xffff, // 1 0 1
1456 0xffff, 0xffff, 0, // 1 1 0
1457 0xffff, 0xffff, 0xffff, // 1 1 1
1462 Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1464 clut = cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table);
1465 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
1467 Target[0] = 0; Target[1] = 0; Target[2] = 0;
1468 Hint[0] = 0; Hint[1] = 0; Hint[2] = 0;
1469 cmsPipelineEvalReverseFloat(Target, Result, NULL, Lut);
1470 if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0){
1472 Fail("Reverse interpolation didn't find zero");
1476 // Transverse identity
1478 for (i=0; i <= 100; i++) {
1480 cmsFloat32Number in = i / 100.0F;
1482 Target[0] = in; Target[1] = 0; Target[2] = 0;
1483 cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1485 err = fabsf(in - Result[0]);
1486 if (err > max) max = err;
1488 memcpy(Hint, Result, sizeof(Hint));
1491 cmsPipelineFree(Lut);
1492 return (max <= FLOAT_PRECISSION);
1497 cmsInt32Number CheckReverseInterpolation4x3(void)
1501 cmsFloat32Number Target[4], Result[4], Hint[4];
1502 cmsFloat32Number err, max;
1505 // 4 -> 3, output gets 3 first channels copied
1506 cmsUInt16Number Table[] = {
1508 0, 0, 0, // 0 0 0 0 = ( 0, 0, 0)
1509 0, 0, 0, // 0 0 0 1 = ( 0, 0, 0)
1511 0, 0, 0xffff, // 0 0 1 0 = ( 0, 0, 1)
1512 0, 0, 0xffff, // 0 0 1 1 = ( 0, 0, 1)
1514 0, 0xffff, 0, // 0 1 0 0 = ( 0, 1, 0)
1515 0, 0xffff, 0, // 0 1 0 1 = ( 0, 1, 0)
1517 0, 0xffff, 0xffff, // 0 1 1 0 = ( 0, 1, 1)
1518 0, 0xffff, 0xffff, // 0 1 1 1 = ( 0, 1, 1)
1520 0xffff, 0, 0, // 1 0 0 0 = ( 1, 0, 0)
1521 0xffff, 0, 0, // 1 0 0 1 = ( 1, 0, 0)
1523 0xffff, 0, 0xffff, // 1 0 1 0 = ( 1, 0, 1)
1524 0xffff, 0, 0xffff, // 1 0 1 1 = ( 1, 0, 1)
1526 0xffff, 0xffff, 0, // 1 1 0 0 = ( 1, 1, 0)
1527 0xffff, 0xffff, 0, // 1 1 0 1 = ( 1, 1, 0)
1529 0xffff, 0xffff, 0xffff, // 1 1 1 0 = ( 1, 1, 1)
1530 0xffff, 0xffff, 0xffff, // 1 1 1 1 = ( 1, 1, 1)
1534 Lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1536 clut = cmsStageAllocCLut16bit(DbgThread(), 2, 4, 3, Table);
1537 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
1539 // Check if the LUT is behaving as expected
1540 SubTest("4->3 feasibility");
1541 for (i=0; i <= 100; i++) {
1543 Target[0] = i / 100.0F;
1544 Target[1] = Target[0];
1548 cmsPipelineEvalFloat(Target, Result, Lut);
1550 if (!IsGoodFixed15_16("0", Target[0], Result[0])) return 0;
1551 if (!IsGoodFixed15_16("1", Target[1], Result[1])) return 0;
1552 if (!IsGoodFixed15_16("2", Target[2], Result[2])) return 0;
1555 SubTest("4->3 zero");
1560 // This one holds the fixed K
1563 // This is our hint (which is a big lie in this case)
1564 Hint[0] = 0.1F; Hint[1] = 0.1F; Hint[2] = 0.1F;
1566 cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1568 if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0 || Result[3] != 0){
1570 Fail("Reverse interpolation didn't find zero");
1574 SubTest("4->3 find CMY");
1576 for (i=0; i <= 100; i++) {
1578 cmsFloat32Number in = i / 100.0F;
1580 Target[0] = in; Target[1] = 0; Target[2] = 0;
1581 cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1583 err = fabsf(in - Result[0]);
1584 if (err > max) max = err;
1586 memcpy(Hint, Result, sizeof(Hint));
1589 cmsPipelineFree(Lut);
1590 return (max <= FLOAT_PRECISSION);
1595 // Check all interpolation.
1598 cmsUInt16Number Fn8D1(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1599 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1602 return (cmsUInt16Number) ((a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8) / m);
1607 cmsUInt16Number Fn8D2(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1608 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1611 return (cmsUInt16Number) ((a1 + 3* a2 + 3* a3 + a4 + a5 + a6 + a7 + a8 ) / (m + 4));
1616 cmsUInt16Number Fn8D3(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1617 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1620 return (cmsUInt16Number) ((3*a1 + 2*a2 + 3*a3 + a4 + a5 + a6 + a7 + a8) / (m + 5));
1627 cmsInt32Number Sampler3D(register const cmsUInt16Number In[],
1628 register cmsUInt16Number Out[],
1629 register void * Cargo)
1632 Out[0] = Fn8D1(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1633 Out[1] = Fn8D2(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1634 Out[2] = Fn8D3(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1638 cmsUNUSED_PARAMETER(Cargo);
1643 cmsInt32Number Sampler4D(register const cmsUInt16Number In[],
1644 register cmsUInt16Number Out[],
1645 register void * Cargo)
1648 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1649 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1650 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1654 cmsUNUSED_PARAMETER(Cargo);
1658 cmsInt32Number Sampler5D(register const cmsUInt16Number In[],
1659 register cmsUInt16Number Out[],
1660 register void * Cargo)
1663 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1664 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1665 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1669 cmsUNUSED_PARAMETER(Cargo);
1673 cmsInt32Number Sampler6D(register const cmsUInt16Number In[],
1674 register cmsUInt16Number Out[],
1675 register void * Cargo)
1678 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1679 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1680 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1684 cmsUNUSED_PARAMETER(Cargo);
1688 cmsInt32Number Sampler7D(register const cmsUInt16Number In[],
1689 register cmsUInt16Number Out[],
1690 register void * Cargo)
1693 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1694 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1695 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1699 cmsUNUSED_PARAMETER(Cargo);
1703 cmsInt32Number Sampler8D(register const cmsUInt16Number In[],
1704 register cmsUInt16Number Out[],
1705 register void * Cargo)
1708 Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1709 Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1710 Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1714 cmsUNUSED_PARAMETER(Cargo);
1718 cmsBool CheckOne3D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3)
1720 cmsUInt16Number In[3], Out1[3], Out2[3];
1722 In[0] = a1; In[1] = a2; In[2] = a3;
1724 // This is the interpolated value
1725 cmsPipelineEval16(In, Out1, lut);
1727 // This is the real value
1728 Sampler3D(In, Out2, NULL);
1730 // Let's see the difference
1732 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1733 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1734 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1740 cmsBool CheckOne4D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4)
1742 cmsUInt16Number In[4], Out1[3], Out2[3];
1744 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4;
1746 // This is the interpolated value
1747 cmsPipelineEval16(In, Out1, lut);
1749 // This is the real value
1750 Sampler4D(In, Out2, NULL);
1752 // Let's see the difference
1754 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1755 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1756 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1762 cmsBool CheckOne5D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1763 cmsUInt16Number a3, cmsUInt16Number a4, cmsUInt16Number a5)
1765 cmsUInt16Number In[5], Out1[3], Out2[3];
1767 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5;
1769 // This is the interpolated value
1770 cmsPipelineEval16(In, Out1, lut);
1772 // This is the real value
1773 Sampler5D(In, Out2, NULL);
1775 // Let's see the difference
1777 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1778 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1779 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1785 cmsBool CheckOne6D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1786 cmsUInt16Number a3, cmsUInt16Number a4,
1787 cmsUInt16Number a5, cmsUInt16Number a6)
1789 cmsUInt16Number In[6], Out1[3], Out2[3];
1791 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6;
1793 // This is the interpolated value
1794 cmsPipelineEval16(In, Out1, lut);
1796 // This is the real value
1797 Sampler6D(In, Out2, NULL);
1799 // Let's see the difference
1801 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1802 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1803 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1810 cmsBool CheckOne7D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1811 cmsUInt16Number a3, cmsUInt16Number a4,
1812 cmsUInt16Number a5, cmsUInt16Number a6,
1815 cmsUInt16Number In[7], Out1[3], Out2[3];
1817 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6; In[6] = a7;
1819 // This is the interpolated value
1820 cmsPipelineEval16(In, Out1, lut);
1822 // This is the real value
1823 Sampler7D(In, Out2, NULL);
1825 // Let's see the difference
1827 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1828 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1829 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1836 cmsBool CheckOne8D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1837 cmsUInt16Number a3, cmsUInt16Number a4,
1838 cmsUInt16Number a5, cmsUInt16Number a6,
1839 cmsUInt16Number a7, cmsUInt16Number a8)
1841 cmsUInt16Number In[8], Out1[3], Out2[3];
1843 In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6; In[6] = a7; In[7] = a8;
1845 // This is the interpolated value
1846 cmsPipelineEval16(In, Out1, lut);
1848 // This is the real value
1849 Sampler8D(In, Out2, NULL);
1851 // Let's see the difference
1853 if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1854 if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1855 if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1862 cmsInt32Number Check3Dinterp(void)
1867 lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1868 mpe = cmsStageAllocCLut16bit(DbgThread(), 9, 3, 3, NULL);
1869 cmsStageSampleCLut16bit(mpe, Sampler3D, NULL, 0);
1870 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1874 if (!CheckOne3D(lut, 0, 0, 0)) return 0;
1875 if (!CheckOne3D(lut, 0xffff, 0xffff, 0xffff)) return 0;
1877 if (!CheckOne3D(lut, 0x8080, 0x8080, 0x8080)) return 0;
1878 if (!CheckOne3D(lut, 0x0000, 0xFE00, 0x80FF)) return 0;
1879 if (!CheckOne3D(lut, 0x1111, 0x2222, 0x3333)) return 0;
1880 if (!CheckOne3D(lut, 0x0000, 0x0012, 0x0013)) return 0;
1881 if (!CheckOne3D(lut, 0x3141, 0x1415, 0x1592)) return 0;
1882 if (!CheckOne3D(lut, 0xFF00, 0xFF01, 0xFF12)) return 0;
1884 cmsPipelineFree(lut);
1890 cmsInt32Number Check3DinterpGranular(void)
1894 cmsUInt32Number Dimensions[] = { 7, 8, 9 };
1896 lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1897 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 3, 3, NULL);
1898 cmsStageSampleCLut16bit(mpe, Sampler3D, NULL, 0);
1899 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1903 if (!CheckOne3D(lut, 0, 0, 0)) return 0;
1904 if (!CheckOne3D(lut, 0xffff, 0xffff, 0xffff)) return 0;
1906 if (!CheckOne3D(lut, 0x8080, 0x8080, 0x8080)) return 0;
1907 if (!CheckOne3D(lut, 0x0000, 0xFE00, 0x80FF)) return 0;
1908 if (!CheckOne3D(lut, 0x1111, 0x2222, 0x3333)) return 0;
1909 if (!CheckOne3D(lut, 0x0000, 0x0012, 0x0013)) return 0;
1910 if (!CheckOne3D(lut, 0x3141, 0x1415, 0x1592)) return 0;
1911 if (!CheckOne3D(lut, 0xFF00, 0xFF01, 0xFF12)) return 0;
1913 cmsPipelineFree(lut);
1920 cmsInt32Number Check4Dinterp(void)
1925 lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1926 mpe = cmsStageAllocCLut16bit(DbgThread(), 9, 4, 3, NULL);
1927 cmsStageSampleCLut16bit(mpe, Sampler4D, NULL, 0);
1928 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1932 if (!CheckOne4D(lut, 0, 0, 0, 0)) return 0;
1933 if (!CheckOne4D(lut, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
1935 if (!CheckOne4D(lut, 0x8080, 0x8080, 0x8080, 0x8080)) return 0;
1936 if (!CheckOne4D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888)) return 0;
1937 if (!CheckOne4D(lut, 0x1111, 0x2222, 0x3333, 0x4444)) return 0;
1938 if (!CheckOne4D(lut, 0x0000, 0x0012, 0x0013, 0x0014)) return 0;
1939 if (!CheckOne4D(lut, 0x3141, 0x1415, 0x1592, 0x9261)) return 0;
1940 if (!CheckOne4D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13)) return 0;
1942 cmsPipelineFree(lut);
1950 cmsInt32Number Check4DinterpGranular(void)
1954 cmsUInt32Number Dimensions[] = { 9, 8, 7, 6 };
1956 lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1957 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 4, 3, NULL);
1958 cmsStageSampleCLut16bit(mpe, Sampler4D, NULL, 0);
1959 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1963 if (!CheckOne4D(lut, 0, 0, 0, 0)) return 0;
1964 if (!CheckOne4D(lut, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
1966 if (!CheckOne4D(lut, 0x8080, 0x8080, 0x8080, 0x8080)) return 0;
1967 if (!CheckOne4D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888)) return 0;
1968 if (!CheckOne4D(lut, 0x1111, 0x2222, 0x3333, 0x4444)) return 0;
1969 if (!CheckOne4D(lut, 0x0000, 0x0012, 0x0013, 0x0014)) return 0;
1970 if (!CheckOne4D(lut, 0x3141, 0x1415, 0x1592, 0x9261)) return 0;
1971 if (!CheckOne4D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13)) return 0;
1973 cmsPipelineFree(lut);
1980 cmsInt32Number Check5DinterpGranular(void)
1984 cmsUInt32Number Dimensions[] = { 3, 2, 2, 2, 2 };
1986 lut = cmsPipelineAlloc(DbgThread(), 5, 3);
1987 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 5, 3, NULL);
1988 cmsStageSampleCLut16bit(mpe, Sampler5D, NULL, 0);
1989 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1993 if (!CheckOne5D(lut, 0, 0, 0, 0, 0)) return 0;
1994 if (!CheckOne5D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
1996 if (!CheckOne5D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234)) return 0;
1997 if (!CheckOne5D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078)) return 0;
1998 if (!CheckOne5D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455)) return 0;
1999 if (!CheckOne5D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333)) return 0;
2000 if (!CheckOne5D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567)) return 0;
2001 if (!CheckOne5D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344)) return 0;
2003 cmsPipelineFree(lut);
2009 cmsInt32Number Check6DinterpGranular(void)
2013 cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2 };
2015 lut = cmsPipelineAlloc(DbgThread(), 6, 3);
2016 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 6, 3, NULL);
2017 cmsStageSampleCLut16bit(mpe, Sampler6D, NULL, 0);
2018 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
2022 if (!CheckOne6D(lut, 0, 0, 0, 0, 0, 0)) return 0;
2023 if (!CheckOne6D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
2025 if (!CheckOne6D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122)) return 0;
2026 if (!CheckOne6D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233)) return 0;
2027 if (!CheckOne6D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344)) return 0;
2028 if (!CheckOne6D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455)) return 0;
2029 if (!CheckOne6D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566)) return 0;
2030 if (!CheckOne6D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677)) return 0;
2032 cmsPipelineFree(lut);
2038 cmsInt32Number Check7DinterpGranular(void)
2042 cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2, 2 };
2044 lut = cmsPipelineAlloc(DbgThread(), 7, 3);
2045 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 7, 3, NULL);
2046 cmsStageSampleCLut16bit(mpe, Sampler7D, NULL, 0);
2047 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
2051 if (!CheckOne7D(lut, 0, 0, 0, 0, 0, 0, 0)) return 0;
2052 if (!CheckOne7D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
2054 if (!CheckOne7D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122, 0x0056)) return 0;
2055 if (!CheckOne7D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233, 0x0088)) return 0;
2056 if (!CheckOne7D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344, 0x1987)) return 0;
2057 if (!CheckOne7D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455, 0x9988)) return 0;
2058 if (!CheckOne7D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566, 0xfe56)) return 0;
2059 if (!CheckOne7D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677, 0xbabe)) return 0;
2061 cmsPipelineFree(lut);
2068 cmsInt32Number Check8DinterpGranular(void)
2072 cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2, 2, 2 };
2074 lut = cmsPipelineAlloc(DbgThread(), 8, 3);
2075 mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 8, 3, NULL);
2076 cmsStageSampleCLut16bit(mpe, Sampler8D, NULL, 0);
2077 cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
2081 if (!CheckOne8D(lut, 0, 0, 0, 0, 0, 0, 0, 0)) return 0;
2082 if (!CheckOne8D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0;
2084 if (!CheckOne8D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122, 0x0056, 0x0011)) return 0;
2085 if (!CheckOne8D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233, 0x0088, 0x2020)) return 0;
2086 if (!CheckOne8D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344, 0x1987, 0x4532)) return 0;
2087 if (!CheckOne8D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455, 0x9988, 0x1200)) return 0;
2088 if (!CheckOne8D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566, 0xfe56, 0x6666)) return 0;
2089 if (!CheckOne8D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677, 0xbabe, 0xface)) return 0;
2091 cmsPipelineFree(lut);
2096 // Colorimetric conversions -------------------------------------------------------------------------------------------------
2098 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2100 cmsInt32Number CheckLab2LCh(void)
2102 cmsInt32Number l, a, b;
2103 cmsFloat64Number dist, Max = 0;
2104 cmsCIELab Lab, Lab2;
2107 for (l=0; l <= 100; l += 10) {
2109 for (a=-128; a <= +128; a += 8) {
2111 for (b=-128; b <= 128; b += 8) {
2117 cmsLab2LCh(&LCh, &Lab);
2118 cmsLCh2Lab(&Lab2, &LCh);
2120 dist = cmsDeltaE(&Lab, &Lab2);
2121 if (dist > Max) Max = dist;
2129 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2131 cmsInt32Number CheckLab2XYZ(void)
2133 cmsInt32Number l, a, b;
2134 cmsFloat64Number dist, Max = 0;
2135 cmsCIELab Lab, Lab2;
2138 for (l=0; l <= 100; l += 10) {
2140 for (a=-128; a <= +128; a += 8) {
2142 for (b=-128; b <= 128; b += 8) {
2148 cmsLab2XYZ(NULL, &XYZ, &Lab);
2149 cmsXYZ2Lab(NULL, &Lab2, &XYZ);
2151 dist = cmsDeltaE(&Lab, &Lab2);
2152 if (dist > Max) Max = dist;
2161 // Lab to xyY and back should be performed at 1E-12 accuracy at least
2163 cmsInt32Number CheckLab2xyY(void)
2165 cmsInt32Number l, a, b;
2166 cmsFloat64Number dist, Max = 0;
2167 cmsCIELab Lab, Lab2;
2171 for (l=0; l <= 100; l += 10) {
2173 for (a=-128; a <= +128; a += 8) {
2175 for (b=-128; b <= 128; b += 8) {
2181 cmsLab2XYZ(NULL, &XYZ, &Lab);
2182 cmsXYZ2xyY(&xyY, &XYZ);
2183 cmsxyY2XYZ(&XYZ, &xyY);
2184 cmsXYZ2Lab(NULL, &Lab2, &XYZ);
2186 dist = cmsDeltaE(&Lab, &Lab2);
2187 if (dist > Max) Max = dist;
2198 cmsInt32Number CheckLabV2encoding(void)
2200 cmsInt32Number n2, i, j;
2201 cmsUInt16Number Inw[3], aw[3];
2206 for (j=0; j < 65535; j++) {
2208 Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
2210 cmsLabEncoded2FloatV2(&Lab, Inw);
2211 cmsFloat2LabEncodedV2(aw, &Lab);
2213 for (i=0; i < 3; i++) {
2226 cmsInt32Number CheckLabV4encoding(void)
2228 cmsInt32Number n2, i, j;
2229 cmsUInt16Number Inw[3], aw[3];
2234 for (j=0; j < 65535; j++) {
2236 Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
2238 cmsLabEncoded2Float(&Lab, Inw);
2239 cmsFloat2LabEncoded(aw, &Lab);
2241 for (i=0; i < 3; i++) {
2254 // BlackBody -----------------------------------------------------------------------------------------------------
2257 cmsInt32Number CheckTemp2CHRM(void)
2260 cmsFloat64Number d, v, Max = 0;
2263 for (j=4000; j < 25000; j++) {
2265 cmsWhitePointFromTemp(&White, j);
2266 if (!cmsTempFromWhitePoint(&v, &White)) return 0;
2269 if (d > Max) Max = d;
2272 // 100 degree is the actual resolution
2278 // Tone curves -----------------------------------------------------------------------------------------------------
2281 cmsInt32Number CheckGammaEstimation(cmsToneCurve* c, cmsFloat64Number g)
2283 cmsFloat64Number est = cmsEstimateGamma(c, 0.001);
2285 SubTest("Gamma estimation");
2286 if (fabs(est - g) > 0.001) return 0;
2291 cmsInt32Number CheckGammaCreation16(void)
2293 cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2295 cmsUInt16Number in, out;
2297 for (i=0; i < 0xffff; i++) {
2299 in = (cmsUInt16Number) i;
2300 out = cmsEvalToneCurve16(LinGamma, in);
2302 Fail("(lin gamma): Must be %x, But is %x : ", in, out);
2303 cmsFreeToneCurve(LinGamma);
2308 if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2310 cmsFreeToneCurve(LinGamma);
2316 cmsInt32Number CheckGammaCreationFlt(void)
2318 cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2320 cmsFloat32Number in, out;
2322 for (i=0; i < 0xffff; i++) {
2324 in = (cmsFloat32Number) (i / 65535.0);
2325 out = cmsEvalToneCurveFloat(LinGamma, in);
2326 if (fabs(in - out) > (1/65535.0)) {
2327 Fail("(lin gamma): Must be %f, But is %f : ", in, out);
2328 cmsFreeToneCurve(LinGamma);
2333 if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2334 cmsFreeToneCurve(LinGamma);
2338 // Curve curves using a single power function
2339 // Error is given in 0..ffff counts
2341 cmsInt32Number CheckGammaFloat(cmsFloat64Number g)
2343 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), g);
2345 cmsFloat32Number in, out;
2346 cmsFloat64Number val, Err;
2349 for (i=0; i < 0xffff; i++) {
2351 in = (cmsFloat32Number) (i / 65535.0);
2352 out = cmsEvalToneCurveFloat(Curve, in);
2353 val = pow((cmsFloat64Number) in, g);
2355 Err = fabs( val - out);
2356 if (Err > MaxErr) MaxErr = Err;
2359 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2361 if (!CheckGammaEstimation(Curve, g)) return 0;
2363 cmsFreeToneCurve(Curve);
2367 static cmsInt32Number CheckGamma18(void)
2369 return CheckGammaFloat(1.8);
2372 static cmsInt32Number CheckGamma22(void)
2374 return CheckGammaFloat(2.2);
2377 static cmsInt32Number CheckGamma30(void)
2379 return CheckGammaFloat(3.0);
2383 // Check table-based gamma functions
2385 cmsInt32Number CheckGammaFloatTable(cmsFloat64Number g)
2387 cmsFloat32Number Values[1025];
2388 cmsToneCurve* Curve;
2390 cmsFloat32Number in, out;
2391 cmsFloat64Number val, Err;
2393 for (i=0; i <= 1024; i++) {
2395 in = (cmsFloat32Number) (i / 1024.0);
2396 Values[i] = powf(in, (float) g);
2399 Curve = cmsBuildTabulatedToneCurveFloat(DbgThread(), 1025, Values);
2402 for (i=0; i <= 0xffff; i++) {
2404 in = (cmsFloat32Number) (i / 65535.0);
2405 out = cmsEvalToneCurveFloat(Curve, in);
2408 Err = fabs(val - out);
2409 if (Err > MaxErr) MaxErr = Err;
2412 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2414 if (!CheckGammaEstimation(Curve, g)) return 0;
2416 cmsFreeToneCurve(Curve);
2421 static cmsInt32Number CheckGamma18Table(void)
2423 return CheckGammaFloatTable(1.8);
2426 static cmsInt32Number CheckGamma22Table(void)
2428 return CheckGammaFloatTable(2.2);
2431 static cmsInt32Number CheckGamma30Table(void)
2433 return CheckGammaFloatTable(3.0);
2436 // Create a curve from a table (which is a pure gamma function) and check it against the pow function.
2438 cmsInt32Number CheckGammaWordTable(cmsFloat64Number g)
2440 cmsUInt16Number Values[1025];
2441 cmsToneCurve* Curve;
2443 cmsFloat32Number in, out;
2444 cmsFloat64Number val, Err;
2446 for (i=0; i <= 1024; i++) {
2448 in = (cmsFloat32Number) (i / 1024.0);
2449 Values[i] = (cmsUInt16Number) floor(pow(in, g) * 65535.0 + 0.5);
2452 Curve = cmsBuildTabulatedToneCurve16(DbgThread(), 1025, Values);
2455 for (i=0; i <= 0xffff; i++) {
2457 in = (cmsFloat32Number) (i / 65535.0);
2458 out = cmsEvalToneCurveFloat(Curve, in);
2461 Err = fabs(val - out);
2462 if (Err > MaxErr) MaxErr = Err;
2465 if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2467 if (!CheckGammaEstimation(Curve, g)) return 0;
2469 cmsFreeToneCurve(Curve);
2473 static cmsInt32Number CheckGamma18TableWord(void)
2475 return CheckGammaWordTable(1.8);
2478 static cmsInt32Number CheckGamma22TableWord(void)
2480 return CheckGammaWordTable(2.2);
2483 static cmsInt32Number CheckGamma30TableWord(void)
2485 return CheckGammaWordTable(3.0);
2489 // Curve joining test. Joining two high-gamma of 3.0 curves should
2490 // give something like linear
2492 cmsInt32Number CheckJointCurves(void)
2494 cmsToneCurve *Forward, *Reverse, *Result;
2497 Forward = cmsBuildGamma(DbgThread(), 3.0);
2498 Reverse = cmsBuildGamma(DbgThread(), 3.0);
2500 Result = cmsJoinToneCurve(DbgThread(), Forward, Reverse, 256);
2502 cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
2504 rc = cmsIsToneCurveLinear(Result);
2505 cmsFreeToneCurve(Result);
2508 Fail("Joining same curve twice does not result in a linear ramp");
2514 // Create a gamma curve by cheating the table
2516 cmsToneCurve* GammaTableLinear(cmsInt32Number nEntries, cmsBool Dir)
2519 cmsToneCurve* g = cmsBuildTabulatedToneCurve16(DbgThread(), nEntries, NULL);
2521 for (i=0; i < nEntries; i++) {
2523 cmsInt32Number v = _cmsQuantizeVal(i, nEntries);
2526 g->Table16[i] = (cmsUInt16Number) v;
2528 g->Table16[i] = (cmsUInt16Number) (0xFFFF - v);
2536 cmsInt32Number CheckJointCurvesDescending(void)
2538 cmsToneCurve *Forward, *Reverse, *Result;
2539 cmsInt32Number i, rc;
2541 Forward = cmsBuildGamma(DbgThread(), 2.2);
2543 // Fake the curve to be table-based
2545 for (i=0; i < 4096; i++)
2546 Forward ->Table16[i] = 0xffff - Forward->Table16[i];
2547 Forward ->Segments[0].Type = 0;
2549 Reverse = cmsReverseToneCurve(Forward);
2551 Result = cmsJoinToneCurve(DbgThread(), Reverse, Reverse, 256);
2553 cmsFreeToneCurve(Forward);
2554 cmsFreeToneCurve(Reverse);
2556 rc = cmsIsToneCurveLinear(Result);
2557 cmsFreeToneCurve(Result);
2564 cmsInt32Number CheckFToneCurvePoint(cmsToneCurve* c, cmsUInt16Number Point, cmsInt32Number Value)
2566 cmsInt32Number Result;
2568 Result = cmsEvalToneCurve16(c, Point);
2570 return (abs(Value - Result) < 2);
2574 cmsInt32Number CheckReverseDegenerated(void)
2576 cmsToneCurve* p, *g;
2577 cmsUInt16Number Tab[16];
2596 p = cmsBuildTabulatedToneCurve16(DbgThread(), 16, Tab);
2597 g = cmsReverseToneCurve(p);
2599 // Now let's check some points
2600 if (!CheckFToneCurvePoint(g, 0x5555, 0x5555)) return 0;
2601 if (!CheckFToneCurvePoint(g, 0x7777, 0x7777)) return 0;
2603 // First point for zero
2604 if (!CheckFToneCurvePoint(g, 0x0000, 0x4444)) return 0;
2607 if (!CheckFToneCurvePoint(g, 0xFFFF, 0xFFFF)) return 0;
2609 cmsFreeToneCurve(p);
2610 cmsFreeToneCurve(g);
2616 // Build a parametric sRGB-like curve
2618 cmsToneCurve* Build_sRGBGamma(void)
2620 cmsFloat64Number Parameters[5];
2622 Parameters[0] = 2.4;
2623 Parameters[1] = 1. / 1.055;
2624 Parameters[2] = 0.055 / 1.055;
2625 Parameters[3] = 1. / 12.92;
2626 Parameters[4] = 0.04045; // d
2628 return cmsBuildParametricToneCurve(DbgThread(), 4, Parameters);
2633 // Join two gamma tables in floting point format. Result should be a straight line
2635 cmsToneCurve* CombineGammaFloat(cmsToneCurve* g1, cmsToneCurve* g2)
2637 cmsUInt16Number Tab[256];
2641 for (i=0; i < 256; i++) {
2643 f = (cmsFloat32Number) i / 255.0F;
2644 f = cmsEvalToneCurveFloat(g2, cmsEvalToneCurveFloat(g1, f));
2646 Tab[i] = (cmsUInt16Number) floor(f * 65535.0 + 0.5);
2649 return cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
2652 // Same of anterior, but using quantized tables
2654 cmsToneCurve* CombineGamma16(cmsToneCurve* g1, cmsToneCurve* g2)
2656 cmsUInt16Number Tab[256];
2660 for (i=0; i < 256; i++) {
2662 cmsUInt16Number wValIn;
2664 wValIn = _cmsQuantizeVal(i, 256);
2665 Tab[i] = cmsEvalToneCurve16(g2, cmsEvalToneCurve16(g1, wValIn));
2668 return cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
2672 cmsInt32Number CheckJointFloatCurves_sRGB(void)
2674 cmsToneCurve *Forward, *Reverse, *Result;
2677 Forward = Build_sRGBGamma();
2678 Reverse = cmsReverseToneCurve(Forward);
2679 Result = CombineGammaFloat(Forward, Reverse);
2680 cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
2682 rc = cmsIsToneCurveLinear(Result);
2683 cmsFreeToneCurve(Result);
2689 cmsInt32Number CheckJoint16Curves_sRGB(void)
2691 cmsToneCurve *Forward, *Reverse, *Result;
2694 Forward = Build_sRGBGamma();
2695 Reverse = cmsReverseToneCurve(Forward);
2696 Result = CombineGamma16(Forward, Reverse);
2697 cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
2699 rc = cmsIsToneCurveLinear(Result);
2700 cmsFreeToneCurve(Result);
2705 // sigmoidal curve f(x) = (1-x^g) ^(1/g)
2708 cmsInt32Number CheckJointCurvesSShaped(void)
2710 cmsFloat64Number p = 3.2;
2711 cmsToneCurve *Forward, *Reverse, *Result;
2714 Forward = cmsBuildParametricToneCurve(DbgThread(), 108, &p);
2715 Reverse = cmsReverseToneCurve(Forward);
2716 Result = cmsJoinToneCurve(DbgThread(), Forward, Forward, 4096);
2718 cmsFreeToneCurve(Forward);
2719 cmsFreeToneCurve(Reverse);
2721 rc = cmsIsToneCurveLinear(Result);
2722 cmsFreeToneCurve(Result);
2727 // --------------------------------------------------------------------------------------------------------
2729 // Implementation of some tone curve functions
2731 cmsFloat32Number Gamma(cmsFloat32Number x, const cmsFloat64Number Params[])
2733 return (cmsFloat32Number) pow(x, Params[0]);
2737 cmsFloat32Number CIE122(cmsFloat32Number x, const cmsFloat64Number Params[])
2740 cmsFloat64Number e, Val;
2742 if (x >= -Params[2] / Params[1]) {
2744 e = Params[1]*x + Params[2];
2747 Val = pow(e, Params[0]);
2754 return (cmsFloat32Number) Val;
2758 cmsFloat32Number IEC61966_3(cmsFloat32Number x, const cmsFloat64Number Params[])
2760 cmsFloat64Number e, Val;
2762 if (x >= -Params[2] / Params[1]) {
2764 e = Params[1]*x + Params[2];
2767 Val = pow(e, Params[0]) + Params[3];
2774 return (cmsFloat32Number) Val;
2778 cmsFloat32Number IEC61966_21(cmsFloat32Number x, const cmsFloat64Number Params[])
2780 cmsFloat64Number e, Val;
2782 if (x >= Params[4]) {
2784 e = Params[1]*x + Params[2];
2787 Val = pow(e, Params[0]);
2792 Val = x * Params[3];
2794 return (cmsFloat32Number) Val;
2798 cmsFloat32Number param_5(cmsFloat32Number x, const cmsFloat64Number Params[])
2800 cmsFloat64Number e, Val;
2801 // Y = (aX + b)^Gamma + e | X >= d
2802 // Y = cX + f | else
2803 if (x >= Params[4]) {
2805 e = Params[1]*x + Params[2];
2807 Val = pow(e, Params[0]) + Params[5];
2812 Val = x*Params[3] + Params[6];
2814 return (cmsFloat32Number) Val;
2818 cmsFloat32Number param_6(cmsFloat32Number x, const cmsFloat64Number Params[])
2820 cmsFloat64Number e, Val;
2822 e = Params[1]*x + Params[2];
2824 Val = pow(e, Params[0]) + Params[3];
2828 return (cmsFloat32Number) Val;
2832 cmsFloat32Number param_7(cmsFloat32Number x, const cmsFloat64Number Params[])
2834 cmsFloat64Number Val;
2837 Val = Params[1]*log10(Params[2] * pow(x, Params[0]) + Params[3]) + Params[4];
2839 return (cmsFloat32Number) Val;
2844 cmsFloat32Number param_8(cmsFloat32Number x, const cmsFloat64Number Params[])
2846 cmsFloat64Number Val;
2848 Val = (Params[0] * pow(Params[1], Params[2] * x + Params[3]) + Params[4]);
2850 return (cmsFloat32Number) Val;
2855 cmsFloat32Number sigmoidal(cmsFloat32Number x, const cmsFloat64Number Params[])
2857 cmsFloat64Number Val;
2859 Val = pow(1.0 - pow(1 - x, 1/Params[0]), 1/Params[0]);
2861 return (cmsFloat32Number) Val;
2866 cmsBool CheckSingleParametric(const char* Name, dblfnptr fn, cmsInt32Number Type, const cmsFloat64Number Params[])
2871 char InverseText[256];
2873 tc = cmsBuildParametricToneCurve(DbgThread(), Type, Params);
2874 tc_1 = cmsBuildParametricToneCurve(DbgThread(), -Type, Params);
2876 for (i=0; i <= 1000; i++) {
2878 cmsFloat32Number x = (cmsFloat32Number) i / 1000;
2879 cmsFloat32Number y_fn, y_param, x_param, y_param2;
2881 y_fn = fn(x, Params);
2882 y_param = cmsEvalToneCurveFloat(tc, x);
2883 x_param = cmsEvalToneCurveFloat(tc_1, y_param);
2885 y_param2 = fn(x_param, Params);
2887 if (!IsGoodVal(Name, y_fn, y_param, FIXED_PRECISION_15_16))
2890 sprintf(InverseText, "Inverse %s", Name);
2891 if (!IsGoodVal(InverseText, y_fn, y_param2, FIXED_PRECISION_15_16))
2895 cmsFreeToneCurve(tc);
2896 cmsFreeToneCurve(tc_1);
2900 cmsFreeToneCurve(tc);
2901 cmsFreeToneCurve(tc_1);
2905 // Check against some known values
2907 cmsInt32Number CheckParametricToneCurves(void)
2909 cmsFloat64Number Params[10];
2915 if (!CheckSingleParametric("Gamma", Gamma, 1, Params)) return 0;
2918 // Y = (aX + b)^Gamma | X >= -b/a
2925 if (!CheckSingleParametric("CIE122-1966", CIE122, 2, Params)) return 0;
2928 // Y = (aX + b)^Gamma | X <= -b/a
2937 if (!CheckSingleParametric("IEC 61966-3", IEC61966_3, 3, Params)) return 0;
2939 // 4) IEC 61966-2.1 (sRGB)
2940 // Y = (aX + b)^Gamma | X >= d
2944 Params[1] = 1. / 1.055;
2945 Params[2] = 0.055 / 1.055;
2946 Params[3] = 1. / 12.92;
2947 Params[4] = 0.04045;
2949 if (!CheckSingleParametric("IEC 61966-2.1", IEC61966_21, 4, Params)) return 0;
2952 // 5) Y = (aX + b)^Gamma + e | X >= d
2953 // Y = cX + f | else
2963 if (!CheckSingleParametric("param_5", param_5, 5, Params)) return 0;
2965 // 6) Y = (aX + b) ^ Gamma + c
2972 if (!CheckSingleParametric("param_6", param_6, 6, Params)) return 0;
2974 // 7) Y = a * log (b * X^Gamma + c) + d
2982 if (!CheckSingleParametric("param_7", param_7, 7, Params)) return 0;
2984 // 8) Y = a * b ^ (c*X+d) + e
2992 if (!CheckSingleParametric("param_8", param_8, 8, Params)) return 0;
2994 // 108: S-Shaped: (1 - (1-x)^1/g)^1/g
2997 if (!CheckSingleParametric("sigmoidal", sigmoidal, 108, Params)) return 0;
3004 // LUT checks ------------------------------------------------------------------------------
3007 cmsInt32Number CheckLUTcreation(void)
3011 cmsInt32Number n1, n2;
3013 lut = cmsPipelineAlloc(DbgThread(), 1, 1);
3014 n1 = cmsPipelineStageCount(lut);
3015 lut2 = cmsPipelineDup(lut);
3016 n2 = cmsPipelineStageCount(lut2);
3018 cmsPipelineFree(lut);
3019 cmsPipelineFree(lut2);
3021 return (n1 == 0) && (n2 == 0);
3024 // Create a MPE for a identity matrix
3026 void AddIdentityMatrix(cmsPipeline* lut)
3028 const cmsFloat64Number Identity[] = { 1, 0, 0,
3033 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocMatrix(DbgThread(), 3, 3, Identity, NULL));
3036 // Create a MPE for identity cmsFloat32Number CLUT
3038 void AddIdentityCLUTfloat(cmsPipeline* lut)
3040 const cmsFloat32Number Table[] = {
3055 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLutFloat(DbgThread(), 2, 3, 3, Table));
3058 // Create a MPE for identity cmsFloat32Number CLUT
3060 void AddIdentityCLUT16(cmsPipeline* lut)
3062 const cmsUInt16Number Table[] = {
3074 0xffff, 0xffff, 0xffff
3078 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table));
3082 // Create a 3 fn identity curves
3085 void Add3GammaCurves(cmsPipeline* lut, cmsFloat64Number Curve)
3087 cmsToneCurve* id = cmsBuildGamma(DbgThread(), Curve);
3088 cmsToneCurve* id3[3];
3094 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, id3));
3096 cmsFreeToneCurve(id);
3101 cmsInt32Number CheckFloatLUT(cmsPipeline* lut)
3103 cmsInt32Number n1, i, j;
3104 cmsFloat32Number Inf[3], Outf[3];
3108 for (j=0; j < 65535; j++) {
3110 cmsInt32Number af[3];
3112 Inf[0] = Inf[1] = Inf[2] = (cmsFloat32Number) j / 65535.0F;
3113 cmsPipelineEvalFloat(Inf, Outf, lut);
3115 af[0] = (cmsInt32Number) floor(Outf[0]*65535.0 + 0.5);
3116 af[1] = (cmsInt32Number) floor(Outf[1]*65535.0 + 0.5);
3117 af[2] = (cmsInt32Number) floor(Outf[2]*65535.0 + 0.5);
3119 for (i=0; i < 3; i++) {
3133 cmsInt32Number Check16LUT(cmsPipeline* lut)
3135 cmsInt32Number n2, i, j;
3136 cmsUInt16Number Inw[3], Outw[3];
3140 for (j=0; j < 65535; j++) {
3142 cmsInt32Number aw[3];
3144 Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
3145 cmsPipelineEval16(Inw, Outw, lut);
3150 for (i=0; i < 3; i++) {
3163 // Check any LUT that is linear
3165 cmsInt32Number CheckStagesLUT(cmsPipeline* lut, cmsInt32Number ExpectedStages)
3168 cmsInt32Number nInpChans, nOutpChans, nStages;
3170 nInpChans = cmsPipelineInputChannels(lut);
3171 nOutpChans = cmsPipelineOutputChannels(lut);
3172 nStages = cmsPipelineStageCount(lut);
3174 return (nInpChans == 3) && (nOutpChans == 3) && (nStages == ExpectedStages);
3179 cmsInt32Number CheckFullLUT(cmsPipeline* lut, cmsInt32Number ExpectedStages)
3181 cmsInt32Number rc = CheckStagesLUT(lut, ExpectedStages) && Check16LUT(lut) && CheckFloatLUT(lut);
3183 cmsPipelineFree(lut);
3189 cmsInt32Number Check1StageLUT(void)
3191 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3193 AddIdentityMatrix(lut);
3194 return CheckFullLUT(lut, 1);
3200 cmsInt32Number Check2StageLUT(void)
3202 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3204 AddIdentityMatrix(lut);
3205 AddIdentityCLUTfloat(lut);
3207 return CheckFullLUT(lut, 2);
3211 cmsInt32Number Check2Stage16LUT(void)
3213 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3215 AddIdentityMatrix(lut);
3216 AddIdentityCLUT16(lut);
3218 return CheckFullLUT(lut, 2);
3224 cmsInt32Number Check3StageLUT(void)
3226 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3228 AddIdentityMatrix(lut);
3229 AddIdentityCLUTfloat(lut);
3230 Add3GammaCurves(lut, 1.0);
3232 return CheckFullLUT(lut, 3);
3236 cmsInt32Number Check3Stage16LUT(void)
3238 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3240 AddIdentityMatrix(lut);
3241 AddIdentityCLUT16(lut);
3242 Add3GammaCurves(lut, 1.0);
3244 return CheckFullLUT(lut, 3);
3250 cmsInt32Number Check4StageLUT(void)
3252 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3254 AddIdentityMatrix(lut);
3255 AddIdentityCLUTfloat(lut);
3256 Add3GammaCurves(lut, 1.0);
3257 AddIdentityMatrix(lut);
3259 return CheckFullLUT(lut, 4);
3263 cmsInt32Number Check4Stage16LUT(void)
3265 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3267 AddIdentityMatrix(lut);
3268 AddIdentityCLUT16(lut);
3269 Add3GammaCurves(lut, 1.0);
3270 AddIdentityMatrix(lut);
3272 return CheckFullLUT(lut, 4);
3276 cmsInt32Number Check5StageLUT(void)
3278 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3280 AddIdentityMatrix(lut);
3281 AddIdentityCLUTfloat(lut);
3282 Add3GammaCurves(lut, 1.0);
3283 AddIdentityMatrix(lut);
3284 Add3GammaCurves(lut, 1.0);
3286 return CheckFullLUT(lut, 5);
3291 cmsInt32Number Check5Stage16LUT(void)
3293 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3295 AddIdentityMatrix(lut);
3296 AddIdentityCLUT16(lut);
3297 Add3GammaCurves(lut, 1.0);
3298 AddIdentityMatrix(lut);
3299 Add3GammaCurves(lut, 1.0);
3301 return CheckFullLUT(lut, 5);
3305 cmsInt32Number Check6StageLUT(void)
3307 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3309 AddIdentityMatrix(lut);
3310 Add3GammaCurves(lut, 1.0);
3311 AddIdentityCLUTfloat(lut);
3312 Add3GammaCurves(lut, 1.0);
3313 AddIdentityMatrix(lut);
3314 Add3GammaCurves(lut, 1.0);
3316 return CheckFullLUT(lut, 6);
3320 cmsInt32Number Check6Stage16LUT(void)
3322 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3324 AddIdentityMatrix(lut);
3325 Add3GammaCurves(lut, 1.0);
3326 AddIdentityCLUT16(lut);
3327 Add3GammaCurves(lut, 1.0);
3328 AddIdentityMatrix(lut);
3329 Add3GammaCurves(lut, 1.0);
3331 return CheckFullLUT(lut, 6);
3336 cmsInt32Number CheckLab2LabLUT(void)
3338 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3341 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3342 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3344 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3346 cmsPipelineFree(lut);
3353 cmsInt32Number CheckXYZ2XYZLUT(void)
3355 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3358 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3359 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3361 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3363 cmsPipelineFree(lut);
3371 cmsInt32Number CheckLab2LabMatLUT(void)
3373 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3376 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3377 AddIdentityMatrix(lut);
3378 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3380 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 3);
3382 cmsPipelineFree(lut);
3388 cmsInt32Number CheckNamedColorLUT(void)
3390 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3391 cmsNAMEDCOLORLIST* nc;
3392 cmsInt32Number i,j, rc = 1, n2;
3393 cmsUInt16Number PCS[3];
3394 cmsUInt16Number Colorant[cmsMAXCHANNELS];
3396 cmsUInt16Number Inw[3], Outw[3];
3400 nc = cmsAllocNamedColorList(DbgThread(), 256, 3, "pre", "post");
3401 if (nc == NULL) return 0;
3403 for (i=0; i < 256; i++) {
3405 PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
3406 Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) i;
3408 sprintf(Name, "#%d", i);
3409 if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
3412 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocNamedColor(nc, FALSE));
3414 cmsFreeNamedColorList(nc);
3415 if (rc == 0) return 0;
3419 for (j=0; j < 256; j++) {
3421 Inw[0] = (cmsUInt16Number) j;
3423 cmsPipelineEval16(Inw, Outw, lut);
3424 for (i=0; i < 3; i++) {
3433 cmsPipelineFree(lut);
3439 // --------------------------------------------------------------------------------------------
3441 // A lightweight test of multilocalized unicode structures.
3444 cmsInt32Number CheckMLU(void)
3446 cmsMLU* mlu, *mlu2, *mlu3;
3447 char Buffer[256], Buffer2[256];
3448 cmsInt32Number rc = 1;
3450 cmsHPROFILE h= NULL;
3452 // Allocate a MLU structure, no preferred size
3453 mlu = cmsMLUalloc(DbgThread(), 0);
3455 // Add some localizations
3456 cmsMLUsetWide(mlu, "en", "US", L"Hello, world");
3457 cmsMLUsetWide(mlu, "es", "ES", L"Hola, mundo");
3458 cmsMLUsetWide(mlu, "fr", "FR", L"Bonjour, le monde");
3459 cmsMLUsetWide(mlu, "ca", "CA", L"Hola, mon");
3462 // Check the returned string for each language
3464 cmsMLUgetASCII(mlu, "en", "US", Buffer, 256);
3465 if (strcmp(Buffer, "Hello, world") != 0) rc = 0;
3468 cmsMLUgetASCII(mlu, "es", "ES", Buffer, 256);
3469 if (strcmp(Buffer, "Hola, mundo") != 0) rc = 0;
3472 cmsMLUgetASCII(mlu, "fr", "FR", Buffer, 256);
3473 if (strcmp(Buffer, "Bonjour, le monde") != 0) rc = 0;
3476 cmsMLUgetASCII(mlu, "ca", "CA", Buffer, 256);
3477 if (strcmp(Buffer, "Hola, mon") != 0) rc = 0;
3480 Fail("Unexpected string '%s'", Buffer);
3485 // Now for performance, allocate an empty struct
3486 mlu = cmsMLUalloc(DbgThread(), 0);
3488 // Fill it with several thousands of different lenguages
3489 for (i=0; i < 4096; i++) {
3493 Lang[0] = (char) (i % 255);
3494 Lang[1] = (char) (i / 255);
3497 sprintf(Buffer, "String #%i", i);
3498 cmsMLUsetASCII(mlu, Lang, Lang, Buffer);
3502 mlu2 = cmsMLUdup(mlu);
3504 // Get rid of original
3507 // Check all is still in place
3508 for (i=0; i < 4096; i++) {
3512 Lang[0] = (char)(i % 255);
3513 Lang[1] = (char)(i / 255);
3516 cmsMLUgetASCII(mlu2, Lang, Lang, Buffer2, 256);
3517 sprintf(Buffer, "String #%i", i);
3519 if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
3523 Fail("Unexpected string '%s'", Buffer2);
3527 h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "w");
3529 cmsSetProfileVersion(h, 4.3);
3531 cmsWriteTag(h, cmsSigProfileDescriptionTag, mlu2);
3536 h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "r");
3538 mlu3 = cmsReadTag(h, cmsSigProfileDescriptionTag);
3539 if (mlu3 == NULL) { Fail("Profile didn't get the MLU\n"); rc = 0; goto Error; }
3541 // Check all is still in place
3542 for (i=0; i < 4096; i++) {
3546 Lang[0] = (char) (i % 255);
3547 Lang[1] = (char) (i / 255);
3550 cmsMLUgetASCII(mlu3, Lang, Lang, Buffer2, 256);
3551 sprintf(Buffer, "String #%i", i);
3553 if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
3556 if (rc == 0) Fail("Unexpected string '%s'", Buffer2);
3560 if (h != NULL) cmsCloseProfile(h);
3561 remove("mlucheck.icc");
3567 // A lightweight test of named color structures.
3569 cmsInt32Number CheckNamedColorList(void)
3571 cmsNAMEDCOLORLIST* nc = NULL, *nc2;
3572 cmsInt32Number i, j, rc=1;
3574 cmsUInt16Number PCS[3];
3575 cmsUInt16Number Colorant[cmsMAXCHANNELS];
3576 char CheckName[255];
3577 cmsUInt16Number CheckPCS[3];
3578 cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
3581 nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
3582 if (nc == NULL) return 0;
3584 for (i=0; i < 4096; i++) {
3587 PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
3588 Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) (4096 - i);
3590 sprintf(Name, "#%d", i);
3591 if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
3594 for (i=0; i < 4096; i++) {
3596 CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
3597 CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (4096 - i);
3599 sprintf(CheckName, "#%d", i);
3600 if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
3603 for (j=0; j < 3; j++) {
3604 if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
3607 for (j=0; j < 4; j++) {
3608 if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
3611 if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
3614 h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "w");
3615 if (h == NULL) return 0;
3616 if (!cmsWriteTag(h, cmsSigNamedColor2Tag, nc)) return 0;
3618 cmsFreeNamedColorList(nc);
3621 h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "r");
3622 nc2 = cmsReadTag(h, cmsSigNamedColor2Tag);
3624 if (cmsNamedColorCount(nc2) != 4096) { rc = 0; Fail("Invalid count"); goto Error; }
3626 i = cmsNamedColorIndex(nc2, "#123");
3627 if (i != 123) { rc = 0; Fail("Invalid index"); goto Error; }
3630 for (i=0; i < 4096; i++) {
3632 CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
3633 CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (4096 - i);
3635 sprintf(CheckName, "#%d", i);
3636 if (!cmsNamedColorInfo(nc2, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
3639 for (j=0; j < 3; j++) {
3640 if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
3643 for (j=0; j < 4; j++) {
3644 if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
3647 if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
3651 remove("namedcol.icc");
3654 if (nc != NULL) cmsFreeNamedColorList(nc);
3660 // ----------------------------------------------------------------------------------------------------------
3664 static cmsBool FormatterFailed;
3667 void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text)
3669 cmsUInt16Number Values[cmsMAXCHANNELS];
3670 cmsUInt8Number Buffer[1024];
3672 cmsInt32Number i, j, nChannels, bytes;
3676 if (FormatterFailed) return;
3678 memset(&info, 0, sizeof(info));
3679 info.OutputFormat = info.InputFormat = Type;
3681 // Go forth and back
3682 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_16BITS);
3683 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS);
3685 if (f.Fmt16 == NULL || b.Fmt16 == NULL) {
3686 Fail("no formatter for %s", Text);
3687 FormatterFailed = TRUE;
3690 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_16BITS);
3691 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS);
3695 nChannels = T_CHANNELS(Type);
3696 bytes = T_BYTES(Type);
3698 for (j=0; j < 5; j++) {
3700 for (i=0; i < nChannels; i++) {
3701 Values[i] = (cmsUInt16Number) (i+j);
3707 b.Fmt16(&info, Values, Buffer, 1);
3708 memset(Values, 0, sizeof(Values));
3709 f.Fmt16(&info, Values, Buffer, 1);
3711 for (i=0; i < nChannels; i++) {
3715 if (Values[i] != i+j) {
3717 Fail("%s failed", Text);
3718 FormatterFailed = TRUE;
3721 for (i=0; i < nChannels; i++) {
3722 Values[i] = (cmsUInt16Number) (i+j);
3728 b.Fmt16(&info, Values, Buffer, 1);
3729 f.Fmt16(&info, Values, Buffer, 1);
3736 #define C(a) CheckSingleFormatter16(a, #a)
3739 // Check all formatters
3741 cmsInt32Number CheckFormatters16(void)
3743 FormatterFailed = FALSE;
3746 C( TYPE_GRAY_8_REV );
3748 C( TYPE_GRAY_16_REV );
3749 C( TYPE_GRAY_16_SE );
3752 C( TYPE_GRAYA_16_SE );
3753 C( TYPE_GRAYA_8_PLANAR );
3754 C( TYPE_GRAYA_16_PLANAR );
3756 C( TYPE_RGB_8_PLANAR );
3758 C( TYPE_BGR_8_PLANAR );
3760 C( TYPE_RGB_16_PLANAR );
3761 C( TYPE_RGB_16_SE );
3763 C( TYPE_BGR_16_PLANAR );
3764 C( TYPE_BGR_16_SE );
3766 C( TYPE_RGBA_8_PLANAR );
3768 C( TYPE_RGBA_16_PLANAR );
3769 C( TYPE_RGBA_16_SE );
3771 C( TYPE_ARGB_8_PLANAR );
3774 C( TYPE_ABGR_8_PLANAR );
3776 C( TYPE_ABGR_16_PLANAR );
3777 C( TYPE_ABGR_16_SE );
3779 C( TYPE_BGRA_8_PLANAR );
3781 C( TYPE_BGRA_16_SE );
3783 C( TYPE_CMY_8_PLANAR );
3785 C( TYPE_CMY_16_PLANAR );
3786 C( TYPE_CMY_16_SE );
3789 C( TYPE_CMYK_8_REV );
3791 C( TYPE_CMYK_8_PLANAR );
3793 C( TYPE_CMYK_16_REV );
3795 C( TYPE_CMYK_16_PLANAR );
3796 C( TYPE_CMYK_16_SE );
3799 C( TYPE_KYMC_16_SE );
3801 C( TYPE_KCMY_8_REV );
3803 C( TYPE_KCMY_16_REV );
3804 C( TYPE_KCMY_16_SE );
3807 C( TYPE_CMYK5_16_SE );
3810 C( TYPE_KYMC5_16_SE );
3812 C( TYPE_CMYK6_8_PLANAR );
3814 C( TYPE_CMYK6_16_PLANAR );
3815 C( TYPE_CMYK6_16_SE );
3818 C( TYPE_CMYK7_16_SE );
3821 C( TYPE_KYMC7_16_SE );
3824 C( TYPE_CMYK8_16_SE );
3827 C( TYPE_KYMC8_16_SE );
3830 C( TYPE_CMYK9_16_SE );
3833 C( TYPE_KYMC9_16_SE );
3835 C( TYPE_CMYK10_16 );
3836 C( TYPE_CMYK10_16_SE );
3838 C( TYPE_KYMC10_16 );
3839 C( TYPE_KYMC10_16_SE );
3841 C( TYPE_CMYK11_16 );
3842 C( TYPE_CMYK11_16_SE );
3844 C( TYPE_KYMC11_16 );
3845 C( TYPE_KYMC11_16_SE );
3847 C( TYPE_CMYK12_16 );
3848 C( TYPE_CMYK12_16_SE );
3850 C( TYPE_KYMC12_16 );
3851 C( TYPE_KYMC12_16_SE );
3858 C( TYPE_YCbCr_8_PLANAR );
3860 C( TYPE_YCbCr_16_PLANAR );
3861 C( TYPE_YCbCr_16_SE );
3863 C( TYPE_YUV_8_PLANAR );
3865 C( TYPE_YUV_16_PLANAR );
3866 C( TYPE_YUV_16_SE );
3868 C( TYPE_HLS_8_PLANAR );
3870 C( TYPE_HLS_16_PLANAR );
3871 C( TYPE_HLS_16_SE );
3873 C( TYPE_HSV_8_PLANAR );
3875 C( TYPE_HSV_16_PLANAR );
3876 C( TYPE_HSV_16_SE );
3902 C( TYPE_GRAY_HALF_FLT );
3903 C( TYPE_RGB_HALF_FLT );
3904 C( TYPE_CMYK_HALF_FLT );
3905 C( TYPE_RGBA_HALF_FLT );
3907 C( TYPE_RGBA_HALF_FLT );
3908 C( TYPE_ARGB_HALF_FLT );
3909 C( TYPE_BGR_HALF_FLT );
3910 C( TYPE_BGRA_HALF_FLT );
3911 C( TYPE_ABGR_HALF_FLT );
3914 return FormatterFailed == 0 ? 1 : 0;
3919 void CheckSingleFormatterFloat(cmsUInt32Number Type, const char* Text)
3921 cmsFloat32Number Values[cmsMAXCHANNELS];
3922 cmsUInt8Number Buffer[1024];
3924 cmsInt32Number i, j, nChannels;
3928 if (FormatterFailed) return;
3930 memset(&info, 0, sizeof(info));
3931 info.OutputFormat = info.InputFormat = Type;
3933 // Go forth and back
3934 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
3935 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
3937 if (f.FmtFloat == NULL || b.FmtFloat == NULL) {
3938 Fail("no formatter for %s", Text);
3939 FormatterFailed = TRUE;
3942 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
3943 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
3947 nChannels = T_CHANNELS(Type);
3949 for (j=0; j < 5; j++) {
3951 for (i=0; i < nChannels; i++) {
3952 Values[i] = (cmsFloat32Number) (i+j);
3955 b.FmtFloat(&info, Values, Buffer, 1);
3956 memset(Values, 0, sizeof(Values));
3957 f.FmtFloat(&info, Values, Buffer, 1);
3959 for (i=0; i < nChannels; i++) {
3961 cmsFloat64Number delta = fabs(Values[i] - ( i+j));
3963 if (delta > 0.000000001) {
3965 Fail("%s failed", Text);
3966 FormatterFailed = TRUE;
3969 for (i=0; i < nChannels; i++) {
3970 Values[i] = (cmsFloat32Number) (i+j);
3973 b.FmtFloat(&info, Values, Buffer, 1);
3974 f.FmtFloat(&info, Values, Buffer, 1);
3981 #define C(a) CheckSingleFormatterFloat(a, #a)
3984 cmsInt32Number CheckFormattersFloat(void)
3986 FormatterFailed = FALSE;
4009 C( TYPE_GRAY_HALF_FLT );
4010 C( TYPE_RGB_HALF_FLT );
4011 C( TYPE_CMYK_HALF_FLT );
4012 C( TYPE_RGBA_HALF_FLT );
4014 C( TYPE_RGBA_HALF_FLT );
4015 C( TYPE_ARGB_HALF_FLT );
4016 C( TYPE_BGR_HALF_FLT );
4017 C( TYPE_BGRA_HALF_FLT );
4018 C( TYPE_ABGR_HALF_FLT );
4021 return FormatterFailed == 0 ? 1 : 0;
4025 #ifndef CMS_NO_HALF_SUPPORT
4028 #define my_isfinite(x) ((x) != (x))
4030 cmsInt32Number CheckFormattersHalf(void)
4035 for (i=0; i < 0xffff; i++) {
4037 cmsFloat32Number f = _cmsHalf2Float((cmsUInt16Number) i);
4039 if (!my_isfinite(f)) {
4041 j = _cmsFloat2Half(f);
4044 Fail("%d != %d in Half float support!\n", i, j);
4056 cmsInt32Number CheckOneRGB(cmsHTRANSFORM xform, cmsUInt16Number R, cmsUInt16Number G, cmsUInt16Number B, cmsUInt16Number Ro, cmsUInt16Number Go, cmsUInt16Number Bo)
4058 cmsUInt16Number RGB[3];
4059 cmsUInt16Number Out[3];
4065 cmsDoTransform(xform, RGB, Out, 1);
4067 return IsGoodWord("R", Ro , Out[0]) &&
4068 IsGoodWord("G", Go , Out[1]) &&
4069 IsGoodWord("B", Bo , Out[2]);
4072 // Check known values going from sRGB to XYZ
4074 cmsInt32Number CheckOneRGB_double(cmsHTRANSFORM xform, cmsFloat64Number R, cmsFloat64Number G, cmsFloat64Number B, cmsFloat64Number Ro, cmsFloat64Number Go, cmsFloat64Number Bo)
4076 cmsFloat64Number RGB[3];
4077 cmsFloat64Number Out[3];
4083 cmsDoTransform(xform, RGB, Out, 1);
4085 return IsGoodVal("R", Ro , Out[0], 0.01) &&
4086 IsGoodVal("G", Go , Out[1], 0.01) &&
4087 IsGoodVal("B", Bo , Out[2], 0.01);
4092 cmsInt32Number CheckChangeBufferFormat(void)
4094 cmsHPROFILE hsRGB = cmsCreate_sRGBProfile();
4095 cmsHTRANSFORM xform;
4098 xform = cmsCreateTransform(hsRGB, TYPE_RGB_16, hsRGB, TYPE_RGB_16, INTENT_PERCEPTUAL, 0);
4099 cmsCloseProfile(hsRGB);
4100 if (xform == NULL) return 0;
4103 if (!CheckOneRGB(xform, 0, 0, 0, 0, 0, 0)) return 0;
4104 if (!CheckOneRGB(xform, 120, 0, 0, 120, 0, 0)) return 0;
4105 if (!CheckOneRGB(xform, 0, 222, 255, 0, 222, 255)) return 0;
4108 if (!cmsChangeBuffersFormat(xform, TYPE_BGR_16, TYPE_RGB_16)) return 0;
4110 if (!CheckOneRGB(xform, 0, 0, 123, 123, 0, 0)) return 0;
4111 if (!CheckOneRGB(xform, 154, 234, 0, 0, 234, 154)) return 0;
4113 if (!cmsChangeBuffersFormat(xform, TYPE_RGB_DBL, TYPE_RGB_DBL)) return 0;
4115 if (!CheckOneRGB_double(xform, 0.20, 0, 0, 0.20, 0, 0)) return 0;
4116 if (!CheckOneRGB_double(xform, 0, 0.9, 1, 0, 0.9, 1)) return 0;
4118 cmsDeleteTransform(xform);
4124 // Write tag testbed ----------------------------------------------------------------------------------------
4127 cmsInt32Number CheckXYZ(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4136 XYZ.X = 1.0; XYZ.Y = 1.1; XYZ.Z = 1.2;
4137 return cmsWriteTag(hProfile, tag, &XYZ);
4140 Pt = cmsReadTag(hProfile, tag);
4141 if (Pt == NULL) return 0;
4142 return IsGoodFixed15_16("X", 1.0, Pt ->X) &&
4143 IsGoodFixed15_16("Y", 1.1, Pt->Y) &&
4144 IsGoodFixed15_16("Z", 1.2, Pt -> Z);
4153 cmsInt32Number CheckGamma(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4155 cmsToneCurve *g, *Pt;
4162 g = cmsBuildGamma(DbgThread(), 1.0);
4163 rc = cmsWriteTag(hProfile, tag, g);
4164 cmsFreeToneCurve(g);
4168 Pt = cmsReadTag(hProfile, tag);
4169 if (Pt == NULL) return 0;
4170 return cmsIsToneCurveLinear(Pt);
4178 cmsInt32Number CheckText(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4188 m = cmsMLUalloc(DbgThread(), 0);
4189 cmsMLUsetASCII(m, cmsNoLanguage, cmsNoCountry, "Test test");
4190 rc = cmsWriteTag(hProfile, tag, m);
4195 Pt = cmsReadTag(hProfile, tag);
4196 if (Pt == NULL) return 0;
4197 cmsMLUgetASCII(Pt, cmsNoLanguage, cmsNoCountry, Buffer, 256);
4198 return strcmp(Buffer, "Test test") == 0;
4206 cmsInt32Number CheckData(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4209 cmsICCData d = { 1, 0, { '?' }};
4216 rc = cmsWriteTag(hProfile, tag, &d);
4220 Pt = cmsReadTag(hProfile, tag);
4221 if (Pt == NULL) return 0;
4222 return (Pt ->data[0] == '?') && (Pt ->flag == 0) && (Pt ->len == 1);
4231 cmsInt32Number CheckSignature(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4233 cmsTagSignature *Pt, Holder;
4238 Holder = cmsSigPerceptualReferenceMediumGamut;
4239 return cmsWriteTag(hProfile, tag, &Holder);
4242 Pt = cmsReadTag(hProfile, tag);
4243 if (Pt == NULL) return 0;
4244 return *Pt == cmsSigPerceptualReferenceMediumGamut;
4253 cmsInt32Number CheckDateTime(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4255 struct tm *Pt, Holder;
4266 Holder.tm_year = 2009 - 1900;
4267 return cmsWriteTag(hProfile, tag, &Holder);
4270 Pt = cmsReadTag(hProfile, tag);
4271 if (Pt == NULL) return 0;
4273 return (Pt ->tm_hour == 1 &&
4276 Pt ->tm_mday == 4 &&
4278 Pt ->tm_year == 2009 - 1900);
4288 cmsInt32Number CheckNamedColor(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag, cmsInt32Number max_check, cmsBool colorant_check)
4290 cmsNAMEDCOLORLIST* nc;
4291 cmsInt32Number i, j, rc;
4293 cmsUInt16Number PCS[3];
4294 cmsUInt16Number Colorant[cmsMAXCHANNELS];
4295 char CheckName[255];
4296 cmsUInt16Number CheckPCS[3];
4297 cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
4303 nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
4304 if (nc == NULL) return 0;
4306 for (i=0; i < max_check; i++) {
4308 PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
4309 Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) (max_check - i);
4311 sprintf(Name, "#%d", i);
4312 if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { Fail("Couldn't append named color"); return 0; }
4315 rc = cmsWriteTag(hProfile, tag, nc);
4316 cmsFreeNamedColorList(nc);
4321 nc = cmsReadTag(hProfile, tag);
4322 if (nc == NULL) return 0;
4324 for (i=0; i < max_check; i++) {
4326 CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
4327 CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (max_check - i);
4329 sprintf(CheckName, "#%d", i);
4330 if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { Fail("Invalid string"); return 0; }
4333 for (j=0; j < 3; j++) {
4334 if (CheckPCS[j] != PCS[j]) { Fail("Invalid PCS"); return 0; }
4337 // This is only used on named color list
4338 if (colorant_check) {
4340 for (j=0; j < 4; j++) {
4341 if (CheckColorant[j] != Colorant[j]) { Fail("Invalid Colorant"); return 0; };
4345 if (strcmp(Name, CheckName) != 0) { Fail("Invalid Name"); return 0; };
4356 cmsInt32Number CheckLUT(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4358 cmsPipeline* Lut, *Pt;
4366 Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
4367 if (Lut == NULL) return 0;
4369 // Create an identity LUT
4370 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(DbgThread(), 3));
4371 cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCLut(DbgThread(), 3));
4372 cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCurves(DbgThread(), 3));
4374 rc = cmsWriteTag(hProfile, tag, Lut);
4375 cmsPipelineFree(Lut);
4379 Pt = cmsReadTag(hProfile, tag);
4380 if (Pt == NULL) return 0;
4382 // Transform values, check for identity
4383 return Check16LUT(Pt);
4391 cmsInt32Number CheckCHAD(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4393 cmsFloat64Number *Pt;
4394 cmsFloat64Number CHAD[] = { 0, .1, .2, .3, .4, .5, .6, .7, .8 };
4400 return cmsWriteTag(hProfile, tag, CHAD);
4404 Pt = cmsReadTag(hProfile, tag);
4405 if (Pt == NULL) return 0;
4407 for (i=0; i < 9; i++) {
4408 if (!IsGoodFixed15_16("CHAD", Pt[i], CHAD[i])) return 0;
4419 cmsInt32Number CheckChromaticity(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4421 cmsCIExyYTRIPLE *Pt, c = { {0, .1, 1 }, { .3, .4, 1 }, { .6, .7, 1 }};
4426 return cmsWriteTag(hProfile, tag, &c);
4430 Pt = cmsReadTag(hProfile, tag);
4431 if (Pt == NULL) return 0;
4433 if (!IsGoodFixed15_16("xyY", Pt ->Red.x, c.Red.x)) return 0;
4434 if (!IsGoodFixed15_16("xyY", Pt ->Red.y, c.Red.y)) return 0;
4435 if (!IsGoodFixed15_16("xyY", Pt ->Green.x, c.Green.x)) return 0;
4436 if (!IsGoodFixed15_16("xyY", Pt ->Green.y, c.Green.y)) return 0;
4437 if (!IsGoodFixed15_16("xyY", Pt ->Blue.x, c.Blue.x)) return 0;
4438 if (!IsGoodFixed15_16("xyY", Pt ->Blue.y, c.Blue.y)) return 0;
4448 cmsInt32Number CheckColorantOrder(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4450 cmsUInt8Number *Pt, c[cmsMAXCHANNELS];
4456 for (i=0; i < cmsMAXCHANNELS; i++) c[i] = (cmsUInt8Number) (cmsMAXCHANNELS - i - 1);
4457 return cmsWriteTag(hProfile, tag, c);
4461 Pt = cmsReadTag(hProfile, tag);
4462 if (Pt == NULL) return 0;
4464 for (i=0; i < cmsMAXCHANNELS; i++) {
4465 if (Pt[i] != ( cmsMAXCHANNELS - i - 1 )) return 0;
4475 cmsInt32Number CheckMeasurement(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4477 cmsICCMeasurementConditions *Pt, m;
4487 m.IlluminantType = cmsILLUMINANT_TYPE_D50;
4489 return cmsWriteTag(hProfile, tag, &m);
4493 Pt = cmsReadTag(hProfile, tag);
4494 if (Pt == NULL) return 0;
4496 if (!IsGoodFixed15_16("Backing", Pt ->Backing.X, 0.1)) return 0;
4497 if (!IsGoodFixed15_16("Backing", Pt ->Backing.Y, 0.2)) return 0;
4498 if (!IsGoodFixed15_16("Backing", Pt ->Backing.Z, 0.3)) return 0;
4499 if (!IsGoodFixed15_16("Flare", Pt ->Flare, 1.0)) return 0;
4501 if (Pt ->Geometry != 1) return 0;
4502 if (Pt ->IlluminantType != cmsILLUMINANT_TYPE_D50) return 0;
4503 if (Pt ->Observer != 1) return 0;
4513 cmsInt32Number CheckUcrBg(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4522 m.Ucr = cmsBuildGamma(DbgThread(), 2.4);
4523 m.Bg = cmsBuildGamma(DbgThread(), -2.2);
4524 m.Desc = cmsMLUalloc(DbgThread(), 1);
4525 cmsMLUsetASCII(m.Desc, cmsNoLanguage, cmsNoCountry, "test UCR/BG");
4526 rc = cmsWriteTag(hProfile, tag, &m);
4528 cmsFreeToneCurve(m.Bg);
4529 cmsFreeToneCurve(m.Ucr);
4534 Pt = cmsReadTag(hProfile, tag);
4535 if (Pt == NULL) return 0;
4537 cmsMLUgetASCII(Pt ->Desc, cmsNoLanguage, cmsNoCountry, Buffer, 256);
4538 if (strcmp(Buffer, "test UCR/BG") != 0) return 0;
4548 cmsInt32Number CheckCRDinfo(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4557 mlu = cmsMLUalloc(DbgThread(), 5);
4559 cmsMLUsetWide(mlu, "PS", "nm", L"test postscript");
4560 cmsMLUsetWide(mlu, "PS", "#0", L"perceptual");
4561 cmsMLUsetWide(mlu, "PS", "#1", L"relative_colorimetric");
4562 cmsMLUsetWide(mlu, "PS", "#2", L"saturation");
4563 cmsMLUsetWide(mlu, "PS", "#3", L"absolute_colorimetric");
4564 rc = cmsWriteTag(hProfile, tag, mlu);
4570 mlu = (cmsMLU*) cmsReadTag(hProfile, tag);
4571 if (mlu == NULL) return 0;
4575 cmsMLUgetASCII(mlu, "PS", "nm", Buffer, 256);
4576 if (strcmp(Buffer, "test postscript") != 0) return 0;
4579 cmsMLUgetASCII(mlu, "PS", "#0", Buffer, 256);
4580 if (strcmp(Buffer, "perceptual") != 0) return 0;
4583 cmsMLUgetASCII(mlu, "PS", "#1", Buffer, 256);
4584 if (strcmp(Buffer, "relative_colorimetric") != 0) return 0;
4587 cmsMLUgetASCII(mlu, "PS", "#2", Buffer, 256);
4588 if (strcmp(Buffer, "saturation") != 0) return 0;
4591 cmsMLUgetASCII(mlu, "PS", "#3", Buffer, 256);
4592 if (strcmp(Buffer, "absolute_colorimetric") != 0) return 0;
4602 cmsToneCurve *CreateSegmentedCurve(void)
4604 cmsCurveSegment Seg[3];
4605 cmsFloat32Number Sampled[2] = { 0, 1};
4608 Seg[0].Params[0] = 1;
4609 Seg[0].Params[1] = 0;
4610 Seg[0].Params[2] = 0;
4611 Seg[0].Params[3] = 0;
4616 Seg[1].nGridPoints = 2;
4617 Seg[1].SampledPoints = Sampled;
4622 Seg[2].Params[0] = 1;
4623 Seg[2].Params[1] = 0;
4624 Seg[2].Params[2] = 0;
4625 Seg[2].Params[3] = 0;
4629 return cmsBuildSegmentedToneCurve(DbgThread(), 3, Seg);
4634 cmsInt32Number CheckMPE(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4636 cmsPipeline* Lut, *Pt;
4644 Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
4646 cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4(DbgThread()));
4647 cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV4ToV2(DbgThread()));
4648 AddIdentityCLUTfloat(Lut);
4650 G[0] = G[1] = G[2] = CreateSegmentedCurve();
4651 cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, G));
4652 cmsFreeToneCurve(G[0]);
4654 rc = cmsWriteTag(hProfile, tag, Lut);
4655 cmsPipelineFree(Lut);
4659 Pt = cmsReadTag(hProfile, tag);
4660 if (Pt == NULL) return 0;
4661 return CheckFloatLUT(Pt);
4670 cmsInt32Number CheckScreening(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4672 cmsScreening *Pt, sc;
4681 sc.Channels[0].Frequency = 2.0;
4682 sc.Channels[0].ScreenAngle = 3.0;
4683 sc.Channels[0].SpotShape = cmsSPOT_ELLIPSE;
4685 rc = cmsWriteTag(hProfile, tag, &sc);
4690 Pt = cmsReadTag(hProfile, tag);
4691 if (Pt == NULL) return 0;
4693 if (Pt ->nChannels != 1) return 0;
4694 if (Pt ->Flag != 0) return 0;
4695 if (!IsGoodFixed15_16("Freq", Pt ->Channels[0].Frequency, 2.0)) return 0;
4696 if (!IsGoodFixed15_16("Angle", Pt ->Channels[0].ScreenAngle, 3.0)) return 0;
4697 if (Pt ->Channels[0].SpotShape != cmsSPOT_ELLIPSE) return 0;
4707 cmsBool CheckOneStr(cmsMLU* mlu, cmsInt32Number n)
4709 char Buffer[256], Buffer2[256];
4712 cmsMLUgetASCII(mlu, "en", "US", Buffer, 255);
4713 sprintf(Buffer2, "Hello, world %d", n);
4714 if (strcmp(Buffer, Buffer2) != 0) return FALSE;
4717 cmsMLUgetASCII(mlu, "es", "ES", Buffer, 255);
4718 sprintf(Buffer2, "Hola, mundo %d", n);
4719 if (strcmp(Buffer, Buffer2) != 0) return FALSE;
4726 void SetOneStr(cmsMLU** mlu, wchar_t* s1, wchar_t* s2)
4728 *mlu = cmsMLUalloc(DbgThread(), 0);
4729 cmsMLUsetWide(*mlu, "en", "US", s1);
4730 cmsMLUsetWide(*mlu, "es", "ES", s2);
4735 cmsInt32Number CheckProfileSequenceTag(cmsInt32Number Pass, cmsHPROFILE hProfile)
4744 s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
4745 if (s == NULL) return 0;
4747 SetOneStr(&s -> seq[0].Manufacturer, L"Hello, world 0", L"Hola, mundo 0");
4748 SetOneStr(&s -> seq[0].Model, L"Hello, world 0", L"Hola, mundo 0");
4749 SetOneStr(&s -> seq[1].Manufacturer, L"Hello, world 1", L"Hola, mundo 1");
4750 SetOneStr(&s -> seq[1].Model, L"Hello, world 1", L"Hola, mundo 1");
4751 SetOneStr(&s -> seq[2].Manufacturer, L"Hello, world 2", L"Hola, mundo 2");
4752 SetOneStr(&s -> seq[2].Model, L"Hello, world 2", L"Hola, mundo 2");
4755 #ifdef CMS_DONT_USE_INT64
4756 s ->seq[0].attributes[0] = cmsTransparency|cmsMatte;
4757 s ->seq[0].attributes[1] = 0;
4759 s ->seq[0].attributes = cmsTransparency|cmsMatte;
4762 #ifdef CMS_DONT_USE_INT64
4763 s ->seq[1].attributes[0] = cmsReflective|cmsMatte;
4764 s ->seq[1].attributes[1] = 0;
4766 s ->seq[1].attributes = cmsReflective|cmsMatte;
4769 #ifdef CMS_DONT_USE_INT64
4770 s ->seq[2].attributes[0] = cmsTransparency|cmsGlossy;
4771 s ->seq[2].attributes[1] = 0;
4773 s ->seq[2].attributes = cmsTransparency|cmsGlossy;
4776 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, s)) return 0;
4777 cmsFreeProfileSequenceDescription(s);
4782 s = cmsReadTag(hProfile, cmsSigProfileSequenceDescTag);
4783 if (s == NULL) return 0;
4785 if (s ->n != 3) return 0;
4787 #ifdef CMS_DONT_USE_INT64
4788 if (s ->seq[0].attributes[0] != (cmsTransparency|cmsMatte)) return 0;
4789 if (s ->seq[0].attributes[1] != 0) return 0;
4791 if (s ->seq[0].attributes != (cmsTransparency|cmsMatte)) return 0;
4794 #ifdef CMS_DONT_USE_INT64
4795 if (s ->seq[1].attributes[0] != (cmsReflective|cmsMatte)) return 0;
4796 if (s ->seq[1].attributes[1] != 0) return 0;
4798 if (s ->seq[1].attributes != (cmsReflective|cmsMatte)) return 0;
4801 #ifdef CMS_DONT_USE_INT64
4802 if (s ->seq[2].attributes[0] != (cmsTransparency|cmsGlossy)) return 0;
4803 if (s ->seq[2].attributes[1] != 0) return 0;
4805 if (s ->seq[2].attributes != (cmsTransparency|cmsGlossy)) return 0;
4809 for (i=0; i < 3; i++) {
4811 if (!CheckOneStr(s -> seq[i].Manufacturer, i)) return 0;
4812 if (!CheckOneStr(s -> seq[i].Model, i)) return 0;
4823 cmsInt32Number CheckProfileSequenceIDTag(cmsInt32Number Pass, cmsHPROFILE hProfile)
4832 s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
4833 if (s == NULL) return 0;
4835 memcpy(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16);
4836 memcpy(s ->seq[1].ProfileID.ID8, "1111111111111111", 16);
4837 memcpy(s ->seq[2].ProfileID.ID8, "2222222222222222", 16);
4840 SetOneStr(&s -> seq[0].Description, L"Hello, world 0", L"Hola, mundo 0");
4841 SetOneStr(&s -> seq[1].Description, L"Hello, world 1", L"Hola, mundo 1");
4842 SetOneStr(&s -> seq[2].Description, L"Hello, world 2", L"Hola, mundo 2");
4844 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, s)) return 0;
4845 cmsFreeProfileSequenceDescription(s);
4850 s = cmsReadTag(hProfile, cmsSigProfileSequenceIdTag);
4851 if (s == NULL) return 0;
4853 if (s ->n != 3) return 0;
4855 if (memcmp(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16) != 0) return 0;
4856 if (memcmp(s ->seq[1].ProfileID.ID8, "1111111111111111", 16) != 0) return 0;
4857 if (memcmp(s ->seq[2].ProfileID.ID8, "2222222222222222", 16) != 0) return 0;
4859 for (i=0; i < 3; i++) {
4861 if (!CheckOneStr(s -> seq[i].Description, i)) return 0;
4873 cmsInt32Number CheckICCViewingConditions(cmsInt32Number Pass, cmsHPROFILE hProfile)
4875 cmsICCViewingConditions* v;
4876 cmsICCViewingConditions s;
4881 s.IlluminantType = 1;
4882 s.IlluminantXYZ.X = 0.1;
4883 s.IlluminantXYZ.Y = 0.2;
4884 s.IlluminantXYZ.Z = 0.3;
4885 s.SurroundXYZ.X = 0.4;
4886 s.SurroundXYZ.Y = 0.5;
4887 s.SurroundXYZ.Z = 0.6;
4889 if (!cmsWriteTag(hProfile, cmsSigViewingConditionsTag, &s)) return 0;
4893 v = cmsReadTag(hProfile, cmsSigViewingConditionsTag);
4894 if (v == NULL) return 0;
4896 if (v ->IlluminantType != 1) return 0;
4897 if (!IsGoodVal("IlluminantXYZ.X", v ->IlluminantXYZ.X, 0.1, 0.001)) return 0;
4898 if (!IsGoodVal("IlluminantXYZ.Y", v ->IlluminantXYZ.Y, 0.2, 0.001)) return 0;
4899 if (!IsGoodVal("IlluminantXYZ.Z", v ->IlluminantXYZ.Z, 0.3, 0.001)) return 0;
4901 if (!IsGoodVal("SurroundXYZ.X", v ->SurroundXYZ.X, 0.4, 0.001)) return 0;
4902 if (!IsGoodVal("SurroundXYZ.Y", v ->SurroundXYZ.Y, 0.5, 0.001)) return 0;
4903 if (!IsGoodVal("SurroundXYZ.Z", v ->SurroundXYZ.Z, 0.6, 0.001)) return 0;
4915 cmsInt32Number CheckVCGT(cmsInt32Number Pass, cmsHPROFILE hProfile)
4917 cmsToneCurve* Curves[3];
4918 cmsToneCurve** PtrCurve;
4923 Curves[0] = cmsBuildGamma(DbgThread(), 1.1);
4924 Curves[1] = cmsBuildGamma(DbgThread(), 2.2);
4925 Curves[2] = cmsBuildGamma(DbgThread(), 3.4);
4927 if (!cmsWriteTag(hProfile, cmsSigVcgtTag, Curves)) return 0;
4929 cmsFreeToneCurveTriple(Curves);
4935 PtrCurve = cmsReadTag(hProfile, cmsSigVcgtTag);
4936 if (PtrCurve == NULL) return 0;
4937 if (!IsGoodVal("VCGT R", cmsEstimateGamma(PtrCurve[0], 0.01), 1.1, 0.001)) return 0;
4938 if (!IsGoodVal("VCGT G", cmsEstimateGamma(PtrCurve[1], 0.01), 2.2, 0.001)) return 0;
4939 if (!IsGoodVal("VCGT B", cmsEstimateGamma(PtrCurve[2], 0.01), 3.4, 0.001)) return 0;
4949 // Only one of the two following may be used, as they share the same tag
4951 cmsInt32Number CheckDictionary16(cmsInt32Number Pass, cmsHPROFILE hProfile)
4954 const cmsDICTentry* e;
4958 hDict = cmsDictAlloc(DbgThread());
4959 cmsDictAddEntry(hDict, L"Name0", NULL, NULL, NULL);
4960 cmsDictAddEntry(hDict, L"Name1", L"", NULL, NULL);
4961 cmsDictAddEntry(hDict, L"Name", L"String", NULL, NULL);
4962 cmsDictAddEntry(hDict, L"Name2", L"12", NULL, NULL);
4963 if (!cmsWriteTag(hProfile, cmsSigMetaTag, hDict)) return 0;
4970 hDict = cmsReadTag(hProfile, cmsSigMetaTag);
4971 if (hDict == NULL) return 0;
4972 e = cmsDictGetEntryList(hDict);
4973 if (memcmp(e ->Name, L"Name2", sizeof(wchar_t) * 5) != 0) return 0;
4974 if (memcmp(e ->Value, L"12", sizeof(wchar_t) * 2) != 0) return 0;
4975 e = cmsDictNextEntry(e);
4976 if (memcmp(e ->Name, L"Name", sizeof(wchar_t) * 4) != 0) return 0;
4977 if (memcmp(e ->Value, L"String", sizeof(wchar_t) * 5) != 0) return 0;
4978 e = cmsDictNextEntry(e);
4979 if (memcmp(e ->Name, L"Name1", sizeof(wchar_t) *5) != 0) return 0;
4980 if (e ->Value == NULL) return 0;
4981 if (*e->Value != 0) return 0;
4982 e = cmsDictNextEntry(e);
4983 if (memcmp(e ->Name, L"Name0", sizeof(wchar_t) * 5) != 0) return 0;
4984 if (e ->Value != NULL) return 0;
4997 cmsInt32Number CheckDictionary24(cmsInt32Number Pass, cmsHPROFILE hProfile)
5000 const cmsDICTentry* e;
5001 cmsMLU* DisplayName;
5003 cmsInt32Number rc = 1;
5008 hDict = cmsDictAlloc(DbgThread());
5010 DisplayName = cmsMLUalloc(DbgThread(), 0);
5012 cmsMLUsetWide(DisplayName, "en", "US", L"Hello, world");
5013 cmsMLUsetWide(DisplayName, "es", "ES", L"Hola, mundo");
5014 cmsMLUsetWide(DisplayName, "fr", "FR", L"Bonjour, le monde");
5015 cmsMLUsetWide(DisplayName, "ca", "CA", L"Hola, mon");
5017 cmsDictAddEntry(hDict, L"Name", L"String", DisplayName, NULL);
5018 cmsMLUfree(DisplayName);
5020 cmsDictAddEntry(hDict, L"Name2", L"12", NULL, NULL);
5021 if (!cmsWriteTag(hProfile, cmsSigMetaTag, hDict)) return 0;
5029 hDict = cmsReadTag(hProfile, cmsSigMetaTag);
5030 if (hDict == NULL) return 0;
5032 e = cmsDictGetEntryList(hDict);
5033 if (memcmp(e ->Name, L"Name2", sizeof(wchar_t) * 5) != 0) return 0;
5034 if (memcmp(e ->Value, L"12", sizeof(wchar_t) * 2) != 0) return 0;
5035 e = cmsDictNextEntry(e);
5036 if (memcmp(e ->Name, L"Name", sizeof(wchar_t) * 4) != 0) return 0;
5037 if (memcmp(e ->Value, L"String", sizeof(wchar_t) * 5) != 0) return 0;
5039 cmsMLUgetASCII(e->DisplayName, "en", "US", Buffer, 256);
5040 if (strcmp(Buffer, "Hello, world") != 0) rc = 0;
5043 cmsMLUgetASCII(e->DisplayName, "es", "ES", Buffer, 256);
5044 if (strcmp(Buffer, "Hola, mundo") != 0) rc = 0;
5047 cmsMLUgetASCII(e->DisplayName, "fr", "FR", Buffer, 256);
5048 if (strcmp(Buffer, "Bonjour, le monde") != 0) rc = 0;
5051 cmsMLUgetASCII(e->DisplayName, "ca", "CA", Buffer, 256);
5052 if (strcmp(Buffer, "Hola, mon") != 0) rc = 0;
5055 Fail("Unexpected string '%s'", Buffer);
5065 cmsInt32Number CheckRAWtags(cmsInt32Number Pass, cmsHPROFILE hProfile)
5072 return cmsWriteRawTag(hProfile, 0x31323334, "data123", 7);
5075 if (!cmsReadRawTag(hProfile, 0x31323334, Buffer, 7)) return 0;
5077 if (strncmp(Buffer, "data123", 7) != 0) return 0;
5086 // This is a very big test that checks every single tag
5088 cmsInt32Number CheckProfileCreation(void)
5091 cmsInt32Number Pass;
5093 h = cmsCreateProfilePlaceholder(DbgThread());
5094 if (h == NULL) return 0;
5096 cmsSetProfileVersion(h, 4.3);
5097 if (cmsGetTagCount(h) != 0) { Fail("Empty profile with nonzero number of tags"); return 0; }
5098 if (cmsIsTag(h, cmsSigAToB0Tag)) { Fail("Found a tag in an empty profile"); return 0; }
5100 cmsSetColorSpace(h, cmsSigRgbData);
5101 if (cmsGetColorSpace(h) != cmsSigRgbData) { Fail("Unable to set colorspace"); return 0; }
5103 cmsSetPCS(h, cmsSigLabData);
5104 if (cmsGetPCS(h) != cmsSigLabData) { Fail("Unable to set colorspace"); return 0; }
5106 cmsSetDeviceClass(h, cmsSigDisplayClass);
5107 if (cmsGetDeviceClass(h) != cmsSigDisplayClass) { Fail("Unable to set deviceclass"); return 0; }
5109 cmsSetHeaderRenderingIntent(h, INTENT_SATURATION);
5110 if (cmsGetHeaderRenderingIntent(h) != INTENT_SATURATION) { Fail("Unable to set rendering intent"); return 0; }
5112 for (Pass = 1; Pass <= 2; Pass++) {
5114 SubTest("Tags holding XYZ");
5116 if (!CheckXYZ(Pass, h, cmsSigBlueColorantTag)) return 0;
5117 if (!CheckXYZ(Pass, h, cmsSigGreenColorantTag)) return 0;
5118 if (!CheckXYZ(Pass, h, cmsSigRedColorantTag)) return 0;
5119 if (!CheckXYZ(Pass, h, cmsSigMediaBlackPointTag)) return 0;
5120 if (!CheckXYZ(Pass, h, cmsSigMediaWhitePointTag)) return 0;
5121 if (!CheckXYZ(Pass, h, cmsSigLuminanceTag)) return 0;
5123 SubTest("Tags holding curves");
5125 if (!CheckGamma(Pass, h, cmsSigBlueTRCTag)) return 0;
5126 if (!CheckGamma(Pass, h, cmsSigGrayTRCTag)) return 0;
5127 if (!CheckGamma(Pass, h, cmsSigGreenTRCTag)) return 0;
5128 if (!CheckGamma(Pass, h, cmsSigRedTRCTag)) return 0;
5130 SubTest("Tags holding text");
5132 if (!CheckText(Pass, h, cmsSigCharTargetTag)) return 0;
5133 if (!CheckText(Pass, h, cmsSigCopyrightTag)) return 0;
5134 if (!CheckText(Pass, h, cmsSigProfileDescriptionTag)) return 0;
5135 if (!CheckText(Pass, h, cmsSigDeviceMfgDescTag)) return 0;
5136 if (!CheckText(Pass, h, cmsSigDeviceModelDescTag)) return 0;
5137 if (!CheckText(Pass, h, cmsSigViewingCondDescTag)) return 0;
5138 if (!CheckText(Pass, h, cmsSigScreeningDescTag)) return 0;
5140 SubTest("Tags holding cmsICCData");
5142 if (!CheckData(Pass, h, cmsSigPs2CRD0Tag)) return 0;
5143 if (!CheckData(Pass, h, cmsSigPs2CRD1Tag)) return 0;
5144 if (!CheckData(Pass, h, cmsSigPs2CRD2Tag)) return 0;
5145 if (!CheckData(Pass, h, cmsSigPs2CRD3Tag)) return 0;
5146 if (!CheckData(Pass, h, cmsSigPs2CSATag)) return 0;
5147 if (!CheckData(Pass, h, cmsSigPs2RenderingIntentTag)) return 0;
5149 SubTest("Tags holding signatures");
5151 if (!CheckSignature(Pass, h, cmsSigColorimetricIntentImageStateTag)) return 0;
5152 if (!CheckSignature(Pass, h, cmsSigPerceptualRenderingIntentGamutTag)) return 0;
5153 if (!CheckSignature(Pass, h, cmsSigSaturationRenderingIntentGamutTag)) return 0;
5154 if (!CheckSignature(Pass, h, cmsSigTechnologyTag)) return 0;
5156 SubTest("Tags holding date_time");
5158 if (!CheckDateTime(Pass, h, cmsSigCalibrationDateTimeTag)) return 0;
5159 if (!CheckDateTime(Pass, h, cmsSigDateTimeTag)) return 0;
5161 SubTest("Tags holding named color lists");
5163 if (!CheckNamedColor(Pass, h, cmsSigColorantTableTag, 15, FALSE)) return 0;
5164 if (!CheckNamedColor(Pass, h, cmsSigColorantTableOutTag, 15, FALSE)) return 0;
5165 if (!CheckNamedColor(Pass, h, cmsSigNamedColor2Tag, 4096, TRUE)) return 0;
5167 SubTest("Tags holding LUTs");
5169 if (!CheckLUT(Pass, h, cmsSigAToB0Tag)) return 0;
5170 if (!CheckLUT(Pass, h, cmsSigAToB1Tag)) return 0;
5171 if (!CheckLUT(Pass, h, cmsSigAToB2Tag)) return 0;
5172 if (!CheckLUT(Pass, h, cmsSigBToA0Tag)) return 0;
5173 if (!CheckLUT(Pass, h, cmsSigBToA1Tag)) return 0;
5174 if (!CheckLUT(Pass, h, cmsSigBToA2Tag)) return 0;
5175 if (!CheckLUT(Pass, h, cmsSigPreview0Tag)) return 0;
5176 if (!CheckLUT(Pass, h, cmsSigPreview1Tag)) return 0;
5177 if (!CheckLUT(Pass, h, cmsSigPreview2Tag)) return 0;
5178 if (!CheckLUT(Pass, h, cmsSigGamutTag)) return 0;
5180 SubTest("Tags holding CHAD");
5181 if (!CheckCHAD(Pass, h, cmsSigChromaticAdaptationTag)) return 0;
5183 SubTest("Tags holding Chromaticity");
5184 if (!CheckChromaticity(Pass, h, cmsSigChromaticityTag)) return 0;
5186 SubTest("Tags holding colorant order");
5187 if (!CheckColorantOrder(Pass, h, cmsSigColorantOrderTag)) return 0;
5189 SubTest("Tags holding measurement");
5190 if (!CheckMeasurement(Pass, h, cmsSigMeasurementTag)) return 0;
5192 SubTest("Tags holding CRD info");
5193 if (!CheckCRDinfo(Pass, h, cmsSigCrdInfoTag)) return 0;
5195 SubTest("Tags holding UCR/BG");
5196 if (!CheckUcrBg(Pass, h, cmsSigUcrBgTag)) return 0;
5198 SubTest("Tags holding MPE");
5199 if (!CheckMPE(Pass, h, cmsSigDToB0Tag)) return 0;
5200 if (!CheckMPE(Pass, h, cmsSigDToB1Tag)) return 0;
5201 if (!CheckMPE(Pass, h, cmsSigDToB2Tag)) return 0;
5202 if (!CheckMPE(Pass, h, cmsSigDToB3Tag)) return 0;
5203 if (!CheckMPE(Pass, h, cmsSigBToD0Tag)) return 0;
5204 if (!CheckMPE(Pass, h, cmsSigBToD1Tag)) return 0;
5205 if (!CheckMPE(Pass, h, cmsSigBToD2Tag)) return 0;
5206 if (!CheckMPE(Pass, h, cmsSigBToD3Tag)) return 0;
5208 SubTest("Tags using screening");
5209 if (!CheckScreening(Pass, h, cmsSigScreeningTag)) return 0;
5211 SubTest("Tags holding profile sequence description");
5212 if (!CheckProfileSequenceTag(Pass, h)) return 0;
5213 if (!CheckProfileSequenceIDTag(Pass, h)) return 0;
5215 SubTest("Tags holding ICC viewing conditions");
5216 if (!CheckICCViewingConditions(Pass, h)) return 0;
5219 SubTest("VCGT tags");
5220 if (!CheckVCGT(Pass, h)) return 0;
5222 SubTest("RAW tags");
5223 if (!CheckRAWtags(Pass, h)) return 0;
5225 SubTest("Dictionary meta tags");
5226 // if (!CheckDictionary16(Pass, h)) return 0;
5227 if (!CheckDictionary24(Pass, h)) return 0;
5230 cmsSaveProfileToFile(h, "alltags.icc");
5232 h = cmsOpenProfileFromFileTHR(DbgThread(), "alltags.icc", "r");
5238 Not implemented (by design):
5240 cmsSigDataTag = 0x64617461, // 'data' -- Unused
5241 cmsSigDeviceSettingsTag = 0x64657673, // 'devs' -- Unused
5242 cmsSigNamedColorTag = 0x6E636f6C, // 'ncol' -- Don't use this one, deprecated by ICC
5243 cmsSigOutputResponseTag = 0x72657370, // 'resp' -- Possible patent on this
5247 remove("alltags.icc");
5252 // Error reporting -------------------------------------------------------------------------------------------------------
5256 void ErrorReportingFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
5258 TrappedError = TRUE;
5259 SimultaneousErrors++;
5260 strncpy(ReasonToFailBuffer, Text, TEXT_ERROR_BUFFER_SIZE-1);
5262 cmsUNUSED_PARAMETER(ContextID);
5263 cmsUNUSED_PARAMETER(ErrorCode);
5268 cmsInt32Number CheckBadProfiles(void)
5272 h = cmsOpenProfileFromFileTHR(DbgThread(), "IDoNotExist.icc", "r");
5278 h = cmsOpenProfileFromFileTHR(DbgThread(), "IAmIllFormed*.icc", "r");
5284 // No profile name given
5285 h = cmsOpenProfileFromFileTHR(DbgThread(), "", "r");
5291 h = cmsOpenProfileFromFileTHR(DbgThread(), "..", "r");
5297 h = cmsOpenProfileFromFileTHR(DbgThread(), "IHaveBadAccessMode.icc", "@");
5303 h = cmsOpenProfileFromFileTHR(DbgThread(), "bad.icc", "r");
5309 h = cmsOpenProfileFromFileTHR(DbgThread(), "toosmall.icc", "r");
5315 h = cmsOpenProfileFromMemTHR(DbgThread(), NULL, 3);
5321 h = cmsOpenProfileFromMemTHR(DbgThread(), "123", 3);
5327 if (SimultaneousErrors != 9) return 0;
5334 cmsInt32Number CheckErrReportingOnBadProfiles(void)
5338 cmsSetLogErrorHandler(ErrorReportingFunction);
5339 rc = CheckBadProfiles();
5340 cmsSetLogErrorHandler(FatalErrorQuit);
5342 // Reset the error state
5343 TrappedError = FALSE;
5349 cmsInt32Number CheckBadTransforms(void)
5351 cmsHPROFILE h1 = cmsCreate_sRGBProfile();
5354 x1 = cmsCreateTransform(NULL, 0, NULL, 0, 0, 0);
5356 cmsDeleteTransform(x1);
5362 x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_RGB_8, 12345, 0);
5364 cmsDeleteTransform(x1);
5368 x1 = cmsCreateTransform(h1, TYPE_CMYK_8, h1, TYPE_RGB_8, 0, 0);
5370 cmsDeleteTransform(x1);
5374 x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_CMYK_8, 1, 0);
5376 cmsDeleteTransform(x1);
5380 // sRGB does its output as XYZ!
5381 x1 = cmsCreateTransform(h1, TYPE_RGB_8, NULL, TYPE_Lab_8, 1, 0);
5383 cmsDeleteTransform(x1);
5387 cmsCloseProfile(h1);
5392 cmsHPROFILE h1 = cmsOpenProfileFromFile("test1.icc", "r");
5393 cmsHPROFILE h2 = cmsCreate_sRGBProfile();
5395 x1 = cmsCreateTransform(h1, TYPE_BGR_8, h2, TYPE_BGR_8, INTENT_PERCEPTUAL, 0);
5397 cmsCloseProfile(h1); cmsCloseProfile(h2);
5399 cmsDeleteTransform(x1);
5409 cmsInt32Number CheckErrReportingOnBadTransforms(void)
5413 cmsSetLogErrorHandler(ErrorReportingFunction);
5414 rc = CheckBadTransforms();
5415 cmsSetLogErrorHandler(FatalErrorQuit);
5417 // Reset the error state
5418 TrappedError = FALSE;
5425 // ---------------------------------------------------------------------------------------------------------
5427 // Check a linear xform
5429 cmsInt32Number Check8linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5431 cmsInt32Number n2, i, j;
5432 cmsUInt8Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5436 for (j=0; j < 0xFF; j++) {
5438 memset(Inw, j, sizeof(Inw));
5439 cmsDoTransform(xform, Inw, Outw, 1);
5441 for (i=0; i < nChan; i++) {
5443 cmsInt32Number dif = abs(Outw[i] - j);
5444 if (dif > n2) n2 = dif;
5449 // We allow 2 contone of difference on 8 bits
5452 Fail("Differences too big (%x)", n2);
5460 cmsInt32Number Compare8bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5462 cmsInt32Number n2, i, j;
5463 cmsUInt8Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5467 for (j=0; j < 0xFF; j++) {
5469 memset(Inw, j, sizeof(Inw));
5470 cmsDoTransform(xform1, Inw, Outw1, 1);
5471 cmsDoTransform(xform2, Inw, Outw2, 1);
5473 for (i=0; i < nChan; i++) {
5475 cmsInt32Number dif = abs(Outw2[i] - Outw1[i]);
5476 if (dif > n2) n2 = dif;
5481 // We allow 2 contone of difference on 8 bits
5484 Fail("Differences too big (%x)", n2);
5493 // Check a linear xform
5495 cmsInt32Number Check16linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5497 cmsInt32Number n2, i, j;
5498 cmsUInt16Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5501 for (j=0; j < 0xFFFF; j++) {
5503 for (i=0; i < nChan; i++) Inw[i] = (cmsUInt16Number) j;
5505 cmsDoTransform(xform, Inw, Outw, 1);
5507 for (i=0; i < nChan; i++) {
5509 cmsInt32Number dif = abs(Outw[i] - j);
5510 if (dif > n2) n2 = dif;
5515 // We allow 2 contone of difference on 16 bits
5518 Fail("Differences too big (%x)", n2);
5527 cmsInt32Number Compare16bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5529 cmsInt32Number n2, i, j;
5530 cmsUInt16Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5534 for (j=0; j < 0xFFFF; j++) {
5536 for (i=0; i < nChan; i++) Inw[i] = (cmsUInt16Number) j;
5538 cmsDoTransform(xform1, Inw, Outw1, 1);
5539 cmsDoTransform(xform2, Inw, Outw2, 1);
5541 for (i=0; i < nChan; i++) {
5543 cmsInt32Number dif = abs(Outw2[i] - Outw1[i]);
5544 if (dif > n2) n2 = dif;
5549 // We allow 2 contone of difference on 16 bits
5552 Fail("Differences too big (%x)", n2);
5561 // Check a linear xform
5563 cmsInt32Number CheckFloatlinearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5565 cmsInt32Number n2, i, j;
5566 cmsFloat32Number In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS];
5570 for (j=0; j < 0xFFFF; j++) {
5572 for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5574 cmsDoTransform(xform, In, Out, 1);
5576 for (i=0; i < nChan; i++) {
5578 // We allow no difference in floating point
5579 if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out[i], (cmsFloat32Number) (j / 65535.0)))
5588 // Check a linear xform
5590 cmsInt32Number CompareFloatXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5592 cmsInt32Number n2, i, j;
5593 cmsFloat32Number In[cmsMAXCHANNELS], Out1[cmsMAXCHANNELS], Out2[cmsMAXCHANNELS];
5597 for (j=0; j < 0xFFFF; j++) {
5599 for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5601 cmsDoTransform(xform1, In, Out1, 1);
5602 cmsDoTransform(xform2, In, Out2, 1);
5604 for (i=0; i < nChan; i++) {
5606 // We allow no difference in floating point
5607 if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out1[i], Out2[i]))
5617 // Curves only transforms ----------------------------------------------------------------------------------------
5620 cmsInt32Number CheckCurvesOnlyTransforms(void)
5623 cmsHTRANSFORM xform1, xform2;
5624 cmsHPROFILE h1, h2, h3;
5625 cmsToneCurve* c1, *c2, *c3;
5626 cmsInt32Number rc = 1;
5629 c1 = cmsBuildGamma(DbgThread(), 2.2);
5630 c2 = cmsBuildGamma(DbgThread(), 1/2.2);
5631 c3 = cmsBuildGamma(DbgThread(), 4.84);
5633 h1 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c1);
5634 h2 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c2);
5635 h3 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c3);
5637 SubTest("Gray float optimizeable transform");
5638 xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h2, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5639 rc &= CheckFloatlinearXFORM(xform1, 1);
5640 cmsDeleteTransform(xform1);
5641 if (rc == 0) goto Error;
5643 SubTest("Gray 8 optimizeable transform");
5644 xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h2, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5645 rc &= Check8linearXFORM(xform1, 1);
5646 cmsDeleteTransform(xform1);
5647 if (rc == 0) goto Error;
5649 SubTest("Gray 16 optimizeable transform");
5650 xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h2, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5651 rc &= Check16linearXFORM(xform1, 1);
5652 cmsDeleteTransform(xform1);
5653 if (rc == 0) goto Error;
5655 SubTest("Gray float non-optimizeable transform");
5656 xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h1, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5657 xform2 = cmsCreateTransform(h3, TYPE_GRAY_FLT, NULL, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5659 rc &= CompareFloatXFORM(xform1, xform2, 1);
5660 cmsDeleteTransform(xform1);
5661 cmsDeleteTransform(xform2);
5662 if (rc == 0) goto Error;
5664 SubTest("Gray 8 non-optimizeable transform");
5665 xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h1, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5666 xform2 = cmsCreateTransform(h3, TYPE_GRAY_8, NULL, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5668 rc &= Compare8bitXFORM(xform1, xform2, 1);
5669 cmsDeleteTransform(xform1);
5670 cmsDeleteTransform(xform2);
5671 if (rc == 0) goto Error;
5674 SubTest("Gray 16 non-optimizeable transform");
5675 xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h1, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5676 xform2 = cmsCreateTransform(h3, TYPE_GRAY_16, NULL, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5678 rc &= Compare16bitXFORM(xform1, xform2, 1);
5679 cmsDeleteTransform(xform1);
5680 cmsDeleteTransform(xform2);
5681 if (rc == 0) goto Error;
5685 cmsCloseProfile(h1); cmsCloseProfile(h2); cmsCloseProfile(h3);
5686 cmsFreeToneCurve(c1); cmsFreeToneCurve(c2); cmsFreeToneCurve(c3);
5693 // Lab to Lab trivial transforms ----------------------------------------------------------------------------------------
5695 static cmsFloat64Number MaxDE;
5698 cmsInt32Number CheckOneLab(cmsHTRANSFORM xform, cmsFloat64Number L, cmsFloat64Number a, cmsFloat64Number b)
5701 cmsFloat64Number dE;
5703 In.L = L; In.a = a; In.b = b;
5704 cmsDoTransform(xform, &In, &Out, 1);
5706 dE = cmsDeltaE(&In, &Out);
5708 if (dE > MaxDE) MaxDE = dE;
5710 if (MaxDE > 0.003) {
5711 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);
5712 cmsDoTransform(xform, &In, &Out, 1);
5719 // Check several Lab, slicing at non-exact values. Precision should be 16 bits. 50x50x50 checks aprox.
5721 cmsInt32Number CheckSeveralLab(cmsHTRANSFORM xform)
5723 cmsInt32Number L, a, b;
5726 for (L=0; L < 65536; L += 1311) {
5728 for (a = 0; a < 65536; a += 1232) {
5730 for (b = 0; b < 65536; b += 1111) {
5732 if (!CheckOneLab(xform, (L * 100.0) / 65535.0,
5733 (a / 257.0) - 128, (b / 257.0) - 128))
5745 cmsInt32Number OneTrivialLab(cmsHPROFILE hLab1, cmsHPROFILE hLab2, const char* txt)
5747 cmsHTRANSFORM xform;
5751 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5752 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5754 rc = CheckSeveralLab(xform);
5755 cmsDeleteTransform(xform);
5761 cmsInt32Number CheckFloatLabTransforms(void)
5763 return OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab4/Lab4") &&
5764 OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab2/Lab2") &&
5765 OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab4/Lab2") &&
5766 OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab2/Lab4");
5771 cmsInt32Number CheckEncodedLabTransforms(void)
5773 cmsHTRANSFORM xform;
5774 cmsUInt16Number In[3];
5776 cmsCIELab White = { 100, 0, 0 };
5777 cmsHPROFILE hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5778 cmsHPROFILE hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5781 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5782 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5788 cmsDoTransform(xform, In, &Lab, 1);
5790 if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5791 cmsDeleteTransform(xform);
5793 hLab1 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5794 hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5796 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_LabV2_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5797 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5804 cmsDoTransform(xform, In, &Lab, 1);
5806 if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5808 cmsDeleteTransform(xform);
5810 hLab2 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5811 hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5813 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_LabV2_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5814 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5820 cmsDoTransform(xform, &Lab, In, 1);
5821 if (In[0] != 0xFF00 ||
5823 In[2] != 0x8000) return 0;
5825 cmsDeleteTransform(xform);
5827 hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5828 hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5830 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5831 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5837 cmsDoTransform(xform, &Lab, In, 1);
5839 if (In[0] != 0xFFFF ||
5841 In[2] != 0x8080) return 0;
5843 cmsDeleteTransform(xform);
5849 cmsInt32Number CheckStoredIdentities(void)
5851 cmsHPROFILE hLab, hLink, h4, h2;
5852 cmsHTRANSFORM xform;
5853 cmsInt32Number rc = 1;
5855 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5856 xform = cmsCreateTransformTHR(DbgThread(), hLab, TYPE_Lab_8, hLab, TYPE_Lab_8, 0, 0);
5858 hLink = cmsTransform2DeviceLink(xform, 3.4, 0);
5859 cmsSaveProfileToFile(hLink, "abstractv2.icc");
5860 cmsCloseProfile(hLink);
5862 hLink = cmsTransform2DeviceLink(xform, 4.3, 0);
5863 cmsSaveProfileToFile(hLink, "abstractv4.icc");
5864 cmsCloseProfile(hLink);
5866 cmsDeleteTransform(xform);
5867 cmsCloseProfile(hLab);
5869 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5871 xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5874 rc &= CheckSeveralLab(xform);
5876 cmsDeleteTransform(xform);
5877 cmsCloseProfile(h4);
5878 if (!rc) goto Error;
5882 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5884 xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5885 rc &= CheckSeveralLab(xform);
5886 cmsDeleteTransform(xform);
5887 cmsCloseProfile(h2);
5888 if (!rc) goto Error;
5891 SubTest("V2 -> V4");
5892 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5893 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5895 xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5896 rc &= CheckSeveralLab(xform);
5897 cmsDeleteTransform(xform);
5898 cmsCloseProfile(h2);
5899 cmsCloseProfile(h4);
5901 SubTest("V4 -> V2");
5902 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5903 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5905 xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5906 rc &= CheckSeveralLab(xform);
5907 cmsDeleteTransform(xform);
5908 cmsCloseProfile(h2);
5909 cmsCloseProfile(h4);
5912 remove("abstractv2.icc");
5913 remove("abstractv4.icc");
5920 // Check a simple xform from a matrix profile to itself. Test floating point accuracy.
5922 cmsInt32Number CheckMatrixShaperXFORMFloat(void)
5924 cmsHPROFILE hAbove, hSRGB;
5925 cmsHTRANSFORM xform;
5926 cmsInt32Number rc1, rc2;
5928 hAbove = Create_AboveRGB();
5929 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
5930 cmsCloseProfile(hAbove);
5931 rc1 = CheckFloatlinearXFORM(xform, 3);
5932 cmsDeleteTransform(xform);
5934 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5935 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_FLT, hSRGB, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
5936 cmsCloseProfile(hSRGB);
5937 rc2 = CheckFloatlinearXFORM(xform, 3);
5938 cmsDeleteTransform(xform);
5944 // Check a simple xform from a matrix profile to itself. Test 16 bits accuracy.
5946 cmsInt32Number CheckMatrixShaperXFORM16(void)
5948 cmsHPROFILE hAbove, hSRGB;
5949 cmsHTRANSFORM xform;
5950 cmsInt32Number rc1, rc2;
5952 hAbove = Create_AboveRGB();
5953 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5954 cmsCloseProfile(hAbove);
5956 rc1 = Check16linearXFORM(xform, 3);
5957 cmsDeleteTransform(xform);
5959 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5960 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_16, hSRGB, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5961 cmsCloseProfile(hSRGB);
5962 rc2 = Check16linearXFORM(xform, 3);
5963 cmsDeleteTransform(xform);
5970 // Check a simple xform from a matrix profile to itself. Test 8 bits accuracy.
5972 cmsInt32Number CheckMatrixShaperXFORM8(void)
5974 cmsHPROFILE hAbove, hSRGB;
5975 cmsHTRANSFORM xform;
5976 cmsInt32Number rc1, rc2;
5978 hAbove = Create_AboveRGB();
5979 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_8, hAbove, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
5980 cmsCloseProfile(hAbove);
5981 rc1 = Check8linearXFORM(xform, 3);
5982 cmsDeleteTransform(xform);
5984 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5985 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_8, hSRGB, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
5986 cmsCloseProfile(hSRGB);
5987 rc2 = Check8linearXFORM(xform, 3);
5988 cmsDeleteTransform(xform);
5995 // TODO: Check LUT based to LUT based transforms for CMYK
6002 // -----------------------------------------------------------------------------------------------------------------
6005 // Check known values going from sRGB to XYZ
6007 cmsInt32Number CheckOneRGB_f(cmsHTRANSFORM xform, cmsInt32Number R, cmsInt32Number G, cmsInt32Number B, cmsFloat64Number X, cmsFloat64Number Y, cmsFloat64Number Z, cmsFloat64Number err)
6009 cmsFloat32Number RGB[3];
6010 cmsFloat64Number Out[3];
6012 RGB[0] = (cmsFloat32Number) (R / 255.0);
6013 RGB[1] = (cmsFloat32Number) (G / 255.0);
6014 RGB[2] = (cmsFloat32Number) (B / 255.0);
6016 cmsDoTransform(xform, RGB, Out, 1);
6018 return IsGoodVal("X", X , Out[0], err) &&
6019 IsGoodVal("Y", Y , Out[1], err) &&
6020 IsGoodVal("Z", Z , Out[2], err);
6024 cmsInt32Number Chack_sRGB_Float(void)
6026 cmsHPROFILE hsRGB, hXYZ, hLab;
6027 cmsHTRANSFORM xform1, xform2;
6031 hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
6032 hXYZ = cmsCreateXYZProfileTHR(DbgThread());
6033 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6035 xform1 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hXYZ, TYPE_XYZ_DBL,
6036 INTENT_RELATIVE_COLORIMETRIC, 0);
6038 xform2 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hLab, TYPE_Lab_DBL,
6039 INTENT_RELATIVE_COLORIMETRIC, 0);
6040 cmsCloseProfile(hsRGB);
6041 cmsCloseProfile(hXYZ);
6042 cmsCloseProfile(hLab);
6046 // Xform 1 goes from 8 bits to XYZ,
6047 rc = CheckOneRGB_f(xform1, 1, 1, 1, 0.0002926, 0.00030352, 0.00025037, 0.0001);
6048 rc &= CheckOneRGB_f(xform1, 127, 127, 127, 0.2046329, 0.212230, 0.175069, 0.0001);
6049 rc &= CheckOneRGB_f(xform1, 12, 13, 15, 0.0038364, 0.0039928, 0.00385212, 0.0001);
6050 rc &= CheckOneRGB_f(xform1, 128, 0, 0, 0.0940846, 0.0480030, 0.00300543, 0.0001);
6051 rc &= CheckOneRGB_f(xform1, 190, 25, 210, 0.3203491, 0.1605240, 0.46817115, 0.0001);
6053 // Xform 2 goes from 8 bits to Lab, we allow 0.01 error max
6054 rc &= CheckOneRGB_f(xform2, 1, 1, 1, 0.2741748, 0, 0, 0.01);
6055 rc &= CheckOneRGB_f(xform2, 127, 127, 127, 53.192776, 0, 0, 0.01);
6056 rc &= CheckOneRGB_f(xform2, 190, 25, 210, 47.043171, 74.564576, -56.89373, 0.01);
6057 rc &= CheckOneRGB_f(xform2, 128, 0, 0, 26.158100, 48.474477, 39.425916, 0.01);
6059 cmsDeleteTransform(xform1);
6060 cmsDeleteTransform(xform2);
6065 // ---------------------------------------------------
6068 cmsBool GetProfileRGBPrimaries(cmsHPROFILE hProfile,
6069 cmsCIEXYZTRIPLE *result,
6070 cmsUInt32Number intent)
6073 cmsHTRANSFORM hTransform;
6074 cmsFloat64Number rgb[3][3] = {{1., 0., 0.},
6078 hXYZ = cmsCreateXYZProfile();
6079 if (hXYZ == NULL) return FALSE;
6081 hTransform = cmsCreateTransform(hProfile, TYPE_RGB_DBL, hXYZ, TYPE_XYZ_DBL,
6082 intent, cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
6083 cmsCloseProfile(hXYZ);
6084 if (hTransform == NULL) return FALSE;
6086 cmsDoTransform(hTransform, rgb, result, 3);
6087 cmsDeleteTransform(hTransform);
6093 int CheckRGBPrimaries(void)
6096 cmsCIEXYZTRIPLE tripXYZ;
6097 cmsCIExyYTRIPLE tripxyY;
6100 cmsSetAdaptationState(0);
6101 hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
6102 if (!hsRGB) return 0;
6104 result = GetProfileRGBPrimaries(hsRGB, &tripXYZ,
6105 INTENT_ABSOLUTE_COLORIMETRIC);
6107 cmsCloseProfile(hsRGB);
6108 if (!result) return 0;
6110 cmsXYZ2xyY(&tripxyY.Red, &tripXYZ.Red);
6111 cmsXYZ2xyY(&tripxyY.Green, &tripXYZ.Green);
6112 cmsXYZ2xyY(&tripxyY.Blue, &tripXYZ.Blue);
6114 /* valus were taken from
6115 http://en.wikipedia.org/wiki/RGB_color_spaces#Specifications */
6117 if (!IsGoodFixed15_16("xRed", tripxyY.Red.x, 0.64) ||
6118 !IsGoodFixed15_16("yRed", tripxyY.Red.y, 0.33) ||
6119 !IsGoodFixed15_16("xGreen", tripxyY.Green.x, 0.30) ||
6120 !IsGoodFixed15_16("yGreen", tripxyY.Green.y, 0.60) ||
6121 !IsGoodFixed15_16("xBlue", tripxyY.Blue.x, 0.15) ||
6122 !IsGoodFixed15_16("yBlue", tripxyY.Blue.y, 0.06)) {
6123 Fail("One or more primaries are wrong.");
6131 // -----------------------------------------------------------------------------------------------------------------
6133 // This function will check CMYK -> CMYK transforms. It uses FOGRA29 and SWOP ICC profiles
6136 cmsInt32Number CheckCMYK(cmsInt32Number Intent, const char *Profile1, const char* Profile2)
6138 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), Profile1, "r");
6139 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), Profile2, "r");
6140 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6141 cmsFloat32Number CMYK1[4], CMYK2[4];
6142 cmsCIELab Lab1, Lab2;
6144 cmsFloat64Number DeltaL, Max;
6147 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6149 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, Intent, 0);
6151 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6152 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6155 for (i=0; i <= 100; i++) {
6160 CMYK1[3] = (cmsFloat32Number) i;
6162 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6163 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6164 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6166 DeltaL = fabs(Lab1.L - Lab2.L);
6168 if (DeltaL > Max) Max = DeltaL;
6172 cmsDeleteTransform(xform);
6174 if (Max > 3.0) return 0;
6176 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, Intent, 0);
6180 for (i=0; i <= 100; i++) {
6184 CMYK1[3] = (cmsFloat32Number) i;
6186 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6187 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6188 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6190 DeltaL = fabs(Lab1.L - Lab2.L);
6192 if (DeltaL > Max) Max = DeltaL;
6196 cmsCloseProfile(hSWOP);
6197 cmsCloseProfile(hFOGRA);
6198 cmsCloseProfile(hLab);
6200 cmsDeleteTransform(xform);
6201 cmsDeleteTransform(swop_lab);
6202 cmsDeleteTransform(fogra_lab);
6208 cmsInt32Number CheckCMYKRoundtrip(void)
6210 return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test1.icc");
6215 cmsInt32Number CheckCMYKPerceptual(void)
6217 return CheckCMYK(INTENT_PERCEPTUAL, "test1.icc", "test2.icc");
6223 cmsInt32Number CheckCMYKRelCol(void)
6225 return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test2.icc");
6231 cmsInt32Number CheckKOnlyBlackPreserving(void)
6233 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6234 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6235 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6236 cmsFloat32Number CMYK1[4], CMYK2[4];
6237 cmsCIELab Lab1, Lab2;
6239 cmsFloat64Number DeltaL, Max;
6242 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6244 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6246 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6247 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6251 for (i=0; i <= 100; i++) {
6255 CMYK1[3] = (cmsFloat32Number) i;
6257 // SWOP CMYK to Lab1
6258 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6260 // SWOP To FOGRA using black preservation
6261 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6263 // Obtained FOGRA CMYK to Lab2
6264 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6266 // We care only on L*
6267 DeltaL = fabs(Lab1.L - Lab2.L);
6269 if (DeltaL > Max) Max = DeltaL;
6273 cmsDeleteTransform(xform);
6275 // dL should be below 3.0
6276 if (Max > 3.0) return 0;
6279 // Same, but FOGRA to SWOP
6280 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6284 for (i=0; i <= 100; i++) {
6288 CMYK1[3] = (cmsFloat32Number) i;
6290 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6291 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6292 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6294 DeltaL = fabs(Lab1.L - Lab2.L);
6296 if (DeltaL > Max) Max = DeltaL;
6300 cmsCloseProfile(hSWOP);
6301 cmsCloseProfile(hFOGRA);
6302 cmsCloseProfile(hLab);
6304 cmsDeleteTransform(xform);
6305 cmsDeleteTransform(swop_lab);
6306 cmsDeleteTransform(fogra_lab);
6312 cmsInt32Number CheckKPlaneBlackPreserving(void)
6314 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6315 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6316 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6317 cmsFloat32Number CMYK1[4], CMYK2[4];
6318 cmsCIELab Lab1, Lab2;
6320 cmsFloat64Number DeltaE, Max;
6323 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6325 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PERCEPTUAL, 0);
6327 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6328 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6332 for (i=0; i <= 100; i++) {
6336 CMYK1[3] = (cmsFloat32Number) i;
6338 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6339 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6340 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6342 DeltaE = cmsDeltaE(&Lab1, &Lab2);
6344 if (DeltaE > Max) Max = DeltaE;
6348 cmsDeleteTransform(xform);
6350 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_PLANE_PERCEPTUAL, 0);
6352 for (i=0; i <= 100; i++) {
6356 CMYK1[3] = (cmsFloat32Number) i;
6358 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6359 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6360 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6362 DeltaE = cmsDeltaE(&Lab1, &Lab2);
6364 if (DeltaE > Max) Max = DeltaE;
6367 cmsDeleteTransform(xform);
6371 cmsCloseProfile(hSWOP);
6372 cmsCloseProfile(hFOGRA);
6373 cmsCloseProfile(hLab);
6376 cmsDeleteTransform(swop_lab);
6377 cmsDeleteTransform(fogra_lab);
6383 // ------------------------------------------------------------------------------------------------------
6387 cmsInt32Number CheckProofingXFORMFloat(void)
6390 cmsHTRANSFORM xform;
6393 hAbove = Create_AboveRGB();
6394 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
6395 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING);
6396 cmsCloseProfile(hAbove);
6397 rc = CheckFloatlinearXFORM(xform, 3);
6398 cmsDeleteTransform(xform);
6403 cmsInt32Number CheckProofingXFORM16(void)
6406 cmsHTRANSFORM xform;
6409 hAbove = Create_AboveRGB();
6410 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
6411 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING|cmsFLAGS_NOCACHE);
6412 cmsCloseProfile(hAbove);
6413 rc = Check16linearXFORM(xform, 3);
6414 cmsDeleteTransform(xform);
6420 cmsInt32Number CheckGamutCheck(void)
6422 cmsHPROFILE hSRGB, hAbove;
6423 cmsHTRANSFORM xform;
6425 cmsUInt16Number Alarm[3] = { 0xDEAD, 0xBABE, 0xFACE };
6427 // Set alarm codes to fancy values so we could check the out of gamut condition
6428 cmsSetAlarmCodes(Alarm);
6430 // Create the profiles
6431 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
6432 hAbove = Create_AboveRGB();
6434 if (hSRGB == NULL || hAbove == NULL) return 0; // Failed
6436 SubTest("Gamut check on floating point");
6438 // Create a gamut checker in the same space. No value should be out of gamut
6439 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
6440 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6443 if (!CheckFloatlinearXFORM(xform, 3)) {
6444 cmsCloseProfile(hSRGB);
6445 cmsCloseProfile(hAbove);
6446 cmsDeleteTransform(xform);
6447 Fail("Gamut check on same profile failed");
6451 cmsDeleteTransform(xform);
6453 SubTest("Gamut check on 16 bits");
6455 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
6456 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6458 cmsCloseProfile(hSRGB);
6459 cmsCloseProfile(hAbove);
6461 rc = Check16linearXFORM(xform, 3);
6463 cmsDeleteTransform(xform);
6470 // -------------------------------------------------------------------------------------------------------------------
6473 cmsInt32Number CheckBlackPoint(void)
6475 cmsHPROFILE hProfile;
6479 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test5.icc", "r");
6480 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6481 cmsCloseProfile(hProfile);
6484 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6485 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6486 cmsXYZ2Lab(NULL, &Lab, &Black);
6487 cmsCloseProfile(hProfile);
6489 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "lcms2cmyk.icc", "r");
6490 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6491 cmsXYZ2Lab(NULL, &Lab, &Black);
6492 cmsCloseProfile(hProfile);
6494 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6495 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6496 cmsXYZ2Lab(NULL, &Lab, &Black);
6497 cmsCloseProfile(hProfile);
6499 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6500 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_PERCEPTUAL, 0);
6501 cmsXYZ2Lab(NULL, &Lab, &Black);
6502 cmsCloseProfile(hProfile);
6509 cmsInt32Number CheckOneTAC(cmsFloat64Number InkLimit)
6514 h =CreateFakeCMYK(InkLimit, TRUE);
6515 cmsSaveProfileToFile(h, "lcmstac.icc");
6518 h = cmsOpenProfileFromFile("lcmstac.icc", "r");
6519 d = cmsDetectTAC(h);
6522 remove("lcmstac.icc");
6524 if (fabs(d - InkLimit) > 5) return 0;
6531 cmsInt32Number CheckTAC(void)
6533 if (!CheckOneTAC(180)) return 0;
6534 if (!CheckOneTAC(220)) return 0;
6535 if (!CheckOneTAC(286)) return 0;
6536 if (!CheckOneTAC(310)) return 0;
6537 if (!CheckOneTAC(330)) return 0;
6542 // -------------------------------------------------------------------------------------------------------
6545 #define NPOINTS_IT8 10 // (17*17*17*17)
6548 cmsInt32Number CheckCGATS(void)
6553 SubTest("IT8 creation");
6554 it8 = cmsIT8Alloc(DbgThread());
6555 if (it8 == NULL) return 0;
6557 cmsIT8SetSheetType(it8, "LCMS/TESTING");
6558 cmsIT8SetPropertyStr(it8, "ORIGINATOR", "1 2 3 4");
6559 cmsIT8SetPropertyUncooked(it8, "DESCRIPTOR", "1234");
6560 cmsIT8SetPropertyStr(it8, "MANUFACTURER", "3");
6561 cmsIT8SetPropertyDbl(it8, "CREATED", 4);
6562 cmsIT8SetPropertyDbl(it8, "SERIAL", 5);
6563 cmsIT8SetPropertyHex(it8, "MATERIAL", 0x123);
6565 cmsIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", NPOINTS_IT8);
6566 cmsIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", 4);
6568 cmsIT8SetDataFormat(it8, 0, "SAMPLE_ID");
6569 cmsIT8SetDataFormat(it8, 1, "RGB_R");
6570 cmsIT8SetDataFormat(it8, 2, "RGB_G");
6571 cmsIT8SetDataFormat(it8, 3, "RGB_B");
6573 SubTest("Table creation");
6574 for (i=0; i < NPOINTS_IT8; i++) {
6578 sprintf(Patch, "P%d", i);
6580 cmsIT8SetDataRowCol(it8, i, 0, Patch);
6581 cmsIT8SetDataRowColDbl(it8, i, 1, i);
6582 cmsIT8SetDataRowColDbl(it8, i, 2, i);
6583 cmsIT8SetDataRowColDbl(it8, i, 3, i);
6586 SubTest("Save to file");
6587 cmsIT8SaveToFile(it8, "TEST.IT8");
6590 SubTest("Load from file");
6591 it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6592 if (it8 == NULL) return 0;
6594 SubTest("Save again file");
6595 cmsIT8SaveToFile(it8, "TEST.IT8");
6599 SubTest("Load from file (II)");
6600 it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6601 if (it8 == NULL) return 0;
6604 SubTest("Change prop value");
6605 if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 1234) {
6611 cmsIT8SetPropertyDbl(it8, "DESCRIPTOR", 5678);
6612 if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 5678) {
6617 SubTest("Positive numbers");
6618 if (cmsIT8GetDataDbl(it8, "P3", "RGB_G") != 3) {
6624 SubTest("Positive exponent numbers");
6625 cmsIT8SetPropertyDbl(it8, "DBL_PROP", 123E+12);
6626 if ((cmsIT8GetPropertyDbl(it8, "DBL_PROP") - 123E+12) > 1 ) {
6631 SubTest("Negative exponent numbers");
6632 cmsIT8SetPropertyDbl(it8, "DBL_PROP_NEG", 123E-45);
6633 if ((cmsIT8GetPropertyDbl(it8, "DBL_PROP_NEG") - 123E-45) > 1E-45 ) {
6639 SubTest("Negative numbers");
6640 cmsIT8SetPropertyDbl(it8, "DBL_NEG_VAL", -123);
6641 if ((cmsIT8GetPropertyDbl(it8, "DBL_NEG_VAL")) != -123 ) {
6657 void GenerateCSA(const char* cInProf, const char* FileName)
6659 cmsHPROFILE hProfile;
6662 cmsContext BuffThread = DbgThread();
6666 if (cInProf == NULL)
6667 hProfile = cmsCreateLab4Profile(NULL);
6669 hProfile = cmsOpenProfileFromFile(cInProf, "r");
6671 n = cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, NULL, 0);
6674 Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6675 cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, Buffer, n);
6678 if (FileName != NULL) {
6679 o = fopen(FileName, "wb");
6680 fwrite(Buffer, n, 1, o);
6684 _cmsFree(BuffThread, Buffer);
6685 cmsCloseProfile(hProfile);
6691 void GenerateCRD(const char* cOutProf, const char* FileName)
6693 cmsHPROFILE hProfile;
6696 cmsUInt32Number dwFlags = 0;
6697 cmsContext BuffThread = DbgThread();
6700 if (cOutProf == NULL)
6701 hProfile = cmsCreateLab4Profile(NULL);
6703 hProfile = cmsOpenProfileFromFile(cOutProf, "r");
6705 n = cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, NULL, 0);
6708 Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6709 cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, Buffer, n);
6712 if (FileName != NULL) {
6713 FILE* o = fopen(FileName, "wb");
6714 fwrite(Buffer, n, 1, o);
6718 _cmsFree(BuffThread, Buffer);
6719 cmsCloseProfile(hProfile);
6724 cmsInt32Number CheckPostScript(void)
6726 GenerateCSA("test5.icc", "sRGB_CSA.ps");
6727 GenerateCSA("aRGBlcms2.icc", "aRGB_CSA.ps");
6728 GenerateCSA("test4.icc", "sRGBV4_CSA.ps");
6729 GenerateCSA("test1.icc", "SWOP_CSA.ps");
6730 GenerateCSA(NULL, "Lab_CSA.ps");
6731 GenerateCSA("graylcms2.icc", "gray_CSA.ps");
6733 GenerateCRD("test5.icc", "sRGB_CRD.ps");
6734 GenerateCRD("aRGBlcms2.icc", "aRGB_CRD.ps");
6735 GenerateCRD(NULL, "Lab_CRD.ps");
6736 GenerateCRD("test1.icc", "SWOP_CRD.ps");
6737 GenerateCRD("test4.icc", "sRGBV4_CRD.ps");
6738 GenerateCRD("graylcms2.icc", "gray_CRD.ps");
6745 cmsInt32Number CheckGray(cmsHTRANSFORM xform, cmsUInt8Number g, double L)
6749 cmsDoTransform(xform, &g, &Lab, 1);
6751 if (!IsGoodVal("a axis on gray", 0, Lab.a, 0.001)) return 0;
6752 if (!IsGoodVal("b axis on gray", 0, Lab.b, 0.001)) return 0;
6754 return IsGoodVal("Gray value", L, Lab.L, 0.01);
6758 cmsInt32Number CheckInputGray(void)
6760 cmsHPROFILE hGray = Create_Gray22();
6761 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6762 cmsHTRANSFORM xform;
6764 if (hGray == NULL || hLab == NULL) return 0;
6766 xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
6767 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6769 if (!CheckGray(xform, 0, 0)) return 0;
6770 if (!CheckGray(xform, 125, 52.768)) return 0;
6771 if (!CheckGray(xform, 200, 81.069)) return 0;
6772 if (!CheckGray(xform, 255, 100.0)) return 0;
6774 cmsDeleteTransform(xform);
6779 cmsInt32Number CheckLabInputGray(void)
6781 cmsHPROFILE hGray = Create_GrayLab();
6782 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6783 cmsHTRANSFORM xform;
6785 if (hGray == NULL || hLab == NULL) return 0;
6787 xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
6788 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6790 if (!CheckGray(xform, 0, 0)) return 0;
6791 if (!CheckGray(xform, 125, 49.019)) return 0;
6792 if (!CheckGray(xform, 200, 78.431)) return 0;
6793 if (!CheckGray(xform, 255, 100.0)) return 0;
6795 cmsDeleteTransform(xform);
6801 cmsInt32Number CheckOutGray(cmsHTRANSFORM xform, double L, cmsUInt8Number g)
6804 cmsUInt8Number g_out;
6810 cmsDoTransform(xform, &Lab, &g_out, 1);
6812 return IsGoodVal("Gray value", g, (double) g_out, 0.01);
6816 cmsInt32Number CheckOutputGray(void)
6818 cmsHPROFILE hGray = Create_Gray22();
6819 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6820 cmsHTRANSFORM xform;
6822 if (hGray == NULL || hLab == NULL) return 0;
6824 xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0);
6825 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6827 if (!CheckOutGray(xform, 0, 0)) return 0;
6828 if (!CheckOutGray(xform, 100, 255)) return 0;
6830 if (!CheckOutGray(xform, 20, 52)) return 0;
6831 if (!CheckOutGray(xform, 50, 118)) return 0;
6834 cmsDeleteTransform(xform);
6840 cmsInt32Number CheckLabOutputGray(void)
6842 cmsHPROFILE hGray = Create_GrayLab();
6843 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6844 cmsHTRANSFORM xform;
6847 if (hGray == NULL || hLab == NULL) return 0;
6849 xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0);
6850 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6852 if (!CheckOutGray(xform, 0, 0)) return 0;
6853 if (!CheckOutGray(xform, 100, 255)) return 0;
6855 for (i=0; i < 100; i++) {
6859 g = (cmsUInt8Number) floor(i * 255.0 / 100.0 + 0.5);
6861 if (!CheckOutGray(xform, i, g)) return 0;
6865 cmsDeleteTransform(xform);
6871 cmsInt32Number CheckV4gamma(void)
6874 cmsUInt16Number Lin[] = {0, 0xffff};
6875 cmsToneCurve*g = cmsBuildTabulatedToneCurve16(DbgThread(), 2, Lin);
6877 h = cmsOpenProfileFromFileTHR(DbgThread(), "v4gamma.icc", "w");
6878 if (h == NULL) return 0;
6881 cmsSetProfileVersion(h, 4.3);
6883 if (!cmsWriteTag(h, cmsSigGrayTRCTag, g)) return 0;
6886 cmsFreeToneCurve(g);
6887 remove("v4gamma.icc");
6891 // cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname);
6893 // Gamut descriptor routines
6895 cmsInt32Number CheckGBD(void)
6899 cmsInt32Number L, a, b;
6900 cmsUInt32Number r1, g1, b1;
6901 cmsHPROFILE hLab, hsRGB;
6902 cmsHTRANSFORM xform;
6904 h = cmsGBDAlloc(DbgThread());
6905 if (h == NULL) return 0;
6907 // Fill all Lab gamut as valid
6908 SubTest("Filling RAW gamut");
6910 for (L=0; L <= 100; L += 10)
6911 for (a = -128; a <= 128; a += 5)
6912 for (b = -128; b <= 128; b += 5) {
6917 if (!cmsGDBAddPoint(h, &Lab)) return 0;
6920 // Complete boundaries
6921 SubTest("computing Lab gamut");
6922 if (!cmsGDBCompute(h, 0)) return 0;
6925 // All points should be inside gamut
6926 SubTest("checking Lab gamut");
6927 for (L=10; L <= 90; L += 25)
6928 for (a = -120; a <= 120; a += 25)
6929 for (b = -120; b <= 120; b += 25) {
6934 if (!cmsGDBCheckPoint(h, &Lab)) {
6942 SubTest("checking sRGB gamut");
6943 h = cmsGBDAlloc(DbgThread());
6944 hsRGB = cmsCreate_sRGBProfile();
6945 hLab = cmsCreateLab4Profile(NULL);
6947 xform = cmsCreateTransform(hsRGB, TYPE_RGB_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE);
6948 cmsCloseProfile(hsRGB); cmsCloseProfile(hLab);
6951 for (r1=0; r1 < 256; r1 += 5) {
6952 for (g1=0; g1 < 256; g1 += 5)
6953 for (b1=0; b1 < 256; b1 += 5) {
6956 cmsUInt8Number rgb[3];
6958 rgb[0] = (cmsUInt8Number) r1;
6959 rgb[1] = (cmsUInt8Number) g1;
6960 rgb[2] = (cmsUInt8Number) b1;
6962 cmsDoTransform(xform, rgb, &Lab, 1);
6964 // if (fabs(Lab.b) < 20 && Lab.a > 0) continue;
6966 if (!cmsGDBAddPoint(h, &Lab)) {
6976 if (!cmsGDBCompute(h, 0)) return 0;
6977 // cmsGBDdumpVRML(h, "c:\\colormaps\\lab.wrl");
6979 for (r1=10; r1 < 200; r1 += 10) {
6980 for (g1=10; g1 < 200; g1 += 10)
6981 for (b1=10; b1 < 200; b1 += 10) {
6984 cmsUInt8Number rgb[3];
6986 rgb[0] = (cmsUInt8Number) r1;
6987 rgb[1] = (cmsUInt8Number) g1;
6988 rgb[2] = (cmsUInt8Number) b1;
6990 cmsDoTransform(xform, rgb, &Lab, 1);
6991 if (!cmsGDBCheckPoint(h, &Lab)) {
6993 cmsDeleteTransform(xform);
7001 cmsDeleteTransform(xform);
7004 SubTest("checking LCh chroma ring");
7005 h = cmsGBDAlloc(DbgThread());
7008 for (r1=0; r1 < 360; r1++) {
7016 cmsLCh2Lab(&Lab, &LCh);
7017 if (!cmsGDBAddPoint(h, &Lab)) {
7024 if (!cmsGDBCompute(h, 0)) return 0;
7036 cmsHPROFILE pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r");
7037 cmsProfileID ProfileID1, ProfileID2, ProfileID3, ProfileID4;
7039 h =(_cmsICCPROFILE*) pProfile;
7040 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID1.ID8);
7041 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID2.ID8);
7043 cmsCloseProfile(pProfile);
7046 pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r");
7048 h =(_cmsICCPROFILE*) pProfile;
7049 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID3.ID8);
7050 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID4.ID8);
7052 cmsCloseProfile(pProfile);
7054 return ((memcmp(ProfileID1.ID8, ProfileID3.ID8, sizeof(ProfileID1)) == 0) &&
7055 (memcmp(ProfileID2.ID8, ProfileID4.ID8, sizeof(ProfileID2)) == 0));
7061 int CheckLinking(void)
7064 cmsPipeline * pipeline;
7065 cmsStage *stageBegin, *stageEnd;
7067 // Create a CLUT based profile
7068 h = cmsCreateInkLimitingDeviceLinkTHR(DbgThread(), cmsSigCmykData, 150);
7070 // link a second tag
7071 cmsLinkTag(h, cmsSigAToB1Tag, cmsSigAToB0Tag);
7073 // Save the linked devicelink
7074 if (!cmsSaveProfileToFile(h, "lcms2link.icc")) return 0;
7077 // Now open the profile and read the pipeline
7078 h = cmsOpenProfileFromFile("lcms2link.icc", "r");
7079 if (h == NULL) return 0;
7081 pipeline = (cmsPipeline*) cmsReadTag(h, cmsSigAToB1Tag);
7082 if (pipeline == NULL)
7087 pipeline = cmsPipelineDup(pipeline);
7089 // extract stage from pipe line
7090 cmsPipelineUnlinkStage(pipeline, cmsAT_BEGIN, &stageBegin);
7091 cmsPipelineUnlinkStage(pipeline, cmsAT_END, &stageEnd);
7092 cmsPipelineInsertStage(pipeline, cmsAT_END, stageEnd);
7093 cmsPipelineInsertStage(pipeline, cmsAT_BEGIN, stageBegin);
7095 if (cmsTagLinkedTo(h, cmsSigAToB1Tag) != cmsSigAToB0Tag) return 0;
7097 cmsWriteTag(h, cmsSigAToB0Tag, pipeline);
7098 cmsPipelineFree(pipeline);
7100 if (!cmsSaveProfileToFile(h, "lcms2link2.icc")) return 0;
7110 // Created by Paul Miller on 30/08/2012.
7113 cmsHPROFILE IdentityMatrixProfile( cmsColorSpaceSignature dataSpace)
7116 cmsVEC3 zero = {{0,0,0}};
7118 cmsPipeline* forward;
7119 cmsPipeline* reverse;
7120 cmsHPROFILE identityProfile = cmsCreateProfilePlaceholder( ctx);
7123 cmsSetProfileVersion(identityProfile, 4.3);
7125 cmsSetDeviceClass( identityProfile, cmsSigColorSpaceClass);
7126 cmsSetColorSpace(identityProfile, dataSpace);
7127 cmsSetPCS(identityProfile, cmsSigXYZData);
7129 cmsSetHeaderRenderingIntent(identityProfile, INTENT_RELATIVE_COLORIMETRIC);
7131 cmsWriteTag(identityProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ());
7135 _cmsMAT3identity( &identity);
7137 // build forward transform.... (RGB to PCS)
7138 forward = cmsPipelineAlloc( 0, 3, 3);
7139 cmsPipelineInsertStage( forward, cmsAT_END, cmsStageAllocMatrix( ctx, 3, 3, (cmsFloat64Number*)&identity, (cmsFloat64Number*)&zero));
7140 cmsWriteTag( identityProfile, cmsSigDToB1Tag, forward);
7142 cmsPipelineFree( forward);
7144 reverse = cmsPipelineAlloc( 0, 3, 3);
7145 cmsPipelineInsertStage( reverse, cmsAT_END, cmsStageAllocMatrix( ctx, 3, 3, (cmsFloat64Number*)&identity, (cmsFloat64Number*)&zero));
7146 cmsWriteTag( identityProfile, cmsSigBToD1Tag, reverse);
7148 cmsPipelineFree( reverse);
7150 return identityProfile;
7154 cmsInt32Number CheckFloatXYZ(void)
7157 cmsHPROFILE xyzProfile = cmsCreateXYZProfile();
7158 cmsHTRANSFORM xform;
7159 cmsFloat32Number in[3];
7160 cmsFloat32Number out[3];
7167 input = IdentityMatrixProfile( cmsSigRgbData);
7169 xform = cmsCreateTransform( input, TYPE_RGB_FLT, xyzProfile, TYPE_XYZ_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7170 cmsCloseProfile(input);
7172 cmsDoTransform( xform, in, out, 1);
7173 cmsDeleteTransform( xform);
7175 if (!IsGoodVal("Float RGB->XYZ", in[0], out[0], FLOAT_PRECISSION) ||
7176 !IsGoodVal("Float RGB->XYZ", in[1], out[1], FLOAT_PRECISSION) ||
7177 !IsGoodVal("Float RGB->XYZ", in[2], out[2], FLOAT_PRECISSION))
7182 input = IdentityMatrixProfile( cmsSigXYZData);
7184 xform = cmsCreateTransform( input, TYPE_XYZ_FLT, xyzProfile, TYPE_XYZ_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7185 cmsCloseProfile(input);
7187 cmsDoTransform( xform, in, out, 1);
7190 cmsDeleteTransform( xform);
7192 if (!IsGoodVal("Float XYZ->XYZ", in[0], out[0], FLOAT_PRECISSION) ||
7193 !IsGoodVal("Float XYZ->XYZ", in[1], out[1], FLOAT_PRECISSION) ||
7194 !IsGoodVal("Float XYZ->XYZ", in[2], out[2], FLOAT_PRECISSION))
7199 input = IdentityMatrixProfile( cmsSigRgbData);
7201 xform = cmsCreateTransform( xyzProfile, TYPE_XYZ_FLT, input, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7202 cmsCloseProfile(input);
7204 cmsDoTransform( xform, in, out, 1);
7206 cmsDeleteTransform( xform);
7208 if (!IsGoodVal("Float XYZ->RGB", in[0], out[0], FLOAT_PRECISSION) ||
7209 !IsGoodVal("Float XYZ->RGB", in[1], out[1], FLOAT_PRECISSION) ||
7210 !IsGoodVal("Float XYZ->RGB", in[2], out[2], FLOAT_PRECISSION))
7214 // Now the optimizer should remove a stage
7217 input = IdentityMatrixProfile( cmsSigRgbData);
7219 xform = cmsCreateTransform( input, TYPE_RGB_FLT, input, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7220 cmsCloseProfile(input);
7222 cmsDoTransform( xform, in, out, 1);
7224 cmsDeleteTransform( xform);
7226 if (!IsGoodVal("Float RGB->RGB", in[0], out[0], FLOAT_PRECISSION) ||
7227 !IsGoodVal("Float RGB->RGB", in[1], out[1], FLOAT_PRECISSION) ||
7228 !IsGoodVal("Float RGB->RGB", in[2], out[2], FLOAT_PRECISSION))
7231 cmsCloseProfile(xyzProfile);
7242 sRGB built-in V4.3 -> Lab identity built-in V4.3
7243 Flags: "cmsFLAGS_NOCACHE", "cmsFLAGS_NOOPTIMIZE"
7244 Input format: TYPE_RGBA_FLT
7245 Output format: TYPE_LabA_FLT
7248 Lab identity built-in V4.3 -> sRGB built-in V4.3
7249 Flags: "cmsFLAGS_NOCACHE", "cmsFLAGS_NOOPTIMIZE"
7250 Input format: TYPE_LabA_FLT
7251 Output format: TYPE_RGBA_FLT
7255 cmsInt32Number ChecksRGB2LabFLT(void)
7257 cmsHPROFILE hSRGB = cmsCreate_sRGBProfile();
7258 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
7260 cmsHTRANSFORM xform1 = cmsCreateTransform(hSRGB, TYPE_RGBA_FLT, hLab, TYPE_LabA_FLT, 0, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
7261 cmsHTRANSFORM xform2 = cmsCreateTransform(hLab, TYPE_LabA_FLT, hSRGB, TYPE_RGBA_FLT, 0, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
7263 cmsFloat32Number RGBA1[4], RGBA2[4], LabA[4];
7267 for (i = 0; i <= 100; i++)
7269 RGBA1[0] = i / 100.0F;
7270 RGBA1[1] = i / 100.0F;
7271 RGBA1[2] = i / 100.0F;
7274 cmsDoTransform(xform1, RGBA1, LabA, 1);
7275 cmsDoTransform(xform2, LabA, RGBA2, 1);
7277 if (!IsGoodVal("Float RGB->RGB", RGBA1[0], RGBA2[0], FLOAT_PRECISSION) ||
7278 !IsGoodVal("Float RGB->RGB", RGBA1[1], RGBA2[1], FLOAT_PRECISSION) ||
7279 !IsGoodVal("Float RGB->RGB", RGBA1[2], RGBA2[2], FLOAT_PRECISSION))
7284 cmsDeleteTransform(xform1);
7285 cmsDeleteTransform(xform2);
7286 cmsCloseProfile(hSRGB);
7287 cmsCloseProfile(hLab);
7293 // --------------------------------------------------------------------------------------------------
7294 // P E R F O R M A N C E C H E C K S
7295 // --------------------------------------------------------------------------------------------------
7298 typedef struct {cmsUInt8Number r, g, b, a;} Scanline_rgb1;
7299 typedef struct {cmsUInt16Number r, g, b, a;} Scanline_rgb2;
7300 typedef struct {cmsUInt8Number r, g, b;} Scanline_rgb8;
7301 typedef struct {cmsUInt16Number r, g, b;} Scanline_rgb0;
7305 void TitlePerformance(const char* Txt)
7307 printf("%-45s: ", Txt); fflush(stdout);
7311 void PrintPerformance(cmsUInt32Number Bytes, cmsUInt32Number SizeOfPixel, cmsFloat64Number diff)
7313 cmsFloat64Number seconds = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
7314 cmsFloat64Number mpix_sec = Bytes / (1024.0*1024.0*seconds*SizeOfPixel);
7316 printf("%g MPixel/sec.\n", mpix_sec);
7322 void SpeedTest16bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7325 cmsInt32Number r, g, b, j;
7327 cmsFloat64Number diff;
7328 cmsHTRANSFORM hlcmsxform;
7332 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7333 Die("Unable to open profiles");
7335 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_16,
7336 hlcmsProfileOut, TYPE_RGB_16, Intent, cmsFLAGS_NOCACHE);
7337 cmsCloseProfile(hlcmsProfileIn);
7338 cmsCloseProfile(hlcmsProfileOut);
7340 Mb = 256*256*256*sizeof(Scanline_rgb0);
7341 In = (Scanline_rgb0*) malloc(Mb);
7344 for (r=0; r < 256; r++)
7345 for (g=0; g < 256; g++)
7346 for (b=0; b < 256; b++) {
7348 In[j].r = (cmsUInt16Number) ((r << 8) | r);
7349 In[j].g = (cmsUInt16Number) ((g << 8) | g);
7350 In[j].b = (cmsUInt16Number) ((b << 8) | b);
7356 TitlePerformance(Title);
7360 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7362 diff = clock() - atime;
7365 PrintPerformance(Mb, sizeof(Scanline_rgb0), diff);
7366 cmsDeleteTransform(hlcmsxform);
7372 void SpeedTest16bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
7374 cmsInt32Number r, g, b, j;
7376 cmsFloat64Number diff;
7377 cmsHTRANSFORM hlcmsxform;
7381 if (hlcmsProfileOut == NULL || hlcmsProfileOut == NULL)
7382 Die("Unable to open profiles");
7384 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_16,
7385 hlcmsProfileOut, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
7386 cmsCloseProfile(hlcmsProfileIn);
7387 cmsCloseProfile(hlcmsProfileOut);
7389 Mb = 256*256*256*sizeof(Scanline_rgb2);
7391 In = (Scanline_rgb2*) malloc(Mb);
7394 for (r=0; r < 256; r++)
7395 for (g=0; g < 256; g++)
7396 for (b=0; b < 256; b++) {
7398 In[j].r = (cmsUInt16Number) ((r << 8) | r);
7399 In[j].g = (cmsUInt16Number) ((g << 8) | g);
7400 In[j].b = (cmsUInt16Number) ((b << 8) | b);
7407 TitlePerformance(Title);
7411 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7413 diff = clock() - atime;
7417 PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7419 cmsDeleteTransform(hlcmsxform);
7425 void SpeedTest8bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7427 cmsInt32Number r, g, b, j;
7429 cmsFloat64Number diff;
7430 cmsHTRANSFORM hlcmsxform;
7434 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7435 Die("Unable to open profiles");
7437 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_8,
7438 hlcmsProfileOut, TYPE_RGB_8, Intent, cmsFLAGS_NOCACHE);
7439 cmsCloseProfile(hlcmsProfileIn);
7440 cmsCloseProfile(hlcmsProfileOut);
7442 Mb = 256*256*256*sizeof(Scanline_rgb8);
7444 In = (Scanline_rgb8*) malloc(Mb);
7447 for (r=0; r < 256; r++)
7448 for (g=0; g < 256; g++)
7449 for (b=0; b < 256; b++) {
7451 In[j].r = (cmsUInt8Number) r;
7452 In[j].g = (cmsUInt8Number) g;
7453 In[j].b = (cmsUInt8Number) b;
7458 TitlePerformance(Title);
7462 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7464 diff = clock() - atime;
7468 PrintPerformance(Mb, sizeof(Scanline_rgb8), diff);
7470 cmsDeleteTransform(hlcmsxform);
7476 void SpeedTest8bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
7478 cmsInt32Number r, g, b, j;
7480 cmsFloat64Number diff;
7481 cmsHTRANSFORM hlcmsxform;
7485 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7486 Die("Unable to open profiles");
7488 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_8,
7489 hlcmsProfileOut, TYPE_CMYK_8, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
7490 cmsCloseProfile(hlcmsProfileIn);
7491 cmsCloseProfile(hlcmsProfileOut);
7493 Mb = 256*256*256*sizeof(Scanline_rgb2);
7495 In = (Scanline_rgb2*) malloc(Mb);
7498 for (r=0; r < 256; r++)
7499 for (g=0; g < 256; g++)
7500 for (b=0; b < 256; b++) {
7502 In[j].r = (cmsUInt8Number) r;
7503 In[j].g = (cmsUInt8Number) g;
7504 In[j].b = (cmsUInt8Number) b;
7505 In[j].a = (cmsUInt8Number) 0;
7510 TitlePerformance(Title);
7514 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7516 diff = clock() - atime;
7520 PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7523 cmsDeleteTransform(hlcmsxform);
7529 void SpeedTest8bitsGray(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7531 cmsInt32Number r, g, b, j;
7533 cmsFloat64Number diff;
7534 cmsHTRANSFORM hlcmsxform;
7539 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7540 Die("Unable to open profiles");
7542 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn,
7543 TYPE_GRAY_8, hlcmsProfileOut, TYPE_GRAY_8, Intent, cmsFLAGS_NOCACHE);
7544 cmsCloseProfile(hlcmsProfileIn);
7545 cmsCloseProfile(hlcmsProfileOut);
7548 In = (cmsUInt8Number*) malloc(Mb);
7551 for (r=0; r < 256; r++)
7552 for (g=0; g < 256; g++)
7553 for (b=0; b < 256; b++) {
7555 In[j] = (cmsUInt8Number) r;
7560 TitlePerformance(Title);
7564 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7566 diff = clock() - atime;
7569 PrintPerformance(Mb, sizeof(cmsUInt8Number), diff);
7570 cmsDeleteTransform(hlcmsxform);
7575 cmsHPROFILE CreateCurves(void)
7577 cmsToneCurve* Gamma = cmsBuildGamma(DbgThread(), 1.1);
7578 cmsToneCurve* Transfer[3];
7581 Transfer[0] = Transfer[1] = Transfer[2] = Gamma;
7582 h = cmsCreateLinearizationDeviceLink(cmsSigRgbData, Transfer);
7584 cmsFreeToneCurve(Gamma);
7591 void SpeedTest(void)
7593 printf("\n\nP E R F O R M A N C E T E S T S\n");
7594 printf( "=================================\n\n");
7597 SpeedTest16bits("16 bits on CLUT profiles",
7598 cmsOpenProfileFromFile("test5.icc", "r"),
7599 cmsOpenProfileFromFile("test3.icc", "r"), INTENT_PERCEPTUAL);
7601 SpeedTest8bits("8 bits on CLUT profiles",
7602 cmsOpenProfileFromFile("test5.icc", "r"),
7603 cmsOpenProfileFromFile("test3.icc", "r"),
7606 SpeedTest8bits("8 bits on Matrix-Shaper profiles",
7607 cmsOpenProfileFromFile("test5.icc", "r"),
7608 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7611 SpeedTest8bits("8 bits on SAME Matrix-Shaper profiles",
7612 cmsOpenProfileFromFile("test5.icc", "r"),
7613 cmsOpenProfileFromFile("test5.icc", "r"),
7616 SpeedTest8bits("8 bits on Matrix-Shaper profiles (AbsCol)",
7617 cmsOpenProfileFromFile("test5.icc", "r"),
7618 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7619 INTENT_ABSOLUTE_COLORIMETRIC);
7621 SpeedTest16bits("16 bits on Matrix-Shaper profiles",
7622 cmsOpenProfileFromFile("test5.icc", "r"),
7623 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7626 SpeedTest16bits("16 bits on SAME Matrix-Shaper profiles",
7627 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7628 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7631 SpeedTest16bits("16 bits on Matrix-Shaper profiles (AbsCol)",
7632 cmsOpenProfileFromFile("test5.icc", "r"),
7633 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7634 INTENT_ABSOLUTE_COLORIMETRIC);
7636 SpeedTest8bits("8 bits on curves",
7641 SpeedTest16bits("16 bits on curves",
7646 SpeedTest8bitsCMYK("8 bits on CMYK profiles",
7647 cmsOpenProfileFromFile("test1.icc", "r"),
7648 cmsOpenProfileFromFile("test2.icc", "r"));
7650 SpeedTest16bitsCMYK("16 bits on CMYK profiles",
7651 cmsOpenProfileFromFile("test1.icc", "r"),
7652 cmsOpenProfileFromFile("test2.icc", "r"));
7654 SpeedTest8bitsGray("8 bits on gray-to gray",
7655 cmsOpenProfileFromFile("gray3lcms2.icc", "r"),
7656 cmsOpenProfileFromFile("graylcms2.icc", "r"), INTENT_RELATIVE_COLORIMETRIC);
7658 SpeedTest8bitsGray("8 bits on gray-to-lab gray",
7659 cmsOpenProfileFromFile("graylcms2.icc", "r"),
7660 cmsOpenProfileFromFile("glablcms2.icc", "r"), INTENT_RELATIVE_COLORIMETRIC);
7662 SpeedTest8bitsGray("8 bits on SAME gray-to-gray",
7663 cmsOpenProfileFromFile("graylcms2.icc", "r"),
7664 cmsOpenProfileFromFile("graylcms2.icc", "r"), INTENT_PERCEPTUAL);
7668 // -----------------------------------------------------------------------------------------------------
7671 // Print the supported intents
7673 void PrintSupportedIntents(void)
7675 cmsUInt32Number n, i;
7676 cmsUInt32Number Codes[200];
7677 char* Descriptions[200];
7679 n = cmsGetSupportedIntents(200, Codes, Descriptions);
7681 printf("Supported intents:\n");
7682 for (i=0; i < n; i++) {
7683 printf("\t%d - %s\n", Codes[i], Descriptions[i]);
7688 // ZOO checks ------------------------------------------------------------------------------------------------------------
7691 #ifdef CMS_IS_WINDOWS_
7693 static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\";
7694 static char ZOOwrite[cmsMAX_PATH] = "c:\\colormaps\\write\\";
7695 static char ZOORawWrite[cmsMAX_PATH] = "c:\\colormaps\\rawwrite\\";
7698 // Read all tags on a profile given by its handle
7700 void ReadAllTags(cmsHPROFILE h)
7702 cmsInt32Number i, n;
7703 cmsTagSignature sig;
7705 n = cmsGetTagCount(h);
7706 for (i=0; i < n; i++) {
7708 sig = cmsGetTagSignature(h, i);
7709 if (cmsReadTag(h, sig) == NULL) return;
7714 // Read all tags on a profile given by its handle
7716 void ReadAllRAWTags(cmsHPROFILE h)
7718 cmsInt32Number i, n;
7719 cmsTagSignature sig;
7722 n = cmsGetTagCount(h);
7723 for (i=0; i < n; i++) {
7725 sig = cmsGetTagSignature(h, i);
7726 len = cmsReadRawTag(h, sig, NULL, 0);
7732 void PrintInfo(cmsHPROFILE h, cmsInfoType Info)
7736 cmsContext id = DbgThread();
7738 len = cmsGetProfileInfo(h, Info, "en", "US", NULL, 0);
7739 if (len == 0) return;
7741 text = _cmsMalloc(id, len);
7742 cmsGetProfileInfo(h, Info, "en", "US", text, len);
7744 wprintf(L"%s\n", text);
7750 void PrintAllInfos(cmsHPROFILE h)
7752 PrintInfo(h, cmsInfoDescription);
7753 PrintInfo(h, cmsInfoManufacturer);
7754 PrintInfo(h, cmsInfoModel);
7755 PrintInfo(h, cmsInfoCopyright);
7760 void ReadAllLUTS(cmsHPROFILE h)
7765 a = _cmsReadInputLUT(h, INTENT_PERCEPTUAL);
7766 if (a) cmsPipelineFree(a);
7768 a = _cmsReadInputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7769 if (a) cmsPipelineFree(a);
7771 a = _cmsReadInputLUT(h, INTENT_SATURATION);
7772 if (a) cmsPipelineFree(a);
7774 a = _cmsReadInputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7775 if (a) cmsPipelineFree(a);
7778 a = _cmsReadOutputLUT(h, INTENT_PERCEPTUAL);
7779 if (a) cmsPipelineFree(a);
7781 a = _cmsReadOutputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7782 if (a) cmsPipelineFree(a);
7784 a = _cmsReadOutputLUT(h, INTENT_SATURATION);
7785 if (a) cmsPipelineFree(a);
7787 a = _cmsReadOutputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7788 if (a) cmsPipelineFree(a);
7791 a = _cmsReadDevicelinkLUT(h, INTENT_PERCEPTUAL);
7792 if (a) cmsPipelineFree(a);
7794 a = _cmsReadDevicelinkLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7795 if (a) cmsPipelineFree(a);
7797 a = _cmsReadDevicelinkLUT(h, INTENT_SATURATION);
7798 if (a) cmsPipelineFree(a);
7800 a = _cmsReadDevicelinkLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7801 if (a) cmsPipelineFree(a);
7804 cmsDetectDestinationBlackPoint(&Black, h, INTENT_PERCEPTUAL, 0);
7805 cmsDetectDestinationBlackPoint(&Black, h, INTENT_RELATIVE_COLORIMETRIC, 0);
7806 cmsDetectDestinationBlackPoint(&Black, h, INTENT_SATURATION, 0);
7807 cmsDetectDestinationBlackPoint(&Black, h, INTENT_ABSOLUTE_COLORIMETRIC, 0);
7811 // Check one specimen in the ZOO
7814 cmsInt32Number CheckSingleSpecimen(const char* Profile)
7820 sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
7821 sprintf(BuffDst, "%s%s", ZOOwrite, Profile);
7823 h = cmsOpenProfileFromFile(BuffSrc, "r");
7824 if (h == NULL) return 0;
7826 printf("%s\n", Profile);
7829 // ReadAllRAWTags(h);
7832 cmsSaveProfileToFile(h, BuffDst);
7835 h = cmsOpenProfileFromFile(BuffDst, "r");
7836 if (h == NULL) return 0;
7846 cmsInt32Number CheckRAWSpecimen(const char* Profile)
7852 sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
7853 sprintf(BuffDst, "%s%s", ZOORawWrite, Profile);
7855 h = cmsOpenProfileFromFile(BuffSrc, "r");
7856 if (h == NULL) return 0;
7860 cmsSaveProfileToFile(h, BuffDst);
7863 h = cmsOpenProfileFromFile(BuffDst, "r");
7864 if (h == NULL) return 0;
7873 void CheckProfileZOO(void)
7876 struct _finddata_t c_file;
7879 cmsSetLogErrorHandler(NULL);
7881 if ( (hFile = _findfirst("c:\\colormaps\\*.*", &c_file)) == -1L )
7882 printf("No files in current directory");
7888 printf("%s\n", c_file.name);
7889 if (strcmp(c_file.name, ".") != 0 &&
7890 strcmp(c_file.name, "..") != 0) {
7892 CheckSingleSpecimen( c_file.name);
7893 CheckRAWSpecimen( c_file.name);
7895 if (TotalMemory > 0)
7896 printf("Ok, but %s are left!\n", MemStr(TotalMemory));
7902 } while ( _findnext(hFile, &c_file) == 0 );
7907 cmsSetLogErrorHandler(FatalErrorQuit);
7914 #define TYPE_709 709
7915 static double Rec709Math(int Type, const double Params[], double R)
7922 if (R <= (Params[3]*Params[4])) Fun = R / Params[3];
7923 else Fun = pow(((R - Params[2])/Params[1]), Params[0]);
7928 if (R <= Params[4]) Fun = R * Params[3];
7929 else Fun = Params[1] * pow(R, (1/Params[0])) + Params[2];
7936 // Add nonstandard TRC curves -> Rec709
7937 cmsPluginParametricCurves NewCurvePlugin = {
7938 { cmsPluginMagicNumber, 2000, cmsPluginParametricCurveSig, NULL },
7939 1, {TYPE_709}, {5}, Rec709Math};
7945 // ---------------------------------------------------------------------------------------
7947 int main(int argc, char* argv[])
7950 cmsInt32Number Exhaustive = 0;
7951 cmsInt32Number DoSpeedTests = 1;
7952 cmsInt32Number DoCheckTests = 1;
7955 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
7958 printf("LittleCMS %2.2f test bed %s %s\n\n", LCMS_VERSION / 1000.0, __DATE__, __TIME__);
7960 if ((argc == 2) && strcmp(argv[1], "--exhaustive") == 0) {
7963 printf("Running exhaustive tests (will take a while...)\n\n");
7966 printf("Installing debug memory plug-in ... ");
7967 cmsPlugin(&DebugMemHandler);
7970 printf("Installing error logger ... ");
7971 cmsSetLogErrorHandler(FatalErrorQuit);
7974 #ifdef CMS_IS_WINDOWS_
7975 // CheckProfileZOO();
7978 PrintSupportedIntents();
7982 // Create utility profiles
7983 Check("Creation of test profiles", CreateTestProfiles);
7987 Check("Base types", CheckBaseTypes);
7988 Check("endianess", CheckEndianess);
7989 Check("quick floor", CheckQuickFloor);
7990 Check("quick floor word", CheckQuickFloorWord);
7991 Check("Fixed point 15.16 representation", CheckFixedPoint15_16);
7992 Check("Fixed point 8.8 representation", CheckFixedPoint8_8);
7994 // Forward 1D interpolation
7995 Check("1D interpolation in 2pt tables", Check1DLERP2);
7996 Check("1D interpolation in 3pt tables", Check1DLERP3);
7997 Check("1D interpolation in 4pt tables", Check1DLERP4);
7998 Check("1D interpolation in 6pt tables", Check1DLERP6);
7999 Check("1D interpolation in 18pt tables", Check1DLERP18);
8000 Check("1D interpolation in descending 2pt tables", Check1DLERP2Down);
8001 Check("1D interpolation in descending 3pt tables", Check1DLERP3Down);
8002 Check("1D interpolation in descending 6pt tables", Check1DLERP6Down);
8003 Check("1D interpolation in descending 18pt tables", Check1DLERP18Down);
8007 Check("1D interpolation in n tables", ExhaustiveCheck1DLERP);
8008 Check("1D interpolation in descending tables", ExhaustiveCheck1DLERPDown);
8011 // Forward 3D interpolation
8012 Check("3D interpolation Tetrahedral (float) ", Check3DinterpolationFloatTetrahedral);
8013 Check("3D interpolation Trilinear (float) ", Check3DinterpolationFloatTrilinear);
8014 Check("3D interpolation Tetrahedral (16) ", Check3DinterpolationTetrahedral16);
8015 Check("3D interpolation Trilinear (16) ", Check3DinterpolationTrilinear16);
8019 Check("Exhaustive 3D interpolation Tetrahedral (float) ", ExaustiveCheck3DinterpolationFloatTetrahedral);
8020 Check("Exhaustive 3D interpolation Trilinear (float) ", ExaustiveCheck3DinterpolationFloatTrilinear);
8021 Check("Exhaustive 3D interpolation Tetrahedral (16) ", ExhaustiveCheck3DinterpolationTetrahedral16);
8022 Check("Exhaustive 3D interpolation Trilinear (16) ", ExhaustiveCheck3DinterpolationTrilinear16);
8025 Check("Reverse interpolation 3 -> 3", CheckReverseInterpolation3x3);
8026 Check("Reverse interpolation 4 -> 3", CheckReverseInterpolation4x3);
8029 // High dimensionality interpolation
8031 Check("3D interpolation", Check3Dinterp);
8032 Check("3D interpolation with granularity", Check3DinterpGranular);
8033 Check("4D interpolation", Check4Dinterp);
8034 Check("4D interpolation with granularity", Check4DinterpGranular);
8035 Check("5D interpolation with granularity", Check5DinterpGranular);
8036 Check("6D interpolation with granularity", Check6DinterpGranular);
8037 Check("7D interpolation with granularity", Check7DinterpGranular);
8038 Check("8D interpolation with granularity", Check8DinterpGranular);
8040 // Encoding of colorspaces
8041 Check("Lab to LCh and back (float only) ", CheckLab2LCh);
8042 Check("Lab to XYZ and back (float only) ", CheckLab2XYZ);
8043 Check("Lab to xyY and back (float only) ", CheckLab2xyY);
8044 Check("Lab V2 encoding", CheckLabV2encoding);
8045 Check("Lab V4 encoding", CheckLabV4encoding);
8048 Check("Blackbody radiator", CheckTemp2CHRM);
8051 Check("Linear gamma curves (16 bits)", CheckGammaCreation16);
8052 Check("Linear gamma curves (float)", CheckGammaCreationFlt);
8054 Check("Curve 1.8 (float)", CheckGamma18);
8055 Check("Curve 2.2 (float)", CheckGamma22);
8056 Check("Curve 3.0 (float)", CheckGamma30);
8058 Check("Curve 1.8 (table)", CheckGamma18Table);
8059 Check("Curve 2.2 (table)", CheckGamma22Table);
8060 Check("Curve 3.0 (table)", CheckGamma30Table);
8062 Check("Curve 1.8 (word table)", CheckGamma18TableWord);
8063 Check("Curve 2.2 (word table)", CheckGamma22TableWord);
8064 Check("Curve 3.0 (word table)", CheckGamma30TableWord);
8066 Check("Parametric curves", CheckParametricToneCurves);
8068 Check("Join curves", CheckJointCurves);
8069 Check("Join curves descending", CheckJointCurvesDescending);
8070 Check("Join curves degenerated", CheckReverseDegenerated);
8071 Check("Join curves sRGB (Float)", CheckJointFloatCurves_sRGB);
8072 Check("Join curves sRGB (16 bits)", CheckJoint16Curves_sRGB);
8073 Check("Join curves sigmoidal", CheckJointCurvesSShaped);
8076 Check("LUT creation & dup", CheckLUTcreation);
8077 Check("1 Stage LUT ", Check1StageLUT);
8078 Check("2 Stage LUT ", Check2StageLUT);
8079 Check("2 Stage LUT (16 bits)", Check2Stage16LUT);
8080 Check("3 Stage LUT ", Check3StageLUT);
8081 Check("3 Stage LUT (16 bits)", Check3Stage16LUT);
8082 Check("4 Stage LUT ", Check4StageLUT);
8083 Check("4 Stage LUT (16 bits)", Check4Stage16LUT);
8084 Check("5 Stage LUT ", Check5StageLUT);
8085 Check("5 Stage LUT (16 bits) ", Check5Stage16LUT);
8086 Check("6 Stage LUT ", Check6StageLUT);
8087 Check("6 Stage LUT (16 bits) ", Check6Stage16LUT);
8090 Check("Lab to Lab LUT (float only) ", CheckLab2LabLUT);
8091 Check("XYZ to XYZ LUT (float only) ", CheckXYZ2XYZLUT);
8092 Check("Lab to Lab MAT LUT (float only) ", CheckLab2LabMatLUT);
8093 Check("Named Color LUT", CheckNamedColorLUT);
8094 Check("Usual formatters", CheckFormatters16);
8095 Check("Floating point formatters", CheckFormattersFloat);
8097 #ifndef CMS_NO_HALF_SUPPORT
8098 Check("HALF formatters", CheckFormattersHalf);
8100 // ChangeBuffersFormat
8101 Check("ChangeBuffersFormat", CheckChangeBufferFormat);
8104 Check("Multilocalized Unicode", CheckMLU);
8107 Check("Named color lists", CheckNamedColorList);
8109 // Profile I/O (this one is huge!)
8110 Check("Profile creation", CheckProfileCreation);
8114 Check("Error reporting on bad profiles", CheckErrReportingOnBadProfiles);
8115 Check("Error reporting on bad transforms", CheckErrReportingOnBadTransforms);
8118 Check("Curves only transforms", CheckCurvesOnlyTransforms);
8119 Check("Float Lab->Lab transforms", CheckFloatLabTransforms);
8120 Check("Encoded Lab->Lab transforms", CheckEncodedLabTransforms);
8121 Check("Stored identities", CheckStoredIdentities);
8123 Check("Matrix-shaper transform (float)", CheckMatrixShaperXFORMFloat);
8124 Check("Matrix-shaper transform (16 bits)", CheckMatrixShaperXFORM16);
8125 Check("Matrix-shaper transform (8 bits)", CheckMatrixShaperXFORM8);
8127 Check("Primaries of sRGB", CheckRGBPrimaries);
8130 Check("Known values across matrix-shaper", Chack_sRGB_Float);
8131 Check("Gray input profile", CheckInputGray);
8132 Check("Gray Lab input profile", CheckLabInputGray);
8133 Check("Gray output profile", CheckOutputGray);
8134 Check("Gray Lab output profile", CheckLabOutputGray);
8136 Check("Matrix-shaper proofing transform (float)", CheckProofingXFORMFloat);
8137 Check("Matrix-shaper proofing transform (16 bits)", CheckProofingXFORM16);
8139 Check("Gamut check", CheckGamutCheck);
8141 Check("CMYK roundtrip on perceptual transform", CheckCMYKRoundtrip);
8143 Check("CMYK perceptual transform", CheckCMYKPerceptual);
8144 // Check("CMYK rel.col. transform", CheckCMYKRelCol);
8146 Check("Black ink only preservation", CheckKOnlyBlackPreserving);
8147 Check("Black plane preservation", CheckKPlaneBlackPreserving);
8150 Check("Deciding curve types", CheckV4gamma);
8152 Check("Black point detection", CheckBlackPoint);
8153 Check("TAC detection", CheckTAC);
8155 Check("CGATS parser", CheckCGATS);
8156 Check("PostScript generator", CheckPostScript);
8157 Check("Segment maxima GBD", CheckGBD);
8158 Check("MD5 digest", CheckMD5);
8159 Check("Linking", CheckLinking);
8160 Check("floating point tags on XYZ", CheckFloatXYZ);
8161 Check("RGB->Lab->RGB with alpha on FLT", ChecksRGB2LabFLT);
8168 DebugMemPrintTotals();
8170 cmsUnregisterPlugins();
8173 RemoveTestProfiles();