Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
6 enum
7 {
8 FileHdrLen= 6,
9 IconDescrLen= 16,
10 IconHdrLen= 40
11 };
13 typedef struct Icon Icon;
14 struct Icon
15 {
16 Icon *next;
17 char *file;
19 uchar w; /* icon width */
20 uchar h; /* icon height */
21 ushort ncolor; /* number of colors */
22 ushort nplane; /* number of bit planes */
23 ushort bits; /* bits per pixel */
24 ulong len; /* length of data */
25 ulong offset; /* file offset to data */
26 uchar map[4*256]; /* color map */
28 Image *img;
30 uchar *xor;
31 int xorlen;
32 uchar *and;
33 int andlen;
34 };
36 typedef struct Header Header;
37 struct Header
38 {
39 uint n;
40 Icon *first;
41 Icon *last;
42 };
44 void
45 Bputs(Biobuf *b, ushort x)
46 {
47 Bputc(b, x&0xff);
48 Bputc(b, x>>8);
49 }
51 void
52 Bputl(Biobuf *b, ulong x)
53 {
54 Bputs(b, x&0xffff);
55 Bputs(b, x>>16);
56 }
58 Header h;
60 void* emalloc(int);
61 void mk8bit(Icon*, int);
62 void mkxorand(Icon*, int);
63 void readicon(char*);
65 void
66 main(int argc, char **argv)
67 {
68 int i;
69 Biobuf *b, out;
70 Icon *icon;
71 ulong offset;
72 ulong len;
74 ARGBEGIN{
75 }ARGEND;
77 /* read in all the images */
78 initdraw(0, nil, nil);
79 if(argc < 1){
80 readicon("/dev/stdin");
81 } else {
82 for(i = 0; i < argc; i++)
83 readicon(argv[i]);
84 }
86 /* create the .ico file */
87 b = &out;
88 Binit(b, 1, OWRITE);
90 /* offset to first icon */
91 offset = FileHdrLen + h.n*IconDescrLen;
93 /* file header is */
94 Bputs(b, 0);
95 Bputs(b, 1);
96 Bputs(b, h.n);
98 /* icon description */
99 for(icon = h.first; icon != nil; icon = icon->next){
100 Bputc(b, icon->w);
101 Bputc(b, icon->h);
102 Bputc(b, icon->ncolor);
103 Bputc(b, 0);
104 Bputs(b, icon->nplane);
105 Bputs(b, icon->bits);
106 len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen;
107 Bputl(b, len);
108 Bputl(b, offset);
109 offset += len;
112 /* icons */
113 for(icon = h.first; icon != nil; icon = icon->next){
114 /* icon header (BMP like) */
115 Bputl(b, IconHdrLen);
116 Bputl(b, icon->w);
117 Bputl(b, 2*icon->h);
118 Bputs(b, icon->nplane);
119 Bputs(b, icon->bits);
120 Bputl(b, 0); /* compression info */
121 Bputl(b, 0);
122 Bputl(b, 0);
123 Bputl(b, 0);
124 Bputl(b, 0);
125 Bputl(b, 0);
127 /* color map */
128 if(Bwrite(b, icon->map, 4*icon->ncolor) < 0)
129 sysfatal("writing color map: %r");
131 /* xor bits */
132 if(Bwrite(b, icon->xor, icon->xorlen) < 0)
133 sysfatal("writing xor bits: %r");
135 /* and bits */
136 if(Bwrite(b, icon->and, icon->andlen) < 0)
137 sysfatal("writing and bits: %r");
140 Bterm(b);
141 exits(0);
144 void
145 readicon(char *file)
147 int fd;
148 Icon *icon;
150 fd = open(file, OREAD);
151 if(fd < 0)
152 sysfatal("opening %s: %r", file);
153 icon = emalloc(sizeof(Icon));
154 icon->img = readimage(display, fd, 0);
155 if(icon->img == nil)
156 sysfatal("reading image %s: %r", file);
157 close(fd);
159 if(h.first)
160 h.last->next = icon;
161 else
162 h.first = icon;
163 h.last = icon;
164 h.n++;
166 icon->h = Dy(icon->img->r);
167 icon->w = Dx(icon->img->r);
168 icon->bits = 1<<icon->img->depth;
169 icon->nplane = 1;
171 /* convert to 8 bits per pixel */
172 switch(icon->img->chan){
173 case GREY8:
174 case CMAP8:
175 break;
176 case GREY1:
177 case GREY2:
178 case GREY4:
179 mk8bit(icon, 1);
180 break;
181 default:
182 mk8bit(icon, 0);
183 break;
185 icon->bits = 8;
186 icon->file = file;
188 /* create xor/and masks, minimizing bits per pixel */
189 mkxorand(icon, icon->img->chan == GREY8);
192 void*
193 emalloc(int len)
195 void *x;
197 x = mallocz(len, 1);
198 if(x == nil)
199 sysfatal("memory: %r");
200 return x;
203 /* convert to 8 bit */
204 void
205 mk8bit(Icon *icon, int grey)
207 Image *img;
209 img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill);
210 if(img == nil)
211 sysfatal("can't allocimage: %r");
212 draw(img, img->r, icon->img, nil, ZP);
213 freeimage(icon->img);
214 icon->img = img;
217 /* make xor and and mask */
218 void
219 mkxorand(Icon *icon, int grey)
221 int i, x, y, s, sa;
222 uchar xx[256];
223 uchar *data, *p, *e;
224 int ndata;
225 uchar *mp;
226 int ncolor;
227 ulong color;
228 int bits;
229 uchar andbyte, xorbyte;
230 uchar *ato, *xto;
231 int xorrl, andrl;
233 ndata = icon->h * icon->w;
234 data = emalloc(ndata);
235 if(unloadimage(icon->img, icon->img->r, data, ndata) < 0)
236 sysfatal("can't unload %s: %r", icon->file);
237 e = data + ndata;
239 /* find colors used */
240 memset(xx, 0, sizeof xx);
241 for(p = data; p < e; p++)
242 xx[*p]++;
244 /* count the colors and create a mapping from plan 9 */
245 mp = icon->map;
246 ncolor = 0;
247 for(i = 0; i < 256; i++){
248 if(xx[i] == 0)
249 continue;
250 if(grey){
251 *mp++ = i;
252 *mp++ = i;
253 *mp++ = i;
254 *mp++ = 0;
255 } else {
256 color = cmap2rgb(i);
257 *mp++ = color;
258 *mp++ = color>>8;
259 *mp++ = color>>16;
260 *mp++ = 0;
262 xx[i] = ncolor;
263 ncolor++;
266 /* get minimum number of pixels per bit (with a color map) */
267 if(ncolor <= 2){
268 ncolor = 2;
269 bits = 1;
270 } else if(ncolor <= 4){
271 ncolor = 4;
272 bits = 2;
273 } else if(ncolor <= 16){
274 ncolor = 16;
275 bits = 4;
276 } else {
277 ncolor = 256;
278 bits = 8;
280 icon->bits = bits;
281 icon->ncolor = ncolor;
283 /* the xor mask rows are justified to a 32 bit boundary */
284 /* the and mask is 1 bit grey */
285 xorrl = 4*((bits*icon->w + 31)/32);
286 andrl = 4*((icon->w + 31)/32);
287 icon->xor = emalloc(xorrl * icon->h);
288 icon->and = emalloc(andrl * icon->h);
289 icon->xorlen = xorrl*icon->h;
290 icon->andlen = andrl*icon->h;
292 /* make both masks. they're upside down relative to plan9 ones */
293 p = data;
294 for(y = 0; y < icon->h; y++){
295 andbyte = 0;
296 xorbyte = 0;
297 sa = s = 0;
298 xto = icon->xor + (icon->h-1-y)*xorrl;
299 ato = icon->and + (icon->h-1-y)*andrl;
300 for(x = 0; x < icon->w; x++){
301 xorbyte <<= bits;
302 xorbyte |= xx[*p];
303 s += bits;
304 if(s == 8){
305 *xto++ = xorbyte;
306 xorbyte = 0;
307 s = 0;
309 andbyte <<= 1;
310 if(*p == 0xff)
311 andbyte |= 1;
312 sa++;
313 if(sa == 0){
314 *ato++ = andbyte;
315 sa = 0;
316 andbyte = 0;
318 p++;
321 free(data);