ico.c (8791B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <draw.h> 5 #include <event.h> 6 #include <cursor.h> 7 8 typedef struct Icon Icon; 9 struct Icon 10 { 11 Icon *next; 12 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 */ 20 21 Image *img; 22 Image *mask; 23 24 Rectangle r; /* relative */ 25 Rectangle sr; /* abs */ 26 }; 27 28 typedef struct Header Header; 29 struct Header 30 { 31 uint n; 32 Icon *first; 33 Icon *last; 34 }; 35 36 int debug; 37 Mouse mouse; 38 Header h; 39 Image *background; 40 41 ushort 42 gets(uchar *p) 43 { 44 return p[0] | (p[1]<<8); 45 } 46 47 ulong 48 getl(uchar *p) 49 { 50 return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); 51 } 52 53 int 54 Bgetheader(Biobuf *b, Header *h) 55 { 56 Icon *icon; 57 int i; 58 uchar buf[40]; 59 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]); 68 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]); 84 85 if(i == 0) 86 h->first = icon; 87 else 88 h->last->next = icon; 89 h->last = icon; 90 } 91 return 0; 92 93 eof: 94 werrstr("unexpected EOF"); 95 return -1; 96 header: 97 werrstr("unknown header format"); 98 return -1; 99 } 100 101 uchar* 102 transcmap(Icon *icon, uchar *map) 103 { 104 uchar *m, *p; 105 int i; 106 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; 111 } 112 return m; 113 } 114 115 Image* 116 xor2img(Icon *icon, uchar *xor, uchar *map) 117 { 118 uchar *data; 119 Image *img; 120 int inxlen; 121 uchar *from, *to; 122 int s, byte, mask; 123 int x, y; 124 125 inxlen = 4*((icon->bits*icon->w+31)/32); 126 to = data = malloc(icon->w*icon->h); 127 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; 138 } 139 *to++ = map[(byte>>s) & mask]; 140 s -= icon->bits; 141 } 142 } 143 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); 147 148 free(data); 149 return img; 150 } 151 152 Image* 153 and2img(Icon *icon, uchar *and) 154 { 155 uchar *data; 156 Image *img; 157 int inxlen; 158 int outxlen; 159 uchar *from, *to; 160 int x, y; 161 162 inxlen = 4*((icon->w+31)/32); 163 to = data = malloc(inxlen*icon->h); 164 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++); 171 } 172 } 173 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); 177 178 free(data); 179 return img; 180 } 181 182 int 183 Bgeticon(Biobuf *b, Icon *icon) 184 { 185 ulong l; 186 ushort s; 187 uchar *xor; 188 uchar *and; 189 uchar *cm; 190 uchar *buf; 191 uchar *map2map; 192 Image *img; 193 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; 201 } 202 203 /* this header's info takes precedence over previous one */ 204 if(getl(buf) != 40){ 205 werrstr("bad icon header"); 206 return -1; 207 } 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; 220 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; 231 } 232 if(icon->nplane != 1){ 233 werrstr("don't support %d planes", icon->nplane); 234 return -1; 235 } 236 237 cm = buf + 40; 238 xor = cm + 4*icon->ncolor; 239 and = xor + icon->h*4*((icon->bits*icon->w+31)/32); 240 241 /* translate the color map to a plan 9 one */ 242 map2map = transcmap(icon, cm); 243 244 /* convert the images */ 245 icon->img = xor2img(icon, xor, map2map); 246 icon->mask = and2img(icon, and); 247 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; 252 253 free(buf); 254 free(map2map); 255 return 0; 256 } 257 258 void 259 usage(void) 260 { 261 fprint(2, "usage: %s -W winsize [file]\n", argv0); 262 exits("usage"); 263 } 264 265 enum 266 { 267 Mimage, 268 Mmask, 269 Mexit, 270 271 Up= 1, 272 Down= 0 273 }; 274 275 char *menu3str[] = { 276 "write image", 277 "write mask", 278 "exit", 279 0 280 }; 281 282 Menu menu3 = { 283 menu3str 284 }; 285 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 }; 297 298 void 299 buttons(int ud) 300 { 301 while((mouse.buttons==0) != ud) 302 mouse = emouse(); 303 } 304 305 void 306 mesg(char *fmt, ...) 307 { 308 va_list arg; 309 char buf[1024]; 310 static char obuf[1024]; 311 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); 318 } 319 320 void 321 doimage(Icon *icon) 322 { 323 int rv; 324 char file[256]; 325 int fd; 326 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); 333 } 334 if(rv < 0) 335 mesg("error writing %s: %r", file); 336 else 337 mesg("created %s", file); 338 } 339 340 void 341 domask(Icon *icon) 342 { 343 int rv; 344 char file[64]; 345 int fd; 346 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); 353 } 354 if(rv < 0) 355 mesg("error writing %s: %r", file); 356 else 357 mesg("created %s", file); 358 } 359 360 void 361 apply(void (*f)(Icon*)) 362 { 363 Icon *icon; 364 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; 373 } 374 buttons(Up); 375 esetcursor(0); 376 } 377 378 void 379 menu(void) 380 { 381 int sel; 382 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; 394 } 395 } 396 397 void 398 mousemoved(void) 399 { 400 Icon *icon; 401 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; 406 } 407 mesg(""); 408 } 409 410 enum 411 { 412 BORDER= 1 413 }; 414 415 void 416 eresized(int new) 417 { 418 Icon *icon; 419 Rectangle r; 420 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; 433 } 434 flushimage(display, 1); 435 } 436 437 void 438 main(int argc, char **argv) 439 { 440 Biobuf in; 441 Icon *icon; 442 int fd; 443 Rectangle r; 444 Event e; 445 446 ARGBEGIN{ 447 case 'W': 448 winsize = EARGF(usage()); 449 break; 450 case 'd': 451 debug = 1; 452 break; 453 }ARGEND; 454 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; 468 } 469 470 Binit(&in, fd, OREAD); 471 472 if(Bgetheader(&in, &h) < 0) 473 sysfatal("reading header: %r"); 474 475 initdraw(0, nil, "ico"); 476 background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x808080FF); 477 478 einit(Emouse|Ekeyboard); 479 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; 485 } 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; 492 } 493 eresized(0); 494 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; 506 } 507 }