[babl] babl: add icc meta-data extraction functions
- From: Øyvind Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [babl] babl: add icc meta-data extraction functions
- Date: Mon, 11 Sep 2017 13:33:06 +0000 (UTC)
commit 522cb75c540b20cbdaaae1c60d856812673897f0
Author: Øyvind Kolås <pippin gimp org>
Date: Fri Sep 8 21:26:04 2017 +0200
babl: add icc meta-data extraction functions
Instead of extracting all meta-data of ICC profiles and attaching them to
BablSpaces, we provide an string key/value API to query meta data of in-memory
ICC profiles.
babl/babl-icc.c | 308 ++++++++++++++++++++++++++++++++++++++--------
babl/babl-space.h | 2 -
babl/babl.h | 34 ++++-
tools/babl-icc-rewrite.c | 40 +++++--
4 files changed, 318 insertions(+), 66 deletions(-)
---
diff --git a/babl/babl-icc.c b/babl/babl-icc.c
index 0bf74cb..7d79051 100644
--- a/babl/babl-icc.c
+++ b/babl/babl-icc.c
@@ -578,7 +578,27 @@ const char *babl_space_to_icc (const Babl *babl, int *ret_length)
return icc;
}
-static char *icc_decode_mluc (ICC *state, int offset, int element_length, char *lang, char *country)
+
+typedef uint32_t UTF32;
+typedef uint16_t UTF16;
+typedef uint8_t UTF8;
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+static int ConvertUTF16toUTF8 (const UTF16** sourceStart,
+ const UTF16* sourceEnd,
+ UTF8** targetStart,
+ UTF8* targetEnd,
+ ConversionFlags flags);
+
+static char *icc_decode_mluc (ICC *state,
+ int offset,
+ int element_length,
+ const char *lang,
+ const char *country)
{
int n_records = icc_read (u32, offset + 8);
int record_size = icc_read (u32, offset + 12);
@@ -598,32 +618,60 @@ static char *icc_decode_mluc (ICC *state, int offset, int element_length, char *
(!country || !strcmp (country, icountry))) ||
(i == n_records - 1))
{
- int slength = icc_read(u32, offset + o + 4);
+ int slength = (icc_read(u32, offset + o + 4))/2;
int soffset = icc_read(u32, offset + o + 8);
- char *ret = babl_malloc (slength * 2);
+ UTF16 *tmp_ret = babl_calloc (sizeof (uint16_t), slength + 1);
+ UTF16 *tmp_ret2 = tmp_ret;
+ unsigned char *ret = babl_calloc (1, slength * 4 + 1); // worst case scenario
+ unsigned char *ret2 = ret;
int j;
- for (j = 0; j < slength/2; j++)
+ for (j = 0; j < slength; j++)
{
- int hi = icc_read(u8, offset + soffset + j * 2 + 0);
- int lo = icc_read(u8, offset + soffset + j * 2 + 1);
-
- ret[j] = lo + hi * 0; // only ASCII survives this
- // brute utf16 decoding, so it is
- // good we ask for english.
+ tmp_ret[j] = icc_read(u16, offset + soffset + j * 2);
}
- ret[j] = 0;
- return ret;
+ tmp_ret[j] = 0;
+ memset (ret, 0, slength * 4 + 1);
+ ConvertUTF16toUTF8 ((void*)&tmp_ret2, tmp_ret + slength, &ret2, ret + slength, lenientConversion);
+ babl_free(tmp_ret);
+ { // trim down to actually used utf8
+ unsigned char *tmp = (void*)strdup ((void*)ret);
+ babl_free (ret);
+ ret = tmp;
+ }
+ return (void*)ret;
}
o+=record_size;
}
- return babl_strdup ("");
+ return NULL;
+}
+
+static char *decode_string (ICC *state, const char *tag, const char *lang, const char *country)
+{
+ int offset, element_size;
+
+ if (!icc_tag (state, tag, &offset, &element_size))
+ return NULL;
+
+ if (!strcmp (state->data + offset, "mluc"))
+ {
+ return icc_decode_mluc (state, offset, element_size, lang, country);
+ }
+ else if (!strcmp (state->data + offset, "text"))
+ {
+ return strdup (state->data + offset + 8);
+ }
+ else if (!strcmp (state->data + offset, "desc"))
+ {
+ return strdup (state->data + offset + 12);
+ }
+ return NULL;
}
const Babl *
-babl_space_from_icc (const char *icc_data,
- int icc_length,
- const char **error)
+babl_space_from_icc (const char *icc_data,
+ int icc_length,
+ const char **error)
{
ICC *state = icc_state_new ((char*)icc_data, icc_length, 0);
int profile_size = icc_read (u32, 0);
@@ -632,8 +680,6 @@ babl_space_from_icc (const char *icc_data,
const Babl *trc_green = NULL;
const Babl *trc_blue = NULL;
const char *int_err;
- char *descr = NULL;
- char *copyright = NULL;
Babl *ret = NULL;
sign_t profile_class, color_space;
@@ -691,34 +737,6 @@ babl_space_from_icc (const char *icc_data,
return NULL;
}
- {
- int offset, element_size;
- icc_tag (state, "desc", &offset, &element_size);
- if (!strcmp (state->data + offset, "mluc"))
- {
- descr = icc_decode_mluc (state, offset, element_size, "en", NULL);
- }
- else
- if (!strcmp (state->data + offset, "desc"))
- {
- descr = babl_strdup (state->data + offset + 12);
- }
- }
-
- {
- int offset, element_size;
- icc_tag (state, "cprt", &offset, &element_size);
- if (!strcmp (state->data + offset, "mluc"))
- {
- copyright = icc_decode_mluc (state, offset, element_size, "en", NULL);
- }
- else
- if (!strcmp (state->data + offset, "desc"))
- {
- copyright = babl_strdup (state->data + offset + 8);
- }
- }
-
if (icc_tag (state, "rXYZ", NULL, NULL) &&
icc_tag (state, "gXYZ", NULL, NULL) &&
icc_tag (state, "bXYZ", NULL, NULL) &&
@@ -763,8 +781,6 @@ babl_space_from_icc (const char *icc_data,
ry, gy, by,
rz, gz, bz,
trc_red, trc_green, trc_blue);
- ret->space.description = descr;
- ret->space.copyright = copyright;
babl_free (state);
return ret;
@@ -813,8 +829,6 @@ babl_space_from_icc (const char *icc_data,
green_x, green_y,
blue_x, blue_y,
trc_red, trc_green, trc_blue);
- ret->space.description = descr;
- ret->space.copyright = copyright;
return ret;
}
}
@@ -847,3 +861,197 @@ static void symmetry_test (ICC *state)
assert (icc_read (u32, 8) == 4);
}
+char *babl_icc_get_key (const char *icc_data,
+ int icc_length,
+ const char *key,
+ const char *language,
+ const char *country)
+{
+ char *ret = NULL;
+ ICC *state = icc_state_new ((void*)icc_data, icc_length, 0);
+
+ if (!state)
+ return ret;
+
+ if (!strcmp (key, "copyright") ||
+ !strcmp (key, "cprt"))
+ {
+ ret = decode_string (state, "cprt", language, country);
+
+ } else if (!strcmp (key, "description") ||
+ !strcmp (key, "profileDescriptionTag") ||
+ !strcmp (key, "desc"))
+ {
+ ret = decode_string (state, "desc", language, country);
+
+ } else if (!strcmp (key, "manufacturer") ||
+ !strcmp (key, "deviceMfgDescTag") ||
+ !strcmp (key, "dmnd"))
+ {
+ ret = decode_string (state, "dmnd", language, country);
+
+ } else if (!strcmp (key, "device") ||
+ !strcmp (key, "deviceModelDescTag") ||
+ !strcmp (key, "dmdd"))
+ {
+ ret = decode_string (state, "dmdd", language, country);
+ } else if (!strcmp (key, "class") ||
+ !strcmp (key, "profile-class"))
+ {
+ sign_t tag = icc_read (sign, 12);
+ return strdup (tag.str);
+ } else if (!strcmp (key, "color-space"))
+ {
+ sign_t tag = icc_read (sign, 16);
+ return strdup (tag.str);
+ } else if (!strcmp (key, "pcs"))
+ {
+ sign_t tag = icc_read (sign, 20);
+ return strdup (tag.str);
+ } else if (!strcmp (key, "intent"))
+ {
+ char tag[5];
+ int val = icc_read (u32, 64);
+ sprintf (tag, "%i", val);
+ return strdup (tag);
+ } else if (!strcmp (key, "tags"))
+ {
+ char tag[4096]="NYI";
+ return strdup (tag);
+ }
+ babl_free (state);
+ return ret;
+}
+
+
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+ Sep 2017: copied only the bits neccesary for utf16toutf8 into babl,
+ otherwise unchanged from upstream.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+typedef uint32_t UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+static int ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags)
+{
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ UTF32 ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
diff --git a/babl/babl-space.h b/babl/babl-space.h
index 70ed535..6aeefb6 100644
--- a/babl/babl-space.h
+++ b/babl/babl-space.h
@@ -62,8 +62,6 @@ typedef struct
* standard should win.
*/
- char *description; /* meta data possibly read from ICC */
- char *copyright;
} BablSpace;
static inline void babl_space_to_xyzf (const Babl *space, const float *rgb, float *xyz)
diff --git a/babl/babl.h b/babl/babl.h
index 5d16b35..80f342b 100644
--- a/babl/babl.h
+++ b/babl/babl.h
@@ -95,22 +95,44 @@ const Babl * babl_space (const char *name);
*
* @icc_data: pointer to icc profile in memory
* @icc_length: length of icc profile in bytes
+ * @intent: the intent from the ICC profile to use.
+ *
" @error: pointer to a string where decoding errors can be stored,
* if an error occurs, NULL is returned and an error message
* is provided in error.
*
* Create a babl space from an in memory ICC profile, the
* profile does no longer need to be loaded for the space to work,
- * multiple calls with the same icc profile will result in the
- * same space.
+ * multiple calls with the same icc profile and same icc_transform
+ * will result in the same space.
*
* If a BablSpace cannot be created from the profile NULL is returned and a
* static string is set on the const char *value pointed at with &value
- * containing a message describing why the icc does not yield a babl space.
+ * containing a message describing why the provided data does not yield a babl
+ * space.
*/
-const Babl *babl_space_from_icc (const char *icc_data,
- int icc_length,
- const char **error);
+const Babl *babl_space_from_icc (const char *icc_data,
+ int icc_length,
+ const char **error);
+
+/* babl_icc_get_key:
+ *
+ * @icc_data: pointer to in-memory icc profile
+ * @icc_length: length of icc profile in bytes
+ * @key: the key we want to quey, see below for some supported values
+ * @language: 2 char code for language to extract or NULL
+ * @country: 2 char country code or NULL
+ *
+ * Returns NULL if key not found or a malloc allocated utf8 string of the key
+ * when found, free with free() when done. Supported keys: "description",
+ * "copyright", "manufacturer", "device", "profile-class", "color-space" and
+ * "pcs".
+ */
+char *babl_icc_get_key (const char *icc_data,
+ int icc_length,
+ const char *key,
+ const char *language,
+ const char *counter);
/* babl_space_to_icc:
diff --git a/tools/babl-icc-rewrite.c b/tools/babl-icc-rewrite.c
index 1d292b4..0746d50 100644
--- a/tools/babl-icc-rewrite.c
+++ b/tools/babl-icc-rewrite.c
@@ -36,6 +36,8 @@ main (int argc,
long icc_len;
int genlen;
const char *error;
+ const char *la = NULL;
+ const char *co = NULL;
babl_init ();
if (!argv[1] || !argv[2])
@@ -47,6 +49,36 @@ main (int argc,
if (file_get_contents (argv[1], &icc_data, &icc_len, NULL))
return -1;
+ {
+ char *description = babl_icc_get_key (icc_data, icc_len, "description", la, co);
+ if (description)
+ fprintf (stderr, "description: %s\n", description);
+ }
+
+ {
+ char *str = babl_icc_get_key (icc_data, icc_len, "copyright", la, co);
+ if (str)
+ {
+ fprintf (stderr, "copyright: %s\n", str);
+ free (str);
+ }
+ }
+ {
+ char *str = babl_icc_get_key (icc_data, icc_len, "device", la, co);
+ if (str)
+ {
+ fprintf (stderr, "device: %s\n", str);
+ free (str);
+ }
+ }
+ {
+ char *str = babl_icc_get_key (icc_data, icc_len, "manufacturer", la, co);
+ if (str)
+ {
+ fprintf (stderr, "manufacturer: %s\n", str);
+ free (str);
+ }
+ }
babl = babl_space_from_icc (icc_data, icc_len, &error);
free (icc_data);
if (error || !babl)
@@ -55,14 +87,6 @@ main (int argc,
return -1;
}
- {
- BablSpace *space = (void*)babl;
- if (space->description)
- fprintf (stderr, "description: %s\n", space->description);
- if (space->copyright)
- fprintf (stderr, "copyright: %s\n", space->copyright);
- }
-
icc_data = (char *)babl_space_to_icc (babl, &genlen);
if (icc_data)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]