]> Pileus Git - grits/blobdiff - src/objects/grits-tile.c
Move threading out of tile update/gc functions
[grits] / src / objects / grits-tile.c
index 649d0f42970936e225e7c6b6ab2715f4f6e09bd7..e42a1606338d916f0704ff1959e4aece9db7edbe 100644 (file)
  * representing the OpenGL texture to use when drawing the tile.
  */
 
+#define GL_GLEXT_PROTOTYPES
 #include <config.h>
 #include <math.h>
+#include <string.h>
 #include "gtkgl.h"
 #include "grits-tile.h"
 
-guint  grits_tile_mask = 0;
+static guint  grits_tile_mask = 0;
 
 gchar *grits_tile_path_table[2][2] = {
        {"00.", "01."},
@@ -64,10 +66,8 @@ GritsTile *grits_tile_new(GritsTile *parent,
        tile->atime  = time(NULL);
        grits_bounds_set_bounds(&tile->coords, 0, 1, 1, 0);
        grits_bounds_set_bounds(&tile->edge, n, s, e, w);
-       if (parent) {
+       if (parent)
                tile->proj   = parent->proj;
-               tile->zindex = parent->zindex+1;
-       }
        return tile;
 }
 
@@ -217,13 +217,14 @@ void grits_tile_update(GritsTile *tile, GritsPoint *eye,
         * resolution for this part? */
        gint xs = G_N_ELEMENTS(tile->children);
        gint ys = G_N_ELEMENTS(tile->children[0]);
-       if (_grits_tile_precise(eye, &tile->edge, res, width/xs, height/ys)) {
+       if (tile->parent && _grits_tile_precise(eye, &tile->edge,
+                               res, width/xs, height/ys)) {
                GRITS_OBJECT(tile)->hidden = TRUE;
                return;
        }
 
        /* Load the tile */
-       if (!tile->load && !tile->data)
+       if (!tile->load && !tile->data && !tile->tex && !tile->pixels && !tile->pixbuf)
                load_func(tile, user_data);
        tile->atime = time(NULL);
        tile->load  = TRUE;
@@ -245,6 +246,94 @@ void grits_tile_update(GritsTile *tile, GritsPoint *eye,
                                load_func, user_data);
 }
 
+/**
+ * grits_tile_load_pixels:
+ * @tile:   the tile to load data into
+ * @pixels: buffered pixel data
+ * @width:  width of the pixel buffer (in pixels)
+ * @height: height of the pixel buffer (in pixels)
+ * @alpha:  TRUE if the pixel data contains an alpha channel
+ *
+ * Load tile data from an in memory pixel buffer.
+ *
+ * This function is thread safe and my be called from outside the main thread.
+ *
+ * Ownership of the pixel buffer is passed to the tile, it should not be freed
+ * or modified after calling this function.
+ *
+ * Returns: TRUE if the image was loaded successfully
+ */
+gboolean grits_tile_load_pixels(GritsTile *tile, guchar *pixels,
+               gint width, gint height, gint alpha)
+{
+       g_debug("GritsTile: load_pixels - %p -> %p (%dx%d:%d)",
+                       tile, pixels, width, height, alpha);
+
+       /* Copy pixbuf data for callback */
+       tile->width  = width;
+       tile->height = height;
+       tile->alpha  = alpha;
+       tile->pixels = pixels;
+
+       /* Queue OpenGL texture load/draw */
+       grits_object_queue_draw(GRITS_OBJECT(tile));
+       return TRUE;
+}
+
+/**
+ * grits_tile_load_file:
+ * @tile: the tile to load data into
+ * @file: path to an image file to load
+ *
+ * Load tile data from a GdkPixbuf
+ * This function is thread safe and my be called from outside the main thread.
+ *
+ * Returns: TRUE if the image was loaded successfully
+ */
+gboolean grits_tile_load_pixbuf(GritsTile *tile, GdkPixbuf *pixbuf)
+{
+       g_debug("GritsTile: load_pixbuf %p -> %p", tile, pixbuf);
+
+       /* Copy pixbuf data for callback */
+       tile->pixbuf = g_object_ref(pixbuf);
+       tile->width  = gdk_pixbuf_get_width(pixbuf);
+       tile->height = gdk_pixbuf_get_height(pixbuf);
+       tile->alpha  = gdk_pixbuf_get_has_alpha(pixbuf);
+
+       /* Queue OpenGL texture load/draw */
+       grits_object_queue_draw(GRITS_OBJECT(tile));
+
+       return TRUE;
+}
+
+/**
+ * grits_tile_load_file:
+ * @tile: the tile to load data into
+ * @file: path to an image file to load
+ *
+ * Load tile data from an image file
+ * This function is thread safe and my be called from outside the main thread.
+ *
+ * Returns: TRUE if the image was loaded successfully
+ */
+gboolean grits_tile_load_file(GritsTile *tile, const gchar *file)
+{
+       g_debug("GritsTile: load_file %p -> %s", tile, file);
+
+       /* Copy pixbuf data for callback */
+       tile->pixbuf = gdk_pixbuf_new_from_file(file, NULL);
+       if (!tile->pixbuf)
+               return FALSE;
+       tile->width  = gdk_pixbuf_get_width(tile->pixbuf);
+       tile->height = gdk_pixbuf_get_height(tile->pixbuf);
+       tile->alpha  = gdk_pixbuf_get_has_alpha(tile->pixbuf);
+
+       /* Queue OpenGL texture load/draw */
+       grits_object_queue_draw(GRITS_OBJECT(tile));
+
+       return TRUE;
+}
+
 /**
  * grits_tile_find:
  * @root: the root tile to search from
@@ -253,7 +342,7 @@ void grits_tile_update(GritsTile *tile, GritsPoint *eye,
  *
  * Locate the subtile with the highest resolution which contains the given
  * lat/lon point.
- * 
+ *
  * Returns: the child tile
  */
 GritsTile *grits_tile_find(GritsTile *root, gdouble lat, gdouble lon)
@@ -311,13 +400,23 @@ GritsTile *grits_tile_gc(GritsTile *root, time_t atime,
                if (root->children[x][y])
                        has_children = TRUE;
        }
-       //g_debug("GritsTile: gc - %p->atime=%u < atime=%u",
-       //              root, (guint)root->atime, (guint)atime);
-       if (!has_children && root->atime < atime &&
-                       (root->data || !root->load)) {
+       //g_debug("GritsTile: gc - %p kids=%d time=%d data=%d load=%d",
+       //      root, !!has_children, root->atime < atime, !!root->data, !!root->load);
+       int thread_safe = !root->load || root->data || root->tex || root->pixels || root->pixbuf;
+       if (root->parent && !has_children && root->atime < atime && thread_safe) {
                //g_debug("GritsTile: gc/free - %p", root);
-               if (root->data)
-                       free_func(root, user_data);
+               if (root->pixbuf)
+                       g_object_unref(root->pixbuf);
+               if (root->pixels)
+                       g_free(root->pixels);
+               if (root->tex)
+                       glDeleteTextures(1, &root->tex);
+               if (root->data) {
+                       if (free_func)
+                               free_func(root, user_data);
+                       else
+                               g_free(root->data);
+               }
                g_object_unref(root);
                return NULL;
        }
@@ -348,26 +447,80 @@ void grits_tile_free(GritsTile *root, GritsTileFreeFunc free_func, gpointer user
 /* Load texture mask so we can draw a texture to just a part of a triangle */
 static guint _grits_tile_load_mask(void)
 {
-       guint  tex;
-       guint8 byte = 0xff;
+       guint tex;
+       const int width = 256, height = 256;
+       guint8 *bytes = g_malloc(width*height);
+       memset(bytes, 0xff, width*height);
        glGenTextures(1, &tex);
        glBindTexture(GL_TEXTURE_2D, tex);
 
-       glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 0,
-                       GL_ALPHA, GL_UNSIGNED_BYTE, &byte);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
+                       GL_ALPHA, GL_UNSIGNED_BYTE, bytes);
 
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+       g_free(bytes);
        return tex;
 }
 
+/* Load the texture from saved pixel data */
+static gboolean _grits_tile_load_tex(GritsTile *tile)
+{
+       /* Abort for null tiles */
+       if (!tile)
+               return FALSE;
+
+       /* Defer loading of hidden tiles */
+       if (GRITS_OBJECT(tile)->hidden)
+               return FALSE;
+
+       /* If we're already done loading the text stop */
+       if (tile->tex)
+               return TRUE;
+
+       /* Check if the tile has data yet */
+       if (!tile->pixels && !tile->pixbuf)
+               return FALSE;
+
+       /* Get correct pixel buffer */
+       guchar *pixels = tile->pixels ?:
+               gdk_pixbuf_get_pixels(tile->pixbuf);
+
+       /* Create texture */
+       g_debug("GritsTile: load_tex");
+       glGenTextures(1, &tile->tex);
+       glBindTexture(GL_TEXTURE_2D, tile->tex);
+
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+       glPixelStorei(GL_PACK_ALIGNMENT, 1);
+       glTexImage2D(GL_TEXTURE_2D, 0, 4, tile->width, tile->height, 0,
+                       (tile->alpha ? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, pixels);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+       /* Free data */
+       if (tile->pixbuf) {
+               g_object_unref(tile->pixbuf);
+               tile->pixbuf = NULL;
+       }
+       if (tile->pixels) {
+               g_free(tile->pixels);
+               tile->pixels = NULL;
+       }
+
+       return TRUE;
+
+}
+
 /* Draw a single tile */
 static void grits_tile_draw_one(GritsTile *tile, GritsOpenGL *opengl, GList *triangles)
 {
-       if (!tile || !tile->data)
+       if (!tile || !tile->tex)
                return;
        if (!triangles)
                g_warning("GritsOpenGL: _draw_tiles - No triangles to draw: edges=%f,%f,%f,%f",
@@ -433,7 +586,7 @@ static void grits_tile_draw_one(GritsTile *tile, GritsOpenGL *opengl, GList *tri
                }
 
                /* Draw triangle */
-               glBindTexture(GL_TEXTURE_2D, *(guint*)tile->data);
+               glBindTexture(GL_TEXTURE_2D, tile->tex);
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                glBegin(GL_TRIANGLES);
                glNormal3dv(tri->p.r->norm); glMultiTexCoord2dv(GL_TEXTURE0, xy[0]); glMultiTexCoord2dv(GL_TEXTURE1, xy[0]); glVertex3dv((double*)tri->p.r);
@@ -451,35 +604,25 @@ static gboolean grits_tile_draw_rec(GritsTile *tile, GritsOpenGL *opengl)
        //              tile ? !!tile->load : 0,
        //              tile ? !!GRITS_OBJECT(tile)->hidden : 0);
 
-       if (!tile || !tile->data || GRITS_OBJECT(tile)->hidden)
+       if (!_grits_tile_load_tex(tile))
                return FALSE;
 
        GritsTile *child = NULL;
-       gboolean   done  = FALSE;
-       while (!done) {
-               /* Only draw children if possible */
-               gboolean draw_parent = FALSE;
-               grits_tile_foreach(tile, child)
-                       if (!child || !child->data || GRITS_OBJECT(child)->hidden)
-                               draw_parent = TRUE;
-
-               /* Draw parent tile underneath */
-               if (draw_parent) {
-                       GList *triangles = roam_sphere_get_intersect(opengl->sphere, FALSE,
-                                       tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w);
-                       grits_tile_draw_one(tile, opengl, triangles);
-                       g_list_free(triangles);
-               }
 
-               /* Draw child tiles */
-               gboolean drew_all_children = TRUE;
-               grits_tile_foreach(tile, child)
-                       if (!grits_tile_draw_rec(child, opengl))
-                               drew_all_children = FALSE;
-
-               /* Check if tiles were hidden by a thread while drawing */
-               done = draw_parent || drew_all_children;
+       /* Draw child tiles */
+       gboolean draw_parent = FALSE;
+       grits_tile_foreach(tile, child)
+               if (!grits_tile_draw_rec(child, opengl))
+                       draw_parent = TRUE;
+
+       /* Draw parent tile underneath using depth test */
+       if (draw_parent) {
+               GList *triangles = roam_sphere_get_intersect(opengl->sphere, FALSE,
+                               tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w);
+               grits_tile_draw_one(tile, opengl, triangles);
+               g_list_free(triangles);
        }
+
        return TRUE;
 }