[cogl] meta-texture: Support CLAMP_TO_EDGE



commit 4c49f830569d2aacf8ed459683098d259206fc09
Author: Robert Bragg <robert linux intel com>
Date:   Thu Oct 27 14:31:15 2011 +0100

    meta-texture: Support CLAMP_TO_EDGE
    
    cogl_meta_texture_foreach_in_region() now directly supports
    CLAMP_TO_EDGE wrap modes. This means the cogl_rectangle code will be
    able to build on this and makes the logic accessible to anyone
    developing custom primitives that need to support meta textures.
    
    Reviewed-by: Neil Roberts <neil linux intel com>

 cogl/cogl-meta-texture.c |  245 ++++++++++++++++++++++++++++++++++++++++++++++
 cogl/cogl-meta-texture.h |   13 +--
 2 files changed, 247 insertions(+), 11 deletions(-)
---
diff --git a/cogl/cogl-meta-texture.c b/cogl/cogl-meta-texture.c
index 6ec006b..6aa4626 100644
--- a/cogl/cogl-meta-texture.c
+++ b/cogl/cogl-meta-texture.c
@@ -217,6 +217,224 @@ create_grid_and_repeat_cb (CoglTexture *slice_texture,
   data->padded_textures[n_y_spans * y_real_index + x_real_index] = NULL;
 }
 
+#define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0)
+
+typedef struct _ClampData
+{
+  float start;
+  float end;
+  gboolean s_flipped;
+  gboolean t_flipped;
+  CoglMetaTextureCallback callback;
+  void *user_data;
+} ClampData;
+
+static void
+clamp_s_cb (CoglTexture *sub_texture,
+            const float *sub_texture_coords,
+            const float *meta_coords,
+            void *user_data)
+{
+  ClampData *clamp_data = user_data;
+  float mapped_meta_coords[4] = {
+    clamp_data->start,
+    meta_coords[1],
+    clamp_data->end,
+    meta_coords[3]
+  };
+  if (clamp_data->s_flipped)
+    SWAP (mapped_meta_coords[0], mapped_meta_coords[2]);
+  /* NB: we never need to flip the t coords when dealing with
+   * s-axis clamping so no need to check if ->t_flipped */
+  clamp_data->callback (sub_texture,
+                        sub_texture_coords, mapped_meta_coords,
+                        clamp_data->user_data);
+}
+
+static void
+clamp_t_cb (CoglTexture *sub_texture,
+            const float *sub_texture_coords,
+            const float *meta_coords,
+            void *user_data)
+{
+  ClampData *clamp_data = user_data;
+  float mapped_meta_coords[4] = {
+    meta_coords[0],
+    clamp_data->start,
+    meta_coords[2],
+    clamp_data->end,
+  };
+  if (clamp_data->s_flipped)
+    SWAP (mapped_meta_coords[0], mapped_meta_coords[2]);
+  if (clamp_data->t_flipped)
+    SWAP (mapped_meta_coords[1], mapped_meta_coords[3]);
+  clamp_data->callback (sub_texture,
+                        sub_texture_coords, mapped_meta_coords,
+                        clamp_data->user_data);
+}
+
+static gboolean
+foreach_clamped_region (CoglMetaTexture *meta_texture,
+                        float *tx_1,
+                        float *ty_1,
+                        float *tx_2,
+                        float *ty_2,
+                        CoglPipelineWrapMode wrap_s,
+                        CoglPipelineWrapMode wrap_t,
+                        CoglMetaTextureCallback callback,
+                        void *user_data)
+{
+  float width = cogl_texture_get_width (COGL_TEXTURE (meta_texture));
+  ClampData clamp_data;
+
+  /* Consider that *tx_1 may be > *tx_2 and to simplify things
+   * we just flip them around if that's the case and keep a note
+   * of the fact that they are flipped. */
+  if (*tx_1 > *tx_2)
+    {
+      SWAP (*tx_1, *tx_2);
+      clamp_data.s_flipped = TRUE;
+    }
+  else
+    clamp_data.s_flipped = FALSE;
+
+  /* The same goes for ty_1 and ty_2... */
+  if (*ty_1 > *ty_2)
+    {
+      SWAP (*ty_1, *ty_2);
+      clamp_data.t_flipped = TRUE;
+    }
+  else
+    clamp_data.t_flipped = FALSE;
+
+  clamp_data.callback = callback;
+  clamp_data.user_data = user_data;
+
+  if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
+    {
+      float max_s_coord;
+      float half_texel_width;
+
+      /* Consider that rectangle textures have non-normalized
+       * coordinates... */
+      if (cogl_is_texture_rectangle (meta_texture))
+        max_s_coord = width;
+      else
+        max_s_coord = 1.0;
+
+      half_texel_width = max_s_coord / (width * 2);
+
+      /* Handle any left clamped region */
+      if (*tx_1 < 0)
+        {
+          clamp_data.start = *tx_1;
+          clamp_data.end = MIN (0, *tx_2);;
+          cogl_meta_texture_foreach_in_region (meta_texture,
+                                               half_texel_width, *ty_1,
+                                               half_texel_width, *ty_2,
+                                               COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                               wrap_t,
+                                               clamp_s_cb,
+                                               &clamp_data);
+          /* Have we handled everything? */
+          if (tx_2 <= 0)
+            return TRUE;
+
+          /* clamp tx_1 since we've handled everything with x < 0 */
+          *tx_1 = 0;
+        }
+
+      /* Handle any right clamped region - including the corners */
+      if (*tx_2 > max_s_coord)
+        {
+          clamp_data.start = MAX (max_s_coord, *tx_1);
+          clamp_data.end = *tx_2;
+          cogl_meta_texture_foreach_in_region (meta_texture,
+                                               max_s_coord - half_texel_width,
+                                               *ty_1,
+                                               max_s_coord - half_texel_width,
+                                               *ty_2,
+                                               COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                               wrap_t,
+                                               clamp_s_cb,
+                                               &clamp_data);
+          /* Have we handled everything? */
+          if (*tx_1 >= max_s_coord)
+            return TRUE;
+
+          /* clamp tx_2 since we've handled everything with x >
+           * max_s_coord */
+          *tx_2 = max_s_coord;
+        }
+    }
+
+  if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
+    {
+      float height = cogl_texture_get_height (COGL_TEXTURE (meta_texture));
+      float max_t_coord;
+      float half_texel_height;
+
+      /* Consider that rectangle textures have non-normalized
+       * coordinates... */
+      if (cogl_is_texture_rectangle (meta_texture))
+        max_t_coord = height;
+      else
+        max_t_coord = 1.0;
+
+      half_texel_height = max_t_coord / (height * 2);
+
+      /* Handle any top clamped region */
+      if (*ty_1 < 0)
+        {
+          clamp_data.start = *ty_1;
+          clamp_data.end = MIN (0, *ty_2);;
+          cogl_meta_texture_foreach_in_region (meta_texture,
+                                               *tx_1, half_texel_height,
+                                               *tx_2, half_texel_height,
+                                               wrap_s,
+                                               COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                               clamp_t_cb,
+                                               &clamp_data);
+          /* Have we handled everything? */
+          if (tx_2 <= 0)
+            return TRUE;
+
+          /* clamp ty_1 since we've handled everything with y < 0 */
+          *ty_1 = 0;
+        }
+
+      /* Handle any bottom clamped region */
+      if (*ty_2 > max_t_coord)
+        {
+          clamp_data.start = MAX (max_t_coord, *ty_1);;
+          clamp_data.end = *ty_2;
+          cogl_meta_texture_foreach_in_region (meta_texture,
+                                               *tx_1,
+                                               max_t_coord - half_texel_height,
+                                               *tx_2,
+                                               max_t_coord - half_texel_height,
+                                               wrap_s,
+                                               COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                               clamp_t_cb,
+                                               &clamp_data);
+          /* Have we handled everything? */
+          if (*ty_1 >= max_t_coord)
+            return TRUE;
+
+          /* clamp ty_2 since we've handled everything with y >
+           * max_t_coord */
+          *ty_2 = max_t_coord;
+        }
+    }
+
+  if (clamp_data.s_flipped)
+    SWAP (*tx_1, *tx_2);
+  if (clamp_data.t_flipped)
+    SWAP (*ty_1, *ty_2);
+
+  return FALSE;
+}
+
 typedef struct _NormalizeData
 {
   CoglMetaTextureCallback callback;
@@ -287,6 +505,32 @@ cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture,
   float height = cogl_texture_get_height (texture);
   NormalizeData normalize_data;
 
+  if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+    wrap_s = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+  if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+    wrap_t = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+
+  if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE ||
+      wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
+    {
+      gboolean finished = foreach_clamped_region (meta_texture,
+                                                  &tx_1, &ty_1, &tx_2, &ty_2,
+                                                  wrap_s, wrap_t,
+                                                  callback,
+                                                  user_data);
+      if (finished)
+        return;
+
+      /* Since clamping has been handled we now want to normalize our
+       * wrap modes we se can assume from this point on we don't
+       * need to consider CLAMP_TO_EDGE. (NB: The spans code will
+       * assert that CLAMP_TO_EDGE isn't requested) */
+      if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
+        wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT;
+      if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
+        wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT;
+    }
+
   /* It makes things simpler to deal with non-normalized region
    * coordinates beyond this point and only re-normalize just before
    * calling the user's callback... */
@@ -389,3 +633,4 @@ cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture,
                                              user_data);
     }
 }
