mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 10:00:45 -05:00
cookbook: Added a recipe for cross-fading between two images
The recipe covers a two texture approach (using the Clutter API) and a single texture approach (using COGL). It also discusses issues with cross-fading between images of different sizes with the COGL API, and gives a longer example of cycling through multiple images in a slideshow application.
This commit is contained in:
parent
c492faecb2
commit
c207820bef
@ -841,4 +841,539 @@ typedef struct _CoglTextureVertex {
|
|||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="textures-crossfade">
|
||||||
|
<title>Cross-fading between two images</title>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Problem</title>
|
||||||
|
|
||||||
|
<para>You want to do a cross-fade animation (a.k.a. a dissolve
|
||||||
|
transition) between two images.</para>
|
||||||
|
|
||||||
|
<para>An example use case would be creating a slideshow effect:
|
||||||
|
load an image from a file, display it in the UI, then load a second
|
||||||
|
image and cross-fade to it.</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Solutions</title>
|
||||||
|
|
||||||
|
<para>There are two main approaches you could take:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>Use two <type>ClutterTextures</type>, one on top
|
||||||
|
of the other.</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Use a single <type>ClutterTexture</type>
|
||||||
|
with the two images in separate layers inside it.</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Solution 1: two textures</title>
|
||||||
|
|
||||||
|
<para>This approach uses two <type>ClutterTextures</type>,
|
||||||
|
<varname>bottom</varname> and <varname>top</varname>. To begin
|
||||||
|
with, the <varname>bottom</varname> texture shows the
|
||||||
|
<emphasis>source</emphasis> image and is opaque; the
|
||||||
|
<varname>top</varname> texture is loaded with
|
||||||
|
the <emphasis>target</emphasis> image, but is not visible as
|
||||||
|
it is fully transparent.</para>
|
||||||
|
|
||||||
|
<para>An animation is then used to fade in the
|
||||||
|
<varname>top</varname> texture and fade out the
|
||||||
|
<varname>bottom</varname> texture, leaving just <varname>top</varname>
|
||||||
|
visible.</para>
|
||||||
|
|
||||||
|
<para>To implement this, first create the two textures inside a
|
||||||
|
<type>ClutterBinLayout</type>:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
/* ... initialise Clutter, get default stage etc. ... */
|
||||||
|
|
||||||
|
/* Actors added to this layout are centered on both x and y axes */
|
||||||
|
ClutterLayoutManager *layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
|
||||||
|
CLUTTER_BIN_ALIGNMENT_CENTER);
|
||||||
|
|
||||||
|
ClutterActor *box = clutter_box_new (layout);
|
||||||
|
|
||||||
|
/* set the size of the box so it fills the stage (in this case 600x600) */
|
||||||
|
clutter_actor_set_size (box, 400, 400);
|
||||||
|
|
||||||
|
ClutterActor *bottom = clutter_texture_new ();
|
||||||
|
ClutterActor *top = clutter_texture_new ();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the textures to the layout;
|
||||||
|
* NB because top is added last, it will be "on top of" bottom
|
||||||
|
*/
|
||||||
|
clutter_container_add_actor (CLUTTER_CONTAINER (box), bottom);
|
||||||
|
clutter_container_add_actor (CLUTTER_CONTAINER (box), top);
|
||||||
|
|
||||||
|
/* stage is a ClutterStage instance */
|
||||||
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Load the <varname>source</varname> image into the bottom
|
||||||
|
texture and the <varname>target</varname> image into the top one.
|
||||||
|
As this is the same operation each time, it makes sense to write
|
||||||
|
a function for loading an image into a texture and checking
|
||||||
|
for errors, e.g.:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
static gboolean
|
||||||
|
load_image (ClutterTexture *texture,
|
||||||
|
gchar *image_path)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
gboolean success = clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
|
||||||
|
image_path,
|
||||||
|
&error);
|
||||||
|
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
g_warning ("Error loading %s\n%s", image_path, error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>The <function>load_image()</function> function can then
|
||||||
|
be called for each texture:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
/* file path to the image visible when the UI is first displayed */
|
||||||
|
gchar *source = NULL;
|
||||||
|
|
||||||
|
/* file path to the image we're going to cross-fade to */
|
||||||
|
gchar *target = NULL;
|
||||||
|
|
||||||
|
/* ...set image file paths, e.g. from command line or directory read... */
|
||||||
|
|
||||||
|
/* the bottom texture contains the source image */
|
||||||
|
load_image (CLUTTER_TEXTURE (bottom), source);
|
||||||
|
|
||||||
|
/* the top texture contains the target image */
|
||||||
|
load_image (CLUTTER_TEXTURE (top), target);
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>For the animations, we use <type>ClutterState</type> as we
|
||||||
|
want to animate two actors at once (<varname>top</varname>
|
||||||
|
and <varname>bottom</varname>):</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
ClutterState *transitions = clutter_state_new ();
|
||||||
|
|
||||||
|
/* start state, where bottom is opaque and top is transparent */
|
||||||
|
clutter_state_set (transitions, NULL, "show-bottom",
|
||||||
|
top, "opacity", CLUTTER_LINEAR, 0,
|
||||||
|
bottom, "opacity", CLUTTER_LINEAR, 255,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* end state, where top is opaque and bottom is transparent */
|
||||||
|
clutter_state_set (transitions, NULL, "show-top",
|
||||||
|
top, "opacity", CLUTTER_EASE_IN_CUBIC, 255,
|
||||||
|
bottom, "opacity", CLUTTER_EASE_IN_CUBIC, 0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* set 1000ms duration for all transitions between states */
|
||||||
|
clutter_state_set_duration (transitions, NULL, NULL, 1000);
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Note that rather than set the start opacities manually
|
||||||
|
on the actors (e.g. using
|
||||||
|
<function>clutter_actor_set_opacity()</function>),
|
||||||
|
I've used a <type>ClutterState</type> to define the start
|
||||||
|
state (as well as the end state). This makes it easier to
|
||||||
|
track transitions, as they are all kept in one data structure.</para>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>The easing modes used for the cross-fade animation
|
||||||
|
(<constant>CLUTTER_EASE_IN_CUBIC</constant>)
|
||||||
|
can be set to whatever you like. I personally think that
|
||||||
|
ease-in modes look best for cross-fading.</para>
|
||||||
|
</note>
|
||||||
|
|
||||||
|
<para>"Warp" the two textures into the start state
|
||||||
|
(<varname>bottom</varname> opaque, <varname>top</varname>
|
||||||
|
transparent):</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
clutter_state_warp_to_state (transitions, "show-bottom");
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Using <function>clutter_state_warp_to_state()</function>
|
||||||
|
immediately transitions to a state without animating, which
|
||||||
|
in this case sets up the initial state of the UI.</para>
|
||||||
|
|
||||||
|
<para>Finally, use the <type>ClutterState</type> to animate
|
||||||
|
the two textures, so <varname>top</varname> fades in and
|
||||||
|
<varname>bottom</varname> fades out:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
clutter_state_set_state (transitions, "show-top");
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Here's what it looks like:</para>
|
||||||
|
|
||||||
|
<inlinemediaobject>
|
||||||
|
<videoobject>
|
||||||
|
<videodata fileref="videos/textures-crossfade-two-textures.ogv"/>
|
||||||
|
</videoobject>
|
||||||
|
<alt>
|
||||||
|
<para>Video showing a cross-fade between two textures</para>
|
||||||
|
</alt>
|
||||||
|
</inlinemediaobject>
|
||||||
|
|
||||||
|
<para>The full code for this example
|
||||||
|
<link linkend="textures-crossfade-example-1">is in the
|
||||||
|
appendix</link>.</para>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Solution 2: one texture with two layers</title>
|
||||||
|
|
||||||
|
<para>The alternative solution is to use a single texture
|
||||||
|
and the low-level COGL API to set up two different layers
|
||||||
|
inside it, one for each image.</para>
|
||||||
|
|
||||||
|
<para>Then, rather than fade between two textures,
|
||||||
|
progressively combine the two layers together using an
|
||||||
|
alpha value which changes over the course of an animation
|
||||||
|
(from 0.0 at the start of the animation to 1.0 at its end).</para>
|
||||||
|
|
||||||
|
<para>At any point in the cross-fade animation, you are
|
||||||
|
actually seeing a combination of the color
|
||||||
|
values in the two images (modified by an alpha component), rather
|
||||||
|
than seeing one image through the other. This can give a smoother
|
||||||
|
cross-fade effect than the two texture approach.</para>
|
||||||
|
|
||||||
|
<para>As this solution is more complex
|
||||||
|
and relies on the lower-level (and more difficult to follow)
|
||||||
|
COGL API, the next section is just a short summary of how it
|
||||||
|
works; see <link linkend="textures-crossfade-example-2">the
|
||||||
|
sample code, which has liberal comments</link> for more details.</para>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>For more about texture combining, refer to the COGL
|
||||||
|
API documentation (particularly the section about material
|
||||||
|
blend strings). You may also find it useful to get hold of
|
||||||
|
a decent OpenGL reference. (So you can look it up, what we're
|
||||||
|
doing in this solution is using a texture combiner with
|
||||||
|
interpolation as the texture combiner function.)</para>
|
||||||
|
</note>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Cross-fading using a texture combiner with interpolation</title>
|
||||||
|
|
||||||
|
<para>The cross-fade is implemented by combining the two layers,
|
||||||
|
computing a color value for each pixel in the resulting texture.
|
||||||
|
The value for each pixel at a given point in the animation
|
||||||
|
is based on three things:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>The color value of the <emphasis>source</emphasis>
|
||||||
|
pixel</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>The color value of the <emphasis>target</emphasis>
|
||||||
|
pixel</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>The alpha value of a third colour at the given point
|
||||||
|
in the animation's timeline</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<para>The resulting value for each RGBA color component in each pixel
|
||||||
|
is computed using an interpolation function. In pseudo-code, it
|
||||||
|
looks like this:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
color component value = (target pixel value * alpha) + (source pixel value * (1 - alpha))
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>The effect is that as the alpha increases towards 1.0,
|
||||||
|
progressively more of the <emphasis>target</emphasis> pixel's
|
||||||
|
color is used, and progressively less of the <emphasis>source</emphasis>
|
||||||
|
pixel's: so the <emphasis>target</emphasis> fades in, while
|
||||||
|
the <emphasis>source</emphasis> fades out.</para>
|
||||||
|
|
||||||
|
<para>The advantage of this approach is that color and
|
||||||
|
brightness transitions only occur where pixels differ between
|
||||||
|
the two images. This means that you transitions are smoother
|
||||||
|
where you are cross-fading between images with similar color ranges
|
||||||
|
and brightness levels.</para>
|
||||||
|
|
||||||
|
<para>A special case is where you're cross-fading
|
||||||
|
from an image to itself: the two texture approach can cause some
|
||||||
|
dimming during this kind of transition; but the single texture
|
||||||
|
approach results in no color or brightness changes (it's not even
|
||||||
|
a transition as such, as all the pixels are identical in
|
||||||
|
the two layers).</para>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="textures-crossfade-discussion">
|
||||||
|
<title>Discussion</title>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Cross-fades between images of different sizes</title>
|
||||||
|
|
||||||
|
<para>The code examples
|
||||||
|
(<link linkend="textures-crossfade-example-1">two textures</link>,
|
||||||
|
<link linkend="textures-crossfade-example-2">one texture with
|
||||||
|
COGL</link>) don't take account of the size of the images being
|
||||||
|
loaded.</para>
|
||||||
|
|
||||||
|
<para>In the two texture example, this isn't so much of a problem,
|
||||||
|
as you can resize the textures individually to the images:
|
||||||
|
providing you use
|
||||||
|
<function>clutter_texture_set_keep_aspect_ratio()</function>,
|
||||||
|
different image sizes shouldn't be a problem. See
|
||||||
|
<link linkend="textures-crossfade-example-3">the slideshow
|
||||||
|
example</link>, for a demonstration of how to cycle through
|
||||||
|
different sized images.</para>
|
||||||
|
|
||||||
|
<para>In the case of the single texture approach, you will get
|
||||||
|
problems when cross-fading between two images with
|
||||||
|
different sizes. There is no easy way to maintain the aspect
|
||||||
|
ratio (as you have two layers, potentially with different sizes,
|
||||||
|
in the same texture). The last layer added to the
|
||||||
|
<type>CoglMaterial</type> determines the size of the texture;
|
||||||
|
so if the previous layer has different dimensions, it will
|
||||||
|
appear distorted in the UI. In the
|
||||||
|
<link linkend="textures-crossfade-example-2">single texture
|
||||||
|
code example</link>, the <emphasis>source</emphasis> layer
|
||||||
|
is added first; so, if the <emphasis>target</emphasis> layer has
|
||||||
|
different dimensions, the <emphasis>source</emphasis> will
|
||||||
|
appear distorted.</para>
|
||||||
|
|
||||||
|
<para>There are a few ways you can remedy this:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>As you load each image into its own
|
||||||
|
<type>CoglTexture</type>, get its size with
|
||||||
|
<function>cogl_texture_get_width()</function> and
|
||||||
|
<function>cogl_texture_get_height()</function>. Then set the
|
||||||
|
<type>ClutterTexture's</type> size to the
|
||||||
|
size of the source layer. Next, as
|
||||||
|
you cross-fade, simultaneously animate a
|
||||||
|
size change in the <type>ClutterTexture</type> to
|
||||||
|
the target image's size.</para>
|
||||||
|
<para>This could work with non-realistic images where
|
||||||
|
some distortion of the image is acceptable (the target image
|
||||||
|
may be the wrong size to start with, but transition to the
|
||||||
|
correct size by the time it's fully faded in). But it can
|
||||||
|
look a bit odd for transitions between photos.</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>If the bounds of the images you're going to be loading
|
||||||
|
are known (i.e. you know the greatest width and height
|
||||||
|
of all the images you're going to load), you can size
|
||||||
|
the <type>ClutterTexture</type> to those bounds.</para>
|
||||||
|
|
||||||
|
<para>Then, as you load each image (e.g. you could do this in the
|
||||||
|
<function>load_cogl_texture()</function> function of
|
||||||
|
the <link linkend="textures-crossfade-example-2">code
|
||||||
|
example</link>):</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>Load the image into a temporary
|
||||||
|
<type>CoglTexture</type> (e.g. called
|
||||||
|
<varname>filetex</varname>). Then use the
|
||||||
|
<type>CoglTexture</type> API to get the data from it.
|
||||||
|
For example:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
/* load texture from a file */
|
||||||
|
CoglHandle *filetex = cogl_texture_new_from_file (FOX_FILE,
|
||||||
|
COGL_TEXTURE_NO_SLICING,
|
||||||
|
COGL_PIXEL_FORMAT_ANY,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
CoglPixelFormat format = cogl_texture_get_format (filetex);
|
||||||
|
guint rowstride = cogl_texture_get_rowstride (filetex);
|
||||||
|
guint width = cogl_texture_get_width (filetex);
|
||||||
|
guint height = cogl_texture_get_height (filetex);
|
||||||
|
|
||||||
|
/* allocate memory large enough for the data */
|
||||||
|
gint size = cogl_texture_get_data (filetex, format, 0, NULL);
|
||||||
|
guchar *data = g_new0 (guchar, size);
|
||||||
|
|
||||||
|
/* get the data from the texture */
|
||||||
|
cogl_texture_get_data (filetex,
|
||||||
|
format,
|
||||||
|
rowstride,
|
||||||
|
data);
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Create another <type>CoglTexture</type> as
|
||||||
|
large as the <type>ClutterTexture</type> (i.e. big enough
|
||||||
|
to fit any image you're going to load). Then copy the
|
||||||
|
data from <varname>filetex</varname> into a region of
|
||||||
|
it. For example:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
/* COGL texture which is the same size as the ClutterTexture */
|
||||||
|
CoglHandle *tex = cogl_texture_new_with_size (clutter_texture_width,
|
||||||
|
clutter_texture_height,
|
||||||
|
COGL_TEXTURE_NO_SLICING,
|
||||||
|
format);
|
||||||
|
|
||||||
|
/* copy the texture data (from filetex) into a region of the full-sized texture */
|
||||||
|
cogl_texture_set_region (tex,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
clutter_texture_width,
|
||||||
|
clutter_texture_height,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
rowstride,
|
||||||
|
data);
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Because you're copying the image data from the
|
||||||
|
file into a region of the <type>CoglTexture</type>
|
||||||
|
that's the same size as the image data, it won't be
|
||||||
|
distorted. However, it's worth stressing that the
|
||||||
|
<type>ClutterTexture</type> needs to be as wide as
|
||||||
|
the widest image and as tall as the tallest, so
|
||||||
|
all the images can be accommodated. Otherwise this
|
||||||
|
works, but images might be clipped.</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="textures-crossfade-discussion-slideshows">
|
||||||
|
<title>Slideshows</title>
|
||||||
|
|
||||||
|
<para>The two texture solution can be easily extended
|
||||||
|
to cycle through multiple images. To begin with, the first
|
||||||
|
image is loaded into the <varname>top</varname> texture. Then,
|
||||||
|
the basic pattern for transitioning to the next image is as follows:</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>Copy the data from the <varname>top</varname> texture
|
||||||
|
to the <varname>bottom</varname> texture.</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Make the <varname>top</varname> texture transparent
|
||||||
|
and the <varname>bottom</varname> texture opaque (using
|
||||||
|
<function>clutter_state_warp_to_state()</function>). At this
|
||||||
|
point, it appears as though the textures haven't changed.</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Load the next image into <varname>top</varname>.</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>When <varname>top</varname> has finished loading,
|
||||||
|
fade it in while simultaneously fading out
|
||||||
|
<varname>bottom</varname> (using
|
||||||
|
<function>clutter_state_set_state()</function>).</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>The <link linkend="textures-crossfade-example-3">sample
|
||||||
|
code in the appendix</link> implements this as part of
|
||||||
|
a simple slideshow application.</para>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Full examples</title>
|
||||||
|
|
||||||
|
<example id="textures-crossfade-example-1">
|
||||||
|
<title>Cross-fading between two images using two
|
||||||
|
<type>ClutterTextures</type></title>
|
||||||
|
<programlisting>
|
||||||
|
<xi:include href="examples/textures-crossfade.c" parse="text">
|
||||||
|
<xi:fallback>there should be a code sample here, but there isn't...</xi:fallback>
|
||||||
|
</xi:include>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example id="textures-crossfade-example-2">
|
||||||
|
<title>Cross-fading between two images using one
|
||||||
|
<type>ClutterTexture</type> and the COGL API</title>
|
||||||
|
<programlisting>
|
||||||
|
<xi:include href="examples/textures-crossfade-cogl.c" parse="text">
|
||||||
|
<xi:fallback>there should be a code sample here, but there isn't...</xi:fallback>
|
||||||
|
</xi:include>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example id="textures-crossfade-example-3">
|
||||||
|
<title>A simple slideshow application using two
|
||||||
|
<type>ClutterTextures</type></title>
|
||||||
|
<programlisting>
|
||||||
|
<xi:include href="examples/textures-crossfade-slideshow.c" parse="text">
|
||||||
|
<xi:fallback>there should be a code sample here, but there isn't...</xi:fallback>
|
||||||
|
</xi:include>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
</chapter>
|
</chapter>
|
||||||
|
Loading…
Reference in New Issue
Block a user