static void _cogl_sub_texture_map_quad (CoglSubTexture *sub_tex, float *coords) { CoglTexture *tex = COGL_TEXTURE (sub_tex); /* NB: coords[] always come in as normalized coordinates but may go * out as non-normalized if sub_tex->full_texture is a * CoglTextureRectangle. * * NB: sub_tex->sub_x/y/width/height are in non-normalized * coordinates. */ if (cogl_is_texture_rectangle (sub_tex->full_texture)) { coords[0] = coords[0] * tex->width + sub_tex->sub_x; coords[1] = coords[1] * tex->height + sub_tex->sub_y; coords[2] = coords[2] * tex->width + sub_tex->sub_x; coords[3] = coords[3] * tex->height + sub_tex->sub_y; } else { float width = cogl_texture_get_width (sub_tex->full_texture); float height = cogl_texture_get_height (sub_tex->full_texture); coords[0] = (coords[0] * tex->width + sub_tex->sub_x) / width; coords[1] = (coords[1] * tex->height + sub_tex->sub_y) / height; coords[2] = (coords[2] * tex->width + sub_tex->sub_x) / width; coords[3] = (coords[3] * tex->height + sub_tex->sub_y) / height; } }
static void _cogl_sub_texture_unmap_quad (CoglSubTexture *sub_tex, float *coords) { CoglTexture *tex = COGL_TEXTURE (sub_tex); /* NB: coords[] come in as non-normalized if sub_tex->full_texture * is a CoglTextureRectangle otherwhise they are normalized. The * coordinates we write out though must always be normalized. * * NB: sub_tex->sub_x/y/width/height are in non-normalized * coordinates. */ if (cogl_is_texture_rectangle (sub_tex->full_texture)) { coords[0] = (coords[0] - sub_tex->sub_x) / tex->width; coords[1] = (coords[1] - sub_tex->sub_y) / tex->height; coords[2] = (coords[2] - sub_tex->sub_x) / tex->width; coords[3] = (coords[3] - sub_tex->sub_y) / tex->height; } else { float width = cogl_texture_get_width (sub_tex->full_texture); float height = cogl_texture_get_height (sub_tex->full_texture); coords[0] = (coords[0] * width - sub_tex->sub_x) / tex->width; coords[1] = (coords[1] * height - sub_tex->sub_y) / tex->height; coords[2] = (coords[2] * width - sub_tex->sub_x) / tex->width; coords[3] = (coords[3] * height - sub_tex->sub_y) / tex->height; } }
static void _cogl_texture_pixmap_x11_foreach_sub_texture_in_region (CoglTexture *tex, float virtual_tx_1, float virtual_ty_1, float virtual_tx_2, float virtual_ty_2, CoglMetaTextureCallback callback, void *user_data) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ /* tfp textures may be implemented in terms of a * CoglTextureRectangle texture which uses un-normalized texture * coordinates but we want to consistently deal with normalized * texture coordinates with CoglTexturePixmapX11... */ if (cogl_is_texture_rectangle (child_tex)) { NormalizeCoordsWrapperData data; int width = tex->width; int height = tex->height; virtual_tx_1 *= width; virtual_ty_1 *= height; virtual_tx_2 *= width; virtual_ty_2 *= height; data.width = width; data.height = height; data.callback = callback; data.user_data = user_data; cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (child_tex), virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, normalize_coords_wrapper_cb, &data); } else cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (child_tex), virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, callback, user_data); }
static void texture_rectangle_check_cb (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data) { gboolean *result = user_data; if (cogl_is_texture_rectangle (sub_texture)) *result = TRUE; }
static void _cogl_sub_texture_foreach_sub_texture_in_region ( CoglTexture *tex, float virtual_tx_1, float virtual_ty_1, float virtual_tx_2, float virtual_ty_2, CoglMetaTextureCallback callback, void *user_data) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); CoglTexture *full_texture = sub_tex->full_texture; float mapped_coords[4] = { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2}; float virtual_coords[4] = { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2}; /* map the virtual coordinates to ->full_texture coordinates */ _cogl_sub_texture_map_quad (sub_tex, mapped_coords); /* TODO: Add something like cogl_is_low_level_texture() */ if (cogl_is_texture_2d (full_texture) || cogl_is_texture_rectangle (full_texture)) { callback (sub_tex->full_texture, mapped_coords, virtual_coords, user_data); } else { CoglSubTextureForeachData data; data.sub_tex = sub_tex; data.callback = callback; data.user_data = user_data; cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (full_texture), mapped_coords[0], mapped_coords[1], mapped_coords[2], mapped_coords[3], COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, unmap_coords_cb, &data); } }
void cogl_meta_texture_foreach_in_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) { CoglTexture *texture = COGL_TEXTURE (meta_texture); float width = cogl_texture_get_width (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) { CoglBool 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... */ if (!cogl_is_texture_rectangle (COGL_TEXTURE (meta_texture))) { normalize_data.callback = callback; normalize_data.user_data = user_data; normalize_data.s_normalize_factor = 1.0f / width; normalize_data.t_normalize_factor = 1.0f / height; callback = normalize_meta_coords_cb; user_data = &normalize_data; tx_1 *= width; ty_1 *= height; tx_2 *= width; ty_2 *= height; } /* XXX: at some point this wont be routed through the CoglTexture * vtable, instead there will be a separate CoglMetaTexture * interface vtable. */ if (texture->vtable->foreach_sub_texture_in_region) { ForeachData data; data.meta_region_coords[0] = tx_1; data.meta_region_coords[1] = ty_1; data.meta_region_coords[2] = tx_2; data.meta_region_coords[3] = ty_2; data.wrap_s = wrap_s; data.wrap_t = wrap_t; data.callback = callback; data.user_data = user_data; data.width = width; data.height = height; memset (data.padded_textures, 0, sizeof (data.padded_textures)); /* * 1) We iterate all the slices of the meta-texture only within * the range [0,1]. * * 2) We define a "padded grid" for each slice of the * meta-texture in the range [0,1]. * * The padded grid maps over the meta-texture coordinates in * the range [0,1] but only contains one valid cell that * corresponds to current slice being iterated and all the * surrounding cells just provide padding. * * 3) Once we've defined our padded grid we then repeat that * across the user's original region, calling their callback * whenever we see our current slice - ignoring padding. * * A notable benefit of this design is that repeating a texture * made of multiple slices will result in us repeating each * slice in-turn so the user gets repeat callbacks for the same * texture batched together. For manual emulation of texture * repeats done by drawing geometry this makes it more likely * that we can batch geometry. */ texture->vtable->foreach_sub_texture_in_region (texture, 0, 0, 1, 1, create_grid_and_repeat_cb, &data); } else { CoglSpan x_span = { 0, width, 0 }; CoglSpan y_span = { 0, height, 0 }; float meta_region_coords[4] = { tx_1, ty_1, tx_2, ty_2 }; UnNormalizeData un_normalize_data; /* If we are dealing with a CoglTextureRectangle then we need a shim * callback that un_normalizes the slice coordinates we get from * _cogl_texture_spans_foreach_in_region before passing them to * the user's callback. */ if (cogl_is_texture_rectangle (meta_texture)) { un_normalize_data.callback = callback; un_normalize_data.user_data = user_data; un_normalize_data.width = width; un_normalize_data.height = height; callback = un_normalize_slice_coords_cb; user_data = &un_normalize_data; } _cogl_texture_spans_foreach_in_region (&x_span, 1, &y_span, 1, &texture, meta_region_coords, width, height, wrap_s, wrap_t, callback, user_data); } }
static CoglBool 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; }