/* xpm-ximage.c --- converts XPM data to an XImage for use with OpenGL. * xscreensaver, Copyright (c) 1998-2013 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * Alpha channel support by Eric Lassauge */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #if defined(HAVE_COCOA) || defined(HAVE_ANDROID) # include "jwxyz.h" #else # include #endif #include "xpm-ximage.h" extern char *progname; #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM) static Bool bigendian (void) { union { int i; char c[sizeof(int)]; } u; u.i = 1; return !u.c[0]; } #endif /* HAVE_GDK_PIXBUF || HAVE_XPM */ #if defined(HAVE_GDK_PIXBUF) # include # ifdef HAVE_GTK2 # include # else /* !HAVE_GTK2 */ # include # endif /* !HAVE_GTK2 */ /* Returns an XImage structure containing the bits of the given XPM image. This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.) The Display and Visual arguments are used only for creating the XImage; no bits are pushed to the server. The Colormap argument is used just for parsing color names; no colors are allocated. This is the gdk_pixbuf version of this function. */ static XImage * xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap, const char *filename, char **xpm_data) { GdkPixbuf *pb; static int initted = 0; #ifdef HAVE_GTK2 GError *gerr = NULL; #endif if (!initted) { #ifdef HAVE_GTK2 #if !GLIB_CHECK_VERSION(2, 36 ,0) g_type_init (); #endif #endif gdk_pixbuf_xlib_init (dpy, DefaultScreen (dpy)); xlib_rgb_init (dpy, DefaultScreenOfDisplay (dpy)); initted = 1; } pb = (filename #ifdef HAVE_GTK2 ? gdk_pixbuf_new_from_file (filename, &gerr) #else ? gdk_pixbuf_new_from_file (filename) #endif /* HAVE_GTK2 */ : gdk_pixbuf_new_from_xpm_data ((const char **) xpm_data)); if (pb) { XImage *image; int w = gdk_pixbuf_get_width (pb); int h = gdk_pixbuf_get_height (pb); guchar *row = gdk_pixbuf_get_pixels (pb); int stride = gdk_pixbuf_get_rowstride (pb); int chan = gdk_pixbuf_get_n_channels (pb); int x, y; image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0, w, h, 32, 0); image->data = (char *) malloc(h * image->bytes_per_line); /* Set the bit order in the XImage structure to whatever the local host's native bit order is. */ image->bitmap_bit_order = image->byte_order = (bigendian() ? MSBFirst : LSBFirst); if (!image->data) { fprintf (stderr, "%s: out of memory (%d x %d)\n", progname, w, h); exit (1); } for (y = 0; y < h; y++) { int y2 = (h-1-y); /* Texture maps are upside down. */ guchar *i = row; for (x = 0; x < w; x++) { unsigned long rgba = 0; switch (chan) { case 1: rgba = ((0xFF << 24) | (*i << 16) | (*i << 8) | *i); i++; break; case 3: rgba = ((0xFF << 24) | (i[2] << 16) | (i[1] << 8) | i[0]); i += 3; break; case 4: rgba = ((i[3] << 24) | (i[2] << 16) | (i[1] << 8) | i[0]); i += 4; break; default: abort(); break; } XPutPixel (image, x, y2, rgba); } row += stride; } /* #### are colors getting freed here? */ g_object_unref (pb); return image; } else if (filename) { #ifdef HAVE_GTK2 fprintf (stderr, "%s: %s\n", progname, gerr->message); g_error_free (gerr); #else fprintf (stderr, "%s: unable to load %s\n", progname, filename); #endif /* HAVE_GTK2 */ exit (1); } else { fprintf (stderr, "%s: unable to initialize builtin texture\n", progname); exit (1); } } #elif defined(HAVE_XPM) #include #include #include #include #include #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) /* The libxpm version of this function... */ static XImage * xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap, const char *filename, char **xpm_data) { /* All we want to do is get RGB data out of the XPM file built in to this program. This is a pain, because there is no way (as of XPM version 4.6, at least) to get libXpm to make an XImage without also allocating colors with XAllocColor. So, instead, we create an XpmImage and parse out the RGB values of the pixels ourselves; and construct an XImage by hand. Regardless of the depth of the visual we're using, this XImage will have 32 bits per pixel, 8 each per R, G, and B. We put 0xFF or 0x00 in the fourth (alpha) slot, depending on the file's mask. */ XImage *ximage = 0; XpmImage xpm_image; XpmInfo xpm_info; int result; int transparent_color_index = -1; int x, y, i; int bpl, wpl; XColor colors[256]; memset (&xpm_image, 0, sizeof(xpm_image)); memset (&xpm_info, 0, sizeof(xpm_info)); if (filename) { xpm_data = 0; if (XpmSuccess != XpmReadFileToData ((char *) filename, &xpm_data)) { fprintf (stderr, "%s: unable to read XPM file %s\n", progname, filename); exit (1); } } result = XpmCreateXpmImageFromData (xpm_data, &xpm_image, &xpm_info); if (result != XpmSuccess) { fprintf(stderr, "%s: unable to parse xpm data (%d).\n", progname, result); exit (1); } if (xpm_image.ncolors > countof(colors)) { fprintf (stderr, "%s: too many colors (%d) in XPM.\n", progname, xpm_image.ncolors); exit (1); } ximage = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0, xpm_image.width, xpm_image.height, 32, 0); bpl = ximage->bytes_per_line; wpl = bpl/4; ximage->data = (char *) malloc(xpm_image.height * bpl); /* Parse the colors in the XPM into RGB values. */ for (i = 0; i < xpm_image.ncolors; i++) { const char *c = xpm_image.colorTable[i].c_color; if (!c) { fprintf(stderr, "%s: bogus color table? (%d)\n", progname, i); exit (1); } else if (!strncasecmp (c, "None", 4)) { transparent_color_index = i; colors[transparent_color_index].red = 0xFF; colors[transparent_color_index].green = 0xFF; colors[transparent_color_index].blue = 0xFF; } else if (!XParseColor (dpy, cmap, c, &colors[i])) { fprintf(stderr, "%s: unparsable color: %s\n", progname, c); exit (1); } } /* Translate the XpmImage to an RGB XImage. */ { int rpos, gpos, bpos, apos; /* bitfield positions */ /* Note that unlike X, which is endianness-agnostic (since any XImage can have its own specific bit ordering, with the server reversing things as necessary) OpenGL pretends everything is client-side, so we need to pack things in the right order for the client machine. */ ximage->bitmap_bit_order = ximage->byte_order = (bigendian() ? MSBFirst : LSBFirst); #if 0 /* #### Cherub says that the little-endian case must be taken on MacOSX, or else the colors/alpha are the wrong way around. How can that be the case? */ if (bigendian()) rpos = 24, gpos = 16, bpos = 8, apos = 0; else #endif rpos = 0, gpos = 8, bpos = 16, apos = 24; } /* I sure hope these only free the contents, and not the args. */ #if 0 /* Apparently not? Gotta love those well-documented APIs! */ XpmFreeXpmImage (&xpm_image); XpmFreeXpmInfo (&xpm_info); #endif return ximage; } #else /* !HAVE_XPM && !HAVE_GDK_PIXBUF */ /* If we don't have libXPM or Pixbuf, then use "minixpm". This can read XPM data from memory, but can't read files. */ #include #include #include "minixpm.h" #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) /* Given a bitmask, returns the position and width of the field. */ static void decode_mask (unsigned long mask, unsigned long *pos_ret, unsigned long *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; } } /* The minixpm version of this function... */ static XImage * xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap, const char *filename, char **xpm_data) { int iw, ih, w8, x, y; XImage *ximage; char *data; unsigned char *mask = 0; int depth = 32; unsigned long background_color = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy)); unsigned long *pixels = 0; int npixels = 0; int bpl; unsigned long rpos=0, gpos=0, bpos=0, apos=0; unsigned long rmsk=0, gmsk=0, bmsk=0, amsk=0; unsigned long rsiz=0, gsiz=0, bsiz=0, asiz=0; if (filename) { fprintf(stderr, "%s: no files: not compiled with XPM or Pixbuf support.\n", progname); exit (1); } if (! xpm_data) abort(); ximage = minixpm_to_ximage (dpy, visual, cmap, depth, background_color, (const char * const *) xpm_data, &iw, &ih, &pixels, &npixels, &mask); if (pixels) free (pixels); bpl = ximage->bytes_per_line; data = ximage->data; ximage->data = malloc (ximage->height * bpl); /* Flip image upside down, for texture maps; process the mask; and re-arrange the color components for GL. */ w8 = (ximage->width + 7) / 8; rmsk = ximage->red_mask; gmsk = ximage->green_mask; bmsk = ximage->blue_mask; amsk = ~(rmsk|gmsk|bmsk); decode_mask (rmsk, &rpos, &rsiz); decode_mask (gmsk, &gpos, &gsiz); decode_mask (bmsk, &bpos, &bsiz); decode_mask (amsk, &apos, &asiz); for (y = 0; y < ximage->height; y++) { int y2 = (ximage->height-1-y); unsigned int *oline = (unsigned int *) (ximage->data + (y * bpl)); unsigned int *iline = (unsigned int *) (data + (y2 * bpl)); for (x = 0; x < ximage->width; x++) { unsigned int pixel = iline[x]; unsigned char r = (pixel & rmsk) >> rpos; unsigned char g = (pixel & gmsk) >> gpos; unsigned char b = (pixel & bmsk) >> bpos; unsigned char a = (mask ? ((mask [(y2 * w8) + (x >> 3)] & (1 << (x % 8))) ? 0xFF : 0) : 0xFF); # if 0 pixel = ((r << rpos) | (g << gpos) | (b << bpos) | (a << apos)); # else pixel = ((a << 24) | (b << 16) | (g << 8) | r); # endif oline[x] = pixel; } } free (data); return ximage; } #endif /* !HAVE_XPM */ /* Returns an XImage structure containing the bits of the given XPM image. This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.) The Display and Visual arguments are used only for creating the XImage; no bits are pushed to the server. The Colormap argument is used just for parsing color names; no colors are allocated. */ XImage * xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data) { return xpm_to_ximage_1 (dpy, visual, cmap, 0, xpm_data); } XImage * xpm_file_to_ximage (Display *dpy, Visual *visual, Colormap cmap, const char *filename) { return xpm_to_ximage_1 (dpy, visual, cmap, filename, 0); }