http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.23.tar.gz
[xscreensaver] / hacks / glx / grab-ximage.c
1 /* grab-ximage.c --- grab the screen to an XImage for use with OpenGL.
2  * xscreensaver, Copyright (c) 2001, 2003, 2005 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <GL/gl.h>      /* only for GLfloat */
23 #include <GL/glu.h>     /* for gluBuild2DMipmaps */
24
25 #include "grabscreen.h"
26 #include "visual.h"
27
28 /* If REFORMAT_IMAGE_DATA is defined, then we convert Pixmaps to textures
29    like this:
30
31      - get Pixmap as an XImage in whatever form the server hands us;
32      - convert that XImage to 32-bit RGBA in client-local endianness;
33      - make the texture using RGBA, UNSIGNED_BYTE.
34
35    If undefined, we do this:
36
37      - get Pixmap as an XImage in whatever form the server hands us;
38      - figure out what OpenGL texture packing parameters correspond to
39        the image data that the server sent us and use that, e.g.,
40        BGRA, INT_8_8_8_8_REV.
41
42    You might expect the second method to be faster, since we're not making
43    a second copy of the data and iterating each pixel before we hand it
44    to GL.  But, you'd be wrong.  The first method is almost 6x faster.
45    I guess GL is reformatting it *again*, and doing it very inefficiently!
46 */
47 #define REFORMAT_IMAGE_DATA
48
49
50 #ifdef HAVE_XSHM_EXTENSION
51 # include "resources.h"
52 # include "xshm.h"
53 #endif /* HAVE_XSHM_EXTENSION */
54
55 extern char *progname;
56
57 #include <X11/Xutil.h>
58 #include <sys/time.h>
59
60 #undef MAX
61 #define MAX(a,b) ((a)>(b)?(a):(b))
62
63 #undef countof
64 #define countof(x) (sizeof((x))/sizeof((*x)))
65
66
67 static int debug_p = 0;
68
69 static Bool
70 bigendian (void)
71 {
72   union { int i; char c[sizeof(int)]; } u;
73   u.i = 1;
74   return !u.c[0];
75 }
76
77
78 #ifdef REFORMAT_IMAGE_DATA
79
80 /* Given a bitmask, returns the position and width of the field.
81  */
82 static void
83 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
84 {
85   int i;
86   for (i = 0; i < 32; i++)
87     if (mask & (1L << i))
88       {
89         int j = 0;
90         *pos_ret = i;
91         for (; i < 32; i++, j++)
92           if (! (mask & (1L << i)))
93             break;
94         *size_ret = j;
95         return;
96       }
97 }
98
99
100 /* Given a value and a field-width, expands the field to fill out 8 bits.
101  */
102 static unsigned char
103 spread_bits (unsigned char value, unsigned char width)
104 {
105   switch (width)
106     {
107     case 8: return value;
108     case 7: return (value << 1) | (value >> 6);
109     case 6: return (value << 2) | (value >> 4);
110     case 5: return (value << 3) | (value >> 2);
111     case 4: return (value << 4) | (value);
112     case 3: return (value << 5) | (value << 2) | (value >> 2);
113     case 2: return (value << 6) | (value << 4) | (value);
114     default: abort(); break;
115     }
116 }
117
118
119 static XImage *
120 convert_ximage_to_rgba32 (Screen *screen, XImage *image)
121 {
122   Display *dpy = DisplayOfScreen (screen);
123   Visual *visual = DefaultVisualOfScreen (screen);
124
125   int x, y;
126   unsigned int crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
127   unsigned int srpos=0, sgpos=0, sbpos=0;
128   unsigned int srmsk=0, sgmsk=0, sbmsk=0;
129   unsigned int srsiz=0, sgsiz=0, sbsiz=0;
130   int i;
131   XColor *colors = 0;
132   unsigned char spread_map[3][256];
133
134   /* Note: height+2 in "to" to be to work around an array bounds overrun
135      in gluBuild2DMipmaps / gluScaleImage.
136    */
137   XImage *from = image;
138   XImage *to = XCreateImage (dpy, visual, 32,  /* depth */
139                              ZPixmap, 0, 0, from->width, from->height + 2,
140                              32, /* bitmap pad */
141                              0);
142   to->data = (char *) calloc (to->height, to->bytes_per_line);
143
144   /* Set the bit order in the XImage structure to whatever the
145      local host's native bit order is.
146    */
147   to->bitmap_bit_order =
148     to->byte_order =
149     (bigendian() ? MSBFirst : LSBFirst);
150
151   if (visual_class (screen, visual) == PseudoColor ||
152       visual_class (screen, visual) == GrayScale)
153     {
154       Colormap cmap = DefaultColormapOfScreen (screen);
155       int ncolors = visual_cells (screen, visual);
156       int i;
157       colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
158       for (i = 0; i < ncolors; i++)
159         colors[i].pixel = i;
160       XQueryColors (dpy, cmap, colors, ncolors);
161     }
162
163   if (colors == 0)  /* truecolor */
164     {
165       srmsk = to->red_mask;
166       sgmsk = to->green_mask;
167       sbmsk = to->blue_mask;
168
169       decode_mask (srmsk, &srpos, &srsiz);
170       decode_mask (sgmsk, &sgpos, &sgsiz);
171       decode_mask (sbmsk, &sbpos, &sbsiz);
172     }
173
174   /* Pack things in "RGBA" order in client endianness. */
175   if (bigendian())
176     crpos = 24, cgpos = 16, cbpos =  8, capos =  0;
177   else
178     crpos =  0, cgpos =  8, cbpos = 16, capos = 24;
179
180   if (colors == 0)  /* truecolor */
181     {
182       for (i = 0; i < 256; i++)
183         {
184           spread_map[0][i] = spread_bits (i, srsiz);
185           spread_map[1][i] = spread_bits (i, sgsiz);
186           spread_map[2][i] = spread_bits (i, sbsiz);
187         }
188     }
189
190   for (y = 0; y < from->height; y++)
191     for (x = 0; x < from->width; x++)
192       {
193         unsigned long sp = XGetPixel (from, x, y);
194         unsigned char sr, sg, sb;
195         unsigned long cp;
196
197         if (colors)
198           {
199             sr = colors[sp].red   & 0xFF;
200             sg = colors[sp].green & 0xFF;
201             sb = colors[sp].blue  & 0xFF;
202           }
203         else
204           {
205             sr = (sp & srmsk) >> srpos;
206             sg = (sp & sgmsk) >> sgpos;
207             sb = (sp & sbmsk) >> sbpos;
208
209             sr = spread_map[0][sr];
210             sg = spread_map[1][sg];
211             sb = spread_map[2][sb];
212           }
213
214         cp = ((sr << crpos) |
215               (sg << cgpos) |
216               (sb << cbpos) |
217               (0xFF << capos));
218
219         XPutPixel (to, x, y, cp);
220       }
221
222   if (colors) free (colors);
223
224   return to;
225 }
226
227 #endif /* REFORMAT_IMAGE_DATA */
228
229 /* Shrinks the XImage by a factor of two.
230    We use this when mipmapping fails on large textures.
231  */
232 static void
233 halve_image (XImage *ximage, XRectangle *geom)
234 {
235   int w2 = ximage->width/2;
236   int h2 = ximage->height/2;
237   int x, y;
238   XImage *ximage2;
239
240   if (w2 <= 32 || h2 <= 32)   /* let's not go crazy here, man. */
241     return;
242
243   if (debug_p)
244     fprintf (stderr, "%s: shrinking image %dx%d -> %dx%d\n",
245              progname, ximage->width, ximage->height, w2, h2);
246
247   ximage2 = (XImage *) calloc (1, sizeof (*ximage2));
248   *ximage2 = *ximage;
249   ximage2->width = w2;
250   ximage2->height = h2;
251   ximage2->bytes_per_line = 0;
252   ximage2->data = 0;
253   XInitImage (ximage2);
254
255   ximage2->data = (char *) calloc (h2, ximage2->bytes_per_line);
256   if (!ximage2->data)
257     {
258       fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n",
259                progname, ximage->width, ximage->height, w2, h2);
260       exit (1);
261     }
262
263   for (y = 0; y < h2; y++)
264     for (x = 0; x < w2; x++)
265       XPutPixel (ximage2, x, y, XGetPixel (ximage, x*2, y*2));
266
267   free (ximage->data);
268   *ximage = *ximage2;
269   ximage2->data = 0;
270   XFree (ximage2);
271
272   if (geom)
273     {
274       geom->x /= 2;
275       geom->y /= 2;
276       geom->width  /= 2;
277       geom->height /= 2;
278     }
279 }
280
281
282 #ifdef REFORMAT_IMAGE_DATA
283
284 /* Pulls the Pixmap bits from the server and returns an XImage
285    in some format acceptable to OpenGL.
286  */
287 static XImage *
288 pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
289 {
290   Display *dpy = DisplayOfScreen (screen);
291   Visual *visual = DefaultVisualOfScreen (screen);
292   unsigned int width, height, depth;
293
294 # ifdef HAVE_XSHM_EXTENSION
295   Bool use_shm = get_boolean_resource ("useSHM", "Boolean");
296   XShmSegmentInfo shm_info;
297 # endif /* HAVE_XSHM_EXTENSION */
298
299   XImage *server_ximage = 0;
300   XImage *client_ximage = 0;
301
302   {
303     Window root;
304     int x, y;
305     unsigned int bw;
306     XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
307   }
308
309   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
310    */
311 # ifdef HAVE_XSHM_EXTENSION
312   if (use_shm)
313     {
314       server_ximage = create_xshm_image (dpy, visual, depth,
315                                          ZPixmap, 0, &shm_info,
316                                          width, height);
317       if (server_ximage)
318         XShmGetImage (dpy, pixmap, server_ximage, 0, 0, ~0L);
319       else
320         use_shm = False;
321     }
322 # endif /* HAVE_XSHM_EXTENSION */
323
324   if (!server_ximage)
325     server_ximage = XGetImage (dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
326
327   client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
328
329 # ifdef HAVE_XSHM_EXTENSION
330   if (use_shm)
331     destroy_xshm_image (dpy, server_ximage, &shm_info);
332   else
333 # endif /* HAVE_XSHM_EXTENSION */
334     XDestroyImage (server_ximage);
335
336   return client_ximage;
337 }
338
339
340 # else /* ! REFORMAT_IMAGE_DATA */
341
342 typedef struct {
343   unsigned int depth, red_mask, green_mask, blue_mask;  /* when this... */
344   GLint type, format;                                   /* ...use this. */
345 } conversion_table;
346
347 /* Abbreviate these so that the table entries all fit on one line...
348  */
349 #define BYTE               GL_UNSIGNED_BYTE
350 #define BYTE_2_3_3_REV     GL_UNSIGNED_BYTE_2_3_3_REV
351 #define BYTE_3_3_2         GL_UNSIGNED_BYTE_3_3_2
352 #define INT_10_10_10_2     GL_UNSIGNED_INT_10_10_10_2
353 #define INT_2_10_10_10_REV GL_UNSIGNED_INT_2_10_10_10_REV
354 #define INT_8_8_8_8        GL_UNSIGNED_INT_8_8_8_8
355 #define INT_8_8_8_8_REV    GL_UNSIGNED_INT_8_8_8_8_REV
356 #define SHORT_1_5_5_5_REV  GL_UNSIGNED_SHORT_1_5_5_5_REV
357 #define SHORT_4_4_4_4      GL_UNSIGNED_SHORT_4_4_4_4
358 #define SHORT_4_4_4_4_REV  GL_UNSIGNED_SHORT_4_4_4_4_REV
359 #define SHORT_5_5_5_1      GL_UNSIGNED_SHORT_5_5_5_1
360 #define SHORT_5_6_5        GL_UNSIGNED_SHORT_5_6_5
361 #define SHORT_5_6_5_REV    GL_UNSIGNED_SHORT_5_6_5_REV
362
363 static conversion_table ctable[] = {
364   { 8,  0x000000E0, 0x0000001C, 0x00000003, BYTE_3_3_2,         GL_RGB      },
365   { 8,  0x00000007, 0x00000038, 0x000000C0, BYTE_2_3_3_REV,     GL_RGB      },
366   { 16, 0x0000F800, 0x000007E0, 0x0000001F, SHORT_5_6_5,        GL_RGB      },
367   { 16, 0x0000001F, 0x000007E0, 0x0000F800, SHORT_5_6_5_REV,    GL_RGB      },
368   { 16, 0x0000F000, 0x00000F00, 0x000000F0, SHORT_4_4_4_4,      GL_RGBA     },
369   { 16, 0x000000F0, 0x00000F00, 0x0000F000, SHORT_4_4_4_4,      GL_BGRA     },
370   { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4,      GL_ABGR_EXT },
371   { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4_REV,  GL_RGBA     },
372   { 16, 0x00000F00, 0x000000F0, 0x0000000F, SHORT_4_4_4_4_REV,  GL_BGRA     },
373   { 16, 0x0000F800, 0x000007C0, 0x0000003E, SHORT_5_5_5_1,      GL_RGBA     },
374   { 16, 0x0000003E, 0x000007C0, 0x0000F800, SHORT_5_5_5_1,      GL_BGRA     },
375   { 16, 0x00000001, 0x0000003E, 0x000007C0, SHORT_5_5_5_1,      GL_ABGR_EXT },
376   { 16, 0x0000001F, 0x000003E0, 0x00007C00, SHORT_1_5_5_5_REV,  GL_RGBA     },
377   { 16, 0x00007C00, 0x000003E0, 0x0000001F, SHORT_1_5_5_5_REV,  GL_BGRA     },
378   { 32, 0xFF000000, 0x00FF0000, 0x0000FF00, INT_8_8_8_8,        GL_RGBA     },
379   { 32, 0x0000FF00, 0x00FF0000, 0xFF000000, INT_8_8_8_8,        GL_BGRA     },
380   { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8,        GL_ABGR_EXT },
381   { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8_REV,    GL_RGBA     },
382   { 32, 0x00FF0000, 0x0000FF00, 0x000000FF, INT_8_8_8_8_REV,    GL_BGRA     },
383   { 32, 0xFFC00000, 0x003FF000, 0x00000FFC, INT_10_10_10_2,     GL_RGBA     },
384   { 32, 0x00000FFC, 0x003FF000, 0xFFC00000, INT_10_10_10_2,     GL_BGRA     },
385   { 32, 0x00000003, 0x00000FFC, 0x003FF000, INT_10_10_10_2,     GL_ABGR_EXT },
386   { 32, 0x000003FF, 0x000FFC00, 0x3FF00000, INT_2_10_10_10_REV, GL_RGBA     },
387   { 32, 0x3FF00000, 0x000FFC00, 0x000003FF, INT_2_10_10_10_REV, GL_BGRA     },
388   { 24, 0x000000FF, 0x0000FF00, 0x00FF0000, BYTE,               GL_RGB      },
389   { 24, 0x00FF0000, 0x0000FF00, 0x000000FF, BYTE,               GL_BGR      },
390 };
391
392
393 /* Given an XImage, returns the GL settings to use its data as a texture.
394  */
395 static void
396 gl_settings_for_ximage (XImage *image,
397                         GLint *type_ret, GLint *format_ret, GLint *swap_ret)
398 {
399   int i;
400   for (i = 0; i < countof(ctable); ++i)
401     {
402       if (image->bits_per_pixel == ctable[i].depth &&
403           image->red_mask       == ctable[i].red_mask &&
404           image->green_mask     == ctable[i].green_mask &&
405           image->blue_mask      == ctable[i].blue_mask)
406         {
407           *type_ret   = ctable[i].type;
408           *format_ret = ctable[i].format;
409
410           if (image->bits_per_pixel == 24)
411             {
412               /* don't know how to test this... */
413               *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
414               *swap_ret = 0;
415             }
416           else
417             {
418               *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
419             }
420
421           if (debug_p)
422             {
423               fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
424                        progname,
425                        (*format_ret == GL_RGB      ? "RGB" :
426                         *format_ret == GL_BGR      ? "BGR" :
427                         *format_ret == GL_RGBA     ? "RGBA" :
428                         *format_ret == GL_BGRA     ? "BGRA" :
429                         *format_ret == GL_ABGR_EXT ? "ABGR_EXT" :
430                         "???"),
431                        (*type_ret == BYTE               ? "BYTE" :
432                         *type_ret == BYTE_3_3_2         ? "BYTE_3_3_2" :
433                         *type_ret == BYTE_2_3_3_REV     ? "BYTE_2_3_3_REV" :
434                         *type_ret == INT_8_8_8_8        ? "INT_8_8_8_8" :
435                         *type_ret == INT_8_8_8_8_REV    ? "INT_8_8_8_8_REV" :
436                         *type_ret == INT_10_10_10_2     ? "INT_10_10_10_2" :
437                         *type_ret == INT_2_10_10_10_REV ? "INT_2_10_10_10_REV":
438                         *type_ret == SHORT_4_4_4_4      ? "SHORT_4_4_4_4" :
439                         *type_ret == SHORT_4_4_4_4_REV  ? "SHORT_4_4_4_4_REV" :
440                         *type_ret == SHORT_5_5_5_1      ? "SHORT_5_5_5_1" :
441                         *type_ret == SHORT_1_5_5_5_REV  ? "SHORT_1_5_5_5_REV" :
442                         *type_ret == SHORT_5_6_5        ? "SHORT_5_6_5" :
443                         *type_ret == SHORT_5_6_5_REV    ? "SHORT_5_6_5_REV" :
444                         "???"),
445                        *swap_ret,
446                        image->bits_per_pixel,
447                        image->red_mask, image->green_mask, image->blue_mask);
448             }
449
450           return;
451         }
452     }
453
454   /* Unknown RGB fields? */
455   abort();
456 }
457
458 #endif /* ! REFORMAT_IMAGE_DATA */
459
460 typedef struct {
461   Pixmap pixmap;
462   int pix_width, pix_height, pix_depth;
463   int texid;
464   Bool mipmap_p;
465   double load_time;
466
467   /* Used in async mode
468    */
469   void (*callback) (const char *filename, XRectangle *geometry,
470                     int iw, int ih, int tw, int th,
471                     void *closure);
472   void *closure;
473
474   /* Used in sync mode
475    */
476   char **filename_return;
477   XRectangle *geometry_return;
478   int *image_width_return;
479   int *image_height_return;
480   int *texture_width_return;
481   int *texture_height_return;
482
483 } img_closure;
484
485
486 /* Returns the current time in seconds as a double.
487  */
488 static double
489 double_time (void)
490 {
491   struct timeval now;
492 # ifdef GETTIMEOFDAY_TWO_ARGS
493   struct timezone tzp;
494   gettimeofday(&now, &tzp);
495 # else
496   gettimeofday(&now);
497 # endif
498
499   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
500 }
501
502
503 /* return the next larger power of 2. */
504 static int
505 to_pow2 (int i)
506 {
507   static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
508                                  2048, 4096, 8192, 16384, 32768, 65536 };
509   int j;
510   for (j = 0; j < countof(pow2); j++)
511     if (pow2[j] >= i) return pow2[j];
512   abort();  /* too big! */
513 }
514
515
516 /* Loads the given XImage into GL's texture memory.
517    The image may be of any size.
518    If mipmap_p is true, then make mipmaps instead of just a single texture.
519    Writes to stderr and returns False on error.
520  */
521 static Bool
522 ximage_to_texture (XImage *ximage,
523                    GLint type, GLint format,
524                    int *width_return,
525                    int *height_return,
526                    XRectangle *geometry,
527                    Bool mipmap_p)
528 {
529   int max_reduction = 7;
530   int err_count = 0;
531   GLenum err = 0;
532   int orig_width = ximage->width;
533   int orig_height = ximage->height;
534   int tex_width = 0;
535   int tex_height = 0;
536
537  AGAIN:
538
539   if (mipmap_p)
540     {
541       /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
542       tex_width  = ximage->width;
543       tex_height = ximage->height;
544
545       if (debug_p)
546         fprintf (stderr, "%s: mipmap %d x %d\n",
547                  progname, ximage->width, ximage->height);
548
549       gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
550                          format, type, ximage->data);
551       err = glGetError();
552     }
553   else
554     {
555       /* glTexImage2D() requires the texture sizes to be powers of 2.
556          So first, create a texture of that size (but don't write any
557          data into it.)
558        */
559       tex_width  = to_pow2 (ximage->width);
560       tex_height = to_pow2 (ximage->height);
561
562       if (debug_p)
563         fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
564                  progname, ximage->width, ximage->height,
565                  tex_width, tex_height);
566
567       glTexImage2D (GL_TEXTURE_2D, 0, 3, tex_width, tex_height, 0,
568                     format, type, 0);
569       err = glGetError();
570
571       /* Now load our non-power-of-2 image data into the existing texture. */
572       if (!err)
573         {
574           glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
575                            ximage->width, ximage->height,
576                            GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
577           err = glGetError();
578         }
579     }
580
581   if (err)
582     {
583       char buf[100];
584       const char *s = (char *) gluErrorString (err);
585
586       if (!s || !*s)
587         {
588           sprintf (buf, "unknown error %d", err);
589           s = buf;
590         }
591
592       while (glGetError() != GL_NO_ERROR)
593         ;  /* clear any lingering errors */
594
595       if (++err_count > max_reduction)
596         {
597           fprintf (stderr,
598                    "\n"
599                    "%s: %dx%d texture failed, even after reducing to %dx%d:\n"
600                    "%s: The error was: \"%s\".\n"
601                    "%s: probably this means "
602                    "\"your video card is worthless and weak\"?\n\n",
603                    progname, orig_width, orig_height,
604                    ximage->width, ximage->height,
605                    progname, s,
606                    progname);
607           return False;
608         }
609       else
610         {
611           if (debug_p)
612             fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
613                      progname, ximage->width, ximage->height, s);
614           halve_image (ximage, geometry);
615           goto AGAIN;
616         }
617     }
618
619   if (width_return)  *width_return  = tex_width;
620   if (height_return) *height_return = tex_height;
621   return True;
622 }
623
624
625 static void screen_to_texture_async_cb (Screen *screen,
626                                         Window window, Drawable drawable,
627                                         const char *name, XRectangle *geometry,
628                                         void *closure);
629
630
631 /* Grabs an image of the desktop (or another random image file) and
632    loads tht image into GL's texture memory.
633    Writes to stderr and returns False on error.
634  */
635 Bool
636 screen_to_texture (Screen *screen, Window window,
637                    int desired_width, int desired_height,
638                    Bool mipmap_p,
639                    char **filename_return,
640                    XRectangle *geometry_return,
641                    int *image_width_return,
642                    int *image_height_return,
643                    int *texture_width_return,
644                    int *texture_height_return)
645 {
646   Display *dpy = DisplayOfScreen (screen);
647   img_closure *data = (img_closure *) calloc (1, sizeof(*data));
648   XWindowAttributes xgwa;
649   char *filename = 0;
650   XRectangle geom = { 0, 0, 0, 0 };
651   int wret;
652
653   if (! image_width_return)
654     image_width_return = &wret;
655
656   if (debug_p)
657     data->load_time = double_time();
658
659   data->texid     = -1;
660   data->mipmap_p  = mipmap_p;
661   data->filename_return       = filename_return;
662   data->geometry_return       = geometry_return;
663   data->image_width_return    = image_width_return;
664   data->image_height_return   = image_height_return;
665   data->texture_width_return  = texture_width_return;
666   data->texture_height_return = texture_height_return;
667
668   XGetWindowAttributes (dpy, window, &xgwa);
669   data->pix_width  = xgwa.width;
670   data->pix_height = xgwa.height;
671   data->pix_depth  = xgwa.depth;
672
673   if (desired_width  && desired_width  < xgwa.width)
674     data->pix_width  = desired_width;
675   if (desired_height && desired_height < xgwa.height)
676     data->pix_height = desired_height;
677
678   data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
679                                 data->pix_depth);
680   load_random_image (screen, window, data->pixmap, &filename, &geom);
681   screen_to_texture_async_cb (screen, window, data->pixmap, filename, &geom,
682                               data);
683
684   return (*image_width_return != 0);
685 }
686
687
688 /* Like the above, but the image is loaded in a background process,
689    and a callback is run when the loading is complete.
690    When the callback is called, the image data will have been loaded
691    into texture number `texid' (via glBindTexture.)
692
693    If an error occurred, width/height will be 0.
694  */
695 void
696 screen_to_texture_async (Screen *screen, Window window,
697                          int desired_width, int desired_height,
698                          Bool mipmap_p,
699                          GLuint texid,
700                          void (*callback) (const char *filename,
701                                            XRectangle *geometry,
702                                            int image_width,
703                                            int image_height,
704                                            int texture_width,
705                                            int texture_height,
706                                            void *closure),
707                          void *closure)
708 {
709   Display *dpy = DisplayOfScreen (screen);
710   XWindowAttributes xgwa;
711   img_closure *data = (img_closure *) calloc (1, sizeof(*data));
712
713   if (debug_p)
714     data->load_time = double_time();
715
716   data->texid     = texid;
717   data->mipmap_p  = mipmap_p;
718   data->callback  = callback;
719   data->closure   = closure;
720
721   XGetWindowAttributes (dpy, window, &xgwa);
722   data->pix_width  = xgwa.width;
723   data->pix_height = xgwa.height;
724   data->pix_depth  = xgwa.depth;
725
726   if (desired_width  && desired_width  < xgwa.width)
727     data->pix_width  = desired_width;
728   if (desired_height && desired_height < xgwa.height)
729     data->pix_height = desired_height;
730
731   data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
732                                 data->pix_depth);
733   fork_load_random_image (screen, window, data->pixmap,
734                           screen_to_texture_async_cb, data);
735 }
736
737
738 /* Once we have an XImage, this loads it into GL.
739    This is used in both synchronous and asynchronous mode.
740  */
741 static void
742 screen_to_texture_async_cb (Screen *screen, Window window, Drawable drawable,
743                             const char *name, XRectangle *geometry,
744                             void *closure)
745 {
746   Display *dpy = DisplayOfScreen (screen);
747   Bool ok;
748   XImage *ximage;
749   GLint type, format;
750   int iw=0, ih=0, tw=0, th=0;
751   double cvt_time=0, tex_time=0, done_time=0;
752   img_closure *data = (img_closure *) closure;
753   /* copy closure data to stack and free the original before running cb */
754   img_closure dd = *data;
755   memset (data, 0, sizeof (*data));
756   free (data);
757   data = 0;
758
759   if (geometry->width <= 0 || geometry->height <= 0)
760     {
761       /* This can happen if an old version of xscreensaver-getimage
762          is installed. */
763       geometry->x = 0;
764       geometry->y = 0;
765       geometry->width  = dd.pix_width;
766       geometry->height = dd.pix_height;
767     }
768
769   if (geometry->width <= 0 || geometry->height <= 0)
770     abort();
771
772   if (debug_p)
773     cvt_time = double_time();
774
775 # ifdef REFORMAT_IMAGE_DATA
776   ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
777   format = GL_RGBA;
778   type = GL_UNSIGNED_BYTE;
779
780 #else /* ! REFORMAT_IMAGE_DATA */
781   {
782     Visual *visual = DefaultVisualOfScreen (screen);
783     GLint swap;
784
785     ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
786                            dd.pix_width, dd.pix_height, 32, 0);
787
788     /* Note: height+2 in "to" to be to work around an array bounds overrun
789        in gluBuild2DMipmaps / gluScaleImage. */
790     ximage->data = (char *) calloc (ximage->height+2, ximage->bytes_per_line);
791
792     if (!ximage->data ||
793         !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
794                        ~0L, ximage->format, ximage, 0, 0))
795       {
796         XDestroyImage (ximage);
797         ximage = 0;
798       }
799
800     gl_settings_for_ximage (ximage, &type, &format, &swap);
801     glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
802   }
803 #endif /* REFORMAT_IMAGE_DATA */
804
805   XFreePixmap (dpy, dd.pixmap);
806   dd.pixmap = 0;
807
808   if (debug_p)
809     tex_time = double_time();
810
811   if (! ximage)
812     ok = False;
813   else
814     {
815       iw = ximage->width;
816       ih = ximage->height;
817       if (dd.texid != -1)
818         glBindTexture (GL_TEXTURE_2D, dd.texid);
819
820       glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
821       ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
822                               dd.mipmap_p);
823       if (ok)
824         {
825           iw = ximage->width;   /* in case the image was shrunk */
826           ih = ximage->height;
827         }
828     }
829
830   if (ximage) XDestroyImage (ximage);
831
832   if (! ok)
833     iw = ih = tw = th = 0;
834
835   if (debug_p)
836     done_time = double_time();
837
838   if (debug_p)
839     fprintf (stderr,
840              /* prints: A + B + C = D
841                 A = file I/O time (happens in background)
842                 B = time to pull bits from server (this process)
843                 C = time to convert bits to GL textures (this process)
844                 D = total elapsed time from "want image" to "see image"
845
846                 B+C is responsible for any frame-rate glitches.
847               */
848              "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
849              progname,
850              cvt_time  - dd.load_time,
851              tex_time  - cvt_time,
852              done_time - tex_time,
853              done_time - dd.load_time);
854
855   if (dd.callback)
856     /* asynchronous mode */
857     dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
858   else
859     {
860       /* synchronous mode */
861       if (dd.filename_return)       *dd.filename_return       = (char *) name;
862       if (dd.geometry_return)       *dd.geometry_return       = *geometry;
863       if (dd.image_width_return)    *dd.image_width_return    = iw;
864       if (dd.image_height_return)   *dd.image_height_return   = ih;
865       if (dd.texture_width_return)  *dd.texture_width_return  = tw;
866       if (dd.texture_height_return) *dd.texture_height_return = th;
867     }
868 }