Commit | Line | Data |
---|---|---|
3144ee8a AT |
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 | #ifdef HAVE_JWXYZ | |
32 | # include "jwxyz.h" | |
33 | #else /* real Xlib */ | |
34 | # include <X11/Xlib.h> | |
35 | # include <X11/Xutil.h> | |
36 | #endif /* !HAVE_JWXYZ */ | |
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 | } |