615e0cd1672b4ce36d792a520df4a9e34542ca94
[xscreensaver] / hacks / glx / xpm-ximage.c
1 /* xpm-ximage.c --- converts XPM data to an XImage for use with OpenGL.
2  * xscreensaver, Copyright (c) 1998, 2001 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  * Alpha channel support by Eric Lassauge <lassauge@mail.dotcom.fr>.
13  */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <X11/Intrinsic.h>
22
23 extern char *progname;
24
25 #ifdef HAVE_XPM         /* whole file */
26
27 #include <X11/Xutil.h>
28 #include <X11/xpm.h>
29
30 #undef countof
31 #define countof(x) (sizeof((x))/sizeof((*x)))
32
33 static Bool
34 bigendian (void)
35 {
36   union { int i; char c[sizeof(int)]; } u;
37   u.i = 1;
38   return !u.c[0];
39 }
40
41
42 /* Returns an XImage structure containing the bits of the given XPM image.
43    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
44    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
45
46    The Display and Visual arguments are used only for creating the XImage;
47    no bits are pushed to the server.
48
49    The Colormap argument is used just for parsing color names; no colors
50    are allocated.
51  */
52 XImage *
53 xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data)
54 {
55   /* All we want to do is get RGB data out of the XPM file built in to this
56      program.  This is a pain, because there is no way  (as of XPM version
57      4.6, at least) to get libXpm to make an XImage without also allocating
58      colors with XAllocColor.  So, instead, we create an XpmImage and parse
59      out the RGB values of the pixels ourselves; and construct an XImage
60      by hand.  Regardless of the depth of the visual we're using, this
61      XImage will have 32 bits per pixel, 8 each per R, G, and B.  We put
62      0xFF or 0x00 in the fourth (alpha) slot, depending on the file's mask.
63    */
64   XImage *ximage = 0;
65   XpmImage xpm_image;
66   XpmInfo xpm_info;
67   int result;
68   int transparent_color_index = -1;
69   int x, y, i;
70   int bpl, wpl;
71   XColor colors[256];
72
73   memset (&xpm_image, 0, sizeof(xpm_image));
74   memset (&xpm_info, 0, sizeof(xpm_info));
75   result = XpmCreateXpmImageFromData (xpm_data, &xpm_image, &xpm_info);
76   if (result != XpmSuccess)
77     {
78       fprintf(stderr, "%s: unable to parse xpm data (%d).\n", progname,
79               result);
80       exit (1);
81     }
82
83   if (xpm_image.ncolors > countof(colors))
84     {
85       fprintf (stderr, "%s: too many colors (%d) in XPM.\n",
86                progname, xpm_image.ncolors);
87       exit (1);
88     }
89
90   ximage = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
91                          xpm_image.width, xpm_image.height, 32, 0);
92
93   bpl = ximage->bytes_per_line;
94   wpl = bpl/4;
95
96   ximage->data = (char *) malloc(xpm_image.height * bpl);
97
98   /* Parse the colors in the XPM into RGB values. */
99   for (i = 0; i < xpm_image.ncolors; i++)
100     {
101       const char *c = xpm_image.colorTable[i].c_color;
102       if (!c)
103         {
104           fprintf(stderr, "%s: bogus color table?  ($d)\n", progname, i);
105           exit (1);
106         }
107       else if (!strncasecmp (c, "None", 4))
108         {
109           transparent_color_index = i;
110           colors[transparent_color_index].red   = 0xFF;
111           colors[transparent_color_index].green = 0xFF;
112           colors[transparent_color_index].blue  = 0xFF;
113         }
114       else if (!XParseColor (dpy, cmap, c, &colors[i]))
115         {
116           fprintf(stderr, "%s: unparsable color: %s\n", progname, c);
117           exit (1);
118         }
119     }
120
121   /* Translate the XpmImage to an RGB XImage. */
122   {
123     int rpos, gpos, bpos, apos;  /* bitfield positions */
124
125     /* Note that unlike X, which is endianness-agnostic (since any XImage
126        can have its own specific bit ordering, with the server reversing
127        things as necessary) OpenGL pretends everything is client-side, so
128        we need to pack things in the right order for the client machine.
129      */
130     if (bigendian())
131       rpos = 24, gpos = 16, bpos =  8, apos =  0;
132     else
133       rpos =  0, gpos =  8, bpos = 16, apos = 24;
134
135     for (y = 0; y < xpm_image.height; y++)
136       {
137         int y2 = (xpm_image.height-1-y); /* Texture maps are upside down. */
138
139         unsigned int *oline = (unsigned int *) (ximage->data   + (y  * bpl));
140         unsigned int *iline = (unsigned int *) (xpm_image.data + (y2 * wpl));
141
142         for (x = 0; x < xpm_image.width; x++)
143           {
144             XColor *c = &colors[iline[x]];
145             int alpha = ((iline[x] == transparent_color_index) ? 0x00 : 0xFF);
146             oline[x] = (((c->red   >> 8) << rpos) |
147                         ((c->green >> 8) << gpos) |
148                         ((c->blue  >> 8) << bpos) |
149                         (alpha           << apos));
150           }
151       }
152   }
153
154   /* I sure hope these only free the contents, and not the args. */
155 #if 0  /* Apparently not?  Gotta love those well-documented APIs! */
156   XpmFreeXpmImage (&xpm_image);
157   XpmFreeXpmInfo (&xpm_info);
158 #endif
159
160   return ximage;
161 }
162
163
164 #else  /* !HAVE_XPM */
165
166 XImage *
167 xpm_to_ximage (char **xpm_data)
168 {
169   fprintf(stderr, "%s: not compiled with XPM support.\n", progname);
170   exit (1);
171 }
172
173 #endif /* !HAVE_XPM */