Merge branch 'more-texture-backends'

This adds three new texture backends.

- CoglTexture2D: This is a trimmed down version of CoglTexture2DSliced
  which only supports a single texture and only works with the
  GL_TEXTURE_2D target. The code is a lot simpler so it has a less
  overheads than dealing with slices. Cogl will use this wherever
  possible.

- CoglSubTexture: This is used to get a CoglHandle to represent a
  subregion of another texture. The texture can be used as if it was a
  standalone texture but it does not need to copy the resources.

- CoglAtlasTexture: This collects RGB and RGBA textures into a single
  GL texture with the aim of reducing texture state changes and
  increasing batching. The backend will try to manage the atlas and
  may move the textures around to close gaps in the texture. By
  default all textures will be placed in the atlas.
This commit is contained in:
Neil Roberts 2010-02-06 00:20:07 +00:00
commit 830f2402d4
37 changed files with 4493 additions and 735 deletions

View File

@ -112,11 +112,19 @@ cogl_sources_c = \
$(srcdir)/cogl-blend-string.c \ $(srcdir)/cogl-blend-string.c \
$(srcdir)/cogl-blend-string.h \ $(srcdir)/cogl-blend-string.h \
$(srcdir)/cogl-debug.c \ $(srcdir)/cogl-debug.c \
$(srcdir)/cogl-sub-texture-private.h \
$(srcdir)/cogl-texture-private.h \ $(srcdir)/cogl-texture-private.h \
$(srcdir)/cogl-texture-2d-private.h \
$(srcdir)/cogl-texture-2d-sliced-private.h \ $(srcdir)/cogl-texture-2d-sliced-private.h \
$(srcdir)/cogl-texture-driver.h \ $(srcdir)/cogl-texture-driver.h \
$(srcdir)/cogl-sub-texture.c \
$(srcdir)/cogl-texture.c \ $(srcdir)/cogl-texture.c \
$(srcdir)/cogl-texture-2d.c \
$(srcdir)/cogl-texture-2d-sliced.c \ $(srcdir)/cogl-texture-2d-sliced.c \
$(srcdir)/cogl-atlas.h \
$(srcdir)/cogl-atlas.c \
$(srcdir)/cogl-atlas-texture-private.h \
$(srcdir)/cogl-atlas-texture.c \
$(srcdir)/cogl-spans.h \ $(srcdir)/cogl-spans.h \
$(srcdir)/cogl-spans.c \ $(srcdir)/cogl-spans.c \
$(srcdir)/cogl-journal-private.h \ $(srcdir)/cogl-journal-private.h \

View File

@ -0,0 +1,64 @@
/*
* 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.
*/
#ifndef __COGL_ATLAS_TEXTURE_H
#define __COGL_ATLAS_TEXTURE_H
#include "cogl-handle.h"
#include "cogl-texture-private.h"
#include "cogl-atlas.h"
#define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex)
typedef struct _CoglAtlasTexture CoglAtlasTexture;
struct _CoglAtlasTexture
{
CoglTexture _parent;
/* The format that the texture is in. This isn't necessarily the
same format as the atlas texture because we can store
pre-multiplied and non-pre-multiplied textures together */
CoglPixelFormat format;
/* The rectangle that was used to add this texture to the
atlas. This includes the 1-pixel border */
CoglAtlasRectangle rectangle;
/* The texture might need to be migrated out in which case this will
be set to TRUE and sub_texture will actually be a real texture */
gboolean in_atlas;
/* A CoglSubTexture representing the region for easy rendering */
CoglHandle sub_texture;
};
GQuark
_cogl_handle_atlas_texture_get_type (void);
CoglHandle
_cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle,
CoglTextureFlags flags,
CoglPixelFormat internal_format);
#endif /* __COGL_ATLAS_TEXTURE_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,596 @@
/*
* 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 <glib.h>
#include "cogl-atlas.h"
#include "cogl-debug.h"
/* Implements a data structure which keeps track of unused
sub-rectangles within a larger rectangle using a binary tree
structure. The algorithm for this is based on the description here:
http://www.blackpawn.com/texts/lightmaps/default.html
*/
#ifdef COGL_ENABLE_DEBUG
/* The cairo header is only used for debugging to generate an image of
the atlas */
#include <cairo.h>
static void _cogl_atlas_dump_image (CoglAtlas *atlas);
#endif /* COGL_ENABLE_DEBUG */
typedef struct _CoglAtlasNode CoglAtlasNode;
typedef struct _CoglAtlasStackEntry CoglAtlasStackEntry;
typedef void (* CoglAtlasInternalForeachCb) (CoglAtlasNode *node,
gpointer data);
typedef enum
{
COGL_ATLAS_BRANCH,
COGL_ATLAS_FILLED_LEAF,
COGL_ATLAS_EMPTY_LEAF
} CoglAtlasNodeType;
struct _CoglAtlas
{
CoglAtlasNode *root;
guint space_remaining;
guint n_rectangles;
GDestroyNotify value_destroy_func;
};
struct _CoglAtlasNode
{
CoglAtlasNodeType type;
CoglAtlasRectangle rectangle;
CoglAtlasNode *parent;
union
{
/* Fields used when this is a branch */
struct
{
CoglAtlasNode *left;
CoglAtlasNode *right;
} branch;
/* Field used when this is a filled leaf */
gpointer data;
} d;
};
struct _CoglAtlasStackEntry
{
/* The node to search */
CoglAtlasNode *node;
/* Index of next branch of this node to explore. Basically either 0
to go left or 1 to go right */
gboolean next_index;
/* Next entry in the stack */
CoglAtlasStackEntry *next;
};
static CoglAtlasNode *
_cogl_atlas_node_new (void)
{
return g_slice_new (CoglAtlasNode);
}
static void
_cogl_atlas_node_free (CoglAtlasNode *node)
{
g_slice_free (CoglAtlasNode, node);
}
CoglAtlas *
_cogl_atlas_new (guint width, guint height,
GDestroyNotify value_destroy_func)
{
CoglAtlas *atlas = g_new (CoglAtlas, 1);
CoglAtlasNode *root = _cogl_atlas_node_new ();
root->type = COGL_ATLAS_EMPTY_LEAF;
root->parent = NULL;
root->rectangle.x = 0;
root->rectangle.y = 0;
root->rectangle.width = width;
root->rectangle.height = height;
atlas->root = root;
atlas->space_remaining = width * height;
atlas->n_rectangles = 0;
atlas->value_destroy_func = value_destroy_func;
return atlas;
}
static CoglAtlasStackEntry *
_cogl_atlas_stack_push (CoglAtlasStackEntry *stack,
CoglAtlasNode *node,
gboolean next_index)
{
CoglAtlasStackEntry *new_entry = g_slice_new (CoglAtlasStackEntry);
new_entry->node = node;
new_entry->next_index = next_index;
new_entry->next = stack;
return new_entry;
}
static CoglAtlasStackEntry *
_cogl_atlas_stack_pop (CoglAtlasStackEntry *stack)
{
CoglAtlasStackEntry *next = stack->next;
g_slice_free (CoglAtlasStackEntry, stack);
return next;
}
static CoglAtlasNode *
_cogl_atlas_node_split_horizontally (CoglAtlasNode *node,
guint left_width)
{
/* Splits the node horizontally (according to emacs' definition, not
vim) by converting it to a branch and adding two new leaf
nodes. The leftmost branch will have the width left_width and
will be returned. If the node is already just the right size it
won't do anything */
CoglAtlasNode *left_node, *right_node;
if (node->rectangle.width == left_width)
return node;
left_node = _cogl_atlas_node_new ();
left_node->type = COGL_ATLAS_EMPTY_LEAF;
left_node->parent = node;
left_node->rectangle.x = node->rectangle.x;
left_node->rectangle.y = node->rectangle.y;
left_node->rectangle.width = left_width;
left_node->rectangle.height = node->rectangle.height;
node->d.branch.left = left_node;
right_node = _cogl_atlas_node_new ();
right_node->type = COGL_ATLAS_EMPTY_LEAF;
right_node->parent = node;
right_node->rectangle.x = node->rectangle.x + left_width;
right_node->rectangle.y = node->rectangle.y;
right_node->rectangle.width = node->rectangle.width - left_width;
right_node->rectangle.height = node->rectangle.height;
node->d.branch.right = right_node;
node->type = COGL_ATLAS_BRANCH;
return left_node;
}
static CoglAtlasNode *
_cogl_atlas_node_split_vertically (CoglAtlasNode *node,
guint top_height)
{
/* Splits the node vertically (according to emacs' definition, not
vim) by converting it to a branch and adding two new leaf
nodes. The topmost branch will have the height top_height and
will be returned. If the node is already just the right size it
won't do anything */
CoglAtlasNode *top_node, *bottom_node;
if (node->rectangle.height == top_height)
return node;
top_node = _cogl_atlas_node_new ();
top_node->type = COGL_ATLAS_EMPTY_LEAF;
top_node->parent = node;
top_node->rectangle.x = node->rectangle.x;
top_node->rectangle.y = node->rectangle.y;
top_node->rectangle.width = node->rectangle.width;
top_node->rectangle.height = top_height;
node->d.branch.left = top_node;
bottom_node = _cogl_atlas_node_new ();
bottom_node->type = COGL_ATLAS_EMPTY_LEAF;
bottom_node->parent = node;
bottom_node->rectangle.x = node->rectangle.x;
bottom_node->rectangle.y = node->rectangle.y + top_height;
bottom_node->rectangle.width = node->rectangle.width;
bottom_node->rectangle.height = node->rectangle.height - top_height;
node->d.branch.right = bottom_node;
node->type = COGL_ATLAS_BRANCH;
return top_node;
}
gboolean
_cogl_atlas_add_rectangle (CoglAtlas *atlas,
guint width, guint height,
gpointer data,
CoglAtlasRectangle *rectangle)
{
/* Stack of nodes to search in */
CoglAtlasStackEntry *node_stack;
CoglAtlasNode *found_node = NULL;
/* Zero-sized rectangles break the algorithm for removing rectangles
so we'll disallow them */
g_return_val_if_fail (width > 0 && height > 0, FALSE);
/* Start with the root node */
node_stack = _cogl_atlas_stack_push (NULL, atlas->root, FALSE);
/* Depth-first search for an empty node that is big enough */
while (node_stack)
{
/* Pop an entry off the stack */
CoglAtlasNode *node = node_stack->node;
int next_index = node_stack->next_index;
node_stack = _cogl_atlas_stack_pop (node_stack);
/* Regardless of the type of the node, there's no point
descending any further if the new rectangle won't fit within
it */
if (node->rectangle.width >= width &&
node->rectangle.height >= height)
{
if (node->type == COGL_ATLAS_EMPTY_LEAF)
{
/* We've found a node we can use */
found_node = node;
break;
}
else if (node->type == COGL_ATLAS_BRANCH)
{
if (next_index)
/* Try the right branch */
node_stack = _cogl_atlas_stack_push (node_stack,
node->d.branch.right,
0);
else
{
/* Make sure we remember to try the right branch once
we've finished descending the left branch */
node_stack = _cogl_atlas_stack_push (node_stack,
node,
1);
/* Try the left branch */
node_stack = _cogl_atlas_stack_push (node_stack,
node->d.branch.left,
0);
}
}
}
}
/* Free the stack */
while (node_stack)
node_stack = _cogl_atlas_stack_pop (node_stack);
if (found_node)
{
/* Split according to whichever axis will leave us with the
largest space */
if (found_node->rectangle.width - width >
found_node->rectangle.height - height)
{
found_node = _cogl_atlas_node_split_horizontally (found_node, width);
found_node = _cogl_atlas_node_split_vertically (found_node, height);
}
else
{
found_node = _cogl_atlas_node_split_vertically (found_node, height);
found_node = _cogl_atlas_node_split_horizontally (found_node, width);
}
found_node->type = COGL_ATLAS_FILLED_LEAF;
found_node->d.data = data;
if (rectangle)
*rectangle = found_node->rectangle;
/* Record how much empty space is remaining after this rectangle
is added */
g_assert (width * height <= atlas->space_remaining);
atlas->space_remaining -= width * height;
atlas->n_rectangles++;
#ifdef COGL_ENABLE_DEBUG
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE))
_cogl_atlas_dump_image (atlas);
#endif
return TRUE;
}
else
return FALSE;
}
void
_cogl_atlas_remove_rectangle (CoglAtlas *atlas,
const CoglAtlasRectangle *rectangle)
{
CoglAtlasNode *node = atlas->root;
/* We can do a binary-chop down the search tree to find the rectangle */
while (node->type == COGL_ATLAS_BRANCH)
{
CoglAtlasNode *left_node = node->d.branch.left;
/* If and only if the rectangle is in the left node then the x,y
position of the rectangle will be within the node's
rectangle */
if (rectangle->x < left_node->rectangle.x + left_node->rectangle.width &&
rectangle->y < left_node->rectangle.y + left_node->rectangle.height)
/* Go left */
node = left_node;
else
/* Go right */
node = node->d.branch.right;
}
/* Make sure we found the right node */
if (node->type != COGL_ATLAS_FILLED_LEAF ||
node->rectangle.x != rectangle->x ||
node->rectangle.y != rectangle->y ||
node->rectangle.width != rectangle->width ||
node->rectangle.height != rectangle->height)
/* This should only happen if someone tried to remove a rectangle
that was not in the atlas so something has gone wrong */
g_return_if_reached ();
else
{
/* Convert the node back to an empty node */
if (atlas->value_destroy_func)
atlas->value_destroy_func (node->d.data);
node->type = COGL_ATLAS_EMPTY_LEAF;
/* Walk back up the tree combining branch nodes that have two
empty leaves back into a single empty leaf */
for (node = node->parent; node; node = node->parent)
{
/* This node is a parent so it should always be a branch */
g_assert (node->type == COGL_ATLAS_BRANCH);
if (node->d.branch.left->type == COGL_ATLAS_EMPTY_LEAF &&
node->d.branch.right->type == COGL_ATLAS_EMPTY_LEAF)
{
_cogl_atlas_node_free (node->d.branch.left);
_cogl_atlas_node_free (node->d.branch.right);
node->type = COGL_ATLAS_EMPTY_LEAF;
}
else
break;
}
/* There is now more free space and one less rectangle */
atlas->space_remaining += rectangle->width * rectangle->height;
g_assert (atlas->n_rectangles > 0);
atlas->n_rectangles--;
}
#ifdef COGL_ENABLE_DEBUG
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE))
_cogl_atlas_dump_image (atlas);
#endif
}
guint
_cogl_atlas_get_width (CoglAtlas *atlas)
{
return atlas->root->rectangle.width;
}
guint
_cogl_atlas_get_height (CoglAtlas *atlas)
{
return atlas->root->rectangle.height;
}
guint
_cogl_atlas_get_remaining_space (CoglAtlas *atlas)
{
return atlas->space_remaining;
}
guint
_cogl_atlas_get_n_rectangles (CoglAtlas *atlas)
{
return atlas->n_rectangles;
}
static void
_cogl_atlas_internal_foreach (CoglAtlas *atlas,
CoglAtlasInternalForeachCb callback,
gpointer data)
{
/* Stack of nodes to search in */
CoglAtlasStackEntry *node_stack;
/* Start with the root node */
node_stack = _cogl_atlas_stack_push (NULL, atlas->root, 0);
/* Iterate all nodes depth-first */
while (node_stack)
{
CoglAtlasNode *node = node_stack->node;
switch (node->type)
{
case COGL_ATLAS_BRANCH:
if (node_stack->next_index == 0)
{
/* Next time we come back to this node, go to the right */
node_stack->next_index = 1;
/* Explore the left branch next */
node_stack = _cogl_atlas_stack_push (node_stack,
node->d.branch.left,
0);
}
else if (node_stack->next_index == 1)
{
/* Next time we come back to this node, stop processing it */
node_stack->next_index = 2;
/* Explore the right branch next */
node_stack = _cogl_atlas_stack_push (node_stack,
node->d.branch.right,
0);
}
else
{
/* We're finished with this node so we can call the callback */
callback (node, data);
node_stack = _cogl_atlas_stack_pop (node_stack);
}
break;
default:
/* Some sort of leaf node, just call the callback */
callback (node, data);
node_stack = _cogl_atlas_stack_pop (node_stack);
break;
}
}
/* The stack should now be empty */
g_assert (node_stack == NULL);
}
typedef struct _CoglAtlasForeachClosure
{
CoglAtlasCallback callback;
gpointer data;
} CoglAtlasForeachClosure;
static void
_cogl_atlas_foreach_cb (CoglAtlasNode *node, gpointer data)
{
CoglAtlasForeachClosure *closure = data;
if (node->type == COGL_ATLAS_FILLED_LEAF)
closure->callback (&node->rectangle, node->d.data, closure->data);
}
void
_cogl_atlas_foreach (CoglAtlas *atlas,
CoglAtlasCallback callback,
gpointer data)
{
CoglAtlasForeachClosure closure;
closure.callback = callback;
closure.data = data;
_cogl_atlas_internal_foreach (atlas, _cogl_atlas_foreach_cb, &closure);
}
static void
_cogl_atlas_free_cb (CoglAtlasNode *node, gpointer data)
{
CoglAtlas *atlas = data;
if (node->type == COGL_ATLAS_FILLED_LEAF && atlas->value_destroy_func)
atlas->value_destroy_func (node->d.data);
_cogl_atlas_node_free (node);
}
void
_cogl_atlas_free (CoglAtlas *atlas)
{
_cogl_atlas_internal_foreach (atlas, _cogl_atlas_free_cb, atlas);
g_free (atlas);
}
#ifdef COGL_ENABLE_DEBUG
static void
_cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data)
{
cairo_t *cr = data;
if (node->type == COGL_ATLAS_FILLED_LEAF ||
node->type == COGL_ATLAS_EMPTY_LEAF)
{
/* Fill the rectangle using a different colour depending on
whether the rectangle is used */
if (node->type == COGL_ATLAS_FILLED_LEAF)
cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
else
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_rectangle (cr,
node->rectangle.x,
node->rectangle.y,
node->rectangle.width,
node->rectangle.height);
cairo_fill_preserve (cr);
/* Draw a white outline around the rectangle */
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_stroke (cr);
}
}
static void
_cogl_atlas_dump_image (CoglAtlas *atlas)
{
/* This dumps a png to help visualize the atlas. Each leaf rectangle
is drawn with a white outline. Unused leaves are filled in black
and used leaves are blue */
cairo_surface_t *surface =
cairo_image_surface_create (CAIRO_FORMAT_RGB24,
_cogl_atlas_get_width (atlas),
_cogl_atlas_get_height (atlas));
cairo_t *cr = cairo_create (surface);
_cogl_atlas_internal_foreach (atlas, _cogl_atlas_dump_image_cb, cr);
cairo_destroy (cr);
cairo_surface_write_to_png (surface, "cogl-atlas-dump.png");
cairo_surface_destroy (surface);
}
#endif /* COGL_ENABLE_DEBUG */