+#undef SWAP
diff --git a/cogl/cogl-meta-texture.h b/cogl/cogl-meta-texture.h
index f78f1b3..3c82f4f 100644
--- a/cogl/cogl-meta-texture.h
+++ b/cogl/cogl-meta-texture.h
@@ -129,10 +129,8 @@ typedef void (*CoglMetaTextureCallback) (CoglTexture *sub_texture,
  * @ty_1: The top-left y coordinate of the region to iterate
  * @tx_2: The bottom-right x coordinate of the region to iterate
  * @ty_2: The bottom-right y coordinate of the region to iterate
- * @wrap_x: The wrap mode for the x-axis (Only
- *          %COGL_PIPELINE_WRAP_MODE_REPEAT may be passed here.)
- * @wrap_y: The wrap mode for the y-axis (Only
- *          %COGL_PIPELINE_WRAP_MODE_REPEAT may be passed here.)
+ * @wrap_x: The wrap mode for the x-axis
+ * @wrap_y: The wrap mode for the y-axis
  * @callback: A #CoglMetaTextureCallback pointer to be called
  *            for each low-level texture within the specified region.
  * @user_data: A private pointer that is passed to @callback.
@@ -164,13 +162,6 @@ typedef void (*CoglMetaTextureCallback) (CoglTexture *sub_texture,
  * of the @meta_texture, @callback is called specifying how the
  * low-level texture maps to the original region.
  *
- * <note>Currently this interface can only be used to iterate in terms
- * of %COGL_PIPELINE_WRAP_MODE_REPEAT) which means if you want to
- * support %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE for a custom
- * primitive then you need to do that manually by using this interface
- * to find edge textures and then stretching the edge texels out using
- * geometry.</note>
- *
  * Since: 1.10
  * Stability: unstable
  */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]