ref: 3d5dca521797b5a91ebc4281fbaf58211bb4f40f
dir: /sys/src/cmd/jpg/ico.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <draw.h> #include <memdraw.h> #include <event.h> #include <cursor.h> #include "imagefile.h" typedef struct Icon Icon; struct Icon { Icon *next; ushort w; /* icon width */ ushort 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 */ Memimage *img; Memimage *mask; Rectangle r; /* relative */ Rectangle sr; /* abs */ }; typedef struct Header Header; struct Header { uint n; Icon *first; Icon *last; }; int debug; int cflag; 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) { uchar buf[40]; Icon *icon; int i; 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] == 0 ? 256 : buf[0]; icon->h = buf[1] == 0 ? 256 : buf[1]; icon->ncolor = buf[2] == 0 ? 256 : buf[2]; 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, int ncolor, uchar *map) { uchar *m, *p; int i; p = m = mallocz(sizeof(int)*(1<<icon->bits), 1); if(m == nil) sysfatal("malloc: %r"); for(i = 0; i < ncolor; i++){ *p++ = rgb2cmap(map[2], map[1], map[0]); map += 4; } return m; } Memimage* xor2img(Icon *icon, long chan, uchar *xor, uchar *map) { uchar *data; Memimage *img; int inxlen; uchar *from, *to; int s, byte, mask; int x, y; inxlen = 4*((icon->bits*icon->w+31)/32); img = allocmemimage(Rect(0,0,icon->w,icon->h), chan); if(img == nil) return nil; if(chan != CMAP8){ from = xor + icon->h*inxlen; for(y = 0; y < icon->h; y++){ from -= inxlen; loadmemimage(img, Rect(0,y,icon->w,y+1), from, inxlen); } return img; } to = data = malloc(icon->w*icon->h); if(data == nil){ freememimage(img); return nil; } /* rotate around the y axis, go to 8 bits, and convert color */ mask = (1<<icon->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 */ loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w); free(data); return img; } Memimage* and2img(Icon *icon, uchar *and) { uchar *data; Memimage *img; int inxlen; int outxlen; uchar *from, *to; int x, y; inxlen = 4*((icon->w+31)/32); to = data = malloc(inxlen*icon->h); if(data == nil) return nil; /* 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 */ if(img = allocmemimage(Rect(0,0,icon->w,icon->h), GREY1)) loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen); free(data); return img; } int Bgeticon(Biobuf *b, Icon *icon) { uchar *end; uchar *xor; uchar *and; uchar *cm; uchar *buf; uchar *map2map; Memimage *img; uchar magic[4]; int ncolor; long chan; Bseek(b, icon->offset, 0); if(Bread(b, magic, 4) != 4){ werrstr("unexpected EOF"); return -1; } if(magic[0] == 137 && memcmp(magic+1, "PNG", 3) == 0){ Rawimage **png; Bseek(b, -4, 1); png = Breadpng(b, CRGB); if(png == nil || png[0] == nil) return -1; switch(png[0]->chandesc){ case CY: chan = GREY8; break; case CYA16: chan = CHAN2(CGrey, 8, CAlpha, 8); break; case CRGB24: chan = RGB24; break; case CRGBA32: chan = RGBA32; break; default: werrstr("bad icon png channel descriptor"); return -1; } icon->mask = nil; icon->img = allocmemimage(png[0]->r, chan); loadmemimage(icon->img, icon->img->r, png[0]->chans[0], png[0]->chanlen); return 0; } if(getl(magic) != 40){ werrstr("bad icon bmp header"); return -1; } if(icon->len < 40){ werrstr("bad icon bmp header length"); return -1; } buf = malloc(icon->len); if(buf == nil) return -1; memmove(buf, magic, 4); if(Bread(b, buf+4, icon->len-4) != icon->len-4){ werrstr("unexpected EOF"); return -1; } /* this header's info takes precedence over previous one */ ncolor = 0; icon->w = getl(buf+4); icon->h = getl(buf+8)>>1; icon->nplane = gets(buf+12); icon->bits = gets(buf+14); if(icon->w == 0) icon->w = 256; if(icon->h == 0) icon->h = 256; /* limit what we handle */ switch(icon->bits){ case 1: case 2: case 4: case 8: ncolor = icon->ncolor; if(ncolor > (1<<icon->bits)) ncolor = 1<<icon->bits; chan = CMAP8; break; case 15: case 16: chan = RGB16; break; case 24: chan = RGB24; break; case 32: chan = ARGB32; 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; } xor = cm = buf + 40; if(chan == CMAP8) xor += 4*ncolor; end = xor + icon->h*4*((icon->bits*icon->w+31)/32); if(end < buf || end > buf+icon->len){ werrstr("bad icon length %zux != %lux", end - buf, icon->len); return -1; } /* translate the color map to a plan 9 one */ map2map = nil; if(chan == CMAP8) map2map = transcmap(icon, ncolor, cm); /* convert the images */ icon->img = xor2img(icon, chan, xor, map2map); if(icon->img == nil){ werrstr("xor2img: %r"); return -1; } icon->mask = nil; /* check for and mask */ and = end; end += icon->h*4*((icon->w+31)/32); if(end <= buf+icon->len) icon->mask = and2img(icon, and); /* so that we save an image with a white background */ if(img = allocmemimage(icon->img->r, icon->img->chan)){ memfillcolor(img, DWhite); memimagedraw(img, icon->img->r, icon->img, ZP, icon->mask, ZP, SoverD); freememimage(icon->img); icon->img = img; } free(buf); free(map2map); return 0; } void usage(void) { fprint(2, "usage: %s [ -c ] [ 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 = writememimage(fd, icon->img); 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; if(icon->mask == nil) return; rv = -1; snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h); fd = create(file, OWRITE, 0664); if(fd >= 0){ rv = writememimage(fd, icon->mask); 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, }; Image* screenimage(Memimage *m) { Rectangle r; Image *i; if(i = allocimage(display, m->r, m->chan, 0, DNofill)){ r = m->r; while(r.min.y < m->r.max.y){ r.max.y = r.min.y+1; loadimage(i, r, byteaddr(m, r.min), bytesperline(r, m->depth)); r.min.y++; } } return i; } void eresized(int new) { Icon *icon; Image *i; 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){ if(icon->img == nil) continue; 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); if(i = screenimage(icon->img)){ draw(screen, r, i, nil, ZP); freeimage(i); } 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; case 'c': cflag = 1; break; default: usage(); }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; } memimageinit(); Binit(&in, fd, OREAD); if(Bgetheader(&in, &h) < 0) sysfatal("reading header: %r"); 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; if(cflag){ writememimage(1, icon->img); exits(0); } r.min.x += r.max.x; num++; } if(num == 0 || cflag) sysfatal("no images"); initdraw(nil, nil, "ico"); background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x808080FF); eresized(0); einit(Emouse|Ekeyboard); for(;;) switch(event(&e)){ case Ekeyboard: break; case Emouse: mouse = e.mouse; if(mouse.buttons & 4) menu(); else mousemoved(); break; } /* not reached */ }