View File

@ -0,0 +1,76 @@
/*
* 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.
*/
#ifndef __COGL_ATLAS_H
#define __COGL_ATLAS_H
#include <glib.h>
typedef struct _CoglAtlas CoglAtlas;
typedef struct _CoglAtlasRectangle CoglAtlasRectangle;
typedef void (* CoglAtlasCallback) (const CoglAtlasRectangle *rectangle,
gpointer rectangle_data,
gpointer user_data);
struct _CoglAtlasRectangle
{
guint x, y;
guint width, height;
};
CoglAtlas *
_cogl_atlas_new (guint width, guint height,
GDestroyNotify value_destroy_func);
gboolean
_cogl_atlas_add_rectangle (CoglAtlas *atlas,
guint width, guint height,
gpointer data,
CoglAtlasRectangle *rectangle);
void
_cogl_atlas_remove_rectangle (CoglAtlas *atlas,
const CoglAtlasRectangle *rectangle);
guint
_cogl_atlas_get_width (CoglAtlas *atlas);
guint
_cogl_atlas_get_height (CoglAtlas *atlas);
guint
_cogl_atlas_get_remaining_space (CoglAtlas *atlas);
guint
_cogl_atlas_get_n_rectangles (CoglAtlas *atlas);
void
_cogl_atlas_foreach (CoglAtlas *atlas,
CoglAtlasCallback callback,
gpointer data);
void
_cogl_atlas_free (CoglAtlas *atlas);
#endif /* __COGL_ATLAS_H */

View File

@ -150,7 +150,7 @@ _cogl_rgba_to_abgr (const guchar *src, guchar *dst)
/* (Un)Premultiplication */ /* (Un)Premultiplication */
inline static void inline static void
_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) _cogl_unpremult_alpha_0 (guchar *dst)
{ {
dst[0] = 0; dst[0] = 0;
dst[1] = 0; dst[1] = 0;
@ -159,58 +159,58 @@ _cogl_unpremult_alpha_0 (const guchar *src, guchar *dst)
} }
inline static void inline static void
_cogl_unpremult_alpha_last (const guchar *src, guchar *dst) _cogl_unpremult_alpha_last (guchar *dst)
{ {
guchar alpha = src[3]; guchar alpha = dst[3];
dst[0] = (src[0] * 255) / alpha; dst[0] = (dst[0] * 255) / alpha;
dst[1] = (src[1] * 255) / alpha; dst[1] = (dst[1] * 255) / alpha;
dst[2] = (src[2] * 255) / alpha; dst[2] = (dst[2] * 255) / alpha;
dst[3] = alpha;
} }
inline static void inline static void
_cogl_unpremult_alpha_first (const guchar *src, guchar *dst) _cogl_unpremult_alpha_first (guchar *dst)
{ {
guchar alpha = src[0]; guchar alpha = dst[0];
dst[0] = alpha; dst[1] = (dst[1] * 255) / alpha;
dst[1] = (src[1] * 255) / alpha; dst[2] = (dst[2] * 255) / alpha;
dst[2] = (src[2] * 255) / alpha; dst[3] = (dst[3] * 255) / alpha;
dst[3] = (src[3] * 255) / alpha;
} }
/* No division form of floor((c*a + 128)/255) (I first encountered /* No division form of floor((c*a + 128)/255) (I first encountered
* this in the RENDER implementation in the X server.) Being exact * this in the RENDER implementation in the X server.) Being exact
* is important for a == 255 - we want to get exactly c. * is important for a == 255 - we want to get exactly c.
*/ */
#define MULT(d,c,a,t) G_STMT_START { t = c * a + 128; d = ((t >> 8) + t) >> 8; } G_STMT_END #define MULT(d,a,t) \
G_STMT_START { \
t = d * a + 128; \
d = ((t >> 8) + t) >> 8; \
} G_STMT_END
inline static void inline static void
_cogl_premult_alpha_last (const guchar *src, guchar *dst) _cogl_premult_alpha_last (guchar *dst)
{ {
guchar alpha = src[3]; guchar alpha = dst[3];
/* Using a separate temporary per component has given slightly better /* Using a separate temporary per component has given slightly better
* code generation with GCC in the past; it shouldn't do any worse in * code generation with GCC in the past; it shouldn't do any worse in
* any case. * any case.
*/ */
guint t1, t2, t3; guint t1, t2, t3;
MULT(dst[0], src[0], alpha, t1); MULT(dst[0], alpha, t1);
MULT(dst[1], src[1], alpha, t2); MULT(dst[1], alpha, t2);
MULT(dst[2], src[2], alpha, t3); MULT(dst[2], alpha, t3);
dst[3] = alpha;
} }
inline static void inline static void
_cogl_premult_alpha_first (const guchar *src, guchar *dst) _cogl_premult_alpha_first (guchar *dst)
{ {
guchar alpha = src[0]; guchar alpha = dst[0];
guint t1, t2, t3; guint t1, t2, t3;
dst[0] = alpha; MULT(dst[1], alpha, t1);
MULT(dst[1], src[1], alpha, t1); MULT(dst[2], alpha, t2);
MULT(dst[2], src[2], alpha, t2); MULT(dst[3], alpha, t3);
MULT(dst[3], src[3], alpha, t3);
} }
#undef MULT #undef MULT
@ -342,112 +342,82 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
} }
gboolean gboolean
_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, _cogl_bitmap_fallback_unpremult (CoglBitmap *bmp)
CoglBitmap *dst_bmp)
{ {
guchar *src; guchar *p;
guchar *dst;
gint bpp;
gint x,y; gint x,y;
/* Make sure format supported for un-premultiplication */ /* Make sure format supported for un-premultiplication */
if (!_cogl_bitmap_fallback_can_unpremult (bmp->format)) if (!_cogl_bitmap_fallback_can_unpremult (bmp->format))
return FALSE; return FALSE;
bpp = _cogl_get_format_bpp (bmp->format);
/* Initialize destination bitmap */
*dst_bmp = *bmp;
dst_bmp->format = (bmp->format & COGL_UNPREMULT_MASK);
/* Allocate a new buffer to hold converted data */
dst_bmp->data = g_malloc (sizeof(guchar)
* dst_bmp->height
* dst_bmp->rowstride);
for (y = 0; y < bmp->height; y++) for (y = 0; y < bmp->height; y++)
{ {
src = (guchar*)bmp->data + y * bmp->rowstride; p = (guchar*) bmp->data + y * bmp->rowstride;
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
if (bmp->format & COGL_AFIRST_BIT) if (bmp->format & COGL_AFIRST_BIT)
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
if (src[0] == 0) if (p[0] == 0)
_cogl_unpremult_alpha_0 (src, dst); _cogl_unpremult_alpha_0 (p);
else else
_cogl_unpremult_alpha_first (src, dst); _cogl_unpremult_alpha_first (p);
src += bpp; p += 4;
dst += bpp;
} }
} }
else else
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
if (src[0] == 0) if (p[3] == 0)
_cogl_unpremult_alpha_0 (src, dst); _cogl_unpremult_alpha_0 (p);
else else
_cogl_unpremult_alpha_last (src, dst); _cogl_unpremult_alpha_last (p);
src += bpp; p += 4;
dst += bpp;
} }
} }
} }
bmp->format &= ~COGL_PREMULT_BIT;
return TRUE; return TRUE;
} }
gboolean gboolean
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, _cogl_bitmap_fallback_premult (CoglBitmap *bmp)
CoglBitmap *dst_bmp)
{ {
guchar *src; guchar *p;
guchar *dst;
gint bpp;
gint x,y; gint x,y;
/* Make sure format supported for un-premultiplication */ /* Make sure format supported for un-premultiplication */
if (!_cogl_bitmap_fallback_can_premult (bmp->format)) if (!_cogl_bitmap_fallback_can_premult (bmp->format))
return FALSE; return FALSE;
bpp = _cogl_get_format_bpp (bmp->format);
/* Initialize destination bitmap */
*dst_bmp = *bmp;
dst_bmp->format |= COGL_PREMULT_BIT;
/* Allocate a new buffer to hold converted data */
dst_bmp->data = g_malloc (sizeof(guchar)
* dst_bmp->height
* dst_bmp->rowstride);
for (y = 0; y < bmp->height; y++) for (y = 0; y < bmp->height; y++)
{ {
src = (guchar*)bmp->data + y * bmp->rowstride; p = (guchar*) bmp->data + y * bmp->rowstride;
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
if (bmp->format & COGL_AFIRST_BIT) if (bmp->format & COGL_AFIRST_BIT)
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
_cogl_premult_alpha_first (src, dst); _cogl_premult_alpha_first (p);
src += bpp; p += 4;
dst += bpp; }
} }
}
else else
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
_cogl_premult_alpha_last (src, dst); _cogl_premult_alpha_last (p);
src += bpp; p += 4;
dst += bpp; }
} }
}
} }
bmp->format |= COGL_PREMULT_BIT;
return TRUE; return TRUE;
} }

View File

@ -64,15 +64,13 @@ _cogl_bitmap_convert (const CoglBitmap *bmp,
} }
gboolean gboolean
_cogl_bitmap_unpremult (const CoglBitmap *bmp, _cogl_bitmap_unpremult (CoglBitmap *dst_bmp)
CoglBitmap *dst_bmp)
{ {
return FALSE; return FALSE;
} }
gboolean gboolean
_cogl_bitmap_premult (const CoglBitmap *bmp, _cogl_bitmap_premult (CoglBitmap *dst_bmp)
CoglBitmap *dst_bmp)
{ {
return FALSE; return FALSE;
} }

View File

