threaded_testbed.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. //---------------------------------------------------------------------------------
  2. //
  3. // Little Color Management System, multithreaded extensions
  4. // Copyright (c) 1998-2023 Marti Maria Saguer, all rights reserved
  5. //
  6. // This program is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // This program is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU General Public License
  17. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. //
  19. //---------------------------------------------------------------------------------
  20. #include "threaded_internal.h"
  21. #include <stdlib.h>
  22. #include <memory.h>
  23. #include <time.h>
  24. // On Visual Studio, use debug CRT
  25. #ifdef _MSC_VER
  26. # include "crtdbg.h"
  27. #define HAVE_TIMESPEC_GET 1
  28. #endif
  29. #ifndef PROFILES_DIR
  30. #define PROFILES_DIR "../../test_profiles/"
  31. #endif
  32. #define FLAGS cmsFLAGS_NOOPTIMIZE
  33. // A fast way to convert from/to 16 <-> 8 bits
  34. #define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb))
  35. #define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((rgb) * 65281 + 8388608) >> 24) & 0xFF)
  36. // Some pixel representations
  37. typedef struct { cmsUInt8Number r, g, b; } Scanline_rgb8bits;
  38. typedef struct { cmsUInt8Number r, g, b, a; } Scanline_rgba8bits;
  39. typedef struct { cmsUInt8Number c, m, y, k; } Scanline_cmyk8bits;
  40. typedef struct { cmsUInt16Number r, g, b; } Scanline_rgb16bits;
  41. typedef struct { cmsUInt16Number r, g, b, a; } Scanline_rgba16bits;
  42. typedef struct { cmsUInt16Number c, m, y, k; } Scanline_cmyk16bits;
  43. static struct timespec start, finish;
  44. cmsINLINE void MeasureTimeStart(void)
  45. {
  46. #if defined(HAVE_TIMESPEC_GET)
  47. timespec_get(&start, TIME_UTC);
  48. #else
  49. clock_gettime(CLOCK_MONOTONIC, &start);
  50. #endif
  51. }
  52. cmsINLINE double MeasureTimeStop(void)
  53. {
  54. double elapsed;
  55. #if defined(HAVE_TIMESPEC_GET)
  56. timespec_get(&finish, TIME_UTC);
  57. #else
  58. clock_gettime(CLOCK_MONOTONIC, &finish);
  59. #endif
  60. elapsed = ((double) finish.tv_sec - start.tv_sec);
  61. elapsed += ((double) finish.tv_nsec - start.tv_nsec) / 1000000000.0;
  62. return elapsed;
  63. }
  64. // A flushed printf
  65. static
  66. void trace(const char* frm, ...)
  67. {
  68. va_list args;
  69. va_start(args, frm);
  70. vfprintf(stderr, frm, args);
  71. fflush(stderr);
  72. va_end(args);
  73. }
  74. // The callback function used by cmsSetLogErrorHandler()
  75. static
  76. void FatalErrorQuit(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
  77. {
  78. UNUSED_PARAMETER(ContextID);
  79. UNUSED_PARAMETER(ErrorCode);
  80. trace("** Fatal error: %s\n", Text);
  81. exit(1);
  82. }
  83. // Rise an error and exit
  84. static
  85. void Fail(const char* frm, ...)
  86. {
  87. char ReasonToFailBuffer[1024];
  88. va_list args;
  89. va_start(args, frm);
  90. vsprintf(ReasonToFailBuffer, frm, args);
  91. FatalErrorQuit(0, 0, ReasonToFailBuffer);
  92. // unreachable va_end(args);
  93. }
  94. // Creates a fake profile that only has a curve. Used in several places
  95. static
  96. cmsHPROFILE CreateCurves(void)
  97. {
  98. cmsToneCurve* Gamma = cmsBuildGamma(0, 1.1);
  99. cmsToneCurve* Transfer[3];
  100. cmsHPROFILE h;
  101. Transfer[0] = Transfer[1] = Transfer[2] = Gamma;
  102. h = cmsCreateLinearizationDeviceLink(cmsSigRgbData, Transfer);
  103. cmsFreeToneCurve(Gamma);
  104. return h;
  105. }
  106. // --------------------------------------------------------------------------------------------------
  107. // A C C U R A C Y C H E C K S
  108. // --------------------------------------------------------------------------------------------------
  109. // Check change format feature
  110. static
  111. void CheckChangeFormat(void)
  112. {
  113. cmsHPROFILE hsRGB, hLab;
  114. cmsHTRANSFORM xform;
  115. cmsUInt8Number rgb8[3] = { 10, 120, 40 };
  116. cmsUInt16Number rgb16[3] = { 10* 257, 120*257, 40*257 };
  117. cmsUInt16Number lab16_1[3], lab16_2[3];
  118. trace("Checking change format feature...");
  119. hsRGB = cmsCreate_sRGBProfile();
  120. hLab = cmsCreateLab4Profile(NULL);
  121. xform = cmsCreateTransform(hsRGB, TYPE_RGB_16, hLab, TYPE_Lab_16, INTENT_PERCEPTUAL, FLAGS);
  122. cmsCloseProfile(hsRGB);
  123. cmsCloseProfile(hLab);
  124. cmsDoTransform(xform, rgb16, lab16_1, 1);
  125. cmsChangeBuffersFormat(xform, TYPE_RGB_8, TYPE_Lab_16);
  126. cmsDoTransform(xform, rgb8, lab16_2, 1);
  127. cmsDeleteTransform(xform);
  128. if (memcmp(lab16_1, lab16_2, sizeof(lab16_1)) != 0)
  129. Fail("Change format failed!");
  130. trace("Ok\n");
  131. }
  132. // Next test checks results of optimized 8 bits versus raw 8 bits.
  133. static
  134. void TryAllValues8bits(cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
  135. {
  136. cmsContext Raw = cmsCreateContext(NULL, NULL);
  137. cmsContext Plugin = cmsCreateContext(cmsThreadedExtensions(CMS_THREADED_GUESS_MAX_THREADS, 0), NULL);
  138. Scanline_rgba8bits* bufferIn;
  139. Scanline_rgba8bits* bufferRawOut;
  140. Scanline_rgba8bits* bufferPluginOut;
  141. int r, g, b;
  142. int j;
  143. cmsUInt32Number npixels = 256 * 256 * 256;
  144. cmsHTRANSFORM xformRaw = cmsCreateTransformTHR(Raw, hlcmsProfileIn, TYPE_RGBA_8, hlcmsProfileOut, TYPE_RGBA_8, Intent, FLAGS|cmsFLAGS_NOCACHE | cmsFLAGS_COPY_ALPHA);
  145. cmsHTRANSFORM xformPlugin = cmsCreateTransformTHR(Plugin, hlcmsProfileIn, TYPE_RGBA_8, hlcmsProfileOut, TYPE_RGBA_8, Intent, FLAGS|cmsFLAGS_NOCACHE | cmsFLAGS_COPY_ALPHA);
  146. cmsCloseProfile(hlcmsProfileIn);
  147. cmsCloseProfile(hlcmsProfileOut);
  148. if (xformRaw == NULL || xformPlugin == NULL) {
  149. Fail("NULL transforms on check float conversions");
  150. }
  151. // Again, no checking on mem alloc because this is just a test
  152. bufferIn = (Scanline_rgba8bits*)malloc(npixels * sizeof(Scanline_rgba8bits));
  153. bufferRawOut = (Scanline_rgba8bits*)malloc(npixels * sizeof(Scanline_rgba8bits));
  154. bufferPluginOut = (Scanline_rgba8bits*)malloc(npixels * sizeof(Scanline_rgba8bits));
  155. // Same input to both transforms
  156. j = 0;
  157. for (r = 0; r < 256; r++)
  158. for (g = 0; g < 256; g++)
  159. for (b = 0; b < 256; b++) {
  160. bufferIn[j].r = (cmsUInt8Number) r;
  161. bufferIn[j].g = (cmsUInt8Number) g;
  162. bufferIn[j].b = (cmsUInt8Number) b;
  163. bufferIn[j].a = 0xff;
  164. j++;
  165. }
  166. // Different transforms, different output buffers
  167. cmsDoTransform(xformRaw, bufferIn, bufferRawOut, npixels);
  168. cmsDoTransform(xformPlugin, bufferIn, bufferPluginOut, npixels);
  169. // Lets compare results
  170. j = 0;
  171. for (r = 0; r < 256; r++)
  172. for (g = 0; g < 256; g++)
  173. for (b = 0; b < 256; b++) {
  174. if (bufferRawOut[j].r != bufferPluginOut[j].r ||
  175. bufferRawOut[j].g != bufferPluginOut[j].g ||
  176. bufferRawOut[j].b != bufferPluginOut[j].b ||
  177. bufferRawOut[j].a != bufferPluginOut[j].a)
  178. Fail(
  179. "Conversion failed at [%x %x %x %x] (%x %x %x %x) != (%x %x %x %x)",
  180. bufferIn[j].r, bufferIn[j].g, bufferIn[j].b, bufferIn[j].a,
  181. bufferRawOut[j].r, bufferRawOut[j].g, bufferRawOut[j].b, bufferRawOut[j].a,
  182. bufferPluginOut[j].r, bufferPluginOut[j].g, bufferPluginOut[j].b, bufferPluginOut[j].a);
  183. j++;
  184. }
  185. free(bufferIn); free(bufferRawOut);
  186. free(bufferPluginOut);
  187. cmsDeleteTransform(xformRaw);
  188. cmsDeleteTransform(xformPlugin);
  189. cmsDeleteContext(Plugin);
  190. cmsDeleteContext(Raw);
  191. }
  192. static
  193. void CheckAccuracy8Bits(void)
  194. {
  195. trace("Checking accuracy of 8 bits CLUT...");
  196. TryAllValues8bits(cmsOpenProfileFromFile(PROFILES_DIR "test5.icc", "r"), cmsOpenProfileFromFile(PROFILES_DIR "test3.icc", "r"), INTENT_PERCEPTUAL);
  197. trace("OK\n");
  198. }
  199. // Next test checks results of optimized 16 bits versus raw 16 bits.
  200. static
  201. void TryAllValues16bits(cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent)
  202. {
  203. cmsContext Raw = cmsCreateContext(NULL, NULL);
  204. cmsContext Plugin = cmsCreateContext(cmsThreadedExtensions(CMS_THREADED_GUESS_MAX_THREADS, 0), NULL);
  205. Scanline_rgba16bits* bufferIn;
  206. Scanline_rgba16bits* bufferRawOut;
  207. Scanline_rgba16bits* bufferPluginOut;
  208. int r, g, b;
  209. int j;
  210. cmsUInt32Number npixels = 256 * 256 * 256;
  211. cmsHTRANSFORM xformRaw = cmsCreateTransformTHR(Raw, hlcmsProfileIn, TYPE_RGBA_16, hlcmsProfileOut, TYPE_RGBA_16, Intent, FLAGS|cmsFLAGS_NOCACHE | cmsFLAGS_COPY_ALPHA);
  212. cmsHTRANSFORM xformPlugin = cmsCreateTransformTHR(Plugin, hlcmsProfileIn, TYPE_RGBA_16, hlcmsProfileOut, TYPE_RGBA_16, Intent, FLAGS|cmsFLAGS_NOCACHE | cmsFLAGS_COPY_ALPHA);
  213. cmsCloseProfile(hlcmsProfileIn);
  214. cmsCloseProfile(hlcmsProfileOut);
  215. if (xformRaw == NULL || xformPlugin == NULL) {
  216. Fail("NULL transforms on check float conversions");
  217. }
  218. // Again, no checking on mem alloc because this is just a test
  219. bufferIn = (Scanline_rgba16bits*)malloc(npixels * sizeof(Scanline_rgba16bits));
  220. bufferRawOut = (Scanline_rgba16bits*)malloc(npixels * sizeof(Scanline_rgba16bits));
  221. bufferPluginOut = (Scanline_rgba16bits*)malloc(npixels * sizeof(Scanline_rgba16bits));
  222. // Same input to both transforms
  223. j = 0;
  224. for (r = 0; r < 256; r++)
  225. for (g = 0; g < 256; g++)
  226. for (b = 0; b < 256; b++) {
  227. bufferIn[j].r = FROM_8_TO_16(r);
  228. bufferIn[j].g = FROM_8_TO_16(g);
  229. bufferIn[j].b = FROM_8_TO_16(b);
  230. bufferIn[j].a = 0xffff;
  231. j++;
  232. }
  233. // Different transforms, different output buffers
  234. cmsDoTransform(xformRaw, bufferIn, bufferRawOut, npixels);
  235. cmsDoTransform(xformPlugin, bufferIn, bufferPluginOut, npixels);
  236. // Lets compare results
  237. j = 0;
  238. for (r = 0; r < 256; r++)
  239. for (g = 0; g < 256; g++)
  240. for (b = 0; b < 256; b++) {
  241. if (bufferRawOut[j].r != bufferPluginOut[j].r ||
  242. bufferRawOut[j].g != bufferPluginOut[j].g ||
  243. bufferRawOut[j].b != bufferPluginOut[j].b ||
  244. bufferRawOut[j].a != bufferPluginOut[j].a)
  245. Fail(
  246. "Conversion failed at [%x %x %x %x] (%x %x %x %x) != (%x %x %x %x)",
  247. bufferIn[j].r, bufferIn[j].g, bufferIn[j].b, bufferIn[j].a,
  248. bufferRawOut[j].r, bufferRawOut[j].g, bufferRawOut[j].b, bufferRawOut[j].a,
  249. bufferPluginOut[j].r, bufferPluginOut[j].g, bufferPluginOut[j].b, bufferPluginOut[j].a);
  250. j++;
  251. }
  252. free(bufferIn); free(bufferRawOut);
  253. free(bufferPluginOut);
  254. cmsDeleteTransform(xformRaw);
  255. cmsDeleteTransform(xformPlugin);
  256. cmsDeleteContext(Plugin);
  257. cmsDeleteContext(Raw);
  258. }
  259. static
  260. void CheckAccuracy16Bits(void)
  261. {
  262. // CLUT should be as 16 bits or better
  263. trace("Checking accuracy of 16 bits CLUT...");
  264. TryAllValues16bits(cmsOpenProfileFromFile(PROFILES_DIR "test5.icc", "r"), cmsOpenProfileFromFile(PROFILES_DIR "test3.icc", "r"), INTENT_PERCEPTUAL);
  265. trace("OK\n");
  266. }
  267. // --------------------------------------------------------------------------------------------------
  268. // P E R F O R M A N C E C H E C K S
  269. // --------------------------------------------------------------------------------------------------
  270. static
  271. cmsFloat64Number MPixSec(cmsFloat64Number seconds)
  272. {
  273. return (256.0 * 256.0 * 256.0) / (1024.0*1024.0*seconds);
  274. }
  275. typedef cmsFloat64Number(*perf_fn)(cmsContext ct, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut);
  276. static
  277. void PerformanceHeader(void)
  278. {
  279. trace(" MPixel/sec. MByte/sec.\n");
  280. }
  281. static
  282. cmsHPROFILE loadProfile(const char* name)
  283. {
  284. if (*name == '*')
  285. {
  286. if (strcmp(name, "*lab") == 0)
  287. {
  288. return cmsCreateLab4Profile(NULL);
  289. }
  290. else
  291. if (strcmp(name, "*xyz") == 0)
  292. {
  293. return cmsCreateXYZProfile();
  294. }
  295. else
  296. if (strcmp(name, "*curves") == 0)
  297. {
  298. return CreateCurves();
  299. }
  300. else
  301. Fail("Unknown builtin '%s'", name);
  302. }
  303. return cmsOpenProfileFromFile(name, "r");
  304. }
  305. static
  306. cmsFloat64Number Performance(const char* Title, perf_fn fn, cmsContext ct, const char* inICC, const char* outICC, size_t sz, cmsFloat64Number prev)
  307. {
  308. cmsHPROFILE hlcmsProfileIn = loadProfile(inICC);
  309. cmsHPROFILE hlcmsProfileOut = loadProfile(outICC);
  310. cmsFloat64Number n = fn(ct, hlcmsProfileIn, hlcmsProfileOut);
  311. trace("%-30s: ", Title); fflush(stdout);
  312. trace("%-12.2f %-12.2f", n, n * sz);
  313. if (prev > 0.0) {
  314. cmsFloat64Number imp = n / prev;
  315. if (imp > 1)
  316. trace(" (x %-2.1f)", imp);
  317. }
  318. trace("\n"); fflush(stdout);
  319. return n;
  320. }
  321. static
  322. void ComparativeCt(cmsContext ct1, cmsContext ct2, const char* Title, perf_fn fn1, perf_fn fn2, const char* inICC, const char* outICC)
  323. {
  324. cmsHPROFILE hlcmsProfileIn;
  325. cmsHPROFILE hlcmsProfileOut;
  326. if (inICC == NULL)
  327. hlcmsProfileIn = CreateCurves();
  328. else
  329. hlcmsProfileIn = cmsOpenProfileFromFile(inICC, "r");
  330. if (outICC == NULL)
  331. hlcmsProfileOut = CreateCurves();
  332. else
  333. hlcmsProfileOut = cmsOpenProfileFromFile(outICC, "r");
  334. cmsFloat64Number n1 = fn1(ct1, hlcmsProfileIn, hlcmsProfileOut);
  335. if (inICC == NULL)
  336. hlcmsProfileIn = CreateCurves();
  337. else
  338. hlcmsProfileIn = cmsOpenProfileFromFile(inICC, "r");
  339. if (outICC == NULL)
  340. hlcmsProfileOut = CreateCurves();
  341. else
  342. hlcmsProfileOut = cmsOpenProfileFromFile(outICC, "r");
  343. cmsFloat64Number n2 = fn2(ct2, hlcmsProfileIn, hlcmsProfileOut);
  344. trace("%-30s: ", Title); fflush(stdout);
  345. trace("%-12.2f %-12.2f\n", n1, n2);
  346. }
  347. static
  348. void Comparative(const char* Title, perf_fn fn1, perf_fn fn2, const char* inICC, const char* outICC)
  349. {
  350. ComparativeCt(0, 0, Title, fn1, fn2, inICC, outICC);
  351. }
  352. // The worst case is used, no cache and all rgb combinations
  353. static
  354. cmsFloat64Number SpeedTest8bitsRGB(cmsContext ct, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
  355. {
  356. cmsInt32Number r, g, b, j;
  357. cmsFloat64Number diff;
  358. cmsHTRANSFORM hlcmsxform;
  359. Scanline_rgb8bits* In;
  360. cmsUInt32Number Mb;
  361. if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
  362. Fail("Unable to open profiles");
  363. hlcmsxform = cmsCreateTransformTHR(ct, hlcmsProfileIn, TYPE_RGB_8, hlcmsProfileOut, TYPE_RGB_8, INTENT_PERCEPTUAL, FLAGS|cmsFLAGS_NOCACHE);
  364. cmsCloseProfile(hlcmsProfileIn);
  365. cmsCloseProfile(hlcmsProfileOut);
  366. Mb = 256 * 256 * 256 * sizeof(Scanline_rgb8bits);
  367. In = (Scanline_rgb8bits*)malloc(Mb);
  368. j = 0;
  369. for (r = 0; r < 256; r++)
  370. for (g = 0; g < 256; g++)
  371. for (b = 0; b < 256; b++) {
  372. In[j].r = (cmsUInt8Number)r;
  373. In[j].g = (cmsUInt8Number)g;
  374. In[j].b = (cmsUInt8Number)b;
  375. j++;
  376. }
  377. MeasureTimeStart();
  378. cmsDoTransform(hlcmsxform, In, In, 256 * 256 * 256);
  379. diff = MeasureTimeStop();
  380. free(In);
  381. cmsDeleteTransform(hlcmsxform);
  382. return MPixSec(diff);
  383. }
  384. static
  385. cmsFloat64Number SpeedTest8bitsRGBA(cmsContext ct, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
  386. {
  387. cmsInt32Number r, g, b, j;
  388. cmsFloat64Number diff;
  389. cmsHTRANSFORM hlcmsxform;
  390. Scanline_rgba8bits* In;
  391. cmsUInt32Number Mb;
  392. if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
  393. Fail("Unable to open profiles");
  394. hlcmsxform = cmsCreateTransformTHR(ct, hlcmsProfileIn, TYPE_RGBA_8, hlcmsProfileOut, TYPE_RGBA_8, INTENT_PERCEPTUAL, FLAGS|cmsFLAGS_NOCACHE);
  395. cmsCloseProfile(hlcmsProfileIn);
  396. cmsCloseProfile(hlcmsProfileOut);
  397. Mb = 256 * 256 * 256 * sizeof(Scanline_rgba8bits);
  398. In = (Scanline_rgba8bits*)malloc(Mb);
  399. j = 0;
  400. for (r = 0; r < 256; r++)
  401. for (g = 0; g < 256; g++)
  402. for (b = 0; b < 256; b++) {
  403. In[j].r = (cmsUInt8Number)r;
  404. In[j].g = (cmsUInt8Number)g;
  405. In[j].b = (cmsUInt8Number)b;
  406. In[j].a = 0;
  407. j++;
  408. }
  409. MeasureTimeStart();
  410. cmsDoTransform(hlcmsxform, In, In, 256 * 256 * 256);
  411. diff = MeasureTimeStop();
  412. free(In);
  413. cmsDeleteTransform(hlcmsxform);
  414. return MPixSec(diff);
  415. }
  416. // The worst case is used, no cache and all rgb combinations
  417. static
  418. cmsFloat64Number SpeedTest16bitsRGB(cmsContext ct, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
  419. {
  420. cmsInt32Number r, g, b, j;
  421. cmsFloat64Number diff;
  422. cmsHTRANSFORM hlcmsxform;
  423. Scanline_rgb16bits *In;
  424. cmsUInt32Number Mb;
  425. if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
  426. Fail("Unable to open profiles");
  427. hlcmsxform = cmsCreateTransformTHR(ct, hlcmsProfileIn, TYPE_RGB_16, hlcmsProfileOut, TYPE_RGB_16, INTENT_PERCEPTUAL, FLAGS | cmsFLAGS_NOCACHE);
  428. cmsCloseProfile(hlcmsProfileIn);
  429. cmsCloseProfile(hlcmsProfileOut);
  430. Mb = 256 * 256 * 256 * sizeof(Scanline_rgb16bits);
  431. In = (Scanline_rgb16bits*)malloc(Mb);
  432. j = 0;
  433. for (r = 0; r < 256; r++)
  434. for (g = 0; g < 256; g++)
  435. for (b = 0; b < 256; b++) {
  436. In[j].r = (cmsUInt16Number)FROM_8_TO_16(r);
  437. In[j].g = (cmsUInt16Number)FROM_8_TO_16(g);
  438. In[j].b = (cmsUInt16Number)FROM_8_TO_16(b);
  439. j++;
  440. }
  441. MeasureTimeStart();
  442. cmsDoTransform(hlcmsxform, In, In, 256 * 256 * 256);
  443. diff = MeasureTimeStop();
  444. free(In);
  445. cmsDeleteTransform(hlcmsxform);
  446. return MPixSec(diff);
  447. }
  448. static
  449. cmsFloat64Number SpeedTest16bitsCMYK(cmsContext ct, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
  450. {
  451. cmsInt32Number r, g, b, j;
  452. cmsFloat64Number diff;
  453. cmsHTRANSFORM hlcmsxform;
  454. Scanline_cmyk16bits* In;
  455. cmsUInt32Number Mb;
  456. if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
  457. Fail("Unable to open profiles");
  458. hlcmsxform = cmsCreateTransformTHR(ct, hlcmsProfileIn, TYPE_CMYK_16, hlcmsProfileOut, TYPE_CMYK_16, INTENT_PERCEPTUAL, FLAGS | cmsFLAGS_NOCACHE);
  459. cmsCloseProfile(hlcmsProfileIn);
  460. cmsCloseProfile(hlcmsProfileOut);
  461. Mb = 256 * 256 * 256 * sizeof(Scanline_cmyk16bits);
  462. In = (Scanline_cmyk16bits*)malloc(Mb);
  463. j = 0;
  464. for (r = 0; r < 256; r++)
  465. for (g = 0; g < 256; g++)
  466. for (b = 0; b < 256; b++) {
  467. In[j].c = (cmsUInt16Number)r;
  468. In[j].m = (cmsUInt16Number)g;
  469. In[j].y = (cmsUInt16Number)b;
  470. In[j].k = (cmsUInt16Number)r;
  471. j++;
  472. }
  473. MeasureTimeStart();
  474. cmsDoTransform(hlcmsxform, In, In, 256 * 256 * 256);
  475. diff = MeasureTimeStop();
  476. free(In);
  477. cmsDeleteTransform(hlcmsxform);
  478. return MPixSec(diff);
  479. }
  480. static
  481. void SpeedTest8(void)
  482. {
  483. cmsContext noPlugin = cmsCreateContext(0, 0);
  484. cmsFloat64Number t[10];
  485. trace("\n\n");
  486. trace("P E R F O R M A N C E T E S T S 8 B I T S (D E F A U L T)\n");
  487. trace("==============================================================\n\n");
  488. fflush(stdout);
  489. PerformanceHeader();
  490. t[0] = Performance("8 bits on CLUT profiles ", SpeedTest8bitsRGB, noPlugin, PROFILES_DIR "test5.icc", PROFILES_DIR "test3.icc", sizeof(Scanline_rgb8bits), 0);
  491. t[1] = Performance("8 bits on Matrix-Shaper ", SpeedTest8bitsRGB, noPlugin, PROFILES_DIR "test5.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb8bits), 0);
  492. t[2] = Performance("8 bits on same MatrixSh ", SpeedTest8bitsRGB, noPlugin, PROFILES_DIR "test0.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb8bits), 0);
  493. t[3] = Performance("8 bits on curves ", SpeedTest8bitsRGB, noPlugin, "*curves", "*curves", sizeof(Scanline_rgb8bits), 0);
  494. // Note that context 0 has the plug-in installed
  495. trace("\n\n");
  496. trace("P E R F O R M A N C E T E S T S 8 B I T S (P L U G I N)\n");
  497. trace("===========================================================\n\n");
  498. fflush(stdout);
  499. PerformanceHeader();
  500. Performance("8 bits on CLUT profiles ", SpeedTest8bitsRGB, 0, PROFILES_DIR "test5.icc", PROFILES_DIR "test3.icc", sizeof(Scanline_rgb8bits), t[0]);
  501. Performance("8 bits on Matrix-Shaper ", SpeedTest8bitsRGB, 0, PROFILES_DIR "test5.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb8bits), t[1]);
  502. Performance("8 bits on same MatrixSh ", SpeedTest8bitsRGB, 0, PROFILES_DIR "test0.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb8bits), t[2]);
  503. Performance("8 bits on curves ", SpeedTest8bitsRGB, 0, "*curves", "*curves", sizeof(Scanline_rgb8bits), t[3]);
  504. cmsDeleteContext(noPlugin);
  505. }
  506. static
  507. void SpeedTest16(void)
  508. {
  509. cmsContext noPlugin = cmsCreateContext(0, 0);
  510. cmsFloat64Number t[10];
  511. trace("\n\n");
  512. trace("P E R F O R M A N C E T E S T S 1 6 B I T S (D E F A U L T)\n");
  513. trace("=================================================================\n\n");
  514. PerformanceHeader();
  515. t[0] = Performance("16 bits on CLUT profiles ", SpeedTest16bitsRGB, noPlugin, PROFILES_DIR "test5.icc", PROFILES_DIR "test3.icc", sizeof(Scanline_rgb16bits), 0);
  516. t[1] = Performance("16 bits on Matrix-Shaper profiles", SpeedTest16bitsRGB, noPlugin, PROFILES_DIR "test5.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb16bits), 0);
  517. t[2] = Performance("16 bits on same Matrix-Shaper ", SpeedTest16bitsRGB, noPlugin, PROFILES_DIR "test0.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb16bits), 0);
  518. t[3] = Performance("16 bits on curves ", SpeedTest16bitsRGB, noPlugin, "*curves", "*curves", sizeof(Scanline_rgb16bits), 0);
  519. t[4] = Performance("16 bits on CMYK CLUT profiles ", SpeedTest16bitsCMYK, noPlugin, PROFILES_DIR "test1.icc", PROFILES_DIR "test2.icc", sizeof(Scanline_cmyk16bits), 0);
  520. trace("\n\n");
  521. trace("P E R F O R M A N C E T E S T S 1 6 B I T S (P L U G I N)\n");
  522. trace("===============================================================\n\n");
  523. PerformanceHeader();
  524. Performance("16 bits on CLUT profiles ", SpeedTest16bitsRGB, 0, PROFILES_DIR "test5.icc", PROFILES_DIR "test3.icc", sizeof(Scanline_rgb16bits), t[0]);
  525. Performance("16 bits on Matrix-Shaper profiles", SpeedTest16bitsRGB, 0, PROFILES_DIR "test5.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb16bits), t[1]);
  526. Performance("16 bits on same Matrix-Shaper ", SpeedTest16bitsRGB, 0, PROFILES_DIR "test0.icc", PROFILES_DIR "test0.icc", sizeof(Scanline_rgb16bits), t[2]);
  527. Performance("16 bits on curves ", SpeedTest16bitsRGB, 0, "*curves", "*curves", sizeof(Scanline_rgb16bits), t[3]);
  528. Performance("16 bits on CMYK CLUT profiles ", SpeedTest16bitsCMYK, 0, PROFILES_DIR "test1.icc", PROFILES_DIR "test2.icc", sizeof(Scanline_cmyk16bits), t[4]);
  529. cmsDeleteContext(noPlugin);
  530. }
  531. typedef struct
  532. {
  533. Scanline_rgba8bits pixels[256][256];
  534. cmsUInt8Number padding[4];
  535. } padded_line;
  536. typedef struct
  537. {
  538. padded_line line[256];
  539. } big_bitmap;
  540. static
  541. cmsFloat64Number SpeedTest8bitDoTransform(cmsContext ct, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
  542. {
  543. cmsInt32Number r, g, b, j;
  544. cmsFloat64Number diff;
  545. cmsHTRANSFORM hlcmsxform;
  546. big_bitmap* In;
  547. big_bitmap* Out;
  548. cmsUInt32Number Mb;
  549. if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
  550. Fail("Unable to open profiles");
  551. hlcmsxform = cmsCreateTransformTHR(ct, hlcmsProfileIn, TYPE_RGBA_8, hlcmsProfileOut, TYPE_RGBA_8, INTENT_PERCEPTUAL, FLAGS | cmsFLAGS_NOCACHE);
  552. cmsCloseProfile(hlcmsProfileIn);
  553. cmsCloseProfile(hlcmsProfileOut);
  554. // Our test bitmap is 256 x 256 padded lines
  555. Mb = sizeof(big_bitmap);
  556. In = (big_bitmap*)malloc(Mb);
  557. Out = (big_bitmap*)malloc(Mb);
  558. for (r = 0; r < 256; r++)
  559. for (g = 0; g < 256; g++)
  560. for (b = 0; b < 256; b++) {
  561. In->line[r].pixels[g][b].r = (cmsUInt8Number)r;
  562. In->line[r].pixels[g][b].g = (cmsUInt8Number)g;
  563. In->line[r].pixels[g][b].b = (cmsUInt8Number)b;
  564. In->line[r].pixels[g][b].a = 0;
  565. }
  566. MeasureTimeStart();
  567. for (j = 0; j < 256; j++) {
  568. cmsDoTransform(hlcmsxform, In->line[j].pixels, Out->line[j].pixels, 256 * 256);
  569. }
  570. diff = MeasureTimeStop();
  571. free(In); free(Out);
  572. cmsDeleteTransform(hlcmsxform);
  573. return MPixSec(diff);
  574. }
  575. static
  576. cmsFloat64Number SpeedTest8bitLineStride(cmsContext ct, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
  577. {
  578. cmsInt32Number r, g, b;
  579. cmsFloat64Number diff;
  580. cmsHTRANSFORM hlcmsxform;
  581. big_bitmap* In;
  582. big_bitmap* Out;
  583. cmsUInt32Number Mb;
  584. if (hlcmsProfileIn == NULL || hlcmsProfileOut == NULL)
  585. Fail("Unable to open profiles");
  586. hlcmsxform = cmsCreateTransformTHR(ct, hlcmsProfileIn, TYPE_RGBA_8, hlcmsProfileOut, TYPE_RGBA_8, INTENT_PERCEPTUAL, FLAGS | cmsFLAGS_NOCACHE);
  587. cmsCloseProfile(hlcmsProfileIn);
  588. cmsCloseProfile(hlcmsProfileOut);
  589. // Our test bitmap is 256 x 256 padded lines
  590. Mb = sizeof(big_bitmap);
  591. In = (big_bitmap*)malloc(Mb);
  592. Out = (big_bitmap*)malloc(Mb);
  593. for (r = 0; r < 256; r++)
  594. for (g = 0; g < 256; g++)
  595. for (b = 0; b < 256; b++) {
  596. In->line[r].pixels[g][b].r = (cmsUInt8Number)r;
  597. In->line[r].pixels[g][b].g = (cmsUInt8Number)g;
  598. In->line[r].pixels[g][b].b = (cmsUInt8Number)b;
  599. In->line[r].pixels[g][b].a = 0;
  600. }
  601. MeasureTimeStart();
  602. cmsDoTransformLineStride(hlcmsxform, In, Out, 256 * 256, 256, sizeof(padded_line), sizeof(padded_line), 0, 0);
  603. diff = MeasureTimeStop();
  604. free(In); free(Out);
  605. cmsDeleteTransform(hlcmsxform);
  606. return MPixSec(diff);
  607. }
  608. static
  609. void ComparativeLineStride8bits(void)
  610. {
  611. cmsContext NoPlugin, Plugin;
  612. trace("\n\n");
  613. trace("C O M P A R A T I V E cmsDoTransform() vs. cmsDoTransformLineStride()\n");
  614. trace(" values given in MegaPixels per second.\n");
  615. trace("====================================================================\n");
  616. fflush(stdout);
  617. NoPlugin = cmsCreateContext(NULL, NULL);
  618. Plugin = cmsCreateContext(cmsThreadedExtensions(CMS_THREADED_GUESS_MAX_THREADS, 0), NULL);
  619. ComparativeCt(NoPlugin, Plugin, "CLUT profiles ", SpeedTest8bitDoTransform, SpeedTest8bitLineStride, PROFILES_DIR "test5.icc", PROFILES_DIR "test3.icc");
  620. ComparativeCt(NoPlugin, Plugin, "CLUT 16 bits ", SpeedTest16bitsRGB, SpeedTest16bitsRGB, PROFILES_DIR "test5.icc", PROFILES_DIR "test3.icc");
  621. ComparativeCt(NoPlugin, Plugin, "Matrix-Shaper ", SpeedTest8bitDoTransform, SpeedTest8bitLineStride, PROFILES_DIR "test5.icc", PROFILES_DIR "test0.icc");
  622. ComparativeCt(NoPlugin, Plugin, "same MatrixSh ", SpeedTest8bitDoTransform, SpeedTest8bitLineStride, PROFILES_DIR "test0.icc", PROFILES_DIR "test0.icc");
  623. ComparativeCt(NoPlugin, Plugin, "curves ", SpeedTest8bitDoTransform, SpeedTest8bitLineStride, NULL, NULL);
  624. cmsDeleteContext(Plugin);
  625. cmsDeleteContext(NoPlugin);
  626. }
  627. // The harness test
  628. int main()
  629. {
  630. #ifdef _MSC_VER
  631. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  632. #endif
  633. trace("Multithreaded extensions testbed - 1.1\n");
  634. trace("Copyright (c) 1998-2023 Marti Maria Saguer, all rights reserved\n");
  635. trace("\nInstalling error logger ... ");
  636. cmsSetLogErrorHandler(FatalErrorQuit);
  637. trace("done.\n");
  638. trace("Installing plug-in ... ");
  639. cmsPlugin(cmsThreadedExtensions(CMS_THREADED_GUESS_MAX_THREADS, 0));
  640. trace("done.\n\n");
  641. // Change format
  642. CheckChangeFormat();
  643. // Accuracy
  644. CheckAccuracy8Bits();
  645. CheckAccuracy16Bits();
  646. // Check speed
  647. SpeedTest8();
  648. SpeedTest16();
  649. ComparativeLineStride8bits();
  650. cmsUnregisterPlugins();
  651. trace("\nAll tests passed OK\n");
  652. return 0;
  653. }