| 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 | } |