Merge branch 'matthiasc/for-main' into 'main'

image-tool: Allow specifying cicp tuples

See merge request GNOME/gtk!7511
This commit is contained in:
Matthias Clasen 2024-07-27 23:45:21 +00:00
commit 2ee1f3e8cf
9 changed files with 220 additions and 149 deletions

View File

@ -70,6 +70,11 @@ The ``convert`` command converts the image to a different format or color state.
Convert to the given color state. The supported color states can be Convert to the given color state. The supported color states can be
listed with ``--format=list``. listed with ``--format=list``.
``--cicp=CICP``
Convert to a color state that is specified as a cicp tuple. The cicp tuple
must be specified as four numbers, separated by /, e.g. 1/13/6/0.
Relabeling Relabeling
^^^^^^^^^^ ^^^^^^^^^^
@ -78,5 +83,10 @@ This can be useful to produce wrong color renderings for diagnostics.
``--color-state=COLORSTATE`` ``--color-state=COLORSTATE``
Convert to the given color state. The supported color states can be Relabel to the given color state. The supported color states can be
listed with ``--format=list``. listed with ``--format=list``.
``--cicp=CICP``
Relabel to a color state that is specified as a cicp tuple. The cicp tuple
must be specified as four numbers, separated by /, e.g. 1/13/6/0.

View File

@ -119,76 +119,82 @@ hlg_oetf (float v)
* for how to derive the abc_to_xyz matrices from chromaticity coordinates. * for how to derive the abc_to_xyz matrices from chromaticity coordinates.
*/ */
static const float srgb_to_xyz[3][3] = { static const float identity[9] = {
{ 0.4124564, 0.3575761, 0.1804375 }, 1, 0, 0,
{ 0.2126729, 0.7151522, 0.0721750 }, 0, 1, 0,
{ 0.0193339, 0.1191920, 0.9503041 } 0, 0, 1,
}; };
static const float xyz_to_srgb[3][3] = { static const float srgb_to_xyz[9] = {
{ 3.2404542, -1.5371385, -0.4985314 }, 0.4124564, 0.3575761, 0.1804375,
{ -0.9692660, 1.8760108, 0.0415560 }, 0.2126729, 0.7151522, 0.0721750,
{ 0.0556434, -0.2040259, 1.0572252 }, 0.0193339, 0.1191920, 0.9503041,
}; };
static const float rec2020_to_xyz[3][3] = { static const float xyz_to_srgb[9] = {
{ 0.6369580, 0.1446169, 0.1688810 }, 3.2404542, -1.5371385, -0.4985314,
{ 0.2627002, 0.6779981, 0.0593017 }, -0.9692660, 1.8760108, 0.0415560,
{ 0.0000000, 0.0280727, 1.0609851 } 0.0556434, -0.2040259, 1.0572252,
}; };
static const float xyz_to_rec2020[3][3] = { static const float rec2020_to_xyz[9] = {
{ 1.7166512, -0.3556708, -0.2533663 }, 0.6369580, 0.1446169, 0.1688810,
{ -0.6666844, 1.6164812, 0.0157685 }, 0.2627002, 0.6779981, 0.0593017,
{ 0.0176399, -0.0427706, 0.9421031 }, 0.0000000, 0.0280727, 1.0609851,
}; };
static const float pal_to_xyz[3][3] = { static const float xyz_to_rec2020[9] = {
{ 0.4305538, 0.3415498, 0.1783523 }, 1.7166512, -0.3556708, -0.2533663,
{ 0.2220043, 0.7066548, 0.0713409 }, -0.6666844, 1.6164812, 0.0157685,
{ 0.0201822, 0.1295534, 0.9393222 }, 0.0176399, -0.0427706, 0.9421031,
}; };
static const float xyz_to_pal[3][3] = { static const float pal_to_xyz[9] = {
{ 3.0633611, -1.3933902, -0.4758237 }, 0.4305538, 0.3415498, 0.1783523,
{ -0.9692436, 1.8759675, 0.0415551 }, 0.2220043, 0.7066548, 0.0713409,
{ 0.0678610, -0.2287993, 1.0690896 }, 0.0201822, 0.1295534, 0.9393222,
}; };
static const float ntsc_to_xyz[3][3] = { static const float xyz_to_pal[9] = {
{ 0.3935209, 0.3652581, 0.1916769 }, 3.0633611, -1.3933902, -0.4758237,
{ 0.2123764, 0.7010599, 0.0865638 }, -0.9692436, 1.8759675, 0.0415551,
{ 0.0187391, 0.1119339, 0.9583847 }, 0.0678610, -0.2287993, 1.0690896,
}; };
static const float xyz_to_ntsc[3][3] = { static const float ntsc_to_xyz[9] = {
{ 3.5060033, -1.7397907, -0.5440583 }, 0.3935209, 0.3652581, 0.1916769,
{ -1.0690476, 1.9777789, 0.0351714 }, 0.2123764, 0.7010599, 0.0865638,
{ 0.0563066, -0.1969757, 1.0499523 }, 0.0187391, 0.1119339, 0.9583847,
}; };
static const float p3_to_xyz[3][3] = { static const float xyz_to_ntsc[9] = {
{ 0.4865709, 0.2656677, 0.1982173 }, 3.5060033, -1.7397907, -0.5440583,
{ 0.2289746, 0.6917385, 0.0792869 }, -1.0690476, 1.9777789, 0.0351714,
{ 0.0000000, 0.0451134, 1.0439444 }, 0.0563066, -0.1969757, 1.0499523,
}; };
static const float xyz_to_p3[3][3] = { static const float p3_to_xyz[9] = {
{ 2.4934969, -0.9313836, -0.4027108 }, 0.4865709, 0.2656677, 0.1982173,
{ -0.8294890, 1.7626641, 0.0236247 }, 0.2289746, 0.6917385, 0.0792869,
{ 0.0358458, -0.0761724, 0.9568845 }, 0.0000000, 0.0451134, 1.0439444,
};
static const float xyz_to_p3[9] = {
2.4934969, -0.9313836, -0.4027108,
-0.8294890, 1.7626641, 0.0236247,
0.0358458, -0.0761724, 0.9568845,
}; };
/* premultiplied matrices for default conversions */ /* premultiplied matrices for default conversions */
static const float rec2020_to_srgb[3][3] = { static const float rec2020_to_srgb[9] = {
{ 1.660227, -0.587548, -0.072838 }, 1.660227, -0.587548, -0.072838,
{ -0.124553, 1.132926, -0.008350 }, -0.124553, 1.132926, -0.008350,
{ -0.018155, -0.100603, 1.118998 }, -0.018155, -0.100603, 1.118998,
}; };
static const float srgb_to_rec2020[3][3] = { static const float srgb_to_rec2020[9] = {
{ 0.627504, 0.329275, 0.043303 }, 0.627504, 0.329275, 0.043303,
{ 0.069108, 0.919519, 0.011360 }, 0.069108, 0.919519, 0.011360,
{ 0.016394, 0.088011, 0.895380 }, 0.016394, 0.088011, 0.895380,
}; };

