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