Tizen 2.0 Release
[external/lcms.git] / testbed / testcms2.c
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:
12 //
13 // The above copyright notice and this permission notice shall be included in 
14 // all copies or substantial portions of the Software.
15 //
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.
23 //
24 //---------------------------------------------------------------------------------
25 //
26
27 #ifdef _MSC_VER
28 #    define _CRT_SECURE_NO_WARNINGS 1
29 #endif
30
31 #include "lcms2_internal.h"
32
33 // On Visual Studio, use debug CRT
34 #ifdef _MSC_VER
35 #     include "crtdbg.h"
36 #     include <io.h>
37 #endif
38
39 // A single check. Returns 1 if success, 0 if failed
40 typedef cmsInt32Number (*TestFn)(void);
41
42 // A parametric Tone curve test function
43 typedef cmsFloat32Number (* dblfnptr)(cmsFloat32Number x, const cmsFloat64Number Params[]);
44
45 // Some globals to keep track of error
46 #define TEXT_ERROR_BUFFER_SIZE  4096
47
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;
53
54
55 #define cmsmin(a, b) (((a) < (b)) ? (a) : (b))
56
57 // Die, a fatal unexpected error is detected!
58 static
59 void Die(const char* Reason)
60 {
61     printf("\n\nArrrgggg!!: %s!\n\n", Reason);
62     fflush(stdout);
63     exit(1);
64 }
65
66 // Memory management replacement -----------------------------------------------------------------------------
67
68
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;
72
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; 
78
79     union {
80         cmsUInt64Number HiSparc;
81
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.
86
87     } alignment;
88
89    
90 } _cmsMemoryBlock;
91
92 #define SIZE_OF_MEM_HEADER (sizeof(_cmsMemoryBlock))
93
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)
100 {
101     static cmsUInt32Number n = 1;
102
103     return (cmsContext) n++;
104 }
105
106 // The allocate routine
107 static
108 void* DebugMalloc(cmsContext ContextID, cmsUInt32Number size)
109 {
110     _cmsMemoryBlock* blk;
111
112     if (size <= 0) {
113        Die("malloc requested with zero bytes");
114     }
115
116     TotalMemory += size;
117
118     if (TotalMemory > MaxAllocated)
119         MaxAllocated = TotalMemory;
120
121     if (size > SingleHit) 
122         SingleHit = size;
123
124     blk = (_cmsMemoryBlock*) malloc(size + SIZE_OF_MEM_HEADER);
125     if (blk == NULL) return NULL;
126
127     blk ->KeepSize = size;
128     blk ->WhoAllocated = ContextID;
129
130     return (void*) ((cmsUInt8Number*) blk + SIZE_OF_MEM_HEADER);
131 }
132
133 // The free routine
134 static
135 void  DebugFree(cmsContext ContextID, void *Ptr)
136 {
137     _cmsMemoryBlock* blk;
138     
139     if (Ptr == NULL) {
140         Die("NULL free (which is a no-op in C, but may be an clue of something going wrong)");
141     }
142
143     blk = (_cmsMemoryBlock*) (((cmsUInt8Number*) Ptr) - SIZE_OF_MEM_HEADER);
144     TotalMemory -= blk ->KeepSize;
145
146     if (blk ->WhoAllocated != ContextID) {
147         Die("Trying to free memory allocated by a different thread");
148     }
149
150     free(blk);
151 }
152
153 // Reallocate, just a malloc, a copy and a free in this case.
154 static
155 void * DebugRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize)
156 {
157     _cmsMemoryBlock* blk;
158     void*  NewPtr;
159     cmsUInt32Number max_sz;
160
161     NewPtr = DebugMalloc(ContextID, NewSize);
162     if (Ptr == NULL) return NewPtr;
163
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);
168     
169     return NewPtr;
170 }
171
172 // Let's know the totals
173 static
174 void DebugMemPrintTotals(void)
175 {
176     printf("[Memory statistics]\n");
177     printf("Allocated = %d MaxAlloc = %d Single block hit = %d\n", TotalMemory, MaxAllocated, SingleHit);
178 }
179
180 // Here we go with the plug-in declaration
181 static cmsPluginMemHandler DebugMemHandler = {{ cmsPluginMagicNumber, 2000, cmsPluginMemHandlerSig, NULL },
182                                                DebugMalloc, DebugFree, DebugRealloc, NULL, NULL, NULL };
183
184 // Utils  -------------------------------------------------------------------------------------
185
186 static
187 void FatalErrorQuit(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
188 {
189     Die(Text); 
190 }
191
192 // Print a dot for gauging
193 static 
194 void Dot(void)
195 {
196     fprintf(stdout, "."); fflush(stdout);
197 }
198
199 // Keep track of the reason to fail
200 static
201 void Fail(const char* frm, ...)
202 {
203     va_list args;
204     va_start(args, frm);
205     vsprintf(ReasonToFailBuffer, frm, args);
206     va_end(args);
207 }
208
209 // Keep track of subtest
210 static
211 void SubTest(const char* frm, ...)
212 {
213     va_list args;
214
215     Dot();
216     va_start(args, frm);
217     vsprintf(SubTestBuffer, frm, args);
218     va_end(args);
219 }
220
221
222 // Memory string
223 static
224 const char* MemStr(cmsUInt32Number size)
225 {
226     static char Buffer[1024];
227
228     if (size > 1024*1024) {
229         sprintf(Buffer, "%g Mb", (cmsFloat64Number) size / (1024.0*1024.0));
230     }
231     else
232         if (size > 1024) {
233             sprintf(Buffer, "%g Kb", (cmsFloat64Number) size / 1024.0);
234         }
235         else
236             sprintf(Buffer, "%g bytes", (cmsFloat64Number) size);
237
238     return Buffer;
239 }
240
241
242 // The check framework
243 static
244 void Check(const char* Title, TestFn Fn)
245 {
246     printf("Checking %s ...", Title);
247     fflush(stdout);
248
249     ReasonToFailBuffer[0] = 0;
250     SubTestBuffer[0] = 0;
251     TrappedError = FALSE;
252     SimultaneousErrors = 0;
253     TotalTests++;
254
255     if (Fn() && !TrappedError) {
256
257         // It is a good place to check memory
258         if (TotalMemory > 0)
259             printf("Ok, but %s are left!\n", MemStr(TotalMemory));
260         else
261             printf("Ok.\n");
262     }
263     else {
264         printf("FAIL!\n");
265
266         if (SubTestBuffer[0]) 
267             printf("%s: [%s]\n\t%s\n", Title, SubTestBuffer, ReasonToFailBuffer);
268         else
269             printf("%s:\n\t%s\n", Title, ReasonToFailBuffer);
270
271         if (SimultaneousErrors > 1) 
272                printf("\tMore than one (%d) errors were reported\n", SimultaneousErrors); 
273
274         TotalFail++;
275     }   
276     fflush(stdout);
277 }
278
279 // Dump a tone curve, for easy diagnostic
280 void DumpToneCurve(cmsToneCurve* gamma, const char* FileName)
281 {
282     cmsHANDLE hIT8;
283     cmsUInt32Number i;
284
285     hIT8 = cmsIT8Alloc(gamma ->InterpParams->ContextID);
286
287     cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_FIELDS", 2);
288     cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_SETS", gamma ->nEntries);
289
290     cmsIT8SetDataFormat(hIT8, 0, "SAMPLE_ID");
291     cmsIT8SetDataFormat(hIT8, 1, "VALUE");
292
293     for (i=0; i < gamma ->nEntries; i++) {
294         char Val[30];
295
296         sprintf(Val, "%d", i);
297         cmsIT8SetDataRowCol(hIT8, i, 0, Val);
298         sprintf(Val, "0x%x", gamma ->Table16[i]);
299         cmsIT8SetDataRowCol(hIT8, i, 1, Val);
300     }
301
302     cmsIT8SaveToFile(hIT8, FileName);
303     cmsIT8Free(hIT8);
304 }
305
306 // -------------------------------------------------------------------------------------------------
307
308
309 // Used to perform several checks. 
310 // The space used is a clone of a well-known commercial 
311 // color space which I will name "Above RGB"
312 static
313 cmsHPROFILE Create_AboveRGB(void)
314 {
315     cmsToneCurve* Curve[3];
316     cmsHPROFILE hProfile;
317     cmsCIExyY D65;
318     cmsCIExyYTRIPLE Primaries = {{0.64, 0.33, 1 },
319                                  {0.21, 0.71, 1 },
320                                  {0.15, 0.06, 1 }};
321     
322     Curve[0] = Curve[1] = Curve[2] = cmsBuildGamma(DbgThread(), 2.19921875);
323
324     cmsWhitePointFromTemp(&D65, 6504);
325     hProfile = cmsCreateRGBProfileTHR(DbgThread(), &D65, &Primaries, Curve);
326     cmsFreeToneCurve(Curve[0]);
327     
328     return hProfile;
329 }
330
331 // A gamma-2.2 gray space
332 static
333 cmsHPROFILE Create_Gray22(void)
334 {
335     cmsHPROFILE hProfile;
336     cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 2.2);
337     if (Curve == NULL) return NULL;
338
339     hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
340     cmsFreeToneCurve(Curve);
341
342     return hProfile;
343 }
344
345
346 static
347 cmsHPROFILE Create_GrayLab(void)
348 {
349     cmsHPROFILE hProfile;
350     cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 1.0);
351     if (Curve == NULL) return NULL;
352
353     hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
354     cmsFreeToneCurve(Curve);
355
356     cmsSetPCS(hProfile, cmsSigLabData);
357     return hProfile;
358 }
359
360 // A CMYK devicelink that adds gamma 3.0 to each channel
361 static
362 cmsHPROFILE Create_CMYK_DeviceLink(void)
363 {
364     cmsHPROFILE hProfile;
365     cmsToneCurve* Tab[4];
366     cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 3.0);
367     if (Curve == NULL) return NULL;
368
369     Tab[0] = Curve;
370     Tab[1] = Curve;
371     Tab[2] = Curve;
372     Tab[3] = Curve;   
373
374     hProfile = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigCmykData, Tab);
375     if (hProfile == NULL) return NULL;
376
377     cmsFreeToneCurve(Curve);
378
379     return hProfile;
380 }
381
382
383 // Create a fake CMYK profile, without any other requeriment that being coarse CMYK. 
384 // DONT USE THIS PROFILE FOR ANYTHING, IT IS USELESS BUT FOR TESTING PURPOSES.
385 typedef struct {
386
387     cmsHTRANSFORM hLab2sRGB;
388     cmsHTRANSFORM sRGB2Lab;
389     cmsHTRANSFORM hIlimit;
390
391 } FakeCMYKParams;
392
393 static
394 cmsFloat64Number Clip(cmsFloat64Number v)
395 {
396     if (v < 0) return 0;
397     if (v > 1) return 1;
398
399     return v;
400 }
401
402 static
403 cmsInt32Number ForwardSampler(register const cmsUInt16Number In[], cmsUInt16Number Out[], void* Cargo)
404 {
405     FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
406     cmsFloat64Number rgb[3], cmyk[4];   
407     cmsFloat64Number c, m, y, k;
408
409     cmsDoTransform(p ->hLab2sRGB, In, rgb, 1);
410          
411     c = 1 - rgb[0];
412     m = 1 - rgb[1];
413     y = 1 - rgb[2];
414
415     k = (c < m ? cmsmin(c, y) : cmsmin(m, y));
416    
417     // NONSENSE WARNING!: I'm doing this just because this is a test 
418     // profile that may have ink limit up to 400%. There is no UCR here
419     // so the profile is basically useless for anything but testing.
420
421     cmyk[0] = c;
422     cmyk[1] = m;
423     cmyk[2] = y;
424     cmyk[3] = k;
425
426     cmsDoTransform(p ->hIlimit, cmyk, Out, 1);
427
428     return 1;
429 }
430
431
432 static
433 cmsInt32Number ReverseSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
434 {
435     FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
436     cmsFloat64Number c, m, y, k, rgb[3];    
437
438     c = In[0] / 65535.0;
439     m = In[1] / 65535.0;
440     y = In[2] / 65535.0;
441     k = In[3] / 65535.0;
442
443     if (k == 0) {
444
445         rgb[0] = Clip(1 - c);
446         rgb[1] = Clip(1 - m);
447         rgb[2] = Clip(1 - y);
448     } 
449     else 
450         if (k == 1) {
451
452             rgb[0] = rgb[1] = rgb[2] = 0;
453         } 
454         else {
455
456             rgb[0] = Clip((1 - c) * (1 - k));
457             rgb[1] = Clip((1 - m) * (1 - k));
458             rgb[2] = Clip((1 - y) * (1 - k));       
459         }   
460
461         cmsDoTransform(p ->sRGB2Lab, rgb, Out, 1);
462         return 1;
463 }
464
465
466
467 static
468 cmsHPROFILE CreateFakeCMYK(cmsFloat64Number InkLimit, cmsBool lUseAboveRGB)
469 {
470     cmsHPROFILE hICC;
471     cmsPipeline* AToB0, *BToA0;
472     cmsStage* CLUT;
473     cmsContext ContextID;
474     FakeCMYKParams p;
475     cmsHPROFILE hLab, hsRGB, hLimit;
476     cmsUInt32Number cmykfrm;
477
478
479     if (lUseAboveRGB)
480         hsRGB = Create_AboveRGB();
481     else
482        hsRGB  = cmsCreate_sRGBProfile();
483
484     hLab   = cmsCreateLab4Profile(NULL);
485     hLimit = cmsCreateInkLimitingDeviceLink(cmsSigCmykData, InkLimit);
486
487     cmykfrm = FLOAT_SH(1) | BYTES_SH(0)|CHANNELS_SH(4);
488     p.hLab2sRGB = cmsCreateTransform(hLab,  TYPE_Lab_16,  hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
489     p.sRGB2Lab  = cmsCreateTransform(hsRGB, TYPE_RGB_DBL, hLab,  TYPE_Lab_16,  INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
490     p.hIlimit   = cmsCreateTransform(hLimit, cmykfrm, NULL, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
491
492     cmsCloseProfile(hLab); cmsCloseProfile(hsRGB); cmsCloseProfile(hLimit);
493
494     ContextID = DbgThread();
495     hICC = cmsCreateProfilePlaceholder(ContextID);
496     if (!hICC) return NULL;
497
498     cmsSetProfileVersion(hICC, 4.3);
499
500     cmsSetDeviceClass(hICC, cmsSigOutputClass);
501     cmsSetColorSpace(hICC,  cmsSigCmykData);
502     cmsSetPCS(hICC,         cmsSigLabData);
503
504     BToA0 = cmsPipelineAlloc(ContextID, 3, 4);
505     if (BToA0 == NULL) return 0;
506     CLUT = cmsStageAllocCLut16bit(ContextID, 17, 3, 4, NULL);
507     if (CLUT == NULL) return 0;
508     if (!cmsStageSampleCLut16bit(CLUT, ForwardSampler, &p, 0)) return 0;
509     
510     cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)); 
511     cmsPipelineInsertStage(BToA0, cmsAT_END, CLUT);
512     cmsPipelineInsertStage(BToA0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 4));
513     
514     if (!cmsWriteTag(hICC, cmsSigBToA0Tag, (void*) BToA0)) return 0;
515     cmsPipelineFree(BToA0);
516     
517     AToB0 = cmsPipelineAlloc(ContextID, 4, 3);
518     if (AToB0 == NULL) return 0;
519     CLUT = cmsStageAllocCLut16bit(ContextID, 17, 4, 3, NULL);
520     if (CLUT == NULL) return 0;
521     if (!cmsStageSampleCLut16bit(CLUT, ReverseSampler, &p, 0)) return 0;
522
523     cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 4)); 
524     cmsPipelineInsertStage(AToB0, cmsAT_END, CLUT);
525     cmsPipelineInsertStage(AToB0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 3));
526     
527     if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) AToB0)) return 0;
528     cmsPipelineFree(AToB0);
529
530     cmsDeleteTransform(p.hLab2sRGB);
531     cmsDeleteTransform(p.sRGB2Lab);
532     cmsDeleteTransform(p.hIlimit);
533
534     cmsLinkTag(hICC, cmsSigAToB1Tag, cmsSigAToB0Tag);
535     cmsLinkTag(hICC, cmsSigAToB2Tag, cmsSigAToB0Tag);
536     cmsLinkTag(hICC, cmsSigBToA1Tag, cmsSigBToA0Tag);
537     cmsLinkTag(hICC, cmsSigBToA2Tag, cmsSigBToA0Tag);
538
539     return hICC;    
540 }
541
542
543 // Does create several profiles for latter use------------------------------------------------------------------------------------------------
544
545 static
546 cmsInt32Number OneVirtual(cmsHPROFILE h, const char* SubTestTxt, const char* FileName)
547 {
548     SubTest(SubTestTxt);
549     if (h == NULL) return 0;
550
551     if (!cmsSaveProfileToFile(h, FileName)) return 0;
552     cmsCloseProfile(h);
553
554     h = cmsOpenProfileFromFile(FileName, "r");
555     if (h == NULL) return 0;
556
557     // Do some teste....
558
559     cmsCloseProfile(h);
560
561     return 1;
562 }
563
564
565
566 // This test checks the ability of lcms2 to save its built-ins as valid profiles. 
567 // It does not check the functionality of such profiles
568 static
569 cmsInt32Number CreateTestProfiles(void)
570 {
571     cmsHPROFILE h;
572
573     h = cmsCreate_sRGBProfileTHR(DbgThread());
574     if (!OneVirtual(h, "sRGB profile", "sRGBlcms2.icc")) return 0;
575
576     // ----
577
578     h = Create_AboveRGB();
579     if (!OneVirtual(h, "aRGB profile", "aRGBlcms2.icc")) return 0;
580
581     // ----
582     
583     h = Create_Gray22();
584     if (!OneVirtual(h, "Gray profile", "graylcms2.icc")) return 0;
585
586     // ----
587
588     h = Create_GrayLab();
589     if (!OneVirtual(h, "Gray Lab profile", "glablcms2.icc")) return 0;
590     
591     // ----
592
593     h = Create_CMYK_DeviceLink();
594     if (!OneVirtual(h, "Linearization profile", "linlcms2.icc")) return 0;
595
596     // -------
597     h = cmsCreateInkLimitingDeviceLinkTHR(DbgThread(), cmsSigCmykData, 150);
598     if (h == NULL) return 0;
599     if (!OneVirtual(h, "Ink-limiting profile", "limitlcms2.icc")) return 0;
600
601     // ------
602
603     h = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
604     if (!OneVirtual(h, "Lab 2 identity profile", "labv2lcms2.icc")) return 0;
605
606     // ----
607
608     h = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
609     if (!OneVirtual(h, "Lab 4 identity profile", "labv4lcms2.icc")) return 0;
610
611     // ----
612
613     h = cmsCreateXYZProfileTHR(DbgThread());
614     if (!OneVirtual(h, "XYZ identity profile", "xyzlcms2.icc")) return 0;
615
616     // ----
617
618     h = cmsCreateNULLProfileTHR(DbgThread());
619     if (!OneVirtual(h, "NULL profile", "nullcms2.icc")) return 0;
620
621     // ---
622
623     h = cmsCreateBCHSWabstractProfileTHR(DbgThread(), 17, 0, 0, 0, 0, 5000, 6000);
624     if (!OneVirtual(h, "BCHS profile", "bchslcms2.icc")) return 0;
625
626     // ---
627
628     h = CreateFakeCMYK(300, FALSE);
629     if (!OneVirtual(h, "Fake CMYK profile", "lcms2cmyk.icc")) return 0;
630
631     return 1;
632 }
633
634 static
635 void RemoveTestProfiles(void)
636 {
637     remove("sRGBlcms2.icc");
638     remove("aRGBlcms2.icc");
639     remove("graylcms2.icc");
640     remove("linlcms2.icc");
641     remove("limitlcms2.icc");
642     remove("labv2lcms2.icc");
643     remove("labv4lcms2.icc");
644     remove("xyzlcms2.icc");
645     remove("nullcms2.icc");
646     remove("bchslcms2.icc");
647     remove("lcms2cmyk.icc");
648     remove("glablcms2.icc");
649 }
650
651 // -------------------------------------------------------------------------------------------------
652
653 // Check the size of basic types. If this test fails, nothing is going to work anyway
654 static
655 cmsInt32Number CheckBaseTypes(void)
656 {
657     if (sizeof(cmsUInt8Number) != 1) return 0;
658     if (sizeof(cmsInt8Number) != 1) return 0;
659     if (sizeof(cmsUInt16Number) != 2) return 0;
660     if (sizeof(cmsInt16Number) != 2) return 0;
661     if (sizeof(cmsUInt32Number) != 4) return 0;
662     if (sizeof(cmsInt32Number) != 4) return 0;
663     if (sizeof(cmsUInt64Number) != 8) return 0;
664     if (sizeof(cmsInt64Number) != 8) return 0;
665     if (sizeof(cmsFloat32Number) != 4) return 0;
666     if (sizeof(cmsFloat64Number) != 8) return 0;
667     if (sizeof(cmsSignature) != 4) return 0;
668     if (sizeof(cmsU8Fixed8Number) != 2) return 0;
669     if (sizeof(cmsS15Fixed16Number) != 4) return 0;
670     if (sizeof(cmsU16Fixed16Number) != 4) return 0;
671     
672     return 1;
673 }
674
675 // -------------------------------------------------------------------------------------------------
676
677
678 // Are we little or big endian?  From Harbison&Steele.  
679 static
680 cmsInt32Number CheckEndianess(void)
681 {
682     cmsInt32Number BigEndian, IsOk;   
683     union {
684         long l;
685         char c[sizeof (long)];
686     } u;
687
688     u.l = 1;
689     BigEndian = (u.c[sizeof (long) - 1] == 1);
690
691 #ifdef CMS_USE_BIG_ENDIAN
692     IsOk = BigEndian;
693 #else
694     IsOk = !BigEndian;
695 #endif
696
697     if (!IsOk) {
698         Fail("\nOOOPPSS! You have CMS_USE_BIG_ENDIAN toggle misconfigured!\n\n"
699             "Please, edit lcms2.h and %s the CMS_USE_BIG_ENDIAN toggle.\n", BigEndian? "uncomment" : "comment");
700         return 0;
701     }
702
703     return 1;
704 }
705
706 // Check quick floor
707 static
708 cmsInt32Number CheckQuickFloor(void)
709 {
710     if ((_cmsQuickFloor(1.234) != 1) ||
711         (_cmsQuickFloor(32767.234) != 32767) ||
712         (_cmsQuickFloor(-1.234) != -2) ||
713         (_cmsQuickFloor(-32767.1) != -32768)) {
714
715             Fail("\nOOOPPSS! _cmsQuickFloor() does not work as expected in your machine!\n\n"
716                 "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
717             return 0;
718
719     }
720
721     return 1;
722 }
723
724 // Quick floor restricted to word
725 static
726 cmsInt32Number CheckQuickFloorWord(void)
727 {
728     cmsUInt32Number i;
729
730     for (i=0; i < 65535; i++) {
731
732         if (_cmsQuickFloorWord((cmsFloat64Number) i + 0.1234) != i) {
733
734             Fail("\nOOOPPSS! _cmsQuickFloorWord() does not work as expected in your machine!\n\n"
735                 "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
736             return 0;
737         }
738     }
739
740     return 1;
741 }
742
743 // -------------------------------------------------------------------------------------------------
744
745 // Precision stuff. 
746
747 // On 15.16 fixed point, this is the maximum we can obtain. Remember ICC profiles have storage limits on this number 
748 #define FIXED_PRECISION_15_16 (1.0 / 65535.0)
749
750 // On 8.8 fixed point, that is the max we can obtain.
751 #define FIXED_PRECISION_8_8 (1.0 / 255.0)
752
753 // On cmsFloat32Number type, this is the precision we expect
754 #define FLOAT_PRECISSION      (0.00001)
755
756 static cmsFloat64Number MaxErr;
757 static cmsFloat64Number AllowedErr = FIXED_PRECISION_15_16;
758
759 static
760 cmsBool IsGoodVal(const char *title, cmsFloat64Number in, cmsFloat64Number out, cmsFloat64Number max)
761 {
762     cmsFloat64Number Err = fabs(in - out);
763
764     if (Err > MaxErr) MaxErr = Err;
765
766         if ((Err > max )) {
767
768               Fail("(%s): Must be %f, But is %f ", title, in, out);
769               return FALSE;
770               }
771
772        return TRUE;
773 }
774
775 static
776 cmsBool  IsGoodFixed15_16(const char *title, cmsFloat64Number in, cmsFloat64Number out)
777 {   
778     return IsGoodVal(title, in, out, FIXED_PRECISION_15_16);
779 }
780
781 static
782 cmsBool  IsGoodFixed8_8(const char *title, cmsFloat64Number in, cmsFloat64Number out)
783 {
784     return IsGoodVal(title, in, out, FIXED_PRECISION_8_8);
785 }
786
787 static
788 cmsBool  IsGoodWord(const char *title, cmsUInt16Number in, cmsUInt16Number out)
789 {
790     if ((abs(in - out) > 0 )) {
791
792         Fail("(%s): Must be %x, But is %x ", title, in, out);
793         return FALSE;
794     }
795
796     return TRUE;
797 }
798
799 static
800 cmsBool  IsGoodWordPrec(const char *title, cmsUInt16Number in, cmsUInt16Number out, cmsUInt16Number maxErr)
801 {
802     if ((abs(in - out) > maxErr )) {
803
804         Fail("(%s): Must be %x, But is %x ", title, in, out);
805         return FALSE;
806     }
807
808     return TRUE;
809 }
810
811 // Fixed point ----------------------------------------------------------------------------------------------
812
813 static
814 cmsInt32Number TestSingleFixed15_16(cmsFloat64Number d)
815 {
816     cmsS15Fixed16Number f = _cmsDoubleTo15Fixed16(d);
817     cmsFloat64Number RoundTrip = _cms15Fixed16toDouble(f);
818     cmsFloat64Number Error     = fabs(d - RoundTrip);
819
820     return ( Error <= FIXED_PRECISION_15_16);
821 }
822
823 static
824 cmsInt32Number CheckFixedPoint15_16(void)
825 {
826     if (!TestSingleFixed15_16(1.0)) return 0;
827     if (!TestSingleFixed15_16(2.0)) return 0;
828     if (!TestSingleFixed15_16(1.23456)) return 0;
829     if (!TestSingleFixed15_16(0.99999)) return 0;
830     if (!TestSingleFixed15_16(0.1234567890123456789099999)) return 0;
831     if (!TestSingleFixed15_16(-1.0)) return 0;
832     if (!TestSingleFixed15_16(-2.0)) return 0;
833     if (!TestSingleFixed15_16(-1.23456)) return 0;
834     if (!TestSingleFixed15_16(-1.1234567890123456789099999)) return 0;
835     if (!TestSingleFixed15_16(+32767.1234567890123456789099999)) return 0;
836     if (!TestSingleFixed15_16(-32767.1234567890123456789099999)) return 0;
837     return 1;
838 }
839
840 static
841 cmsInt32Number TestSingleFixed8_8(cmsFloat64Number d)
842 {
843     cmsS15Fixed16Number f = _cmsDoubleTo8Fixed8(d);
844     cmsFloat64Number RoundTrip = _cms8Fixed8toDouble((cmsUInt16Number) f);
845     cmsFloat64Number Error     = fabs(d - RoundTrip);
846
847     return ( Error <= FIXED_PRECISION_8_8);
848 }
849
850 static
851 cmsInt32Number CheckFixedPoint8_8(void)
852 {
853     if (!TestSingleFixed8_8(1.0)) return 0;
854     if (!TestSingleFixed8_8(2.0)) return 0;
855     if (!TestSingleFixed8_8(1.23456)) return 0;
856     if (!TestSingleFixed8_8(0.99999)) return 0;
857     if (!TestSingleFixed8_8(0.1234567890123456789099999)) return 0;
858     if (!TestSingleFixed8_8(+255.1234567890123456789099999)) return 0;
859
860     return 1;
861 }
862
863 // Linear interpolation -----------------------------------------------------------------------------------------------
864
865 // Since prime factors of 65535 (FFFF) are,
866 //
867 //            0xFFFF = 3 * 5 * 17 * 257
868 //
869 // I test tables of 2, 4, 6, and 18 points, that will be exact.
870
871 static
872 void BuildTable(cmsInt32Number n, cmsUInt16Number Tab[], cmsBool  Descending)
873 {
874     cmsInt32Number i;
875
876     for (i=0; i < n; i++) {
877         cmsFloat64Number v = (cmsFloat64Number) ((cmsFloat64Number) 65535.0 * i ) / (n-1);
878
879         Tab[Descending ? (n - i - 1) : i ] = (cmsUInt16Number) floor(v + 0.5);
880     }
881 }
882
883 // A single function that does check 1D interpolation
884 // nNodesToCheck = number on nodes to check
885 // Down = Create decreasing tables
886 // Reverse = Check reverse interpolation
887 // max_err = max allowed error 
888
889 static
890 cmsInt32Number Check1D(cmsInt32Number nNodesToCheck, cmsBool  Down, cmsInt32Number max_err)
891 {
892     cmsUInt32Number i;
893     cmsUInt16Number in, out;
894     cmsInterpParams* p;
895     cmsUInt16Number* Tab;
896
897     Tab = (cmsUInt16Number*) malloc(sizeof(cmsUInt16Number)* nNodesToCheck);
898     if (Tab == NULL) return 0;
899
900     p = _cmsComputeInterpParams(DbgThread(), nNodesToCheck, 1, 1, Tab, CMS_LERP_FLAGS_16BITS);
901     if (p == NULL) return 0;
902
903     BuildTable(nNodesToCheck, Tab, Down);
904
905     for (i=0; i <= 0xffff; i++) {
906
907         in = (cmsUInt16Number) i;
908         out = 0;
909
910         p ->Interpolation.Lerp16(&in, &out, p);
911
912         if (Down) out = 0xffff - out;
913
914         if (abs(out - in) > max_err) {
915
916             Fail("(%dp): Must be %x, But is %x : ", nNodesToCheck, in, out);
917             _cmsFreeInterpParams(p);
918             free(Tab);
919             return 0;
920         }
921     }
922
923     _cmsFreeInterpParams(p);
924     free(Tab);
925     return 1;
926 }
927
928
929 static
930 cmsInt32Number Check1DLERP2(void)
931 {
932     return Check1D(2, FALSE, 0);
933 }
934
935
936 static
937 cmsInt32Number Check1DLERP3(void)
938 {
939     return Check1D(3, FALSE, 1);    
940 }
941
942
943 static
944 cmsInt32Number Check1DLERP4(void)
945 {
946     return Check1D(4, FALSE, 0);
947 }
948
949 static
950 cmsInt32Number Check1DLERP6(void)
951 {
952     return Check1D(6, FALSE, 0);
953 }
954
955 static
956 cmsInt32Number Check1DLERP18(void)
957 {
958     return Check1D(18, FALSE, 0);
959 }
960
961
962 static
963 cmsInt32Number Check1DLERP2Down(void)
964 {
965     return Check1D(2, TRUE, 0);
966 }
967
968
969 static
970 cmsInt32Number Check1DLERP3Down(void)
971 {
972     return Check1D(3, TRUE, 1);
973 }
974
975 static
976 cmsInt32Number Check1DLERP6Down(void)
977 {
978     return Check1D(6, TRUE, 0);
979 }
980
981 static
982 cmsInt32Number Check1DLERP18Down(void)
983 {
984     return Check1D(18, TRUE, 0);
985 }
986
987 static
988 cmsInt32Number ExhaustiveCheck1DLERP(void)
989 {
990     cmsUInt32Number j;
991     
992     printf("\n");
993     for (j=10; j <= 4096; j++) {
994
995         if ((j % 10) == 0) printf("%d    \r", j);
996
997         if (!Check1D(j, FALSE, 1)) return 0;    
998     }
999
1000     printf("\rResult is ");
1001     return 1;
1002 }
1003
1004 static
1005 cmsInt32Number ExhaustiveCheck1DLERPDown(void)
1006 {
1007     cmsUInt32Number j;
1008     
1009     printf("\n");
1010     for (j=10; j <= 4096; j++) {
1011
1012         if ((j % 10) == 0) printf("%d    \r", j);
1013
1014         if (!Check1D(j, TRUE, 1)) return 0; 
1015     }
1016
1017
1018     printf("\rResult is ");
1019     return 1;
1020 }
1021
1022
1023
1024 // 3D interpolation -------------------------------------------------------------------------------------------------
1025
1026 static
1027 cmsInt32Number Check3DinterpolationFloatTetrahedral(void)
1028 {
1029     cmsInterpParams* p;
1030     cmsInt32Number i;
1031     cmsFloat32Number In[3], Out[3];
1032     cmsFloat32Number FloatTable[] = { //R     G    B
1033
1034         0,    0,   0,     // B=0,G=0,R=0
1035         0,    0,  .25,    // B=1,G=0,R=0
1036
1037         0,   .5,    0,    // B=0,G=1,R=0
1038         0,   .5,  .25,    // B=1,G=1,R=0
1039
1040         1,    0,    0,    // B=0,G=0,R=1
1041         1,    0,  .25,    // B=1,G=0,R=1
1042
1043         1,    .5,   0,    // B=0,G=1,R=1
1044         1,    .5,  .25    // B=1,G=1,R=1
1045
1046     };
1047
1048     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
1049
1050
1051     MaxErr = 0.0;
1052      for (i=0; i < 0xffff; i++) {
1053
1054        In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
1055
1056         p ->Interpolation.LerpFloat(In, Out, p);
1057
1058        if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1059        if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1060        if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1061      }
1062
1063     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1064     _cmsFreeInterpParams(p);
1065     return 1;
1066
1067 Error:
1068     _cmsFreeInterpParams(p);
1069     return 0;
1070 }
1071
1072 static
1073 cmsInt32Number Check3DinterpolationFloatTrilinear(void)
1074 {
1075     cmsInterpParams* p;
1076     cmsInt32Number i;
1077     cmsFloat32Number In[3], Out[3];
1078     cmsFloat32Number FloatTable[] = { //R     G    B
1079
1080         0,    0,   0,     // B=0,G=0,R=0
1081         0,    0,  .25,    // B=1,G=0,R=0
1082
1083         0,   .5,    0,    // B=0,G=1,R=0
1084         0,   .5,  .25,    // B=1,G=1,R=0
1085
1086         1,    0,    0,    // B=0,G=0,R=1
1087         1,    0,  .25,    // B=1,G=0,R=1
1088
1089         1,    .5,   0,    // B=0,G=1,R=1
1090         1,    .5,  .25    // B=1,G=1,R=1
1091
1092     };
1093
1094     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
1095
1096     MaxErr = 0.0;
1097      for (i=0; i < 0xffff; i++) {
1098
1099        In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
1100
1101         p ->Interpolation.LerpFloat(In, Out, p);
1102
1103        if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1104        if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1105        if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1106      }
1107
1108     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1109     _cmsFreeInterpParams(p);
1110     return 1;
1111
1112 Error:
1113     _cmsFreeInterpParams(p);
1114     return 0;
1115
1116 }
1117
1118 static
1119 cmsInt32Number Check3DinterpolationTetrahedral16(void)
1120 {
1121     cmsInterpParams* p;
1122     cmsInt32Number i;
1123     cmsUInt16Number In[3], Out[3];
1124     cmsUInt16Number Table[] = { 
1125
1126         0,    0,   0,     
1127         0,    0,   0xffff,    
1128
1129         0,    0xffff,    0,   
1130         0,    0xffff,    0xffff,  
1131
1132         0xffff,    0,    0,    
1133         0xffff,    0,    0xffff,   
1134
1135         0xffff,    0xffff,   0,    
1136         0xffff,    0xffff,   0xffff    
1137     };
1138
1139     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
1140
1141     MaxErr = 0.0;
1142      for (i=0; i < 0xffff; i++) {
1143
1144        In[0] = In[1] = In[2] = (cmsUInt16Number) i;
1145
1146         p ->Interpolation.Lerp16(In, Out, p);
1147
1148        if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1149        if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1150        if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1151      }
1152
1153     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1154     _cmsFreeInterpParams(p);
1155     return 1;
1156
1157 Error:
1158     _cmsFreeInterpParams(p);
1159     return 0;
1160 }
1161
1162 static
1163 cmsInt32Number Check3DinterpolationTrilinear16(void)
1164 {
1165     cmsInterpParams* p;
1166     cmsInt32Number i;
1167     cmsUInt16Number In[3], Out[3];
1168     cmsUInt16Number Table[] = { 
1169
1170         0,    0,   0,     
1171         0,    0,   0xffff,    
1172
1173         0,    0xffff,    0,   
1174         0,    0xffff,    0xffff,  
1175
1176         0xffff,    0,    0,    
1177         0xffff,    0,    0xffff,   
1178
1179         0xffff,    0xffff,   0,    
1180         0xffff,    0xffff,   0xffff    
1181     };
1182
1183     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
1184
1185     MaxErr = 0.0;
1186      for (i=0; i < 0xffff; i++) {
1187
1188        In[0] = In[1] = In[2] = (cmsUInt16Number) i;
1189
1190         p ->Interpolation.Lerp16(In, Out, p);
1191
1192        if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1193        if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1194        if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1195      }
1196
1197     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1198     _cmsFreeInterpParams(p);
1199     return 1;
1200
1201 Error:
1202     _cmsFreeInterpParams(p);
1203     return 0;
1204 }
1205
1206
1207 static
1208 cmsInt32Number ExaustiveCheck3DinterpolationFloatTetrahedral(void)
1209 {
1210     cmsInterpParams* p;
1211     cmsInt32Number r, g, b;
1212     cmsFloat32Number In[3], Out[3];
1213     cmsFloat32Number FloatTable[] = { //R     G    B
1214
1215         0,    0,   0,     // B=0,G=0,R=0
1216         0,    0,  .25,    // B=1,G=0,R=0
1217
1218         0,   .5,    0,    // B=0,G=1,R=0
1219         0,   .5,  .25,    // B=1,G=1,R=0
1220
1221         1,    0,    0,    // B=0,G=0,R=1
1222         1,    0,  .25,    // B=1,G=0,R=1
1223
1224         1,    .5,   0,    // B=0,G=1,R=1
1225         1,    .5,  .25    // B=1,G=1,R=1
1226
1227     };
1228
1229     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
1230
1231     MaxErr = 0.0;
1232     for (r=0; r < 0xff; r++) 
1233         for (g=0; g < 0xff; g++) 
1234             for (b=0; b < 0xff; b++) 
1235         {
1236
1237             In[0] = (cmsFloat32Number) r / 255.0F;
1238             In[1] = (cmsFloat32Number) g / 255.0F;
1239             In[2] = (cmsFloat32Number) b / 255.0F;
1240
1241        
1242         p ->Interpolation.LerpFloat(In, Out, p);
1243
1244        if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1245        if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1246        if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1247      }
1248
1249     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1250     _cmsFreeInterpParams(p);
1251     return 1;
1252
1253 Error:
1254     _cmsFreeInterpParams(p);
1255     return 0;
1256 }
1257
1258 static
1259 cmsInt32Number ExaustiveCheck3DinterpolationFloatTrilinear(void)
1260 {
1261     cmsInterpParams* p;
1262     cmsInt32Number r, g, b;
1263     cmsFloat32Number In[3], Out[3];
1264     cmsFloat32Number FloatTable[] = { //R     G    B
1265
1266         0,    0,   0,     // B=0,G=0,R=0
1267         0,    0,  .25,    // B=1,G=0,R=0
1268
1269         0,   .5,    0,    // B=0,G=1,R=0
1270         0,   .5,  .25,    // B=1,G=1,R=0
1271
1272         1,    0,    0,    // B=0,G=0,R=1
1273         1,    0,  .25,    // B=1,G=0,R=1
1274
1275         1,    .5,   0,    // B=0,G=1,R=1
1276         1,    .5,  .25    // B=1,G=1,R=1
1277
1278     };
1279
1280     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
1281
1282     MaxErr = 0.0;
1283     for (r=0; r < 0xff; r++) 
1284         for (g=0; g < 0xff; g++) 
1285             for (b=0; b < 0xff; b++) 
1286             {
1287
1288                 In[0] = (cmsFloat32Number) r / 255.0F;
1289                 In[1] = (cmsFloat32Number) g / 255.0F;
1290                 In[2] = (cmsFloat32Number) b / 255.0F;
1291
1292
1293                 p ->Interpolation.LerpFloat(In, Out, p);
1294
1295                 if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
1296                 if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
1297                 if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
1298             }
1299
1300     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
1301     _cmsFreeInterpParams(p);
1302     return 1;
1303
1304 Error:
1305     _cmsFreeInterpParams(p);
1306     return 0;
1307
1308 }
1309
1310 static
1311 cmsInt32Number ExhaustiveCheck3DinterpolationTetrahedral16(void)
1312 {
1313     cmsInterpParams* p;
1314     cmsInt32Number r, g, b;
1315     cmsUInt16Number In[3], Out[3];
1316     cmsUInt16Number Table[] = { 
1317
1318         0,    0,   0,     
1319         0,    0,   0xffff,    
1320
1321         0,    0xffff,    0,   
1322         0,    0xffff,    0xffff,  
1323
1324         0xffff,    0,    0,    
1325         0xffff,    0,    0xffff,   
1326
1327         0xffff,    0xffff,   0,    
1328         0xffff,    0xffff,   0xffff    
1329     };
1330
1331     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
1332
1333     for (r=0; r < 0xff; r++) 
1334         for (g=0; g < 0xff; g++) 
1335             for (b=0; b < 0xff; b++) 
1336         {
1337             In[0] = (cmsUInt16Number) r ;
1338             In[1] = (cmsUInt16Number) g ;
1339             In[2] = (cmsUInt16Number) b ;
1340
1341
1342         p ->Interpolation.Lerp16(In, Out, p);
1343
1344        if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1345        if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1346        if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1347      }
1348     
1349     _cmsFreeInterpParams(p);
1350     return 1;
1351
1352 Error:
1353     _cmsFreeInterpParams(p);
1354     return 0;
1355 }
1356
1357 static
1358 cmsInt32Number ExhaustiveCheck3DinterpolationTrilinear16(void)
1359 {
1360     cmsInterpParams* p;
1361     cmsInt32Number r, g, b;
1362     cmsUInt16Number In[3], Out[3];
1363     cmsUInt16Number Table[] = { 
1364
1365         0,    0,   0,     
1366         0,    0,   0xffff,    
1367
1368         0,    0xffff,    0,   
1369         0,    0xffff,    0xffff,  
1370
1371         0xffff,    0,    0,    
1372         0xffff,    0,    0xffff,   
1373
1374         0xffff,    0xffff,   0,    
1375         0xffff,    0xffff,   0xffff    
1376     };
1377
1378     p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
1379
1380     for (r=0; r < 0xff; r++) 
1381         for (g=0; g < 0xff; g++) 
1382             for (b=0; b < 0xff; b++) 
1383         {
1384             In[0] = (cmsUInt16Number) r ;
1385             In[1] = (cmsUInt16Number)g ;
1386             In[2] = (cmsUInt16Number)b ;
1387
1388
1389         p ->Interpolation.Lerp16(In, Out, p);
1390
1391        if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
1392        if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
1393        if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
1394      }
1395
1396     
1397     _cmsFreeInterpParams(p);
1398     return 1;
1399
1400 Error:
1401     _cmsFreeInterpParams(p);
1402     return 0;
1403 }
1404
1405 // Check reverse interpolation on LUTS. This is right now exclusively used by K preservation algorithm
1406 static
1407 cmsInt32Number CheckReverseInterpolation3x3(void)
1408 {
1409  cmsPipeline* Lut;
1410  cmsStage* clut;
1411  cmsFloat32Number Target[3], Result[3], Hint[3];
1412  cmsFloat32Number err, max;
1413  cmsInt32Number i;
1414  cmsUInt16Number Table[] = { 
1415
1416         0,    0,   0,                 // 0 0 0  
1417         0,    0,   0xffff,            // 0 0 1  
1418
1419         0,    0xffff,    0,           // 0 1 0  
1420         0,    0xffff,    0xffff,      // 0 1 1  
1421
1422         0xffff,    0,    0,           // 1 0 0  
1423         0xffff,    0,    0xffff,      // 1 0 1  
1424
1425         0xffff,    0xffff,   0,       // 1 1 0  
1426         0xffff,    0xffff,   0xffff,  // 1 1 1  
1427     };
1428
1429   
1430
1431    Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1432
1433    clut = cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table);
1434    cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
1435  
1436    Target[0] = 0; Target[1] = 0; Target[2] = 0; 
1437    Hint[0] = 0; Hint[1] = 0; Hint[2] = 0;
1438    cmsPipelineEvalReverseFloat(Target, Result, NULL, Lut);
1439    if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0){
1440
1441        Fail("Reverse interpolation didn't find zero");
1442        return 0;
1443    }
1444
1445    // Transverse identity
1446    max = 0;
1447    for (i=0; i <= 100; i++) {
1448
1449        cmsFloat32Number in = i / 100.0F;
1450
1451        Target[0] = in; Target[1] = 0; Target[2] = 0; 
1452        cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1453
1454        err = fabsf(in - Result[0]);
1455        if (err > max) max = err;
1456
1457        memcpy(Hint, Result, sizeof(Hint));
1458    }
1459
1460     cmsPipelineFree(Lut);
1461     return (max <= FLOAT_PRECISSION);
1462 }
1463
1464
1465 static
1466 cmsInt32Number CheckReverseInterpolation4x3(void)
1467 {
1468  cmsPipeline* Lut;
1469  cmsStage* clut;
1470  cmsFloat32Number Target[4], Result[4], Hint[4];
1471  cmsFloat32Number err, max;
1472  cmsInt32Number i;
1473
1474  // 4 -> 3, output gets 3 first channels copied
1475  cmsUInt16Number Table[] = { 
1476
1477         0,         0,         0,          //  0 0 0 0   = ( 0, 0, 0)
1478         0,         0,         0,          //  0 0 0 1   = ( 0, 0, 0)
1479                                         
1480         0,         0,         0xffff,     //  0 0 1 0   = ( 0, 0, 1)
1481         0,         0,         0xffff,     //  0 0 1 1   = ( 0, 0, 1)
1482                                             
1483         0,         0xffff,    0,          //  0 1 0 0   = ( 0, 1, 0)
1484         0,         0xffff,    0,          //  0 1 0 1   = ( 0, 1, 0)
1485                                             
1486         0,         0xffff,    0xffff,     //  0 1 1 0    = ( 0, 1, 1)
1487         0,         0xffff,    0xffff,     //  0 1 1 1    = ( 0, 1, 1)
1488
1489         0xffff,    0,         0,          //  1 0 0 0    = ( 1, 0, 0)
1490         0xffff,    0,         0,          //  1 0 0 1    = ( 1, 0, 0)
1491                                             
1492         0xffff,    0,         0xffff,     //  1 0 1 0    = ( 1, 0, 1)
1493         0xffff,    0,         0xffff,     //  1 0 1 1    = ( 1, 0, 1)
1494                                             
1495         0xffff,    0xffff,    0,          //  1 1 0 0    = ( 1, 1, 0)
1496         0xffff,    0xffff,    0,          //  1 1 0 1    = ( 1, 1, 0)
1497                                             
1498         0xffff,    0xffff,    0xffff,     //  1 1 1 0    = ( 1, 1, 1)
1499         0xffff,    0xffff,    0xffff,     //  1 1 1 1    = ( 1, 1, 1)
1500     };
1501
1502    
1503    Lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1504
1505    clut = cmsStageAllocCLut16bit(DbgThread(), 2, 4, 3, Table);
1506    cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
1507  
1508    // Check if the LUT is behaving as expected
1509    SubTest("4->3 feasibility");
1510    for (i=0; i <= 100; i++) {
1511
1512        Target[0] = i / 100.0F;
1513        Target[1] = Target[0];
1514        Target[2] = 0;
1515        Target[3] = 12;
1516
1517        cmsPipelineEvalFloat(Target, Result, Lut);
1518
1519        if (!IsGoodFixed15_16("0", Target[0], Result[0])) return 0;
1520        if (!IsGoodFixed15_16("1", Target[1], Result[1])) return 0;
1521        if (!IsGoodFixed15_16("2", Target[2], Result[2])) return 0;
1522    }
1523
1524    SubTest("4->3 zero");
1525    Target[0] = 0; 
1526    Target[1] = 0; 
1527    Target[2] = 0; 
1528
1529    // This one holds the fixed K
1530    Target[3] = 0; 
1531
1532    // This is our hint (which is a big lie in this case)
1533    Hint[0] = 0.1F; Hint[1] = 0.1F; Hint[2] = 0.1F;
1534
1535    cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1536
1537    if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0 || Result[3] != 0){
1538
1539        Fail("Reverse interpolation didn't find zero");
1540        return 0;
1541    }
1542
1543    SubTest("4->3 find CMY");
1544    max = 0;
1545    for (i=0; i <= 100; i++) {
1546
1547        cmsFloat32Number in = i / 100.0F;
1548
1549        Target[0] = in; Target[1] = 0; Target[2] = 0; 
1550        cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
1551
1552        err = fabsf(in - Result[0]);
1553        if (err > max) max = err;
1554
1555        memcpy(Hint, Result, sizeof(Hint));
1556    }
1557
1558     cmsPipelineFree(Lut);
1559     return (max <= FLOAT_PRECISSION);
1560 }
1561
1562
1563
1564 // Check all interpolation. 
1565
1566 static
1567 cmsUInt16Number Fn8D1(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1568                       cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1569                       cmsUInt32Number m)
1570 {
1571     return (cmsUInt16Number) ((a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8) / m);
1572 }
1573
1574
1575 static
1576 cmsUInt16Number Fn8D2(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1577                       cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1578                       cmsUInt32Number m)
1579 {
1580     return (cmsUInt16Number) ((a1 + 3* a2 + 3* a3 + a4 + a5 + a6 + a7 + a8 ) / (m + 4));
1581 }
1582
1583
1584 static
1585 cmsUInt16Number Fn8D3(cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4,
1586                       cmsUInt16Number a5, cmsUInt16Number a6, cmsUInt16Number a7, cmsUInt16Number a8,
1587                       cmsUInt32Number m)
1588 {
1589     return (cmsUInt16Number) ((3*a1 + 2*a2 + 3*a3 + a4 + a5 + a6 + a7 + a8) / (m + 5));
1590 }
1591
1592
1593
1594
1595 static
1596 cmsInt32Number Sampler3D(register const cmsUInt16Number In[],
1597                register cmsUInt16Number Out[],
1598                register void * Cargo)
1599 {
1600
1601     Out[0] = Fn8D1(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1602     Out[1] = Fn8D2(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1603     Out[2] = Fn8D3(In[0], In[1], In[2], 0, 0, 0, 0, 0, 3);
1604
1605     return 1;
1606 }
1607
1608 static
1609 cmsInt32Number Sampler4D(register const cmsUInt16Number In[],
1610                register cmsUInt16Number Out[],
1611                register void * Cargo)
1612 {
1613
1614     Out[0] = Fn8D1(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1615     Out[1] = Fn8D2(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1616     Out[2] = Fn8D3(In[0], In[1], In[2], In[3], 0, 0, 0, 0, 4);
1617
1618     return 1;
1619 }
1620
1621 static
1622 cmsInt32Number Sampler5D(register const cmsUInt16Number In[],
1623                register cmsUInt16Number Out[],
1624                register void * Cargo)
1625 {
1626
1627     Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1628     Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1629     Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], 0, 0, 0, 5);
1630
1631     return 1;
1632 }
1633
1634 static
1635 cmsInt32Number Sampler6D(register const cmsUInt16Number In[],
1636                register cmsUInt16Number Out[],
1637                register void * Cargo)
1638 {
1639
1640     Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1641     Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1642     Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], 0, 0, 6);
1643
1644     return 1;
1645 }
1646
1647 static
1648 cmsInt32Number Sampler7D(register const cmsUInt16Number In[],
1649                register cmsUInt16Number Out[],
1650                register void * Cargo)
1651 {
1652
1653     Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1654     Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1655     Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], In[6], 0, 7);
1656
1657     return 1;
1658 }
1659
1660 static
1661 cmsInt32Number Sampler8D(register const cmsUInt16Number In[],
1662                register cmsUInt16Number Out[],
1663                register void * Cargo)
1664 {
1665
1666     Out[0] = Fn8D1(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1667     Out[1] = Fn8D2(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1668     Out[2] = Fn8D3(In[0], In[1], In[2], In[3], In[4], In[5], In[6], In[7], 8);
1669
1670     return 1;
1671 }
1672
1673 static 
1674 cmsBool CheckOne3D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3)
1675 {
1676     cmsUInt16Number In[3], Out1[3], Out2[3];
1677
1678     In[0] = a1; In[1] = a2; In[2] = a3; 
1679
1680     // This is the interpolated value
1681     cmsPipelineEval16(In, Out1, lut);
1682
1683     // This is the real value
1684     Sampler3D(In, Out2, NULL);
1685
1686     // Let's see the difference
1687
1688     if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1689     if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1690     if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1691
1692     return TRUE;
1693 }
1694
1695 static 
1696 cmsBool CheckOne4D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, cmsUInt16Number a3, cmsUInt16Number a4)
1697 {
1698     cmsUInt16Number In[4], Out1[3], Out2[3];
1699
1700     In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4;
1701
1702     // This is the interpolated value
1703     cmsPipelineEval16(In, Out1, lut);
1704
1705     // This is the real value
1706     Sampler4D(In, Out2, NULL);
1707
1708     // Let's see the difference
1709
1710     if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1711     if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1712     if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1713
1714     return TRUE;
1715 }
1716
1717 static 
1718 cmsBool CheckOne5D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, 
1719                                      cmsUInt16Number a3, cmsUInt16Number a4, cmsUInt16Number a5)
1720 {
1721     cmsUInt16Number In[5], Out1[3], Out2[3];
1722
1723     In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5;
1724
1725     // This is the interpolated value
1726     cmsPipelineEval16(In, Out1, lut);
1727
1728     // This is the real value
1729     Sampler5D(In, Out2, NULL);
1730
1731     // Let's see the difference
1732
1733     if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1734     if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1735     if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1736
1737     return TRUE;
1738 }
1739
1740 static 
1741 cmsBool CheckOne6D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, 
1742                                      cmsUInt16Number a3, cmsUInt16Number a4, 
1743                                      cmsUInt16Number a5, cmsUInt16Number a6)
1744 {
1745     cmsUInt16Number In[6], Out1[3], Out2[3];
1746
1747     In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6;
1748
1749     // This is the interpolated value
1750     cmsPipelineEval16(In, Out1, lut);
1751
1752     // This is the real value
1753     Sampler6D(In, Out2, NULL);
1754
1755     // Let's see the difference
1756
1757     if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1758     if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1759     if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1760
1761     return TRUE;
1762 }
1763
1764
1765 static 
1766 cmsBool CheckOne7D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, 
1767                                      cmsUInt16Number a3, cmsUInt16Number a4, 
1768                                      cmsUInt16Number a5, cmsUInt16Number a6,
1769                                      cmsUInt16Number a7)
1770 {
1771     cmsUInt16Number In[7], Out1[3], Out2[3];
1772
1773     In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6; In[6] = a7;
1774
1775     // This is the interpolated value
1776     cmsPipelineEval16(In, Out1, lut);
1777
1778     // This is the real value
1779     Sampler7D(In, Out2, NULL);
1780
1781     // Let's see the difference
1782
1783     if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1784     if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1785     if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1786
1787     return TRUE;
1788 }
1789
1790
1791 static 
1792 cmsBool CheckOne8D(cmsPipeline* lut, cmsUInt16Number a1, cmsUInt16Number a2, 
1793                                      cmsUInt16Number a3, cmsUInt16Number a4, 
1794                                      cmsUInt16Number a5, cmsUInt16Number a6,
1795                                      cmsUInt16Number a7, cmsUInt16Number a8)
1796 {
1797     cmsUInt16Number In[8], Out1[3], Out2[3];
1798
1799     In[0] = a1; In[1] = a2; In[2] = a3; In[3] = a4; In[4] = a5; In[5] = a6; In[6] = a7; In[7] = a8;
1800
1801     // This is the interpolated value
1802     cmsPipelineEval16(In, Out1, lut);
1803
1804     // This is the real value
1805     Sampler8D(In, Out2, NULL);
1806
1807     // Let's see the difference
1808
1809     if (!IsGoodWordPrec("Channel 1", Out1[0], Out2[0], 2)) return FALSE;
1810     if (!IsGoodWordPrec("Channel 2", Out1[1], Out2[1], 2)) return FALSE;
1811     if (!IsGoodWordPrec("Channel 3", Out1[2], Out2[2], 2)) return FALSE;
1812
1813     return TRUE;
1814 }
1815
1816
1817 static
1818 cmsInt32Number Check3Dinterp(void)
1819 {
1820     cmsPipeline* lut;
1821     cmsStage* mpe;
1822
1823     lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1824     mpe = cmsStageAllocCLut16bit(DbgThread(), 9, 3, 3, NULL);
1825     cmsStageSampleCLut16bit(mpe, Sampler3D, NULL, 0);
1826     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1827
1828     // Check accuracy
1829
1830     if (!CheckOne3D(lut, 0, 0, 0)) return 0;
1831     if (!CheckOne3D(lut, 0xffff, 0xffff, 0xffff)) return 0; 
1832
1833     if (!CheckOne3D(lut, 0x8080, 0x8080, 0x8080)) return 0;
1834     if (!CheckOne3D(lut, 0x0000, 0xFE00, 0x80FF)) return 0;
1835     if (!CheckOne3D(lut, 0x1111, 0x2222, 0x3333)) return 0;
1836     if (!CheckOne3D(lut, 0x0000, 0x0012, 0x0013)) return 0; 
1837     if (!CheckOne3D(lut, 0x3141, 0x1415, 0x1592)) return 0; 
1838     if (!CheckOne3D(lut, 0xFF00, 0xFF01, 0xFF12)) return 0; 
1839
1840     cmsPipelineFree(lut);
1841
1842     return 1;
1843 }
1844
1845 static
1846 cmsInt32Number Check3DinterpGranular(void)
1847 {
1848     cmsPipeline* lut;
1849     cmsStage* mpe;
1850     cmsUInt32Number Dimensions[] = { 7, 8, 9 };
1851
1852     lut = cmsPipelineAlloc(DbgThread(), 3, 3);
1853     mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 3, 3, NULL);
1854     cmsStageSampleCLut16bit(mpe, Sampler3D, NULL, 0);
1855     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1856
1857     // Check accuracy
1858
1859     if (!CheckOne3D(lut, 0, 0, 0)) return 0;
1860     if (!CheckOne3D(lut, 0xffff, 0xffff, 0xffff)) return 0; 
1861
1862     if (!CheckOne3D(lut, 0x8080, 0x8080, 0x8080)) return 0;
1863     if (!CheckOne3D(lut, 0x0000, 0xFE00, 0x80FF)) return 0;
1864     if (!CheckOne3D(lut, 0x1111, 0x2222, 0x3333)) return 0;
1865     if (!CheckOne3D(lut, 0x0000, 0x0012, 0x0013)) return 0; 
1866     if (!CheckOne3D(lut, 0x3141, 0x1415, 0x1592)) return 0; 
1867     if (!CheckOne3D(lut, 0xFF00, 0xFF01, 0xFF12)) return 0; 
1868
1869     cmsPipelineFree(lut);
1870
1871     return 1;
1872 }
1873
1874
1875 static
1876 cmsInt32Number Check4Dinterp(void)
1877 {
1878     cmsPipeline* lut;
1879     cmsStage* mpe;
1880
1881     lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1882     mpe = cmsStageAllocCLut16bit(DbgThread(), 9, 4, 3, NULL);
1883     cmsStageSampleCLut16bit(mpe, Sampler4D, NULL, 0);
1884     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1885
1886     // Check accuracy
1887
1888     if (!CheckOne4D(lut, 0, 0, 0, 0)) return 0;
1889     if (!CheckOne4D(lut, 0xffff, 0xffff, 0xffff, 0xffff)) return 0; 
1890
1891     if (!CheckOne4D(lut, 0x8080, 0x8080, 0x8080, 0x8080)) return 0;
1892     if (!CheckOne4D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888)) return 0;
1893     if (!CheckOne4D(lut, 0x1111, 0x2222, 0x3333, 0x4444)) return 0;
1894     if (!CheckOne4D(lut, 0x0000, 0x0012, 0x0013, 0x0014)) return 0; 
1895     if (!CheckOne4D(lut, 0x3141, 0x1415, 0x1592, 0x9261)) return 0; 
1896     if (!CheckOne4D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13)) return 0; 
1897
1898     cmsPipelineFree(lut);
1899
1900     return 1;
1901 }
1902
1903
1904
1905 static
1906 cmsInt32Number Check4DinterpGranular(void)
1907 {
1908     cmsPipeline* lut;
1909     cmsStage* mpe;
1910     cmsUInt32Number Dimensions[] = { 9, 8, 7, 6 };
1911
1912     lut = cmsPipelineAlloc(DbgThread(), 4, 3);
1913     mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 4, 3, NULL);
1914     cmsStageSampleCLut16bit(mpe, Sampler4D, NULL, 0);
1915     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1916
1917     // Check accuracy
1918
1919     if (!CheckOne4D(lut, 0, 0, 0, 0)) return 0;
1920     if (!CheckOne4D(lut, 0xffff, 0xffff, 0xffff, 0xffff)) return 0; 
1921
1922     if (!CheckOne4D(lut, 0x8080, 0x8080, 0x8080, 0x8080)) return 0;
1923     if (!CheckOne4D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888)) return 0;
1924     if (!CheckOne4D(lut, 0x1111, 0x2222, 0x3333, 0x4444)) return 0;
1925     if (!CheckOne4D(lut, 0x0000, 0x0012, 0x0013, 0x0014)) return 0; 
1926     if (!CheckOne4D(lut, 0x3141, 0x1415, 0x1592, 0x9261)) return 0; 
1927     if (!CheckOne4D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13)) return 0; 
1928
1929     cmsPipelineFree(lut);
1930
1931     return 1;
1932 }
1933
1934
1935 static
1936 cmsInt32Number Check5DinterpGranular(void)
1937 {
1938     cmsPipeline* lut;
1939     cmsStage* mpe;
1940     cmsUInt32Number Dimensions[] = { 3, 2, 2, 2, 2 };
1941
1942     lut = cmsPipelineAlloc(DbgThread(), 5, 3);
1943     mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 5, 3, NULL);
1944     cmsStageSampleCLut16bit(mpe, Sampler5D, NULL, 0);
1945     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1946
1947     // Check accuracy
1948
1949     if (!CheckOne5D(lut, 0, 0, 0, 0, 0)) return 0;
1950     if (!CheckOne5D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0; 
1951
1952     if (!CheckOne5D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234)) return 0;
1953     if (!CheckOne5D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078)) return 0;
1954     if (!CheckOne5D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455)) return 0;
1955     if (!CheckOne5D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333)) return 0; 
1956     if (!CheckOne5D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567)) return 0; 
1957     if (!CheckOne5D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344)) return 0; 
1958
1959     cmsPipelineFree(lut);
1960
1961     return 1;
1962 }
1963
1964 static
1965 cmsInt32Number Check6DinterpGranular(void)
1966 {
1967     cmsPipeline* lut;
1968     cmsStage* mpe;
1969     cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2 };
1970
1971     lut = cmsPipelineAlloc(DbgThread(), 6, 3);
1972     mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 6, 3, NULL);
1973     cmsStageSampleCLut16bit(mpe, Sampler6D, NULL, 0);
1974     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
1975
1976     // Check accuracy
1977
1978     if (!CheckOne6D(lut, 0, 0, 0, 0, 0, 0)) return 0;
1979     if (!CheckOne6D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0; 
1980
1981     if (!CheckOne6D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122)) return 0;
1982     if (!CheckOne6D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233)) return 0;
1983     if (!CheckOne6D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344)) return 0;
1984     if (!CheckOne6D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455)) return 0; 
1985     if (!CheckOne6D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566)) return 0; 
1986     if (!CheckOne6D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677)) return 0; 
1987
1988     cmsPipelineFree(lut);
1989
1990     return 1;
1991 }
1992
1993 static
1994 cmsInt32Number Check7DinterpGranular(void)
1995 {
1996     cmsPipeline* lut;
1997     cmsStage* mpe;
1998     cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2, 2 };
1999
2000     lut = cmsPipelineAlloc(DbgThread(), 7, 3);
2001     mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 7, 3, NULL);
2002     cmsStageSampleCLut16bit(mpe, Sampler7D, NULL, 0);
2003     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
2004
2005     // Check accuracy
2006
2007     if (!CheckOne7D(lut, 0, 0, 0, 0, 0, 0, 0)) return 0;
2008     if (!CheckOne7D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0; 
2009
2010     if (!CheckOne7D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122, 0x0056)) return 0;
2011     if (!CheckOne7D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233, 0x0088)) return 0;
2012     if (!CheckOne7D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344, 0x1987)) return 0;
2013     if (!CheckOne7D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455, 0x9988)) return 0; 
2014     if (!CheckOne7D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566, 0xfe56)) return 0; 
2015     if (!CheckOne7D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677, 0xbabe)) return 0; 
2016
2017     cmsPipelineFree(lut);
2018
2019     return 1;
2020 }
2021
2022
2023 static
2024 cmsInt32Number Check8DinterpGranular(void)
2025 {
2026     cmsPipeline* lut;
2027     cmsStage* mpe;
2028     cmsUInt32Number Dimensions[] = { 4, 3, 3, 2, 2, 2, 2, 2 };
2029
2030     lut = cmsPipelineAlloc(DbgThread(), 8, 3);
2031     mpe = cmsStageAllocCLut16bitGranular(DbgThread(), Dimensions, 8, 3, NULL);
2032     cmsStageSampleCLut16bit(mpe, Sampler8D, NULL, 0);
2033     cmsPipelineInsertStage(lut, cmsAT_BEGIN, mpe);
2034
2035     // Check accuracy
2036
2037     if (!CheckOne8D(lut, 0, 0, 0, 0, 0, 0, 0, 0)) return 0;
2038     if (!CheckOne8D(lut, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) return 0; 
2039
2040     if (!CheckOne8D(lut, 0x8080, 0x8080, 0x8080, 0x8080, 0x1234, 0x1122, 0x0056, 0x0011)) return 0;
2041     if (!CheckOne8D(lut, 0x0000, 0xFE00, 0x80FF, 0x8888, 0x8078, 0x2233, 0x0088, 0x2020)) return 0;
2042     if (!CheckOne8D(lut, 0x1111, 0x2222, 0x3333, 0x4444, 0x1455, 0x3344, 0x1987, 0x4532)) return 0;
2043     if (!CheckOne8D(lut, 0x0000, 0x0012, 0x0013, 0x0014, 0x2333, 0x4455, 0x9988, 0x1200)) return 0; 
2044     if (!CheckOne8D(lut, 0x3141, 0x1415, 0x1592, 0x9261, 0x4567, 0x5566, 0xfe56, 0x6666)) return 0; 
2045     if (!CheckOne8D(lut, 0xFF00, 0xFF01, 0xFF12, 0xFF13, 0xF344, 0x6677, 0xbabe, 0xface)) return 0; 
2046
2047     cmsPipelineFree(lut);
2048
2049     return 1;
2050 }
2051
2052 // Colorimetric conversions -------------------------------------------------------------------------------------------------
2053
2054 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2055 static
2056 cmsInt32Number CheckLab2LCh(void)
2057 {
2058     cmsInt32Number l, a, b;
2059     cmsFloat64Number dist, Max = 0;
2060     cmsCIELab Lab, Lab2;
2061     cmsCIELCh LCh;
2062
2063     for (l=0; l <= 100; l += 10) {
2064
2065         for (a=-128; a <= +128; a += 8) {
2066             
2067             for (b=-128; b <= 128; b += 8) {
2068
2069                 Lab.L = l;
2070                 Lab.a = a;
2071                 Lab.b = b;
2072
2073                 cmsLab2LCh(&LCh, &Lab);
2074                 cmsLCh2Lab(&Lab2, &LCh);
2075
2076                 dist = cmsDeltaE(&Lab, &Lab2);
2077                 if (dist > Max) Max = dist;                
2078             }
2079         }
2080     }
2081
2082     return Max < 1E-12;
2083 }
2084
2085 // Lab to LCh and back should be performed at 1E-12 accuracy at least
2086 static
2087 cmsInt32Number CheckLab2XYZ(void)
2088 {
2089     cmsInt32Number l, a, b;
2090     cmsFloat64Number dist, Max = 0;
2091     cmsCIELab Lab, Lab2;
2092     cmsCIEXYZ XYZ;
2093
2094     for (l=0; l <= 100; l += 10) {
2095
2096         for (a=-128; a <= +128; a += 8) {
2097
2098             for (b=-128; b <= 128; b += 8) {
2099
2100                 Lab.L = l;
2101                 Lab.a = a;
2102                 Lab.b = b;
2103
2104                 cmsLab2XYZ(NULL, &XYZ, &Lab);
2105                 cmsXYZ2Lab(NULL, &Lab2, &XYZ);
2106
2107                 dist = cmsDeltaE(&Lab, &Lab2);
2108                 if (dist > Max) Max = dist;
2109
2110             }
2111         }
2112     }
2113
2114     return Max < 1E-12;
2115 }
2116
2117 // Lab to xyY and back should be performed at 1E-12 accuracy at least
2118 static
2119 cmsInt32Number CheckLab2xyY(void)
2120 {
2121     cmsInt32Number l, a, b;
2122     cmsFloat64Number dist, Max = 0;
2123     cmsCIELab Lab, Lab2;
2124     cmsCIEXYZ XYZ;
2125     cmsCIExyY xyY;
2126
2127     for (l=0; l <= 100; l += 10) {
2128
2129         for (a=-128; a <= +128; a += 8) {
2130
2131             for (b=-128; b <= 128; b += 8) {
2132
2133                 Lab.L = l;
2134                 Lab.a = a;
2135                 Lab.b = b;
2136
2137                 cmsLab2XYZ(NULL, &XYZ, &Lab);
2138                 cmsXYZ2xyY(&xyY, &XYZ);
2139                 cmsxyY2XYZ(&XYZ, &xyY);
2140                 cmsXYZ2Lab(NULL, &Lab2, &XYZ);
2141
2142                 dist = cmsDeltaE(&Lab, &Lab2);
2143                 if (dist > Max) Max = dist;
2144
2145             }
2146         }
2147     }
2148
2149     return Max < 1E-12;
2150 }
2151
2152
2153 static
2154 cmsInt32Number CheckLabV2encoding(void)
2155 {
2156     cmsInt32Number n2, i, j;
2157     cmsUInt16Number Inw[3], aw[3];
2158     cmsCIELab Lab;
2159
2160     n2=0;
2161     
2162     for (j=0; j < 65535; j++) {
2163
2164         Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
2165
2166         cmsLabEncoded2FloatV2(&Lab, Inw);
2167         cmsFloat2LabEncodedV2(aw, &Lab);
2168
2169         for (i=0; i < 3; i++) {
2170
2171         if (aw[i] != j) {
2172             n2++;
2173         }
2174         }
2175
2176     }
2177
2178     return (n2 == 0);
2179 }
2180
2181 static
2182 cmsInt32Number CheckLabV4encoding(void)
2183 {
2184     cmsInt32Number n2, i, j;
2185     cmsUInt16Number Inw[3], aw[3];
2186     cmsCIELab Lab;
2187
2188     n2=0;
2189     
2190     for (j=0; j < 65535; j++) {
2191
2192         Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
2193
2194         cmsLabEncoded2Float(&Lab, Inw);
2195         cmsFloat2LabEncoded(aw, &Lab);
2196         
2197         for (i=0; i < 3; i++) {
2198
2199         if (aw[i] != j) {
2200             n2++;
2201         }
2202         }
2203
2204     }
2205
2206     return (n2 == 0);
2207 }
2208
2209
2210 // BlackBody -----------------------------------------------------------------------------------------------------
2211
2212 static
2213 cmsInt32Number CheckTemp2CHRM(void)
2214 {
2215     cmsInt32Number j;
2216     cmsFloat64Number d, v, Max = 0;
2217     cmsCIExyY White;
2218     
2219     for (j=4000; j < 25000; j++) {
2220
2221         cmsWhitePointFromTemp(&White, j);
2222         if (!cmsTempFromWhitePoint(&v, &White)) return 0;
2223
2224         d = fabs(v - j);
2225         if (d > Max) Max = d;
2226     }
2227
2228     // 100 degree is the actual resolution
2229     return (Max < 100);
2230 }
2231
2232
2233
2234 // Tone curves -----------------------------------------------------------------------------------------------------
2235
2236 static
2237 cmsInt32Number CheckGammaEstimation(cmsToneCurve* c, cmsFloat64Number g)
2238 {
2239     cmsFloat64Number est = cmsEstimateGamma(c, 0.001);
2240
2241     SubTest("Gamma estimation");
2242     if (fabs(est - g) > 0.001) return 0;
2243     return 1;
2244 }
2245
2246 static
2247 cmsInt32Number CheckGammaCreation16(void)
2248 {
2249     cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2250     cmsInt32Number i;
2251     cmsUInt16Number in, out;
2252
2253     for (i=0; i < 0xffff; i++) {
2254
2255         in = (cmsUInt16Number) i;
2256         out = cmsEvalToneCurve16(LinGamma, in);
2257         if (in != out) {
2258             Fail("(lin gamma): Must be %x, But is %x : ", in, out);
2259             cmsFreeToneCurve(LinGamma);
2260             return 0;
2261         }
2262     }       
2263
2264     if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2265
2266     cmsFreeToneCurve(LinGamma);
2267     return 1;
2268
2269 }
2270
2271 static
2272 cmsInt32Number CheckGammaCreationFlt(void)
2273 {
2274     cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
2275     cmsInt32Number i;
2276     cmsFloat32Number in, out;
2277
2278     for (i=0; i < 0xffff; i++) {
2279
2280         in = (cmsFloat32Number) (i / 65535.0);
2281         out = cmsEvalToneCurveFloat(LinGamma, in);
2282         if (fabs(in - out) > (1/65535.0)) {
2283             Fail("(lin gamma): Must be %f, But is %f : ", in, out);
2284             cmsFreeToneCurve(LinGamma);
2285             return 0;
2286         }
2287     }       
2288
2289     if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
2290     cmsFreeToneCurve(LinGamma);
2291     return 1;
2292 }
2293
2294 // Curve curves using a single power function
2295 // Error is given in 0..ffff counts
2296 static
2297 cmsInt32Number CheckGammaFloat(cmsFloat64Number g)
2298 {
2299     cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), g);
2300     cmsInt32Number i;
2301     cmsFloat32Number in, out;
2302     cmsFloat64Number val, Err;
2303
2304     MaxErr = 0.0;
2305     for (i=0; i < 0xffff; i++) {
2306
2307         in = (cmsFloat32Number) (i / 65535.0);
2308         out = cmsEvalToneCurveFloat(Curve, in);
2309         val = pow((cmsFloat64Number) in, g);
2310
2311         Err = fabs( val - out);
2312         if (Err > MaxErr) MaxErr = Err;     
2313     }
2314
2315     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2316
2317     if (!CheckGammaEstimation(Curve, g)) return 0;
2318
2319     cmsFreeToneCurve(Curve);
2320     return 1;
2321 }
2322
2323 static cmsInt32Number CheckGamma18(void) 
2324 {
2325     return CheckGammaFloat(1.8);
2326 }
2327
2328 static cmsInt32Number CheckGamma22(void) 
2329 {
2330     return CheckGammaFloat(2.2);
2331 }
2332
2333 static cmsInt32Number CheckGamma30(void) 
2334 {
2335     return CheckGammaFloat(3.0);
2336 }
2337
2338
2339 // Check table-based gamma functions
2340 static
2341 cmsInt32Number CheckGammaFloatTable(cmsFloat64Number g)
2342 {
2343     cmsFloat32Number Values[1025];
2344     cmsToneCurve* Curve; 
2345     cmsInt32Number i;
2346     cmsFloat32Number in, out;
2347     cmsFloat64Number val, Err;
2348
2349     for (i=0; i <= 1024; i++) {
2350
2351         in = (cmsFloat32Number) (i / 1024.0);
2352         Values[i] = powf(in, (float) g);
2353     }
2354
2355     Curve = cmsBuildTabulatedToneCurveFloat(DbgThread(), 1025, Values);
2356
2357     MaxErr = 0.0;
2358     for (i=0; i <= 0xffff; i++) {
2359
2360         in = (cmsFloat32Number) (i / 65535.0);
2361         out = cmsEvalToneCurveFloat(Curve, in);
2362         val = pow(in, g);
2363
2364         Err = fabs(val - out);
2365         if (Err > MaxErr) MaxErr = Err;     
2366     }
2367
2368     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2369
2370     if (!CheckGammaEstimation(Curve, g)) return 0;
2371
2372     cmsFreeToneCurve(Curve);
2373     return 1;
2374 }
2375
2376
2377 static cmsInt32Number CheckGamma18Table(void) 
2378 {
2379     return CheckGammaFloatTable(1.8);
2380 }
2381
2382 static cmsInt32Number CheckGamma22Table(void) 
2383 {
2384     return CheckGammaFloatTable(2.2);
2385 }
2386
2387 static cmsInt32Number CheckGamma30Table(void) 
2388 {
2389     return CheckGammaFloatTable(3.0);
2390 }
2391
2392 // Create a curve from a table (which is a pure gamma function) and check it against the pow function.
2393 static
2394 cmsInt32Number CheckGammaWordTable(cmsFloat64Number g)
2395 {
2396     cmsUInt16Number Values[1025];
2397     cmsToneCurve* Curve; 
2398     cmsInt32Number i;
2399     cmsFloat32Number in, out;
2400     cmsFloat64Number val, Err;
2401
2402     for (i=0; i <= 1024; i++) {
2403
2404         in = (cmsFloat32Number) (i / 1024.0);
2405         Values[i] = (cmsUInt16Number) floor(pow(in, g) * 65535.0 + 0.5);
2406     }
2407
2408     Curve = cmsBuildTabulatedToneCurve16(DbgThread(), 1025, Values);
2409
2410     MaxErr = 0.0;
2411     for (i=0; i <= 0xffff; i++) {
2412
2413         in = (cmsFloat32Number) (i / 65535.0);
2414         out = cmsEvalToneCurveFloat(Curve, in);
2415         val = pow(in, g);
2416
2417         Err = fabs(val - out);
2418         if (Err > MaxErr) MaxErr = Err;     
2419     }
2420
2421     if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
2422
2423     if (!CheckGammaEstimation(Curve, g)) return 0;
2424
2425     cmsFreeToneCurve(Curve);
2426     return 1;
2427 }
2428
2429 static cmsInt32Number CheckGamma18TableWord(void) 
2430 {
2431     return CheckGammaWordTable(1.8);
2432 }
2433
2434 static cmsInt32Number CheckGamma22TableWord(void) 
2435 {
2436     return CheckGammaWordTable(2.2);
2437 }
2438
2439 static cmsInt32Number CheckGamma30TableWord(void) 
2440 {
2441     return CheckGammaWordTable(3.0);
2442 }
2443
2444
2445 // Curve joining test. Joining two high-gamma of 3.0 curves should
2446 // give something like linear
2447 static
2448 cmsInt32Number CheckJointCurves(void)
2449 {
2450     cmsToneCurve *Forward, *Reverse, *Result;
2451     cmsBool  rc;
2452
2453     Forward = cmsBuildGamma(DbgThread(), 3.0);
2454     Reverse = cmsBuildGamma(DbgThread(), 3.0);
2455
2456     Result = cmsJoinToneCurve(DbgThread(), Forward, Reverse, 256);
2457
2458     cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse); 
2459
2460     rc = cmsIsToneCurveLinear(Result);
2461     cmsFreeToneCurve(Result); 
2462
2463     if (!rc)
2464         Fail("Joining same curve twice does not result in a linear ramp");
2465
2466     return rc;
2467 }
2468
2469
2470 // Create a gamma curve by cheating the table
2471 static
2472 cmsToneCurve* GammaTableLinear(cmsInt32Number nEntries, cmsBool Dir)
2473 {
2474     cmsInt32Number i;
2475     cmsToneCurve* g = cmsBuildTabulatedToneCurve16(DbgThread(), nEntries, NULL);
2476
2477     for (i=0; i < nEntries; i++) {
2478
2479         cmsInt32Number v = _cmsQuantizeVal(i, nEntries);
2480
2481         if (Dir)
2482             g->Table16[i] = (cmsUInt16Number) v;
2483         else
2484             g->Table16[i] = (cmsUInt16Number) (0xFFFF - v);
2485     }
2486
2487     return g;
2488 }
2489
2490
2491 static
2492 cmsInt32Number CheckJointCurvesDescending(void)
2493 {
2494     cmsToneCurve *Forward, *Reverse, *Result;
2495     cmsInt32Number i, rc;
2496
2497      Forward = cmsBuildGamma(DbgThread(), 2.2); 
2498
2499     // Fake the curve to be table-based
2500
2501     for (i=0; i < 4096; i++)
2502         Forward ->Table16[i] = 0xffff - Forward->Table16[i];
2503     Forward ->Segments[0].Type = 0;
2504
2505     Reverse = cmsReverseToneCurve(Forward); 
2506     
2507     Result = cmsJoinToneCurve(DbgThread(), Reverse, Reverse, 256);
2508     
2509     cmsFreeToneCurve(Forward); 
2510     cmsFreeToneCurve(Reverse); 
2511
2512     rc = cmsIsToneCurveLinear(Result);
2513     cmsFreeToneCurve(Result); 
2514
2515     return rc;
2516 }
2517
2518
2519 static
2520 cmsInt32Number CheckFToneCurvePoint(cmsToneCurve* c, cmsUInt16Number Point, cmsInt32Number Value)
2521 {
2522     cmsInt32Number Result;
2523
2524     Result = cmsEvalToneCurve16(c, Point);
2525     
2526     return (abs(Value - Result) < 2);
2527 }
2528
2529 static
2530 cmsInt32Number CheckReverseDegenerated(void)
2531 {
2532     cmsToneCurve* p, *g;
2533     cmsUInt16Number Tab[16];
2534     
2535     Tab[0] = 0;
2536     Tab[1] = 0;
2537     Tab[2] = 0;
2538     Tab[3] = 0;
2539     Tab[4] = 0;
2540     Tab[5] = 0x5555;
2541     Tab[6] = 0x6666;
2542     Tab[7] = 0x7777;
2543     Tab[8] = 0x8888;
2544     Tab[9] = 0x9999;
2545     Tab[10]= 0xffff;
2546     Tab[11]= 0xffff;
2547     Tab[12]= 0xffff;
2548     Tab[13]= 0xffff;
2549     Tab[14]= 0xffff;
2550     Tab[15]= 0xffff;
2551
2552     p = cmsBuildTabulatedToneCurve16(DbgThread(), 16, Tab);
2553     g = cmsReverseToneCurve(p);
2554     
2555     // Now let's check some points
2556     if (!CheckFToneCurvePoint(g, 0x5555, 0x5555)) return 0;
2557     if (!CheckFToneCurvePoint(g, 0x7777, 0x7777)) return 0;
2558
2559     // First point for zero
2560     if (!CheckFToneCurvePoint(g, 0x0000, 0x4444)) return 0;
2561
2562     // Last point
2563     if (!CheckFToneCurvePoint(g, 0xFFFF, 0xFFFF)) return 0;
2564
2565     cmsFreeToneCurve(p);
2566     cmsFreeToneCurve(g);
2567
2568     return 1;
2569 }
2570
2571
2572 // Build a parametric sRGB-like curve
2573 static
2574 cmsToneCurve* Build_sRGBGamma(void)
2575 {
2576     cmsFloat64Number Parameters[5];
2577
2578     Parameters[0] = 2.4;
2579     Parameters[1] = 1. / 1.055;
2580     Parameters[2] = 0.055 / 1.055;
2581     Parameters[3] = 1. / 12.92;
2582     Parameters[4] = 0.04045;    // d
2583
2584     return cmsBuildParametricToneCurve(DbgThread(), 4, Parameters);
2585 }
2586
2587
2588
2589 // Join two gamma tables in floting point format. Result should be a straight line
2590 static
2591 cmsToneCurve* CombineGammaFloat(cmsToneCurve* g1, cmsToneCurve* g2)
2592 {
2593     cmsUInt16Number Tab[256];
2594     cmsFloat32Number f;
2595     cmsInt32Number i;
2596
2597     for (i=0; i < 256; i++) {
2598
2599         f = (cmsFloat32Number) i / 255.0F;
2600         f = cmsEvalToneCurveFloat(g2, cmsEvalToneCurveFloat(g1, f));
2601
2602         Tab[i] = (cmsUInt16Number) floor(f * 65535.0 + 0.5);
2603     }
2604
2605     return  cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
2606 }
2607
2608 // Same of anterior, but using quantized tables
2609 static
2610 cmsToneCurve* CombineGamma16(cmsToneCurve* g1, cmsToneCurve* g2)
2611 {
2612     cmsUInt16Number Tab[256];
2613
2614     cmsInt32Number i;
2615
2616     for (i=0; i < 256; i++) {
2617
2618         cmsUInt16Number wValIn;
2619
2620         wValIn = _cmsQuantizeVal(i, 256);     
2621         Tab[i] = cmsEvalToneCurve16(g2, cmsEvalToneCurve16(g1, wValIn));
2622     }
2623
2624     return  cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
2625 }
2626
2627 static
2628 cmsInt32Number CheckJointFloatCurves_sRGB(void)
2629 {
2630     cmsToneCurve *Forward, *Reverse, *Result;
2631     cmsBool  rc;
2632
2633     Forward = Build_sRGBGamma();
2634     Reverse = cmsReverseToneCurve(Forward);
2635     Result = CombineGammaFloat(Forward, Reverse);
2636     cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse); 
2637
2638     rc = cmsIsToneCurveLinear(Result);
2639     cmsFreeToneCurve(Result); 
2640
2641     return rc;
2642 }
2643
2644 static
2645 cmsInt32Number CheckJoint16Curves_sRGB(void)
2646 {
2647     cmsToneCurve *Forward, *Reverse, *Result;
2648     cmsBool  rc;
2649
2650     Forward = Build_sRGBGamma();
2651     Reverse = cmsReverseToneCurve(Forward);
2652     Result = CombineGamma16(Forward, Reverse);
2653     cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse); 
2654
2655     rc = cmsIsToneCurveLinear(Result);
2656     cmsFreeToneCurve(Result); 
2657
2658     return rc;
2659 }
2660
2661 // sigmoidal curve f(x) = (1-x^g) ^(1/g)
2662
2663 static
2664 cmsInt32Number CheckJointCurvesSShaped(void)
2665 {
2666     cmsFloat64Number p = 3.2;
2667     cmsToneCurve *Forward, *Reverse, *Result;
2668     cmsInt32Number rc;
2669
2670     Forward = cmsBuildParametricToneCurve(DbgThread(), 108, &p);    
2671     Reverse = cmsReverseToneCurve(Forward);
2672     Result = cmsJoinToneCurve(DbgThread(), Forward, Forward, 4096);
2673
2674     cmsFreeToneCurve(Forward); 
2675     cmsFreeToneCurve(Reverse); 
2676
2677     rc = cmsIsToneCurveLinear(Result);
2678     cmsFreeToneCurve(Result); 
2679     return rc;
2680 }
2681
2682
2683 // --------------------------------------------------------------------------------------------------------
2684
2685 // Implementation of some tone curve functions
2686 static
2687 cmsFloat32Number Gamma(cmsFloat32Number x, const cmsFloat64Number Params[])
2688 {
2689     return (cmsFloat32Number) pow(x, Params[0]);
2690 }
2691
2692 static
2693 cmsFloat32Number CIE122(cmsFloat32Number x, const cmsFloat64Number Params[])
2694
2695 {
2696     cmsFloat64Number e, Val;
2697
2698     if (x >= -Params[2] / Params[1]) {
2699
2700         e = Params[1]*x + Params[2];
2701
2702         if (e > 0)
2703             Val = pow(e, Params[0]);
2704         else
2705             Val = 0;
2706     }
2707     else
2708         Val = 0;
2709
2710     return (cmsFloat32Number) Val;
2711 }
2712
2713 static
2714 cmsFloat32Number IEC61966_3(cmsFloat32Number x, const cmsFloat64Number Params[])
2715 {
2716     cmsFloat64Number e, Val;
2717
2718     if (x >= -Params[2] / Params[1]) {
2719
2720         e = Params[1]*x + Params[2];                    
2721
2722         if (e > 0)
2723             Val = pow(e, Params[0]) + Params[3];
2724         else
2725             Val = 0;
2726     }
2727     else
2728         Val = Params[3];
2729
2730     return (cmsFloat32Number) Val;
2731 }
2732
2733 static
2734 cmsFloat32Number IEC61966_21(cmsFloat32Number x, const cmsFloat64Number Params[])
2735 {
2736     cmsFloat64Number e, Val;
2737
2738     if (x >= Params[4]) {
2739
2740         e = Params[1]*x + Params[2];
2741
2742         if (e > 0)
2743             Val = pow(e, Params[0]);
2744         else
2745             Val = 0;
2746     }
2747     else
2748         Val = x * Params[3];
2749
2750     return (cmsFloat32Number) Val;
2751 }
2752
2753 static
2754 cmsFloat32Number param_5(cmsFloat32Number x, const cmsFloat64Number Params[])
2755 {
2756     cmsFloat64Number e, Val;
2757     // Y = (aX + b)^Gamma + e | X >= d
2758     // Y = cX + f             | else
2759     if (x >= Params[4]) {
2760
2761         e = Params[1]*x + Params[2];
2762         if (e > 0)
2763             Val = pow(e, Params[0]) + Params[5];
2764         else
2765             Val = 0;
2766     }        
2767     else
2768         Val = x*Params[3] + Params[6];
2769
2770     return (cmsFloat32Number) Val;
2771 }
2772
2773 static
2774 cmsFloat32Number param_6(cmsFloat32Number x, const cmsFloat64Number Params[])
2775 {
2776     cmsFloat64Number e, Val;
2777     
2778     e = Params[1]*x + Params[2];
2779     if (e > 0) 
2780         Val = pow(e, Params[0]) + Params[3];
2781     else
2782         Val = 0;
2783
2784     return (cmsFloat32Number) Val;
2785 }
2786
2787 static
2788 cmsFloat32Number param_7(cmsFloat32Number x, const cmsFloat64Number Params[])
2789 {
2790     cmsFloat64Number Val;
2791
2792
2793     Val = Params[1]*log10(Params[2] * pow(x, Params[0]) + Params[3]) + Params[4];
2794
2795     return (cmsFloat32Number) Val;
2796 }
2797
2798
2799 static
2800 cmsFloat32Number param_8(cmsFloat32Number x, const cmsFloat64Number Params[])
2801 {
2802     cmsFloat64Number Val;
2803
2804     Val = (Params[0] * pow(Params[1], Params[2] * x + Params[3]) + Params[4]);
2805
2806     return (cmsFloat32Number) Val;
2807 }
2808
2809
2810 static
2811 cmsFloat32Number sigmoidal(cmsFloat32Number x, const cmsFloat64Number Params[])
2812 {
2813     cmsFloat64Number Val;
2814
2815     Val = pow(1.0 - pow(1 - x, 1/Params[0]), 1/Params[0]);
2816
2817     return (cmsFloat32Number) Val;
2818 }
2819
2820
2821 static
2822 cmsBool CheckSingleParametric(const char* Name, dblfnptr fn, cmsInt32Number Type, const cmsFloat64Number Params[])
2823 {
2824     cmsInt32Number i;
2825     cmsToneCurve* tc;
2826     cmsToneCurve* tc_1;
2827     char InverseText[256];
2828
2829     tc = cmsBuildParametricToneCurve(DbgThread(), Type, Params);
2830     tc_1 = cmsBuildParametricToneCurve(DbgThread(), -Type, Params);
2831
2832     for (i=0; i <= 1000; i++) {
2833
2834         cmsFloat32Number x = (cmsFloat32Number) i / 1000;
2835         cmsFloat32Number y_fn, y_param, x_param, y_param2;
2836
2837         y_fn = fn(x, Params);
2838         y_param = cmsEvalToneCurveFloat(tc, x);
2839         x_param = cmsEvalToneCurveFloat(tc_1, y_param);
2840
2841         y_param2 = fn(x_param, Params);
2842
2843         if (!IsGoodVal(Name, y_fn, y_param, FIXED_PRECISION_15_16))
2844             goto Error;
2845
2846         sprintf(InverseText, "Inverse %s", Name);
2847         if (!IsGoodVal(InverseText, y_fn, y_param2, FIXED_PRECISION_15_16)) 
2848             goto Error;       
2849     }
2850
2851     cmsFreeToneCurve(tc);
2852     cmsFreeToneCurve(tc_1);
2853     return TRUE;
2854
2855 Error:
2856     cmsFreeToneCurve(tc);
2857     cmsFreeToneCurve(tc_1);
2858     return FALSE;
2859 }
2860
2861 // Check against some known values
2862 static
2863 cmsInt32Number CheckParametricToneCurves(void)
2864 {
2865     cmsFloat64Number Params[10];
2866
2867      // 1) X = Y ^ Gamma
2868      
2869      Params[0] = 2.2;
2870
2871      if (!CheckSingleParametric("Gamma", Gamma, 1, Params)) return 0;
2872
2873      // 2) CIE 122-1966
2874      // Y = (aX + b)^Gamma  | X >= -b/a
2875      // Y = 0               | else
2876
2877      Params[0] = 2.2;
2878      Params[1] = 1.5;
2879      Params[2] = -0.5;
2880
2881      if (!CheckSingleParametric("CIE122-1966", CIE122, 2, Params)) return 0;
2882
2883      // 3) IEC 61966-3
2884      // Y = (aX + b)^Gamma | X <= -b/a
2885      // Y = c              | else
2886
2887      Params[0] = 2.2;
2888      Params[1] = 1.5;
2889      Params[2] = -0.5;
2890      Params[3] = 0.3;
2891   
2892
2893      if (!CheckSingleParametric("IEC 61966-3", IEC61966_3, 3, Params)) return 0;
2894
2895      // 4) IEC 61966-2.1 (sRGB)
2896      // Y = (aX + b)^Gamma | X >= d
2897      // Y = cX             | X < d
2898
2899      Params[0] = 2.4;
2900      Params[1] = 1. / 1.055;
2901      Params[2] = 0.055 / 1.055;
2902      Params[3] = 1. / 12.92;
2903      Params[4] = 0.04045;    
2904
2905      if (!CheckSingleParametric("IEC 61966-2.1", IEC61966_21, 4, Params)) return 0;
2906
2907                
2908      // 5) Y = (aX + b)^Gamma + e | X >= d
2909      // Y = cX + f             | else
2910
2911      Params[0] = 2.2;
2912      Params[1] = 0.7;
2913      Params[2] = 0.2;
2914      Params[3] = 0.3;
2915      Params[4] = 0.1;
2916      Params[5] = 0.5;
2917      Params[6] = 0.2;
2918      
2919      if (!CheckSingleParametric("param_5", param_5, 5, Params)) return 0;
2920
2921      // 6) Y = (aX + b) ^ Gamma + c
2922
2923      Params[0] = 2.2;
2924      Params[1] = 0.7;
2925      Params[2] = 0.2;
2926      Params[3] = 0.3;
2927      
2928      if (!CheckSingleParametric("param_6", param_6, 6, Params)) return 0;
2929
2930      // 7) Y = a * log (b * X^Gamma + c) + d
2931
2932      Params[0] = 2.2;
2933      Params[1] = 0.9;
2934      Params[2] = 0.9;
2935      Params[3] = 0.02;
2936      Params[4] = 0.1;
2937
2938      if (!CheckSingleParametric("param_7", param_7, 7, Params)) return 0;
2939
2940      // 8) Y = a * b ^ (c*X+d) + e          
2941      
2942      Params[0] = 0.9;
2943      Params[1] = 0.9;
2944      Params[2] = 1.02;
2945      Params[3] = 0.1;
2946      Params[4] = 0.2;
2947
2948      if (!CheckSingleParametric("param_8", param_8, 8, Params)) return 0;
2949
2950      // 108: S-Shaped: (1 - (1-x)^1/g)^1/g
2951
2952      Params[0] = 1.9;
2953      if (!CheckSingleParametric("sigmoidal", sigmoidal, 108, Params)) return 0;
2954
2955      // All OK
2956
2957      return 1;
2958 }
2959
2960 // LUT checks ------------------------------------------------------------------------------
2961
2962 static
2963 cmsInt32Number CheckLUTcreation(void)
2964 {
2965     cmsPipeline* lut;
2966     cmsPipeline* lut2;
2967     cmsInt32Number n1, n2;
2968     
2969     lut = cmsPipelineAlloc(DbgThread(), 1, 1);
2970     n1 = cmsPipelineStageCount(lut);
2971     lut2 = cmsPipelineDup(lut);
2972     n2 = cmsPipelineStageCount(lut2);
2973
2974     cmsPipelineFree(lut);
2975     cmsPipelineFree(lut2);
2976
2977     return (n1 == 0) && (n2 == 0);
2978 }
2979
2980 // Create a MPE for a identity matrix
2981 static
2982 void AddIdentityMatrix(cmsPipeline* lut)
2983 {
2984     const cmsFloat64Number Identity[] = { 1, 0, 0,
2985                           0, 1, 0, 
2986                           0, 0, 1, 
2987                           0, 0, 0 };
2988
2989     cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocMatrix(DbgThread(), 3, 3, Identity, NULL));
2990 }
2991
2992 // Create a MPE for identity cmsFloat32Number CLUT
2993 static
2994 void AddIdentityCLUTfloat(cmsPipeline* lut)
2995 {
2996     const cmsFloat32Number  Table[] = { 
2997
2998         0,    0,    0,    
2999         0,    0,    1.0,
3000
3001         0,    1.0,    0, 
3002         0,    1.0,    1.0,
3003
3004         1.0,    0,    0,    
3005         1.0,    0,    1.0,
3006
3007         1.0,    1.0,    0, 
3008         1.0,    1.0,    1.0
3009     };
3010
3011     cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLutFloat(DbgThread(), 2, 3, 3, Table));
3012 }
3013
3014 // Create a MPE for identity cmsFloat32Number CLUT
3015 static
3016 void AddIdentityCLUT16(cmsPipeline* lut)
3017 {
3018     const cmsUInt16Number Table[] = { 
3019
3020         0,    0,    0,    
3021         0,    0,    0xffff,
3022
3023         0,    0xffff,    0, 
3024         0,    0xffff,    0xffff,
3025
3026         0xffff,    0,    0,    
3027         0xffff,    0,    0xffff,
3028
3029         0xffff,    0xffff,    0, 
3030         0xffff,    0xffff,    0xffff
3031     };
3032    
3033
3034     cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table));
3035 }
3036
3037
3038 // Create a 3 fn identity curves
3039
3040 static 
3041 void Add3GammaCurves(cmsPipeline* lut, cmsFloat64Number Curve)
3042 {
3043     cmsToneCurve* id = cmsBuildGamma(DbgThread(), Curve);
3044     cmsToneCurve* id3[3];
3045
3046     id3[0] = id;
3047     id3[1] = id;
3048     id3[2] = id;
3049
3050     cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, id3));
3051     
3052     cmsFreeToneCurve(id);
3053 }
3054
3055
3056 static
3057 cmsInt32Number CheckFloatLUT(cmsPipeline* lut)
3058 {
3059     cmsInt32Number n1, i, j;
3060     cmsFloat32Number Inf[3], Outf[3];
3061
3062     n1=0;
3063
3064     for (j=0; j < 65535; j++) {
3065
3066         cmsInt32Number af[3];
3067
3068         Inf[0] = Inf[1] = Inf[2] = (cmsFloat32Number) j / 65535.0F;
3069         cmsPipelineEvalFloat(Inf, Outf, lut);
3070
3071         af[0] = (cmsInt32Number) floor(Outf[0]*65535.0 + 0.5);
3072         af[1] = (cmsInt32Number) floor(Outf[1]*65535.0 + 0.5);
3073         af[2] = (cmsInt32Number) floor(Outf[2]*65535.0 + 0.5);
3074
3075         for (i=0; i < 3; i++) {
3076
3077             if (af[i] != j) {
3078                 n1++;
3079             }
3080         }
3081
3082     }
3083
3084     return (n1 == 0);
3085 }
3086
3087
3088 static
3089 cmsInt32Number Check16LUT(cmsPipeline* lut)
3090 {
3091     cmsInt32Number n2, i, j;
3092     cmsUInt16Number Inw[3], Outw[3];
3093
3094     n2=0;
3095     
3096     for (j=0; j < 65535; j++) {
3097
3098         cmsInt32Number aw[3];
3099
3100         Inw[0] = Inw[1] = Inw[2] = (cmsUInt16Number) j;
3101         cmsPipelineEval16(Inw, Outw, lut);
3102         aw[0] = Outw[0];
3103         aw[1] = Outw[1];
3104         aw[2] = Outw[2];
3105
3106         for (i=0; i < 3; i++) {
3107
3108         if (aw[i] != j) {
3109             n2++;
3110         }
3111         }
3112
3113     }
3114
3115     return (n2 == 0);
3116 }
3117
3118
3119 // Check any LUT that is linear
3120 static
3121 cmsInt32Number CheckStagesLUT(cmsPipeline* lut, cmsInt32Number ExpectedStages)
3122 {
3123     
3124     cmsInt32Number nInpChans, nOutpChans, nStages;
3125
3126     nInpChans  = cmsPipelineInputChannels(lut);
3127     nOutpChans = cmsPipelineOutputChannels(lut);
3128     nStages    = cmsPipelineStageCount(lut);
3129     
3130     return (nInpChans == 3) && (nOutpChans == 3) && (nStages == ExpectedStages);
3131 }
3132
3133
3134 static
3135 cmsInt32Number CheckFullLUT(cmsPipeline* lut, cmsInt32Number ExpectedStages)
3136 {
3137     cmsInt32Number rc = CheckStagesLUT(lut, ExpectedStages) && Check16LUT(lut) && CheckFloatLUT(lut);
3138
3139     cmsPipelineFree(lut);
3140     return rc;
3141 }
3142
3143
3144 static
3145 cmsInt32Number Check1StageLUT(void)
3146 {
3147     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3148
3149     AddIdentityMatrix(lut);
3150     return CheckFullLUT(lut, 1);
3151 }
3152
3153
3154
3155 static
3156 cmsInt32Number Check2StageLUT(void)
3157 {
3158     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3159
3160     AddIdentityMatrix(lut);
3161     AddIdentityCLUTfloat(lut);
3162
3163     return CheckFullLUT(lut, 2);
3164 }
3165
3166 static
3167 cmsInt32Number Check2Stage16LUT(void)
3168 {
3169     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3170
3171     AddIdentityMatrix(lut);
3172     AddIdentityCLUT16(lut);
3173
3174     return CheckFullLUT(lut, 2);
3175 }
3176
3177
3178
3179 static
3180 cmsInt32Number Check3StageLUT(void)
3181 {
3182     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3183
3184     AddIdentityMatrix(lut);
3185     AddIdentityCLUTfloat(lut);
3186     Add3GammaCurves(lut, 1.0);
3187
3188     return CheckFullLUT(lut, 3);
3189 }
3190
3191 static
3192 cmsInt32Number Check3Stage16LUT(void)
3193 {
3194     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3195
3196     AddIdentityMatrix(lut);
3197     AddIdentityCLUT16(lut);
3198     Add3GammaCurves(lut, 1.0);
3199
3200     return CheckFullLUT(lut, 3);
3201 }
3202
3203
3204
3205 static
3206 cmsInt32Number Check4StageLUT(void)
3207 {
3208     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3209
3210     AddIdentityMatrix(lut);
3211     AddIdentityCLUTfloat(lut);
3212     Add3GammaCurves(lut, 1.0);
3213     AddIdentityMatrix(lut);
3214
3215     return CheckFullLUT(lut, 4);
3216 }
3217
3218 static
3219 cmsInt32Number Check4Stage16LUT(void)
3220 {
3221     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3222
3223     AddIdentityMatrix(lut);
3224     AddIdentityCLUT16(lut);
3225     Add3GammaCurves(lut, 1.0);
3226     AddIdentityMatrix(lut);
3227
3228     return CheckFullLUT(lut, 4);
3229 }
3230
3231 static
3232 cmsInt32Number Check5StageLUT(void)
3233 {
3234     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3235
3236     AddIdentityMatrix(lut);
3237     AddIdentityCLUTfloat(lut);
3238     Add3GammaCurves(lut, 1.0);
3239     AddIdentityMatrix(lut);
3240     Add3GammaCurves(lut, 1.0);
3241     
3242     return CheckFullLUT(lut, 5);
3243 }
3244
3245
3246 static
3247 cmsInt32Number Check5Stage16LUT(void)
3248 {
3249     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3250
3251     AddIdentityMatrix(lut);
3252     AddIdentityCLUT16(lut);
3253     Add3GammaCurves(lut, 1.0);
3254     AddIdentityMatrix(lut);
3255     Add3GammaCurves(lut, 1.0);
3256     
3257     return CheckFullLUT(lut, 5);
3258 }
3259
3260 static
3261 cmsInt32Number Check6StageLUT(void)
3262 {
3263     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3264
3265     AddIdentityMatrix(lut);
3266     Add3GammaCurves(lut, 1.0);
3267     AddIdentityCLUTfloat(lut);
3268     Add3GammaCurves(lut, 1.0);
3269     AddIdentityMatrix(lut);
3270     Add3GammaCurves(lut, 1.0);
3271     
3272     return CheckFullLUT(lut, 6);
3273 }
3274
3275 static
3276 cmsInt32Number Check6Stage16LUT(void)
3277 {
3278     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3279
3280     AddIdentityMatrix(lut);
3281     Add3GammaCurves(lut, 1.0);
3282     AddIdentityCLUT16(lut);
3283     Add3GammaCurves(lut, 1.0);
3284     AddIdentityMatrix(lut);
3285     Add3GammaCurves(lut, 1.0);
3286     
3287     return CheckFullLUT(lut, 6);
3288 }
3289
3290
3291 static
3292 cmsInt32Number CheckLab2LabLUT(void)
3293 {
3294     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3295     cmsInt32Number rc;
3296
3297     cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3298     cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3299
3300     rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3301
3302     cmsPipelineFree(lut);
3303
3304     return rc;
3305 }
3306
3307
3308 static
3309 cmsInt32Number CheckXYZ2XYZLUT(void)
3310 {
3311     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3312     cmsInt32Number rc;
3313
3314     cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3315     cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3316
3317     rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
3318
3319     cmsPipelineFree(lut);
3320
3321     return rc;
3322 }
3323
3324
3325
3326 static
3327 cmsInt32Number CheckLab2LabMatLUT(void)
3328 {
3329     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3330     cmsInt32Number rc;
3331
3332     cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
3333     AddIdentityMatrix(lut);
3334     cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
3335
3336     rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 3);
3337
3338     cmsPipelineFree(lut);
3339
3340     return rc;
3341 }
3342
3343 static
3344 cmsInt32Number CheckNamedColorLUT(void)
3345 {
3346     cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
3347     cmsNAMEDCOLORLIST* nc;
3348     cmsInt32Number i,j, rc = 1, n2;
3349     cmsUInt16Number PCS[3];
3350     cmsUInt16Number Colorant[cmsMAXCHANNELS];
3351     char Name[255];
3352     cmsUInt16Number Inw[3], Outw[3];
3353
3354
3355
3356     nc = cmsAllocNamedColorList(DbgThread(), 256, 3, "pre", "post");
3357     if (nc == NULL) return 0;
3358
3359     for (i=0; i < 256; i++) {
3360
3361         PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
3362         Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) i;
3363
3364         sprintf(Name, "#%d", i);
3365         if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
3366     }
3367
3368     cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocNamedColor(nc, FALSE));
3369
3370     cmsFreeNamedColorList(nc);
3371     if (rc == 0) return 0;
3372
3373     n2=0;
3374
3375     for (j=0; j < 256; j++) {
3376
3377         Inw[0] = (cmsUInt16Number) j;
3378
3379         cmsPipelineEval16(Inw, Outw, lut);
3380         for (i=0; i < 3; i++) {
3381
3382             if (Outw[i] != j) {
3383                 n2++;
3384             }
3385         }
3386
3387     }
3388
3389     cmsPipelineFree(lut);
3390     return (n2 == 0);
3391 }
3392
3393
3394
3395 // --------------------------------------------------------------------------------------------
3396
3397 // A lightweight test of multilocalized unicode structures. 
3398
3399 static
3400 cmsInt32Number CheckMLU(void)
3401 {
3402     cmsMLU* mlu, *mlu2, *mlu3;
3403     char Buffer[256], Buffer2[256];
3404     cmsInt32Number rc = 1;
3405     cmsInt32Number i;
3406     cmsHPROFILE h= NULL;
3407
3408     // Allocate a MLU structure, no preferred size
3409     mlu = cmsMLUalloc(DbgThread(), 0);    
3410
3411     // Add some localizations
3412     cmsMLUsetWide(mlu, "en", "US", L"Hello, world");
3413     cmsMLUsetWide(mlu, "es", "ES", L"Hola, mundo");
3414     cmsMLUsetWide(mlu, "fr", "FR", L"Bonjour, le monde");
3415     cmsMLUsetWide(mlu, "ca", "CA", L"Hola, mon");
3416
3417
3418     // Check the returned string for each language
3419
3420     cmsMLUgetASCII(mlu, "en", "US", Buffer, 256);
3421     if (strcmp(Buffer, "Hello, world") != 0) rc = 0;
3422
3423
3424     cmsMLUgetASCII(mlu, "es", "ES", Buffer, 256);
3425     if (strcmp(Buffer, "Hola, mundo") != 0) rc = 0;
3426
3427   
3428     cmsMLUgetASCII(mlu, "fr", "FR", Buffer, 256);
3429     if (strcmp(Buffer, "Bonjour, le monde") != 0) rc = 0;
3430
3431    
3432     cmsMLUgetASCII(mlu, "ca", "CA", Buffer, 256);
3433     if (strcmp(Buffer, "Hola, mon") != 0) rc = 0;
3434
3435     if (rc == 0)
3436         Fail("Unexpected string '%s'", Buffer);
3437
3438     // So far, so good.
3439     cmsMLUfree(mlu);
3440
3441     // Now for performance, allocate an empty struct
3442     mlu = cmsMLUalloc(DbgThread(), 0);    
3443
3444     // Fill it with several thousands of different lenguages
3445     for (i=0; i < 4096; i++) {
3446
3447         char Lang[3];
3448         
3449         Lang[0] = (char) (i % 255);
3450         Lang[1] = (char) (i / 255);
3451         Lang[2] = 0;
3452
3453         sprintf(Buffer, "String #%i", i);
3454         cmsMLUsetASCII(mlu, Lang, Lang, Buffer);
3455     }
3456
3457     // Duplicate it
3458     mlu2 = cmsMLUdup(mlu);
3459
3460     // Get rid of original
3461     cmsMLUfree(mlu);
3462
3463     // Check all is still in place
3464     for (i=0; i < 4096; i++) {
3465
3466         char Lang[3];
3467         
3468         Lang[0] = (char)(i % 255);
3469         Lang[1] = (char)(i / 255);
3470         Lang[2] = 0;
3471
3472         cmsMLUgetASCII(mlu2, Lang, Lang, Buffer2, 256);
3473         sprintf(Buffer, "String #%i", i);
3474
3475         if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
3476     }
3477
3478     if (rc == 0) 
3479         Fail("Unexpected string '%s'", Buffer2);
3480
3481     // Check profile IO
3482
3483     h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "w");
3484         
3485     cmsSetProfileVersion(h, 4.3);
3486
3487     cmsWriteTag(h, cmsSigProfileDescriptionTag, mlu2);
3488     cmsCloseProfile(h);
3489     cmsMLUfree(mlu2);
3490
3491     
3492     h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "r");
3493
3494     mlu3 = cmsReadTag(h, cmsSigProfileDescriptionTag);
3495     if (mlu3 == NULL) { Fail("Profile didn't get the MLU\n"); rc = 0; goto Error; }
3496
3497     // Check all is still in place
3498     for (i=0; i < 4096; i++) {
3499
3500         char Lang[3];
3501         
3502         Lang[0] = (char) (i % 255);
3503         Lang[1] = (char) (i / 255);
3504         Lang[2] = 0;
3505
3506         cmsMLUgetASCII(mlu3, Lang, Lang, Buffer2, 256);
3507         sprintf(Buffer, "String #%i", i);
3508
3509         if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
3510     }
3511
3512     if (rc == 0) Fail("Unexpected string '%s'", Buffer2);
3513
3514 Error:
3515
3516     if (h != NULL) cmsCloseProfile(h);
3517     remove("mlucheck.icc");
3518
3519     return rc;
3520 }
3521
3522
3523 // A lightweight test of named color structures.
3524 static
3525 cmsInt32Number CheckNamedColorList(void)
3526 {
3527     cmsNAMEDCOLORLIST* nc = NULL, *nc2;
3528     cmsInt32Number i, j, rc=1;
3529     char Name[255];
3530     cmsUInt16Number PCS[3];
3531     cmsUInt16Number Colorant[cmsMAXCHANNELS];
3532     char CheckName[255];
3533     cmsUInt16Number CheckPCS[3];
3534     cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
3535     cmsHPROFILE h;
3536
3537     nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
3538     if (nc == NULL) return 0;
3539
3540     for (i=0; i < 4096; i++) {
3541
3542
3543         PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
3544         Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) (4096 - i);
3545
3546         sprintf(Name, "#%d", i);
3547         if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
3548     }
3549
3550     for (i=0; i < 4096; i++) {
3551
3552         CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
3553         CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (4096 - i);
3554
3555         sprintf(CheckName, "#%d", i);
3556         if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
3557
3558
3559         for (j=0; j < 3; j++) {
3560             if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
3561         }
3562
3563         for (j=0; j < 4; j++) {
3564             if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
3565         }
3566
3567         if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
3568     }
3569
3570     h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "w");
3571     if (h == NULL) return 0;
3572     if (!cmsWriteTag(h, cmsSigNamedColor2Tag, nc)) return 0;
3573     cmsCloseProfile(h);
3574     cmsFreeNamedColorList(nc);
3575     nc = NULL;
3576
3577     h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "r");
3578     nc2 = cmsReadTag(h, cmsSigNamedColor2Tag);
3579
3580     if (cmsNamedColorCount(nc2) != 4096) { rc = 0; Fail("Invalid count"); goto Error; }
3581
3582     i = cmsNamedColorIndex(nc2, "#123");
3583     if (i != 123) { rc = 0; Fail("Invalid index"); goto Error; }
3584
3585
3586     for (i=0; i < 4096; i++) {
3587
3588         CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
3589         CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (4096 - i);
3590
3591         sprintf(CheckName, "#%d", i);
3592         if (!cmsNamedColorInfo(nc2, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
3593
3594
3595         for (j=0; j < 3; j++) {
3596             if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
3597         }
3598
3599         for (j=0; j < 4; j++) {
3600             if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
3601         }
3602
3603         if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
3604     }
3605
3606     cmsCloseProfile(h);
3607     remove("namedcol.icc");
3608
3609 Error:
3610     if (nc != NULL) cmsFreeNamedColorList(nc);
3611     return rc;
3612 }
3613
3614
3615
3616 // ----------------------------------------------------------------------------------------------------------
3617
3618 // Formatters
3619
3620 static cmsBool  FormatterFailed;
3621
3622 static
3623 void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text)
3624 {
3625     cmsUInt16Number Values[cmsMAXCHANNELS];
3626     cmsUInt8Number Buffer[1024];
3627     cmsFormatter f, b;
3628     cmsInt32Number i, j, nChannels, bytes;
3629     _cmsTRANSFORM info;
3630
3631     // Already failed?
3632     if (FormatterFailed) return;
3633
3634     memset(&info, 0, sizeof(info));
3635     info.OutputFormat = info.InputFormat = Type;
3636
3637     // Go forth and back
3638     f = _cmsGetFormatter(Type,  cmsFormatterInput, 0);
3639     b = _cmsGetFormatter(Type,  cmsFormatterOutput, 0);
3640
3641     if (f.Fmt16 == NULL || b.Fmt16 == NULL) {
3642         Fail("no formatter for %s", Text);
3643         FormatterFailed = TRUE;
3644
3645         // Useful for debug
3646         f = _cmsGetFormatter(Type,  cmsFormatterInput, 0);
3647         b = _cmsGetFormatter(Type,  cmsFormatterOutput, 0);
3648         return;
3649     }
3650
3651     nChannels = T_CHANNELS(Type);
3652     bytes     = T_BYTES(Type);
3653
3654     for (j=0; j < 5; j++) {
3655
3656         for (i=0; i < nChannels; i++) { 
3657             Values[i] = (cmsUInt16Number) (i+j);
3658             // For 8-bit
3659             if (bytes == 1) 
3660                 Values[i] <<= 8;
3661         }
3662
3663     b.Fmt16(&info, Values, Buffer, 1);
3664     memset(Values, 0, sizeof(Values));
3665     f.Fmt16(&info, Values, Buffer, 1);
3666
3667     for (i=0; i < nChannels; i++) {
3668         if (bytes == 1) 
3669             Values[i] >>= 8;
3670
3671         if (Values[i] != i+j) {
3672
3673             Fail("%s failed", Text);        
3674             FormatterFailed = TRUE;
3675
3676             // Useful for debug
3677             for (i=0; i < nChannels; i++) { 
3678                 Values[i] = (cmsUInt16Number) (i+j);
3679                 // For 8-bit
3680                 if (bytes == 1) 
3681                     Values[i] <<= 8;
3682             }
3683
3684             b.Fmt16(&info, Values, Buffer, 1);
3685             f.Fmt16(&info, Values, Buffer, 1);
3686             return;
3687         }
3688     }
3689     }
3690 }
3691
3692 #define C(a) CheckSingleFormatter16(a, #a)
3693
3694
3695 // Check all formatters
3696 static
3697 cmsInt32Number CheckFormatters16(void)
3698 {
3699     FormatterFailed = FALSE;
3700
3701    C( TYPE_GRAY_8            );
3702    C( TYPE_GRAY_8_REV        );
3703    C( TYPE_GRAY_16           );
3704    C( TYPE_GRAY_16_REV       );
3705    C( TYPE_GRAY_16_SE        );
3706    C( TYPE_GRAYA_8           );
3707    C( TYPE_GRAYA_16          );
3708    C( TYPE_GRAYA_16_SE       );
3709    C( TYPE_GRAYA_8_PLANAR    );
3710    C( TYPE_GRAYA_16_PLANAR   );
3711    C( TYPE_RGB_8             );
3712    C( TYPE_RGB_8_PLANAR      );
3713    C( TYPE_BGR_8             );
3714    C( TYPE_BGR_8_PLANAR      );
3715    C( TYPE_RGB_16            );
3716    C( TYPE_RGB_16_PLANAR     );
3717    C( TYPE_RGB_16_SE         );
3718    C( TYPE_BGR_16            );
3719    C( TYPE_BGR_16_PLANAR     );
3720    C( TYPE_BGR_16_SE         );
3721    C( TYPE_RGBA_8            );
3722    C( TYPE_RGBA_8_PLANAR     );
3723    C( TYPE_RGBA_16           );
3724    C( TYPE_RGBA_16_PLANAR    );
3725    C( TYPE_RGBA_16_SE        );
3726    C( TYPE_ARGB_8            );
3727    C( TYPE_ARGB_16           );
3728    C( TYPE_ABGR_8            );
3729    C( TYPE_ABGR_16           );
3730    C( TYPE_ABGR_16_PLANAR    );
3731    C( TYPE_ABGR_16_SE        );
3732    C( TYPE_BGRA_8            );
3733    C( TYPE_BGRA_16           );
3734    C( TYPE_BGRA_16_SE        );
3735    C( TYPE_CMY_8             );
3736    C( TYPE_CMY_8_PLANAR      );
3737    C( TYPE_CMY_16            );
3738    C( TYPE_CMY_16_PLANAR     );
3739    C( TYPE_CMY_16_SE         );
3740    C( TYPE_CMYK_8            );
3741    C( TYPE_CMYKA_8           );
3742    C( TYPE_CMYK_8_REV        );
3743    C( TYPE_YUVK_8            );
3744    C( TYPE_CMYK_8_PLANAR     );
3745    C( TYPE_CMYK_16           );
3746    C( TYPE_CMYK_16_REV       );
3747    C( TYPE_YUVK_16           );
3748    C( TYPE_CMYK_16_PLANAR    );
3749    C( TYPE_CMYK_16_SE        );
3750    C( TYPE_KYMC_8            );
3751    C( TYPE_KYMC_16           );
3752    C( TYPE_KYMC_16_SE        );
3753    C( TYPE_KCMY_8            );
3754    C( TYPE_KCMY_8_REV        );
3755    C( TYPE_KCMY_16           );
3756    C( TYPE_KCMY_16_REV       );
3757    C( TYPE_KCMY_16_SE        );
3758    C( TYPE_CMYK5_8           );
3759    C( TYPE_CMYK5_16          );
3760    C( TYPE_CMYK5_16_SE       );
3761    C( TYPE_KYMC5_8           );
3762    C( TYPE_KYMC5_16          );
3763    C( TYPE_KYMC5_16_SE       );
3764    C( TYPE_CMYK6_8          );
3765    C( TYPE_CMYK6_8_PLANAR   );
3766    C( TYPE_CMYK6_16         );
3767    C( TYPE_CMYK6_16_PLANAR  );
3768    C( TYPE_CMYK6_16_SE      );
3769    C( TYPE_CMYK7_8           );
3770    C( TYPE_CMYK7_16          );
3771    C( TYPE_CMYK7_16_SE       );
3772    C( TYPE_KYMC7_8           );
3773    C( TYPE_KYMC7_16          );
3774    C( TYPE_KYMC7_16_SE       );
3775    C( TYPE_CMYK8_8           );
3776    C( TYPE_CMYK8_16          );
3777    C( TYPE_CMYK8_16_SE       );
3778    C( TYPE_KYMC8_8           );
3779    C( TYPE_KYMC8_16          );
3780    C( TYPE_KYMC8_16_SE       );
3781    C( TYPE_CMYK9_8           );
3782    C( TYPE_CMYK9_16          );
3783    C( TYPE_CMYK9_16_SE       );
3784    C( TYPE_KYMC9_8           );
3785    C( TYPE_KYMC9_16          );
3786    C( TYPE_KYMC9_16_SE       );
3787    C( TYPE_CMYK10_8          );
3788    C( TYPE_CMYK10_16         );
3789    C( TYPE_CMYK10_16_SE      );
3790    C( TYPE_KYMC10_8          );
3791    C( TYPE_KYMC10_16         );
3792    C( TYPE_KYMC10_16_SE      );
3793    C( TYPE_CMYK11_8          );
3794    C( TYPE_CMYK11_16         );
3795    C( TYPE_CMYK11_16_SE      );
3796    C( TYPE_KYMC11_8          );
3797    C( TYPE_KYMC11_16         );
3798    C( TYPE_KYMC11_16_SE      );
3799    C( TYPE_CMYK12_8          );
3800    C( TYPE_CMYK12_16         );
3801    C( TYPE_CMYK12_16_SE      );
3802    C( TYPE_KYMC12_8          );
3803    C( TYPE_KYMC12_16         );
3804    C( TYPE_KYMC12_16_SE      );
3805    C( TYPE_XYZ_16            );
3806    C( TYPE_Lab_8             );
3807    C( TYPE_ALab_8            );
3808    C( TYPE_Lab_16            );
3809    C( TYPE_Yxy_16            );
3810    C( TYPE_YCbCr_8           );
3811    C( TYPE_YCbCr_8_PLANAR    );
3812    C( TYPE_YCbCr_16          );
3813    C( TYPE_YCbCr_16_PLANAR   );
3814    C( TYPE_YCbCr_16_SE       );
3815    C( TYPE_YUV_8             );
3816    C( TYPE_YUV_8_PLANAR      );
3817    C( TYPE_YUV_16            );
3818    C( TYPE_YUV_16_PLANAR     );
3819    C( TYPE_YUV_16_SE         );
3820    C( TYPE_HLS_8             );
3821    C( TYPE_HLS_8_PLANAR      );
3822    C( TYPE_HLS_16            );
3823    C( TYPE_HLS_16_PLANAR     );
3824    C( TYPE_HLS_16_SE         );
3825    C( TYPE_HSV_8             );
3826    C( TYPE_HSV_8_PLANAR      );
3827    C( TYPE_HSV_16            );
3828    C( TYPE_HSV_16_PLANAR     );
3829    C( TYPE_HSV_16_SE         );
3830         
3831    C( TYPE_XYZ_FLT  );
3832    C( TYPE_Lab_FLT  );
3833    C( TYPE_GRAY_FLT );
3834    C( TYPE_RGB_FLT  );
3835    C( TYPE_CMYK_FLT );
3836    C( TYPE_XYZA_FLT );
3837    C( TYPE_LabA_FLT );
3838    C( TYPE_RGBA_FLT );
3839
3840    C( TYPE_XYZ_DBL  );
3841    C( TYPE_Lab_DBL  );
3842    C( TYPE_GRAY_DBL );
3843    C( TYPE_RGB_DBL  );
3844    C( TYPE_CMYK_DBL );
3845
3846    C( TYPE_LabV2_8  );
3847    C( TYPE_ALabV2_8 );
3848    C( TYPE_LabV2_16 );
3849
3850    return FormatterFailed == 0 ? 1 : 0;
3851 }
3852 #undef C
3853
3854 static
3855 void CheckSingleFormatterFloat(cmsUInt32Number Type, const char* Text)
3856 {
3857     cmsFloat32Number Values[cmsMAXCHANNELS];
3858     cmsUInt8Number Buffer[1024];
3859     cmsFormatter f, b;
3860     cmsInt32Number i, j, nChannels;
3861     _cmsTRANSFORM info;
3862
3863     // Already failed?
3864     if (FormatterFailed) return;
3865
3866     memset(&info, 0, sizeof(info));
3867     info.OutputFormat = info.InputFormat = Type;
3868
3869     // Go forth and back
3870     f = _cmsGetFormatter(Type,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
3871     b = _cmsGetFormatter(Type,  cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
3872
3873     if (f.FmtFloat == NULL || b.FmtFloat == NULL) {
3874         Fail("no formatter for %s", Text);
3875         FormatterFailed = TRUE;
3876
3877         // Useful for debug
3878         f = _cmsGetFormatter(Type,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
3879         b = _cmsGetFormatter(Type,  cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
3880         return;
3881     }
3882
3883     nChannels = T_CHANNELS(Type);
3884
3885     for (j=0; j < 5; j++) {
3886
3887         for (i=0; i < nChannels; i++) { 
3888             Values[i] = (cmsFloat32Number) (i+j);
3889         }
3890
3891         b.FmtFloat(&info, Values, Buffer, 1);
3892         memset(Values, 0, sizeof(Values));
3893         f.FmtFloat(&info, Values, Buffer, 1);
3894
3895         for (i=0; i < nChannels; i++) {
3896
3897             cmsFloat64Number delta = fabs(Values[i] - ( i+j));
3898
3899             if (delta > 0.000000001) {
3900
3901                 Fail("%s failed", Text);        
3902                 FormatterFailed = TRUE;
3903
3904                 // Useful for debug
3905                 for (i=0; i < nChannels; i++) { 
3906                     Values[i] = (cmsFloat32Number) (i+j);
3907                 }
3908
3909                 b.FmtFloat(&info, Values, Buffer, 1);
3910                 f.FmtFloat(&info, Values, Buffer, 1);
3911                 return;
3912             }
3913         }
3914     }
3915 }
3916
3917 #define C(a) CheckSingleFormatterFloat(a, #a)
3918
3919 static
3920 cmsInt32Number CheckFormattersFloat(void)
3921 {
3922     FormatterFailed = FALSE;
3923
3924     C( TYPE_XYZ_FLT  ); 
3925     C( TYPE_Lab_FLT  );
3926     C( TYPE_GRAY_FLT );
3927     C( TYPE_RGB_FLT  );
3928     C( TYPE_CMYK_FLT );
3929
3930     // User
3931     C( TYPE_XYZA_FLT );
3932     C( TYPE_LabA_FLT );
3933     C( TYPE_RGBA_FLT );
3934
3935     C( TYPE_XYZ_DBL  );
3936     C( TYPE_Lab_DBL  );
3937     C( TYPE_GRAY_DBL );
3938     C( TYPE_RGB_DBL  );
3939     C( TYPE_CMYK_DBL );
3940
3941     return FormatterFailed == 0 ? 1 : 0;
3942 }
3943 #undef C
3944
3945
3946 static
3947 cmsInt32Number CheckOneRGB(cmsHTRANSFORM xform, cmsUInt32Number R, cmsUInt32Number G, cmsUInt32Number B, cmsUInt32Number Ro, cmsUInt32Number Go, cmsUInt32Number Bo)
3948 {
3949     cmsUInt16Number RGB[3];
3950     cmsUInt16Number Out[3];
3951
3952     RGB[0] = R;
3953     RGB[1] = G;
3954     RGB[2] = B;
3955
3956     cmsDoTransform(xform, RGB, Out, 1);
3957
3958     return IsGoodWord("R", Ro , Out[0]) &&
3959            IsGoodWord("G", Go , Out[1]) &&
3960            IsGoodWord("B", Bo , Out[2]);
3961 }
3962
3963 // Check known values going from sRGB to XYZ
3964 static
3965 cmsInt32Number CheckOneRGB_double(cmsHTRANSFORM xform, cmsFloat64Number R, cmsFloat64Number G, cmsFloat64Number B, cmsFloat64Number Ro, cmsFloat64Number Go, cmsFloat64Number Bo)
3966 {
3967     cmsFloat64Number RGB[3];
3968     cmsFloat64Number Out[3];
3969
3970     RGB[0] = R;
3971     RGB[1] = G;
3972     RGB[2] = B;
3973
3974     cmsDoTransform(xform, RGB, Out, 1);
3975
3976     return IsGoodVal("R", Ro , Out[0], 0.01) &&
3977            IsGoodVal("G", Go , Out[1], 0.01) &&
3978            IsGoodVal("B", Bo , Out[2], 0.01);
3979 }
3980
3981
3982 static
3983 cmsInt32Number CheckChangeBufferFormat(void)
3984 {
3985     cmsHPROFILE hsRGB = cmsCreate_sRGBProfile();
3986     cmsHTRANSFORM xform;
3987
3988
3989     xform = cmsCreateTransform(hsRGB, TYPE_RGB_16, hsRGB, TYPE_RGB_16, INTENT_PERCEPTUAL, 0);
3990     cmsCloseProfile(hsRGB);
3991     if (xform == NULL) return 0;
3992
3993     
3994     if (!CheckOneRGB(xform, 0, 0, 0, 0, 0, 0)) return 0;
3995     if (!CheckOneRGB(xform, 120, 0, 0, 120, 0, 0)) return 0;
3996     if (!CheckOneRGB(xform, 0, 222, 255, 0, 222, 255)) return 0;
3997
3998
3999     if (!cmsChangeBuffersFormat(xform, TYPE_BGR_16, TYPE_RGB_16)) return 0;
4000
4001     if (!CheckOneRGB(xform, 0, 0, 123, 123, 0, 0)) return 0;
4002     if (!CheckOneRGB(xform, 154, 234, 0, 0, 234, 154)) return 0;
4003
4004     if (!cmsChangeBuffersFormat(xform, TYPE_RGB_DBL, TYPE_RGB_DBL)) return 0;
4005
4006     if (!CheckOneRGB_double(xform, 0.20, 0, 0, 0.20, 0, 0)) return 0;
4007     if (!CheckOneRGB_double(xform, 0, 0.9, 1, 0, 0.9, 1)) return 0;
4008
4009     cmsDeleteTransform(xform);
4010     
4011 return 1;
4012 }
4013
4014
4015 // Write tag testbed ----------------------------------------------------------------------------------------
4016
4017 static
4018 cmsInt32Number CheckXYZ(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4019 {
4020     cmsCIEXYZ XYZ, *Pt;
4021
4022     
4023     switch (Pass) {
4024
4025         case 1:
4026
4027             XYZ.X = 1.0; XYZ.Y = 1.1; XYZ.Z = 1.2;
4028             return cmsWriteTag(hProfile, tag, &XYZ);
4029     
4030         case 2:
4031             Pt = cmsReadTag(hProfile, tag);
4032             if (Pt == NULL) return 0;
4033             return IsGoodFixed15_16("X", 1.0, Pt ->X) &&
4034                    IsGoodFixed15_16("Y", 1.1, Pt->Y) &&
4035                    IsGoodFixed15_16("Z", 1.2, Pt -> Z);
4036
4037         default:
4038             return 0;
4039     }
4040 }
4041
4042
4043 static
4044 cmsInt32Number CheckGamma(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4045 {
4046     cmsToneCurve *g, *Pt;
4047     cmsInt32Number rc;
4048
4049     switch (Pass) {
4050
4051         case 1:
4052
4053             g = cmsBuildGamma(DbgThread(), 1.0);
4054             rc = cmsWriteTag(hProfile, tag, g);
4055             cmsFreeToneCurve(g);
4056             return rc;
4057     
4058         case 2:
4059             Pt = cmsReadTag(hProfile, tag);
4060             if (Pt == NULL) return 0;
4061             return cmsIsToneCurveLinear(Pt);
4062             
4063         default:
4064             return 0;
4065     }
4066 }
4067
4068 static
4069 cmsInt32Number CheckText(cmsInt32Number Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
4070 {
4071     cmsMLU *m, *Pt;
4072     cmsInt32Number rc;
4073     char Buffer[256];
4074
4075     
4076     switch (Pass) {
4077
4078         case 1:
4079             m = cmsMLUalloc(DbgThread(), 0);
4080             cmsMLUsetASCII(m, cmsNoLanguage, cmsNoCountry, "Test test");
4081             rc = cmsWriteTag(hProfile, tag, m);
4082             cmsMLUfree(m);
4083             return rc;
4084     
4085         case 2:
4086             Pt = cmsReadTag(hProfile, tag);
4087             if (Pt == NULL) return 0;
4088             cmsMLUgetASCII(Pt, cmsNoLanguage, cmsNoCountry, Buffer, 256);
4089             return strcmp(Buffer, "Test test") == 0;
4090             
4091         default:
4092             return 0;
4093     }
4094 }
4095
4096 static
4097 cmsInt32Number CheckData(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4098 {
4099     cmsICCData *Pt;
4100     cmsICCData d = { 1, 0, { '?' }};
4101     cmsInt32Number rc;
4102
4103     
4104     switch (Pass) {
4105
4106         case 1:
4107             rc = cmsWriteTag(hProfile, tag, &d);
4108             return rc;
4109     
4110         case 2:
4111             Pt = cmsReadTag(hProfile, tag);
4112             if (Pt == NULL) return 0;
4113             return (Pt ->data[0] == '?') && (Pt ->flag == 0) && (Pt ->len == 1);
4114
4115         default:
4116             return 0;
4117     }
4118 }
4119
4120
4121 static
4122 cmsInt32Number CheckSignature(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4123 {
4124     cmsTagSignature *Pt, Holder;
4125     
4126     switch (Pass) {
4127
4128         case 1:
4129             Holder = cmsSigPerceptualReferenceMediumGamut;
4130             return cmsWriteTag(hProfile, tag, &Holder);
4131     
4132         case 2:
4133             Pt = cmsReadTag(hProfile, tag);
4134             if (Pt == NULL) return 0;
4135             return *Pt == cmsSigPerceptualReferenceMediumGamut;
4136
4137         default:
4138             return 0;
4139     }
4140 }
4141
4142
4143 static
4144 cmsInt32Number CheckDateTime(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4145 {
4146     struct tm *Pt, Holder;
4147
4148     switch (Pass) {
4149
4150         case 1:
4151     
4152             Holder.tm_hour = 1;
4153             Holder.tm_min = 2;
4154             Holder.tm_sec = 3;
4155             Holder.tm_mday = 4;
4156             Holder.tm_mon = 5;
4157             Holder.tm_year = 2009 - 1900;
4158             return cmsWriteTag(hProfile, tag, &Holder);
4159     
4160         case 2:
4161             Pt = cmsReadTag(hProfile, tag);
4162             if (Pt == NULL) return 0;
4163
4164             return (Pt ->tm_hour == 1 && 
4165                 Pt ->tm_min == 2 && 
4166                 Pt ->tm_sec == 3 &&
4167                 Pt ->tm_mday == 4 &&
4168                 Pt ->tm_mon == 5 &&
4169                 Pt ->tm_year == 2009 - 1900);
4170
4171         default:
4172             return 0;
4173     }
4174
4175 }
4176
4177
4178 static
4179 cmsInt32Number CheckNamedColor(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag, cmsInt32Number max_check, cmsBool  colorant_check)
4180 {
4181     cmsNAMEDCOLORLIST* nc;
4182     cmsInt32Number i, j, rc;
4183     char Name[255];
4184     cmsUInt16Number PCS[3];
4185     cmsUInt16Number Colorant[cmsMAXCHANNELS];
4186     char CheckName[255];
4187     cmsUInt16Number CheckPCS[3];
4188     cmsUInt16Number CheckColorant[cmsMAXCHANNELS];
4189
4190     switch (Pass) {
4191
4192     case 1:
4193
4194         nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
4195         if (nc == NULL) return 0;
4196
4197         for (i=0; i < max_check; i++) {
4198
4199             PCS[0] = PCS[1] = PCS[2] = (cmsUInt16Number) i;
4200             Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = (cmsUInt16Number) (max_check - i);
4201
4202             sprintf(Name, "#%d", i);
4203             if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { Fail("Couldn't append named color"); return 0; }
4204         }
4205
4206         rc = cmsWriteTag(hProfile, tag, nc);
4207         cmsFreeNamedColorList(nc);
4208         return rc;
4209
4210     case 2:
4211
4212         nc = cmsReadTag(hProfile, tag);
4213         if (nc == NULL) return 0;
4214
4215         for (i=0; i < max_check; i++) {
4216
4217             CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = (cmsUInt16Number) i;
4218             CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = (cmsUInt16Number) (max_check - i);
4219
4220             sprintf(CheckName, "#%d", i);
4221             if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { Fail("Invalid string"); return 0; }
4222
4223
4224             for (j=0; j < 3; j++) {
4225                 if (CheckPCS[j] != PCS[j]) {  Fail("Invalid PCS"); return 0; }
4226             }
4227
4228             // This is only used on named color list
4229             if (colorant_check) {
4230
4231             for (j=0; j < 4; j++) {
4232                 if (CheckColorant[j] != Colorant[j]) { Fail("Invalid Colorant"); return 0; };
4233             }
4234             }
4235
4236             if (strcmp(Name, CheckName) != 0) { Fail("Invalid Name");  return 0; };
4237         }
4238         return 1;
4239
4240
4241     default: return 0;
4242     }
4243 }
4244
4245
4246 static
4247 cmsInt32Number CheckLUT(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4248 {
4249     cmsPipeline* Lut, *Pt;
4250     cmsInt32Number rc;
4251
4252
4253     switch (Pass) {
4254
4255         case 1:
4256
4257             Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
4258             if (Lut == NULL) return 0;
4259
4260             // Create an identity LUT
4261             cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(DbgThread(), 3));
4262             cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCLut(DbgThread(), 3));
4263             cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCurves(DbgThread(), 3));
4264         
4265             rc =  cmsWriteTag(hProfile, tag, Lut);
4266             cmsPipelineFree(Lut);
4267             return rc;
4268
4269         case 2:
4270             Pt = cmsReadTag(hProfile, tag);
4271             if (Pt == NULL) return 0;
4272
4273             // Transform values, check for identity
4274             return Check16LUT(Pt);
4275
4276         default:
4277             return 0;
4278     }
4279 }
4280
4281 static
4282 cmsInt32Number CheckCHAD(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4283 {
4284     cmsFloat64Number *Pt;
4285     cmsFloat64Number CHAD[] = { 0, .1, .2, .3, .4, .5, .6, .7, .8 };
4286     cmsInt32Number i;
4287
4288     switch (Pass) {
4289
4290         case 1:         
4291             return cmsWriteTag(hProfile, tag, CHAD);
4292             
4293
4294         case 2:
4295             Pt = cmsReadTag(hProfile, tag);
4296             if (Pt == NULL) return 0;
4297
4298             for (i=0; i < 9; i++) {
4299                 if (!IsGoodFixed15_16("CHAD", Pt[i], CHAD[i])) return 0;
4300             }
4301
4302             return 1;
4303
4304         default:
4305             return 0;
4306     }
4307 }
4308
4309 static
4310 cmsInt32Number CheckChromaticity(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4311 {
4312     cmsCIExyYTRIPLE *Pt, c = { {0, .1, 1 }, { .3, .4, 1 }, { .6, .7, 1 }};
4313
4314     switch (Pass) {
4315
4316         case 1:         
4317             return cmsWriteTag(hProfile, tag, &c);
4318             
4319
4320         case 2:
4321             Pt = cmsReadTag(hProfile, tag);
4322             if (Pt == NULL) return 0;
4323
4324             if (!IsGoodFixed15_16("xyY", Pt ->Red.x, c.Red.x)) return 0;
4325             if (!IsGoodFixed15_16("xyY", Pt ->Red.y, c.Red.y)) return 0;
4326             if (!IsGoodFixed15_16("xyY", Pt ->Green.x, c.Green.x)) return 0;
4327             if (!IsGoodFixed15_16("xyY", Pt ->Green.y, c.Green.y)) return 0;
4328             if (!IsGoodFixed15_16("xyY", Pt ->Blue.x, c.Blue.x)) return 0;
4329             if (!IsGoodFixed15_16("xyY", Pt ->Blue.y, c.Blue.y)) return 0;
4330             return 1;
4331
4332         default:
4333             return 0;
4334     }
4335 }
4336
4337
4338 static
4339 cmsInt32Number CheckColorantOrder(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4340 {
4341     cmsUInt8Number *Pt, c[cmsMAXCHANNELS];
4342     cmsInt32Number i;
4343
4344     switch (Pass) {
4345
4346         case 1:         
4347             for (i=0; i < cmsMAXCHANNELS; i++) c[i] = (cmsUInt8Number) (cmsMAXCHANNELS - i - 1);
4348             return cmsWriteTag(hProfile, tag, c);
4349             
4350
4351         case 2:
4352             Pt = cmsReadTag(hProfile, tag);
4353             if (Pt == NULL) return 0;
4354
4355             for (i=0; i < cmsMAXCHANNELS; i++) {
4356                 if (Pt[i] != ( cmsMAXCHANNELS - i - 1 )) return 0;
4357             }
4358             return 1;
4359
4360         default:
4361             return 0;
4362     }
4363 }
4364
4365 static
4366 cmsInt32Number CheckMeasurement(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4367 {
4368     cmsICCMeasurementConditions *Pt, m;
4369
4370     switch (Pass) {
4371
4372         case 1:         
4373             m.Backing.X = 0.1;
4374             m.Backing.Y = 0.2;
4375             m.Backing.Z = 0.3;
4376             m.Flare = 1.0;
4377             m.Geometry = 1;
4378             m.IlluminantType = cmsILLUMINANT_TYPE_D50;
4379             m.Observer = 1;
4380             return cmsWriteTag(hProfile, tag, &m);
4381             
4382
4383         case 2:
4384             Pt = cmsReadTag(hProfile, tag);
4385             if (Pt == NULL) return 0;
4386
4387             if (!IsGoodFixed15_16("Backing", Pt ->Backing.X, 0.1)) return 0;
4388             if (!IsGoodFixed15_16("Backing", Pt ->Backing.Y, 0.2)) return 0;
4389             if (!IsGoodFixed15_16("Backing", Pt ->Backing.Z, 0.3)) return 0;
4390             if (!IsGoodFixed15_16("Flare",   Pt ->Flare, 1.0)) return 0;
4391
4392             if (Pt ->Geometry != 1) return 0;
4393             if (Pt ->IlluminantType != cmsILLUMINANT_TYPE_D50) return 0;
4394             if (Pt ->Observer != 1) return 0;
4395             return 1;
4396
4397         default:
4398             return 0;
4399     }
4400 }
4401
4402
4403 static
4404 cmsInt32Number CheckUcrBg(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4405 {
4406     cmsUcrBg *Pt, m;
4407     cmsInt32Number rc;
4408     char Buffer[256];
4409
4410     switch (Pass) {
4411
4412         case 1:         
4413             m.Ucr = cmsBuildGamma(DbgThread(), 2.4);
4414             m.Bg  = cmsBuildGamma(DbgThread(), -2.2);
4415             m.Desc = cmsMLUalloc(DbgThread(), 1);
4416             cmsMLUsetASCII(m.Desc,  cmsNoLanguage, cmsNoCountry, "test UCR/BG");         
4417             rc = cmsWriteTag(hProfile, tag, &m);
4418             cmsMLUfree(m.Desc);
4419             cmsFreeToneCurve(m.Bg);
4420             cmsFreeToneCurve(m.Ucr);
4421             return rc;
4422             
4423
4424         case 2:
4425             Pt = cmsReadTag(hProfile, tag);
4426             if (Pt == NULL) return 0;
4427
4428             cmsMLUgetASCII(Pt ->Desc, cmsNoLanguage, cmsNoCountry, Buffer, 256);
4429             if (strcmp(Buffer, "test UCR/BG") != 0) return 0;       
4430             return 1;
4431
4432         default:
4433             return 0;
4434     }
4435 }
4436
4437
4438 static
4439 cmsInt32Number CheckCRDinfo(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4440 {
4441     cmsMLU *mlu;
4442     char Buffer[256];
4443     cmsInt32Number rc;
4444
4445     switch (Pass) {
4446
4447         case 1: 
4448             mlu = cmsMLUalloc(DbgThread(), 5);
4449
4450             cmsMLUsetWide(mlu,  "PS", "nm", L"test postscript");
4451             cmsMLUsetWide(mlu,  "PS", "#0", L"perceptual");
4452             cmsMLUsetWide(mlu,  "PS", "#1", L"relative_colorimetric");
4453             cmsMLUsetWide(mlu,  "PS", "#2", L"saturation");
4454             cmsMLUsetWide(mlu,  "PS", "#3", L"absolute_colorimetric");  
4455             rc = cmsWriteTag(hProfile, tag, mlu);
4456             cmsMLUfree(mlu);
4457             return rc;
4458             
4459
4460         case 2:
4461             mlu = (cmsMLU*) cmsReadTag(hProfile, tag);
4462             if (mlu == NULL) return 0;
4463
4464
4465              
4466              cmsMLUgetASCII(mlu, "PS", "nm", Buffer, 256);
4467              if (strcmp(Buffer, "test postscript") != 0) return 0;
4468
4469            
4470              cmsMLUgetASCII(mlu, "PS", "#0", Buffer, 256);
4471              if (strcmp(Buffer, "perceptual") != 0) return 0;
4472
4473             
4474              cmsMLUgetASCII(mlu, "PS", "#1", Buffer, 256);
4475              if (strcmp(Buffer, "relative_colorimetric") != 0) return 0;
4476
4477              
4478              cmsMLUgetASCII(mlu, "PS", "#2", Buffer, 256);
4479              if (strcmp(Buffer, "saturation") != 0) return 0;
4480
4481             
4482              cmsMLUgetASCII(mlu, "PS", "#3", Buffer, 256);
4483              if (strcmp(Buffer, "absolute_colorimetric") != 0) return 0;
4484              return 1;
4485
4486         default:
4487             return 0;
4488     }
4489 }
4490
4491
4492 static
4493 cmsToneCurve *CreateSegmentedCurve(void)
4494 {
4495     cmsCurveSegment Seg[3];
4496     cmsFloat32Number Sampled[2] = { 0, 1};
4497
4498     Seg[0].Type = 6;
4499     Seg[0].Params[0] = 1;
4500     Seg[0].Params[1] = 0;
4501     Seg[0].Params[2] = 0;
4502     Seg[0].Params[3] = 0;
4503     Seg[0].x0 = -1E22F;
4504     Seg[0].x1 = 0;
4505
4506     Seg[1].Type = 0;
4507     Seg[1].nGridPoints = 2;
4508     Seg[1].SampledPoints = Sampled;
4509     Seg[1].x0 = 0;
4510     Seg[1].x1 = 1;
4511
4512     Seg[2].Type = 6;
4513     Seg[2].Params[0] = 1;
4514     Seg[2].Params[1] = 0;
4515     Seg[2].Params[2] = 0;
4516     Seg[2].Params[3] = 0;               
4517     Seg[2].x0 = 1;
4518     Seg[2].x1 = 1E22F;
4519
4520     return cmsBuildSegmentedToneCurve(DbgThread(), 3, Seg);
4521 }
4522
4523
4524 static
4525 cmsInt32Number CheckMPE(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4526 {
4527     cmsPipeline* Lut, *Pt;
4528     cmsToneCurve* G[3];
4529     cmsInt32Number rc;
4530
4531     switch (Pass) {
4532
4533         case 1: 
4534
4535             Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
4536             
4537             cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4(DbgThread()));
4538             cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV4ToV2(DbgThread()));
4539             AddIdentityCLUTfloat(Lut);
4540
4541             G[0] = G[1] = G[2] = CreateSegmentedCurve();
4542             cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, G));
4543             cmsFreeToneCurve(G[0]);
4544             
4545             rc = cmsWriteTag(hProfile, tag, Lut);
4546             cmsPipelineFree(Lut);
4547             return rc;
4548             
4549         case 2:
4550             Pt = cmsReadTag(hProfile, tag);
4551             if (Pt == NULL) return 0;           
4552             return CheckFloatLUT(Pt);
4553
4554         default:
4555             return 0;
4556     }
4557 }
4558
4559
4560 static
4561 cmsInt32Number CheckScreening(cmsInt32Number Pass,  cmsHPROFILE hProfile, cmsTagSignature tag)
4562 {
4563     cmsScreening *Pt, sc;
4564     cmsInt32Number rc;
4565     
4566     switch (Pass) {
4567
4568         case 1:         
4569             
4570             sc.Flag = 0;
4571             sc.nChannels = 1;
4572             sc.Channels[0].Frequency = 2.0;
4573             sc.Channels[0].ScreenAngle = 3.0;
4574             sc.Channels[0].SpotShape = cmsSPOT_ELLIPSE;
4575
4576             rc = cmsWriteTag(hProfile, tag, &sc);
4577             return rc;
4578             
4579
4580         case 2:
4581             Pt = cmsReadTag(hProfile, tag);
4582             if (Pt == NULL) return 0;
4583
4584             if (Pt ->nChannels != 1) return 0;
4585             if (Pt ->Flag      != 0) return 0;
4586             if (!IsGoodFixed15_16("Freq", Pt ->Channels[0].Frequency, 2.0)) return 0;
4587             if (!IsGoodFixed15_16("Angle", Pt ->Channels[0].ScreenAngle, 3.0)) return 0;
4588             if (Pt ->Channels[0].SpotShape != cmsSPOT_ELLIPSE) return 0;
4589             return 1;
4590
4591         default:
4592             return 0;
4593     }
4594 }
4595
4596
4597 static
4598 cmsBool CheckOneStr(cmsMLU* mlu, cmsInt32Number n)
4599 {
4600     char Buffer[256], Buffer2[256];
4601
4602     
4603     cmsMLUgetASCII(mlu, "en", "US", Buffer, 255);
4604     sprintf(Buffer2, "Hello, world %d", n);
4605     if (strcmp(Buffer, Buffer2) != 0) return FALSE;
4606
4607   
4608     cmsMLUgetASCII(mlu, "es", "ES", Buffer, 255);
4609     sprintf(Buffer2, "Hola, mundo %d", n);
4610     if (strcmp(Buffer, Buffer2) != 0) return FALSE;
4611
4612     return TRUE;
4613 }
4614
4615
4616 static
4617 void SetOneStr(cmsMLU** mlu, wchar_t* s1, wchar_t* s2)
4618 {
4619     *mlu = cmsMLUalloc(DbgThread(), 0);
4620     cmsMLUsetWide(*mlu, "en", "US", s1);
4621     cmsMLUsetWide(*mlu, "es", "ES", s2);
4622 }
4623
4624
4625 static
4626 cmsInt32Number CheckProfileSequenceTag(cmsInt32Number Pass,  cmsHPROFILE hProfile)
4627 {
4628     cmsSEQ* s;
4629     cmsInt32Number i;
4630
4631     switch (Pass) {
4632
4633     case 1:
4634
4635         s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
4636         if (s == NULL) return 0;
4637
4638         SetOneStr(&s -> seq[0].Manufacturer, L"Hello, world 0", L"Hola, mundo 0");
4639         SetOneStr(&s -> seq[0].Model, L"Hello, world 0", L"Hola, mundo 0");
4640         SetOneStr(&s -> seq[1].Manufacturer, L"Hello, world 1", L"Hola, mundo 1");
4641         SetOneStr(&s -> seq[1].Model, L"Hello, world 1", L"Hola, mundo 1");
4642         SetOneStr(&s -> seq[2].Manufacturer, L"Hello, world 2", L"Hola, mundo 2");
4643         SetOneStr(&s -> seq[2].Model, L"Hello, world 2", L"Hola, mundo 2");
4644
4645
4646 #ifdef CMS_DONT_USE_INT64
4647         s ->seq[0].attributes[0] = cmsTransparency|cmsMatte;
4648         s ->seq[0].attributes[1] = 0;
4649 #else
4650         s ->seq[0].attributes = cmsTransparency|cmsMatte;
4651 #endif
4652
4653 #ifdef CMS_DONT_USE_INT64
4654         s ->seq[1].attributes[0] = cmsReflective|cmsMatte;
4655         s ->seq[1].attributes[1] = 0;
4656 #else
4657         s ->seq[1].attributes = cmsReflective|cmsMatte;
4658 #endif
4659
4660 #ifdef CMS_DONT_USE_INT64
4661         s ->seq[2].attributes[0] = cmsTransparency|cmsGlossy;
4662         s ->seq[2].attributes[1] = 0;
4663 #else
4664         s ->seq[2].attributes = cmsTransparency|cmsGlossy;
4665 #endif
4666
4667         if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, s)) return 0;    
4668         cmsFreeProfileSequenceDescription(s);
4669         return 1;
4670
4671     case 2:
4672
4673         s = cmsReadTag(hProfile, cmsSigProfileSequenceDescTag);
4674         if (s == NULL) return 0;
4675
4676         if (s ->n != 3) return 0;
4677
4678 #ifdef CMS_DONT_USE_INT64
4679         if (s ->seq[0].attributes[0] != (cmsTransparency|cmsMatte)) return 0;
4680         if (s ->seq[0].attributes[1] != 0) return 0;
4681 #else
4682         if (s ->seq[0].attributes != (cmsTransparency|cmsMatte)) return 0;
4683 #endif
4684
4685 #ifdef CMS_DONT_USE_INT64
4686         if (s ->seq[1].attributes[0] != (cmsReflective|cmsMatte)) return 0;
4687         if (s ->seq[1].attributes[1] != 0) return 0;
4688 #else
4689         if (s ->seq[1].attributes != (cmsReflective|cmsMatte)) return 0;
4690 #endif
4691
4692 #ifdef CMS_DONT_USE_INT64
4693         if (s ->seq[2].attributes[0] != (cmsTransparency|cmsGlossy)) return 0;
4694         if (s ->seq[2].attributes[1] != 0) return 0;
4695 #else
4696         if (s ->seq[2].attributes != (cmsTransparency|cmsGlossy)) return 0;
4697 #endif
4698
4699         // Check MLU
4700         for (i=0; i < 3; i++) {
4701
4702             if (!CheckOneStr(s -> seq[i].Manufacturer, i)) return 0;
4703             if (!CheckOneStr(s -> seq[i].Model, i)) return 0;
4704         }
4705         return 1;
4706
4707     default:
4708         return 0;
4709     }
4710 }
4711
4712
4713 static
4714 cmsInt32Number CheckProfileSequenceIDTag(cmsInt32Number Pass,  cmsHPROFILE hProfile)
4715 {
4716     cmsSEQ* s;
4717     cmsInt32Number i;
4718
4719     switch (Pass) {
4720
4721     case 1:
4722
4723         s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
4724         if (s == NULL) return 0;
4725
4726         memcpy(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16);
4727         memcpy(s ->seq[1].ProfileID.ID8, "1111111111111111", 16);
4728         memcpy(s ->seq[2].ProfileID.ID8, "2222222222222222", 16);
4729
4730
4731         SetOneStr(&s -> seq[0].Description, L"Hello, world 0", L"Hola, mundo 0");
4732         SetOneStr(&s -> seq[1].Description, L"Hello, world 1", L"Hola, mundo 1");
4733         SetOneStr(&s -> seq[2].Description, L"Hello, world 2", L"Hola, mundo 2");
4734
4735         if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, s)) return 0;    
4736         cmsFreeProfileSequenceDescription(s);
4737         return 1;
4738
4739     case 2:
4740
4741         s = cmsReadTag(hProfile, cmsSigProfileSequenceIdTag);
4742         if (s == NULL) return 0;
4743
4744         if (s ->n != 3) return 0;
4745
4746         if (memcmp(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16) != 0) return 0;
4747         if (memcmp(s ->seq[1].ProfileID.ID8, "1111111111111111", 16) != 0) return 0;
4748         if (memcmp(s ->seq[2].ProfileID.ID8, "2222222222222222", 16) != 0) return 0;
4749
4750         for (i=0; i < 3; i++) {
4751
4752             if (!CheckOneStr(s -> seq[i].Description, i)) return 0;
4753         }
4754
4755         return 1;
4756
4757     default:
4758         return 0;
4759     }
4760 }
4761
4762
4763 static
4764 cmsInt32Number CheckICCViewingConditions(cmsInt32Number Pass,  cmsHPROFILE hProfile)
4765 {
4766     cmsICCViewingConditions* v;
4767     cmsICCViewingConditions  s;
4768
4769     switch (Pass) {
4770
4771         case 1:         
4772             s.IlluminantType = 1;
4773             s.IlluminantXYZ.X = 0.1;
4774             s.IlluminantXYZ.Y = 0.2;
4775             s.IlluminantXYZ.Z = 0.3;
4776             s.SurroundXYZ.X = 0.4;
4777             s.SurroundXYZ.Y = 0.5;
4778             s.SurroundXYZ.Z = 0.6;
4779
4780             if (!cmsWriteTag(hProfile, cmsSigViewingConditionsTag, &s)) return 0;    
4781             return 1;
4782
4783         case 2:
4784             v = cmsReadTag(hProfile, cmsSigViewingConditionsTag);
4785             if (v == NULL) return 0;
4786
4787             if (v ->IlluminantType != 1) return 0;
4788             if (!IsGoodVal("IlluminantXYZ.X", v ->IlluminantXYZ.X, 0.1, 0.001)) return 0;
4789             if (!IsGoodVal("IlluminantXYZ.Y", v ->IlluminantXYZ.Y, 0.2, 0.001)) return 0;
4790             if (!IsGoodVal("IlluminantXYZ.Z", v ->IlluminantXYZ.Z, 0.3, 0.001)) return 0;
4791
4792             if (!IsGoodVal("SurroundXYZ.X", v ->SurroundXYZ.X, 0.4, 0.001)) return 0;
4793             if (!IsGoodVal("SurroundXYZ.Y", v ->SurroundXYZ.Y, 0.5, 0.001)) return 0;
4794             if (!IsGoodVal("SurroundXYZ.Z", v ->SurroundXYZ.Z, 0.6, 0.001)) return 0;
4795
4796             return 1;
4797
4798         default:
4799             return 0;
4800     }
4801
4802 }
4803
4804
4805 static
4806 cmsInt32Number CheckVCGT(cmsInt32Number Pass,  cmsHPROFILE hProfile)
4807 {
4808     cmsToneCurve* Curves[3];
4809     cmsToneCurve** PtrCurve;
4810     
4811      switch (Pass) {
4812
4813         case 1:     
4814             Curves[0] = cmsBuildGamma(DbgThread(), 1.1);
4815             Curves[1] = cmsBuildGamma(DbgThread(), 2.2);
4816             Curves[2] = cmsBuildGamma(DbgThread(), 3.4);
4817
4818             if (!cmsWriteTag(hProfile, cmsSigVcgtTag, Curves)) return 0;    
4819
4820             cmsFreeToneCurveTriple(Curves);
4821             return 1;
4822
4823
4824         case 2:
4825
4826              PtrCurve = cmsReadTag(hProfile, cmsSigVcgtTag);
4827              if (PtrCurve == NULL) return 0;
4828              if (!IsGoodVal("VCGT R", cmsEstimateGamma(PtrCurve[0], 0.01), 1.1, 0.001)) return 0;
4829              if (!IsGoodVal("VCGT G", cmsEstimateGamma(PtrCurve[1], 0.01), 2.2, 0.001)) return 0;
4830              if (!IsGoodVal("VCGT B", cmsEstimateGamma(PtrCurve[2], 0.01), 3.4, 0.001)) return 0;
4831              return 1;
4832
4833         default:;
4834     }
4835
4836     return 0;
4837 }
4838
4839
4840 // Only one of the two following may be used, as they share the same tag
4841 static
4842 cmsInt32Number CheckDictionary16(cmsInt32Number Pass,  cmsHPROFILE hProfile)
4843 {
4844       cmsHANDLE hDict;
4845       const cmsDICTentry* e;
4846       switch (Pass) {
4847
4848         case 1:     
4849             hDict = cmsDictAlloc(DbgThread());
4850             cmsDictAddEntry(hDict, L"Name0",  NULL, NULL, NULL);
4851             cmsDictAddEntry(hDict, L"Name1",  L"", NULL, NULL);
4852             cmsDictAddEntry(hDict, L"Name",  L"String", NULL, NULL);
4853             cmsDictAddEntry(hDict, L"Name2", L"12",    NULL, NULL);
4854             if (!cmsWriteTag(hProfile, cmsSigMetaTag, hDict)) return 0;    
4855             cmsDictFree(hDict);
4856             return 1;
4857
4858
4859         case 2:
4860
4861              hDict = cmsReadTag(hProfile, cmsSigMetaTag);
4862              if (hDict == NULL) return 0;
4863              e = cmsDictGetEntryList(hDict);
4864              if (memcmp(e ->Name, L"Name2", sizeof(wchar_t) * 5) != 0) return 0;
4865              if (memcmp(e ->Value, L"12",  sizeof(wchar_t) * 2) != 0) return 0;
4866              e = cmsDictNextEntry(e);         
4867              if (memcmp(e ->Name, L"Name", sizeof(wchar_t) * 4) != 0) return 0;
4868              if (memcmp(e ->Value, L"String",  sizeof(wchar_t) * 5) != 0) return 0;
4869              e = cmsDictNextEntry(e);         
4870              if (memcmp(e ->Name, L"Name1", sizeof(wchar_t) *5) != 0) return 0;
4871              if (e ->Value == NULL) return 0;
4872              if (*e->Value != 0) return 0;
4873              e = cmsDictNextEntry(e);         
4874              if (memcmp(e ->Name, L"Name0", sizeof(wchar_t) * 5) != 0) return 0;
4875              if (e ->Value != NULL) return 0;
4876              return 1;
4877
4878
4879         default:;
4880     }
4881
4882     return 0;
4883 }
4884
4885
4886
4887 static
4888 cmsInt32Number CheckDictionary24(cmsInt32Number Pass,  cmsHPROFILE hProfile)
4889 {
4890     cmsHANDLE hDict;
4891     const cmsDICTentry* e;
4892     cmsMLU* DisplayName;
4893     char Buffer[256];
4894     cmsInt32Number rc = 1;
4895     
4896     switch (Pass) {
4897
4898     case 1:     
4899         hDict = cmsDictAlloc(DbgThread());
4900
4901         DisplayName = cmsMLUalloc(DbgThread(), 0);    
4902
4903         cmsMLUsetWide(DisplayName, "en", "US", L"Hello, world");
4904         cmsMLUsetWide(DisplayName, "es", "ES", L"Hola, mundo");
4905         cmsMLUsetWide(DisplayName, "fr", "FR", L"Bonjour, le monde");
4906         cmsMLUsetWide(DisplayName, "ca", "CA", L"Hola, mon");
4907
4908         cmsDictAddEntry(hDict, L"Name",  L"String", DisplayName, NULL);
4909         cmsMLUfree(DisplayName);
4910
4911         cmsDictAddEntry(hDict, L"Name2", L"12",    NULL, NULL);
4912         if (!cmsWriteTag(hProfile, cmsSigMetaTag, hDict)) return 0;    
4913         cmsDictFree(hDict);
4914         
4915         return 1;
4916
4917
4918     case 2:
4919
4920         hDict = cmsReadTag(hProfile, cmsSigMetaTag);
4921         if (hDict == NULL) return 0;
4922
4923         e = cmsDictGetEntryList(hDict);
4924         if (memcmp(e ->Name, L"Name2", sizeof(wchar_t) * 5) != 0) return 0;
4925         if (memcmp(e ->Value, L"12",  sizeof(wchar_t) * 2) != 0) return 0;
4926         e = cmsDictNextEntry(e);         
4927         if (memcmp(e ->Name, L"Name", sizeof(wchar_t) * 4) != 0) return 0;
4928         if (memcmp(e ->Value, L"String",  sizeof(wchar_t) * 5) != 0) return 0;  
4929
4930         cmsMLUgetASCII(e->DisplayName, "en", "US", Buffer, 256);
4931         if (strcmp(Buffer, "Hello, world") != 0) rc = 0;
4932
4933
4934         cmsMLUgetASCII(e->DisplayName, "es", "ES", Buffer, 256);
4935         if (strcmp(Buffer, "Hola, mundo") != 0) rc = 0;
4936
4937
4938         cmsMLUgetASCII(e->DisplayName, "fr", "FR", Buffer, 256);
4939         if (strcmp(Buffer, "Bonjour, le monde") != 0) rc = 0;
4940
4941
4942         cmsMLUgetASCII(e->DisplayName, "ca", "CA", Buffer, 256);
4943         if (strcmp(Buffer, "Hola, mon") != 0) rc = 0;
4944
4945         if (rc == 0)
4946             Fail("Unexpected string '%s'", Buffer);
4947         return 1;
4948
4949     default:;
4950     }
4951
4952     return 0;
4953 }
4954
4955 static
4956 cmsInt32Number CheckRAWtags(cmsInt32Number Pass,  cmsHPROFILE hProfile)
4957 {
4958     char Buffer[7];
4959
4960     switch (Pass) {
4961
4962         case 1:         
4963             return cmsWriteRawTag(hProfile, 0x31323334, "data123", 7);
4964
4965         case 2:
4966             if (!cmsReadRawTag(hProfile, 0x31323334, Buffer, 7)) return 0;
4967
4968             if (strncmp(Buffer, "data123", 7) != 0) return 0;
4969             return 1;
4970
4971         default:
4972             return 0;
4973     }
4974 }
4975
4976
4977 // This is a very big test that checks every single tag
4978 static
4979 cmsInt32Number CheckProfileCreation(void)
4980 {
4981     cmsHPROFILE h;
4982     cmsInt32Number Pass;
4983
4984     h = cmsCreateProfilePlaceholder(DbgThread());
4985     if (h == NULL) return 0;
4986
4987     cmsSetProfileVersion(h, 4.3);
4988     if (cmsGetTagCount(h) != 0) { Fail("Empty profile with nonzero number of tags"); return 0; }
4989     if (cmsIsTag(h, cmsSigAToB0Tag)) { Fail("Found a tag in an empty profile"); return 0; }
4990
4991     cmsSetColorSpace(h, cmsSigRgbData);
4992     if (cmsGetColorSpace(h) !=  cmsSigRgbData) { Fail("Unable to set colorspace"); return 0; }
4993
4994     cmsSetPCS(h, cmsSigLabData);
4995     if (cmsGetPCS(h) !=  cmsSigLabData) { Fail("Unable to set colorspace"); return 0; }
4996
4997     cmsSetDeviceClass(h, cmsSigDisplayClass);
4998     if (cmsGetDeviceClass(h) != cmsSigDisplayClass) { Fail("Unable to set deviceclass"); return 0; }
4999
5000     cmsSetHeaderRenderingIntent(h, INTENT_SATURATION);
5001     if (cmsGetHeaderRenderingIntent(h) != INTENT_SATURATION) { Fail("Unable to set rendering intent"); return 0; }
5002
5003     for (Pass = 1; Pass <= 2; Pass++) {
5004
5005         SubTest("Tags holding XYZ");
5006
5007         if (!CheckXYZ(Pass, h, cmsSigBlueColorantTag)) return 0;
5008         if (!CheckXYZ(Pass, h, cmsSigGreenColorantTag)) return 0;
5009         if (!CheckXYZ(Pass, h, cmsSigRedColorantTag)) return 0;
5010         if (!CheckXYZ(Pass, h, cmsSigMediaBlackPointTag)) return 0;
5011         if (!CheckXYZ(Pass, h, cmsSigMediaWhitePointTag)) return 0;
5012         if (!CheckXYZ(Pass, h, cmsSigLuminanceTag)) return 0;
5013
5014         SubTest("Tags holding curves");
5015         
5016         if (!CheckGamma(Pass, h, cmsSigBlueTRCTag)) return 0;
5017         if (!CheckGamma(Pass, h, cmsSigGrayTRCTag)) return 0;
5018         if (!CheckGamma(Pass, h, cmsSigGreenTRCTag)) return 0;
5019         if (!CheckGamma(Pass, h, cmsSigRedTRCTag)) return 0;
5020         
5021         SubTest("Tags holding text");
5022
5023         if (!CheckText(Pass, h, cmsSigCharTargetTag)) return 0;
5024         if (!CheckText(Pass, h, cmsSigCopyrightTag)) return 0;
5025         if (!CheckText(Pass, h, cmsSigProfileDescriptionTag)) return 0;
5026         if (!CheckText(Pass, h, cmsSigDeviceMfgDescTag)) return 0;
5027         if (!CheckText(Pass, h, cmsSigDeviceModelDescTag)) return 0;
5028         if (!CheckText(Pass, h, cmsSigViewingCondDescTag)) return 0;
5029         if (!CheckText(Pass, h, cmsSigScreeningDescTag)) return 0;
5030
5031         SubTest("Tags holding cmsICCData");
5032
5033         if (!CheckData(Pass, h, cmsSigPs2CRD0Tag)) return 0;
5034         if (!CheckData(Pass, h, cmsSigPs2CRD1Tag)) return 0;
5035         if (!CheckData(Pass, h, cmsSigPs2CRD2Tag)) return 0;
5036         if (!CheckData(Pass, h, cmsSigPs2CRD3Tag)) return 0;
5037         if (!CheckData(Pass, h, cmsSigPs2CSATag)) return 0;
5038         if (!CheckData(Pass, h, cmsSigPs2RenderingIntentTag)) return 0;
5039
5040         SubTest("Tags holding signatures");
5041
5042         if (!CheckSignature(Pass, h, cmsSigColorimetricIntentImageStateTag)) return 0;
5043         if (!CheckSignature(Pass, h, cmsSigPerceptualRenderingIntentGamutTag)) return 0;
5044         if (!CheckSignature(Pass, h, cmsSigSaturationRenderingIntentGamutTag)) return 0;
5045         if (!CheckSignature(Pass, h, cmsSigTechnologyTag)) return 0;
5046
5047         SubTest("Tags holding date_time");
5048
5049         if (!CheckDateTime(Pass, h, cmsSigCalibrationDateTimeTag)) return 0;
5050         if (!CheckDateTime(Pass, h, cmsSigDateTimeTag)) return 0;
5051
5052         SubTest("Tags holding named color lists");
5053
5054         if (!CheckNamedColor(Pass, h, cmsSigColorantTableTag, 15, FALSE)) return 0;
5055         if (!CheckNamedColor(Pass, h, cmsSigColorantTableOutTag, 15, FALSE)) return 0;
5056         if (!CheckNamedColor(Pass, h, cmsSigNamedColor2Tag, 4096, TRUE)) return 0;
5057
5058         SubTest("Tags holding LUTs");
5059
5060         if (!CheckLUT(Pass, h, cmsSigAToB0Tag)) return 0;
5061         if (!CheckLUT(Pass, h, cmsSigAToB1Tag)) return 0;
5062         if (!CheckLUT(Pass, h, cmsSigAToB2Tag)) return 0;
5063         if (!CheckLUT(Pass, h, cmsSigBToA0Tag)) return 0;
5064         if (!CheckLUT(Pass, h, cmsSigBToA1Tag)) return 0;
5065         if (!CheckLUT(Pass, h, cmsSigBToA2Tag)) return 0;
5066         if (!CheckLUT(Pass, h, cmsSigPreview0Tag)) return 0;
5067         if (!CheckLUT(Pass, h, cmsSigPreview1Tag)) return 0;
5068         if (!CheckLUT(Pass, h, cmsSigPreview2Tag)) return 0;
5069         if (!CheckLUT(Pass, h, cmsSigGamutTag)) return 0;
5070         
5071         SubTest("Tags holding CHAD");
5072         if (!CheckCHAD(Pass, h, cmsSigChromaticAdaptationTag)) return 0;
5073
5074         SubTest("Tags holding Chromaticity");
5075         if (!CheckChromaticity(Pass, h, cmsSigChromaticityTag)) return 0;
5076
5077         SubTest("Tags holding colorant order");
5078         if (!CheckColorantOrder(Pass, h, cmsSigColorantOrderTag)) return 0;
5079
5080         SubTest("Tags holding measurement");
5081         if (!CheckMeasurement(Pass, h, cmsSigMeasurementTag)) return 0;
5082
5083         SubTest("Tags holding CRD info");
5084         if (!CheckCRDinfo(Pass, h, cmsSigCrdInfoTag)) return 0;
5085
5086         SubTest("Tags holding UCR/BG");
5087         if (!CheckUcrBg(Pass, h, cmsSigUcrBgTag)) return 0;
5088
5089         SubTest("Tags holding MPE");
5090         if (!CheckMPE(Pass, h, cmsSigDToB0Tag)) return 0;
5091         if (!CheckMPE(Pass, h, cmsSigDToB1Tag)) return 0;
5092         if (!CheckMPE(Pass, h, cmsSigDToB2Tag)) return 0;
5093         if (!CheckMPE(Pass, h, cmsSigDToB3Tag)) return 0;
5094         if (!CheckMPE(Pass, h, cmsSigBToD0Tag)) return 0;
5095         if (!CheckMPE(Pass, h, cmsSigBToD1Tag)) return 0;
5096         if (!CheckMPE(Pass, h, cmsSigBToD2Tag)) return 0;
5097         if (!CheckMPE(Pass, h, cmsSigBToD3Tag)) return 0;
5098         
5099         SubTest("Tags using screening");
5100         if (!CheckScreening(Pass, h, cmsSigScreeningTag)) return 0;
5101
5102         SubTest("Tags holding profile sequence description");
5103         if (!CheckProfileSequenceTag(Pass, h)) return 0;
5104         if (!CheckProfileSequenceIDTag(Pass, h)) return 0;
5105
5106         SubTest("Tags holding ICC viewing conditions");
5107         if (!CheckICCViewingConditions(Pass, h)) return 0;
5108
5109
5110         SubTest("VCGT tags");
5111         if (!CheckVCGT(Pass, h)) return 0;
5112
5113         SubTest("RAW tags");
5114         if (!CheckRAWtags(Pass, h)) return 0;
5115
5116         SubTest("Dictionary meta tags");
5117         // if (!CheckDictionary16(Pass, h)) return 0;
5118         if (!CheckDictionary24(Pass, h)) return 0;
5119
5120         if (Pass == 1) {
5121             cmsSaveProfileToFile(h, "alltags.icc");
5122             cmsCloseProfile(h);
5123             h = cmsOpenProfileFromFileTHR(DbgThread(), "alltags.icc", "r");
5124         }
5125
5126     }
5127
5128     /*    
5129     Not implemented (by design):
5130
5131     cmsSigDataTag                           = 0x64617461,  // 'data'  -- Unused   
5132     cmsSigDeviceSettingsTag                 = 0x64657673,  // 'devs'  -- Unused
5133     cmsSigNamedColorTag                     = 0x6E636f6C,  // 'ncol'  -- Don't use this one, deprecated by ICC
5134     cmsSigOutputResponseTag                 = 0x72657370,  // 'resp'  -- Possible patent on this 
5135     */
5136     
5137     cmsCloseProfile(h);
5138     remove("alltags.icc");
5139     return 1;
5140 }
5141
5142
5143 // Error reporting  -------------------------------------------------------------------------------------------------------
5144
5145
5146 static
5147 void ErrorReportingFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
5148 {
5149     TrappedError = TRUE;
5150     SimultaneousErrors++;
5151     strncpy(ReasonToFailBuffer, Text, TEXT_ERROR_BUFFER_SIZE-1);    
5152 }
5153
5154
5155 static
5156 cmsInt32Number CheckBadProfiles(void)
5157 {
5158     cmsHPROFILE h;
5159
5160     h = cmsOpenProfileFromFileTHR(DbgThread(), "IDoNotExist.icc", "r");
5161     if (h != NULL) {
5162         cmsCloseProfile(h);
5163         return 0;
5164     }
5165
5166     h = cmsOpenProfileFromFileTHR(DbgThread(), "IAmIllFormed*.icc", "r");
5167     if (h != NULL) {
5168         cmsCloseProfile(h);
5169         return 0;
5170     }
5171
5172     // No profile name given
5173     h = cmsOpenProfileFromFileTHR(DbgThread(), "", "r");
5174     if (h != NULL) {
5175         cmsCloseProfile(h);
5176         return 0;
5177     }
5178     
5179     h = cmsOpenProfileFromFileTHR(DbgThread(), "..", "r");
5180     if (h != NULL) {
5181         cmsCloseProfile(h);
5182         return 0;
5183     }
5184     
5185     h = cmsOpenProfileFromFileTHR(DbgThread(), "IHaveBadAccessMode.icc", "@");
5186     if (h != NULL) {
5187         cmsCloseProfile(h);
5188         return 0;
5189     }
5190
5191     h = cmsOpenProfileFromFileTHR(DbgThread(), "bad.icc", "r");
5192     if (h != NULL) {
5193         cmsCloseProfile(h);
5194         return 0;
5195     }
5196
5197      h = cmsOpenProfileFromFileTHR(DbgThread(), "toosmall.icc", "r");
5198     if (h != NULL) {
5199         cmsCloseProfile(h);
5200         return 0;
5201     }
5202
5203     h = cmsOpenProfileFromMemTHR(DbgThread(), NULL, 3);
5204     if (h != NULL) {
5205         cmsCloseProfile(h);
5206         return 0;
5207     }
5208
5209     h = cmsOpenProfileFromMemTHR(DbgThread(), "123", 3);
5210     if (h != NULL) {
5211         cmsCloseProfile(h);
5212         return 0;
5213     }
5214
5215     if (SimultaneousErrors != 9) return 0;      
5216     
5217     return 1;
5218 }
5219
5220
5221 static
5222 cmsInt32Number CheckErrReportingOnBadProfiles(void)
5223 {
5224     cmsInt32Number rc;
5225
5226     cmsSetLogErrorHandler(ErrorReportingFunction);
5227     rc = CheckBadProfiles();
5228     cmsSetLogErrorHandler(FatalErrorQuit);
5229
5230     // Reset the error state
5231     TrappedError = FALSE;
5232     return rc;
5233 }
5234
5235
5236 static
5237 cmsInt32Number CheckBadTransforms(void)
5238 {
5239     cmsHPROFILE h1 = cmsCreate_sRGBProfile();
5240     cmsHTRANSFORM x1;
5241
5242     x1 = cmsCreateTransform(NULL, 0, NULL, 0, 0, 0);
5243     if (x1 != NULL) {
5244         cmsDeleteTransform(x1);
5245         return 0;
5246     }
5247
5248     
5249
5250     x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_RGB_8, 12345, 0);
5251     if (x1 != NULL) {
5252         cmsDeleteTransform(x1);
5253         return 0;
5254     }
5255
5256     x1 = cmsCreateTransform(h1, TYPE_CMYK_8, h1, TYPE_RGB_8, 0, 0);
5257     if (x1 != NULL) {
5258         cmsDeleteTransform(x1);
5259         return 0;
5260     }
5261
5262     x1 = cmsCreateTransform(h1, TYPE_RGB_8, h1, TYPE_CMYK_8, 1, 0);
5263     if (x1 != NULL) {
5264         cmsDeleteTransform(x1);
5265         return 0;
5266     }
5267
5268     // sRGB does its output as XYZ!
5269     x1 = cmsCreateTransform(h1, TYPE_RGB_8, NULL, TYPE_Lab_8, 1, 0);
5270     if (x1 != NULL) {
5271         cmsDeleteTransform(x1);
5272         return 0;
5273     }
5274
5275     cmsCloseProfile(h1);
5276
5277
5278     {
5279
5280     cmsHPROFILE h1 = cmsOpenProfileFromFile("test1.icc", "r");
5281     cmsHPROFILE h2 = cmsCreate_sRGBProfile();
5282
5283     x1 = cmsCreateTransform(h1, TYPE_BGR_8, h2, TYPE_BGR_8, INTENT_PERCEPTUAL, 0);
5284
5285     cmsCloseProfile(h1); cmsCloseProfile(h2);
5286     if (x1 != NULL) {
5287         cmsDeleteTransform(x1);
5288         return 0;
5289     }
5290     }
5291
5292     return 1;
5293
5294 }
5295
5296 static
5297 cmsInt32Number CheckErrReportingOnBadTransforms(void)
5298 {
5299     cmsInt32Number rc;
5300
5301     cmsSetLogErrorHandler(ErrorReportingFunction);
5302     rc = CheckBadTransforms();
5303     cmsSetLogErrorHandler(FatalErrorQuit);
5304
5305     // Reset the error state
5306     TrappedError = FALSE;
5307     return rc;
5308 }
5309
5310
5311
5312
5313 // ---------------------------------------------------------------------------------------------------------
5314
5315 // Check a linear xform
5316 static
5317 cmsInt32Number Check8linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5318 {
5319     cmsInt32Number n2, i, j;
5320     cmsUInt8Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5321
5322     n2=0;
5323     
5324     for (j=0; j < 0xFF; j++) {
5325         
5326         memset(Inw, j, sizeof(Inw));
5327         cmsDoTransform(xform, Inw, Outw, 1);
5328         
5329         for (i=0; i < nChan; i++) {
5330
5331            cmsInt32Number dif = abs(Outw[i] - j);
5332            if (dif > n2) n2 = dif;
5333
5334         }
5335     }
5336
5337    // We allow 2 contone of difference on 8 bits 
5338     if (n2 > 2) {
5339
5340         Fail("Differences too big (%x)", n2);
5341         return 0;
5342     }
5343     
5344     return 1;
5345 }
5346
5347 static
5348 cmsInt32Number Compare8bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5349 {
5350     cmsInt32Number n2, i, j;
5351     cmsUInt8Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5352
5353     n2=0;
5354     
5355     for (j=0; j < 0xFF; j++) {
5356         
5357         memset(Inw, j, sizeof(Inw));
5358         cmsDoTransform(xform1, Inw, Outw1, 1);
5359         cmsDoTransform(xform2, Inw, Outw2, 1);
5360
5361         for (i=0; i < nChan; i++) {
5362
5363            cmsInt32Number dif = abs(Outw2[i] - Outw1[i]);
5364            if (dif > n2) n2 = dif;
5365
5366         }
5367     }
5368
5369    // We allow 2 contone of difference on 8 bits 
5370     if (n2 > 2) {
5371
5372         Fail("Differences too big (%x)", n2);
5373         return 0;
5374     }
5375
5376     
5377     return 1;
5378 }
5379
5380
5381 // Check a linear xform
5382 static
5383 cmsInt32Number Check16linearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5384 {
5385     cmsInt32Number n2, i, j;
5386     cmsUInt16Number Inw[cmsMAXCHANNELS], Outw[cmsMAXCHANNELS];
5387
5388     n2=0;    
5389     for (j=0; j < 0xFFFF; j++) {
5390
5391         for (i=0; i < nChan; i++) Inw[i] = (cmsUInt16Number) j;
5392
5393         cmsDoTransform(xform, Inw, Outw, 1);
5394         
5395         for (i=0; i < nChan; i++) {
5396
5397            cmsInt32Number dif = abs(Outw[i] - j);
5398            if (dif > n2) n2 = dif;
5399
5400         }
5401     
5402
5403    // We allow 2 contone of difference on 16 bits
5404     if (n2 > 0x200) {
5405
5406         Fail("Differences too big (%x)", n2);
5407         return 0;
5408     }
5409     }
5410     
5411     return 1;
5412 }
5413
5414 static
5415 cmsInt32Number Compare16bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5416 {
5417     cmsInt32Number n2, i, j;
5418     cmsUInt16Number Inw[cmsMAXCHANNELS], Outw1[cmsMAXCHANNELS], Outw2[cmsMAXCHANNELS];;
5419
5420     n2=0;
5421     
5422     for (j=0; j < 0xFFFF; j++) {
5423         
5424         for (i=0; i < nChan; i++) Inw[i] = (cmsUInt16Number) j;
5425
5426         cmsDoTransform(xform1, Inw, Outw1, 1);
5427         cmsDoTransform(xform2, Inw, Outw2, 1);
5428
5429         for (i=0; i < nChan; i++) {
5430
5431            cmsInt32Number dif = abs(Outw2[i] - Outw1[i]);
5432            if (dif > n2) n2 = dif;
5433
5434         }
5435     }
5436
5437    // We allow 2 contone of difference on 16 bits 
5438     if (n2 > 0x200) {
5439
5440         Fail("Differences too big (%x)", n2);
5441         return 0;
5442     }
5443
5444     
5445     return 1;
5446 }
5447
5448
5449 // Check a linear xform
5450 static
5451 cmsInt32Number CheckFloatlinearXFORM(cmsHTRANSFORM xform, cmsInt32Number nChan)
5452 {
5453     cmsInt32Number n2, i, j;
5454     cmsFloat32Number In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS];
5455
5456     n2=0;
5457     
5458     for (j=0; j < 0xFFFF; j++) {
5459
5460         for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5461
5462         cmsDoTransform(xform, In, Out, 1);
5463         
5464         for (i=0; i < nChan; i++) {
5465
5466            // We allow no difference in floating point
5467             if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out[i], (cmsFloat32Number) (j / 65535.0))) 
5468                 return 0;
5469         }        
5470     }
5471     
5472     return 1;
5473 }
5474
5475
5476 // Check a linear xform
5477 static
5478 cmsInt32Number CompareFloatXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, cmsInt32Number nChan)
5479 {
5480     cmsInt32Number n2, i, j;
5481     cmsFloat32Number In[cmsMAXCHANNELS], Out1[cmsMAXCHANNELS], Out2[cmsMAXCHANNELS];
5482
5483     n2=0;
5484     
5485     for (j=0; j < 0xFFFF; j++) {
5486
5487         for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
5488
5489         cmsDoTransform(xform1, In, Out1, 1);
5490         cmsDoTransform(xform2, In, Out2, 1);
5491
5492         for (i=0; i < nChan; i++) {
5493
5494            // We allow no difference in floating point
5495             if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out1[i], Out2[i])) 
5496                 return 0;
5497         }
5498         
5499     }
5500     
5501     return 1;
5502 }
5503
5504
5505 // Curves only transforms ----------------------------------------------------------------------------------------
5506
5507 static
5508 cmsInt32Number CheckCurvesOnlyTransforms(void)
5509 {
5510
5511     cmsHTRANSFORM xform1, xform2;
5512     cmsHPROFILE h1, h2, h3;
5513     cmsToneCurve* c1, *c2, *c3;
5514     cmsInt32Number rc = 1;
5515
5516
5517     c1 = cmsBuildGamma(DbgThread(), 2.2);
5518     c2 = cmsBuildGamma(DbgThread(), 1/2.2);
5519     c3 = cmsBuildGamma(DbgThread(), 4.84);    
5520
5521     h1 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c1);
5522     h2 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c2);
5523     h3 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c3);
5524
5525     SubTest("Gray float optimizeable transform");
5526     xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h2, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);    
5527     rc &= CheckFloatlinearXFORM(xform1, 1);
5528     cmsDeleteTransform(xform1);
5529     if (rc == 0) goto Error;
5530     
5531     SubTest("Gray 8 optimizeable transform");
5532     xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h2, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);    
5533     rc &= Check8linearXFORM(xform1, 1);
5534     cmsDeleteTransform(xform1);
5535     if (rc == 0) goto Error;
5536     
5537     SubTest("Gray 16 optimizeable transform");
5538     xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h2, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);  
5539     rc &= Check16linearXFORM(xform1, 1);
5540     cmsDeleteTransform(xform1);
5541     if (rc == 0) goto Error;
5542
5543     SubTest("Gray float non-optimizeable transform");
5544     xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h1, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);    
5545     xform2 = cmsCreateTransform(h3, TYPE_GRAY_FLT, NULL, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);  
5546
5547     rc &= CompareFloatXFORM(xform1, xform2, 1);
5548     cmsDeleteTransform(xform1);
5549     cmsDeleteTransform(xform2);
5550     if (rc == 0) goto Error;
5551     
5552     SubTest("Gray 8 non-optimizeable transform");
5553     xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h1, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);    
5554     xform2 = cmsCreateTransform(h3, TYPE_GRAY_8, NULL, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);  
5555
5556     rc &= Compare8bitXFORM(xform1, xform2, 1);
5557     cmsDeleteTransform(xform1);
5558     cmsDeleteTransform(xform2);
5559     if (rc == 0) goto Error;
5560
5561
5562     SubTest("Gray 16 non-optimizeable transform");
5563     xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h1, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);  
5564     xform2 = cmsCreateTransform(h3, TYPE_GRAY_16, NULL, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);    
5565
5566     rc &= Compare16bitXFORM(xform1, xform2, 1);
5567     cmsDeleteTransform(xform1);
5568     cmsDeleteTransform(xform2);
5569     if (rc == 0) goto Error;
5570
5571 Error:
5572
5573     cmsCloseProfile(h1); cmsCloseProfile(h2); cmsCloseProfile(h3);
5574     cmsFreeToneCurve(c1); cmsFreeToneCurve(c2); cmsFreeToneCurve(c3);
5575     
5576     return rc;
5577 }
5578     
5579
5580
5581 // Lab to Lab trivial transforms ----------------------------------------------------------------------------------------
5582
5583 static cmsFloat64Number MaxDE;
5584
5585 static
5586 cmsInt32Number CheckOneLab(cmsHTRANSFORM xform, cmsFloat64Number L, cmsFloat64Number a, cmsFloat64Number b)
5587 {
5588     cmsCIELab In, Out;
5589     cmsFloat64Number dE;
5590
5591     In.L = L; In.a = a; In.b = b;
5592     cmsDoTransform(xform, &In, &Out, 1);
5593
5594     dE = cmsDeltaE(&In, &Out);
5595     
5596     if (dE > MaxDE) MaxDE = dE;
5597
5598     if (MaxDE >  0.003) {
5599         Fail("dE=%f Lab1=(%f, %f, %f)\n\tLab2=(%f %f %f)", MaxDE, In.L, In.a, In.b, Out.L, Out.a, Out.b);
5600         cmsDoTransform(xform, &In, &Out, 1);
5601         return 0;
5602     }
5603
5604     return 1;
5605 }
5606
5607 // Check several Lab, slicing at non-exact values. Precision should be 16 bits. 50x50x50 checks aprox.
5608 static
5609 cmsInt32Number CheckSeveralLab(cmsHTRANSFORM xform)
5610 {
5611     cmsInt32Number L, a, b;
5612
5613     MaxDE = 0;
5614     for (L=0; L < 65536; L += 1311) {
5615     
5616         for (a = 0; a < 65536; a += 1232) {
5617
5618             for (b = 0; b < 65536; b += 1111) {
5619
5620                 if (!CheckOneLab(xform, (L * 100.0) / 65535.0, 
5621                                         (a  / 257.0) - 128, (b / 257.0) - 128)) 
5622                     return 0;
5623             }
5624
5625         }
5626
5627     }
5628     return 1;
5629 }
5630
5631
5632 static
5633 cmsInt32Number OneTrivialLab(cmsHPROFILE hLab1, cmsHPROFILE hLab2, const char* txt)
5634 {   
5635     cmsHTRANSFORM xform;
5636     cmsInt32Number rc;
5637
5638     SubTest(txt);
5639     xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5640     cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5641
5642     rc = CheckSeveralLab(xform);
5643     cmsDeleteTransform(xform);
5644     return rc;
5645 }
5646
5647
5648 static
5649 cmsInt32Number CheckFloatLabTransforms(void)
5650 {
5651     return OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL),  "Lab4/Lab4") &&
5652            OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL),  "Lab2/Lab2") &&
5653            OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL),  "Lab4/Lab2") &&
5654            OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL),  "Lab2/Lab4");
5655 }
5656
5657
5658 static
5659 cmsInt32Number CheckEncodedLabTransforms(void)
5660 {
5661     cmsHTRANSFORM xform;
5662     cmsUInt16Number In[3];
5663     cmsCIELab Lab;
5664     cmsCIELab White = { 100, 0, 0 };
5665     cmsHPROFILE hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5666     cmsHPROFILE hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5667   
5668
5669     xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5670     cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5671
5672     In[0] = 0xFFFF;
5673     In[1] = 0x8080;
5674     In[2] = 0x8080;
5675
5676     cmsDoTransform(xform, In, &Lab, 1);
5677
5678     if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5679     cmsDeleteTransform(xform);
5680
5681     hLab1 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5682     hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5683
5684     xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_LabV2_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5685     cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5686
5687
5688     In[0] = 0xFF00;
5689     In[1] = 0x8000;
5690     In[2] = 0x8000;
5691
5692     cmsDoTransform(xform, In, &Lab, 1);
5693
5694     if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
5695
5696     cmsDeleteTransform(xform);
5697
5698     hLab2 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
5699     hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5700
5701     xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_LabV2_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5702     cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5703
5704     Lab.L = 100;
5705     Lab.a = 0;
5706     Lab.b = 0;
5707
5708     cmsDoTransform(xform, &Lab, In, 1);
5709     if (In[0] != 0xFF00 ||
5710         In[1] != 0x8000 ||
5711         In[2] != 0x8000) return 0;
5712
5713     cmsDeleteTransform(xform);
5714
5715     hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5716     hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5717
5718     xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_16, INTENT_RELATIVE_COLORIMETRIC, 0);
5719     cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
5720
5721     Lab.L = 100;
5722     Lab.a = 0;
5723     Lab.b = 0;
5724
5725     cmsDoTransform(xform, &Lab, In, 1);
5726
5727     if (In[0] != 0xFFFF ||
5728         In[1] != 0x8080 ||
5729         In[2] != 0x8080) return 0;
5730
5731     cmsDeleteTransform(xform);
5732    
5733     return 1;
5734 }
5735
5736 static
5737 cmsInt32Number CheckStoredIdentities(void)
5738 {
5739     cmsHPROFILE hLab, hLink, h4, h2;
5740     cmsHTRANSFORM xform;
5741     cmsInt32Number rc = 1;
5742
5743     hLab  = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5744     xform = cmsCreateTransformTHR(DbgThread(), hLab, TYPE_Lab_8, hLab, TYPE_Lab_8, 0, 0);
5745     
5746     hLink = cmsTransform2DeviceLink(xform, 3.4, 0);
5747     cmsSaveProfileToFile(hLink, "abstractv2.icc");
5748     cmsCloseProfile(hLink);
5749
5750     hLink = cmsTransform2DeviceLink(xform, 4.3, 0);
5751     cmsSaveProfileToFile(hLink, "abstractv4.icc");
5752     cmsCloseProfile(hLink);
5753
5754     cmsDeleteTransform(xform);
5755     cmsCloseProfile(hLab);
5756
5757     h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5758
5759     xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5760
5761     SubTest("V4");
5762     rc &= CheckSeveralLab(xform);
5763     
5764     cmsDeleteTransform(xform);
5765     cmsCloseProfile(h4);
5766     if (!rc) goto Error;
5767
5768     
5769     SubTest("V2");
5770     h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5771
5772     xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5773     rc &= CheckSeveralLab(xform);
5774     cmsDeleteTransform(xform);
5775     cmsCloseProfile(h2);
5776     if (!rc) goto Error;
5777
5778
5779     SubTest("V2 -> V4");
5780     h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5781     h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5782
5783     xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5784     rc &= CheckSeveralLab(xform);
5785     cmsDeleteTransform(xform);
5786     cmsCloseProfile(h2);
5787     cmsCloseProfile(h4);
5788
5789     SubTest("V4 -> V2");
5790     h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
5791     h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
5792
5793     xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
5794     rc &= CheckSeveralLab(xform);
5795     cmsDeleteTransform(xform);
5796     cmsCloseProfile(h2);
5797     cmsCloseProfile(h4);
5798
5799 Error:
5800     remove("abstractv2.icc");
5801     remove("abstractv4.icc");
5802     return rc;
5803
5804 }
5805
5806
5807
5808 // Check a simple xform from a matrix profile to itself. Test floating point accuracy.
5809 static
5810 cmsInt32Number CheckMatrixShaperXFORMFloat(void)
5811 {
5812     cmsHPROFILE hAbove, hSRGB;
5813     cmsHTRANSFORM xform;
5814     cmsInt32Number rc1, rc2;
5815
5816     hAbove = Create_AboveRGB();
5817     xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT,  INTENT_RELATIVE_COLORIMETRIC, 0);
5818     cmsCloseProfile(hAbove);
5819     rc1 = CheckFloatlinearXFORM(xform, 3);
5820     cmsDeleteTransform(xform);
5821
5822     hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5823     xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_FLT, hSRGB, TYPE_RGB_FLT,  INTENT_RELATIVE_COLORIMETRIC, 0);
5824     cmsCloseProfile(hSRGB);
5825     rc2 = CheckFloatlinearXFORM(xform, 3);
5826     cmsDeleteTransform(xform);
5827
5828
5829     return rc1 && rc2;  
5830 }
5831
5832 // Check a simple xform from a matrix profile to itself. Test 16 bits accuracy.
5833 static
5834 cmsInt32Number CheckMatrixShaperXFORM16(void)
5835 {   
5836     cmsHPROFILE hAbove, hSRGB;
5837     cmsHTRANSFORM xform;
5838     cmsInt32Number rc1, rc2;
5839
5840     hAbove = Create_AboveRGB();
5841     xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16,  INTENT_RELATIVE_COLORIMETRIC, 0);
5842     cmsCloseProfile(hAbove);
5843
5844     rc1 = Check16linearXFORM(xform, 3);
5845     cmsDeleteTransform(xform);
5846
5847     hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5848     xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_16, hSRGB, TYPE_RGB_16,  INTENT_RELATIVE_COLORIMETRIC, 0);
5849     cmsCloseProfile(hSRGB);
5850     rc2 = Check16linearXFORM(xform, 3);
5851     cmsDeleteTransform(xform);
5852
5853     return rc1 && rc2;  
5854
5855 }
5856
5857
5858 // Check a simple xform from a matrix profile to itself. Test 8 bits accuracy.
5859 static
5860 cmsInt32Number CheckMatrixShaperXFORM8(void)
5861 {   
5862     cmsHPROFILE hAbove, hSRGB;
5863     cmsHTRANSFORM xform;
5864     cmsInt32Number rc1, rc2;
5865
5866     hAbove = Create_AboveRGB();
5867     xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_8, hAbove, TYPE_RGB_8,  INTENT_RELATIVE_COLORIMETRIC, 0);
5868     cmsCloseProfile(hAbove);
5869     rc1 = Check8linearXFORM(xform, 3);
5870     cmsDeleteTransform(xform);
5871
5872     hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5873     xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_8, hSRGB, TYPE_RGB_8,  INTENT_RELATIVE_COLORIMETRIC, 0);
5874     cmsCloseProfile(hSRGB);
5875     rc2 = Check8linearXFORM(xform, 3);
5876     cmsDeleteTransform(xform);
5877
5878
5879     return rc1 && rc2;  
5880 }
5881
5882
5883 // TODO: Check LUT based to LUT based transforms for CMYK
5884
5885
5886
5887
5888
5889
5890 // -----------------------------------------------------------------------------------------------------------------
5891
5892
5893 // Check known values going from sRGB to XYZ
5894 static
5895 cmsInt32Number CheckOneRGB_f(cmsHTRANSFORM xform, cmsInt32Number R, cmsInt32Number G, cmsInt32Number B, cmsFloat64Number X, cmsFloat64Number Y, cmsFloat64Number Z, cmsFloat64Number err)
5896 {
5897     cmsFloat32Number RGB[3];
5898     cmsFloat64Number Out[3];
5899
5900     RGB[0] = (cmsFloat32Number) (R / 255.0);
5901     RGB[1] = (cmsFloat32Number) (G / 255.0);
5902     RGB[2] = (cmsFloat32Number) (B / 255.0);
5903
5904     cmsDoTransform(xform, RGB, Out, 1);
5905
5906     return IsGoodVal("X", X , Out[0], err) &&
5907            IsGoodVal("Y", Y , Out[1], err) &&
5908            IsGoodVal("Z", Z , Out[2], err);
5909 }
5910
5911 static
5912 cmsInt32Number Chack_sRGB_Float(void)
5913 {
5914     cmsHPROFILE hsRGB, hXYZ, hLab;
5915     cmsHTRANSFORM xform1, xform2;
5916     cmsInt32Number rc;
5917
5918
5919     hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5920     hXYZ  = cmsCreateXYZProfileTHR(DbgThread());
5921     hLab  = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
5922
5923     xform1 =  cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hXYZ, TYPE_XYZ_DBL,
5924                                 INTENT_RELATIVE_COLORIMETRIC, 0);
5925
5926     xform2 =  cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hLab, TYPE_Lab_DBL,
5927                                 INTENT_RELATIVE_COLORIMETRIC, 0);
5928     cmsCloseProfile(hsRGB);
5929     cmsCloseProfile(hXYZ);
5930     cmsCloseProfile(hLab);
5931
5932     MaxErr = 0;
5933
5934     // Xform 1 goes from 8 bits to XYZ,  
5935     rc  = CheckOneRGB_f(xform1, 1, 1, 1,        0.0002926, 0.00030352, 0.00025037, 0.0001);
5936     rc  &= CheckOneRGB_f(xform1, 127, 127, 127, 0.2046329, 0.212230,   0.175069,   0.0001);
5937     rc  &= CheckOneRGB_f(xform1, 12, 13, 15,    0.0038364, 0.0039928,  0.00385212, 0.0001);
5938     rc  &= CheckOneRGB_f(xform1, 128, 0, 0,     0.0940846, 0.0480030,  0.00300543, 0.0001);
5939     rc  &= CheckOneRGB_f(xform1, 190, 25, 210,  0.3203491, 0.1605240,  0.46817115, 0.0001);
5940     
5941     // Xform 2 goes from 8 bits to Lab, we allow 0.01 error max
5942     rc  &= CheckOneRGB_f(xform2, 1, 1, 1,       0.2741748, 0, 0,                  0.01);
5943     rc  &= CheckOneRGB_f(xform2, 127, 127, 127, 53.192776, 0, 0,                  0.01);
5944     rc  &= CheckOneRGB_f(xform2, 190, 25, 210,  47.043171, 74.564576, -56.89373,  0.01);
5945     rc  &= CheckOneRGB_f(xform2, 128, 0, 0,     26.158100, 48.474477, 39.425916,  0.01);
5946
5947     cmsDeleteTransform(xform1);
5948     cmsDeleteTransform(xform2);
5949     return rc;  
5950 }
5951
5952
5953 // ---------------------------------------------------
5954
5955 static
5956 cmsBool GetProfileRGBPrimaries(cmsHPROFILE hProfile,
5957                                 cmsCIEXYZTRIPLE *result,
5958                                 cmsUInt32Number intent)
5959 {
5960     cmsHPROFILE hXYZ;
5961     cmsHTRANSFORM hTransform;
5962     cmsFloat64Number rgb[3][3] = {{1., 0., 0.},
5963     {0., 1., 0.},
5964     {0., 0., 1.}};
5965
5966     hXYZ = cmsCreateXYZProfile();
5967     if (hXYZ == NULL) return FALSE;
5968
5969     hTransform = cmsCreateTransform(hProfile, TYPE_RGB_DBL, hXYZ, TYPE_XYZ_DBL,
5970         intent, cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
5971     cmsCloseProfile(hXYZ);
5972     if (hTransform == NULL) return FALSE;
5973
5974     cmsDoTransform(hTransform, rgb, result, 3);
5975     cmsDeleteTransform(hTransform);
5976     return TRUE;
5977 }
5978
5979
5980 static
5981 int CheckRGBPrimaries(void)
5982 {
5983     cmsHPROFILE hsRGB;
5984     cmsCIEXYZTRIPLE tripXYZ;
5985     cmsCIExyYTRIPLE tripxyY;
5986     cmsBool result;
5987
5988     cmsSetAdaptationState(0);
5989     hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
5990     if (!hsRGB) return 0;
5991
5992     result = GetProfileRGBPrimaries(hsRGB, &tripXYZ,
5993         INTENT_ABSOLUTE_COLORIMETRIC);
5994
5995     cmsCloseProfile(hsRGB);
5996     if (!result) return 0;
5997
5998     cmsXYZ2xyY(&tripxyY.Red, &tripXYZ.Red);
5999     cmsXYZ2xyY(&tripxyY.Green, &tripXYZ.Green);
6000     cmsXYZ2xyY(&tripxyY.Blue, &tripXYZ.Blue);
6001
6002     /* valus were taken from
6003     http://en.wikipedia.org/wiki/RGB_color_spaces#Specifications */
6004
6005     if (!IsGoodFixed15_16("xRed", tripxyY.Red.x, 0.64) ||
6006         !IsGoodFixed15_16("yRed", tripxyY.Red.y, 0.33) ||
6007         !IsGoodFixed15_16("xGreen", tripxyY.Green.x, 0.30) || 
6008         !IsGoodFixed15_16("yGreen", tripxyY.Green.y, 0.60) ||
6009         !IsGoodFixed15_16("xBlue", tripxyY.Blue.x, 0.15) || 
6010         !IsGoodFixed15_16("yBlue", tripxyY.Blue.y, 0.06)) {
6011             Fail("One or more primaries are wrong.");
6012             return FALSE;
6013     }
6014
6015     return TRUE;
6016 }
6017
6018
6019 // -----------------------------------------------------------------------------------------------------------------
6020
6021 // This function will check CMYK -> CMYK transforms. It uses FOGRA29 and SWOP ICC profiles
6022
6023 static
6024 cmsInt32Number CheckCMYK(cmsInt32Number Intent, const char *Profile1, const char* Profile2)
6025 {
6026     cmsHPROFILE hSWOP  = cmsOpenProfileFromFileTHR(DbgThread(), Profile1, "r");
6027     cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), Profile2, "r");
6028     cmsHTRANSFORM xform, swop_lab, fogra_lab;
6029     cmsFloat32Number CMYK1[4], CMYK2[4];
6030     cmsCIELab Lab1, Lab2;
6031     cmsHPROFILE hLab;
6032     cmsFloat64Number DeltaL, Max;
6033     cmsInt32Number i;
6034
6035     hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6036
6037     xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, Intent, 0);
6038
6039     swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP,   TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6040     fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
6041
6042     Max = 0;
6043     for (i=0; i <= 100; i++) {
6044
6045         CMYK1[0] = 10;
6046         CMYK1[1] = 20;
6047         CMYK1[2] = 30;
6048         CMYK1[3] = (cmsFloat32Number) i;
6049
6050         cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6051         cmsDoTransform(xform, CMYK1, CMYK2, 1);
6052         cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6053
6054         DeltaL = fabs(Lab1.L - Lab2.L);
6055
6056         if (DeltaL > Max) Max = DeltaL;
6057     }
6058
6059
6060     cmsDeleteTransform(xform);
6061
6062     if (Max > 3.0) return 0;
6063
6064     xform = cmsCreateTransformTHR(DbgThread(),  hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, Intent, 0);
6065
6066     Max = 0;
6067
6068     for (i=0; i <= 100; i++) {
6069         CMYK1[0] = 10;
6070         CMYK1[1] = 20;
6071         CMYK1[2] = 30;
6072         CMYK1[3] = (cmsFloat32Number) i;
6073
6074         cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6075         cmsDoTransform(xform, CMYK1, CMYK2, 1);
6076         cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6077
6078         DeltaL = fabs(Lab1.L - Lab2.L);
6079
6080         if (DeltaL > Max) Max = DeltaL;
6081     }
6082
6083
6084     cmsCloseProfile(hSWOP);
6085     cmsCloseProfile(hFOGRA);
6086     cmsCloseProfile(hLab);
6087
6088     cmsDeleteTransform(xform);
6089     cmsDeleteTransform(swop_lab);
6090     cmsDeleteTransform(fogra_lab);
6091
6092     return Max < 3.0;
6093 }
6094
6095 static
6096 cmsInt32Number CheckCMYKRoundtrip(void)
6097 {
6098     return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test1.icc");
6099 }
6100
6101
6102 static
6103 cmsInt32Number CheckCMYKPerceptual(void)
6104 {
6105     return CheckCMYK(INTENT_PERCEPTUAL, "test1.icc", "test2.icc");
6106 }
6107
6108
6109
6110 static
6111 cmsInt32Number CheckCMYKRelCol(void)
6112 {
6113     return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "test1.icc", "test2.icc");
6114 }
6115
6116
6117
6118 static
6119 cmsInt32Number CheckKOnlyBlackPreserving(void)
6120 {
6121     cmsHPROFILE hSWOP  = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6122     cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6123     cmsHTRANSFORM xform, swop_lab, fogra_lab;
6124     cmsFloat32Number CMYK1[4], CMYK2[4];
6125     cmsCIELab Lab1, Lab2;
6126     cmsHPROFILE hLab;
6127     cmsFloat64Number DeltaL, Max;
6128     cmsInt32Number i;
6129
6130     hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6131
6132     xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6133
6134     swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP,   TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6135     fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6136
6137     Max = 0;
6138
6139     for (i=0; i <= 100; i++) {
6140         CMYK1[0] = 0;
6141         CMYK1[1] = 0;
6142         CMYK1[2] = 0;
6143         CMYK1[3] = (cmsFloat32Number) i;
6144
6145         // SWOP CMYK to Lab1
6146         cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6147
6148         // SWOP To FOGRA using black preservation
6149         cmsDoTransform(xform, CMYK1, CMYK2, 1);
6150
6151         // Obtained FOGRA CMYK to Lab2
6152         cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6153
6154         // We care only on L*
6155         DeltaL = fabs(Lab1.L - Lab2.L);
6156
6157         if (DeltaL > Max) Max = DeltaL;
6158     }
6159
6160
6161     cmsDeleteTransform(xform);
6162
6163     // dL should be below 3.0
6164     if (Max > 3.0) return 0;
6165
6166
6167     // Same, but FOGRA to SWOP
6168     xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
6169
6170     Max = 0;
6171
6172     for (i=0; i <= 100; i++) {
6173         CMYK1[0] = 0;
6174         CMYK1[1] = 0;
6175         CMYK1[2] = 0;
6176         CMYK1[3] = (cmsFloat32Number) i;
6177
6178         cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6179         cmsDoTransform(xform, CMYK1, CMYK2, 1);
6180         cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6181
6182         DeltaL = fabs(Lab1.L - Lab2.L);
6183
6184         if (DeltaL > Max) Max = DeltaL;
6185     }
6186
6187
6188     cmsCloseProfile(hSWOP);
6189     cmsCloseProfile(hFOGRA);
6190     cmsCloseProfile(hLab);
6191
6192     cmsDeleteTransform(xform);
6193     cmsDeleteTransform(swop_lab);
6194     cmsDeleteTransform(fogra_lab);
6195
6196     return Max < 3.0;
6197 }
6198
6199 static
6200 cmsInt32Number CheckKPlaneBlackPreserving(void)
6201 {
6202     cmsHPROFILE hSWOP  = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6203     cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6204     cmsHTRANSFORM xform, swop_lab, fogra_lab;
6205     cmsFloat32Number CMYK1[4], CMYK2[4];
6206     cmsCIELab Lab1, Lab2;
6207     cmsHPROFILE hLab;
6208     cmsFloat64Number DeltaE, Max;
6209     cmsInt32Number i;
6210
6211     hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
6212
6213     xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PERCEPTUAL, 0);
6214
6215     swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP,  TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6216     fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
6217
6218     Max = 0;
6219
6220     for (i=0; i <= 100; i++) {
6221         CMYK1[0] = 0;
6222         CMYK1[1] = 0;
6223         CMYK1[2] = 0;
6224         CMYK1[3] = (cmsFloat32Number) i;
6225
6226         cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
6227         cmsDoTransform(xform, CMYK1, CMYK2, 1);
6228         cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
6229
6230         DeltaE = cmsDeltaE(&Lab1, &Lab2);
6231
6232         if (DeltaE > Max) Max = DeltaE;
6233     }
6234
6235
6236     cmsDeleteTransform(xform);
6237     
6238     xform = cmsCreateTransformTHR(DbgThread(),  hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_PLANE_PERCEPTUAL, 0);
6239
6240     for (i=0; i <= 100; i++) {
6241         CMYK1[0] = 30;
6242         CMYK1[1] = 20;
6243         CMYK1[2] = 10;
6244         CMYK1[3] = (cmsFloat32Number) i;
6245
6246         cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
6247         cmsDoTransform(xform, CMYK1, CMYK2, 1);
6248         cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
6249
6250         DeltaE = cmsDeltaE(&Lab1, &Lab2);
6251
6252         if (DeltaE > Max) Max = DeltaE;
6253     }
6254     
6255     cmsDeleteTransform(xform);
6256     
6257     
6258
6259     cmsCloseProfile(hSWOP);
6260     cmsCloseProfile(hFOGRA);
6261     cmsCloseProfile(hLab);
6262
6263     
6264     cmsDeleteTransform(swop_lab);
6265     cmsDeleteTransform(fogra_lab);
6266
6267     return Max < 30.0;
6268 }
6269
6270
6271 // ------------------------------------------------------------------------------------------------------
6272
6273
6274 static
6275 cmsInt32Number CheckProofingXFORMFloat(void)
6276 {
6277     cmsHPROFILE hAbove;
6278     cmsHTRANSFORM xform;
6279     cmsInt32Number rc;
6280
6281     hAbove = Create_AboveRGB();
6282     xform =  cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove, 
6283                                 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING);
6284     cmsCloseProfile(hAbove);
6285     rc = CheckFloatlinearXFORM(xform, 3);
6286     cmsDeleteTransform(xform);
6287     return rc;  
6288 }
6289
6290 static
6291 cmsInt32Number CheckProofingXFORM16(void)
6292 {
6293     cmsHPROFILE hAbove;
6294     cmsHTRANSFORM xform;
6295     cmsInt32Number rc;
6296
6297     hAbove = Create_AboveRGB();
6298     xform =  cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove, 
6299                                 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING|cmsFLAGS_NOCACHE);
6300     cmsCloseProfile(hAbove);
6301     rc = Check16linearXFORM(xform, 3);
6302     cmsDeleteTransform(xform);
6303     return rc;  
6304 }
6305
6306
6307 static
6308 cmsInt32Number CheckGamutCheck(void)
6309 {
6310         cmsHPROFILE hSRGB, hAbove;
6311         cmsHTRANSFORM xform;
6312         cmsInt32Number rc;
6313         cmsUInt16Number Alarm[3] = { 0xDEAD, 0xBABE, 0xFACE };
6314
6315         // Set alarm codes to fancy values so we could check the out of gamut condition
6316         cmsSetAlarmCodes(Alarm);
6317
6318         // Create the profiles
6319         hSRGB  = cmsCreate_sRGBProfileTHR(DbgThread());
6320         hAbove = Create_AboveRGB();
6321
6322         if (hSRGB == NULL || hAbove == NULL) return 0;  // Failed
6323
6324         SubTest("Gamut check on floating point");
6325
6326         // Create a gamut checker in the same space. No value should be out of gamut
6327         xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove, 
6328                                 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6329
6330
6331         if (!CheckFloatlinearXFORM(xform, 3)) {
6332             cmsCloseProfile(hSRGB);
6333             cmsCloseProfile(hAbove);
6334             cmsDeleteTransform(xform);
6335             Fail("Gamut check on same profile failed");
6336             return 0;
6337         }
6338
6339         cmsDeleteTransform(xform);
6340
6341         SubTest("Gamut check on 16 bits");
6342
6343         xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove, 
6344                                 INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
6345
6346         cmsCloseProfile(hSRGB);
6347         cmsCloseProfile(hAbove);
6348
6349         rc = Check16linearXFORM(xform, 3);
6350
6351         cmsDeleteTransform(xform);
6352
6353         return rc;
6354 }
6355
6356
6357
6358 // -------------------------------------------------------------------------------------------------------------------
6359
6360 static
6361 cmsInt32Number CheckBlackPoint(void)
6362 {
6363     cmsHPROFILE hProfile;
6364     cmsCIEXYZ Black;
6365     cmsCIELab Lab;
6366
6367     hProfile  = cmsOpenProfileFromFileTHR(DbgThread(), "test5.icc", "r");  
6368     cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6369     cmsCloseProfile(hProfile);
6370
6371
6372     hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6373     cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6374     cmsXYZ2Lab(NULL, &Lab, &Black);
6375     cmsCloseProfile(hProfile);
6376
6377     hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "lcms2cmyk.icc", "r");
6378     cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6379     cmsXYZ2Lab(NULL, &Lab, &Black);
6380     cmsCloseProfile(hProfile);
6381
6382     hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test2.icc", "r");
6383     cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
6384     cmsXYZ2Lab(NULL, &Lab, &Black);
6385     cmsCloseProfile(hProfile);
6386
6387     hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "test1.icc", "r");
6388     cmsDetectBlackPoint(&Black, hProfile, INTENT_PERCEPTUAL, 0);
6389     cmsXYZ2Lab(NULL, &Lab, &Black);
6390     cmsCloseProfile(hProfile);
6391
6392     return 1;
6393 }
6394
6395
6396 static
6397 cmsInt32Number CheckOneTAC(cmsFloat64Number InkLimit)
6398 {
6399     cmsHPROFILE h;
6400     cmsFloat64Number d;
6401
6402     h =CreateFakeCMYK(InkLimit, TRUE);
6403     cmsSaveProfileToFile(h, "lcmstac.icc");
6404     cmsCloseProfile(h);
6405
6406     h = cmsOpenProfileFromFile("lcmstac.icc", "r");
6407     d = cmsDetectTAC(h);
6408     cmsCloseProfile(h);
6409
6410     remove("lcmstac.icc");
6411
6412     if (fabs(d - InkLimit) > 5) return 0;
6413
6414     return 1;
6415 }
6416
6417
6418 static
6419 cmsInt32Number CheckTAC(void)
6420 {
6421     if (!CheckOneTAC(180)) return 0;
6422     if (!CheckOneTAC(220)) return 0;
6423     if (!CheckOneTAC(286)) return 0;
6424     if (!CheckOneTAC(310)) return 0;
6425     if (!CheckOneTAC(330)) return 0;
6426
6427     return 1;
6428 }
6429
6430 // -------------------------------------------------------------------------------------------------------
6431
6432
6433 #define NPOINTS_IT8 10  // (17*17*17*17)
6434
6435 static
6436 cmsInt32Number CheckCGATS(void)
6437 {
6438     cmsHANDLE  it8;
6439     cmsInt32Number i;
6440     
6441
6442     it8 = cmsIT8Alloc(DbgThread());
6443     if (it8 == NULL) return 0;
6444
6445     cmsIT8SetSheetType(it8, "LCMS/TESTING");
6446     cmsIT8SetPropertyStr(it8, "ORIGINATOR",   "1 2 3 4");
6447     cmsIT8SetPropertyUncooked(it8, "DESCRIPTOR",   "1234");
6448     cmsIT8SetPropertyStr(it8, "MANUFACTURER", "3");
6449     cmsIT8SetPropertyDbl(it8, "CREATED",      4);
6450     cmsIT8SetPropertyDbl(it8, "SERIAL",       5);
6451     cmsIT8SetPropertyHex(it8, "MATERIAL",     0x123);
6452
6453     cmsIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", NPOINTS_IT8);
6454     cmsIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", 4);
6455
6456     cmsIT8SetDataFormat(it8, 0, "SAMPLE_ID");
6457     cmsIT8SetDataFormat(it8, 1, "RGB_R");
6458     cmsIT8SetDataFormat(it8, 2, "RGB_G");
6459     cmsIT8SetDataFormat(it8, 3, "RGB_B");
6460
6461     for (i=0; i < NPOINTS_IT8; i++) {
6462
6463           char Patch[20];
6464
6465           sprintf(Patch, "P%d", i);
6466
6467           cmsIT8SetDataRowCol(it8, i, 0, Patch);
6468           cmsIT8SetDataRowColDbl(it8, i, 1, i);
6469           cmsIT8SetDataRowColDbl(it8, i, 2, i);
6470           cmsIT8SetDataRowColDbl(it8, i, 3, i);
6471     }
6472
6473     cmsIT8SaveToFile(it8, "TEST.IT8");
6474     cmsIT8Free(it8);
6475
6476
6477     it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6478     cmsIT8SaveToFile(it8, "TEST.IT8");
6479     cmsIT8Free(it8);
6480
6481
6482
6483     it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
6484
6485     if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 1234) {
6486    
6487         return 0;
6488     }
6489
6490
6491     cmsIT8SetPropertyDbl(it8, "DESCRIPTOR", 5678);
6492
6493     if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 5678) {
6494          
6495         return 0;
6496     }
6497
6498     if (cmsIT8GetDataDbl(it8, "P3", "RGB_G") != 3) {
6499    
6500         return 0;
6501     }
6502
6503     cmsIT8Free(it8);
6504
6505     remove("TEST.IT8");
6506     return 1;
6507
6508 }
6509
6510
6511 // Create CSA/CRD
6512
6513 static
6514 void GenerateCSA(const char* cInProf, const char* FileName)
6515 {
6516     cmsHPROFILE hProfile;   
6517     cmsUInt32Number n;
6518     char* Buffer;
6519     cmsContext BuffThread = DbgThread();
6520     FILE* o;
6521
6522
6523     if (cInProf == NULL) 
6524         hProfile = cmsCreateLab4Profile(NULL);
6525     else 
6526         hProfile = cmsOpenProfileFromFile(cInProf, "r");
6527
6528     n = cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, NULL, 0);
6529     if (n == 0) return;
6530
6531     Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6532     cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, Buffer, n);
6533     Buffer[n] = 0;
6534
6535     if (FileName != NULL) {
6536         o = fopen(FileName, "wb");
6537         fwrite(Buffer, n, 1, o);
6538         fclose(o);
6539     }
6540
6541     _cmsFree(BuffThread, Buffer);
6542     cmsCloseProfile(hProfile);
6543     remove(FileName);
6544 }
6545
6546
6547 static
6548 void GenerateCRD(const char* cOutProf, const char* FileName)
6549 {
6550     cmsHPROFILE hProfile;
6551     cmsUInt32Number n;
6552     char* Buffer;
6553     cmsUInt32Number dwFlags = 0;
6554     cmsContext BuffThread = DbgThread();
6555
6556
6557     if (cOutProf == NULL) 
6558         hProfile = cmsCreateLab4Profile(NULL);
6559     else 
6560         hProfile = cmsOpenProfileFromFile(cOutProf, "r");
6561
6562     n = cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, NULL, 0);
6563     if (n == 0) return;
6564
6565     Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
6566     cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, Buffer, n);
6567     Buffer[n] = 0;
6568
6569     if (FileName != NULL) {
6570         FILE* o = fopen(FileName, "wb");
6571         fwrite(Buffer, n, 1, o);
6572         fclose(o);
6573     }
6574
6575     _cmsFree(BuffThread, Buffer);
6576     cmsCloseProfile(hProfile);
6577     remove(FileName);
6578 }
6579
6580 static 
6581 cmsInt32Number CheckPostScript(void)
6582 {
6583     GenerateCSA("test5.icc", "sRGB_CSA.ps");
6584     GenerateCSA("aRGBlcms2.icc", "aRGB_CSA.ps");
6585     GenerateCSA("test4.icc", "sRGBV4_CSA.ps");
6586     GenerateCSA("test1.icc", "SWOP_CSA.ps");
6587     GenerateCSA(NULL, "Lab_CSA.ps");
6588     GenerateCSA("graylcms2.icc", "gray_CSA.ps");
6589     
6590     GenerateCRD("test5.icc", "sRGB_CRD.ps");
6591     GenerateCRD("aRGBlcms2.icc", "aRGB_CRD.ps");
6592     GenerateCRD(NULL, "Lab_CRD.ps");
6593     GenerateCRD("test1.icc", "SWOP_CRD.ps");
6594     GenerateCRD("test4.icc", "sRGBV4_CRD.ps");
6595     GenerateCRD("graylcms2.icc", "gray_CRD.ps");
6596
6597     return 1;
6598 }
6599
6600
6601 static
6602 cmsInt32Number CheckGray(cmsHTRANSFORM xform, cmsUInt8Number g, double L)
6603 {
6604     cmsCIELab Lab;
6605
6606     cmsDoTransform(xform, &g, &Lab, 1);
6607
6608     if (!IsGoodVal("a axis on gray", 0, Lab.a, 0.001)) return 0;
6609     if (!IsGoodVal("b axis on gray", 0, Lab.b, 0.001)) return 0;
6610
6611     return IsGoodVal("Gray value", L, Lab.L, 0.01);
6612 }
6613
6614 static
6615 cmsInt32Number CheckInputGray(void)
6616 {
6617     cmsHPROFILE hGray = Create_Gray22();
6618     cmsHPROFILE hLab  = cmsCreateLab4Profile(NULL);
6619     cmsHTRANSFORM xform;
6620
6621     if (hGray == NULL || hLab == NULL) return 0;
6622
6623     xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0); 
6624     cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6625
6626     if (!CheckGray(xform, 0, 0)) return 0;
6627     if (!CheckGray(xform, 125, 52.768)) return 0;
6628     if (!CheckGray(xform, 200, 81.069)) return 0;
6629     if (!CheckGray(xform, 255, 100.0)) return 0;
6630
6631     cmsDeleteTransform(xform);
6632     return 1;
6633 }
6634
6635 static
6636 cmsInt32Number CheckLabInputGray(void)
6637 {
6638     cmsHPROFILE hGray = Create_GrayLab();
6639     cmsHPROFILE hLab  = cmsCreateLab4Profile(NULL);
6640     cmsHTRANSFORM xform;
6641
6642     if (hGray == NULL || hLab == NULL) return 0;
6643
6644     xform = cmsCreateTransform(hGray, TYPE_GRAY_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
6645     cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6646
6647     if (!CheckGray(xform, 0, 0)) return 0;
6648     if (!CheckGray(xform, 125, 49.019)) return 0;
6649     if (!CheckGray(xform, 200, 78.431)) return 0;
6650     if (!CheckGray(xform, 255, 100.0)) return 0;
6651
6652     cmsDeleteTransform(xform);
6653     return 1;
6654 }
6655
6656
6657 static
6658 cmsInt32Number CheckOutGray(cmsHTRANSFORM xform, double L, cmsUInt8Number g)
6659 {
6660     cmsCIELab Lab;
6661     cmsUInt8Number g_out;
6662
6663     Lab.L = L;
6664     Lab.a = 0;
6665     Lab.b = 0;
6666
6667     cmsDoTransform(xform, &Lab, &g_out, 1);
6668
6669     return IsGoodVal("Gray value", g, (double) g_out, 0.01);
6670 }
6671
6672 static
6673 cmsInt32Number CheckOutputGray(void)
6674 {
6675     cmsHPROFILE hGray = Create_Gray22();
6676     cmsHPROFILE hLab  = cmsCreateLab4Profile(NULL);
6677     cmsHTRANSFORM xform;
6678
6679     if (hGray == NULL || hLab == NULL) return 0;
6680
6681     xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0); 
6682     cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6683
6684     if (!CheckOutGray(xform, 0, 0)) return 0;
6685     if (!CheckOutGray(xform, 100, 255)) return 0;
6686     
6687     if (!CheckOutGray(xform, 20, 52)) return 0;
6688     if (!CheckOutGray(xform, 50, 118)) return 0;
6689     
6690
6691     cmsDeleteTransform(xform);
6692     return 1;
6693 }
6694
6695
6696 static
6697 cmsInt32Number CheckLabOutputGray(void)
6698 {
6699     cmsHPROFILE hGray = Create_GrayLab();
6700     cmsHPROFILE hLab  = cmsCreateLab4Profile(NULL);
6701     cmsHTRANSFORM xform;
6702     cmsInt32Number i;
6703
6704     if (hGray == NULL || hLab == NULL) return 0;
6705
6706     xform = cmsCreateTransform( hLab, TYPE_Lab_DBL, hGray, TYPE_GRAY_8, INTENT_RELATIVE_COLORIMETRIC, 0);
6707     cmsCloseProfile(hGray); cmsCloseProfile(hLab);
6708
6709     if (!CheckOutGray(xform, 0, 0)) return 0;
6710     if (!CheckOutGray(xform, 100, 255)) return 0;
6711
6712     for (i=0; i < 100; i++) {
6713
6714         cmsUInt8Number g;
6715
6716         g = (cmsUInt8Number) floor(i * 255.0 / 100.0 + 0.5);
6717
6718         if (!CheckOutGray(xform, i, g)) return 0;
6719     }
6720     
6721
6722     cmsDeleteTransform(xform);
6723     return 1;
6724 }
6725
6726
6727 static
6728 cmsInt32Number CheckV4gamma(void)
6729 {
6730     cmsHPROFILE h;
6731     cmsUInt16Number Lin[] = {0, 0xffff};
6732     cmsToneCurve*g = cmsBuildTabulatedToneCurve16(DbgThread(), 2, Lin);
6733
6734     h = cmsOpenProfileFromFileTHR(DbgThread(), "v4gamma.icc", "w");
6735     if (h == NULL) return 0;
6736
6737     
6738     cmsSetProfileVersion(h, 4.3);
6739
6740     if (!cmsWriteTag(h, cmsSigGrayTRCTag, g)) return 0;
6741     cmsCloseProfile(h);
6742
6743     cmsFreeToneCurve(g);
6744     remove("v4gamma.icc");
6745     return 1;
6746 }
6747
6748 // cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname);
6749
6750 // Gamut descriptor routines
6751 static
6752 cmsInt32Number CheckGBD(void)
6753 {
6754     cmsCIELab Lab;
6755     cmsHANDLE  h;
6756     cmsInt32Number L, a, b;
6757     cmsUInt32Number r1, g1, b1;
6758     cmsHPROFILE hLab, hsRGB;
6759     cmsHTRANSFORM xform;
6760
6761     h = cmsGBDAlloc(DbgThread());
6762     if (h == NULL) return 0;
6763
6764     // Fill all Lab gamut as valid
6765     SubTest("Filling RAW gamut");
6766
6767     for (L=0; L <= 100; L += 10)
6768         for (a = -128; a <= 128; a += 5)
6769             for (b = -128; b <= 128; b += 5) {
6770
6771                 Lab.L = L;
6772                 Lab.a = a;
6773                 Lab.b = b;
6774                 if (!cmsGDBAddPoint(h, &Lab)) return 0;
6775             }
6776
6777     // Complete boundaries
6778     SubTest("computing Lab gamut");
6779     if (!cmsGDBCompute(h, 0)) return 0;
6780
6781
6782     // All points should be inside gamut
6783     SubTest("checking Lab gamut");
6784     for (L=10; L <= 90; L += 25)
6785         for (a = -120; a <= 120; a += 25)
6786             for (b = -120; b <= 120; b += 25) {
6787
6788                 Lab.L = L;
6789                 Lab.a = a;
6790                 Lab.b = b;
6791                 if (!cmsGDBCheckPoint(h, &Lab)) {
6792                     return 0;
6793                 }
6794             }
6795     cmsGBDFree(h);
6796
6797
6798     // Now for sRGB
6799     SubTest("checking sRGB gamut");
6800     h = cmsGBDAlloc(DbgThread());
6801     hsRGB = cmsCreate_sRGBProfile();
6802     hLab  = cmsCreateLab4Profile(NULL);
6803
6804     xform = cmsCreateTransform(hsRGB, TYPE_RGB_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE);
6805     cmsCloseProfile(hsRGB); cmsCloseProfile(hLab);
6806
6807
6808     for (r1=0; r1 < 256; r1 += 5) {
6809         for (g1=0; g1 < 256; g1 += 5)
6810             for (b1=0; b1 < 256; b1 += 5) {
6811
6812                 
6813                 cmsUInt8Number rgb[3];  
6814
6815                 rgb[0] = (cmsUInt8Number) r1;
6816                 rgb[1] = (cmsUInt8Number) g1; 
6817                 rgb[2] = (cmsUInt8Number) b1;
6818
6819                 cmsDoTransform(xform, rgb, &Lab, 1);
6820
6821                 // if (fabs(Lab.b) < 20 && Lab.a > 0) continue;
6822
6823                 if (!cmsGDBAddPoint(h, &Lab)) {
6824                     cmsGBDFree(h);
6825                     return 0;
6826                 }
6827                 
6828
6829             }
6830     }
6831     
6832
6833     if (!cmsGDBCompute(h, 0)) return 0;
6834     // cmsGBDdumpVRML(h, "c:\\colormaps\\lab.wrl");
6835
6836     for (r1=10; r1 < 200; r1 += 10) {
6837         for (g1=10; g1 < 200; g1 += 10)
6838             for (b1=10; b1 < 200; b1 += 10) {
6839
6840                 
6841                 cmsUInt8Number rgb[3];  
6842
6843                 rgb[0] = (cmsUInt8Number) r1;
6844                 rgb[1] = (cmsUInt8Number) g1; 
6845                 rgb[2] = (cmsUInt8Number) b1;
6846
6847                 cmsDoTransform(xform, rgb, &Lab, 1);
6848                 if (!cmsGDBCheckPoint(h, &Lab)) {
6849
6850                     cmsDeleteTransform(xform);
6851                     cmsGBDFree(h);
6852                     return 0;
6853                 }
6854             }
6855     }
6856
6857
6858     cmsDeleteTransform(xform);
6859     cmsGBDFree(h);
6860
6861     SubTest("checking LCh chroma ring");
6862     h = cmsGBDAlloc(DbgThread());
6863
6864     
6865     for (r1=0; r1 < 360; r1++) {
6866
6867         cmsCIELCh LCh;
6868
6869         LCh.L = 70;
6870         LCh.C = 60;
6871         LCh.h = r1;
6872
6873         cmsLCh2Lab(&Lab, &LCh);
6874         if (!cmsGDBAddPoint(h, &Lab)) {
6875                     cmsGBDFree(h);
6876                     return 0;
6877                 }
6878     }
6879     
6880
6881     if (!cmsGDBCompute(h, 0)) return 0;
6882
6883     cmsGBDFree(h);
6884
6885     return 1;
6886 }
6887
6888
6889 static
6890 int CheckMD5(void)
6891 {
6892     _cmsICCPROFILE* h;
6893     cmsHPROFILE pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r"); 
6894     cmsProfileID ProfileID1, ProfileID2, ProfileID3, ProfileID4;
6895
6896     h =(_cmsICCPROFILE*) pProfile;
6897     if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID1.ID8); 
6898     if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID2.ID8); 
6899
6900     cmsCloseProfile(pProfile);
6901     
6902
6903      pProfile = cmsOpenProfileFromFile("sRGBlcms2.icc", "r");  
6904
6905       h =(_cmsICCPROFILE*) pProfile;
6906     if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile, ProfileID3.ID8); 
6907     if (cmsMD5computeID(pProfile)) cmsGetHeaderProfileID(pProfile,ProfileID4.ID8); 
6908
6909     cmsCloseProfile(pProfile);
6910
6911     return ((memcmp(ProfileID1.ID8, ProfileID3.ID8, sizeof(ProfileID1)) == 0) && 
6912             (memcmp(ProfileID2.ID8, ProfileID4.ID8, sizeof(ProfileID2)) == 0));
6913 }
6914
6915
6916 // --------------------------------------------------------------------------------------------------
6917 // P E R F O R M A N C E   C H E C K S
6918 // --------------------------------------------------------------------------------------------------
6919
6920
6921 typedef struct {cmsUInt8Number r, g, b, a;}   Scanline_rgb1;
6922 typedef struct {cmsUInt16Number r, g, b, a;}  Scanline_rgb2;
6923 typedef struct {cmsUInt8Number r, g, b;}      Scanline_rgb8;
6924 typedef struct {cmsUInt16Number r, g, b;}     Scanline_rgb0;
6925
6926
6927 static 
6928 void TitlePerformance(const char* Txt)
6929 {
6930     printf("%-45s: ", Txt); fflush(stdout);
6931 }
6932
6933 static
6934 void PrintPerformance(cmsUInt32Number Bytes, cmsUInt32Number SizeOfPixel, cmsFloat64Number diff)
6935 {
6936     cmsFloat64Number seconds  = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
6937     cmsFloat64Number mpix_sec = Bytes / (1024.0*1024.0*seconds*SizeOfPixel);
6938
6939     printf("%g MPixel/sec.\n", mpix_sec);
6940     fflush(stdout);
6941 }
6942
6943
6944 static
6945 void SpeedTest16bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
6946 {
6947
6948     cmsInt32Number r, g, b, j;
6949     clock_t atime;
6950     cmsFloat64Number diff;
6951     cmsHTRANSFORM hlcmsxform;
6952     Scanline_rgb0 *In;
6953     cmsUInt32Number Mb;
6954
6955     if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL) 
6956         Die("Unable to open profiles");
6957
6958     hlcmsxform  = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_16, 
6959                                hlcmsProfileOut, TYPE_RGB_16, Intent, cmsFLAGS_NOCACHE);
6960     cmsCloseProfile(hlcmsProfileIn);
6961     cmsCloseProfile(hlcmsProfileOut);
6962
6963     Mb = 256*256*256*sizeof(Scanline_rgb0);
6964     In = (Scanline_rgb0*) malloc(Mb);
6965
6966     j = 0;
6967     for (r=0; r < 256; r++)
6968         for (g=0; g < 256; g++)
6969             for (b=0; b < 256; b++) {
6970
6971         In[j].r = (cmsUInt16Number) ((r << 8) | r);
6972         In[j].g = (cmsUInt16Number) ((g << 8) | g);
6973         In[j].b = (cmsUInt16Number) ((b << 8) | b);
6974
6975         j++;
6976     }
6977
6978
6979     TitlePerformance(Title);
6980
6981     atime = clock();
6982
6983     cmsDoTransform(hlcmsxform, In, In, 256*256*256);
6984
6985     diff = clock() - atime;
6986     free(In);
6987         
6988     PrintPerformance(Mb, sizeof(Scanline_rgb0), diff);
6989     cmsDeleteTransform(hlcmsxform);
6990     
6991 }
6992
6993
6994 static
6995 void SpeedTest16bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
6996 {
6997     cmsInt32Number r, g, b, j;
6998     clock_t atime;
6999     cmsFloat64Number diff;
7000     cmsHTRANSFORM hlcmsxform;
7001     Scanline_rgb2 *In;
7002     cmsUInt32Number Mb;
7003      
7004     if (hlcmsProfileOut == NULL || hlcmsProfileOut == NULL) 
7005         Die("Unable to open profiles");
7006
7007     hlcmsxform  = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_16, 
7008                 hlcmsProfileOut, TYPE_CMYK_16, INTENT_PERCEPTUAL,  cmsFLAGS_NOCACHE);
7009     cmsCloseProfile(hlcmsProfileIn);
7010     cmsCloseProfile(hlcmsProfileOut);
7011
7012     Mb = 256*256*256*sizeof(Scanline_rgb2);
7013
7014     In = (Scanline_rgb2*) malloc(Mb);
7015
7016     j = 0;
7017     for (r=0; r < 256; r++)
7018         for (g=0; g < 256; g++)
7019             for (b=0; b < 256; b++) {
7020
7021         In[j].r = (cmsUInt16Number) ((r << 8) | r);
7022         In[j].g = (cmsUInt16Number) ((g << 8) | g);
7023         In[j].b = (cmsUInt16Number) ((b << 8) | b);
7024         In[j].a = 0;
7025
7026         j++;
7027     }
7028
7029
7030     TitlePerformance(Title);
7031
7032     atime = clock();
7033
7034     cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7035
7036     diff = clock() - atime;
7037
7038     free(In);
7039     
7040     PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7041
7042     cmsDeleteTransform(hlcmsxform);
7043  
7044 }
7045
7046
7047 static
7048 void SpeedTest8bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7049 {
7050     cmsInt32Number r, g, b, j;
7051     clock_t atime;
7052     cmsFloat64Number diff;
7053     cmsHTRANSFORM hlcmsxform;
7054     Scanline_rgb8 *In;
7055     cmsUInt32Number Mb;
7056    
7057     if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL) 
7058         Die("Unable to open profiles");
7059
7060     hlcmsxform  = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_8, 
7061                             hlcmsProfileOut, TYPE_RGB_8, Intent, cmsFLAGS_NOCACHE);
7062     cmsCloseProfile(hlcmsProfileIn);
7063     cmsCloseProfile(hlcmsProfileOut);
7064
7065     Mb = 256*256*256*sizeof(Scanline_rgb8);
7066
7067     In = (Scanline_rgb8*) malloc(Mb);
7068
7069     j = 0;
7070     for (r=0; r < 256; r++)
7071         for (g=0; g < 256; g++)
7072             for (b=0; b < 256; b++) {
7073
7074         In[j].r = (cmsUInt8Number) r;
7075         In[j].g = (cmsUInt8Number) g;
7076         In[j].b = (cmsUInt8Number) b;
7077
7078         j++;
7079     }
7080
7081     TitlePerformance(Title);
7082
7083     atime = clock();
7084
7085     cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7086
7087     diff = clock() - atime;
7088
7089     free(In);
7090     
7091     PrintPerformance(Mb, sizeof(Scanline_rgb8), diff);
7092     
7093     cmsDeleteTransform(hlcmsxform);
7094
7095 }
7096
7097
7098 static
7099 void SpeedTest8bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
7100 {
7101     cmsInt32Number r, g, b, j;
7102     clock_t atime;
7103     cmsFloat64Number diff;
7104     cmsHTRANSFORM hlcmsxform;
7105     Scanline_rgb2 *In;
7106     cmsUInt32Number Mb;
7107     
7108     if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL) 
7109         Die("Unable to open profiles");
7110
7111     hlcmsxform  = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_8, 
7112                         hlcmsProfileOut, TYPE_CMYK_8, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
7113     cmsCloseProfile(hlcmsProfileIn);
7114     cmsCloseProfile(hlcmsProfileOut);
7115
7116     Mb = 256*256*256*sizeof(Scanline_rgb2);
7117
7118     In = (Scanline_rgb2*) malloc(Mb);
7119
7120     j = 0;
7121     for (r=0; r < 256; r++)
7122         for (g=0; g < 256; g++)
7123             for (b=0; b < 256; b++) {
7124
7125         In[j].r = (cmsUInt8Number) r;
7126         In[j].g = (cmsUInt8Number) g;
7127         In[j].b = (cmsUInt8Number) b;
7128         In[j].a = (cmsUInt8Number) 0;
7129
7130         j++;
7131     }
7132
7133     TitlePerformance(Title);
7134
7135     atime = clock();
7136
7137     cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7138
7139     diff = clock() - atime;
7140
7141     free(In);
7142     
7143     PrintPerformance(Mb, sizeof(Scanline_rgb2), diff);
7144
7145
7146     cmsDeleteTransform(hlcmsxform);
7147
7148 }
7149
7150
7151 static
7152 void SpeedTest8bitsGray(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
7153 {
7154     cmsInt32Number r, g, b, j;
7155     clock_t atime;
7156     cmsFloat64Number diff;
7157     cmsHTRANSFORM hlcmsxform;
7158     cmsUInt8Number *In;
7159     cmsUInt32Number Mb;
7160    
7161    
7162     if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL) 
7163         Die("Unable to open profiles");
7164
7165     hlcmsxform  = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, 
7166                         TYPE_GRAY_8, hlcmsProfileOut, TYPE_GRAY_8, Intent, cmsFLAGS_NOCACHE);
7167     cmsCloseProfile(hlcmsProfileIn);
7168     cmsCloseProfile(hlcmsProfileOut);
7169     Mb = 256*256*256;
7170
7171     In = (cmsUInt8Number*) malloc(Mb);
7172
7173     j = 0;
7174     for (r=0; r < 256; r++)
7175         for (g=0; g < 256; g++)
7176             for (b=0; b < 256; b++) {
7177
7178         In[j] = (cmsUInt8Number) r;
7179         
7180         j++;
7181     }
7182
7183     TitlePerformance(Title);
7184
7185     atime = clock();
7186
7187     cmsDoTransform(hlcmsxform, In, In, 256*256*256);
7188
7189     diff = clock() - atime;
7190     free(In);
7191         
7192     PrintPerformance(Mb, sizeof(cmsUInt8Number), diff);
7193     cmsDeleteTransform(hlcmsxform);
7194 }
7195
7196
7197 static
7198 cmsHPROFILE CreateCurves(void)
7199 {
7200     cmsToneCurve* Gamma = cmsBuildGamma(DbgThread(), 1.1);
7201     cmsToneCurve* Transfer[3];
7202     cmsHPROFILE h;
7203     
7204     Transfer[0] = Transfer[1] = Transfer[2] = Gamma;
7205     h = cmsCreateLinearizationDeviceLink(cmsSigRgbData, Transfer);
7206
7207     cmsFreeToneCurve(Gamma);
7208
7209     return h;
7210 }
7211     
7212
7213 static
7214 void SpeedTest(void)
7215 {
7216
7217     printf("\n\nP E R F O R M A N C E   T E S T S\n");
7218     printf(    "=================================\n\n");
7219     fflush(stdout);
7220
7221     SpeedTest16bits("16 bits on CLUT profiles", 
7222         cmsOpenProfileFromFile("test5.icc", "r"),
7223         cmsOpenProfileFromFile("test3.icc", "r"), INTENT_PERCEPTUAL);
7224
7225     SpeedTest8bits("8 bits on CLUT profiles", 
7226         cmsOpenProfileFromFile("test5.icc", "r"),
7227         cmsOpenProfileFromFile("test3.icc", "r"),
7228         INTENT_PERCEPTUAL);
7229     
7230     SpeedTest8bits("8 bits on Matrix-Shaper profiles", 
7231         cmsOpenProfileFromFile("test5.icc", "r"), 
7232         cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7233         INTENT_PERCEPTUAL);
7234
7235     SpeedTest8bits("8 bits on SAME Matrix-Shaper profiles",
7236         cmsOpenProfileFromFile("test5.icc", "r"),
7237         cmsOpenProfileFromFile("test5.icc", "r"),
7238         INTENT_PERCEPTUAL);
7239
7240     SpeedTest8bits("8 bits on Matrix-Shaper profiles (AbsCol)", 
7241        cmsOpenProfileFromFile("test5.icc", "r"),
7242        cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7243         INTENT_ABSOLUTE_COLORIMETRIC);  
7244
7245     SpeedTest16bits("16 bits on Matrix-Shaper profiles", 
7246        cmsOpenProfileFromFile("test5.icc", "r"),
7247         cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7248         INTENT_PERCEPTUAL);
7249
7250     SpeedTest16bits("16 bits on SAME Matrix-Shaper profiles", 
7251         cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7252         cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7253         INTENT_PERCEPTUAL);
7254
7255     SpeedTest16bits("16 bits on Matrix-Shaper profiles (AbsCol)", 
7256        cmsOpenProfileFromFile("test5.icc", "r"),
7257        cmsOpenProfileFromFile("aRGBlcms2.icc", "r"),
7258         INTENT_ABSOLUTE_COLORIMETRIC);
7259
7260     SpeedTest8bits("8 bits on curves", 
7261         CreateCurves(), 
7262         CreateCurves(),
7263         INTENT_PERCEPTUAL);
7264
7265     SpeedTest16bits("16 bits on curves", 
7266         CreateCurves(), 
7267         CreateCurves(),
7268         INTENT_PERCEPTUAL);
7269
7270     SpeedTest8bitsCMYK("8 bits on CMYK profiles", 
7271         cmsOpenProfileFromFile("test1.icc", "r"),
7272         cmsOpenProfileFromFile("test2.icc", "r"));
7273
7274     SpeedTest16bitsCMYK("16 bits on CMYK profiles", 
7275         cmsOpenProfileFromFile("test1.icc", "r"),
7276         cmsOpenProfileFromFile("test2.icc", "r"));
7277
7278     SpeedTest8bitsGray("8 bits on gray-to-gray",
7279         cmsOpenProfileFromFile("graylcms2.icc", "r"), 
7280         cmsOpenProfileFromFile("glablcms2.icc", "r"), INTENT_RELATIVE_COLORIMETRIC);
7281
7282     SpeedTest8bitsGray("8 bits on SAME gray-to-gray",
7283         cmsOpenProfileFromFile("graylcms2.icc", "r"), 
7284         cmsOpenProfileFromFile("graylcms2.icc", "r"), INTENT_PERCEPTUAL);
7285 }
7286
7287
7288 // -----------------------------------------------------------------------------------------------------
7289
7290
7291 // Print the supported intents
7292 static
7293 void PrintSupportedIntents(void)
7294 {
7295     cmsUInt32Number n, i;
7296     cmsUInt32Number Codes[200];
7297     char* Descriptions[200];
7298
7299     n = cmsGetSupportedIntents(200, Codes, Descriptions);
7300
7301     printf("Supported intents:\n");
7302     for (i=0; i < n; i++) {
7303         printf("\t%d - %s\n", Codes[i], Descriptions[i]);
7304     }
7305     printf("\n");
7306 }
7307
7308 // ZOO checks ------------------------------------------------------------------------------------------------------------
7309
7310
7311 #ifdef CMS_IS_WINDOWS_
7312
7313 static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\";
7314 static char ZOOwrite[cmsMAX_PATH]  = "c:\\colormaps\\write\\";
7315 static char ZOORawWrite[cmsMAX_PATH]  = "c:\\colormaps\\rawwrite\\";
7316
7317
7318 // Read all tags on a profile given by its handle
7319 static
7320 void ReadAllTags(cmsHPROFILE h)
7321 {
7322     cmsInt32Number i, n;
7323     cmsTagSignature sig;
7324
7325     n = cmsGetTagCount(h);
7326     for (i=0; i < n; i++) {
7327
7328         sig = cmsGetTagSignature(h, i);
7329         if (cmsReadTag(h, sig) == NULL) return;
7330     }
7331 }
7332
7333
7334 // Read all tags on a profile given by its handle
7335 static
7336 void ReadAllRAWTags(cmsHPROFILE h)
7337 {
7338     cmsInt32Number i, n;
7339     cmsTagSignature sig;
7340     cmsInt32Number len;
7341
7342     n = cmsGetTagCount(h);
7343     for (i=0; i < n; i++) {
7344
7345         sig = cmsGetTagSignature(h, i);
7346         len = cmsReadRawTag(h, sig, NULL, 0);
7347     }
7348 }
7349
7350
7351 static
7352 void PrintInfo(cmsHPROFILE h, cmsInfoType Info)
7353 {
7354     wchar_t* text;
7355     cmsInt32Number len;
7356     cmsContext id = DbgThread();
7357
7358     len = cmsGetProfileInfo(h, Info, "en", "US", NULL, 0);
7359     if (len == 0) return;
7360
7361     text = _cmsMalloc(id, len);
7362     cmsGetProfileInfo(h, Info, "en", "US", text, len);
7363
7364     wprintf(L"%s\n", text);
7365     _cmsFree(id, text);
7366 }
7367
7368
7369 static
7370 void PrintAllInfos(cmsHPROFILE h)
7371 {
7372      PrintInfo(h, cmsInfoDescription);
7373      PrintInfo(h, cmsInfoManufacturer);
7374      PrintInfo(h, cmsInfoModel);       
7375      PrintInfo(h, cmsInfoCopyright);   
7376      printf("\n\n");
7377 }
7378
7379 static
7380 void ReadAllLUTS(cmsHPROFILE h)
7381 {
7382     cmsPipeline* a;
7383     cmsCIEXYZ Black;
7384   
7385     a = _cmsReadInputLUT(h, INTENT_PERCEPTUAL);
7386     if (a) cmsPipelineFree(a);
7387
7388     a = _cmsReadInputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7389     if (a) cmsPipelineFree(a);
7390
7391     a = _cmsReadInputLUT(h, INTENT_SATURATION);
7392     if (a) cmsPipelineFree(a);
7393
7394     a = _cmsReadInputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7395     if (a) cmsPipelineFree(a);
7396
7397
7398     a = _cmsReadOutputLUT(h, INTENT_PERCEPTUAL);
7399     if (a) cmsPipelineFree(a);
7400
7401     a = _cmsReadOutputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7402     if (a) cmsPipelineFree(a);
7403
7404     a = _cmsReadOutputLUT(h, INTENT_SATURATION);
7405     if (a) cmsPipelineFree(a);
7406
7407     a = _cmsReadOutputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7408     if (a) cmsPipelineFree(a);
7409
7410
7411     a = _cmsReadDevicelinkLUT(h, INTENT_PERCEPTUAL);
7412     if (a) cmsPipelineFree(a);
7413
7414     a = _cmsReadDevicelinkLUT(h, INTENT_RELATIVE_COLORIMETRIC);
7415     if (a) cmsPipelineFree(a);
7416
7417     a = _cmsReadDevicelinkLUT(h, INTENT_SATURATION);
7418     if (a) cmsPipelineFree(a);
7419
7420     a = _cmsReadDevicelinkLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
7421     if (a) cmsPipelineFree(a);
7422
7423
7424     cmsDetectBlackPoint(&Black, h, INTENT_PERCEPTUAL, 0);
7425     cmsDetectBlackPoint(&Black, h, INTENT_RELATIVE_COLORIMETRIC, 0);
7426     cmsDetectBlackPoint(&Black, h, INTENT_SATURATION, 0);
7427     cmsDetectBlackPoint(&Black, h, INTENT_ABSOLUTE_COLORIMETRIC, 0);
7428     cmsDetectTAC(h);
7429 }
7430
7431 // Check one specimen in the ZOO
7432
7433 static
7434 cmsInt32Number CheckSingleSpecimen(const char* Profile)
7435 {
7436     char BuffSrc[256];
7437     char BuffDst[256];
7438     cmsHPROFILE h;
7439
7440     sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
7441     sprintf(BuffDst, "%s%s", ZOOwrite,  Profile);
7442
7443     h = cmsOpenProfileFromFile(BuffSrc, "r");
7444     if (h == NULL) return 0;
7445     
7446     printf("%s\n", Profile);
7447     PrintAllInfos(h);   
7448     ReadAllTags(h);  
7449     // ReadAllRAWTags(h);   
7450     ReadAllLUTS(h);
7451
7452     cmsSaveProfileToFile(h, BuffDst);
7453     cmsCloseProfile(h);
7454
7455     h = cmsOpenProfileFromFile(BuffDst, "r");
7456     if (h == NULL) return 0;
7457     ReadAllTags(h); 
7458     
7459
7460     cmsCloseProfile(h);
7461
7462     return 1;
7463 }
7464
7465 static
7466 cmsInt32Number CheckRAWSpecimen(const char* Profile)
7467 {
7468     char BuffSrc[256];
7469     char BuffDst[256];
7470     cmsHPROFILE h;
7471
7472     sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
7473     sprintf(BuffDst, "%s%s", ZOORawWrite,  Profile);
7474
7475     h = cmsOpenProfileFromFile(BuffSrc, "r");
7476     if (h == NULL) return 0;
7477     
7478     ReadAllTags(h);
7479     ReadAllRAWTags(h);  
7480     cmsSaveProfileToFile(h, BuffDst);
7481     cmsCloseProfile(h);
7482
7483     h = cmsOpenProfileFromFile(BuffDst, "r");
7484     if (h == NULL) return 0;
7485     ReadAllTags(h); 
7486     cmsCloseProfile(h);
7487
7488     return 1;
7489 }
7490
7491
7492 static
7493 void CheckProfileZOO(void)
7494 {
7495
7496     struct _finddata_t c_file;
7497     intptr_t hFile;
7498
7499     cmsSetLogErrorHandler(NULL);
7500
7501     if ( (hFile = _findfirst("c:\\colormaps\\*.*", &c_file)) == -1L )
7502         printf("No files in current directory");
7503     else
7504     {
7505         do
7506         {
7507
7508             printf("%s\n", c_file.name);
7509             if (strcmp(c_file.name, ".") != 0 &&
7510                 strcmp(c_file.name, "..") != 0) {
7511
7512                     CheckSingleSpecimen( c_file.name);
7513                     CheckRAWSpecimen( c_file.name);
7514
7515                     if (TotalMemory > 0)
7516                         printf("Ok, but %s are left!\n", MemStr(TotalMemory));
7517                     else
7518                         printf("Ok.\n");
7519
7520             }
7521
7522         } while ( _findnext(hFile, &c_file) == 0 );
7523
7524         _findclose(hFile);
7525     }
7526
7527     cmsSetLogErrorHandler(FatalErrorQuit);
7528 }
7529
7530 #endif
7531
7532
7533 #if 0
7534 #define TYPE_709 709 
7535 static double Rec709Math(int Type, const double Params[], double R) 
7536 { double Fun; 
7537
7538 switch (Type) 
7539
7540 case 709: 
7541
7542 if (R <= (Params[3]*Params[4])) Fun = R / Params[3]; 
7543 else Fun = pow(((R - Params[2])/Params[1]), Params[0]); 
7544 break; 
7545
7546 case -709: 
7547
7548 if (R <= Params[4]) Fun = R * Params[3]; 
7549 else Fun = Params[1] * pow(R, (1/Params[0])) + Params[2]; 
7550 break; 
7551
7552 return Fun; 
7553 }
7554
7555
7556 // Add nonstandard TRC curves -> Rec709 
7557 cmsPluginParametricCurves NewCurvePlugin = { 
7558 { cmsPluginMagicNumber, 2000, cmsPluginParametricCurveSig, NULL }, 
7559 1, {TYPE_709}, {5}, Rec709Math}; 
7560 #endif
7561
7562
7563
7564
7565 // ---------------------------------------------------------------------------------------
7566
7567 int main(int argc, char* argv[])
7568 {
7569     cmsInt32Number Exhaustive = 0;
7570     cmsInt32Number DoSpeedTests = 1;
7571
7572
7573 #ifdef _MSC_VER
7574     _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 
7575 #endif
7576
7577     printf("LittleCMS %2.2f test bed %s %s\n\n", LCMS_VERSION / 1000.0, __DATE__, __TIME__);
7578
7579     if ((argc == 2) && strcmp(argv[1], "--exhaustive") == 0) {
7580
7581         Exhaustive = 1;
7582         printf("Running exhaustive tests (will take a while...)\n\n");
7583     }
7584
7585     
7586     printf("Installing debug memory plug-in ... ");
7587     cmsPlugin(&DebugMemHandler);
7588     printf("done.\n");
7589
7590     printf("Installing error logger ... ");
7591     cmsSetLogErrorHandler(FatalErrorQuit);
7592     printf("done.\n");
7593
7594 #ifdef CMS_IS_WINDOWS_     
7595      // CheckProfileZOO();
7596 #endif
7597
7598     PrintSupportedIntents();
7599
7600     // Create utility profiles
7601     Check("Creation of test profiles", CreateTestProfiles);    
7602
7603     Check("Base types", CheckBaseTypes);
7604     Check("endianess", CheckEndianess);
7605     Check("quick floor", CheckQuickFloor);
7606     Check("quick floor word", CheckQuickFloorWord);
7607     Check("Fixed point 15.16 representation", CheckFixedPoint15_16);
7608     Check("Fixed point 8.8 representation", CheckFixedPoint8_8);
7609     
7610     // Forward 1D interpolation
7611     Check("1D interpolation in 2pt tables", Check1DLERP2);
7612     Check("1D interpolation in 3pt tables", Check1DLERP3);
7613     Check("1D interpolation in 4pt tables", Check1DLERP4);
7614     Check("1D interpolation in 6pt tables", Check1DLERP6);
7615     Check("1D interpolation in 18pt tables", Check1DLERP18);
7616     Check("1D interpolation in descending 2pt tables", Check1DLERP2Down);
7617     Check("1D interpolation in descending 3pt tables", Check1DLERP3Down);
7618     Check("1D interpolation in descending 6pt tables", Check1DLERP6Down);
7619     Check("1D interpolation in descending 18pt tables", Check1DLERP18Down);
7620     
7621     if (Exhaustive) {
7622
7623         Check("1D interpolation in n tables", ExhaustiveCheck1DLERP);
7624         Check("1D interpolation in descending tables", ExhaustiveCheck1DLERPDown);
7625     }
7626     
7627     // Forward 3D interpolation
7628     Check("3D interpolation Tetrahedral (float) ", Check3DinterpolationFloatTetrahedral);
7629     Check("3D interpolation Trilinear (float) ", Check3DinterpolationFloatTrilinear);
7630     Check("3D interpolation Tetrahedral (16) ", Check3DinterpolationTetrahedral16);
7631     Check("3D interpolation Trilinear (16) ", Check3DinterpolationTrilinear16);
7632     
7633     if (Exhaustive) {
7634
7635         Check("Exhaustive 3D interpolation Tetrahedral (float) ", ExaustiveCheck3DinterpolationFloatTetrahedral);
7636         Check("Exhaustive 3D interpolation Trilinear  (float) ", ExaustiveCheck3DinterpolationFloatTrilinear);
7637         Check("Exhaustive 3D interpolation Tetrahedral (16) ", ExhaustiveCheck3DinterpolationTetrahedral16);
7638         Check("Exhaustive 3D interpolation Trilinear (16) ", ExhaustiveCheck3DinterpolationTrilinear16);
7639     }
7640
7641     Check("Reverse interpolation 3 -> 3", CheckReverseInterpolation3x3);
7642     Check("Reverse interpolation 4 -> 3", CheckReverseInterpolation4x3);
7643   
7644
7645     // High dimensionality interpolation
7646
7647     Check("3D interpolation", Check3Dinterp);
7648     Check("3D interpolation with granularity", Check3DinterpGranular);
7649     Check("4D interpolation", Check4Dinterp);
7650     Check("4D interpolation with granularity", Check4DinterpGranular);
7651     Check("5D interpolation with granularity", Check5DinterpGranular);
7652     Check("6D interpolation with granularity", Check6DinterpGranular);
7653     Check("7D interpolation with granularity", Check7DinterpGranular);
7654     Check("8D interpolation with granularity", Check8DinterpGranular);
7655
7656     // Encoding of colorspaces
7657     Check("Lab to LCh and back (float only) ", CheckLab2LCh);
7658     Check("Lab to XYZ and back (float only) ", CheckLab2XYZ);
7659     Check("Lab to xyY and back (float only) ", CheckLab2xyY);
7660     Check("Lab V2 encoding", CheckLabV2encoding);
7661     Check("Lab V4 encoding", CheckLabV4encoding);
7662
7663     // BlackBody
7664     Check("Blackbody radiator", CheckTemp2CHRM);
7665     
7666     // Tone curves
7667     Check("Linear gamma curves (16 bits)", CheckGammaCreation16);
7668     Check("Linear gamma curves (float)", CheckGammaCreationFlt);
7669
7670     Check("Curve 1.8 (float)", CheckGamma18);
7671     Check("Curve 2.2 (float)", CheckGamma22);
7672     Check("Curve 3.0 (float)", CheckGamma30);
7673
7674     Check("Curve 1.8 (table)", CheckGamma18Table);
7675     Check("Curve 2.2 (table)", CheckGamma22Table);
7676     Check("Curve 3.0 (table)", CheckGamma30Table);
7677
7678     Check("Curve 1.8 (word table)", CheckGamma18TableWord);
7679     Check("Curve 2.2 (word table)", CheckGamma22TableWord);
7680     Check("Curve 3.0 (word table)", CheckGamma30TableWord);
7681
7682     Check("Parametric curves", CheckParametricToneCurves);
7683     
7684     Check("Join curves", CheckJointCurves);
7685     Check("Join curves descending", CheckJointCurvesDescending);
7686     Check("Join curves degenerated", CheckReverseDegenerated);  
7687     Check("Join curves sRGB (Float)", CheckJointFloatCurves_sRGB);
7688     Check("Join curves sRGB (16 bits)", CheckJoint16Curves_sRGB);
7689     Check("Join curves sigmoidal", CheckJointCurvesSShaped);
7690         
7691     // LUT basics
7692     Check("LUT creation & dup", CheckLUTcreation);
7693     Check("1 Stage LUT ", Check1StageLUT);
7694     Check("2 Stage LUT ", Check2StageLUT);
7695     Check("2 Stage LUT (16 bits)", Check2Stage16LUT);
7696     Check("3 Stage LUT ", Check3StageLUT);
7697     Check("3 Stage LUT (16 bits)", Check3Stage16LUT);
7698     Check("4 Stage LUT ", Check4StageLUT);
7699     Check("4 Stage LUT (16 bits)", Check4Stage16LUT);
7700     Check("5 Stage LUT ", Check5StageLUT);
7701     Check("5 Stage LUT (16 bits) ", Check5Stage16LUT);
7702     Check("6 Stage LUT ", Check6StageLUT);
7703     Check("6 Stage LUT (16 bits) ", Check6Stage16LUT);
7704     
7705     // LUT operation
7706     Check("Lab to Lab LUT (float only) ", CheckLab2LabLUT);
7707     Check("XYZ to XYZ LUT (float only) ", CheckXYZ2XYZLUT);
7708     Check("Lab to Lab MAT LUT (float only) ", CheckLab2LabMatLUT);
7709     Check("Named Color LUT", CheckNamedColorLUT);
7710     Check("Usual formatters", CheckFormatters16);
7711     Check("Floating point formatters", CheckFormattersFloat);
7712
7713     // ChangeBuffersFormat
7714     Check("ChangeBuffersFormat", CheckChangeBufferFormat);
7715     
7716     // MLU    
7717     Check("Multilocalized Unicode", CheckMLU);
7718     
7719     // Named color
7720     Check("Named color lists", CheckNamedColorList);
7721     
7722     // Profile I/O (this one is huge!)
7723     Check("Profile creation", CheckProfileCreation);
7724
7725     
7726     // Error reporting
7727     Check("Error reporting on bad profiles", CheckErrReportingOnBadProfiles);
7728     Check("Error reporting on bad transforms", CheckErrReportingOnBadTransforms);
7729     
7730     // Transforms
7731     Check("Curves only transforms", CheckCurvesOnlyTransforms);
7732     Check("Float Lab->Lab transforms", CheckFloatLabTransforms);
7733     Check("Encoded Lab->Lab transforms", CheckEncodedLabTransforms);    
7734     Check("Stored identities", CheckStoredIdentities);
7735
7736     Check("Matrix-shaper transform (float)",   CheckMatrixShaperXFORMFloat);
7737     Check("Matrix-shaper transform (16 bits)", CheckMatrixShaperXFORM16);   
7738     Check("Matrix-shaper transform (8 bits)",  CheckMatrixShaperXFORM8);
7739
7740     Check("Primaries of sRGB", CheckRGBPrimaries);
7741
7742     // Known values
7743     Check("Known values across matrix-shaper", Chack_sRGB_Float);
7744     Check("Gray input profile", CheckInputGray);
7745     Check("Gray Lab input profile", CheckLabInputGray);
7746     Check("Gray output profile", CheckOutputGray);
7747     Check("Gray Lab output profile", CheckLabOutputGray);
7748
7749     Check("Matrix-shaper proofing transform (float)",   CheckProofingXFORMFloat);
7750     Check("Matrix-shaper proofing transform (16 bits)",  CheckProofingXFORM16);
7751     
7752     Check("Gamut check", CheckGamutCheck);
7753         
7754     Check("CMYK roundtrip on perceptual transform",   CheckCMYKRoundtrip);
7755     
7756     Check("CMYK perceptual transform",   CheckCMYKPerceptual);
7757     // Check("CMYK rel.col. transform",   CheckCMYKRelCol);
7758     
7759     Check("Black ink only preservation", CheckKOnlyBlackPreserving);
7760     Check("Black plane preservation", CheckKPlaneBlackPreserving);
7761
7762  
7763     Check("Deciding curve types", CheckV4gamma);
7764
7765     Check("Black point detection", CheckBlackPoint);
7766     Check("TAC detection", CheckTAC);    
7767
7768     Check("CGATS parser", CheckCGATS);
7769     Check("PostScript generator", CheckPostScript);
7770     Check("Segment maxima GBD", CheckGBD);
7771     Check("MD5 digest", CheckMD5);
7772
7773
7774     if (DoSpeedTests)
7775         SpeedTest();
7776     
7777     DebugMemPrintTotals();
7778
7779     cmsUnregisterPlugins();
7780
7781     // Cleanup
7782    RemoveTestProfiles();
7783
7784    return TotalFail;
7785 }
7786  
7787
7788