Blob Blame History Raw
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>6. Creating a reflection of a texture</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot"><link rel="home" href="index.html" title="The Clutter Cookbook"><link rel="up" href="textures.html" title="Chapter 4. Textures"><link rel="prev" href="textures-sub-textures.html" title="5. Creating sub-textures from an existing texture"><link rel="next" href="textures-crossfade.html" title="7. Cross-fading between two images"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">6. Creating a reflection of a texture</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="textures-sub-textures.html">Prev</a> </td><th width="60%" align="center">Chapter 4. Textures</th><td width="20%" align="right"> <a accesskey="n" href="textures-crossfade.html">Next</a></td></tr></table><hr></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="textures-reflection"></a>6. Creating a reflection of a texture</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm140200508101264"></a>6.1. Problem</h3></div></div></div><p>You want to create the reflection of a texture.</p><p>The reflection is going to be positioned below the original
      texture, and is going to fade out as if the original was placed on
      a glassy surface.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm140200508099360"></a>6.2. Solution</h3></div></div></div><p>You can use a ClutterClone actor and override its paint
      implementation with a custom one:</p><div class="informalexample"><pre class="programlisting">static void
_clone_paint_cb (ClutterActor *actor)
{
  /* ... */

  /* get the Cogl material of the source texture */
  material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (source));

  /* get the size of the actor, which will be used to size the reflection */
  clutter_actor_get_allocation_box (actor, &amp;box);
  clutter_actor_box_get_size (&amp;box, &amp;width, &amp;height);

  /* get the composite opacity of the actor */
  opacity = clutter_actor_get_paint_opacity (actor);

  /* figure out the two colors for the reflection: the first is
   * full color and the second is the same, but at 0 opacity
   */
  cogl_color_init_from_4f (&amp;color_1, 1.0, 1.0, 1.0, opacity / 255.);
  cogl_color_premultiply (&amp;color_1);
  cogl_color_init_from_4f (&amp;color_2, 1.0, 1.0, 1.0, 0.0);
  cogl_color_premultiply (&amp;color_2);

  /* describe the four vertices of the quad; since it has
   * to be a reflection, we need to invert it as well
   */
  vertices[0].x = 0; vertices[0].y = 0; vertices[0].z = 0;
  vertices[0].tx = 0.0; vertices[0].ty = 1.0;
  vertices[0].color = color_1;

  vertices[1].x = width; vertices[1].y = 0; vertices[1].z = 0;
  vertices[1].tx = 1.0; vertices[1].ty = 1.0;
  vertices[1].color = color_1;

  vertices[2].x = width; vertices[2].y = height; vertices[2].z = 0;
  vertices[2].tx = 1.0; vertices[2].ty = 0.0;
  vertices[2].color = color_2;

  vertices[3].x = 0; vertices[3].y = height; vertices[3].z = 0;
  vertices[3].tx = 0.0; vertices[3].ty = 0.0;
  vertices[3].color = color_2;

  /* paint the same texture but with a different geometry */
  cogl_set_source (material);
  cogl_polygon (vertices, 4, TRUE);

  /* ... */
}

int
main (int argc, char *argv[])
{
  if (clutter_init (&amp;argc, &amp;argv) != CLUTTER_INIT_SUCCESS)
    return 1;

  /* ... get stage etc. */

  ClutterActor *texture;
  GError *error = NULL;

  texture = clutter_texture_new ();

  /* load the image from a file */
  clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
                                 image_path,
                                 &amp;error);

  ClutterActor *clone;

  clone = clutter_clone_new (texture);

  g_signal_connect (clone,
                    "paint",
                    G_CALLBACK (_clone_paint_cb),
                    NULL);

  /* ... clutter_main () etc. */
}</pre></div><div class="screenshot"><div class="mediaobject"><img src="images/textures-reflection.png" alt="A texture and its reflection below"></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm140200508091392"></a>6.3. Discussion</h3></div></div></div><p>The essence of painting a reflection of a texture lies in reusing
      the same material used by the original. This not only allows painting
      always an up to date version of the original, but it also saves
      resources.</p><p>In the code example above we take the <span class="type">CoglMaterial</span>
      out of the source <span class="type">ClutterTexture</span> and we ask the Cogl
      pipeline to paint it by using <code class="function">cogl_set_source()</code>. The
      main difference between this code and the equivalent code inside the
      <span class="type">ClutterTexture</span> <code class="function">paint()</code> implementation
      is that we also specify the texture vertices and their color by using the
      <span class="type">CoglTextureVertex</span> structure and the
      <code class="function">cogl_polygon()</code> function.</p><p>The <span class="type">CoglTextureVertex</span> structure contains three fields
      for the position of the vertex in 3D space:</p><div class="informalexample"><pre class="programlisting">typedef struct _CoglTextureVertex {
  float x;
  float y;
  float z;
  ...</pre></div><p>It also contains the normalized texture coordinate (also known as
      texture element, or <span class="emphasis"><em>texel</em></span>):</p><div class="informalexample"><pre class="programlisting">  ...
  float tx;
  float ty;
  ...</pre></div><p>And, finally, the color of the vertex, expressed as a
      <span class="type">CoglColor</span>:</p><div class="informalexample"><pre class="programlisting">  ...
  CoglColor color;
} CoglTextureVertex;</pre></div><p>The example code sets the position of the vertices in clockwise
    order starting from the top left corner, and sets the coordinate of the
    texels in counter-clockwise order, starting with the bottom left corner.
    This makes sure that the copy of the original texture appears as being
    flipped vertically.</p><p>The gradual fading out to the background color is done by setting
    the color of the top vertices to be fully opaque, and the color of the
    bottom ones to be fully transparent; GL will then automatically create a
    gradient that will be applied when painting the material.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The color values must be pre-multiplied with their alpha
    component, otherwise the bleding will not be correct. You can either
    multiply the values by yourself when creating the color or, better yet,
    use the <code class="function">cogl_color_premultiply()</code> that Cogl provides
    for this operation.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm140200508076160"></a>6.4. Full example</h3></div></div></div><div class="example"><a name="textures-reflection-example"></a><p class="title"><b>Example 4.3. Creating a glassy reflection of a texture</b></p><div class="example-contents"><pre class="programlisting">          #include &lt;stdlib.h&gt;