View File

@ -223,9 +223,9 @@ gdk_color_state_create_cicp_params (GdkColorState *self)
/* {{{ Conversion functions */ /* {{{ Conversion functions */
typedef float (* GdkTransferFunc) (float v); typedef float (* GdkTransferFunc) (float v);
typedef const float GdkColorMatrix[3][3]; typedef const float GdkColorMatrix[9];
#define IDENTITY ((float**)0) #define IDENTITY ((float*)0)
#define NONE ((GdkTransferFunc)0) #define NONE ((GdkTransferFunc)0)
#define TRANSFORM(name, eotf, matrix, oetf) \ #define TRANSFORM(name, eotf, matrix, oetf) \
@ -242,12 +242,12 @@ name (GdkColorState *self, \
values[i][1] = eotf (values[i][1]); \ values[i][1] = eotf (values[i][1]); \
values[i][2] = eotf (values[i][2]); \ values[i][2] = eotf (values[i][2]); \
} \ } \
if ((float **)matrix != IDENTITY) \ if (matrix != IDENTITY) \
{ \ { \
float res[3]; \ float res[3]; \
res[0] = matrix[0][0] * values[i][0] + matrix[0][1] * values[i][1] + matrix[0][2] * values[i][2]; \ res[0] = matrix[0] * values[i][0] + matrix[1] * values[i][1] + matrix[2] * values[i][2]; \
res[1] = matrix[1][0] * values[i][0] + matrix[1][1] * values[i][1] + matrix[1][2] * values[i][2]; \ res[1] = matrix[3] * values[i][0] + matrix[4] * values[i][1] + matrix[5] * values[i][2]; \
res[2] = matrix[2][0] * values[i][0] + matrix[2][1] * values[i][1] + matrix[2][2] * values[i][2]; \ res[2] = matrix[6] * values[i][0] + matrix[7] * values[i][1] + matrix[8] * values[i][2]; \
values[i][0] = res[0]; \ values[i][0] = res[0]; \
values[i][1] = res[1]; \ values[i][1] = res[1]; \
values[i][2] = res[2]; \ values[i][2] = res[2]; \
@ -426,10 +426,10 @@ struct _GdkCicpColorState
GdkTransferFunc eotf; GdkTransferFunc eotf;
GdkTransferFunc oetf; GdkTransferFunc oetf;
float to_srgb[3][3]; float *to_srgb;
float to_rec2020[3][3]; float *to_rec2020;
float from_srgb[3][3]; float *from_srgb;
float from_rec2020[3][3]; float *from_rec2020;
GdkCicp cicp; GdkCicp cicp;
}; };
@ -449,7 +449,8 @@ TRANSFORM(gdk_cicp_from_rec2100_linear, NONE, cicp->from_rec2020, cicp->o
#undef cicp #undef cicp
/* }}} */ /* }}} */
/* }}} */
/* {{{ Vfuncs */ /* {{{ Vfuncs */
static void static void
@ -460,6 +461,11 @@ gdk_cicp_color_state_free (GdkColorState *cs)
if (self->no_srgb) if (self->no_srgb)
gdk_color_state_unref (self->no_srgb); gdk_color_state_unref (self->no_srgb);
g_free (self->to_srgb);
g_free (self->to_rec2020);
g_free (self->from_srgb);
g_free (self->from_rec2020);
g_free (self); g_free (self);
} }
@ -549,7 +555,7 @@ gdk_cicp_color_state_get_cicp (GdkColorState *color_state)
return &self->cicp; return &self->cicp;
} }
/* }}} */ /* }}} */
static const static const
GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = { GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = {
@ -562,19 +568,19 @@ GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = {
.get_cicp = gdk_cicp_color_state_get_cicp, .get_cicp = gdk_cicp_color_state_get_cicp,
}; };
static inline void static inline float *
multiply (float res[3][3], multiply (float res[9],
const float m1[3][3], const float m1[9],
const float m2[3][3]) const float m2[9])
{ {
if ((float **) m1 == IDENTITY) #define IDX(i,j) 3*i+j
memcpy (res, m2, sizeof (float) * 3 * 3); for (int i = 0; i < 3; i++)
else if ((float **) m2 == IDENTITY) for (int j = 0; j < 3; j++)
memcpy (res, m1, sizeof (float) * 3 * 3); res[IDX(i,j)] = m1[IDX(i,0)] * m2[IDX(0,j)]
else + m1[IDX(i,1)] * m2[IDX(1,j)]
for (int i = 0; i < 3; i++) + m1[IDX(i,2)] * m2[IDX(2,j)];
for (int j = 0; j < 3; j++)
res[i][j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] + m1[i][2] * m2[2][j]; return res;
} }
GdkColorState * GdkColorState *
@ -663,8 +669,8 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
from_xyz = xyz_to_rec2020; from_xyz = xyz_to_rec2020;
break; break;
case 10: case 10:
to_xyz = IDENTITY; to_xyz = identity;
from_xyz = IDENTITY; from_xyz = identity;
break; break;
case 12: case 12:
to_xyz = p3_to_xyz; to_xyz = p3_to_xyz;
@ -693,10 +699,10 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
self->eotf = eotf; self->eotf = eotf;
self->oetf = oetf; self->oetf = oetf;
multiply (self->to_srgb, xyz_to_srgb, to_xyz); self->to_srgb = multiply (g_new (float, 9), xyz_to_srgb, to_xyz);
multiply (self->to_rec2020, xyz_to_rec2020, to_xyz); self->to_rec2020 = multiply (g_new (float, 9), xyz_to_rec2020, to_xyz);
multiply (self->from_srgb, from_xyz, srgb_to_xyz); self->from_srgb = multiply (g_new (float, 9), from_xyz, srgb_to_xyz);
multiply (self->from_rec2020, from_xyz, rec2020_to_xyz); self->from_rec2020 = multiply (g_new (float, 9), from_xyz, rec2020_to_xyz);
self->name = g_strdup_printf ("cicp-%u/%u/%u/%u", self->name = g_strdup_printf ("cicp-%u/%u/%u/%u",
cicp->color_primaries, cicp->color_primaries,

View File

@ -78,31 +78,6 @@ output_message_handler (j_common_ptr cinfo)
/* }}} */ /* }}} */
/* {{{ Format conversion */ /* {{{ Format conversion */
static void
convert_grayscale_to_rgb (guchar *data,
int width,
int height,
int stride)
{
gsize x, y;
guchar *dest, *src;
for (y = 0; y < height; y++)
{
src = data + width;
dest = data + 3 * width;
for (x = 0; x < width; x++)
{
dest -= 3;
src -= 1;
dest[0] = *src;
dest[1] = *src;
dest[2] = *src;
}
data += stride;
}
}
static void static void
convert_cmyk_to_rgba (guchar *data, convert_cmyk_to_rgba (guchar *data,
int width, int width,
@ -184,6 +159,10 @@ gdk_load_jpeg (GBytes *input_bytes,
switch ((int)info.out_color_space) switch ((int)info.out_color_space)
{ {
case JCS_GRAYSCALE: case JCS_GRAYSCALE:
stride = width;
data = g_try_malloc_n (stride, height);
format = GDK_MEMORY_G8;
break;
case JCS_RGB: case JCS_RGB:
stride = 3 * width; stride = 3 * width;
data = g_try_malloc_n (stride, height); data = g_try_malloc_n (stride, height);
@ -217,20 +196,8 @@ gdk_load_jpeg (GBytes *input_bytes,
jpeg_read_scanlines (&info, row, 1); jpeg_read_scanlines (&info, row, 1);
} }
switch ((int)info.out_color_space) if (info.out_color_space == JCS_CMYK)
{ convert_cmyk_to_rgba (data, width, height, stride);
case JCS_GRAYSCALE:
convert_grayscale_to_rgb (data, width, height, stride);
format = GDK_MEMORY_R8G8B8;
break;
case JCS_RGB:
break;
case JCS_CMYK:
convert_cmyk_to_rgba (data, width, height, stride);
break;
default:
g_assert_not_reached ();
}
jpeg_finish_decompress (&info); jpeg_finish_decompress (&info);
jpeg_destroy_decompress (&info); jpeg_destroy_decompress (&info);
@ -307,6 +274,7 @@ gdk_save_jpeg (GdkTexture *texture)
gdk_texture_downloader_init (&downloader, texture); gdk_texture_downloader_init (&downloader, texture);
gdk_texture_downloader_set_format (&downloader, GDK_MEMORY_R8G8B8); gdk_texture_downloader_set_format (&downloader, GDK_MEMORY_R8G8B8);
gdk_texture_downloader_set_color_state (&downloader, GDK_COLOR_STATE_SRGB);
texbytes = gdk_texture_downloader_download_bytes (&downloader, &texstride); texbytes = gdk_texture_downloader_download_bytes (&downloader, &texstride);
gdk_texture_downloader_finish (&downloader); gdk_texture_downloader_finish (&downloader);
texdata = g_bytes_get_data (texbytes, NULL); texdata = g_bytes_get_data (texbytes, NULL);

View File

@ -33,7 +33,7 @@ test_transfer (gconstpointer data)
} }
} }
typedef const float Matrix[3][3]; typedef const float Matrix[9];
typedef struct typedef struct
{ {
@ -50,59 +50,54 @@ static MatrixTest matrices[] = {
{ "srgb<>rec2020", &rec2020_to_srgb, &srgb_to_rec2020 }, { "srgb<>rec2020", &rec2020_to_srgb, &srgb_to_rec2020 },
}; };
#define IDX(i,j) 3*i+j
static inline void static inline void
multiply (float res[3][3], multiply (float res[9],
const float m1[3][3], const float m1[9],
const float m2[3][3]) const float m2[9])
{ {
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
res[i][j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] + m1[i][2] * m2[2][j]; res[IDX(i,j)] = m1[IDX(i,0)] * m2[IDX(0,j)]
+ m1[IDX(i,1)] * m2[IDX(1,j)]
+ m1[IDX(i,2)] * m2[IDX(2,j)];
} }
static inline void static inline void
difference (float res[3][3], difference (float res[9],
const float m1[3][3], const float m1[9],
const float m2[3][3]) const float m2[9])
{ {
for (int i = 0; i < 3; i++) for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++) res[i] = m1[i] - m2[i];
res[i][j] = m1[i][j] - m2[i][j];
} }
static float static float
norm (const float m[3][3]) norm (const float m[9])
{ {
float sum = 0; float sum = 0;
for (int i = 0; i < 3; i++) for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++) sum += m[i] * m[i];
sum += m[i][j] * m[i][j];
return sqrtf (sum); return sqrtf (sum);
} }
static const float identity[3][3] = {
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 },
};
static void static void
print_matrix (const float m[3][3]) print_matrix (const float m[9])
{ {
g_print ("%f %f %f\n%f %f %f\n%f %f %f\n", g_print ("%f %f %f\n%f %f %f\n%f %f %f\n",
m[0][0], m[0][1], m[0][2], m[0], m[1], m[2],
m[1][0], m[1][1], m[1][2], m[3], m[4], m[5],
m[2][0], m[2][1], m[2][2]); m[6], m[7], m[8]);
} }
static void static void
test_matrix (gconstpointer data) test_matrix (gconstpointer data)
{ {
MatrixTest *matrix = (MatrixTest *) data; MatrixTest *matrix = (MatrixTest *) data;
float res[3][3]; float res[9];
float res2[3][3]; float res2[9];
multiply (res, *matrix->to_xyz, *matrix->from_xyz); multiply (res, *matrix->to_xyz, *matrix->from_xyz);
@ -120,7 +115,7 @@ test_matrix (gconstpointer data)
static void static void
test_srgb_to_rec2020 (void) test_srgb_to_rec2020 (void)
{ {
float m[3][3], res[3][3]; float m[9], res[9];
multiply (m, xyz_to_rec2020, srgb_to_xyz); multiply (m, xyz_to_rec2020, srgb_to_xyz);
difference (res, m, srgb_to_rec2020); difference (res, m, srgb_to_rec2020);
@ -131,7 +126,7 @@ test_srgb_to_rec2020 (void)
static void static void
test_rec2020_to_srgb (void) test_rec2020_to_srgb (void)
{ {
float m[3][3], res[3][3]; float m[9], res[9];
multiply (m, xyz_to_srgb, rec2020_to_xyz); multiply (m, xyz_to_srgb, rec2020_to_xyz);
difference (res, m, rec2020_to_srgb); difference (res, m, rec2020_to_srgb);

View File

@ -81,15 +81,17 @@ do_convert (int *argc,
char **filenames = NULL; char **filenames = NULL;
char *format_name = NULL; char *format_name = NULL;
char *colorstate_name = NULL; char *colorstate_name = NULL;
char *cicp_tuple = NULL;
const GOptionEntry entries[] = { const GOptionEntry entries[] = {
{ "format", 0, 0, G_OPTION_ARG_STRING, &format_name, N_("Format to use"), N_("FORMAT") }, { "format", 0, 0, G_OPTION_ARG_STRING, &format_name, N_("Format to use"), N_("FORMAT") },
{ "color-state", 0, 0, G_OPTION_ARG_STRING, &colorstate_name, N_("Color state to use"), N_("COLORSTATE") }, { "color-state", 0, 0, G_OPTION_ARG_STRING, &colorstate_name, N_("Color state to use"), N_("COLORSTATE") },
{ "cicp", 0, 0, G_OPTION_ARG_STRING, &cicp_tuple, N_("Color state to use, as cicp tuple"), N_("CICP") },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") },
{ NULL, } { NULL, }
}; };
GError *error = NULL; GError *error = NULL;
GdkMemoryFormat format = GDK_MEMORY_DEFAULT; GdkMemoryFormat format = GDK_MEMORY_DEFAULT;
GdkColorState *color_state = gdk_color_state_get_srgb (); GdkColorState *color_state = NULL;
g_set_prgname ("gtk4-image-tool convert"); g_set_prgname ("gtk4-image-tool convert");
context = g_option_context_new (NULL); context = g_option_context_new (NULL);
@ -151,6 +153,26 @@ do_convert (int *argc,
} }
} }
if (cicp_tuple)
{
if (color_state)
{
g_printerr (_("Can't specify both --color-state and --cicp\n"));
exit (1);
}
color_state = parse_cicp_tuple (cicp_tuple, &error);
if (!color_state)
{
g_printerr (_("Not a supported cicp tuple: %s\n"), error->message);
exit (1);
}
}
if (!color_state)
color_state = gdk_color_state_get_srgb ();
save_image (filenames[0], filenames[1], format, color_state); save_image (filenames[0], filenames[1], format, color_state);
g_strfreev (filenames); g_strfreev (filenames);

View File

@ -79,13 +79,15 @@ do_relabel (int *argc,
GOptionContext *context; GOptionContext *context;
char **filenames = NULL; char **filenames = NULL;
char *colorstate_name = NULL; char *colorstate_name = NULL;
char *cicp_tuple = NULL;
const GOptionEntry entries[] = { const GOptionEntry entries[] = {
{ "color-state", 0, 0, G_OPTION_ARG_STRING, &colorstate_name, N_("Color state to use"), N_("COLORSTATE") }, { "color-state", 0, 0, G_OPTION_ARG_STRING, &colorstate_name, N_("Color state to use"), N_("COLORSTATE") },
{ "cicp", 0, 0, G_OPTION_ARG_STRING, &cicp_tuple, N_("Color state to use, as cicp tuple"), N_("CICP") },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") },
{ NULL, } { NULL, }
}; };
GError *error = NULL; GError *error = NULL;
GdkColorState *color_state = gdk_color_state_get_srgb (); GdkColorState *color_state = NULL;
g_set_prgname ("gtk4-image-tool relabel"); g_set_prgname ("gtk4-image-tool relabel");
context = g_option_context_new (NULL); context = g_option_context_new (NULL);
@ -131,6 +133,26 @@ do_relabel (int *argc,
} }
} }
if (cicp_tuple)
{
if (color_state)
{
g_printerr (_("Can't specify both --color-state and --cicp\n"));
exit (1);
}
color_state = parse_cicp_tuple (cicp_tuple, &error);
if (!color_state)
{
g_printerr (_("Not a supported cicp tuple: %s\n"), error->message);
exit (1);
}
}
if (!color_state)
color_state = gdk_color_state_get_srgb ();
relabel_image (filenames[0], filenames[1], color_state); relabel_image (filenames[0], filenames[1], color_state);
g_strfreev (filenames); g_strfreev (filenames);

View File

@ -221,3 +221,42 @@ get_color_state_name (GdkColorState *color_state)
return name; return name;
} }
GdkColorState *
parse_cicp_tuple (const char *cicp_tuple,
GError **error)
{
char **tokens;
guint64 num[4];
GdkCicpParams *params;
GdkColorState *color_state;
tokens = g_strsplit (cicp_tuple, "/", 0);
if (g_strv_length (tokens) != 4 ||
!g_ascii_string_to_unsigned (tokens[0], 10, 0, 255, &num[0], NULL) ||
!g_ascii_string_to_unsigned (tokens[1], 10, 0, 255, &num[1], NULL) ||
!g_ascii_string_to_unsigned (tokens[2], 10, 0, 255, &num[2], NULL) ||
!g_ascii_string_to_unsigned (tokens[3], 10, 0, 255, &num[3], NULL))
{
g_strfreev (tokens);
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
_("cicp must be 4 numbers, separated by /\n"));
return NULL;
}
g_strfreev (tokens);
params = gdk_cicp_params_new ();
gdk_cicp_params_set_color_primaries (params, (guint) num[0]);
gdk_cicp_params_set_transfer_function (params, (guint) num[1]);
gdk_cicp_params_set_matrix_coefficients (params, (guint) num[2]);
gdk_cicp_params_set_range (params, num[3] == 0 ? GDK_CICP_RANGE_NARROW : GDK_CICP_RANGE_FULL);
color_state = gdk_cicp_params_build_color_state (params, error);
g_object_unref (params);
return color_state;
}

View File

@ -17,3 +17,6 @@ GdkColorState * find_color_state_by_name (const char *name);
char ** get_color_state_names (void); char ** get_color_state_names (void);
char * get_color_state_name (GdkColorState *color_state); char * get_color_state_name (GdkColorState *color_state);
GdkColorState *parse_cicp_tuple (const char *cicp_tuple,
GError **error);