[babl] tools: add babl-icc-dump



commit c804bd9153d9e5ad304ce2aef4bb0e9c15efe60a
Author: Øyvind Kolås <pippin gimp org>
Date:   Sun Aug 20 22:39:04 2017 +0200

    tools: add babl-icc-dump
    
    This commandline tool contains code to parse the v4 elements babl-icc currently
    doesn't make use of. It loads data from the ICC profile directly into double
    precision floats.
    
      CIE xy red: 0.640009 0.330022
      CIE xy green: 0.300008 0.600015
      CIE xy blue: 0.150011 0.059998
    
    Is due to IEEE double precision floats not being as precise as the fixed point
    values in the profile, which are *exactly*:
    
      CIE xy red:   0.6400  0.3300
      CIE xy green: 0.3000  0.6000
      CIE xy blue:  0.1500  0.0600

 babl/babl-icc.c        |   34 ++--
 tools/Makefile.am      |    2 +-
 tools/babl-benchmark.c |   18 +-
 tools/babl-icc-dump.c  |  460 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 484 insertions(+), 30 deletions(-)
---
diff --git a/babl/babl-icc.c b/babl/babl-icc.c
index 8521fce..7277f67 100644
--- a/babl/babl-icc.c
+++ b/babl/babl-icc.c
@@ -45,8 +45,8 @@ static int load_sbyte (const char *icc, int length, int offset)
 
 static int16_t load_u1f15 (const char *icc, int length, int offset)
 {
-  return load_sbyte (icc, length, offset + 1) +
-         (load_byte (icc, length, offset + 0) << 8);
+  return load_byte (icc, length, offset + 1) +
+         (load_sbyte (icc, length, offset + 0) << 8);
 }
 
 static uint16_t load_u16 (const char *icc, int length, int offset)
@@ -61,12 +61,6 @@ static double load_s15f16 (const char *icc, int length, int offset)
          load_u16 (icc, length, offset + 2) / 65535.0f;
 }
 