@ -68,20 +68,16 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
CoglPixelFormat dst_format); CoglPixelFormat dst_format);
gboolean gboolean
_cogl_bitmap_unpremult (const CoglBitmap *bmp, _cogl_bitmap_unpremult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, _cogl_bitmap_fallback_unpremult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_premult (const CoglBitmap *bmp, _cogl_bitmap_premult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, _cogl_bitmap_fallback_premult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_from_file (CoglBitmap *bmp, _cogl_bitmap_from_file (CoglBitmap *bmp,
@ -93,9 +89,13 @@ _cogl_bitmap_fallback_from_file (CoglBitmap *bmp,
const gchar *filename); const gchar *filename);
gboolean gboolean
_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, _cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
CoglBitmap *dst_bmp, CoglPixelFormat dst_format);
CoglPixelFormat dst_format);
gboolean
_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp,
CoglPixelFormat dst_format);
void void
_cogl_bitmap_copy_subregion (CoglBitmap *src, _cogl_bitmap_copy_subregion (CoglBitmap *src,

View File

@ -61,84 +61,58 @@ _cogl_get_format_bpp (CoglPixelFormat format)
} }
gboolean gboolean
_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, _cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
CoglBitmap *dst_bmp, CoglPixelFormat dst_format)
CoglPixelFormat dst_format)
{ {
CoglBitmap tmp_bmp = *bmp; /* Do we need to unpremultiply? */
CoglBitmap new_bmp = *bmp; if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
gboolean new_bmp_owner = FALSE; (dst_format & COGL_PREMULT_BIT) == 0)
/* Try unpremultiplying using imaging library */
return (_cogl_bitmap_unpremult (bmp)
/* ... or try fallback */
|| _cogl_bitmap_fallback_unpremult (bmp));
/* Do we need to premultiply? */
if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
(dst_format & COGL_PREMULT_BIT) > 0)
/* Try premultiplying using imaging library */
return (_cogl_bitmap_premult (bmp)
/* ... or try fallback */
|| _cogl_bitmap_fallback_premult (bmp));
return TRUE;
}
gboolean
_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp,
CoglPixelFormat dst_format)
{
/* Is base format different (not considering premult status)? */ /* Is base format different (not considering premult status)? */
if ((bmp->format & COGL_UNPREMULT_MASK) != if ((bmp->format & COGL_UNPREMULT_MASK) !=
(dst_format & COGL_UNPREMULT_MASK)) (dst_format & COGL_UNPREMULT_MASK))
{ {
/* Try converting using imaging library */ /* Try converting using imaging library */
if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format)) if (!_cogl_bitmap_convert (bmp, dst_bmp, dst_format))
{ {
/* ... or try fallback */ /* ... or try fallback */
if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format)) if (!_cogl_bitmap_fallback_convert (bmp, dst_bmp, dst_format))
return FALSE; return FALSE;
} }
/* Update bitmap with new data */
new_bmp = tmp_bmp;
new_bmp_owner = TRUE;
} }
else
/* Do we need to unpremultiply */
if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
(dst_format & COGL_PREMULT_BIT) == 0)
{ {
/* Try unpremultiplying using imaging library */ /* Copy the bitmap so that we can premultiply in-place */
if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp)) *dst_bmp = *bmp;
{ dst_bmp->data = g_memdup (bmp->data, bmp->rowstride * bmp->height);
/* ... or try fallback */
if (!_cogl_bitmap_fallback_unpremult (&new_bmp, &tmp_bmp))
{
if (new_bmp_owner)
g_free (new_bmp.data);
return FALSE;
}
}
/* Update bitmap with new data */
if (new_bmp_owner)
g_free (new_bmp.data);
new_bmp = tmp_bmp;
new_bmp_owner = TRUE;
} }
/* Do we need to premultiply */ if (!_cogl_bitmap_convert_premult_status (dst_bmp, dst_format))
if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
(dst_format & COGL_PREMULT_BIT) > 0)
{ {
/* Try premultiplying using imaging library */ g_free (dst_bmp->data);
if (!_cogl_bitmap_premult (&new_bmp, &tmp_bmp)) return FALSE;
{
/* ... or try fallback */
if (!_cogl_bitmap_fallback_premult (&new_bmp, &tmp_bmp))
{
if (new_bmp_owner)
g_free (new_bmp.data);
return FALSE;
}
}
/* Update bitmap with new data */
if (new_bmp_owner)
g_free (new_bmp.data);
new_bmp = tmp_bmp;
new_bmp_owner = TRUE;
} }
/* Output new bitmap info */
*dst_bmp = new_bmp;
return TRUE; return TRUE;
} }

View File

@ -147,6 +147,9 @@ cogl_create_context (void)
cogl_enable (enable_flags); cogl_enable (enable_flags);
_cogl_flush_face_winding (); _cogl_flush_face_winding ();
_context->atlas = NULL;
_context->atlas_texture = COGL_INVALID_HANDLE;
return TRUE; return TRUE;
} }
@ -188,6 +191,11 @@ _cogl_destroy_context ()
if (_context->default_material) if (_context->default_material)
cogl_handle_unref (_context->default_material); cogl_handle_unref (_context->default_material);
if (_context->atlas)
_cogl_atlas_free (_context->atlas);
if (_context->atlas_texture)
cogl_handle_unref (_context->atlas_texture);
g_free (_context); g_free (_context);
} }

View File

