diff options
Diffstat (limited to 'src/cmd/jpg/ico.c')
-rw-r--r-- | src/cmd/jpg/ico.c | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/src/cmd/jpg/ico.c b/src/cmd/jpg/ico.c new file mode 100644 index 00000000..31806afb --- /dev/null +++ b/src/cmd/jpg/ico.c @@ -0,0 +1,506 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include <cursor.h> + +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<<icon->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<<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 */ + 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 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); + + r.min = Pt(4, 4); + for(icon = h.first; icon != nil; icon = icon->next){ + if(Bgeticon(&in, icon) < 0){ + fprint(2, "bad rectangle: %r\n"); + 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; + } + eresized(0); + + for(;;) + switch(event(&e)){ + case Ekeyboard: + break; + case Emouse: + mouse = e.mouse; + if(mouse.buttons & 4) + menu(); + else + mousemoved(); + break; + } + + exits(0); +} |