From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / xpm-pixmap.c
1 /* xpm-pixmap.c --- converts XPM data to a Pixmap.
2  * xscreensaver, Copyright (c) 1998-2006 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
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdlib.h>
18 #include <stdio.h>
19
20 #ifdef HAVE_JWXYZ
21 # include "jwxyz.h"
22 #else
23 # include <X11/Xlib.h>
24 # include <X11/Xutil.h>
25 # include "visual.h"  /* for screen_number() */
26 #endif
27
28 #include "xpm-pixmap.h"
29
30 extern char *progname;
31
32 #if defined(HAVE_GDK_PIXBUF)
33
34 # include <gdk-pixbuf/gdk-pixbuf.h>
35
36 # ifdef HAVE_GTK2
37 #  include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
38 # else  /* !HAVE_GTK2 */
39 #  include <gdk-pixbuf/gdk-pixbuf-xlib.h>
40 # endif /* !HAVE_GTK2 */
41
42
43 /* Returns a Pixmap structure containing the bits of the given XPM image.
44    This is the gdk_pixbuf version of this function.
45  */
46 static Pixmap
47 xpm_to_pixmap_1 (Display *dpy, Window window,
48                  int *width_ret, int *height_ret,
49                  Pixmap *mask_ret,
50                  const char *filename,
51                  /*const*/ char * const *xpm_data)
52 {
53   GdkPixbuf *pb;
54   static int initted = 0;
55   XWindowAttributes xgwa;
56 #ifdef HAVE_GTK2
57   GError *gerr = NULL;
58 #endif /* HAVE_GTK2 */
59   XGetWindowAttributes (dpy, window, &xgwa);
60
61   if (!initted)
62     {
63 #ifdef HAVE_GTK2
64 #if !GLIB_CHECK_VERSION(2, 36 ,0)
65       g_type_init ();
66 #endif
67 #endif /* HAVE_GTK2 */
68       gdk_pixbuf_xlib_init (dpy, screen_number (xgwa.screen));
69       xlib_rgb_init (dpy, xgwa.screen);
70       initted = 1;
71     }
72
73   pb = (filename
74 #ifdef HAVE_GTK2
75         ? gdk_pixbuf_new_from_file (filename, &gerr)
76 #else  /* !HAVE_GTK2 */
77         ? gdk_pixbuf_new_from_file (filename)
78 #endif /* !HAVE_GTK2 */
79         : gdk_pixbuf_new_from_xpm_data ((const char **) xpm_data));
80   if (pb)
81     {
82       int w = gdk_pixbuf_get_width (pb);
83       int h = gdk_pixbuf_get_height (pb);
84       Pixmap pixmap = 0;
85
86       /* #### Note that this always uses the default colormap!  Morons!
87               Owen says that in Gnome 2.0, I should try using
88               gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
89               But I don't have Gnome 2.0 yet.
90        */
91       gdk_pixbuf_xlib_render_pixmap_and_mask (pb, &pixmap, mask_ret, 128);
92
93       if (!pixmap)
94         {
95           fprintf (stderr, "%s: out of memory (%d x %d)\n", progname, w, h);
96           exit (-1);
97         }
98       /* gdk_pixbuf_unref (pb);  -- #### does doing this free colors? */
99
100       if (width_ret)  *width_ret  = w;
101       if (height_ret) *height_ret = h;
102
103       return pixmap;
104     }
105   else if (filename)
106     {
107 #ifdef HAVE_GTK2
108       fprintf (stderr, "%s: %s\n", progname, gerr->message);
109       g_error_free (gerr);
110 #else /* !HAVE_GTK2 */
111       fprintf (stderr, "%s: unable to load %s\n", progname, filename);
112 #endif /* !HAVE_GTK2 */
113       exit (-1);
114     }
115   else
116     {
117       fprintf (stderr, "%s: unable to initialize built-in images\n", progname);
118       exit (-1);
119     }
120 }
121
122
123 #elif defined(HAVE_XPM)
124
125 #include <X11/Intrinsic.h>
126 #include <X11/xpm.h>
127
128 #ifdef HAVE_XMU
129 # ifndef VMS
130 #  include <X11/Xmu/Drawing.h>
131 #else  /* VMS */
132 #  include <Xmu/Drawing.h>
133 # endif /* VMS */
134 #endif
135
136 #undef countof
137 #define countof(x) (sizeof((x))/sizeof((*x)))
138
139
140 static int
141 handle_xpm_error (const char *filename, int status)
142 {
143   if (!filename) filename = "builtin";
144   switch (status)
145     {
146     case XpmSuccess:
147       return 0;
148       break;
149     case XpmColorError:
150       fprintf (stderr, "%s: %s: warning: color substitution performed\n",
151                progname, filename);
152       return 0;
153       break;
154     case XpmFileInvalid:
155       return 1;
156       break;
157     case XpmOpenFailed:
158       fprintf (stderr, "%s: %s: no such file\n", progname, filename);
159       break;
160     case XpmNoMemory:
161       fprintf (stderr, "%s: %s: out of memory\n", progname, filename);
162       break;
163     case XpmColorFailed:
164       fprintf (stderr, "%s: %s: color allocation failed\n",
165                progname, filename);
166       break;
167     default:
168       fprintf (stderr, "%s: %s: unknown XPM error %d\n", progname,
169                filename, status);
170       break;
171     }
172   exit (-1);
173 }
174
175
176 /* The libxpm version of this function...
177  */
178 static Pixmap
179 xpm_to_pixmap_1 (Display *dpy, Window window,
180                  int *width_ret, int *height_ret,
181                  Pixmap *mask_ret,
182                  const char *filename,
183                  /*const*/ char * const *xpm_data)
184 {
185   Pixmap pixmap = 0;
186   XpmAttributes xpmattrs;
187   XpmImage xpm_image;
188   XpmInfo xpm_info;
189   int status;
190   XWindowAttributes xgwa;
191   XGetWindowAttributes (dpy, window, &xgwa);
192
193   memset (&xpm_image, 0, sizeof(xpm_image));
194   memset (&xpm_info, 0, sizeof(xpm_info));
195
196   if (filename)
197     {
198       xpm_data = 0;
199       status = XpmReadFileToData ((char *) filename, &xpm_data);
200       if (handle_xpm_error (filename, status))
201         goto TRY_XBM;
202     }
203
204   xpmattrs.valuemask = 0;
205
206 # ifdef XpmCloseness
207   xpmattrs.valuemask |= XpmCloseness;
208   xpmattrs.closeness = 40000;
209 # endif
210 # ifdef XpmVisual
211   xpmattrs.valuemask |= XpmVisual;
212   xpmattrs.visual = xgwa.visual;
213 # endif
214 # ifdef XpmDepth
215   xpmattrs.valuemask |= XpmDepth;
216   xpmattrs.depth = xgwa.depth;
217 # endif
218 # ifdef XpmColormap
219   xpmattrs.valuemask |= XpmColormap;
220   xpmattrs.colormap = xgwa.colormap;
221 # endif
222
223   status = XpmCreatePixmapFromData (dpy, window, xpm_data,
224                                     &pixmap, mask_ret, &xpmattrs);
225   if (handle_xpm_error (filename, status))
226     pixmap = 0;
227   else
228     {
229       if (width_ret)  *width_ret  = xpmattrs.width;
230       if (height_ret) *height_ret = xpmattrs.height;
231     }
232
233  TRY_XBM:
234
235 #ifdef HAVE_XMU
236   if (! pixmap)
237     {
238       unsigned long fg = BlackPixelOfScreen (xgwa.screen);
239       unsigned long bg = WhitePixelOfScreen (xgwa.screen);
240       int xh, yh;
241       Pixmap b2;
242       pixmap = XmuLocateBitmapFile (xgwa.screen, filename,
243                                     0, 0, width_ret, height_ret, &xh, &yh);
244       if (! pixmap)
245         {
246           fprintf (stderr, "%s: couldn't find XBM %s\n", progname,
247                    filename);
248           exit (-1);
249         }
250       b2 = XmuCreatePixmapFromBitmap (dpy, window, pixmap,
251                                       *width_ret, *height_ret,
252                                       xgwa.depth, fg, bg);
253       XFreePixmap (dpy, pixmap);
254       pixmap = b2;
255
256       if (!pixmap)
257         {
258           fprintf (stderr, "%s: couldn't load XBM %s\n", progname, filename);
259           exit (-1);
260         }
261     }
262 #else  /* !XMU */
263     {
264       fprintf (stderr,
265                "%s: your vendor doesn't ship the standard Xmu library.\n",
266                progname);
267       fprintf (stderr, "\tWe can't load XBM files without it.\n");
268       exit (-1);
269     }
270 #endif /* !XMU */
271
272   return pixmap;
273 }
274
275 #else  /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
276
277 /* If we don't have libXPM or Pixbuf, then use "minixpm".
278    This can read XPM data from memory, but can't read files.
279  */
280
281 #include "minixpm.h"
282
283 static Pixmap
284 xpm_to_pixmap_1 (Display *dpy, Window window,
285                  int *width_ret, int *height_ret,
286                  Pixmap *mask_ret,
287                  const char *filename,
288                  /*const*/ char * const *xpm_data)
289 {
290   XWindowAttributes xgwa;
291   XImage *ximage;
292   Pixmap pixmap, p2 = 0;
293   int iw, ih, npixels;
294   unsigned long *pixels = 0;
295   unsigned char *mask = 0;
296   XGCValues gcv;
297   GC gc;
298
299   if (filename)
300     {
301       fprintf(stderr, 
302               "%s: no files: not compiled with XPM or Pixbuf support.\n", 
303               progname);
304       exit (1);
305     }
306
307   if (! xpm_data) abort();
308
309   XGetWindowAttributes (dpy, window, &xgwa);
310   ximage = minixpm_to_ximage (dpy, xgwa.visual, xgwa.colormap, xgwa.depth, 
311                               BlackPixelOfScreen (xgwa.screen),
312                               (const char * const *)
313                               xpm_data, &iw, &ih, &pixels, &npixels, 
314                               (mask_ret ? &mask : 0));
315   if (pixels) free (pixels);
316
317   pixmap = XCreatePixmap (dpy, window, iw, ih, xgwa.depth);
318   gc = XCreateGC (dpy, pixmap, 0, &gcv);
319   XPutImage (dpy, pixmap, gc, ximage, 0, 0, 0, 0, iw, ih);
320   XFreeGC (dpy, gc);
321   XDestroyImage (ximage);
322
323   if (mask)
324     {
325       p2 = XCreatePixmap (dpy, window, iw, ih, 1);
326       gcv.foreground = 1;
327       gcv.background = 0;
328       gc = XCreateGC (dpy, p2, GCForeground|GCBackground, &gcv);
329       ximage = XCreateImage (dpy, xgwa.visual, 1, XYBitmap, 0, (char *) mask,
330                              iw, ih, 8, 0);
331       ximage->byte_order = ximage->bitmap_bit_order = LSBFirst;
332       if (!ximage) abort();
333       XPutImage (dpy, p2, gc, ximage, 0, 0, 0, 0, iw, ih);
334       XFreeGC (dpy, gc);
335       XDestroyImage (ximage);
336     }
337
338   if (width_ret)  *width_ret  = iw;
339   if (height_ret) *height_ret = ih;
340   if (mask_ret)   *mask_ret   = p2;
341   return pixmap;
342 }
343
344 #endif /* minixpm */
345
346
347 Pixmap
348 xpm_data_to_pixmap (Display *dpy, Window window, 
349                     /*const*/ char * const *xpm_data,
350                     int *width_ret, int *height_ret,
351                     Pixmap *mask_ret)
352 {
353   return xpm_to_pixmap_1 (dpy, window, width_ret, height_ret, mask_ret,
354                           0, xpm_data);
355 }
356
357
358 Pixmap
359 xpm_file_to_pixmap (Display *dpy, Window window, const char *filename,
360                     int *width_ret, int *height_ret,
361                     Pixmap *mask_ret)
362 {
363   return xpm_to_pixmap_1 (dpy, window, width_ret, height_ret, mask_ret,
364                           filename, 0);
365 }