Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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 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 */
}