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