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