grub2: bring back build of aros-side grub2 tools
[AROS.git] / workbench / libs / lcms2 / testbed / testcms2.c
blob01deae27cb7de17592f621fe96a4794cf5261a6e
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2010 Marti Maria Saguer
5 //
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 //---------------------------------------------------------------------------------
27 #ifdef _MSC_VER
28 # define _CRT_SECURE_NO_WARNINGS 1
29 #endif
31 #include "lcms2_internal.h"
33 // On Visual Studio, use debug CRT
34 #ifdef _MSC_VER
35 # include "crtdbg.h"
36 # include <io.h>
37 #endif
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!
58 static
59 void Die(const char* Reason)
61 printf("\n\nArrrgggg!!: %s!\n\n", Reason);
62 fflush(stdout);
63 exit(1);
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.
75 typedef struct {
76 cmsUInt32Number KeepSize;
77 cmsContext WhoAllocated;
79 union {
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.
87 } alignment;
90 } _cmsMemoryBlock;
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()
98 static
99 cmsContext DbgThread(void)
101 static cmsUInt32Number n = 1;
103 return (cmsContext) n++;
106 // The allocate routine
107 static
108 void* DebugMalloc(cmsContext ContextID, cmsUInt32Number size)
110 _cmsMemoryBlock* blk;
112 if (size <= 0) {
113 Die("malloc requested with zero bytes");
116 TotalMemory += size;
118 if (TotalMemory > MaxAllocated)
119 MaxAllocated = TotalMemory;
121 if (size > SingleHit)
122 SingleHit = size;
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);
133 // The free routine
134 static
135 void DebugFree(cmsContext ContextID, void *Ptr)
137 _cmsMemoryBlock* blk;
139 if (Ptr == NULL) {
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");
150 free(blk);
153 // Reallocate, just a malloc, a copy and a free in this case.
154 static
155 void * DebugRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize)
157 _cmsMemoryBlock* blk;
158 void* NewPtr;
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);
169 return NewPtr;
172 // Let's know the totals
173 static
174 void DebugMemPrintTotals(void)
176 printf("[Memory statistics]\n");
177 printf("Allocated = %u MaxAlloc = %u Single block hit = %u\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 -------------------------------------------------------------------------------------
186 static
187 void FatalErrorQuit(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
189 Die(Text);
191 cmsUNUSED_PARAMETER(ContextID);
192 cmsUNUSED_PARAMETER(ErrorCode);
196 // Print a dot for gauging
197 static
198 void Dot(void)
200 fprintf(stdout, "."); fflush(stdout);
203 // Keep track of the reason to fail
204 static
205 void Fail(const char* frm, ...)
207 va_list args;
208 va_start(args, frm);
209 vsprintf(ReasonToFailBuffer, frm, args);
210 va_end(args);
213 // Keep track of subtest
214 static
215 void SubTest(const char* frm, ...)
217 va_list args;
219 Dot();
220 va_start(args, frm);
221 vsprintf(SubTestBuffer, frm, args);
222 va_end(args);
226 // Memory string
227 static
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));
235 else
236 if (size > 1024) {
237 sprintf(Buffer, "%g Kb", (cmsFloat64Number) size / 1024.0);
239 else
240 sprintf(Buffer, "%g bytes", (cmsFloat64Number) size);
242 return Buffer;
246 // The check framework
247 static
248 void Check(const char* Title, TestFn Fn)
250 printf("Checking %s ...", Title);
251 fflush(stdout);
253 ReasonToFailBuffer[0] = 0;
254 SubTestBuffer[0] = 0;
255 TrappedError = FALSE;
256 SimultaneousErrors = 0;
257 TotalTests++;
259 if (Fn() && !TrappedError) {
261 // It is a good place to check memory
262 if (TotalMemory > 0)
263 printf("Ok, but %s are left!\n", MemStr(TotalMemory));
264 else
265 printf("Ok.\n");
267 else {
268 printf("FAIL!\n");
270 if (SubTestBuffer[0])
271 printf("%s: [%s]\n\t%s\n", Title, SubTestBuffer, ReasonToFailBuffer);
272 else
273 printf("%s:\n\t%s\n", Title, ReasonToFailBuffer);
275 if (SimultaneousErrors > 1)
276 printf("\tMore than one (%d) errors were reported\n", SimultaneousErrors);
278 TotalFail++;
280 fflush(stdout);
283 // Dump a tone curve, for easy diagnostic
284 void DumpToneCurve(cmsToneCurve* gamma, const char* FileName)
286 cmsHANDLE hIT8;
287 cmsUInt32Number i;
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++) {
298 char Val[30];
300 sprintf(Val, "%u", 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);
307 cmsIT8Free(hIT8);
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"
316 static
317 cmsHPROFILE Create_AboveRGB(void)
319 cmsToneCurve* Curve[3];
320 cmsHPROFILE hProfile;
321 cmsCIExyY D65;
322 cmsCIExyYTRIPLE Primaries = {{0.64, 0.33, 1 },
323 {0.21, 0.71, 1 },
324 {0.15, 0.06, 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]);
332 return hProfile;
335 // A gamma-2.2 gray space
336 static
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);
346 return hProfile;
349 // A gamma-3.0 gray space
350 static
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);
360 return hProfile;
364 static
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);
375 return hProfile;
378 // A CMYK devicelink that adds gamma 3.0 to each channel
379 static
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;
387 Tab[0] = Curve;
388 Tab[1] = Curve;
389 Tab[2] = Curve;
390 Tab[3] = Curve;
392 hProfile = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigCmykData, Tab);
393 if (hProfile == NULL) return NULL;
395 cmsFreeToneCurve(Curve);
397 return hProfile;
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.
403 typedef struct {
405 cmsHTRANSFORM hLab2sRGB;
406 cmsHTRANSFORM sRGB2Lab;
407 cmsHTRANSFORM hIlimit;
409 } FakeCMYKParams;
411 static
412 cmsFloat64Number Clip(cmsFloat64Number v)
414 if (v < 0) return 0;
415 if (v > 1) return 1;
417 return v;
420 static
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);
429 c = 1 - rgb[0];
430 m = 1 - rgb[1];
431 y = 1 - rgb[2];
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.
439 cmyk[0] = c;
440 cmyk[1] = m;
441 cmyk[2] = y;
442 cmyk[3] = k;
444 cmsDoTransform(p ->hIlimit, cmyk, Out, 1);
446 return 1;
450 static
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];
456 c = In[0] / 65535.0;
457 m = In[1] / 65535.0;
458 y = In[2] / 65535.0;
459 k = In[3] / 65535.0;
461 if (k == 0) {
463 rgb[0] = Clip(1 - c);
464 rgb[1] = Clip(1 - m);
465 rgb[2] = Clip(1 - y);
467 else
468 if (k == 1) {
470 rgb[0] = rgb[1] = rgb[2] = 0;
472 else {
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);
480 return 1;
485 static
486 cmsHPROFILE CreateFakeCMYK(cmsFloat64Number InkLimit, cmsBool lUseAboveRGB)
488 cmsHPROFILE hICC;
489 cmsPipeline* AToB0, *BToA0;
490 cmsStage* CLUT;
491 cmsContext ContextID;
492 FakeCMYKParams p;
493 cmsHPROFILE hLab, hsRGB, hLimit;
494 cmsUInt32Number cmykfrm;
497 if (lUseAboveRGB)
498 hsRGB = Create_AboveRGB();
499 else
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);
557 return hICC;
561 // Does create several profiles for latter use------------------------------------------------------------------------------------------------
563 static
564 cmsInt32Number OneVirtual(cmsHPROFILE h, const char* SubTestTxt, const char* FileName)
566 SubTest(SubTestTxt);
567 if (h == NULL) return 0;
569 if (!cmsSaveProfileToFile(h, FileName)) return 0;
570 cmsCloseProfile(h);
572 h = cmsOpenProfileFromFile(FileName, "r");
573 if (h == NULL) return 0;
575 // Do some teste....
577 cmsCloseProfile(h);
579 return 1;
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
586 static
587 cmsInt32Number CreateTestProfiles(void)
589 cmsHPROFILE h;
591 h = cmsCreate_sRGBProfileTHR(DbgThread());
592 if (!OneVirtual(h, "sRGB profile", "sRGBlcms2.icc")) return 0;
594 // ----
596 h = Create_AboveRGB();
597 if (!OneVirtual(h, "aRGB profile", "aRGBlcms2.icc")) return 0;
599 // ----
601 h = Create_Gray22();
602 if (!OneVirtual(h, "Gray profile", "graylcms2.icc")) return 0;
604 // ----
606 h = Create_Gray30();
607 if (!OneVirtual(h, "Gray 3.0 profile", "gray3lcms2.icc")) return 0;
609 // ----
611 h = Create_GrayLab();
612 if (!OneVirtual(h, "Gray Lab profile", "glablcms2.icc")) return 0;
614 // ----
616 h = Create_CMYK_DeviceLink();
617 if (!OneVirtual(h, "Linearization profile", "linlcms2.icc")) return 0;
619 // -------
620 h = cmsCreateInkLimitingDeviceLinkTHR(DbgThread(), cmsSigCmykData, 150);
621 if (h == NULL) return 0;
622 if (!OneVirtual(h, "Ink-limiting profile", "limitlcms2.icc")) return 0;
624 // ------
626 h = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
627 if (!OneVirtual(h, "Lab 2 identity profile", "labv2lcms2.icc")) return 0;
629 // ----
631 h = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
632 if (!OneVirtual(h, "Lab 4 identity profile", "labv4lcms2.icc")) return 0;
634 // ----
636 h = cmsCreateXYZProfileTHR(DbgThread());
637 if (!OneVirtual(h, "XYZ identity profile", "xyzlcms2.icc")) return 0;
639 // ----
641 h = cmsCreateNULLProfileTHR(DbgThread());
642 if (!OneVirtual(h, "NULL profile", "nullcms2.icc")) return 0;
644 // ---
646 h = cmsCreateBCHSWabstractProfileTHR(DbgThread(), 17, 0, 0, 0, 0, 5000, 6000);
647 if (!OneVirtual(h, "BCHS profile", "bchslcms2.icc")) return 0;
649 // ---
651 h = CreateFakeCMYK(300, FALSE);
652 if (!OneVirtual(h, "Fake CMYK profile", "lcms2cmyk.icc")) return 0;
654 return 1;
657 static
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
680 static
681 cmsInt32Number CheckBaseTypes(void)
683 // Ignore warnings about conditional expression
684 #ifdef _MSC_VER
685 #pragma warning(disable: 4127)
686 #endif
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;
703 return 1;
706 // -------------------------------------------------------------------------------------------------
709 // Are we little or big endian? From Harbison&Steele.
710 static
711 cmsInt32Number CheckEndianess(void)
713 cmsInt32Number BigEndian, IsOk;
714 union {
715 long l;
716 char c[sizeof (long)];
717 } u;
719 u.l = 1;
720 BigEndian = (u.c[sizeof (long) - 1] == 1);
722 #ifdef CMS_USE_BIG_ENDIAN
723 IsOk = BigEndian;
724 #else
725 IsOk = !BigEndian;
726 #endif
728 if (!IsOk) {
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");
731 return 0;
734 return 1;
737 // Check quick floor
738 static
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");
748 return 0;
752 return 1;
755 // Quick floor restricted to word
756 static
757 cmsInt32Number CheckQuickFloorWord(void)
759 cmsUInt32Number i;
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");
767 return 0;
771 return 1;
774 // -------------------------------------------------------------------------------------------------
776 // Precision stuff.
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;
790 static
791 cmsBool IsGoodVal(const char *title, cmsFloat64Number in, cmsFloat64Number out, cmsFloat64Number max)
793 cmsFloat64Number Err = fabs(in - out);
795 if (Err > MaxErr) MaxErr = Err;
797 if ((Err > max )) {
799 Fail("(%s): Must be %f, But is %f ", title, in, out);
800 return FALSE;
803 return TRUE;
806 static
807 cmsBool IsGoodFixed15_16(const char *title, cmsFloat64Number in, cmsFloat64Number out)
809 return IsGoodVal(title, in, out, FIXED_PRECISION_15_16);
812 static
813 cmsBool IsGoodFixed8_8(const char *title, cmsFloat64Number in, cmsFloat64Number out)
815 return IsGoodVal(title, in, out, FIXED_PRECISION_8_8);
818 static
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);
824 return FALSE;
827 return TRUE;
830 static
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);
836 return FALSE;
839 return TRUE;
842 // Fixed point ----------------------------------------------------------------------------------------------
844 static
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);
854 static
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;
868 return 1;
871 static
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);
881 static
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;
891 return 1;
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.
902 static
903 void BuildTable(cmsInt32Number n, cmsUInt16Number Tab[], cmsBool Descending)
905 cmsInt32Number i;
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
920 static
921 cmsInt32Number Check1D(cmsInt32Number nNodesToCheck, cmsBool Down, cmsInt32Number max_err)
923 cmsUInt32Number i;
924 cmsUInt16Number in, out;
925 cmsInterpParams* p;
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;
939 out = 0;
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);
949 free(Tab);
950 return 0;
954 _cmsFreeInterpParams(p);
955 free(Tab);
956 return 1;
960 static
961 cmsInt32Number Check1DLERP2(void)
963 return Check1D(2, FALSE, 0);
967 static
968 cmsInt32Number Check1DLERP3(void)
970 return Check1D(3, FALSE, 1);
974 static
975 cmsInt32Number Check1DLERP4(void)
977 return Check1D(4, FALSE, 0);
980 static
981 cmsInt32Number Check1DLERP6(void)
983 return Check1D(6, FALSE, 0);
986 static
987 cmsInt32Number Check1DLERP18(void)
989 return Check1D(18, FALSE, 0);
993 static
994 cmsInt32Number Check1DLERP2Down(void)
996 return Check1D(2, TRUE, 0);
1000 static
1001 cmsInt32Number Check1DLERP3Down(void)
1003 return Check1D(3, TRUE, 1);
1006 static
1007 cmsInt32Number Check1DLERP6Down(void)
1009 return Check1D(6, TRUE, 0);
1012 static
1013 cmsInt32Number Check1DLERP18Down(void)
1015 return Check1D(18, TRUE, 0);
1018 static
1019 cmsInt32Number ExhaustiveCheck1DLERP(void)
1021 cmsUInt32Number j;
1023 printf("\n");
1024 for (j=10; j <= 4096; j++) {
1026 if ((j % 10) == 0) printf("%u \r", j);
1028 if (!Check1D(j, FALSE, 1)) return 0;
1031 printf("\rResult is ");
1032 return 1;
1035 static
1036 cmsInt32Number ExhaustiveCheck1DLERPDown(void)
1038 cmsUInt32Number j;
1040 printf("\n");
1041 for (j=10; j <= 4096; j++) {
1043 if ((j % 10) == 0) printf("%u \r", j);
1045 if (!Check1D(j, TRUE, 1)) return 0;
1049 printf("\rResult is ");
1050 return 1;
1055 // 3D interpolation -------------------------------------------------------------------------------------------------
1057 static
1058 cmsInt32Number Check3DinterpolationFloatTetrahedral(void)
1060 cmsInterpParams* p;
1061 cmsInt32Number i;
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);
1082 MaxErr = 0.0;
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);
1096 return 1;
1098 Error:
1099 _cmsFreeInterpParams(p);
1100 return 0;
1103 static
1104 cmsInt32Number Check3DinterpolationFloatTrilinear(void)
1106 cmsInterpParams* p;
1107 cmsInt32Number i;
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);
1127 MaxErr = 0.0;
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);
1141 return 1;
1143 Error:
1144 _cmsFreeInterpParams(p);
1145 return 0;
1149 static
1150 cmsInt32Number Check3DinterpolationTetrahedral16(void)
1152 cmsInterpParams* p;
1153 cmsInt32Number i;
1154 cmsUInt16Number In[3], Out[3];
1155 cmsUInt16Number Table[] = {
1157 0, 0, 0,
1158 0, 0, 0xffff,
1160 0, 0xffff, 0,
1161 0, 0xffff, 0xffff,
1163 0xffff, 0, 0,
1164 0xffff, 0, 0xffff,
1166 0xffff, 0xffff, 0,
1167 0xffff, 0xffff, 0xffff
1170 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
1172 MaxErr = 0.0;
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);
1186 return 1;
1188 Error:
1189 _cmsFreeInterpParams(p);
1190 return 0;
1193 static
1194 cmsInt32Number Check3DinterpolationTrilinear16(void)
1196 cmsInterpParams* p;
1197 cmsInt32Number i;
1198 cmsUInt16Number In[3], Out[3];
1199 cmsUInt16Number Table[] = {
1201 0, 0, 0,
1202 0, 0, 0xffff,
1204 0, 0xffff, 0,
1205 0, 0xffff, 0xffff,
1207 0xffff, 0, 0,
1208 0xffff, 0, 0xffff,
1210 0xffff, 0xffff, 0,
1211 0xffff, 0xffff, 0xffff
1214 p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
1216 MaxErr = 0.0;
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);
1230 return 1;
1232 Error:
1233 _cmsFreeInterpParams(p);
1234 return 0;
1238 static
1239 cmsInt32Number ExaustiveCheck3DinterpolationFloatTetrahedral(void)
1241 cmsInterpParams* p;
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);
1262 MaxErr = 0.0;
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);
1282 return 1;
1284 Error:
1285 _cmsFreeInterpParams(p);
1286 return 0;
1289 static
1290 cmsInt32Number ExaustiveCheck3DinterpolationFloatTrilinear(void)
1292 cmsInterpParams* p;
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);
1313 MaxErr = 0.0;
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);
1333 return 1;
1335 Error:
1336 _cmsFreeInterpParams(p);
1337 return 0;
1341 static
1342 cmsInt32Number ExhaustiveCheck3DinterpolationTetrahedral16(void)
1344 cmsInterpParams* p;
1345 cmsInt32Number r, g, b;
1346 cmsUInt16Number In[3], Out[3];
1347 cmsUInt16Number Table[] = {
1349 0, 0, 0,
1350 0, 0, 0xffff,
1352 0, 0xffff, 0,
1353 0, 0xffff, 0xffff,
1355 0xffff, 0, 0,
1356 0xffff, 0, 0xffff,
1358 0xffff, 0xffff, 0,
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);
1381 return 1;
1383 Error:
1384 _cmsFreeInterpParams(p);
1385 return 0;
1388 static
1389 cmsInt32Number ExhaustiveCheck3DinterpolationTrilinear16(void)
1391 cmsInterpParams* p;
1392 cmsInt32Number r, g, b;
1393 cmsUInt16Number In[3], Out[3];
1394 cmsUInt16Number Table[] = {
1396 0, 0, 0,
1397 0, 0, 0xffff,
1399 0, 0xffff, 0,
1400 0, 0xffff, 0xffff,
1402 0xffff, 0, 0,
1403 0xffff, 0, 0xffff,
1405 0xffff, 0xffff, 0,
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);
1429 return 1;
1431 Error:
1432 _cmsFreeInterpParams(p);
1433 return 0;
1436 // Check reverse interpolation on LUTS. This is right now exclusively used by K preservation algorithm
1437 static
1438 cmsInt32Number CheckReverseInterpolation3x3(void)
1440 cmsPipeline* Lut;
1441 cmsStage* clut;
1442 cmsFloat32Number Target[3], Result[3], Hint[3];
1443 cmsFloat32Number err, max;
1444 cmsInt32Number i;
1445 cmsUInt16Number Table[] = {
1447 0, 0, 0, // 0 0 0
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");
1473 return 0;
1476 // Transverse identity
1477 max = 0;
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);
1496 static
1497 cmsInt32Number CheckReverseInterpolation4x3(void)
1499 cmsPipeline* Lut;
1500 cmsStage* clut;
1501 cmsFloat32Number Target[4], Result[4], Hint[4];
1502 cmsFloat32Number err, max;
1503 cmsInt32Number i;
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];
1545 Target[2] = 0;
1546 Target[3] = 12;
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");
1556 Target[0] = 0;
1557 Target[1] = 0;
1558 Target[2] = 0;
1560 // This one holds the fixed K
1561 Target[3] = 0;
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");
1571 return 0;
1574 SubTest("4->3 find CMY");
1575 max = 0;
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.
1597 static
1598 cmsUInt16Number Fn8D1(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1599 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1600 cmsUInt32Number m)
1602 return (cmsUInt16Number) ((a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8) / m);
1606 static
1607 cmsUInt16Number Fn8D2(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1608 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1609 cmsUInt32Number m)
1611 return (cmsUInt16Number) ((a1 + 3* a2 + 3* a3 + a4 + a5 + a6 + a7 + a8 ) / (m + 4));
1615 static
1616 cmsUInt16Number Fn8D3(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1617 cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1618 cmsUInt32Number m)
1620 return (cmsUInt16Number) ((3*a1 + 2*a2 + 3*a3 + a4 + a5 + a6 + a7 + a8) / (m + 5));
1626 static
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);
1636 return 1;
1638 cmsUNUSED_PARAMETER(Cargo);
1642 static
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);
1652 return 1;
1654 cmsUNUSED_PARAMETER(Cargo);
1657 static
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);
1667 return 1;
1669 cmsUNUSED_PARAMETER(Cargo);
1672 static
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);
1682 return 1;
1684 cmsUNUSED_PARAMETER(Cargo);
1687 static
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);
1697 return 1;
1699 cmsUNUSED_PARAMETER(Cargo);
1702 static
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);
1712 return 1;
1714 cmsUNUSED_PARAMETER(Cargo);
1717 static
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;
1736 return TRUE;
1739 static
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;
1758 return TRUE;
1761 static
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;
1781 return TRUE;
1784 static
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;
1805 return TRUE;
1809 static
1810 cmsBool CheckOne7D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2,
1811 cmsUInt16Number a3, cmsUInt16Number a4,
1812 cmsUInt16Number a5, cmsUInt16Number a6,
1813 cmsUInt16Number a7)
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;
1831 return TRUE;
1835 static
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;
1857 return TRUE;
1861 static
1862 cmsInt32Number Check3Dinterp(void)
1864 cmsPipeline* lut;
1865 cmsStage* mpe;
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);
1872 // Check accuracy
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);
1886 return 1;
1889 static
1890 cmsInt32Number Check3DinterpGranular(void)
1892 cmsPipeline* lut;
1893 cmsStage* mpe;
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);
1901 // Check accuracy
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);
1915 return 1;
1919 static
1920 cmsInt32Number Check4Dinterp(void)
1922 cmsPipeline* lut;
1923 cmsStage* mpe;
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);
1930 // Check accuracy
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);
1944 return 1;
1949 static
1950 cmsInt32Number Check4DinterpGranular(void)
1952 cmsPipeline* lut;
1953 cmsStage* mpe;
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);
1961 // Check accuracy
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);
1975 return 1;
1979 static
1980 cmsInt32Number Check5DinterpGranular(void)
1982 cmsPipeline* lut;
1983 cmsStage* mpe;
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);
1991 // Check accuracy
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);
2005 return 1;
2008 static
2009 cmsInt32Number Check6DinterpGranular(void)
2011 cmsPipeline* lut;
2012 cmsStage* mpe;
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);
2020 // Check accuracy
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);
2034 return 1;
2037 static
2038 cmsInt32Number Check7DinterpGranular(void)
2040 cmsPipeline* lut;
2041 cmsStage* mpe;
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);
2049 // Check accuracy
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);
2063 return 1;
2067 static
2068 cmsInt32Number Check8DinterpGranular(void)
2070 cmsPipeline* lut;
2071 cmsStage* mpe;
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);
2079 // Check accuracy
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);
2093 return 1;
2096 // Colorimetric conversions -------------------------------------------------------------------------------------------------
2098 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2099 static
2100 cmsInt32Number CheckLab2LCh(void)
2102 cmsInt32Number l, a, b;
2103 cmsFloat64Number dist, Max = 0;
2104 cmsCIELab Lab, Lab2;
2105 cmsCIELCh LCh;
2107 for (l=0; l <= 100; l += 10) {
2109 for (a=-128; a <= +128; a += 8) {
2111 for (b=-128; b <= 128; b += 8) {
2113 Lab.L = l;
2114 Lab.a = a;
2115 Lab.b = b;
2117 cmsLab2LCh(&LCh, &Lab);
2118 cmsLCh2Lab(&Lab2, &LCh);
2120 dist = cmsDeltaE(&Lab, &Lab2);
2121 if (dist > Max) Max = dist;
2126 return Max < 1E-12;
2129 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2130 static
2131 cmsInt32Number CheckLab2XYZ(void)
2133 cmsInt32Number l, a, b;
2134 cmsFloat64Number dist, Max = 0;
2135 cmsCIELab Lab, Lab2;
2136 cmsCIEXYZ XYZ;
2138 for (l=0; l <= 100; l += 10) {
2140 for (a=-128; a <= +128; a += 8) {
2142 for (b=-128; b <= 128; b += 8) {
2144 Lab.L = l;
2145 Lab.a = a;
2146 Lab.b = b;
2148 cmsLab2XYZ(NULL, &XYZ, &Lab);
2149 cmsXYZ2Lab(NULL, &Lab2, &XYZ);
2151 dist = cmsDeltaE(&Lab, &Lab2);
2152 if (dist > Max) Max = dist;
2158 return Max < 1E-12;
2161 // Lab to xyY and back should be performed at 1E-12 accuracy at least
2162 static
2163 cmsInt32Number CheckLab2xyY(void)
2165 cmsInt32Number l, a, b;
2166 cmsFloat64Number dist, Max = 0;
2167 cmsCIELab Lab, Lab2;
2168 cmsCIEXYZ XYZ;
2169 cmsCIExyY xyY;
2171 for (l=0; l <= 100; l += 10) {
2173 for (a=-128; a <= +128; a += 8) {
2175 for (b=-128; b <= 128; b += 8) {
2177 Lab.L = l;
2178 Lab.a = a;
2179 Lab.b = b;
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;
2193 return Max < 1E-12;
2197 static
2198 cmsInt32Number CheckLabV2encoding(void)
2200 cmsInt32Number n2, i, j;
2201 cmsUInt16Number Inw[3], aw[3];
2202 cmsCIELab Lab;
2204 n2=0;
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++) {
2215 if (aw[i] != j) {
2216 n2++;
2222 return (n2 == 0);
2225 static
2226 cmsInt32Number CheckLabV4encoding(void)
2228 cmsInt32Number n2, i, j;
2229 cmsUInt16Number Inw[3], aw[3];
2230 cmsCIELab Lab;
2232 n2=0;
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++) {
2243 if (aw[i] != j) {
2244 n2++;
2250 return (n2 == 0);
2254 // BlackBody -----------------------------------------------------------------------------------------------------
2256 static
2257 cmsInt32Number CheckTemp2CHRM(void)
2259 cmsInt32Number j;
2260 cmsFloat64Number d, v, Max = 0;
2261 cmsCIExyY White;
2263 for (j=4000; j < 25000; j++) {
2265 cmsWhitePointFromTemp(&White, j);
2266 if (!cmsTempFromWhitePoint(&v, &White)) return 0;
2268 d = fabs(v - j);
2269 if (d > Max) Max = d;
2272 // 100 degree is the actual resolution
2273 return (Max < 100);
2278 // Tone curves -----------------------------------------------------------------------------------------------------
2280 static
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;
2287 return 1;
2290 static
2291 cmsInt32Number CheckGammaCreation16(void)
2293 cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2294 cmsInt32Number i;
2295 cmsUInt16Number in, out;
2297 for (i=0; i < 0xffff; i++) {
2299 in = (cmsUInt16Number) i;
2300 out = cmsEvalToneCurve16(LinGamma, in);
2301 if (in != out) {
2302 Fail("(lin gamma): Must be %x, But is %x : ", in, out);
2303 cmsFreeToneCurve(LinGamma);
2304 return 0;
2308 if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2310 cmsFreeToneCurve(LinGamma);
2311 return 1;
2315 static
2316 cmsInt32Number CheckGammaCreationFlt(void)
2318 cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2319 cmsInt32Number i;
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);
2329 return 0;
2333 if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2334 cmsFreeToneCurve(LinGamma);
2335 return 1;
2338 // Curve curves using a single power function
2339 // Error is given in 0..ffff counts
2340 static
2341 cmsInt32Number CheckGammaFloat(cmsFloat64Number g)
2343 cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), g);
2344 cmsInt32Number i;
2345 cmsFloat32Number in, out;
2346 cmsFloat64Number val, Err;
2348 MaxErr = 0.0;
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);
2364 return 1;
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
2384 static
2385 cmsInt32Number CheckGammaFloatTable(cmsFloat64Number g)
2387 cmsFloat32Number Values[1025];
2388 cmsToneCurve* Curve;
2389 cmsInt32Number i;
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);
2401 MaxErr = 0.0;
2402 for (i=0; i <= 0xffff; i++) {
2404 in = (cmsFloat32Number) (i / 65535.0);
2405 out = cmsEvalToneCurveFloat(Curve, in);
2406 val = pow(in, g);
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);
2417 return 1;
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.
2437 static
2438 cmsInt32Number CheckGammaWordTable(cmsFloat64Number g)
2440 cmsUInt16Number Values[1025];
2441 cmsToneCurve* Curve;
2442 cmsInt32Number i;
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);
2454 MaxErr = 0.0;
2455 for (i=0; i <= 0xffff; i++) {
2457 in = (cmsFloat32Number) (i / 65535.0);
2458 out = cmsEvalToneCurveFloat(Curve, in);
2459 val = pow(in, g);
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);
2470 return 1;
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
2491 static
2492 cmsInt32Number CheckJointCurves(void)
2494 cmsToneCurve *Forward, *Reverse, *Result;
2495 cmsBool rc;
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);
2507 if (!rc)
2508 Fail("Joining same curve twice does not result in a linear ramp");
2510 return rc;
2514 // Create a gamma curve by cheating the table
2515 static
2516 cmsToneCurve* GammaTableLinear(cmsInt32Number nEntries, cmsBool Dir)
2518 cmsInt32Number i;
2519 cmsToneCurve* g = cmsBuildTabulatedToneCurve16(DbgThread(), nEntries, NULL);
2521 for (i=0; i < nEntries; i++) {
2523 cmsInt32Number v = _cmsQuantizeVal(i, nEntries);
2525 if (Dir)
2526 g->Table16[i] = (cmsUInt16Number) v;
2527 else
2528 g->Table16[i] = (cmsUInt16Number) (0xFFFF - v);
2531 return g;
2535 static
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);
2559 return rc;
2563 static
2564 cmsInt32Number CheckFToneCurvePoint(cmsToneCurve* c, cmsUInt16Number Point, cmsInt32Number Value)
2566 cmsInt32Number Result;
2568 Result = cmsEvalToneCurve16(c, Point);
2570 return (abs(Value - Result) < 2);
2573 static
2574 cmsInt32Number CheckReverseDegenerated(void)
2576 cmsToneCurve* p, *g;
2577 cmsUInt16Number Tab[16];
2579 Tab[0] = 0;
2580 Tab[1] = 0;
2581 Tab[2] = 0;
2582 Tab[3] = 0;
2583 Tab[4] = 0;
2584 Tab[5] = 0x5555;
2585 Tab[6] = 0x6666;
2586 Tab[7] = 0x7777;
2587 Tab[8] = 0x8888;
2588 Tab[9] = 0x9999;
2589 Tab[10]= 0xffff;
2590 Tab[11]= 0xffff;
2591 Tab[12]= 0xffff;
2592 Tab[13]= 0xffff;
2593 Tab[14]= 0xffff;
2594 Tab[15]= 0xffff;
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;
2606 // Last point
2607 if (!CheckFToneCurvePoint(g, 0xFFFF, 0xFFFF)) return 0;
2609 cmsFreeToneCurve(p);
2610 cmsFreeToneCurve(g);
2612 return 1;
2616 // Build a parametric sRGB-like curve
2617 static
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
2634 static
2635 cmsToneCurve* CombineGammaFloat(cmsToneCurve* g1, cmsToneCurve* g2)
2637 cmsUInt16Number Tab[256];
2638 cmsFloat32Number f;
2639 cmsInt32Number i;
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
2653 static
2654 cmsToneCurve* CombineGamma16(cmsToneCurve* g1, cmsToneCurve* g2)
2656 cmsUInt16Number Tab[256];
2658 cmsInt32Number i;
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);
2671 static
2672 cmsInt32Number CheckJointFloatCurves_sRGB(void)
2674 cmsToneCurve *Forward, *Reverse, *Result;
2675 cmsBool rc;
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);
2685 return rc;
2688 static
2689 cmsInt32Number CheckJoint16Curves_sRGB(void)
2691 cmsToneCurve *Forward, *Reverse, *Result;
2692 cmsBool rc;
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);
2702 return rc;
2705 // sigmoidal curve f(x) = (1-x^g) ^(1/g)
2707 static
2708 cmsInt32Number CheckJointCurvesSShaped(void)
2710 cmsFloat64Number p = 3.2;
2711 cmsToneCurve *Forward, *Reverse, *Result;
2712 cmsInt32Number rc;
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);
2723 return rc;
2727 // --------------------------------------------------------------------------------------------------------
2729 // Implementation of some tone curve functions
2730 static
2731 cmsFloat32Number Gamma(cmsFloat32Number x, const cmsFloat64Number Params[])
2733 return (cmsFloat32Number) pow(x, Params[0]);
2736 static
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];
2746 if (e > 0)
2747 Val = pow(e, Params[0]);
2748 else
2749 Val = 0;
2751 else
2752 Val = 0;
2754 return (cmsFloat32Number) Val;
2757 static
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];
2766 if (e > 0)
2767 Val = pow(e, Params[0]) + Params[3];
2768 else
2769 Val = 0;
2771 else
2772 Val = Params[3];
2774 return (cmsFloat32Number) Val;
2777 static
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];
2786 if (e > 0)
2787 Val = pow(e, Params[0]);
2788 else
2789 Val = 0;
2791 else
2792 Val = x * Params[3];
2794 return (cmsFloat32Number) Val;
2797 static
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];
2806 if (e > 0)
2807 Val = pow(e, Params[0]) + Params[5];
2808 else
2809 Val = 0;
2811 else
2812 Val = x*Params[3] + Params[6];
2814 return (cmsFloat32Number) Val;
2817 static
2818 cmsFloat32Number param_6(cmsFloat32Number x, const cmsFloat64Number Params[])
2820 cmsFloat64Number e, Val;
2822 e = Params[1]*x + Params[2];
2823 if (e > 0)
2824 Val = pow(e, Params[0]) + Params[3];
2825 else
2826 Val = 0;
2828 return (cmsFloat32Number) Val;
2831 static
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;
2843 static
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;
2854 static
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;
2865 static
2866 cmsBool CheckSingleParametric(const char* Name, dblfnptr fn, cmsInt32Number Type, const cmsFloat64Number Params[])
2868 cmsInt32Number i;
2869 cmsToneCurve* tc;
2870 cmsToneCurve* tc_1;
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))
2888 goto Error;
2890 sprintf(InverseText, "Inverse %s", Name);
2891 if (!IsGoodVal(InverseText, y_fn, y_param2, FIXED_PRECISION_15_16))
2892 goto Error;
2895 cmsFreeToneCurve(tc);
2896 cmsFreeToneCurve(tc_1);
2897 return TRUE;
2899 Error:
2900 cmsFreeToneCurve(tc);
2901 cmsFreeToneCurve(tc_1);
2902 return FALSE;
2905 // Check against some known values
2906 static
2907 cmsInt32Number CheckParametricToneCurves(void)
2909 cmsFloat64Number Params[10];
2911 // 1) X = Y ^ Gamma
2913 Params[0] = 2.2;
2915 if (!CheckSingleParametric("Gamma", Gamma, 1, Params)) return 0;
2917 // 2) CIE 122-1966
2918 // Y = (aX + b)^Gamma | X >= -b/a
2919 // Y = 0 | else
2921 Params[0] = 2.2;
2922 Params[1] = 1.5;
2923 Params[2] = -0.5;
2925 if (!CheckSingleParametric("CIE122-1966", CIE122, 2, Params)) return 0;
2927 // 3) IEC 61966-3
2928 // Y = (aX + b)^Gamma | X <= -b/a
2929 // Y = c | else
2931 Params[0] = 2.2;
2932 Params[1] = 1.5;
2933 Params[2] = -0.5;
2934 Params[3] = 0.3;
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
2941 // Y = cX | X < d
2943 Params[0] = 2.4;
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
2955 Params[0] = 2.2;
2956 Params[1] = 0.7;
2957 Params[2] = 0.2;
2958 Params[3] = 0.3;
2959 Params[4] = 0.1;
2960 Params[5] = 0.5;
2961 Params[6] = 0.2;
2963 if (!CheckSingleParametric("param_5", param_5, 5, Params)) return 0;
2965 // 6) Y = (aX + b) ^ Gamma + c
2967 Params[0] = 2.2;
2968 Params[1] = 0.7;
2969 Params[2] = 0.2;
2970 Params[3] = 0.3;
2972 if (!CheckSingleParametric("param_6", param_6, 6, Params)) return 0;
2974 // 7) Y = a * log (b * X^Gamma + c) + d
2976 Params[0] = 2.2;
2977 Params[1] = 0.9;
2978 Params[2] = 0.9;
2979 Params[3] = 0.02;
2980 Params[4] = 0.1;
2982 if (!CheckSingleParametric("param_7", param_7, 7, Params)) return 0;
2984 // 8) Y = a * b ^ (c*X+d) + e
2986 Params[0] = 0.9;
2987 Params[1] = 0.9;
2988 Params[2] = 1.02;
2989 Params[3] = 0.1;
2990 Params[4] = 0.2;
2992 if (!CheckSingleParametric("param_8", param_8, 8, Params)) return 0;
2994 // 108: S-Shaped: (1 - (1-x)^1/g)^1/g
2996 Params[0] = 1.9;
2997 if (!CheckSingleParametric("sigmoidal", sigmoidal, 108, Params)) return 0;
2999 // All OK
3001 return 1;
3004 // LUT checks ------------------------------------------------------------------------------
3006 static
3007 cmsInt32Number CheckLUTcreation(void)
3009 cmsPipeline* lut;
3010 cmsPipeline* lut2;
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
3025 static
3026 void AddIdentityMatrix(cmsPipeline* lut)
3028 const cmsFloat64Number Identity[] = { 1, 0, 0,
3029 0, 1, 0,
3030 0, 0, 1,
3031 0, 0, 0 };
3033 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocMatrix(DbgThread(), 3, 3, Identity, NULL));
3036 // Create a MPE for identity cmsFloat32Number CLUT
3037 static
3038 void AddIdentityCLUTfloat(cmsPipeline* lut)
3040 const cmsFloat32Number Table[] = {
3042 0, 0, 0,
3043 0, 0, 1.0,
3045 0, 1.0, 0,
3046 0, 1.0, 1.0,
3048 1.0, 0, 0,
3049 1.0, 0, 1.0,
3051 1.0, 1.0, 0,
3052 1.0, 1.0, 1.0
3055 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLutFloat(DbgThread(), 2, 3, 3, Table));
3058 // Create a MPE for identity cmsFloat32Number CLUT
3059 static
3060 void AddIdentityCLUT16(cmsPipeline* lut)
3062 const cmsUInt16Number Table[] = {
3064 0, 0, 0,
3065 0, 0, 0xffff,
3067 0, 0xffff, 0,
3068 0, 0xffff, 0xffff,
3070 0xffff, 0, 0,
3071 0xffff, 0, 0xffff,
3073 0xffff, 0xffff, 0,
3074 0xffff, 0xffff, 0xffff
3078 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table));
3082 // Create a 3 fn identity curves
3084 static
3085 void Add3GammaCurves(cmsPipeline* lut, cmsFloat64Number Curve)
3087 cmsToneCurve* id = cmsBuildGamma(DbgThread(), Curve);
3088 cmsToneCurve* id3[3];
3090 id3[0] = id;
3091 id3[1] = id;
3092 id3[2] = id;
3094 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, id3));
3096 cmsFreeToneCurve(id);
3100 static
3101 cmsInt32Number CheckFloatLUT(cmsPipeline* lut)
3103 cmsInt32Number n1, i, j;
3104 cmsFloat32Number Inf[3], Outf[3];
3106 n1=0;
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++) {
3121 if (af[i] != j) {
3122 n1++;
3128 return (n1 == 0);
3132 static
3133 cmsInt32Number Check16LUT(cmsPipeline* lut)
3135 cmsInt32Number n2, i, j;
3136 cmsUInt16Number Inw[3], Outw[3];
3138 n2=0;
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);
3146 aw[0] = Outw[0];
3147 aw[1] = Outw[1];
3148 aw[2] = Outw[2];
3150 for (i=0; i < 3; i++) {
3152 if (aw[i] != j) {
3153 n2++;
3159 return (n2 == 0);
3163 // Check any LUT that is linear
3164 static
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);
3178 static
3179 cmsInt32Number CheckFullLUT(cmsPipeline* lut, cmsInt32Number ExpectedStages)
3181 cmsInt32Number rc = CheckStagesLUT(lut, ExpectedStages) && Check16LUT(lut) && CheckFloatLUT(lut);
3183 cmsPipelineFree(lut);
3184 return rc;
3188 static
3189 cmsInt32Number Check1StageLUT(void)
3191 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3193 AddIdentityMatrix(lut);
3194 return CheckFullLUT(lut, 1);
3199 static
3200 cmsInt32Number Check2StageLUT(void)
3202 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3204 AddIdentityMatrix(lut);
3205 AddIdentityCLUTfloat(lut);
3207 return CheckFullLUT(lut, 2);
3210 static
3211 cmsInt32Number Check2Stage16LUT(void)
3213 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3215 AddIdentityMatrix(lut);
3216 AddIdentityCLUT16(lut);
3218 return CheckFullLUT(lut, 2);
3223 static
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);
3235 static
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);
3249 static
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);
3262 static
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);
3275 static
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);
3290 static
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);
3304 static
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);
3319 static
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);
3335 static
3336 cmsInt32Number CheckLab2LabLUT(void)
3338 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3339 cmsInt32Number rc;
3341 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3342 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3344 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3346 cmsPipelineFree(lut);
3348 return rc;
3352 static
3353 cmsInt32Number CheckXYZ2XYZLUT(void)
3355 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3356 cmsInt32Number rc;
3358 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3359 cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3361 rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3363 cmsPipelineFree(lut);
3365 return rc;
3370 static
3371 cmsInt32Number CheckLab2LabMatLUT(void)
3373 cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3374 cmsInt32Number rc;
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);
3384 return rc;
3387 static
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];
3395 char Name[255];
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;
3417 n2=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++) {
3426 if (Outw[i] != j) {
3427 n2++;
3433 cmsPipelineFree(lut);
3434 return (n2 == 0);
3439 // --------------------------------------------------------------------------------------------
3441 // A lightweight test of multilocalized unicode structures.
3443 static
3444 cmsInt32Number CheckMLU(void)
3446 cmsMLU* mlu, *mlu2, *mlu3;
3447 char Buffer[256], Buffer2[256];
3448 cmsInt32Number rc = 1;
3449 cmsInt32Number i;
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;
3479 if (rc == 0)
3480 Fail("Unexpected string '%s'", Buffer);
3482 // So far, so good.
3483 cmsMLUfree(mlu);
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++) {
3491 char Lang[3];
3493 Lang[0] = (char) (i % 255);
3494 Lang[1] = (char) (i / 255);
3495 Lang[2] = 0;
3497 sprintf(Buffer, "String #%i", i);
3498 cmsMLUsetASCII(mlu, Lang, Lang, Buffer);
3501 // Duplicate it
3502 mlu2 = cmsMLUdup(mlu);
3504 // Get rid of original
3505 cmsMLUfree(mlu);
3507 // Check all is still in place
3508 for (i=0; i < 4096; i++) {
3510 char Lang[3];
3512 Lang[0] = (char)(i % 255);
3513 Lang[1] = (char)(i / 255);
3514 Lang[2] = 0;
3516 cmsMLUgetASCII(mlu2, Lang, Lang, Buffer2, 256);
3517 sprintf(Buffer, "String #%i", i);
3519 if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
3522 if (rc == 0)
3523 Fail("Unexpected string '%s'", Buffer2);
3525 // Check profile IO
3527 h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "w");
3529 cmsSetProfileVersion(h, 4.3);
3531 cmsWriteTag(h, cmsSigProfileDescriptionTag, mlu2);
3532 cmsCloseProfile(h);
3533 cmsMLUfree(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++) {
3544 char Lang[3];
3546 Lang[0] = (char) (i % 255);
3547 Lang[1] = (char) (i / 255);
3548 Lang[2] = 0;
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);
3558 Error:
3560 if (h != NULL) cmsCloseProfile(h);
3561 remove("mlucheck.icc");
3563 return rc;
3567 // A lightweight test of named color structures.
3568 static
3569 cmsInt32Number CheckNamedColorList(void)
3571 cmsNAMEDCOLORLIST* nc = NULL, *nc2;
3572 cmsInt32Number i, j, rc=1;
3573 char Name[255];
3574 cmsUInt16Number PCS[3];
3575 cmsUInt16Number Colorant[cmsMAXCHANNELS];
3576 char CheckName[255];
3577 cmsUInt16Number CheckPCS[3];
3578 cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
3579 cmsHPROFILE h;
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;
3617 cmsCloseProfile(h);
3618 cmsFreeNamedColorList(nc);
3619 nc = NULL;
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; };
3650 cmsCloseProfile(h);
3651 remove("namedcol.icc");
3653 Error:
3654 if (nc != NULL) cmsFreeNamedColorList(nc);
3655 return rc;
3660 // ----------------------------------------------------------------------------------------------------------
3662 // Formatters
3664 static cmsBool FormatterFailed;
3666 static
3667 void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text)
3669 cmsUInt16Number Values[cmsMAXCHANNELS];
3670 cmsUInt8Number Buffer[1024];
3671 cmsFormatter f, b;
3672 cmsInt32Number i, j, nChannels, bytes;
3673 _cmsTRANSFORM info;
3675 // Already failed?
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;
3689 // Useful for debug
3690 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_16BITS);
3691 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS);
3692 return;
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);
3702 // For 8-bit
3703 if (bytes == 1)
3704 Values[i] <<= 8;
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++) {
3712 if (bytes == 1)
3713 Values[i] >>= 8;
3715 if (Values[i] != i+j) {
3717 Fail("%s failed", Text);
3718 FormatterFailed = TRUE;
3720 // Useful for debug
3721 for (i=0; i < nChannels; i++) {
3722 Values[i] = (cmsUInt16Number) (i+j);
3723 // For 8-bit
3724 if (bytes == 1)
3725 Values[i] <<= 8;
3728 b.Fmt16(&info, Values, Buffer, 1);
3729 f.Fmt16(&info, Values, Buffer, 1);
3730 return;
3736 #define C(a) CheckSingleFormatter16(a, #a)
3739 // Check all formatters
3740 static
3741 cmsInt32Number CheckFormatters16(void)
3743 FormatterFailed = FALSE;
3745 C( TYPE_GRAY_8 );
3746 C( TYPE_GRAY_8_REV );
3747 C( TYPE_GRAY_16 );
3748 C( TYPE_GRAY_16_REV );
3749 C( TYPE_GRAY_16_SE );
3750 C( TYPE_GRAYA_8 );
3751 C( TYPE_GRAYA_16 );
3752 C( TYPE_GRAYA_16_SE );
3753 C( TYPE_GRAYA_8_PLANAR );
3754 C( TYPE_GRAYA_16_PLANAR );
3755 C( TYPE_RGB_8 );
3756 C( TYPE_RGB_8_PLANAR );
3757 C( TYPE_BGR_8 );
3758 C( TYPE_BGR_8_PLANAR );
3759 C( TYPE_RGB_16 );
3760 C( TYPE_RGB_16_PLANAR );
3761 C( TYPE_RGB_16_SE );
3762 C( TYPE_BGR_16 );
3763 C( TYPE_BGR_16_PLANAR );
3764 C( TYPE_BGR_16_SE );
3765 C( TYPE_RGBA_8 );
3766 C( TYPE_RGBA_8_PLANAR );
3767 C( TYPE_RGBA_16 );
3768 C( TYPE_RGBA_16_PLANAR );
3769 C( TYPE_RGBA_16_SE );
3770 C( TYPE_ARGB_8 );
3771 C( TYPE_ARGB_8_PLANAR );
3772 C( TYPE_ARGB_16 );
3773 C( TYPE_ABGR_8 );
3774 C( TYPE_ABGR_8_PLANAR );
3775 C( TYPE_ABGR_16 );
3776 C( TYPE_ABGR_16_PLANAR );
3777 C( TYPE_ABGR_16_SE );
3778 C( TYPE_BGRA_8 );
3779 C( TYPE_BGRA_8_PLANAR );
3780 C( TYPE_BGRA_16 );
3781 C( TYPE_BGRA_16_SE );
3782 C( TYPE_CMY_8 );
3783 C( TYPE_CMY_8_PLANAR );
3784 C( TYPE_CMY_16 );
3785 C( TYPE_CMY_16_PLANAR );
3786 C( TYPE_CMY_16_SE );
3787 C( TYPE_CMYK_8 );
3788 C( TYPE_CMYKA_8 );
3789 C( TYPE_CMYK_8_REV );
3790 C( TYPE_YUVK_8 );
3791 C( TYPE_CMYK_8_PLANAR );
3792 C( TYPE_CMYK_16 );
3793 C( TYPE_CMYK_16_REV );
3794 C( TYPE_YUVK_16 );
3795 C( TYPE_CMYK_16_PLANAR );
3796 C( TYPE_CMYK_16_SE );
3797 C( TYPE_KYMC_8 );
3798 C( TYPE_KYMC_16 );
3799 C( TYPE_KYMC_16_SE );
3800 C( TYPE_KCMY_8 );
3801 C( TYPE_KCMY_8_REV );
3802 C( TYPE_KCMY_16 );
3803 C( TYPE_KCMY_16_REV );
3804 C( TYPE_KCMY_16_SE );
3805 C( TYPE_CMYK5_8 );
3806 C( TYPE_CMYK5_16 );
3807 C( TYPE_CMYK5_16_SE );
3808 C( TYPE_KYMC5_8 );
3809 C( TYPE_KYMC5_16 );
3810 C( TYPE_KYMC5_16_SE );
3811 C( TYPE_CMYK6_8 );
3812 C( TYPE_CMYK6_8_PLANAR );
3813 C( TYPE_CMYK6_16 );
3814 C( TYPE_CMYK6_16_PLANAR );
3815 C( TYPE_CMYK6_16_SE );
3816 C( TYPE_CMYK7_8 );
3817 C( TYPE_CMYK7_16 );
3818 C( TYPE_CMYK7_16_SE );
3819 C( TYPE_KYMC7_8 );
3820 C( TYPE_KYMC7_16 );
3821 C( TYPE_KYMC7_16_SE );
3822 C( TYPE_CMYK8_8 );
3823 C( TYPE_CMYK8_16 );
3824 C( TYPE_CMYK8_16_SE );
3825 C( TYPE_KYMC8_8 );
3826 C( TYPE_KYMC8_16 );
3827 C( TYPE_KYMC8_16_SE );
3828 C( TYPE_CMYK9_8 );
3829 C( TYPE_CMYK9_16 );
3830 C( TYPE_CMYK9_16_SE );
3831 C( TYPE_KYMC9_8 );
3832 C( TYPE_KYMC9_16 );
3833 C( TYPE_KYMC9_16_SE );
3834 C( TYPE_CMYK10_8 );
3835 C( TYPE_CMYK10_16 );
3836 C( TYPE_CMYK10_16_SE );
3837 C( TYPE_KYMC10_8 );
3838 C( TYPE_KYMC10_16 );
3839 C( TYPE_KYMC10_16_SE );
3840 C( TYPE_CMYK11_8 );
3841 C( TYPE_CMYK11_16 );
3842 C( TYPE_CMYK11_16_SE );
3843 C( TYPE_KYMC11_8 );
3844 C( TYPE_KYMC11_16 );
3845 C( TYPE_KYMC11_16_SE );
3846 C( TYPE_CMYK12_8 );
3847 C( TYPE_CMYK12_16 );
3848 C( TYPE_CMYK12_16_SE );
3849 C( TYPE_KYMC12_8 );
3850 C( TYPE_KYMC12_16 );
3851 C( TYPE_KYMC12_16_SE );
3852 C( TYPE_XYZ_16 );
3853 C( TYPE_Lab_8 );
3854 C( TYPE_ALab_8 );
3855 C( TYPE_Lab_16 );
3856 C( TYPE_Yxy_16 );
3857 C( TYPE_YCbCr_8 );
3858 C( TYPE_YCbCr_8_PLANAR );
3859 C( TYPE_YCbCr_16 );
3860 C( TYPE_YCbCr_16_PLANAR );
3861 C( TYPE_YCbCr_16_SE );
3862 C( TYPE_YUV_8 );
3863 C( TYPE_YUV_8_PLANAR );
3864 C( TYPE_YUV_16 );
3865 C( TYPE_YUV_16_PLANAR );
3866 C( TYPE_YUV_16_SE );
3867 C( TYPE_HLS_8 );
3868 C( TYPE_HLS_8_PLANAR );
3869 C( TYPE_HLS_16 );
3870 C( TYPE_HLS_16_PLANAR );
3871 C( TYPE_HLS_16_SE );
3872 C( TYPE_HSV_8 );
3873 C( TYPE_HSV_8_PLANAR );
3874 C( TYPE_HSV_16 );
3875 C( TYPE_HSV_16_PLANAR );
3876 C( TYPE_HSV_16_SE );
3878 C( TYPE_XYZ_FLT );
3879 C( TYPE_Lab_FLT );
3880 C( TYPE_GRAY_FLT );
3881 C( TYPE_RGB_FLT );
3882 C( TYPE_BGR_FLT );
3883 C( TYPE_CMYK_FLT );
3884 C( TYPE_LabA_FLT );
3885 C( TYPE_RGBA_FLT );
3886 C( TYPE_ARGB_FLT );
3887 C( TYPE_BGRA_FLT );
3888 C( TYPE_ABGR_FLT );
3891 C( TYPE_XYZ_DBL );
3892 C( TYPE_Lab_DBL );
3893 C( TYPE_GRAY_DBL );
3894 C( TYPE_RGB_DBL );
3895 C( TYPE_BGR_DBL );
3896 C( TYPE_CMYK_DBL );
3898 C( TYPE_LabV2_8 );
3899 C( TYPE_ALabV2_8 );
3900 C( TYPE_LabV2_16 );
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;
3916 #undef C
3918 static
3919 void CheckSingleFormatterFloat(cmsUInt32Number Type, const char* Text)
3921 cmsFloat32Number Values[cmsMAXCHANNELS];
3922 cmsUInt8Number Buffer[1024];
3923 cmsFormatter f, b;
3924 cmsInt32Number i, j, nChannels;
3925 _cmsTRANSFORM info;
3927 // Already failed?
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;
3941 // Useful for debug
3942 f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
3943 b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
3944 return;
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;
3968 // Useful for debug
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);
3975 return;
3981 #define C(a) CheckSingleFormatterFloat(a, #a)
3983 static
3984 cmsInt32Number CheckFormattersFloat(void)
3986 FormatterFailed = FALSE;
3988 C( TYPE_XYZ_FLT );
3989 C( TYPE_Lab_FLT );
3990 C( TYPE_GRAY_FLT );
3991 C( TYPE_RGB_FLT );
3992 C( TYPE_BGR_FLT );
3993 C( TYPE_CMYK_FLT );
3995 C( TYPE_LabA_FLT );
3996 C( TYPE_RGBA_FLT );
3998 C( TYPE_ARGB_FLT );
3999 C( TYPE_BGRA_FLT );
4000 C( TYPE_ABGR_FLT );
4002 C( TYPE_XYZ_DBL );
4003 C( TYPE_Lab_DBL );
4004 C( TYPE_GRAY_DBL );
4005 C( TYPE_RGB_DBL );
4006 C( TYPE_BGR_DBL );
4007 C( TYPE_CMYK_DBL );
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;
4023 #undef C
4025 #ifndef CMS_NO_HALF_SUPPORT
4027 // Check half float
4028 #define my_isfinite(x) ((x) != (x))
4029 static
4030 cmsInt32Number CheckFormattersHalf(void)
4032 int i, j;
4035 for (i=0; i < 0xffff; i++) {
4037 cmsFloat32Number f = _cmsHalf2Float((cmsUInt16Number) i);
4039 if (!my_isfinite(f)) {
4041 j = _cmsFloat2Half(f);
4043 if (i != j) {
4044 Fail("%d != %d in Half float support!\n", i, j);
4045 return 0;
4050 return 1;
4053 #endif
4055 static
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];
4061 RGB[0] = R;
4062 RGB[1] = G;
4063 RGB[2] = B;
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
4073 static
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];
4079 RGB[0] = R;
4080 RGB[1] = G;
4081 RGB[2] = B;
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);
4091 static
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);
4120 return 1;
4124 // Write tag testbed ----------------------------------------------------------------------------------------
4126 static
4127 cmsInt32Number CheckXYZ(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4129 cmsCIEXYZ XYZ, *Pt;
4132 switch (Pass) {
4134 case 1:
4136 XYZ.X = 1.0; XYZ.Y = 1.1; XYZ.Z = 1.2;
4137 return cmsWriteTag(hProfile, tag, &XYZ);
4139 case 2:
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);
4146 default:
4147 return 0;
4152 static
4153 cmsInt32Number CheckGamma(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4155 cmsToneCurve *g, *Pt;
4156 cmsInt32Number rc;
4158 switch (Pass) {
4160 case 1:
4162 g = cmsBuildGamma(DbgThread(), 1.0);
4163 rc = cmsWriteTag(hProfile, tag, g);
4164 cmsFreeToneCurve(g);
4165 return rc;
4167 case 2:
4168 Pt = cmsReadTag(hProfile, tag);
4169 if (Pt == NULL) return 0;
4170 return cmsIsToneCurveLinear(Pt);
4172 default:
4173 return 0;
4177 static
4178 cmsInt32Number CheckText(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4180 cmsMLU *m, *Pt;
4181 cmsInt32Number rc;
4182 char Buffer[256];
4185 switch (Pass) {
4187 case 1:
4188 m = cmsMLUalloc(DbgThread(), 0);
4189 cmsMLUsetASCII(m, cmsNoLanguage, cmsNoCountry, "Test test");
4190 rc = cmsWriteTag(hProfile, tag, m);
4191 cmsMLUfree(m);
4192 return rc;
4194 case 2:
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;
4200 default:
4201 return 0;
4205 static
4206 cmsInt32Number CheckData(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4208 cmsICCData *Pt;
4209 cmsICCData d = { 1, 0, { '?' }};
4210 cmsInt32Number rc;
4213 switch (Pass) {
4215 case 1:
4216 rc = cmsWriteTag(hProfile, tag, &d);
4217 return rc;
4219 case 2:
4220 Pt = cmsReadTag(hProfile, tag);
4221 if (Pt == NULL) return 0;
4222 return (Pt ->data[0] == '?') && (Pt ->flag == 0) && (Pt ->len == 1);
4224 default:
4225 return 0;
4230 static
4231 cmsInt32Number CheckSignature(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4233 cmsTagSignature *Pt, Holder;
4235 switch (Pass) {
4237 case 1:
4238 Holder = cmsSigPerceptualReferenceMediumGamut;
4239 return cmsWriteTag(hProfile, tag, &Holder);
4241 case 2:
4242 Pt = cmsReadTag(hProfile, tag);
4243 if (Pt == NULL) return 0;
4244 return *Pt == cmsSigPerceptualReferenceMediumGamut;
4246 default:
4247 return 0;
4252 static
4253 cmsInt32Number CheckDateTime(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4255 struct tm *Pt, Holder;
4257 switch (Pass) {
4259 case 1:
4261 Holder.tm_hour = 1;
4262 Holder.tm_min = 2;
4263 Holder.tm_sec = 3;
4264 Holder.tm_mday = 4;
4265 Holder.tm_mon = 5;
4266 Holder.tm_year = 2009 - 1900;
4267 return cmsWriteTag(hProfile, tag, &Holder);
4269 case 2:
4270 Pt = cmsReadTag(hProfile, tag);
4271 if (Pt == NULL) return 0;
4273 return (Pt ->tm_hour == 1 &&
4274 Pt ->tm_min == 2 &&
4275 Pt ->tm_sec == 3 &&
4276 Pt ->tm_mday == 4 &&
4277 Pt ->tm_mon == 5 &&
4278 Pt ->tm_year == 2009 - 1900);
4280 default:
4281 return 0;
4287 static
4288 cmsInt32Number CheckNamedColor(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag, cmsInt32Number max_check, cmsBool colorant_check)
4290 cmsNAMEDCOLORLIST* nc;
4291 cmsInt32Number i, j, rc;
4292 char Name[255];
4293 cmsUInt16Number PCS[3];
4294 cmsUInt16Number Colorant[cmsMAXCHANNELS];
4295 char CheckName[255];
4296 cmsUInt16Number CheckPCS[3];
4297 cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
4299 switch (Pass) {
4301 case 1:
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);
4317 return rc;
4319 case 2:
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; };
4347 return 1;
4350 default: return 0;
4355 static
4356 cmsInt32Number CheckLUT(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4358 cmsPipeline* Lut, *Pt;
4359 cmsInt32Number rc;
4362 switch (Pass) {
4364 case 1:
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);
4376 return rc;
4378 case 2:
4379 Pt = cmsReadTag(hProfile, tag);
4380 if (Pt == NULL) return 0;
4382 // Transform values, check for identity
4383 return Check16LUT(Pt);
4385 default:
4386 return 0;
4390 static
4391 cmsInt32Number CheckCHAD(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4393 cmsFloat64Number *Pt;
4394 cmsFloat64Number CHAD[] = { 0, .1, .2, .3, .4, .5, .6, .7, .8 };
4395 cmsInt32Number i;
4397 switch (Pass) {
4399 case 1:
4400 return cmsWriteTag(hProfile, tag, CHAD);
4403 case 2:
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;
4411 return 1;
4413 default:
4414 return 0;
4418 static
4419 cmsInt32Number CheckChromaticity(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4421 cmsCIExyYTRIPLE *Pt, c = { {0, .1, 1 }, { .3, .4, 1 }, { .6, .7, 1 }};
4423 switch (Pass) {
4425 case 1:
4426 return cmsWriteTag(hProfile, tag, &c);
4429 case 2:
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;
4439 return 1;
4441 default:
4442 return 0;
4447 static
4448 cmsInt32Number CheckColorantOrder(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4450 cmsUInt8Number *Pt, c[cmsMAXCHANNELS];
4451 cmsInt32Number i;
4453 switch (Pass) {
4455 case 1:
4456 for (i=0; i < cmsMAXCHANNELS; i++) c[i] = (cmsUInt8Number) (cmsMAXCHANNELS - i - 1);
4457 return cmsWriteTag(hProfile, tag, c);
4460 case 2:
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;
4467 return 1;
4469 default:
4470 return 0;
4474 static
4475 cmsInt32Number CheckMeasurement(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4477 cmsICCMeasurementConditions *Pt, m;
4479 switch (Pass) {
4481 case 1:
4482 m.Backing.X = 0.1;
4483 m.Backing.Y = 0.2;
4484 m.Backing.Z = 0.3;
4485 m.Flare = 1.0;
4486 m.Geometry = 1;
4487 m.IlluminantType = cmsILLUMINANT_TYPE_D50;
4488 m.Observer = 1;
4489 return cmsWriteTag(hProfile, tag, &m);
4492 case 2:
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;
4504 return 1;
4506 default:
4507 return 0;
4512 static
4513 cmsInt32Number CheckUcrBg(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4515 cmsUcrBg *Pt, m;
4516 cmsInt32Number rc;
4517 char Buffer[256];
4519 switch (Pass) {
4521 case 1:
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);
4527 cmsMLUfree(m.Desc);
4528 cmsFreeToneCurve(m.Bg);
4529 cmsFreeToneCurve(m.Ucr);
4530 return rc;
4533 case 2:
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;
4539 return 1;
4541 default:
4542 return 0;
4547 static
4548 cmsInt32Number CheckCRDinfo(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4550 cmsMLU *mlu;
4551 char Buffer[256];
4552 cmsInt32Number rc;
4554 switch (Pass) {
4556 case 1:
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);
4565 cmsMLUfree(mlu);
4566 return rc;
4569 case 2:
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;
4593 return 1;
4595 default:
4596 return 0;
4601 static
4602 cmsToneCurve *CreateSegmentedCurve(void)
4604 cmsCurveSegment Seg[3];
4605 cmsFloat32Number Sampled[2] = { 0, 1};
4607 Seg[0].Type = 6;
4608 Seg[0].Params[0] = 1;
4609 Seg[0].Params[1] = 0;
4610 Seg[0].Params[2] = 0;
4611 Seg[0].Params[3] = 0;
4612 Seg[0].x0 = -1E22F;
4613 Seg[0].x1 = 0;
4615 Seg[1].Type = 0;
4616 Seg[1].nGridPoints = 2;
4617 Seg[1].SampledPoints = Sampled;
4618 Seg[1].x0 = 0;
4619 Seg[1].x1 = 1;
4621 Seg[2].Type = 6;
4622 Seg[2].Params[0] = 1;
4623 Seg[2].Params[1] = 0;
4624 Seg[2].Params[2] = 0;
4625 Seg[2].Params[3] = 0;
4626 Seg[2].x0 = 1;
4627 Seg[2].x1 = 1E22F;
4629 return cmsBuildSegmentedToneCurve(DbgThread(), 3, Seg);
4633 static
4634 cmsInt32Number CheckMPE(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4636 cmsPipeline* Lut, *Pt;
4637 cmsToneCurve* G[3];
4638 cmsInt32Number rc;
4640 switch (Pass) {
4642 case 1:
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);
4656 return rc;
4658 case 2:
4659 Pt = cmsReadTag(hProfile, tag);
4660 if (Pt == NULL) return 0;
4661 return CheckFloatLUT(Pt);
4663 default:
4664 return 0;
4669 static
4670 cmsInt32Number CheckScreening(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4672 cmsScreening *Pt, sc;
4673 cmsInt32Number rc;
4675 switch (Pass) {
4677 case 1:
4679 sc.Flag = 0;
4680 sc.nChannels = 1;
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);
4686 return rc;
4689 case 2:
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;
4698 return 1;
4700 default:
4701 return 0;
4706 static
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;
4721 return TRUE;
4725 static
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);
4734 static
4735 cmsInt32Number CheckProfileSequenceTag(cmsInt32Number Pass, cmsHPROFILE hProfile)
4737 cmsSEQ* s;
4738 cmsInt32Number i;
4740 switch (Pass) {
4742 case 1:
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;
4758 #else
4759 s ->seq[0].attributes = cmsTransparency|cmsMatte;
4760 #endif
4762 #ifdef CMS_DONT_USE_INT64
4763 s ->seq[1].attributes[0] = cmsReflective|cmsMatte;
4764 s ->seq[1].attributes[1] = 0;
4765 #else
4766 s ->seq[1].attributes = cmsReflective|cmsMatte;
4767 #endif
4769 #ifdef CMS_DONT_USE_INT64
4770 s ->seq[2].attributes[0] = cmsTransparency|cmsGlossy;
4771 s ->seq[2].attributes[1] = 0;
4772 #else
4773 s ->seq[2].attributes = cmsTransparency|cmsGlossy;
4774 #endif
4776 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, s)) return 0;
4777 cmsFreeProfileSequenceDescription(s);
4778 return 1;
4780 case 2:
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;
4790 #else
4791 if (s ->seq[0].attributes != (cmsTransparency|cmsMatte)) return 0;
4792 #endif
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;
4797 #else
4798 if (s ->seq[1].attributes != (cmsReflective|cmsMatte)) return 0;
4799 #endif
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;
4804 #else
4805 if (s ->seq[2].attributes != (cmsTransparency|cmsGlossy)) return 0;
4806 #endif
4808 // Check MLU
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;
4814 return 1;
4816 default:
4817 return 0;
4822 static
4823 cmsInt32Number CheckProfileSequenceIDTag(cmsInt32Number Pass, cmsHPROFILE hProfile)
4825 cmsSEQ* s;
4826 cmsInt32Number i;
4828 switch (Pass) {
4830 case 1:
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);
4846 return 1;
4848 case 2:
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;
4864 return 1;
4866 default:
4867 return 0;
4872 static
4873 cmsInt32Number CheckICCViewingConditions(cmsInt32Number Pass, cmsHPROFILE hProfile)
4875 cmsICCViewingConditions* v;
4876 cmsICCViewingConditions s;
4878 switch (Pass) {
4880 case 1:
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;
4890 return 1;
4892 case 2:
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;
4905 return 1;
4907 default:
4908 return 0;
4914 static
4915 cmsInt32Number CheckVCGT(cmsInt32Number Pass, cmsHPROFILE hProfile)
4917 cmsToneCurve* Curves[3];
4918 cmsToneCurve** PtrCurve;
4920 switch (Pass) {
4922 case 1:
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);
4930 return 1;
4933 case 2:
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;
4940 return 1;
4942 default:;
4945 return 0;
4949 // Only one of the two following may be used, as they share the same tag
4950 static
4951 cmsInt32Number CheckDictionary16(cmsInt32Number Pass, cmsHPROFILE hProfile)
4953 cmsHANDLE hDict;
4954 const cmsDICTentry* e;
4955 switch (Pass) {
4957 case 1:
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;
4964 cmsDictFree(hDict);
4965 return 1;
4968 case 2:
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;
4985 return 1;
4988 default:;
4991 return 0;
4996 static
4997 cmsInt32Number CheckDictionary24(cmsInt32Number Pass, cmsHPROFILE hProfile)
4999 cmsHANDLE hDict;
5000 const cmsDICTentry* e;
5001 cmsMLU* DisplayName;
5002 char Buffer[256];
5003 cmsInt32Number rc = 1;
5005 switch (Pass) {
5007 case 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;
5022 cmsDictFree(hDict);
5024 return 1;
5027 case 2:
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;
5054 if (rc == 0)
5055 Fail("Unexpected string '%s'", Buffer);
5056 return 1;
5058 default:;
5061 return 0;
5064 static
5065 cmsInt32Number CheckRAWtags(cmsInt32Number Pass, cmsHPROFILE hProfile)
5067 char Buffer[7];
5069 switch (Pass) {
5071 case 1:
5072 return cmsWriteRawTag(hProfile, 0x31323334, "data123", 7);
5074 case 2:
5075 if (!cmsReadRawTag(hProfile, 0x31323334, Buffer, 7)) return 0;
5077 if (strncmp(Buffer, "data123", 7) != 0) return 0;
5078 return 1;
5080 default:
5081 return 0;
5086 // This is a very big test that checks every single tag
5087 static
5088 cmsInt32Number CheckProfileCreation(void)
5090 cmsHPROFILE h;
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;
5229 if (Pass == 1) {
5230 cmsSaveProfileToFile(h, "alltags.icc");
5231 cmsCloseProfile(h);
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
5246 cmsCloseProfile(h);
5247 remove("alltags.icc");
5248 return 1;
5252 // Error reporting -------------------------------------------------------------------------------------------------------
5255 static
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);
5267 static
5268 cmsInt32Number CheckBadProfiles(void)
5270 cmsHPROFILE h;
5272 h = cmsOpenProfileFromFileTHR(DbgThread(), "IDoNotExist.icc", "r");
5273 if (h != NULL) {
5274 cmsCloseProfile(h);
5275 return 0;
5278 h = cmsOpenProfileFromFileTHR(DbgThread(), "IAmIllFormed*.icc", "r");
5279 if (h != NULL) {
5280 cmsCloseProfile(h);
5281 return 0;
5284 // No profile name given
5285 h = cmsOpenProfileFromFileTHR(DbgThread(), "", "r");
5286 if (h != NULL) {
5287 cmsCloseProfile(h);
5288 return 0;
5291 h = cmsOpenProfileFromFileTHR(DbgThread(), "..", "r");
5292 if (h != NULL) {
5293 cmsCloseProfile(h);
5294 return 0;
5297 h = cmsOpenProfileFromFileTHR(DbgThread(), "IHaveBadAccessMode.icc", "@");
5298 if (h != NULL) {
5299 cmsCloseProfile(h);
5300 return 0;
5303 h = cmsOpenProfileFromFileTHR(DbgThread(), "bad.icc", "r");
5304 if (h != NULL) {
5305 cmsCloseProfile(h);
5306 return 0;
5309 h = cmsOpenProfileFromFileTHR(DbgThread(), "toosmall.icc", "r");
5310 if (h != NULL) {
5311 cmsCloseProfile(h);
5312 return 0;
5315 h = cmsOpenProfileFromMemTHR(DbgThread(), NULL, 3);
5316 if (h != NULL) {
5317 cmsCloseProfile(h);
5318 return 0;
5321 h = cmsOpenProfileFromMemTHR(DbgThread(), "123", 3);
5322 if (h != NULL) {
5323 cmsCloseProfile(h);
5324 return 0;
5327 if (SimultaneousErrors != 9) return 0;
5329 return 1;
5333 static
5334 cmsInt32Number CheckErrReportingOnBadProfiles(void)
5336 cmsInt32Number rc;
5338 cmsSetLogErrorHandler(ErrorReportingFunction);
5339 rc = CheckBadProfiles();
5340 cmsSetLogErrorHandler(FatalErrorQuit);
5342 // Reset the error state
5343 TrappedError = FALSE;
5344 return rc;
5348 static
5349 cmsInt32Number CheckBadTransforms(void)
5351 cmsHPROFILE h1 = cmsCreate_sRGBProfile();
5352 cmsHTRANSFORM x1;
5354 x1 = cmsCreateTransform(NULL, 0, NULL, 0, 0, 0);
5355 if (x1 != NULL) {
5356 cmsDeleteTransform(x1);
5357 return 0;
5362 x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_RGB_8, 12345, 0);
5363 if (x1 != NULL) {
5364 cmsDeleteTransform(x1);
5365 return 0;
5368 x1 = cmsCreateTransform(h1, TYPE_CMYK_8, h1, TYPE_RGB_8, 0, 0);
5369 if (x1 != NULL) {
5370 cmsDeleteTransform(x1);
5371 return 0;
5374 x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_CMYK_8, 1, 0);
5375 if (x1 != NULL) {
5376 cmsDeleteTransform(x1);
5377 return 0;
5380 // sRGB does its output as XYZ!
5381 x1 = cmsCreateTransform(h1, TYPE_RGB_8, NULL, TYPE_Lab_8, 1, 0);
5382 if (x1 != NULL) {
5383 cmsDeleteTransform(x1);
5384 return 0;
5387 cmsCloseProfile(h1);
5392 cmsHPROFILE hp1 = cmsOpenProfileFromFile("test1.icc", "r");
5393 cmsHPROFILE hp2 = cmsCreate_sRGBProfile();
5395 x1 = cmsCreateTransform(hp1, TYPE_BGR_8, hp2, TYPE_BGR_8, INTENT_PERCEPTUAL, 0);
5397 cmsCloseProfile(hp1); cmsCloseProfile(hp2);
5398 if (x1 != NULL) {
5399 cmsDeleteTransform(x1);
5400 return 0;
5404 return 1;
5408 static
5409 cmsInt32Number CheckErrReportingOnBadTransforms(void)
5411 cmsInt32Number rc;
5413 cmsSetLogErrorHandler(ErrorReportingFunction);
5414 rc = CheckBadTransforms();
5415 cmsSetLogErrorHandler(FatalErrorQuit);
5417 // Reset the error state
5418 TrappedError = FALSE;
5419 return rc;
5425 // ---------------------------------------------------------------------------------------------------------
5427 // Check a linear xform
5428 static
5429 cmsInt32Number Check8linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5431 cmsInt32Number n2, i, j;
5432 cmsUInt8Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5434 n2=0;
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
5450 if (n2 > 2) {
5452 Fail("Differences too big (%x)", n2);
5453 return 0;
5456 return 1;
5459 static
5460 cmsInt32Number Compare8bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5462 cmsInt32Number n2, i, j;
5463 cmsUInt8Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5465 n2=0;
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
5482 if (n2 > 2) {
5484 Fail("Differences too big (%x)", n2);
5485 return 0;
5489 return 1;
5493 // Check a linear xform
5494 static
5495 cmsInt32Number Check16linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5497 cmsInt32Number n2, i, j;
5498 cmsUInt16Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5500 n2=0;
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
5516 if (n2 > 0x200) {
5518 Fail("Differences too big (%x)", n2);
5519 return 0;
5523 return 1;
5526 static
5527 cmsInt32Number Compare16bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5529 cmsInt32Number n2, i, j;
5530 cmsUInt16Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5532 n2=0;
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
5550 if (n2 > 0x200) {
5552 Fail("Differences too big (%x)", n2);
5553 return 0;
5557 return 1;
5561 // Check a linear xform
5562 static
5563 cmsInt32Number CheckFloatlinearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5565 cmsInt32Number i, j;
5566 cmsFloat32Number In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS];
5568 for (j=0; j < 0xFFFF; j++) {
5570 for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5572 cmsDoTransform(xform, In, Out, 1);
5574 for (i=0; i < nChan; i++) {
5576 // We allow no difference in floating point
5577 if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out[i], (cmsFloat32Number) (j / 65535.0)))
5578 return 0;
5582 return 1;
5586 // Check a linear xform
5587 static
5588 cmsInt32Number CompareFloatXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5590 cmsInt32Number i, j;
5591 cmsFloat32Number In[cmsMAXCHANNELS], Out1[cmsMAXCHANNELS], Out2[cmsMAXCHANNELS];
5593 for (j=0; j < 0xFFFF; j++) {
5595 for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5597 cmsDoTransform(xform1, In, Out1, 1);
5598 cmsDoTransform(xform2, In, Out2, 1);
5600 for (i=0; i < nChan; i++) {
5602 // We allow no difference in floating point
5603 if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out1[i], Out2[i]))
5604 return 0;
5609 return 1;
5613 // Curves only transforms ----------------------------------------------------------------------------------------
5615 static
5616 cmsInt32Number CheckCurvesOnlyTransforms(void)
5619 cmsHTRANSFORM xform1, xform2;
5620 cmsHPROFILE h1, h2, h3;
5621 cmsToneCurve* c1, *c2, *c3;
5622 cmsInt32Number rc = 1;
5625 c1 = cmsBuildGamma(DbgThread(), 2.2);
5626 c2 = cmsBuildGamma(DbgThread(), 1/2.2);
5627 c3 = cmsBuildGamma(DbgThread(), 4.84);
5629 h1 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c1);
5630 h2 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c2);
5631 h3 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c3);
5633 SubTest("Gray float optimizeable transform");
5634 xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h2, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5635 rc &= CheckFloatlinearXFORM(xform1, 1);
5636 cmsDeleteTransform(xform1);
5637 if (rc == 0) goto Error;
5639 SubTest("Gray 8 optimizeable transform");
5640 xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h2, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5641 rc &= Check8linearXFORM(xform1, 1);
5642 cmsDeleteTransform(xform1);
5643 if (rc == 0) goto Error;
5645 SubTest("Gray 16 optimizeable transform");
5646 xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h2, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5647 rc &= Check16linearXFORM(xform1, 1);
5648 cmsDeleteTransform(xform1);
5649 if (rc == 0) goto Error;
5651 SubTest("Gray float non-optimizeable transform");
5652 xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h1, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5653 xform2 = cmsCreateTransform(h3, TYPE_GRAY_FLT, NULL, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
5655 rc &= CompareFloatXFORM(xform1, xform2, 1);
5656 cmsDeleteTransform(xform1);
5657 cmsDeleteTransform(xform2);
5658 if (rc == 0) goto Error;
5660 SubTest("Gray 8 non-optimizeable transform");
5661 xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h1, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5662 xform2 = cmsCreateTransform(h3, TYPE_GRAY_8, NULL, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
5664 rc &= Compare8bitXFORM(xform1, xform2, 1);
5665 cmsDeleteTransform(xform1);
5666 cmsDeleteTransform(xform2);
5667 if (rc == 0) goto Error;
5670 SubTest("Gray 16 non-optimizeable transform");
5671 xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h1, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5672 xform2 = cmsCreateTransform(h3, TYPE_GRAY_16, NULL, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
5674 rc &= Compare16bitXFORM(xform1, xform2, 1);
5675 cmsDeleteTransform(xform1);
5676 cmsDeleteTransform(xform2);
5677 if (rc == 0) goto Error;
5679 Error:
5681 cmsCloseProfile(h1); cmsCloseProfile(h2); cmsCloseProfile(h3);
5682 cmsFreeToneCurve(c1); cmsFreeToneCurve(c2); cmsFreeToneCurve(c3);
5684 return rc;
5689 // Lab to Lab trivial transforms ----------------------------------------------------------------------------------------
5691 static cmsFloat64Number MaxDE;
5693 static
5694 cmsInt32Number CheckOneLab(cmsHTRANSFORM xform, cmsFloat64Number L, cmsFloat64Number a, cmsFloat64Number b)
5696 cmsCIELab In, Out;
5697 cmsFloat64Number dE;
5699 In.L = L; In.a = a; In.b = b;
5700 cmsDoTransform(xform, &In, &Out, 1);
5702 dE = cmsDeltaE(&In, &Out);
5704 if (dE > MaxDE) MaxDE = dE;
5706 if (MaxDE > 0.003) {
5707 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);
5708 cmsDoTransform(xform, &In, &Out, 1);
5709 return 0;
5712 return 1;
5715 // Check several Lab, slicing at non-exact values. Precision should be 16 bits. 50x50x50 checks aprox.
5716 static
5717 cmsInt32Number CheckSeveralLab(cmsHTRANSFORM xform)
5719 cmsInt32Number L, a, b;
5721 MaxDE = 0;
5722 for (L=0; L < 65536; L += 1311) {
5724 for (a = 0; a < 65536; a += 1232) {
5726 for (b = 0; b < 65536; b += 1111) {
5728 if (!CheckOneLab(xform, (L * 100.0) / 65535.0,
5729 (a / 257.0) - 128, (b / 257.0) - 128))
5730 return 0;
5736 return 1;
5740 static
5741 cmsInt32Number OneTrivialLab(cmsHPROFILE hLab1, cmsHPROFILE hLab2, const char* txt)
5743 cmsHTRANSFORM xform;
5744 cmsInt32Number rc;
5746 SubTest(txt);
5747 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5748 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5750 rc = CheckSeveralLab(xform);
5751 cmsDeleteTransform(xform);
5752 return rc;
5756 static
5757 cmsInt32Number CheckFloatLabTransforms(void)
5759 return OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab4/Lab4") &&
5760 OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab2/Lab2") &&
5761 OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab4/Lab2") &&
5762 OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab2/Lab4");
5766 static
5767 cmsInt32Number CheckEncodedLabTransforms(void)
5769 cmsHTRANSFORM xform;
5770 cmsUInt16Number In[3];
5771 cmsCIELab Lab;
5772 cmsCIELab White = { 100, 0, 0 };
5773 cmsHPROFILE hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5774 cmsHPROFILE hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5777 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5778 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5780 In[0] = 0xFFFF;
5781 In[1] = 0x8080;
5782 In[2] = 0x8080;
5784 cmsDoTransform(xform, In, &Lab, 1);
5786 if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5787 cmsDeleteTransform(xform);
5789 hLab1 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5790 hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5792 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_LabV2_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5793 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5796 In[0] = 0xFF00;
5797 In[1] = 0x8000;
5798 In[2] = 0x8000;
5800 cmsDoTransform(xform, In, &Lab, 1);
5802 if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5804 cmsDeleteTransform(xform);
5806 hLab2 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5807 hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5809 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_LabV2_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5810 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5812 Lab.L = 100;
5813 Lab.a = 0;
5814 Lab.b = 0;
5816 cmsDoTransform(xform, &Lab, In, 1);
5817 if (In[0] != 0xFF00 ||
5818 In[1] != 0x8000 ||
5819 In[2] != 0x8000) return 0;
5821 cmsDeleteTransform(xform);
5823 hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5824 hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5826 xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5827 cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5829 Lab.L = 100;
5830 Lab.a = 0;
5831 Lab.b = 0;
5833 cmsDoTransform(xform, &Lab, In, 1);
5835 if (In[0] != 0xFFFF ||
5836 In[1] != 0x8080 ||
5837 In[2] != 0x8080) return 0;
5839 cmsDeleteTransform(xform);
5841 return 1;
5844 static
5845 cmsInt32Number CheckStoredIdentities(void)
5847 cmsHPROFILE hLab, hLink, h4, h2;
5848 cmsHTRANSFORM xform;
5849 cmsInt32Number rc = 1;
5851 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5852 xform = cmsCreateTransformTHR(DbgThread(), hLab, TYPE_Lab_8, hLab, TYPE_Lab_8, 0, 0);
5854 hLink = cmsTransform2DeviceLink(xform, 3.4, 0);
5855 cmsSaveProfileToFile(hLink, "abstractv2.icc");
5856 cmsCloseProfile(hLink);
5858 hLink = cmsTransform2DeviceLink(xform, 4.3, 0);
5859 cmsSaveProfileToFile(hLink, "abstractv4.icc");
5860 cmsCloseProfile(hLink);
5862 cmsDeleteTransform(xform);
5863 cmsCloseProfile(hLab);
5865 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5867 xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5869 SubTest("V4");
5870 rc &= CheckSeveralLab(xform);
5872 cmsDeleteTransform(xform);
5873 cmsCloseProfile(h4);
5874 if (!rc) goto Error;
5877 SubTest("V2");
5878 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5880 xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5881 rc &= CheckSeveralLab(xform);
5882 cmsDeleteTransform(xform);
5883 cmsCloseProfile(h2);
5884 if (!rc) goto Error;
5887 SubTest("V2 -> V4");
5888 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5889 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5891 xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5892 rc &= CheckSeveralLab(xform);
5893 cmsDeleteTransform(xform);
5894 cmsCloseProfile(h2);
5895 cmsCloseProfile(h4);
5897 SubTest("V4 -> V2");
5898 h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5899 h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5901 xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5902 rc &= CheckSeveralLab(xform);
5903 cmsDeleteTransform(xform);
5904 cmsCloseProfile(h2);
5905 cmsCloseProfile(h4);
5907 Error:
5908 remove("abstractv2.icc");
5909 remove("abstractv4.icc");
5910 return rc;
5916 // Check a simple xform from a matrix profile to itself. Test floating point accuracy.
5917 static
5918 cmsInt32Number CheckMatrixShaperXFORMFloat(void)
5920 cmsHPROFILE hAbove, hSRGB;
5921 cmsHTRANSFORM xform;
5922 cmsInt32Number rc1, rc2;
5924 hAbove = Create_AboveRGB();
5925 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
5926 cmsCloseProfile(hAbove);
5927 rc1 = CheckFloatlinearXFORM(xform, 3);
5928 cmsDeleteTransform(xform);
5930 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5931 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_FLT, hSRGB, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
5932 cmsCloseProfile(hSRGB);
5933 rc2 = CheckFloatlinearXFORM(xform, 3);
5934 cmsDeleteTransform(xform);
5937 return rc1 && rc2;
5940 // Check a simple xform from a matrix profile to itself. Test 16 bits accuracy.
5941 static
5942 cmsInt32Number CheckMatrixShaperXFORM16(void)
5944 cmsHPROFILE hAbove, hSRGB;
5945 cmsHTRANSFORM xform;
5946 cmsInt32Number rc1, rc2;
5948 hAbove = Create_AboveRGB();
5949 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5950 cmsCloseProfile(hAbove);
5952 rc1 = Check16linearXFORM(xform, 3);
5953 cmsDeleteTransform(xform);
5955 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5956 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_16, hSRGB, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5957 cmsCloseProfile(hSRGB);
5958 rc2 = Check16linearXFORM(xform, 3);
5959 cmsDeleteTransform(xform);
5961 return rc1 && rc2;
5966 // Check a simple xform from a matrix profile to itself. Test 8 bits accuracy.
5967 static
5968 cmsInt32Number CheckMatrixShaperXFORM8(void)
5970 cmsHPROFILE hAbove, hSRGB;
5971 cmsHTRANSFORM xform;
5972 cmsInt32Number rc1, rc2;
5974 hAbove = Create_AboveRGB();
5975 xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_8, hAbove, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
5976 cmsCloseProfile(hAbove);
5977 rc1 = Check8linearXFORM(xform, 3);
5978 cmsDeleteTransform(xform);
5980 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5981 xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_8, hSRGB, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
5982 cmsCloseProfile(hSRGB);
5983 rc2 = Check8linearXFORM(xform, 3);
5984 cmsDeleteTransform(xform);
5987 return rc1 && rc2;
5991 // TODO: Check LUT based to LUT based transforms for CMYK
5998 // -----------------------------------------------------------------------------------------------------------------
6001 // Check known values going from sRGB to XYZ
6002 static
6003 cmsInt32Number CheckOneRGB_f(cmsHTRANSFORM xform, cmsInt32Number R, cmsInt32Number G, cmsInt32Number B, cmsFloat64Number X, cmsFloat64Number Y, cmsFloat64Number Z, cmsFloat64Number err)
6005 cmsFloat32Number RGB[3];
6006 cmsFloat64Number Out[3];
6008 RGB[0] = (cmsFloat32Number) (R / 255.0);
6009 RGB[1] = (cmsFloat32Number) (G / 255.0);
6010 RGB[2] = (cmsFloat32Number) (B / 255.0);
6012 cmsDoTransform(xform, RGB, Out, 1);
6014 return IsGoodVal("X", X , Out[0], err) &&
6015 IsGoodVal("Y", Y , Out[1], err) &&
6016 IsGoodVal("Z", Z , Out[2], err);
6019 static
6020 cmsInt32Number Chack_sRGB_Float(void)
6022 cmsHPROFILE hsRGB, hXYZ, hLab;
6023 cmsHTRANSFORM xform1, xform2;
6024 cmsInt32Number rc;
6027 hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
6028 hXYZ = cmsCreateXYZProfileTHR(DbgThread());
6029 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6031 xform1 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hXYZ, TYPE_XYZ_DBL,
6032 INTENT_RELATIVE_COLORIMETRIC, 0);
6034 xform2 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hLab, TYPE_Lab_DBL,
6035 INTENT_RELATIVE_COLORIMETRIC, 0);
6036 cmsCloseProfile(hsRGB);
6037 cmsCloseProfile(hXYZ);
6038 cmsCloseProfile(hLab);
6040 MaxErr = 0;
6042 // Xform 1 goes from 8 bits to XYZ,
6043 rc = CheckOneRGB_f(xform1, 1, 1, 1, 0.0002926, 0.00030352, 0.00025037, 0.0001);
6044 rc &= CheckOneRGB_f(xform1, 127, 127, 127, 0.2046329, 0.212230, 0.175069, 0.0001);
6045 rc &= CheckOneRGB_f(xform1, 12, 13, 15, 0.0038364, 0.0039928, 0.00385212, 0.0001);
6046 rc &= CheckOneRGB_f(xform1, 128, 0, 0, 0.0940846, 0.0480030, 0.00300543, 0.0001);
6047 rc &= CheckOneRGB_f(xform1, 190, 25, 210, 0.3203491, 0.1605240, 0.46817115, 0.0001);
6049 // Xform 2 goes from 8 bits to Lab, we allow 0.01 error max
6050 rc &= CheckOneRGB_f(xform2, 1, 1, 1, 0.2741748, 0, 0, 0.01);
6051 rc &= CheckOneRGB_f(xform2, 127, 127, 127, 53.192776, 0, 0, 0.01);
6052 rc &= CheckOneRGB_f(xform2, 190, 25, 210, 47.043171, 74.564576, -56.89373, 0.01);
6053 rc &= CheckOneRGB_f(xform2, 128, 0, 0, 26.158100, 48.474477, 39.425916, 0.01);
6055 cmsDeleteTransform(xform1);
6056 cmsDeleteTransform(xform2);
6057 return rc;
6061 // ---------------------------------------------------
6063 static
6064 cmsBool GetProfileRGBPrimaries(cmsHPROFILE hProfile,
6065 cmsCIEXYZTRIPLE *result,
6066 cmsUInt32Number intent)
6068 cmsHPROFILE hXYZ;
6069 cmsHTRANSFORM hTransform;
6070 cmsFloat64Number rgb[3][3] = {{1., 0., 0.},
6071 {0., 1., 0.},
6072 {0., 0., 1.}};
6074 hXYZ = cmsCreateXYZProfile();
6075 if (hXYZ == NULL) return FALSE;
6077 hTransform = cmsCreateTransform(hProfile, TYPE_RGB_DBL, hXYZ, TYPE_XYZ_DBL,
6078 intent, cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
6079 cmsCloseProfile(hXYZ);
6080 if (hTransform == NULL) return FALSE;
6082 cmsDoTransform(hTransform, rgb, result, 3);
6083 cmsDeleteTransform(hTransform);
6084 return TRUE;
6088 static
6089 int CheckRGBPrimaries(void)
6091 cmsHPROFILE hsRGB;
6092 cmsCIEXYZTRIPLE tripXYZ;
6093 cmsCIExyYTRIPLE tripxyY;
6094 cmsBool result;
6096 cmsSetAdaptationState(0);
6097 hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
6098 if (!hsRGB) return 0;
6100 result = GetProfileRGBPrimaries(hsRGB, &tripXYZ,
6101 INTENT_ABSOLUTE_COLORIMETRIC);
6103 cmsCloseProfile(hsRGB);
6104 if (!result) return 0;
6106 cmsXYZ2xyY(&tripxyY.Red, &tripXYZ.Red);
6107 cmsXYZ2xyY(&tripxyY.Green, &tripXYZ.Green);
6108 cmsXYZ2xyY(&tripxyY.Blue, &tripXYZ.Blue);
6110 /* valus were taken from
6111 http://en.wikipedia.org/wiki/RGB_color_spaces#Specifications */
6113 if (!IsGoodFixed15_16("xRed", tripxyY.Red.x, 0.64) ||
6114 !IsGoodFixed15_16("yRed", tripxyY.Red.y, 0.33) ||
6115 !IsGoodFixed15_16("xGreen", tripxyY.Green.x, 0.30) ||
6116 !IsGoodFixed15_16("yGreen", tripxyY.Green.y, 0.60) ||
6117 !IsGoodFixed15_16("xBlue", tripxyY.Blue.x, 0.15) ||
6118 !IsGoodFixed15_16("yBlue", tripxyY.Blue.y, 0.06)) {
6119 Fail("One or more primaries are wrong.");
6120 return FALSE;
6123 return TRUE;
6127 // -----------------------------------------------------------------------------------------------------------------
6129 // This function will check CMYK -> CMYK transforms. It uses FOGRA29 and SWOP ICC profiles
6131 static
6132 cmsInt32Number CheckCMYK(cmsInt32Number Intent, const char *Profile1, const char* Profile2)
6134 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), Profile1, "r");
6135 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), Profile2, "r");
6136 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6137 cmsFloat32Number CMYK1[4], CMYK2[4];
6138 cmsCIELab Lab1, Lab2;
6139 cmsHPROFILE hLab;
6140 cmsFloat64Number DeltaL, Max;
6141 cmsInt32Number i;
6143 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6145 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, Intent, 0);
6147 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6148 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6150 Max = 0;
6151 for (i=0; i <= 100; i++) {
6153 CMYK1[0] = 10;
6154 CMYK1[1] = 20;
6155 CMYK1[2] = 30;
6156 CMYK1[3] = (cmsFloat32Number) i;
6158 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6159 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6160 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6162 DeltaL = fabs(Lab1.L - Lab2.L);
6164 if (DeltaL > Max) Max = DeltaL;
6168 cmsDeleteTransform(xform);
6170 if (Max > 3.0) return 0;
6172 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, Intent, 0);
6174 Max = 0;
6176 for (i=0; i <= 100; i++) {
6177 CMYK1[0] = 10;
6178 CMYK1[1] = 20;
6179 CMYK1[2] = 30;
6180 CMYK1[3] = (cmsFloat32Number) i;
6182 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6183 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6184 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6186 DeltaL = fabs(Lab1.L - Lab2.L);
6188 if (DeltaL > Max) Max = DeltaL;
6192 cmsCloseProfile(hSWOP);
6193 cmsCloseProfile(hFOGRA);
6194 cmsCloseProfile(hLab);
6196 cmsDeleteTransform(xform);
6197 cmsDeleteTransform(swop_lab);
6198 cmsDeleteTransform(fogra_lab);
6200 return Max < 3.0;
6203 static
6204 cmsInt32Number CheckCMYKRoundtrip(void)
6206 return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test1.icc");
6210 static
6211 cmsInt32Number CheckCMYKPerceptual(void)
6213 return CheckCMYK(INTENT_PERCEPTUAL, "test1.icc", "test2.icc");
6218 static
6219 cmsInt32Number CheckCMYKRelCol(void)
6221 return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test2.icc");
6226 static
6227 cmsInt32Number CheckKOnlyBlackPreserving(void)
6229 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6230 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6231 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6232 cmsFloat32Number CMYK1[4], CMYK2[4];
6233 cmsCIELab Lab1, Lab2;
6234 cmsHPROFILE hLab;
6235 cmsFloat64Number DeltaL, Max;
6236 cmsInt32Number i;
6238 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6240 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6242 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6243 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6245 Max = 0;
6247 for (i=0; i <= 100; i++) {
6248 CMYK1[0] = 0;
6249 CMYK1[1] = 0;
6250 CMYK1[2] = 0;
6251 CMYK1[3] = (cmsFloat32Number) i;
6253 // SWOP CMYK to Lab1
6254 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6256 // SWOP To FOGRA using black preservation
6257 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6259 // Obtained FOGRA CMYK to Lab2
6260 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6262 // We care only on L*
6263 DeltaL = fabs(Lab1.L - Lab2.L);
6265 if (DeltaL > Max) Max = DeltaL;
6269 cmsDeleteTransform(xform);
6271 // dL should be below 3.0
6272 if (Max > 3.0) return 0;
6275 // Same, but FOGRA to SWOP
6276 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6278 Max = 0;
6280 for (i=0; i <= 100; i++) {
6281 CMYK1[0] = 0;
6282 CMYK1[1] = 0;
6283 CMYK1[2] = 0;
6284 CMYK1[3] = (cmsFloat32Number) i;
6286 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6287 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6288 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6290 DeltaL = fabs(Lab1.L - Lab2.L);
6292 if (DeltaL > Max) Max = DeltaL;
6296 cmsCloseProfile(hSWOP);
6297 cmsCloseProfile(hFOGRA);
6298 cmsCloseProfile(hLab);
6300 cmsDeleteTransform(xform);
6301 cmsDeleteTransform(swop_lab);
6302 cmsDeleteTransform(fogra_lab);
6304 return Max < 3.0;
6307 static
6308 cmsInt32Number CheckKPlaneBlackPreserving(void)
6310 cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6311 cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6312 cmsHTRANSFORM xform, swop_lab, fogra_lab;
6313 cmsFloat32Number CMYK1[4], CMYK2[4];
6314 cmsCIELab Lab1, Lab2;
6315 cmsHPROFILE hLab;
6316 cmsFloat64Number DeltaE, Max;
6317 cmsInt32Number i;
6319 hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6321 xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PERCEPTUAL, 0);
6323 swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6324 fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6326 Max = 0;
6328 for (i=0; i <= 100; i++) {
6329 CMYK1[0] = 0;
6330 CMYK1[1] = 0;
6331 CMYK1[2] = 0;
6332 CMYK1[3] = (cmsFloat32Number) i;
6334 cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6335 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6336 cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6338 DeltaE = cmsDeltaE(&Lab1, &Lab2);
6340 if (DeltaE > Max) Max = DeltaE;
6344 cmsDeleteTransform(xform);
6346 xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_PLANE_PERCEPTUAL, 0);
6348 for (i=0; i <= 100; i++) {
6349 CMYK1[0] = 30;
6350 CMYK1[1] = 20;
6351 CMYK1[2] = 10;
6352 CMYK1[3] = (cmsFloat32Number) i;
6354 cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6355 cmsDoTransform(xform, CMYK1, CMYK2, 1);
6356 cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6358 DeltaE = cmsDeltaE(&Lab1, &Lab2);
6360 if (DeltaE > Max) Max = DeltaE;
6363 cmsDeleteTransform(xform);
6367 cmsCloseProfile(hSWOP);
6368 cmsCloseProfile(hFOGRA);
6369 cmsCloseProfile(hLab);
6372 cmsDeleteTransform(swop_lab);
6373 cmsDeleteTransform(fogra_lab);
6375 return Max < 30.0;
6379 // ------------------------------------------------------------------------------------------------------
6382 static
6383 cmsInt32Number CheckProofingXFORMFloat(void)
6385 cmsHPROFILE hAbove;
6386 cmsHTRANSFORM xform;
6387 cmsInt32Number rc;
6389 hAbove = Create_AboveRGB();
6390 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
6391 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING);
6392 cmsCloseProfile(hAbove);
6393 rc = CheckFloatlinearXFORM(xform, 3);
6394 cmsDeleteTransform(xform);
6395 return rc;
6398 static
6399 cmsInt32Number CheckProofingXFORM16(void)
6401 cmsHPROFILE hAbove;
6402 cmsHTRANSFORM xform;
6403 cmsInt32Number rc;
6405 hAbove = Create_AboveRGB();
6406 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
6407 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING|cmsFLAGS_NOCACHE);
6408 cmsCloseProfile(hAbove);
6409 rc = Check16linearXFORM(xform, 3);
6410 cmsDeleteTransform(xform);
6411 return rc;
6415 static
6416 cmsInt32Number CheckGamutCheck(void)
6418 cmsHPROFILE hSRGB, hAbove;
6419 cmsHTRANSFORM xform;
6420 cmsInt32Number rc;
6421 cmsUInt16Number Alarm[3] = { 0xDEAD, 0xBABE, 0xFACE };
6423 // Set alarm codes to fancy values so we could check the out of gamut condition
6424 cmsSetAlarmCodes(Alarm);
6426 // Create the profiles
6427 hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
6428 hAbove = Create_AboveRGB();
6430 if (hSRGB == NULL || hAbove == NULL) return 0; // Failed
6432 SubTest("Gamut check on floating point");
6434 // Create a gamut checker in the same space. No value should be out of gamut
6435 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
6436 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6439 if (!CheckFloatlinearXFORM(xform, 3)) {
6440 cmsCloseProfile(hSRGB);
6441 cmsCloseProfile(hAbove);
6442 cmsDeleteTransform(xform);
6443 Fail("Gamut check on same profile failed");
6444 return 0;
6447 cmsDeleteTransform(xform);
6449 SubTest("Gamut check on 16 bits");
6451 xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
6452 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6454 cmsCloseProfile(hSRGB);
6455 cmsCloseProfile(hAbove);
6457 rc = Check16linearXFORM(xform, 3);
6459 cmsDeleteTransform(xform);
6461 return rc;
6466 // -------------------------------------------------------------------------------------------------------------------
6468 static
6469 cmsInt32Number CheckBlackPoint(void)
6471 cmsHPROFILE hProfile;
6472 cmsCIEXYZ Black;
6473 cmsCIELab Lab;
6475 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test5.icc", "r");
6476 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6477 cmsCloseProfile(hProfile);
6480 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6481 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6482 cmsXYZ2Lab(NULL, &Lab, &Black);
6483 cmsCloseProfile(hProfile);
6485 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "lcms2cmyk.icc", "r");
6486 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6487 cmsXYZ2Lab(NULL, &Lab, &Black);
6488 cmsCloseProfile(hProfile);
6490 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6491 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6492 cmsXYZ2Lab(NULL, &Lab, &Black);
6493 cmsCloseProfile(hProfile);
6495 hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6496 cmsDetectDestinationBlackPoint(&Black, hProfile, INTENT_PERCEPTUAL, 0);
6497 cmsXYZ2Lab(NULL, &Lab, &Black);
6498 cmsCloseProfile(hProfile);
6500 return 1;
6504 static
6505 cmsInt32Number CheckOneTAC(cmsFloat64Number InkLimit)
6507 cmsHPROFILE h;
6508 cmsFloat64Number d;
6510 h =CreateFakeCMYK(InkLimit, TRUE);
6511 cmsSaveProfileToFile(h, "lcmstac.icc");
6512 cmsCloseProfile(h);
6514 h = cmsOpenProfileFromFile("lcmstac.icc", "r");
6515 d = cmsDetectTAC(h);
6516 cmsCloseProfile(h);
6518 remove("lcmstac.icc");
6520 if (fabs(d - InkLimit) > 5) return 0;
6522 return 1;
6526 static
6527 cmsInt32Number CheckTAC(void)
6529 if (!CheckOneTAC(180)) return 0;
6530 if (!CheckOneTAC(220)) return 0;
6531 if (!CheckOneTAC(286)) return 0;
6532 if (!CheckOneTAC(310)) return 0;
6533 if (!CheckOneTAC(330)) return 0;
6535 return 1;
6538 // -------------------------------------------------------------------------------------------------------
6541 #define NPOINTS_IT8 10 // (17*17*17*17)
6543 static
6544 cmsInt32Number CheckCGATS(void)
6546 cmsHANDLE it8;
6547 cmsInt32Number i;
6549 SubTest("IT8 creation");
6550 it8 = cmsIT8Alloc(DbgThread());
6551 if (it8 == NULL) return 0;
6553 cmsIT8SetSheetType(it8, "LCMS/TESTING");
6554 cmsIT8SetPropertyStr(it8, "ORIGINATOR", "1 2 3 4");
6555 cmsIT8SetPropertyUncooked(it8, "DESCRIPTOR", "1234");
6556 cmsIT8SetPropertyStr(it8, "MANUFACTURER", "3");
6557 cmsIT8SetPropertyDbl(it8, "CREATED", 4);
6558 cmsIT8SetPropertyDbl(it8, "SERIAL", 5);
6559 cmsIT8SetPropertyHex(it8, "MATERIAL", 0x123);
6561 cmsIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", NPOINTS_IT8);
6562 cmsIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", 4);
6564 cmsIT8SetDataFormat(it8, 0, "SAMPLE_ID");
6565 cmsIT8SetDataFormat(it8, 1, "RGB_R");
6566 cmsIT8SetDataFormat(it8, 2, "RGB_G");
6567 cmsIT8SetDataFormat(it8, 3, "RGB_B");
6569 SubTest("Table creation");
6570 for (i=0; i < NPOINTS_IT8; i++) {
6572 char Patch[20];
6574 sprintf(Patch, "P%d", i);
6576 cmsIT8SetDataRowCol(it8, i, 0, Patch);
6577 cmsIT8SetDataRowColDbl(it8, i, 1, i);
6578 cmsIT8SetDataRowColDbl(it8, i, 2, i);
6579 cmsIT8SetDataRowColDbl(it8, i, 3, i);
6582 SubTest("Save to file");
6583 cmsIT8SaveToFile(it8, "TEST.IT8");
6584 cmsIT8Free(it8);
6586 SubTest("Load from file");
6587 it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6588 if (it8 == NULL) return 0;
6590 SubTest("Save again file");
6591 cmsIT8SaveToFile(it8, "TEST.IT8");
6592 cmsIT8Free(it8);
6595 SubTest("Load from file (II)");
6596 it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6597 if (it8 == NULL) return 0;
6600 SubTest("Change prop value");
6601 if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 1234) {
6603 return 0;
6607 cmsIT8SetPropertyDbl(it8, "DESCRIPTOR", 5678);
6608 if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 5678) {
6610 return 0;
6613 SubTest("Positive numbers");
6614 if (cmsIT8GetDataDbl(it8, "P3", "RGB_G") != 3) {
6616 return 0;
6620 SubTest("Positive exponent numbers");
6621 cmsIT8SetPropertyDbl(it8, "DBL_PROP", 123E+12);
6622 if ((cmsIT8GetPropertyDbl(it8, "DBL_PROP") - 123E+12) > 1 ) {
6624 return 0;
6627 SubTest("Negative exponent numbers");
6628 cmsIT8SetPropertyDbl(it8, "DBL_PROP_NEG", 123E-45);
6629 if ((cmsIT8GetPropertyDbl(it8, "DBL_PROP_NEG") - 123E-45) > 1E-45 ) {
6631 return 0;
6635 SubTest("Negative numbers");
6636 cmsIT8SetPropertyDbl(it8, "DBL_NEG_VAL", -123);
6637 if ((cmsIT8GetPropertyDbl(it8, "DBL_NEG_VAL")) != -123 ) {
6639 return 0;
6642 cmsIT8Free(it8);
6644 remove("TEST.IT8");
6645 return 1;
6650 // Create CSA/CRD
6652 static
6653 void GenerateCSA(const char* cInProf, const char* FileName)
6655 cmsHPROFILE hProfile;
6656 cmsUInt32Number n;
6657 char* Buffer;
6658 cmsContext BuffThread = DbgThread();
6659 FILE* o;
6662 if (cInProf == NULL)
6663 hProfile = cmsCreateLab4Profile(NULL);
6664 else
6665 hProfile = cmsOpenProfileFromFile(cInProf, "r");
6667 n = cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, NULL, 0);
6668 if (n == 0) return;
6670 Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6671 cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, Buffer, n);
6672 Buffer[n] = 0;
6674 if (FileName != NULL) {
6675 o = fopen(FileName, "wb");
6676 fwrite(Buffer, n, 1, o);
6677 fclose(o);
6680 _cmsFree(BuffThread, Buffer);
6681 cmsCloseProfile(hProfile);
6682 if (FileName != NULL)
6683 remove(FileName);
6687 static
6688 void GenerateCRD(const char* cOutProf, const char* FileName)
6690 cmsHPROFILE hProfile;
6691 cmsUInt32Number n;
6692 char* Buffer;
6693 cmsUInt32Number dwFlags = 0;
6694 cmsContext BuffThread = DbgThread();
6697 if (cOutProf == NULL)
6698 hProfile = cmsCreateLab4Profile(NULL);
6699 else
6700 hProfile = cmsOpenProfileFromFile(cOutProf, "r");
6702 n = cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, NULL, 0);
6703 if (n == 0) return;
6705 Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6706 cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, Buffer, n);
6707 Buffer[n] = 0;
6709 if (FileName != NULL) {
6710 FILE* o = fopen(FileName, "wb");
6711 fwrite(Buffer, n, 1, o);
6712 fclose(o);
6715 _cmsFree(BuffThread, Buffer);
6716 cmsCloseProfile(hProfile);
6717 if (FileName != NULL)
6718 remove(FileName);
6721 static
6722 cmsInt32Number CheckPostScript(void)
6724 GenerateCSA("test5.icc", "sRGB_CSA.ps");
6725 GenerateCSA("aRGBlcms2.icc", "aRGB_CSA.ps");
6726 GenerateCSA("test4.icc", "sRGBV4_CSA.ps");
6727 GenerateCSA("test1.icc", "SWOP_CSA.ps");
6728 GenerateCSA(NULL, "Lab_CSA.ps");
6729 GenerateCSA("graylcms2.icc", "gray_CSA.ps");
6731 GenerateCRD("test5.icc", "sRGB_CRD.ps");
6732 GenerateCRD("aRGBlcms2.icc", "aRGB_CRD.ps");
6733 GenerateCRD(NULL, "Lab_CRD.ps");
6734 GenerateCRD("test1.icc", "SWOP_CRD.ps");
6735 GenerateCRD("test4.icc", "sRGBV4_CRD.ps");
6736 GenerateCRD("graylcms2.icc", "gray_CRD.ps");
6738 return 1;
6742 static
6743 cmsInt32Number CheckGray(cmsHTRANSFORM xform, cmsUInt8Number g, double L)
6745 cmsCIELab Lab;
6747 cmsDoTransform(xform, &g, &Lab, 1);
6749 if (!IsGoodVal("a axis on gray", 0, Lab.a, 0.001)) return 0;
6750 if (!IsGoodVal("b axis on gray", 0, Lab.b, 0.001)) return 0;
6752 return IsGoodVal("Gray value", L, Lab.L, 0.01);
6755 static
6756 cmsInt32Number CheckInputGray(void)
6758 cmsHPROFILE hGray = Create_Gray22();
6759 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6760 cmsHTRANSFORM xform;
6762 if (hGray == NULL || hLab == NULL) return 0;
6764 xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
6765 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6767 if (!CheckGray(xform, 0, 0)) return 0;
6768 if (!CheckGray(xform, 125, 52.768)) return 0;
6769 if (!CheckGray(xform, 200, 81.069)) return 0;
6770 if (!CheckGray(xform, 255, 100.0)) return 0;
6772 cmsDeleteTransform(xform);
6773 return 1;
6776 static
6777 cmsInt32Number CheckLabInputGray(void)
6779 cmsHPROFILE hGray = Create_GrayLab();
6780 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6781 cmsHTRANSFORM xform;
6783 if (hGray == NULL || hLab == NULL) return 0;
6785 xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
6786 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6788 if (!CheckGray(xform, 0, 0)) return 0;
6789 if (!CheckGray(xform, 125, 49.019)) return 0;
6790 if (!CheckGray(xform, 200, 78.431)) return 0;
6791 if (!CheckGray(xform, 255, 100.0)) return 0;
6793 cmsDeleteTransform(xform);
6794 return 1;
6798 static
6799 cmsInt32Number CheckOutGray(cmsHTRANSFORM xform, double L, cmsUInt8Number g)
6801 cmsCIELab Lab;
6802 cmsUInt8Number g_out;
6804 Lab.L = L;
6805 Lab.a = 0;
6806 Lab.b = 0;
6808 cmsDoTransform(xform, &Lab, &g_out, 1);
6810 return IsGoodVal("Gray value", g, (double) g_out, 0.01);
6813 static
6814 cmsInt32Number CheckOutputGray(void)
6816 cmsHPROFILE hGray = Create_Gray22();
6817 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6818 cmsHTRANSFORM xform;
6820 if (hGray == NULL || hLab == NULL) return 0;
6822 xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0);
6823 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6825 if (!CheckOutGray(xform, 0, 0)) return 0;
6826 if (!CheckOutGray(xform, 100, 255)) return 0;
6828 if (!CheckOutGray(xform, 20, 52)) return 0;
6829 if (!CheckOutGray(xform, 50, 118)) return 0;
6832 cmsDeleteTransform(xform);
6833 return 1;
6837 static
6838 cmsInt32Number CheckLabOutputGray(void)
6840 cmsHPROFILE hGray = Create_GrayLab();
6841 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
6842 cmsHTRANSFORM xform;
6843 cmsInt32Number i;
6845 if (hGray == NULL || hLab == NULL) return 0;
6847 xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0);
6848 cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6850 if (!CheckOutGray(xform, 0, 0)) return 0;
6851 if (!CheckOutGray(xform, 100, 255)) return 0;
6853 for (i=0; i < 100; i++) {
6855 cmsUInt8Number g;
6857 g = (cmsUInt8Number) floor(i * 255.0 / 100.0 + 0.5);
6859 if (!CheckOutGray(xform, i, g)) return 0;
6863 cmsDeleteTransform(xform);
6864 return 1;
6868 static
6869 cmsInt32Number CheckV4gamma(void)
6871 cmsHPROFILE h;
6872 cmsUInt16Number Lin[] = {0, 0xffff};
6873 cmsToneCurve*g = cmsBuildTabulatedToneCurve16(DbgThread(), 2, Lin);
6875 h = cmsOpenProfileFromFileTHR(DbgThread(), "v4gamma.icc", "w");
6876 if (h == NULL) return 0;
6879 cmsSetProfileVersion(h, 4.3);
6881 if (!cmsWriteTag(h, cmsSigGrayTRCTag, g)) return 0;
6882 cmsCloseProfile(h);
6884 cmsFreeToneCurve(g);
6885 remove("v4gamma.icc");
6886 return 1;
6889 // cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname);
6891 // Gamut descriptor routines
6892 static
6893 cmsInt32Number CheckGBD(void)
6895 cmsCIELab Lab;
6896 cmsHANDLE h;
6897 cmsInt32Number L, a, b;
6898 cmsUInt32Number r1, g1, b1;
6899 cmsHPROFILE hLab, hsRGB;
6900 cmsHTRANSFORM xform;
6902 h = cmsGBDAlloc(DbgThread());
6903 if (h == NULL) return 0;
6905 // Fill all Lab gamut as valid
6906 SubTest("Filling RAW gamut");
6908 for (L=0; L <= 100; L += 10)
6909 for (a = -128; a <= 128; a += 5)
6910 for (b = -128; b <= 128; b += 5) {
6912 Lab.L = L;
6913 Lab.a = a;
6914 Lab.b = b;
6915 if (!cmsGDBAddPoint(h, &Lab)) return 0;
6918 // Complete boundaries
6919 SubTest("computing Lab gamut");
6920 if (!cmsGDBCompute(h, 0)) return 0;
6923 // All points should be inside gamut
6924 SubTest("checking Lab gamut");
6925 for (L=10; L <= 90; L += 25)
6926 for (a = -120; a <= 120; a += 25)
6927 for (b = -120; b <= 120; b += 25) {
6929 Lab.L = L;
6930 Lab.a = a;
6931 Lab.b = b;
6932 if (!cmsGDBCheckPoint(h, &Lab)) {
6933 return 0;
6936 cmsGBDFree(h);
6939 // Now for sRGB
6940 SubTest("checking sRGB gamut");
6941 h = cmsGBDAlloc(DbgThread());
6942 hsRGB = cmsCreate_sRGBProfile();
6943 hLab = cmsCreateLab4Profile(NULL);
6945 xform = cmsCreateTransform(hsRGB, TYPE_RGB_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE);
6946 cmsCloseProfile(hsRGB); cmsCloseProfile(hLab);
6949 for (r1=0; r1 < 256; r1 += 5) {
6950 for (g1=0; g1 < 256; g1 += 5)
6951 for (b1=0; b1 < 256; b1 += 5) {
6954 cmsUInt8Number rgb[3];
6956 rgb[0] = (cmsUInt8Number) r1;
6957 rgb[1] = (cmsUInt8Number) g1;
6958 rgb[2] = (cmsUInt8Number) b1;
6960 cmsDoTransform(xform, rgb, &Lab, 1);
6962 // if (fabs(Lab.b) < 20 && Lab.a > 0) continue;
6964 if (!cmsGDBAddPoint(h, &Lab)) {
6965 cmsGBDFree(h);
6966 return 0;
6974 if (!cmsGDBCompute(h, 0)) return 0;
6975 // cmsGBDdumpVRML(h, "c:\\colormaps\\lab.wrl");
6977 for (r1=10; r1 < 200; r1 += 10) {
6978 for (g1=10; g1 < 200; g1 += 10)
6979 for (b1=10; b1 < 200; b1 += 10) {
6982 cmsUInt8Number rgb[3];
6984 rgb[0] = (cmsUInt8Number) r1;
6985 rgb[1] = (cmsUInt8Number) g1;
6986 rgb[2] = (cmsUInt8Number) b1;
6988 cmsDoTransform(xform, rgb, &Lab, 1);
6989 if (!cmsGDBCheckPoint(h, &Lab)) {
6991 cmsDeleteTransform(xform);
6992 cmsGBDFree(h);
6993 return 0;
6999 cmsDeleteTransform(xform);
7000 cmsGBDFree(h);
7002 SubTest("checking LCh chroma ring");
7003 h = cmsGBDAlloc(DbgThread());
7006 for (r1=0; r1 < 360; r1++) {
7008 cmsCIELCh LCh;
7010 LCh.L = 70;
7011 LCh.C = 60;
7012 LCh.h = r1;
7014 cmsLCh2Lab(&Lab, &LCh);
7015 if (!cmsGDBAddPoint(h, &Lab)) {
7016 cmsGBDFree(h);
7017 return 0;
7022 if (!cmsGDBCompute(h, 0)) return 0;
7024 cmsGBDFree(h);
7026 return 1;
7030 static
7031 int CheckMD5(void)
7033 _cmsICCPROFILE* h;
7034 cmsHPROFILE pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r");
7035 cmsProfileID ProfileID1, ProfileID2, ProfileID3, ProfileID4;
7037 h =(_cmsICCPROFILE*) pProfile;
7038 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID1.ID8);
7039 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID2.ID8);
7041 cmsCloseProfile(pProfile);
7044 pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r");
7046 h =(_cmsICCPROFILE*) pProfile;
7047 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID3.ID8);
7048 if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID4.ID8);
7050 cmsCloseProfile(pProfile);
7052 return ((memcmp(ProfileID1.ID8, ProfileID3.ID8, sizeof(ProfileID1)) == 0) &&
7053 (memcmp(ProfileID2.ID8, ProfileID4.ID8, sizeof(ProfileID2)) == 0));
7058 static
7059 int CheckLinking(void)
7061 cmsHPROFILE h;
7062 cmsPipeline * pipeline;
7063 cmsStage *stageBegin, *stageEnd;
7065 // Create a CLUT based profile
7066 h = cmsCreateInkLimitingDeviceLinkTHR(DbgThread(), cmsSigCmykData, 150);
7068 // link a second tag
7069 cmsLinkTag(h, cmsSigAToB1Tag, cmsSigAToB0Tag);
7071 // Save the linked devicelink
7072 if (!cmsSaveProfileToFile(h, "lcms2link.icc")) return 0;
7073 cmsCloseProfile(h);
7075 // Now open the profile and read the pipeline
7076 h = cmsOpenProfileFromFile("lcms2link.icc", "r");
7077 if (h == NULL) return 0;
7079 pipeline = (cmsPipeline*) cmsReadTag(h, cmsSigAToB1Tag);
7080 if (pipeline == NULL)
7082 return 0;
7085 pipeline = cmsPipelineDup(pipeline);
7087 // extract stage from pipe line
7088 cmsPipelineUnlinkStage(pipeline, cmsAT_BEGIN, &stageBegin);
7089 cmsPipelineUnlinkStage(pipeline, cmsAT_END, &stageEnd);
7090 cmsPipelineInsertStage(pipeline, cmsAT_END, stageEnd);
7091 cmsPipelineInsertStage(pipeline, cmsAT_BEGIN, stageBegin);
7093 if (cmsTagLinkedTo(h, cmsSigAToB1Tag) != cmsSigAToB0Tag) return 0;
7095 cmsWriteTag(h, cmsSigAToB0Tag, pipeline);
7096 cmsPipelineFree(pipeline);
7098 if (!cmsSaveProfileToFile(h, "lcms2link2.icc")) return 0;
7099 cmsCloseProfile(h);
7102 return 1;
7106 // TestMPE
7108 // Created by Paul Miller on 30/08/2012.
7110 static
7111 cmsHPROFILE IdentityMatrixProfile( cmsColorSpaceSignature dataSpace)
7113 cmsContext ctx = 0;
7114 cmsVEC3 zero = {{0,0,0}};
7115 cmsMAT3 identity;
7116 cmsPipeline* forward;
7117 cmsPipeline* reverse;
7118 cmsHPROFILE identityProfile = cmsCreateProfilePlaceholder( ctx);
7121 cmsSetProfileVersion(identityProfile, 4.3);
7123 cmsSetDeviceClass( identityProfile, cmsSigColorSpaceClass);
7124 cmsSetColorSpace(identityProfile, dataSpace);
7125 cmsSetPCS(identityProfile, cmsSigXYZData);
7127 cmsSetHeaderRenderingIntent(identityProfile, INTENT_RELATIVE_COLORIMETRIC);
7129 cmsWriteTag(identityProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ());
7133 _cmsMAT3identity( &identity);
7135 // build forward transform.... (RGB to PCS)
7136 forward = cmsPipelineAlloc( 0, 3, 3);
7137 cmsPipelineInsertStage( forward, cmsAT_END, cmsStageAllocMatrix( ctx, 3, 3, (cmsFloat64Number*)&identity, (cmsFloat64Number*)&zero));
7138 cmsWriteTag( identityProfile, cmsSigDToB1Tag, forward);
7140 cmsPipelineFree( forward);
7142 reverse = cmsPipelineAlloc( 0, 3, 3);
7143 cmsPipelineInsertStage( reverse, cmsAT_END, cmsStageAllocMatrix( ctx, 3, 3, (cmsFloat64Number*)&identity, (cmsFloat64Number*)&zero));
7144 cmsWriteTag( identityProfile, cmsSigBToD1Tag, reverse);
7146 cmsPipelineFree( reverse);
7148 return identityProfile;
7151 static
7152 cmsInt32Number CheckFloatXYZ(void)
7154 cmsHPROFILE input;
7155 cmsHPROFILE xyzProfile = cmsCreateXYZProfile();
7156 cmsHTRANSFORM xform;
7157 cmsFloat32Number in[3];
7158 cmsFloat32Number out[3];
7160 in[0] = 1.0;
7161 in[1] = 1.0;
7162 in[2] = 1.0;
7164 // RGB to XYZ
7165 input = IdentityMatrixProfile( cmsSigRgbData);
7167 xform = cmsCreateTransform( input, TYPE_RGB_FLT, xyzProfile, TYPE_XYZ_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7168 cmsCloseProfile(input);
7170 cmsDoTransform( xform, in, out, 1);
7171 cmsDeleteTransform( xform);
7173 if (!IsGoodVal("Float RGB->XYZ", in[0], out[0], FLOAT_PRECISSION) ||
7174 !IsGoodVal("Float RGB->XYZ", in[1], out[1], FLOAT_PRECISSION) ||
7175 !IsGoodVal("Float RGB->XYZ", in[2], out[2], FLOAT_PRECISSION))
7176 return 0;
7179 // XYZ to XYZ
7180 input = IdentityMatrixProfile( cmsSigXYZData);
7182 xform = cmsCreateTransform( input, TYPE_XYZ_FLT, xyzProfile, TYPE_XYZ_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7183 cmsCloseProfile(input);
7185 cmsDoTransform( xform, in, out, 1);
7188 cmsDeleteTransform( xform);
7190 if (!IsGoodVal("Float XYZ->XYZ", in[0], out[0], FLOAT_PRECISSION) ||
7191 !IsGoodVal("Float XYZ->XYZ", in[1], out[1], FLOAT_PRECISSION) ||
7192 !IsGoodVal("Float XYZ->XYZ", in[2], out[2], FLOAT_PRECISSION))
7193 return 0;
7196 // XYZ to RGB
7197 input = IdentityMatrixProfile( cmsSigRgbData);
7199 xform = cmsCreateTransform( xyzProfile, TYPE_XYZ_FLT, input, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7200 cmsCloseProfile(input);
7202 cmsDoTransform( xform, in, out, 1);
7204 cmsDeleteTransform( xform);
7206 if (!IsGoodVal("Float XYZ->RGB", in[0], out[0], FLOAT_PRECISSION) ||
7207 !IsGoodVal("Float XYZ->RGB", in[1], out[1], FLOAT_PRECISSION) ||
7208 !IsGoodVal("Float XYZ->RGB", in[2], out[2], FLOAT_PRECISSION))
7209 return 0;
7212 // Now the optimizer should remove a stage
7214 // XYZ to RGB
7215 input = IdentityMatrixProfile( cmsSigRgbData);
7217 xform = cmsCreateTransform( input, TYPE_RGB_FLT, input, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
7218 cmsCloseProfile(input);
7220 cmsDoTransform( xform, in, out, 1);
7222 cmsDeleteTransform( xform);
7224 if (!IsGoodVal("Float RGB->RGB", in[0], out[0], FLOAT_PRECISSION) ||
7225 !IsGoodVal("Float RGB->RGB", in[1], out[1], FLOAT_PRECISSION) ||
7226 !IsGoodVal("Float RGB->RGB", in[2], out[2], FLOAT_PRECISSION))
7227 return 0;
7229 cmsCloseProfile(xyzProfile);
7232 return 1;
7237 Bug reported
7240 sRGB built-in V4.3 -> Lab identity built-in V4.3
7241 Flags: "cmsFLAGS_NOCACHE", "cmsFLAGS_NOOPTIMIZE"
7242 Input format: TYPE_RGBA_FLT
7243 Output format: TYPE_LabA_FLT
7245 2) and back
7246 Lab identity built-in V4.3 -> sRGB built-in V4.3
7247 Flags: "cmsFLAGS_NOCACHE", "cmsFLAGS_NOOPTIMIZE"
7248 Input format: TYPE_LabA_FLT
7249 Output format: TYPE_RGBA_FLT
7252 static
7253 cmsInt32Number ChecksRGB2LabFLT(void)
7255 cmsHPROFILE hSRGB = cmsCreate_sRGBProfile();
7256 cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
7258 cmsHTRANSFORM xform1 = cmsCreateTransform(hSRGB, TYPE_RGBA_FLT, hLab, TYPE_LabA_FLT, 0, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
7259 cmsHTRANSFORM xform2 = cmsCreateTransform(hLab, TYPE_LabA_FLT, hSRGB, TYPE_RGBA_FLT, 0, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
7261 cmsFloat32Number RGBA1[4], RGBA2[4], LabA[4];
7262 int i;
7265 for (i = 0; i <= 100; i++)
7267 RGBA1[0] = i / 100.0F;
7268 RGBA1[1] = i / 100.0F;
7269 RGBA1[2] = i / 100.0F;
7270 RGBA1[3] = 0;
7272 cmsDoTransform(xform1, RGBA1, LabA, 1);
7273 cmsDoTransform(xform2, LabA, RGBA2, 1);
7275 if (!IsGoodVal("Float RGB->RGB", RGBA1[0], RGBA2[0], FLOAT_PRECISSION) ||
7276 !IsGoodVal("Float RGB->RGB", RGBA1[1], RGBA2[1], FLOAT_PRECISSION) ||
7277 !IsGoodVal("Float RGB->RGB", RGBA1[2], RGBA2[2], FLOAT_PRECISSION))
7278 return 0;
7282 cmsDeleteTransform(xform1);
7283 cmsDeleteTransform(xform2);
7284 cmsCloseProfile(hSRGB);
7285 cmsCloseProfile(hLab);
7287 return 1;
7291 * parametric curve for Rec709
7293 static
7294 double Rec709(double L)
7296 if (L <0.018) return 4.5*L;
7297 else
7299 double a = 1.099* pow(L, 0.45);
7301 a = a - 0.099;
7302 return a;
7307 static
7308 cmsInt32Number CheckParametricRec709(void)
7310 cmsFloat64Number params[7];
7311 cmsToneCurve* t;
7312 int i;
7314 params[0] = 0.45; /* y */
7315 params[1] = pow(1.099, 1.0 / 0.45); /* a */
7316 params[2] = 0.0; /* b */
7317 params[3] = 4.5; /* c */
7318 params[4] = 0.018; /* d */
7319 params[5] = -0.099; /* e */
7320 params[6] = 0.0; /* f */
7322 t = cmsBuildParametricToneCurve (NULL, 5, params);
7325 for (i=0; i < 256; i++)
7327 cmsFloat32Number n = (cmsFloat32Number) i / 255.0F;
7328 cmsUInt16Number f1 = (cmsUInt16Number) floor(255.0 * cmsEvalToneCurveFloat(t, n) + 0.5);
7329 cmsUInt16Number f2 = (cmsUInt16Number) floor(255.0*Rec709((double) i / 255.0) + 0.5);
7331 if (f1 != f2)
7333 cmsFreeToneCurve(t);
7334 return 0;
7338 cmsFreeToneCurve(t);
7339 return 1;
7343 #define kNumPoints 10
7345 typedef cmsFloat32Number(*Function)(cmsFloat32Number x);
7347 static cmsFloat32Number StraightLine( cmsFloat32Number x)
7349 return (cmsFloat32Number) (0.1 + 0.9 * x);
7352 static cmsInt32Number TestCurve( const char* label, cmsToneCurve* curve, Function fn)
7354 cmsInt32Number ok = 1;
7355 int i;
7356 for (i = 0; i < kNumPoints*3; i++) {
7358 cmsFloat32Number x = (cmsFloat32Number)i / (kNumPoints*3 - 1);
7359 cmsFloat32Number expectedY = fn(x);
7360 cmsFloat32Number out = cmsEvalToneCurveFloat( curve, x);
7362 if (!IsGoodVal(label, expectedY, out, FLOAT_PRECISSION)) {
7363 ok = 0;
7366 return ok;
7369 static
7370 cmsInt32Number CheckFloatSamples(void)
7372 cmsFloat32Number y[kNumPoints];
7373 int i;
7374 cmsToneCurve *curve;
7375 cmsInt32Number ok;
7377 for (i = 0; i < kNumPoints; i++) {
7378 cmsFloat32Number x = (cmsFloat32Number)i / (kNumPoints-1);
7380 y[i] = StraightLine(x);
7383 curve = cmsBuildTabulatedToneCurveFloat(NULL, kNumPoints, y);
7384 ok = TestCurve( "Float Samples", curve, StraightLine);
7385 cmsFreeToneCurve(curve);
7387 return ok;
7390 static
7391 cmsInt32Number CheckFloatSegments(void)
7393 cmsInt32Number ok = 1;
7394 int i;
7395 cmsToneCurve *curve;
7397 cmsFloat32Number y[ kNumPoints];
7399 // build a segmented curve with a sampled section...
7400 cmsCurveSegment Seg[3];
7402 // Initialize segmented curve part up to 0.1
7403 Seg[0].x0 = -1e22f; // -infinity
7404 Seg[0].x1 = 0.1f;
7405 Seg[0].Type = 6; // Y = (a * X + b) ^ Gamma + c
7406 Seg[0].Params[0] = 1.0f; // gamma
7407 Seg[0].Params[1] = 0.9f; // a
7408 Seg[0].Params[2] = 0.0f; // b
7409 Seg[0].Params[3] = 0.1f; // c
7410 Seg[0].Params[4] = 0.0f;
7412 // From zero to 1
7413 Seg[1].x0 = 0.1f;
7414 Seg[1].x1 = 0.9f;
7415 Seg[1].Type = 0;
7417 Seg[1].nGridPoints = kNumPoints;
7418 Seg[1].SampledPoints = y;
7420 for (i = 0; i < kNumPoints; i++) {
7421 cmsFloat32Number x = (cmsFloat32Number) (0.1 + ((cmsFloat32Number)i / (kNumPoints-1)) * (0.9 - 0.1));
7422 y[i] = StraightLine(x);
7425 // from 1 to +infinity
7426 Seg[2].x0 = 0.9f;
7427 Seg[2].x1 = 1e22f; // +infinity
7428 Seg[2].Type = 6;
7430 Seg[2].Params[0] = 1.0f;
7431 Seg[2].Params[1] = 0.9f;
7432 Seg[2].Params[2] = 0.0f;
7433 Seg[2].Params[3] = 0.1f;
7434 Seg[2].Params[4] = 0.0f;
7436 curve = cmsBuildSegmentedToneCurve(0, 3, Seg);
7438 ok = TestCurve( "Float Segmented Curve", curve, StraightLine);
7440 cmsFreeToneCurve( curve);
7442 return ok;
7446 static
7447 cmsInt32Number CheckReadRAW(void)
7449 cmsInt32Number tag_size, tag_size1;
7450 char buffer[4];
7451 cmsHPROFILE hProfile;
7454 SubTest("RAW read on on-disk");
7455 hProfile = cmsOpenProfileFromFile("test1.icc", "r");
7457 if (hProfile == NULL)
7458 return 0;
7460 tag_size = cmsReadRawTag(hProfile, cmsSigGamutTag, buffer, 4);
7461 tag_size1 = cmsReadRawTag(hProfile, cmsSigGamutTag, NULL, 0);
7463 cmsCloseProfile(hProfile);
7465 if (tag_size != 4)
7466 return 0;
7468 if (tag_size1 != 37009)
7469 return 0;
7471 SubTest("RAW read on in-memory created profiles");
7472 hProfile = cmsCreate_sRGBProfile();
7473 tag_size = cmsReadRawTag(hProfile, cmsSigGreenColorantTag, buffer, 4);
7474 tag_size1 = cmsReadRawTag(hProfile, cmsSigGreenColorantTag, NULL, 0);
7476 cmsCloseProfile(hProfile);
7478 if (tag_size != 4)
7479 return 0;
7480 if (tag_size1 != 20)
7481 return 0;
7483 return 1;
7488 // --------------------------------------------------------------------------------------------------
7489 // P E R F O R M A N C E C H E C K S
7490 // --------------------------------------------------------------------------------------------------
7493 typedef struct {cmsUInt8Number r, g, b, a;} Scanline_rgb1;
7494 typedef struct {cmsUInt16Number r, g, b, a;} Scanline_rgb2;
7495 typedef struct {cmsUInt8Number r, g, b;} Scanline_rgb8;
7496 typedef struct {cmsUInt16Number r, g, b;} Scanline_rgb0;
7499 static
7500 void TitlePerformance(const char* Txt)
7502 printf("%-45s: ", Txt); fflush(stdout);
7505 static
7506 void PrintPerformance(cmsUInt32Number Bytes, cmsUInt32Number SizeOfPixel, cmsFloat64Number diff)
7508 cmsFloat64Number seconds = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
7509 cmsFloat64Number mpix_sec = Bytes / (1024.0*1024.0*seconds*SizeOfPixel);
7511 printf("%g MPixel/sec.\n", mpix_sec);
7512 fflush(stdout);
7516 static
7517 void SpeedTest16bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7520 cmsInt32Number r, g, b, j;
7521 clock_t atime;
7522 cmsFloat64Number diff;
7523 cmsHTRANSFORM hlcmsxform;
7524 Scanline_rgb0 *In;
7525 cmsUInt32Number Mb;
7527 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7528 Die("Unable to open profiles");
7530 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_16,
7531 hlcmsProfileOut, TYPE_RGB_16, Intent, cmsFLAGS_NOCACHE);
7532 cmsCloseProfile(hlcmsProfileIn);
7533 cmsCloseProfile(hlcmsProfileOut);
7535 Mb = 256*256*256*sizeof(Scanline_rgb0);
7536 In = (Scanline_rgb0*) malloc(Mb);
7538 j = 0;
7539 for (r=0; r < 256; r++)
7540 for (g=0; g < 256; g++)
7541 for (b=0; b < 256; b++) {
7543 In[j].r = (cmsUInt16Number) ((r << 8) | r);
7544 In[j].g = (cmsUInt16Number) ((g << 8) | g);
7545 In[j].b = (cmsUInt16Number) ((b << 8) | b);
7547 j++;
7551 TitlePerformance(Title);
7553 atime = clock();
7555 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7557 diff = clock() - atime;
7558 free(In);
7560 PrintPerformance(Mb, sizeof(Scanline_rgb0), diff);
7561 cmsDeleteTransform(hlcmsxform);
7566 static
7567 void SpeedTest16bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
7569 cmsInt32Number r, g, b, j;
7570 clock_t atime;
7571 cmsFloat64Number diff;
7572 cmsHTRANSFORM hlcmsxform;
7573 Scanline_rgb2 *In;
7574 cmsUInt32Number Mb;
7576 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7577 Die("Unable to open profiles");
7579 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_16,
7580 hlcmsProfileOut, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
7581 cmsCloseProfile(hlcmsProfileIn);
7582 cmsCloseProfile(hlcmsProfileOut);
7584 Mb = 256*256*256*sizeof(Scanline_rgb2);
7586 In = (Scanline_rgb2*) malloc(Mb);
7588 j = 0;
7589 for (r=0; r < 256; r++)
7590 for (g=0; g < 256; g++)
7591 for (b=0; b < 256; b++) {
7593 In[j].r = (cmsUInt16Number) ((r << 8) | r);
7594 In[j].g = (cmsUInt16Number) ((g << 8) | g);
7595 In[j].b = (cmsUInt16Number) ((b << 8) | b);
7596 In[j].a = 0;
7598 j++;
7602 TitlePerformance(Title);
7604 atime = clock();
7606 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7608 diff = clock() - atime;
7610 free(In);
7612 PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7614 cmsDeleteTransform(hlcmsxform);
7619 static
7620 void SpeedTest8bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7622 cmsInt32Number r, g, b, j;
7623 clock_t atime;
7624 cmsFloat64Number diff;
7625 cmsHTRANSFORM hlcmsxform;
7626 Scanline_rgb8 *In;
7627 cmsUInt32Number Mb;
7629 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7630 Die("Unable to open profiles");
7632 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_8,
7633 hlcmsProfileOut, TYPE_RGB_8, Intent, cmsFLAGS_NOCACHE);
7634 cmsCloseProfile(hlcmsProfileIn);
7635 cmsCloseProfile(hlcmsProfileOut);
7637 Mb = 256*256*256*sizeof(Scanline_rgb8);
7639 In = (Scanline_rgb8*) malloc(Mb);
7641 j = 0;
7642 for (r=0; r < 256; r++)
7643 for (g=0; g < 256; g++)
7644 for (b=0; b < 256; b++) {
7646 In[j].r = (cmsUInt8Number) r;
7647 In[j].g = (cmsUInt8Number) g;
7648 In[j].b = (cmsUInt8Number) b;
7650 j++;
7653 TitlePerformance(Title);
7655 atime = clock();
7657 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7659 diff = clock() - atime;
7661 free(In);
7663 PrintPerformance(Mb, sizeof(Scanline_rgb8), diff);
7665 cmsDeleteTransform(hlcmsxform);
7670 static
7671 void SpeedTest8bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
7673 cmsInt32Number r, g, b, j;
7674 clock_t atime;
7675 cmsFloat64Number diff;
7676 cmsHTRANSFORM hlcmsxform;
7677 Scanline_rgb2 *In;
7678 cmsUInt32Number Mb;
7680 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7681 Die("Unable to open profiles");
7683 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_8,
7684 hlcmsProfileOut, TYPE_CMYK_8, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
7685 cmsCloseProfile(hlcmsProfileIn);
7686 cmsCloseProfile(hlcmsProfileOut);
7688 Mb = 256*256*256*sizeof(Scanline_rgb2);
7690 In = (Scanline_rgb2*) malloc(Mb);
7692 j = 0;
7693 for (r=0; r < 256; r++)
7694 for (g=0; g < 256; g++)
7695 for (b=0; b < 256; b++) {
7697 In[j].r = (cmsUInt8Number) r;
7698 In[j].g = (cmsUInt8Number) g;
7699 In[j].b = (cmsUInt8Number) b;
7700 In[j].a = (cmsUInt8Number) 0;
7702 j++;
7705 TitlePerformance(Title);
7707 atime = clock();
7709 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7711 diff = clock() - atime;
7713 free(In);
7715 PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7718 cmsDeleteTransform(hlcmsxform);
7723 static
7724 void SpeedTest8bitsGray(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7726 cmsInt32Number r, g, b, j;
7727 clock_t atime;
7728 cmsFloat64Number diff;
7729 cmsHTRANSFORM hlcmsxform;
7730 cmsUInt8Number *In;
7731 cmsUInt32Number Mb;
7734 if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
7735 Die("Unable to open profiles");
7737 hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn,
7738 TYPE_GRAY_8, hlcmsProfileOut, TYPE_GRAY_8, Intent, cmsFLAGS_NOCACHE);
7739 cmsCloseProfile(hlcmsProfileIn);
7740 cmsCloseProfile(hlcmsProfileOut);
7741 Mb = 256*256*256;
7743 In = (cmsUInt8Number*) malloc(Mb);
7745 j = 0;
7746 for (r=0; r < 256; r++)
7747 for (g=0; g < 256; g++)
7748 for (b=0; b < 256; b++) {
7750 In[j] = (cmsUInt8Number) r;
7752 j++;
7755 TitlePerformance(Title);
7757 atime = clock();
7759 cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7761 diff = clock() - atime;
7762 free(In);
7764 PrintPerformance(Mb, sizeof(cmsUInt8Number), diff);
7765 cmsDeleteTransform(hlcmsxform);
7769 static
7770 cmsHPROFILE CreateCurves(void)
7772 cmsToneCurve* Gamma = cmsBuildGamma(DbgThread(), 1.1);
7773 cmsToneCurve* Transfer[3];
7774 cmsHPROFILE h;
7776 Transfer[0] = Transfer[1] = Transfer[2] = Gamma;
7777 h = cmsCreateLinearizationDeviceLink(cmsSigRgbData, Transfer);
7779 cmsFreeToneCurve(Gamma);
7781 return h;
7785 static
7786 void SpeedTest(void)
7788 printf("\n\nP E R F O R M A N C E T E S T S\n");
7789 printf( "=================================\n\n");
7790 fflush(stdout);
7792 SpeedTest16bits("16 bits on CLUT profiles",
7793 cmsOpenProfileFromFile("test5.icc", "r"),
7794 cmsOpenProfileFromFile("test3.icc", "r"), INTENT_PERCEPTUAL);
7796 SpeedTest8bits("8 bits on CLUT profiles",
7797 cmsOpenProfileFromFile("test5.icc", "r"),
7798 cmsOpenProfileFromFile("test3.icc", "r"),
7799 INTENT_PERCEPTUAL);
7801 SpeedTest8bits("8 bits on Matrix-Shaper profiles",
7802 cmsOpenProfileFromFile("test5.icc", "r"),
7803 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7804 INTENT_PERCEPTUAL);
7806 SpeedTest8bits("8 bits on SAME Matrix-Shaper profiles",
7807 cmsOpenProfileFromFile("test5.icc", "r"),
7808 cmsOpenProfileFromFile("test5.icc", "r"),
7809 INTENT_PERCEPTUAL);
7811 SpeedTest8bits("8 bits on Matrix-Shaper profiles (AbsCol)",
7812 cmsOpenProfileFromFile("test5.icc", "r"),
7813 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7814 INTENT_ABSOLUTE_COLORIMETRIC);
7816 SpeedTest16bits("16 bits on Matrix-Shaper profiles",
7817 cmsOpenProfileFromFile("test5.icc", "r"),
7818 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7819 INTENT_PERCEPTUAL);
7821 SpeedTest16bits("16 bits on SAME Matrix-Shaper profiles",
7822 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7823 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7824 INTENT_PERCEPTUAL);
7826 SpeedTest16bits("16 bits on Matrix-Shaper profiles (AbsCol)",
7827 cmsOpenProfileFromFile("test5.icc", "r"),
7828 cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7829 INTENT_ABSOLUTE_COLORIMETRIC);
7831 SpeedTest8bits("8 bits on curves",
7832 CreateCurves(),
7833 CreateCurves(),
7834 INTENT_PERCEPTUAL);
7836 SpeedTest16bits("16 bits on curves",
7837 CreateCurves(),
7838 CreateCurves(),
7839 INTENT_PERCEPTUAL);
7841 SpeedTest8bitsCMYK("8 bits on CMYK profiles",
7842 cmsOpenProfileFromFile("test1.icc", "r"),
7843 cmsOpenProfileFromFile("test2.icc", "r"));
7845 SpeedTest16bitsCMYK("16 bits on CMYK profiles",
7846 cmsOpenProfileFromFile("test1.icc", "r"),
7847 cmsOpenProfileFromFile("test2.icc", "r"));
7849 SpeedTest8bitsGray("8 bits on gray-to gray",
7850 cmsOpenProfileFromFile("gray3lcms2.icc", "r"),
7851 cmsOpenProfileFromFile("graylcms2.icc", "r"), INTENT_RELATIVE_COLORIMETRIC);
7853 SpeedTest8bitsGray("8 bits on gray-to-lab gray",
7854 cmsOpenProfileFromFile("graylcms2.icc", "r"),
7855 cmsOpenProfileFromFile("glablcms2.icc", "r"), INTENT_RELATIVE_COLORIMETRIC);
7857 SpeedTest8bitsGray("8 bits on SAME gray-to-gray",
7858 cmsOpenProfileFromFile("graylcms2.icc", "r"),
7859 cmsOpenProfileFromFile("graylcms2.icc", "r"), INTENT_PERCEPTUAL);
7863 // -----------------------------------------------------------------------------------------------------
7866 // Print the supported intents
7867 static
7868 void PrintSupportedIntents(void)
7870 cmsUInt32Number n, i;
7871 cmsUInt32Number Codes[200];
7872 char* Descriptions[200];
7874 n = cmsGetSupportedIntents(200, Codes, Descriptions);
7876 printf("Supported intents:\n");
7877 for (i=0; i < n; i++) {
7878 printf("\t%u - %s\n", Codes[i], Descriptions[i]);
7880 printf("\n");
7883 // ZOO checks ------------------------------------------------------------------------------------------------------------
7886 #ifdef CMS_IS_WINDOWS_
7888 static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\";
7889 static char ZOOwrite[cmsMAX_PATH] = "c:\\colormaps\\write\\";
7890 static char ZOORawWrite[cmsMAX_PATH] = "c:\\colormaps\\rawwrite\\";
7893 // Read all tags on a profile given by its handle
7894 static
7895 void ReadAllTags(cmsHPROFILE h)
7897 cmsInt32Number i, n;
7898 cmsTagSignature sig;
7900 n = cmsGetTagCount(h);
7901 for (i=0; i < n; i++) {
7903 sig = cmsGetTagSignature(h, i);
7904 if (cmsReadTag(h, sig) == NULL) return;
7909 // Read all tags on a profile given by its handle
7910 static
7911 void ReadAllRAWTags(cmsHPROFILE h)
7913 cmsInt32Number i, n;
7914 cmsTagSignature sig;
7915 cmsInt32Number len;
7917 n = cmsGetTagCount(h);
7918 for (i=0; i < n; i++) {
7920 sig = cmsGetTagSignature(h, i);
7921 len = cmsReadRawTag(h, sig, NULL, 0);
7926 static
7927 void PrintInfo(cmsHPROFILE h, cmsInfoType Info)
7929 wchar_t* text;
7930 cmsInt32Number len;
7931 cmsContext id = DbgThread();
7933 len = cmsGetProfileInfo(h, Info, "en", "US", NULL, 0);
7934 if (len == 0) return;
7936 text = _cmsMalloc(id, len);
7937 cmsGetProfileInfo(h, Info, "en", "US", text, len);
7939 wprintf(L"%s\n", text);
7940 _cmsFree(id, text);
7944 static
7945 void PrintAllInfos(cmsHPROFILE h)
7947 PrintInfo(h, cmsInfoDescription);
7948 PrintInfo(h, cmsInfoManufacturer);
7949 PrintInfo(h, cmsInfoModel);
7950 PrintInfo(h, cmsInfoCopyright);
7951 printf("\n\n");
7954 static
7955 void ReadAllLUTS(cmsHPROFILE h)
7957 cmsPipeline* a;
7958 cmsCIEXYZ Black;
7960 a = _cmsReadInputLUT(h, INTENT_PERCEPTUAL);
7961 if (a) cmsPipelineFree(a);
7963 a = _cmsReadInputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7964 if (a) cmsPipelineFree(a);
7966 a = _cmsReadInputLUT(h, INTENT_SATURATION);
7967 if (a) cmsPipelineFree(a);
7969 a = _cmsReadInputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7970 if (a) cmsPipelineFree(a);
7973 a = _cmsReadOutputLUT(h, INTENT_PERCEPTUAL);
7974 if (a) cmsPipelineFree(a);
7976 a = _cmsReadOutputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7977 if (a) cmsPipelineFree(a);
7979 a = _cmsReadOutputLUT(h, INTENT_SATURATION);
7980 if (a) cmsPipelineFree(a);
7982 a = _cmsReadOutputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7983 if (a) cmsPipelineFree(a);
7986 a = _cmsReadDevicelinkLUT(h, INTENT_PERCEPTUAL);
7987 if (a) cmsPipelineFree(a);
7989 a = _cmsReadDevicelinkLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7990 if (a) cmsPipelineFree(a);
7992 a = _cmsReadDevicelinkLUT(h, INTENT_SATURATION);
7993 if (a) cmsPipelineFree(a);
7995 a = _cmsReadDevicelinkLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7996 if (a) cmsPipelineFree(a);
7999 cmsDetectDestinationBlackPoint(&Black, h, INTENT_PERCEPTUAL, 0);
8000 cmsDetectDestinationBlackPoint(&Black, h, INTENT_RELATIVE_COLORIMETRIC, 0);
8001 cmsDetectDestinationBlackPoint(&Black, h, INTENT_SATURATION, 0);
8002 cmsDetectDestinationBlackPoint(&Black, h, INTENT_ABSOLUTE_COLORIMETRIC, 0);
8003 cmsDetectTAC(h);
8006 // Check one specimen in the ZOO
8008 static
8009 cmsInt32Number CheckSingleSpecimen(const char* Profile)
8011 char BuffSrc[256];
8012 char BuffDst[256];
8013 cmsHPROFILE h;
8015 sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
8016 sprintf(BuffDst, "%s%s", ZOOwrite, Profile);
8018 h = cmsOpenProfileFromFile(BuffSrc, "r");
8019 if (h == NULL) return 0;
8021 printf("%s\n", Profile);
8022 PrintAllInfos(h);
8023 ReadAllTags(h);
8024 // ReadAllRAWTags(h);
8025 ReadAllLUTS(h);
8027 cmsSaveProfileToFile(h, BuffDst);
8028 cmsCloseProfile(h);
8030 h = cmsOpenProfileFromFile(BuffDst, "r");
8031 if (h == NULL) return 0;
8032 ReadAllTags(h);
8035 cmsCloseProfile(h);
8037 return 1;
8040 static
8041 cmsInt32Number CheckRAWSpecimen(const char* Profile)
8043 char BuffSrc[256];
8044 char BuffDst[256];
8045 cmsHPROFILE h;
8047 sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
8048 sprintf(BuffDst, "%s%s", ZOORawWrite, Profile);
8050 h = cmsOpenProfileFromFile(BuffSrc, "r");
8051 if (h == NULL) return 0;
8053 ReadAllTags(h);
8054 ReadAllRAWTags(h);
8055 cmsSaveProfileToFile(h, BuffDst);
8056 cmsCloseProfile(h);
8058 h = cmsOpenProfileFromFile(BuffDst, "r");
8059 if (h == NULL) return 0;
8060 ReadAllTags(h);
8061 cmsCloseProfile(h);
8063 return 1;
8067 static
8068 void CheckProfileZOO(void)
8071 struct _finddata_t c_file;
8072 intptr_t hFile;
8074 cmsSetLogErrorHandler(NULL);
8076 if ( (hFile = _findfirst("c:\\colormaps\\*.*", &c_file)) == -1L )
8077 printf("No files in current directory");
8078 else
8083 printf("%s\n", c_file.name);
8084 if (strcmp(c_file.name, ".") != 0 &&
8085 strcmp(c_file.name, "..") != 0) {
8087 CheckSingleSpecimen( c_file.name);
8088 CheckRAWSpecimen( c_file.name);
8090 if (TotalMemory > 0)
8091 printf("Ok, but %s are left!\n", MemStr(TotalMemory));
8092 else
8093 printf("Ok.\n");
8097 } while ( _findnext(hFile, &c_file) == 0 );
8099 _findclose(hFile);
8102 cmsSetLogErrorHandler(FatalErrorQuit);
8105 #endif
8108 #if 0
8109 #define TYPE_709 709
8110 static double Rec709Math(int Type, const double Params[], double R)
8111 { double Fun;
8113 switch (Type)
8115 case 709:
8117 if (R <= (Params[3]*Params[4])) Fun = R / Params[3];
8118 else Fun = pow(((R - Params[2])/Params[1]), Params[0]);
8119 break;
8121 case -709:
8123 if (R <= Params[4]) Fun = R * Params[3];
8124 else Fun = Params[1] * pow(R, (1/Params[0])) + Params[2];
8125 break;
8127 return Fun;
8131 // Add nonstandard TRC curves -> Rec709
8132 cmsPluginParametricCurves NewCurvePlugin = {
8133 { cmsPluginMagicNumber, 2000, cmsPluginParametricCurveSig, NULL },
8134 1, {TYPE_709}, {5}, Rec709Math};
8135 #endif
8140 // ---------------------------------------------------------------------------------------
8142 int main(int argc, char* argv[])
8145 cmsInt32Number Exhaustive = 0;
8146 cmsInt32Number DoSpeedTests = 1;
8147 cmsInt32Number DoCheckTests = 1;
8149 #ifdef _MSC_VER
8150 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
8151 #endif
8153 printf("LittleCMS %2.2f test bed %s %s\n\n", LCMS_VERSION / 1000.0, __DATE__, __TIME__);
8155 if ((argc == 2) && strcmp(argv[1], "--exhaustive") == 0) {
8157 Exhaustive = 1;
8158 printf("Running exhaustive tests (will take a while...)\n\n");
8161 printf("Installing debug memory plug-in ... ");
8162 cmsPlugin(&DebugMemHandler);
8163 printf("done.\n");
8165 printf("Installing error logger ... ");
8166 cmsSetLogErrorHandler(FatalErrorQuit);
8167 printf("done.\n");
8169 #ifdef CMS_IS_WINDOWS_
8170 // CheckProfileZOO();
8171 #endif
8173 PrintSupportedIntents();
8177 // Create utility profiles
8178 Check("Creation of test profiles", CreateTestProfiles);
8180 if (DoCheckTests)
8182 Check("Base types", CheckBaseTypes);
8183 Check("endianess", CheckEndianess);
8184 Check("quick floor", CheckQuickFloor);
8185 Check("quick floor word", CheckQuickFloorWord);
8186 Check("Fixed point 15.16 representation", CheckFixedPoint15_16);
8187 Check("Fixed point 8.8 representation", CheckFixedPoint8_8);
8189 // Forward 1D interpolation
8190 Check("1D interpolation in 2pt tables", Check1DLERP2);
8191 Check("1D interpolation in 3pt tables", Check1DLERP3);
8192 Check("1D interpolation in 4pt tables", Check1DLERP4);
8193 Check("1D interpolation in 6pt tables", Check1DLERP6);
8194 Check("1D interpolation in 18pt tables", Check1DLERP18);
8195 Check("1D interpolation in descending 2pt tables", Check1DLERP2Down);
8196 Check("1D interpolation in descending 3pt tables", Check1DLERP3Down);
8197 Check("1D interpolation in descending 6pt tables", Check1DLERP6Down);
8198 Check("1D interpolation in descending 18pt tables", Check1DLERP18Down);
8200 if (Exhaustive) {
8202 Check("1D interpolation in n tables", ExhaustiveCheck1DLERP);
8203 Check("1D interpolation in descending tables", ExhaustiveCheck1DLERPDown);
8206 // Forward 3D interpolation
8207 Check("3D interpolation Tetrahedral (float) ", Check3DinterpolationFloatTetrahedral);
8208 Check("3D interpolation Trilinear (float) ", Check3DinterpolationFloatTrilinear);
8209 Check("3D interpolation Tetrahedral (16) ", Check3DinterpolationTetrahedral16);
8210 Check("3D interpolation Trilinear (16) ", Check3DinterpolationTrilinear16);
8212 if (Exhaustive) {
8214 Check("Exhaustive 3D interpolation Tetrahedral (float) ", ExaustiveCheck3DinterpolationFloatTetrahedral);
8215 Check("Exhaustive 3D interpolation Trilinear (float) ", ExaustiveCheck3DinterpolationFloatTrilinear);
8216 Check("Exhaustive 3D interpolation Tetrahedral (16) ", ExhaustiveCheck3DinterpolationTetrahedral16);
8217 Check("Exhaustive 3D interpolation Trilinear (16) ", ExhaustiveCheck3DinterpolationTrilinear16);
8220 Check("Reverse interpolation 3 -> 3", CheckReverseInterpolation3x3);
8221 Check("Reverse interpolation 4 -> 3", CheckReverseInterpolation4x3);
8224 // High dimensionality interpolation
8226 Check("3D interpolation", Check3Dinterp);
8227 Check("3D interpolation with granularity", Check3DinterpGranular);
8228 Check("4D interpolation", Check4Dinterp);
8229 Check("4D interpolation with granularity", Check4DinterpGranular);
8230 Check("5D interpolation with granularity", Check5DinterpGranular);
8231 Check("6D interpolation with granularity", Check6DinterpGranular);
8232 Check("7D interpolation with granularity", Check7DinterpGranular);
8233 Check("8D interpolation with granularity", Check8DinterpGranular);
8235 // Encoding of colorspaces
8236 Check("Lab to LCh and back (float only) ", CheckLab2LCh);
8237 Check("Lab to XYZ and back (float only) ", CheckLab2XYZ);
8238 Check("Lab to xyY and back (float only) ", CheckLab2xyY);
8239 Check("Lab V2 encoding", CheckLabV2encoding);
8240 Check("Lab V4 encoding", CheckLabV4encoding);
8242 // BlackBody
8243 Check("Blackbody radiator", CheckTemp2CHRM);
8245 // Tone curves
8246 Check("Linear gamma curves (16 bits)", CheckGammaCreation16);
8247 Check("Linear gamma curves (float)", CheckGammaCreationFlt);
8249 Check("Curve 1.8 (float)", CheckGamma18);
8250 Check("Curve 2.2 (float)", CheckGamma22);
8251 Check("Curve 3.0 (float)", CheckGamma30);
8253 Check("Curve 1.8 (table)", CheckGamma18Table);
8254 Check("Curve 2.2 (table)", CheckGamma22Table);
8255 Check("Curve 3.0 (table)", CheckGamma30Table);
8257 Check("Curve 1.8 (word table)", CheckGamma18TableWord);
8258 Check("Curve 2.2 (word table)", CheckGamma22TableWord);
8259 Check("Curve 3.0 (word table)", CheckGamma30TableWord);
8261 Check("Parametric curves", CheckParametricToneCurves);
8263 Check("Join curves", CheckJointCurves);
8264 Check("Join curves descending", CheckJointCurvesDescending);
8265 Check("Join curves degenerated", CheckReverseDegenerated);
8266 Check("Join curves sRGB (Float)", CheckJointFloatCurves_sRGB);
8267 Check("Join curves sRGB (16 bits)", CheckJoint16Curves_sRGB);
8268 Check("Join curves sigmoidal", CheckJointCurvesSShaped);
8270 // LUT basics
8271 Check("LUT creation & dup", CheckLUTcreation);
8272 Check("1 Stage LUT ", Check1StageLUT);
8273 Check("2 Stage LUT ", Check2StageLUT);
8274 Check("2 Stage LUT (16 bits)", Check2Stage16LUT);
8275 Check("3 Stage LUT ", Check3StageLUT);
8276 Check("3 Stage LUT (16 bits)", Check3Stage16LUT);
8277 Check("4 Stage LUT ", Check4StageLUT);
8278 Check("4 Stage LUT (16 bits)", Check4Stage16LUT);
8279 Check("5 Stage LUT ", Check5StageLUT);
8280 Check("5 Stage LUT (16 bits) ", Check5Stage16LUT);
8281 Check("6 Stage LUT ", Check6StageLUT);
8282 Check("6 Stage LUT (16 bits) ", Check6Stage16LUT);
8284 // LUT operation
8285 Check("Lab to Lab LUT (float only) ", CheckLab2LabLUT);
8286 Check("XYZ to XYZ LUT (float only) ", CheckXYZ2XYZLUT);
8287 Check("Lab to Lab MAT LUT (float only) ", CheckLab2LabMatLUT);
8288 Check("Named Color LUT", CheckNamedColorLUT);
8289 Check("Usual formatters", CheckFormatters16);
8290 Check("Floating point formatters", CheckFormattersFloat);
8292 #ifndef CMS_NO_HALF_SUPPORT
8293 Check("HALF formatters", CheckFormattersHalf);
8294 #endif
8295 // ChangeBuffersFormat
8296 Check("ChangeBuffersFormat", CheckChangeBufferFormat);
8298 // MLU
8299 Check("Multilocalized Unicode", CheckMLU);
8301 // Named color
8302 Check("Named color lists", CheckNamedColorList);
8304 // Profile I/O (this one is huge!)
8305 Check("Profile creation", CheckProfileCreation);
8308 // Error reporting
8309 Check("Error reporting on bad profiles", CheckErrReportingOnBadProfiles);
8310 Check("Error reporting on bad transforms", CheckErrReportingOnBadTransforms);
8312 // Transforms
8313 Check("Curves only transforms", CheckCurvesOnlyTransforms);
8314 Check("Float Lab->Lab transforms", CheckFloatLabTransforms);
8315 Check("Encoded Lab->Lab transforms", CheckEncodedLabTransforms);
8316 Check("Stored identities", CheckStoredIdentities);
8318 Check("Matrix-shaper transform (float)", CheckMatrixShaperXFORMFloat);
8319 Check("Matrix-shaper transform (16 bits)", CheckMatrixShaperXFORM16);
8320 Check("Matrix-shaper transform (8 bits)", CheckMatrixShaperXFORM8);
8322 Check("Primaries of sRGB", CheckRGBPrimaries);
8324 // Known values
8325 Check("Known values across matrix-shaper", Chack_sRGB_Float);
8326 Check("Gray input profile", CheckInputGray);
8327 Check("Gray Lab input profile", CheckLabInputGray);
8328 Check("Gray output profile", CheckOutputGray);
8329 Check("Gray Lab output profile", CheckLabOutputGray);
8331 Check("Matrix-shaper proofing transform (float)", CheckProofingXFORMFloat);
8332 Check("Matrix-shaper proofing transform (16 bits)", CheckProofingXFORM16);
8334 Check("Gamut check", CheckGamutCheck);
8336 Check("CMYK roundtrip on perceptual transform", CheckCMYKRoundtrip);
8338 Check("CMYK perceptual transform", CheckCMYKPerceptual);
8339 // Check("CMYK rel.col. transform", CheckCMYKRelCol);
8341 Check("Black ink only preservation", CheckKOnlyBlackPreserving);
8342 Check("Black plane preservation", CheckKPlaneBlackPreserving);
8345 Check("Deciding curve types", CheckV4gamma);
8347 Check("Black point detection", CheckBlackPoint);
8348 Check("TAC detection", CheckTAC);
8350 Check("CGATS parser", CheckCGATS);
8351 Check("PostScript generator", CheckPostScript);
8352 Check("Segment maxima GBD", CheckGBD);
8353 Check("MD5 digest", CheckMD5);
8354 Check("Linking", CheckLinking);
8355 Check("floating point tags on XYZ", CheckFloatXYZ);
8356 Check("RGB->Lab->RGB with alpha on FLT", ChecksRGB2LabFLT);
8357 Check("Parametric curve on Rec709", CheckParametricRec709);
8358 Check("Floating Point sampled curve with non-zero start", CheckFloatSamples);
8359 Check("Floating Point segmented curve with short sampled segement", CheckFloatSegments);
8360 Check("Read RAW portions", CheckReadRAW);
8364 if (DoSpeedTests)
8365 SpeedTest();
8367 DebugMemPrintTotals();
8369 cmsUnregisterPlugins();
8371 // Cleanup
8372 RemoveTestProfiles();
8374 return TotalFail;