@ -30,6 +30,7 @@
#include "cogl-clip-stack.h" #include "cogl-clip-stack.h"
#include "cogl-matrix-stack.h" #include "cogl-matrix-stack.h"
#include "cogl-material-private.h" #include "cogl-material-private.h"
#include "cogl-atlas.h"
typedef struct typedef struct
{ {
@ -111,6 +112,9 @@ typedef struct
CoglHandle texture_download_material; CoglHandle texture_download_material;
CoglAtlas *atlas;
CoglHandle atlas_texture;
/* This debugging variable is used to pick a colour for visually /* This debugging variable is used to pick a colour for visually
displaying the quad batches. It needs to be global so that it can displaying the quad batches. It needs to be global so that it can
be reset by cogl_clear. It needs to be reset to increase the be reset by cogl_clear. It needs to be reset to increase the

View File

@ -47,7 +47,10 @@ static const GDebugKey cogl_debug_keys[] = {
{ "batching", COGL_DEBUG_BATCHING }, { "batching", COGL_DEBUG_BATCHING },
{ "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM },
{ "matrices", COGL_DEBUG_MATRICES }, { "matrices", COGL_DEBUG_MATRICES },
{ "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS } { "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS },
{ "atlas", COGL_DEBUG_ATLAS },
{ "dump-atlas-image", COGL_DEBUG_DUMP_ATLAS_IMAGE },
{ "disable-atlas", COGL_DEBUG_DISABLE_ATLAS }
}; };
static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys);

View File

@ -45,7 +45,10 @@ typedef enum {
COGL_DEBUG_BATCHING = 1 << 13, COGL_DEBUG_BATCHING = 1 << 13,
COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 14, COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 14,
COGL_DEBUG_MATRICES = 1 << 15, COGL_DEBUG_MATRICES = 1 << 15,
COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16 COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16,
COGL_DEBUG_ATLAS = 1 << 17,
COGL_DEBUG_DUMP_ATLAS_IMAGE = 1 << 18,
COGL_DEBUG_DISABLE_ATLAS = 1 << 19
} CoglDebugFlags; } CoglDebugFlags;
#ifdef COGL_ENABLE_DEBUG #ifdef COGL_ENABLE_DEBUG

View File

@ -51,7 +51,7 @@ _cogl_journal_log_quad (float x_1,
int n_layers, int n_layers,
guint32 fallback_layers, guint32 fallback_layers,
GLuint layer0_override_texture, GLuint layer0_override_texture,
float *tex_coords, const float *tex_coords,
unsigned int tex_coords_len); unsigned int tex_coords_len);
#endif /* __COGL_JOURNAL_PRIVATE_H */ #endif /* __COGL_JOURNAL_PRIVATE_H */

View File

@ -665,7 +665,7 @@ _cogl_journal_log_quad (float x_1,
int n_layers, int n_layers,
guint32 fallback_layers, guint32 fallback_layers,
GLuint layer0_override_texture, GLuint layer0_override_texture,
float *tex_coords, const float *tex_coords,
unsigned int tex_coords_len) unsigned int tex_coords_len)
{ {
size_t stride; size_t stride;

View File

@ -204,6 +204,12 @@ typedef enum _CoglMaterialLayerFlags
*/ */
gulong _cogl_material_layer_get_flags (CoglHandle layer_handle); gulong _cogl_material_layer_get_flags (CoglHandle layer_handle);
/*
* Ensures the mipmaps are available for the texture in the layer if
* the filter settings would require it
*/
void _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handler);
/* /*
* CoglMaterialFlushFlag: * CoglMaterialFlushFlag:
* @COGL_MATERIAL_FLUSH_FALLBACK_MASK: The fallback_layers member is set to * @COGL_MATERIAL_FLUSH_FALLBACK_MASK: The fallback_layers member is set to

View File

@ -1320,6 +1320,19 @@ _cogl_material_layer_flush_gl_sampler_state (CoglMaterialLayer *layer,
_cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE); _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE);
} }
void
_cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle)
{
CoglMaterialLayer *layer;
layer = _cogl_material_layer_pointer_from_handle (layer_handle);
if (layer->texture &&
(is_mipmap_filter (layer->min_filter) ||
is_mipmap_filter (layer->mag_filter)))
_cogl_texture_ensure_mipmaps (layer->texture);
}
/* /*
* _cogl_material_flush_layers_gl_state: * _cogl_material_flush_layers_gl_state:
* @fallback_mask: is a bitmask of the material layers that need to be * @fallback_mask: is a bitmask of the material layers that need to be
@ -1391,6 +1404,8 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
#endif #endif
CoglTextureUnit *unit; CoglTextureUnit *unit;
_cogl_material_layer_ensure_mipmaps (layer_handle);
new_gl_layer_info.layer0_overridden = new_gl_layer_info.layer0_overridden =
layer0_override_texture ? TRUE : FALSE; layer0_override_texture ? TRUE : FALSE;
new_gl_layer_info.fallback = new_gl_layer_info.fallback =
@ -1400,7 +1415,13 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
tex_handle = layer->texture; tex_handle = layer->texture;
if (tex_handle != COGL_INVALID_HANDLE) if (tex_handle != COGL_INVALID_HANDLE)
cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target); {
_cogl_texture_set_filters (tex_handle,
layer->min_filter,
layer->mag_filter);
cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target);
}
else else
{ {
new_gl_layer_info.fallback = TRUE; new_gl_layer_info.fallback = TRUE;
@ -1431,13 +1452,6 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
GE (glActiveTexture (GL_TEXTURE0 + i)); GE (glActiveTexture (GL_TEXTURE0 + i));
unit = _cogl_get_texture_unit (i); unit = _cogl_get_texture_unit (i);
_cogl_texture_set_filters (layer->texture,
layer->min_filter,
layer->mag_filter);
if (is_mipmap_filter (layer->min_filter)
|| is_mipmap_filter (layer->mag_filter))
_cogl_texture_ensure_mipmaps (layer->texture);
/* FIXME: We could be more clever here and only bind the texture /* FIXME: We could be more clever here and only bind the texture
if it is different from gl_layer_info->gl_texture to avoid if it is different from gl_layer_info->gl_texture to avoid
redundant GL calls. However a few other places in Cogl and redundant GL calls. However a few other places in Cogl and
@ -1692,13 +1706,36 @@ _cogl_material_flush_gl_state (CoglHandle handle,
0, sizeof (CoglMaterialFlushOptions)); 0, sizeof (CoglMaterialFlushOptions));
} }
static gboolean
_cogl_material_texture_equal (CoglHandle texture0, CoglHandle texture1)
{
GLenum gl_handle0, gl_handle1, gl_target0, gl_target1;
/* If the texture handles are the same then the textures are
definitely equal */
if (texture0 == texture1)
return TRUE;
/* If neither texture is sliced then they could still be the same if
the are referring to the same GL texture */
if (cogl_texture_is_sliced (texture0) ||
cogl_texture_is_sliced (texture1))
return FALSE;
cogl_texture_get_gl_texture (texture0, &gl_handle0, &gl_target0);
cogl_texture_get_gl_texture (texture1, &gl_handle1, &gl_target1);
return gl_handle0 == gl_handle1 && gl_target0 == gl_target1;
}
static gboolean static gboolean
_cogl_material_layer_equal (CoglMaterialLayer *material0_layer, _cogl_material_layer_equal (CoglMaterialLayer *material0_layer,
CoglHandle material0_layer_texture, CoglHandle material0_layer_texture,
CoglMaterialLayer *material1_layer, CoglMaterialLayer *material1_layer,
CoglHandle material1_layer_texture) CoglHandle material1_layer_texture)
{ {
if (material0_layer_texture != material1_layer_texture) if (!_cogl_material_texture_equal (material0_layer_texture,
material1_layer_texture))
return FALSE; return FALSE;
if ((material0_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) != if ((material0_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) !=

View File

@ -71,8 +71,8 @@ static void
log_quad_sub_textures_cb (CoglHandle texture_handle, log_quad_sub_textures_cb (CoglHandle texture_handle,
GLuint gl_handle, GLuint gl_handle,
GLenum gl_target, GLenum gl_target,
float *subtexture_coords, const float *subtexture_coords,
float *virtual_coords, const float *virtual_coords,
void *user_data) void *user_data)
{ {
TextureSlicedQuadState *state = user_data; TextureSlicedQuadState *state = user_data;
@ -249,6 +249,9 @@ _cogl_multitexture_quad_single_primitive (float x_1,
const float *in_tex_coords; const float *in_tex_coords;
float *out_tex_coords; float *out_tex_coords;
float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
gboolean need_repeat = FALSE;
gint coord_num;
GLenum wrap_mode;
tex_handle = cogl_material_layer_get_texture (layer); tex_handle = cogl_material_layer_get_texture (layer);
@ -257,27 +260,41 @@ _cogl_multitexture_quad_single_primitive (float x_1,
if (tex_handle == COGL_INVALID_HANDLE) if (tex_handle == COGL_INVALID_HANDLE)
continue; continue;
in_tex_coords = &user_tex_coords[i * 4]; /* If the user didn't supply texture coordinates for this layer
then use the default coords */
if (i >= user_tex_coords_len / 4)
in_tex_coords = default_tex_coords;
else
in_tex_coords = &user_tex_coords[i * 4];
out_tex_coords = &final_tex_coords[i * 4]; out_tex_coords = &final_tex_coords[i * 4];
memcpy (out_tex_coords, in_tex_coords, sizeof (GLfloat) * 4);
/* Convert the texture coordinates to GL. We also work out
whether any of the texture coordinates are outside the range
[0.0,1.0]. We need to do this after calling
transform_coords_to_gl in case the texture backend is munging
the coordinates (such as in the sub texture backend). This
should be safe to call because we know that the texture only
has one slice. */
if (!_cogl_texture_transform_quad_coords_to_gl (tex_handle,
out_tex_coords))
/* If the backend can't support these coordinates then bail out */
return FALSE;
for (coord_num = 0; coord_num < 4; coord_num++)
if (out_tex_coords[coord_num] < 0.0f ||
out_tex_coords[coord_num] > 1.0f)
need_repeat = TRUE;
/* If the texture has waste or we are using GL_TEXTURE_RECT we /* If the texture has waste or we are using GL_TEXTURE_RECT we
* can't handle texture repeating so we check that the texture * can't handle texture repeating so we can't use the layer if
* coords lie in the range [0,1]. * repeating is required.
*
* NB: We already know that the texture isn't sliced so we can assume
* that the default coords (0,0) and (1,1) would only reference a single
* GL texture.
* *
* NB: We already know that no texture matrix is being used if the * NB: We already know that no texture matrix is being used if the
* texture doesn't support hardware repeat. * texture doesn't support hardware repeat.
*/ */
if (!_cogl_texture_can_hardware_repeat (tex_handle) if (!_cogl_texture_can_hardware_repeat (tex_handle) && need_repeat)
&& i < user_tex_coords_len / 4
&& (in_tex_coords[0] < 0 || in_tex_coords[0] > 1.0
|| in_tex_coords[1] < 0 || in_tex_coords[1] > 1.0
|| in_tex_coords[2] < 0 || in_tex_coords[2] > 1.0
|| in_tex_coords[3] < 0 || in_tex_coords[3] > 1.0))
{ {
if (i == 0) if (i == 0)
{ {
@ -315,45 +332,15 @@ _cogl_multitexture_quad_single_primitive (float x_1,
} }
} }
/* If we're not repeating then we want to clamp the coords
/* to the edge otherwise it can pull in edge pixels from the
* Setup the texture unit... wrong side when scaled */
*/ if (need_repeat)
wrap_mode = GL_REPEAT;
/* NB: The user might not have supplied texture coordinates for all
* layers... */
if (i < (user_tex_coords_len / 4))
{
GLenum wrap_mode;
/* If the texture coords are all in the range [0,1] then we want to
clamp the coords to the edge otherwise it can pull in edge pixels
from the wrong side when scaled */
if (in_tex_coords[0] >= 0 && in_tex_coords[0] <= 1.0
&& in_tex_coords[1] >= 0 && in_tex_coords[1] <= 1.0
&& in_tex_coords[2] >= 0 && in_tex_coords[2] <= 1.0
&& in_tex_coords[3] >= 0 && in_tex_coords[3] <= 1.0)
wrap_mode = GL_CLAMP_TO_EDGE;
else
wrap_mode = GL_REPEAT;
memcpy (out_tex_coords, in_tex_coords, sizeof (GLfloat) * 4);
_cogl_texture_set_wrap_mode_parameter (tex_handle, wrap_mode);
}
else else
{ wrap_mode = GL_CLAMP_TO_EDGE;
memcpy (out_tex_coords, default_tex_coords, sizeof (GLfloat) * 4);
_cogl_texture_set_wrap_mode_parameter (tex_handle, GL_CLAMP_TO_EDGE); _cogl_texture_set_wrap_mode_parameter (tex_handle, wrap_mode);
}
_cogl_texture_transform_coords_to_gl (tex_handle,
&out_tex_coords[0],
&out_tex_coords[1]);
_cogl_texture_transform_coords_to_gl (tex_handle,
&out_tex_coords[2],
&out_tex_coords[3]);
} }
_cogl_journal_log_quad (x_1, _cogl_journal_log_quad (x_1,
@ -414,6 +401,12 @@ _cogl_rectangles_with_multitexture_coords (
!= COGL_MATERIAL_LAYER_TYPE_TEXTURE) != COGL_MATERIAL_LAYER_TYPE_TEXTURE)
continue; continue;
/* We need to ensure the mipmaps are ready before deciding
anything else about the texture because it could become
something completely different if it needs to be migrated out
of the atlas */
_cogl_material_layer_ensure_mipmaps (layer);
tex_handle = cogl_material_layer_get_texture (layer); tex_handle = cogl_material_layer_get_texture (layer);
/* COGL_INVALID_HANDLE textures are handled by /* COGL_INVALID_HANDLE textures are handled by
@ -651,11 +644,11 @@ cogl_rectangle (float x_1,
void void
draw_polygon_sub_texture_cb (CoglHandle tex_handle, draw_polygon_sub_texture_cb (CoglHandle tex_handle,
GLuint gl_handle, GLuint gl_handle,
GLenum gl_target, GLenum gl_target,
float *subtexture_coords, const float *subtexture_coords,
float *virtual_coords, const float *virtual_coords,
void *user_data) void *user_data)
{ {
TextureSlicedPolygonState *state = user_data; TextureSlicedPolygonState *state = user_data;
GLfloat *v; GLfloat *v;
@ -880,6 +873,11 @@ cogl_polygon (const CoglTextureVertex *vertices,
if (tex_handle == COGL_INVALID_HANDLE) if (tex_handle == COGL_INVALID_HANDLE)
continue; continue;
/* Give the texture a chance to know that we're rendering
non-quad shaped primitives. If the texture is in an atlas it
will be migrated */
_cogl_texture_ensure_non_quad_rendering (tex_handle);
if (i == 0 && cogl_texture_is_sliced (tex_handle)) if (i == 0 && cogl_texture_is_sliced (tex_handle))
{ {
#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2) #if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2)

View File

@ -0,0 +1,55 @@
/*
* 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.
*/
#ifndef __COGL_SUB_TEXTURE_H
#define __COGL_SUB_TEXTURE_H
#include "cogl-handle.h"
#include "cogl-texture-private.h"
#define COGL_SUB_TEXTURE(tex) ((CoglSubTexture *) tex)
typedef struct _CoglSubTexture CoglSubTexture;
struct _CoglSubTexture
{
CoglTexture _parent;
CoglHandle full_texture;
/* The region represented by this sub-texture */
gint sub_x;
gint sub_y;
gint sub_width;
gint sub_height;
};
GQuark
_cogl_handle_sub_texture_get_type (void);
CoglHandle
_cogl_sub_texture_new (CoglHandle full_texture,
gint sub_x, gint sub_y,
gint sub_width, gint sub_height);
#endif /* __COGL_SUB_TEXTURE_H */

View File

@ -0,0 +1,539 @@
/*
* 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;
static void
_cogl_sub_texture_map_range (gfloat *t1, gfloat *t2,
gint sub_offset,
gint sub_size,
gint full_size)
{
gfloat t1_frac, t1_int, t2_frac, t2_int;
t1_frac = modff (*t1, &t1_int);
t2_frac = modff (*t2, &t2_int);
if (t1_frac < 0.0f)
{
t1_frac += 1.0f;
t1_int -= 1.0f;
}
if (t2_frac < 0.0f)
{
t2_frac += 1.0f;
t2_int -= 1.0f;
}
/* If one of the coordinates is zero we need to make sure it is
still greater than the other coordinate if it was originally so
we'll flip it to the other side */
if (*t1 < *t2)
{
if (t2_frac == 0.0f)
{
t2_frac = 1.0f;
t2_int -= 1.0f;
}
}
else
{
if (t1_frac == 0.0f)
{
t1_frac = 1.0f;
t1_int -= 1.0f;
}
}
/* Convert the fractional part leaving the integer part intact */
t1_frac = (sub_offset + t1_frac * sub_size) / full_size;
*t1 = t1_frac + t1_int;
t2_frac = (sub_offset + t2_frac * sub_size) / full_size;
*t2 = t2_frac + t2_int;
}
static void
_cogl_sub_texture_map_quad (CoglSubTexture *sub_tex,
gfloat *coords)
{
guint full_width = cogl_texture_get_width (sub_tex->full_texture);
guint full_height = cogl_texture_get_height (sub_tex->full_texture);
_cogl_sub_texture_map_range (coords + 0, coords + 2,
sub_tex->sub_x, sub_tex->sub_width,
full_width);
_cogl_sub_texture_map_range (coords + 1, coords + 3,
sub_tex->sub_y, sub_tex->sub_height,
full_height);
}
/* Maps from the texture coordinates of the full texture to the
texture coordinates of the sub texture */
static gfloat
_cogl_sub_texture_unmap_coord (gfloat t,
gint sub_offset,
gint sub_size,
gint full_size)
{
gfloat frac_part, int_part;
/* Convert the fractional part leaving the integer part in tact */
frac_part = modff (t, &int_part);
if (signbit (frac_part))
frac_part = ((1.0f + frac_part) * full_size -
sub_offset - sub_size) / sub_size;
else
frac_part = (frac_part * full_size - sub_offset) / sub_size;
return frac_part + int_part;
}
static void
_cogl_sub_texture_unmap_coords (CoglSubTexture *sub_tex,
gfloat *s,
gfloat *t)
{
guint full_width = cogl_texture_get_width (sub_tex->full_texture);
guint full_height = cogl_texture_get_height (sub_tex->full_texture);
*s = _cogl_sub_texture_unmap_coord (*s, sub_tex->sub_x, sub_tex->sub_width,
full_width);
*t = _cogl_sub_texture_unmap_coord (*t, sub_tex->sub_y, sub_tex->sub_height,
full_height);
}
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_coords (data->sub_tex,
&virtual_coords[0],
&virtual_coords[1]);
_cogl_sub_texture_unmap_coords (data->sub_tex,
&virtual_coords[2],
&virtual_coords[3]);
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_quad (data->sub_tex, mapped_coords);
_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;
_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,
gint sub_x, gint sub_y,
gint sub_width, gint sub_height)
{
CoglSubTexture *sub_tex;
CoglTexture *tex;
guint full_width, full_height;
full_width = cogl_texture_get_width (full_texture);
full_height = cogl_texture_get_height (full_texture);
/* The region must specify a non-zero subset of the full texture */
g_return_val_if_fail (sub_x >= 0 && sub_y >= 0, COGL_INVALID_HANDLE);
g_return_val_if_fail (sub_width > 0 && sub_height > 0, COGL_INVALID_HANDLE);
g_return_val_if_fail (sub_x + sub_width <= full_width, COGL_INVALID_HANDLE);
g_return_val_if_fail (sub_y + sub_height <= full_height, COGL_INVALID_HANDLE);
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->sub_x = sub_x;
sub_tex->sub_y = sub_y;
sub_tex->sub_width = sub_width;
sub_tex->sub_height = sub_height;
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 subtexture actually represents all of the
of the full texture */
return (sub_tex->sub_width ==
cogl_texture_get_width (sub_tex->full_texture) &&
sub_tex->sub_height ==
cogl_texture_get_height (sub_tex->full_texture) &&
_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);
/* This won't work if the sub texture is not the size of the full
texture and the coordinates are outside the range [0,1] */
*s = ((*s * sub_tex->sub_width + sub_tex->sub_x) /
cogl_texture_get_width (sub_tex->full_texture));
*t = ((*t * sub_tex->sub_height + sub_tex->sub_y) /
cogl_texture_get_height (sub_tex->full_texture));
return _cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t);
}
static gboolean
_cogl_sub_texture_transform_quad_coords_to_gl (CoglTexture *tex,
float *coords)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
int i;
/* We can't support repeating with this method. In this case
cogl-primitives will resort to manual repeating */
for (i = 0; i < 4; i++)
if (coords[i] < 0.0f || coords[i] > 1.0f)
return FALSE;
_cogl_sub_texture_map_quad (sub_tex, coords);
_cogl_texture_transform_quad_coords_to_gl (sub_tex->full_texture, coords);
return TRUE;
}
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_ensure_non_quad_rendering (CoglTexture *tex)
{
}
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);
return cogl_texture_set_region (sub_tex->full_texture,
src_x, src_y,
dst_x + sub_tex->sub_x,
dst_y + sub_tex->sub_y,
dst_width, dst_height,
width, height,
format,
rowstride,
data);
}
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 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);
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 = sub_tex->sub_width * bpp;
/* Return byte size if only that requested */
byte_size = sub_tex->sub_height * 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)
_cogl_sub_texture_copy_region (data, full_data,
0, 0,
sub_tex->sub_x,
sub_tex->sub_y,
sub_tex->sub_width,
sub_tex->sub_height,
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);
return sub_tex->sub_width;
}
static gint
_cogl_sub_texture_get_height (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
return sub_tex->sub_height;
}
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_transform_quad_coords_to_gl,
_cogl_sub_texture_get_gl_texture,
_cogl_sub_texture_set_filters,
_cogl_sub_texture_ensure_mipmaps,
_cogl_sub_texture_ensure_non_quad_rendering,
_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
};

View File

@ -0,0 +1,69 @@
/*
* 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.
*/
#ifndef __COGL_TEXTURE_2D_H
#define __COGL_TEXTURE_2D_H
#include "cogl-handle.h"
#include "cogl-material-private.h"
#include "cogl-texture-private.h"
#define COGL_TEXTURE_2D(tex) ((CoglTexture2D *) tex)
typedef struct _CoglTexture2D CoglTexture2D;
struct _CoglTexture2D
{
CoglTexture _parent;
/* The internal format of the GL texture represented as a
CoglPixelFormat */
CoglPixelFormat format;
/* The internal format of the GL texture represented as a GL enum */
GLenum gl_format;
/* The texture object number */
GLuint gl_texture;
gint width;
gint height;
GLenum min_filter;
GLenum mag_filter;
GLint wrap_mode;
gboolean auto_mipmap;
gboolean mipmaps_dirty;
};
GQuark
_cogl_handle_texture_2d_get_type (void);
CoglHandle
_cogl_texture_2d_new_with_size (unsigned int width,
unsigned int height,
CoglTextureFlags flags,
CoglPixelFormat internal_format);
CoglHandle
_cogl_texture_2d_new_from_bitmap (CoglHandle bmp_handle,
CoglTextureFlags flags,
CoglPixelFormat internal_format);
#endif /* __COGL_TEXTURE_2D_H */

View File

@ -49,15 +49,30 @@ struct _CoglTexturePixel
struct _CoglTexture2DSliced struct _CoglTexture2DSliced
{ {
CoglTexture _parent; CoglTexture _parent;
GArray *slice_x_spans; GArray *slice_x_spans;
GArray *slice_y_spans; GArray *slice_y_spans;
GArray *slice_gl_handles; GArray *slice_gl_handles;
gint max_waste; gint max_waste;
/* The internal format of the GL texture represented as a
CoglPixelFormat */
CoglPixelFormat format;
/* The internal format of the GL texture represented as a GL enum */
GLenum gl_format;
GLenum gl_target;
gint width;
gint height;
GLenum min_filter;
GLenum mag_filter;
gboolean is_foreign;
GLint wrap_mode;
gboolean auto_mipmap;
gboolean mipmaps_dirty;
/* This holds a copy of the first pixel in each slice. It is only /* This holds a copy of the first pixel in each slice. It is only
used to force an automatic update of the mipmaps when used to force an automatic update of the mipmaps when
glGenerateMipmap is not available. */ glGenerateMipmap is not available. */
CoglTexturePixel *first_pixels; CoglTexturePixel *first_pixels;
}; };
GQuark GQuark
@ -69,12 +84,6 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width,
CoglTextureFlags flags, CoglTextureFlags flags,
CoglPixelFormat internal_format); CoglPixelFormat internal_format);
CoglHandle
_cogl_texture_2d_sliced_new_from_file (const gchar *filename,
CoglTextureFlags flags,
CoglPixelFormat internal_format,
GError **error);
CoglHandle CoglHandle
_cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle,
GLenum gl_target, GLenum gl_target,
@ -84,16 +93,6 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle,
GLuint y_pot_waste, GLuint y_pot_waste,
CoglPixelFormat format); CoglPixelFormat format);
CoglHandle
_cogl_texture_2d_sliced_new_from_data (unsigned int width,
unsigned int height,
CoglTextureFlags flags,
CoglPixelFormat format,
CoglPixelFormat internal_format,
unsigned int rowstride,
const guint8 *data);
CoglHandle CoglHandle
_cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle,
CoglTextureFlags flags, CoglTextureFlags flags,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,630 @@
/*
* 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-texture-2d-private.h"
#include "cogl-texture-driver.h"
#include "cogl-context.h"
#include "cogl-handle.h"
#include <string.h>
#include <math.h>
static void _cogl_texture_2d_free (CoglTexture2D *tex_2d);
COGL_HANDLE_DEFINE (Texture2D, texture_2d);
static const CoglTextureVtable cogl_texture_2d_vtable;
typedef struct _CoglTexture2DManualRepeatData
{
CoglTexture2D *tex_2d;
CoglTextureSliceCallback callback;
void *user_data;
} CoglTexture2DManualRepeatData;
static void
_cogl_texture_2d_wrap_coords (float t_1, float t_2,
float *out_t_1, float *out_t_2)
{
float int_part;
/* Wrap t_1 and t_2 to the range [0,1] */
modff (t_1 < t_2 ? t_1 : t_2, &int_part);
t_1 -= int_part;
t_2 -= int_part;
if (signbit (int_part))
{
*out_t_1 = 1.0f + t_1;
*out_t_2 = 1.0f + t_2;
}
else
{
*out_t_1 = t_1;
*out_t_2 = t_2;
}
}
static void
_cogl_texture_2d_manual_repeat_cb (const float *coords,
void *user_data)
{
CoglTexture2DManualRepeatData *data = user_data;
float slice_coords[4];
_cogl_texture_2d_wrap_coords (coords[0], coords[2],
slice_coords + 0, slice_coords + 2);
_cogl_texture_2d_wrap_coords (coords[1], coords[3],
slice_coords + 1, slice_coords + 3);
data->callback (COGL_TEXTURE (data->tex_2d),
data->tex_2d->gl_texture,
GL_TEXTURE_2D,
slice_coords,
coords,
data->user_data);
}
static void
_cogl_texture_2d_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)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
CoglTexture2DManualRepeatData data;
data.tex_2d = tex_2d;
data.callback = callback;
data.user_data = user_data;
/* We need to implement manual repeating because if Cogl is calling
this function then it will set the wrap mode to GL_CLAMP_TO_EDGE
and hardware repeating can't be done */
_cogl_texture_iterate_manual_repeats (_cogl_texture_2d_manual_repeat_cb,
virtual_tx_1, virtual_ty_1,
virtual_tx_2, virtual_ty_2,
&data);
}
static void
_cogl_texture_2d_set_wrap_mode_parameter (CoglTexture *tex,
GLenum wrap_mode)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
/* Only set the wrap mode if it's different from the current
value to avoid too many GL calls */
if (tex_2d->wrap_mode != wrap_mode)
{
/* Any queued texture rectangles may be depending on the
* previous wrap mode... */
_cogl_journal_flush ();
GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode) );
tex_2d->wrap_mode = wrap_mode;
}
}
static void
_cogl_texture_2d_free (CoglTexture2D *tex_2d)
{
GE( glDeleteTextures (1, &tex_2d->gl_texture) );
g_free (tex_2d);
}
static gboolean
_cogl_texture_2d_is_pot (unsigned int num)
{
gboolean have_bit = FALSE;
/* Make sure there is only one bit set */
while (num)
{
if (num & 1)
{
if (have_bit)
return FALSE;
have_bit = TRUE;
}
num >>= 1;
}
return TRUE;
}
static gboolean
_cogl_texture_2d_can_create (unsigned int width,
unsigned int height,
CoglPixelFormat internal_format)
{
GLenum gl_intformat;
GLenum gl_type;
/* If the driver doesn't support glGenerateMipmap then we need to
store a copy of the first pixels to cause an update. Instead of
duplicating the code here we'll just make it fallback to
CoglTexture2DSliced */
if (!cogl_features_available (COGL_FEATURE_OFFSCREEN))
return FALSE;
/* If NPOT textures aren't supported then the size must be a power
of two */
if (!cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) &&
(!_cogl_texture_2d_is_pot (width) ||
!_cogl_texture_2d_is_pot (height)))
return FALSE;
_cogl_pixel_format_to_gl (internal_format,
&gl_intformat,
NULL,
&gl_type);
/* Check that the driver can create a texture with that size */
if (!_cogl_texture_driver_size_supported (GL_TEXTURE_2D,
gl_intformat,
gl_type,
width,
height))
return FALSE;
return TRUE;
}
static CoglTexture2D *
_cogl_texture_2d_create_base (unsigned int width,
unsigned int height,
CoglTextureFlags flags,
CoglPixelFormat internal_format)
{
CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1);
CoglTexture *tex = COGL_TEXTURE (tex_2d);
tex->vtable = &cogl_texture_2d_vtable;
tex_2d->width = width;
tex_2d->height = height;
tex_2d->mipmaps_dirty = TRUE;
tex_2d->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0;
/* Unknown filter */
tex_2d->min_filter = GL_FALSE;
tex_2d->mag_filter = GL_FALSE;
/* Wrap mode not yet set */
tex_2d->wrap_mode = GL_FALSE;
tex_2d->format = internal_format;
return tex_2d;
}
CoglHandle
_cogl_texture_2d_new_with_size (unsigned int width,
unsigned int height,
CoglTextureFlags flags,
CoglPixelFormat internal_format)
{
CoglTexture2D *tex_2d;
GLenum gl_intformat;
GLenum gl_format;
GLenum gl_type;
/* Since no data, we need some internal format */
if (internal_format == COGL_PIXEL_FORMAT_ANY)
internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
if (!_cogl_texture_2d_can_create (width, height, internal_format))
return COGL_INVALID_HANDLE;
internal_format = _cogl_pixel_format_to_gl (internal_format,
&gl_intformat,
&gl_format,
&gl_type);
tex_2d = _cogl_texture_2d_create_base (width, height, flags, internal_format);
GE( glGenTextures (1, &tex_2d->gl_texture) );
GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
GE( glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat,
width, height, 0, gl_format, gl_type, NULL) );
return _cogl_texture_2d_handle_new (tex_2d);
}
CoglHandle
_cogl_texture_2d_new_from_bitmap (CoglHandle bmp_handle,
CoglTextureFlags flags,
CoglPixelFormat internal_format)
{
CoglTexture2D *tex_2d;
CoglBitmap *bmp = (CoglBitmap *)bmp_handle;
CoglBitmap dst_bmp;
gboolean dst_bmp_owner;
GLenum gl_intformat;
GLenum gl_format;
GLenum gl_type;
g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE);
if (!_cogl_texture_prepare_for_upload (bmp,
internal_format,
&internal_format,
&dst_bmp,
&dst_bmp_owner,
&gl_intformat,
&gl_format,
&gl_type))
return COGL_INVALID_HANDLE;
tex_2d = _cogl_texture_2d_create_base (bmp->width,
bmp->height,
flags,
internal_format);
GE( glGenTextures (1, &tex_2d->gl_texture) );
_cogl_texture_driver_upload_to_gl (GL_TEXTURE_2D,
tex_2d->gl_texture,
&dst_bmp,
gl_intformat,
gl_format,
gl_type);
tex_2d->gl_format = gl_intformat;
if (dst_bmp_owner)
g_free (dst_bmp.data);
return _cogl_texture_2d_handle_new (tex_2d);
}
static gint
_cogl_texture_2d_get_max_waste (CoglTexture *tex)
{
return -1;
}
static gboolean
_cogl_texture_2d_is_sliced (CoglTexture *tex)
{
return FALSE;
}
static gboolean
_cogl_texture_2d_can_hardware_repeat (CoglTexture *tex)
{
return TRUE;
}
static void
_cogl_texture_2d_transform_coords_to_gl (CoglTexture *tex,
float *s,
float *t)
{
/* The texture coordinates map directly so we don't need to do
anything */
}
static gboolean
_cogl_texture_2d_transform_quad_coords_to_gl (CoglTexture *tex,
float *coords)
{
/* The texture coordinates map directly so we don't need to do
anything */
return TRUE;
}
static gboolean
_cogl_texture_2d_get_gl_texture (CoglTexture *tex,
GLuint *out_gl_handle,
GLenum *out_gl_target)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
if (out_gl_handle)
*out_gl_handle = tex_2d->gl_texture;
if (out_gl_target)
*out_gl_target = GL_TEXTURE_2D;
return TRUE;
}
static void
_cogl_texture_2d_set_filters (CoglTexture *tex,
GLenum min_filter,
GLenum mag_filter)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
if (min_filter == tex_2d->min_filter
&& mag_filter == tex_2d->mag_filter)
return;
/* Store new values */
tex_2d->min_filter = min_filter;
tex_2d->mag_filter = mag_filter;
/* Apply new filters to the texture */
GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) );
}
static void
_cogl_texture_2d_ensure_mipmaps (CoglTexture *tex)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* Only update if the mipmaps are dirty */
if (!tex_2d->auto_mipmap || !tex_2d->mipmaps_dirty)
return;
GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
/* glGenerateMipmap is defined in the FBO extension. We only allow
CoglTexture2D instances to be created if this feature is
available so we don't need to check for the extension */
_cogl_texture_driver_gl_generate_mipmaps (GL_TEXTURE_2D);
tex_2d->mipmaps_dirty = FALSE;
}
static void
_cogl_texture_2d_ensure_non_quad_rendering (CoglTexture *tex)
{
/* Nothing needs to be done */
}
static gboolean
_cogl_texture_2d_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)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
gint bpp;
CoglBitmap source_bmp;
CoglBitmap tmp_bmp;
gboolean tmp_bmp_owner = FALSE;
GLenum closest_gl_format;
GLenum closest_gl_type;
/* 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;
/* Init source bitmap */
source_bmp.width = width;
source_bmp.height = height;
source_bmp.format = format;
source_bmp.data = (guchar*) data;
/* Rowstride from width if none specified */
bpp = _cogl_get_format_bpp (format);
source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride;
/* Prepare the bitmap so that it will do the premultiplication
conversion */
_cogl_texture_prepare_for_upload (&source_bmp,
tex_2d->format,
NULL,
&tmp_bmp,
&tmp_bmp_owner,
NULL,
&closest_gl_format,
&closest_gl_type);
/* Send data to GL */
_cogl_texture_driver_upload_subregion_to_gl (GL_TEXTURE_2D,
tex_2d->gl_texture,
src_x, src_y,
dst_x, dst_y,
dst_width, dst_height,
&tmp_bmp,
closest_gl_format,
closest_gl_type);
/* Free data if owner */
if (tmp_bmp_owner)
g_free (tmp_bmp.data);
return TRUE;
}
static int
_cogl_texture_2d_get_data (CoglTexture *tex,
CoglPixelFormat format,
unsigned int rowstride,
guint8 *data)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
gint bpp;
gint byte_size;
CoglPixelFormat closest_format;
gint closest_bpp;
GLenum closest_gl_format;
GLenum closest_gl_type;
CoglBitmap target_bmp;
CoglBitmap new_bmp;
gboolean success;
guchar *src;
guchar *dst;
gint y;
/* Default to internal format if none specified */
if (format == COGL_PIXEL_FORMAT_ANY)
format = tex_2d->format;
/* Rowstride from texture width if none specified */
bpp = _cogl_get_format_bpp (format);
if (rowstride == 0) rowstride = tex_2d->width * bpp;
/* Return byte size if only that requested */
byte_size = tex_2d->height * rowstride;
if (data == NULL) return byte_size;
closest_format =
_cogl_texture_driver_find_best_gl_get_data_format (format,
&closest_gl_format,
&closest_gl_type);
closest_bpp = _cogl_get_format_bpp (closest_format);
target_bmp.width = tex_2d->width;
target_bmp.height = tex_2d->height;
/* Is the requested format supported? */
if (closest_format == format)
{
/* Target user data directly */
target_bmp.format = format;
target_bmp.rowstride = rowstride;
target_bmp.data = data;
}
else
{
/* Target intermediate buffer */
target_bmp.format = closest_format;
target_bmp.rowstride = target_bmp.width * closest_bpp;
target_bmp.data = (guchar*) g_malloc (target_bmp.height
* target_bmp.rowstride);
}
GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
if (!_cogl_texture_driver_gl_get_tex_image (GL_TEXTURE_2D,
closest_gl_format,
closest_gl_type,
target_bmp.data))
{
/* XXX: In some cases _cogl_texture_2d_download_from_gl may
* fail to read back the texture data; such as for GLES which doesn't
* support glGetTexImage, so here we fallback to drawing the texture
* and reading the pixels from the framebuffer. */
_cogl_texture_draw_and_read (tex, &target_bmp,
closest_gl_format,
closest_gl_type);
}
/* Was intermediate used? */
if (closest_format != format)
{
/* Convert to requested format */
success = _cogl_bitmap_convert_format_and_premult (&target_bmp,
&new_bmp,
format);
/* Free intermediate data and return if failed */
g_free (target_bmp.data);
if (!success) return 0;
/* Copy to user buffer */
for (y = 0; y < new_bmp.height; ++y)
{
src = new_bmp.data + y * new_bmp.rowstride;
dst = data + y * rowstride;
memcpy (dst, src, new_bmp.width);
}
/* Free converted data */
g_free (new_bmp.data);
}
return byte_size;
}
static CoglPixelFormat
_cogl_texture_2d_get_format (CoglTexture *tex)
{
return COGL_TEXTURE_2D (tex)->format;
}
static GLenum
_cogl_texture_2d_get_gl_format (CoglTexture *tex)
{
return COGL_TEXTURE_2D (tex)->gl_format;
}
static gint
_cogl_texture_2d_get_width (CoglTexture *tex)
{
return COGL_TEXTURE_2D (tex)->width;
}
static gint
_cogl_texture_2d_get_height (CoglTexture *tex)
{
return COGL_TEXTURE_2D (tex)->height;
}
static const CoglTextureVtable
cogl_texture_2d_vtable =
{
_cogl_texture_2d_set_region,
_cogl_texture_2d_get_data,
_cogl_texture_2d_foreach_sub_texture_in_region,
_cogl_texture_2d_get_max_waste,
_cogl_texture_2d_is_sliced,
_cogl_texture_2d_can_hardware_repeat,
_cogl_texture_2d_transform_coords_to_gl,
_cogl_texture_2d_transform_quad_coords_to_gl,
_cogl_texture_2d_get_gl_texture,
_cogl_texture_2d_set_filters,
_cogl_texture_2d_ensure_mipmaps,
_cogl_texture_2d_ensure_non_quad_rendering,
_cogl_texture_2d_set_wrap_mode_parameter,
_cogl_texture_2d_get_format,
_cogl_texture_2d_get_gl_format,
_cogl_texture_2d_get_width,
_cogl_texture_2d_get_height
};

