From 446331df3b1cceac4ec631a4ea4f675197ea20e3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Mon, 19 Aug 2019 00:01:13 +0200 Subject: [PATCH] babl: add support for grayscale spaces A grayscale space is just like an RGB space, but has a default set of chromaticities for R,G,B. When ICC profiles are loaded only the TRC is considered is considered (we could also include the whitepoint), blackpoint tag is ignored (and should be baked into the used ICC profile instead.) This works with GIMP-2.10 but master of GIMP currently hangs when trying to load a grayscale jpeg with attached grayscale ICC profile, The if #0 on line 999 of babl/babl-icc.c needs to be turned into a 1 to enable grayscale icc profiles for further testing. --- babl/babl-icc.c | 186 +++++++++++++++++++++++++++++++++++++++++++--- babl/babl-space.c | 72 ++++++++++++++++++ babl/babl-space.h | 11 ++- babl/babl.h | 1 + export-symbols | 1 + 5 files changed, 257 insertions(+), 14 deletions(-) diff --git a/babl/babl-icc.c b/babl/babl-icc.c index e371977..9808c75 100644 --- a/babl/babl-icc.c +++ b/babl/babl-icc.c @@ -561,12 +561,13 @@ switch (trc->type) static void symmetry_test (ICC *state); -char * -babl_space_to_icc (const Babl *babl, - const char *description, - const char *copyright, - BablICCFlags flags, - int *ret_length) + +static char * +babl_space_to_icc_rgb (const Babl *babl, + const char *description, + const char *copyright, + BablICCFlags flags, + int *ret_length) { const BablSpace *space = &babl->space; char icc[65536]; @@ -691,6 +692,119 @@ babl_space_to_icc (const Babl *babl, } } + +static char * +babl_space_to_icc_gray (const Babl *babl, + const char *description, + const char *copyright, + BablICCFlags flags, + int *ret_length) +{ + const BablSpace *space = &babl->space; + char icc[65536]; + int length=65535; + ICC *state = icc_state_new (icc, length, 10); + + icc[length]=0; + + symmetry_test (state); + + icc_write (sign, 4, "babl"); // ICC verison + icc_write (u8, 8, 2); // ICC verison + icc_write (u8, 9, 0x20); // 2.2 for now.. + icc_write (u32,64, 0); // rendering intent + + icc_write (s15f16,68, 0.96421); // Illuminant + icc_write (s15f16,72, 1.0); + icc_write (s15f16,76, 0.82491); + + icc_write (sign, 80, "babl"); // creator + + icc_write (sign, 12, "mntr"); + icc_write (sign, 16, "GRAY"); + icc_write (sign, 20, "XYZ "); + + icc_write (u16, 24, 2222); // babl profiles + icc_write (u16, 26, 11); // should + icc_write (u16, 28, 11); // use a fixed + icc_write (u16, 30, 3); // date + icc_write (u16, 32, 44); // that gets updated + icc_write (u16, 34, 55); // when the generator changes + + icc_write (sign, 36, "acsp"); // changes + + { + state->tags = 6; /* note: we could reserve a couple of spots and + still use a very simple allocator and + still be valid - albeit with tiny waste of + space. + */ + state->no = state->o = 128 + 4 + 12 * state->tags; + + icc_write (u32, 128, state->tags); + + icc_allocate_tag (state, "wtpt", 20); + icc_write (sign, state->o, "XYZ "); + icc_write (u32, state->o + 4, 0); + icc_write (s15f16, state->o + 8, space->whitepoint[0]); + icc_write (s15f16, state->o + 12, space->whitepoint[1]); + icc_write (s15f16, state->o + 16, space->whitepoint[2]); + + write_trc (state, "kTRC", &space->trc[0]->trc, flags); + + { + char str[128]="CC0/public domain"; + int i; + if (!copyright) copyright = str; + icc_allocate_tag(state, "cprt", 8 + strlen (copyright) + 1); + icc_write (sign, state->o, "text"); + icc_write (u32, state->o + 4, 0); + for (i = 0; copyright[i]; i++) + icc_write (u8, state->o + 8 + i, copyright[i]); + } + { + char str[128]="babl"; + int i; + if (!description) description = str; + icc_allocate_tag(state, "desc", 90 + strlen (description) + 0); + icc_write (sign, state->o,"desc"); + icc_write (u32, state->o + 4, 0); + icc_write (u32, state->o + 8, strlen(description) + 1); + for (i = 0; description[i]; i++) + icc_write (u8, state->o + 12 + i, description[i]); + } + icc_write (u32, 0, state->no + 0); + length = state->no + 0; + } + + if (ret_length) + *ret_length = length; + + babl_free (state); + { + char *ret = malloc (length); + memcpy (ret, icc, length); + return ret; + } +} + +char * +babl_space_to_icc (const Babl *babl, + const char *description, + const char *copyright, + BablICCFlags flags, + int *ret_length) +{ + if (babl->space.icc_type == BablICCTypeRGB) + return babl_space_to_icc_rgb (babl, description, copyright, flags, + ret_length); + else if (babl->space.icc_type == BablICCTypeGray) + return babl_space_to_icc_gray (babl, description, copyright, flags, + ret_length); + fprintf (stderr, "unexpected icc type in %s\n", __FUNCTION__); + return NULL; +} + const char * babl_space_get_icc (const Babl *babl, int *length) @@ -819,9 +933,11 @@ babl_space_from_icc (const char *icc_data, const Babl *trc_red = NULL; const Babl *trc_green = NULL; const Babl *trc_blue = NULL; + const Babl *trc_gray = NULL; const char *int_err; Babl *ret = NULL; int speed_over_accuracy = intent & BABL_ICC_INTENT_PERFORMANCE; + int is_gray = 0; sign_t profile_class, color_space, pcs; @@ -842,7 +958,6 @@ babl_space_from_icc (const char *icc_data, ret = _babl_space_for_lcms (icc_data, icc_length); if (ret->space.icc_type == BablICCTypeCMYK) return ret; - ret->space.icc_type = BablICCTypeCMYK; ret->space.icc_length = icc_length; ret->space.icc_profile = malloc (icc_length); memcpy (ret->space.icc_profile, icc_data, icc_length); @@ -877,12 +992,23 @@ babl_space_from_icc (const char *icc_data, return ret; } - if (strcmp (color_space.str, "RGB ")) - *error = "not defining an RGB space"; + + + + if (strcmp (color_space.str, "RGB ") +#if 0 /* XXX: commented out, as gimp-2.99 doesn't like loading grayscale jpegs with grayscale icc profiles when it is enabled */ + && strcmp (color_space.str, "GRAY") +#endif + ) + { + *error = "not defining RGB, CMYK or GRAY space.."; + } else { if (strcmp (profile_class.str, "mntr")) *error = "not a monitor-class profile"; + if (!strcmp (color_space.str, "GRAY")) + is_gray = 1; } } @@ -946,11 +1072,25 @@ babl_space_from_icc (const char *icc_data, { trc_blue = babl_trc_from_icc (state, offset, error); } + if (!*error && icc_tag (state, "kTRC", &offset, &element_size)) + { + trc_gray = babl_trc_from_icc (state, offset, error); + } } - if (!*error && (!trc_red || !trc_green || !trc_blue)) + if (is_gray) { - *error = "missing TRCs"; + if (!*error && (!trc_gray)) + { + *error = "missing TRC"; + } + } + else + { + if (!*error && (!trc_red || !trc_green || !trc_blue)) + { + *error = "missing TRCs"; + } } if (*error) @@ -960,6 +1100,27 @@ babl_space_from_icc (const char *icc_data, return NULL; } + if (is_gray) + { + int offset, element_size; + if (icc_tag (state, "wtpt", &offset, &element_size)) + { + // wX = icc_read (s15f16, offset + 8); + // wY = icc_read (s15f16, offset + 8 + 4); + // wZ = icc_read (s15f16, offset + 8 + 4 * 2); + } + ret = (void*)babl_space_from_gray_trc (NULL, trc_gray, 1); + ret->space.icc_length = icc_length; + ret->space.icc_profile = malloc (icc_length); + memcpy (ret->space.icc_profile, icc_data, icc_length); + babl_free (state); + return ret; + + + *error = "gray parsing NYI"; + } + else + { if (icc_tag (state, "rXYZ", NULL, NULL) && icc_tag (state, "gXYZ", NULL, NULL) && icc_tag (state, "bXYZ", NULL, NULL) && @@ -1076,8 +1237,9 @@ babl_space_from_icc (const char *icc_data, return ret; } } - *error = "didnt find RGB primaries"; + } + babl_free (state); return NULL; } diff --git a/babl/babl-space.c b/babl/babl-space.c index 7a8d7b1..c05796c 100644 --- a/babl/babl-space.c +++ b/babl/babl-space.c @@ -243,6 +243,7 @@ _babl_space_for_lcms (const char *icc_data, memset (&space, 0, sizeof(space)); space.instance.class_type = BABL_SPACE; space.instance.id = 0; + space.icc_type = BablICCTypeCMYK; if (i >= MAX_SPACES-1) { @@ -292,12 +293,14 @@ babl_space_from_rgbxyz_matrix (const char *name, space.RGBtoXYZ[6] = rz; space.RGBtoXYZ[7] = gz; space.RGBtoXYZ[8] = bz; + space.icc_type = BablICCTypeRGB; babl_matrix_invert (space.RGBtoXYZ, space.XYZtoRGB); babl_matrix_to_float (space.RGBtoXYZ, space.RGBtoXYZf); babl_matrix_to_float (space.XYZtoRGB, space.XYZtoRGBf); + /* recover chromaticities from matrix */ { double red[3]={1.,.0,.0}; double xyz[3]={1.,.0,.0}; @@ -392,6 +395,7 @@ babl_space_from_chromaticities (const char *name, space.whitepoint[0] = wx / wy; space.whitepoint[1] = 1.0; space.whitepoint[2] = (1.0 - wx - wy) / wy; + space.icc_type = BablICCTypeRGB; for (i = 0; space_db[i].instance.class_type; i++) { @@ -426,6 +430,67 @@ babl_space_from_chromaticities (const char *name, return (Babl*)&space_db[i]; } +const Babl * +babl_space_from_gray_trc (const char *name, + const Babl *trc_gray, + BablSpaceFlags flags) +{ + int i=0; + BablSpace space = {0,}; + space.instance.class_type = BABL_SPACE; + space.instance.id = 0; + + space.xw = 0.3127; + space.yw = 0.3290; + + space.xr = 0.639998686; + space.yr = 0.330010138; + space.xg = 0.300003784; + space.yg = 0.600003357; + space.xb = 0.150002046; + space.yb = 0.059997204; + space.trc[0] = trc_gray; + space.trc[1] = trc_gray; + space.trc[2] = trc_gray; + + space.whitepoint[0] = space.xw / space.yw; + space.whitepoint[1] = 1.0; + space.whitepoint[2] = (1.0 - space.xw - space.yw) / space.yw; + space.icc_type = BablICCTypeGray; + + for (i = 0; space_db[i].instance.class_type; i++) + { + int offset = ((char*)&space_db[i].xr) - (char*)(&space_db[i]); + int size = ((char*)&space_db[i].trc) + sizeof(space_db[i].trc) - ((char*)&space_db[i].xr); + + if (memcmp ((char*)(&space_db[i]) + offset, ((char*)&space) + offset, size)==0) + { + return (void*)&space_db[i]; + } + } + if (i >= MAX_SPACES-1) + { + babl_log ("too many BablSpaces"); + return NULL; + } + space_db[i]=space; + space_db[i].instance.name = space_db[i].name; + if (name) + snprintf (space_db[i].name, sizeof (space_db[i].name), "%s", name); + else + /* XXX: this can get longer than 256bytes ! */ + snprintf (space_db[i].name, sizeof (space_db[i].name), + "space-gray-%s", babl_get_name(space.trc[0])); + + /* compute matrixes */ + babl_space_compute_matrices (&space_db[i], 1); + + //babl_space_get_icc ((Babl*)&space_db[i], NULL); + return (Babl*)&space_db[i]; + +} + + void babl_space_class_for_each (BablEachFunction each_fun, void *user_data) @@ -1309,6 +1374,13 @@ babl_space_is_cmyk (const Babl *space) return space?space->space.icc_type == BablICCTypeCMYK:0; } +int +babl_space_is_gray (const Babl *space) +{ + return space?space->space.icc_type == BablICCTypeGray:0; +} + + /* Trademarks: * * International Color Consortium is a registered trademarks of the. diff --git a/babl/babl-space.h b/babl/babl-space.h index 1d9934a..86692e9 100644 --- a/babl/babl-space.h +++ b/babl/babl-space.h @@ -111,10 +111,14 @@ typedef struct double xb; // blue primary chromaticity double yb; + BablICCType icc_type; /* taken into account when looking for duplicate spaces*/ + double whitepoint[3]; /* CIE XYZ whitepoint */ const Babl *trc[3]; + + /* ------------- end of dedup zone -------------- */ + char name[512]; // XXX: allocate this dynamically instead - // or use iccv4 style hashes for name. - double whitepoint[3]; /* CIE XYZ whitepoint */ double RGBtoXYZ[9]; /* matrices for conversions */ double XYZtoRGB[9]; @@ -131,7 +135,6 @@ typedef struct */ char *icc_profile; int icc_length; - BablICCType icc_type; BablCMYK cmyk; } BablSpace; @@ -163,6 +166,10 @@ static inline void _babl_space_from_xyz (const Babl *space, const double *xyz, d void babl_space_class_init (void); +const Babl * +babl_space_from_gray_trc (const char *name, + const Babl *trc_gray, + BablSpaceFlags flags); #endif diff --git a/babl/babl.h b/babl/babl.h index 25e5aec..3c025bb 100644 --- a/babl/babl.h +++ b/babl/babl.h @@ -704,6 +704,7 @@ babl_space_from_rgbxyz_matrix (const char *name, const char * babl_format_get_encoding (const Babl *babl); int babl_space_is_cmyk (const Babl *space); +int babl_space_is_gray (const Babl *space); /* values below this are stored associated with this value, it should also be * used as a generic alpha zero epsilon in GEGL to keep the threshold effects diff --git a/export-symbols b/export-symbols index d692f46..9123e7e 100644 --- a/export-symbols +++ b/export-symbols @@ -55,6 +55,7 @@ babl_space_from_xyz babl_space_to_icc babl_space_with_trc babl_space_is_cmyk +babl_space_is_gray babl_icc_make_space babl_icc_get_key babl_ticks -- 2.30.2