Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <event.h>
6 #include <cursor.h>
8 typedef struct Icon Icon;
9 struct Icon
10 {
11 Icon *next;
13 uchar w; /* icon width */
14 uchar h; /* icon height */
15 ushort ncolor; /* number of colors */
16 ushort nplane; /* number of bit planes */
17 ushort bits; /* bits per pixel */
18 ulong len; /* length of data */
19 ulong offset; /* file offset to data */
21 Image *img;
22 Image *mask;
24 Rectangle r; /* relative */
25 Rectangle sr; /* abs */
26 };
28 typedef struct Header Header;
29 struct Header
30 {
31 uint n;
32 Icon *first;
33 Icon *last;
34 };
36 int debug;
37 Mouse mouse;
38 Header h;
39 Image *background;
41 ushort
42 gets(uchar *p)
43 {
44 return p[0] | (p[1]<<8);
45 }
47 ulong
48 getl(uchar *p)
49 {
50 return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
51 }
53 int
54 Bgetheader(Biobuf *b, Header *h)
55 {
56 Icon *icon;
57 int i;
58 uchar buf[40];
60 memset(h, 0, sizeof(*h));
61 if(Bread(b, buf, 6) != 6)
62 goto eof;
63 if(gets(&buf[0]) != 0)
64 goto header;
65 if(gets(&buf[2]) != 1)
66 goto header;
67 h->n = gets(&buf[4]);
69 for(i = 0; i < h->n; i++){
70 icon = mallocz(sizeof(*icon), 1);
71 if(icon == nil)
72 sysfatal("malloc: %r");
73 if(Bread(b, buf, 16) != 16)
74 goto eof;
75 icon->w = buf[0];
76 icon->h = buf[1];
77 icon->ncolor = buf[2] == 0 ? 256 : buf[2];
78 if(buf[3] != 0)
79 goto header;
80 icon->nplane = gets(&buf[4]);
81 icon->bits = gets(&buf[6]);
82 icon->len = getl(&buf[8]);
83 icon->offset = getl(&buf[12]);
85 if(i == 0)
86 h->first = icon;
87 else
88 h->last->next = icon;
89 h->last = icon;
90 }
91 return 0;
93 eof:
94 werrstr("unexpected EOF");
95 return -1;
96 header:
97 werrstr("unknown header format");
98 return -1;
99 }
101 uchar*
102 transcmap(Icon *icon, uchar *map)
104 uchar *m, *p;
105 int i;
107 p = m = malloc(sizeof(int)*(1<<icon->bits));
108 for(i = 0; i < icon->ncolor; i++){
109 *p++ = rgb2cmap(map[2], map[1], map[0]);
110 map += 4;
112 return m;
115 Image*
116 xor2img(Icon *icon, uchar *xor, uchar *map)
118 uchar *data;
119 Image *img;
120 int inxlen;
121 uchar *from, *to;
122 int s, byte, mask;
123 int x, y;
125 inxlen = 4*((icon->bits*icon->w+31)/32);
126 to = data = malloc(icon->w*icon->h);
128 /* rotate around the y axis, go to 8 bits, and convert color */
129 mask = (1<<icon->bits)-1;
130 for(y = 0; y < icon->h; y++){
131 s = -1;
132 byte = 0;
133 from = xor + (icon->h - 1 - y)*inxlen;
134 for(x = 0; x < icon->w; x++){
135 if(s < 0){
136 byte = *from++;
137 s = 8-icon->bits;
139 *to++ = map[(byte>>s) & mask];
140 s -= icon->bits;
144 /* stick in an image */
145 img = allocimage(display, Rect(0,0,icon->w,icon->h), CMAP8, 0, DNofill);
146 loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w);
148 free(data);
149 return img;
152 Image*
153 and2img(Icon *icon, uchar *and)
155 uchar *data;
156 Image *img;
157 int inxlen;
158 int outxlen;
159 uchar *from, *to;
160 int x, y;
162 inxlen = 4*((icon->w+31)/32);
163 to = data = malloc(inxlen*icon->h);
165 /* rotate around the y axis and invert bits */
166 outxlen = (icon->w+7)/8;
167 for(y = 0; y < icon->h; y++){
168 from = and + (icon->h - 1 - y)*inxlen;
169 for(x = 0; x < outxlen; x++){
170 *to++ = ~(*from++);
174 /* stick in an image */
175 img = allocimage(display, Rect(0,0,icon->w,icon->h), GREY1, 0, DNofill);
176 loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen);
178 free(data);
179 return img;
182 int
183 Bgeticon(Biobuf *b, Icon *icon)
185 ulong l;
186 ushort s;
187 uchar *xor;
188 uchar *and;
189 uchar *cm;
190 uchar *buf;
191 uchar *map2map;
192 Image *img;
194 Bseek(b, icon->offset, 0);
195 buf = malloc(icon->len);
196 if(buf == nil)
197 return -1;
198 if(Bread(b, buf, icon->len) != icon->len){
199 werrstr("unexpected EOF");
200 return -1;
203 /* this header's info takes precedence over previous one */
204 if(getl(buf) != 40){
205 werrstr("bad icon header");
206 return -1;
208 l = getl(buf+4);
209 if(l != icon->w)
210 icon->w = l;
211 l = getl(buf+8);
212 if(l>>1 != icon->h)
213 icon->h = l>>1;
214 s = gets(buf+12);
215 if(s != icon->nplane)
216 icon->nplane = s;
217 s = gets(buf+14);
218 if(s != icon->bits)
219 icon->bits = s;
221 /* limit what we handle */
222 switch(icon->bits){
223 case 1:
224 case 2:
225 case 4:
226 case 8:
227 break;
228 default:
229 werrstr("don't support %d bit pixels", icon->bits);
230 return -1;
232 if(icon->nplane != 1){
233 werrstr("don't support %d planes", icon->nplane);
234 return -1;
237 cm = buf + 40;
238 xor = cm + 4*icon->ncolor;
239 and = xor + icon->h*4*((icon->bits*icon->w+31)/32);
241 /* translate the color map to a plan 9 one */
242 map2map = transcmap(icon, cm);
244 /* convert the images */
245 icon->img = xor2img(icon, xor, map2map);
246 icon->mask = and2img(icon, and);
248 /* so that we save an image with a white background */
249 img = allocimage(display, icon->img->r, CMAP8, 0, DWhite);
250 draw(img, icon->img->r, icon->img, icon->mask, ZP);
251 icon->img = img;
253 free(buf);
254 free(map2map);
255 return 0;
258 void
259 usage(void)
261 fprint(2, "usage: %s -W winsize [file]\n", argv0);
262 exits("usage");
265 enum
267 Mimage,
268 Mmask,
269 Mexit,
271 Up= 1,
272 Down= 0,
273 };
275 char *menu3str[] = {
276 "write image",
277 "write mask",
278 "exit",
279 0,
280 };
282 Menu menu3 = {
283 menu3str
284 };
286 Cursor sight = {
287 {-7, -7},
288 {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
289 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
290 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
291 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
292 {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
293 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
294 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
295 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
296 };
298 void
299 buttons(int ud)
301 while((mouse.buttons==0) != ud)
302 mouse = emouse();
305 void
306 mesg(char *fmt, ...)
308 va_list arg;
309 char buf[1024];
310 static char obuf[1024];
312 va_start(arg, fmt);
313 vseprint(buf, buf+sizeof(buf), fmt, arg);
314 va_end(arg);
315 string(screen, screen->r.min, background, ZP, font, obuf);
316 string(screen, screen->r.min, display->white, ZP, font, buf);
317 strcpy(obuf, buf);
320 void
321 doimage(Icon *icon)
323 int rv;
324 char file[256];
325 int fd;
327 rv = -1;
328 snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h);
329 fd = create(file, OWRITE, 0664);
330 if(fd >= 0){
331 rv = writeimage(fd, icon->img, 0);
332 close(fd);
334 if(rv < 0)
335 mesg("error writing %s: %r", file);
336 else
337 mesg("created %s", file);
340 void
341 domask(Icon *icon)
343 int rv;
344 char file[64];
345 int fd;
347 rv = -1;
348 snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h);
349 fd = create(file, OWRITE, 0664);
350 if(fd >= 0){
351 rv = writeimage(fd, icon->mask, 0);
352 close(fd);
354 if(rv < 0)
355 mesg("error writing %s: %r", file);
356 else
357 mesg("created %s", file);
360 void
361 apply(void (*f)(Icon*))
363 Icon *icon;
365 esetcursor(&sight);
366 buttons(Down);
367 if(mouse.buttons == 4)
368 for(icon = h.first; icon; icon = icon->next)
369 if(ptinrect(mouse.xy, icon->sr)){
370 buttons(Up);
371 f(icon);
372 break;
374 buttons(Up);
375 esetcursor(0);
378 void
379 menu(void)
381 int sel;
383 sel = emenuhit(3, &mouse, &menu3);
384 switch(sel){
385 case Mimage:
386 apply(doimage);
387 break;
388 case Mmask:
389 apply(domask);
390 break;
391 case Mexit:
392 exits(0);
393 break;
397 void
398 mousemoved(void)
400 Icon *icon;
402 for(icon = h.first; icon; icon = icon->next)
403 if(ptinrect(mouse.xy, icon->sr)){
404 mesg("%dx%d", icon->w, icon->h);
405 return;
407 mesg("");
410 enum
412 BORDER= 1,
413 };
415 void
416 eresized(int new)
418 Icon *icon;
419 Rectangle r;
421 if(new && getwindow(display, Refnone) < 0)
422 sysfatal("can't reattach to window");
423 draw(screen, screen->clipr, background, nil, ZP);
424 r.max.x = screen->r.min.x;
425 r.min.y = screen->r.min.y + font->height + 2*BORDER;
426 for(icon = h.first; icon != nil; icon = icon->next){
427 r.min.x = r.max.x + BORDER;
428 r.max.x = r.min.x + Dx(icon->img->r);
429 r.max.y = r.min.y + Dy(icon->img->r);
430 draw(screen, r, icon->img, nil, ZP);
431 border(screen, r, -BORDER, display->black, ZP);
432 icon->sr = r;
434 flushimage(display, 1);
437 void
438 main(int argc, char **argv)
440 Biobuf in;
441 Icon *icon;
442 int fd;
443 Rectangle r;
444 Event e;
446 ARGBEGIN{
447 case 'W':
448 winsize = EARGF(usage());
449 break;
450 case 'd':
451 debug = 1;
452 break;
453 }ARGEND;
455 fd = -1;
456 switch(argc){
457 case 0:
458 fd = 0;
459 break;
460 case 1:
461 fd = open(argv[0], OREAD);
462 if(fd < 0)
463 sysfatal("opening: %r");
464 break;
465 default:
466 usage();
467 break;
470 Binit(&in, fd, OREAD);
472 if(Bgetheader(&in, &h) < 0)
473 sysfatal("reading header: %r");
475 initdraw(0, nil, "ico");
476 background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x808080FF);
478 einit(Emouse|Ekeyboard);
480 r.min = Pt(4, 4);
481 for(icon = h.first; icon != nil; icon = icon->next){
482 if(Bgeticon(&in, icon) < 0){
483 fprint(2, "bad rectangle: %r\n");
484 continue;
486 if(debug)
487 fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n",
488 icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
489 r.max = addpt(r.min, Pt(icon->w, icon->h));
490 icon->r = r;
491 r.min.x += r.max.x;
493 eresized(0);
495 for(;;)
496 switch(event(&e)){
497 case Ekeyboard:
498 break;
499 case Emouse:
500 mouse = e.mouse;
501 if(mouse.buttons & 4)
502 menu();
503 else
504 mousemoved();
505 break;