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