From http://www.jwz.org/xscreensaver/xscreensaver-5.27.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-2013 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
22 #ifdef HAVE_COCOA
23 # include "jwxyz.h"
24 #else
25 # include <X11/Xlib.h>
26 #endif
27
28 #include "xpm-ximage.h"
29
30 extern char *progname;
31
32
33 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
34 static Bool
35 bigendian (void)
36 {
37   union { int i; char c[sizeof(int)]; } u;
38   u.i = 1;
39   return !u.c[0];
40 }
41 #endif /* HAVE_GDK_PIXBUF || HAVE_XPM */
42
43
44 #if defined(HAVE_GDK_PIXBUF)
45
46 # include <gdk-pixbuf/gdk-pixbuf.h>
47
48 # ifdef HAVE_GTK2
49 #  include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
50 # else  /* !HAVE_GTK2 */
51 #  include <gdk-pixbuf/gdk-pixbuf-xlib.h>
52 # endif /* !HAVE_GTK2 */
53
54
55 /* Returns an XImage structure containing the bits of the given XPM image.
56    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
57    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
58
59    The Display and Visual arguments are used only for creating the XImage;
60    no bits are pushed to the server.
61
62    The Colormap argument is used just for parsing color names; no colors
63    are allocated.
64
65    This is the gdk_pixbuf version of this function.
66  */
67 static XImage *
68 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
69                  const char *filename,
70                  char **xpm_data)
71 {
72   GdkPixbuf *pb;
73   static int initted = 0;
74 #ifdef HAVE_GTK2
75   GError *gerr = NULL;
76 #endif
77
78   if (!initted)
79     {
80 #ifdef HAVE_GTK2
81       g_type_init ();
82 #endif
83       gdk_pixbuf_xlib_init (dpy, DefaultScreen (dpy));
84       xlib_rgb_init (dpy, DefaultScreenOfDisplay (dpy));
85       initted = 1;
86     }
87
88   pb = (filename
89 #ifdef HAVE_GTK2
90         ? gdk_pixbuf_new_from_file (filename, &gerr)
91 #else
92         ? gdk_pixbuf_new_from_file (filename)
93 #endif /* HAVE_GTK2 */
94         : gdk_pixbuf_new_from_xpm_data ((const char **) xpm_data));
95   if (pb)
96     {
97       XImage *image;
98       int w = gdk_pixbuf_get_width (pb);
99       int h = gdk_pixbuf_get_height (pb);
100       guchar *row = gdk_pixbuf_get_pixels (pb);
101       int stride = gdk_pixbuf_get_rowstride (pb);
102       int chan = gdk_pixbuf_get_n_channels (pb);
103       int x, y;
104
105       image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0, w, h, 32, 0);
106       image->data = (char *) malloc(h * image->bytes_per_line);
107
108       /* Set the bit order in the XImage structure to whatever the
109          local host's native bit order is.
110        */
111       image->bitmap_bit_order =
112         image->byte_order =
113           (bigendian() ? MSBFirst : LSBFirst);
114
115
116       if (!image->data)
117         {
118           fprintf (stderr, "%s: out of memory (%d x %d)\n", progname, w, h);
119           exit (1);
120         }
121
122       for (y = 0; y < h; y++)
123         {
124           int y2 = (h-1-y); /* Texture maps are upside down. */
125
126           guchar *i = row;
127           for (x = 0; x < w; x++)
128             {
129               unsigned long rgba = 0;
130               switch (chan) {
131               case 1:
132                 rgba = ((0xFF << 24) |
133                         (*i << 16) |
134                         (*i << 8) |
135                          *i);
136                 i++;
137                 break;
138               case 3:
139                 rgba = ((0xFF << 24) |
140                         (i[2] << 16) |
141                         (i[1] << 8) |
142                          i[0]);
143                 i += 3;
144                 break;
145               case 4:
146                 rgba = ((i[3] << 24) |
147                         (i[2] << 16) |
148                         (i[1] << 8) |
149                          i[0]);
150                 i += 4;
151                 break;
152               default:
153                 abort();
154                 break;
155               }
156               XPutPixel (image, x, y2, rgba);
157             }
158           row += stride;
159         }
160
161       /* #### are colors getting freed here? */
162       g_object_unref (pb);
163
164       return image;
165     }
166   else if (filename)
167     {
168 #ifdef HAVE_GTK2
169       fprintf (stderr, "%s: %s\n", progname, gerr->message);
170       g_error_free (gerr);
171 #else
172       fprintf (stderr, "%s: unable to load %s\n", progname, filename);
173 #endif /* HAVE_GTK2 */
174       exit (1);
175     }
176   else
177     {
178       fprintf (stderr, "%s: unable to initialize builtin texture\n", progname);
179       exit (1);
180     }
181 }
182
183
184 #elif defined(HAVE_XPM)
185
186
187 #include <stdlib.h>
188 #include <stdio.h>
189 #include <X11/Intrinsic.h>
190
191 #include <X11/Xutil.h>
192 #include <X11/xpm.h>
193
194 #undef countof
195 #define countof(x) (sizeof((x))/sizeof((*x)))
196
197
198
199 /* The libxpm version of this function...
200  */
201 static XImage *
202 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
203                  const char *filename, char **xpm_data)
204 {
205   /* All we want to do is get RGB data out of the XPM file built in to this
206      program.  This is a pain, because there is no way  (as of XPM version
207      4.6, at least) to get libXpm to make an XImage without also allocating
208      colors with XAllocColor.  So, instead, we create an XpmImage and parse
209      out the RGB values of the pixels ourselves; and construct an XImage
210      by hand.  Regardless of the depth of the visual we're using, this
211      XImage will have 32 bits per pixel, 8 each per R, G, and B.  We put
212      0xFF or 0x00 in the fourth (alpha) slot, depending on the file's mask.
213    */
214   XImage *ximage = 0;
215   XpmImage xpm_image;
216   XpmInfo xpm_info;
217   int result;
218   int transparent_color_index = -1;
219   int x, y, i;
220   int bpl, wpl;
221   XColor colors[256];
222
223   memset (&xpm_image, 0, sizeof(xpm_image));
224   memset (&xpm_info, 0, sizeof(xpm_info));
225
226   if (filename)
227     {
228       xpm_data = 0;
229       if (XpmSuccess != XpmReadFileToData ((char *) filename, &xpm_data))
230         {
231           fprintf (stderr, "%s: unable to read XPM file %s\n",
232                    progname, filename);
233           exit (1);
234         }
235     }
236
237   result = XpmCreateXpmImageFromData (xpm_data, &xpm_image, &xpm_info);
238   if (result != XpmSuccess)
239     {
240       fprintf(stderr, "%s: unable to parse xpm data (%d).\n", progname,
241               result);
242       exit (1);
243     }
244
245   if (xpm_image.ncolors > countof(colors))
246     {
247       fprintf (stderr, "%s: too many colors (%d) in XPM.\n",
248                progname, xpm_image.ncolors);
249       exit (1);
250     }
251
252   ximage = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
253                          xpm_image.width, xpm_image.height, 32, 0);
254
255   bpl = ximage->bytes_per_line;
256   wpl = bpl/4;
257
258   ximage->data = (char *) malloc(xpm_image.height * bpl);
259
260   /* Parse the colors in the XPM into RGB values. */
261   for (i = 0; i < xpm_image.ncolors; i++)
262     {
263       const char *c = xpm_image.colorTable[i].c_color;
264       if (!c)
265         {
266           fprintf(stderr, "%s: bogus color table?  (%d)\n", progname, i);
267           exit (1);
268         }
269       else if (!strncasecmp (c, "None", 4))
270         {
271           transparent_color_index = i;
272           colors[transparent_color_index].red   = 0xFF;
273           colors[transparent_color_index].green = 0xFF;
274           colors[transparent_color_index].blue  = 0xFF;
275         }
276       else if (!XParseColor (dpy, cmap, c, &colors[i]))
277         {
278           fprintf(stderr, "%s: unparsable color: %s\n", progname, c);
279           exit (1);
280         }
281     }
282
283   /* Translate the XpmImage to an RGB XImage. */
284   {
285     int rpos, gpos, bpos, apos;  /* bitfield positions */
286
287     /* Note that unlike X, which is endianness-agnostic (since any XImage
288        can have its own specific bit ordering, with the server reversing
289        things as necessary) OpenGL pretends everything is client-side, so
290        we need to pack things in the right order for the client machine.
291      */
292
293     ximage->bitmap_bit_order =
294       ximage->byte_order =
295         (bigendian() ? MSBFirst : LSBFirst);
296
297 #if 0
298     /* #### Cherub says that the little-endian case must be taken on MacOSX,
299             or else the colors/alpha are the wrong way around.  How can
300             that be the case?
301      */
302     if (bigendian())
303       rpos = 24, gpos = 16, bpos =  8, apos =  0;
304     else
305 #endif
306       rpos =  0, gpos =  8, bpos = 16, apos = 24;
307
308   }
309
310   /* I sure hope these only free the contents, and not the args. */
311 #if 0  /* Apparently not?  Gotta love those well-documented APIs! */
312   XpmFreeXpmImage (&xpm_image);
313   XpmFreeXpmInfo (&xpm_info);
314 #endif
315
316   return ximage;
317 }
318
319
320 #else  /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
321
322 /* If we don't have libXPM or Pixbuf, then use "minixpm".
323    This can read XPM data from memory, but can't read files.
324  */
325
326 #include <stdlib.h>
327 #include <stdio.h>
328 #include "minixpm.h"
329
330 #undef countof
331 #define countof(x) (sizeof((x))/sizeof((*x)))
332
333
334 /* Given a bitmask, returns the position and width of the field.
335  */
336 static void
337 decode_mask (unsigned long mask, unsigned long *pos_ret,
338              unsigned long *size_ret)
339 {
340   int i;
341   for (i = 0; i < 32; i++)
342     if (mask & (1L << i))
343       {
344         int j = 0;
345         *pos_ret = i;
346         for (; i < 32; i++, j++)
347           if (! (mask & (1L << i)))
348             break;
349         *size_ret = j;
350         return;
351       }
352 }
353
354
355 /* The minixpm version of this function...
356  */
357 static XImage *
358 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
359                  const char *filename, char **xpm_data)
360 {
361   int iw, ih, w8, x, y;
362   XImage *ximage;
363   char *data;
364   unsigned char *mask = 0;
365   int depth = 32;
366   unsigned long background_color =
367     BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
368   unsigned long *pixels = 0;
369   int npixels = 0;
370   int bpl;
371
372   unsigned long rpos=0, gpos=0, bpos=0, apos=0;
373   unsigned long rmsk=0, gmsk=0, bmsk=0, amsk=0;
374   unsigned long rsiz=0, gsiz=0, bsiz=0, asiz=0;
375
376   if (filename)
377     {
378       fprintf(stderr, 
379               "%s: no files: not compiled with XPM or Pixbuf support.\n", 
380               progname);
381       exit (1);
382     }
383
384   if (! xpm_data) abort();
385   ximage = minixpm_to_ximage (dpy, visual, cmap, depth, background_color,
386                               (const char * const *) xpm_data,
387                               &iw, &ih, &pixels, &npixels, &mask);
388   if (pixels) free (pixels);
389   
390   bpl = ximage->bytes_per_line;
391   data = ximage->data;
392   ximage->data = malloc (ximage->height * bpl);
393   
394   /* Flip image upside down, for texture maps; 
395      process the mask; and re-arrange the color components for GL.
396    */
397   w8 = (ximage->width + 7) / 8;
398
399   rmsk = ximage->red_mask;
400   gmsk = ximage->green_mask;
401   bmsk = ximage->blue_mask;
402   amsk = ~(rmsk|gmsk|bmsk);
403
404   decode_mask (rmsk, &rpos, &rsiz);
405   decode_mask (gmsk, &gpos, &gsiz);
406   decode_mask (bmsk, &bpos, &bsiz);
407   decode_mask (amsk, &apos, &asiz);
408
409   for (y = 0; y < ximage->height; y++)
410     {
411       int y2 = (ximage->height-1-y);
412     
413       unsigned int *oline = (unsigned int *) (ximage->data + (y  * bpl));
414       unsigned int *iline = (unsigned int *) (data         + (y2 * bpl));
415     
416       for (x = 0; x < ximage->width; x++)
417         {
418           unsigned int pixel = iline[x];
419           unsigned char r = (pixel & rmsk) >> rpos;
420           unsigned char g = (pixel & gmsk) >> gpos;
421           unsigned char b = (pixel & bmsk) >> bpos;
422           unsigned char a = (mask
423                              ? ((mask [(y2 * w8) + (x >> 3)] & (1 << (x % 8)))
424                                 ? 0xFF : 0)
425                              : 0xFF);
426 # if 0
427           pixel = ((r << rpos) | (g << gpos) | (b << bpos) | (a << apos));
428 # else
429           pixel = ((a << 24) | (b << 16) | (g <<  8) | r);
430 # endif
431           oline[x] = pixel;
432         }
433     }
434   free (data);
435
436   return ximage;
437 }
438
439 #endif /* !HAVE_XPM */
440
441
442 /* Returns an XImage structure containing the bits of the given XPM image.
443    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
444    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
445
446    The Display and Visual arguments are used only for creating the XImage;
447    no bits are pushed to the server.
448
449    The Colormap argument is used just for parsing color names; no colors
450    are allocated.
451  */
452 XImage *
453 xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, 
454                char **xpm_data)
455 {
456   return xpm_to_ximage_1 (dpy, visual, cmap, 0, xpm_data);
457 }
458
459
460 XImage *
461 xpm_file_to_ximage (Display *dpy, Visual *visual, Colormap cmap,
462                     const char *filename)
463 {
464   return xpm_to_ximage_1 (dpy, visual, cmap, filename, 0);
465 }