clutter/color-state: Add color state transform helper
This helper generates shader snippets that converts pixels from one color state to another. For example if there is content with sRGB color state that should be converted to linear BT.2020 before being written to the framebuffer, a shader that makes the pixel go through the following steps will be generated: 1. sRGB EOTF 2. Luminance gain (hard coded for now) 3. Color space mapping The intention is that it should be possible to composite in a linear color space, into an intermediate framebuffer, which is then passed through an inverse EOTF to produce linear content in the output color state. When transforming from BT.2020/PQ to sRGB/sRGB, clamping to the sRGB max luminance is done. Cases where direct transform is also handled, i.e. where one doesn't go via an linear intermediate buffer, in which case there might be both an EOTF and an inverted EOTF in the same shader snippet to still do color space mapping using optical color encoding. This will be used for e.g. transforming cursor sprites. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3433>
This commit is contained in:
parent
8b425f4e85
commit
4a07242fed
@ -4,6 +4,7 @@
|
||||
* An OpenGL based 'interactive canvas' library.
|
||||
*
|
||||
* Copyright (C) 2022 Intel Corporation.
|
||||
* Copyright (C) 2023-2024 Red Hat
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -20,6 +21,7 @@
|
||||
*
|
||||
* Author:
|
||||
* Naveen Kumar <naveen1.kumar@intel.com>
|
||||
* Jonas Ådahl <jadahl@redhat.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -80,6 +82,40 @@ G_DEFINE_TYPE_WITH_PRIVATE (ClutterColorState,
|
||||
clutter_color_state,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
static const char *
|
||||
clutter_colorspace_to_string (ClutterColorspace colorspace)
|
||||
{
|
||||
switch (colorspace)
|
||||
{
|
||||
case CLUTTER_COLORSPACE_DEFAULT:
|
||||
return "unknown";
|
||||
case CLUTTER_COLORSPACE_SRGB:
|
||||
return "sRGB";
|
||||
case CLUTTER_COLORSPACE_BT2020:
|
||||
return "BT.2020";
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static const char *
|
||||
clutter_transfer_function_to_string (ClutterTransferFunction transfer_function)
|
||||
{
|
||||
switch (transfer_function)
|
||||
{
|
||||
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
|
||||
return "default";
|
||||
case CLUTTER_TRANSFER_FUNCTION_SRGB:
|
||||
return "sRGB";
|
||||
case CLUTTER_TRANSFER_FUNCTION_PQ:
|
||||
return "PQ";
|
||||
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
|
||||
return "linear";
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
ClutterColorspace
|
||||
clutter_color_state_get_colorspace (ClutterColorState *color_state)
|
||||
{
|
||||
@ -218,3 +254,457 @@ clutter_color_state_new (ClutterColorspace colorspace,
|
||||
"transfer-function", transfer_function,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static const char pq_eotf_source[] =
|
||||
"// pq_eotf:\n"
|
||||
"// @pq: Normalized ([0,1]) electrical signal value\n"
|
||||
"// Returns: Luminance in cd/m²\n"
|
||||
"vec3 pq_eotf (vec3 pq)\n"
|
||||
"{\n"
|
||||
" const float c1 = 0.8359375;\n"
|
||||
" const float c2 = 18.8515625;\n"
|
||||
" const float c3 = 18.6875;\n"
|
||||
"\n"
|
||||
" const float oo_m1 = 1.0 / 0.1593017578125;\n"
|
||||
" const float oo_m2 = 1.0 / 78.84375;\n"
|
||||
"\n"
|
||||
" vec3 num = max (pow (pq, vec3 (oo_m2)) - c1, vec3 (0.0));\n"
|
||||
" vec3 den = c2 - c3 * pow (pq, vec3 (oo_m2));\n"
|
||||
"\n"
|
||||
" return 10000.0 * pow (num / den, vec3 (oo_m1));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"vec4 pq_eotf (vec4 pq)\n"
|
||||
"{\n"
|
||||
" return vec4 (pq_eotf (pq.rgb), pq.a);\n"
|
||||
"}\n";
|
||||
|
||||
static const char pq_inv_eotf_source[] =
|
||||
"// pq_inv_eotf:\n"
|
||||
"// @nits: Optical signal value in cd/m²\n"
|
||||
"// Returns: Normalized ([0,1]) electrical signal value\n"
|
||||
"vec3 pq_inv_eotf (vec3 nits)\n"
|
||||
"{\n"
|
||||
" vec3 normalized = clamp (nits / 10000.0, vec3 (0), vec3 (1));\n"
|
||||
" float m1 = 0.1593017578125;\n"
|
||||
" float m2 = 78.84375;\n"
|
||||
" float c1 = 0.8359375;\n"
|
||||
" float c2 = 18.8515625;\n"
|
||||
" float c3 = 18.6875;\n"
|
||||
" vec3 normalized_pow_m1 = pow (normalized, vec3 (m1));\n"
|
||||
" vec3 num = vec3 (c1) + c2 * normalized_pow_m1;\n"
|
||||
" vec3 denum = vec3 (1.0) + c3 * normalized_pow_m1;\n"
|
||||
" return pow (num / denum, vec3 (m2));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"vec4 pq_inv_eotf (vec4 nits)\n"
|
||||
"{\n"
|
||||
" return vec4 (pq_inv_eotf (nits.rgb), nits.a);\n"
|
||||
"}\n";
|
||||
|
||||
static const char srgb_eotf_source[] =
|
||||
"// srgb_eotf:\n"
|
||||
"// @color: Normalized ([0,1]) electrical signal value.\n"
|
||||
"// Returns: Normalized luminance ([0,1])\n"
|
||||
"vec3 srgb_eotf (vec3 color)\n"
|
||||
"{\n"
|
||||
" bvec3 is_low = lessThanEqual (color, vec3 (0.04045));\n"
|
||||
" vec3 lo_part = color / 12.92;\n"
|
||||
" vec3 hi_part = pow ((color + 0.055) / 1.055, vec3 (12.0 / 5.0));\n"
|
||||
" return mix (hi_part, lo_part, is_low);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"vec4 srgb_eotf (vec4 color)\n"
|
||||
"{\n"
|
||||
" return vec4 (srgb_eotf (color.rgb), color.a);\n"
|
||||
"}\n";
|
||||
|
||||
static const char srgb_inv_eotf_source[] =
|
||||
"// srgb_inv_eotf:\n"
|
||||
"// @color: Normalized ([0,1]) optical signal value\n"
|
||||
"// Returns: Normalized ([0,1]) electrical signal value\n"
|
||||
"vec3 srgb_inv_eotf (vec3 color)\n"
|
||||
"{\n"
|
||||
" bvec3 is_lo = lessThanEqual (color, vec3 (0.0031308));\n"
|
||||
"\n"
|
||||
" vec3 lo_part = color * 12.92;\n"
|
||||
" vec3 hi_part = pow (color, vec3 (5.0 / 12.0)) * 1.055 - 0.055;\n"
|
||||
" return mix (hi_part, lo_part, is_lo);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"vec4 srgb_inv_eotf (vec4 color)\n"
|
||||
"{\n"
|
||||
" return vec4 (srgb_inv_eotf (color.rgb), color.a);\n"
|
||||
"}\n";
|
||||
|
||||
/* Luminance gain default value (203) retrieved from
|
||||
* https://github.com/w3c/ColorWeb-CG/blob/feature/add-mastering-display-info/hdr_html_canvas_element.md#srgb-to-rec2100-pq */
|
||||
static const char srgb_luminance_gain_source[] =
|
||||
"vec3 srgb_luminance_gain (vec3 value)\n"
|
||||
"{\n"
|
||||
" return 203.0 * value;\n"
|
||||
"}\n";
|
||||
|
||||
static const char pq_luminance_clamp_source[] =
|
||||
"vec3 pq_luminance_clamp (vec3 value)\n"
|
||||
"{\n"
|
||||
" return clamp (value, 0.0, 203.0) / 203.0;\n"
|
||||
"}\n";
|
||||
|
||||
/* Calculated using:
|
||||
* numpy.dot(colour.models.RGB_COLOURSPACE_BT2020.matrix_XYZ_to_RGB,
|
||||
* colour.models.RGB_COLOURSPACE_BT709.matrix_RGB_to_XYZ)
|
||||
*/
|
||||
static const char bt709_to_bt2020_matrix_source[] =
|
||||
"mat3 bt709_to_bt2020 =\n"
|
||||
" mat3 (vec3 (0.6274039, 0.06909729, 0.01639144),\n"
|
||||
" vec3 (0.32928304, 0.9195404, 0.08801331),\n"
|
||||
" vec3 (0.04331307, 0.01136232, 0.89559525));\n";
|
||||
|
||||
/*
|
||||
* Calculated using:
|
||||
* numpy.dot(colour.models.RGB_COLOURSPACE_BT709.matrix_XYZ_to_RGB,
|
||||
* colour.models.RGB_COLOURSPACE_BT2020.matrix_RGB_to_XYZ)
|
||||
*/
|
||||
static const char bt2020_to_bt709_matrix_source[] =
|
||||
"mat3 bt2020_to_bt709 =\n"
|
||||
" mat3 (vec3 (1.660491, -0.12455047, -0.01815076),\n"
|
||||
" vec3 (-0.58764114, 1.1328999, -0.1005789),\n"
|
||||
" vec3 (-0.07284986, -0.00834942, 1.11872966));\n";
|
||||
|
||||
typedef struct _TransferFunction
|
||||
{
|
||||
const char *source;
|
||||
const char *name;
|
||||
} TransferFunction;
|
||||
|
||||
typedef struct _MatrixMultiplication
|
||||
{
|
||||
const char *source;
|
||||
const char *name;
|
||||
} MatrixMultiplication;
|
||||
|
||||
static const TransferFunction pq_eotf = {
|
||||
.source = pq_eotf_source,
|
||||
.name = "pq_eotf",
|
||||
};
|
||||
|
||||
static const TransferFunction pq_inv_eotf = {
|
||||
.source = pq_inv_eotf_source,
|
||||
.name = "pq_inv_eotf",
|
||||
};
|
||||
|
||||
static const TransferFunction srgb_eotf = {
|
||||
.source = srgb_eotf_source,
|
||||
.name = "srgb_eotf",
|
||||
};
|
||||
|
||||
static const TransferFunction srgb_inv_eotf = {
|
||||
.source = srgb_inv_eotf_source,
|
||||
.name = "srgb_inv_eotf",
|
||||
};
|
||||
|
||||
static const TransferFunction srgb_luminance_gain = {
|
||||
.source = srgb_luminance_gain_source,
|
||||
.name = "srgb_luminance_gain",
|
||||
};
|
||||
|
||||
static const TransferFunction pq_luminance_clamp = {
|
||||
.source = pq_luminance_clamp_source,
|
||||
.name = "pq_luminance_clamp",
|
||||
};
|
||||
|
||||
static const MatrixMultiplication bt709_to_bt2020 = {
|
||||
.source = bt709_to_bt2020_matrix_source,
|
||||
.name = "bt709_to_bt2020",
|
||||
};
|
||||
|
||||
static const MatrixMultiplication bt2020_to_bt709 = {
|
||||
.source = bt2020_to_bt709_matrix_source,
|
||||
.name = "bt2020_to_bt709",
|
||||
};
|
||||
|
||||
static void
|
||||
append_shader_description (GString *snippet_source,
|
||||
ClutterColorState *color_state,
|
||||
ClutterColorState *target_color_state)
|
||||
{
|
||||
ClutterColorStatePrivate *priv =
|
||||
clutter_color_state_get_instance_private (color_state);
|
||||
ClutterColorStatePrivate *target_priv =
|
||||
clutter_color_state_get_instance_private (target_color_state);
|
||||
const char *colorspace =
|
||||
clutter_colorspace_to_string (priv->colorspace);
|
||||
const char *transfer_function =
|
||||
clutter_transfer_function_to_string (priv->transfer_function);
|
||||
const char *target_colorspace =
|
||||
clutter_colorspace_to_string (target_priv->colorspace);
|
||||
const char *target_transfer_function =
|
||||
clutter_transfer_function_to_string (target_priv->transfer_function);
|
||||
|
||||
g_string_append_printf (snippet_source,
|
||||
" // %s (%s) to %s (%s)\n",
|
||||
colorspace,
|
||||
transfer_function,
|
||||
target_colorspace,
|
||||
target_transfer_function);
|
||||
}
|
||||
|
||||
static const TransferFunction *
|
||||
get_eotf (ClutterColorState *color_state)
|
||||
{
|
||||
ClutterColorStatePrivate *priv =
|
||||
clutter_color_state_get_instance_private (color_state);
|
||||
|
||||
switch (priv->transfer_function)
|
||||
{
|
||||
case CLUTTER_TRANSFER_FUNCTION_PQ:
|
||||
return &pq_eotf;
|
||||
case CLUTTER_TRANSFER_FUNCTION_SRGB:
|
||||
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
|
||||
return &srgb_eotf;
|
||||
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_warning ("Unhandled tranfer function %s",
|
||||
clutter_transfer_function_to_string (priv->transfer_function));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const TransferFunction *
|
||||
get_inv_eotf (ClutterColorState *color_state)
|
||||
{
|
||||
ClutterColorStatePrivate *priv =
|
||||
clutter_color_state_get_instance_private (color_state);
|
||||
|
||||
switch (priv->transfer_function)
|
||||
{
|
||||
case CLUTTER_TRANSFER_FUNCTION_PQ:
|
||||
return &pq_inv_eotf;
|
||||
case CLUTTER_TRANSFER_FUNCTION_SRGB:
|
||||
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
|
||||
return &srgb_inv_eotf;
|
||||
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
|
||||
return NULL;
|
||||
}
|
||||
g_warning ("Unhandled tranfer function %s",
|
||||
clutter_transfer_function_to_string (priv->transfer_function));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const TransferFunction *
|
||||
get_denormalize_function (ClutterColorState *color_state,
|
||||
ClutterColorState *target_color_state)
|
||||
{
|
||||
ClutterColorStatePrivate *priv =
|
||||
clutter_color_state_get_instance_private (color_state);
|
||||
ClutterColorStatePrivate *target_priv =
|
||||
clutter_color_state_get_instance_private (target_color_state);
|
||||
|
||||
switch (priv->transfer_function)
|
||||
{
|
||||
case CLUTTER_TRANSFER_FUNCTION_SRGB:
|
||||
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
|
||||
switch (target_priv->transfer_function)
|
||||
{
|
||||
case CLUTTER_TRANSFER_FUNCTION_PQ:
|
||||
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
|
||||
return &srgb_luminance_gain;
|
||||
case CLUTTER_TRANSFER_FUNCTION_SRGB:
|
||||
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case CLUTTER_TRANSFER_FUNCTION_PQ:
|
||||
switch (target_priv->transfer_function)
|
||||
{
|
||||
case CLUTTER_TRANSFER_FUNCTION_PQ:
|
||||
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
|
||||
return NULL;
|
||||
case CLUTTER_TRANSFER_FUNCTION_SRGB:
|
||||
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
|
||||
return &pq_luminance_clamp;
|
||||
}
|
||||
break;
|
||||
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
get_transfer_functions (ClutterColorState *color_state,
|
||||
ClutterColorState *target_color_state,
|
||||
const TransferFunction **pre_transfer_function,
|
||||
const TransferFunction **denormalize_function,
|
||||
const TransferFunction **post_transfer_function)
|
||||
{
|
||||
ClutterColorStatePrivate *priv =
|
||||
clutter_color_state_get_instance_private (color_state);
|
||||
ClutterColorStatePrivate *target_priv =
|
||||
clutter_color_state_get_instance_private (target_color_state);
|
||||
|
||||
if (priv->colorspace == target_priv->colorspace &&
|
||||
priv->transfer_function == target_priv->transfer_function)
|
||||
return;
|
||||
|
||||
if (priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR &&
|
||||
target_priv->transfer_function == CLUTTER_TRANSFER_FUNCTION_LINEAR)
|
||||
{
|
||||
*pre_transfer_function = get_eotf (color_state);
|
||||
*denormalize_function = get_denormalize_function (color_state,
|
||||
target_color_state);
|
||||
}
|
||||
else if (priv->transfer_function == CLUTTER_TRANSFER_FUNCTION_LINEAR &&
|
||||
target_priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR)
|
||||
{
|
||||
*denormalize_function = get_denormalize_function (color_state,
|
||||
target_color_state);
|
||||
*post_transfer_function = get_inv_eotf (target_color_state);
|
||||
}
|
||||
else if (priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR &&
|
||||
target_priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR)
|
||||
{
|
||||
*pre_transfer_function = get_eotf (color_state);
|
||||
*denormalize_function = get_denormalize_function (color_state,
|
||||
target_color_state);
|
||||
*post_transfer_function = get_inv_eotf (target_color_state);
|
||||
}
|
||||
}
|
||||
|
||||
static const MatrixMultiplication *
|
||||
get_color_space_mapping_matrix (ClutterColorState *color_state,
|
||||
ClutterColorState *target_color_state)
|
||||
{
|
||||
ClutterColorStatePrivate *priv =
|
||||
clutter_color_state_get_instance_private (color_state);
|
||||
ClutterColorStatePrivate *target_priv =
|
||||
clutter_color_state_get_instance_private (target_color_state);
|
||||
|
||||
switch (priv->colorspace)
|
||||
{
|
||||
case CLUTTER_COLORSPACE_SRGB:
|
||||
case CLUTTER_COLORSPACE_DEFAULT:
|
||||
switch (target_priv->colorspace)
|
||||
{
|
||||
case CLUTTER_COLORSPACE_BT2020:
|
||||
return &bt709_to_bt2020;
|
||||
case CLUTTER_COLORSPACE_SRGB:
|
||||
case CLUTTER_COLORSPACE_DEFAULT:
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case CLUTTER_COLORSPACE_BT2020:
|
||||
switch (target_priv->colorspace)
|
||||
{
|
||||
case CLUTTER_COLORSPACE_BT2020:
|
||||
return NULL;
|
||||
case CLUTTER_COLORSPACE_SRGB:
|
||||
case CLUTTER_COLORSPACE_DEFAULT:
|
||||
return &bt2020_to_bt709;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_color_state_get_transform_snippet: (skip)
|
||||
*/
|
||||
CoglSnippet *
|
||||
clutter_color_state_get_transform_snippet (ClutterColorState *color_state,
|
||||
ClutterColorState *target_color_state)
|
||||
{
|
||||
const MatrixMultiplication *color_space_mapping = NULL;
|
||||
const TransferFunction *pre_transfer_function = NULL;
|
||||
const TransferFunction *denormalize_function = NULL;
|
||||
const TransferFunction *post_transfer_function = NULL;
|
||||
g_autoptr (GString) globals_source = NULL;
|
||||
g_autoptr (GString) snippet_source = NULL;
|
||||
|
||||
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (target_color_state), NULL);
|
||||
|
||||
color_space_mapping = get_color_space_mapping_matrix (color_state,
|
||||
target_color_state);
|
||||
|
||||
get_transfer_functions (color_state, target_color_state,
|
||||
&pre_transfer_function,
|
||||
&denormalize_function,
|
||||
&post_transfer_function);
|
||||
|
||||
globals_source = g_string_new (NULL);
|
||||
if (pre_transfer_function)
|
||||
g_string_append_printf (globals_source, "%s\n", pre_transfer_function->source);
|
||||
if (denormalize_function)
|
||||
g_string_append_printf (globals_source, "%s\n", denormalize_function->source);
|
||||
if (post_transfer_function)
|
||||
g_string_append_printf (globals_source, "%s\n", post_transfer_function->source);
|
||||
if (color_space_mapping)
|
||||
g_string_append_printf (globals_source, "%s\n", color_space_mapping->source);
|
||||
|
||||
/*
|
||||
* The following statements generate a shader snippet that transforms colors
|
||||
* from one color state (transfer function, color space, color encoding) into
|
||||
* another. When the target color state is optically encoded, we always draw
|
||||
* into an intermediate 64 bit half float typed pixel.
|
||||
*
|
||||
* The value stored in this pixel is roughly the luminance expected by the
|
||||
* target color state's transfer function.
|
||||
*
|
||||
* For sRGB that means luminance relative the reference display as defined by
|
||||
* the sRGB specification, i.e. a value typically between 0.0 and 1.0. For PQ
|
||||
* this means absolute luminance in cd/m² (nits).
|
||||
*
|
||||
* The snippet contains a pipeline that roughly looks like this:
|
||||
*
|
||||
* color = pre_transfer_function (color)
|
||||
* color *= luminance_gain
|
||||
* color = color_space_mapping_matrix * color
|
||||
* color = post_transfer_function (color)
|
||||
*
|
||||
*/
|
||||
|
||||
snippet_source = g_string_new (NULL);
|
||||
append_shader_description (snippet_source, color_state, target_color_state);
|
||||
|
||||
g_string_append (snippet_source,
|
||||
" vec3 color_state_color = cogl_color_out.rgb;\n");
|
||||
|
||||
if (pre_transfer_function)
|
||||
{
|
||||
g_string_append_printf (snippet_source,
|
||||
" color_state_color = %s (color_state_color);\n",
|
||||
pre_transfer_function->name);
|
||||
}
|
||||
|
||||
if (denormalize_function)
|
||||
{
|
||||
g_string_append_printf (snippet_source,
|
||||
" color_state_color = %s (color_state_color);\n",
|
||||
denormalize_function->name);
|
||||
}
|
||||
if (color_space_mapping)
|
||||
{
|
||||
g_string_append_printf (snippet_source,
|
||||
" color_state_color = %s * color_state_color;\n",
|
||||
color_space_mapping->name);
|
||||
}
|
||||
|
||||
if (post_transfer_function)
|
||||
{
|
||||
g_string_append_printf (snippet_source,
|
||||
" // Post transfer function\n"
|
||||
" color_state_color = %s (color_state_color);\n",
|
||||
post_transfer_function->name);
|
||||
}
|
||||
|
||||
g_string_append (snippet_source,
|
||||
" cogl_color_out = vec4 (color_state_color, cogl_color_out.a);\n");
|
||||
|
||||
return cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
|
||||
globals_source->str,
|
||||
snippet_source->str);
|
||||
}
|
||||
|
@ -48,4 +48,8 @@ ClutterColorspace clutter_color_state_get_colorspace (ClutterColorState *color_s
|
||||
CLUTTER_EXPORT
|
||||
ClutterTransferFunction clutter_color_state_get_transfer_function (ClutterColorState *color_state);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
CoglSnippet * clutter_color_state_get_transform_snippet (ClutterColorState *color_state,
|
||||
ClutterColorState *target_color_state);
|
||||
|
||||
G_END_DECLS
|
||||
|
Loading…
x
Reference in New Issue
Block a user