View File

@ -57,7 +57,8 @@ _cogl_texture_driver_prep_gl_for_pixels_upload (int pixels_rowstride,
* XXX: sorry for the ridiculous number of arguments :-( * XXX: sorry for the ridiculous number of arguments :-(
*/ */
void void
_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, _cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target,
GLuint gl_handle,
int src_x, int src_x,
int src_y, int src_y,
int dst_x, int dst_x,
@ -66,8 +67,21 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
int height, int height,
CoglBitmap *source_bmp, CoglBitmap *source_bmp,
GLuint source_gl_format, GLuint source_gl_format,
GLuint source_gl_type, GLuint source_gl_type);
GLuint gl_handle);
/*
* Replaces the contents of the GL texture with the entire bitmap. On
* GL this just directly calls glTexImage2D, but under GLES it needs
* to copy the bitmap if the rowstride is not a multiple of a possible
* alignment value because there is no GL_UNPACK_ROW_LENGTH
*/
void
_cogl_texture_driver_upload_to_gl (GLenum gl_target,
GLuint gl_handle,
CoglBitmap *source_bmp,
GLint internal_gl_format,
GLuint source_gl_format,
GLuint source_gl_type);
/* /*
* This sets up the glPixelStore state for an download to a destination with * This sets up the glPixelStore state for an download to a destination with

View File

@ -30,16 +30,19 @@
#define COGL_TEXTURE(tex) ((CoglTexture *)(tex)) #define COGL_TEXTURE(tex) ((CoglTexture *)(tex))
typedef struct _CoglTexture CoglTexture; typedef struct _CoglTexture CoglTexture;
typedef struct _CoglTextureVtable CoglTextureVtable; typedef struct _CoglTextureVtable CoglTextureVtable;
typedef void (*CoglTextureSliceCallback) (CoglHandle handle, typedef void (*CoglTextureSliceCallback) (CoglHandle handle,
GLuint gl_handle, GLuint gl_handle,
GLenum gl_target, GLenum gl_target,
float *slice_coords, const float *slice_coords,
float *virtual_coords, const float *virtual_coords,
void *user_data); void *user_data);
typedef void (* CoglTextureManualRepeatCallback) (const float *coords,
void *user_data);
struct _CoglTextureVtable struct _CoglTextureVtable
{ {
/* Virtual functions that must be implemented for a texture /* Virtual functions that must be implemented for a texture
@ -80,6 +83,8 @@ struct _CoglTextureVtable
void (* transform_coords_to_gl) (CoglTexture *tex, void (* transform_coords_to_gl) (CoglTexture *tex,
float *s, float *s,
float *t); float *t);
gboolean (* transform_quad_coords_to_gl) (CoglTexture *tex,
float *coords);
gboolean (* get_gl_texture) (CoglTexture *tex, gboolean (* get_gl_texture) (CoglTexture *tex,
GLuint *out_gl_handle, GLuint *out_gl_handle,
@ -90,27 +95,21 @@ struct _CoglTextureVtable
GLenum mag_filter); GLenum mag_filter);
void (* ensure_mipmaps) (CoglTexture *tex); void (* ensure_mipmaps) (CoglTexture *tex);
void (* ensure_non_quad_rendering) (CoglTexture *tex);
void (* set_wrap_mode_parameter) (CoglTexture *tex, void (* set_wrap_mode_parameter) (CoglTexture *tex,
GLenum wrap_mode); GLenum wrap_mode);
CoglPixelFormat (* get_format) (CoglTexture *tex);
GLenum (* get_gl_format) (CoglTexture *tex);
gint (* get_width) (CoglTexture *tex);
gint (* get_height) (CoglTexture *tex);
}; };
struct _CoglTexture struct _CoglTexture
{ {
CoglHandleObject _parent; CoglHandleObject _parent;
const CoglTextureVtable *vtable; const CoglTextureVtable *vtable;
CoglBitmap bitmap;
gboolean bitmap_owner;
GLenum gl_target;
GLenum gl_intformat;
GLenum gl_format;
GLenum gl_type;
GLenum min_filter;
GLenum mag_filter;
gboolean is_foreign;
GLint wrap_mode;
gboolean auto_mipmap;
gboolean mipmaps_dirty;
}; };
void void
@ -129,8 +128,12 @@ void
_cogl_texture_transform_coords_to_gl (CoglHandle handle, _cogl_texture_transform_coords_to_gl (CoglHandle handle,
float *s, float *s,
float *t); float *t);
gboolean
_cogl_texture_transform_quad_coords_to_gl (CoglHandle handle,
float *coords);
GLenum GLenum
_cogl_texture_get_internal_gl_format (CoglHandle handle); _cogl_texture_get_gl_format (CoglHandle handle);
void void
_cogl_texture_set_wrap_mode_parameter (CoglHandle handle, _cogl_texture_set_wrap_mode_parameter (CoglHandle handle,
@ -144,23 +147,30 @@ _cogl_texture_set_filters (CoglHandle handle,
void void
_cogl_texture_ensure_mipmaps (CoglHandle handle); _cogl_texture_ensure_mipmaps (CoglHandle handle);
/* Functions currently only used by CoglTexture implementations or
* drivers... */
void void
_cogl_texture_free (CoglTexture *tex); _cogl_texture_ensure_non_quad_rendering (CoglHandle handle);
void /* Utility function to determine which pixel format to use when
_cogl_texture_bitmap_free (CoglTexture *tex); dst_format is COGL_PIXEL_FORMAT_ANY. If dst_format is not ANY then
it will just be returned directly */
CoglPixelFormat
_cogl_texture_determine_internal_format (CoglPixelFormat src_format,
CoglPixelFormat dst_format);
void /* Utility function to help uploading a bitmap. If the bitmap needs
_cogl_texture_bitmap_swap (CoglTexture *tex, premult conversion then it will be copied and *copied_bitmap will
CoglBitmap *new_bitmap); be set to TRUE. Otherwise dst_bmp will be set to a shallow copy of
src_bmp. The GLenums needed for uploading are returned */
gboolean gboolean
_cogl_texture_bitmap_prepare (CoglTexture *tex, _cogl_texture_prepare_for_upload (CoglBitmap *src_bmp,
CoglPixelFormat internal_format); CoglPixelFormat dst_format,
CoglPixelFormat *dst_format_out,
CoglBitmap *dst_bmp,
gboolean *copied_bitmap,
GLenum *out_glintformat,
GLenum *out_glformat,
GLenum *out_gltype);
void void
_cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride);
@ -168,8 +178,22 @@ _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride);
void void
_cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride); _cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride);
/* Utility function for implementing manual repeating. Even texture
backends that always support hardware repeating need this because
when foreach_sub_texture_in_region is invoked Cogl will set the
wrap mode to GL_CLAMP_TO_EDGE so hardware repeating can't be
done */
void
_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback,
float tx_1, float ty_1,
float tx_2, float ty_2,
void *user_data);
/* Utility function to use as a fallback for getting the data of any
texture via the framebuffer */
gboolean gboolean
_cogl_texture_draw_and_read (CoglTexture *tex, _cogl_texture_draw_and_read (CoglHandle handle,
CoglBitmap *target_bmp, CoglBitmap *target_bmp,
GLuint target_gl_format, GLuint target_gl_format,
GLuint target_gl_type); GLuint target_gl_type);

