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:
commit
830f2402d4
@ -112,11 +112,19 @@ cogl_sources_c = \
|
||||
$(srcdir)/cogl-blend-string.c \
|
||||
$(srcdir)/cogl-blend-string.h \
|
||||
$(srcdir)/cogl-debug.c \
|
||||
$(srcdir)/cogl-sub-texture-private.h \
|
||||
$(srcdir)/cogl-texture-private.h \
|
||||
$(srcdir)/cogl-texture-2d-private.h \
|
||||
$(srcdir)/cogl-texture-2d-sliced-private.h \
|
||||
$(srcdir)/cogl-texture-driver.h \
|
||||
$(srcdir)/cogl-sub-texture.c \
|
||||
$(srcdir)/cogl-texture.c \
|
||||
$(srcdir)/cogl-texture-2d.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.c \
|
||||
$(srcdir)/cogl-journal-private.h \
|
||||
|
64
clutter/cogl/cogl/cogl-atlas-texture-private.h
Normal file
64
clutter/cogl/cogl/cogl-atlas-texture-private.h
Normal 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 */
|
1045
clutter/cogl/cogl/cogl-atlas-texture.c
Normal file
1045
clutter/cogl/cogl/cogl-atlas-texture.c
Normal file
File diff suppressed because it is too large
Load Diff
596
clutter/cogl/cogl/cogl-atlas.c
Normal file
596
clutter/cogl/cogl/cogl-atlas.c
Normal 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 */
|
76
clutter/cogl/cogl/cogl-atlas.h
Normal file
76
clutter/cogl/cogl/cogl-atlas.h
Normal 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 */
|
@ -150,7 +150,7 @@ _cogl_rgba_to_abgr (const guchar *src, guchar *dst)
|
||||
/* (Un)Premultiplication */
|
||||
|
||||
inline static void
|
||||
_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst)
|
||||
_cogl_unpremult_alpha_0 (guchar *dst)
|
||||
{
|
||||
dst[0] = 0;
|
||||
dst[1] = 0;
|
||||
@ -159,58 +159,58 @@ _cogl_unpremult_alpha_0 (const guchar *src, guchar *dst)
|
||||
}
|
||||
|
||||
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[1] = (src[1] * 255) / alpha;
|
||||
dst[2] = (src[2] * 255) / alpha;
|
||||
dst[3] = alpha;
|
||||
dst[0] = (dst[0] * 255) / alpha;
|
||||
dst[1] = (dst[1] * 255) / alpha;
|
||||
dst[2] = (dst[2] * 255) / alpha;
|
||||
}
|
||||
|
||||
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] = (src[1] * 255) / alpha;
|
||||
dst[2] = (src[2] * 255) / alpha;
|
||||
dst[3] = (src[3] * 255) / alpha;
|
||||
dst[1] = (dst[1] * 255) / alpha;
|
||||
dst[2] = (dst[2] * 255) / alpha;
|
||||
dst[3] = (dst[3] * 255) / alpha;
|
||||
}
|
||||
|
||||
/* No division form of floor((c*a + 128)/255) (I first encountered
|
||||
* this in the RENDER implementation in the X server.) Being exact
|
||||
* 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
|
||||
_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
|
||||
* code generation with GCC in the past; it shouldn't do any worse in
|
||||
* any case.
|
||||
*/
|
||||
guint t1, t2, t3;
|
||||
MULT(dst[0], src[0], alpha, t1);
|
||||
MULT(dst[1], src[1], alpha, t2);
|
||||
MULT(dst[2], src[2], alpha, t3);
|
||||
dst[3] = alpha;
|
||||
MULT(dst[0], alpha, t1);
|
||||
MULT(dst[1], alpha, t2);
|
||||
MULT(dst[2], alpha, t3);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
dst[0] = alpha;
|
||||
MULT(dst[1], src[1], alpha, t1);
|
||||
MULT(dst[2], src[2], alpha, t2);
|
||||
MULT(dst[3], src[3], alpha, t3);
|
||||
MULT(dst[1], alpha, t1);
|
||||
MULT(dst[2], alpha, t2);
|
||||
MULT(dst[3], alpha, t3);
|
||||
}
|
||||
|
||||
#undef MULT
|
||||
@ -342,112 +342,82 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
|
||||
}
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp)
|
||||
_cogl_bitmap_fallback_unpremult (CoglBitmap *bmp)
|
||||
{
|
||||
guchar *src;
|
||||
guchar *dst;
|
||||
gint bpp;
|
||||
guchar *p;
|
||||
gint x,y;
|
||||
|
||||
/* Make sure format supported for un-premultiplication */
|
||||
if (!_cogl_bitmap_fallback_can_unpremult (bmp->format))
|
||||
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++)
|
||||
{
|
||||
src = (guchar*)bmp->data + y * bmp->rowstride;
|
||||
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
|
||||
p = (guchar*) bmp->data + y * bmp->rowstride;
|
||||
|
||||
if (bmp->format & COGL_AFIRST_BIT)
|
||||
{
|
||||
for (x = 0; x < bmp->width; x++)
|
||||
{
|
||||
if (src[0] == 0)
|
||||
_cogl_unpremult_alpha_0 (src, dst);
|
||||
if (p[0] == 0)
|
||||
_cogl_unpremult_alpha_0 (p);
|
||||
else
|
||||
_cogl_unpremult_alpha_first (src, dst);
|
||||
src += bpp;
|
||||
dst += bpp;
|
||||
_cogl_unpremult_alpha_first (p);
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (x = 0; x < bmp->width; x++)
|
||||
{
|
||||
if (src[0] == 0)
|
||||
_cogl_unpremult_alpha_0 (src, dst);
|
||||
if (p[3] == 0)
|
||||
_cogl_unpremult_alpha_0 (p);
|
||||
else
|
||||
_cogl_unpremult_alpha_last (src, dst);
|
||||
src += bpp;
|
||||
dst += bpp;
|
||||
_cogl_unpremult_alpha_last (p);
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp->format &= ~COGL_PREMULT_BIT;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp)
|
||||
_cogl_bitmap_fallback_premult (CoglBitmap *bmp)
|
||||
{
|
||||
guchar *src;
|
||||
guchar *dst;
|
||||
gint bpp;
|
||||
guchar *p;
|
||||
gint x,y;
|
||||
|
||||
/* Make sure format supported for un-premultiplication */
|
||||
if (!_cogl_bitmap_fallback_can_premult (bmp->format))
|
||||
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++)
|
||||
{
|
||||
src = (guchar*)bmp->data + y * bmp->rowstride;
|
||||
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
|
||||
p = (guchar*) bmp->data + y * bmp->rowstride;
|
||||
|
||||
if (bmp->format & COGL_AFIRST_BIT)
|
||||
{
|
||||
for (x = 0; x < bmp->width; x++)
|
||||
{
|
||||
_cogl_premult_alpha_first (src, dst);
|
||||
src += bpp;
|
||||
dst += bpp;
|
||||
}
|
||||
}
|
||||
{
|
||||
for (x = 0; x < bmp->width; x++)
|
||||
{
|
||||
_cogl_premult_alpha_first (p);
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (x = 0; x < bmp->width; x++)
|
||||
{
|
||||
_cogl_premult_alpha_last (src, dst);
|
||||
src += bpp;
|
||||
dst += bpp;
|
||||
}
|
||||
}
|
||||
{
|
||||
for (x = 0; x < bmp->width; x++)
|
||||
{
|
||||
_cogl_premult_alpha_last (p);
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp->format |= COGL_PREMULT_BIT;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -64,15 +64,13 @@ _cogl_bitmap_convert (const CoglBitmap *bmp,
|
||||
}
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_unpremult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp)
|
||||
_cogl_bitmap_unpremult (CoglBitmap *dst_bmp)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_premult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp)
|
||||
_cogl_bitmap_premult (CoglBitmap *dst_bmp)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -68,20 +68,16 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
|
||||
CoglPixelFormat dst_format);
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_unpremult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp);
|
||||
_cogl_bitmap_unpremult (CoglBitmap *dst_bmp);
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp);
|
||||
_cogl_bitmap_fallback_unpremult (CoglBitmap *dst_bmp);
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_premult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp);
|
||||
_cogl_bitmap_premult (CoglBitmap *dst_bmp);
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp);
|
||||
_cogl_bitmap_fallback_premult (CoglBitmap *dst_bmp);
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_from_file (CoglBitmap *bmp,
|
||||
@ -93,9 +89,13 @@ _cogl_bitmap_fallback_from_file (CoglBitmap *bmp,
|
||||
const gchar *filename);
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp,
|
||||
CoglPixelFormat dst_format);
|
||||
_cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
|
||||
CoglPixelFormat dst_format);
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp,
|
||||
CoglPixelFormat dst_format);
|
||||
|
||||
void
|
||||
_cogl_bitmap_copy_subregion (CoglBitmap *src,
|
||||
|
@ -61,84 +61,58 @@ _cogl_get_format_bpp (CoglPixelFormat format)
|
||||
}
|
||||
|
||||
gboolean
|
||||
_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp,
|
||||
CoglBitmap *dst_bmp,
|
||||
CoglPixelFormat dst_format)
|
||||
_cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
|
||||
CoglPixelFormat dst_format)
|
||||
{
|
||||
CoglBitmap tmp_bmp = *bmp;
|
||||
CoglBitmap new_bmp = *bmp;
|
||||
gboolean new_bmp_owner = FALSE;
|
||||
/* Do we need to unpremultiply? */
|
||||
if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
|
||||
(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)? */
|
||||
if ((bmp->format & COGL_UNPREMULT_MASK) !=
|
||||
(dst_format & COGL_UNPREMULT_MASK))
|
||||
{
|
||||
/* Try converting using imaging library */
|
||||
if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format))
|
||||
{
|
||||
/* ... or try fallback */
|
||||
if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Update bitmap with new data */
|
||||
new_bmp = tmp_bmp;
|
||||
new_bmp_owner = TRUE;
|
||||
if (!_cogl_bitmap_convert (bmp, dst_bmp, dst_format))
|
||||
{
|
||||
/* ... or try fallback */
|
||||
if (!_cogl_bitmap_fallback_convert (bmp, dst_bmp, dst_format))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do we need to unpremultiply */
|
||||
if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
|
||||
(dst_format & COGL_PREMULT_BIT) == 0)
|
||||
else
|
||||
{
|
||||
/* Try unpremultiplying using imaging library */
|
||||
if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp))
|
||||
{
|
||||
/* ... 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;
|
||||
/* Copy the bitmap so that we can premultiply in-place */
|
||||
*dst_bmp = *bmp;
|
||||
dst_bmp->data = g_memdup (bmp->data, bmp->rowstride * bmp->height);
|
||||
}
|
||||
|
||||
/* Do we need to premultiply */
|
||||
if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
|
||||
(dst_format & COGL_PREMULT_BIT) > 0)
|
||||
if (!_cogl_bitmap_convert_premult_status (dst_bmp, dst_format))
|
||||
{
|
||||
/* Try premultiplying using imaging library */
|
||||
if (!_cogl_bitmap_premult (&new_bmp, &tmp_bmp))
|
||||
{
|
||||
/* ... 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;
|
||||
g_free (dst_bmp->data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Output new bitmap info */
|
||||
*dst_bmp = new_bmp;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,9 @@ cogl_create_context (void)
|
||||
cogl_enable (enable_flags);
|
||||
_cogl_flush_face_winding ();
|
||||
|
||||
_context->atlas = NULL;
|
||||
_context->atlas_texture = COGL_INVALID_HANDLE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -188,6 +191,11 @@ _cogl_destroy_context ()
|
||||
if (_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);
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "cogl-clip-stack.h"
|
||||
#include "cogl-matrix-stack.h"
|
||||
#include "cogl-material-private.h"
|
||||
#include "cogl-atlas.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -111,6 +112,9 @@ typedef struct
|
||||
|
||||
CoglHandle texture_download_material;
|
||||
|
||||
CoglAtlas *atlas;
|
||||
CoglHandle atlas_texture;
|
||||
|
||||
/* This debugging variable is used to pick a colour for visually
|
||||
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
|
||||
|
@ -47,7 +47,10 @@ static const GDebugKey cogl_debug_keys[] = {
|
||||
{ "batching", COGL_DEBUG_BATCHING },
|
||||
{ "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM },
|
||||
{ "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);
|
||||
|
@ -45,7 +45,10 @@ typedef enum {
|
||||
COGL_DEBUG_BATCHING = 1 << 13,
|
||||
COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 14,
|
||||
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;
|
||||
|
||||
#ifdef COGL_ENABLE_DEBUG
|
||||
|
@ -51,7 +51,7 @@ _cogl_journal_log_quad (float x_1,
|
||||
int n_layers,
|
||||
guint32 fallback_layers,
|
||||
GLuint layer0_override_texture,
|
||||
float *tex_coords,
|
||||
const float *tex_coords,
|
||||
unsigned int tex_coords_len);
|
||||
|
||||
#endif /* __COGL_JOURNAL_PRIVATE_H */
|
||||
|
@ -665,7 +665,7 @@ _cogl_journal_log_quad (float x_1,
|
||||
int n_layers,
|
||||
guint32 fallback_layers,
|
||||
GLuint layer0_override_texture,
|
||||
float *tex_coords,
|
||||
const float *tex_coords,
|
||||
unsigned int tex_coords_len)
|
||||
{
|
||||
size_t stride;
|
||||
|
@ -204,6 +204,12 @@ typedef enum _CoglMaterialLayerFlags
|
||||
*/
|
||||
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:
|
||||
* @COGL_MATERIAL_FLUSH_FALLBACK_MASK: The fallback_layers member is set to
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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:
|
||||
* @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
|
||||
CoglTextureUnit *unit;
|
||||
|
||||
_cogl_material_layer_ensure_mipmaps (layer_handle);
|
||||
|
||||
new_gl_layer_info.layer0_overridden =
|
||||
layer0_override_texture ? TRUE : FALSE;
|
||||
new_gl_layer_info.fallback =
|
||||
@ -1400,7 +1415,13 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
|
||||
|
||||
tex_handle = layer->texture;
|
||||
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
|
||||
{
|
||||
new_gl_layer_info.fallback = TRUE;
|
||||
@ -1431,13 +1452,6 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
|
||||
GE (glActiveTexture (GL_TEXTURE0 + 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
|
||||
if it is different from gl_layer_info->gl_texture to avoid
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
_cogl_material_layer_equal (CoglMaterialLayer *material0_layer,
|
||||
CoglHandle material0_layer_texture,
|
||||
CoglMaterialLayer *material1_layer,
|
||||
CoglHandle material1_layer_texture)
|
||||
{
|
||||
if (material0_layer_texture != material1_layer_texture)
|
||||
if (!_cogl_material_texture_equal (material0_layer_texture,
|
||||
material1_layer_texture))
|
||||
return FALSE;
|
||||
|
||||
if ((material0_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) !=
|
||||
|
@ -71,8 +71,8 @@ static void
|
||||
log_quad_sub_textures_cb (CoglHandle texture_handle,
|
||||
GLuint gl_handle,
|
||||
GLenum gl_target,
|
||||
float *subtexture_coords,
|
||||
float *virtual_coords,
|
||||
const float *subtexture_coords,
|
||||
const float *virtual_coords,
|
||||
void *user_data)
|
||||
{
|
||||
TextureSlicedQuadState *state = user_data;
|
||||
@ -249,6 +249,9 @@ _cogl_multitexture_quad_single_primitive (float x_1,
|
||||
const float *in_tex_coords;
|
||||
float *out_tex_coords;
|
||||
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);
|
||||
|
||||
@ -257,27 +260,41 @@ _cogl_multitexture_quad_single_primitive (float x_1,
|
||||
if (tex_handle == COGL_INVALID_HANDLE)
|
||||
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];
|
||||
|
||||
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
|
||||
* can't handle texture repeating so we check that the texture
|
||||
* coords lie in the range [0,1].
|
||||
*
|
||||
* 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.
|
||||
* can't handle texture repeating so we can't use the layer if
|
||||
* repeating is required.
|
||||
*
|
||||
* NB: We already know that no texture matrix is being used if the
|
||||
* texture doesn't support hardware repeat.
|
||||
*/
|
||||
if (!_cogl_texture_can_hardware_repeat (tex_handle)
|
||||
&& 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 (!_cogl_texture_can_hardware_repeat (tex_handle) && need_repeat)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
@ -315,45 +332,15 @@ _cogl_multitexture_quad_single_primitive (float x_1,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Setup the texture unit...
|
||||
*/
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* If we're not repeating then we want to clamp the coords
|
||||
to the edge otherwise it can pull in edge pixels from the
|
||||
wrong side when scaled */
|
||||
if (need_repeat)
|
||||
wrap_mode = GL_REPEAT;
|
||||
else
|
||||
{
|
||||
memcpy (out_tex_coords, default_tex_coords, sizeof (GLfloat) * 4);
|
||||
wrap_mode = GL_CLAMP_TO_EDGE;
|
||||
|
||||
_cogl_texture_set_wrap_mode_parameter (tex_handle, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
_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_texture_set_wrap_mode_parameter (tex_handle, wrap_mode);
|
||||
}
|
||||
|
||||
_cogl_journal_log_quad (x_1,
|
||||
@ -414,6 +401,12 @@ _cogl_rectangles_with_multitexture_coords (
|
||||
!= COGL_MATERIAL_LAYER_TYPE_TEXTURE)
|
||||
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);
|
||||
|
||||
/* COGL_INVALID_HANDLE textures are handled by
|
||||
@ -651,11 +644,11 @@ cogl_rectangle (float x_1,
|
||||
|
||||
void
|
||||
draw_polygon_sub_texture_cb (CoglHandle tex_handle,
|
||||
GLuint gl_handle,
|
||||
GLenum gl_target,
|
||||
float *subtexture_coords,
|
||||
float *virtual_coords,
|
||||
void *user_data)
|
||||
GLuint gl_handle,
|
||||
GLenum gl_target,
|
||||
const float *subtexture_coords,
|
||||
const float *virtual_coords,
|
||||
void *user_data)
|
||||
{
|
||||
TextureSlicedPolygonState *state = user_data;
|
||||
GLfloat *v;
|
||||
@ -880,6 +873,11 @@ cogl_polygon (const CoglTextureVertex *vertices,
|
||||
if (tex_handle == COGL_INVALID_HANDLE)
|
||||
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 defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2)
|
||||
|
55
clutter/cogl/cogl/cogl-sub-texture-private.h
Normal file
55
clutter/cogl/cogl/cogl-sub-texture-private.h
Normal 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 */
|
539
clutter/cogl/cogl/cogl-sub-texture.c
Normal file
539
clutter/cogl/cogl/cogl-sub-texture.c
Normal 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
|
||||
};
|
69
clutter/cogl/cogl/cogl-texture-2d-private.h
Normal file
69
clutter/cogl/cogl/cogl-texture-2d-private.h
Normal 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 */
|
@ -49,15 +49,30 @@ struct _CoglTexturePixel
|
||||
struct _CoglTexture2DSliced
|
||||
{
|
||||
CoglTexture _parent;
|
||||
GArray *slice_x_spans;
|
||||
GArray *slice_y_spans;
|
||||
GArray *slice_gl_handles;
|
||||
gint max_waste;
|
||||
GArray *slice_x_spans;
|
||||
GArray *slice_y_spans;
|
||||
GArray *slice_gl_handles;
|
||||
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
|
||||
used to force an automatic update of the mipmaps when
|
||||
glGenerateMipmap is not available. */
|
||||
CoglTexturePixel *first_pixels;
|
||||
CoglTexturePixel *first_pixels;
|
||||
};
|
||||
|
||||
GQuark
|
||||
@ -69,12 +84,6 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width,
|
||||
CoglTextureFlags flags,
|
||||
CoglPixelFormat internal_format);
|
||||
|
||||
CoglHandle
|
||||
_cogl_texture_2d_sliced_new_from_file (const gchar *filename,
|
||||
CoglTextureFlags flags,
|
||||
CoglPixelFormat internal_format,
|
||||
GError **error);
|
||||
|
||||
CoglHandle
|
||||
_cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle,
|
||||
GLenum gl_target,
|
||||
@ -84,16 +93,6 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle,
|
||||
GLuint y_pot_waste,
|
||||
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
|
||||
_cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle,
|
||||
CoglTextureFlags flags,
|
||||
|
File diff suppressed because it is too large
Load Diff
630
clutter/cogl/cogl/cogl-texture-2d.c
Normal file
630
clutter/cogl/cogl/cogl-texture-2d.c
Normal 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
|
||||
};
|
@ -57,7 +57,8 @@ _cogl_texture_driver_prep_gl_for_pixels_upload (int pixels_rowstride,
|
||||
* XXX: sorry for the ridiculous number of arguments :-(
|
||||
*/
|
||||
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_y,
|
||||
int dst_x,
|
||||
@ -66,8 +67,21 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
|
||||
int height,
|
||||
CoglBitmap *source_bmp,
|
||||
GLuint source_gl_format,
|
||||
GLuint source_gl_type,
|
||||
GLuint gl_handle);
|
||||
GLuint source_gl_type);
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -30,16 +30,19 @@
|
||||
|
||||
#define COGL_TEXTURE(tex) ((CoglTexture *)(tex))
|
||||
|
||||
typedef struct _CoglTexture CoglTexture;
|
||||
typedef struct _CoglTextureVtable CoglTextureVtable;
|
||||
typedef struct _CoglTexture CoglTexture;
|
||||
typedef struct _CoglTextureVtable CoglTextureVtable;
|
||||
|
||||
typedef void (*CoglTextureSliceCallback) (CoglHandle handle,
|
||||
GLuint gl_handle,
|
||||
GLenum gl_target,
|
||||
float *slice_coords,
|
||||
float *virtual_coords,
|
||||
const float *slice_coords,
|
||||
const float *virtual_coords,
|
||||
void *user_data);
|
||||
|
||||
typedef void (* CoglTextureManualRepeatCallback) (const float *coords,
|
||||
void *user_data);
|
||||
|
||||
struct _CoglTextureVtable
|
||||
{
|
||||
/* Virtual functions that must be implemented for a texture
|
||||
@ -80,6 +83,8 @@ struct _CoglTextureVtable
|
||||
void (* transform_coords_to_gl) (CoglTexture *tex,
|
||||
float *s,
|
||||
float *t);
|
||||
gboolean (* transform_quad_coords_to_gl) (CoglTexture *tex,
|
||||
float *coords);
|
||||
|
||||
gboolean (* get_gl_texture) (CoglTexture *tex,
|
||||
GLuint *out_gl_handle,
|
||||
@ -90,27 +95,21 @@ struct _CoglTextureVtable
|
||||
GLenum mag_filter);
|
||||
|
||||
void (* ensure_mipmaps) (CoglTexture *tex);
|
||||
void (* ensure_non_quad_rendering) (CoglTexture *tex);
|
||||
|
||||
void (* set_wrap_mode_parameter) (CoglTexture *tex,
|
||||
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
|
||||
{
|
||||
CoglHandleObject _parent;
|
||||
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
|
||||
@ -129,8 +128,12 @@ void
|
||||
_cogl_texture_transform_coords_to_gl (CoglHandle handle,
|
||||
float *s,
|
||||
float *t);
|
||||
gboolean
|
||||
_cogl_texture_transform_quad_coords_to_gl (CoglHandle handle,
|
||||
float *coords);
|
||||
|
||||
GLenum
|
||||
_cogl_texture_get_internal_gl_format (CoglHandle handle);
|
||||
_cogl_texture_get_gl_format (CoglHandle handle);
|
||||
|
||||
void
|
||||
_cogl_texture_set_wrap_mode_parameter (CoglHandle handle,
|
||||
@ -144,23 +147,30 @@ _cogl_texture_set_filters (CoglHandle handle,
|
||||
void
|
||||
_cogl_texture_ensure_mipmaps (CoglHandle handle);
|
||||
|
||||
|
||||
/* Functions currently only used by CoglTexture implementations or
|
||||
* drivers... */
|
||||
|
||||
void
|
||||
_cogl_texture_free (CoglTexture *tex);
|
||||
_cogl_texture_ensure_non_quad_rendering (CoglHandle handle);
|
||||
|
||||
void
|
||||
_cogl_texture_bitmap_free (CoglTexture *tex);
|
||||
/* Utility function to determine which pixel format to use when
|
||||
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
|
||||
_cogl_texture_bitmap_swap (CoglTexture *tex,
|
||||
CoglBitmap *new_bitmap);
|
||||
/* Utility function to help uploading a bitmap. If the bitmap needs
|
||||
premult conversion then it will be copied and *copied_bitmap will
|
||||
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
|
||||
_cogl_texture_bitmap_prepare (CoglTexture *tex,
|
||||
CoglPixelFormat internal_format);
|
||||
_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);
|
||||
|
||||
void
|
||||
_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
|
||||
_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
|
||||
_cogl_texture_draw_and_read (CoglTexture *tex,
|
||||
_cogl_texture_draw_and_read (CoglHandle handle,
|
||||
CoglBitmap *target_bmp,
|
||||
GLuint target_gl_format,
|
||||
GLuint target_gl_type);
|
||||
|
@ -38,6 +38,9 @@
|
||||
#include "cogl-texture-private.h"
|
||||
#include "cogl-texture-driver.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-context.h"
|
||||
#include "cogl-handle.h"
|
||||
@ -62,8 +65,10 @@ cogl_is_texture (CoglHandle handle)
|
||||
if (handle == COGL_INVALID_HANDLE)
|
||||
return FALSE;
|
||||
|
||||
return obj->klass->type == _cogl_handle_texture_2d_sliced_get_type ();
|
||||
//|| obj->klass->type == _cogl_handle_texture_3d_get_type ();
|
||||
return (obj->klass->type == _cogl_handle_texture_2d_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
|
||||
@ -95,25 +100,86 @@ cogl_texture_unref (CoglHandle handle)
|
||||
cogl_handle_unref (handle);
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_texture_bitmap_free (CoglTexture *tex)
|
||||
static gboolean
|
||||
_cogl_texture_needs_premult_conversion (CoglPixelFormat src_format,
|
||||
CoglPixelFormat dst_format)
|
||||
{
|
||||
if (tex->bitmap.data != NULL && tex->bitmap_owner)
|
||||
g_free (tex->bitmap.data);
|
||||
|
||||
tex->bitmap.data = NULL;
|
||||
tex->bitmap_owner = FALSE;
|
||||
return ((src_format & COGL_A_BIT) &&
|
||||
src_format != COGL_PIXEL_FORMAT_A_8 &&
|
||||
(src_format & COGL_PREMULT_BIT) !=
|
||||
(dst_format & COGL_PREMULT_BIT));
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_texture_bitmap_swap (CoglTexture *tex,
|
||||
CoglBitmap *new_bitmap)
|
||||
CoglPixelFormat
|
||||
_cogl_texture_determine_internal_format (CoglPixelFormat src_format,
|
||||
CoglPixelFormat dst_format)
|
||||
{
|
||||
if (tex->bitmap.data != NULL && tex->bitmap_owner)
|
||||
g_free (tex->bitmap.data);
|
||||
/* If the application hasn't specified a specific format then we'll
|
||||
* 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;
|
||||
tex->bitmap_owner = TRUE;
|
||||
gboolean
|
||||
_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
|
||||
@ -152,53 +218,99 @@ _cogl_texture_set_wrap_mode_parameter (CoglHandle handle,
|
||||
tex->vtable->set_wrap_mode_parameter (tex, wrap_mode);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_cogl_texture_bitmap_prepare (CoglTexture *tex,
|
||||
CoglPixelFormat internal_format)
|
||||
/* This is like CoglSpanIter except it deals with floats and it
|
||||
effectively assumes there is only one span from 0.0 to 1.0 */
|
||||
typedef struct _CoglTextureIter
|
||||
{
|
||||
CoglBitmap new_bitmap;
|
||||
CoglPixelFormat new_data_format;
|
||||
gboolean success;
|
||||
gfloat pos, end, next_pos;
|
||||
gboolean flipped;
|
||||
gfloat t_1, t_2;
|
||||
} CoglTextureIter;
|
||||
|
||||
/* Was there any internal conversion requested?
|
||||
* By default Cogl will use a premultiplied internal format. Later we will
|
||||
* add control over this. */
|
||||
if (internal_format == COGL_PIXEL_FORMAT_ANY)
|
||||
static void
|
||||
_cogl_texture_iter_update (CoglTextureIter *iter)
|
||||
{
|
||||
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) &&
|
||||
tex->bitmap.format != COGL_PIXEL_FORMAT_A_8)
|
||||
internal_format = tex->bitmap.format | COGL_PREMULT_BIT;
|
||||
else
|
||||
internal_format = tex->bitmap.format;
|
||||
iter->t_1 = t_2;
|
||||
iter->t_2 = iter->pos;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
else
|
||||
{
|
||||
success = _cogl_bitmap_convert_and_premult (&tex->bitmap,
|
||||
&new_bitmap,
|
||||
new_data_format);
|
||||
|
||||
if (!success)
|
||||
return FALSE;
|
||||
|
||||
/* Update texture with new data */
|
||||
_cogl_texture_bitmap_swap (tex, &new_bitmap);
|
||||
iter->t_1 = iter->pos;
|
||||
iter->t_2 = t_2;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_texture_free (CoglTexture *tex)
|
||||
static void
|
||||
_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
|
||||
@ -207,10 +319,19 @@ cogl_texture_new_with_size (guint width,
|
||||
CoglTextureFlags flags,
|
||||
CoglPixelFormat internal_format)
|
||||
{
|
||||
return _cogl_texture_2d_sliced_new_with_size (width,
|
||||
height,
|
||||
flags,
|
||||
internal_format);
|
||||
CoglHandle tex;
|
||||
|
||||
/* First try creating a fast-path non-sliced texture */
|
||||
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
|
||||
@ -222,13 +343,26 @@ cogl_texture_new_from_data (guint width,
|
||||
guint rowstride,
|
||||
const guchar *data)
|
||||
{
|
||||
return _cogl_texture_2d_sliced_new_from_data (width,
|
||||
height,
|
||||
flags,
|
||||
format,
|
||||
internal_format,
|
||||
rowstride,
|
||||
data);
|
||||
CoglBitmap bitmap;
|
||||
|
||||
if (format == COGL_PIXEL_FORMAT_ANY)
|
||||
return COGL_INVALID_HANDLE;
|
||||
|
||||
if (data == NULL)
|
||||
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
|
||||
@ -236,6 +370,21 @@ cogl_texture_new_from_bitmap (CoglHandle bmp_handle,
|
||||
CoglTextureFlags flags,
|
||||
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,
|
||||
flags,
|
||||
internal_format);
|
||||
@ -247,10 +396,31 @@ cogl_texture_new_from_file (const gchar *filename,
|
||||
CoglPixelFormat internal_format,
|
||||
GError **error)
|
||||
{
|
||||
return _cogl_texture_2d_sliced_new_from_file (filename,
|
||||
flags,
|
||||
internal_format,
|
||||
error);
|
||||
CoglHandle bmp_handle;
|
||||
CoglBitmap *bmp;
|
||||
CoglHandle handle = COGL_INVALID_HANDLE;
|
||||
|
||||
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
|
||||
@ -271,6 +441,17 @@ cogl_texture_new_from_foreign (GLuint gl_handle,
|
||||
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
|
||||
cogl_texture_get_width (CoglHandle handle)
|
||||
{
|
||||
@ -281,7 +462,7 @@ cogl_texture_get_width (CoglHandle handle)
|
||||
|
||||
tex = COGL_TEXTURE (handle);
|
||||
|
||||
return tex->bitmap.width;
|
||||
return tex->vtable->get_width (tex);
|
||||
}
|
||||
|
||||
guint
|
||||
@ -294,7 +475,7 @@ cogl_texture_get_height (CoglHandle handle)
|
||||
|
||||
tex = COGL_TEXTURE (handle);
|
||||
|
||||
return tex->bitmap.height;
|
||||
return tex->vtable->get_height (tex);
|
||||
}
|
||||
|
||||
CoglPixelFormat
|
||||
@ -307,7 +488,7 @@ cogl_texture_get_format (CoglHandle handle)
|
||||
|
||||
tex = COGL_TEXTURE (handle);
|
||||
|
||||
return tex->bitmap.format;
|
||||
return tex->vtable->get_format (tex);
|
||||
}
|
||||
|
||||
guint
|
||||
@ -318,9 +499,15 @@ cogl_texture_get_rowstride (CoglHandle handle)
|
||||
if (!cogl_is_texture (handle))
|
||||
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);
|
||||
|
||||
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
|
||||
@ -387,12 +574,6 @@ _cogl_texture_can_hardware_repeat (CoglHandle 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);
|
||||
}
|
||||
|
||||
@ -409,12 +590,21 @@ _cogl_texture_transform_coords_to_gl (CoglHandle handle,
|
||||
tex->vtable->transform_coords_to_gl (tex, s, t);
|
||||
}
|
||||
|
||||
GLenum
|
||||
_cogl_texture_get_internal_gl_format (CoglHandle handle)
|
||||
gboolean
|
||||
_cogl_texture_transform_quad_coords_to_gl (CoglHandle handle,
|
||||
float *coords)
|
||||
{
|
||||
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
|
||||
@ -460,6 +650,19 @@ _cogl_texture_ensure_mipmaps (CoglHandle handle)
|
||||
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
|
||||
cogl_texture_set_region (CoglHandle handle,
|
||||
gint src_x,
|
||||
@ -505,7 +708,7 @@ cogl_texture_set_region (CoglHandle handle,
|
||||
* glGetTexImage, but may be used as a fallback in some circumstances.
|
||||
*/
|
||||
static void
|
||||
do_texture_draw_and_read (CoglTexture *tex,
|
||||
do_texture_draw_and_read (CoglHandle handle,
|
||||
CoglBitmap *target_bmp,
|
||||
GLint *viewport)
|
||||
{
|
||||
@ -516,16 +719,18 @@ do_texture_draw_and_read (CoglTexture *tex,
|
||||
float tx2, ty2;
|
||||
int bw, bh;
|
||||
CoglBitmap rect_bmp;
|
||||
CoglHandle handle;
|
||||
guint tex_width, tex_height;
|
||||
|
||||
handle = (CoglHandle) tex;
|
||||
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;
|
||||
ty1 = 0; ty2 = 0;
|
||||
|
||||
/* 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 */
|
||||
ry1 = ry2;
|
||||
@ -533,13 +738,13 @@ do_texture_draw_and_read (CoglTexture *tex,
|
||||
|
||||
/* Normalized texture Y coords */
|
||||
ty1 = ty2;
|
||||
ty2 = (ry2 / (float)tex->bitmap.height);
|
||||
ty2 = (ry2 / (float) tex_height);
|
||||
|
||||
rx1 = 0; rx2 = 0;
|
||||
tx1 = 0; tx2 = 0;
|
||||
|
||||
/* 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 */
|
||||
rx1 = rx2;
|
||||
@ -547,7 +752,7 @@ do_texture_draw_and_read (CoglTexture *tex,
|
||||
|
||||
/* Normalized texture X coords */
|
||||
tx1 = tx2;
|
||||
tx2 = (rx2 / (float)tex->bitmap.width);
|
||||
tx2 = (rx2 / (float) tex_width);
|
||||
|
||||
/* Draw a portion of texture */
|
||||
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.
|
||||
*/
|
||||
gboolean
|
||||
_cogl_texture_draw_and_read (CoglTexture *tex,
|
||||
_cogl_texture_draw_and_read (CoglHandle handle,
|
||||
CoglBitmap *target_bmp,
|
||||
GLuint target_gl_format,
|
||||
GLuint target_gl_type)
|
||||
@ -648,14 +853,14 @@ _cogl_texture_draw_and_read (CoglTexture *tex,
|
||||
prev_source = cogl_handle_ref (ctx->source_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,
|
||||
0, /* layer */
|
||||
"RGBA = REPLACE (TEXTURE)",
|
||||
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 */
|
||||
/* 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 ("B bits: %d\n", b_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 *dstdata;
|
||||
@ -692,7 +897,7 @@ _cogl_texture_draw_and_read (CoglTexture *tex,
|
||||
"RGBA = REPLACE (TEXTURE[A])",
|
||||
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 */
|
||||
srcdata = alpha_bmp.data;
|
||||
|
@ -317,6 +317,33 @@ gboolean cogl_texture_set_region (CoglHandle handle,
|
||||
guint rowstride,
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -291,7 +291,8 @@ struct _CoglTextureVertex
|
||||
typedef enum {
|
||||
COGL_TEXTURE_NONE = 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;
|
||||
|
||||
/**
|
||||
|
@ -1637,6 +1637,11 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
|
||||
if (tex_handle == COGL_INVALID_HANDLE)
|
||||
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))
|
||||
{
|
||||
g_warning ("Disabling layer %d of the current source material, "
|
||||
|
@ -102,7 +102,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride,
|
||||
}
|
||||
|
||||
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_y,
|
||||
int dst_x,
|
||||
@ -111,8 +112,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
|
||||
int height,
|
||||
CoglBitmap *source_bmp,
|
||||
GLuint source_gl_format,
|
||||
GLuint source_gl_type,
|
||||
GLuint gl_handle)
|
||||
GLuint source_gl_type)
|
||||
{
|
||||
int bpp = _cogl_get_format_bpp (source_bmp->format);
|
||||
|
||||
@ -122,11 +122,11 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
|
||||
src_y,
|
||||
bpp);
|
||||
|
||||
/* Upload new image data */
|
||||
GE( _cogl_texture_driver_bind (tex->gl_target,
|
||||
gl_handle, tex->gl_intformat) );
|
||||
/* 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( glTexSubImage2D (tex->gl_target, 0,
|
||||
GE( glTexSubImage2D (gl_target, 0,
|
||||
dst_x, dst_y,
|
||||
width, height,
|
||||
source_gl_format,
|
||||
@ -134,6 +134,32 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
|
||||
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
|
||||
_cogl_texture_driver_gl_get_tex_image (GLenum gl_target,
|
||||
GLenum dest_gl_format,
|
||||
|
@ -71,7 +71,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride,
|
||||
}
|
||||
|
||||
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_y,
|
||||
int dst_x,
|
||||
@ -80,8 +81,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
|
||||
int height,
|
||||
CoglBitmap *source_bmp,
|
||||
GLuint source_gl_format,
|
||||
GLuint source_gl_type,
|
||||
GLuint gl_handle)
|
||||
GLuint source_gl_type)
|
||||
{
|
||||
int bpp = _cogl_get_format_bpp (source_bmp->format);
|
||||
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 */
|
||||
|
||||
/* Setup temp bitmap for slice subregion */
|
||||
slice_bmp.format = tex->bitmap.format;
|
||||
slice_bmp.format = source_bmp->format;
|
||||
slice_bmp.width = width;
|
||||
slice_bmp.height = height;
|
||||
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.height);
|
||||
|
||||
/* Upload new image data */
|
||||
GE( _cogl_texture_driver_bind (tex->gl_target,
|
||||
gl_handle, tex->gl_intformat) );
|
||||
/* 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( glTexSubImage2D (tex->gl_target, 0,
|
||||
GE( glTexSubImage2D (gl_target, 0,
|
||||
dst_x, dst_y,
|
||||
width, height,
|
||||
source_gl_format,
|
||||
@ -128,6 +128,53 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex,
|
||||
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
|
||||
* fallback to a generic render + readpixels approach to downloading
|
||||
* texture data. (See _cogl_texture_draw_and_read() ) */
|
||||
|
@ -192,6 +192,7 @@ cogl_texture_new_from_file
|
||||
cogl_texture_new_from_data
|
||||
cogl_texture_new_from_foreign
|
||||
cogl_texture_new_from_bitmap
|
||||
cogl_texture_new_from_sub_texture
|
||||
cogl_is_texture
|
||||
cogl_texture_ref
|
||||
cogl_texture_unref
|
||||
|
@ -41,6 +41,7 @@ test_conformance_SOURCES = \
|
||||
test-group.c \
|
||||
test-actor-size.c \
|
||||
test-texture-fbo.c \
|
||||
test-cogl-sub-texture.c \
|
||||
test-script-parser.c \
|
||||
test-actor-destroy.c \
|
||||
test-behaviours.c \
|
||||
|
@ -157,7 +157,7 @@ make_texture (void)
|
||||
|
||||
tex = cogl_texture_new_from_data (TEXTURE_SIZE,
|
||||
TEXTURE_SIZE,
|
||||
COGL_TEXTURE_NONE,
|
||||
COGL_TEXTURE_NO_ATLAS,
|
||||
COGL_PIXEL_FORMAT_RGBA_8888,
|
||||
COGL_PIXEL_FORMAT_ANY,
|
||||
TEXTURE_SIZE * 4,
|
||||
|
351
tests/conform/test-cogl-sub-texture.c
Normal file
351
tests/conform/test-cogl-sub-texture.c
Normal 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");
|
||||
}
|
||||
|
@ -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_multitexture);
|
||||
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_interleved);
|
||||
|
Loading…
Reference in New Issue
Block a user