From http://www.jwz.org/xscreensaver/xscreensaver-5.34.tar.gz
[xscreensaver] / utils / minixpm.c
1 /* xscreensaver, Copyright (c) 2001-2014 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 #if defined (HAVE_COCOA) || defined(HAVE_ANDROID)
32 # include "jwxyz.h"
33 #else  /* real Xlib */
34 # include <X11/Xlib.h>
35 # include <X11/Xutil.h>
36 #endif /* !HAVE_COCOA && !HAVE_ANDROID */
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 if (!strncasecmp(line, "white", 5))
125             {
126               r = g = b = 255;
127               line += 5;
128             }
129           else if (!strncasecmp(line, "black", 5))
130             {
131               r = g = b = 0;
132               line += 5;
133             }
134           else
135             {
136               if (*line != '#') {
137                 fprintf (stderr, "%s: unparsable XPM color spec: \"%s\"\n",
138                          progname, line);
139                 abort();
140               }
141               if (*line == '#')
142                 line++;
143               r = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
144               g = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
145               b = (hex[(int) line[0]] << 4) | hex[(int) line[1]]; line += 2;
146             }
147
148           if (which == 'c')
149             {
150               cmap[i].cr = r;
151               cmap[i].cg = g;
152               cmap[i].cb = b;
153             }
154           else
155             {
156               cmap[i].mr = r;
157               cmap[i].mg = g;
158               cmap[i].mb = b;
159             }
160         }
161
162       data++;
163     }
164
165   if (depth == 1) transparent_color = 1;
166
167   pixels = (unsigned long *) calloc (ncolors+1, sizeof(*pixels));
168   pixel_count = 0;
169   for (i = 0; i < ncolors; i++)
170     {
171       if (cmap[i].cr == -1) /* transparent */
172         {
173           rmap[(int) cmap[i].byte] = 255;
174         }
175       else
176         {
177           XColor color;
178           color.flags = DoRed|DoGreen|DoBlue;
179           color.red   = (cmap[i].cr << 8) | cmap[i].cr;
180           color.green = (cmap[i].cg << 8) | cmap[i].cg;
181           color.blue  = (cmap[i].cb << 8) | cmap[i].cb;
182           if (depth == 1 ||
183               !XAllocColor (dpy, colormap, &color))
184             {
185               color.red   = (cmap[i].mr << 8) | cmap[i].mr;
186               color.green = (cmap[i].mg << 8) | cmap[i].mg;
187               color.blue  = (cmap[i].mb << 8) | cmap[i].mb;
188               if (!XAllocColor (dpy, colormap, &color)) {
189                 fprintf (stderr, "%s: unable to allocate XPM color\n",
190                          progname);
191                 abort();
192               }
193             }
194           pixels[pixel_count] = color.pixel;
195           rmap[(int) cmap[i].byte] = pixel_count;
196           pixel_count++;
197         }
198     }
199
200   ximage = XCreateImage (dpy, visual, depth,
201                          (depth == 1 ? XYBitmap : ZPixmap),
202                          0, 0, w, h, 8, 0);
203   if (! ximage)
204     {
205       if (pixels) free (pixels);
206       return 0;
207     }
208
209   ximage->bitmap_bit_order =
210     ximage->byte_order =
211     (bigendian() ? MSBFirst : LSBFirst);
212
213   ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
214   if (!ximage->data)
215     {
216       XDestroyImage (ximage);
217       if (pixels) free (pixels);
218       return 0;
219     }
220
221   w8 = (w + 7) / 8;
222   if (mask_ret)
223     {
224       int s = (w8 * h) + 1;
225       *mask_ret = (unsigned char *) malloc (s);
226       if (!*mask_ret)
227         mask_ret = 0;
228       else
229         memset (*mask_ret, 255, s);
230     }
231
232   for (y = 0; y < h; y++)
233     {
234       const char *line = *data++;
235       for (x = 0; x < w; x++)
236         {
237           int p = rmap[(int) *line];
238           line++;
239           XPutPixel (ximage, x, y, (p == 255 ? transparent_color : pixels[p]));
240
241           if (p == 255 && mask_ret)
242             (*mask_ret)[(y * w8) + (x >> 3)] &= (~(1 << (x & 7)));
243         }
244     }
245
246   *width_ret = w;
247   *height_ret = h;
248   *pixels_ret = pixels;
249   *npixels_ret = pixel_count;
250   return ximage;
251 }