ico.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <bio.h>
  12. #include <draw.h>
  13. #include <event.h>
  14. #include <cursor.h>
  15. typedef struct Icon Icon;
  16. struct Icon
  17. {
  18. Icon *next;
  19. uint8_t w; /* icon width */
  20. uint8_t h; /* icon height */
  21. uint16_t ncolor; /* number of colors */
  22. uint16_t nplane; /* number of bit planes */
  23. uint16_t bits; /* bits per pixel */
  24. uint32_t len; /* length of data */
  25. uint32_t offset; /* file offset to data */
  26. Image *img;
  27. Image *mask;
  28. Rectangle r; /* relative */
  29. Rectangle sr; /* abs */
  30. };
  31. typedef struct Header Header;
  32. struct Header
  33. {
  34. uint n;
  35. Icon *first;
  36. Icon *last;
  37. };
  38. int debug;
  39. Mouse mouse;
  40. Header h;
  41. Image *background;
  42. uint16_t
  43. gets(uint8_t *p)
  44. {
  45. return p[0] | (p[1]<<8);
  46. }
  47. uint32_t
  48. getl(uint8_t *p)
  49. {
  50. return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
  51. }
  52. int
  53. Bgetheader(Biobuf *b, Header *h)
  54. {
  55. Icon *icon;
  56. int i;
  57. uint8_t buf[40];
  58. memset(h, 0, sizeof(*h));
  59. if(Bread(b, buf, 6) != 6)
  60. goto eof;
  61. if(gets(&buf[0]) != 0)
  62. goto header;
  63. if(gets(&buf[2]) != 1)
  64. goto header;
  65. h->n = gets(&buf[4]);
  66. for(i = 0; i < h->n; i++){
  67. icon = mallocz(sizeof(*icon), 1);
  68. if(icon == nil)
  69. sysfatal("malloc: %r");
  70. if(Bread(b, buf, 16) != 16)
  71. goto eof;
  72. icon->w = buf[0];
  73. icon->h = buf[1];
  74. icon->ncolor = buf[2] == 0 ? 256 : buf[2];
  75. if(buf[3] != 0)
  76. goto header;
  77. icon->nplane = gets(&buf[4]);
  78. icon->bits = gets(&buf[6]);
  79. icon->len = getl(&buf[8]);
  80. icon->offset = getl(&buf[12]);
  81. if(i == 0)
  82. h->first = icon;
  83. else
  84. h->last->next = icon;
  85. h->last = icon;
  86. }
  87. return 0;
  88. eof:
  89. werrstr("unexpected EOF");
  90. return -1;
  91. header:
  92. werrstr("unknown header format");
  93. return -1;
  94. }
  95. uint8_t*
  96. transcmap(Icon *icon, uint8_t *map)
  97. {
  98. uint8_t *m, *p;
  99. int i;
  100. p = m = malloc(sizeof(int)*(1<<icon->bits));
  101. for(i = 0; i < icon->ncolor; i++){
  102. *p++ = rgb2cmap(map[2], map[1], map[0]);
  103. map += 4;
  104. }
  105. return m;
  106. }
  107. Image*
  108. xor2img(Icon *icon, uint8_t *xor, uint8_t *map)
  109. {
  110. uint8_t *data;
  111. Image *img;
  112. int inxlen;
  113. uint8_t *from, *to;
  114. int s, byte, mask;
  115. int x, y;
  116. inxlen = 4*((icon->bits*icon->w+31)/32);
  117. to = data = malloc(icon->w*icon->h);
  118. /* rotate around the y axis, go to 8 bits, and convert color */
  119. mask = (1<<icon->bits)-1;
  120. for(y = 0; y < icon->h; y++){
  121. s = -1;
  122. byte = 0;
  123. from = xor + (icon->h - 1 - y)*inxlen;
  124. for(x = 0; x < icon->w; x++){
  125. if(s < 0){
  126. byte = *from++;
  127. s = 8-icon->bits;
  128. }
  129. *to++ = map[(byte>>s) & mask];
  130. s -= icon->bits;
  131. }
  132. }
  133. /* stick in an image */
  134. img = allocimage(display, Rect(0,0,icon->w,icon->h), CMAP8, 0, DNofill);
  135. loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w);
  136. free(data);
  137. return img;
  138. }
  139. Image*
  140. and2img(Icon *icon, uint8_t *and)
  141. {
  142. uint8_t *data;
  143. Image *img;
  144. int inxlen;
  145. int outxlen;
  146. uint8_t *from, *to;
  147. int x, y;
  148. inxlen = 4*((icon->w+31)/32);
  149. to = data = malloc(inxlen*icon->h);
  150. /* rotate around the y axis and invert bits */
  151. outxlen = (icon->w+7)/8;
  152. for(y = 0; y < icon->h; y++){
  153. from = and + (icon->h - 1 - y)*inxlen;
  154. for(x = 0; x < outxlen; x++){
  155. *to++ = ~(*from++);
  156. }
  157. }
  158. /* stick in an image */
  159. img = allocimage(display, Rect(0,0,icon->w,icon->h), GREY1, 0, DNofill);
  160. loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen);
  161. free(data);
  162. return img;
  163. }
  164. int
  165. Bgeticon(Biobuf *b, Icon *icon)
  166. {
  167. uint32_t l;
  168. uint16_t s;
  169. uint8_t *xor;
  170. uint8_t *and;
  171. uint8_t *cm;
  172. uint8_t *buf;
  173. uint8_t *map2map;
  174. Image *img;
  175. Bseek(b, icon->offset, 0);
  176. buf = malloc(icon->len);
  177. if(buf == nil)
  178. return -1;
  179. if(Bread(b, buf, icon->len) != icon->len){
  180. werrstr("unexpected EOF");
  181. return -1;
  182. }
  183. /* this header's info takes precedence over previous one */
  184. if(getl(buf) != 40){
  185. werrstr("bad icon header");
  186. return -1;
  187. }
  188. l = getl(buf+4);
  189. if(l != icon->w)
  190. icon->w = l;
  191. l = getl(buf+8);
  192. if(l>>1 != icon->h)
  193. icon->h = l>>1;
  194. s = gets(buf+12);
  195. if(s != icon->nplane)
  196. icon->nplane = s;
  197. s = gets(buf+14);
  198. if(s != icon->bits)
  199. icon->bits = s;
  200. /* limit what we handle */
  201. switch(icon->bits){
  202. case 1:
  203. case 2:
  204. case 4:
  205. case 8:
  206. break;
  207. default:
  208. werrstr("don't support %d bit pixels", icon->bits);
  209. return -1;
  210. }
  211. if(icon->nplane != 1){
  212. werrstr("don't support %d planes", icon->nplane);
  213. return -1;
  214. }
  215. cm = buf + 40;
  216. xor = cm + 4*icon->ncolor;
  217. and = xor + icon->h*4*((icon->bits*icon->w+31)/32);
  218. /* translate the color map to a plan 9 one */
  219. map2map = transcmap(icon, cm);
  220. /* convert the images */
  221. icon->img = xor2img(icon, xor, map2map);
  222. icon->mask = and2img(icon, and);
  223. /* so that we save an image with a white background */
  224. img = allocimage(display, icon->img->r, CMAP8, 0, DWhite);
  225. draw(img, icon->img->r, icon->img, icon->mask, ZP);
  226. icon->img = img;
  227. free(buf);
  228. free(map2map);
  229. return 0;
  230. }
  231. void
  232. usage(void)
  233. {
  234. fprint(2, "usage: %s [file]\n", argv0);
  235. exits("usage");
  236. }
  237. enum
  238. {
  239. Mimage,
  240. Mmask,
  241. Mexit,
  242. Up= 1,
  243. Down= 0,
  244. };
  245. char *menu3str[] = {
  246. [Mimage] = "write image",
  247. [Mmask] = "write mask",
  248. [Mexit] = "exit",
  249. 0,
  250. };
  251. Menu menu3 = {
  252. menu3str
  253. };
  254. Cursor sight = {
  255. {-7, -7},
  256. {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
  257. 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
  258. 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
  259. 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
  260. {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
  261. 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
  262. 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
  263. 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
  264. };
  265. void
  266. buttons(int ud)
  267. {
  268. while((mouse.buttons==0) != ud)
  269. mouse = emouse();
  270. }
  271. void
  272. mesg(char *fmt, ...)
  273. {
  274. va_list arg;
  275. char buf[1024];
  276. static char obuf[1024];
  277. va_start(arg, fmt);
  278. vseprint(buf, buf+sizeof(buf), fmt, arg);
  279. va_end(arg);
  280. string(screen, screen->r.min, background, ZP, font, obuf);
  281. string(screen, screen->r.min, display->white, ZP, font, buf);
  282. strcpy(obuf, buf);
  283. }
  284. void
  285. doimage(Icon *icon)
  286. {
  287. int rv;
  288. char file[256];
  289. int fd;
  290. rv = -1;
  291. snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h);
  292. fd = create(file, OWRITE, 0664);
  293. if(fd >= 0){
  294. rv = writeimage(fd, icon->img, 0);
  295. close(fd);
  296. }
  297. if(rv < 0)
  298. mesg("error writing %s: %r", file);
  299. else
  300. mesg("created %s", file);
  301. }
  302. void
  303. domask(Icon *icon)
  304. {
  305. int rv;
  306. char file[64];
  307. int fd;
  308. rv = -1;
  309. snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h);
  310. fd = create(file, OWRITE, 0664);
  311. if(fd >= 0){
  312. rv = writeimage(fd, icon->mask, 0);
  313. close(fd);
  314. }
  315. if(rv < 0)
  316. mesg("error writing %s: %r", file);
  317. else
  318. mesg("created %s", file);
  319. }
  320. void
  321. apply(void (*f)(Icon*))
  322. {
  323. Icon *icon;
  324. esetcursor(&sight);
  325. buttons(Down);
  326. if(mouse.buttons == 4)
  327. for(icon = h.first; icon; icon = icon->next)
  328. if(ptinrect(mouse.xy, icon->sr)){
  329. buttons(Up);
  330. f(icon);
  331. break;
  332. }
  333. buttons(Up);
  334. esetcursor(0);
  335. }
  336. void
  337. menu(void)
  338. {
  339. int sel;
  340. sel = emenuhit(3, &mouse, &menu3);
  341. switch(sel){
  342. case Mimage:
  343. apply(doimage);
  344. break;
  345. case Mmask:
  346. apply(domask);
  347. break;
  348. case Mexit:
  349. exits(0);
  350. break;
  351. }
  352. }
  353. void
  354. mousemoved(void)
  355. {
  356. Icon *icon;
  357. for(icon = h.first; icon; icon = icon->next)
  358. if(ptinrect(mouse.xy, icon->sr)){
  359. mesg("%dx%d", icon->w, icon->h);
  360. return;
  361. }
  362. mesg("");
  363. }
  364. enum
  365. {
  366. BORDER= 1,
  367. };
  368. void
  369. eresized(int new)
  370. {
  371. Icon *icon;
  372. Rectangle r;
  373. if(new && getwindow(display, Refnone) < 0)
  374. sysfatal("can't reattach to window");
  375. draw(screen, screen->clipr, background, nil, ZP);
  376. r.max.x = screen->r.min.x;
  377. r.min.y = screen->r.min.y + font->height + 2*BORDER;
  378. for(icon = h.first; icon != nil; icon = icon->next){
  379. r.min.x = r.max.x + BORDER;
  380. r.max.x = r.min.x + Dx(icon->img->r);
  381. r.max.y = r.min.y + Dy(icon->img->r);
  382. draw(screen, r, icon->img, nil, ZP);
  383. border(screen, r, -BORDER, display->black, ZP);
  384. icon->sr = r;
  385. }
  386. flushimage(display, 1);
  387. }
  388. void
  389. main(int argc, char **argv)
  390. {
  391. Biobuf in;
  392. Icon *icon;
  393. int num, fd;
  394. Rectangle r;
  395. Event e;
  396. ARGBEGIN{
  397. case 'd':
  398. debug = 1;
  399. break;
  400. }ARGEND;
  401. fd = -1;
  402. switch(argc){
  403. case 0:
  404. fd = 0;
  405. break;
  406. case 1:
  407. fd = open(argv[0], OREAD);
  408. if(fd < 0)
  409. sysfatal("opening: %r");
  410. break;
  411. default:
  412. usage();
  413. break;
  414. }
  415. Binit(&in, fd, OREAD);
  416. if(Bgetheader(&in, &h) < 0)
  417. sysfatal("reading header: %r");
  418. initdraw(nil, nil, "ico");
  419. background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (128<<24)|(128<<16)|(128<<8)|0xFF);
  420. einit(Emouse|Ekeyboard);
  421. num = 0;
  422. r.min = Pt(4, 4);
  423. for(icon = h.first; icon != nil; icon = icon->next){
  424. if(Bgeticon(&in, icon) < 0){
  425. fprint(2, "%s: read fail: %r\n", argv0);
  426. continue;
  427. }
  428. if(debug)
  429. fprint(2, "w %u h %u ncolor %u bits %u len %lu offset %lu\n",
  430. icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
  431. r.max = addpt(r.min, Pt(icon->w, icon->h));
  432. icon->r = r;
  433. r.min.x += r.max.x;
  434. num++;
  435. }
  436. if(num == 0)
  437. exits("no images");
  438. eresized(0);
  439. for(;;)
  440. switch(event(&e)){
  441. case Ekeyboard:
  442. break;
  443. case Emouse:
  444. mouse = e.mouse;
  445. if(mouse.buttons & 4)
  446. menu();
  447. else
  448. mousemoved();
  449. break;
  450. }
  451. /* not reached */
  452. }