From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / utils / minixpm.c
1 /* xscreensaver, Copyright (c) 2001-2013 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* I implemented this subset of libXPM here because I don't want the
13    xscreensaver daemon to depend on libXPM for two reasons: first,
14    because I want the logo to show up even if libXPM is not installed
15    on the system; and second, I don't want to have to security-audit
16    libXPM.  The fewer libraries that are linked into the xscreensaver
17    daemon, the more likely to be secure it is.
18
19    Also, the Cocoa port uses this code since libXPM isn't available
20    by default on MacOS.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #ifdef HAVE_COCOA
32 # include "jwxyz.h"
33 #else  /* !HAVE_COCOA - real Xlib */
34 # include <X11/Xlib.h>
35 # include <X11/Xutil.h>
36 #endif /* !HAVE_COCOA */
37
38 #include "minixpm.h"
39
40 extern const char *progname;
41
42 static Bool
43 bigendian (void)
44 {
45   union { int i; char c[sizeof(int)]; } u;
46   u.i = 1;
47   return !u.c[0];
48 }
49
50 static const char hex[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53                               0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
54                               0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
55                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56                               0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
57                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
58
59 XImage *
60 minixpm_to_ximage (Display *dpy, Visual *visual, Colormap colormap, int depth,
61                    unsigned long transparent_color,
62                    const char * const * data,
63                    int *width_ret, int *height_ret,
64                    unsigned long **pixels_ret, int *npixels_ret,
65                    unsigned char **mask_ret)
66 {
67   int w, w8, h, ncolors, nbytes;
68   char c;
69   int x, y, i, pixel_count;
70   struct {
71     char byte;
72     int cr; int cg; int cb;
73     int mr; int mg; int mb;
74   } cmap[256];
75   unsigned char rmap[256];
76
77   unsigned long *pixels;
78   XImage *ximage = 0;
79   
80   memset (cmap, 0, sizeof(cmap)); /* avoid warnings */
81
82   if (4 != sscanf ((const char *) *data,
83                    "%d %d %d %d %c", &w, &h, &ncolors, &nbytes, &c)) {
84     fprintf (stderr, "%s: unparsable XPM header\n", progname);
85     abort();
86   }
87
88   if (ncolors < 1 || ncolors > 255) {
89     fprintf (stderr, "%s: XPM: ncolors is %d\n", progname, ncolors);
90     abort();
91   }
92   if (nbytes != 1) {
93     fprintf (stderr, "%s: %d-byte XPM files not supported\n",
94              progname, nbytes);
95     abort();
96   }
97   data++;
98
99   for (i = 0; i < ncolors; i++)
100     {
101       const char *line = *data;
102       cmap[i].byte = *line++;
103       while (*line)
104         {
105           int r, g, b;
106           char which;
107           while (*line == ' ' || *line == '\t')
108             line++;
109           which = *line;
110           if (!which) continue;  /* whitespace at end of line */
111           line++;
112           if (which != 'c' && which != 'm') {
113             fprintf (stderr, "%s: unknown XPM pixel type '%c' in \"%s\"\n",
114                      progname, which, *data);
115             abort();
116           }
117           while (*line == ' ' || *line == '\t')
118             line++;
119           if (!strncasecmp(line, "None", 4))
120             {
121               r = g = b = -1;
122               line += 4;
123             }
124           else
125             {
126               if (*line != '#') {
127                 fprintf (stderr, "%s: unparsable XPM color spec: \"%s\"\n",
128                          progname, line);
129                 abort();
130               }
131               if (*line == '#')
132                 line++;
133               r = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
134               g = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
135               b = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
136             }
137
138           if (which == 'c')
139             {
140               cmap[i].cr = r;
141               cmap[i].cg = g;
142               cmap[i].cb = b;
143             }
144           else
145             {
146               cmap[i].mr = r;
147               cmap[i].mg = g;
148               cmap[i].mb = b;
149             }
150         }
151
152       data++;
153     }
154
155   if (depth == 1) transparent_color = 1;
156
157   pixels = (unsigned long *) calloc (ncolors+1, sizeof(*pixels));
158   pixel_count = 0;
159   for (i = 0; i < ncolors; i++)
160     {
161       if (cmap[i].cr == -1) /* transparent */
162         {
163           rmap[(int) cmap[i].byte] = 255;
164         }
165       else
166         {
167           XColor color;
168           color.flags = DoRed|DoGreen|DoBlue;
169           color.red   = (cmap[i].cr << 8) | cmap[i].cr;
170           color.green = (cmap[i].cg << 8) | cmap[i].cg;
171           color.blue  = (cmap[i].cb << 8) | cmap[i].cb;
172           if (depth == 1 ||
173               !XAllocColor (dpy, colormap, &color))
174             {
175               color.red   = (cmap[i].mr << 8) | cmap[i].mr;
176               color.green = (cmap[i].mg << 8) | cmap[i].mg;
177               color.blue  = (cmap[i].mb << 8) | cmap[i].mb;
178               if (!XAllocColor (dpy, colormap, &color)) {
179                 fprintf (stderr, "%s: unable to allocate XPM color\n",
180                          progname);
181                 abort();
182               }
183             }
184           pixels[pixel_count] = color.pixel;
185           rmap[(int) cmap[i].byte] = pixel_count;
186           pixel_count++;
187         }
188     }
189
190   ximage = XCreateImage (dpy, visual, depth,
191                          (depth == 1 ? XYBitmap : ZPixmap),
192                          0, 0, w, h, 8, 0);
193   if (! ximage)
194     {
195       if (pixels) free (pixels);
196       return 0;
197     }
198
199   ximage->bitmap_bit_order =
200     ximage->byte_order =
201     (bigendian() ? MSBFirst : LSBFirst);
202
203   ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
204   if (!ximage->data)
205     {
206       XDestroyImage (ximage);
207       if (pixels) free (pixels);
208       return 0;
209     }
210
211   w8 = (w + 7) / 8;
212   if (mask_ret)
213     {
214       int s = (w8 * h) + 1;
215       *mask_ret = (unsigned char *) malloc (s);
216       if (!*mask_ret)
217         mask_ret = 0;
218       else
219         memset (*mask_ret, 255, s);
220     }
221
222   for (y = 0; y < h; y++)
223     {
224       const char *line = *data++;
225       for (x = 0; x < w; x++)
226         {
227           int p = rmap[(int) *line];
228           line++;
229           XPutPixel (ximage, x, y, (p == 255 ? transparent_color : pixels[p]));
230
231           if (p == 255 && mask_ret)
232             (*mask_ret)[(y * w8) + (x >> 3)] &= (~(1 << (x & 7)));
233         }
234     }
235
236   *width_ret = w;
237   *height_ret = h;
238   *pixels_ret = pixels;
239   *npixels_ret = pixel_count;
240   return ximage;
241 }