[babl] tools: add babl-icc-dump
- From: Øyvind Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [babl] tools: add babl-icc-dump
- Date: Sun, 20 Aug 2017 20:44:10 +0000 (UTC)
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]