toico.c (5627B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <draw.h> 5 6 enum 7 { 8 FileHdrLen= 6, 9 IconDescrLen= 16, 10 IconHdrLen= 40 11 }; 12 13 typedef struct Icon Icon; 14 struct Icon 15 { 16 Icon *next; 17 char *file; 18 19 uchar w; /* icon width */ 20 uchar h; /* icon height */ 21 ushort ncolor; /* number of colors */ 22 ushort nplane; /* number of bit planes */ 23 ushort bits; /* bits per pixel */ 24 ulong len; /* length of data */ 25 ulong offset; /* file offset to data */ 26 uchar map[4*256]; /* color map */ 27 28 Image *img; 29 30 uchar *xor; 31 int xorlen; 32 uchar *and; 33 int andlen; 34 }; 35 36 typedef struct Header Header; 37 struct Header 38 { 39 uint n; 40 Icon *first; 41 Icon *last; 42 }; 43 44 void 45 Bputs(Biobuf *b, ushort x) 46 { 47 Bputc(b, x&0xff); 48 Bputc(b, x>>8); 49 } 50 51 void 52 Bputl(Biobuf *b, ulong x) 53 { 54 Bputs(b, x&0xffff); 55 Bputs(b, x>>16); 56 } 57 58 Header h; 59 60 void* emalloc(int); 61 void mk8bit(Icon*, int); 62 void mkxorand(Icon*, int); 63 void readicon(char*); 64 65 void 66 main(int argc, char **argv) 67 { 68 int i; 69 Biobuf *b, out; 70 Icon *icon; 71 ulong offset; 72 ulong len; 73 74 ARGBEGIN{ 75 }ARGEND; 76 77 /* read in all the images */ 78 initdraw(0, nil, nil); 79 if(argc < 1){ 80 readicon("/dev/stdin"); 81 } else { 82 for(i = 0; i < argc; i++) 83 readicon(argv[i]); 84 } 85 86 /* create the .ico file */ 87 b = &out; 88 Binit(b, 1, OWRITE); 89 90 /* offset to first icon */ 91 offset = FileHdrLen + h.n*IconDescrLen; 92 93 /* file header is */ 94 Bputs(b, 0); 95 Bputs(b, 1); 96 Bputs(b, h.n); 97 98 /* icon description */ 99 for(icon = h.first; icon != nil; icon = icon->next){ 100 Bputc(b, icon->w); 101 Bputc(b, icon->h); 102 Bputc(b, icon->ncolor); 103 Bputc(b, 0); 104 Bputs(b, icon->nplane); 105 Bputs(b, icon->bits); 106 len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen; 107 Bputl(b, len); 108 Bputl(b, offset); 109 offset += len; 110 } 111 112 /* icons */ 113 for(icon = h.first; icon != nil; icon = icon->next){ 114 /* icon header (BMP like) */ 115 Bputl(b, IconHdrLen); 116 Bputl(b, icon->w); 117 Bputl(b, 2*icon->h); 118 Bputs(b, icon->nplane); 119 Bputs(b, icon->bits); 120 Bputl(b, 0); /* compression info */ 121 Bputl(b, 0); 122 Bputl(b, 0); 123 Bputl(b, 0); 124 Bputl(b, 0); 125 Bputl(b, 0); 126 127 /* color map */ 128 if(Bwrite(b, icon->map, 4*icon->ncolor) < 0) 129 sysfatal("writing color map: %r"); 130 131 /* xor bits */ 132 if(Bwrite(b, icon->xor, icon->xorlen) < 0) 133 sysfatal("writing xor bits: %r"); 134 135 /* and bits */ 136 if(Bwrite(b, icon->and, icon->andlen) < 0) 137 sysfatal("writing and bits: %r"); 138 } 139 140 Bterm(b); 141 exits(0); 142 } 143 144 void 145 readicon(char *file) 146 { 147 int fd; 148 Icon *icon; 149 150 fd = open(file, OREAD); 151 if(fd < 0) 152 sysfatal("opening %s: %r", file); 153 icon = emalloc(sizeof(Icon)); 154 icon->img = readimage(display, fd, 0); 155 if(icon->img == nil) 156 sysfatal("reading image %s: %r", file); 157 close(fd); 158 159 if(h.first) 160 h.last->next = icon; 161 else 162 h.first = icon; 163 h.last = icon; 164 h.n++; 165 166 icon->h = Dy(icon->img->r); 167 icon->w = Dx(icon->img->r); 168 icon->bits = 1<<icon->img->depth; 169 icon->nplane = 1; 170 171 /* convert to 8 bits per pixel */ 172 switch(icon->img->chan){ 173 case GREY8: 174 case CMAP8: 175 break; 176 case GREY1: 177 case GREY2: 178 case GREY4: 179 mk8bit(icon, 1); 180 break; 181 default: 182 mk8bit(icon, 0); 183 break; 184 } 185 icon->bits = 8; 186 icon->file = file; 187 188 /* create xor/and masks, minimizing bits per pixel */ 189 mkxorand(icon, icon->img->chan == GREY8); 190 } 191 192 void* 193 emalloc(int len) 194 { 195 void *x; 196 197 x = mallocz(len, 1); 198 if(x == nil) 199 sysfatal("memory: %r"); 200 return x; 201 } 202 203 /* convert to 8 bit */ 204 void 205 mk8bit(Icon *icon, int grey) 206 { 207 Image *img; 208 209 img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill); 210 if(img == nil) 211 sysfatal("can't allocimage: %r"); 212 draw(img, img->r, icon->img, nil, ZP); 213 freeimage(icon->img); 214 icon->img = img; 215 } 216 217 /* make xor and and mask */ 218 void 219 mkxorand(Icon *icon, int grey) 220 { 221 int i, x, y, s, sa; 222 uchar xx[256]; 223 uchar *data, *p, *e; 224 int ndata; 225 uchar *mp; 226 int ncolor; 227 ulong color; 228 int bits; 229 uchar andbyte, xorbyte; 230 uchar *ato, *xto; 231 int xorrl, andrl; 232 233 ndata = icon->h * icon->w; 234 data = emalloc(ndata); 235 if(unloadimage(icon->img, icon->img->r, data, ndata) < 0) 236 sysfatal("can't unload %s: %r", icon->file); 237 e = data + ndata; 238 239 /* find colors used */ 240 memset(xx, 0, sizeof xx); 241 for(p = data; p < e; p++) 242 xx[*p]++; 243 244 /* count the colors and create a mapping from plan 9 */ 245 mp = icon->map; 246 ncolor = 0; 247 for(i = 0; i < 256; i++){ 248 if(xx[i] == 0) 249 continue; 250 if(grey){ 251 *mp++ = i; 252 *mp++ = i; 253 *mp++ = i; 254 *mp++ = 0; 255 } else { 256 color = cmap2rgb(i); 257 *mp++ = color; 258 *mp++ = color>>8; 259 *mp++ = color>>16; 260 *mp++ = 0; 261 } 262 xx[i] = ncolor; 263 ncolor++; 264 } 265 266 /* get minimum number of pixels per bit (with a color map) */ 267 if(ncolor <= 2){ 268 ncolor = 2; 269 bits = 1; 270 } else if(ncolor <= 4){ 271 ncolor = 4; 272 bits = 2; 273 } else if(ncolor <= 16){ 274 ncolor = 16; 275 bits = 4; 276 } else { 277 ncolor = 256; 278 bits = 8; 279 } 280 icon->bits = bits; 281 icon->ncolor = ncolor; 282 283 /* the xor mask rows are justified to a 32 bit boundary */ 284 /* the and mask is 1 bit grey */ 285 xorrl = 4*((bits*icon->w + 31)/32); 286 andrl = 4*((icon->w + 31)/32); 287 icon->xor = emalloc(xorrl * icon->h); 288 icon->and = emalloc(andrl * icon->h); 289 icon->xorlen = xorrl*icon->h; 290 icon->andlen = andrl*icon->h; 291 292 /* make both masks. they're upside down relative to plan9 ones */ 293 p = data; 294 for(y = 0; y < icon->h; y++){ 295 andbyte = 0; 296 xorbyte = 0; 297 sa = s = 0; 298 xto = icon->xor + (icon->h-1-y)*xorrl; 299 ato = icon->and + (icon->h-1-y)*andrl; 300 for(x = 0; x < icon->w; x++){ 301 xorbyte <<= bits; 302 xorbyte |= xx[*p]; 303 s += bits; 304 if(s == 8){ 305 *xto++ = xorbyte; 306 xorbyte = 0; 307 s = 0; 308 } 309 andbyte <<= 1; 310 if(*p == 0xff) 311 andbyte |= 1; 312 sa++; 313 if(sa == 0){ 314 *ato++ = andbyte; 315 sa = 0; 316 andbyte = 0; 317 } 318 p++; 319 } 320 } 321 free(data); 322 }