]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/ico.c
cwfs: back to previous version
[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 <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 [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         [Mimage]        "write image",
277         [Mmask]         "write mask",
278         [Mexit]         "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 num, fd;
443         Rectangle r;
444         Event e;
445
446         ARGBEGIN{
447         case 'd':
448                 debug = 1;
449                 break;
450         }ARGEND;
451
452         fd = -1;
453         switch(argc){
454         case 0:
455                 fd = 0;
456                 break;
457         case 1:
458                 fd = open(argv[0], OREAD);
459                 if(fd < 0)
460                         sysfatal("opening: %r");
461                 break;
462         default:
463                 usage();
464                 break;
465         }
466
467         Binit(&in, fd, OREAD);
468
469         if(Bgetheader(&in, &h) < 0)
470                 sysfatal("reading header: %r");
471
472         initdraw(nil, nil, "ico");
473         background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (128<<24)|(128<<16)|(128<<8)|0xFF);
474
475         einit(Emouse|Ekeyboard);
476
477         num = 0;
478         r.min = Pt(4, 4);
479         for(icon = h.first; icon != nil; icon = icon->next){
480                 if(Bgeticon(&in, icon) < 0){
481                         fprint(2, "%s: read fail: %r\n", argv0);
482                         continue;
483                 }
484                 if(debug)
485                         fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n",
486                            icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
487                 r.max = addpt(r.min, Pt(icon->w, icon->h));
488                 icon->r = r;
489                 r.min.x += r.max.x;
490                 num++;
491         }
492
493         if(num == 0)
494                 exits("no images");
495         eresized(0);
496
497         for(;;)
498                 switch(event(&e)){
499                 case Ekeyboard:
500                         break;
501                 case Emouse:
502                         mouse = e.mouse;
503                         if(mouse.buttons & 4)
504                                 menu();
505                         else
506                                 mousemoved();
507                         break;
508                 }
509         /* not reached */
510 }