View File

@ -38,6 +38,9 @@
#include "cogl-texture-private.h" #include "cogl-texture-private.h"
#include "cogl-texture-driver.h" #include "cogl-texture-driver.h"
#include "cogl-texture-2d-sliced-private.h" #include "cogl-texture-2d-sliced-private.h"
#include "cogl-texture-2d-private.h"
#include "cogl-sub-texture-private.h"
#include "cogl-atlas-texture-private.h"
#include "cogl-material.h" #include "cogl-material.h"
#include "cogl-context.h" #include "cogl-context.h"
#include "cogl-handle.h" #include "cogl-handle.h"
@ -62,8 +65,10 @@ cogl_is_texture (CoglHandle handle)
if (handle == COGL_INVALID_HANDLE) if (handle == COGL_INVALID_HANDLE)
return FALSE; return FALSE;
return obj->klass->type == _cogl_handle_texture_2d_sliced_get_type (); return (obj->klass->type == _cogl_handle_texture_2d_get_type () ||
//|| obj->klass->type == _cogl_handle_texture_3d_get_type (); obj->klass->type == _cogl_handle_atlas_texture_get_type () ||
obj->klass->type == _cogl_handle_texture_2d_sliced_get_type () ||
obj->klass->type == _cogl_handle_sub_texture_get_type ());
} }
CoglHandle CoglHandle
@ -95,25 +100,86 @@ cogl_texture_unref (CoglHandle handle)
cogl_handle_unref (handle); cogl_handle_unref (handle);
} }
void static gboolean
_cogl_texture_bitmap_free (CoglTexture *tex) _cogl_texture_needs_premult_conversion (CoglPixelFormat src_format,
CoglPixelFormat dst_format)
{ {
if (tex->bitmap.data != NULL && tex->bitmap_owner) return ((src_format & COGL_A_BIT) &&
g_free (tex->bitmap.data); src_format != COGL_PIXEL_FORMAT_A_8 &&
(src_format & COGL_PREMULT_BIT) !=
tex->bitmap.data = NULL; (dst_format & COGL_PREMULT_BIT));
tex->bitmap_owner = FALSE;
} }
void CoglPixelFormat
_cogl_texture_bitmap_swap (CoglTexture *tex, _cogl_texture_determine_internal_format (CoglPixelFormat src_format,
CoglBitmap *new_bitmap) CoglPixelFormat dst_format)
{ {
if (tex->bitmap.data != NULL && tex->bitmap_owner) /* If the application hasn't specified a specific format then we'll
g_free (tex->bitmap.data); * pick the most appropriate. By default Cogl will use a
* premultiplied internal format. Later we will add control over
* this. */
if (dst_format == COGL_PIXEL_FORMAT_ANY)
{
if ((src_format & COGL_A_BIT) &&
src_format != COGL_PIXEL_FORMAT_A_8)
return src_format | COGL_PREMULT_BIT;
else
return src_format;
}
else
return dst_format;
}
tex->bitmap = *new_bitmap; gboolean
tex->bitmap_owner = TRUE; _cogl_texture_prepare_for_upload (CoglBitmap *src_bmp,
CoglPixelFormat dst_format,
CoglPixelFormat *dst_format_out,
CoglBitmap *dst_bmp,
gboolean *copied_bitmap,
GLenum *out_glintformat,
GLenum *out_glformat,
GLenum *out_gltype)
{
dst_format = _cogl_texture_determine_internal_format (src_bmp->format,
dst_format);
*copied_bitmap = FALSE;
*dst_bmp = *src_bmp;
/* If the source format does not have the same premult flag as the
dst format then we need to copy and convert it */
if (_cogl_texture_needs_premult_conversion (src_bmp->format,
dst_format))
{
dst_bmp->data = g_memdup (dst_bmp->data,
dst_bmp->height * dst_bmp->rowstride);
*copied_bitmap = TRUE;
if (!_cogl_bitmap_convert_premult_status (dst_bmp,
src_bmp->format ^
COGL_PREMULT_BIT))
{
g_free (dst_bmp->data);
return FALSE;
}
}
/* Use the source format from the src bitmap type and the internal
format from the dst format type so that GL can do the
conversion */
_cogl_pixel_format_to_gl (src_bmp->format,
NULL, /* internal format */
out_glformat,
out_gltype);
_cogl_pixel_format_to_gl (dst_format,
out_glintformat,
NULL,
NULL);
if (dst_format_out)
*dst_format_out = dst_format;
return TRUE;
} }
void void
@ -152,53 +218,99 @@ _cogl_texture_set_wrap_mode_parameter (CoglHandle handle,
tex->vtable->set_wrap_mode_parameter (tex, wrap_mode); tex->vtable->set_wrap_mode_parameter (tex, wrap_mode);
} }
gboolean /* This is like CoglSpanIter except it deals with floats and it
_cogl_texture_bitmap_prepare (CoglTexture *tex, effectively assumes there is only one span from 0.0 to 1.0 */
CoglPixelFormat internal_format) typedef struct _CoglTextureIter
{ {
CoglBitmap new_bitmap; gfloat pos, end, next_pos;
CoglPixelFormat new_data_format; gboolean flipped;
gboolean success; gfloat t_1, t_2;
} CoglTextureIter;
/* Was there any internal conversion requested? static void
* By default Cogl will use a premultiplied internal format. Later we will _cogl_texture_iter_update (CoglTextureIter *iter)
* add control over this. */ {
if (internal_format == COGL_PIXEL_FORMAT_ANY) gfloat t_2;
float frac_part;
frac_part = modff (iter->pos, &iter->next_pos);
/* modff rounds the int part towards zero so we need to add one if
we're meant to be heading away from zero */
if (iter->pos >= 0.0f || frac_part == 0.0f)
iter->next_pos += 1.0f;
if (iter->next_pos > iter->end)
t_2 = iter->end;
else
t_2 = iter->next_pos;
if (iter->flipped)
{ {
if ((tex->bitmap.format & COGL_A_BIT) && iter->t_1 = t_2;
tex->bitmap.format != COGL_PIXEL_FORMAT_A_8) iter->t_2 = iter->pos;
internal_format = tex->bitmap.format | COGL_PREMULT_BIT;
else
internal_format = tex->bitmap.format;
} }
else
/* Find closest format accepted by GL */
new_data_format = _cogl_pixel_format_to_gl (internal_format,
&tex->gl_intformat,
&tex->gl_format,
&tex->gl_type);
/* Convert to internal format */
if (new_data_format != tex->bitmap.format)
{ {
success = _cogl_bitmap_convert_and_premult (&tex->bitmap, iter->t_1 = iter->pos;
&new_bitmap, iter->t_2 = t_2;
new_data_format);
if (!success)
return FALSE;
/* Update texture with new data */
_cogl_texture_bitmap_swap (tex, &new_bitmap);
} }
return TRUE;
} }
void static void
_cogl_texture_free (CoglTexture *tex) _cogl_texture_iter_begin (CoglTextureIter *iter,
gfloat t_1, gfloat t_2)
{ {
_cogl_texture_bitmap_free (tex); if (t_1 <= t_2)
{
iter->pos = t_1;
iter->end = t_2;
iter->flipped = FALSE;
}
else
{
iter->pos = t_2;
iter->end = t_1;
iter->flipped = TRUE;
}
_cogl_texture_iter_update (iter);
}
static void
_cogl_texture_iter_next (CoglTextureIter *iter)
{
iter->pos = iter->next_pos;
_cogl_texture_iter_update (iter);
}
static gboolean
_cogl_texture_iter_end (CoglTextureIter *iter)
{
return iter->pos >= iter->end;
}
/* This invokes the callback with enough quads to cover the manually
repeated range specified by the virtual texture coordinates without
emitting coordinates outside the range [0,1] */
void
_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback,
float tx_1, float ty_1,
float tx_2, float ty_2,
void *user_data)
{
CoglTextureIter x_iter, y_iter;
for (_cogl_texture_iter_begin (&y_iter, ty_1, ty_2);
!_cogl_texture_iter_end (&y_iter);
_cogl_texture_iter_next (&y_iter))
for (_cogl_texture_iter_begin (&x_iter, tx_1, tx_2);
!_cogl_texture_iter_end (&x_iter);
_cogl_texture_iter_next (&x_iter))
{
float coords[4] = { x_iter.t_1, y_iter.t_1, x_iter.t_2, y_iter.t_2 };
callback (coords, user_data);
}
} }
CoglHandle CoglHandle
@ -207,10 +319,19 @@ cogl_texture_new_with_size (guint width,
CoglTextureFlags flags, CoglTextureFlags flags,
CoglPixelFormat internal_format) CoglPixelFormat internal_format)
{ {
return _cogl_texture_2d_sliced_new_with_size (width, CoglHandle tex;
height,
flags, /* First try creating a fast-path non-sliced texture */
internal_format); tex = _cogl_texture_2d_new_with_size (width, height, flags, internal_format);
/* If it fails resort to sliced textures */
if (tex == COGL_INVALID_HANDLE)
tex = _cogl_texture_2d_sliced_new_with_size (width,
height,
flags,
internal_format);
return tex;
} }
CoglHandle CoglHandle
@ -222,13 +343,26 @@ cogl_texture_new_from_data (guint width,
guint rowstride, guint rowstride,
const guchar *data) const guchar *data)
{ {
return _cogl_texture_2d_sliced_new_from_data (width, CoglBitmap bitmap;
height,
flags, if (format == COGL_PIXEL_FORMAT_ANY)
format, return COGL_INVALID_HANDLE;
internal_format,
rowstride, if (data == NULL)
data); return COGL_INVALID_HANDLE;
/* Rowstride from width if not given */
if (rowstride == 0)
rowstride = width * _cogl_get_format_bpp (format);
/* Wrap the data into a bitmap */
bitmap.width = width;
bitmap.height = height;
bitmap.data = (guchar *) data;
bitmap.format = format;
bitmap.rowstride = rowstride;
return cogl_texture_new_from_bitmap (&bitmap, flags, internal_format);
} }
CoglHandle CoglHandle
@ -236,6 +370,21 @@ cogl_texture_new_from_bitmap (CoglHandle bmp_handle,
CoglTextureFlags flags, CoglTextureFlags flags,
CoglPixelFormat internal_format) CoglPixelFormat internal_format)
{ {
CoglHandle tex;
/* First try putting the texture in the atlas */
if ((tex = _cogl_atlas_texture_new_from_bitmap (bmp_handle,
flags,
internal_format)))
return tex;
/* If that doesn't work try a fast path 2D texture */
if ((tex = _cogl_texture_2d_new_from_bitmap (bmp_handle,
flags,
internal_format)))
return tex;
/* Otherwise create a sliced texture */
return _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle, return _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle,
flags, flags,
internal_format); internal_format);
@ -247,10 +396,31 @@ cogl_texture_new_from_file (const gchar *filename,
CoglPixelFormat internal_format, CoglPixelFormat internal_format,
GError **error) GError **error)
{ {
return _cogl_texture_2d_sliced_new_from_file (filename, CoglHandle bmp_handle;
flags, CoglBitmap *bmp;
internal_format, CoglHandle handle = COGL_INVALID_HANDLE;
error);
g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE);
bmp_handle = cogl_bitmap_new_from_file (filename, error);
if (bmp_handle == COGL_INVALID_HANDLE)
return COGL_INVALID_HANDLE;
bmp = (CoglBitmap *) bmp_handle;
/* We know that the bitmap data is solely owned by this function so
we can do the premult conversion in place. This avoids having to
copy the bitmap which will otherwise happen in
_cogl_texture_prepare_for_upload */
internal_format = _cogl_texture_determine_internal_format (bmp->format,
internal_format);
if (!_cogl_texture_needs_premult_conversion (bmp->format, internal_format) ||
_cogl_bitmap_convert_premult_status (bmp, bmp->format ^ COGL_PREMULT_BIT))
handle = cogl_texture_new_from_bitmap (bmp, flags, internal_format);
cogl_handle_unref (bmp);
return handle;
} }
CoglHandle CoglHandle
@ -271,6 +441,17 @@ cogl_texture_new_from_foreign (GLuint gl_handle,
format); format);
} }
CoglHandle
cogl_texture_new_from_sub_texture (CoglHandle full_texture,
gint sub_x,
gint sub_y,
gint sub_width,
gint sub_height)
{
return _cogl_sub_texture_new (full_texture, sub_x, sub_y,
sub_width, sub_height);
}
guint guint
cogl_texture_get_width (CoglHandle handle) cogl_texture_get_width (CoglHandle handle)
{ {
@ -281,7 +462,7 @@ cogl_texture_get_width (CoglHandle handle)
tex = COGL_TEXTURE (handle); tex = COGL_TEXTURE (handle);
return tex->bitmap.width; return tex->vtable->get_width (tex);
} }
guint guint
@ -294,7 +475,7 @@ cogl_texture_get_height (CoglHandle handle)
tex = COGL_TEXTURE (handle); tex = COGL_TEXTURE (handle);
return tex->bitmap.height; return tex->vtable->get_height (tex);
} }
CoglPixelFormat CoglPixelFormat
@ -307,7 +488,7 @@ cogl_texture_get_format (CoglHandle handle)
tex = COGL_TEXTURE (handle); tex = COGL_TEXTURE (handle);
return tex->bitmap.format; return tex->vtable->get_format (tex);
} }
guint guint
@ -318,9 +499,15 @@ cogl_texture_get_rowstride (CoglHandle handle)
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return 0; return 0;
/* FIXME: This function should go away. It previously just returned
the rowstride that was used to upload the data as far as I can
tell. This is not helpful */
tex = COGL_TEXTURE (handle); tex = COGL_TEXTURE (handle);
return tex->bitmap.rowstride; /* Just guess at a suitable rowstride */
return (_cogl_get_format_bpp (cogl_texture_get_format (tex))
* cogl_texture_get_width (tex));
} }
gint gint
@ -387,12 +574,6 @@ _cogl_texture_can_hardware_repeat (CoglHandle handle)
{ {
CoglTexture *tex = (CoglTexture *)handle; CoglTexture *tex = (CoglTexture *)handle;
#if HAVE_COGL_GL
/* TODO: COGL_TEXTURE_TYPE_2D_RECTANGLE */
if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB)
return FALSE;
#endif
return tex->vtable->can_hardware_repeat (tex); return tex->vtable->can_hardware_repeat (tex);
} }
@ -409,12 +590,21 @@ _cogl_texture_transform_coords_to_gl (CoglHandle handle,
tex->vtable->transform_coords_to_gl (tex, s, t); tex->vtable->transform_coords_to_gl (tex, s, t);
} }
GLenum gboolean
_cogl_texture_get_internal_gl_format (CoglHandle handle) _cogl_texture_transform_quad_coords_to_gl (CoglHandle handle,
float *coords)
{ {
CoglTexture *tex = COGL_TEXTURE (handle); CoglTexture *tex = COGL_TEXTURE (handle);
return tex->gl_intformat; return tex->vtable->transform_quad_coords_to_gl (tex, coords);
}
GLenum
_cogl_texture_get_gl_format (CoglHandle handle)
{
CoglTexture *tex = COGL_TEXTURE (handle);
return tex->vtable->get_gl_format (tex);
} }
gboolean gboolean
@ -460,6 +650,19 @@ _cogl_texture_ensure_mipmaps (CoglHandle handle)
tex->vtable->ensure_mipmaps (tex); tex->vtable->ensure_mipmaps (tex);
} }
void
_cogl_texture_ensure_non_quad_rendering (CoglHandle handle)
{
CoglTexture *tex;
if (!cogl_is_texture (handle))
return;
tex = COGL_TEXTURE (handle);
return tex->vtable->ensure_non_quad_rendering (tex);
}
gboolean gboolean
cogl_texture_set_region (CoglHandle handle, cogl_texture_set_region (CoglHandle handle,
gint src_x, gint src_x,
@ -505,7 +708,7 @@ cogl_texture_set_region (CoglHandle handle,
* glGetTexImage, but may be used as a fallback in some circumstances. * glGetTexImage, but may be used as a fallback in some circumstances.
*/ */
static void static void
do_texture_draw_and_read (CoglTexture *tex, do_texture_draw_and_read (CoglHandle handle,
CoglBitmap *target_bmp, CoglBitmap *target_bmp,
GLint *viewport) GLint *viewport)
{ {
@ -516,16 +719,18 @@ do_texture_draw_and_read (CoglTexture *tex,
float tx2, ty2; float tx2, ty2;
int bw, bh; int bw, bh;
CoglBitmap rect_bmp; CoglBitmap rect_bmp;
CoglHandle handle; guint tex_width, tex_height;
handle = (CoglHandle) tex;
bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888);
tex_width = cogl_texture_get_width (handle);
tex_height = cogl_texture_get_height (handle);
ry1 = 0; ry2 = 0; ry1 = 0; ry2 = 0;
ty1 = 0; ty2 = 0; ty1 = 0; ty2 = 0;
/* Walk Y axis until whole bitmap height consumed */ /* Walk Y axis until whole bitmap height consumed */
for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) for (bh = tex_height; bh > 0; bh -= viewport[3])
{ {
/* Rectangle Y coords */ /* Rectangle Y coords */
ry1 = ry2; ry1 = ry2;
@ -533,13 +738,13 @@ do_texture_draw_and_read (CoglTexture *tex,
/* Normalized texture Y coords */ /* Normalized texture Y coords */
ty1 = ty2; ty1 = ty2;
ty2 = (ry2 / (float)tex->bitmap.height); ty2 = (ry2 / (float) tex_height);
rx1 = 0; rx2 = 0; rx1 = 0; rx2 = 0;
tx1 = 0; tx2 = 0; tx1 = 0; tx2 = 0;
/* Walk X axis until whole bitmap width consumed */ /* Walk X axis until whole bitmap width consumed */
for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) for (bw = tex_width; bw > 0; bw-=viewport[2])
{ {
/* Rectangle X coords */ /* Rectangle X coords */
rx1 = rx2; rx1 = rx2;
@ -547,7 +752,7 @@ do_texture_draw_and_read (CoglTexture *tex,
/* Normalized texture X coords */ /* Normalized texture X coords */
tx1 = tx2; tx1 = tx2;
tx2 = (rx2 / (float)tex->bitmap.width); tx2 = (rx2 / (float) tex_width);
/* Draw a portion of texture */ /* Draw a portion of texture */
cogl_rectangle_with_texture_coords (0, 0, cogl_rectangle_with_texture_coords (0, 0,
@ -593,7 +798,7 @@ do_texture_draw_and_read (CoglTexture *tex,
* glGetTexImage, but may be used as a fallback in some circumstances. * glGetTexImage, but may be used as a fallback in some circumstances.
*/ */
gboolean gboolean
_cogl_texture_draw_and_read (CoglTexture *tex, _cogl_texture_draw_and_read (CoglHandle handle,
CoglBitmap *target_bmp, CoglBitmap *target_bmp,
GLuint target_gl_format, GLuint target_gl_format,
GLuint target_gl_type) GLuint target_gl_type)
@ -648,14 +853,14 @@ _cogl_texture_draw_and_read (CoglTexture *tex,
prev_source = cogl_handle_ref (ctx->source_material); prev_source = cogl_handle_ref (ctx->source_material);
cogl_set_source (ctx->texture_download_material); cogl_set_source (ctx->texture_download_material);
cogl_material_set_layer (ctx->texture_download_material, 0, tex); cogl_material_set_layer (ctx->texture_download_material, 0, handle);
cogl_material_set_layer_combine (ctx->texture_download_material, cogl_material_set_layer_combine (ctx->texture_download_material,
0, /* layer */ 0, /* layer */
"RGBA = REPLACE (TEXTURE)", "RGBA = REPLACE (TEXTURE)",
NULL); NULL);
do_texture_draw_and_read (tex, target_bmp, viewport); do_texture_draw_and_read (handle, target_bmp, viewport);
/* Check whether texture has alpha and framebuffer not */ /* Check whether texture has alpha and framebuffer not */
/* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer /* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer
@ -670,7 +875,7 @@ _cogl_texture_draw_and_read (CoglTexture *tex,
printf ("G bits: %d\n", g_bits); printf ("G bits: %d\n", g_bits);
printf ("B bits: %d\n", b_bits); printf ("B bits: %d\n", b_bits);
printf ("A bits: %d\n", a_bits); */ printf ("A bits: %d\n", a_bits); */
if ((tex->bitmap.format & COGL_A_BIT)/* && a_bits == 0*/) if ((cogl_texture_get_format (handle) & COGL_A_BIT)/* && a_bits == 0*/)
{ {
guchar *srcdata; guchar *srcdata;
guchar *dstdata; guchar *dstdata;
@ -692,7 +897,7 @@ _cogl_texture_draw_and_read (CoglTexture *tex,
"RGBA = REPLACE (TEXTURE[A])", "RGBA = REPLACE (TEXTURE[A])",
NULL); NULL);
do_texture_draw_and_read (tex, &alpha_bmp, viewport); do_texture_draw_and_read (handle, &alpha_bmp, viewport);
/* Copy temp R to target A */ /* Copy temp R to target A */
srcdata = alpha_bmp.data; srcdata = alpha_bmp.data;

View File

@ -317,6 +317,33 @@ gboolean cogl_texture_set_region (CoglHandle handle,
guint rowstride, guint rowstride,
const guchar *data); const guchar *data);
/**
* cogl_texture_new_from_sub_texture:
* @full_texture: a #CoglHandle to an existing texture
* @sub_x: X coordinate of the top-left of the subregion
* @sub_y: Y coordinate of the top-left of the subregion
* @sub_width: Width in pixels of the subregion
* @sub_height: Height in pixels of the subregion
*
* Creates a new texture which represents a subregion of another
* texture. The GL resources will be shared so that no new texture
* data is actually allocated.
*
* Sub textures have undefined behaviour texture coordinates outside
* of the range [0,1] are used. They also do not work with
* CoglVertexBuffers.
*
* Return value: a #CoglHandle to the new texture.
*
* Since: 1.2
*/
CoglHandle cogl_texture_new_from_sub_texture
(CoglHandle full_texture,
gint sub_x,
gint sub_y,
gint sub_width,
gint sub_height);
#ifndef COGL_DISABLE_DEPRECATED #ifndef COGL_DISABLE_DEPRECATED
/** /**

View File

@ -291,7 +291,8 @@ struct _CoglTextureVertex
typedef enum { typedef enum {
COGL_TEXTURE_NONE = 0, COGL_TEXTURE_NONE = 0,
COGL_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, COGL_TEXTURE_NO_AUTO_MIPMAP = 1 << 0,
COGL_TEXTURE_NO_SLICING = 1 << 1 COGL_TEXTURE_NO_SLICING = 1 << 1,
COGL_TEXTURE_NO_ATLAS = 1 << 2
} CoglTextureFlags; } CoglTextureFlags;
/** /**

View File

@ -1637,6 +1637,11 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
if (tex_handle == COGL_INVALID_HANDLE) if (tex_handle == COGL_INVALID_HANDLE)
continue; continue;
/* Give the texture a chance to know that we're rendering
non-quad shaped primitives. If the texture is in an atlas it
will be migrated */
_cogl_texture_ensure_non_quad_rendering (tex_handle);
if (!_cogl_texture_can_hardware_repeat (tex_handle)) if (!_cogl_texture_can_hardware_repeat (tex_handle))
{ {
g_warning ("Disabling layer %d of the current source material, " g_warning ("Disabling layer %d of the current source material, "

View File

@ -102,7 +102,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride,
} }
void void
_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, _cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target,
GLuint gl_handle,
int src_x, int src_x,
int src_y, int src_y,
int dst_x, int dst_x,
@ -111,8 +112,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
int height, int height,
CoglBitmap *source_bmp, CoglBitmap *source_bmp,
GLuint source_gl_format, GLuint source_gl_format,
GLuint source_gl_type, GLuint source_gl_type)
GLuint gl_handle)
{ {
int bpp = _cogl_get_format_bpp (source_bmp->format); int bpp = _cogl_get_format_bpp (source_bmp->format);
@ -122,11 +122,11 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
src_y, src_y,
bpp); bpp);
/* Upload new image data */ /* We don't need to use _cogl_texture_driver_bind here because we're
GE( _cogl_texture_driver_bind (tex->gl_target, not using the bound texture to render yet */
gl_handle, tex->gl_intformat) ); GE( glBindTexture (gl_target, gl_handle) );
GE( glTexSubImage2D (tex->gl_target, 0, GE( glTexSubImage2D (gl_target, 0,
dst_x, dst_y, dst_x, dst_y,
width, height, width, height,
source_gl_format, source_gl_format,
@ -134,6 +134,32 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
source_bmp->data) ); source_bmp->data) );
} }
void
_cogl_texture_driver_upload_to_gl (GLenum gl_target,
GLuint gl_handle,
CoglBitmap *source_bmp,
GLint internal_gl_format,
GLuint source_gl_format,
GLuint source_gl_type)
{
int bpp = _cogl_get_format_bpp (source_bmp->format);
/* Setup gl alignment to match rowstride and top-left corner */
prep_gl_for_pixels_upload_full (source_bmp->rowstride, 0, 0, bpp);
/* We don't need to use _cogl_texture_driver_bind here because we're
not using the bound texture to render yet */
GE( glBindTexture (gl_target, gl_handle) );
GE( glTexImage2D (gl_target, 0,
internal_gl_format,
source_bmp->width, source_bmp->height,
0,
source_gl_format,
source_gl_type,
source_bmp->data) );
}
gboolean gboolean
_cogl_texture_driver_gl_get_tex_image (GLenum gl_target, _cogl_texture_driver_gl_get_tex_image (GLenum gl_target,
GLenum dest_gl_format, GLenum dest_gl_format,

View File

@ -71,7 +71,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride,
} }
void void
_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, _cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target,
GLuint gl_handle,
int src_x, int src_x,
int src_y, int src_y,
int dst_x, int dst_x,
@ -80,8 +81,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
int height, int height,
CoglBitmap *source_bmp, CoglBitmap *source_bmp,
GLuint source_gl_format, GLuint source_gl_format,
GLuint source_gl_type, GLuint source_gl_type)
GLuint gl_handle)
{ {
int bpp = _cogl_get_format_bpp (source_bmp->format); int bpp = _cogl_get_format_bpp (source_bmp->format);
CoglBitmap slice_bmp; CoglBitmap slice_bmp;
@ -94,7 +94,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
* rowstride = bpp * width and the texture image is not sliced */ * rowstride = bpp * width and the texture image is not sliced */
/* Setup temp bitmap for slice subregion */ /* Setup temp bitmap for slice subregion */
slice_bmp.format = tex->bitmap.format; slice_bmp.format = source_bmp->format;
slice_bmp.width = width; slice_bmp.width = width;
slice_bmp.height = height; slice_bmp.height = height;
slice_bmp.rowstride = bpp * slice_bmp.width; slice_bmp.rowstride = bpp * slice_bmp.width;
@ -113,11 +113,11 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
slice_bmp.width, slice_bmp.width,
slice_bmp.height); slice_bmp.height);
/* Upload new image data */ /* We don't need to use _cogl_texture_driver_bind here because we're
GE( _cogl_texture_driver_bind (tex->gl_target, not using the bound texture to render yet */
gl_handle, tex->gl_intformat) ); GE( glBindTexture (gl_target, gl_handle) );
GE( glTexSubImage2D (tex->gl_target, 0, GE( glTexSubImage2D (gl_target, 0,
dst_x, dst_y, dst_x, dst_y,
width, height, width, height,
source_gl_format, source_gl_format,
@ -128,6 +128,53 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
g_free (slice_bmp.data); g_free (slice_bmp.data);
} }
void
_cogl_texture_driver_upload_to_gl (GLenum gl_target,
GLuint gl_handle,
CoglBitmap *source_bmp,
GLint internal_gl_format,
GLuint source_gl_format,
GLuint source_gl_type)
{
int bpp = _cogl_get_format_bpp (source_bmp->format);
CoglBitmap bmp = *source_bmp;
gboolean bmp_owner = FALSE;
/* If the rowstride can't be specified with just GL_ALIGNMENT alone
then we need to copy the bitmap because there is no GL_ROW_LENGTH */
if (source_bmp->rowstride / bpp != source_bmp->width)
{
bmp.rowstride = bpp * bmp.width;
bmp.data = g_malloc (bmp.rowstride * bmp.height);
bmp_owner = TRUE;
_cogl_bitmap_copy_subregion (source_bmp,
&bmp,
0, 0, 0, 0,
bmp.width,
bmp.height);
}
/* Setup gl alignment to match rowstride and top-left corner */
_cogl_texture_driver_prep_gl_for_pixels_upload (bmp.rowstride,
bpp);
/* We don't need to use _cogl_texture_driver_bind here because we're
not using the bound texture to render yet */
GE( glBindTexture (gl_target, gl_handle) );
GE( glTexImage2D (gl_target, 0,
internal_gl_format,
bmp.width, bmp.height,
0,
source_gl_format,
source_gl_type,
bmp.data) );
if (bmp_owner)
g_free (bmp.data);
}
/* NB: GLES doesn't support glGetTexImage2D, so cogl-texture will instead /* NB: GLES doesn't support glGetTexImage2D, so cogl-texture will instead
* fallback to a generic render + readpixels approach to downloading * fallback to a generic render + readpixels approach to downloading
* texture data. (See _cogl_texture_draw_and_read() ) */ * texture data. (See _cogl_texture_draw_and_read() ) */

View File

@ -192,6 +192,7 @@ cogl_texture_new_from_file
cogl_texture_new_from_data cogl_texture_new_from_data
cogl_texture_new_from_foreign cogl_texture_new_from_foreign
cogl_texture_new_from_bitmap cogl_texture_new_from_bitmap
cogl_texture_new_from_sub_texture
cogl_is_texture cogl_is_texture
cogl_texture_ref cogl_texture_ref
cogl_texture_unref cogl_texture_unref

View File

@ -41,6 +41,7 @@ test_conformance_SOURCES = \
test-group.c \ test-group.c \
test-actor-size.c \ test-actor-size.c \
test-texture-fbo.c \ test-texture-fbo.c \
test-cogl-sub-texture.c \
test-script-parser.c \ test-script-parser.c \
test-actor-destroy.c \ test-actor-destroy.c \
test-behaviours.c \ test-behaviours.c \

View File

@ -157,7 +157,7 @@ make_texture (void)
tex = cogl_texture_new_from_data (TEXTURE_SIZE, tex = cogl_texture_new_from_data (TEXTURE_SIZE,
TEXTURE_SIZE, TEXTURE_SIZE,
COGL_TEXTURE_NONE, COGL_TEXTURE_NO_ATLAS,
COGL_PIXEL_FORMAT_RGBA_8888, COGL_PIXEL_FORMAT_RGBA_8888,
COGL_PIXEL_FORMAT_ANY, COGL_PIXEL_FORMAT_ANY,
TEXTURE_SIZE * 4, TEXTURE_SIZE * 4,

View File

@ -0,0 +1,351 @@
#include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <string.h>
#include "test-conform-common.h"
#define SOURCE_SIZE 32
#define SOURCE_DIVISIONS_X 2
#define SOURCE_DIVISIONS_Y 2
#define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X)
#define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y)
#define TEST_INSET 1
static const ClutterColor
corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] =
{
{ 0xff, 0x00, 0x00, 0xff }, /* red top left */
{ 0x00, 0xff, 0x00, 0xff }, /* green top right */
{ 0x00, 0x00, 0xff, 0xff }, /* blue bottom left */
{ 0xff, 0x00, 0xff, 0xff } /* purple bottom right */
};
static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
typedef struct _TestState
{
ClutterActor *stage;
guint frame;
CoglHandle tex;
} TestState;
static CoglHandle
create_source (void)
{
int dx, dy;
guchar *data = g_malloc (SOURCE_SIZE * SOURCE_SIZE * 4);
/* Create a texture with a different coloured rectangle at each
corner */
for (dy = 0; dy < SOURCE_DIVISIONS_Y; dy++)
for (dx = 0; dx < SOURCE_DIVISIONS_X; dx++)
{
guchar *p = (data + dy * DIVISION_HEIGHT * SOURCE_SIZE * 4 +
dx * DIVISION_WIDTH * 4);
int x, y;
for (y = 0; y < DIVISION_HEIGHT; y++)
{
for (x = 0; x < DIVISION_WIDTH; x++)
{
memcpy (p, corner_colors + dx + dy * SOURCE_DIVISIONS_X, 4);
p += 4;
}
p += SOURCE_SIZE * 4 - DIVISION_WIDTH * 4;
}
}
return cogl_texture_new_from_data (SOURCE_SIZE, SOURCE_SIZE,
COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_RGBA_8888,
COGL_PIXEL_FORMAT_ANY,
SOURCE_SIZE * 4,
data);
}
static void
draw_frame (TestState *state)
{
CoglHandle sub_texture;
/* Create a sub texture of the bottom right quarter of the texture */
sub_texture = cogl_texture_new_from_sub_texture (state->tex,
DIVISION_WIDTH,
DIVISION_HEIGHT,
DIVISION_WIDTH,
DIVISION_HEIGHT);
/* Paint it */
cogl_set_source_texture (sub_texture);
cogl_rectangle (0.0f, 0.0f, DIVISION_WIDTH, DIVISION_HEIGHT);
cogl_handle_unref (sub_texture);
/* Repeat a sub texture of the top half of the full texture. This is
documented to be undefined so it doesn't technically have to work
but it will with the current implementation */
sub_texture = cogl_texture_new_from_sub_texture (state->tex,
0, 0,
SOURCE_SIZE,
DIVISION_HEIGHT);
cogl_set_source_texture (sub_texture);
cogl_rectangle_with_texture_coords (0.0f, SOURCE_SIZE,
SOURCE_SIZE * 2.0f, SOURCE_SIZE * 1.5f,
0.0f, 0.0f,
2.0f, 1.0f);
cogl_handle_unref (sub_texture);
}
static gboolean
validate_part (TestState *state,
int xpos, int ypos,
int width, int height,
const ClutterColor *color)
{
int x, y;
gboolean pass = TRUE;
guchar *pixels, *p;
p = pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
xpos + TEST_INSET,
ypos + TEST_INSET,
width - TEST_INSET - 2,
height - TEST_INSET - 2);
/* Check whether the center of each division is the right color */
for (y = 0; y < height - TEST_INSET - 2; y++)
for (x = 0; x < width - TEST_INSET - 2; x++)
{
if (p[0] != color->red ||
p[1] != color->green ||
p[2] != color->blue)
pass = FALSE;
p += 4;
}
return pass;
}
static CoglHandle
create_test_texture (void)
{
CoglHandle tex;
guint8 *data = g_malloc (256 * 256 * 4), *p = data;
int x, y;
/* Create a texture that is 256x256 where the red component ranges
from 0->255 along the x axis and the green component ranges from
0->255 along the y axis. The blue and alpha components are all
255 */
for (y = 0; y < 256; y++)
for (x = 0; x < 256; x++)
{
*(p++) = x;
*(p++) = y;
*(p++) = 255;
*(p++) = 255;
}
tex = cogl_texture_new_from_data (256, 256, COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
COGL_PIXEL_FORMAT_ANY,
256 * 4,
data);
g_free (data);
return tex;
}
static guint8 *
create_update_data (void)
{
guint8 *data = g_malloc (256 * 256 * 4), *p = data;
int x, y;
/* Create some image data that is 256x256 where the blue component
ranges from 0->255 along the x axis and the alpha component
ranges from 0->255 along the y axis. The red and green components
are all zero */
for (y = 0; y < 256; y++)
for (x = 0; x < 256; x++)
{
*(p++) = 0;
*(p++) = 0;
*(p++) = x;
*(p++) = y;
}
return data;
}
static void
validate_result (TestState *state)
{
int i, division_num, x, y;
CoglHandle sub_texture, test_tex;
guchar *texture_data, *p;
gint tex_width, tex_height;
/* Sub texture of the bottom right corner of the texture */
g_assert (validate_part (state, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT,
corner_colors +
(SOURCE_DIVISIONS_Y - 1) * SOURCE_DIVISIONS_X +
SOURCE_DIVISIONS_X - 1));
/* Sub texture of the top half repeated horizontally */
for (i = 0; i < 2; i++)
for (division_num = 0; division_num < SOURCE_DIVISIONS_X; division_num++)
g_assert (validate_part (state,
i * SOURCE_SIZE + division_num * DIVISION_WIDTH,
SOURCE_SIZE,
DIVISION_WIDTH, DIVISION_HEIGHT,
corner_colors + division_num));
/* Try reading back the texture data */
sub_texture = cogl_texture_new_from_sub_texture (state->tex,
SOURCE_SIZE / 4,
SOURCE_SIZE / 4,
SOURCE_SIZE / 2,
SOURCE_SIZE / 2);
tex_width = cogl_texture_get_width (sub_texture);
tex_height = cogl_texture_get_height (sub_texture);
p = texture_data = g_malloc (tex_width * tex_height * 4);
cogl_texture_get_data (sub_texture, COGL_PIXEL_FORMAT_RGBA_8888,
tex_width * 4,
texture_data);
for (y = 0; y < tex_height; y++)
for (x = 0; x < tex_width; x++)
{
int div_x = ((x * SOURCE_SIZE / 2 / tex_width + SOURCE_SIZE / 4) /
DIVISION_WIDTH);
int div_y = ((y * SOURCE_SIZE / 2 / tex_height + SOURCE_SIZE / 4) /
DIVISION_HEIGHT);
const ClutterColor *color = (corner_colors + div_x +
div_y * SOURCE_DIVISIONS_X);
g_assert (p[0] == color->red);
g_assert (p[1] == color->green);
g_assert (p[2] == color->blue);
p += 4;
}
g_free (texture_data);
cogl_handle_unref (sub_texture);
/* Create a 256x256 test texture */
test_tex = create_test_texture ();
/* Create a sub texture the views the center half of the texture */
sub_texture = cogl_texture_new_from_sub_texture (test_tex,
64, 64, 128, 128);
/* Update the center half of the sub texture */
texture_data = create_update_data ();
cogl_texture_set_region (sub_texture, 0, 0, 32, 32, 64, 64, 256, 256,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4,
texture_data);
g_free (texture_data);
cogl_handle_unref (sub_texture);
/* Get the texture data */
p = texture_data = g_malloc (256 * 256 * 4);
cogl_texture_get_data (test_tex, COGL_PIXEL_FORMAT_RGBA_8888,
256 * 4, texture_data);
/* Verify the texture data */
for (y = 0; y < 256; y++)
for (x = 0; x < 256; x++)
{
/* If we're in the center quarter */
if (x >= 96 && x < 160 && y >= 96 && y < 160)
{
g_assert ((*p++) == 0);
g_assert ((*p++) == 0);
g_assert ((*p++) == x - 96);
g_assert ((*p++) == y - 96);
}
else
{
g_assert ((*p++) == x);
g_assert ((*p++) == y);
g_assert ((*p++) == 255);
g_assert ((*p++) == 255);
}
}
g_free (texture_data);
cogl_handle_unref (test_tex);
/* Comment this out to see what the test paints */
clutter_main_quit ();
}
static void
on_paint (ClutterActor *actor, TestState *state)
{
int frame_num;
draw_frame (state);
/* XXX: Experiments have shown that for some buggy drivers, when using
* glReadPixels there is some kind of race, so we delay our test for a
* few frames and a few seconds:
*/
/* Need to increment frame first because clutter_stage_read_pixels
fires a redraw */
frame_num = state->frame++;
if (frame_num == 2)
validate_result (state);
else if (frame_num < 2)
g_usleep (G_USEC_PER_SEC);
}
static gboolean
queue_redraw (gpointer stage)
{
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
return TRUE;
}
void
test_cogl_sub_texture (TestConformSimpleFixture *fixture,
gconstpointer data)
{
TestState state;
guint idle_source;
guint paint_handler;
state.frame = 0;
state.stage = clutter_stage_get_default ();
state.tex = create_source ();
clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
/* We force continuous redrawing of the stage, since we need to skip
* the first few frames, and we wont be doing anything else that
* will trigger redrawing. */
idle_source = g_idle_add (queue_redraw, state.stage);
paint_handler = g_signal_connect_after (state.stage, "paint",
G_CALLBACK (on_paint), &state);
clutter_actor_show_all (state.stage);
clutter_main ();
g_source_remove (idle_source);
g_signal_handler_disconnect (state.stage, paint_handler);
cogl_handle_unref (state.tex);
/* Remove all of the actors from the stage */
clutter_container_foreach (CLUTTER_CONTAINER (state.stage),
(ClutterCallback) clutter_actor_destroy,
NULL);
if (g_test_verbose ())
g_print ("OK\n");
}

View File

@ -190,6 +190,7 @@ main (int argc, char **argv)
TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_npot_texture); TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_npot_texture);
TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_multitexture); TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_multitexture);
TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_texture_mipmaps); TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_texture_mipmaps);
TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_sub_texture);
TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_contiguous); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_contiguous);
TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved);