mutter/cltr-texture.c
2005-03-31 18:19:25 +00:00

336 lines
7.3 KiB
C

#include "cltr-texture.h"
/*
IDEAS or less memory
+ up to 4 textures tiled per image *DONE*
+ texture compression ?
+ mipmaps - make zoom faster ? ( vs memory )
+ check max texture size *DONE*
+ how much texture mem available ?
*/
static int
next_p2 ( int a )
{
int rval=1;
while(rval < a)
rval <<= 1;
return rval;
}
void
cltr_texture_render_to_gl_quad(CltrTexture *texture,
int x1,
int y1,
int x2,
int y2)
{
int qx1, qx2, qy1, qy2;
int qwidth, qheight;
int x, y, i =0, lastx = 0, lasty = 0;
float tx, ty;
qwidth = x2-x1;
qheight = y2-y1;
if (texture->tiles == NULL)
cltr_texture_realize(texture);
for (x=0; x < texture->n_x_tiles; x++)
{
lasty = 0;
for (y=0; y < texture->n_y_tiles; y++)
{
int actual_w, actual_h;
glBindTexture(GL_TEXTURE_2D, texture->tiles[i]);
actual_w = texture->tile_x_size[x] - texture->tile_x_waste[x];
actual_h = texture->tile_y_size[y] - texture->tile_y_waste[y];
tx = (float) actual_w / texture->tile_x_size[x];
ty = (float) actual_h / texture->tile_y_size[y];
qx1 = x1 + lastx;
qx2 = qx1 + ((qwidth * actual_w ) / texture->width );
qy1 = y1 + lasty;
qy2 = qy1 + ((qheight * actual_h) / texture->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;
i++;
}
lastx += qx2 - qx1;
}
}
/* Code below based heavily from luminocity - copyright Owen Taylor */
/* MAX_WASTE: The maximum dimension of blank area we'll accept
* in a pixmap. Bigger values use less textures, smaller
* values less texture memory. The current value of
* 256 means that the smallest texture we'll split to
* save texture memory is 513x512. (That will be split into
* a 512x512 and, if overlap is 32, a 64x512 texture)
*/
#define MAX_WASTE 64
/*
* OVERLAP: when we divide the full-resolution image into
* tiles to deal with hardware limitations, we overlap
* tiles by this much. This means that we can scale
* down by up to OVERLAP before we start getting
* seems.
*/
#define OVERLAP 0 /* 32 */
static gboolean
can_create (int width, int height)
{
GLint new_width;
glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA,
width, height, 0 /* border */,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0,
GL_TEXTURE_WIDTH, &new_width);
return new_width != 0;
}
static int
tile_dimension (int to_fill,
int start_size,
int *positions,
int *sizes,
int *waste)
{
int pos = 0;
int n_tiles = 0;
int size = start_size;
while (TRUE)
{
if (positions)
positions[n_tiles] = pos;
if (sizes)
sizes[n_tiles] = size;
if (waste)
waste[n_tiles] = 0;
n_tiles++;
if (to_fill <= size)
{
if (waste)
waste[n_tiles-1] = size - to_fill;
break;
}
else
{
to_fill -= (size - OVERLAP);
pos += size - OVERLAP;
while (size >= 2 * to_fill || size - to_fill > MAX_WASTE)
size /= 2;
}
}
return n_tiles;
}
static void
init_tiles (CltrTexture *texture)
{
int x_pot = next_p2 (texture->width);
int y_pot = next_p2 (texture->height);
while (!(can_create (x_pot, y_pot) &&
(x_pot - texture->width < MAX_WASTE) &&
(y_pot - texture->height < MAX_WASTE)))
{
if (x_pot > y_pot)
x_pot /= 2;
else
y_pot /= 2;
}
texture->n_x_tiles = tile_dimension (texture->width, x_pot,
NULL, NULL, NULL);
texture->tile_x_position = g_new (int, texture->n_x_tiles);
texture->tile_x_size = g_new (int, texture->n_x_tiles);
texture->tile_x_waste = g_new (int, texture->n_x_tiles);
tile_dimension (texture->width, x_pot,
texture->tile_x_position,
texture->tile_x_size,
texture->tile_x_waste);
texture->n_y_tiles = tile_dimension (texture->height, y_pot,
NULL, NULL, NULL);
texture->tile_y_position = g_new (int, texture->n_y_tiles);
texture->tile_y_size = g_new (int, texture->n_y_tiles);
texture->tile_y_waste = g_new (int, texture->n_y_tiles);
tile_dimension (texture->height, y_pot,
texture->tile_y_position,
texture->tile_y_size,
texture->tile_y_waste);
#if 0
/* debug info */
{
int i;
g_print("n_x_tiles %i, n_y_tiles %i\n",
texture->n_x_tiles, texture->n_y_tiles);
g_print ("Tiled %d x %d texture as [", texture->width, texture->height);
for (i = 0; i < texture->n_x_tiles; i++)
{
if (i != 0)
g_print (",");
g_print ("%d(%d)", texture->tile_x_size[i], texture->tile_x_position[i]);
}
g_print ("]x[");
for (i = 0; i < texture->n_y_tiles; i++)
{
if (i != 0)
g_print (",");
g_print ("%d(%d)", texture->tile_y_size[i], texture->tile_y_position[i]);
}
g_print ("]\n");
}
#endif
}
/* End borrowed luminocity code */
void
cltr_texture_unrealize(CltrTexture *texture)
{
if (texture->tiles == NULL)
return;
glDeleteTextures(texture->n_x_tiles * texture->n_y_tiles, texture->tiles);
g_free(texture->tiles);
texture->tiles = NULL;
}
void
cltr_texture_realize(CltrTexture *texture)
{
int x, y, i = 0;
texture->tiles = g_new (GLuint, texture->n_x_tiles * texture->n_y_tiles);
glGenTextures (texture->n_x_tiles * texture->n_y_tiles, texture->tiles);
for (x=0; x < texture->n_x_tiles; x++)
for (y=0; y < texture->n_y_tiles; y++)
{
Pixbuf *pixtmp;
int src_h, src_w;
pixtmp = pixbuf_new(texture->tile_x_size[x], texture->tile_y_size[y]);
src_w = texture->tile_x_size[x];
src_h = texture->tile_y_size[y];
/*
CLTR_DBG("%i+%i, %ix%i to %ix%i, waste %ix%i",
texture->tile_x_position[x],
texture->tile_y_position[y],
texture->tile_x_size[x],
texture->tile_y_size[y],
texture->width,
texture->height,
texture->tile_x_waste[x],
texture->tile_y_waste[y]);
*/
pixbuf_copy(texture->pixb,
pixtmp,
texture->tile_x_position[x],
texture->tile_y_position[y],
texture->tile_x_size[x],
texture->tile_y_size[y],
0,0);
glBindTexture(GL_TEXTURE_2D, texture->tiles[i]);
CLTR_GLERR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glTexImage2D(GL_TEXTURE_2D, 0, /*GL_COMPRESSED_RGBA_ARB*/ GL_RGBA,
pixtmp->width,
pixtmp->height,
0, GL_RGBA,
GL_UNSIGNED_INT_8_8_8_8,
pixtmp->data);
CLTR_GLERR();
pixbuf_unref(pixtmp);
i++;
}
}
CltrTexture*
cltr_texture_new(Pixbuf *pixb)
{
CltrTexture *texture;
CLTR_MARK();
texture = g_malloc0(sizeof(CltrTexture));
texture->width = pixb->width;
texture->height = pixb->height;
/* maybe we should copy the pixbuf - a change to refed one would explode */
texture->pixb = pixb;
pixbuf_ref(pixb);
init_tiles (texture);
return texture;
}