+
+#ifdef REFORMAT_IMAGE_DATA
+
+/* Given a bitmask, returns the position and width of the field.
+ */
+static void
+decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
+{
+ int i;
+ for (i = 0; i < 32; i++)
+ if (mask & (1L << i))
+ {
+ int j = 0;
+ *pos_ret = i;
+ for (; i < 32; i++, j++)
+ if (! (mask & (1L << i)))
+ break;
+ *size_ret = j;
+ return;
+ }
+}
+
+
+/* Given a value and a field-width, expands the field to fill out 8 bits.
+ */
+static unsigned char
+spread_bits (unsigned char value, unsigned char width)
+{
+ switch (width)
+ {
+ case 8: return value;
+ case 7: return (value << 1) | (value >> 6);
+ case 6: return (value << 2) | (value >> 4);
+ case 5: return (value << 3) | (value >> 2);
+ case 4: return (value << 4) | (value);
+ case 3: return (value << 5) | (value << 2) | (value >> 2);
+ case 2: return (value << 6) | (value << 4) | (value);
+ default: abort(); break;
+ }
+}
+
+
+static XImage *
+convert_ximage_to_rgba32 (Screen *screen, XImage *image)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Visual *visual = DefaultVisualOfScreen (screen);
+
+ int x, y;
+ unsigned int crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
+ unsigned int srpos=0, sgpos=0, sbpos=0;
+ unsigned int srmsk=0, sgmsk=0, sbmsk=0;
+ unsigned int srsiz=0, sgsiz=0, sbsiz=0;
+ int i;
+ XColor *colors = 0;
+ unsigned char spread_map[3][256];
+
+ /* Note: height+2 in "to" to be to work around an array bounds overrun
+ in gluBuild2DMipmaps / gluScaleImage.
+ */
+ XImage *from = image;
+ XImage *to = XCreateImage (dpy, visual, 32, /* depth */
+ ZPixmap, 0, 0, from->width, from->height + 2,
+ 32, /* bitmap pad */
+ 0);
+ to->data = (char *) calloc (to->height, to->bytes_per_line);
+
+ /* Set the bit order in the XImage structure to whatever the
+ local host's native bit order is.
+ */
+ to->bitmap_bit_order =
+ to->byte_order =
+ (bigendian() ? MSBFirst : LSBFirst);
+
+ if (visual_class (screen, visual) == PseudoColor ||
+ visual_class (screen, visual) == GrayScale)
+ {
+ Colormap cmap = DefaultColormapOfScreen (screen);
+ int ncolors = visual_cells (screen, visual);
+ int i;
+ colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
+ for (i = 0; i < ncolors; i++)
+ colors[i].pixel = i;
+ XQueryColors (dpy, cmap, colors, ncolors);
+ }
+
+ if (colors == 0) /* truecolor */
+ {
+ srmsk = to->red_mask;
+ sgmsk = to->green_mask;
+ sbmsk = to->blue_mask;
+
+ decode_mask (srmsk, &srpos, &srsiz);
+ decode_mask (sgmsk, &sgpos, &sgsiz);
+ decode_mask (sbmsk, &sbpos, &sbsiz);
+ }
+
+ /* Pack things in "RGBA" order in client endianness. */
+ if (bigendian())
+ crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
+ else
+ crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
+
+ if (colors == 0) /* truecolor */
+ {
+ for (i = 0; i < 256; i++)
+ {
+ spread_map[0][i] = spread_bits (i, srsiz);
+ spread_map[1][i] = spread_bits (i, sgsiz);
+ spread_map[2][i] = spread_bits (i, sbsiz);
+ }
+ }
+
+ for (y = 0; y < from->height; y++)
+ for (x = 0; x < from->width; x++)
+ {
+ unsigned long sp = XGetPixel (from, x, y);
+ unsigned char sr, sg, sb;
+ unsigned long cp;
+
+ if (colors)
+ {
+ sr = colors[sp].red & 0xFF;
+ sg = colors[sp].green & 0xFF;
+ sb = colors[sp].blue & 0xFF;
+ }
+ else
+ {
+ sr = (sp & srmsk) >> srpos;
+ sg = (sp & sgmsk) >> sgpos;
+ sb = (sp & sbmsk) >> sbpos;
+
+ sr = spread_map[0][sr];
+ sg = spread_map[1][sg];
+ sb = spread_map[2][sb];
+ }
+
+ cp = ((sr << crpos) |
+ (sg << cgpos) |
+ (sb << cbpos) |
+ (0xFF << capos));
+
+ XPutPixel (to, x, y, cp);
+ }
+
+ if (colors) free (colors);
+
+ return to;
+}
+
+#endif /* REFORMAT_IMAGE_DATA */
+
+/* Shrinks the XImage by a factor of two.
+ We use this when mipmapping fails on large textures.
+ */
+static void
+halve_image (XImage *ximage, XRectangle *geom)
+{
+ int w2 = ximage->width/2;
+ int h2 = ximage->height/2;
+ int x, y;
+ XImage *ximage2;
+
+ if (w2 <= 32 || h2 <= 32) /* let's not go crazy here, man. */
+ return;
+
+ if (debug_p)
+ fprintf (stderr, "%s: shrinking image %dx%d -> %dx%d\n",
+ progname, ximage->width, ximage->height, w2, h2);
+
+ ximage2 = (XImage *) calloc (1, sizeof (*ximage2));
+ *ximage2 = *ximage;
+ ximage2->width = w2;
+ ximage2->height = h2;
+ ximage2->bytes_per_line = 0;
+ ximage2->data = 0;
+ XInitImage (ximage2);
+
+ ximage2->data = (char *) calloc (h2, ximage2->bytes_per_line);
+ if (!ximage2->data)
+ {
+ fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n",
+ progname, ximage->width, ximage->height, w2, h2);
+ exit (1);
+ }
+
+ for (y = 0; y < h2; y++)
+ for (x = 0; x < w2; x++)
+ XPutPixel (ximage2, x, y, XGetPixel (ximage, x*2, y*2));
+
+ free (ximage->data);
+ *ximage = *ximage2;
+ ximage2->data = 0;
+ XFree (ximage2);
+
+ if (geom)
+ {
+ geom->x /= 2;
+ geom->y /= 2;
+ geom->width /= 2;
+ geom->height /= 2;
+ }
+}
+
+
+#ifdef REFORMAT_IMAGE_DATA
+
+/* Pulls the Pixmap bits from the server and returns an XImage
+ in some format acceptable to OpenGL.
+ */
+static XImage *
+pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ unsigned int width, height, depth;
+
+# ifdef HAVE_XSHM_EXTENSION
+ Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
+ XShmSegmentInfo shm_info;
+# endif /* HAVE_XSHM_EXTENSION */
+
+ XImage *server_ximage = 0;
+ XImage *client_ximage = 0;
+
+ {
+ Window root;
+ int x, y;
+ unsigned int bw;
+ XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
+ }
+
+ if (width < 5 || height < 5) /* something's gone wrong somewhere... */
+ return 0;
+
+ /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
+ */
+# ifdef HAVE_XSHM_EXTENSION
+ if (use_shm)
+ {
+ Visual *visual = DefaultVisualOfScreen (screen);
+ server_ximage = create_xshm_image (dpy, visual, depth,
+ ZPixmap, 0, &shm_info,
+ width, height);
+ if (server_ximage)
+ XShmGetImage (dpy, pixmap, server_ximage, 0, 0, ~0L);
+ else
+ use_shm = False;
+ }
+# endif /* HAVE_XSHM_EXTENSION */
+
+ if (!server_ximage)
+ server_ximage = XGetImage (dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
+
+ client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
+
+# ifdef HAVE_XSHM_EXTENSION
+ if (use_shm)
+ destroy_xshm_image (dpy, server_ximage, &shm_info);
+ else
+# endif /* HAVE_XSHM_EXTENSION */
+ XDestroyImage (server_ximage);
+
+ return client_ximage;
+}
+
+
+# else /* ! REFORMAT_IMAGE_DATA */
+
+typedef struct {
+ unsigned int depth, red_mask, green_mask, blue_mask; /* when this... */
+ GLint type, format; /* ...use this. */
+} conversion_table;
+
+/* Abbreviate these so that the table entries all fit on one line...
+ */
+#define BYTE GL_UNSIGNED_BYTE
+#define BYTE_2_3_3_REV GL_UNSIGNED_BYTE_2_3_3_REV
+#define BYTE_3_3_2 GL_UNSIGNED_BYTE_3_3_2
+#define INT_10_10_10_2 GL_UNSIGNED_INT_10_10_10_2
+#define INT_2_10_10_10_REV GL_UNSIGNED_INT_2_10_10_10_REV
+#define INT_8_8_8_8 GL_UNSIGNED_INT_8_8_8_8
+#define INT_8_8_8_8_REV GL_UNSIGNED_INT_8_8_8_8_REV
+#define SHORT_1_5_5_5_REV GL_UNSIGNED_SHORT_1_5_5_5_REV
+#define SHORT_4_4_4_4 GL_UNSIGNED_SHORT_4_4_4_4
+#define SHORT_4_4_4_4_REV GL_UNSIGNED_SHORT_4_4_4_4_REV
+#define SHORT_5_5_5_1 GL_UNSIGNED_SHORT_5_5_5_1
+#define SHORT_5_6_5 GL_UNSIGNED_SHORT_5_6_5
+#define SHORT_5_6_5_REV GL_UNSIGNED_SHORT_5_6_5_REV
+
+static const conversion_table ctable[] = {
+ { 8, 0x000000E0, 0x0000001C, 0x00000003, BYTE_3_3_2, GL_RGB },
+ { 8, 0x00000007, 0x00000038, 0x000000C0, BYTE_2_3_3_REV, GL_RGB },
+ { 16, 0x0000F800, 0x000007E0, 0x0000001F, SHORT_5_6_5, GL_RGB },
+ { 16, 0x0000001F, 0x000007E0, 0x0000F800, SHORT_5_6_5_REV, GL_RGB },
+ { 16, 0x0000F000, 0x00000F00, 0x000000F0, SHORT_4_4_4_4, GL_RGBA },
+ { 16, 0x000000F0, 0x00000F00, 0x0000F000, SHORT_4_4_4_4, GL_BGRA },
+ { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4, GL_ABGR_EXT },
+ { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4_REV, GL_RGBA },
+ { 16, 0x00000F00, 0x000000F0, 0x0000000F, SHORT_4_4_4_4_REV, GL_BGRA },
+ { 16, 0x0000F800, 0x000007C0, 0x0000003E, SHORT_5_5_5_1, GL_RGBA },
+ { 16, 0x0000003E, 0x000007C0, 0x0000F800, SHORT_5_5_5_1, GL_BGRA },
+ { 16, 0x00000001, 0x0000003E, 0x000007C0, SHORT_5_5_5_1, GL_ABGR_EXT },
+ { 16, 0x0000001F, 0x000003E0, 0x00007C00, SHORT_1_5_5_5_REV, GL_RGBA },
+ { 16, 0x00007C00, 0x000003E0, 0x0000001F, SHORT_1_5_5_5_REV, GL_BGRA },
+ { 32, 0xFF000000, 0x00FF0000, 0x0000FF00, INT_8_8_8_8, GL_RGBA },
+ { 32, 0x0000FF00, 0x00FF0000, 0xFF000000, INT_8_8_8_8, GL_BGRA },
+ { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8, GL_ABGR_EXT },
+ { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8_REV, GL_RGBA },
+ { 32, 0x00FF0000, 0x0000FF00, 0x000000FF, INT_8_8_8_8_REV, GL_BGRA },
+ { 32, 0xFFC00000, 0x003FF000, 0x00000FFC, INT_10_10_10_2, GL_RGBA },
+ { 32, 0x00000FFC, 0x003FF000, 0xFFC00000, INT_10_10_10_2, GL_BGRA },
+ { 32, 0x00000003, 0x00000FFC, 0x003FF000, INT_10_10_10_2, GL_ABGR_EXT },
+ { 32, 0x000003FF, 0x000FFC00, 0x3FF00000, INT_2_10_10_10_REV, GL_RGBA },
+ { 32, 0x3FF00000, 0x000FFC00, 0x000003FF, INT_2_10_10_10_REV, GL_BGRA },
+ { 24, 0x000000FF, 0x0000FF00, 0x00FF0000, BYTE, GL_RGB },
+ { 24, 0x00FF0000, 0x0000FF00, 0x000000FF, BYTE, GL_BGR },
+};
+
+
+/* Given an XImage, returns the GL settings to use its data as a texture.
+ */
+static void
+gl_settings_for_ximage (XImage *image,
+ GLint *type_ret, GLint *format_ret, GLint *swap_ret)
+{
+ int i;
+ for (i = 0; i < countof(ctable); ++i)
+ {
+ if (image->bits_per_pixel == ctable[i].depth &&
+ image->red_mask == ctable[i].red_mask &&
+ image->green_mask == ctable[i].green_mask &&
+ image->blue_mask == ctable[i].blue_mask)
+ {
+ *type_ret = ctable[i].type;
+ *format_ret = ctable[i].format;
+
+ if (image->bits_per_pixel == 24)
+ {
+ /* don't know how to test this... */
+ *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
+ *swap_ret = 0;
+ }
+ else
+ {
+ *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
+ }
+
+ if (debug_p)
+ {
+ fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
+ progname,
+ (*format_ret == GL_RGB ? "RGB" :
+ *format_ret == GL_BGR ? "BGR" :
+ *format_ret == GL_RGBA ? "RGBA" :
+ *format_ret == GL_BGRA ? "BGRA" :
+ *format_ret == GL_ABGR_EXT ? "ABGR_EXT" :
+ "???"),
+ (*type_ret == BYTE ? "BYTE" :
+ *type_ret == BYTE_3_3_2 ? "BYTE_3_3_2" :
+ *type_ret == BYTE_2_3_3_REV ? "BYTE_2_3_3_REV" :
+ *type_ret == INT_8_8_8_8 ? "INT_8_8_8_8" :
+ *type_ret == INT_8_8_8_8_REV ? "INT_8_8_8_8_REV" :
+ *type_ret == INT_10_10_10_2 ? "INT_10_10_10_2" :
+ *type_ret == INT_2_10_10_10_REV ? "INT_2_10_10_10_REV":
+ *type_ret == SHORT_4_4_4_4 ? "SHORT_4_4_4_4" :
+ *type_ret == SHORT_4_4_4_4_REV ? "SHORT_4_4_4_4_REV" :
+ *type_ret == SHORT_5_5_5_1 ? "SHORT_5_5_5_1" :
+ *type_ret == SHORT_1_5_5_5_REV ? "SHORT_1_5_5_5_REV" :
+ *type_ret == SHORT_5_6_5 ? "SHORT_5_6_5" :
+ *type_ret == SHORT_5_6_5_REV ? "SHORT_5_6_5_REV" :
+ "???"),
+ *swap_ret,
+ image->bits_per_pixel,
+ image->red_mask, image->green_mask, image->blue_mask);
+ }
+
+ return;
+ }
+ }
+
+ /* Unknown RGB fields? */
+ abort();
+}
+
+#endif /* ! REFORMAT_IMAGE_DATA */
+
+typedef struct {
+ GLXContext glx_context;
+ Pixmap pixmap;
+ int pix_width, pix_height, pix_depth;
+ int texid;
+ Bool mipmap_p;
+ double load_time;
+
+ /* Used in async mode
+ */
+ void (*callback) (const char *filename, XRectangle *geometry,
+ int iw, int ih, int tw, int th,
+ void *closure);
+ void *closure;
+
+ /* Used in sync mode
+ */
+ char **filename_return;
+ XRectangle *geometry_return;
+ int *image_width_return;
+ int *image_height_return;
+ int *texture_width_return;
+ int *texture_height_return;
+
+} img_closure;
+
+
+/* Returns the current time in seconds as a double.
+ */
+static double
+double_time (void)
+{
+ struct timeval now;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&now, &tzp);
+# else
+ gettimeofday(&now);
+# endif
+
+ return (now.tv_sec + ((double) now.tv_usec * 0.000001));
+}
+
+