diff --git a/clutter/cogl/cogl-debug.h b/clutter/cogl/cogl-debug.h index b31a36c52..8aa1e8f87 100644 --- a/clutter/cogl/cogl-debug.h +++ b/clutter/cogl/cogl-debug.h @@ -29,15 +29,16 @@ G_BEGIN_DECLS typedef enum { - COGL_DEBUG_MISC = 1 << 0, - COGL_DEBUG_TEXTURE = 1 << 1, - COGL_DEBUG_MATERIAL = 1 << 2, - COGL_DEBUG_SHADER = 1 << 3, - COGL_DEBUG_OFFSCREEN = 1 << 4, - COGL_DEBUG_DRAW = 1 << 5, - COGL_DEBUG_PANGO = 1 << 6, - COGL_DEBUG_RECTANGLES = 1 << 7, - COGL_DEBUG_HANDLE = 1 << 8 + COGL_DEBUG_MISC = 1 << 0, + COGL_DEBUG_TEXTURE = 1 << 1, + COGL_DEBUG_MATERIAL = 1 << 2, + COGL_DEBUG_SHADER = 1 << 3, + COGL_DEBUG_OFFSCREEN = 1 << 4, + COGL_DEBUG_DRAW = 1 << 5, + COGL_DEBUG_PANGO = 1 << 6, + COGL_DEBUG_RECTANGLES = 1 << 7, + COGL_DEBUG_HANDLE = 1 << 8, + COGL_DEBUG_BLEND_STRINGS = 1 << 9 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG @@ -69,3 +70,4 @@ extern guint cogl_debug_flags; G_END_DECLS #endif /* __COGL_DEBUG_H__ */ + diff --git a/clutter/cogl/cogl-material.h b/clutter/cogl/cogl-material.h index 95a474831..d69ae276c 100644 --- a/clutter/cogl/cogl-material.h +++ b/clutter/cogl/cogl-material.h @@ -43,7 +43,6 @@ G_BEGIN_DECLS * blended together. */ - /** * cogl_material_new: * @@ -378,10 +377,12 @@ void cogl_material_set_alpha_test_function (CoglHandle material, * @COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA: (1-As, 1-As, 1-As, 1-As) * @COGL_MATERIAL_BLEND_FACTOR_DST_ALPHA: (Ad, Ad, Ad, Ad) * @COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_DST_ALPHA: (1-Ad, 1-Ad, 1-Ad, 1-Ad) + * @COGL_MATERIAL_BLEND_FACTOR_CONSTANT: (Rc, Gc, Bc, Ac) + * @COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_CONSTANT: (1-Rc, 1-Gc, 1-Bc, 1-Ac) + * @COGL_MATERIAL_BLEND_FACTOR_CONSTANT_ALPHA: (Ac, Ac, Ac, Ac) + * @COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA: (1-Ac, 1-Ac, 1-Ac, 1-Ac) * @COGL_MATERIAL_BLEND_FACTOR_SRC_ALPHA_SATURATE: (f,f,f,1) where f=MIN(As,1-Ad) * - * Blending occurs after the alpha test function, and combines fragments with - * the framebuffer. * * A fixed function is used to determine the blended color, which is based on * the incoming source color of your fragment (Rs, Gs, Bs, As), a source @@ -411,6 +412,14 @@ typedef enum _CoglMaterialBlendFactor COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA, COGL_MATERIAL_BLEND_FACTOR_DST_ALPHA = GL_DST_ALPHA, COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA, +#ifdef HAVE_COGL_GL + COGL_MATERIAL_BLEND_FACTOR_CONSTANT = GL_CONSTANT_COLOR, + COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_CONSTANT = + GL_ONE_MINUS_CONSTANT_COLOR, + COGL_MATERIAL_BLEND_FACTOR_CONSTANT_ALPHA = GL_CONSTANT_ALPHA, + COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = + GL_ONE_MINUS_CONSTANT_ALPHA, +#endif COGL_MATERIAL_BLEND_FACTOR_SRC_ALPHA_SATURATE = GL_SRC_ALPHA_SATURATE, } CoglMaterialBlendFactor; @@ -442,6 +451,92 @@ void cogl_material_set_blend_factors (CoglHandle material, CoglMaterialBlendFactor src_factor, CoglMaterialBlendFactor dst_factor); +/** + * cogl_material_set_blend: + * @material: A CoglMaterial object + * @blend_string: A Cogl blend string + * describing the desired blend function. + * @error: A GError that may report lack of driver support if you give + * separate blend string statements for the alpha channel and RGB + * channels since some drivers or backends such as GLES 1.1 dont + * support this. + * + * If not already familiar; please refer + * here for an overview of what blend + * strings are and there syntax. + * + * Blending occurs after the alpha test function, and combines fragments with + * the framebuffer. + + * Currently the only blend function Cogl exposes is ADD(). So any valid + * blend statements will be of the form: + * + * + * <channel-mask>=ADD(SRC_COLOR*(<factor>), DST_COLOR*(<factor>)) + * + * + * NOTE: The brackets around blend factors are currently not optional! + * + * This is the list of source-names usable as blend factors: + * + * SRC_COLOR: The color of the in comming fragment + * DST_COLOR: The color of the framebuffer + * + * CONSTANT: The constant set via cogl_material_set_blend_constant() + * + * The source names can be used according to the + * color-source and factor syntax, + * so for example "(1-SRC_COLOR[A])" would be a valid factor, as would + * "(CONSTANT[RGB])" + * + * These can also be used as factors: + * + * 0: (0, 0, 0, 0) + * 1: (1, 1, 1, 1) + * SRC_ALPHA_SATURATE_FACTOR: (f,f,f,1) + * where f=MIN(SRC_COLOR[A],1-DST_COLOR[A]) + * + * + * Remember; all color components are normalized to the range [0, 1] before + * computing the result of blending. + * + *
+ * Examples + * Blend a non-premultiplied source over a destination with + * premultiplied alpha: + * + * "RGB = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))" + * "A = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" + * + * Blend a premultiplied source over a destination with premultiplied alpha: + * + * "RGBA = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" + * + *
+ * + * Returns: TRUE if the blend string was successfully parsed, and the described + * blending is supported by the underlying driver/hardware. If there + * was an error, it returns FALSE. + * + * Since: 1.0 + */ +gboolean cogl_material_set_blend (CoglHandle material, + const char *blend_string, + GError **error); + +/** + * cogl_material_set_blend_constant: + * @material: A CoglMaterial object + * @constant_color: The constant color you want + * + * When blending is setup to reference a CONSTANT blend factor then + * blending will depend on the constant set with this function. + * + * Since: 1.0 + */ +void cogl_material_set_blend_constant (CoglHandle material, + CoglColor *constant_color); + /** * cogl_material_set_layer: * @material: A CoglMaterial object @@ -460,7 +555,7 @@ void cogl_material_set_blend_factors (CoglHandle material, * Since 1.0 */ void cogl_material_set_layer (CoglHandle material, - gint layer_index, + int layer_index, CoglHandle texture); /** @@ -473,6 +568,113 @@ void cogl_material_set_layer (CoglHandle material, void cogl_material_remove_layer (CoglHandle material, gint layer_index); + +/** + * cogl_material_set_layer_combine: + * @material: A CoglMaterial object + * @layer_index: Specifies the layer you want define a combine function for + * @blend_string: A Cogl blend string + * describing the desired texture combine function. + * @error: A GError that may report parse errors or lack of GPU/driver support. + * + * If not already familiar; you can refer + * here for an overview of what blend + * strings are and there syntax. + * + * These are all the functions available for texture combining: + * + * REPLACE(arg0) = arg0 + * MODULATE(arg0, arg1) = arg0 x arg1 + * ADD(arg0, arg1) = arg0 + arg1 + * ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5 + * INTERPOLATE(arg0, arg1, arg2) = + * arg0 x arg2 + arg1 x (1 - arg2) + * SUBTRACT(arg0, arg1) = arg0 - arg1 + * + * DOT3_RGB(arg0, arg1) = + * + * 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) + * + * + * DOT3_RGBA(arg0, arg1) = + * + * 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) + * + * + * + * + * Refer to the + * color-source syntax for + * describing the arguments. The valid source names for texture combining + * are: + * + * + * TEXTURE: Use the color from the current texture layer + * + * + * TEXTURE_0, TEXTURE_1, etc: Use the color from the specified texture layer + * + * + * CONSTANT: Use the color from the constant given with + * cogl_material_set_layer_constant() + * + * + * PRIMARY: Use the color of the material as set with cogl_material_set_color() + * + * + * PREVIOUS: Either use the texture color from the previous layer, or if this + * is layer 0, use the color of the material as set with + * cogl_material_set_color() + * + * + *
+ * Example + * This is effectively what the default blending is: + * + * "RGBA = MODULATE (PREVIOUS, TEXTURE)" + * + * This could be used to cross-fade between two images, using the alpha + * component of a constant as the interpolator. The constant color + * is given by calling cogl_material_set_layer_constant. + * + * RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A]) + * + *
+ * Note: you can't give a multiplication factor for arguments as you can + * with blending. + * + * Returns: TRUE if the blend string was successfully parsed, and the described + * texture combining is supported by the underlying driver/hardware. + * If there was an error, it returns FALSE. + * + * Since: 1.0 + */ +gboolean +cogl_material_set_layer_combine (CoglHandle material, + gint layer_index, + const char *blend_string, + GError **error); + +/** + * cogl_material_set_layer_combine_constant: + * @material: A CoglMaterial object + * @layer_index: Specifies the layer you want to specify a constant used + * for texture combining + * @color_constant: The constant color you want + * + * When you are using the 'CONSTANT' color source in a layer combine + * description then you can use this function to define its value. + * + * Since 1.0 + */ +void cogl_material_set_layer_combine_constant (CoglHandle material, + int layer_index, + CoglColor *constant); + /** * CoglMaterialLayerCombineFunc: * @COGL_MATERIAL_LAYER_COMBINE_FUNC_REPLACE: Arg0 @@ -600,7 +802,7 @@ typedef enum _CoglMaterialLayerCombineChannels * */ void cogl_material_set_layer_combine_function (CoglHandle material, - gint layer_index, + int layer_index, CoglMaterialLayerCombineChannels channels, CoglMaterialLayerCombineFunc func); @@ -650,8 +852,8 @@ typedef enum _CoglMaterialLayerCombineSrc * */ void cogl_material_set_layer_combine_arg_src (CoglHandle material, - gint layer_index, - gint argument, + int layer_index, + int argument, CoglMaterialLayerCombineChannels channels, CoglMaterialLayerCombineSrc src); @@ -674,33 +876,11 @@ typedef enum _CoglMaterialLayerCombineOp * */ void cogl_material_set_layer_combine_arg_op (CoglHandle material, - gint layer_index, - gint argument, + int layer_index, + int argument, CoglMaterialLayerCombineChannels channels, CoglMaterialLayerCombineOp op); -/* TODO: */ -#if 0 - I think it would be be really neat to support a simple string description - of the fixed function texture combine modes exposed above. I think we can - consider this stuff to be set in stone from the POV that more advanced - texture combine functions are catered for with GLSL, so it seems reasonable - to find a concise string representation that can represent all the above - modes in a *much* more readable/useable fashion. I think somthing like - this would be quite nice: - - "MODULATE(TEXTURE[RGB], PREVIOUS[A])" - "ADD(TEXTURE[A],PREVIOUS[RGB])" - "INTERPOLATE(TEXTURE[1-A], PREVIOUS[RGB])" - -void cogl_material_set_layer_rgb_combine (CoglHandle material - gint layer_index, - const char *combine_description); -void cogl_material_set_layer_alpha_combine (CoglHandle material - gint layer_index, - const char *combine_description); -#endif - /** * cogl_material_set_layer_matrix: * @material: A CoglMaterial object @@ -709,7 +889,7 @@ void cogl_material_set_layer_alpha_combine (CoglHandle material * and rotate a single layer of a material used to fill your geometry. */ void cogl_material_set_layer_matrix (CoglHandle material, - gint layer_index, + int layer_index, CoglMatrix *matrix); diff --git a/clutter/cogl/common/Makefile.am b/clutter/cogl/common/Makefile.am index 138893a14..c4e6eb2f7 100644 --- a/clutter/cogl/common/Makefile.am +++ b/clutter/cogl/common/Makefile.am @@ -39,4 +39,6 @@ libclutter_cogl_common_la_SOURCES = \ cogl-matrix-stack.h \ cogl-material.c \ cogl-material-private.h \ + cogl-blend-string.c \ + cogl-blend-string.h \ cogl-debug.c diff --git a/clutter/cogl/common/cogl-blend-string.c b/clutter/cogl/common/cogl-blend-string.c new file mode 100644 index 000000000..2e7bdd8d3 --- /dev/null +++ b/clutter/cogl/common/cogl-blend-string.c @@ -0,0 +1,999 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" +#include "cogl-debug.h" +#include "cogl-blend-string.h" + +typedef enum _ParserState +{ + PARSER_STATE_EXPECT_DEST_CHANNELS, + PARSER_STATE_SCRAPING_DEST_CHANNELS, + PARSER_STATE_EXPECT_FUNCTION_NAME, + PARSER_STATE_SCRAPING_FUNCTION_NAME, + PARSER_STATE_EXPECT_ARG_START, + PARSER_STATE_EXPECT_STATEMENT_END +} ParserState; + +typedef enum _ParserArgState +{ + PARSER_ARG_STATE_START, + PARSER_ARG_STATE_EXPECT_MINUS, + PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME, + PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME, + PARSER_ARG_STATE_MAYBE_COLOR_MASK, + PARSER_ARG_STATE_SCRAPING_MASK, + PARSER_ARG_STATE_MAYBE_MULT, + PARSER_ARG_STATE_EXPECT_OPEN_PAREN, + PARSER_ARG_STATE_EXPECT_FACTOR, + PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE, + PARSER_ARG_STATE_MAYBE_MINUS, + PARSER_ARG_STATE_EXPECT_CLOSE_PAREN, + PARSER_ARG_STATE_EXPECT_END +} ParserArgState; + + +#define DEFINE_COLOR_SOURCE(NAME, NAME_LEN) \ + {.type = COGL_BLEND_STRING_COLOR_SOURCE_ ## NAME, \ + .name = #NAME, \ + .name_len = NAME_LEN} + +static CoglBlendStringColorSourceInfo blending_color_sources[] = { + DEFINE_COLOR_SOURCE (SRC_COLOR, 9), + DEFINE_COLOR_SOURCE (DST_COLOR, 9), + DEFINE_COLOR_SOURCE (CONSTANT, 8) +}; + +static CoglBlendStringColorSourceInfo tex_combine_color_sources[] = { + DEFINE_COLOR_SOURCE (TEXTURE, 7), + /* DEFINE_COLOR_SOURCE (TEXTURE_N, *) - handled manually */ + DEFINE_COLOR_SOURCE (PRIMARY, 7), + DEFINE_COLOR_SOURCE (CONSTANT, 8), + DEFINE_COLOR_SOURCE (PREVIOUS, 8) +}; + +static CoglBlendStringColorSourceInfo tex_combine_texture_n_color_source = { + .type = COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N, + .name = "TEXTURE_N", + .name_len = 0 +}; + +#undef DEFINE_COLOR_SOURCE + +#define DEFINE_FUNCTION(NAME, NAME_LEN, ARGC) \ + { .type = COGL_BLEND_STRING_FUNCTION_ ## NAME, \ + .name = #NAME, \ + .name_len = NAME_LEN, \ + .argc = ARGC } + +/* NB: These must be sorted so any name that's a subset of another + * comes later than the longer name. */ +static CoglBlendStringFunctionInfo tex_combine_functions[] = { + DEFINE_FUNCTION (AUTO_COMPOSITE, 14, 0), + DEFINE_FUNCTION (REPLACE, 7, 1), + DEFINE_FUNCTION (MODULATE, 8, 2), + DEFINE_FUNCTION (ADD_SIGNED, 10, 2), + DEFINE_FUNCTION (ADD, 3, 2), + DEFINE_FUNCTION (INTERPOLATE, 11, 3), + DEFINE_FUNCTION (SUBTRACT, 8, 2), + DEFINE_FUNCTION (DOT3_RGBA, 9, 2), + DEFINE_FUNCTION (DOT3_RGB, 8, 2) +}; + +static CoglBlendStringFunctionInfo blend_functions[] = { + DEFINE_FUNCTION (AUTO_COMPOSITE, 14, 0), + DEFINE_FUNCTION (ADD, 3, 2) +}; + +#undef DEFINE_FUNCTION + +GQuark +_cogl_blend_string_error_quark (void) +{ + return g_quark_from_static_string ("cogl-blend-string-error-quark"); +} + +void +_cogl_blend_string_split_rgba_statement (CoglBlendStringStatement *statement, + CoglBlendStringStatement *rgb, + CoglBlendStringStatement *a) +{ + int i; + + memcpy (rgb, statement, sizeof (CoglBlendStringStatement)); + memcpy (a, statement, sizeof (CoglBlendStringStatement)); + + rgb->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + a->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + + for (i = 0; i < statement->function->argc; i++) + { + CoglBlendStringArgument *arg = &statement->args[i]; + CoglBlendStringArgument *rgb_arg = &rgb->args[i]; + CoglBlendStringArgument *a_arg = &a->args[i]; + + if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + rgb_arg->source.mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + a_arg->source.mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + } + + if (arg->factor.is_color && + arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + rgb_arg->factor.source.mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + a_arg->factor.source.mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + } + } +} + +static gboolean +validate_tex_combine_statements (CoglBlendStringStatement *statements, + int n_statements, + GError **error) +{ + int i, j; + const char *error_string; + CoglBlendStringError detail = COGL_BLEND_STRING_ERROR_INVALID_ERROR; + + for (i = 0; i < n_statements; i++) + { +#ifdef HAVE_COGL_GLES2 + if (statements[i].function->type != COGL_BLEND_STRING_FUNCTION_MODULATE) + { + error_string = "Using anything but MODULATE() for texture combining" + " under GLES 2 is currently unsupported"; + detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR; + goto error; + } +#endif + for (j = 0; j < statements[i].function->argc; j++) + { + CoglBlendStringArgument *arg = &statements[i].args[j]; + if (arg->source.is_zero) + { + error_string = "You can't use the constant '0' as a texture " + "combine argument"; + goto error; + } + if (!arg->factor.is_one) + { + error_string = "Argument factors are only relevant to blending " + "not texture combining"; + goto error; + } +#ifdef HAVE_COGL_GLES2 + if (arg->source.info->type == COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT) + { + error_string = "Using a constant for texture combining isn't " + "currently supported with GLES 2 " + "(TODO: glTexEnvf)"; + detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR; + goto error; + } +#endif + } + } + + return TRUE; + +error: + g_set_error (error, + COGL_BLEND_STRING_ERROR, + detail, + "Invalid texture combine string: %s", + error_string); + + if (cogl_debug_flags & COGL_DEBUG_BLEND_STRINGS) + { + g_debug ("Invalid texture combine string: %s", + error_string); + } + return FALSE; +} + +static gboolean +validate_blend_statements (CoglBlendStringStatement *statements, + int n_statements, + GError **error) +{ + int i, j; + const char *error_string; + CoglBlendStringError detail = COGL_BLEND_STRING_ERROR_INVALID_ERROR; + +#ifdef HAVE_COGL_GL + _COGL_GET_CONTEXT (ctx, 0); +#endif + +#ifdef HAVE_COGL_GL + if (n_statements == 2) + { + /* glBlendEquationSeperate is GL 2.0 only */ + if (!ctx->pf_glBlendEquationSeparate && + statements[0].function->type != statements[1].function->type) + { + error_string = "Separate blend functions for the RGB an A " + "channels isn't supported by the driver"; + detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR; + goto error; + } + } +#elif defined(HAVE_COGL_GLES) + if (n_statements != 1) + { + error_string = "Separate blend functions for the RGB an A " + "channels isn't supported by the GLES 1"; + detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR; + goto error; + } +#endif + + for (i = 0; i < n_statements; i++) + for (j = 0; j < statements[i].function->argc; j++) + { + CoglBlendStringArgument *arg = &statements[i].args[j]; + + if (arg->source.is_zero) + continue; + + if ((j == 0 && + arg->source.info->type != + COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR) + || (j == 1 && + arg->source.info->type != + COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR)) + { + error_string = "For blending you must always use SRC_COLOR " + "for arg0 and DST_COLOR for arg1"; + goto error; + } + +#ifdef HAVE_COGL_GLES + if (arg->factor.is_color && + arg->factor.source.info->type == COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT) + { + error_string = "GLES Doesn't support constant blend factors"; + detail = COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR; + goto error; + } +#endif + } + + return TRUE; + +error: + g_set_error (error, + COGL_BLEND_STRING_ERROR, + detail, + "Invalid blend string: %s", + error_string); + return FALSE; +} + +static gboolean +validate_statements_for_context (CoglBlendStringStatement *statements, + int n_statements, + CoglBlendStringContext context, + GError **error) +{ + const char *error_string; + + if (n_statements == 1) + { + if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) + { + error_string = "You need to also give a blend statement for the RGB" + "channels"; + goto error; + } + else if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + error_string = "You need to also give a blend statement for the " + "Alpha channel"; + goto error; + } + } + + if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) + return validate_blend_statements (statements, n_statements, error); + else + return validate_tex_combine_statements (statements, n_statements, error); + +error: + g_set_error (error, + COGL_BLEND_STRING_ERROR, + COGL_BLEND_STRING_ERROR_INVALID_ERROR, + "Invalid %s string: %s", + context == COGL_BLEND_STRING_CONTEXT_BLENDING ? + "blend" : "texture combine", + error_string); + + if (cogl_debug_flags & COGL_DEBUG_BLEND_STRINGS) + { + g_debug ("Invalid %s string: %s", + context == COGL_BLEND_STRING_CONTEXT_BLENDING ? + "blend" : "texture combine", + error_string); + } + + return FALSE; +} + +static void +print_argument (CoglBlendStringArgument *arg) +{ + const char *mask_names[] = { + "RGB", + "A", + "RGBA" + }; + + g_print (" Arg:\n"); + g_print (" is zero = %s\n", arg->source.is_zero ? "yes" : "no"); + if (!arg->source.is_zero) + { + g_print (" color source = %s\n", arg->source.info->name); + g_print (" one minus = %s\n", arg->source.one_minus ? "yes" : "no"); + g_print (" mask = %s\n", mask_names[arg->source.mask]); + g_print (" texture = %d\n", arg->source.texture); + g_print ("\n"); + g_print (" factor is_one = %s\n", arg->factor.is_one ? "yes" : "no"); + g_print (" factor is_src_alpha_saturate = %s\n", + arg->factor.is_src_alpha_saturate ? "yes" : "no"); + g_print (" factor is_color = %s\n", arg->factor.is_color ? "yes" : "no"); + if (arg->factor.is_color) + { + g_print (" factor color:is zero = %s\n", + arg->factor.source.is_zero ? "yes" : "no"); + g_print (" factor color:color source = %s\n", + arg->factor.source.info->name); + g_print (" factor color:one minus = %s\n", + arg->factor.source.one_minus ? "yes" : "no"); + g_print (" factor color:mask = %s\n", + mask_names[arg->factor.source.mask]); + g_print (" factor color:texture = %d\n", + arg->factor.source.texture); + } + } +} + +static void +print_statement (int num, CoglBlendStringStatement *statement) +{ + const char *mask_names[] = { + "RGB", + "A", + "RGBA" + }; + int i; + g_print ("Statement %d:\n", num); + g_print (" Destination channel mask = %s\n", + mask_names[statement->mask]); + g_print (" Function = %s\n", statement->function->name); + for (i = 0; i < statement->function->argc; i++) + print_argument (&statement->args[i]); +} + +static const CoglBlendStringFunctionInfo * +get_function_info (const char *mark, + const char *p, + CoglBlendStringContext context) +{ + size_t len = p - mark; + CoglBlendStringFunctionInfo *functions; + size_t array_len; + int i; + + if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) + { + functions = blend_functions; + array_len = G_N_ELEMENTS (blend_functions); + } + else + { + functions = tex_combine_functions; + array_len = G_N_ELEMENTS (tex_combine_functions); + } + + for (i = 0; i < array_len; i++) + { + if (len >= functions[i].name_len + && strncmp (mark, functions[i].name, functions[i].name_len) == 0) + return &functions[i]; + } + return NULL; +} + +static const CoglBlendStringColorSourceInfo * +get_color_src_info (const char *mark, + const char *p, + CoglBlendStringContext context) +{ + size_t len = p - mark; + CoglBlendStringColorSourceInfo *sources; + size_t array_len; + int i; + + if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) + { + sources = blending_color_sources; + array_len = G_N_ELEMENTS (blending_color_sources); + } + else + { + sources = tex_combine_color_sources; + array_len = G_N_ELEMENTS (tex_combine_color_sources); + } + + for (i = 0; i < array_len; i++) + { + if (len >= sources[i].name_len + && strncmp (mark, sources[i].name, sources[i].name_len) == 0) + return &sources[i]; + } + + if (len >= 9 && + strncmp (mark, "TEXTURE_", 8) == 0 && + g_ascii_isdigit (mark[8])) + { + return &tex_combine_texture_n_color_source; + } + + return NULL; +} + +static gboolean +is_symbol_char (const char c) +{ + return (g_ascii_isalpha (c) || c == '_') ? TRUE : FALSE; +} + +static gboolean +parse_argument (const char *string, /* original user string */ + const char **ret_p, /* start of argument IN:OUT */ + const CoglBlendStringStatement *statement, + int current_arg, + CoglBlendStringArgument *arg, /* OUT */ + CoglBlendStringContext context, + GError **error) +{ + const char *p = *ret_p; + const char *mark; + const char *error_string; + ParserArgState state = PARSER_ARG_STATE_START; + gboolean parsing_factor = FALSE; + + arg->source.is_zero = FALSE; + arg->source.info = NULL; + arg->source.texture = 0; + arg->source.one_minus = FALSE; + arg->source.mask = statement->mask; + + arg->factor.is_one = FALSE; + arg->factor.is_color = FALSE; + arg->factor.is_src_alpha_saturate = FALSE; + + arg->factor.source.is_zero = FALSE; + arg->factor.source.info = NULL; + arg->factor.source.texture = 0; + arg->factor.source.one_minus = FALSE; + arg->factor.source.mask = statement->mask; + + do + { + if (g_ascii_isspace (*p)) + continue; + + if (*p == '\0') + { + error_string = "Unexpected end of string while parsing argument"; + goto error; + } + + switch (state) + { + case PARSER_ARG_STATE_START: + if (*p == '1') + state = PARSER_ARG_STATE_EXPECT_MINUS; + else if (*p == '0') + { + arg->source.is_zero = TRUE; + state = PARSER_ARG_STATE_EXPECT_END; + } + else + { + p--; /* backtrack */ + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + } + continue; + + case PARSER_ARG_STATE_EXPECT_MINUS: + if (*p != '-') + { + error_string = "expected a '-' following the 1"; + goto error; + } + arg->source.one_minus = TRUE; + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + continue; + + case PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME: + if (!is_symbol_char (*p)) + { + error_string = "expected a color source name"; + goto error; + } + state = PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME; + mark = p; + if (parsing_factor) + arg->factor.is_color = TRUE; + + /* fall through */ + case PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME: + if (!is_symbol_char (*p)) + { + CoglBlendStringColorSource *source = + parsing_factor ? &arg->factor.source : &arg->source; + source->info = get_color_src_info (mark, p, context); + if (!source->info) + { + error_string = "Unknown color source name"; + goto error; + } + if (source->info->type == + COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N) + { + char *endp; + source->texture = + strtoul (&mark[strlen ("TEXTURE_")], &endp, 10); + if (mark == endp) + { + error_string = "invalid texture number given with " + "TEXTURE_N color source"; + goto error; + } + p = endp; + } + state = PARSER_ARG_STATE_MAYBE_COLOR_MASK; + } + else + continue; + + /* fall through */ + case PARSER_ARG_STATE_MAYBE_COLOR_MASK: + if (*p != '[') + { + p--; /* backtrack */ + if (!parsing_factor) + state = PARSER_ARG_STATE_MAYBE_MULT; + else + state = PARSER_ARG_STATE_EXPECT_END; + continue; + } + state = PARSER_ARG_STATE_SCRAPING_MASK; + mark = p; + + /* fall through */ + case PARSER_ARG_STATE_SCRAPING_MASK: + if (*p == ']') + { + size_t len = p - mark; + CoglBlendStringColorSource *source = + parsing_factor ? &arg->factor.source : &arg->source; + + if (len == 5 && strncmp (mark, "[RGBA", len) == 0) + { + if (statement->mask != COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + error_string = "You can't use an RGBA color mask if the " + "statement hasn't also got an RGBA= mask"; + goto error; + } + source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; + } + else if (len == 4 && strncmp (mark, "[RGB", len) == 0) + source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + else if (len == 2 && strncmp (mark, "[A", len) == 0) + source->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + else + { + error_string = "Expected a channel mask of [RGBA]" + "[RGB] or [A]"; + goto error; + } + if (parsing_factor) + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + else + state = PARSER_ARG_STATE_MAYBE_MULT; + } + continue; + + case PARSER_ARG_STATE_EXPECT_OPEN_PAREN: + if (*p != '(') + { + error_string = "Expected '(' before blend factor - the parser " + "currently requires that all blend factors " + "following a '*' be surrounded in brackets"; + goto error; + } + parsing_factor = TRUE; + state = PARSER_ARG_STATE_EXPECT_FACTOR; + continue; + + case PARSER_ARG_STATE_EXPECT_FACTOR: + if (*p == '1') + state = PARSER_ARG_STATE_MAYBE_MINUS; + else if (*p == '0') + { + arg->source.is_zero = TRUE; + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + } + else + { + state = PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE; + mark = p; + } + continue; + + case PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE: + if (!is_symbol_char (*p)) + { + size_t len = p - mark; + if (len >= strlen ("SRC_ALPHA_SATURATE") && + strncmp (mark, "SRC_ALPHA_SATURATE", len) == 0) + { + arg->factor.is_src_alpha_saturate = TRUE; + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + } + else + { + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + p = mark - 1; /* backtrack */ + } + } + continue; + + case PARSER_ARG_STATE_MAYBE_MINUS: + if (*p == '-') + { + arg->factor.source.one_minus = TRUE; + state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; + } + else + { + arg->factor.is_one = TRUE; + state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; + } + continue; + + case PARSER_ARG_STATE_EXPECT_CLOSE_PAREN: + if (*p != ')') + { + error_string = "Expected closing parenthesis after blend factor"; + goto error; + } + state = PARSER_ARG_STATE_EXPECT_END; + continue; + + case PARSER_ARG_STATE_MAYBE_MULT: + if (*p == '*') + { + state = PARSER_ARG_STATE_EXPECT_OPEN_PAREN; + continue; + } + arg->factor.is_one = TRUE; + state = PARSER_ARG_STATE_EXPECT_END; + + /* fall through */ + case PARSER_ARG_STATE_EXPECT_END: + if (*p != ',' && *p != ')') + { + error_string = "expected , or )"; + goto error; + } + + *ret_p = p - 1; + return TRUE; + } + } + while (p++); + +error: + { + int offset = p - string; + g_set_error (error, + COGL_BLEND_STRING_ERROR, + COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR, + "Syntax error for argument %d at offset %d: %s", + current_arg, + offset, + error_string); + + if (cogl_debug_flags & COGL_DEBUG_BLEND_STRINGS) + { + g_debug ("Syntax error for argument %d at offset %d: %s", + current_arg, offset, error_string); + } + return FALSE; + } +} + +int +_cogl_blend_string_compile (const char *string, + CoglBlendStringContext context, + CoglBlendStringStatement *statements, + GError **error) +{ + const char *p = string; + const char *mark; + const char *error_string; + ParserState state = PARSER_STATE_EXPECT_DEST_CHANNELS; + CoglBlendStringStatement *statement = statements; + int current_statement = 0; + int current_arg = 0; + int remaining_argc; + +#if 0 + cogl_debug_flags |= COGL_DEBUG_BLEND_STRINGS; +#endif + + if (cogl_debug_flags & COGL_DEBUG_BLEND_STRINGS) + { + COGL_NOTE (BLEND_STRINGS, "Compiling %s string:\n%s\n", + context == COGL_BLEND_STRING_CONTEXT_BLENDING ? + "blend" : "texture combine", + string); + } + + do + { + if (g_ascii_isspace (*p)) + continue; + + if (*p == '\0') + { + switch (state) + { + case PARSER_STATE_EXPECT_DEST_CHANNELS: + if (current_statement != 0) + goto finished; + error_string = "Empty statement"; + goto error; + case PARSER_STATE_SCRAPING_DEST_CHANNELS: + error_string = "Expected an '=' following the destination " + "channel mask"; + goto error; + case PARSER_STATE_EXPECT_FUNCTION_NAME: + error_string = "Expected a function name"; + goto error; + case PARSER_STATE_SCRAPING_FUNCTION_NAME: + error_string = "Expected parenthesis after the function name"; + goto error; + case PARSER_STATE_EXPECT_ARG_START: + error_string = "Expected to find the start of an argument"; + goto error; + case PARSER_STATE_EXPECT_STATEMENT_END: + error_string = "Expected closing parenthesis for statement"; + goto error; + } + } + + switch (state) + { + case PARSER_STATE_EXPECT_DEST_CHANNELS: + mark = p; + state = PARSER_STATE_SCRAPING_DEST_CHANNELS; + + /* fall through */ + case PARSER_STATE_SCRAPING_DEST_CHANNELS: + if (*p != '=') + continue; + if (strncmp (mark, "RGBA", 4) == 0) + statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; + else if (strncmp (mark, "RGB", 3) == 0) + statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; + else if (strncmp (mark, "A", 1) == 0) + statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; + else + { + error_string = "Unknown destination channel mask; " + "expected RGBA=, RGB= or A="; + goto error; + } + state = PARSER_STATE_EXPECT_FUNCTION_NAME; + continue; + + case PARSER_STATE_EXPECT_FUNCTION_NAME: + mark = p; + state = PARSER_STATE_SCRAPING_FUNCTION_NAME; + + /* fall through */ + case PARSER_STATE_SCRAPING_FUNCTION_NAME: + if (*p != '(') + { + if (!is_symbol_char (*p)) + { + error_string = "non alpha numeric character in function" + "name"; + goto error; + } + continue; + } + statement->function = get_function_info (mark, p, context); + if (!statement->function) + { + error_string = "Unknown function name"; + goto error; + } + remaining_argc = statement->function->argc; + current_arg = 0; + state = PARSER_STATE_EXPECT_ARG_START; + + /* fall through */ + case PARSER_STATE_EXPECT_ARG_START: + if (*p != '(' && *p != ',') + continue; + if (remaining_argc) + { + p++; /* parse_argument expects to see the first char of the arg */ + if (!parse_argument (string, &p, statement, + current_arg, &statement->args[current_arg], + context, error)) + return 0; + current_arg++; + remaining_argc--; + } + if (!remaining_argc) + state = PARSER_STATE_EXPECT_STATEMENT_END; + continue; + + case PARSER_STATE_EXPECT_STATEMENT_END: + if (*p != ')') + { + error_string = "Expected end of statement"; + goto error; + } + state = PARSER_STATE_EXPECT_DEST_CHANNELS; + if (current_statement++ == 1) + goto finished; + statement = &statements[current_statement]; + } + } + while (p++); + +finished: + + if (cogl_debug_flags & COGL_DEBUG_BLEND_STRINGS) + { + if (current_statement > 0) + print_statement (0, &statements[0]); + if (current_statement > 1) + print_statement (1, &statements[1]); + } + + if (!validate_statements_for_context (statements, + current_statement, + context, + error)) + return 0; + + return current_statement; + +error: + { + int offset = p - string; + g_set_error (error, + COGL_BLEND_STRING_ERROR, + COGL_BLEND_STRING_ERROR_PARSE_ERROR, + "Syntax error at offset %d: %s", + offset, + error_string); + + if (cogl_debug_flags & COGL_DEBUG_BLEND_STRINGS) + { + g_debug ("Syntax error at offset %d: %s", + offset, error_string); + } + return 0; + } +} + +/* + * INTERNAL TESTING CODE ... + */ + +struct _TestString +{ + const char *string; + CoglBlendStringContext context; +}; + +int +_cogl_blend_string_test (void) +{ + struct _TestString strings[] = { + {" A = MODULATE ( TEXTURE[RGB], PREVIOUS[A], PREVIOUS[A] ) ", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + {" RGB = MODULATE ( TEXTURE[RGB], PREVIOUS[A] ) ", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + {"A=ADD(TEXTURE[A],PREVIOUS[RGB])", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + {"A=ADD(TEXTURE[A],PREVIOUS[RGB])", + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, + + {"RGBA = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD(SRC_COLOR, DST_COLOR*(0))", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD(SRC_COLOR, 0)", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD()", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {"RGB = ADD(SRC_COLOR, 0, DST_COLOR)", + COGL_BLEND_STRING_CONTEXT_BLENDING }, + {NULL} + }; + int i; + + GError *error = NULL; + for (i = 0; strings[i].string; i++) + { + CoglBlendStringStatement statements[2]; + int count = _cogl_blend_string_compile (strings[i].string, + strings[i].context, + statements, + &error); + if (!count) + { + g_print ("Failed to parse string:\n%s\n%s\n", + strings[i].string, + error->message); + g_error_free (error); + error = NULL; + continue; + } + g_print ("Original:\n"); + g_print ("%s\n", strings[i].string); + if (count > 0) + print_statement (0, &statements[0]); + if (count > 1) + print_statement (1, &statements[1]); + } + + return 0; +} + diff --git a/clutter/cogl/common/cogl-blend-string.h b/clutter/cogl/common/cogl-blend-string.h new file mode 100644 index 000000000..a3e3888f8 --- /dev/null +++ b/clutter/cogl/common/cogl-blend-string.h @@ -0,0 +1,151 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Robert Bragg + */ + +#ifndef COGL_BLEND_STRING_H +#define COGL_BLEND_STRING_H + +#include +#include + +typedef enum _CoglBlendStringContext +{ + COGL_BLEND_STRING_CONTEXT_BLENDING, + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE +} CoglBlendStringContext; + +#define COGL_BLEND_STRING_ERROR _cogl_blend_string_error_quark () + +typedef enum _CoglBlendStringError +{ + COGL_BLEND_STRING_ERROR_PARSE_ERROR, + COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR, + COGL_BLEND_STRING_ERROR_INVALID_ERROR, + COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR +} CoglBlendStringError; + +/* NB: debug stringify code will get upset if these + * are re-ordered */ +typedef enum _CoglBlendStringChannelMask +{ + COGL_BLEND_STRING_CHANNEL_MASK_RGB, + COGL_BLEND_STRING_CHANNEL_MASK_ALPHA, + COGL_BLEND_STRING_CHANNEL_MASK_RGBA +} CoglBlendStringChannelMask; + +typedef enum _CoglBlendStringColorSourceType +{ + /* blending */ + COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR, + COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR, + + /* shared */ + COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT, + + /* texture combining */ + COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE, + COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N, + COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY, + COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS +} CoglBlendStringColorSourceType; + +typedef struct _CoglBlendStringColorSourceInfo +{ + CoglBlendStringColorSourceType type; + const char *name; + size_t name_len; +} CoglBlendStringColorSourceInfo; + +typedef struct _CoglBlendStringColorSource +{ + gboolean is_zero; + const CoglBlendStringColorSourceInfo *info; + int texture; /* for the TEXTURE_N color source */ + gboolean one_minus; + CoglBlendStringChannelMask mask; +} CoglBlendStringColorSource; + +typedef struct _CoglBlendStringFactor +{ + gboolean is_one; + gboolean is_src_alpha_saturate; + gboolean is_color; + CoglBlendStringColorSource source; +} CoglBlendStringFactor; + +typedef struct _CoglBlendStringArgument +{ + CoglBlendStringColorSource source; + CoglBlendStringFactor factor; +} CoglBlendStringArgument; + +typedef enum _CoglBlendStringFunctionType +{ + /* shared */ + COGL_BLEND_STRING_FUNCTION_AUTO_COMPOSITE, + COGL_BLEND_STRING_FUNCTION_ADD, + + /* texture combine only */ + COGL_BLEND_STRING_FUNCTION_REPLACE, + COGL_BLEND_STRING_FUNCTION_MODULATE, + COGL_BLEND_STRING_FUNCTION_ADD_SIGNED, + COGL_BLEND_STRING_FUNCTION_INTERPOLATE, + COGL_BLEND_STRING_FUNCTION_SUBTRACT, + COGL_BLEND_STRING_FUNCTION_DOT3_RGB, + COGL_BLEND_STRING_FUNCTION_DOT3_RGBA +} CoglBlendStringFunctionType; + +typedef struct _CoglBlendStringFunctionInfo +{ + enum _CoglBlendStringFunctionType type; + const char *name; + size_t name_len; + int argc; +} CoglBlendStringFunctionInfo; + +typedef struct _CoglBlendStringStatement +{ + CoglBlendStringChannelMask mask; + const CoglBlendStringFunctionInfo *function; + CoglBlendStringArgument args[3]; +} CoglBlendStringStatement; + + +gboolean +_cogl_blend_string_compile (const char *string, + CoglBlendStringContext context, + CoglBlendStringStatement *statements, + GError **error); + +void +_cogl_blend_string_split_rgba_statement (CoglBlendStringStatement *statement, + CoglBlendStringStatement *rgb, + CoglBlendStringStatement *a); + +GQuark +_cogl_blend_string_error_quark (void); + +#endif /* COGL_BLEND_STRING_H */ + diff --git a/clutter/cogl/common/cogl-debug.c b/clutter/cogl/common/cogl-debug.c index 85721fc22..b6dfd7559 100644 --- a/clutter/cogl/common/cogl-debug.c +++ b/clutter/cogl/common/cogl-debug.c @@ -39,7 +39,8 @@ static const GDebugKey cogl_debug_keys[] = { { "draw", COGL_DEBUG_DRAW }, { "pango", COGL_DEBUG_PANGO }, { "rectangles", COGL_DEBUG_RECTANGLES }, - { "handle", COGL_DEBUG_HANDLE } + { "handle", COGL_DEBUG_HANDLE }, + { "blend-strings", COGL_DEBUG_BLEND_STRINGS } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/clutter/cogl/common/cogl-material-private.h b/clutter/cogl/common/cogl-material-private.h index 05d4242dd..f0e053eff 100644 --- a/clutter/cogl/common/cogl-material-private.h +++ b/clutter/cogl/common/cogl-material-private.h @@ -78,6 +78,8 @@ struct _CoglMaterialLayer CoglMaterialLayerCombineSrc texture_combine_alpha_src[3]; CoglMaterialLayerCombineOp texture_combine_alpha_op[3]; + GLfloat texture_combine_constant[4]; + /* TODO: Support purely GLSL based material layers */ CoglMatrix matrix; @@ -114,8 +116,15 @@ struct _CoglMaterial GLfloat alpha_func_reference; /* Determines how this material is blended with other primitives */ - CoglMaterialBlendFactor blend_src_factor; - CoglMaterialBlendFactor blend_dst_factor; +#ifndef HAVE_COGL_GLES + GLenum blend_equation_rgb; + GLenum blend_equation_alpha; + CoglMaterialBlendFactor blend_dst_factor_alpha; + CoglMaterialBlendFactor blend_src_factor_alpha; + GLfloat blend_constant[4]; +#endif + CoglMaterialBlendFactor blend_src_factor_rgb; + CoglMaterialBlendFactor blend_dst_factor_rgb; GList *layers; }; diff --git a/clutter/cogl/common/cogl-material.c b/clutter/cogl/common/cogl-material.c index c456cac6a..cc1ae0691 100644 --- a/clutter/cogl/common/cogl-material.c +++ b/clutter/cogl/common/cogl-material.c @@ -35,6 +35,7 @@ #include "cogl-material-private.h" #include "cogl-texture-private.h" +#include "cogl-blend-string.h" #include #include @@ -50,6 +51,8 @@ #ifdef HAVE_COGL_GL #define glActiveTexture ctx->pf_glActiveTexture #define glClientActiveTexture ctx->pf_glClientActiveTexture +#define glBlendFuncSeparate ctx->pf_glBlendFuncSeparate +#define glBlendEquationSeparate ctx->pf_glBlendEquationSeparate #endif static void _cogl_material_free (CoglMaterial *tex); @@ -60,6 +63,12 @@ COGL_HANDLE_DEFINE (MaterialLayer, material_layer); /* #define DISABLE_MATERIAL_CACHE 1 */ +GQuark +_cogl_material_error_quark (void) +{ + return g_quark_from_static_string ("cogl-material-error-quark"); +} + CoglHandle cogl_material_new (void) { @@ -88,8 +97,18 @@ cogl_material_new (void) material->flags |= COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC; /* Not the same as the GL default, but seems saner... */ - material->blend_src_factor = COGL_MATERIAL_BLEND_FACTOR_SRC_ALPHA; - material->blend_dst_factor = COGL_MATERIAL_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; +#ifndef HAVE_COGL_GLES + material->blend_equation_rgb = GL_FUNC_ADD; + material->blend_equation_alpha = GL_FUNC_ADD; + material->blend_src_factor_alpha = GL_SRC_ALPHA; + material->blend_dst_factor_alpha = GL_ONE_MINUS_SRC_ALPHA; + material->blend_constant[0] = 0; + material->blend_constant[1] = 0; + material->blend_constant[2] = 0; + material->blend_constant[3] = 0; +#endif + material->blend_src_factor_rgb = GL_SRC_ALPHA; + material->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA; material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC; material->layers = NULL; @@ -408,6 +427,179 @@ cogl_material_set_alpha_test_function (CoglHandle handle, material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC; } +GLenum +arg_to_gl_blend_factor (CoglBlendStringArgument *arg) +{ + if (arg->source.is_zero) + return GL_ZERO; + if (arg->factor.is_one) + return GL_ONE; + else if (arg->factor.is_src_alpha_saturate) + return GL_SRC_ALPHA_SATURATE; + else if (arg->factor.source.info->type == + COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR) + { + if (arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_SRC_COLOR; + else + return GL_SRC_COLOR; + } + else + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_SRC_ALPHA; + else + return GL_SRC_ALPHA; + } + } + else if (arg->factor.source.info->type == + COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR) + { + if (arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_DST_COLOR; + else + return GL_DST_COLOR; + } + else + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_DST_ALPHA; + else + return GL_DST_ALPHA; + } + } +#ifndef HAVE_COGL_GLES + else if (arg->factor.source.info->type == + COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT) + { + if (arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_CONSTANT_COLOR; + else + return GL_CONSTANT_COLOR; + } + else + { + if (arg->factor.source.one_minus) + return GL_ONE_MINUS_CONSTANT_ALPHA; + else + return GL_CONSTANT_ALPHA; + } + } +#endif + + g_warning ("Unable to determine valid blend factor from blend string\n"); + return GL_ONE; +} + +void +setup_blend_state (CoglBlendStringStatement *statement, + GLenum *blend_equation, + CoglMaterialBlendFactor *blend_src_factor, + CoglMaterialBlendFactor *blend_dst_factor) +{ +#ifndef HAVE_COGL_GLES + switch (statement->function->type) + { + case COGL_BLEND_STRING_FUNCTION_ADD: + *blend_equation = GL_FUNC_ADD; + break; + /* TODO - add more */ + default: + g_warning ("Unsupported blend function given"); + *blend_equation = GL_FUNC_ADD; + } +#endif + + *blend_src_factor = arg_to_gl_blend_factor (&statement->args[0]); + *blend_dst_factor = arg_to_gl_blend_factor (&statement->args[1]); +} + +gboolean +cogl_material_set_blend (CoglHandle handle, + const char *blend_description, + GError **error) +{ + CoglMaterial *material; + CoglBlendStringStatement statements[2]; + CoglBlendStringStatement split[2]; + CoglBlendStringStatement *rgb; + CoglBlendStringStatement *a; + int count; + + g_return_val_if_fail (cogl_is_material (handle), FALSE); + + material = _cogl_material_pointer_from_handle (handle); + + count = + _cogl_blend_string_compile (blend_description, + COGL_BLEND_STRING_CONTEXT_BLENDING, + statements, + error); + if (!count) + return FALSE; + + if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + _cogl_blend_string_split_rgba_statement (statements, + &split[0], &split[1]); + rgb = &split[0]; + a = &split[1]; + } + else + { + rgb = &statements[0]; + a = &statements[1]; + } + +#ifndef HAVE_COGL_GLES + setup_blend_state (rgb, + &material->blend_equation_rgb, + &material->blend_src_factor_rgb, + &material->blend_dst_factor_rgb); + setup_blend_state (a, + &material->blend_equation_alpha, + &material->blend_src_factor_alpha, + &material->blend_dst_factor_alpha); +#else + setup_blend_state (rgb, + NULL, + &material->blend_src_factor_rgb, + &material->blend_dst_factor_rgb); +#endif + + material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC; + + return TRUE; +} + +void +cogl_material_set_blend_constant (CoglHandle handle, + CoglColor *constant_color) +{ +#ifndef HAVE_COGL_GLES + CoglMaterial *material; + GLfloat *constant; + + g_return_if_fail (cogl_is_material (handle)); + + material = _cogl_material_pointer_from_handle (handle); + + constant = material->blend_constant; + constant[0] = cogl_color_get_red_float (constant_color); + constant[1] = cogl_color_get_green_float (constant_color); + constant[2] = cogl_color_get_blue_float (constant_color); + constant[3] = cogl_color_get_alpha_float (constant_color); + + material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC; +#endif +} + void cogl_material_set_blend_factors (CoglHandle handle, CoglMaterialBlendFactor src_factor, @@ -418,8 +610,12 @@ cogl_material_set_blend_factors (CoglHandle handle, g_return_if_fail (cogl_is_material (handle)); material = _cogl_material_pointer_from_handle (handle); - material->blend_src_factor = src_factor; - material->blend_dst_factor = dst_factor; + material->blend_src_factor_rgb = src_factor; + material->blend_dst_factor_rgb = dst_factor; +#ifndef HAVE_COGL_GLES + material->blend_src_factor_alpha = src_factor; + material->blend_dst_factor_alpha = dst_factor; +#endif material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC; } @@ -530,6 +726,169 @@ cogl_material_set_layer (CoglHandle material_handle, layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY; } +static void +setup_texture_combine_state (CoglBlendStringStatement *statement, + CoglMaterialLayerCombineFunc *texture_combine_func, + CoglMaterialLayerCombineSrc *texture_combine_src, + CoglMaterialLayerCombineOp *texture_combine_op) +{ + int i; + + switch (statement->function->type) + { + case COGL_BLEND_STRING_FUNCTION_AUTO_COMPOSITE: + *texture_combine_func = GL_MODULATE; /* FIXME */ + break; + case COGL_BLEND_STRING_FUNCTION_REPLACE: + *texture_combine_func = GL_REPLACE; + break; + case COGL_BLEND_STRING_FUNCTION_MODULATE: + *texture_combine_func = GL_MODULATE; + break; + case COGL_BLEND_STRING_FUNCTION_ADD: + *texture_combine_func = GL_ADD; + break; + case COGL_BLEND_STRING_FUNCTION_ADD_SIGNED: + *texture_combine_func = GL_ADD_SIGNED; + break; + case COGL_BLEND_STRING_FUNCTION_INTERPOLATE: + *texture_combine_func = GL_INTERPOLATE; + break; + case COGL_BLEND_STRING_FUNCTION_SUBTRACT: + *texture_combine_func = GL_SUBTRACT; + break; + case COGL_BLEND_STRING_FUNCTION_DOT3_RGB: + *texture_combine_func = GL_DOT3_RGB; + break; + case COGL_BLEND_STRING_FUNCTION_DOT3_RGBA: + *texture_combine_func = GL_DOT3_RGBA; + break; + } + + for (i = 0; i < statement->function->argc; i++) + { + CoglBlendStringArgument *arg = &statement->args[i]; + + switch (arg->source.info->type) + { + case COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT: + texture_combine_src[i] = GL_CONSTANT; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE: + texture_combine_src[i] = GL_TEXTURE; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N: + texture_combine_src[i] = + GL_TEXTURE0 + arg->source.texture; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY: + texture_combine_src[i] = GL_PRIMARY_COLOR; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS: + texture_combine_src[i] = GL_PREVIOUS; + break; + default: + g_warning ("Unexpected texture combine source"); + texture_combine_src[i] = GL_TEXTURE; + } + + if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + if (statement->args[i].source.one_minus) + texture_combine_op[i] = GL_ONE_MINUS_SRC_COLOR; + else + texture_combine_op[i] = GL_SRC_COLOR; + } + else + { + if (statement->args[i].source.one_minus) + texture_combine_op[i] = GL_ONE_MINUS_SRC_ALPHA; + else + texture_combine_op[i] = GL_SRC_ALPHA; + } + } +} + +gboolean +cogl_material_set_layer_combine (CoglHandle handle, + gint layer_index, + const char *combine_description, + GError **error) +{ + CoglMaterial *material; + CoglMaterialLayer *layer; + CoglBlendStringStatement statements[2]; + CoglBlendStringStatement split[2]; + CoglBlendStringStatement *rgb; + CoglBlendStringStatement *a; + int count; + + g_return_val_if_fail (cogl_is_material (handle), FALSE); + + material = _cogl_material_pointer_from_handle (handle); + layer = _cogl_material_get_layer (material, layer_index, TRUE); + + count = + _cogl_blend_string_compile (combine_description, + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE, + statements, + error); + if (!count) + return FALSE; + + if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + _cogl_blend_string_split_rgba_statement (statements, + &split[0], &split[1]); + rgb = &split[0]; + a = &split[1]; + } + else + { + rgb = &statements[0]; + a = &statements[1]; + } + + setup_texture_combine_state (rgb, + &layer->texture_combine_rgb_func, + layer->texture_combine_rgb_src, + layer->texture_combine_rgb_op); + + setup_texture_combine_state (a, + &layer->texture_combine_alpha_func, + layer->texture_combine_alpha_src, + layer->texture_combine_alpha_op); + + + layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY; + layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE; + return TRUE; +} + +void +cogl_material_set_layer_combine_constant (CoglHandle handle, + gint layer_index, + CoglColor *constant_color) +{ + CoglMaterial *material; + CoglMaterialLayer *layer; + GLfloat *constant; + + g_return_if_fail (cogl_is_material (handle)); + + material = _cogl_material_pointer_from_handle (handle); + layer = _cogl_material_get_layer (material, layer_index, TRUE); + + constant = layer->texture_combine_constant; + constant[0] = cogl_color_get_red_float (constant_color); + constant[1] = cogl_color_get_green_float (constant_color); + constant[2] = cogl_color_get_blue_float (constant_color); + constant[3] = cogl_color_get_alpha_float (constant_color); + + layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY; + layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE; +} + void cogl_material_set_layer_combine_function ( CoglHandle handle, @@ -713,7 +1072,7 @@ cogl_material_get_cogl_enable_flags (CoglHandle material_handle) * probably sensible to try and avoid list manipulation for every * primitive emitted in a scene, every frame. * - * Alternativly; we could either add a _foreach function, or maybe + * Alternatively; we could either add a _foreach function, or maybe * a function that gets a passed a buffer (that may be stack allocated) * by the caller. */ @@ -851,6 +1210,9 @@ _cogl_material_layer_flush_gl_sampler_state (CoglMaterialLayer *layer, GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, layer->texture_combine_alpha_op[2])); } + + GE (glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, + layer->texture_combine_constant)); } #ifndef DISABLE_MATERIAL_CACHE @@ -1122,7 +1484,39 @@ _cogl_material_flush_base_gl_state (CoglMaterial *material) if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC && material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC)) { - GE (glBlendFunc (material->blend_src_factor, material->blend_dst_factor)); +#if defined (HAVE_COGL_GLES2) + gboolean have_blend_equation_seperate = TRUE; +#elif defined (HAVE_COGL_GL) + gboolean have_blend_equation_seperate = FALSE; + if (ctx->pf_glBlendEquationSeparate) /* Only GL 2.0 + */ + have_blend_equation_seperate = TRUE; +#endif + +#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */ + if (material->blend_src_factor_rgb != material->blend_src_factor_alpha + || (material->blend_src_factor_rgb != + material->blend_src_factor_alpha)) + { + if (have_blend_equation_seperate && + material->blend_equation_rgb != material->blend_equation_alpha) + GE (glBlendEquationSeparate (material->blend_equation_rgb, + material->blend_equation_alpha)); + else + GE (glBlendEquation (material->blend_equation_rgb)); + + GE (glBlendFuncSeparate (material->blend_src_factor_rgb, + material->blend_dst_factor_rgb, + material->blend_src_factor_alpha, + material->blend_dst_factor_alpha)); + GE (glBlendColor (material->blend_constant[0], + material->blend_constant[1], + material->blend_constant[2], + material->blend_constant[3])); + } + else +#endif + GE (glBlendFunc (material->blend_src_factor_rgb, + material->blend_dst_factor_rgb)); } } diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index 2c17408c2..b5d89f30b 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -136,6 +136,9 @@ cogl_create_context () _context->pf_glActiveTexture = NULL; _context->pf_glClientActiveTexture = NULL; + _context->pf_glBlendFuncSeparate = NULL; + _context->pf_glBlendEquationSeparate = NULL; + /* Initialise the clip stack */ _cogl_clip_stack_state_init (); diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index 2660ed812..f490d39ac 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -164,6 +164,9 @@ typedef struct COGL_PFNGLACTIVETEXTUREPROC pf_glActiveTexture; COGL_PFNGLCLIENTACTIVETEXTUREPROC pf_glClientActiveTexture; + + COGL_PFNGLBLENDFUNCSEPARATEPROC pf_glBlendFuncSeparate; + COGL_PFNGLBLENDEQUATIONSEPARATEPROC pf_glBlendEquationSeparate; } CoglContext; CoglContext * diff --git a/clutter/cogl/gl/cogl-defines.h.in b/clutter/cogl/gl/cogl-defines.h.in index 8e2df9ec3..bde93c559 100644 --- a/clutter/cogl/gl/cogl-defines.h.in +++ b/clutter/cogl/gl/cogl-defines.h.in @@ -1020,6 +1020,18 @@ typedef void (APIENTRYP COGL_PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); +typedef void + (APIENTRYP COGL_PFNGLBLENDFUNCSEPARATEPROC) + (GLenum srcRGB, + GLenum dstRGB, + GLenum srcAlpha, + GLenum dstAlpha); + +typedef void + (APIENTRYP COGL_PFNGLBLENDEQUATIONSEPARATEPROC) + (GLenum modeRGB, + GLenum modeAlpha); + G_END_DECLS #endif diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index b2c5417e7..8fcd3eabd 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -486,6 +486,16 @@ _cogl_features_init (void) (COGL_PFNGLCLIENTACTIVETEXTUREPROC) cogl_get_proc_address ("glClientActiveTexture"); + /* Available in 1.4 */ + ctx->pf_glBlendFuncSeparate = + (COGL_PFNGLBLENDFUNCSEPARATEPROC) + cogl_get_proc_address ("glBlendFuncSeparate"); + + /* Available in 2.0 */ + ctx->pf_glBlendEquationSeparate = + (COGL_PFNGLBLENDEQUATIONSEPARATEPROC) + cogl_get_proc_address ("glBlendEquationSeparate"); + /* Cache features */ ctx->feature_flags = flags; ctx->features_cached = TRUE; diff --git a/clutter/cogl/gles/cogl-gles2-wrapper.c b/clutter/cogl/gles/cogl-gles2-wrapper.c index aec15b7a0..979ade598 100644 --- a/clutter/cogl/gles/cogl-gles2-wrapper.c +++ b/clutter/cogl/gles/cogl-gles2-wrapper.c @@ -1300,13 +1300,20 @@ cogl_gles2_wrapper_bind_texture (GLenum target, GLuint texture, } void -cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLfloat param) +cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLint param) { /* This function is only used to set the texture mode once to GL_MODULATE. The shader is hard-coded to modulate the texture so nothing needs to be done here. */ } +void +cogl_wrap_glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params) +{ + /* FIXME: Currently needed to support texture combining using + * COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT */ +} + void cogl_wrap_glClientActiveTexture (GLenum texture) { diff --git a/clutter/cogl/gles/cogl-gles2-wrapper.h b/clutter/cogl/gles/cogl-gles2-wrapper.h index bd7f6d978..51bee2d5c 100644 --- a/clutter/cogl/gles/cogl-gles2-wrapper.h +++ b/clutter/cogl/gles/cogl-gles2-wrapper.h @@ -259,6 +259,7 @@ struct _CoglGles2WrapperShader #define GL_TEXTURE_ENV 0x2300 #define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 #define GL_MODULATE 0x2100 #define GL_EXP 0x8000 @@ -327,7 +328,8 @@ void cogl_wrap_glColorPointer (GLint size, GLenum type, GLsizei stride, void cogl_wrap_glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer); -void cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLfloat param); +void cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLint param); +void cogl_wrap_glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params); void cogl_wrap_glClientActiveTexture (GLenum texture); void cogl_wrap_glActiveTexture (GLenum texture); @@ -384,6 +386,7 @@ void _cogl_gles2_clear_cache_for_program (CoglHandle program); #define glColorPointer cogl_wrap_glColorPointer #define glNormalPointer cogl_wrap_glNormalPointer #define glTexEnvi cogl_wrap_glTexEnvi +#define glTexEnvfv cogl_wrap_glTexEnvfv #define glActiveTexture cogl_wrap_glActiveTexture #define glClientActiveTexture cogl_wrap_glClientActiveTexture #define glEnableClientState cogl_wrap_glEnableClientState diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index 8f4cf733e..99a18f154 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -442,6 +442,7 @@ _cogl_texture_download_from_gl (CoglTexture *tex, gint bpp; GLint viewport[4]; CoglBitmap alpha_bmp; + CoglHandle prev_source; _COGL_GET_CONTEXT (ctx, FALSE); @@ -477,31 +478,21 @@ _cogl_texture_download_from_gl (CoglTexture *tex, if (ctx->texture_download_material == COGL_INVALID_HANDLE) { ctx->texture_download_material = cogl_material_new (); - cogl_material_set_layer_combine_function ( - ctx->texture_download_material, - 0, /* layer */ - COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, - COGL_MATERIAL_LAYER_COMBINE_FUNC_REPLACE); - cogl_material_set_layer_combine_arg_src ( - ctx->texture_download_material, - 0, /* layer */ - 0, /* arg */ - COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, - COGL_MATERIAL_LAYER_COMBINE_SRC_TEXTURE); - cogl_material_set_blend_factors (ctx->texture_download_material, - COGL_MATERIAL_BLEND_FACTOR_ONE, - COGL_MATERIAL_BLEND_FACTOR_ZERO); + cogl_material_set_blend (ctx->texture_download_material, + "RGBA = ADD (SRC_COLOR, 0)", + NULL); } + prev_source = cogl_handle_ref (ctx->source_material); + cogl_set_source (ctx->texture_download_material); + cogl_material_set_layer (ctx->texture_download_material, 0, tex); - cogl_material_set_layer_combine_arg_op ( - ctx->texture_download_material, - 0, /* layer */ - 0, /* arg */ - COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, - COGL_MATERIAL_LAYER_COMBINE_OP_SRC_COLOR); - cogl_material_flush_gl_state (ctx->texture_download_material, NULL); + cogl_material_set_layer_combine (ctx->texture_download_material, + 0, /* layer */ + "RGBA = REPLACE (TEXTURE)", + NULL); + _cogl_texture_draw_and_read (tex, target_bmp, viewport); /* Check whether texture has alpha and framebuffer not */ @@ -534,13 +525,11 @@ _cogl_texture_download_from_gl (CoglTexture *tex, alpha_bmp.height); /* Draw alpha values into RGB channels */ - cogl_material_set_layer_combine_arg_op ( - ctx->texture_download_material, - 0, /* layer */ - 0, /* arg */ - COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, - COGL_MATERIAL_LAYER_COMBINE_OP_SRC_ALPHA); - cogl_material_flush_gl_state (ctx->texture_download_material, NULL); + cogl_material_set_layer_combine (ctx->texture_download_material, + 0, /* layer */ + "RGBA = REPLACE (TEXTURE[A])", + NULL); + _cogl_texture_draw_and_read (tex, &alpha_bmp, viewport); /* Copy temp R to target A */ @@ -568,6 +557,10 @@ _cogl_texture_download_from_gl (CoglTexture *tex, _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_pop (); + /* restore the original material */ + cogl_set_source (prev_source); + cogl_handle_unref (prev_source); + return TRUE; } diff --git a/doc/reference/cogl/Makefile.am b/doc/reference/cogl/Makefile.am index 82b96928f..c2f075e4d 100644 --- a/doc/reference/cogl/Makefile.am +++ b/doc/reference/cogl/Makefile.am @@ -24,7 +24,7 @@ DOC_SOURCE_DIR=../../../clutter/cogl SCANGOBJ_OPTIONS=--type-init-func="g_type_init()" # Extra options to supply to gtkdoc-scan. -# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS=--deprecated-guards="COGL_DISABLE_DEPRECATED" # Extra options to supply to gtkdoc-mkdb. @@ -70,12 +70,15 @@ HTML_IMAGES= # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml -content_files= version.xml +content_files = \ + version.xml \ + blend-strings.xml # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml -expand_content_files= +expand_content_files = \ + blend-strings.xml # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget diff --git a/doc/reference/cogl/blend-strings.xml b/doc/reference/cogl/blend-strings.xml new file mode 100644 index 000000000..9c3c8d3c9 --- /dev/null +++ b/doc/reference/cogl/blend-strings.xml @@ -0,0 +1,130 @@ + + +]> + + + +Material Blend Strings +3 +COGL Library + + + +Material Blend Strings +A simple syntax and grammar for describing blending and texture +combining functions. + + + +Cogl Blend Strings + +Describing GPU blending and texture combining states is rather awkward to do +in a consise but also readable fashion. Cogl helps by supporting +string based descriptions using a simple syntax. + + +
+Some examples + +Here is an example used for blending: + +"RGBA = ADD (SRC_COLOR * (SRC_COLOR[A]), DST_COLOR * (1-SRC_COLOR[A]))" + +In OpenGL terms this replaces glBlendFunc[Separate] and +glBlendEquation[Separate] + +Actually in this case it's more verbose than the GL equivalent: + + +glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + +But unless you are familiar with OpenGL or refer to its API documentation +you wouldn't know that the default function used by OpenGL is GL_FUNC_ADD +nor would you know that the above arguments determine what the source color +and destination color will be multiplied by before being adding. + + +Here is an example used for texture combining: + +"RGB = REPLACE (PREVIOUS)" +"A = MODULATE (PREVIOUS, TEXTURE)" + + +In OpenGL terms this replaces glTexEnv, and the above example is equivalent +to this OpenGL code: + + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + +
+ +
+Here's the syntax + + +<statement>: + <channel-mask>=<function-name>(<arg-list>) + + You can either use a single statement with an RGBA channel-mask or you can use + two statements; one with an A channel-mask and the other with an RGB + channel-mask. + +<channel-mask>: + A or RGB or RGBA + +<function-name>: + [A-Za-z_]* + +<arg-list>: + <arg>,<arg> + or <arg> + or "" + + I.e. functions may take 0 or more arguments + +<arg>: + <color-source> + 1 - <color-source> : Only intended for texture combining + <color-source> * ( <factor> ) : Only intended for blending + 0 : Only intended for blending + + See the blending or texture combining sections for further notes and examples. + +<color-source>: + <source-name>[<channel-mask>] + <source-name> + + See the blending or texture combining sections for the list of source-names + valid in each context. + + If a channel mask is not given then the channel mask of the statement + is assumed instead. + +<factor>: + 0 + 1 + <color-source> + 1-<color-source> + SRC_ALPHA_SATURATE + + +
+ + +
+ + +
diff --git a/doc/reference/cogl/cogl-docs.xml b/doc/reference/cogl/cogl-docs.xml index ba5d2cf89..87d665f42 100644 --- a/doc/reference/cogl/cogl-docs.xml +++ b/doc/reference/cogl/cogl-docs.xml @@ -55,6 +55,7 @@ + diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 3032a6863..ca7664132 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -354,6 +354,8 @@ CoglMaterialAlphaFunc cogl_material_set_alpha_test_function CoglMaterialBlendFactor cogl_material_set_blend_factors +cogl_material_set_blend +cogl_material_set_blend_constant cogl_material_set_layer cogl_material_remove_layer CoglMaterialLayerCombineFunc @@ -363,6 +365,8 @@ CoglMaterialLayerCombineSrc cogl_material_set_layer_combine_arg_src CoglMaterialLayerCombineOp cogl_material_set_layer_combine_arg_op +cogl_material_set_layer_combine +cogl_material_set_layer_combine_constant cogl_material_set_layer_matrix CoglMaterial diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 5fd68034a..253cb64ed 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -28,6 +28,7 @@ test_conformance_SOURCES = \ test-anchors.c \ test-npot-texture.c \ test-model.c \ + test-blend-strings.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: @@ -59,7 +60,7 @@ clean-wrappers: # NB: BUILT_SOURCES here a misnomer. We aren't building source, just inserting # a phony rule that will generate symlink scripts for running individual tests BUILT_SOURCES = wrappers - + test_conformance_CFLAGS = \ -I$(top_srcdir)/ \ -I$(top_srcdir)/clutter \ diff --git a/tests/conform/test-blend-strings.c b/tests/conform/test-blend-strings.c new file mode 100644 index 000000000..01979964d --- /dev/null +++ b/tests/conform/test-blend-strings.c @@ -0,0 +1,410 @@ + +#include +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +#define QUAD_WIDTH 20 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24); +#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16); +#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8); +#define MASK_ALPHA(COLOR) (COLOR & 0xff); + +#define BLEND_CONSTANT_UNUSED 0xDEADBEEF +#define TEX_CONSTANT_UNUSED 0xDEADBEEF + +typedef struct _TestState +{ + guint frame; + ClutterGeometry stage_geom; +} TestState; + + +static void +check_pixel (GLubyte *pixel, guint32 color) +{ + guint8 r = MASK_RED (color); + guint8 g = MASK_GREEN (color); + guint8 b = MASK_BLUE (color); + guint8 a = MASK_ALPHA (color); + + if (g_test_verbose ()) + g_print (" expected = %x, %x, %x, %x\n", + r, g, b, a); + /* FIXME - allow for hardware in-precision */ + g_assert (pixel[RED] == r); + g_assert (pixel[GREEN] == g); + g_assert (pixel[BLUE] == b); + + /* FIXME + * We ignore the alpha, since we don't know if our render target is + * RGB or RGBA */ + /* g_assert (pixel[ALPHA] == a); */ +} + +static void +test_blend (TestState *state, + int x, + int y, + guint32 src_color, + guint32 dst_color, + const char *blend_string, + guint32 blend_constant, + guint32 expected_result) +{ + /* src color */ + guint8 Sr = MASK_RED (src_color); + guint8 Sg = MASK_GREEN (src_color); + guint8 Sb = MASK_BLUE (src_color); + guint8 Sa = MASK_ALPHA (src_color); + /* dest color */ + guint8 Dr = MASK_RED (dst_color); + guint8 Dg = MASK_GREEN (dst_color); + guint8 Db = MASK_BLUE (dst_color); + guint8 Da = MASK_ALPHA (dst_color); + /* blend constant - when applicable */ + guint8 Br = MASK_RED (blend_constant); + guint8 Bg = MASK_GREEN (blend_constant); + guint8 Bb = MASK_BLUE (blend_constant); + guint8 Ba = MASK_ALPHA (blend_constant); + CoglColor blend_const_color; + + CoglHandle material; + gboolean status; + GError *error = NULL; + GLubyte pixel[4]; + GLint y_off; + GLint x_off; + + /* First write out the destination color without any blending... */ + material = cogl_material_new (); + cogl_material_set_color4ub (material, Dr, Dg, Db, Da); + cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL); + cogl_set_source (material); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_material_unref (material); + + /* + * Now blend a rectangle over our well defined destination: + */ + + material = cogl_material_new (); + cogl_material_set_color4ub (material, Sr, Sg, Sb, Sa); + + status = cogl_material_set_blend (material, blend_string, &error); + if (!status) + { + /* It's not strictly a test failure; you need a more capable GPU or + * driver to test this blend string. */ + g_debug ("Failed to test blend string %s: %s", + blend_string, error->message); + } + + cogl_color_set_from_4ub (&blend_const_color, Br, Bg, Bb, Ba); + cogl_material_set_blend_constant (material, &blend_const_color); + + cogl_set_source (material); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_material_unref (material); + + /* See what we got... */ + + /* NB: glReadPixels is done in GL screen space so y = 0 is at the bottom */ + y_off = state->stage_geom.height - y * QUAD_WIDTH - (QUAD_WIDTH / 2); + x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2); + + /* XXX: + * We haven't always had good luck with GL drivers implementing glReadPixels + * reliably and skipping the first two frames improves our chances... */ + if (state->frame <= 2) + return; + + glReadPixels (x_off, y_off, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); + if (g_test_verbose ()) + { + g_print ("test_blend (%d, %d):\n%s\n", x, y, blend_string); + g_print (" src color = %02x, %02x, %02x, %02x\n", Sr, Sg, Sb, Sa); + g_print (" dst color = %02x, %02x, %02x, %02x\n", Dr, Dg, Db, Da); + if (blend_constant != BLEND_CONSTANT_UNUSED) + g_print (" blend constant = %02x, %02x, %02x, %02x\n", + Br, Bg, Bb, Ba); + else + g_print (" blend constant = UNUSED\n"); + g_print (" result = %x, %x, %x, %x\n", + pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]); + } + + check_pixel (pixel, expected_result); +} + +static CoglHandle +make_texture (guint32 color) +{ + guchar *tex_data, *p; + guint8 r = MASK_RED (color); + guint8 g = MASK_GREEN (color); + guint8 b = MASK_BLUE (color); + guint8 a = MASK_ALPHA (color); + CoglHandle tex; + + tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4); + + for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;) + { + *(--p) = a; + *(--p) = b; + *(--p) = g; + *(--p) = r; + } + + tex = cogl_texture_new_from_data (QUAD_WIDTH, + QUAD_WIDTH, + COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888, + COGL_PIXEL_FORMAT_ANY, + QUAD_WIDTH * 4, + tex_data); + + g_free (tex_data); + + return tex; +} + +static void +test_tex_combine (TestState *state, + int x, + int y, + guint32 tex0_color, + guint32 tex1_color, + guint32 combine_constant, + const char *combine_string, + guint32 expected_result) +{ + CoglHandle tex0, tex1; + + /* combine constant - when applicable */ + guint8 Cr = MASK_RED (combine_constant); + guint8 Cg = MASK_GREEN (combine_constant); + guint8 Cb = MASK_BLUE (combine_constant); + guint8 Ca = MASK_ALPHA (combine_constant); + CoglColor combine_const_color; + + CoglHandle material; + gboolean status; + GError *error = NULL; + GLubyte pixel[4]; + GLint y_off; + GLint x_off; + + + tex0 = make_texture (tex0_color); + tex1 = make_texture (tex1_color); + + material = cogl_material_new (); + + cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80); + cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL); + + cogl_material_set_layer (material, 0, tex0); + cogl_material_set_layer_combine (material, 0, + "RGBA = REPLACE (TEXTURE)", NULL); + + cogl_material_set_layer (material, 1, tex1); + status = cogl_material_set_layer_combine (material, 1, + combine_string, &error); + if (!status) + { + /* It's not strictly a test failure; you need a more capable GPU or + * driver to test this texture combine string. */ + g_debug ("Failed to test texture combine string %s: %s", + combine_string, error->message); + } + + cogl_color_set_from_4ub (&combine_const_color, Cr, Cg, Cb, Ca); + cogl_material_set_layer_combine_constant (material, 1, &combine_const_color); + + cogl_set_source (material); + cogl_rectangle (x * QUAD_WIDTH, + y * QUAD_WIDTH, + x * QUAD_WIDTH + QUAD_WIDTH, + y * QUAD_WIDTH + QUAD_WIDTH); + cogl_material_unref (material); + + cogl_handle_unref (tex0); + cogl_handle_unref (tex1); + + /* See what we got... */ + + /* NB: glReadPixels is done in GL screen space so y = 0 is at the bottom */ + y_off = state->stage_geom.height - y * QUAD_WIDTH - (QUAD_WIDTH / 2); + x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2); + + /* XXX: + * We haven't always had good luck with GL drivers implementing glReadPixels + * reliably and skipping the first two frames improves our chances... */ + if (state->frame <= 2) + return; + + glReadPixels (x_off, y_off, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); + if (g_test_verbose ()) + { + g_print ("test_tex_combine (%d, %d):\n%s\n", x, y, combine_string); + g_print (" texture 0 color = 0x%08lX\n", (unsigned long)tex0_color); + g_print (" texture 1 color = 0x%08lX\n", (unsigned long)tex1_color); + if (combine_constant != TEX_CONSTANT_UNUSED) + g_print (" combine constant = %02x, %02x, %02x, %02x\n", + Cr, Cg, Cb, Ca); + else + g_print (" combine constant = UNUSED\n"); + g_print (" result = %02x, %02x, %02x, %02x\n", + pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]); + } + + check_pixel (pixel, expected_result); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + int frame_num; + + test_blend (state, 0, 0, /* position */ + 0xff0000ff, /* src */ + 0xffffffff, /* dst */ + "RGBA = ADD (SRC_COLOR, 0)", + BLEND_CONSTANT_UNUSED, + 0xff0000ff); /* expected */ + + test_blend (state, 1, 0, /* position */ + 0x11223344, /* src */ + 0x11223344, /* dst */ + "RGBA = ADD (SRC_COLOR, DST_COLOR)", + BLEND_CONSTANT_UNUSED, + 0x22446688); /* expected */ + + test_blend (state, 2, 0, /* position */ + 0x80808080, /* src */ + 0xffffffff, /* dst */ + "RGBA = ADD (SRC_COLOR * (CONSTANT), 0)", + 0x80808080, /* constant (RGBA all = 0.5 when normalized) */ + 0x40404040); /* expected */ + + test_blend (state, 3, 0, /* position */ + 0x80000080, /* src (alpha = 0.5 when normalized) */ + 0x40000000, /* dst */ + "RGBA = ADD (SRC_COLOR * (SRC_COLOR[A])," + " DST_COLOR * (1-SRC_COLOR[A]))", + BLEND_CONSTANT_UNUSED, + 0x60000040); /* expected */ + + /* XXX: + * For all texture combine tests tex0 will use a combine mode of + * "RGBA = REPLACE (TEXTURE)" + */ + + test_tex_combine (state, 4, 0, /* position */ + 0x11111111, /* texture 0 color */ + 0x22222222, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGBA = ADD (PREVIOUS, TEXTURE)", /* tex combine */ + 0x33333333); /* expected */ + + test_tex_combine (state, 5, 0, /* position */ + 0x40404040, /* texture 0 color */ + 0x80808080, /* texture 1 color (RGBA all = 0.5) */ + TEX_CONSTANT_UNUSED, + "RGBA = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */ + 0x20202020); /* expected */ + + test_tex_combine (state, 6, 0, /* position */ + 0xffffff80, /* texture 0 color (alpha = 0.5) */ + 0xDEADBE40, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGB = REPLACE (PREVIOUS)" + "A = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */ + 0xffffff20); /* expected */ + + /* XXX: we are assuming test_tex_combine creates a material with + * a color of 0x80808080 (i.e. the "PRIMARY" color) */ + test_tex_combine (state, 7, 0, /* position */ + 0xffffff80, /* texture 0 color (alpha = 0.5) */ + 0xDEADBE20, /* texture 1 color */ + TEX_CONSTANT_UNUSED, + "RGB = REPLACE (PREVIOUS)" + "A = MODULATE (PRIMARY, TEXTURE)", /* tex combine */ + 0xffffff10); /* expected */ + + /* XXX: Experiments have shown that for some buggy drivers, when using + * glReadPixels there is some kind of race, so we delay our test for a + * few frames and a few seconds: + */ + frame_num = state->frame++; + if (frame_num < 2) + g_usleep (G_USEC_PER_SEC); + + /* Comment this out if you want visual feedback for what this test paints */ +#if 1 + if (frame_num == 2) + clutter_main_quit (); +#endif +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_blend_strings (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + ClutterActor *stage; + ClutterActor *group; + guint idle_source; + + state.frame = 0; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_get_geometry (stage, &state.stage_geom); + + group = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (stage); + + clutter_main (); + + g_source_remove (idle_source); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 46ccef07a..ff614ae98 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -143,5 +143,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/model", test_list_model_iterate); TEST_CONFORM_SIMPLE ("/model", test_list_model_filter); + TEST_CONFORM_SIMPLE ("/material", test_blend_strings); + return g_test_run (); }