cf600bc7312ed9b618d8f079c01eaef761dba26d
[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 int mask, unsigned int *pos_ret, unsigned int *size_ret)
338 {
339   int i;
340   for (i = 0; i < 32; i++)
341     if (mask & (1L << i))
342       {
343         int j = 0;
344         *pos_ret = i;
345         for (; i < 32; i++, j++)
346           if (! (mask & (1L << i)))
347             break;
348         *size_ret = j;
349         return;
350       }
351 }
352
353
354 /* The minixpm version of this function...
355  */
356 static XImage *
357 xpm_to_ximage_1 (Display *dpy, Visual *visual, Colormap cmap,
358                  const char *filename, char **xpm_data)
359 {
360   int iw, ih, w8, x, y;
361   XImage *ximage;
362   char *data;
363   unsigned char *mask = 0;
364   int depth = 32;
365   unsigned long background_color =
366     BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
367   unsigned long *pixels = 0;
368   int npixels = 0;
369   int bpl;
370
371   unsigned int rpos=0, gpos=0, bpos=0, apos=0;
372   unsigned int rmsk=0, gmsk=0, bmsk=0, amsk=0;
373   unsigned int rsiz=0, gsiz=0, bsiz=0, asiz=0;
374
375   if (filename)
376     {
377       fprintf(stderr, 
378               "%s: no files: not compiled with XPM or Pixbuf support.\n", 
379               progname);
380       exit (1);
381     }
382
383   if (! xpm_data) abort();
384   ximage = minixpm_to_ximage (dpy, visual, cmap, depth, background_color,
385                               (const char * const *) xpm_data,
386                               &iw, &ih, &pixels, &npixels, &mask);
387   if (pixels) free (pixels);
388   
389   bpl = ximage->bytes_per_line;
390   data = ximage->data;
391   ximage->data = malloc (ximage->height * bpl);
392   
393   /* Flip image upside down, for texture maps; 
394      process the mask; and re-arrange the color components for GL.
395    */
396   w8 = (ximage->width + 7) / 8;
397
398   rmsk = ximage->red_mask;
399   gmsk = ximage->green_mask;
400   bmsk = ximage->blue_mask;
401   amsk = ~(rmsk|gmsk|bmsk);
402
403   decode_mask (rmsk, &rpos, &rsiz);
404   decode_mask (gmsk, &gpos, &gsiz);
405   decode_mask (bmsk, &bpos, &bsiz);
406   decode_mask (amsk, &apos, &asiz);
407
408   for (y = 0; y < ximage->height; y++)
409     {
410       int y2 = (ximage->height-1-y);
411     
412       unsigned int *oline = (unsigned int *) (ximage->data + (y  * bpl));
413       unsigned int *iline = (unsigned int *) (data         + (y2 * bpl));
414     
415       for (x = 0; x < ximage->width; x++)
416         {
417           unsigned long pixel = iline[x];
418           unsigned char r = (pixel & rmsk) >> rpos;
419           unsigned char g = (pixel & gmsk) >> gpos;
420           unsigned char b = (pixel & bmsk) >> bpos;
421           unsigned char a = (mask
422                              ? ((mask [(y2 * w8) + (x >> 3)] & (1 << (x % 8)))
423                                 ? 0xFF : 0)
424                              : 0xFF);
425 # if 0
426           pixel = ((r << rpos) | (g << gpos) | (b << bpos) | (a << apos));
427 # else
428           pixel = ((a << 24) | (b << 16) | (g <<  8) | r);
429 # endif
430           oline[x] = pixel;
431         }
432     }
433   free (data);
434
435   return ximage;
436 }
437
438 #endif /* !HAVE_XPM */
439
440
441 /* Returns an XImage structure containing the bits of the given XPM image.
442    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
443    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
444
445    The Display and Visual arguments are used only for creating the XImage;
446    no bits are pushed to the server.
447
448    The Colormap argument is used just for parsing color names; no colors
449    are allocated.
450  */
451 XImage *
452 xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, 
453                char **xpm_data)
454 {
455   return xpm_to_ximage_1 (dpy, visual, cmap, 0, xpm_data);
456 }
457
458
459 XImage *
460 xpm_file_to_ximage (Display *dpy, Visual *visual, Colormap cmap,
461                     const char *filename)
462 {
463   return xpm_to_ximage_1 (dpy, visual, cmap, filename, 0);
464 }