1
0
Fork 0
mutter-performance-source/cogl/cogl-sub-texture.c
Neil Roberts c9cafc798c cogl: Add a sub texture backend
This adds a new texture backend which represents a sub texture of a
larger texture. The texture is created with a reference to the full
texture and a set of coordinates describing the region. The backend
simply defers to the full texture for all operations and maps the
coordinates to the other range. You can also use coordinates outside
the range [0,1] to create a repeated version of the full texture.

A new public API function called cogl_texture_new_from_sub_texture is
available to create the sub texture.
2009-12-02 22:03:08 +00:00

699 lines
22 KiB
C

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2009 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Authors:
* Neil Roberts <neil@linux.intel.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl.h"
#include "cogl-internal.h"
#include "cogl-util.h"
#include "cogl-texture-private.h"
#include "cogl-sub-texture-private.h"
#include "cogl-context.h"
#include "cogl-handle.h"
#include "cogl-texture-driver.h"
#include <string.h>
#include <math.h>
static void _cogl_sub_texture_free (CoglSubTexture *sub_tex);
COGL_HANDLE_DEFINE (SubTexture, sub_texture);
static const CoglTextureVtable cogl_sub_texture_vtable;
/* Maps from the texture coordinates of this texture to the texture
coordinates of the full texture */
static void
_cogl_sub_texture_map_coordinate_pair (CoglSubTexture *sub_tex,
gfloat *tx, gfloat *ty)
{
*tx = *tx * (sub_tex->tx2 - sub_tex->tx1) + sub_tex->tx1;
*ty = *ty * (sub_tex->ty2 - sub_tex->ty1) + sub_tex->ty1;
}
static void
_cogl_sub_texture_map_coordinate_set (CoglSubTexture *sub_tex,
gfloat *tx1, gfloat *ty1,
gfloat *tx2, gfloat *ty2)
{
_cogl_sub_texture_map_coordinate_pair (sub_tex, tx1, ty1);
_cogl_sub_texture_map_coordinate_pair (sub_tex, tx2, ty2);
}
/* Maps from the texture coordinates of the full texture to the
texture coordinates of the sub texture */
static void
_cogl_sub_texture_unmap_coordinate_pair (CoglSubTexture *sub_tex,
gfloat *coords)
{
if (sub_tex->tx1 == sub_tex->tx2)
coords[0] = sub_tex->tx1;
else
coords[0] = (coords[0] - sub_tex->tx1) / (sub_tex->tx2 - sub_tex->tx1);
if (sub_tex->ty1 == sub_tex->ty2)
coords[0] = sub_tex->ty1;
else
coords[1] = (coords[1] - sub_tex->ty1) / (sub_tex->ty2 - sub_tex->ty1);
}
static void
_cogl_sub_texture_unmap_coordinate_set (CoglSubTexture *sub_tex,
gfloat *coords)
{
_cogl_sub_texture_unmap_coordinate_pair (sub_tex, coords);
_cogl_sub_texture_unmap_coordinate_pair (sub_tex, coords + 2);
}
static gboolean
_cogl_sub_texture_same_int_part (float t1, float t2)
{
float int_part1, int_part2;
float frac_part1, frac_part2;
frac_part1 = modff (t1, &int_part1);
frac_part2 = modff (t2, &int_part2);
return (int_part1 == int_part2 ||
((frac_part1 == 0.0f || frac_part2 == 0.0f) &&
ABS (int_part1 - int_part2) == 1.0f));
}
typedef struct _CoglSubTextureForeachData
{
CoglSubTexture *sub_tex;
CoglTextureSliceCallback callback;
void *user_data;
} CoglSubTextureForeachData;
static void
_cogl_sub_texture_foreach_cb (CoglHandle handle,
GLuint gl_handle,
GLenum gl_target,
const float *slice_coords,
const float *full_virtual_coords,
void *user_data)
{
CoglSubTextureForeachData *data = user_data;
float virtual_coords[4];
memcpy (virtual_coords, full_virtual_coords, sizeof (virtual_coords));
/* Convert the virtual coords from the full-texture space to the sub
texture space */
_cogl_sub_texture_unmap_coordinate_set (data->sub_tex, virtual_coords);
data->callback (handle, gl_handle, gl_target,
slice_coords, virtual_coords,
data->user_data);
}
static void
_cogl_sub_texture_manual_repeat_cb (const float *coords,
void *user_data)
{
CoglSubTextureForeachData *data = user_data;
float mapped_coords[4];
memcpy (mapped_coords, coords, sizeof (mapped_coords));
_cogl_sub_texture_map_coordinate_set (data->sub_tex,
&mapped_coords[0],
&mapped_coords[1],
&mapped_coords[2],
&mapped_coords[3]);
_cogl_texture_foreach_sub_texture_in_region (data->sub_tex->full_texture,
mapped_coords[0],
mapped_coords[1],
mapped_coords[2],
mapped_coords[3],
_cogl_sub_texture_foreach_cb,
user_data);
}
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,
CoglTextureSliceCallback callback,
void *user_data)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
CoglSubTextureForeachData data;
data.sub_tex = sub_tex;
data.callback = callback;
data.user_data = user_data;
/* If there is no repeating or the sub texture coordinates are a
multiple of the whole texture then we can just directly map the
texture coordinates */
if (sub_tex->tex_coords_are_a_multiple ||
(_cogl_sub_texture_same_int_part (virtual_tx_1, virtual_tx_2) &&
_cogl_sub_texture_same_int_part (virtual_ty_1, virtual_ty_2)))
{
_cogl_sub_texture_map_coordinate_set (sub_tex,
&virtual_tx_1,
&virtual_ty_1,
&virtual_tx_2,
&virtual_ty_2);
_cogl_texture_foreach_sub_texture_in_region
(sub_tex->full_texture,
virtual_tx_1, virtual_ty_1,
virtual_tx_2, virtual_ty_2,
_cogl_sub_texture_foreach_cb, &data);
}
else
_cogl_texture_iterate_manual_repeats (_cogl_sub_texture_manual_repeat_cb,
virtual_tx_1, virtual_ty_1,
virtual_tx_2, virtual_ty_2,
&data);
}
static void
_cogl_sub_texture_set_wrap_mode_parameter (CoglTexture *tex,
GLenum wrap_mode)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
_cogl_texture_set_wrap_mode_parameter (sub_tex->full_texture, wrap_mode);
}
static void
_cogl_sub_texture_free (CoglSubTexture *sub_tex)
{
cogl_handle_unref (sub_tex->full_texture);
g_free (sub_tex);
}
CoglHandle
_cogl_sub_texture_new (CoglHandle full_texture,
gfloat tx1, gfloat ty1,
gfloat tx2, gfloat ty2)
{
CoglSubTexture *sub_tex;
CoglTexture *tex;
gfloat integer_part;
sub_tex = g_new (CoglSubTexture, 1);
tex = COGL_TEXTURE (sub_tex);
tex->vtable = &cogl_sub_texture_vtable;
sub_tex->full_texture = cogl_handle_ref (full_texture);
sub_tex->tx1 = tx1;
sub_tex->ty1 = ty1;
sub_tex->tx2 = tx2;
sub_tex->ty2 = ty2;
/* Track whether the texture coords are a multiple of one because in
that case we can use hardware repeating */
sub_tex->tex_coords_are_a_multiple
= (modff (tx1, &integer_part) == 0.0f &&
modff (ty1, &integer_part) == 0.0f &&
modff (tx2, &integer_part) == 0.0f &&
modff (ty2, &integer_part) == 0.0f);
return _cogl_sub_texture_handle_new (sub_tex);
}
static gint
_cogl_sub_texture_get_max_waste (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
return cogl_texture_get_max_waste (sub_tex->full_texture);
}
static gboolean
_cogl_sub_texture_is_sliced (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
return cogl_texture_is_sliced (sub_tex->full_texture);
}
static gboolean
_cogl_sub_texture_can_hardware_repeat (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
/* We can hardware repeat if the full texture can hardware repeat
and the coordinates for the subregion are all a multiple of the
full size of the texture (ie, they have no fractional part) */
return (sub_tex->tex_coords_are_a_multiple &&
_cogl_texture_can_hardware_repeat (sub_tex->full_texture));
}
static void
_cogl_sub_texture_transform_coords_to_gl (CoglTexture *tex,
float *s,
float *t)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
_cogl_sub_texture_map_coordinate_pair (sub_tex, s, t);
_cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t);
}
static gboolean
_cogl_sub_texture_get_gl_texture (CoglTexture *tex,
GLuint *out_gl_handle,
GLenum *out_gl_target)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
return cogl_texture_get_gl_texture (sub_tex->full_texture,
out_gl_handle,
out_gl_target);
}
static void
_cogl_sub_texture_set_filters (CoglTexture *tex,
GLenum min_filter,
GLenum mag_filter)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
_cogl_texture_set_filters (sub_tex->full_texture, min_filter, mag_filter);
}
static void
_cogl_sub_texture_ensure_mipmaps (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
_cogl_texture_ensure_mipmaps (sub_tex->full_texture);
}
static void
_cogl_sub_texture_get_next_chunk (int pos, int end,
int tex_size,
int *chunk_start, int *chunk_end)
{
/* pos and end may be negative or greater than the size of the
texture. We want to calculate the next largest range we can copy
in one chunk */
if (pos < 0)
/* The behaviour of % for negative numbers is implementation
dependant in C89 so we have to do this */
*chunk_start = (tex_size - pos) % tex_size;
else
*chunk_start = pos % tex_size;
/* If the region is larger than the remaining size of the texture
then we need to crop it */
if (end - pos > tex_size - *chunk_start)
end = pos + tex_size - *chunk_start;
if (end < 0)
*chunk_end = (tex_size - end) % tex_size;
else
*chunk_end = end % tex_size;
if (*chunk_end == 0)
*chunk_end = tex_size;
}
static void
_cogl_sub_texture_get_x_pixel_pos (CoglSubTexture *sub_tex,
gint *px1, gint *px2)
{
gint full_width = cogl_texture_get_width (sub_tex->full_texture);
*px1 = full_width * sub_tex->tx1;
*px2 = full_width * sub_tex->tx2;
if (*px1 > *px2)
{
gint temp = *px1;
*px1 = *px2;
*px2 = temp;
}
}
static void
_cogl_sub_texture_get_y_pixel_pos (CoglSubTexture *sub_tex,
gint *py1, gint *py2)
{
gint full_width = cogl_texture_get_width (sub_tex->full_texture);
*py1 = full_width * sub_tex->ty1;
*py2 = full_width * sub_tex->ty2;
if (*py1 > *py2)
{
gint temp = *py1;
*py1 = *py2;
*py2 = temp;
}
}
static gboolean
_cogl_sub_texture_set_region (CoglTexture *tex,
int src_x,
int src_y,
int dst_x,
int dst_y,
unsigned int dst_width,
unsigned int dst_height,
int width,
int height,
CoglPixelFormat format,
unsigned int rowstride,
const guint8 *data)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
gint full_tex_width, full_tex_height;
gint bpp;
gint px1, py1, px2, py2;
gint it_x, it_y;
gint src_x1, src_y1, src_x2, src_y2;
CoglBitmap source_bmp;
CoglBitmap temp_bmp;
gboolean source_bmp_owner = FALSE;
CoglPixelFormat closest_format;
GLenum closest_gl_format;
GLenum closest_gl_type;
gboolean success;
CoglPixelFormat tex_format;
/* Check for valid format */
if (format == COGL_PIXEL_FORMAT_ANY)
return FALSE;
/* Shortcut out early if the image is empty */
if (width == 0 || height == 0)
return TRUE;
/* FIXME: If the sub texture coordinates are swapped around then we
should flip the bitmap */
_cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2);
_cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2);
full_tex_width = cogl_texture_get_width (sub_tex->full_texture);
full_tex_height = cogl_texture_get_height (sub_tex->full_texture);
/* Init source bitmap */
source_bmp.width = width;
source_bmp.height = height;
source_bmp.format = format;
source_bmp.data = (guchar*) data;
/* Rowstride from texture width if none specified */
bpp = _cogl_get_format_bpp (format);
source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride;
/* Find closest format to internal that's supported by GL */
tex_format = cogl_texture_get_format (sub_tex->full_texture);
closest_format = _cogl_pixel_format_to_gl (tex_format,
NULL, /* don't need */
&closest_gl_format,
&closest_gl_type);
/* If no direct match, convert */
if (closest_format != format)
{
/* Convert to required format */
success = _cogl_bitmap_convert_and_premult (&source_bmp,
&temp_bmp,
closest_format);
/* Swap bitmaps if succeeded */
if (!success) return FALSE;
source_bmp = temp_bmp;
source_bmp_owner = TRUE;
}
for (it_y = py1; it_y < py2; it_y += src_y2 - src_y1)
{
_cogl_sub_texture_get_next_chunk (it_y, py2, full_tex_width,
&src_y1, &src_y2);
for (it_x = px1; it_x < px2; it_x += src_x2 - src_x1)
{
gint virt_x_1, virt_y_1, virt_width, virt_height;
gint copy_dst_x, copy_dst_y, copy_dst_width, copy_dst_height;
_cogl_sub_texture_get_next_chunk (it_x, px2, full_tex_height,
&src_x1, &src_x2);
/* Offset of the chunk from the left edge in the virtual sub
texture coordinates */
virt_x_1 = it_x - px1;
/* Pixel width covered by this chunk */
virt_width = src_x2 - src_x1;
/* Offset of the chunk from the top edge in the virtual sub
texture coordinates */
virt_y_1 = it_y - py1;
/* Pixel height covered by this chunk */
virt_height = src_y2 - src_y1;
/* Check if this chunk intersects with the update region */
if (dst_x + dst_width <= virt_x_1 ||
dst_x >= virt_x_1 + virt_width ||
dst_y + dst_height <= it_y - py1 ||
dst_y >= virt_y_1 + virt_height)
continue;
/* Calculate the intersection in virtual coordinates */
copy_dst_width = dst_width;
if (dst_x < virt_x_1)
{
copy_dst_width -= virt_x_1 - dst_x;
copy_dst_x = virt_x_1;
}
else
copy_dst_x = dst_x;
if (copy_dst_width + copy_dst_x > virt_x_1 + virt_width)
copy_dst_width = virt_x_1 + virt_width - copy_dst_x;
copy_dst_height = dst_height;
if (dst_y < virt_y_1)
{
copy_dst_height -= virt_y_1 - dst_y;
copy_dst_y = virt_y_1;
}
else
copy_dst_y = dst_y;
if (copy_dst_height + copy_dst_y > virt_y_1 + virt_height)
copy_dst_height = virt_y_1 + virt_height - copy_dst_y;
/* Update the region in the full texture */
cogl_texture_set_region (sub_tex->full_texture,
src_x + copy_dst_x - dst_x,
src_y + copy_dst_y - dst_y,
src_x1 + copy_dst_x - virt_x_1,
src_y1 + copy_dst_y - virt_y_1,
copy_dst_width,
copy_dst_height,
width,
height,
format,
rowstride,
data);
}
}
/* Free data if owner */
if (source_bmp_owner)
g_free (source_bmp.data);
return TRUE;
}
static void
_cogl_sub_texture_copy_region (guchar *dst,
const guchar *src,
gint dst_x, gint dst_y,
gint src_x, gint src_y,
gint width, gint height,
gint dst_rowstride,
gint src_rowstride,
gint bpp)
{
int y;
dst += dst_x * bpp + dst_y * dst_rowstride;
src += src_x * bpp + src_y * src_rowstride;
for (y = 0; y < height; y++)
{
memcpy (dst, src, bpp * width);
dst += dst_rowstride;
src += src_rowstride;
}
}
static int
_cogl_sub_texture_get_data (CoglTexture *tex,
CoglPixelFormat format,
unsigned int rowstride,
guint8 *data)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
unsigned int full_rowstride;
guint8 *full_data;
int byte_size, full_size;
gint bpp;
gint px1, py1, px2, py2;
gint full_tex_width, full_tex_height;
/* FIXME: This gets the full data from the full texture and then
copies a subregion of that. It would be better if there was a
texture_get_sub_data virtual and it can just munge the texture
coordinates */
/* Default to internal format if none specified */
if (format == COGL_PIXEL_FORMAT_ANY)
format = cogl_texture_get_format (sub_tex->full_texture);
_cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2);
_cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2);
full_tex_width = cogl_texture_get_width (sub_tex->full_texture);
full_tex_height = cogl_texture_get_height (sub_tex->full_texture);
/* Rowstride from texture width if none specified */
bpp = _cogl_get_format_bpp (format);
if (rowstride == 0)
rowstride = px2 - px1;
/* Return byte size if only that requested */
byte_size = (py2 - py1) * rowstride;
if (data == NULL)
return byte_size;
full_rowstride = _cogl_get_format_bpp (format) * full_tex_width;
full_data = g_malloc (full_rowstride * full_tex_height);
full_size = cogl_texture_get_data (sub_tex->full_texture, format,
full_rowstride, full_data);
if (full_size)
{
int dst_x, dst_y;
int src_x1, src_y1;
int src_x2, src_y2;
for (dst_y = py1; dst_y < py2; dst_y += src_y2 - src_y1)
{
_cogl_sub_texture_get_next_chunk (dst_y, py2, full_tex_width,
&src_y1, &src_y2);
for (dst_x = px1; dst_x < px2; dst_x += src_x2 - src_x1)
{
_cogl_sub_texture_get_next_chunk (dst_x, px2, full_tex_height,
&src_x1, &src_x2);
_cogl_sub_texture_copy_region (data, full_data,
dst_x - px1, dst_y - py1,
src_x1, src_y1,
src_x2 - src_x1,
src_y2 - src_y1,
rowstride,
full_rowstride,
bpp);
}
}
}
else
byte_size = 0;
g_free (full_data);
return byte_size;
}
static CoglPixelFormat
_cogl_sub_texture_get_format (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
return cogl_texture_get_format (sub_tex->full_texture);
}
static GLenum
_cogl_sub_texture_get_gl_format (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
return _cogl_texture_get_gl_format (sub_tex->full_texture);
}
static gint
_cogl_sub_texture_get_width (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
gint px1, px2;
_cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2);
return px2 - px1;
}
static gint
_cogl_sub_texture_get_height (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
gint py1, py2;
_cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2);
return py2 - py1;
}
static const CoglTextureVtable
cogl_sub_texture_vtable =
{
_cogl_sub_texture_set_region,
_cogl_sub_texture_get_data,
_cogl_sub_texture_foreach_sub_texture_in_region,
_cogl_sub_texture_get_max_waste,
_cogl_sub_texture_is_sliced,
_cogl_sub_texture_can_hardware_repeat,
_cogl_sub_texture_transform_coords_to_gl,
_cogl_sub_texture_get_gl_texture,
_cogl_sub_texture_set_filters,
_cogl_sub_texture_ensure_mipmaps,
_cogl_sub_texture_set_wrap_mode_parameter,
_cogl_sub_texture_get_format,
_cogl_sub_texture_get_gl_format,
_cogl_sub_texture_get_width,
_cogl_sub_texture_get_height
};