4fea33858b00f236d553597bc115f26fe7e8bb48
[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-2006 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   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
323    */
324 # ifdef HAVE_XSHM_EXTENSION
325   if (use_shm)
326     {
327       Visual *visual = DefaultVisualOfScreen (screen);
328       server_ximage = create_xshm_image (dpy, visual, depth,
329                                          ZPixmap, 0, &shm_info,
330                                          width, height);
331       if (server_ximage)
332         XShmGetImage (dpy, pixmap, server_ximage, 0, 0, ~0L);
333       else
334         use_shm = False;
335     }
336 # endif /* HAVE_XSHM_EXTENSION */
337
338   if (!server_ximage)
339     server_ximage = XGetImage (dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
340
341   client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
342
343 # ifdef HAVE_XSHM_EXTENSION
344   if (use_shm)
345     destroy_xshm_image (dpy, server_ximage, &shm_info);
346   else
347 # endif /* HAVE_XSHM_EXTENSION */
348     XDestroyImage (server_ximage);
349
350   return client_ximage;
351 }
352
353
354 # else /* ! REFORMAT_IMAGE_DATA */
355
356 typedef struct {
357   unsigned int depth, red_mask, green_mask, blue_mask;  /* when this... */
358   GLint type, format;                                   /* ...use this. */
359 } conversion_table;
360
361 /* Abbreviate these so that the table entries all fit on one line...
362  */
363 #define BYTE               GL_UNSIGNED_BYTE
364 #define BYTE_2_3_3_REV     GL_UNSIGNED_BYTE_2_3_3_REV
365 #define BYTE_3_3_2         GL_UNSIGNED_BYTE_3_3_2
366 #define INT_10_10_10_2     GL_UNSIGNED_INT_10_10_10_2
367 #define INT_2_10_10_10_REV GL_UNSIGNED_INT_2_10_10_10_REV
368 #define INT_8_8_8_8        GL_UNSIGNED_INT_8_8_8_8
369 #define INT_8_8_8_8_REV    GL_UNSIGNED_INT_8_8_8_8_REV
370 #define SHORT_1_5_5_5_REV  GL_UNSIGNED_SHORT_1_5_5_5_REV
371 #define SHORT_4_4_4_4      GL_UNSIGNED_SHORT_4_4_4_4
372 #define SHORT_4_4_4_4_REV  GL_UNSIGNED_SHORT_4_4_4_4_REV
373 #define SHORT_5_5_5_1      GL_UNSIGNED_SHORT_5_5_5_1
374 #define SHORT_5_6_5        GL_UNSIGNED_SHORT_5_6_5
375 #define SHORT_5_6_5_REV    GL_UNSIGNED_SHORT_5_6_5_REV
376
377 static const conversion_table ctable[] = {
378   { 8,  0x000000E0, 0x0000001C, 0x00000003, BYTE_3_3_2,         GL_RGB      },
379   { 8,  0x00000007, 0x00000038, 0x000000C0, BYTE_2_3_3_REV,     GL_RGB      },
380   { 16, 0x0000F800, 0x000007E0, 0x0000001F, SHORT_5_6_5,        GL_RGB      },
381   { 16, 0x0000001F, 0x000007E0, 0x0000F800, SHORT_5_6_5_REV,    GL_RGB      },
382   { 16, 0x0000F000, 0x00000F00, 0x000000F0, SHORT_4_4_4_4,      GL_RGBA     },
383   { 16, 0x000000F0, 0x00000F00, 0x0000F000, SHORT_4_4_4_4,      GL_BGRA     },
384   { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4,      GL_ABGR_EXT },
385   { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4_REV,  GL_RGBA     },
386   { 16, 0x00000F00, 0x000000F0, 0x0000000F, SHORT_4_4_4_4_REV,  GL_BGRA     },
387   { 16, 0x0000F800, 0x000007C0, 0x0000003E, SHORT_5_5_5_1,      GL_RGBA     },
388   { 16, 0x0000003E, 0x000007C0, 0x0000F800, SHORT_5_5_5_1,      GL_BGRA     },
389   { 16, 0x00000001, 0x0000003E, 0x000007C0, SHORT_5_5_5_1,      GL_ABGR_EXT },
390   { 16, 0x0000001F, 0x000003E0, 0x00007C00, SHORT_1_5_5_5_REV,  GL_RGBA     },
391   { 16, 0x00007C00, 0x000003E0, 0x0000001F, SHORT_1_5_5_5_REV,  GL_BGRA     },
392   { 32, 0xFF000000, 0x00FF0000, 0x0000FF00, INT_8_8_8_8,        GL_RGBA     },
393   { 32, 0x0000FF00, 0x00FF0000, 0xFF000000, INT_8_8_8_8,        GL_BGRA     },
394   { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8,        GL_ABGR_EXT },
395   { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8_REV,    GL_RGBA     },
396   { 32, 0x00FF0000, 0x0000FF00, 0x000000FF, INT_8_8_8_8_REV,    GL_BGRA     },
397   { 32, 0xFFC00000, 0x003FF000, 0x00000FFC, INT_10_10_10_2,     GL_RGBA     },
398   { 32, 0x00000FFC, 0x003FF000, 0xFFC00000, INT_10_10_10_2,     GL_BGRA     },
399   { 32, 0x00000003, 0x00000FFC, 0x003FF000, INT_10_10_10_2,     GL_ABGR_EXT },
400   { 32, 0x000003FF, 0x000FFC00, 0x3FF00000, INT_2_10_10_10_REV, GL_RGBA     },
401   { 32, 0x3FF00000, 0x000FFC00, 0x000003FF, INT_2_10_10_10_REV, GL_BGRA     },
402   { 24, 0x000000FF, 0x0000FF00, 0x00FF0000, BYTE,               GL_RGB      },
403   { 24, 0x00FF0000, 0x0000FF00, 0x000000FF, BYTE,               GL_BGR      },
404 };
405
406
407 /* Given an XImage, returns the GL settings to use its data as a texture.
408  */
409 static void
410 gl_settings_for_ximage (XImage *image,
411                         GLint *type_ret, GLint *format_ret, GLint *swap_ret)
412 {
413   int i;
414   for (i = 0; i < countof(ctable); ++i)
415     {
416       if (image->bits_per_pixel == ctable[i].depth &&
417           image->red_mask       == ctable[i].red_mask &&
418           image->green_mask     == ctable[i].green_mask &&
419           image->blue_mask      == ctable[i].blue_mask)
420         {
421           *type_ret   = ctable[i].type;
422           *format_ret = ctable[i].format;
423
424           if (image->bits_per_pixel == 24)
425             {
426               /* don't know how to test this... */
427               *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
428               *swap_ret = 0;
429             }
430           else
431             {
432               *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
433             }
434
435           if (debug_p)
436             {
437               fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
438                        progname,
439                        (*format_ret == GL_RGB      ? "RGB" :
440                         *format_ret == GL_BGR      ? "BGR" :
441                         *format_ret == GL_RGBA     ? "RGBA" :
442                         *format_ret == GL_BGRA     ? "BGRA" :
443                         *format_ret == GL_ABGR_EXT ? "ABGR_EXT" :
444                         "???"),
445                        (*type_ret == BYTE               ? "BYTE" :
446                         *type_ret == BYTE_3_3_2         ? "BYTE_3_3_2" :
447                         *type_ret == BYTE_2_3_3_REV     ? "BYTE_2_3_3_REV" :
448                         *type_ret == INT_8_8_8_8        ? "INT_8_8_8_8" :
449                         *type_ret == INT_8_8_8_8_REV    ? "INT_8_8_8_8_REV" :
450                         *type_ret == INT_10_10_10_2     ? "INT_10_10_10_2" :
451                         *type_ret == INT_2_10_10_10_REV ? "INT_2_10_10_10_REV":
452                         *type_ret == SHORT_4_4_4_4      ? "SHORT_4_4_4_4" :
453                         *type_ret == SHORT_4_4_4_4_REV  ? "SHORT_4_4_4_4_REV" :
454                         *type_ret == SHORT_5_5_5_1      ? "SHORT_5_5_5_1" :
455                         *type_ret == SHORT_1_5_5_5_REV  ? "SHORT_1_5_5_5_REV" :
456                         *type_ret == SHORT_5_6_5        ? "SHORT_5_6_5" :
457                         *type_ret == SHORT_5_6_5_REV    ? "SHORT_5_6_5_REV" :
458                         "???"),
459                        *swap_ret,
460                        image->bits_per_pixel,
461                        image->red_mask, image->green_mask, image->blue_mask);
462             }
463
464           return;
465         }
466     }
467
468   /* Unknown RGB fields? */
469   abort();
470 }
471
472 #endif /* ! REFORMAT_IMAGE_DATA */
473
474 typedef struct {
475   GLXContext glx_context;
476   Pixmap pixmap;
477   int pix_width, pix_height, pix_depth;
478   int texid;
479   Bool mipmap_p;
480   double load_time;
481
482   /* Used in async mode
483    */
484   void (*callback) (const char *filename, XRectangle *geometry,
485                     int iw, int ih, int tw, int th,
486                     void *closure);
487   void *closure;
488
489   /* Used in sync mode
490    */
491   char **filename_return;
492   XRectangle *geometry_return;
493   int *image_width_return;
494   int *image_height_return;
495   int *texture_width_return;
496   int *texture_height_return;
497
498 } img_closure;
499
500
501 /* Returns the current time in seconds as a double.
502  */
503 static double
504 double_time (void)
505 {
506   struct timeval now;
507 # ifdef GETTIMEOFDAY_TWO_ARGS
508   struct timezone tzp;
509   gettimeofday(&now, &tzp);
510 # else
511   gettimeofday(&now);
512 # endif
513
514   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
515 }
516
517
518 /* return the next larger power of 2. */
519 static int
520 to_pow2 (int i)
521 {
522   static const unsigned int pow2[] = { 
523     1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 
524     2048, 4096, 8192, 16384, 32768, 65536 };
525   int j;
526   for (j = 0; j < countof(pow2); j++)
527     if (pow2[j] >= i) return pow2[j];
528   abort();  /* too big! */
529 }
530
531
532 /* Loads the given XImage into GL's texture memory.
533    The image may be of any size.
534    If mipmap_p is true, then make mipmaps instead of just a single texture.
535    Writes to stderr and returns False on error.
536  */
537 static Bool
538 ximage_to_texture (XImage *ximage,
539                    GLint type, GLint format,
540                    int *width_return,
541                    int *height_return,
542                    XRectangle *geometry,
543                    Bool mipmap_p)
544 {
545   int max_reduction = 7;
546   int err_count = 0;
547   GLenum err = 0;
548   int orig_width = ximage->width;
549   int orig_height = ximage->height;
550   int tex_width = 0;
551   int tex_height = 0;
552
553  AGAIN:
554
555   if (mipmap_p)
556     {
557       /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
558       tex_width  = ximage->width;
559       tex_height = ximage->height;
560
561       if (debug_p)
562         fprintf (stderr, "%s: mipmap %d x %d\n",
563                  progname, ximage->width, ximage->height);
564
565       gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
566                          format, type, ximage->data);
567       err = glGetError();
568     }
569   else
570     {
571       /* glTexImage2D() requires the texture sizes to be powers of 2.
572          So first, create a texture of that size (but don't write any
573          data into it.)
574        */
575       tex_width  = to_pow2 (ximage->width);
576       tex_height = to_pow2 (ximage->height);
577
578       if (debug_p)
579         fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
580                  progname, ximage->width, ximage->height,
581                  tex_width, tex_height);
582
583       glTexImage2D (GL_TEXTURE_2D, 0, 3, tex_width, tex_height, 0,
584                     format, type, 0);
585       err = glGetError();
586
587       /* Now load our non-power-of-2 image data into the existing texture. */
588       if (!err)
589         {
590           glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
591                            ximage->width, ximage->height,
592                            GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
593           err = glGetError();
594         }
595     }
596
597   if (err)
598     {
599       char buf[100];
600       const char *s = (char *) gluErrorString (err);
601
602       if (!s || !*s)
603         {
604           sprintf (buf, "unknown error %d", (int) err);
605           s = buf;
606         }
607
608       while (glGetError() != GL_NO_ERROR)
609         ;  /* clear any lingering errors */
610
611       if (++err_count > max_reduction)
612         {
613           fprintf (stderr,
614                    "\n"
615                    "%s: %dx%d texture failed, even after reducing to %dx%d:\n"
616                    "%s: The error was: \"%s\".\n"
617                    "%s: probably this means "
618                    "\"your video card is worthless and weak\"?\n\n",
619                    progname, orig_width, orig_height,
620                    ximage->width, ximage->height,
621                    progname, s,
622                    progname);
623           return False;
624         }
625       else
626         {
627           if (debug_p)
628             fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
629                      progname, ximage->width, ximage->height, s);
630           halve_image (ximage, geometry);
631           goto AGAIN;
632         }
633     }
634
635   if (width_return)  *width_return  = tex_width;
636   if (height_return) *height_return = tex_height;
637   return True;
638 }
639
640
641 static void load_texture_async_cb (Screen *screen,
642                                         Window window, Drawable drawable,
643                                         const char *name, XRectangle *geometry,
644                                         void *closure);
645
646
647 /* Grabs an image of the desktop (or another random image file) and
648    loads the image into GL's texture memory.
649    When the callback is called, the image data will have been loaded
650    into texture number `texid' (via glBindTexture.)
651
652    If an error occurred, width/height will be 0.
653  */
654 void
655 load_texture_async (Screen *screen, Window window,
656                     GLXContext glx_context,
657                     int desired_width, int desired_height,
658                     Bool mipmap_p,
659                     GLuint texid,
660                     void (*callback) (const char *filename,
661                                       XRectangle *geometry,
662                                       int image_width,
663                                       int image_height,
664                                       int texture_width,
665                                       int texture_height,
666                                       void *closure),
667                     void *closure)
668 {
669   Display *dpy = DisplayOfScreen (screen);
670   XWindowAttributes xgwa;
671   img_closure *data = (img_closure *) calloc (1, sizeof(*data));
672
673   if (debug_p)
674     data->load_time = double_time();
675
676   data->texid       = texid;
677   data->mipmap_p    = mipmap_p;
678   data->glx_context = glx_context;
679   data->callback    = callback;
680   data->closure     = closure;
681
682   XGetWindowAttributes (dpy, window, &xgwa);
683   data->pix_width  = xgwa.width;
684   data->pix_height = xgwa.height;
685   data->pix_depth  = xgwa.depth;
686
687   if (desired_width  && desired_width  < xgwa.width)
688     data->pix_width  = desired_width;
689   if (desired_height && desired_height < xgwa.height)
690     data->pix_height = desired_height;
691
692   data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
693                                 data->pix_depth);
694   load_image_async (screen, window, data->pixmap, 
695                     load_texture_async_cb, data);
696 }
697
698
699 /* Once we have an XImage, this loads it into GL.
700    This is used in both synchronous and asynchronous mode.
701  */
702 static void
703 load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
704                        const char *name, XRectangle *geometry, void *closure)
705 {
706   Display *dpy = DisplayOfScreen (screen);
707   Bool ok;
708   XImage *ximage;
709   GLint type, format;
710   int iw=0, ih=0, tw=0, th=0;
711   double cvt_time=0, tex_time=0, done_time=0;
712   img_closure *data = (img_closure *) closure;
713   /* copy closure data to stack and free the original before running cb */
714   img_closure dd = *data;
715   memset (data, 0, sizeof (*data));
716   free (data);
717   data = 0;
718
719   if (dd.glx_context)
720     glXMakeCurrent (dpy, window, dd.glx_context);
721
722   if (geometry->width <= 0 || geometry->height <= 0)
723     {
724       /* This can happen if an old version of xscreensaver-getimage
725          is installed. */
726       geometry->x = 0;
727       geometry->y = 0;
728       geometry->width  = dd.pix_width;
729       geometry->height = dd.pix_height;
730     }
731
732   if (geometry->width <= 0 || geometry->height <= 0)
733     abort();
734
735   if (debug_p)
736     cvt_time = double_time();
737
738 # ifdef REFORMAT_IMAGE_DATA
739   ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
740   format = GL_RGBA;
741   type = GL_UNSIGNED_BYTE;
742
743 #else /* ! REFORMAT_IMAGE_DATA */
744   {
745     Visual *visual = DefaultVisualOfScreen (screen);
746     GLint swap;
747
748     ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
749                            dd.pix_width, dd.pix_height, 32, 0);
750
751     /* Note: height+2 in "to" to be to work around an array bounds overrun
752        in gluBuild2DMipmaps / gluScaleImage. */
753     ximage->data = (char *) calloc (ximage->height+2, ximage->bytes_per_line);
754
755     if (!ximage->data ||
756         !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
757                        ~0L, ximage->format, ximage, 0, 0))
758       {
759         XDestroyImage (ximage);
760         ximage = 0;
761       }
762
763     gl_settings_for_ximage (ximage, &type, &format, &swap);
764     glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
765   }
766 #endif /* REFORMAT_IMAGE_DATA */
767
768   XFreePixmap (dpy, dd.pixmap);
769   dd.pixmap = 0;
770
771   if (debug_p)
772     tex_time = double_time();
773
774   if (! ximage)
775     ok = False;
776   else
777     {
778       iw = ximage->width;
779       ih = ximage->height;
780       if (dd.texid != -1)
781         glBindTexture (GL_TEXTURE_2D, dd.texid);
782
783       glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
784       ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
785                               dd.mipmap_p);
786       if (ok)
787         {
788           iw = ximage->width;   /* in case the image was shrunk */
789           ih = ximage->height;
790         }
791     }
792
793   if (ximage) XDestroyImage (ximage);
794
795   if (! ok)
796     iw = ih = tw = th = 0;
797
798   if (debug_p)
799     done_time = double_time();
800
801   if (debug_p)
802     fprintf (stderr,
803              /* prints: A + B + C = D
804                 A = file I/O time (happens in background)
805                 B = time to pull bits from server (this process)
806                 C = time to convert bits to GL textures (this process)
807                 D = total elapsed time from "want image" to "see image"
808
809                 B+C is responsible for any frame-rate glitches.
810               */
811              "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
812              progname,
813              cvt_time  - dd.load_time,
814              tex_time  - cvt_time,
815              done_time - tex_time,
816              done_time - dd.load_time);
817
818   if (dd.callback)
819     /* asynchronous mode */
820     dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
821   else
822     {
823       /* synchronous mode */
824       if (dd.filename_return)       *dd.filename_return       = (char *) name;
825       if (dd.geometry_return)       *dd.geometry_return       = *geometry;
826       if (dd.image_width_return)    *dd.image_width_return    = iw;
827       if (dd.image_height_return)   *dd.image_height_return   = ih;
828       if (dd.texture_width_return)  *dd.texture_width_return  = tw;
829       if (dd.texture_height_return) *dd.texture_height_return = th;
830     }
831 }