247eca89efc723ac05c7455e855ffaab1a91c317
[xscreensaver] / utils / minixpm.c
1 /* xscreensaver, Copyright (c) 2001-2006 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) return 0;
194
195   ximage->bitmap_bit_order =
196     ximage->byte_order =
197     (bigendian() ? MSBFirst : LSBFirst);
198
199   ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
200   if (!ximage->data) {
201     XDestroyImage (ximage);
202     return 0;
203   }
204
205   w8 = (w + 7) / 8;
206   if (mask_ret)
207     {
208       int s = (w8 * h) + 1;
209       *mask_ret = (unsigned char *) malloc (s);
210       if (!*mask_ret)
211         mask_ret = 0;
212       else
213         memset (*mask_ret, 255, s);
214     }
215
216   for (y = 0; y < h; y++)
217     {
218       const char *line = *data++;
219       for (x = 0; x < w; x++)
220         {
221           int p = rmap[(int) *line];
222           line++;
223           XPutPixel (ximage, x, y, (p == 255 ? transparent_color : pixels[p]));
224
225           if (p == 255 && mask_ret)
226             (*mask_ret)[(y * w8) + (x >> 3)] &= (~(1 << (x & 7)));
227         }
228     }
229
230   *width_ret = w;
231   *height_ret = h;
232   *pixels_ret = pixels;
233   *npixels_ret = pixel_count;
234   return ximage;
235 }