From 99bb51a5bed8cb37c47ad362298faa68da35323b Mon Sep 17 00:00:00 2001 From: Matthew Allum Date: Tue, 29 Mar 2005 23:26:36 +0000 Subject: [PATCH] Added texture tiling code --- ChangeLog | 15 +++ Makefile | 4 +- cltr-photo-grid.c | 128 +++++++------------ cltr-photo-grid.h | 4 +- cltr-tex.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++ cltr-tex.h | 12 ++ cltr.c | 12 +- cltr.h | 42 ++++++- pixbuf.c | 14 +++ pixbuf.h | 3 + 10 files changed, 447 insertions(+), 94 deletions(-) create mode 100644 cltr-tex.c create mode 100644 cltr-tex.h diff --git a/ChangeLog b/ChangeLog index 303aa0da5..980416d64 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2005-03-30 mallum,,, + + * Makefile: + * cltr-photo-grid.c: (cltr_photo_grid_populate), + (cltr_photo_grid_redraw): + * cltr-photo-grid.h: + * cltr-tex.c: + * cltr-tex.h: + * cltr.c: (main): + * cltr.h: + * pixbuf.c: (pixbuf_unref), (pixbuf_copy): + * pixbuf.h: + Add intial new texture tiling code. + 2005-03-27 mallum,,, * Makefile: @@ -10,6 +24,7 @@ (main): * cltr.h: Add a very hacky threaded image loader. + Make event handling a little more efficient, though still hacky * pixbuf.c: (pixel_set_vals), pixbuf.h: Add copy func ( unused as yet ) diff --git a/Makefile b/Makefile index 450e0647e..a380bf4c0 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,12 @@ CFLAGS=`pkg-config --cflags pangoft2 pango glib-2.0 gthread-2.0` .c.o: $(CC) -g -Wall $(CFLAGS) $(INCS) -c $*.c -OBJS=cltr.o pixbuf.o util.o fonts.o cltr-photo-grid.o +OBJS=cltr.o pixbuf.o util.o fonts.o cltr-photo-grid.o cltr-tex.o clutter: $(OBJS) $(CC) -g -Wall -o $@ $(OBJS) $(LIBS) -$(OBJS): pixbuf.h util.h fonts.h cltr.h cltr-photo-grid.h +$(OBJS): pixbuf.h util.h fonts.h cltr.h cltr-photo-grid.h cltr-tex.h clean: rm -fr *.o clutter test diff --git a/cltr-photo-grid.c b/cltr-photo-grid.c index 973e45fc7..39febefdb 100644 --- a/cltr-photo-grid.c +++ b/cltr-photo-grid.c @@ -219,8 +219,10 @@ cltr_photo_grid_populate(gpointer data) CLTR_DBG("estamited %i pixb's\n", n_pixb); + /* grid->texs = util_malloc0(sizeof(GLuint)*n_pixb); glGenTextures(n_pixb, grid->texs); + */ g_dir_rewind (dir); @@ -241,38 +243,9 @@ cltr_photo_grid_populate(gpointer data) g_snprintf(&buf[0], 24, "%i", i); font_draw(font, cell->pixb, buf, 10, 10); - cell->texref = grid->texs[i]; - g_mutex_lock(Mutex_GRID); - glBindTexture(GL_TEXTURE_2D, cell->texref); - - 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_REPLACE); - - CLTR_GLERR(); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - grid->tex_w, - grid->tex_h, - 0, GL_RGBA, - GL_UNSIGNED_INT_8_8_8_8, - grid->tex_data); - - CLTR_GLERR(); - - glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, - (GLsizei)cell->pixb->width, - (GLsizei)cell->pixb->height, - GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, - cell->pixb->data); - - CLTR_GLERR(); + cell->img = cltr_image_new(cell->pixb); g_mutex_unlock(Mutex_GRID); @@ -312,25 +285,40 @@ cltr_photo_grid_redraw(ClutterPhotoGrid *grid) glClear(GL_COLOR_BUFFER_BIT); - glClearColor( 0.6, 0.6, 0.62, 1.0); - if (grid->cells_tail == NULL) { - glPopMatrix(); - glXSwapBuffers(CltrCntx.xdpy, grid->parent->xwin); - return; + /* No pictures to paint yet */ + glColor3f(0.6, 0.6, 0.62); + glRecti(0, 0, 640, 480); + + glPopMatrix(); + glXSwapBuffers(CltrCntx.xdpy, grid->parent->xwin); + return; } - glEnable(GL_TEXTURE_2D); + /* + * Using GL_POLYGON_SMOOTH with 'regular' alpha blends causes ugly seems + * in the textures and texture tile borders. We therefore do this 'saturate' + * trick painting front -> back. + * + * see http://blog.metawrap.com/blog/PermaLink.aspx?guid=db82f92e-9fc8-4635-b3e5-e37a1ca6ee0a + * for more info + * + * Is there a better way.? + */ + + glClearColor( 0.0, 0.0, 0.0, 0.0 ); /* needed for saturate to work */ + + glEnable(GL_BLEND); + + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); /* needed */ + + glEnable(GL_POLYGON_SMOOTH); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnable(GL_POLYGON_SMOOTH); + glBlendFunc(GL_SRC_ALPHA_SATURATE,GL_ONE); /* Assume zoomed out */ zoom = grid->zoom_min; @@ -456,11 +444,6 @@ cltr_photo_grid_redraw(ClutterPhotoGrid *grid) thumb_w -= (2 * ew_border); thumb_h -= (2 * ns_border); - /* - x1 = x + ew_border; - y1 = y + ns_border; - */ - x1 = x + ((grid->cell_width - thumb_w)/2); y1 = y + ((grid->cell_height - thumb_h)/2); @@ -479,8 +462,20 @@ cltr_photo_grid_redraw(ClutterPhotoGrid *grid) /* Rotate around Z axis */ glRotatef ( cell->angle, 0.0, 0.0, 1.0); - /* Border - why need tex disabled ? */ + glEnable(GL_TEXTURE_2D); + + g_mutex_lock(Mutex_GRID); + + cltr_image_render_to_gl_quad(cell->img, + -(thumb_w/2), + -(thumb_h/2), + (thumb_w/2), + (thumb_h/2)); + + g_mutex_unlock(Mutex_GRID); + glDisable(GL_TEXTURE_2D); + if (cell_item == grid->cell_active && grid->state == CLTR_PHOTO_GRID_STATE_BROWSE) glColor4f(1.0, 1.0, 1.0, 1.0); @@ -493,35 +488,6 @@ cltr_photo_grid_redraw(ClutterPhotoGrid *grid) (thumb_w/2)+4, (thumb_h/2)+ns_border); glEnable(GL_TEXTURE_2D); - glEnable(GL_SMOOTH); - - g_mutex_lock(Mutex_GRID); - - glBindTexture(GL_TEXTURE_2D, cell->texref); - - /* - if (cell == grid->cell_active - && grid->state == CLTR_PHOTO_GRID_STATE_BROWSE) - { - glBegin (GL_QUADS); - glTexCoord2f (tx, ty); glVertex2i (x2+5, y2+5); - glTexCoord2f (0, ty); glVertex2i (x1-5, y2+5); - glTexCoord2f (0, 0); glVertex2i (x1-5, y1-5); - glTexCoord2f (tx, 0); glVertex2i (x2+5, y1-5); - glEnd (); - } - else - */ - { - glBegin (GL_QUADS); - glTexCoord2f (tx, ty); glVertex2i ((thumb_w/2), (thumb_h/2)); - glTexCoord2f (0, ty); glVertex2i (-(thumb_w/2), (thumb_h/2)); - glTexCoord2f (0, 0); glVertex2i (-(thumb_w/2), -(thumb_h/2)); - glTexCoord2f (tx, 0); glVertex2i ((thumb_w/2), -(thumb_h/2)); - glEnd (); - } - - g_mutex_unlock(Mutex_GRID); /* back to regular non translated matrix */ glPopMatrix(); @@ -541,17 +507,11 @@ cltr_photo_grid_redraw(ClutterPhotoGrid *grid) glPopMatrix(); - /* + /* finally paint background */ glDisable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBlendFunc(GL_ONE, GL_SRC_ALPHA); - glColor4f(1.0, 1.0, 1.0, xxx); + glColor3f(0.6, 0.6, 0.62); glRecti(0, 0, 640, 480); - xxx += 0.01; - */ - glXSwapBuffers(CltrCntx.xdpy, grid->parent->xwin); g_mutex_lock(Mutex_GRID); diff --git a/cltr-photo-grid.h b/cltr-photo-grid.h index 00b51aab1..e80d2c983 100644 --- a/cltr-photo-grid.h +++ b/cltr-photo-grid.h @@ -32,8 +32,8 @@ struct ClutterPhotoGridCell Pixbuf *pixb; float angle; GLuint texref; - - gint anim_step; + CltrImage *img; + gint anim_step; ClutterPhotoGridCellState state; }; diff --git a/cltr-tex.c b/cltr-tex.c new file mode 100644 index 000000000..116d9490f --- /dev/null +++ b/cltr-tex.c @@ -0,0 +1,307 @@ +#include "cltr-tex.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_image_render_to_gl_quad(CltrImage *img, 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; + + for (x=0; x < img->n_x_tiles; x++) + { + lasty = 0; + + for (y=0; y < img->n_y_tiles; y++) + { + int actual_w, actual_h; + + glBindTexture(GL_TEXTURE_2D, img->tiles[i]); + + actual_w = img->tile_x_size[x] - img->tile_x_waste[x]; + actual_h = img->tile_y_size[y] - img->tile_y_waste[y]; + + tx = (float) actual_w / img->tile_x_size[x]; + ty = (float) actual_h / img->tile_y_size[y]; + + qx1 = x1 + lastx; + qx2 = qx1 + ((qwidth * actual_w ) / img->width ); + + qy1 = y1 + lasty; + qy2 = qy1 + ((qheight * actual_h) / img->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 (CltrImage *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); + + texture->tiles = g_new (GLuint, texture->n_x_tiles * texture->n_y_tiles); + glGenTextures (texture->n_x_tiles * texture->n_y_tiles, texture->tiles); + +#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 */ + +CltrImage* +cltr_image_new(Pixbuf *pixb) +{ + CltrImage *img; + int x, y, i = 0; + + CLTR_MARK(); + + img = g_malloc0(sizeof(CltrImage)); + + img->width = pixb->width; + img->height = pixb->height; + img->pixb = pixb; + + pixbuf_ref(pixb); + + init_tiles (img); + + for (x=0; x < img->n_x_tiles; x++) + for (y=0; y < img->n_y_tiles; y++) + { + Pixbuf *pixtmp; + int src_h, src_w; + + pixtmp = pixbuf_new(img->tile_x_size[x], img->tile_y_size[y]); + + src_w = img->tile_x_size[x]; + src_h = img->tile_y_size[y]; + + /* + CLTR_DBG("%i+%i, %ix%i to %ix%i, waste %ix%i", + img->tile_x_position[x], + img->tile_y_position[y], + img->tile_x_size[x], + img->tile_y_size[y], + img->width, + img->height, + img->tile_x_waste[x], + img->tile_y_waste[y]); + */ + + pixbuf_copy(img->pixb, + pixtmp, + img->tile_x_position[x], + img->tile_y_position[y], + img->tile_x_size[x], + img->tile_y_size[y], + 0,0); + + glBindTexture(GL_TEXTURE_2D, img->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_RGBA, + pixtmp->width, + pixtmp->height, + 0, GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8, + pixtmp->data); + + CLTR_GLERR(); + + pixbuf_unref(pixtmp); + + i++; + + } + + return img; +} diff --git a/cltr-tex.h b/cltr-tex.h new file mode 100644 index 000000000..07499efb7 --- /dev/null +++ b/cltr-tex.h @@ -0,0 +1,12 @@ +#ifndef _HAVE_CLTR_TEX_H +#define _HAVE_CLTR_TEX_H + +#include "cltr.h" + +CltrImage* +cltr_image_new(Pixbuf *pixb); + +void +cltr_image_render_to_gl_quad(CltrImage *img, int x1, int y1, int x2, int y2); + +#endif diff --git a/cltr.c b/cltr.c index a675710a3..dfbe5219a 100644 --- a/cltr.c +++ b/cltr.c @@ -292,7 +292,17 @@ main(int argc, char **argv) { ClutterPhotoGrid *grid = NULL; ClutterWindow *win = NULL; + /* + Pixbuf *p1, *p2, *p3; + p1 = pixbuf_new(2000,2000); + p2 = pixbuf_new(640,480); + p3 = pixbuf_scale_down(p1, 512, 512); + + pixbuf_copy(p3, p2, 0,0, 1000, 1000,0,0); + + exit(1); + */ if (argc < 2) { g_printerr("usage: '%s' \n" @@ -304,7 +314,7 @@ main(int argc, char **argv) win = cltr_window_new(640, 480); - grid = cltr_photo_grid_new(win, 4, 4, argv[1]); + grid = cltr_photo_grid_new(win, 5, 5, argv[1]); Grid = grid; /* laaaaaazy globals */ diff --git a/cltr.h b/cltr.h index da8395e5f..34255383c 100644 --- a/cltr.h +++ b/cltr.h @@ -43,15 +43,25 @@ #define CLTR_MARK() CLTR_DBG("mark") +typedef struct CltrTexturePool CltrTexturePool; + +struct CltrTexturePool +{ + GList *texture_items; + gint n_texture_items; + GLuint *gl_textures; +}; + typedef struct ClutterMainContext ClutterMainContext; struct ClutterMainContext { - Display *xdpy; - Window xwin_root; - int xscreen; - GC xgc; - GLXContext gl_context; + Display *xdpy; + Window xwin_root; + int xscreen; + GC xgc; + GLXContext gl_context; + CltrTexturePool texture_pool; }; typedef enum CltrDirection @@ -86,6 +96,28 @@ typedef struct } CltrXEventSource; +/* texture stuff */ + +typedef struct CltrImage CltrImage; + +struct CltrImage +{ + Pixbuf *pixb; + + int width, height; + + int n_x_tiles, n_y_tiles; + int *tile_x_position, *tile_x_size, *tile_x_waste; + int *tile_y_position, *tile_y_size, *tile_y_waste; + + GLuint *tiles; + + gint refcnt; +}; + +/* ******************* */ + +#include "cltr-tex.h" #include "cltr-photo-grid.h" #endif diff --git a/pixbuf.c b/pixbuf.c index 30c991dbf..31895296a 100644 --- a/pixbuf.c +++ b/pixbuf.c @@ -475,6 +475,12 @@ pixbuf_unref(Pixbuf *pixb) } } +void +pixbuf_ref(Pixbuf *pixb) +{ + pixb->refcnt++; +} + Pixbuf* pixbuf_new_from_file(const char *filename) { @@ -553,6 +559,14 @@ pixbuf_copy(Pixbuf *src_pixb, sp = src_pixb->data + (srcy * src_pixb->width) + srcx; dp = dst_pixb->data + (dsty * dst_pixb->width) + dstx; + /* basic source clipping - needed by texture tiling code */ + + if (srcx + srcw > src_pixb->width) + srcw = src_pixb->width - srcx; + + if (srcy + srch > src_pixb->height) + srch = src_pixb->height - srcy; + while (srch--) { j = srcw; diff --git a/pixbuf.h b/pixbuf.h index 9c81ff641..111c7b62b 100644 --- a/pixbuf.h +++ b/pixbuf.h @@ -46,6 +46,9 @@ pixbuf_new(int width, int height); void pixbuf_unref(Pixbuf *pixb); +void +pixbuf_ref(Pixbuf *pixb); + void pixbuf_set_pixel(Pixbuf *pixb, int x, int y, PixbufPixel *p);