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