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