From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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         for (y = 0; y < ximage->height; y++)
399           for (x = 0; x < ximage->width; x++) {
400             unsigned long p = XGetPixel (ximage, x, y);
401             unsigned long b = (p >> 24) & 0xFF;
402             unsigned long g = (p >> 16) & 0xFF;
403             unsigned long r = (p >>  8) & 0xFF;
404             unsigned long a = (p >>  0) & 0xFF;
405             p = (a << 24) | (r << 16) | (g << 8) | (b << 0);
406             XPutPixel (ximage, x, y, p);
407           }
408       }
409
410     }
411
412     XFreePixmap (dpy, pixmap);
413
414     if (! ximage)
415       fprintf (stderr, "%s: %s failed\n", progname, filename);
416     return ximage;
417   }
418 # endif /* HAVE_COCOA */
419
420   if (filename)
421     {
422       fprintf(stderr, 
423               "%s: can't load %s: not compiled with XPM or Pixbuf support.\n", 
424               progname, filename);
425       return 0;
426     }
427
428   if (! xpm_data) abort();
429   ximage = minixpm_to_ximage (dpy, visual, cmap, depth, background_color,
430                               (const char * const *) xpm_data,
431                               &iw, &ih, &pixels, &npixels, &mask);
432   if (!ximage) abort();
433   if (pixels) free (pixels);
434   
435   bpl = ximage->bytes_per_line;
436   data = ximage->data;
437   ximage->data = malloc (ximage->height * bpl);
438   
439   /* Flip image upside down, for texture maps; 
440      process the mask; and re-arrange the color components for GL.
441    */
442   w8 = (ximage->width + 7) / 8;
443
444   rmsk = ximage->red_mask;
445   gmsk = ximage->green_mask;
446   bmsk = ximage->blue_mask;
447   amsk = ~(rmsk|gmsk|bmsk);
448
449   decode_mask (rmsk, &rpos, &rsiz);
450   decode_mask (gmsk, &gpos, &gsiz);
451   decode_mask (bmsk, &bpos, &bsiz);
452   decode_mask (amsk, &apos, &asiz);
453
454   for (y = 0; y < ximage->height; y++)
455     {
456       int y2 = (ximage->height-1-y);
457     
458       unsigned int *oline = (unsigned int *) (ximage->data + (y  * bpl));
459       unsigned int *iline = (unsigned int *) (data         + (y2 * bpl));
460     
461       for (x = 0; x < ximage->width; x++)
462         {
463           unsigned int pixel = iline[x];
464           unsigned char r = (pixel & rmsk) >> rpos;
465           unsigned char g = (pixel & gmsk) >> gpos;
466           unsigned char b = (pixel & bmsk) >> bpos;
467           unsigned char a = (mask
468                              ? ((mask [(y2 * w8) + (x >> 3)] & (1 << (x % 8)))
469                                 ? 0xFF : 0)
470                              : 0xFF);
471 # if 0
472           pixel = ((r << rpos) | (g << gpos) | (b << bpos) | (a << apos));
473 # else
474           pixel = ((a << 24) | (b << 16) | (g <<  8) | r);
475 # endif
476           oline[x] = pixel;
477         }
478     }
479   free (data);
480
481   return ximage;
482 }
483
484 #endif /* !HAVE_XPM */
485
486
487 /* Returns an XImage structure containing the bits of the given XPM image.
488    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
489    extra byte set to either 0xFF or 0x00 (based on the XPM file's mask.)
490
491    The Display and Visual arguments are used only for creating the XImage;
492    no bits are pushed to the server.
493
494    The Colormap argument is used just for parsing color names; no colors
495    are allocated.
496  */
497 XImage *
498 xpm_to_ximage (Display *dpy, Visual *visual, Colormap cmap, 
499                char **xpm_data)
500 {
501   return xpm_to_ximage_1 (dpy, visual, cmap, 0, xpm_data);
502 }
503
504
505 XImage *
506 xpm_file_to_ximage (Display *dpy, Visual *visual, Colormap cmap,
507                     const char *filename)
508 {
509   return xpm_to_ximage_1 (dpy, visual, cmap, filename, 0);
510 }