X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Fxpm-ximage.c;h=cf600bc7312ed9b618d8f079c01eaef761dba26d;hb=4ade52359b6eba3621566dac79793a33aa4c915f;hp=5913ddddac05b4474b8e34a3a3c20f10badb8ac2;hpb=2a991a811de4c7b22f812682b267b616a809fd9a;p=xscreensaver diff --git a/hacks/glx/xpm-ximage.c b/hacks/glx/xpm-ximage.c index 5913dddd..cf600bc7 100644 --- a/hacks/glx/xpm-ximage.c +++ b/hacks/glx/xpm-ximage.c @@ -1,5 +1,5 @@ /* xpm-ximage.c --- converts XPM data to an XImage for use with OpenGL. - * xscreensaver, Copyright (c) 1998 Jamie Zawinski + * 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 @@ -8,22 +8,29 @@ * 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 -#ifdef HAVE_XPM /* whole file */ - #include #include -#include -#include -#include + +#ifdef HAVE_COCOA +# 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) { @@ -31,20 +38,169 @@ bigendian (void) 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 0xFF. + 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. */ -XImage * -xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data) +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 + g_type_init (); +#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 @@ -53,15 +209,30 @@ xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data) 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 in the fourth slot, as GL will interpret that as "alpha". + 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[255]; + 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) @@ -71,6 +242,13 @@ xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data) 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); @@ -81,12 +259,26 @@ xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data) /* Parse the colors in the XPM into RGB values. */ for (i = 0; i < xpm_image.ncolors; i++) - if (!XParseColor(dpy, cmap, xpm_image.colorTable[i].c_color, &colors[i])) - { - fprintf(stderr, "%s: unparsable color: %s\n", progname, - xpm_image.colorTable[i].c_color); - exit(1); - } + { + 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. */ { @@ -97,45 +289,176 @@ xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data) 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; - for (y = 0; y < xpm_image.height; y++) - { - int y2 = (xpm_image.height-1-y); /* Texture maps are upside down. */ - - unsigned int *oline = (unsigned int *) (ximage->data + (y * bpl)); - unsigned int *iline = (unsigned int *) (xpm_image.data + (y2 * wpl)); - - for (x = 0; x < xpm_image.width; x++) - { - XColor *c = &colors[iline[x]]; - /* pack it as RGBA */ - oline[x] = (((c->red >> 8) << rpos) | - ((c->green >> 8) << gpos) | - ((c->blue >> 8) << bpos) | - (0xFF << apos)); - } - } } /* 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 */ +#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 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; + } +} + + +/* The minixpm version of this function... + */ static XImage * -xpm_to_ximage (char **xpm_data) +xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap, + const char *filename, char **xpm_data) { - fprintf(stderr, "%s: not compiled with XPM support.\n", progname); - exit (1); + 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 int rpos=0, gpos=0, bpos=0, apos=0; + unsigned int rmsk=0, gmsk=0, bmsk=0, amsk=0; + unsigned int 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 long 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); +}