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>
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
21 #include <X11/Xutil.h>
22 #include <GL/gl.h> /* only for GLfloat */
23 #include <GL/glu.h> /* for gluBuild2DMipmaps */
25 #include "grabscreen.h"
28 /* If REFORMAT_IMAGE_DATA is defined, then we convert Pixmaps to textures
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.
35 If undefined, we do this:
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.
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!
47 #define REFORMAT_IMAGE_DATA
50 #ifdef HAVE_XSHM_EXTENSION
51 # include "resources.h"
53 #endif /* HAVE_XSHM_EXTENSION */
55 extern char *progname;
57 #include <X11/Xutil.h>
61 #define MAX(a,b) ((a)>(b)?(a):(b))
64 #define countof(x) (sizeof((x))/sizeof((*x)))
67 static int debug_p = 0;
72 union { int i; char c[sizeof(int)]; } u;
78 #ifdef REFORMAT_IMAGE_DATA
80 /* Given a bitmask, returns the position and width of the field.
83 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
86 for (i = 0; i < 32; i++)
91 for (; i < 32; i++, j++)
92 if (! (mask & (1L << i)))
100 /* Given a value and a field-width, expands the field to fill out 8 bits.
103 spread_bits (unsigned char value, unsigned char width)
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;
120 convert_ximage_to_rgba32 (Screen *screen, XImage *image)
122 Display *dpy = DisplayOfScreen (screen);
123 Visual *visual = DefaultVisualOfScreen (screen);
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;
132 unsigned char spread_map[3][256];
134 /* Note: height+2 in "to" to be to work around an array bounds overrun
135 in gluBuild2DMipmaps / gluScaleImage.
137 XImage *from = image;
138 XImage *to = XCreateImage (dpy, visual, 32, /* depth */
139 ZPixmap, 0, 0, from->width, from->height + 2,
142 to->data = (char *) calloc (to->height, to->bytes_per_line);
144 /* Set the bit order in the XImage structure to whatever the
145 local host's native bit order is.
147 to->bitmap_bit_order =
149 (bigendian() ? MSBFirst : LSBFirst);
151 if (visual_class (screen, visual) == PseudoColor ||
152 visual_class (screen, visual) == GrayScale)
154 Colormap cmap = DefaultColormapOfScreen (screen);
155 int ncolors = visual_cells (screen, visual);
157 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
158 for (i = 0; i < ncolors; i++)
160 XQueryColors (dpy, cmap, colors, ncolors);
163 if (colors == 0) /* truecolor */
165 srmsk = to->red_mask;
166 sgmsk = to->green_mask;
167 sbmsk = to->blue_mask;
169 decode_mask (srmsk, &srpos, &srsiz);
170 decode_mask (sgmsk, &sgpos, &sgsiz);
171 decode_mask (sbmsk, &sbpos, &sbsiz);
174 /* Pack things in "RGBA" order in client endianness. */
176 crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
178 crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
180 if (colors == 0) /* truecolor */
182 for (i = 0; i < 256; i++)
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);
190 for (y = 0; y < from->height; y++)
191 for (x = 0; x < from->width; x++)
193 unsigned long sp = XGetPixel (from, x, y);
194 unsigned char sr, sg, sb;
199 sr = colors[sp].red & 0xFF;
200 sg = colors[sp].green & 0xFF;
201 sb = colors[sp].blue & 0xFF;
205 sr = (sp & srmsk) >> srpos;
206 sg = (sp & sgmsk) >> sgpos;
207 sb = (sp & sbmsk) >> sbpos;
209 sr = spread_map[0][sr];
210 sg = spread_map[1][sg];
211 sb = spread_map[2][sb];
214 cp = ((sr << crpos) |
219 XPutPixel (to, x, y, cp);
222 if (colors) free (colors);
227 #endif /* REFORMAT_IMAGE_DATA */
229 /* Shrinks the XImage by a factor of two.
230 We use this when mipmapping fails on large textures.
233 halve_image (XImage *ximage, XRectangle *geom)
235 int w2 = ximage->width/2;
236 int h2 = ximage->height/2;
240 if (w2 <= 32 || h2 <= 32) /* let's not go crazy here, man. */
244 fprintf (stderr, "%s: shrinking image %dx%d -> %dx%d\n",
245 progname, ximage->width, ximage->height, w2, h2);
247 ximage2 = (XImage *) calloc (1, sizeof (*ximage2));
250 ximage2->height = h2;
251 ximage2->bytes_per_line = 0;
253 XInitImage (ximage2);
255 ximage2->data = (char *) calloc (h2, ximage2->bytes_per_line);
258 fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n",
259 progname, ximage->width, ximage->height, w2, h2);
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));
282 #ifdef REFORMAT_IMAGE_DATA
284 /* Pulls the Pixmap bits from the server and returns an XImage
285 in some format acceptable to OpenGL.
288 pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
290 Display *dpy = DisplayOfScreen (screen);
291 Visual *visual = DefaultVisualOfScreen (screen);
292 unsigned int width, height, depth;
294 # ifdef HAVE_XSHM_EXTENSION
295 Bool use_shm = get_boolean_resource ("useSHM", "Boolean");
296 XShmSegmentInfo shm_info;
297 # endif /* HAVE_XSHM_EXTENSION */
299 XImage *server_ximage = 0;
300 XImage *client_ximage = 0;
306 XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
309 /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
311 # ifdef HAVE_XSHM_EXTENSION
314 server_ximage = create_xshm_image (dpy, visual, depth,
315 ZPixmap, 0, &shm_info,
318 XShmGetImage (dpy, pixmap, server_ximage, 0, 0, ~0L);
322 # endif /* HAVE_XSHM_EXTENSION */
325 server_ximage = XGetImage (dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
327 client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
329 # ifdef HAVE_XSHM_EXTENSION
331 destroy_xshm_image (dpy, server_ximage, &shm_info);
333 # endif /* HAVE_XSHM_EXTENSION */
334 XDestroyImage (server_ximage);
336 return client_ximage;
340 # else /* ! REFORMAT_IMAGE_DATA */
343 unsigned int depth, red_mask, green_mask, blue_mask; /* when this... */
344 GLint type, format; /* ...use this. */
347 /* Abbreviate these so that the table entries all fit on one line...
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
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 },
393 /* Given an XImage, returns the GL settings to use its data as a texture.
396 gl_settings_for_ximage (XImage *image,
397 GLint *type_ret, GLint *format_ret, GLint *swap_ret)
400 for (i = 0; i < countof(ctable); ++i)
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)
407 *type_ret = ctable[i].type;
408 *format_ret = ctable[i].format;
410 if (image->bits_per_pixel == 24)
412 /* don't know how to test this... */
413 *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
418 *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
423 fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
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" :
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" :
446 image->bits_per_pixel,
447 image->red_mask, image->green_mask, image->blue_mask);
454 /* Unknown RGB fields? */
458 #endif /* ! REFORMAT_IMAGE_DATA */
462 int pix_width, pix_height, pix_depth;
467 /* Used in async mode
469 void (*callback) (const char *filename, XRectangle *geometry,
470 int iw, int ih, int tw, int th,
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;
486 /* Returns the current time in seconds as a double.
492 # ifdef GETTIMEOFDAY_TWO_ARGS
494 gettimeofday(&now, &tzp);
499 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
503 /* return the next larger power of 2. */
507 static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
508 2048, 4096, 8192, 16384, 32768, 65536 };
510 for (j = 0; j < countof(pow2); j++)
511 if (pow2[j] >= i) return pow2[j];
512 abort(); /* too big! */
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.
522 ximage_to_texture (XImage *ximage,
523 GLint type, GLint format,
526 XRectangle *geometry,
529 int max_reduction = 7;
532 int orig_width = ximage->width;
533 int orig_height = ximage->height;
541 /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
542 tex_width = ximage->width;
543 tex_height = ximage->height;
546 fprintf (stderr, "%s: mipmap %d x %d\n",
547 progname, ximage->width, ximage->height);
549 gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
550 format, type, ximage->data);
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
559 tex_width = to_pow2 (ximage->width);
560 tex_height = to_pow2 (ximage->height);
563 fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
564 progname, ximage->width, ximage->height,
565 tex_width, tex_height);
567 glTexImage2D (GL_TEXTURE_2D, 0, 3, tex_width, tex_height, 0,
571 /* Now load our non-power-of-2 image data into the existing texture. */
574 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
575 ximage->width, ximage->height,
576 GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
584 const char *s = (char *) gluErrorString (err);
588 sprintf (buf, "unknown error %d", err);
592 while (glGetError() != GL_NO_ERROR)
593 ; /* clear any lingering errors */
595 if (++err_count > max_reduction)
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,
612 fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
613 progname, ximage->width, ximage->height, s);
614 halve_image (ximage, geometry);
619 if (width_return) *width_return = tex_width;
620 if (height_return) *height_return = tex_height;
625 static void screen_to_texture_async_cb (Screen *screen,
626 Window window, Drawable drawable,
627 const char *name, XRectangle *geometry,
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.
636 screen_to_texture (Screen *screen, Window window,
637 int desired_width, int desired_height,
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)
646 Display *dpy = DisplayOfScreen (screen);
647 img_closure *data = (img_closure *) calloc (1, sizeof(*data));
648 XWindowAttributes xgwa;
650 XRectangle geom = { 0, 0, 0, 0 };
653 if (! image_width_return)
654 image_width_return = &wret;
657 data->load_time = double_time();
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;
668 XGetWindowAttributes (dpy, window, &xgwa);
669 data->pix_width = xgwa.width;
670 data->pix_height = xgwa.height;
671 data->pix_depth = xgwa.depth;
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;
678 data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
680 load_random_image (screen, window, data->pixmap, &filename, &geom);
681 screen_to_texture_async_cb (screen, window, data->pixmap, filename, &geom,
684 return (*image_width_return != 0);
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.)
693 If an error occurred, width/height will be 0.
696 screen_to_texture_async (Screen *screen, Window window,
697 int desired_width, int desired_height,
700 void (*callback) (const char *filename,
701 XRectangle *geometry,
709 Display *dpy = DisplayOfScreen (screen);
710 XWindowAttributes xgwa;
711 img_closure *data = (img_closure *) calloc (1, sizeof(*data));
714 data->load_time = double_time();
717 data->mipmap_p = mipmap_p;
718 data->callback = callback;
719 data->closure = closure;
721 XGetWindowAttributes (dpy, window, &xgwa);
722 data->pix_width = xgwa.width;
723 data->pix_height = xgwa.height;
724 data->pix_depth = xgwa.depth;
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;
731 data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
733 fork_load_random_image (screen, window, data->pixmap,
734 screen_to_texture_async_cb, data);
738 /* Once we have an XImage, this loads it into GL.
739 This is used in both synchronous and asynchronous mode.
742 screen_to_texture_async_cb (Screen *screen, Window window, Drawable drawable,
743 const char *name, XRectangle *geometry,
746 Display *dpy = DisplayOfScreen (screen);
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));
759 if (geometry->width <= 0 || geometry->height <= 0)
761 /* This can happen if an old version of xscreensaver-getimage
765 geometry->width = dd.pix_width;
766 geometry->height = dd.pix_height;
769 if (geometry->width <= 0 || geometry->height <= 0)
773 cvt_time = double_time();
775 # ifdef REFORMAT_IMAGE_DATA
776 ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
778 type = GL_UNSIGNED_BYTE;
780 #else /* ! REFORMAT_IMAGE_DATA */
782 Visual *visual = DefaultVisualOfScreen (screen);
785 ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
786 dd.pix_width, dd.pix_height, 32, 0);
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);
793 !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
794 ~0L, ximage->format, ximage, 0, 0))
796 XDestroyImage (ximage);
800 gl_settings_for_ximage (ximage, &type, &format, &swap);
801 glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
803 #endif /* REFORMAT_IMAGE_DATA */
805 XFreePixmap (dpy, dd.pixmap);
809 tex_time = double_time();
818 glBindTexture (GL_TEXTURE_2D, dd.texid);
820 glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
821 ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
825 iw = ximage->width; /* in case the image was shrunk */
830 if (ximage) XDestroyImage (ximage);
833 iw = ih = tw = th = 0;
836 done_time = double_time();
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"
846 B+C is responsible for any frame-rate glitches.
848 "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
850 cvt_time - dd.load_time,
852 done_time - tex_time,
853 done_time - dd.load_time);
856 /* asynchronous mode */
857 dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
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;