#include &lt;clutter/clutter.h&gt;

/* pixels between the source and its reflection */
#define V_PADDING       4

static void
_clone_paint_cb (ClutterActor *actor)
{
  ClutterActor *source;
  ClutterActorBox box;
  CoglHandle material;
  gfloat width, height;
  guint8 opacity;
  CoglColor color_1, color_2;
  CoglTextureVertex vertices[4];

  /* if we don't have a source actor, don't paint */
  source = clutter_clone_get_source (CLUTTER_CLONE (actor));
  if (source == NULL)
    goto out;

  /* if the source texture does not have any content, don't paint */
  material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (source));
  if (material == NULL)
    goto out;

  /* get the size of the reflection */
  clutter_actor_get_allocation_box (actor, &amp;box);
  clutter_actor_box_get_size (&amp;box, &amp;width, &amp;height);

  /* get the composite opacity of the actor */
  opacity = clutter_actor_get_paint_opacity (actor);

  /* figure out the two colors for the reflection: the first is
   * full color and the second is the same, but at 0 opacity
   */
  cogl_color_init_from_4f (&amp;color_1, 1.0, 1.0, 1.0, opacity / 255.);
  cogl_color_premultiply (&amp;color_1);
  cogl_color_init_from_4f (&amp;color_2, 1.0, 1.0, 1.0, 0.0);
  cogl_color_premultiply (&amp;color_2);

  /* now describe the four vertices of the quad; since it has
   * to be a reflection, we need to invert it as well
   */
  vertices[0].x = 0; vertices[0].y = 0; vertices[0].z = 0;
  vertices[0].tx = 0.0; vertices[0].ty = 1.0;
  vertices[0].color = color_1;

  vertices[1].x = width; vertices[1].y = 0; vertices[1].z = 0;
  vertices[1].tx = 1.0; vertices[1].ty = 1.0;
  vertices[1].color = color_1;

  vertices[2].x = width; vertices[2].y = height; vertices[2].z = 0;
  vertices[2].tx = 1.0; vertices[2].ty = 0.0;
  vertices[2].color = color_2;

  vertices[3].x = 0; vertices[3].y = height; vertices[3].z = 0;
  vertices[3].tx = 0.0; vertices[3].ty = 0.0;
  vertices[3].color = color_2;

  /* paint the same texture but with a different geometry */
  cogl_set_source (material);
  cogl_polygon (vertices, 4, TRUE);

out:
  /* prevent the default clone handler from running */
  g_signal_stop_emission_by_name (actor, "paint");
}

int
main (int argc, char *argv[])
{
  ClutterActor *stage;
  ClutterActor *texture;
  GError *error = NULL;
  ClutterActor *clone;
  gfloat y_offset;

  if (clutter_init (&amp;argc, &amp;argv) != CLUTTER_INIT_SUCCESS)
    return 1;

  stage = clutter_stage_new ();
  clutter_stage_set_title (CLUTTER_STAGE (stage), "Reflection");
  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);

  texture = clutter_texture_new ();
  clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
                                 "redhand.png",
                                 &amp;error);
  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.2));

  y_offset = clutter_actor_get_height (texture) + V_PADDING;

  clone = clutter_clone_new (texture);
  clutter_actor_add_constraint (clone, clutter_bind_constraint_new (texture, CLUTTER_BIND_X, 0.0));
  clutter_actor_add_constraint (clone, clutter_bind_constraint_new (texture, CLUTTER_BIND_Y, y_offset));
  g_signal_connect (clone,
                    "paint",
                    G_CALLBACK (_clone_paint_cb),
                    NULL);

  clutter_container_add (CLUTTER_CONTAINER (stage), texture, clone, NULL);

  clutter_actor_show (stage);

  clutter_main ();

  return EXIT_SUCCESS;
}
</pre></div></div><br class="example-break"></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="textures-sub-textures.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="textures.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="textures-crossfade.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">5. Creating sub-textures from an existing texture </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 7. Cross-fading between two images</td></tr></table></div></body></html>