1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2020 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 #include "testcms2.h"
28 
29 // --------------------------------------------------------------------------------------------------
30 // Auxiliary, duplicate a context and mark the block as non-debug because in this case the allocator
31 // and deallocator have different context owners
32 // --------------------------------------------------------------------------------------------------
33 
34 static
DupContext(cmsContext src,void * Data)35 cmsContext DupContext(cmsContext src, void* Data)
36 {
37     cmsContext cpy = cmsDupContext(src, Data);
38 
39     DebugMemDontCheckThis(cpy);
40 
41     return cpy;
42 }
43 
44 // --------------------------------------------------------------------------------------------------
45 // Simple context functions
46 // --------------------------------------------------------------------------------------------------
47 
48 // Allocation order
CheckAllocContext(cmsContext ContextID)49 cmsInt32Number CheckAllocContext(cmsContext ContextID)
50 {
51      cmsContext c1, c2, c3, c4;
52 
53      c1 = cmsCreateContext(NULL, NULL);                 // This creates a context by using the normal malloc
54      DebugMemDontCheckThis(c1);
55      cmsDeleteContext(c1);
56 
57      c2 = cmsCreateContext(PluginMemHandler(), NULL);   // This creates a context by using the debug malloc
58      DebugMemDontCheckThis(c2);
59      cmsDeleteContext(c2);
60 
61      c1 = cmsCreateContext(NULL, NULL);
62      DebugMemDontCheckThis(c1);
63 
64      c2 = cmsCreateContext(PluginMemHandler(), NULL);
65      DebugMemDontCheckThis(c2);
66 
67      cmsPlugin(c1, PluginMemHandler()); // Now the context have custom allocators
68 
69      c3 = DupContext(c1, NULL);
70      c4 = DupContext(c2, NULL);
71 
72      cmsDeleteContext(c1);  // Should be deleted by using nomal malloc
73      cmsDeleteContext(c2);  // Should be deleted by using debug malloc
74      cmsDeleteContext(c3);  // Should be deleted by using nomal malloc
75      cmsDeleteContext(c4);  // Should be deleted by using debug malloc
76 
77      return 1;
78 }
79 
80 // Test the very basic context capabilities
CheckSimpleContext(cmsContext ContextID)81 cmsInt32Number CheckSimpleContext(cmsContext ContextID)
82 {
83     int a = 1;
84     int b = 32;
85     cmsInt32Number rc = 0;
86 
87     cmsContext c1, c2, c3;
88 
89     // This function creates a context with a special
90     // memory manager that check allocation
91     c1 = WatchDogContext(&a);
92     cmsDeleteContext(c1);
93 
94     c1 = WatchDogContext(&a);
95 
96     // Let's check duplication
97     c2 = DupContext(c1, NULL);
98     c3 = DupContext(c2, NULL);
99 
100     // User data should have been propagated
101     rc = (*(int*) cmsGetContextUserData(c3)) == 1 ;
102 
103     // Free resources
104     cmsDeleteContext(c1);
105     cmsDeleteContext(c2);
106     cmsDeleteContext(c3);
107 
108     if (!rc) {
109         Fail("Creation of user data failed");
110         return 0;
111     }
112 
113     // Back to create 3 levels of inherance
114     c1 = cmsCreateContext(NULL, &a);
115     DebugMemDontCheckThis(c1);
116 
117     c2 = DupContext(c1, NULL);
118     c3 = DupContext(c2, &b);
119 
120     rc = (*(int*) cmsGetContextUserData(c3)) == 32 ;
121 
122     cmsDeleteContext(c1);
123     cmsDeleteContext(c2);
124     cmsDeleteContext(c3);
125 
126     if (!rc) {
127         Fail("Modification of user data failed");
128         return 0;
129     }
130 
131     // All seems ok
132     return rc;
133 }
134 
135 
136 
137 
138 // --------------------------------------------------------------------------------------------------
139 //Alarm color functions
140 // --------------------------------------------------------------------------------------------------
141 
142 // This function tests the alarm codes across contexts
CheckAlarmColorsContext(cmsContext ContextID)143 cmsInt32Number CheckAlarmColorsContext(cmsContext ContextID)
144 {
145     cmsInt32Number rc = 0;
146     const cmsUInt16Number codes[] = {0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999, 0xaaaa, 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff};
147     cmsUInt16Number out[16];
148     cmsContext c1, c2, c3;
149     int i;
150 
151     c1 = WatchDogContext(NULL);
152 
153     cmsSetAlarmCodes(c1, codes);
154     c2 = DupContext(c1, NULL);
155     c3 = DupContext(c2, NULL);
156 
157     cmsGetAlarmCodes(c3, out);
158 
159     rc = 1;
160     for (i=0; i < 16; i++) {
161         if (out[i] != codes[i]) {
162             Fail("Bad alarm code %x != %x", out[i], codes[i]);
163             rc = 0;
164             break;
165         }
166     }
167 
168     cmsDeleteContext(c1);
169     cmsDeleteContext(c2);
170     cmsDeleteContext(c3);
171 
172     return rc;
173 }
174 
175 
176 // --------------------------------------------------------------------------------------------------
177 //Adaptation state functions
178 // --------------------------------------------------------------------------------------------------
179 
180 // Similar to the previous, but for adaptation state
CheckAdaptationStateContext(cmsContext ContextID)181 cmsInt32Number CheckAdaptationStateContext(cmsContext ContextID)
182 {
183     cmsInt32Number rc = 0;
184     cmsContext c1, c2, c3;
185     cmsFloat64Number old1, old2;
186 
187     old1 =  cmsSetAdaptationState(NULL, -1);
188 
189     c1 = WatchDogContext(NULL);
190 
191     cmsSetAdaptationState(c1, 0.7);
192 
193     c2 = DupContext(c1, NULL);
194     c3 = DupContext(c2, NULL);
195 
196     rc = IsGoodVal("Adaptation state", cmsSetAdaptationState(c3, -1), 0.7, 0.001);
197 
198     cmsDeleteContext(c1);
199     cmsDeleteContext(c2);
200     cmsDeleteContext(c3);
201 
202     old2 =  cmsSetAdaptationState(NULL, -1);
203 
204     if (old1 != old2) {
205         Fail("Adaptation state has changed");
206         return 0;
207     }
208 
209     return rc;
210 }
211 
212 // --------------------------------------------------------------------------------------------------
213 // Interpolation plugin check: A fake 1D and 3D interpolation will be used to test the functionality.
214 // --------------------------------------------------------------------------------------------------
215 
216 // This fake interpolation takes always the closest lower node in the interpolation table for 1D
217 static
Fake1Dfloat(cmsContext ContextID,const cmsFloat32Number Value[],cmsFloat32Number Output[],const cmsInterpParams * p)218 void Fake1Dfloat(cmsContext ContextID, const cmsFloat32Number Value[],
219                     cmsFloat32Number Output[],
220                     const cmsInterpParams* p)
221 {
222        cmsFloat32Number val2;
223        int cell;
224        const cmsFloat32Number* LutTable = (const cmsFloat32Number*) p ->Table;
225 
226        // Clip upper values
227        if (Value[0] >= 1.0) {
228            Output[0] = LutTable[p -> Domain[0]];
229            return;
230        }
231 
232        val2 = p -> Domain[0] * Value[0];
233        cell = (int) floor(val2);
234        Output[0] =  LutTable[cell] ;
235 }
236 
237 // This fake interpolation just uses scrambled negated indexes for output
238 static
Fake3D16(cmsContext ContextID,register const cmsUInt16Number Input[],register cmsUInt16Number Output[],register const struct _cms_interp_struc * p)239 void Fake3D16(cmsContext ContextID, register const cmsUInt16Number Input[],
240               register cmsUInt16Number Output[],
241               register const struct _cms_interp_struc* p)
242 {
243        Output[0] =  0xFFFF - Input[2];
244        Output[1] =  0xFFFF - Input[1];
245        Output[2] =  0xFFFF - Input[0];
246 }
247 
248 // The factory chooses interpolation routines on depending on certain conditions.
my_Interpolators_Factory(cmsContext ContextID,cmsUInt32Number nInputChannels,cmsUInt32Number nOutputChannels,cmsUInt32Number dwFlags)249 cmsInterpFunction my_Interpolators_Factory(cmsContext ContextID, cmsUInt32Number nInputChannels,
250                                            cmsUInt32Number nOutputChannels,
251                                            cmsUInt32Number dwFlags)
252 {
253     cmsInterpFunction Interpolation;
254     cmsBool  IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT);
255 
256     // Initialize the return to zero as a non-supported mark
257     memset(&Interpolation, 0, sizeof(Interpolation));
258 
259     // For 1D to 1D and floating point
260     if (nInputChannels == 1 && nOutputChannels == 1 && IsFloat) {
261 
262         Interpolation.LerpFloat = Fake1Dfloat;
263     }
264     else
265     if (nInputChannels == 3 && nOutputChannels == 3 && !IsFloat) {
266 
267         // For 3D to 3D and 16 bits
268         Interpolation.Lerp16 = Fake3D16;
269     }
270 
271     // Here is the interpolation
272     return Interpolation;
273 }
274 
275 // Interpolation plug-in
276 static
277 cmsPluginInterpolation InterpPluginSample = {
278 
279     { cmsPluginMagicNumber, 2060-2000, cmsPluginInterpolationSig, NULL },
280     my_Interpolators_Factory
281 };
282 
283 
284 // This is the check code for 1D interpolation plug-in
CheckInterp1DPlugin(cmsContext ContextID)285 cmsInt32Number CheckInterp1DPlugin(cmsContext ContextID)
286 {
287     cmsToneCurve* Sampled1D = NULL;
288     cmsContext ctx = NULL;
289     cmsContext cpy = NULL;
290     const cmsFloat32Number tab[] = { 0.0f, 0.10f, 0.20f, 0.30f, 0.40f, 0.50f, 0.60f, 0.70f, 0.80f, 0.90f, 1.00f };  // A straight line
291 
292     // 1st level context
293     ctx = WatchDogContext(NULL);
294     if (ctx == NULL) {
295         Fail("Cannot create context");
296         goto Error;
297     }
298 
299     cmsPlugin(ctx, &InterpPluginSample);
300 
301     cpy = DupContext(ctx, NULL);
302      if (cpy == NULL) {
303         Fail("Cannot create context (2)");
304         goto Error;
305     }
306 
307     Sampled1D = cmsBuildTabulatedToneCurveFloat(cpy, 11, tab);
308     if (Sampled1D == NULL) {
309         Fail("Cannot create tone curve (1)");
310         goto Error;
311     }
312 
313     // Do some interpolations with the plugin
314     if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(cpy, Sampled1D, 0.10f), 0.10, 0.01)) goto Error;
315     if (!IsGoodVal("0.13", cmsEvalToneCurveFloat(cpy, Sampled1D, 0.13f), 0.10, 0.01)) goto Error;
316     if (!IsGoodVal("0.55", cmsEvalToneCurveFloat(cpy, Sampled1D, 0.55f), 0.50, 0.01)) goto Error;
317     if (!IsGoodVal("0.9999", cmsEvalToneCurveFloat(cpy, Sampled1D, 0.9999f), 0.90, 0.01)) goto Error;
318 
319     cmsFreeToneCurve(cpy, Sampled1D);
320     cmsDeleteContext(ctx);
321     cmsDeleteContext(cpy);
322 
323     // Now in global context
324     Sampled1D = cmsBuildTabulatedToneCurveFloat(NULL, 11, tab);
325     if (Sampled1D == NULL) {
326         Fail("Cannot create tone curve (2)");
327         goto Error;
328     }
329 
330     // Now without the plug-in
331     if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(NULL, Sampled1D, 0.10f), 0.10, 0.001)) goto Error;
332     if (!IsGoodVal("0.13", cmsEvalToneCurveFloat(NULL, Sampled1D, 0.13f), 0.13, 0.001)) goto Error;
333     if (!IsGoodVal("0.55", cmsEvalToneCurveFloat(NULL, Sampled1D, 0.55f), 0.55, 0.001)) goto Error;
334     if (!IsGoodVal("0.9999", cmsEvalToneCurveFloat(NULL, Sampled1D, 0.9999f), 0.9999, 0.001)) goto Error;
335 
336     cmsFreeToneCurve(NULL, Sampled1D);
337     return 1;
338 
339 Error:
340     if (ctx != NULL) cmsDeleteContext(ctx);
341      if (cpy != NULL) cmsDeleteContext(ctx);
342     if (Sampled1D != NULL) cmsFreeToneCurve(NULL, Sampled1D);
343     return 0;
344 
345 }
346 
347 // Checks the 3D interpolation
CheckInterp3DPlugin(cmsContext ContextID)348 cmsInt32Number CheckInterp3DPlugin(cmsContext ContextID)
349 {
350 
351     cmsPipeline* p;
352     cmsStage* clut;
353     cmsContext ctx;
354     cmsUInt16Number In[3], Out[3];
355     cmsUInt16Number identity[] = {
356 
357        0,       0,       0,
358        0,       0,       0xffff,
359        0,       0xffff,  0,
360        0,       0xffff,  0xffff,
361        0xffff,  0,       0,
362        0xffff,  0,       0xffff,
363        0xffff,  0xffff,  0,
364        0xffff,  0xffff,  0xffff
365     };
366 
367 
368     ctx = WatchDogContext(NULL);
369     if (ctx == NULL) {
370         Fail("Cannot create context");
371        return 0;
372     }
373 
374     cmsPlugin(ctx, &InterpPluginSample);
375 
376     p =  cmsPipelineAlloc(ctx, 3, 3);
377     clut = cmsStageAllocCLut16bit(ctx, 2, 3, 3, identity);
378     cmsPipelineInsertStage(ctx, p, cmsAT_BEGIN, clut);
379 
380     // Do some interpolations with the plugin
381 
382     In[0] = 0; In[1] = 0; In[2] = 0;
383     cmsPipelineEval16(ctx, In, Out, p);
384 
385     if (!IsGoodWord("0", Out[0], 0xFFFF - 0)) goto Error;
386     if (!IsGoodWord("1", Out[1], 0xFFFF - 0)) goto Error;
387     if (!IsGoodWord("2", Out[2], 0xFFFF - 0)) goto Error;
388 
389     In[0] = 0x1234; In[1] = 0x5678; In[2] = 0x9ABC;
390     cmsPipelineEval16(ctx, In, Out, p);
391 
392     if (!IsGoodWord("0", 0xFFFF - 0x9ABC, Out[0])) goto Error;
393     if (!IsGoodWord("1", 0xFFFF - 0x5678, Out[1])) goto Error;
394     if (!IsGoodWord("2", 0xFFFF - 0x1234, Out[2])) goto Error;
395 
396     cmsPipelineFree(ctx, p);
397     cmsDeleteContext(ctx);
398 
399     // Now without the plug-in
400 
401     p =  cmsPipelineAlloc(NULL, 3, 3);
402     clut = cmsStageAllocCLut16bit(NULL, 2, 3, 3, identity);
403     cmsPipelineInsertStage(NULL, p, cmsAT_BEGIN, clut);
404 
405     In[0] = 0; In[1] = 0; In[2] = 0;
406     cmsPipelineEval16(NULL, In, Out, p);
407 
408     if (!IsGoodWord("0", 0, Out[0])) goto Error;
409     if (!IsGoodWord("1", 0, Out[1])) goto Error;
410     if (!IsGoodWord("2", 0, Out[2])) goto Error;
411 
412     In[0] = 0x1234; In[1] = 0x5678; In[2] = 0x9ABC;
413     cmsPipelineEval16(NULL, In, Out, p);
414 
415     if (!IsGoodWord("0", 0x1234, Out[0])) goto Error;
416     if (!IsGoodWord("1", 0x5678, Out[1])) goto Error;
417     if (!IsGoodWord("2", 0x9ABC, Out[2])) goto Error;
418 
419     cmsPipelineFree(NULL, p);
420     return 1;
421 
422 Error:
423     cmsPipelineFree(NULL, p);
424     return 0;
425 }
426 
427 // --------------------------------------------------------------------------------------------------
428 // Parametric curve plugin check: sin(x)/cos(x) function will be used to test the functionality.
429 // --------------------------------------------------------------------------------------------------
430 
431 #define TYPE_SIN  1000
432 #define TYPE_COS  1010
433 #define TYPE_TAN  1020
434 #define TYPE_709  709
435 
my_fns(cmsContext ContextID,cmsInt32Number Type,const cmsFloat64Number Params[],cmsFloat64Number R)436 static cmsFloat64Number my_fns(cmsContext ContextID, cmsInt32Number Type,
437                         const cmsFloat64Number Params[],
438                         cmsFloat64Number R)
439 {
440     cmsFloat64Number Val;
441     switch (Type) {
442 
443     case TYPE_SIN:
444         Val = Params[0]* sin(R * M_PI);
445         break;
446 
447     case -TYPE_SIN:
448         Val = asin(R) / (M_PI * Params[0]);
449         break;
450 
451     case TYPE_COS:
452         Val = Params[0]* cos(R * M_PI);
453         break;
454 
455     case -TYPE_COS:
456         Val = acos(R) / (M_PI * Params[0]);
457         break;
458 
459     default: return -1.0;
460 
461      }
462 
463    return Val;
464 }
465 
466 static
my_fns2(cmsContext ContextID,cmsInt32Number Type,const cmsFloat64Number Params[],cmsFloat64Number R)467 cmsFloat64Number my_fns2(cmsContext ContextID, cmsInt32Number Type,
468                         const cmsFloat64Number Params[],
469                         cmsFloat64Number R)
470 {
471     cmsFloat64Number Val;
472     switch (Type) {
473 
474     case TYPE_TAN:
475         Val = Params[0]* tan(R * M_PI);
476         break;
477 
478     case -TYPE_TAN:
479         Val = atan(R) / (M_PI * Params[0]);
480         break;
481 
482      default: return -1.0;
483      }
484 
485    return Val;
486 }
487 
488 
Rec709Math(cmsContext ContextID,int Type,const double Params[],double R)489 static double Rec709Math(cmsContext ContextID, int Type, const double Params[], double R)
490 {
491     double Fun = 0;
492 
493     switch (Type)
494     {
495     case 709:
496 
497         if (R <= (Params[3]*Params[4])) Fun = R / Params[3];
498         else Fun = pow(((R - Params[2])/Params[1]), Params[0]);
499         break;
500 
501     case -709:
502 
503         if (R <= Params[4]) Fun = R * Params[3];
504         else Fun = Params[1] * pow(R, (1/Params[0])) + Params[2];
505         break;
506     }
507     return Fun;
508 }
509 
510 
511 // Add nonstandard TRC curves -> Rec709
512 
513 cmsPluginParametricCurves Rec709Plugin = {
514 
515     { cmsPluginMagicNumber, 2060-2000, cmsPluginParametricCurveSig, NULL },
516 
517     1, {TYPE_709}, {5}, Rec709Math
518 
519 };
520 
521 
522 static
523 cmsPluginParametricCurves CurvePluginSample = {
524     { cmsPluginMagicNumber, 2060-2000, cmsPluginParametricCurveSig, NULL },
525 
526     2,                       // nFunctions
527     { TYPE_SIN, TYPE_COS },  // Function Types
528     { 1, 1 },                // ParameterCount
529     my_fns                   // Evaluator
530 };
531 
532 static
533 cmsPluginParametricCurves CurvePluginSample2 = {
534     { cmsPluginMagicNumber, 2060-2000, cmsPluginParametricCurveSig, NULL },
535 
536     1,                       // nFunctions
537     { TYPE_TAN},             // Function Types
538     { 1 },                   // ParameterCount
539     my_fns2                  // Evaluator
540 };
541 
542 // --------------------------------------------------------------------------------------------------
543 // In this test, the DupContext function will be checked as well
544 // --------------------------------------------------------------------------------------------------
CheckParametricCurvePlugin(cmsContext ContextID)545 cmsInt32Number CheckParametricCurvePlugin(cmsContext ContextID)
546 {
547     cmsContext ctx = NULL;
548     cmsContext cpy = NULL;
549     cmsToneCurve* sinus;
550     cmsToneCurve* cosinus;
551     cmsToneCurve* tangent;
552     cmsToneCurve* reverse_sinus;
553     cmsToneCurve* reverse_cosinus;
554     cmsFloat64Number scale = 1.0;
555 
556     ctx = WatchDogContext(NULL);
557 
558     cmsPlugin(ctx, &CurvePluginSample);
559 
560     cpy = DupContext(ctx, NULL);
561 
562     cmsPlugin(cpy, &CurvePluginSample2);
563 
564     sinus = cmsBuildParametricToneCurve(cpy, TYPE_SIN, &scale);
565     cosinus = cmsBuildParametricToneCurve(cpy, TYPE_COS, &scale);
566     tangent = cmsBuildParametricToneCurve(cpy, TYPE_TAN, &scale);
567     reverse_sinus = cmsReverseToneCurve(cpy, sinus);
568     reverse_cosinus = cmsReverseToneCurve(cpy, cosinus);
569 
570 
571      if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(cpy, sinus, 0.10f), sin(0.10 * M_PI) , 0.001)) goto Error;
572      if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(cpy, sinus, 0.60f), sin(0.60* M_PI), 0.001)) goto Error;
573      if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(cpy, sinus, 0.90f), sin(0.90* M_PI), 0.001)) goto Error;
574 
575      if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(cpy, cosinus, 0.10f), cos(0.10* M_PI), 0.001)) goto Error;
576      if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(cpy, cosinus, 0.60f), cos(0.60* M_PI), 0.001)) goto Error;
577      if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(cpy, cosinus, 0.90f), cos(0.90* M_PI), 0.001)) goto Error;
578 
579      if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(cpy, tangent, 0.10f), tan(0.10* M_PI), 0.001)) goto Error;
580      if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(cpy, tangent, 0.60f), tan(0.60* M_PI), 0.001)) goto Error;
581      if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(cpy, tangent, 0.90f), tan(0.90* M_PI), 0.001)) goto Error;
582 
583 
584      if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(cpy, reverse_sinus, 0.10f), asin(0.10)/M_PI, 0.001)) goto Error;
585      if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(cpy, reverse_sinus, 0.60f), asin(0.60)/M_PI, 0.001)) goto Error;
586      if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(cpy, reverse_sinus, 0.90f), asin(0.90)/M_PI, 0.001)) goto Error;
587 
588      if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(cpy, reverse_cosinus, 0.10f), acos(0.10)/M_PI, 0.001)) goto Error;
589      if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(cpy, reverse_cosinus, 0.60f), acos(0.60)/M_PI, 0.001)) goto Error;
590      if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(cpy, reverse_cosinus, 0.90f), acos(0.90)/M_PI, 0.001)) goto Error;
591 
592      cmsFreeToneCurve(cpy, sinus);
593      cmsFreeToneCurve(cpy, cosinus);
594      cmsFreeToneCurve(cpy, tangent);
595      cmsFreeToneCurve(cpy, reverse_sinus);
596      cmsFreeToneCurve(cpy, reverse_cosinus);
597 
598      cmsDeleteContext(ctx);
599      cmsDeleteContext(cpy);
600 
601      return 1;
602 
603 Error:
604 
605      cmsFreeToneCurve(cpy, sinus);
606      cmsFreeToneCurve(cpy, reverse_sinus);
607      cmsFreeToneCurve(cpy, cosinus);
608      cmsFreeToneCurve(cpy, reverse_cosinus);
609 
610      if (ctx != NULL) cmsDeleteContext(ctx);
611      if (cpy != NULL) cmsDeleteContext(cpy);
612      return 0;
613 }
614 
615 // --------------------------------------------------------------------------------------------------
616 // formatters plugin check: 5-6-5 RGB format
617 // --------------------------------------------------------------------------------------------------
618 
619 // We define this special type as 0 bytes not float, and set the upper bit
620 
621 #define TYPE_RGB_565  (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0) | (1 << 23))
622 
my_Unroll565(cmsContext ContextID,register struct _cmstransform_struct * nfo,register cmsUInt16Number wIn[],register cmsUInt8Number * accum,register cmsUInt32Number Stride)623 cmsUInt8Number* my_Unroll565(cmsContext ContextID, register struct _cmstransform_struct* nfo,
624                             register cmsUInt16Number wIn[],
625                             register cmsUInt8Number* accum,
626                             register cmsUInt32Number Stride)
627 {
628     cmsUInt16Number pixel = *(cmsUInt16Number*) accum;  // Take whole pixel
629 
630     double r = floor(((double) (pixel & 31) * 65535.0) / 31.0 + 0.5);
631     double g = floor((((pixel >> 5) & 63) * 65535.0) / 63.0 + 0.5);
632     double b = floor((((pixel >> 11) & 31) * 65535.0) / 31.0 + 0.5);
633 
634     wIn[2] = (cmsUInt16Number) r;
635     wIn[1] = (cmsUInt16Number) g;
636     wIn[0] = (cmsUInt16Number) b;
637 
638     return accum + 2;
639 }
640 
my_Pack565(cmsContext ContextID,register _cmsTRANSFORM * info,register cmsUInt16Number wOut[],register cmsUInt8Number * output,register cmsUInt32Number Stride)641 cmsUInt8Number* my_Pack565(cmsContext ContextID, register _cmsTRANSFORM* info,
642                            register cmsUInt16Number wOut[],
643                            register cmsUInt8Number* output,
644                            register cmsUInt32Number Stride)
645 {
646 
647     register cmsUInt16Number pixel;
648     int r, g, b;
649 
650     r = (int) floor(( wOut[2] * 31) / 65535.0 + 0.5);
651     g = (int) floor(( wOut[1] * 63) / 65535.0 + 0.5);
652     b = (int) floor(( wOut[0] * 31) / 65535.0 + 0.5);
653 
654 
655     pixel = (r & 31)  | (( g & 63) << 5) | ((b & 31) << 11);
656 
657 
658     *(cmsUInt16Number*) output = pixel;
659     return output + 2;
660 }
661 
662 
my_FormatterFactory(cmsContext ContextID,cmsUInt32Number Type,cmsFormatterDirection Dir,cmsUInt32Number dwFlags)663 cmsFormatter my_FormatterFactory(cmsContext ContextID, cmsUInt32Number Type,
664                                   cmsFormatterDirection Dir,
665                                   cmsUInt32Number dwFlags)
666 {
667     cmsFormatter Result = { NULL };
668 
669     if ((Type == TYPE_RGB_565) &&
670         !(dwFlags & CMS_PACK_FLAGS_FLOAT) &&
671         (Dir == cmsFormatterInput)) {
672             Result.Fmt16 = my_Unroll565;
673     }
674     return Result;
675 }
676 
677 
my_FormatterFactory2(cmsContext ContextID,cmsUInt32Number Type,cmsFormatterDirection Dir,cmsUInt32Number dwFlags)678 cmsFormatter my_FormatterFactory2(cmsContext ContextID, cmsUInt32Number Type,
679                                   cmsFormatterDirection Dir,
680                                   cmsUInt32Number dwFlags)
681 {
682     cmsFormatter Result = { NULL };
683 
684     if ((Type == TYPE_RGB_565) &&
685         !(dwFlags & CMS_PACK_FLAGS_FLOAT) &&
686         (Dir == cmsFormatterOutput)) {
687             Result.Fmt16 = my_Pack565;
688     }
689     return Result;
690 }
691 
692 static
693 cmsPluginFormatters FormattersPluginSample = { {cmsPluginMagicNumber,
694                                 2060-2000,
695                                 cmsPluginFormattersSig,
696                                 NULL},
697                                 my_FormatterFactory };
698 
699 
700 
701 static
702 cmsPluginFormatters FormattersPluginSample2 = { {cmsPluginMagicNumber,
703                                 2060-2000,
704                                 cmsPluginFormattersSig,
705                                 NULL},
706                                 my_FormatterFactory2 };
707 
708 
CheckFormattersPlugin(cmsContext ContextID)709 cmsInt32Number CheckFormattersPlugin(cmsContext ContextID)
710 {
711     cmsContext ctx = WatchDogContext(NULL);
712     cmsContext cpy;
713     cmsHTRANSFORM xform;
714     cmsUInt16Number stream[]= { 0xffffU, 0x1234U, 0x0000U, 0x33ddU };
715     cmsUInt16Number result[4];
716     int i;
717 
718     cmsPlugin(ctx, &FormattersPluginSample);
719 
720     cpy = DupContext(ctx, NULL);
721 
722     cmsPlugin(cpy, &FormattersPluginSample2);
723 
724     xform = cmsCreateTransform(cpy, NULL, TYPE_RGB_565, NULL, TYPE_RGB_565, INTENT_PERCEPTUAL, cmsFLAGS_NULLTRANSFORM);
725 
726     cmsDoTransform(cpy, xform, stream, result, 4);
727 
728     cmsDeleteTransform(cpy, xform);
729     cmsDeleteContext(ctx);
730     cmsDeleteContext(cpy);
731 
732     for (i=0; i < 4; i++)
733         if (stream[i] != result[i]) return 0;
734 
735     return 1;
736 }
737 
738 // --------------------------------------------------------------------------------------------------
739 // TagTypePlugin plugin check
740 // --------------------------------------------------------------------------------------------------
741 
742 #define SigIntType      ((cmsTagTypeSignature)  0x74747448)   //   'tttH'
743 #define SigInt          ((cmsTagSignature)  0x74747448)       //   'tttH'
744 
745 static
Type_int_Read(cmsContext ContextID,struct _cms_typehandler_struct * self,cmsIOHANDLER * io,cmsUInt32Number * nItems,cmsUInt32Number SizeOfTag)746 void *Type_int_Read(cmsContext ContextID, struct _cms_typehandler_struct* self,
747  			    cmsIOHANDLER* io,
748                cmsUInt32Number* nItems,
749                cmsUInt32Number SizeOfTag)
750 {
751     cmsUInt32Number* Ptr = (cmsUInt32Number*) _cmsMalloc(ContextID, sizeof(cmsUInt32Number));
752     if (Ptr == NULL) return NULL;
753     if (!_cmsReadUInt32Number(ContextID, io, Ptr)) return NULL;
754     *nItems = 1;
755     return Ptr;
756 }
757 
758 static
Type_int_Write(cmsContext ContextID,struct _cms_typehandler_struct * self,cmsIOHANDLER * io,void * Ptr,cmsUInt32Number nItems)759 cmsBool Type_int_Write(cmsContext ContextID, struct _cms_typehandler_struct* self,
760                         cmsIOHANDLER* io,
761                         void* Ptr, cmsUInt32Number nItems)
762 {
763     return _cmsWriteUInt32Number(ContextID, io, *(cmsUInt32Number*) Ptr);
764 }
765 
766 static
Type_int_Dup(cmsContext ContextID,struct _cms_typehandler_struct * self,const void * Ptr,cmsUInt32Number n)767 void* Type_int_Dup(cmsContext ContextID, struct _cms_typehandler_struct* self,
768                    const void *Ptr, cmsUInt32Number n)
769 {
770     return _cmsDupMem(ContextID, Ptr, n * sizeof(cmsUInt32Number));
771 }
772 
Type_int_Free(cmsContext ContextID,struct _cms_typehandler_struct * self,void * Ptr)773 void Type_int_Free(cmsContext ContextID, struct _cms_typehandler_struct* self,
774                    void* Ptr)
775 {
776     _cmsFree(ContextID, Ptr);
777 }
778 
779 
780 static cmsPluginTag HiddenTagPluginSample = {
781 
782     { cmsPluginMagicNumber, 2060-2000, cmsPluginTagSig, NULL},
783     SigInt,  {  1, 1, { SigIntType }, NULL }
784 };
785 
786 static cmsPluginTagType TagTypePluginSample = {
787 
788      { cmsPluginMagicNumber, 2060-2000, cmsPluginTagTypeSig,  (cmsPluginBase*) &HiddenTagPluginSample},
789      { SigIntType, Type_int_Read, Type_int_Write, Type_int_Dup, Type_int_Free, 0 }
790 };
791 
792 
CheckTagTypePlugin(cmsContext ContextID)793 cmsInt32Number CheckTagTypePlugin(cmsContext ContextID)
794 {
795     cmsContext ctx = NULL;
796     cmsContext cpy = NULL;
797     cmsContext cpy2 = NULL;
798     cmsHPROFILE h = NULL;
799     cmsUInt32Number myTag = 1234;
800     cmsUInt32Number rc = 0;
801     char* data = NULL;
802     cmsUInt32Number *ptr = NULL;
803     cmsUInt32Number clen = 0;
804 
805     ctx = WatchDogContext(NULL);
806     cmsPlugin(ctx, &TagTypePluginSample);
807 
808     cpy = DupContext(ctx, NULL);
809     cpy2 = DupContext(cpy, NULL);
810 
811     cmsDeleteContext(ctx);
812     cmsDeleteContext(cpy);
813 
814     h = cmsCreateProfilePlaceholder(cpy2);
815     if (h == NULL) {
816         Fail("Create placeholder failed");
817         goto Error;
818     }
819 
820 
821     if (!cmsWriteTag(cpy2, h, SigInt, &myTag)) {
822         Fail("Plug-in failed");
823         goto Error;
824     }
825 
826     rc = cmsSaveProfileToMem(cpy2, h, NULL, &clen);
827     if (!rc) {
828         Fail("Fetch mem size failed");
829         goto Error;
830     }
831 
832 
833     data = (char*) malloc(clen);
834     if (data == NULL) {
835         Fail("malloc failed ?!?");
836         goto Error;
837     }
838 
839 
840     rc = cmsSaveProfileToMem(cpy2, h, data, &clen);
841     if (!rc) {
842         Fail("Save to mem failed");
843         goto Error;
844     }
845 
846     cmsCloseProfile(cpy2, h);
847 
848     cmsSetLogErrorHandler(ContextID, NULL);
849     h = cmsOpenProfileFromMem(ContextID, data, clen);
850     if (h == NULL) {
851         Fail("Open profile failed");
852         goto Error;
853     }
854 
855     ptr = (cmsUInt32Number*) cmsReadTag(ContextID, h, SigInt);
856     if (ptr != NULL) {
857 
858         Fail("read tag/context switching failed");
859         goto Error;
860     }
861 
862     cmsCloseProfile(ContextID, h);
863     ResetFatalError(ContextID);
864 
865     h = cmsOpenProfileFromMem(cpy2, data, clen);
866     if (h == NULL) {
867         Fail("Open profile from mem failed");
868         goto Error;
869     }
870 
871     // Get rid of data
872     free(data); data = NULL;
873 
874     ptr = (cmsUInt32Number*) cmsReadTag(cpy2, h, SigInt);
875     if (ptr == NULL) {
876         Fail("Read tag/conext switching failed (2)");
877         return 0;
878     }
879 
880     rc = (*ptr == 1234);
881 
882     cmsCloseProfile(cpy2, h);
883     cmsDeleteContext(cpy2);
884 
885     return rc;
886 
887 Error:
888 
889     if (h != NULL) cmsCloseProfile(cpy, h);
890     if (ctx != NULL) cmsDeleteContext(ctx);
891     if (cpy != NULL) cmsDeleteContext(cpy);
892     if (data) free(data);
893 
894     return 0;
895 }
896 
897 // --------------------------------------------------------------------------------------------------
898 // MPE plugin check:
899 // --------------------------------------------------------------------------------------------------
900 #define SigNegateType ((cmsStageSignature)0x6E202020)
901 
902 static
EvaluateNegate(cmsContext ContextID,const cmsFloat32Number In[],cmsFloat32Number Out[],const cmsStage * mpe)903 void EvaluateNegate(cmsContext ContextID, const cmsFloat32Number In[],
904                      cmsFloat32Number Out[],
905                      const cmsStage *mpe)
906 {
907     Out[0] = 1.0f - In[0];
908     Out[1] = 1.0f - In[1];
909     Out[2] = 1.0f - In[2];
910 }
911 
912 static
StageAllocNegate(cmsContext ContextID)913 cmsStage* StageAllocNegate(cmsContext ContextID)
914 {
915     return _cmsStageAllocPlaceholder(ContextID,
916                  SigNegateType, 3, 3, EvaluateNegate,
917                  NULL, NULL, NULL);
918 }
919 
920 static
Type_negate_Read(cmsContext ContextID,struct _cms_typehandler_struct * self,cmsIOHANDLER * io,cmsUInt32Number * nItems,cmsUInt32Number SizeOfTag)921 void *Type_negate_Read(cmsContext ContextID, struct _cms_typehandler_struct* self,
922  			    cmsIOHANDLER* io,
923                 cmsUInt32Number* nItems,
924                 cmsUInt32Number SizeOfTag)
925 {
926     cmsUInt16Number   Chans;
927     if (!_cmsReadUInt16Number(ContextID, io, &Chans)) return NULL;
928     if (Chans != 3) return NULL;
929 
930     *nItems = 1;
931     return StageAllocNegate(ContextID);
932 }
933 
934 static
Type_negate_Write(cmsContext ContextID,struct _cms_typehandler_struct * self,cmsIOHANDLER * io,void * Ptr,cmsUInt32Number nItems)935 cmsBool Type_negate_Write(cmsContext ContextID, struct _cms_typehandler_struct* self,
936                         cmsIOHANDLER* io,
937                         void* Ptr, cmsUInt32Number nItems)
938 {
939 
940     if (!_cmsWriteUInt16Number(ContextID, io, 3)) return FALSE;
941     return TRUE;
942 }
943 
944 static
945 cmsPluginMultiProcessElement MPEPluginSample = {
946 
947     {cmsPluginMagicNumber, 2060-2000, cmsPluginMultiProcessElementSig, NULL},
948 
949     { (cmsTagTypeSignature) SigNegateType, Type_negate_Read, Type_negate_Write, NULL, NULL, 0 }
950 };
951 
952 
CheckMPEPlugin(cmsContext ContextID)953 cmsInt32Number CheckMPEPlugin(cmsContext ContextID)
954 {
955     cmsContext ctx = NULL;
956     cmsContext cpy = NULL;
957     cmsContext cpy2 = NULL;
958     cmsHPROFILE h = NULL;
959     cmsUInt32Number myTag = 1234;
960     cmsUInt32Number rc = 0;
961     char* data = NULL;
962     cmsUInt32Number clen = 0;
963     cmsFloat32Number In[3], Out[3];
964     cmsPipeline* pipe;
965 
966     ctx = WatchDogContext(NULL);
967     cmsPlugin(ctx, &MPEPluginSample);
968 
969     cpy =  DupContext(ctx, NULL);
970     cpy2 = DupContext(cpy, NULL);
971 
972     cmsDeleteContext(ctx);
973     cmsDeleteContext(cpy);
974 
975     h = cmsCreateProfilePlaceholder(cpy2);
976     if (h == NULL) {
977         Fail("Create placeholder failed");
978         goto Error;
979     }
980 
981     pipe = cmsPipelineAlloc(cpy2, 3, 3);
982     cmsPipelineInsertStage(cpy2, pipe, cmsAT_BEGIN, StageAllocNegate(cpy2));
983 
984 
985     In[0] = 0.3f; In[1] = 0.2f; In[2] = 0.9f;
986     cmsPipelineEvalFloat(cpy2, In, Out, pipe);
987 
988     rc = (IsGoodVal("0", Out[0], 1.0-In[0], 0.001) &&
989            IsGoodVal("1", Out[1], 1.0-In[1], 0.001) &&
990            IsGoodVal("2", Out[2], 1.0-In[2], 0.001));
991 
992     if (!rc) {
993         Fail("Pipeline failed");
994         goto Error;
995     }
996 
997     if (!cmsWriteTag(cpy2, h, cmsSigDToB3Tag, pipe)) {
998         Fail("Plug-in failed");
999         goto Error;
1000     }
1001 
1002     // This cleans the stage as well
1003     cmsPipelineFree(cpy2, pipe);
1004 
1005     rc = cmsSaveProfileToMem(cpy2, h, NULL, &clen);
1006     if (!rc) {
1007         Fail("Fetch mem size failed");
1008         goto Error;
1009     }
1010 
1011 
1012     data = (char*) malloc(clen);
1013     if (data == NULL) {
1014         Fail("malloc failed ?!?");
1015         goto Error;
1016     }
1017 
1018 
1019     rc = cmsSaveProfileToMem(cpy2, h, data, &clen);
1020     if (!rc) {
1021         Fail("Save to mem failed");
1022         goto Error;
1023     }
1024 
1025     cmsCloseProfile(cpy2, h);
1026 
1027     cmsSetLogErrorHandler(ContextID, NULL);
1028     h = cmsOpenProfileFromMem(ContextID, data, clen);
1029     if (h == NULL) {
1030         Fail("Open profile failed");
1031         goto Error;
1032     }
1033 
1034     pipe = (cmsPipeline*) cmsReadTag(ContextID, h, cmsSigDToB3Tag);
1035     if (pipe != NULL) {
1036 
1037         // Unsupported stage, should fail
1038         Fail("read tag/context switching failed");
1039         goto Error;
1040     }
1041 
1042     cmsCloseProfile(ContextID, h);
1043 
1044     ResetFatalError(ContextID);
1045 
1046     h = cmsOpenProfileFromMem(cpy2, data, clen);
1047     if (h == NULL) {
1048         Fail("Open profile from mem failed");
1049         goto Error;
1050     }
1051 
1052     // Get rid of data
1053     free(data); data = NULL;
1054 
1055     pipe = (cmsPipeline*) cmsReadTag(cpy2, h, cmsSigDToB3Tag);
1056     if (pipe == NULL) {
1057         Fail("Read tag/conext switching failed (2)");
1058         return 0;
1059     }
1060 
1061     // Evaluate for negation
1062     In[0] = 0.3f; In[1] = 0.2f; In[2] = 0.9f;
1063     cmsPipelineEvalFloat(cpy2, In, Out, pipe);
1064 
1065      rc = (IsGoodVal("0", Out[0], 1.0-In[0], 0.001) &&
1066            IsGoodVal("1", Out[1], 1.0-In[1], 0.001) &&
1067            IsGoodVal("2", Out[2], 1.0-In[2], 0.001));
1068 
1069     cmsCloseProfile(cpy2, h);
1070     cmsDeleteContext(cpy2);
1071     return rc;
1072 
1073 Error:
1074 
1075     if (h != NULL) cmsCloseProfile(cpy2, h);
1076     if (cpy2 != NULL) cmsDeleteContext(cpy2);
1077     if (data) free(data);
1078 
1079     return 0;
1080 }
1081 
1082 
1083 // --------------------------------------------------------------------------------------------------
1084 // Optimization plugin check:
1085 // --------------------------------------------------------------------------------------------------
1086 
1087 static
FastEvaluateCurves(cmsContext ContextID,register const cmsUInt16Number In[],register cmsUInt16Number Out[],register const void * Data)1088 void FastEvaluateCurves(cmsContext ContextID, register const cmsUInt16Number In[],
1089                                      register cmsUInt16Number Out[],
1090                                      register const void* Data)
1091 {
1092     Out[0] = In[0];
1093 }
1094 
1095 static
MyOptimize(cmsContext ContextID,cmsPipeline ** Lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)1096 cmsBool MyOptimize(cmsContext ContextID, cmsPipeline** Lut,
1097                    cmsUInt32Number  Intent,
1098                    cmsUInt32Number* InputFormat,
1099                    cmsUInt32Number* OutputFormat,
1100                    cmsUInt32Number* dwFlags)
1101 {
1102     cmsStage* mpe;
1103      _cmsStageToneCurvesData* Data;
1104 
1105     //  Only curves in this LUT? All are identities?
1106     for (mpe = cmsPipelineGetPtrToFirstStage(ContextID, *Lut);
1107          mpe != NULL;
1108          mpe = cmsStageNext(ContextID, mpe)) {
1109 
1110             if (cmsStageType(ContextID, mpe) != cmsSigCurveSetElemType) return FALSE;
1111 
1112             // Check for identity
1113             Data = (_cmsStageToneCurvesData*) cmsStageData(ContextID, mpe);
1114             if (Data ->nCurves != 1) return FALSE;
1115             if (cmsEstimateGamma(ContextID, Data->TheCurves[0], 0.1) > 1.0) return FALSE;
1116 
1117     }
1118 
1119     *dwFlags |= cmsFLAGS_NOCACHE;
1120     _cmsPipelineSetOptimizationParameters(ContextID, *Lut, FastEvaluateCurves, NULL, NULL, NULL);
1121 
1122     return TRUE;
1123 }
1124 
1125 cmsPluginOptimization OptimizationPluginSample = {
1126 
1127     {cmsPluginMagicNumber, 2060-2000, cmsPluginOptimizationSig, NULL},
1128     MyOptimize
1129 };
1130 
1131 
CheckOptimizationPlugin(cmsContext ContextID)1132 cmsInt32Number CheckOptimizationPlugin(cmsContext ContextID)
1133 {
1134     cmsContext ctx = WatchDogContext(NULL);
1135     cmsContext cpy;
1136     cmsHTRANSFORM xform;
1137     cmsUInt8Number In[]= { 10, 20, 30, 40 };
1138     cmsUInt8Number Out[4];
1139     cmsToneCurve* Linear[1];
1140     cmsHPROFILE h;
1141     int i;
1142 
1143     cmsPlugin(ctx, &OptimizationPluginSample);
1144 
1145     cpy = DupContext(ctx, NULL);
1146 
1147     Linear[0] = cmsBuildGamma(cpy, 1.0);
1148     h = cmsCreateLinearizationDeviceLink(cpy, cmsSigGrayData, Linear);
1149     cmsFreeToneCurve(cpy, Linear[0]);
1150 
1151     xform = cmsCreateTransform(cpy, h, TYPE_GRAY_8, h, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
1152     cmsCloseProfile(cpy, h);
1153 
1154     cmsDoTransform(cpy, xform, In, Out, 4);
1155 
1156     cmsDeleteTransform(cpy, xform);
1157     cmsDeleteContext(ctx);
1158     cmsDeleteContext(cpy);
1159 
1160     for (i=0; i < 4; i++)
1161         if (In[i] != Out[i]) return 0;
1162 
1163     return 1;
1164 }
1165 
1166 
1167 // --------------------------------------------------------------------------------------------------
1168 // Check the intent plug-in
1169 // --------------------------------------------------------------------------------------------------
1170 
1171 /*
1172    This example creates a new rendering intent, at intent number 300, that is identical to perceptual
1173    intent for all color spaces but gray to gray transforms, in this case it bypasses the data.
1174    Note that it has to clear all occurrences of intent 300 in the intents array to avoid
1175    infinite recursion.
1176 */
1177 
1178 #define INTENT_DECEPTIVE   300
1179 
1180 static
MyNewIntent(cmsContext ContextID,cmsUInt32Number nProfiles,cmsUInt32Number TheIntents[],cmsHPROFILE hProfiles[],cmsBool BPC[],cmsFloat64Number AdaptationStates[],cmsUInt32Number dwFlags)1181 cmsPipeline*  MyNewIntent(cmsContext      ContextID,
1182                           cmsUInt32Number nProfiles,
1183                           cmsUInt32Number TheIntents[],
1184                           cmsHPROFILE     hProfiles[],
1185                           cmsBool         BPC[],
1186                           cmsFloat64Number AdaptationStates[],
1187                           cmsUInt32Number dwFlags)
1188 {
1189     cmsPipeline*    Result;
1190     cmsUInt32Number ICCIntents[256];
1191     cmsUInt32Number i;
1192 
1193  for (i=0; i < nProfiles; i++)
1194         ICCIntents[i] = (TheIntents[i] == INTENT_DECEPTIVE) ? INTENT_PERCEPTUAL :
1195                                                  TheIntents[i];
1196 
1197  if (cmsGetColorSpace(ContextID, hProfiles[0]) != cmsSigGrayData ||
1198      cmsGetColorSpace(ContextID, hProfiles[nProfiles-1]) != cmsSigGrayData)
1199            return _cmsDefaultICCintents(ContextID, nProfiles,
1200                                    ICCIntents, hProfiles,
1201                                    BPC, AdaptationStates,
1202                                    dwFlags);
1203 
1204     Result = cmsPipelineAlloc(ContextID, 1, 1);
1205     if (Result == NULL) return NULL;
1206 
1207     cmsPipelineInsertStage(ContextID, Result, cmsAT_BEGIN,
1208                             cmsStageAllocIdentity(ContextID, 1));
1209 
1210     return Result;
1211 }
1212 
1213 static cmsPluginRenderingIntent IntentPluginSample = {
1214 
1215     {cmsPluginMagicNumber, 2060-2000, cmsPluginRenderingIntentSig, NULL},
1216 
1217     INTENT_DECEPTIVE, MyNewIntent,  "bypass gray to gray rendering intent"
1218 };
1219 
CheckIntentPlugin(cmsContext ContextID)1220 cmsInt32Number CheckIntentPlugin(cmsContext ContextID)
1221 {
1222     cmsContext ctx = WatchDogContext(NULL);
1223     cmsContext cpy;
1224     cmsHTRANSFORM xform;
1225     cmsHPROFILE h1, h2;
1226     cmsToneCurve* Linear1;
1227     cmsToneCurve* Linear2;
1228     cmsUInt8Number In[]= { 10, 20, 30, 40 };
1229     cmsUInt8Number Out[4];
1230     int i;
1231 
1232     cmsPlugin(ctx, &IntentPluginSample);
1233 
1234     cpy  = DupContext(ctx, NULL);
1235 
1236     Linear1 = cmsBuildGamma(cpy, 3.0);
1237     Linear2 = cmsBuildGamma(cpy, 0.1);
1238     h1 = cmsCreateLinearizationDeviceLink(cpy, cmsSigGrayData, &Linear1);
1239     h2 = cmsCreateLinearizationDeviceLink(cpy, cmsSigGrayData, &Linear2);
1240 
1241     cmsFreeToneCurve(cpy, Linear1);
1242     cmsFreeToneCurve(cpy, Linear2);
1243 
1244     xform = cmsCreateTransform(cpy, h1, TYPE_GRAY_8, h2, TYPE_GRAY_8, INTENT_DECEPTIVE, 0);
1245     cmsCloseProfile(cpy,h1); cmsCloseProfile(cpy, h2);
1246 
1247     cmsDoTransform(cpy, xform, In, Out, 4);
1248 
1249     cmsDeleteTransform(cpy, xform);
1250     cmsDeleteContext(cpy);
1251     cmsDeleteContext(ctx);
1252 
1253     for (i=0; i < 4; i++)
1254         if (Out[i] != In[i]) return 0;
1255 
1256     return 1;
1257 }
1258 
1259 
1260 // --------------------------------------------------------------------------------------------------
1261 // Check the full transform plug-in
1262 // --------------------------------------------------------------------------------------------------
1263 
1264 // This is a sample intent that only works for gray8 as output, and always returns '42'
1265 static
TrancendentalTransform(cmsContext ContextID,struct _cmstransform_struct * CMM,const void * InputBuffer,void * OutputBuffer,cmsUInt32Number Size,cmsUInt32Number Stride)1266 void TrancendentalTransform(cmsContext ContextID, struct _cmstransform_struct * CMM,
1267                               const void* InputBuffer,
1268                               void* OutputBuffer,
1269                               cmsUInt32Number Size,
1270                               cmsUInt32Number Stride)
1271 {
1272     cmsUInt32Number i;
1273 
1274     for (i=0; i < Size; i++)
1275     {
1276         ((cmsUInt8Number*) OutputBuffer)[i] = 0x42;
1277     }
1278 
1279 }
1280 
1281 
TransformFactory(cmsContext ContextID,_cmsTransformFn * xformPtr,void ** UserData,_cmsFreeUserDataFn * FreePrivateDataFn,cmsPipeline ** Lut,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)1282 cmsBool  TransformFactory(cmsContext ContextID, _cmsTransformFn* xformPtr,
1283                           void** UserData,
1284                            _cmsFreeUserDataFn* FreePrivateDataFn,
1285                            cmsPipeline** Lut,
1286                            cmsUInt32Number* InputFormat,
1287                            cmsUInt32Number* OutputFormat,
1288                            cmsUInt32Number* dwFlags)
1289 
1290 {
1291     if (*OutputFormat == TYPE_GRAY_8)
1292     {
1293         // *Lut holds the pipeline to be applied
1294         *xformPtr = TrancendentalTransform;
1295         return TRUE;
1296     }
1297 
1298     return FALSE;
1299 }
1300 
1301 
1302 // The Plug-in entry point
1303 static cmsPluginTransform FullTransformPluginSample = {
1304 
1305      { cmsPluginMagicNumber, 2060-2000, cmsPluginTransformSig, NULL},
1306 
1307      TransformFactory
1308 };
1309 
CheckTransformPlugin(cmsContext ContextID)1310 cmsInt32Number CheckTransformPlugin(cmsContext ContextID)
1311 {
1312     cmsContext ctx = WatchDogContext(NULL);
1313     cmsContext cpy;
1314     cmsHTRANSFORM xform;
1315     cmsUInt8Number In[]= { 10, 20, 30, 40 };
1316     cmsUInt8Number Out[4];
1317     cmsToneCurve* Linear;
1318     cmsHPROFILE h;
1319     int i;
1320 
1321     cmsPlugin(ctx, &FullTransformPluginSample);
1322 
1323     cpy  = DupContext(ctx, NULL);
1324 
1325     Linear = cmsBuildGamma(cpy, 1.0);
1326     h = cmsCreateLinearizationDeviceLink(cpy, cmsSigGrayData, &Linear);
1327     cmsFreeToneCurve(cpy, Linear);
1328 
1329     xform = cmsCreateTransform(cpy, h, TYPE_GRAY_8, h, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
1330     cmsCloseProfile(cpy, h);
1331 
1332     cmsDoTransform(cpy, xform, In, Out, 4);
1333 
1334     cmsDeleteTransform(cpy, xform);
1335     cmsDeleteContext(ctx);
1336     cmsDeleteContext(cpy);
1337 
1338     for (i=0; i < 4; i++)
1339         if (Out[i] != 0x42) return 0;
1340 
1341     return 1;
1342 }
1343 
1344 
1345 // --------------------------------------------------------------------------------------------------
1346 // Check the mutex plug-in
1347 // --------------------------------------------------------------------------------------------------
1348 
1349 typedef struct {
1350     int nlocks;
1351 } MyMtx;
1352 
1353 
1354 static
MyMtxCreate(cmsContext id)1355 void* MyMtxCreate(cmsContext id)
1356 {
1357    MyMtx* mtx = (MyMtx*) _cmsMalloc(id, sizeof(MyMtx));
1358    mtx ->nlocks = 0;
1359    return mtx;
1360 }
1361 
1362 static
MyMtxDestroy(cmsContext id,void * mtx)1363 void MyMtxDestroy(cmsContext id, void* mtx)
1364 {
1365     MyMtx* mtx_ = (MyMtx*) mtx;
1366 
1367     if (mtx_->nlocks != 0)
1368         Die("Locks != 0 when setting free a mutex");
1369 
1370     _cmsFree(id, mtx);
1371 
1372 }
1373 
1374 static
MyMtxLock(cmsContext id,void * mtx)1375 cmsBool MyMtxLock(cmsContext id, void* mtx)
1376 {
1377     MyMtx* mtx_ = (MyMtx*) mtx;
1378     mtx_->nlocks++;
1379 
1380     return TRUE;
1381 }
1382 
1383 static
MyMtxUnlock(cmsContext id,void * mtx)1384 void MyMtxUnlock(cmsContext id, void* mtx)
1385 {
1386     MyMtx* mtx_ = (MyMtx*) mtx;
1387     mtx_->nlocks--;
1388 
1389 }
1390 
1391 
1392 static cmsPluginMutex MutexPluginSample = {
1393 
1394      { cmsPluginMagicNumber, 2060, cmsPluginMutexSig, NULL},
1395 
1396      MyMtxCreate,  MyMtxDestroy,  MyMtxLock,  MyMtxUnlock
1397 };
1398 
1399 
CheckMutexPlugin(cmsContext ContextID)1400 cmsInt32Number CheckMutexPlugin(cmsContext ContextID)
1401 {
1402     cmsContext ctx = WatchDogContext(NULL);
1403     cmsContext cpy;
1404     cmsHTRANSFORM xform;
1405     cmsUInt8Number In[]= { 10, 20, 30, 40 };
1406     cmsUInt8Number Out[4];
1407     cmsToneCurve* Linear;
1408     cmsHPROFILE h;
1409     int i;
1410 
1411 
1412     cmsPlugin(ctx, &MutexPluginSample);
1413 
1414     cpy  = DupContext(ctx, NULL);
1415 
1416     Linear = cmsBuildGamma(cpy, 1.0);
1417     h = cmsCreateLinearizationDeviceLink(cpy, cmsSigGrayData, &Linear);
1418     cmsFreeToneCurve(cpy, Linear);
1419 
1420     xform = cmsCreateTransform(cpy, h, TYPE_GRAY_8, h, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
1421     cmsCloseProfile(cpy, h);
1422 
1423     cmsDoTransform(cpy, xform, In, Out, 4);
1424 
1425     cmsDeleteTransform(cpy, xform);
1426     cmsDeleteContext(ctx);
1427     cmsDeleteContext(cpy);
1428 
1429     for (i=0; i < 4; i++)
1430         if (Out[i] != In[i]) return 0;
1431 
1432     return 1;
1433 }
1434 
1435 
CheckMethodPackDoublesFromFloat(cmsContext ContextID)1436 cmsInt32Number CheckMethodPackDoublesFromFloat(cmsContext ContextID)
1437 {
1438 
1439     cmsContext ctx = WatchDogContext(NULL);
1440 
1441     cmsHTRANSFORM xform;
1442     cmsHTRANSFORM l_pFakeProfileLAB;
1443 
1444     cmsFloat64Number l_D_OutputColorArrayBlack[8];
1445     cmsFloat64Number l_D_OutputColorArrayBlue[8];
1446 
1447     cmsCIELab LabInBlack;
1448     cmsCIELab LabInBlue;
1449 
1450     cmsUInt16Number Lab_UI16_Black[3];
1451     cmsUInt16Number Lab_UI16_Blue[3];
1452 
1453     cmsHPROFILE OutputCMYKProfile;
1454     cmsUInt32Number l_UI32_OutputFormat;
1455 
1456 
1457     cmsPlugin(ctx, &FullTransformPluginSample);
1458 
1459 
1460     l_pFakeProfileLAB = cmsCreateLab2Profile(ctx, NULL);
1461 
1462     if (l_pFakeProfileLAB == NULL)
1463         return 0;
1464 
1465     OutputCMYKProfile = cmsOpenProfileFromFile(ctx, "TestCLT.icc", "r");
1466 
1467     if (OutputCMYKProfile == NULL)
1468         return 0;
1469 
1470     l_UI32_OutputFormat = 0;
1471     l_UI32_OutputFormat |= COLORSPACE_SH(PT_CMYK);
1472     l_UI32_OutputFormat |= PLANAR_SH(1);
1473     l_UI32_OutputFormat |= CHANNELS_SH(4);
1474     l_UI32_OutputFormat |= BYTES_SH(0);
1475     l_UI32_OutputFormat |= FLOAT_SH(1);
1476 
1477 
1478     xform = cmsCreateTransform(ctx, l_pFakeProfileLAB, TYPE_Lab_DBL, OutputCMYKProfile, l_UI32_OutputFormat, INTENT_PERCEPTUAL, 0);
1479     cmsCloseProfile(ctx, OutputCMYKProfile);
1480     cmsCloseProfile(ctx, l_pFakeProfileLAB);
1481 
1482     Lab_UI16_Black[0] = 0;
1483     Lab_UI16_Black[1] = 32768;
1484     Lab_UI16_Black[2] = 32768;
1485 
1486     Lab_UI16_Blue[0] = 0;
1487     Lab_UI16_Blue[1] = 8192;
1488     Lab_UI16_Blue[2] = 8192;
1489 
1490     cmsLabEncoded2Float(ctx, &LabInBlack, Lab_UI16_Black);
1491     cmsLabEncoded2Float(ctx, &LabInBlue, Lab_UI16_Blue);
1492 
1493     memset(l_D_OutputColorArrayBlack, 0, sizeof(l_D_OutputColorArrayBlack));
1494     memset(l_D_OutputColorArrayBlue, 0, sizeof(l_D_OutputColorArrayBlue));
1495 
1496     cmsDoTransform(ctx, xform, &LabInBlack, l_D_OutputColorArrayBlack, 1);
1497     cmsDoTransform(ctx, xform, &LabInBlue, l_D_OutputColorArrayBlue, 1);
1498 
1499 
1500     cmsDeleteTransform(ctx, xform);
1501     cmsDeleteContext(ctx);
1502 
1503     return 1;
1504 }
1505 
1506