37c38eebc516f72df3ae536d83cc18c646244c74
[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-2003 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/Xlib.h>
22
23 extern char *progname;
24
25
26 #if defined(HAVE_GDK_PIXBUF)
27
28 # include <gdk-pixbuf/gdk-pixbuf.h>
29
30 # ifdef HAVE_GTK2
31 #  include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
32 # else  /* !HAVE_GTK2 */
33 #  include <gdk-pixbuf/gdk-pixbuf-xlib.h>
34 # endif /* !HAVE_GTK2 */
35
36
37 /* Returns an XImage structure containing the bits of the given XPM image.
38    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
39    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
40
41    The Display and Visual arguments are used only for creating the XImage;
42    no bits are pushed to the server.
43
44    The Colormap argument is used just for parsing color names; no colors
45    are allocated.
46
47    This is the gdk_pixbuf version of this function.
48  */
49 static XImage *
50 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
51                  const char *filename,
52                  char **xpm_data)
53 {
54   GdkPixbuf *pb;
55   static int initted = 0;
56 #ifdef HAVE_GTK2
57   GError *gerr = NULL;
58 #endif
59
60   if (!initted)
61     {
62 #ifdef HAVE_GTK2
63       g_type_init ();
64 #endif
65       gdk_pixbuf_xlib_init (dpy, DefaultScreen (dpy));
66       xlib_rgb_init (dpy, DefaultScreenOfDisplay (dpy));
67       initted = 1;
68     }
69
70   pb = (filename
71 #ifdef HAVE_GTK2
72         ? gdk_pixbuf_new_from_file (filename, &gerr)
73 #else
74         ? gdk_pixbuf_new_from_file (filename)
75 #endif /* HAVE_GTK2 */
76         : gdk_pixbuf_new_from_xpm_data ((const char **) xpm_data));
77   if (pb)
78     {
79       XImage *image;
80       int w = gdk_pixbuf_get_width (pb);
81       int h = gdk_pixbuf_get_height (pb);
82       guchar *row = gdk_pixbuf_get_pixels (pb);
83       int stride = gdk_pixbuf_get_rowstride (pb);
84       int chan = gdk_pixbuf_get_n_channels (pb);
85       int x, y;
86
87       image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0, w, h, 32, 0);
88       image->data = (char *) malloc(h * image->bytes_per_line);
89       if (!image->data)
90         {
91           fprintf (stderr, "%s: out of memory (%d x %d)\n", progname, w, h);
92           exit (1);
93         }
94
95       for (y = 0; y < h; y++)
96         {
97           int y2 = (h-1-y); /* Texture maps are upside down. */
98
99           guchar *i = row;
100           for (x = 0; x < w; x++)
101             {
102               unsigned long rgba = 0;
103               switch (chan) {
104               case 1:
105                 rgba = ((0xFF << 24) |
106                         (*i << 16) |
107                         (*i << 8) |
108                          *i);
109                 i++;
110                 break;
111               case 3:
112                 rgba = ((0xFF << 24) |
113                         (i[2] << 16) |
114                         (i[1] << 8) |
115                          i[0]);
116                 i += 3;
117                 break;
118               case 4:
119                 rgba = ((i[3] << 24) |
120                         (i[2] << 16) |
121                         (i[1] << 8) |
122                          i[0]);
123                 i += 4;
124                 break;
125               default:
126                 abort();
127                 break;
128               }
129               XPutPixel (image, x, y2, rgba);
130             }
131           row += stride;
132         }
133       gdk_pixbuf_unref (pb); /* #### does doing this free colors? */
134
135       return image;
136     }
137   else if (filename)
138     {
139 #ifdef HAVE_GTK2
140       fprintf (stderr, "%s: %s\n", progname, gerr->message);
141       g_error_free (gerr);
142 #else
143       fprintf (stderr, "%s: unable to load %s\n", progname, filename);
144 #endif /* HAVE_GTK2 */
145       exit (1);
146     }
147   else
148     {
149       fprintf (stderr, "%s: unable to initialize builtin texture\n", progname);
150       exit (1);
151     }
152 }
153
154
155 #elif defined(HAVE_XPM)
156
157
158 #include <stdlib.h>
159 #include <stdio.h>
160 #include <X11/Intrinsic.h>
161
162 #include <X11/Xutil.h>
163 #include <X11/xpm.h>
164
165 #undef countof
166 #define countof(x) (sizeof((x))/sizeof((*x)))
167
168 #if 0
169 static Bool
170 bigendian (void)
171 {
172   union { int i; char c[sizeof(int)]; } u;
173   u.i = 1;
174   return !u.c[0];
175 }
176 #endif
177
178
179 /* The libxpm version of this function...
180  */
181 static XImage *
182 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
183                  const char *filename, char **xpm_data)
184 {
185   /* All we want to do is get RGB data out of the XPM file built in to this
186      program.  This is a pain, because there is no way  (as of XPM version
187      4.6, at least) to get libXpm to make an XImage without also allocating
188      colors with XAllocColor.  So, instead, we create an XpmImage and parse
189      out the RGB values of the pixels ourselves; and construct an XImage
190      by hand.  Regardless of the depth of the visual we're using, this
191      XImage will have 32 bits per pixel, 8 each per R, G, and B.  We put
192      0xFF or 0x00 in the fourth (alpha) slot, depending on the file's mask.
193    */
194   XImage *ximage = 0;
195   XpmImage xpm_image;
196   XpmInfo xpm_info;
197   int result;
198   int transparent_color_index = -1;
199   int x, y, i;
200   int bpl, wpl;
201   XColor colors[256];
202
203   memset (&xpm_image, 0, sizeof(xpm_image));
204   memset (&xpm_info, 0, sizeof(xpm_info));
205
206   if (filename)
207     {
208       xpm_data = 0;
209       if (! XpmReadFileToData ((char *) filename, &xpm_data))
210         {
211           fprintf (stderr, "%s: unable to read XPM file %s\n",
212                    progname, filename);
213           exit (1);
214         }
215     }
216
217   result = XpmCreateXpmImageFromData (xpm_data, &xpm_image, &xpm_info);
218   if (result != XpmSuccess)
219     {
220       fprintf(stderr, "%s: unable to parse xpm data (%d).\n", progname,
221               result);
222       exit (1);
223     }
224
225   if (xpm_image.ncolors > countof(colors))
226     {
227       fprintf (stderr, "%s: too many colors (%d) in XPM.\n",
228                progname, xpm_image.ncolors);
229       exit (1);
230     }
231
232   ximage = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
233                          xpm_image.width, xpm_image.height, 32, 0);
234
235   bpl = ximage->bytes_per_line;
236   wpl = bpl/4;
237
238   ximage->data = (char *) malloc(xpm_image.height * bpl);
239
240   /* Parse the colors in the XPM into RGB values. */
241   for (i = 0; i < xpm_image.ncolors; i++)
242     {
243       const char *c = xpm_image.colorTable[i].c_color;
244       if (!c)
245         {
246           fprintf(stderr, "%s: bogus color table?  (%d)\n", progname, i);
247           exit (1);
248         }
249       else if (!strncasecmp (c, "None", 4))
250         {
251           transparent_color_index = i;
252           colors[transparent_color_index].red   = 0xFF;
253           colors[transparent_color_index].green = 0xFF;
254           colors[transparent_color_index].blue  = 0xFF;
255         }
256       else if (!XParseColor (dpy, cmap, c, &colors[i]))
257         {
258           fprintf(stderr, "%s: unparsable color: %s\n", progname, c);
259           exit (1);
260         }
261     }
262
263   /* Translate the XpmImage to an RGB XImage. */
264   {
265     int rpos, gpos, bpos, apos;  /* bitfield positions */
266
267     /* Note that unlike X, which is endianness-agnostic (since any XImage
268        can have its own specific bit ordering, with the server reversing
269        things as necessary) OpenGL pretends everything is client-side, so
270        we need to pack things in the right order for the client machine.
271      */
272 #if 0
273     /* #### Cherub says that the little-endian case must be taken on MacOSX,
274             or else the colors/alpha are the wrong way around.  How can
275             that be the case?
276      */
277     if (bigendian())
278       rpos = 24, gpos = 16, bpos =  8, apos =  0;
279     else
280 #endif
281       rpos =  0, gpos =  8, bpos = 16, apos = 24;
282
283     for (y = 0; y < xpm_image.height; y++)
284       {
285         int y2 = (xpm_image.height-1-y); /* Texture maps are upside down. */
286
287         unsigned int *oline = (unsigned int *) (ximage->data   + (y  * bpl));
288         unsigned int *iline = (unsigned int *) (xpm_image.data + (y2 * wpl));
289
290         for (x = 0; x < xpm_image.width; x++)
291           {
292             XColor *c = &colors[iline[x]];
293             int alpha = ((iline[x] == transparent_color_index) ? 0x00 : 0xFF);
294             oline[x] = (((c->red   >> 8) << rpos) |
295                         ((c->green >> 8) << gpos) |
296                         ((c->blue  >> 8) << bpos) |
297                         (alpha           << apos));
298           }
299       }
300   }
301
302   /* I sure hope these only free the contents, and not the args. */
303 #if 0  /* Apparently not?  Gotta love those well-documented APIs! */
304   XpmFreeXpmImage (&xpm_image);
305   XpmFreeXpmInfo (&xpm_info);
306 #endif
307
308   return ximage;
309 }
310
311
312 #else  /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
313
314 static XImage *
315 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
316                  const char *filename, char **xpm_data)
317 {
318   fprintf(stderr, "%s: not compiled with XPM or Pixbuf support.\n", progname);
319   exit (1);
320 }
321
322 #endif /* !HAVE_XPM */
323
324
325 /* Returns an XImage structure containing the bits of the given XPM image.
326    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
327    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
328
329    The Display and Visual arguments are used only for creating the XImage;
330    no bits are pushed to the server.
331
332    The Colormap argument is used just for parsing color names; no colors
333    are allocated.
334  */
335 XImage *
336 xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data)
337 {
338   return xpm_to_ximage_1 (dpy, visual, cmap, 0, xpm_data);
339 }
340
341
342 XImage *
343 xpm_file_to_ximage (Display *dpy, Visual *visual, Colormap cmap,
344                     const char *filename)
345 {
346   return xpm_to_ximage_1 (dpy, visual, cmap, filename, 0);
347 }