a524e7659adb6ed55495592577e1176e08503fb7
[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 static Bool
169 bigendian (void)
170 {
171   union { int i; char c[sizeof(int)]; } u;
172   u.i = 1;
173   return !u.c[0];
174 }
175
176
177 /* The libxpm version of this function...
178  */
179 static XImage *
180 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
181                  const char *filename, char **xpm_data)
182 {
183   /* All we want to do is get RGB data out of the XPM file built in to this
184      program.  This is a pain, because there is no way  (as of XPM version
185      4.6, at least) to get libXpm to make an XImage without also allocating
186      colors with XAllocColor.  So, instead, we create an XpmImage and parse
187      out the RGB values of the pixels ourselves; and construct an XImage
188      by hand.  Regardless of the depth of the visual we're using, this
189      XImage will have 32 bits per pixel, 8 each per R, G, and B.  We put
190      0xFF or 0x00 in the fourth (alpha) slot, depending on the file's mask.
191    */
192   XImage *ximage = 0;
193   XpmImage xpm_image;
194   XpmInfo xpm_info;
195   int result;
196   int transparent_color_index = -1;
197   int x, y, i;
198   int bpl, wpl;
199   XColor colors[256];
200
201   memset (&xpm_image, 0, sizeof(xpm_image));
202   memset (&xpm_info, 0, sizeof(xpm_info));
203
204   if (filename)
205     {
206       xpm_data = 0;
207       if (! XpmReadFileToData ((char *) filename, &xpm_data))
208         {
209           fprintf (stderr, "%s: unable to read XPM file %s\n",
210                    progname, filename);
211           exit (1);
212         }
213     }
214
215   result = XpmCreateXpmImageFromData (xpm_data, &xpm_image, &xpm_info);
216   if (result != XpmSuccess)
217     {
218       fprintf(stderr, "%s: unable to parse xpm data (%d).\n", progname,
219               result);
220       exit (1);
221     }
222
223   if (xpm_image.ncolors > countof(colors))
224     {
225       fprintf (stderr, "%s: too many colors (%d) in XPM.\n",
226                progname, xpm_image.ncolors);
227       exit (1);
228     }
229
230   ximage = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
231                          xpm_image.width, xpm_image.height, 32, 0);
232
233   bpl = ximage->bytes_per_line;
234   wpl = bpl/4;
235
236   ximage->data = (char *) malloc(xpm_image.height * bpl);
237
238   /* Parse the colors in the XPM into RGB values. */
239   for (i = 0; i < xpm_image.ncolors; i++)
240     {
241       const char *c = xpm_image.colorTable[i].c_color;
242       if (!c)
243         {
244           fprintf(stderr, "%s: bogus color table?  (%d)\n", progname, i);
245           exit (1);
246         }
247       else if (!strncasecmp (c, "None", 4))
248         {
249           transparent_color_index = i;
250           colors[transparent_color_index].red   = 0xFF;
251           colors[transparent_color_index].green = 0xFF;
252           colors[transparent_color_index].blue  = 0xFF;
253         }
254       else if (!XParseColor (dpy, cmap, c, &colors[i]))
255         {
256           fprintf(stderr, "%s: unparsable color: %s\n", progname, c);
257           exit (1);
258         }
259     }
260
261   /* Translate the XpmImage to an RGB XImage. */
262   {
263     int rpos, gpos, bpos, apos;  /* bitfield positions */
264
265     /* Note that unlike X, which is endianness-agnostic (since any XImage
266        can have its own specific bit ordering, with the server reversing
267        things as necessary) OpenGL pretends everything is client-side, so
268        we need to pack things in the right order for the client machine.
269      */
270     if (bigendian())
271       rpos = 24, gpos = 16, bpos =  8, apos =  0;
272     else
273       rpos =  0, gpos =  8, bpos = 16, apos = 24;
274
275     for (y = 0; y < xpm_image.height; y++)
276       {
277         int y2 = (xpm_image.height-1-y); /* Texture maps are upside down. */
278
279         unsigned int *oline = (unsigned int *) (ximage->data   + (y  * bpl));
280         unsigned int *iline = (unsigned int *) (xpm_image.data + (y2 * wpl));
281
282         for (x = 0; x < xpm_image.width; x++)
283           {
284             XColor *c = &colors[iline[x]];
285             int alpha = ((iline[x] == transparent_color_index) ? 0x00 : 0xFF);
286             oline[x] = (((c->red   >> 8) << rpos) |
287                         ((c->green >> 8) << gpos) |
288                         ((c->blue  >> 8) << bpos) |
289                         (alpha           << apos));
290           }
291       }
292   }
293
294   /* I sure hope these only free the contents, and not the args. */
295 #if 0  /* Apparently not?  Gotta love those well-documented APIs! */
296   XpmFreeXpmImage (&xpm_image);
297   XpmFreeXpmInfo (&xpm_info);
298 #endif
299
300   return ximage;
301 }
302
303
304 #else  /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
305
306 static XImage *
307 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
308                  const char *filename, char **xpm_data)
309 {
310   fprintf(stderr, "%s: not compiled with XPM or Pixbuf support.\n", progname);
311   exit (1);
312 }
313
314 #endif /* !HAVE_XPM */
315
316
317 /* Returns an XImage structure containing the bits of the given XPM image.
318    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
319    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
320
321    The Display and Visual arguments are used only for creating the XImage;
322    no bits are pushed to the server.
323
324    The Colormap argument is used just for parsing color names; no colors
325    are allocated.
326  */
327 XImage *
328 xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, char **xpm_data)
329 {
330   return xpm_to_ximage_1 (dpy, visual, cmap, 0, xpm_data);
331 }
332
333
334 XImage *
335 xpm_file_to_ximage (Display *dpy, Visual *visual, Colormap cmap,
336                     const char *filename)
337 {
338   return xpm_to_ximage_1 (dpy, visual, cmap, filename, 0);
339 }