]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/ico.c
sshfs: usage
[plan9front.git] / sys / src / cmd / jpg / ico.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <memdraw.h>
6 #include <event.h>
7 #include <cursor.h>
8
9 #include "imagefile.h"
10
11 typedef struct Icon Icon;
12 struct Icon
13 {
14         Icon    *next;
15
16         uchar   w;              /* icon width */
17         uchar   h;              /* icon height */
18         ushort  ncolor;         /* number of colors */
19         ushort  nplane;         /* number of bit planes */
20         ushort  bits;           /* bits per pixel */
21         ulong   len;            /* length of data */
22         ulong   offset;         /* file offset to data */
23
24         Memimage        *img;
25         Memimage        *mask;
26
27         Rectangle r;            /* relative */
28         Rectangle sr;           /* abs */
29 };
30
31 typedef struct Header Header;
32 struct Header
33 {
34         uint    n;
35         Icon    *first;
36         Icon    *last;
37 };
38
39 int debug;
40 int cflag;
41 Mouse mouse;
42 Header h;
43 Image *background;
44
45 ushort
46 gets(uchar *p)
47 {
48         return p[0] | (p[1]<<8);
49 }
50
51 ulong
52 getl(uchar *p)
53 {
54         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
55 }
56
57 int
58 Bgetheader(Biobuf *b, Header *h)
59 {
60         uchar buf[40];
61         Icon *icon;
62         int i;
63
64         memset(h, 0, sizeof(*h));
65         if(Bread(b, buf, 6) != 6)
66                 goto eof;
67         if(gets(&buf[0]) != 0)
68                 goto header;
69         if(gets(&buf[2]) != 1)
70                 goto header;
71         h->n = gets(&buf[4]);
72         for(i = 0; i < h->n; i++){
73                 icon = mallocz(sizeof(*icon), 1);
74                 if(icon == nil)
75                         sysfatal("malloc: %r");
76                 if(Bread(b, buf, 16) != 16)
77                         goto eof;
78                 icon->w = buf[0] == 0 ? 256 : buf[0];
79                 icon->h = buf[1] == 0 ? 256 : buf[1];
80                 icon->ncolor = buf[2] == 0 ? 256 : buf[2];
81                 icon->nplane = gets(&buf[4]);
82                 icon->bits = gets(&buf[6]);
83                 icon->len = getl(&buf[8]);
84                 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;
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, int ncolor, uchar *map)
103 {
104         uchar *m, *p;
105         int i;
106
107         p = m = mallocz(sizeof(int)*(1<<icon->bits), 1);
108         if(m == nil)
109                 sysfatal("malloc: %r");
110         for(i = 0; i < ncolor; i++){
111                 *p++ = rgb2cmap(map[2], map[1], map[0]);
112                 map += 4;
113         }
114         return m;
115 }
116
117 Memimage*
118 xor2img(Icon *icon, long chan, uchar *xor, uchar *map)
119 {
120         uchar *data;
121         Memimage *img;
122         int inxlen;
123         uchar *from, *to;
124         int s, byte, mask;
125         int x, y;
126
127         inxlen = 4*((icon->bits*icon->w+31)/32);
128         img = allocmemimage(Rect(0,0,icon->w,icon->h), chan);
129         if(img == nil)
130                 return nil;
131
132         if(chan != CMAP8){
133                 from = xor + icon->h*inxlen;
134                 for(y = 0; y < icon->h; y++){
135                         from -= inxlen;
136                         loadmemimage(img, Rect(0,y,icon->w,y+1), from, inxlen);
137                 }
138                 return img;
139         }
140
141         to = data = malloc(icon->w*icon->h);
142         if(data == nil){
143                 freememimage(img);
144                 return nil;
145         }
146
147         /* rotate around the y axis, go to 8 bits, and convert color */
148         mask = (1<<icon->bits)-1;
149         for(y = 0; y < icon->h; y++){
150                 s = -1;
151                 byte = 0;
152                 from = xor + (icon->h - 1 - y)*inxlen;
153                 for(x = 0; x < icon->w; x++){
154                         if(s < 0){
155                                 byte = *from++;
156                                 s = 8-icon->bits;
157                         }
158                         *to++ = map[(byte>>s) & mask];
159                         s -= icon->bits;
160                 }
161         }
162         /* stick in an image */
163         loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w);
164         free(data);
165         return img;
166 }
167
168 Memimage*
169 and2img(Icon *icon, uchar *and)
170 {
171         uchar *data;
172         Memimage *img;
173         int inxlen;
174         int outxlen;
175         uchar *from, *to;
176         int x, y;
177
178         inxlen = 4*((icon->w+31)/32);
179         to = data = malloc(inxlen*icon->h);
180         if(data == nil)
181                 return nil;
182
183         /* rotate around the y axis and invert bits */
184         outxlen = (icon->w+7)/8;
185         for(y = 0; y < icon->h; y++){
186                 from = and + (icon->h - 1 - y)*inxlen;
187                 for(x = 0; x < outxlen; x++)
188                         *to++ = ~(*from++);
189         }
190
191         /* stick in an image */
192         if(img = allocmemimage(Rect(0,0,icon->w,icon->h), GREY1))
193                 loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen);
194
195         free(data);
196         return img;
197 }
198
199 int
200 Bgeticon(Biobuf *b, Icon *icon)
201 {
202         uchar *end;
203         uchar *xor;
204         uchar *and;
205         uchar *cm;
206         uchar *buf;
207         uchar *map2map;
208         Memimage *img;
209         uchar magic[4];
210         int ncolor;
211         long chan;
212
213         Bseek(b, icon->offset, 0);
214         if(Bread(b, magic, 4) != 4){
215                 werrstr("unexpected EOF");
216                 return -1;
217         }
218         if(magic[0] == 137 && memcmp(magic+1, "PNG", 3) == 0){
219                 Rawimage **png;
220
221                 Bseek(b, -4, 1);
222                 png = Breadpng(b, CRGB);
223                 if(png == nil || png[0] == nil)
224                         return -1;
225                 switch(png[0]->chandesc){
226                 case CY:
227                         chan = GREY8;
228                         break;
229                 case CYA16:
230                         chan = CHAN2(CGrey, 8, CAlpha, 8);
231                         break;
232                 case CRGB24:
233                         chan = RGB24;
234                         break;
235                 case CRGBA32:
236                         chan = RGBA32;
237                         break;
238                 default:
239                         werrstr("bad icon png channel descriptor");
240                         return -1;
241                 }
242                 icon->mask = nil;
243                 icon->img = allocmemimage(png[0]->r, chan);
244                 loadmemimage(icon->img, icon->img->r, png[0]->chans[0], png[0]->chanlen);
245                 return 0;
246         }
247
248         if(getl(magic) != 40){
249                 werrstr("bad icon bmp header");
250                 return -1;
251         }
252         if(icon->len < 40){
253                 werrstr("bad icon bmp header length");
254                 return -1;
255         }
256         buf = malloc(icon->len);
257         if(buf == nil)
258                 return -1;
259         memmove(buf, magic, 4);
260         if(Bread(b, buf+4, icon->len-4) != icon->len-4){
261                 werrstr("unexpected EOF");
262                 return -1;
263         }
264
265         /* this header's info takes precedence over previous one */
266         ncolor = 0;
267         icon->w = getl(buf+4);
268         icon->h = getl(buf+8)>>1;
269         icon->nplane = gets(buf+12);
270         icon->bits = gets(buf+14);
271
272         /* limit what we handle */
273         switch(icon->bits){
274         case 1:
275         case 2:
276         case 4:
277         case 8:
278                 ncolor = icon->ncolor;
279                 if(ncolor > (1<<icon->bits))
280                         ncolor = 1<<icon->bits;
281                 chan = CMAP8;
282                 break;
283         case 15:
284         case 16:
285                 chan = RGB16;
286                 break;
287         case 24:
288                 chan = RGB24;
289                 break;
290         case 32:
291                 chan = ARGB32;
292                 break;
293         default:
294                 werrstr("don't support %d bit pixels", icon->bits);
295                 return -1;
296         }
297         if(icon->nplane != 1){
298                 werrstr("don't support %d planes", icon->nplane);
299                 return -1;
300         }
301
302         xor = cm = buf + 40;
303         if(chan == CMAP8)
304                 xor += 4*ncolor;
305         end = xor + icon->h*4*((icon->bits*icon->w+31)/32);
306         if(end < buf || end > buf+icon->len){
307                 werrstr("bad icon length %zux != %lux", end - buf, icon->len);
308                 return -1;
309         }
310
311         /* translate the color map to a plan 9 one */
312         map2map = nil;
313         if(chan == CMAP8)
314                 map2map = transcmap(icon, ncolor, cm);
315
316         /* convert the images */
317         icon->img = xor2img(icon, chan, xor, map2map);
318         if(icon->img == nil){
319                 werrstr("xor2img: %r");
320                 return -1;
321         }
322         icon->mask = nil;
323
324         /* check for and mask */
325         and = end;
326         end += icon->h*4*((icon->w+31)/32);
327         if(end <= buf+icon->len)
328                 icon->mask = and2img(icon, and);
329
330         /* so that we save an image with a white background */
331         if(img = allocmemimage(icon->img->r, icon->img->chan)){
332                 memfillcolor(img, DWhite);
333                 memimagedraw(img, icon->img->r, icon->img, ZP, icon->mask, ZP, SoverD);
334                 freememimage(icon->img);
335                 icon->img = img;
336         }
337
338         free(buf);
339         free(map2map);
340         return 0;
341 }
342
343 void
344 usage(void)
345 {
346         fprint(2, "usage: %s [ -c ] [ file ]\n", argv0);
347         exits("usage");
348 }
349
350 enum
351 {
352         Mimage,
353         Mmask,
354         Mexit,
355
356         Up= 1,
357         Down= 0,
358 };
359
360 char    *menu3str[] = {
361         [Mimage]        "write image",
362         [Mmask]         "write mask",
363         [Mexit]         "exit",
364         0,
365 };
366
367 Menu    menu3 = {
368         menu3str
369 };
370
371 Cursor sight = {
372         {-7, -7},
373         {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
374          0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
375          0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
376          0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
377         {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
378          0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
379          0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
380          0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
381 };
382
383 void
384 buttons(int ud)
385 {
386         while((mouse.buttons==0) != ud)
387                 mouse = emouse();
388 }
389
390 void
391 mesg(char *fmt, ...)
392 {
393         va_list arg;
394         char buf[1024];
395         static char obuf[1024];
396
397         va_start(arg, fmt);
398         vseprint(buf, buf+sizeof(buf), fmt, arg);
399         va_end(arg);
400         string(screen, screen->r.min, background, ZP, font, obuf);
401         string(screen, screen->r.min, display->white, ZP, font, buf);
402         strcpy(obuf, buf);
403 }
404
405 void
406 doimage(Icon *icon)
407 {
408         int rv;
409         char file[256];
410         int fd;
411
412         rv = -1;
413         snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h);
414         fd = create(file, OWRITE, 0664);
415         if(fd >= 0){
416                 rv = writememimage(fd, icon->img);
417                 close(fd);
418         }
419         if(rv < 0)
420                 mesg("error writing %s: %r", file);
421         else
422                 mesg("created %s", file);
423 }
424
425 void
426 domask(Icon *icon)
427 {
428         int rv;
429         char file[64];
430         int fd;
431
432         if(icon->mask == nil)
433                 return;
434
435         rv = -1;
436         snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h);
437         fd = create(file, OWRITE, 0664);
438         if(fd >= 0){
439                 rv = writememimage(fd, icon->mask);
440                 close(fd);
441         }
442         if(rv < 0)
443                 mesg("error writing %s: %r", file);
444         else
445                 mesg("created %s", file);
446 }
447
448 void
449 apply(void (*f)(Icon*))
450 {
451         Icon *icon;
452
453         esetcursor(&sight);
454         buttons(Down);
455         if(mouse.buttons == 4)
456                 for(icon = h.first; icon; icon = icon->next)
457                         if(ptinrect(mouse.xy, icon->sr)){
458                                 buttons(Up);
459                                 f(icon);
460                                 break;
461                         }
462         buttons(Up);
463         esetcursor(0);
464 }
465
466 void
467 menu(void)
468 {
469         int sel;
470
471         sel = emenuhit(3, &mouse, &menu3);
472         switch(sel){
473         case Mimage:
474                 apply(doimage);
475                 break;
476         case Mmask:
477                 apply(domask);
478                 break;
479         case Mexit:
480                 exits(0);
481                 break;
482         }
483 }
484
485 void
486 mousemoved(void)
487 {
488         Icon *icon;
489
490         for(icon = h.first; icon; icon = icon->next)
491                 if(ptinrect(mouse.xy, icon->sr)){
492                         mesg("%dx%d", icon->w, icon->h);
493                         return;
494                 }
495         mesg("");
496 }
497
498 enum
499 {
500         BORDER= 1,
501 };
502
503 Image*
504 screenimage(Memimage *m)
505 {
506         Rectangle r;
507         Image *i;
508
509         if(i = allocimage(display, m->r, m->chan, 0, DNofill)){
510                 r = m->r;
511                 while(r.min.y < m->r.max.y){
512                         r.max.y = r.min.y+1;
513                         loadimage(i, r, byteaddr(m, r.min), bytesperline(r, m->depth));
514                         r.min.y++;
515                 }
516         }
517         return i;
518 }
519
520 void
521 eresized(int new)
522 {
523         Icon *icon;
524         Image *i;
525         Rectangle r;
526
527         if(new && getwindow(display, Refnone) < 0)
528                 sysfatal("can't reattach to window");
529         draw(screen, screen->clipr, background, nil, ZP);
530         r.max.x = screen->r.min.x;
531         r.min.y = screen->r.min.y + font->height + 2*BORDER;
532         for(icon = h.first; icon != nil; icon = icon->next){
533                 r.min.x = r.max.x + BORDER;
534                 r.max.x = r.min.x + Dx(icon->img->r);
535                 r.max.y = r.min.y + Dy(icon->img->r);
536                 if(i = screenimage(icon->img)){
537                         draw(screen, r, i, nil, ZP);
538                         freeimage(i);
539                 }
540                 border(screen, r, -BORDER, display->black, ZP);
541                 icon->sr = r;
542         }
543         flushimage(display, 1);
544 }
545
546 void
547 main(int argc, char **argv)
548 {
549         Biobuf in;
550         Icon *icon;
551         int num, fd;
552         Rectangle r;
553         Event e;
554
555         ARGBEGIN{
556         case 'd':
557                 debug = 1;
558                 break;
559         case 'c':
560                 cflag = 1;
561                 break;
562         default:
563                 usage();
564         }ARGEND;
565
566         fd = -1;
567         switch(argc){
568         case 0:
569                 fd = 0;
570                 break;
571         case 1:
572                 fd = open(argv[0], OREAD);
573                 if(fd < 0)
574                         sysfatal("opening: %r");
575                 break;
576         default:
577                 usage();
578                 break;
579         }
580
581         memimageinit();
582         Binit(&in, fd, OREAD);
583
584         if(Bgetheader(&in, &h) < 0)
585                 sysfatal("reading header: %r");
586
587         num = 0;
588         r.min = Pt(4, 4);
589         for(icon = h.first; icon != nil; icon = icon->next){
590                 if(Bgeticon(&in, icon) < 0){
591                         fprint(2, "%s: read fail: %r\n", argv0);
592                         continue;
593                 }
594                 if(debug)
595                         fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n",
596                            icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
597                 r.max = addpt(r.min, Pt(icon->w, icon->h));
598                 icon->r = r;
599                 if(cflag){
600                         writememimage(1, icon->img);
601                         exits(0);
602                 }
603                 r.min.x += r.max.x;
604                 num++;
605         }
606
607         if(num == 0 || cflag)
608                 sysfatal("no images");
609
610         initdraw(nil, nil, "ico");
611         background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x808080FF);
612         eresized(0);
613         einit(Emouse|Ekeyboard);
614         for(;;)
615                 switch(event(&e)){
616                 case Ekeyboard:
617                         break;
618                 case Emouse:
619                         mouse = e.mouse;
620                         if(mouse.buttons & 4)
621                                 menu();
622                         else
623                                 mousemoved();
624                         break;
625                 }
626         /* not reached */
627 }