#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
enum
{
FileHdrLen= 6,
IconDescrLen= 16,
IconHdrLen= 40,
};
typedef struct Icon Icon;
struct Icon
{
Icon *next;
char *file;
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 */
uchar map[4*256]; /* color map */
Image *img;
uchar *xor;
int xorlen;
uchar *and;
int andlen;
};
typedef struct Header Header;
struct Header
{
uint n;
Icon *first;
Icon *last;
};
void
Bputs(Biobuf *b, ushort x)
{
Bputc(b, x&0xff);
Bputc(b, x>>8);
}
void
Bputl(Biobuf *b, ulong x)
{
Bputs(b, x&0xffff);
Bputs(b, x>>16);
}
Header h;
void* emalloc(int);
void mk8bit(Icon*, int);
void mkxorand(Icon*, int);
void readicon(char*);
void
main(int argc, char **argv)
{
int i;
Biobuf *b, out;
Icon *icon;
ulong offset;
ulong len;
ARGBEGIN{
}ARGEND;
/* read in all the images */
display = initdisplay(nil, nil, nil);
if(argc < 1){
readicon("/fd/0");
} else {
for(i = 0; i < argc; i++)
readicon(argv[i]);
}
/* create the .ico file */
b = &out;
Binit(b, 1, OWRITE);
/* offset to first icon */
offset = FileHdrLen + h.n*IconDescrLen;
/* file header is */
Bputs(b, 0);
Bputs(b, 1);
Bputs(b, h.n);
/* icon description */
for(icon = h.first; icon != nil; icon = icon->next){
Bputc(b, icon->w);
Bputc(b, icon->h);
Bputc(b, icon->ncolor);
Bputc(b, 0);
Bputs(b, icon->nplane);
Bputs(b, icon->bits);
len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen;
Bputl(b, len);
Bputl(b, offset);
offset += len;
}
/* icons */
for(icon = h.first; icon != nil; icon = icon->next){
/* icon header (BMP like) */
Bputl(b, IconHdrLen);
Bputl(b, icon->w);
Bputl(b, 2*icon->h);
Bputs(b, icon->nplane);
Bputs(b, icon->bits);
Bputl(b, 0); /* compression info */
Bputl(b, 0);
Bputl(b, 0);
Bputl(b, 0);
Bputl(b, 0);
Bputl(b, 0);
/* color map */
if(Bwrite(b, icon->map, 4*icon->ncolor) < 0)
sysfatal("writing color map: %r");
/* xor bits */
if(Bwrite(b, icon->xor, icon->xorlen) < 0)
sysfatal("writing xor bits: %r");
/* and bits */
if(Bwrite(b, icon->and, icon->andlen) < 0)
sysfatal("writing and bits: %r");
}
Bterm(b);
exits(0);
}
void
readicon(char *file)
{
int fd;
Icon *icon;
fd = open(file, OREAD);
if(fd < 0)
sysfatal("opening %s: %r", file);
icon = emalloc(sizeof(Icon));
icon->img = readimage(display, fd, 0);
if(icon->img == nil)
sysfatal("reading image %s: %r", file);
close(fd);
if(h.first)
h.last->next = icon;
else
h.first = icon;
h.last = icon;
h.n++;
icon->h = Dy(icon->img->r);
icon->w = Dx(icon->img->r);
icon->bits = 1<<icon->img->depth;
icon->nplane = 1;
/* convert to 8 bits per pixel */
switch(icon->img->chan){
case GREY8:
case CMAP8:
break;
case GREY1:
case GREY2:
case GREY4:
mk8bit(icon, 1);
break;
default:
mk8bit(icon, 0);
break;
}
icon->bits = 8;
icon->file = file;
/* create xor/and masks, minimizing bits per pixel */
mkxorand(icon, icon->img->chan == GREY8);
}
void*
emalloc(int len)
{
void *x;
x = mallocz(len, 1);
if(x == nil)
sysfatal("memory: %r");
return x;
}
/* convert to 8 bit */
void
mk8bit(Icon *icon, int grey)
{
Image *img;
img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill);
if(img == nil)
sysfatal("can't allocimage: %r");
draw(img, img->r, icon->img, nil, ZP);
freeimage(icon->img);
icon->img = img;
}
/* make xor and and mask */
void
mkxorand(Icon *icon, int grey)
{
int i, x, y, s, sa;
uchar xx[256];
uchar *data, *p, *e;
int ndata;
uchar *mp;
int ncolor;
ulong color;
int bits;
uchar andbyte, xorbyte;
uchar *ato, *xto;
int xorrl, andrl;
ndata = icon->h * icon->w;
data = emalloc(ndata);
if(unloadimage(icon->img, icon->img->r, data, ndata) < 0)
sysfatal("can't unload %s: %r", icon->file);
e = data + ndata;
/* find colors used */
memset(xx, 0, sizeof xx);
for(p = data; p < e; p++)
xx[*p]++;
/* count the colors and create a mapping from plan 9 */
mp = icon->map;
ncolor = 0;
for(i = 0; i < 256; i++){
if(xx[i] == 0)
continue;
if(grey){
*mp++ = i;
*mp++ = i;
*mp++ = i;
*mp++ = 0;
} else {
color = cmap2rgb(i);
*mp++ = color;
*mp++ = color>>8;
*mp++ = color>>16;
*mp++ = 0;
}
xx[i] = ncolor;
ncolor++;
}
/* get minimum number of pixels per bit (with a color map) */
if(ncolor <= 2){
ncolor = 2;
bits = 1;
} else if(ncolor <= 4){
ncolor = 4;
bits = 2;
} else if(ncolor <= 16){
ncolor = 16;
bits = 4;
} else {
ncolor = 256;
bits = 8;
}
icon->bits = bits;
icon->ncolor = ncolor;
/* the xor mask rows are justified to a 32 bit boundary */
/* the and mask is 1 bit grey */
xorrl = 4*((bits*icon->w + 31)/32);
andrl = 4*((icon->w + 31)/32);
icon->xor = emalloc(xorrl * icon->h);
icon->and = emalloc(andrl * icon->h);
icon->xorlen = xorrl*icon->h;
icon->andlen = andrl*icon->h;
/* make both masks. they're upside down relative to plan9 ones */
p = data;
for(y = 0; y < icon->h; y++){
andbyte = 0;
xorbyte = 0;
sa = s = 0;
xto = icon->xor + (icon->h-1-y)*xorrl;
ato = icon->and + (icon->h-1-y)*andrl;
for(x = 0; x < icon->w; x++){
xorbyte <<= bits;
xorbyte |= xx[*p];
s += bits;
if(s == 8){
*xto++ = xorbyte;
xorbyte = 0;
s = 0;
}
andbyte <<= 1;
if(*p == 0xff)
andbyte |= 1;
sa++;
if(sa == 0){
*ato++ = andbyte;
sa = 0;
andbyte = 0;
}
p++;
}
}
free(data);
}
|