#include #include #include #include enum { FileHdrLen= 6, IconDescrLen= 16, IconHdrLen= 40, }; typedef struct Icon Icon; struct Icon { Icon *next; char *file; uchar w; /* icon width */ uchar h; /* icon height */ ushort ncolor; /* number of colors */ ushort nplane; /* number of bit planes */ ushort bits; /* bits per pixel */ ulong len; /* length of data */ ulong offset; /* file offset to data */ uchar map[4*256]; /* color map */ Image *img; uchar *xor; int xorlen; uchar *and; int andlen; }; typedef struct Header Header; struct Header { uint n; Icon *first; Icon *last; }; void Bputs(Biobuf *b, ushort x) { Bputc(b, x&0xff); Bputc(b, x>>8); } void Bputl(Biobuf *b, ulong x) { Bputs(b, x&0xffff); Bputs(b, x>>16); } Header h; void* emalloc(int); void mk8bit(Icon*, int); void mkxorand(Icon*, int); void readicon(char*); void main(int argc, char **argv) { int i; Biobuf *b, out; Icon *icon; ulong offset; ulong len; ARGBEGIN{ }ARGEND; /* read in all the images */ display = initdisplay(nil, nil, nil); if(argc < 1){ readicon("/fd/0"); } else { for(i = 0; i < argc; i++) readicon(argv[i]); } /* create the .ico file */ b = &out; Binit(b, 1, OWRITE); /* offset to first icon */ offset = FileHdrLen + h.n*IconDescrLen; /* file header is */ Bputs(b, 0); Bputs(b, 1); Bputs(b, h.n); /* icon description */ for(icon = h.first; icon != nil; icon = icon->next){ Bputc(b, icon->w); Bputc(b, icon->h); Bputc(b, icon->ncolor); Bputc(b, 0); Bputs(b, icon->nplane); Bputs(b, icon->bits); len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen; Bputl(b, len); Bputl(b, offset); offset += len; } /* icons */ for(icon = h.first; icon != nil; icon = icon->next){ /* icon header (BMP like) */ Bputl(b, IconHdrLen); Bputl(b, icon->w); Bputl(b, 2*icon->h); Bputs(b, icon->nplane); Bputs(b, icon->bits); Bputl(b, 0); /* compression info */ Bputl(b, 0); Bputl(b, 0); Bputl(b, 0); Bputl(b, 0); Bputl(b, 0); /* color map */ if(Bwrite(b, icon->map, 4*icon->ncolor) < 0) sysfatal("writing color map: %r"); /* xor bits */ if(Bwrite(b, icon->xor, icon->xorlen) < 0) sysfatal("writing xor bits: %r"); /* and bits */ if(Bwrite(b, icon->and, icon->andlen) < 0) sysfatal("writing and bits: %r"); } Bterm(b); exits(0); } void readicon(char *file) { int fd; Icon *icon; fd = open(file, OREAD); if(fd < 0) sysfatal("opening %s: %r", file); icon = emalloc(sizeof(Icon)); icon->img = readimage(display, fd, 0); if(icon->img == nil) sysfatal("reading image %s: %r", file); close(fd); if(h.first) h.last->next = icon; else h.first = icon; h.last = icon; h.n++; icon->h = Dy(icon->img->r); icon->w = Dx(icon->img->r); icon->bits = 1<img->depth; icon->nplane = 1; /* convert to 8 bits per pixel */ switch(icon->img->chan){ case GREY8: case CMAP8: break; case GREY1: case GREY2: case GREY4: mk8bit(icon, 1); break; default: mk8bit(icon, 0); break; } icon->bits = 8; icon->file = file; /* create xor/and masks, minimizing bits per pixel */ mkxorand(icon, icon->img->chan == GREY8); } void* emalloc(int len) { void *x; x = mallocz(len, 1); if(x == nil) sysfatal("memory: %r"); return x; } /* convert to 8 bit */ void mk8bit(Icon *icon, int grey) { Image *img; img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill); if(img == nil) sysfatal("can't allocimage: %r"); draw(img, img->r, icon->img, nil, ZP); freeimage(icon->img); icon->img = img; } /* make xor and and mask */ void mkxorand(Icon *icon, int grey) { int i, x, y, s, sa; uchar xx[256]; uchar *data, *p, *e; int ndata; uchar *mp; int ncolor; ulong color; int bits; uchar andbyte, xorbyte; uchar *ato, *xto; int xorrl, andrl; ndata = icon->h * icon->w; data = emalloc(ndata); if(unloadimage(icon->img, icon->img->r, data, ndata) < 0) sysfatal("can't unload %s: %r", icon->file); e = data + ndata; /* find colors used */ memset(xx, 0, sizeof xx); for(p = data; p < e; p++) xx[*p]++; /* count the colors and create a mapping from plan 9 */ mp = icon->map; ncolor = 0; for(i = 0; i < 256; i++){ if(xx[i] == 0) continue; if(grey){ *mp++ = i; *mp++ = i; *mp++ = i; *mp++ = 0; } else { color = cmap2rgb(i); *mp++ = color; *mp++ = color>>8; *mp++ = color>>16; *mp++ = 0; } xx[i] = ncolor; ncolor++; } /* get minimum number of pixels per bit (with a color map) */ if(ncolor <= 2){ ncolor = 2; bits = 1; } else if(ncolor <= 4){ ncolor = 4; bits = 2; } else if(ncolor <= 16){ ncolor = 16; bits = 4; } else { ncolor = 256; bits = 8; } icon->bits = bits; icon->ncolor = ncolor; /* the xor mask rows are justified to a 32 bit boundary */ /* the and mask is 1 bit grey */ xorrl = 4*((bits*icon->w + 31)/32); andrl = 4*((icon->w + 31)/32); icon->xor = emalloc(xorrl * icon->h); icon->and = emalloc(andrl * icon->h); icon->xorlen = xorrl*icon->h; icon->andlen = andrl*icon->h; /* make both masks. they're upside down relative to plan9 ones */ p = data; for(y = 0; y < icon->h; y++){ andbyte = 0; xorbyte = 0; sa = s = 0; xto = icon->xor + (icon->h-1-y)*xorrl; ato = icon->and + (icon->h-1-y)*andrl; for(x = 0; x < icon->w; x++){ xorbyte <<= bits; xorbyte |= xx[*p]; s += bits; if(s == 8){ *xto++ = xorbyte; xorbyte = 0; s = 0; } andbyte <<= 1; if(*p == 0xff) andbyte |= 1; sa++; if(sa == 0){ *ato++ = andbyte; sa = 0; andbyte = 0; } p++; } } free(data); }