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>
This commit is contained in:
Robert Bragg 2011-10-27 14:31:15 +01:00
parent 8cf76ee36b
commit 4c49f83056
2 changed files with 247 additions and 11 deletions

View File

@ -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; 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 typedef struct _NormalizeData
{ {
CoglMetaTextureCallback callback; CoglMetaTextureCallback callback;
@ -287,6 +505,32 @@ cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture,
float height = cogl_texture_get_height (texture); float height = cogl_texture_get_height (texture);
NormalizeData normalize_data; 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 /* It makes things simpler to deal with non-normalized region
* coordinates beyond this point and only re-normalize just before * coordinates beyond this point and only re-normalize just before
* calling the user's callback... */ * calling the user's callback... */
@ -389,3 +633,4 @@ cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture,
user_data); user_data);
} }
} }
#undef SWAP

View File

@ -129,10 +129,8 @@ typedef void (*CoglMetaTextureCallback) (CoglTexture *sub_texture,
* @ty_1: The top-left y coordinate of the region to iterate * @ty_1: The top-left y coordinate of the region to iterate
* @tx_2: The bottom-right x 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 * @ty_2: The bottom-right y coordinate of the region to iterate
* @wrap_x: The wrap mode for the x-axis (Only * @wrap_x: The wrap mode for the x-axis
* %COGL_PIPELINE_WRAP_MODE_REPEAT may be passed here.) * @wrap_y: The wrap mode for the y-axis
* @wrap_y: The wrap mode for the y-axis (Only
* %COGL_PIPELINE_WRAP_MODE_REPEAT may be passed here.)
* @callback: A #CoglMetaTextureCallback pointer to be called * @callback: A #CoglMetaTextureCallback pointer to be called
* for each low-level texture within the specified region. * for each low-level texture within the specified region.
* @user_data: A private pointer that is passed to @callback. * @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 * of the @meta_texture, @callback is called specifying how the
* low-level texture maps to the original region. * 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 * Since: 1.10
* Stability: unstable * Stability: unstable
*/ */