testplugin.c 43 KB

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