-static double load_u16f16 (const char *icc, int length, int offset)
-{
-  return load_u16 (icc, length, offset) +
-         load_u16 (icc, length, offset + 2) / 65535.0;
-}
-
 static uint32_t load_u32 (const char *icc, int length, int offset)
 {
   return load_byte (icc, length, offset + 3) +
@@ -238,9 +232,9 @@ babl_space_rgb_icc (const char *icc,
 
      icc_tag (icc, length, "wtpt", &offset, &element_size);
      {
-       double wX = load_u16f16 (icc, length, offset + 8);
-       double wY = load_u16f16 (icc, length, offset + 8 + 4);
-       double wZ = load_u16f16 (icc, length, offset + 8 + 4 * 2);
+       double wX = load_s15f16 (icc, length, offset + 8);
+       double wY = load_s15f16 (icc, length, offset + 8 + 4);
+       double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2);
 
        return babl_space_rgb_chromaticities (NULL,
                        wX / (wX + wY + wZ),
@@ -262,17 +256,17 @@ babl_space_rgb_icc (const char *icc,
      double rz, gz, bz;
 
      icc_tag (icc, length, "rXYZ", &offset, &element_size);
-     rx = load_u16f16 (icc, length, offset + 8 + 4 * 0);
-     ry = load_u16f16 (icc, length, offset + 8 + 4 * 1);
-     rz = load_u16f16 (icc, length, offset + 8 + 4 * 2);
+     rx = load_s15f16 (icc, length, offset + 8 + 4 * 0);
+     ry = load_s15f16 (icc, length, offset + 8 + 4 * 1);
+     rz = load_s15f16 (icc, length, offset + 8 + 4 * 2);
      icc_tag (icc, length, "gXYZ", &offset, &element_size);
-     gx = load_u16f16 (icc, length, offset + 8 + 4 * 0);
-     gy = load_u16f16 (icc, length, offset + 8 + 4 * 1);
-     gz = load_u16f16 (icc, length, offset + 8 + 4 * 2);
+     gx = load_s15f16 (icc, length, offset + 8 + 4 * 0);
+     gy = load_s15f16 (icc, length, offset + 8 + 4 * 1);
+     gz = load_s15f16 (icc, length, offset + 8 + 4 * 2);
      icc_tag (icc, length, "bXYZ", &offset, &element_size);
-     bx = load_u16f16 (icc, length, offset + 8 + 4 * 0);
-     by = load_u16f16 (icc, length, offset + 8 + 4 * 1);
-     bz = load_u16f16 (icc, length, offset + 8 + 4 * 2);
+     bx = load_s15f16 (icc, length, offset + 8 + 4 * 0);
+     by = load_s15f16 (icc, length, offset + 8 + 4 * 1);
+     bz = load_s15f16 (icc, length, offset + 8 + 4 * 2);
 
      return babl_space_rgb_matrix (NULL,
                 rx, gx, bx,
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 3fb8fd5..3cde051 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/babl
 LDADD = $(top_builddir)/babl/libbabl-@BABL_API_VERSION@.la \
        $(MATH_LIB) $(THREAD_LIB)
 
-noinst_PROGRAMS = babl-verify babl-benchmark
+noinst_PROGRAMS = babl-verify babl-benchmark babl-icc-dump
 if HAVE_SRANDOM
 noinst_PROGRAMS +=             \
        babl-gen-test-pixels
diff --git a/tools/babl-benchmark.c b/tools/babl-benchmark.c
index a887f5c..3ed3539 100644
--- a/tools/babl-benchmark.c
+++ b/tools/babl-benchmark.c
@@ -74,15 +74,15 @@ test (void)
      babl_format_with_space("RGBA float",     babl_space("ProPhoto")),
      babl_format_with_space("R'G'B' u16",     babl_space("ProPhoto")),
 #endif
-     babl_format("RGBA float"),
-     babl_format("R'G'B'A float"),
-     babl_format("R'G'B' u8"),
      babl_format("CIE Lab float"),
-     babl_format_with_space("R'G'B' u8",     babl_space("Adobe")),
-     babl_format_with_space("R'G'B' u8",     babl_space("ProPhoto")),
-     babl_format_with_space("Y float",       babl_space("ProPhoto")),
+     babl_format("RGBA float"),
+     babl_format("Y float"),
+     babl_format("R'G'B'A u8"),
+     babl_format_with_space("RGBA float",    babl_space("ProPhoto")),
      babl_format_with_space("R'G'B'A float", babl_space("ProPhoto")),
-     babl_format_with_space("RGBA float",    babl_space("ProPhoto"))
+     babl_format_with_space("Y float",       babl_space("ProPhoto")),
+     babl_format_with_space("R'G'B'A u8",     babl_space("Adobe")),
+     babl_format_with_space("R'G'B'A u8",     babl_space("ProPhoto")),
      };
   int n_formats = sizeof (formats) / sizeof (formats[0]);
   double mbps[50 * 50] = {0,};
@@ -105,8 +105,8 @@ test (void)
       long end, start;
       int iters = ITERATIONS;
 
-      fprintf (stderr, "%s to %s\r", babl_get_name (formats[i]),
-                                     babl_get_name (formats[j]));
+      fprintf (stderr, "%s to %s          \r", babl_get_name (formats[i]),
+                                               babl_get_name (formats[j]));
       fflush (0);
 
       /* a quarter round of warmup */
diff --git a/tools/babl-icc-dump.c b/tools/babl-icc-dump.c
new file mode 100644
index 0000000..6adb8ff
--- /dev/null
+++ b/tools/babl-icc-dump.c
@@ -0,0 +1,460 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "../config.h"
+#include "babl/babl-internal.h"
+
+static int
+file_get_contents (const char  *path,
+                   char       **contents,
+                   long        *length,
+                   void        *error);
+
+
+#define ICC_HEADER_LEN 128
+#define TAG_COUNT_OFF  ICC_HEADER_LEN
+
+static int load_byte (const char *icc, int length, int offset)
+{
+/* all reading functions take both the char *pointer and the length of the
+ * buffer, and all reads thus gets protected by this condition.
+ */
+  if (offset < 0 || offset > length)
+    return 0;
+
+  return *(uint8_t*) (&icc[offset]);
+}
+
+static int load_sbyte (const char *icc, int length, int offset)
+{
+  if (offset < 0 || offset > length)
+    return 0;
+
+  return *(int8_t*) (&icc[offset]);
+}
+
+static int16_t load_u1f15 (const char *icc, int length, int offset)
+{
+  return load_byte (icc, length, offset + 1) +
+         (load_sbyte (icc, length, offset + 0) << 8);
+}
+
+static uint16_t load_u16 (const char *icc, int length, int offset)
+{
+  return load_byte (icc, length, offset + 1) +
+         (load_byte (icc, length, offset + 0) << 8);
+}
+
+static double load_s15f16 (const char *icc, int length, int offset)
+{
+  return load_u1f15 (icc, length, offset) +
+         load_u16 (icc, length, offset + 2) / 65535.0f;
+}
+
+static uint32_t load_u32 (const char *icc, int length, int offset)
+{
+  return load_byte (icc, length, offset + 3) +
+         (load_byte (icc, length, offset + 2) << 8) +
+         (load_byte (icc, length, offset + 1) << 16) +
+         (load_byte (icc, length, offset + 0) << 24);
+}
+
+static void load_sign (const char *icc, int length,
+                       int offset, char *sign)
+{
+  sign[0]=load_byte(icc, length, offset);
+  sign[1]=load_byte(icc, length, offset + 1);
+  sign[2]=load_byte(icc, length, offset + 2);
+  sign[3]=load_byte(icc, length, offset + 3);
+  sign[4]=0;
+}
+
+/* looks up offset and length for a specific icc tag
+ */
+static int icc_tag (const char *icc, int length,
+                    const char *tag, int *offset, int *el_length)
+{
+  int tag_count = load_u32 (icc, length, TAG_COUNT_OFF);
+  int t;
+
+  for (t =  0; t < tag_count; t++)
+  {
+     char tag_signature[5];
+     load_sign (icc, length, TAG_COUNT_OFF + 4 + 12 * t, tag_signature);
+     if (!strcmp (tag_signature, tag))
+     {
+        if (offset)
+          *offset = load_u32 (icc, length, TAG_COUNT_OFF + 4 + 12* t + 4);
+        if (el_length)
+          *el_length = load_u32 (icc, length, TAG_COUNT_OFF + 4 + 12* t + 4*2);
+        return 1;
+     }
+  }
+  return 0;
+}
+
+#if 0
+
+#define ICC_HEADER_LEN 128
+#define TAG_COUNT_OFF  ICC_HEADER_LEN
+
+static int load_byte (const char *icc, int offset)
+{
+  return *(uint8_t*) (&icc[offset]);
+}
+
+static int16_t load_u1Fixed15 (const char *icc, int offset)
+{
+  return load_byte (icc, offset + 1) +
+         (load_byte (icc, offset + 0) << 8);
+}
+
+static uint16_t load_u16 (const char *icc, int offset)
+{
+  return load_byte (icc, offset + 1) +
+         (load_byte (icc, offset + 0) << 8);
+}
+
+static double load_s15f16 (const char *icc, int offset)
+{
+  return load_u1Fixed15 (icc, offset) + load_u16 (icc, offset + 2) / 65535.0f;
+}
+
+static double load_u16f16 (const char *icc, int offset)
+{
+  return load_u16 (icc, offset) + load_u16 (icc, offset + 2) / 65535.0;
+}
+
+static uint32_t load_u32 (const char *icc, int offset)
+{
+  return load_byte (icc, offset + 3) +
+         (load_byte (icc, offset + 2) << 8) +
+         (load_byte (icc, offset + 1) << 16) +
+         (load_byte (icc, offset + 0) << 24);
+}
+
+static float load_float32 (const char *icc, int offset)
+{
+  char buf[4]={load_byte (icc, offset + 3),
+         load_byte (icc, offset + 2),
+         load_byte (icc, offset + 1),
+         load_byte (icc, offset + 0)};
+  float *val = (float*)(&buf[0]);
+  return *val;
+}
+
+static uint64_t load_uint64 (const char *icc, int offset)
+{
+  return ((uint64_t)load_byte (icc, offset + 7) << (8*0)) +
+         ((uint64_t)load_byte (icc, offset + 6) << (8*1)) +
+         ((uint64_t)load_byte (icc, offset + 5) << (8*2)) +
+         ((uint64_t)load_byte (icc, offset + 4) << (8*3)) +
+         ((uint64_t)load_byte (icc, offset + 3) << (8*4)) +
+         ((uint64_t)load_byte (icc, offset + 2) << (8*5)) +
+         ((uint64_t)load_byte (icc, offset + 1) << (8*6)) +
+         ((uint64_t)load_byte (icc, offset + 0) << (8*7));
+}
+
+static void load_sign (const char *icc, int offset, char *sign)
+{
+  sign[0]=load_byte(icc, offset);
+  sign[1]=load_byte(icc, offset + 1);
+  sign[2]=load_byte(icc, offset + 2);
+  sign[3]=load_byte(icc, offset + 3);
+  sign[4]=0;
+}
+
+static int icc_tag (const char *icc, const char *tag, int *offset, int *length)
+{
+  int tag_count = load_u32 (icc, TAG_COUNT_OFF);
+  int profile_size = load_u32 (icc, 0);
+  int t;
+
+  for (t =  0; t < tag_count; t++)
+  {
+     char tag_signature[5];
+     load_sign (icc, TAG_COUNT_OFF + 4 + 12 * t, tag_signature);
+     if (!strcmp (tag_signature, tag))
+     {
+        *offset = load_u32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4);
+        *length = load_u32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4 * 2);
+        /* avert potential for maliciousnes.. */
+        if (*offset >= profile_size)
+          {
+            *offset = profile_size - 1;
+          }
+        if (*offset + *length >= profile_size)
+          {
+            *length = profile_size - *offset - 1;
+          }
+        return 1;
+     }
+  }
+  return 0;
+}
+#endif
+
+static int load_icc_from_memory (const char *icc, long length, char **error)
+{
+  int  tag_count         = load_u32 (icc, length, TAG_COUNT_OFF);
+  int  profile_size      = load_u32 (icc, length, 0);
+  int  profile_version_major = load_byte (icc, length, 8);
+  int  profile_version_minor = load_byte (icc, length, 9) >> 4;
+  int  profile_version_micro = load_byte (icc, length, 9) & 0xf;
+  char profile_class[5];
+  char color_space[5];
+  char pcs_space[5];
+  int rendering_intent = load_u32 (icc, length, 64);
+  int t;
+  // 64..67 rendering intent
+  // 68..79 XYZ of D50
+
+  load_sign (icc, length, 16, color_space);
+  load_sign (icc, length, 20, pcs_space);
+  load_sign (icc, length, 12, profile_class);
+
+  if (strcmp (profile_class, "mntr"))
+  {
+    *error = "not a monitor-class profile";
+    return -1;
+  }
+  if (strcmp (color_space, "RGB "))
+  {
+    *error = "not defining an RGB space";
+    return -1;
+  }
+#if 0
+  if (profile_version_major > 2)
+  {
+    *error = "only ICC v2 profiles supported";
+    return -1;
+  }
+#endif
+  {
+     int offset, element_size;
+     icc_tag (icc, length, "desc", &offset, &element_size);
+     fprintf (stdout, "desc: %s\n", icc + offset + 12);
+  }
+  {
+     int offset, element_size;
+     icc_tag (icc, length, "cprt", &offset, &element_size);
+     fprintf (stdout, "copyright: %s\n", icc + offset + 8);
+  }
+
+#if 1
+  fprintf (stdout, "icc version: %i.%i.%i\n", profile_version_major, profile_version_minor, 
profile_version_micro);
+  fprintf (stdout, "profile-size: %i\n", profile_size);
+  fprintf (stdout, "profile-class: %s\n", profile_class);
+  fprintf (stdout, "color-space: %s\n", color_space);
+  fprintf (stdout, "rendering-intent: %i\n", rendering_intent);
+  fprintf (stdout, "pcs-space: %s\n", pcs_space);
+  fprintf (stdout, "byte length: %li\n", length);
+  fprintf (stdout, "tag-count: %i\n", tag_count);
+
+  for (t =  0; t < tag_count; t++)
+  {
+     char tag_signature[5];
+     int offset, element_size;
+     load_sign (icc, length, TAG_COUNT_OFF + 4 + 12 * t, tag_signature);
+     icc_tag (icc, length, tag_signature, &offset, &element_size);
+     fprintf (stdout, "tag %i %s %i %i\n", t, tag_signature, offset, element_size);
+  }
+#endif
+  fprintf (stdout, "tags: ");
+  for (t =  0; t < tag_count; t++)
+  {
+     char tag_signature[5];
+     int offset, element_size;
+     load_sign (icc, length, TAG_COUNT_OFF + 4 + 12 * t, tag_signature);
+     icc_tag (icc, length, tag_signature, &offset, &element_size);
+     fprintf (stdout, "%s ", tag_signature);
+  }
+  fprintf (stdout, "\n");
+
+  {
+     int offset, element_size;
+     if (icc_tag (icc, length, "chrm", &offset, &element_size))
+     {
+     int channels = load_u16 (icc, length, offset + 8);
+     int phosporant = load_u16 (icc, length, offset + 10);
+     double redX   = load_s15f16 (icc, length, offset + 12);
+     double redY   = load_s15f16 (icc, length, offset + 12 + 4);
+     double greenX = load_s15f16 (icc, length, offset + 20);
+     double greenY = load_s15f16 (icc, length, offset + 20 + 4);
+     double blueX  = load_s15f16 (icc, length, offset + 28);
+     double blueY  = load_s15f16 (icc, length, offset + 28 + 4);
+     fprintf (stdout, "chromaticity:\n");
+     fprintf (stdout, "  channels: %i\n", channels);
+     fprintf (stdout, "  phosphorant: %i\n", phosporant);
+     fprintf (stdout, "  CIE xy red: %f %f\n", redX, redY);
+     fprintf (stdout, "  CIE xy green: %f %f\n", greenX, greenY);
+     fprintf (stdout, "  CIE xy blue: %f %f\n", blueX, blueY);
+     }
+  }
+
+  {
+     int offset, element_size;
+     if (icc_tag (icc, length, "wtpt", &offset, &element_size))
+     {
+       double wX = load_s15f16 (icc, length, offset + 8);
+       double wY = load_s15f16 (icc, length, offset + 8 + 4);
+       double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2);
+       fprintf (stdout, "whitepoint CIE xyz: %f %f %f\n", wX, wY, wZ);
+     }
+  }
+
+  {
+     int offset, element_size;
+     if (icc_tag (icc, length, "rXYZ", &offset, &element_size))
+     {
+       double wX = load_s15f16 (icc, length, offset + 8);
+       double wY = load_s15f16 (icc, length, offset + 8 + 4);
+       double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2);
+       fprintf (stdout, "Red        CIE xyz: %f %f %f\n", wX, wY, wZ);
+     }
+  }
+  {
+     int offset, element_size;
+     if (icc_tag (icc, length, "gXYZ", &offset, &element_size))
+     {
+       double wX = load_s15f16 (icc, length, offset + 8);
+       double wY = load_s15f16 (icc, length, offset + 8 + 4);
+       double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2);
+       fprintf (stdout, "Green      CIE xyz: %f %f %f\n", wX, wY, wZ);
+     }
+  }
+  {
+     int offset, element_size;
+     if (icc_tag (icc, length, "bXYZ", &offset, &element_size))
+     {
+       double wX = load_s15f16 (icc, length, offset + 8);
+       double wY = load_s15f16 (icc, length, offset + 8 + 4);
+       double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2);
+       fprintf (stdout, "Blue       CIE xyz: %f %f %f\n", wX, wY, wZ);
+     }
+  }
+
+  if (1) {
+     int offset, element_size;
+     if (icc_tag (icc, length, "rTRC", &offset, &element_size))
+     {
+       int count = load_u32 (icc, length, offset + 8);
+       int i;
+       if (!strcmp (icc + offset, "para"))
+       {
+         int function_type = load_u16 (icc, length, offset + 8);
+         float g,a,b,c,d,e,f;
+         switch (function_type)
+         {
+            case 0:
+              g = load_s15f16 (icc, length, offset + 12 + 2 * 0);
+              fprintf (stdout, "parametric TRC gamma type %f\n", g);
+              break;
+
+            case 3:
+              g = load_s15f16 (icc, length, offset + 12 + 2 * 0);
+              a = load_s15f16 (icc, length, offset + 12 + 2 * 1);
+              b = load_s15f16 (icc, length, offset + 12 + 2 * 2);
+              c = load_s15f16 (icc, length, offset + 12 + 2 * 3);
+              d = load_s15f16 (icc, length, offset + 12 + 2 * 4);
+              e = load_s15f16 (icc, length, offset + 12 + 2 * 5);
+              f = load_s15f16 (icc, length, offset + 12 + 2 * 5);
+              fprintf (stdout, "parametric TRC sRGB type %f %f %f %f %f %f %f\n", g, a, b, c, d, e, f);
+              break;
+            default:
+            fprintf (stdout, "unhandled parametric TRC type %i\n", function_type);
+            break;
+         }
+       }
+       else
+       {
+       fprintf (stdout, "rTRC count: %i  %s\n", count,  icc + offset);
+       if (count == 0)
+       {
+         fprintf (stdout, "linear TRC\n");
+       }
+       else if (count == 1)
+       {
+         fprintf (stdout, "gamma TRC of: %f\n", load_byte (icc, length, offset + 12) +
+                                                load_byte (icc, length, offset + 12 + 1) / 255.0);
+       }
+       else for (i = 0; i < count && i < 10; i ++)
+       {
+         fprintf (stdout, "%i=%i ", i, load_u16 (icc, length, offset + 12 + i * 2));
+         if (i % 7 == 0)
+            fprintf (stdout, "\n");
+       }
+       }
+     }
+  }
+  return 0;
+}
+
+static int load_icc (const char *path, char **error)
+{
+  char *icc = NULL;
+  long length = 0;
+  int ret = 0;
+  file_get_contents (path, &icc, &length, NULL);
+  if (icc)
+  {
+    ret = load_icc_from_memory (icc, length, error);
+    free (icc);
+  }
+  return ret;
+}
+
+static int
+file_get_contents (const char  *path,
+                   char       **contents,
+                   long        *length,
+                   void        *error)
+{
+  FILE *file;
+  long  size;
+  char *buffer;
+
+  file = fopen (path,"rb");
+
+  if (!file)
+    return -1;
+
+  fseek (file, 0, SEEK_END);
+  *length = size = ftell (file);
+  rewind (file);
+  buffer = malloc(size + 8);
+
+  if (!buffer)
+    {
+      fclose(file);
+      return -1;
+    }
+
+  size -= fread (buffer, 1, size, file);
+  if (size)
+    {
+      fclose (file);
+      free (buffer);
+      return -1;
+    }
+  fclose (file);
+  *contents = buffer;
+  return 0;
+}
+
+int main (int argc, char **argv)
+{
+  char *error = NULL;
+  if (argc < 2)
+  {
+    fprintf (stdout, "need one arg, an ICC profile file\n");
+    return -1;
+  }
+
+  load_icc (argv[1], &error);
+  if (error)
+  {
+    fprintf (stdout, "icc-parse-problem: %s\n", error);
+  }
+
+  return 0;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]