mirror of
https://github.com/brl/mutter.git
synced 2024-11-10 07:56:14 -05:00
cookbook: Added example code for texture cross-fading
Added simple image viewer which loads image file names from a directory, displays the first one, then displays the next in the list with each key press. Uses the primitive fade front in/fade back out approach. Also adapted Emmanuele's example code which uses Cogl to produce a similar effect, but within a single texture. This code loads two images specified on the command line and cross-fades between them.
This commit is contained in:
parent
dc7d62a42a
commit
83a8d0b3bb
@ -11,6 +11,8 @@ noinst_PROGRAMS = \
|
||||
layouts-stacking \
|
||||
layouts-stacking-diff-sized-actors \
|
||||
events-mouse-scroll \
|
||||
textures-crossfade \
|
||||
textures-crossfade-cogl \
|
||||
$(NULL)
|
||||
|
||||
INCLUDES = \
|
||||
@ -40,5 +42,7 @@ textures_sub_texture_SOURCES = textures-sub-texture.c
|
||||
layouts_stacking_SOURCES = layouts-stacking.c
|
||||
layouts_stacking_diff_sized_actors_SOURCES = layouts-stacking-diff-sized-actors.c
|
||||
events_mouse_scroll_SOURCES = events-mouse-scroll.c
|
||||
textures_crossfade_SOURCES = textures-crossfade.c
|
||||
textures_crossfade_cogl_SOURCES = textures-crossfade-cogl.c
|
||||
|
||||
-include $(top_srcdir)/build/autotools/Makefile.am.gitignore
|
||||
|
174
doc/cookbook/examples/textures-crossfade-cogl.c
Normal file
174
doc/cookbook/examples/textures-crossfade-cogl.c
Normal file
@ -0,0 +1,174 @@
|
||||
#include <stdlib.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
static gchar *source = NULL;
|
||||
static gchar *target = NULL;
|
||||
static guint duration = 1000;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{
|
||||
"source", 's',
|
||||
0,
|
||||
G_OPTION_ARG_FILENAME, &source,
|
||||
"The source image of the cross-fade", "FILE"
|
||||
},
|
||||
{
|
||||
"target", 't',
|
||||
0,
|
||||
G_OPTION_ARG_FILENAME, &target,
|
||||
"The target image of the cross-fade", "FILE"
|
||||
},
|
||||
{
|
||||
"duration", 'd',
|
||||
0,
|
||||
G_OPTION_ARG_INT, &duration,
|
||||
"The duration of the cross-fade, in milliseconds", "MSECS"
|
||||
},
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
_update_progress_cb (ClutterTimeline *timeline,
|
||||
guint elapsed_msecs,
|
||||
ClutterTexture *texture)
|
||||
{
|
||||
CoglHandle material = clutter_texture_get_cogl_material (texture);
|
||||
if (material == COGL_INVALID_HANDLE)
|
||||
return;
|
||||
|
||||
/* You should assume that a material can only be modified once, after
|
||||
* its creation; if you need to modify it later you should use a copy
|
||||
* instead. Cogl makes copying materials reasonably cheap
|
||||
*/
|
||||
CoglHandle copy = cogl_material_copy (material);
|
||||
|
||||
gdouble progress = clutter_timeline_get_progress (timeline);
|
||||
|
||||
/* Create the constant color to be used when combining the two
|
||||
* material layers; we use a black color with an alpha component
|
||||
* depending on the current progress of the timeline
|
||||
*/
|
||||
CoglColor constant;
|
||||
cogl_color_set_from_4ub (&constant, 0x00, 0x00, 0x00, 0xff * progress);
|
||||
|
||||
/* This sets the value of the constant color we use when combining
|
||||
* the two layers
|
||||
*/
|
||||
cogl_material_set_layer_combine_constant (copy, 1, &constant);
|
||||
|
||||
/* The Texture now owns the material */
|
||||
clutter_texture_set_cogl_material (texture, copy);
|
||||
cogl_handle_unref (copy);
|
||||
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
|
||||
}
|
||||
|
||||
static CoglHandle
|
||||
load_cogl_texture (const char *type,
|
||||
const char *file)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
CoglHandle retval = cogl_texture_new_from_file (file,
|
||||
COGL_TEXTURE_NO_SLICING,
|
||||
COGL_PIXEL_FORMAT_ANY,
|
||||
&error);
|
||||
if (error != NULL)
|
||||
{
|
||||
g_print ("Unable to load %s image: %s\n", type, error->message);
|
||||
g_error_free (error);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
print_usage_and_exit (const char *exec_name,
|
||||
int exit_code)
|
||||
{
|
||||
g_print ("Usage: %s -s <source> -t <target> [-d <duration>]\n", exec_name);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
clutter_init_with_args (&argc, &argv,
|
||||
" - Crossfade", entries,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
if (source == NULL || target == NULL)
|
||||
return print_usage_and_exit (argv[0], EXIT_FAILURE);
|
||||
|
||||
/* Load the source and target images using Cogl, because we need
|
||||
* to combine them into the same ClutterTexture.
|
||||
*/
|
||||
CoglHandle texture_1 = load_cogl_texture ("source", source);
|
||||
CoglHandle texture_2 = load_cogl_texture ("target", target);
|
||||
|
||||
/* sizes of textures */
|
||||
gfloat source_width, source_height, target_width, target_height;
|
||||
source_width = cogl_texture_get_width (texture_1);
|
||||
source_height = cogl_texture_get_height (texture_1);
|
||||
target_width = cogl_texture_get_width (texture_2);
|
||||
target_height = cogl_texture_get_height (texture_2);
|
||||
|
||||
/* Create a new Cogl material holding the two textures inside two
|
||||
* separate layers. Layer 0 is the one which will end
|
||||
* up being visible.
|
||||
*/
|
||||
CoglHandle material = cogl_material_new ();
|
||||
cogl_material_set_layer (material, 1, texture_1);
|
||||
cogl_material_set_layer (material, 0, texture_2);
|
||||
|
||||
/* Set the layer combination description for the second layer; the
|
||||
* default for Cogl is to simply multiply the layer with the
|
||||
* precendent one. In this case we interpolate the color for each
|
||||
* pixel between the pixel value of the previous layer and the
|
||||
* current one, using the alpha component of a constant color as
|
||||
* the interpolation factor.
|
||||
*/
|
||||
cogl_material_set_layer_combine (material, 1,
|
||||
"RGBA = INTERPOLATE (PREVIOUS, "
|
||||
"TEXTURE, "
|
||||
"CONSTANT[A])",
|
||||
NULL);
|
||||
|
||||
/* The material now owns the two textures */
|
||||
cogl_handle_unref (texture_1);
|
||||
cogl_handle_unref (texture_2);
|
||||
|
||||
/* Create a Texture and place it in the middle of the stage; then
|
||||
* assign the material we created earlier to the Texture for painting
|
||||
* it
|
||||
*/
|
||||
ClutterActor *stage = clutter_stage_new ();
|
||||
clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade");
|
||||
clutter_actor_set_size (stage, 600, 600);
|
||||
clutter_actor_show (stage);
|
||||
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||
|
||||
ClutterActor *texture = clutter_texture_new ();
|
||||
clutter_actor_set_size (texture, source_width, source_height);
|
||||
clutter_texture_set_cogl_material (CLUTTER_TEXTURE (texture), material);
|
||||
clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
|
||||
clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5));
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture);
|
||||
cogl_handle_unref (material);
|
||||
|
||||
/* The timeline will drive the cross-fading */
|
||||
ClutterTimeline *timeline = clutter_timeline_new (duration);
|
||||
g_signal_connect (timeline, "new-frame", G_CALLBACK (_update_progress_cb), texture);
|
||||
|
||||
/* animate */
|
||||
clutter_timeline_start (timeline);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
g_object_unref (timeline);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
174
doc/cookbook/examples/textures-crossfade.c
Normal file
174
doc/cookbook/examples/textures-crossfade.c
Normal file
@ -0,0 +1,174 @@
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
static guint stage_side = 600;
|
||||
static guint animation_duration_ms = 1500;
|
||||
|
||||
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||
|
||||
typedef struct {
|
||||
ClutterActor *front;
|
||||
ClutterActor *back;
|
||||
ClutterState *transitions;
|
||||
GSList *image_paths;
|
||||
guint next_image_index;
|
||||
} State;
|
||||
|
||||
static gboolean
|
||||
load_next_image (State *app)
|
||||
{
|
||||
/* don't do anything if already animating */
|
||||
ClutterTimeline *timeline = clutter_state_get_timeline (app->transitions);
|
||||
|
||||
if (clutter_timeline_is_playing (timeline) == 1)
|
||||
{
|
||||
g_debug ("Animation is running already");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!app->next_image_index)
|
||||
app->next_image_index = 0;
|
||||
|
||||
gpointer next = g_slist_nth_data (app->image_paths, app->next_image_index);
|
||||
|
||||
if (next == NULL)
|
||||
return FALSE;
|
||||
|
||||
gchar *image_path = (gchar *)next;
|
||||
|
||||
g_debug ("Loading %s", image_path);
|
||||
|
||||
CoglHandle *cogl_texture;
|
||||
cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (app->front));
|
||||
|
||||
if (cogl_texture != NULL)
|
||||
{
|
||||
/* copy the current texture into the background */
|
||||
clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (app->back), cogl_texture);
|
||||
|
||||
/* make the back opaque and front transparent */
|
||||
clutter_state_warp_to_state (app->transitions, "show-back");
|
||||
}
|
||||
|
||||
/* load the next image into the front */
|
||||
GError *error = NULL;
|
||||
clutter_texture_set_from_file (CLUTTER_TEXTURE (app->front),
|
||||
image_path,
|
||||
&error);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
g_warning ("Error loading %s\n%s", image_path, error->message);
|
||||
g_error_free (error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* fade in the front texture and fade out the back texture */
|
||||
clutter_state_set_state (app->transitions, "show-front");
|
||||
|
||||
app->next_image_index++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_key_pressed_cb (ClutterActor *actor,
|
||||
ClutterEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
State *app = (State *)user_data;
|
||||
|
||||
load_next_image (app);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
g_print ("Usage: %s <image paths to load>\n", argv[0]);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
State *app = g_new0 (State, 1);
|
||||
app->image_paths = NULL;
|
||||
|
||||
/*
|
||||
* NB if your shell globs arguments to this program so argv
|
||||
* includes non-image files, they will fail to load and throw errors
|
||||
*/
|
||||
guint i;
|
||||
for (i = 1; i < argc; i++)
|
||||
app->image_paths = g_slist_append (app->image_paths, argv[i]);
|
||||
|
||||
GError *error = NULL;
|
||||
|
||||
/* UI */
|
||||
ClutterActor *stage;
|
||||
ClutterLayoutManager *layout;
|
||||
ClutterActor *box;
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
stage = clutter_stage_get_default ();
|
||||
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||
clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade");
|
||||
clutter_actor_set_size (stage, stage_side, stage_side);
|
||||
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||
|
||||
layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
|
||||
CLUTTER_BIN_ALIGNMENT_CENTER);
|
||||
|
||||
box = clutter_box_new (layout);
|
||||
clutter_actor_set_size (box, stage_side, stage_side);
|
||||
|
||||
app->back = clutter_texture_new ();
|
||||
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->back), TRUE);
|
||||
|
||||
app->front = clutter_texture_new ();
|
||||
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->front), TRUE);
|
||||
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (box), app->back);
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (box), app->front);
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
|
||||
|
||||
/* animations */
|
||||
app->transitions = clutter_state_new ();
|
||||
clutter_state_set (app->transitions, NULL, "show-front",
|
||||
app->front, "opacity", CLUTTER_EASE_IN_CUBIC, 255,
|
||||
app->back, "opacity", CLUTTER_EASE_IN_CUBIC, 0,
|
||||
NULL);
|
||||
clutter_state_set (app->transitions, NULL, "show-back",
|
||||
app->front, "opacity", CLUTTER_LINEAR, 0,
|
||||
app->back, "opacity", CLUTTER_LINEAR, 255,
|
||||
NULL);
|
||||
clutter_state_set_duration (app->transitions,
|
||||
NULL,
|
||||
NULL,
|
||||
animation_duration_ms);
|
||||
|
||||
/* display the next (first) image */
|
||||
load_next_image (app);
|
||||
|
||||
/* key press displays the next image */
|
||||
g_signal_connect (stage,
|
||||
"key-press-event",
|
||||
G_CALLBACK (_key_pressed_cb),
|
||||
app);
|
||||
|
||||
clutter_actor_show (stage);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
g_slist_free (app->image_paths);
|
||||
g_object_unref (app->transitions);
|
||||
g_free (app);
|
||||
|
||||
if (error != NULL)
|
||||
g_error_free (error);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user