Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / gfx / thebes / test / gfxColorManagementTest.cpp
blob95efddaea2f6a252b0d4193439da7a65b85541ee
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <cctype>
7 #include "lcms.h"
9 /* Nabbed from the http://www.jonh.net/~jonh/md5/crc32/crc32.c. License is
10 * "do anything, no restrictions." */
11 unsigned long crc32(const unsigned char *s, unsigned int len);
14 * Test Framework Header stuff
17 #define ASSERT(foo) { \
18 if (!(foo)) { \
19 fprintf(stderr, "%s: Failed Assertion Line %d\n", __FILE__, __LINE__); \
20 exit(-1); \
21 } \
24 #define CHECK(condition, var, value, message, label) { \
25 if (!(condition)) { \
26 var = value; \
27 fprintf(stderr, message); \
28 goto label; \
29 } \
32 #define MAX(a,b) ((a) > (b) ? (a) : (b))
33 #define MIN(a,b) ((a) <= (b) ? (a) : (b))
34 #define ABS(a) ((a < 0) ? -a : a)
36 #define BITMAP_PIXEL_COUNT (256 * 256 * 256)
37 #define BITMAP_SIZE (BITMAP_PIXEL_COUNT * 3)
39 /* Relative, Perceptual, and Saturation all take the same code path through
40 * LCMS. As such, we just check perceptual and absolute. */
41 int testedIntents[] = {INTENT_PERCEPTUAL, INTENT_ABSOLUTE_COLORIMETRIC};
42 #define TESTED_INTENT_COUNT (sizeof(testedIntents)/sizeof(int))
44 const char *profileDir = "testprofiles";
46 /* Parameters detailing a single test. */
47 struct TestParams {
49 /* name of the input profile. */
50 char *iProfileName;
52 /* name of the output profile. */
53 char *oProfileName;
55 /* Golden CRC32s. */
56 unsigned goldenCRCs[TESTED_INTENT_COUNT];
58 /* Did we read golden sums? */
59 int hasGolden;
61 /* Generated CRC32. */
62 unsigned ourCRCs[TESTED_INTENT_COUNT];
64 /* Linked list pointer. */
65 struct TestParams *next;
69 /* Top level context structure for the test run. */
70 struct TestContext {
72 /* Base path for files. */
73 char *basePath;
75 /* Linked list of param structures. */
76 struct TestParams *paramList;
78 /* Our GIANT, ~50 meg buffers for every pixel value. */
79 unsigned char *src, *fixedX, *floatX;
83 /* Reads a line from the directive file. Returns 0 on success,
84 -1 on malformed file. */
85 static int
86 ReadTestFileLine(struct TestContext *ctx, FILE *handle)
89 /* Locals. */
90 char buff[4096];
91 int status = 0;
92 char *iter, *base;
93 char *strings[2];
94 char *rv;
95 unsigned i;
96 struct TestParams *params = NULL;
98 /* Check input. */
99 ASSERT(ctx != NULL);
100 ASSERT(handle != NULL);
102 /* Null out string pointers. */
103 for (i = 0; i < 2; ++i)
104 strings[i] = NULL;
106 /* Read in the line. */
107 rv = fgets(buff, 4096, handle);
108 if (feof(handle))
109 goto done;
110 CHECK(rv != NULL, status, -1, "Bad Test File\n", error);
112 /* Allow for comments and blanklines. */
113 if ((buff[0] == '#') || isspace(buff[0]))
114 goto done;
116 /* Allocate a param file. */
117 params = (struct TestParams *) calloc(sizeof(struct TestParams), 1);
118 ASSERT(params);
120 /* Parse the profile names. */
121 iter = buff;
122 for (i = 0; i < 2; ++i) {
123 for (base = iter; (*iter != '\0') && !isspace(*iter); ++iter);
124 *iter = '\0';
125 CHECK((iter - base) > 0, status, -1, "Bad Test File\n", error);
126 strings[i] = strdup(base);
127 ++iter;
130 /* Fill the param file. */
131 params->iProfileName = strings[0];
132 params->oProfileName = strings[1];
134 /* Skip any whitespace. */
135 for (; (*iter != '\0') && isspace(*iter); ++iter);
137 /* if we have more to parse, we should have golden CRCs. */
138 if (*iter != '\0') {
139 for (i = 0; i < TESTED_INTENT_COUNT; ++i) {
140 params->goldenCRCs[i] = strtoul(iter, &base, 16);
141 CHECK((errno != EINVAL) && (errno != ERANGE) && (base != iter),
142 status, -1, "Bad Checksum List\n", error);
143 iter = base;
145 params->hasGolden = 1;
148 /* Link up our param structure. */
149 params->next = ctx->paramList;
150 ctx->paramList = params;
152 done:
153 return status;
155 error:
157 /* Free the strings. */
158 for (i = 0; i < 2; ++i)
159 free(strings[i]);
161 /* Free the param structure. */
162 if (params != NULL)
163 free(params);
165 return status;
168 /* Initializes the test context. 0 on success, -1 on failure. */
169 static int
170 TestInit(struct TestContext *ctx, const char *filePath)
173 /* Locals. */
174 FILE *tfHandle = NULL;
175 const char *iter, *last;
176 unsigned n;
177 int status = 0;
178 unsigned i, j, k, l;
179 struct TestParams *curr, *before, *after;
181 /* Checks. */
182 ASSERT(ctx != NULL);
183 ASSERT(filePath != NULL);
185 /* Allocate our buffers. If it's going to fail, we should know now. */
186 ctx->src = (unsigned char *) malloc(BITMAP_SIZE);
187 CHECK(ctx->src != NULL, status, -1, "Can't allocate enough memory\n", error);
188 ctx->fixedX = (unsigned char *) malloc(BITMAP_SIZE);
189 CHECK(ctx->fixedX != NULL, status, -1, "Can't allocate enough memory\n", error);
190 ctx->floatX = (unsigned char *) malloc(BITMAP_SIZE);
191 CHECK(ctx->floatX != NULL, status, -1, "Can't allocate enough memory\n", error);
193 /* Open the test file. */
194 tfHandle = fopen(filePath, "r");
195 CHECK(tfHandle != NULL, status, -1, "Unable to open test file\n", done);
197 /* Extract the base. XXX: Do we need to worry about windows separators? */
198 for (last = iter = filePath; *iter != '\0'; ++iter)
199 if (*iter == '/')
200 last = iter;
201 n = last - filePath;
202 ctx->basePath = (char *) malloc(n + 1);
203 ASSERT(ctx->basePath != NULL);
204 memcpy(ctx->basePath, filePath, n);
205 ctx->basePath[n] = '\0';
207 /* Read through the directive file. */
208 while (!feof(tfHandle)) {
209 CHECK(!ReadTestFileLine(ctx, tfHandle), status, -1,
210 "Failed to Read Test File\n", error);
213 /* Reverse the list so that we process things in the order we read them
214 in. */
215 curr = ctx->paramList;
216 before = NULL;
217 while (curr->next != NULL) {
218 after = curr->next;
219 curr->next = before;
220 before = curr;
221 curr = after;
223 curr->next = before;
224 ctx->paramList = curr;
226 /* Generate our source bitmap. */
227 printf("Generating source bitmap...");
228 fflush(stdout);
229 for (i = 0; i < 256; ++i) {
230 for (j = 0; j < 256; ++j)
231 for (k = 0; k < 256; ++k) {
232 l = ((256 * 256 * i) + (256 * j) + k) * 3;
233 ctx->src[l] = (unsigned char) i;
234 ctx->src[l + 1] = (unsigned char) j;
235 ctx->src[l + 2] = (unsigned char) k;
238 ASSERT(l == (BITMAP_SIZE - 3));
239 printf("done!\n");
241 goto done;
243 error:
244 /* Free up the buffers. */
245 if (ctx->src != NULL)
246 free(ctx->src);
247 if (ctx->fixedX != NULL)
248 free(ctx->fixedX);
249 if (ctx->floatX != NULL)
250 free(ctx->floatX);
252 done:
254 /* We're done with the test directive file. */
255 if (tfHandle != NULL)
256 fclose(tfHandle);
258 return status;
261 /* Runs a test for the given param structure. Returns 0 on success (even if
262 * the test itself fails), -1 on code failure.
264 * 'mode' is either "generate" or "check".
266 * 'intentIndex' is an index in testedIntents
268 static int
269 RunTest(struct TestContext *ctx, struct TestParams *params,
270 char *mode, unsigned intentIndex)
273 /* Locals. */
274 cmsHPROFILE inProfile = NULL;
275 cmsHPROFILE outProfile = NULL;
276 cmsHTRANSFORM transformFixed = NULL;
277 cmsHTRANSFORM transformFloat = NULL;
278 char *filePath;
279 unsigned i;
280 int difference;
281 int failures;
282 int status = 0;
284 /* Allocate a big enough string for either file path. */
285 filePath = (char *)malloc(strlen(ctx->basePath) + 1 +
286 strlen(profileDir) + 1 +
287 MAX(strlen(params->iProfileName),
288 strlen(params->oProfileName)) + 1);
289 ASSERT(filePath != NULL);
291 /* Set up the profile path for the input profile. */
292 strcpy(filePath, ctx->basePath);
293 strcat(filePath, "/");
294 strcat(filePath, profileDir);
295 strcat(filePath, "/");
296 strcat(filePath, params->iProfileName);
297 inProfile = cmsOpenProfileFromFile(filePath, "r");
298 CHECK(inProfile != NULL, status, -1, "unable to open input profile!\n", done);
300 /* Set up the profile path for the output profile. */
301 strcpy(filePath, ctx->basePath);
302 strcat(filePath, "/");
303 strcat(filePath, profileDir);
304 strcat(filePath, "/");
305 strcat(filePath, params->oProfileName);
306 outProfile = cmsOpenProfileFromFile(filePath, "r");
307 CHECK(outProfile != NULL, status, -1, "unable to open input profile!\n", done);
309 /* Precache. */
310 cmsPrecacheProfile(inProfile, CMS_PRECACHE_LI16W_FORWARD);
311 cmsPrecacheProfile(inProfile, CMS_PRECACHE_LI8F_FORWARD);
312 cmsPrecacheProfile(outProfile, CMS_PRECACHE_LI1616_REVERSE);
313 cmsPrecacheProfile(outProfile, CMS_PRECACHE_LI168_REVERSE);
315 /* Create the fixed transform. */
316 transformFixed = cmsCreateTransform(inProfile, TYPE_RGB_8,
317 outProfile, TYPE_RGB_8,
318 testedIntents[intentIndex], 0);
319 CHECK(transformFixed != NULL, status, -1,
320 "unable to create fixed transform!\n", done);
322 /* Do the fixed transform. */
323 cmsDoTransform(transformFixed, ctx->src, ctx->fixedX, BITMAP_PIXEL_COUNT);
325 /* Compute the CRC of the fixed transform. */
326 params->ourCRCs[intentIndex] = crc32(ctx->fixedX, BITMAP_SIZE);
328 /* If we're just generating, we have everything we need. */
329 if (!strcmp(mode, "generate")) {
330 printf("In: %s, Out: %s, Intent: %u Generated\n",
331 params->iProfileName, params->oProfileName, testedIntents[intentIndex]);
332 goto done;
335 /* Create the float transform. */
336 transformFloat = cmsCreateTransform(inProfile, TYPE_RGB_8,
337 outProfile, TYPE_RGB_8,
338 testedIntents[intentIndex],
339 cmsFLAGS_FLOATSHAPER);
340 CHECK(transformFloat != NULL, status, -1,
341 "unable to create float transform!\n", done);
343 /* Make sure we have golden values. */
344 CHECK(params->hasGolden, status, -1,
345 "Error: Check mode enabled but no golden values in file\n", done);
347 /* Print out header. */
348 printf("In: %s, Out: %s, Intent: %u\n",
349 params->iProfileName, params->oProfileName,
350 testedIntents[intentIndex]);
352 /* CRC check the fixed point path. */
353 if (params->goldenCRCs[intentIndex] == params->ourCRCs[intentIndex])
354 printf("\tPASSED - CRC Check of Fixed Point Path\n");
355 else
356 printf("\tFAILED - CRC Check of Fixed Point Path - Expected %x, Got %x\n",
357 params->goldenCRCs[intentIndex], params->ourCRCs[intentIndex]);
359 /* Do the floating point transform. */
360 cmsDoTransform(transformFloat, ctx->src, ctx->floatX, BITMAP_PIXEL_COUNT);
362 /* Compare fixed with floating. */
363 failures = 0;
364 for (i = 0; i < BITMAP_SIZE; ++i) {
365 difference = (int)ctx->fixedX[i] - (int)ctx->floatX[i];
366 /* Allow off-by-one from fixed point, nothing more. */
367 if (ABS(difference) > 1)
368 ++failures;
370 if (failures == 0)
371 printf("\tPASSED - floating point path within acceptable parameters\n");
372 else
373 printf("\tWARNING - floating point path off by 2 or more in %d cases!\n",
374 failures);
376 done:
378 /* Free the temporary string. */
379 free(filePath);
381 /* Close the transforms and profiles if non-null. */
382 if (transformFixed != NULL)
383 cmsDeleteTransform(transformFixed);
384 if (transformFloat != NULL)
385 cmsDeleteTransform(transformFloat);
386 if (inProfile != NULL)
387 cmsCloseProfile(inProfile);
388 if (outProfile != NULL)
389 cmsCloseProfile(outProfile);
391 return status;
394 /* Writes the in memory data structures out to the original test directive
395 * file, using the generated CRCs as golden CRCs. */
396 static int
397 WriteTestFile(struct TestContext *ctx, const char *filename)
400 /* Locals. */
401 FILE *tfHandle = NULL;
402 int status = 0;
403 struct TestParams *iter;
404 unsigned i;
406 /* Check Input. */
407 ASSERT(ctx != NULL);
408 ASSERT(filename != NULL);
410 /* Open the file in write mode. */
411 tfHandle = fopen(filename, "w");
412 CHECK(tfHandle != NULL, status, -1, "Couldn't Open Test File For Writing",
413 done);
415 /* Print Instructional Comment. */
416 fprintf(tfHandle, "# Color Management Test Directive File\n#\n# Format:\n"
417 "# InputProfileFilename OutputProfileFilename "
418 "<CRC32 For Each Intent>\n#\n");
419 /* Iterate and Print. */
420 for (iter = ctx->paramList; iter != NULL; iter = iter->next) {
421 fprintf(tfHandle, "%s %s", iter->iProfileName, iter->oProfileName);
422 for (i = 0; i < TESTED_INTENT_COUNT; ++i)
423 fprintf(tfHandle, " %x", iter->ourCRCs[i]);
424 fprintf(tfHandle, "\n");
427 done:
429 /* Close the test file. */
430 if (tfHandle != NULL)
431 fclose(tfHandle);
433 return status;
437 /* Main Function. */
438 int
439 main (int argc, char **argv)
442 /* Locals. */
443 struct TestContext ctx;
444 struct TestParams *iter;
445 unsigned i;
446 int status = 0;
448 /* Zero out context. */
449 memset(&ctx, 0, sizeof(ctx));
451 if ((argc != 3) ||
452 (strcmp(argv[1], "generate") && strcmp(argv[1], "check"))) {
453 printf("Usage: %s generate|check PATH/FILE.cmtest\n", argv[0]);
454 return -1;
457 /* Initialize the test. */
458 TestInit(&ctx, argv[2]);
460 /* Run each individual test. */
461 iter = ctx.paramList;
462 while (iter != NULL) {
464 /* For each intent. */
465 for (i = 0; i < TESTED_INTENT_COUNT; ++i)
466 CHECK(!RunTest(&ctx, iter, argv[1], i),
467 status, -1, "RunTest Failed\n", done);
468 iter = iter->next;
471 /* If we're generating, write back out. */
472 if (!strcmp(argv[1], "generate"))
473 WriteTestFile(&ctx, argv[2]);
475 done:
477 return status;
482 * CRC32 Implementation.
485 static unsigned long crc32_tab[] = {
486 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
487 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
488 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
489 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
490 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
491 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
492 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
493 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
494 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
495 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
496 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
497 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
498 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
499 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
500 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
501 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
502 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
503 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
504 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
505 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
506 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
507 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
508 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
509 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
510 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
511 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
512 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
513 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
514 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
515 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
516 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
517 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
518 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
519 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
520 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
521 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
522 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
523 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
524 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
525 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
526 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
527 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
528 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
529 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
530 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
531 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
532 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
533 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
534 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
535 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
536 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
537 0x2d02ef8dL
540 /* Return a 32-bit CRC of the contents of the buffer. */
542 unsigned long
543 crc32(const unsigned char *s, unsigned int len)
545 unsigned int i;
546 unsigned long crc32val;
548 crc32val = 0;
549 for (i = 0; i < len; i ++)
551 crc32val =
552 crc32_tab[(crc32val ^ s[i]) & 0xff] ^
553 (crc32val >> 8);
555 return crc32val;