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