ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / glx / grab-ximage.c
index 3ca543fd2e079d1e4b77ed65f317a23d2e2f9498..6000e563c7b6bb5fa14a1f030e64bac22b2e0b2c 100644 (file)
@@ -1,5 +1,5 @@
 /* grab-ximage.c --- grab the screen to an XImage for use with OpenGL.
- * xscreensaver, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 2001, 2003 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 "grabscreen.h"
+#include "visual.h"
 
 extern char *progname;
 
 #include <X11/Xutil.h>
+#include <sys/time.h>
 
 #undef MAX
 #define MAX(a,b) ((a)>(b)?(a):(b))
@@ -32,14 +35,6 @@ extern char *progname;
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
-static Bool
-bigendian (void)
-{
-  union { int i; char c[sizeof(int)]; } u;
-  u.i = 1;
-  return !u.c[0];
-}
-
 /* return the next larger power of 2. */
 static int
 to_pow2 (int i)
@@ -53,21 +48,62 @@ to_pow2 (int i)
 }
 
 
-/* Returns an XImage structure containing an image of the desktop.
-   (As a side-effect, that image will be painted onto the given Window.)
-   This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
-   extra byte set to 0xFF.
+/* Given a bitmask, returns the position and width of the field.
  */
-XImage *
-screen_to_ximage (Screen *screen, Window window)
+static void
+decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
+{
+  int i;
+  for (i = 0; i < 32; i++)
+    if (mask & (1L << i))
+      {
+        int j = 0;
+        *pos_ret = i;
+        for (; i < 32; i++, j++)
+          if (! (mask & (1L << i)))
+            break;
+        *size_ret = j;
+        return;
+      }
+}
+
+
+/* Given a value and a field-width, expands the field to fill out 8 bits.
+ */
+static unsigned char
+spread_bits (unsigned char value, unsigned char width)
+{
+  switch (width)
+    {
+    case 8: return value;
+    case 7: return (value << 1) | (value >> 6);
+    case 6: return (value << 2) | (value >> 4);
+    case 5: return (value << 3) | (value >> 2);
+    case 4: return (value << 4) | (value);
+    case 3: return (value << 5) | (value << 2) | (value >> 2);
+    case 2: return (value << 6) | (value << 4) | (value);
+    default: abort(); break;
+    }
+}
+
+
+static Bool
+bigendian (void)
+{
+  union { int i; char c[sizeof(int)]; } u;
+  u.i = 1;
+  return !u.c[0];
+}
+
+
+static XImage *
+screen_to_ximage_1 (Screen *screen, Window window, Pixmap pixmap)
 {
   Display *dpy = DisplayOfScreen (screen);
   XWindowAttributes xgwa;
   int win_width, win_height;
   int tex_width, tex_height;
 
-  grab_screen_image (screen, window);
-
   XGetWindowAttributes (dpy, window, &xgwa);
   win_width = xgwa.width;
   win_height = xgwa.height;
@@ -76,71 +112,120 @@ screen_to_ximage (Screen *screen, Window window)
   tex_width  = to_pow2(win_width);
   tex_height = to_pow2(win_height);
 
-  /* Convert the server-side Drawable to a client-side GL-ordered XImage.
+  /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
    */
   {
     XImage *ximage1, *ximage2;
+    XColor *colors = 0;
 
-    ximage1 = XGetImage (dpy, window, 0, 0, win_width, win_height, ~0L,
+    ximage1 = XGetImage (dpy, pixmap, 0, 0, win_width, win_height, ~0L,
                          ZPixmap);
+    XFreePixmap (dpy, pixmap);
+    pixmap = 0;
+
     ximage2 = XCreateImage (dpy, xgwa.visual, 32, ZPixmap, 0, 0,
                             tex_width, tex_height, 32, 0);
 
     ximage2->data = (char *) calloc (tex_height, ximage2->bytes_per_line);
 
+    {
+      Screen *dscreen = DefaultScreenOfDisplay (dpy);
+      Visual *dvisual = DefaultVisualOfScreen (dscreen);
+      if (visual_class (dscreen, dvisual) == PseudoColor ||
+          visual_class (dscreen, dvisual) == GrayScale)
+        {
+          Colormap cmap = DefaultColormapOfScreen(dscreen);
+          int ncolors = visual_cells (dscreen, dvisual);
+          int i;
+          colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
+          for (i = 0; i < ncolors; i++)
+            colors[i].pixel = i;
+          XQueryColors (dpy, cmap, colors, ncolors);
+        }
+    }
+
     /* Translate the server-ordered image to a client-ordered image.
      */
     {
       int x, y;
-      int crpos, cgpos, cbpos, capos;  /* bitfield positions */
-      int srpos, sgpos, sbpos;
-      int srmsk, sgmsk, sbmsk;
-      int sdepth = ximage1->depth;
-
-      srmsk = ximage1->red_mask;
-      sgmsk = ximage1->green_mask;
-      sbmsk = ximage1->blue_mask;
-      if (sdepth == 32 || sdepth == 24)
-        srpos = 16, sgpos = 8, sbpos = 0;
-      else /* 15 or 16 */
-        srpos = 10, sgpos = 5, sbpos = 0;
+      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 char spread_map[3][256];
+
+      if (colors == 0)  /* truecolor */
+        {
+          srmsk = ximage2->red_mask;
+          sgmsk = ximage2->green_mask;
+          sbmsk = ximage2->blue_mask;
+
+          decode_mask (srmsk, &srpos, &srsiz);
+          decode_mask (sgmsk, &sgpos, &sgsiz);
+          decode_mask (sbmsk, &sbpos, &sbsiz);
+        }
 
       /* Note that unlike X, which is endianness-agnostic (since any XImage
          can have its own specific bit ordering, with the server reversing
          things as necessary) OpenGL pretends everything is client-side, so
-         we need to pack things in the right order for the client machine.
+         we need to pack things in "RGBA" order on the client machine,
+         regardless of its endianness.
        */
       if (bigendian())
         crpos = 24, cgpos = 16, cbpos =  8, capos =  0;
       else
         crpos =  0, cgpos =  8, cbpos = 16, capos = 24;
 
+      if (colors == 0)  /* truecolor */
+        {
+          for (i = 0; i < 256; i++)
+            {
+              spread_map[0][i] = spread_bits (i, srsiz);
+              spread_map[1][i] = spread_bits (i, sgsiz);
+              spread_map[2][i] = spread_bits (i, sbsiz);
+            }
+        }
+
       for (y = 0; y < win_height; y++)
         {
           int y2 = (win_height-1-y); /* Texture maps are upside down. */
           for (x = 0; x < win_width; x++)
             {
               unsigned long sp = XGetPixel (ximage1, x, y2);
-              unsigned char sr = (sp & srmsk) >> srpos;
-              unsigned char sg = (sp & sgmsk) >> sgpos;
-              unsigned char sb = (sp & sbmsk) >> sbpos;
+              unsigned char sr, sg, sb;
               unsigned long cp;
 
-              if (sdepth < 24)   /* spread 5 bits to 8 */
+              if (colors)
+                {
+                  sr = colors[sp].red   & 0xFF;
+                  sg = colors[sp].green & 0xFF;
+                  sb = colors[sp].blue  & 0xFF;
+                }
+              else
                 {
-                  sr = (sr << 3) | (sr >> 2);
-                  sg = (sg << 3) | (sg >> 2);
-                  sb = (sb << 3) | (sb >> 2);
+                  sr = (sp & srmsk) >> srpos;
+                  sg = (sp & sgmsk) >> sgpos;
+                  sb = (sp & sbmsk) >> sbpos;
+
+                  sr = spread_map[0][sr];
+                  sg = spread_map[1][sg];
+                  sb = spread_map[2][sb];
                 }
+
               cp = ((sr << crpos) |
                     (sg << cgpos) |
                     (sb << cbpos) |
                     (0xFF << capos));
+
               XPutPixel (ximage2, x, y, cp);
             }
         }
     }
 
+    if (pixmap) XFreePixmap (dpy, pixmap);
+    if (colors) free (colors);
     free (ximage1->data);
     ximage1->data = 0;
     XDestroyImage (ximage1);
@@ -154,3 +239,92 @@ screen_to_ximage (Screen *screen, Window window)
     return ximage2;
   }
 }
+
+
+/* Returns an XImage structure containing an image of the desktop.
+   (As a side-effect, that image *may* be painted onto the given Window.)
+   This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
+   extra byte set to 0xFF.
+ */
+XImage *
+screen_to_ximage (Screen *screen, Window window, char **filename_return)
+{
+  Display *dpy = DisplayOfScreen (screen);
+  Pixmap pixmap = 0;
+  XWindowAttributes xgwa;
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+  pixmap = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
+  load_random_image (screen, window, pixmap, filename_return);
+
+  return screen_to_ximage_1 (screen, window, pixmap);
+}
+
+
+typedef struct {
+  void (*callback) (Screen *, Window, XImage *,
+                    const char *name, void *closure, double cvt_time);
+  void *closure;
+  Pixmap pixmap;
+} img_closure;
+
+
+/* Returns the current time in seconds as a double.
+ */
+static double
+double_time (void)
+{
+  struct timeval now;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+  struct timezone tzp;
+  gettimeofday(&now, &tzp);
+# else
+  gettimeofday(&now);
+# endif
+
+  return (now.tv_sec + ((double) now.tv_usec * 0.000001));
+}
+
+
+static void
+img_cb (Screen *screen, Window window, Drawable drawable,
+        const char *name, void *closure)
+{
+  XImage *ximage;
+  double cvt_time = double_time();
+  img_closure *data = (img_closure *) closure;
+  /* copy closure data to stack and free the original before running cb */
+  img_closure dd = *data;
+  memset (data, 0, sizeof (*data));
+  free (data);
+  data = 0;
+  ximage = screen_to_ximage_1 (screen, window, dd.pixmap);
+  dd.callback (screen, window, ximage, name, dd.closure, cvt_time);
+}
+
+
+/* Like the above, but loads the image in the background and runs the
+   given callback once it has been loaded.
+ */
+#include <X11/Intrinsic.h>
+extern XtAppContext app;
+
+void
+fork_screen_to_ximage (Screen *screen, Window window,
+                       void (*callback) (Screen *, Window, XImage *,
+                                         const char *name,
+                                         void *closure,
+                                         double cvt_time),
+                       void *closure)
+{
+  Display *dpy = DisplayOfScreen (screen);
+  XWindowAttributes xgwa;
+  img_closure *data = (img_closure *) calloc (1, sizeof(*data));
+  data->callback = callback;
+  data->closure  = closure;
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+  data->pixmap = XCreatePixmap (dpy, window, xgwa.width, xgwa.height,
+                                xgwa.depth);
+  fork_load_random_image (screen, window, data->pixmap, img_cb, data);
+}