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