From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / grab-ximage.c
index 519f8c453e8f70d825679f80369df40ce77a5674..f57bd9aa5436ce770f78dbb13cc93db0c785d5f7 100644 (file)
@@ -1,5 +1,5 @@
 /* grab-ximage.c --- grab the screen to an XImage for use with OpenGL.
- * xscreensaver, Copyright (c) 2001, 2003, 2005 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 2001-2008 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <GL/gl.h>     /* only for GLfloat */
-#include <GL/glu.h>    /* for gluBuild2DMipmaps */
 
+#ifdef HAVE_ANDROID
+#include <GLES/gl.h>
+#endif
+
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+# ifndef HAVE_JWZGLES
+#  include <OpenGL/glu.h>
+# endif
+#else
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <GL/gl.h>    /* only for GLfloat */
+# include <GL/glu.h>   /* for gluBuild2DMipmaps */
+# include <GL/glx.h>   /* for glXMakeCurrent() */
+#endif
+
+#ifdef HAVE_JWZGLES
+# include "jwzgles.h"
+#endif /* HAVE_JWZGLES */
+
+#include "grab-ximage.h"
 #include "grabscreen.h"
 #include "visual.h"
 
 
 extern char *progname;
 
-#include <X11/Xutil.h>
 #include <sys/time.h>
 
+#ifdef HAVE_JWXYZ
+# include "jwxyz.h"
+#else
+# include <X11/Xutil.h>
+#endif
+
 #undef MAX
 #define MAX(a,b) ((a)>(b)?(a):(b))
 
@@ -80,7 +103,8 @@ bigendian (void)
 /* Given a bitmask, returns the position and width of the field.
  */
 static void
-decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
+decode_mask (unsigned long mask, unsigned long *pos_ret,
+             unsigned long *size_ret)
 {
   int i;
   for (i = 0; i < 32; i++)
@@ -123,15 +147,14 @@ convert_ximage_to_rgba32 (Screen *screen, XImage *image)
   Visual *visual = DefaultVisualOfScreen (screen);
 
   int x, y;
-  unsigned int crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
-  unsigned int srpos=0, sgpos=0, sbpos=0;
-  unsigned int srmsk=0, sgmsk=0, sbmsk=0;
-  unsigned int srsiz=0, sgsiz=0, sbsiz=0;
-  int i;
+  unsigned long crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
+  unsigned long srpos=0, sgpos=0, sbpos=0;
+  unsigned long srmsk=0, sgmsk=0, sbmsk=0;
+  unsigned long srsiz=0, sgsiz=0, sbsiz=0;
   XColor *colors = 0;
   unsigned char spread_map[3][256];
 
-  /* Note: height+2 in "to" to be to work around an array bounds overrun
+  /* Note: height+2 in "to" to work around an array bounds overrun
      in gluBuild2DMipmaps / gluScaleImage.
    */
   XImage *from = image;
@@ -141,6 +164,13 @@ convert_ximage_to_rgba32 (Screen *screen, XImage *image)
                              0);
   to->data = (char *) calloc (to->height, to->bytes_per_line);
 
