iccdump .pngs if we have zlib

Change-Id: Ide5e21598563d58dc5813a09b8a6158ca7b15fdc
Reviewed-on: https://skia-review.googlesource.com/121780
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Klein <mtklein@chromium.org>
diff --git a/build/common b/build/common
index 0acdddf..86f4558 100644
--- a/build/common
+++ b/build/common
@@ -9,7 +9,7 @@
     description = compile $out
 
 rule link
-    command = $disabled && touch $out || $cc $ldflags $extra_ldflags -pthread $in -o $out
+    command = $disabled && touch $out || $cc $ldflags $extra_ldflags -ldl -pthread $in -o $out
     description = link $out
 
 include build/targets
diff --git a/iccdump.c b/iccdump.c
index aa69904..ed08707 100644
--- a/iccdump.c
+++ b/iccdump.c
@@ -9,6 +9,7 @@
     #define _CRT_SECURE_NO_WARNINGS
     #define SKCMS_NORETURN __declspec(noreturn)
 #else
+    #include <dlfcn.h>
     #include <stdnoreturn.h>
     #define SKCMS_NORETURN noreturn
 #endif
@@ -112,6 +113,16 @@
 #endif
 }
 
+static uint32_t read_big_u32(const uint8_t* ptr) {
+    uint32_t be;
+    memcpy(&be, ptr, sizeof(be));
+#if defined(_MSC_VER)
+    return _byteswap_ulong(be);
+#else
+    return __builtin_bswap32(be);
+#endif
+}
+
 // TODO: Put state into struct with FP
 static int desmos_id = 0;
 
@@ -362,6 +373,81 @@
     svg_close(fp);
 }
 
+static const uint8_t png_signature[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
+
+#if defined(_MSC_VER)
+    static bool parse_png_profile(const uint8_t* buf, size_t len, skcms_ICCProfile* profile) {
+        (void)buf;
+        (void)len;
+        (void)profile;
+        (void)read_big_u32;
+        return false;
+    }
+#else
+    static bool parse_png_profile(const uint8_t* buf, size_t len, skcms_ICCProfile* profile) {
+        void* zlib = NULL;
+        if (!zlib) { zlib = dlopen("libz.so",    RTLD_LAZY); }
+        if (!zlib) { zlib = dlopen("libz.dylib", RTLD_LAZY); }
+        if (!zlib) {
+            return false;
+        }
+
+        typedef int(*UncompressFn)(uint8_t*, unsigned long*, const uint8_t*, unsigned long);
+        UncompressFn uncompress = (UncompressFn)dlsym(zlib, "uncompress");
+        if (!uncompress) {
+            return false;
+        }
+
+        const uint8_t* end = buf+len;
+
+        // skip over signature
+        buf += sizeof(png_signature);
+
+        const uint32_t IEND = 0x49454e44,
+                       iCCP = 0x69434350;
+
+        uint32_t size, tag = 0;
+
+        while (buf < end && tag != IEND) {
+            size = read_big_u32(buf+0);
+            tag  = read_big_u32(buf+4);
+            buf += 8;
+
+            if (tag == iCCP) {
+                const char* name = (const char*)buf;
+                printf("Profile name from .png: '%s'\n", name);
+
+                size_t header = strlen(name)
+                              + 1/*NUL*/
+                              + 1/*PNG compression method, always 0 == zlib*/;
+
+                unsigned long inf_size,
+                              guess = len;
+                void* inflated = NULL;
+
+                int err;
+                do {
+                    inf_size = guess;
+                    inflated = realloc(inflated, inf_size);
+
+                    err = uncompress(inflated, &inf_size,
+                                     (const uint8_t*)name+header, size-header);
+                    guess *= 2;
+                } while (err == -5/*Z_BUF_ERROR*/);
+
+                bool ok = err == 0/*Z_OK*/
+                       && skcms_Parse(inflated, inf_size, profile);
+                free(inflated);
+                return ok;
+            }
+
+            buf += size;
+            buf += 4/*skip the PNG CRC*/;
+        }
+        return false;
+    }
+#endif
+
 int main(int argc, char** argv) {
     const char* filename = NULL;
     bool svg = false;
@@ -389,7 +475,11 @@
     }
 
     skcms_ICCProfile profile;
-    if (!skcms_Parse(buf, len, &profile)) {
+    if (len >= sizeof(png_signature) && 0 == memcmp(buf, png_signature, sizeof(png_signature))) {
+        if (!parse_png_profile(buf, len, &profile)) {
+            fatal("Could not find an ICC profile in this .png");
+        }
+    } else if (!skcms_Parse(buf, len, &profile)) {
         fatal("Unable to parse ICC profile");
     }