diff options
Diffstat (limited to 'src/cmd/jpg/toico.c')
-rw-r--r-- | src/cmd/jpg/toico.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/src/cmd/jpg/toico.c b/src/cmd/jpg/toico.c new file mode 100644 index 00000000..059e018b --- /dev/null +++ b/src/cmd/jpg/toico.c @@ -0,0 +1,322 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> + +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 */ + initdraw(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<<icon->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); +} |