+  /* Set the bit order in the XImage structure to whatever the
+     local host's native bit order is.
+   */
+  to->bitmap_bit_order =
+    to->byte_order =
+    (bigendian() ? MSBFirst : LSBFirst);
+
   if (visual_class (screen, visual) == PseudoColor ||
       visual_class (screen, visual) == GrayScale)
     {
@@ -172,6 +202,7 @@ convert_ximage_to_rgba32 (Screen *screen, XImage *image)
 
   if (colors == 0)  /* truecolor */
     {
+      int i;
       for (i = 0; i < 256; i++)
         {
           spread_map[0][i] = spread_bits (i, srsiz);
@@ -180,6 +211,10 @@ convert_ximage_to_rgba32 (Screen *screen, XImage *image)
         }
     }
 
+  /* trying to track down an intermittent crash in ximage_putpixel_32 */
+  if (to->width  < from->width)  abort();
+  if (to->height < from->height) abort();
+
   for (y = 0; y < from->height; y++)
     for (x = 0; x < from->width; x++)
       {
@@ -223,7 +258,7 @@ convert_ximage_to_rgba32 (Screen *screen, XImage *image)
    We use this when mipmapping fails on large textures.
  */
 static void
-halve_image (XImage *ximage)
+halve_image (XImage *ximage, XRectangle *geom)
 {
   int w2 = ximage->width/2;
   int h2 = ximage->height/2;
@@ -261,6 +296,14 @@ halve_image (XImage *ximage)
   *ximage = *ximage2;
   ximage2->data = 0;
   XFree (ximage2);
+
+  if (geom)
+    {
+      geom->x /= 2;
+      geom->y /= 2;
+      geom->width  /= 2;
+      geom->height /= 2;
+    }
 }
 
 
@@ -273,11 +316,10 @@ static XImage *
 pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
 {
   Display *dpy = DisplayOfScreen (screen);
-  Visual *visual = DefaultVisualOfScreen (screen);
   unsigned int width, height, depth;
 
 # ifdef HAVE_XSHM_EXTENSION
-  Bool use_shm = get_boolean_resource ("useSHM", "Boolean");
+  Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
   XShmSegmentInfo shm_info;
 # endif /* HAVE_XSHM_EXTENSION */
 
@@ -291,11 +333,15 @@ pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
     XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
   }
 
+  if (width < 5 || height < 5)  /* something's gone wrong somewhere... */
+    return 0;
+
   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
    */
 # ifdef HAVE_XSHM_EXTENSION
   if (use_shm)
     {
+      Visual *visual = DefaultVisualOfScreen (screen);
       server_ximage = create_xshm_image (dpy, visual, depth,
                                          ZPixmap, 0, &shm_info,
                                          width, height);
@@ -345,7 +391,7 @@ typedef struct {
 #define SHORT_5_6_5        GL_UNSIGNED_SHORT_5_6_5
 #define SHORT_5_6_5_REV    GL_UNSIGNED_SHORT_5_6_5_REV
 
-static conversion_table ctable[] = {
+static const conversion_table ctable[] = {
   { 8,  0x000000E0, 0x0000001C, 0x00000003, BYTE_3_3_2,         GL_RGB      },
   { 8,  0x00000007, 0x00000038, 0x000000C0, BYTE_2_3_3_REV,     GL_RGB      },
   { 16, 0x0000F800, 0x000007E0, 0x0000001F, SHORT_5_6_5,        GL_RGB      },
@@ -443,6 +489,7 @@ gl_settings_for_ximage (XImage *image,
 #endif /* ! REFORMAT_IMAGE_DATA */
 
 typedef struct {
+  GLXContext glx_context;
   Pixmap pixmap;
   int pix_width, pix_height, pix_depth;
   int texid;
@@ -487,14 +534,11 @@ double_time (void)
 
 /* return the next larger power of 2. */
 static int
-to_pow2 (int i)
+to_pow2 (int value)
 {
-  static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
-                                 2048, 4096, 8192, 16384, 32768, 65536 };
-  int j;
-  for (j = 0; j < countof(pow2); j++)
-    if (pow2[j] >= i) return pow2[j];
-  abort();  /* too big! */
+  int i = 1;
+  while (i < value) i <<= 1;
+  return i;
 }
 
 
@@ -508,6 +552,7 @@ ximage_to_texture (XImage *ximage,
                    GLint type, GLint format,
                    int *width_return,
                    int *height_return,
+                   XRectangle *geometry,
                    Bool mipmap_p)
 {
   int max_reduction = 7;
@@ -548,7 +593,7 @@ ximage_to_texture (XImage *ximage,
                  progname, ximage->width, ximage->height,
                  tex_width, tex_height);
 
-      glTexImage2D (GL_TEXTURE_2D, 0, 3, tex_width, tex_height, 0,
+      glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0,
                     format, type, 0);
       err = glGetError();
 
@@ -569,7 +614,7 @@ ximage_to_texture (XImage *ximage,
 
       if (!s || !*s)
         {
-          sprintf (buf, "unknown error %d", err);
+          sprintf (buf, "unknown error %d", (int) err);
           s = buf;
         }
 
@@ -595,7 +640,7 @@ ximage_to_texture (XImage *ximage,
           if (debug_p)
             fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
                      progname, ximage->width, ximage->height, s);
-          halve_image (ximage);
+          halve_image (ximage, geometry);
           goto AGAIN;
         }
     }
@@ -606,89 +651,33 @@ ximage_to_texture (XImage *ximage,
 }
 
 
-static void screen_to_texture_async_cb (Screen *screen,
+static void load_texture_async_cb (Screen *screen,
                                         Window window, Drawable drawable,
                                         const char *name, XRectangle *geometry,
                                         void *closure);
 
 
 /* Grabs an image of the desktop (or another random image file) and
-   loads tht image into GL's texture memory.
-   Writes to stderr and returns False on error.
- */
-Bool
-screen_to_texture (Screen *screen, Window window,
-                   int desired_width, int desired_height,
-                   Bool mipmap_p,
-                   char **filename_return,
-                   XRectangle *geometry_return,
-                   int *image_width_return,
-                   int *image_height_return,
-                   int *texture_width_return,
-                   int *texture_height_return)
-{
-  Display *dpy = DisplayOfScreen (screen);
-  img_closure *data = (img_closure *) calloc (1, sizeof(*data));
-  XWindowAttributes xgwa;
-  char *filename = 0;
-  XRectangle geom = { 0, 0, 0, 0 };
-  int wret;
-
-  if (! image_width_return)
-    image_width_return = &wret;
-
-  if (debug_p)
-    data->load_time = double_time();
-
-  data->texid     = -1;
-  data->mipmap_p  = mipmap_p;
-  data->filename_return       = filename_return;
-  data->geometry_return       = geometry_return;
-  data->image_width_return    = image_width_return;
-  data->image_height_return   = image_height_return;
-  data->texture_width_return  = texture_width_return;
-  data->texture_height_return = texture_height_return;
-
-  XGetWindowAttributes (dpy, window, &xgwa);
-  data->pix_width  = xgwa.width;
-  data->pix_height = xgwa.height;
-  data->pix_depth  = xgwa.depth;
-
-  if (desired_width  && desired_width  < xgwa.width)
-    data->pix_width  = desired_width;
-  if (desired_height && desired_height < xgwa.height)
-    data->pix_height = desired_height;
-
-  data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
-                                data->pix_depth);
-  load_random_image (screen, window, data->pixmap, &filename, &geom);
-  screen_to_texture_async_cb (screen, window, data->pixmap, filename, &geom,
-                              data);
-
-  return (*image_width_return != 0);
-}
-
-
-/* Like the above, but the image is loaded in a background process,
-   and a callback is run when the loading is complete.
+   loads the image into GL's texture memory.
    When the callback is called, the image data will have been loaded
    into texture number `texid' (via glBindTexture.)
 
    If an error occurred, width/height will be 0.
  */
 void
-screen_to_texture_async (Screen *screen, Window window,
-                         int desired_width, int desired_height,
-                         Bool mipmap_p,
-                         GLuint texid,
-                         void (*callback) (const char *filename,
-                                           XRectangle *geometry,
-                                           int image_width,
-                                           int image_height,
-                                           int texture_width,
-                                           int texture_height,
-                                           void *closure),
-                         void *closure)
+load_texture_async (Screen *screen, Window window,
+                    GLXContext glx_context,
+                    int desired_width, int desired_height,
+                    Bool mipmap_p,
+                    GLuint texid,
+                    void (*callback) (const char *filename,
+                                      XRectangle *geometry,
+                                      int image_width,
+                                      int image_height,
+                                      int texture_width,
+                                      int texture_height,
+                                      void *closure),
+                    void *closure)
 {
   Display *dpy = DisplayOfScreen (screen);
   XWindowAttributes xgwa;
@@ -697,10 +686,11 @@ screen_to_texture_async (Screen *screen, Window window,
   if (debug_p)
     data->load_time = double_time();
 
-  data->texid     = texid;
-  data->mipmap_p  = mipmap_p;
-  data->callback  = callback;
-  data->closure   = closure;
+  data->texid       = texid;
+  data->mipmap_p    = mipmap_p;
+  data->glx_context = glx_context;
+  data->callback    = callback;
+  data->closure     = closure;
 
   XGetWindowAttributes (dpy, window, &xgwa);
   data->pix_width  = xgwa.width;
@@ -714,8 +704,8 @@ screen_to_texture_async (Screen *screen, Window window,
 
   data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
                                 data->pix_depth);
-  fork_load_random_image (screen, window, data->pixmap,
-                          screen_to_texture_async_cb, data);
+  load_image_async (screen, window, data->pixmap, 
+                    load_texture_async_cb, data);
 }
 
 
@@ -723,9 +713,8 @@ screen_to_texture_async (Screen *screen, Window window,
    This is used in both synchronous and asynchronous mode.
  */
 static void
-screen_to_texture_async_cb (Screen *screen, Window window, Drawable drawable,
-                            const char *name, XRectangle *geometry,
-                            void *closure)
+load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
+                       const char *name, XRectangle *geometry, void *closure)
 {
   Display *dpy = DisplayOfScreen (screen);
   Bool ok;
@@ -740,6 +729,9 @@ screen_to_texture_async_cb (Screen *screen, Window window, Drawable drawable,
   free (data);
   data = 0;
 
+  if (dd.glx_context)
+    glXMakeCurrent (dpy, window, dd.glx_context);
+
   if (geometry->width <= 0 || geometry->height <= 0)
     {
       /* This can happen if an old version of xscreensaver-getimage
@@ -802,7 +794,13 @@ screen_to_texture_async_cb (Screen *screen, Window window, Drawable drawable,
         glBindTexture (GL_TEXTURE_2D, dd.texid);
 
       glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
-      ok = ximage_to_texture (ximage, type, format, &tw, &th, dd.mipmap_p);
+      ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
+                              dd.mipmap_p);
+      if (ok)
+        {
+          iw = ximage->width;  /* in case the image was shrunk */
+          ih = ximage->height;
+        }
     }
 
   if (ximage) XDestroyImage (ximage);