mutter/clutter/clutter-texture.c

1489 lines
37 KiB
C
Raw Normal View History

2006-05-29 08:59:36 +00:00
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Matthew Allum <mallum@openedhand.com>
*
* Copyright (C) 2006 OpenedHand
*
* 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.
*/
/**
* SECTION:clutter-texture
* @short_description: An actor for displaying and manipulating images.
*
* #ClutterTexture is a base class for displaying and manipulating pixel
* buffer type data.
*
* The #clutter_texture_set_from_data and #clutter_texture_set_pixbuf are
* used to copy image data into texture memory and subsequently realize the
* the texture. Unrealizing/hiding frees image data from texture memory moving
* to main system memory. Re-realizing then performs the opposite operation.
* This process allows basic management of commonly limited available texture
* memory.
*/
2006-05-29 08:59:36 +00:00
#include "clutter-texture.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
#include "clutter-feature.h"
#include "clutter-util.h"
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com> * configure.ac: Enable debug messages also when --enable-debug is set to "minimum". * clutter/Makefile.am: * clutter/clutter-debug.h: Move all debugging macros inside this private header; make all debug macros depend on the CLUTTER_ENABLE_DEBUG compile time define, controlled by the --enable-debug configure switch; add G_LOG_DOMAIN define. * clutter/clutter-main.c: Clean up the debug stuff; add command line argument parsing using GOption; the debug messages now are triggered like this: CLUTTER_DEBUG=section:section:... clutter-app or like this: clutter-app --clutter-debug=section:section:... where "section" is one of the sections listed in clutter-main.c, or "all", for all sections; each section is bound to a flag, which can be used to define a domain when adding a debug note using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is just a wrapper around that, under the CLUTTER_DEBUG_MISC domain; CLUTTER_NOTE() is used like this: CLUTTER_NOTE (DOMAIN, log-function); where log function is g_printerr(), g_message(), g_warning(), g_critical() or directly g_log() - for instance: CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph)); will print the warning only if the "pango" flag has been set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug command line argument. similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps command line switch; also, the --display and --screen command line switches have been added: the first overrides the DISPLAY envvar and the second controls the X screen used by Clutter to get the root window on the display. * clutter/clutter-main.h: * clutter/clutter-main.c: Add extended support for GOption in Clutter; use clutter_init_with_args() to let Clutter parse your own command line arguments; use instead clutter_get_option_group() to get the GOptionGroup used by Clutter if you want to do the parsing yourself with g_option_context_parse(). The init sequence has been verified, updated and moved into common functions where possible. * clutter/pango/pangoclutter-render.c: * clutter/*.c: Include "clutter-debug.h" where needed; use CLUTTER_NOTE() instead of CLUTTER_DBG(). * examples/super-oh.c: Use the new clutter_init_with_args() function, and add a --num-hands command line switch to the SuperOH example code controlling the number of hands at runtime.
2006-11-21 21:27:53 +00:00
#include "clutter-private.h"
#include "clutter-debug.h"
2006-05-29 08:59:36 +00:00
#include <GL/gl.h>
G_DEFINE_TYPE (ClutterTexture, clutter_texture, CLUTTER_TYPE_ACTOR);
2006-05-29 08:59:36 +00:00
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define PIXEL_TYPE GL_UNSIGNED_BYTE
#else
#define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
#endif
typedef struct {
gint pos;
gint size;
gint waste;
} ClutterTextureTileDimension;
2006-05-29 08:59:36 +00:00
struct _ClutterTexturePrivate
2006-05-29 08:59:36 +00:00
{
gint width;
gint height;
2006-05-29 08:59:36 +00:00
GLenum pixel_format;
GLenum pixel_type;
GLenum target_type;
GdkPixbuf *local_pixbuf; /* non video memory copy */
guint sync_actor_size : 1;
gint max_tile_waste;
guint filter_quality;
guint repeat_x : 1; /* non working */
guint repeat_y : 1; /* non working */
2006-05-29 08:59:36 +00:00
guint is_tiled : 1;
ClutterTextureTileDimension *x_tiles;
ClutterTextureTileDimension *y_tiles;
gint n_x_tiles;
gint n_y_tiles;
GLuint *tiles;
2006-05-29 08:59:36 +00:00
};
enum
{
PROP_0,
PROP_PIXBUF,
PROP_USE_TILES,
PROP_MAX_TILE_WASTE,
PROP_PIXEL_TYPE, /* Texture type */
PROP_PIXEL_FORMAT, /* Texture format */
PROP_SYNC_SIZE,
PROP_REPEAT_Y,
PROP_REPEAT_X,
PROP_FILTER_QUALITY
2006-05-29 08:59:36 +00:00
};
enum
{
SIZE_CHANGE,
PIXBUF_CHANGE,
2006-05-29 08:59:36 +00:00
LAST_SIGNAL
};
static int texture_signals[LAST_SIGNAL] = { 0 };
static gboolean
can_create (int width,
int height,
GLenum pixel_format,
GLenum pixel_type)
{
GLint new_width = 0;
2006-05-29 08:59:36 +00:00
CLUTTER_NOTE (TEXTURE, "checking %ix%i", width, height);
2006-05-29 08:59:36 +00:00
glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA,
width, height, 0 /* border */,
pixel_format, pixel_type, NULL);
CLUTTER_GLERR();
2006-05-29 08:59:36 +00:00
glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0,
GL_TEXTURE_WIDTH, &new_width);
return new_width != 0;
}
static gboolean
can_create_rect_arb (int width,
int height,
GLenum pixel_format,
GLenum pixel_type)
{
/* FIXME: How to correctly query what max size of NPOTS text can be */
if (width > 4096 || height > 4096)
return FALSE;
return TRUE;
}
2006-05-29 08:59:36 +00:00
static int
tile_dimension (int to_fill,
int start_size,
int waste,
ClutterTextureTileDimension *tiles)
2006-05-29 08:59:36 +00:00
{
int pos = 0;
int n_tiles = 0;
int size = start_size;
while (TRUE)
{
if (tiles)
{
tiles[n_tiles].pos = pos;
tiles[n_tiles].size = size;
tiles[n_tiles].waste = 0;
}
n_tiles++;
if (to_fill <= size)
{
if (tiles)
tiles[n_tiles-1].waste = size - to_fill;
break;
}
else
{
to_fill -= size; pos += size;
while (size >= 2 * to_fill || size - to_fill > waste)
2006-05-29 08:59:36 +00:00
size /= 2;
}
}
return n_tiles;
}
static void
texture_init_tiles (ClutterTexture *texture)
2006-05-29 08:59:36 +00:00
{
ClutterTexturePrivate *priv;
gint x_pot, y_pot;
priv = texture->priv;
x_pot = clutter_util_next_p2 (priv->width);
y_pot = clutter_util_next_p2 (priv->height);
2006-05-29 08:59:36 +00:00
while (!(can_create (x_pot, y_pot, priv->pixel_format, priv->pixel_type)
&& (x_pot - priv->width < priv->max_tile_waste)
&& (y_pot - priv->height < priv->max_tile_waste)))
{
CLUTTER_NOTE (TEXTURE, "x_pot:%i - width:%i < max_waste:%i",
x_pot,
priv->width,
priv->max_tile_waste);
CLUTTER_NOTE (TEXTURE, "y_pot:%i - height:%i < max_waste:%i",
y_pot,
priv->height,
priv->max_tile_waste);
if (x_pot > y_pot)
x_pot /= 2;
else
y_pot /= 2;
}
2006-05-29 08:59:36 +00:00
if (priv->x_tiles)
g_free(priv->x_tiles);
priv->n_x_tiles = tile_dimension (priv->width, x_pot,
priv->max_tile_waste, NULL);
priv->x_tiles = g_new (ClutterTextureTileDimension, priv->n_x_tiles);
tile_dimension (priv->width, x_pot, priv->max_tile_waste, priv->x_tiles);
2006-05-29 08:59:36 +00:00
if (priv->y_tiles)
g_free(priv->y_tiles);
priv->n_y_tiles = tile_dimension (priv->height, y_pot,
priv->max_tile_waste, NULL);
priv->y_tiles = g_new (ClutterTextureTileDimension, priv->n_y_tiles);
tile_dimension (priv->height, y_pot, priv->max_tile_waste, priv->y_tiles);
2006-05-29 08:59:36 +00:00
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com> * configure.ac: Enable debug messages also when --enable-debug is set to "minimum". * clutter/Makefile.am: * clutter/clutter-debug.h: Move all debugging macros inside this private header; make all debug macros depend on the CLUTTER_ENABLE_DEBUG compile time define, controlled by the --enable-debug configure switch; add G_LOG_DOMAIN define. * clutter/clutter-main.c: Clean up the debug stuff; add command line argument parsing using GOption; the debug messages now are triggered like this: CLUTTER_DEBUG=section:section:... clutter-app or like this: clutter-app --clutter-debug=section:section:... where "section" is one of the sections listed in clutter-main.c, or "all", for all sections; each section is bound to a flag, which can be used to define a domain when adding a debug note using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is just a wrapper around that, under the CLUTTER_DEBUG_MISC domain; CLUTTER_NOTE() is used like this: CLUTTER_NOTE (DOMAIN, log-function); where log function is g_printerr(), g_message(), g_warning(), g_critical() or directly g_log() - for instance: CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph)); will print the warning only if the "pango" flag has been set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug command line argument. similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps command line switch; also, the --display and --screen command line switches have been added: the first overrides the DISPLAY envvar and the second controls the X screen used by Clutter to get the root window on the display. * clutter/clutter-main.h: * clutter/clutter-main.c: Add extended support for GOption in Clutter; use clutter_init_with_args() to let Clutter parse your own command line arguments; use instead clutter_get_option_group() to get the GOptionGroup used by Clutter if you want to do the parsing yourself with g_option_context_parse(). The init sequence has been verified, updated and moved into common functions where possible. * clutter/pango/pangoclutter-render.c: * clutter/*.c: Include "clutter-debug.h" where needed; use CLUTTER_NOTE() instead of CLUTTER_DBG(). * examples/super-oh.c: Use the new clutter_init_with_args() function, and add a --num-hands command line switch to the SuperOH example code controlling the number of hands at runtime.
2006-11-21 21:27:53 +00:00
CLUTTER_NOTE (TEXTURE,
"x_pot:%i, width:%i, y_pot:%i, height: %i "
"max_waste:%i, n_x_tiles: %i, n_y_tiles: %i",
x_pot, priv->width, y_pot, priv->height,
priv->max_tile_waste,
priv->n_x_tiles, priv->n_y_tiles);
2006-05-29 08:59:36 +00:00
}
static void
texture_render_to_gl_quad (ClutterTexture *texture,
int x1,
int y1,
int x2,
int y2)
{
int qx1 = 0, qx2 = 0, qy1 = 0, qy2 = 0;
int qwidth = 0, qheight = 0;
int x, y, i =0, lastx = 0, lasty = 0;
float tx, ty;
ClutterTexturePrivate *priv;
priv = texture->priv;
qwidth = x2-x1;
qheight = y2-y1;
if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR(texture)))
clutter_actor_realize (CLUTTER_ACTOR(texture));
2006-05-29 08:59:36 +00:00
g_return_if_fail(priv->tiles != NULL);
/* OPT: Put in display list */
/* OPT: Optionally avoid tiling and use texture rectangles ext if
* supported.
*/
if (!priv->is_tiled)
2006-05-29 08:59:36 +00:00
{
glBindTexture(priv->target_type, priv->tiles[0]);
2006-05-29 08:59:36 +00:00
if (priv->target_type == GL_TEXTURE_2D) /* POT */
{
tx = (float) priv->width / clutter_util_next_p2 (priv->width);
ty = (float) priv->height / clutter_util_next_p2 (priv->height);
}
else
{
tx = (float) priv->width;
ty = (float) priv->height;
}
2006-05-29 08:59:36 +00:00
qx1 = x1; qx2 = x2;
qy1 = y1; qy2 = y2;
glBegin (GL_QUADS);
glTexCoord2f (tx, ty); glVertex2i (qx2, qy2);
glTexCoord2f (0, ty); glVertex2i (qx1, qy2);
glTexCoord2f (0, 0); glVertex2i (qx1, qy1);
glTexCoord2f (tx, 0); glVertex2i (qx2, qy1);
glEnd ();
return;
}
for (x=0; x < priv->n_x_tiles; x++)
{
lasty = 0;
for (y=0; y < priv->n_y_tiles; y++)
{
int actual_w, actual_h;
glBindTexture(priv->target_type, priv->tiles[i]);
2006-05-29 08:59:36 +00:00
actual_w = priv->x_tiles[x].size - priv->x_tiles[x].waste;
actual_h = priv->y_tiles[y].size - priv->y_tiles[y].waste;
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com> * configure.ac: Enable debug messages also when --enable-debug is set to "minimum". * clutter/Makefile.am: * clutter/clutter-debug.h: Move all debugging macros inside this private header; make all debug macros depend on the CLUTTER_ENABLE_DEBUG compile time define, controlled by the --enable-debug configure switch; add G_LOG_DOMAIN define. * clutter/clutter-main.c: Clean up the debug stuff; add command line argument parsing using GOption; the debug messages now are triggered like this: CLUTTER_DEBUG=section:section:... clutter-app or like this: clutter-app --clutter-debug=section:section:... where "section" is one of the sections listed in clutter-main.c, or "all", for all sections; each section is bound to a flag, which can be used to define a domain when adding a debug note using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is just a wrapper around that, under the CLUTTER_DEBUG_MISC domain; CLUTTER_NOTE() is used like this: CLUTTER_NOTE (DOMAIN, log-function); where log function is g_printerr(), g_message(), g_warning(), g_critical() or directly g_log() - for instance: CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph)); will print the warning only if the "pango" flag has been set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug command line argument. similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps command line switch; also, the --display and --screen command line switches have been added: the first overrides the DISPLAY envvar and the second controls the X screen used by Clutter to get the root window on the display. * clutter/clutter-main.h: * clutter/clutter-main.c: Add extended support for GOption in Clutter; use clutter_init_with_args() to let Clutter parse your own command line arguments; use instead clutter_get_option_group() to get the GOptionGroup used by Clutter if you want to do the parsing yourself with g_option_context_parse(). The init sequence has been verified, updated and moved into common functions where possible. * clutter/pango/pangoclutter-render.c: * clutter/*.c: Include "clutter-debug.h" where needed; use CLUTTER_NOTE() instead of CLUTTER_DBG(). * examples/super-oh.c: Use the new clutter_init_with_args() function, and add a --num-hands command line switch to the SuperOH example code controlling the number of hands at runtime.
2006-11-21 21:27:53 +00:00
CLUTTER_NOTE (TEXTURE,
"rendering text tile x: %i, y: %i - %ix%i",
x, y, actual_w, actual_h);
2006-05-29 08:59:36 +00:00
tx = (float) actual_w / priv->x_tiles[x].size;
ty = (float) actual_h / priv->y_tiles[y].size;
qx1 = x1 + lastx;
qx2 = qx1 + ((qwidth * actual_w ) / priv->width );
qy1 = y1 + lasty;
qy2 = qy1 + ((qheight * actual_h) / priv->height );
glBegin (GL_QUADS);
glTexCoord2f (tx, ty); glVertex2i (qx2, qy2);
glTexCoord2f (0, ty); glVertex2i (qx1, qy2);
glTexCoord2f (0, 0); glVertex2i (qx1, qy1);
glTexCoord2f (tx, 0); glVertex2i (qx2, qy1);
glEnd ();
lasty += (qy2 - qy1) ;
2006-05-29 08:59:36 +00:00
i++;
}
lastx += (qx2 - qx1);
2006-05-29 08:59:36 +00:00
}
}
static void
texture_free_gl_resources (ClutterTexture *texture)
2006-05-29 08:59:36 +00:00
{
ClutterTexturePrivate *priv;
priv = texture->priv;
CLUTTER_MARK();
if (priv->tiles)
{
if (!priv->is_tiled)
glDeleteTextures(1, priv->tiles);
else
glDeleteTextures(priv->n_x_tiles * priv->n_y_tiles, priv->tiles);
2006-05-29 08:59:36 +00:00
g_free(priv->tiles);
priv->tiles = NULL;
}
if (priv->x_tiles)
{
g_free(priv->x_tiles);
priv->x_tiles = NULL;
}
if (priv->y_tiles)
{
g_free(priv->y_tiles);
priv->y_tiles = NULL;
}
}
static void
texture_upload_data (ClutterTexture *texture,
const guchar *data,
gboolean has_alpha,
gint width,
gint height,
gint rowstride,
gint bpp)
2006-05-29 08:59:36 +00:00
{
ClutterTexturePrivate *priv;
gint x, y;
gint i = 0;
gboolean create_textures = FALSE;
GdkPixbuf *master_pixbuf = NULL;
2006-05-29 08:59:36 +00:00
priv = texture->priv;
g_return_if_fail (data != NULL);
2006-05-29 08:59:36 +00:00
CLUTTER_MARK();
if (!priv->is_tiled)
2006-05-29 08:59:36 +00:00
{
/* Single Texture */
2006-05-29 08:59:36 +00:00
if (!priv->tiles)
{
priv->tiles = g_new (GLuint, 1);
glGenTextures (1, priv->tiles);
create_textures = TRUE;
}
CLUTTER_NOTE (TEXTURE, "syncing for single tile");
2006-05-29 08:59:36 +00:00
glBindTexture(priv->target_type, priv->tiles[0]);
2006-05-29 08:59:36 +00:00
glTexParameteri(priv->target_type,
2006-05-29 08:59:36 +00:00
GL_TEXTURE_WRAP_S,
priv->repeat_x ? GL_REPEAT : GL_CLAMP_TO_EDGE);
2006-05-29 08:59:36 +00:00
glTexParameteri(priv->target_type,
2006-05-29 08:59:36 +00:00
GL_TEXTURE_WRAP_T,
priv->repeat_y ? GL_REPEAT : GL_CLAMP_TO_EDGE);
2006-05-29 08:59:36 +00:00
priv->filter_quality = 1;
glTexParameteri(priv->target_type, GL_TEXTURE_MAG_FILTER,
priv->filter_quality ? GL_LINEAR : GL_NEAREST);
glTexParameteri(priv->target_type, GL_TEXTURE_MIN_FILTER,
priv->filter_quality ? GL_LINEAR : GL_NEAREST);
2006-05-29 08:59:36 +00:00
glPixelStorei (GL_UNPACK_ROW_LENGTH, priv->width);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
2006-05-29 08:59:36 +00:00
if (create_textures)
{
gint width, height;
width = priv->width;
height = priv->height;
if (priv->target_type == GL_TEXTURE_2D) /* POT */
{
width = clutter_util_next_p2(priv->width);
height = clutter_util_next_p2(priv->height);
}
glTexImage2D(priv->target_type,
2006-05-29 08:59:36 +00:00
0,
/* (has_alpha) ? GL_RGBA : GL_RGB, */
GL_RGBA,
width,
height,
2006-05-29 08:59:36 +00:00
0,
priv->pixel_format,
priv->pixel_type,
NULL);
}
glTexSubImage2D (priv->target_type, 0, 0, 0,
2006-05-29 08:59:36 +00:00
priv->width,
priv->height,
priv->pixel_format,
priv->pixel_type,
data);
2006-05-29 08:59:36 +00:00
return;
}
/* Multiple tiled texture */
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com> * configure.ac: Enable debug messages also when --enable-debug is set to "minimum". * clutter/Makefile.am: * clutter/clutter-debug.h: Move all debugging macros inside this private header; make all debug macros depend on the CLUTTER_ENABLE_DEBUG compile time define, controlled by the --enable-debug configure switch; add G_LOG_DOMAIN define. * clutter/clutter-main.c: Clean up the debug stuff; add command line argument parsing using GOption; the debug messages now are triggered like this: CLUTTER_DEBUG=section:section:... clutter-app or like this: clutter-app --clutter-debug=section:section:... where "section" is one of the sections listed in clutter-main.c, or "all", for all sections; each section is bound to a flag, which can be used to define a domain when adding a debug note using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is just a wrapper around that, under the CLUTTER_DEBUG_MISC domain; CLUTTER_NOTE() is used like this: CLUTTER_NOTE (DOMAIN, log-function); where log function is g_printerr(), g_message(), g_warning(), g_critical() or directly g_log() - for instance: CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph)); will print the warning only if the "pango" flag has been set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug command line argument. similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps command line switch; also, the --display and --screen command line switches have been added: the first overrides the DISPLAY envvar and the second controls the X screen used by Clutter to get the root window on the display. * clutter/clutter-main.h: * clutter/clutter-main.c: Add extended support for GOption in Clutter; use clutter_init_with_args() to let Clutter parse your own command line arguments; use instead clutter_get_option_group() to get the GOptionGroup used by Clutter if you want to do the parsing yourself with g_option_context_parse(). The init sequence has been verified, updated and moved into common functions where possible. * clutter/pango/pangoclutter-render.c: * clutter/*.c: Include "clutter-debug.h" where needed; use CLUTTER_NOTE() instead of CLUTTER_DBG(). * examples/super-oh.c: Use the new clutter_init_with_args() function, and add a --num-hands command line switch to the SuperOH example code controlling the number of hands at runtime.
2006-11-21 21:27:53 +00:00
CLUTTER_NOTE (TEXTURE,
"syncing for multiple tiles for %ix%i pixbuf",
priv->width, priv->height);
2006-05-29 08:59:36 +00:00
g_return_if_fail (priv->x_tiles != NULL && priv->y_tiles != NULL);
master_pixbuf = gdk_pixbuf_new_from_data (data,
GDK_COLORSPACE_RGB,
has_alpha,
8,
width, height, rowstride,
NULL, NULL);
2006-05-29 08:59:36 +00:00
if (priv->tiles == NULL)
{
priv->tiles = g_new (GLuint, priv->n_x_tiles * priv->n_y_tiles);
glGenTextures (priv->n_x_tiles * priv->n_y_tiles, priv->tiles);
create_textures = TRUE;
}
for (x = 0; x < priv->n_x_tiles; x++)
for (y = 0; y < priv->n_y_tiles; y++)
2006-05-29 08:59:36 +00:00
{
GdkPixbuf *pixtmp;
gint src_h, src_w;
2006-05-29 08:59:36 +00:00
src_w = priv->x_tiles[x].size;
src_h = priv->y_tiles[y].size;
pixtmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
has_alpha,
8,
src_w, src_h);
2006-05-29 08:59:36 +00:00
/* clip */
if (priv->x_tiles[x].pos + src_w > priv->width)
src_w = priv->width - priv->x_tiles[x].pos;
2006-05-29 08:59:36 +00:00
if (priv->y_tiles[y].pos + src_h > priv->height)
src_h = priv->height - priv->y_tiles[y].pos;
2006-05-29 08:59:36 +00:00
gdk_pixbuf_copy_area (master_pixbuf,
priv->x_tiles[x].pos,
priv->y_tiles[y].pos,
src_w,
src_h,
pixtmp,
0, 0);
2006-05-29 08:59:36 +00:00
#ifdef CLUTTER_DUMP_TILES
{
gchar *filename;
filename = g_strdup_printf("/tmp/%i-%i-%i.png",
clutter_actor_get_id(CLUTTER_ACTOR(texture)),
2006-05-29 08:59:36 +00:00
x, y);
printf("saving %s\n", filename);
gdk_pixbuf_save (pixtmp, filename , "png", NULL, NULL);
}
#endif
glBindTexture(priv->target_type, priv->tiles[i]);
2006-05-29 08:59:36 +00:00
glTexParameteri(priv->target_type,
2006-05-29 08:59:36 +00:00
GL_TEXTURE_WRAP_S,
priv->repeat_x ? GL_REPEAT : GL_CLAMP_TO_EDGE);
2006-05-29 08:59:36 +00:00
glTexParameteri(priv->target_type,
2006-05-29 08:59:36 +00:00
GL_TEXTURE_WRAP_T,
priv->repeat_y ? GL_REPEAT : GL_CLAMP_TO_EDGE);
2006-05-29 08:59:36 +00:00
glTexParameteri(priv->target_type, GL_TEXTURE_MAG_FILTER,
priv->filter_quality ? GL_LINEAR : GL_NEAREST);
glTexParameteri(priv->target_type, GL_TEXTURE_MIN_FILTER,
priv->filter_quality ? GL_LINEAR : GL_NEAREST);
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
2006-05-29 08:59:36 +00:00
if (create_textures)
{
glTexImage2D (priv->target_type, 0, GL_RGBA,
gdk_pixbuf_get_width (pixtmp),
gdk_pixbuf_get_height (pixtmp),
0,
priv->pixel_format,
priv->pixel_type,
gdk_pixbuf_get_pixels (pixtmp));
2006-05-29 08:59:36 +00:00
}
else
{
/* Textures already created, so just update whats inside
*/
glTexSubImage2D (priv->target_type, 0,
2006-05-29 08:59:36 +00:00
0, 0,
gdk_pixbuf_get_width (pixtmp),
gdk_pixbuf_get_height (pixtmp),
priv->pixel_format,
2006-05-29 08:59:36 +00:00
priv->pixel_type,
gdk_pixbuf_get_pixels (pixtmp));
2006-05-29 08:59:36 +00:00
}
g_object_unref (pixtmp);
2006-05-29 08:59:36 +00:00
i++;
}
g_object_unref (master_pixbuf);
2006-05-29 08:59:36 +00:00
}
static void
clutter_texture_unrealize (ClutterActor *actor)
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
texture = CLUTTER_TEXTURE(actor);
priv = texture->priv;
if (priv->tiles == NULL)
return;
CLUTTER_MARK();
/* Move image data from video to main memory
*/
if (priv->local_pixbuf == NULL)
priv->local_pixbuf = clutter_texture_get_pixbuf (texture);
texture_free_gl_resources (texture);
CLUTTER_NOTE (TEXTURE, "Texture unrealized");
}
2006-05-29 08:59:36 +00:00
static void
clutter_texture_realize (ClutterActor *actor)
2006-05-29 08:59:36 +00:00
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
2006-05-29 08:59:36 +00:00
texture = CLUTTER_TEXTURE(actor);
priv = texture->priv;
2006-05-29 08:59:36 +00:00
CLUTTER_MARK();
if (priv->local_pixbuf != NULL)
{
/* Move any local image data we have from unrealization
* back into video memory.
*/
clutter_texture_set_pixbuf (texture, priv->local_pixbuf);
g_object_unref (priv->local_pixbuf);
priv->local_pixbuf = NULL;
}
else
2006-05-29 08:59:36 +00:00
{
/* Dont allow realization with no pixbuf - note set_pixbuf/data
* will set realize flags.
*/
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com> * configure.ac: Enable debug messages also when --enable-debug is set to "minimum". * clutter/Makefile.am: * clutter/clutter-debug.h: Move all debugging macros inside this private header; make all debug macros depend on the CLUTTER_ENABLE_DEBUG compile time define, controlled by the --enable-debug configure switch; add G_LOG_DOMAIN define. * clutter/clutter-main.c: Clean up the debug stuff; add command line argument parsing using GOption; the debug messages now are triggered like this: CLUTTER_DEBUG=section:section:... clutter-app or like this: clutter-app --clutter-debug=section:section:... where "section" is one of the sections listed in clutter-main.c, or "all", for all sections; each section is bound to a flag, which can be used to define a domain when adding a debug note using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is just a wrapper around that, under the CLUTTER_DEBUG_MISC domain; CLUTTER_NOTE() is used like this: CLUTTER_NOTE (DOMAIN, log-function); where log function is g_printerr(), g_message(), g_warning(), g_critical() or directly g_log() - for instance: CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph)); will print the warning only if the "pango" flag has been set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug command line argument. similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps command line switch; also, the --display and --screen command line switches have been added: the first overrides the DISPLAY envvar and the second controls the X screen used by Clutter to get the root window on the display. * clutter/clutter-main.h: * clutter/clutter-main.c: Add extended support for GOption in Clutter; use clutter_init_with_args() to let Clutter parse your own command line arguments; use instead clutter_get_option_group() to get the GOptionGroup used by Clutter if you want to do the parsing yourself with g_option_context_parse(). The init sequence has been verified, updated and moved into common functions where possible. * clutter/pango/pangoclutter-render.c: * clutter/*.c: Include "clutter-debug.h" where needed; use CLUTTER_NOTE() instead of CLUTTER_DBG(). * examples/super-oh.c: Use the new clutter_init_with_args() function, and add a --num-hands command line switch to the SuperOH example code controlling the number of hands at runtime.
2006-11-21 21:27:53 +00:00
CLUTTER_NOTE (TEXTURE,
"Texture has no image data cannot realize");
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com> * configure.ac: Enable debug messages also when --enable-debug is set to "minimum". * clutter/Makefile.am: * clutter/clutter-debug.h: Move all debugging macros inside this private header; make all debug macros depend on the CLUTTER_ENABLE_DEBUG compile time define, controlled by the --enable-debug configure switch; add G_LOG_DOMAIN define. * clutter/clutter-main.c: Clean up the debug stuff; add command line argument parsing using GOption; the debug messages now are triggered like this: CLUTTER_DEBUG=section:section:... clutter-app or like this: clutter-app --clutter-debug=section:section:... where "section" is one of the sections listed in clutter-main.c, or "all", for all sections; each section is bound to a flag, which can be used to define a domain when adding a debug note using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is just a wrapper around that, under the CLUTTER_DEBUG_MISC domain; CLUTTER_NOTE() is used like this: CLUTTER_NOTE (DOMAIN, log-function); where log function is g_printerr(), g_message(), g_warning(), g_critical() or directly g_log() - for instance: CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph)); will print the warning only if the "pango" flag has been set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug command line argument. similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps command line switch; also, the --display and --screen command line switches have been added: the first overrides the DISPLAY envvar and the second controls the X screen used by Clutter to get the root window on the display. * clutter/clutter-main.h: * clutter/clutter-main.c: Add extended support for GOption in Clutter; use clutter_init_with_args() to let Clutter parse your own command line arguments; use instead clutter_get_option_group() to get the GOptionGroup used by Clutter if you want to do the parsing yourself with g_option_context_parse(). The init sequence has been verified, updated and moved into common functions where possible. * clutter/pango/pangoclutter-render.c: * clutter/*.c: Include "clutter-debug.h" where needed; use CLUTTER_NOTE() instead of CLUTTER_DBG(). * examples/super-oh.c: Use the new clutter_init_with_args() function, and add a --num-hands command line switch to the SuperOH example code controlling the number of hands at runtime.
2006-11-21 21:27:53 +00:00
CLUTTER_NOTE (TEXTURE, "flags %i", actor->flags);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
CLUTTER_NOTE (TEXTURE, "flags %i", actor->flags);
2006-05-29 08:59:36 +00:00
return;
}
CLUTTER_NOTE (TEXTURE, "Texture realized");
2006-05-29 08:59:36 +00:00
}
static void
clutter_texture_show (ClutterActor *self)
2006-05-29 08:59:36 +00:00
{
2006-12-12 Emmanuele Bassi <ebassi@openedhand.com> Rework part of the show/hide machinery. Allow groups sub-classes and composite actors to override show_all/hide_all in order to decide which children they wish to show/hide. This means that if an actor overrides the default show/hide virtual methods, it'll have to chain up to the parent class show/hide. While we're at it, provide the fully recursive clutter_actor_show_all() and clutter_actor_hide_all() methods. * clutter/clutter-behaviour-path.c: Add apidoc for the ClutterKnot functions; add pathological equality case for clutter_knot_equal(). * clutter/clutter-event.h: * clutter/clutter-feature.h: * clutter/clutter-behaviour.c: * clutter/clutter-behaviour-scale.c:Fix parameters name so that gtk-doc doesn't complain. * clutter/clutter-actor.c: * clutter/clutter-event.c: Add apidoc * clutter/clutter-actor.h: * clutter/clutter-actor.c: Add a clutter_actor_show_all() and a clutter_actor_hide_all() functions; provide a mechanism for groups and composited actors to programmatically select what to show/hide when clutter_actor_show_all() and clutter_actor_hide_all() are called. If you are overriding the ClutterActor::show or the ClutterActor::hide virtual methods you should chain up with the parent class. * clutter/clutter-group.c: Override show_all and hide_all and recursively show/hide every child inside the group; clutter_group_show_all() and clutter_group_hide_all() remain as non recursive versions of clutter_actor_show_all() and clutter_actor_hide_all() (maybe we should rename them in order to avoid name clashes with the bindings). * clutter/clutter-stage.c: * clutter/clutter-texture.c: Chain up with parent class show and hide vfuncs. * clutter/clutter-clone-texture.h: * clutter/clutter-clone-texture.c: Provide API for changing the parent texture of a clone texture actor. * examples/behave.c: * examples/super-oh.c: * examples/test.c: Use clutter_actor_show_all() instead of clutter_group_show_all().
2006-12-12 20:20:04 +00:00
ClutterActorClass *parent_class;
/* chain up parent show */
parent_class = CLUTTER_ACTOR_CLASS (clutter_texture_parent_class);
if (parent_class->show)
parent_class->show (self);
clutter_actor_realize (self);
2006-05-29 08:59:36 +00:00
}
static void
clutter_texture_hide (ClutterActor *self)
2006-05-29 08:59:36 +00:00
{
2006-12-12 Emmanuele Bassi <ebassi@openedhand.com> Rework part of the show/hide machinery. Allow groups sub-classes and composite actors to override show_all/hide_all in order to decide which children they wish to show/hide. This means that if an actor overrides the default show/hide virtual methods, it'll have to chain up to the parent class show/hide. While we're at it, provide the fully recursive clutter_actor_show_all() and clutter_actor_hide_all() methods. * clutter/clutter-behaviour-path.c: Add apidoc for the ClutterKnot functions; add pathological equality case for clutter_knot_equal(). * clutter/clutter-event.h: * clutter/clutter-feature.h: * clutter/clutter-behaviour.c: * clutter/clutter-behaviour-scale.c:Fix parameters name so that gtk-doc doesn't complain. * clutter/clutter-actor.c: * clutter/clutter-event.c: Add apidoc * clutter/clutter-actor.h: * clutter/clutter-actor.c: Add a clutter_actor_show_all() and a clutter_actor_hide_all() functions; provide a mechanism for groups and composited actors to programmatically select what to show/hide when clutter_actor_show_all() and clutter_actor_hide_all() are called. If you are overriding the ClutterActor::show or the ClutterActor::hide virtual methods you should chain up with the parent class. * clutter/clutter-group.c: Override show_all and hide_all and recursively show/hide every child inside the group; clutter_group_show_all() and clutter_group_hide_all() remain as non recursive versions of clutter_actor_show_all() and clutter_actor_hide_all() (maybe we should rename them in order to avoid name clashes with the bindings). * clutter/clutter-stage.c: * clutter/clutter-texture.c: Chain up with parent class show and hide vfuncs. * clutter/clutter-clone-texture.h: * clutter/clutter-clone-texture.c: Provide API for changing the parent texture of a clone texture actor. * examples/behave.c: * examples/super-oh.c: * examples/test.c: Use clutter_actor_show_all() instead of clutter_group_show_all().
2006-12-12 20:20:04 +00:00
ClutterActorClass *parent_class;
/* chain up parent hide */
parent_class = CLUTTER_ACTOR_CLASS (clutter_texture_parent_class);
if (parent_class->hide)
parent_class->hide (self);
clutter_actor_unrealize (self);
2006-05-29 08:59:36 +00:00
}
static void
clutter_texture_paint (ClutterActor *self)
2006-05-29 08:59:36 +00:00
{
ClutterTexture *texture = CLUTTER_TEXTURE (self);
2006-05-29 08:59:36 +00:00
gint x1, y1, x2, y2;
guint8 opacity;
2006-05-29 08:59:36 +00:00
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com> * configure.ac: Enable debug messages also when --enable-debug is set to "minimum". * clutter/Makefile.am: * clutter/clutter-debug.h: Move all debugging macros inside this private header; make all debug macros depend on the CLUTTER_ENABLE_DEBUG compile time define, controlled by the --enable-debug configure switch; add G_LOG_DOMAIN define. * clutter/clutter-main.c: Clean up the debug stuff; add command line argument parsing using GOption; the debug messages now are triggered like this: CLUTTER_DEBUG=section:section:... clutter-app or like this: clutter-app --clutter-debug=section:section:... where "section" is one of the sections listed in clutter-main.c, or "all", for all sections; each section is bound to a flag, which can be used to define a domain when adding a debug note using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is just a wrapper around that, under the CLUTTER_DEBUG_MISC domain; CLUTTER_NOTE() is used like this: CLUTTER_NOTE (DOMAIN, log-function); where log function is g_printerr(), g_message(), g_warning(), g_critical() or directly g_log() - for instance: CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph)); will print the warning only if the "pango" flag has been set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug command line argument. similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps command line switch; also, the --display and --screen command line switches have been added: the first overrides the DISPLAY envvar and the second controls the X screen used by Clutter to get the root window on the display. * clutter/clutter-main.h: * clutter/clutter-main.c: Add extended support for GOption in Clutter; use clutter_init_with_args() to let Clutter parse your own command line arguments; use instead clutter_get_option_group() to get the GOptionGroup used by Clutter if you want to do the parsing yourself with g_option_context_parse(). The init sequence has been verified, updated and moved into common functions where possible. * clutter/pango/pangoclutter-render.c: * clutter/*.c: Include "clutter-debug.h" where needed; use CLUTTER_NOTE() instead of CLUTTER_DBG(). * examples/super-oh.c: Use the new clutter_init_with_args() function, and add a --num-hands command line switch to the SuperOH example code controlling the number of hands at runtime.
2006-11-21 21:27:53 +00:00
CLUTTER_NOTE (PAINT,
"@@@ for '%s' @@@",
clutter_actor_get_name (self) ? clutter_actor_get_name (self)
: "unknown");
glPushMatrix ();
2006-05-29 08:59:36 +00:00
glEnable (GL_BLEND);
glEnable (texture->priv->target_type);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2006-05-29 08:59:36 +00:00
opacity = clutter_actor_get_opacity (self);
CLUTTER_NOTE (PAINT, "setting opacity to %i\n", opacity);
glColor4ub (255, 255, 255, opacity);
2006-05-29 08:59:36 +00:00
clutter_actor_get_coords (self, &x1, &y1, &x2, &y2);
/* Paint will of translated us */
texture_render_to_gl_quad (texture, 0, 0, x2 - x1, y2 - y1);
2006-05-29 08:59:36 +00:00
glDisable (texture->priv->target_type);
glDisable (GL_BLEND);
2006-05-29 08:59:36 +00:00
glPopMatrix ();
2006-05-29 08:59:36 +00:00
}
static void
clutter_texture_dispose (GObject *object)
{
ClutterTexture *texture = CLUTTER_TEXTURE(object);
2006-05-29 08:59:36 +00:00
ClutterTexturePrivate *priv;
priv = texture->priv;
2006-05-29 08:59:36 +00:00
if (priv != NULL)
{
texture_free_gl_resources (texture);
2006-05-29 08:59:36 +00:00
if (priv->local_pixbuf != NULL)
2006-05-29 08:59:36 +00:00
{
g_object_unref (priv->local_pixbuf);
priv->local_pixbuf = NULL;
2006-05-29 08:59:36 +00:00
}
}
G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
}
static void
clutter_texture_finalize (GObject *object)
{
ClutterTexture *self = CLUTTER_TEXTURE(object);
if (self->priv)
{
g_free(self->priv);
self->priv = NULL;
}
G_OBJECT_CLASS (clutter_texture_parent_class)->finalize (object);
}
static void
clutter_texture_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
texture = CLUTTER_TEXTURE(object);
priv = texture->priv;
switch (prop_id)
{
case PROP_PIXBUF:
clutter_texture_set_pixbuf (texture,
(GdkPixbuf*)g_value_get_pointer(value));
break;
case PROP_USE_TILES:
priv->is_tiled = g_value_get_boolean (value);
if (priv->target_type == GL_TEXTURE_RECTANGLE_ARB && priv->is_tiled)
priv->target_type = GL_TEXTURE_2D;
CLUTTER_NOTE (TEXTURE, "Texture is tiled ? %s",
priv->is_tiled ? "yes" : "no");
2006-05-29 08:59:36 +00:00
break;
case PROP_MAX_TILE_WASTE:
priv->max_tile_waste = g_value_get_int (value);
2006-05-29 08:59:36 +00:00
break;
case PROP_PIXEL_TYPE:
priv->pixel_type = g_value_get_int (value);
break;
case PROP_PIXEL_FORMAT:
priv->pixel_format = g_value_get_int (value);
break;
case PROP_SYNC_SIZE:
priv->sync_actor_size = g_value_get_boolean (value);
2006-05-29 08:59:36 +00:00
break;
case PROP_REPEAT_X:
priv->repeat_x = g_value_get_boolean (value);
break;
case PROP_REPEAT_Y:
priv->repeat_y = g_value_get_boolean (value);
break;
case PROP_FILTER_QUALITY:
priv->filter_quality = g_value_get_int (value);
break;
2006-05-29 08:59:36 +00:00
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clutter_texture_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
texture = CLUTTER_TEXTURE(object);
priv = texture->priv;
switch (prop_id)
{
case PROP_PIXBUF:
{
GdkPixbuf *pixb;
pixb = clutter_texture_get_pixbuf (texture);
g_value_set_pointer (value, pixb);
}
2006-05-29 08:59:36 +00:00
break;
case PROP_USE_TILES:
g_value_set_boolean (value, priv->is_tiled);
2006-05-29 08:59:36 +00:00
break;
case PROP_MAX_TILE_WASTE:
g_value_set_int (value, priv->max_tile_waste);
2006-05-29 08:59:36 +00:00
break;
case PROP_PIXEL_TYPE:
g_value_set_int (value, priv->pixel_type);
break;
case PROP_PIXEL_FORMAT:
g_value_set_int (value, priv->pixel_format);
break;
case PROP_SYNC_SIZE:
g_value_set_boolean (value, priv->sync_actor_size);
2006-05-29 08:59:36 +00:00
break;
case PROP_REPEAT_X:
g_value_set_boolean (value, priv->repeat_x);
break;
case PROP_REPEAT_Y:
g_value_set_boolean (value, priv->repeat_y);
break;
case PROP_FILTER_QUALITY:
g_value_set_int (value, priv->filter_quality);
break;
2006-05-29 08:59:36 +00:00
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clutter_texture_class_init (ClutterTextureClass *klass)
{
GObjectClass *gobject_class;
ClutterActorClass *actor_class;
2006-05-29 08:59:36 +00:00
gobject_class = (GObjectClass*)klass;
actor_class = (ClutterActorClass*)klass;
2006-05-29 08:59:36 +00:00
actor_class->paint = clutter_texture_paint;
actor_class->realize = clutter_texture_realize;
actor_class->unrealize = clutter_texture_unrealize;
actor_class->show = clutter_texture_show;
actor_class->hide = clutter_texture_hide;
2006-05-29 08:59:36 +00:00
gobject_class->dispose = clutter_texture_dispose;
gobject_class->finalize = clutter_texture_finalize;
gobject_class->set_property = clutter_texture_set_property;
gobject_class->get_property = clutter_texture_get_property;
g_object_class_install_property
(gobject_class, PROP_PIXBUF,
g_param_spec_pointer ("pixbuf",
"Pixbuf source for Texture.",
"Pixbuf source for Texture.",
CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
g_object_class_install_property
(gobject_class, PROP_USE_TILES,
g_param_spec_boolean ("tiled",
"Enable use of tiled textures",
"Enables the use of tiled GL textures to more "
"efficiently use available texture memory",
/* FIXME: This default set at runtime :/
* As tiling depends on what GL features available.
* Need to figure out better solution
*/
(clutter_feature_available
(CLUTTER_FEATURE_TEXTURE_RECTANGLE) == FALSE),
G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
g_object_class_install_property
(gobject_class, PROP_SYNC_SIZE,
g_param_spec_boolean ("sync-size",
"Sync size of actor",
"Auto sync size of actor to underlying pixbuf"
2006-05-29 08:59:36 +00:00
"dimentions",
TRUE,
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
g_object_class_install_property
(gobject_class, PROP_REPEAT_X,
g_param_spec_boolean ("repeat-x",
"Tile underlying pixbuf in x direction",
"Reapeat underlying pixbuf rather than scale"
"in x direction. Currently UNWORKING",
2006-05-29 08:59:36 +00:00
FALSE,
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
g_object_class_install_property
(gobject_class, PROP_REPEAT_Y,
g_param_spec_boolean ("repeat-y",
"Tile underlying pixbuf in y direction",
"Reapeat underlying pixbuf rather than scale"
"in y direction. Currently UNWORKING",
2006-05-29 08:59:36 +00:00
FALSE,
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
/* FIXME: Ideally this option needs to have some kind of global
* overide as to imporve performance.
*/
g_object_class_install_property
(gobject_class, PROP_FILTER_QUALITY,
g_param_spec_int ("filter-quality",
"Quality of filter used when scaling a texture",
"Values 0 and 1 current only supported, with 0"
"being lower quality but fast, 1 being better "
"quality but slower. ( Currently just maps to "
" GL_NEAREST / GL_LINEAR )",
0,
G_MAXINT,
1,
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
g_object_class_install_property
(gobject_class, PROP_MAX_TILE_WASTE,
g_param_spec_int ("tile-waste",
"Tile dimention to waste",
"Max wastage dimention of a texture when using "
"tiled textures. Bigger values use less textures, "
2006-05-29 08:59:36 +00:00
"smaller values less texture memory. ",
0,
G_MAXINT,
64,
G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
g_object_class_install_property
(gobject_class, PROP_PIXEL_TYPE,
g_param_spec_int ("pixel-type",
"Texture Pixel Type",
"GL texture pixel type used",
0,
G_MAXINT,
PIXEL_TYPE,
G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
g_object_class_install_property
(gobject_class, PROP_PIXEL_FORMAT,
g_param_spec_int ("pixel-format",
"Texture pixel format",
"GL texture pixel format used",
0,
G_MAXINT,
GL_RGBA,
G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE));
2006-05-29 08:59:36 +00:00
/**
* ClutterTexture::size-change:
* @texture: the texture which received the signal
* @width: the width of the new texture
* @height: the height of the new texture
*
* The ::size-change signal is emitted each time the size of the
* pixbuf used by @texture changes. The new size is given as
* argument to the callback.
*/
texture_signals[SIZE_CHANGE] =
2006-05-29 08:59:36 +00:00
g_signal_new ("size-change",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterTextureClass, size_change),
NULL, NULL,
clutter_marshal_VOID__INT_INT,
G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_INT);
/**
* ClutterTexture::pixbuf-change:
* @texture: the texture which received the signal
*
* The ::pixbuf-change signal is emitted each time the pixbuf
* used by @texture changes.
*/
texture_signals[PIXBUF_CHANGE] =
2006-05-29 08:59:36 +00:00
g_signal_new ("pixbuf-change",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterTextureClass, pixbuf_change),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
}
static void
clutter_texture_init (ClutterTexture *self)
{
ClutterTexturePrivate *priv;
priv = g_new0 (ClutterTexturePrivate, 1);
priv->max_tile_waste = 64;
priv->filter_quality = 0;
priv->is_tiled = TRUE;
priv->pixel_type = PIXEL_TYPE;
priv->pixel_format = GL_RGBA;
priv->repeat_x = FALSE;
priv->repeat_y = FALSE;
2006-05-29 08:59:36 +00:00
if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE))
{
priv->target_type = GL_TEXTURE_RECTANGLE_ARB;
priv->is_tiled = FALSE;
}
else
priv->target_type = GL_TEXTURE_2D;
2006-05-29 08:59:36 +00:00
self->priv = priv;
}
static void
pixbuf_destroy_notify (guchar *pixels, gpointer data)
{
g_free (pixels);
}
2006-05-29 08:59:36 +00:00
/**
* clutter_texture_get_pixbuf:
* @texture: A #ClutterTexture
2006-05-29 08:59:36 +00:00
*
* Gets a #GdkPixbuf representation of the #ClutterTexture data.
* The created #GdkPixbuf is not owned by the texture but the caller.
2006-05-29 08:59:36 +00:00
*
* Return value: A #GdkPixbuf
2006-05-29 08:59:36 +00:00
**/
GdkPixbuf*
clutter_texture_get_pixbuf (ClutterTexture* texture)
{
ClutterTexturePrivate *priv;
GdkPixbuf *pixbuf = NULL;
guchar *pixels = NULL;
int bpp = 4;
priv = texture->priv;
if (priv->tiles == NULL)
return NULL;
if (priv->pixel_format == GL_RGB)
bpp = 3;
if (!priv->is_tiled)
{
pixels = g_malloc (((priv->width * bpp + 3) &~ 3) * priv->height);
if (!pixels)
return NULL;
glBindTexture(priv->target_type, priv->tiles[0]);
glPixelStorei (GL_UNPACK_ROW_LENGTH, priv->width);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
/* read data from gl text and return as pixbuf */
glGetTexImage (priv->target_type,
0,
priv->pixel_format,
priv->pixel_type,
(GLvoid*)pixels);
pixbuf = gdk_pixbuf_new_from_data ((const guchar*)pixels,
GDK_COLORSPACE_RGB,
(priv->pixel_format == GL_RGBA),
8,
priv->width,
priv->height,
((priv->width * bpp + 3) &~ 3),
pixbuf_destroy_notify,
NULL);
}
else
{
int x,y,i;
i = 0;
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
(priv->pixel_format == GL_RGBA),
8,
priv->width,
priv->height);
for (x = 0; x < priv->n_x_tiles; x++)
for (y = 0; y < priv->n_y_tiles; y++)
{
GdkPixbuf *tmp_pixb;
gint src_h, src_w;
src_w = priv->x_tiles[x].size;
src_h = priv->y_tiles[y].size;
pixels = g_malloc (((src_w * bpp) &~ 3) * src_h);
glBindTexture(priv->target_type, priv->tiles[i]);
glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
glGetTexImage (priv->target_type,
0,
priv->pixel_format,
priv->pixel_type,
(GLvoid *) pixels);
/* Clip */
if (priv->x_tiles[x].pos + src_w > priv->width)
src_w = priv->width - priv->x_tiles[x].pos;
if (priv->y_tiles[y].pos + src_h > priv->height)
src_h = priv->height = priv->y_tiles[y].pos;
tmp_pixb =
gdk_pixbuf_new_from_data ((const guchar*)pixels,
GDK_COLORSPACE_RGB,
(priv->pixel_format == GL_RGBA),
8,
src_w,
src_h,
((src_w * bpp + 3) &~ 3),
pixbuf_destroy_notify,
NULL);
gdk_pixbuf_copy_area (tmp_pixb,
0,
0,
src_w,
src_h,
pixbuf,
priv->x_tiles[x].pos,
priv->x_tiles[y].pos);
g_object_unref (tmp_pixb);
i++;
}
}
return pixbuf;
2006-05-29 08:59:36 +00:00
}
/**
* clutter_texture_set_from_data:
* @texture: A #ClutterTexture
* @data: Image data in RGB type colorspace.
* @has_alpha: Set to TRUE if image data has a alpha channel.
* @width: Width in pixels of image data.
* @height: Height in pixels of image data
* @rowstride: Distance in bytes between row starts.
* @bpp: bytes per pixel ( Currently only 4 supported )
2006-05-29 08:59:36 +00:00
*
* Sets #ClutterTexture image data.
2006-05-29 08:59:36 +00:00
*
* Since 0.2. This function is likely to change in future versions.
2006-05-29 08:59:36 +00:00
**/
void
clutter_texture_set_from_data (ClutterTexture *texture,
const guchar *data,
gboolean has_alpha,
gint width,
gint height,
gint rowstride,
gint bpp)
2006-05-29 08:59:36 +00:00
{
ClutterTexturePrivate *priv;
gboolean texture_dirty = TRUE;
priv = texture->priv;
g_return_if_fail (data != NULL);
g_return_if_fail (bpp == 4);
2006-05-29 08:59:36 +00:00
/* FIXME: check other image props */
texture_dirty = (width != priv->width || height != priv->height);
2006-05-29 08:59:36 +00:00
priv->width = width;
priv->height = height;
2006-05-29 08:59:36 +00:00
if (has_alpha)
2006-05-29 08:59:36 +00:00
priv->pixel_format = GL_RGBA;
else
priv->pixel_format = GL_RGB;
2006-05-29 08:59:36 +00:00
if (texture_dirty)
{
texture_free_gl_resources (texture);
if (priv->is_tiled == FALSE)
{
if (priv->target_type == GL_TEXTURE_RECTANGLE_ARB
&& !can_create_rect_arb (priv->width,
priv->height,
priv->pixel_format,
priv->pixel_type))
{
/* If we cant create NPOT tex of this size fall back to tiles */
priv->is_tiled = TRUE;
priv->target_type = GL_TEXTURE_2D;
}
else if (priv->target_type == GL_TEXTURE_2D
&& !can_create(clutter_util_next_p2(priv->width),
clutter_util_next_p2(priv->height),
priv->pixel_format,
priv->pixel_type))
{
priv->is_tiled = TRUE;
}
}
2006-05-29 08:59:36 +00:00
/* Figure our tiling etc */
if (priv->is_tiled)
texture_init_tiles (texture);
}
2006-05-29 08:59:36 +00:00
CLUTTER_NOTE (TEXTURE, "set size %ix%i\n",
priv->width,
priv->height);
2006-05-29 08:59:36 +00:00
texture_upload_data (texture, data, has_alpha,
width, height, rowstride, bpp);
2006-05-29 08:59:36 +00:00
CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR (texture), CLUTTER_ACTOR_REALIZED);
2006-05-29 08:59:36 +00:00
if (texture_dirty)
{
g_signal_emit (texture, texture_signals[SIZE_CHANGE],
0, priv->width, priv->height);
if (priv->sync_actor_size)
clutter_actor_set_size (CLUTTER_ACTOR(texture),
priv->width,
priv->height);
}
/* rename signal */
g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0);
2006-05-29 08:59:36 +00:00
/* If resized actor may need resizing but paint() will do this */
if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR(texture)))
clutter_actor_queue_redraw (CLUTTER_ACTOR(texture));
}
/**
* clutter_texture_set_pixbuf:
* @texture: A #ClutterTexture
* @pixbuf: A #GdkPixbuf
*
* Sets a #ClutterTexture image data from a #GdkPixbuf
*
**/
void
clutter_texture_set_pixbuf (ClutterTexture *texture,
GdkPixbuf *pixbuf)
{
ClutterTexturePrivate *priv;
priv = texture->priv;
g_return_if_fail (pixbuf != NULL);
clutter_texture_set_from_data (texture,
gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf),
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
gdk_pixbuf_get_rowstride (pixbuf),
4);
2006-05-29 08:59:36 +00:00
}
/**
* clutter_texture_new_from_pixbuf:
2006-05-29 08:59:36 +00:00
* @pixbuf: A #GdkPixbuf
*
* Creates a new #ClutterTexture object.
*
* Return value: A newly created #ClutterTexture object.
**/
ClutterActor*
2006-05-29 08:59:36 +00:00
clutter_texture_new_from_pixbuf (GdkPixbuf *pixbuf)
{
ClutterTexture *texture;
texture = g_object_new (CLUTTER_TYPE_TEXTURE, "pixbuf", pixbuf, NULL);
return CLUTTER_ACTOR(texture);
2006-05-29 08:59:36 +00:00
}
/**
* clutter_texture_new:
2006-05-29 08:59:36 +00:00
*
* Creates a new empty #ClutterTexture object.
*
* Return value: A newly created #ClutterTexture object.
**/
ClutterActor *
2006-05-29 08:59:36 +00:00
clutter_texture_new (void)
{
return g_object_new (CLUTTER_TYPE_TEXTURE, NULL);
2006-05-29 08:59:36 +00:00
}
/**
* clutter_texture_get_base_size:
2006-05-29 08:59:36 +00:00
* @texture: A #ClutterTexture
* @width: Pointer to gint to be populated with width value if non NULL.
* @height: Pointer to gint to be populated with height value if non NULL.
*
* Gets the size in pixels of the untransformed underlying texture pixbuf data.
*
**/
void /* FIXME: rename to get_image_size */
2006-05-29 08:59:36 +00:00
clutter_texture_get_base_size (ClutterTexture *texture,
gint *width,
gint *height)
{
/* Attempt to realize, mainly for subclasses ( such as labels )
* which maynot create pixbuf data and thus base size until
* realization happens.
*/
if (!CLUTTER_ACTOR_IS_REALIZED(CLUTTER_ACTOR(texture)))
clutter_actor_realize (CLUTTER_ACTOR(texture));
2006-05-29 08:59:36 +00:00
if (width)
*width = texture->priv->width;
if (height)
*height = texture->priv->height;
}
/**
* clutter_texture_bind_tile:
* @texture: A #ClutterTexture
2006-05-29 08:59:36 +00:00
* @index: Tile index to bind
*
* Proxys a call to glBindTexture a to bind an internal 'tile'.
*
* This function is only useful for sub class implementations
* and never should be called by an application.
**/
void
clutter_texture_bind_tile (ClutterTexture *texture, gint index)
{
ClutterTexturePrivate *priv;
priv = texture->priv;
glBindTexture(priv->target_type, priv->tiles[index]);
2006-05-29 08:59:36 +00:00
}
/**
* clutter_texture_get_n_tiles:
* @texture: A #ClutterTexture
2006-05-29 08:59:36 +00:00
* @n_x_tiles: Location to store number of tiles in horizonally axis
* @n_y_tiles: Location to store number of tiles in vertical axis
*
* Retreives internal tile dimentioning.
*
* This function is only useful for sub class implementations
* and never should be called by an application.
**/
void
clutter_texture_get_n_tiles (ClutterTexture *texture,
gint *n_x_tiles,
gint *n_y_tiles)
{
if (n_x_tiles)
*n_x_tiles = texture->priv->n_x_tiles;
if (n_y_tiles)
*n_y_tiles = texture->priv->n_y_tiles;
}
/**
* clutter_texture_get_x_tile_detail:
* @texture: A #ClutterTexture
2006-05-29 08:59:36 +00:00
* @x_index: X index of tile to query
* @pos: Location to store tiles X position
* @size: Location to store tiles horizontal size in pixels
* @waste: Location to store tiles horizontal wastage in pixels
*
* Retreives details of a tile on x axis.
*
* This function is only useful for sub class implementations
* and never should be called by an application.
**/
void
clutter_texture_get_x_tile_detail (ClutterTexture *texture,
gint x_index,
gint *pos,
gint *size,
gint *waste)
{
g_return_if_fail(x_index < texture->priv->n_x_tiles);
if (pos)
*pos = texture->priv->x_tiles[x_index].pos;
if (size)
*size = texture->priv->x_tiles[x_index].size;
if (waste)
*waste = texture->priv->x_tiles[x_index].waste;
}
/**
* clutter_texture_get_y_tile_detail:
* @texture: A #ClutterTexture
* @y_index: Y index of tile to query
2006-05-29 08:59:36 +00:00
* @pos: Location to store tiles Y position
* @size: Location to store tiles vertical size in pixels
* @waste: Location to store tiles vertical wastage in pixels
*
* Retreives details of a tile on y axis.
*
* This function is only useful for sub class implementations
* and never should be called by an application.
**/
void
clutter_texture_get_y_tile_detail (ClutterTexture *texture,
gint y_index,
gint *pos,
gint *size,
gint *waste)
{
ClutterTexturePrivate *priv;
g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
priv = texture->priv;
g_return_if_fail (y_index < priv->n_y_tiles);
2006-05-29 08:59:36 +00:00
if (pos)
*pos = priv->y_tiles[y_index].pos;
2006-05-29 08:59:36 +00:00
if (size)
*size = priv->y_tiles[y_index].size;
2006-05-29 08:59:36 +00:00
if (waste)
*waste = priv->y_tiles[y_index].waste;
2006-05-29 08:59:36 +00:00
}
/**
* clutter_texture_has_generated_tiles:
* @texture: A #ClutterTexture
2006-05-29 08:59:36 +00:00
*
* Checks if #ClutterTexture has generated underlying GL texture tiles.
*
* This function is only useful for sub class implementations
* and never should be called by an application.
*
* Return value: TRUE if texture has pregenerated GL tiles.
**/
gboolean
clutter_texture_has_generated_tiles (ClutterTexture *texture)
{
g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2006-05-29 08:59:36 +00:00
return (texture->priv->tiles != NULL);
}
/**
2006-06-22 13:56:04 +00:00
* clutter_texture_is_tiled:
* @texture: A #ClutterTexture
2006-05-29 08:59:36 +00:00
*
* Checks if #ClutterTexture is tiled.
*
* This function is only useful for sub class implementations
* and never should be called by an application.
*
* Return value: TRUE if texture is tiled
**/
gboolean
clutter_texture_is_tiled (ClutterTexture *texture)
{
g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
return texture->priv->is_tiled;
2006-05-29 08:59:36 +00:00
}