diff --git a/doc/cookbook/textures.xml b/doc/cookbook/textures.xml index 56b661d7d..efc190de9 100644 --- a/doc/cookbook/textures.xml +++ b/doc/cookbook/textures.xml @@ -841,4 +841,539 @@ typedef struct _CoglTextureVertex { +
+ Cross-fading between two images + +
+ Problem + + You want to do a cross-fade animation (a.k.a. a dissolve + transition) between two images. + + 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. +
+ +
+ Solutions + + There are two main approaches you could take: + + + + Use two ClutterTextures, one on top + of the other. + + + Use a single ClutterTexture + with the two images in separate layers inside it. + + + +
+ Solution 1: two textures + + This approach uses two ClutterTextures, + bottom and top. To begin + with, the bottom texture shows the + source image and is opaque; the + top texture is loaded with + the target image, but is not visible as + it is fully transparent. + + An animation is then used to fade in the + top texture and fade out the + bottom texture, leaving just top + visible. + + To implement this, first create the two textures inside a + ClutterBinLayout: + + + + + + + + Load the source image into the bottom + texture and the target 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.: + + + +message); + g_error_free (error); + exit (EXIT_FAILURE); + } + + return success; +} +]]> + + + + The load_image() function can then + be called for each texture: + + + + + + + + For the animations, we use ClutterState as we + want to animate two actors at once (top + and bottom): + + + + + + + + Note that rather than set the start opacities manually + on the actors (e.g. using + clutter_actor_set_opacity()), + I've used a ClutterState 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. + + + The easing modes used for the cross-fade animation + (CLUTTER_EASE_IN_CUBIC) + can be set to whatever you like. I personally think that + ease-in modes look best for cross-fading. + + + "Warp" the two textures into the start state + (bottom opaque, top + transparent): + + + + + + + + Using clutter_state_warp_to_state() + immediately transitions to a state without animating, which + in this case sets up the initial state of the UI. + + Finally, use the ClutterState to animate + the two textures, so top fades in and + bottom fades out: + + + + + + + + Here's what it looks like: + + + + + + + Video showing a cross-fade between two textures + + + + The full code for this example + is in the + appendix. + +
+ +
+ Solution 2: one texture with two layers + + 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. + + 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). + + 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. + + 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 the + sample code, which has liberal comments for more details. + + + 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.) + + +
+ Cross-fading using a texture combiner with interpolation + + 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: + + + + The color value of the source + pixel + + + The color value of the target + pixel + + + The alpha value of a third colour at the given point + in the animation's timeline + + + + The resulting value for each RGBA color component in each pixel + is computed using an interpolation function. In pseudo-code, it + looks like this: + + + + color component value = (target pixel value * alpha) + (source pixel value * (1 - alpha)) + + + + The effect is that as the alpha increases towards 1.0, + progressively more of the target pixel's + color is used, and progressively less of the source + pixel's: so the target fades in, while + the source fades out. + + 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. + + 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). + +
+ +
+ +
+ +
+ Discussion + +
+ Cross-fades between images of different sizes + + The code examples + (two textures, + one texture with + COGL) don't take account of the size of the images being + loaded. + + 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 + clutter_texture_set_keep_aspect_ratio(), + different image sizes shouldn't be a problem. See + the slideshow + example, for a demonstration of how to cycle through + different sized images. + + 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 + CoglMaterial determines the size of the texture; + so if the previous layer has different dimensions, it will + appear distorted in the UI. In the + single texture + code example, the source layer + is added first; so, if the target layer has + different dimensions, the source will + appear distorted. + + There are a few ways you can remedy this: + + + + + As you load each image into its own + CoglTexture, get its size with + cogl_texture_get_width() and + cogl_texture_get_height(). Then set the + ClutterTexture's size to the + size of the source layer. Next, as + you cross-fade, simultaneously animate a + size change in the ClutterTexture to + the target image's size. + 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. + + + + 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 ClutterTexture to those bounds. + + Then, as you load each image (e.g. you could do this in the + load_cogl_texture() function of + the code + example): + + + + Load the image into a temporary + CoglTexture (e.g. called + filetex). Then use the + CoglTexture API to get the data from it. + For example: + + + + + + + + + Create another CoglTexture as + large as the ClutterTexture (i.e. big enough + to fit any image you're going to load). Then copy the + data from filetex into a region of + it. For example: + + + + + + + + Because you're copying the image data from the + file into a region of the CoglTexture + that's the same size as the image data, it won't be + distorted. However, it's worth stressing that the + ClutterTexture 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. + + + + + + + +
+ +
+ Slideshows + + The two texture solution can be easily extended + to cycle through multiple images. To begin with, the first + image is loaded into the top texture. Then, + the basic pattern for transitioning to the next image is as follows: + + + + Copy the data from the top texture + to the bottom texture. + + + Make the top texture transparent + and the bottom texture opaque (using + clutter_state_warp_to_state()). At this + point, it appears as though the textures haven't changed. + + + Load the next image into top. + + + When top has finished loading, + fade it in while simultaneously fading out + bottom (using + clutter_state_set_state()). + + + + The sample + code in the appendix implements this as part of + a simple slideshow application. + +
+ +
+ +
+ Full examples + + + Cross-fading between two images using two + <type>ClutterTextures</type> + + + there should be a code sample here, but there isn't... + + + + + + Cross-fading between two images using one + <type>ClutterTexture</type> and the COGL API + + + there should be a code sample here, but there isn't... + + + + + + A simple slideshow application using two + <type>ClutterTextures</type> + + + there should be a code sample here, but there isn't... + + + + +
+ +
+