#include #include #include #include #include #include typedef struct Icon Icon; struct Icon { Icon *next; 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 */ Image *img; Image *mask; Rectangle r; /* relative */ Rectangle sr; /* abs */ }; typedef struct Header Header; struct Header { uint n; Icon *first; Icon *last; }; int debug; Mouse mouse; Header h; Image *background; ushort gets(uchar *p) { return p[0] | (p[1]<<8); } ulong getl(uchar *p) { return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); } int Bgetheader(Biobuf *b, Header *h) { Icon *icon; int i; uchar buf[40]; memset(h, 0, sizeof(*h)); if(Bread(b, buf, 6) != 6) goto eof; if(gets(&buf[0]) != 0) goto header; if(gets(&buf[2]) != 1) goto header; h->n = gets(&buf[4]); for(i = 0; i < h->n; i++){ icon = mallocz(sizeof(*icon), 1); if(icon == nil) sysfatal("malloc: %r"); if(Bread(b, buf, 16) != 16) goto eof; icon->w = buf[0]; icon->h = buf[1]; icon->ncolor = buf[2] == 0 ? 256 : buf[2]; if(buf[3] != 0) goto header; icon->nplane = gets(&buf[4]); icon->bits = gets(&buf[6]); icon->len = getl(&buf[8]); icon->offset = getl(&buf[12]); if(i == 0) h->first = icon; else h->last->next = icon; h->last = icon; } return 0; eof: werrstr("unexpected EOF"); return -1; header: werrstr("unknown header format"); return -1; } uchar* transcmap(Icon *icon, uchar *map) { uchar *m, *p; int i; p = m = malloc(sizeof(int)*(1<bits)); for(i = 0; i < icon->ncolor; i++){ *p++ = rgb2cmap(map[2], map[1], map[0]); map += 4; } return m; } Image* xor2img(Icon *icon, uchar *xor, uchar *map) { uchar *data; Image *img; int inxlen; uchar *from, *to; int s, byte, mask; int x, y; inxlen = 4*((icon->bits*icon->w+31)/32); to = data = malloc(icon->w*icon->h); /* rotate around the y axis, go to 8 bits, and convert color */ mask = (1<bits)-1; for(y = 0; y < icon->h; y++){ s = -1; byte = 0; from = xor + (icon->h - 1 - y)*inxlen; for(x = 0; x < icon->w; x++){ if(s < 0){ byte = *from++; s = 8-icon->bits; } *to++ = map[(byte>>s) & mask]; s -= icon->bits; } } /* stick in an image */ img = allocimage(display, Rect(0,0,icon->w,icon->h), CMAP8, 0, DNofill); loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w); free(data); return img; } Image* and2img(Icon *icon, uchar *and) { uchar *data; Image *img; int inxlen; int outxlen; uchar *from, *to; int x, y; inxlen = 4*((icon->w+31)/32); to = data = malloc(inxlen*icon->h); /* rotate around the y axis and invert bits */ outxlen = (icon->w+7)/8; for(y = 0; y < icon->h; y++){ from = and + (icon->h - 1 - y)*inxlen; for(x = 0; x < outxlen; x++){ *to++ = ~(*from++); } } /* stick in an image */ img = allocimage(display, Rect(0,0,icon->w,icon->h), GREY1, 0, DNofill); loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen); free(data); return img; } int Bgeticon(Biobuf *b, Icon *icon) { ulong l; ushort s; uchar *xor; uchar *and; uchar *cm; uchar *buf; uchar *map2map; Image *img; Bseek(b, icon->offset, 0); buf = malloc(icon->len); if(buf == nil) return -1; if(Bread(b, buf, icon->len) != icon->len){ werrstr("unexpected EOF"); return -1; } /* this header's info takes precedence over previous one */ if(getl(buf) != 40){ werrstr("bad icon header"); return -1; } l = getl(buf+4); if(l != icon->w) icon->w = l; l = getl(buf+8); if(l>>1 != icon->h) icon->h = l>>1; s = gets(buf+12); if(s != icon->nplane) icon->nplane = s; s = gets(buf+14); if(s != icon->bits) icon->bits = s; /* limit what we handle */ switch(icon->bits){ case 1: case 2: case 4: case 8: break; default: werrstr("don't support %d bit pixels", icon->bits); return -1; } if(icon->nplane != 1){ werrstr("don't support %d planes", icon->nplane); return -1; } cm = buf + 40; xor = cm + 4*icon->ncolor; and = xor + icon->h*4*((icon->bits*icon->w+31)/32); /* translate the color map to a plan 9 one */ map2map = transcmap(icon, cm); /* convert the images */ icon->img = xor2img(icon, xor, map2map); icon->mask = and2img(icon, and); /* so that we save an image with a white background */ img = allocimage(display, icon->img->r, CMAP8, 0, DWhite); draw(img, icon->img->r, icon->img, icon->mask, ZP); icon->img = img; free(buf); free(map2map); return 0; } void usage(void) { fprint(2, "usage: %s [file]\n", argv0); exits("usage"); } enum { Mimage, Mmask, Mexit, Up= 1, Down= 0, }; char *menu3str[] = { [Mimage] "write image", [Mmask] "write mask", [Mexit] "exit", 0, }; Menu menu3 = { menu3str }; Cursor sight = { {-7, -7}, {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,}, {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,} }; void buttons(int ud) { while((mouse.buttons==0) != ud) mouse = emouse(); } void mesg(char *fmt, ...) { va_list arg; char buf[1024]; static char obuf[1024]; va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); string(screen, screen->r.min, background, ZP, font, obuf); string(screen, screen->r.min, display->white, ZP, font, buf); strcpy(obuf, buf); } void doimage(Icon *icon) { int rv; char file[256]; int fd; rv = -1; snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h); fd = create(file, OWRITE, 0664); if(fd >= 0){ rv = writeimage(fd, icon->img, 0); close(fd); } if(rv < 0) mesg("error writing %s: %r", file); else mesg("created %s", file); } void domask(Icon *icon) { int rv; char file[64]; int fd; rv = -1; snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h); fd = create(file, OWRITE, 0664); if(fd >= 0){ rv = writeimage(fd, icon->mask, 0); close(fd); } if(rv < 0) mesg("error writing %s: %r", file); else mesg("created %s", file); } void apply(void (*f)(Icon*)) { Icon *icon; esetcursor(&sight); buttons(Down); if(mouse.buttons == 4) for(icon = h.first; icon; icon = icon->next) if(ptinrect(mouse.xy, icon->sr)){ buttons(Up); f(icon); break; } buttons(Up); esetcursor(0); } void menu(void) { int sel; sel = emenuhit(3, &mouse, &menu3); switch(sel){ case Mimage: apply(doimage); break; case Mmask: apply(domask); break; case Mexit: exits(0); break; } } void mousemoved(void) { Icon *icon; for(icon = h.first; icon; icon = icon->next) if(ptinrect(mouse.xy, icon->sr)){ mesg("%dx%d", icon->w, icon->h); return; } mesg(""); } enum { BORDER= 1, }; void eresized(int new) { Icon *icon; Rectangle r; if(new && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window"); draw(screen, screen->clipr, background, nil, ZP); r.max.x = screen->r.min.x; r.min.y = screen->r.min.y + font->height + 2*BORDER; for(icon = h.first; icon != nil; icon = icon->next){ r.min.x = r.max.x + BORDER; r.max.x = r.min.x + Dx(icon->img->r); r.max.y = r.min.y + Dy(icon->img->r); draw(screen, r, icon->img, nil, ZP); border(screen, r, -BORDER, display->black, ZP); icon->sr = r; } flushimage(display, 1); } void main(int argc, char **argv) { Biobuf in; Icon *icon; int num, fd; Rectangle r; Event e; ARGBEGIN{ case 'd': debug = 1; break; }ARGEND; fd = -1; switch(argc){ case 0: fd = 0; break; case 1: fd = open(argv[0], OREAD); if(fd < 0) sysfatal("opening: %r"); break; default: usage(); break; } Binit(&in, fd, OREAD); if(Bgetheader(&in, &h) < 0) sysfatal("reading header: %r"); initdraw(nil, nil, "ico"); background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (128<<24)|(128<<16)|(128<<8)|0xFF); einit(Emouse|Ekeyboard); num = 0; r.min = Pt(4, 4); for(icon = h.first; icon != nil; icon = icon->next){ if(Bgeticon(&in, icon) < 0){ fprint(2, "%s: read fail: %r\n", argv0); continue; } if(debug) fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n", icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset); r.max = addpt(r.min, Pt(icon->w, icon->h)); icon->r = r; r.min.x += r.max.x; num++; } if(num == 0) exits("no images"); eresized(0); for(;;) switch(event(&e)){ case Ekeyboard: break; case Emouse: mouse = e.mouse; if(mouse.buttons & 4) menu(); else mousemoved(); break; } /* not reached */ }