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