]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/mug.c
togif: -E flag to read animation from stdin
[plan9front.git] / sys / src / cmd / mug.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 #include <cursor.h>
6
7 #define initstate muginitstate
8
9 typedef struct State State;
10 struct State {
11         double black;
12         double white;
13         double stretch;
14         double gamma;
15         int depth;
16         int gtab[1001];
17         Rectangle selr;
18 };
19
20 typedef struct Face Face;
21 struct Face {
22         Rectangle r;
23         State state;
24         Image *small;
25 };
26
27 double GAMMA = 1.0;             /* theory tells me this should be 2.2, but 1.0 sure looks better */
28 enum {
29         Left=0,
30         Right,
31         Top,
32         Bottom,
33
34         RTopLeft=0,
35         RTop,
36         RTopRight,
37         RLeft,
38         RMiddle,
39         RRight,
40         RBotLeft,
41         RBot,
42         RBotRight,
43 };
44
45 void*
46 emalloc(ulong sz)
47 {
48         void *v;
49
50         v = malloc(sz);
51         if(v == nil)
52                 sysfatal("malloc %lud fails", sz);
53         memset(v, 0, sz);
54         return v;
55 }
56
57 Face *face[8];
58 int nface;
59 uchar grey2cmap[256];
60 Image *bkgd;
61 Image *orig;
62 Image *ramp, *small, *osmall, *tmp8, *red, *green, *blue;
63 State state, ostate;
64 uchar val2cmap[256];
65 uchar clamp[3*256];
66 Rectangle rbig, rramp, rface[nelem(face)], rsmall;
67 double *rdata;
68 int sdy, sdx;
69
70 void
71 geometry(Rectangle r)
72 {
73         int i;
74         Rectangle fr[9];
75
76         rramp.min = addpt(r.min, Pt(4,4));
77         rramp.max = addpt(rramp.min, Pt(256,256));
78
79         rbig.min = Pt(rramp.max.x+6, rramp.min.y);
80         rbig.max = addpt(rbig.min, Pt(Dx(orig->r), Dy(orig->r)));
81
82         for(i=0; i<9; i++)
83                 fr[i] = rectaddpt(Rect(0,0,48,48), Pt(rramp.min.x+48+56*(i%3), rramp.max.y+6+56*(i/3)));
84
85         rsmall = fr[4];
86         for(i=0; i<4; i++)
87                 rface[i] = fr[i];
88         for(i=4; i<8; i++)
89                 rface[i] = fr[i+1];
90 }
91
92 double
93 y2gamma(int y)
94 {
95         double g;
96
97         g = (double)y / 128.0;
98         return 0.5+g*g;         /* gamma from 0.5 to 4.5, with 1.0 near the middle */
99 }
100
101 int
102 gamma2y(double g)
103 {
104         g -= 0.5;
105         return (int)(128.0*sqrt(g)+0.5);
106 }
107
108 void
109 drawface(int i)
110 {
111         if(i==-1){
112                 border(screen, rsmall, -3, blue, ZP);
113                 draw(screen, rsmall, small, nil, ZP);
114                 return;
115         }
116         border(screen, rface[i], -1, display->black, ZP);
117         if(face[i])
118                 draw(screen, rface[i], face[i]->small, nil, ZP);
119         else
120                 draw(screen, rface[i], display->white, nil, ZP);
121 }
122
123 void
124 drawrampbar(Image *color, State *s)
125 {
126         Rectangle liner, r;
127         static Rectangle br;
128
129         if(Dx(br))
130                 draw(screen, br, ramp, nil, subpt(br.min, rramp.min));
131
132         r = rramp;
133         r.max.x = r.min.x + (int)(s->white*255.0);
134         r.min.x += (int)(s->black*255.0);
135         r.min.y += gamma2y(s->gamma);
136         r.max.y = r.min.y+1; 
137         rectclip(&r, rramp);
138         draw(screen, r, color, nil, ZP);
139         br = r;
140
141         r.min.y -= 2;
142         r.max.y += 2;
143         
144         liner = r;
145         r.min.x += Dx(liner)/3;
146         r.max.x -= Dx(liner)/3;
147         rectclip(&r, rramp);
148         draw(screen, r, color, nil, ZP);
149         combinerect(&br, r);
150
151         r = liner;
152         r.max.x = r.min.x+3;
153         rectclip(&r, rramp);
154         draw(screen, r, color, nil, ZP);
155         combinerect(&br, r);
156
157         r = liner;
158         r.min.x = r.max.x-3;
159         rectclip(&r, rramp);
160         draw(screen, r, color, nil, ZP);
161         combinerect(&br, r);
162 }
163
164 void
165 drawscreen(int clear)
166 {
167         int i;
168
169         if(clear){
170                 geometry(screen->r);
171                 draw(screen, screen->r, bkgd, nil, ZP);
172         }
173
174         border(screen, rbig, -1, display->black, ZP);
175         draw(screen, rbig, orig, nil, orig->r.min);
176
177         border(screen, rramp, -1, display->black, ZP);
178         draw(screen, rramp, ramp, nil, ramp->r.min);
179         drawrampbar(red, &state);
180
181         border(screen, rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), -2, red, ZP);
182         if(clear){
183                 drawface(-1);
184                 for(i=0; i<nelem(face); i++)
185                         drawface(i);
186         }
187 }
188
189 void
190 moveframe(Rectangle old, Rectangle new)
191 {
192         border(screen, rectaddpt(old, subpt(rbig.min, orig->r.min)), -2, orig, old.min);
193         border(screen, rectaddpt(new, subpt(rbig.min, orig->r.min)), -2, red, ZP);
194 }
195
196
197 /*
198  * Initialize gamma ramp; should dither for
199  * benefit of non-true-color displays.
200  */
201 void
202 initramp(void)
203 {
204         int k, x, y;
205         uchar dat[256*256];
206         double g;
207
208         k = 0;
209         for(y=0; y<256; y++) {
210                 g = y2gamma(y);
211                 for(x=0; x<256; x++)
212                         dat[k++] = 255.0 * pow(x/255.0, g);
213         }
214         assert(k == sizeof dat);
215
216         ramp = allocimage(display, Rect(0,0,256,256), GREY8, 0, DNofill);
217         if(ramp == nil)
218                 sysfatal("allocimage: %r");
219
220         if(loadimage(ramp, ramp->r, dat, sizeof dat) != sizeof dat)
221                 sysfatal("loadimage: %r");
222 }
223
224 void
225 initclamp(void)
226 {
227         int i;
228
229         for(i=0; i<256; i++) {
230                 clamp[i] = 0;
231                 clamp[256+i] = i;
232                 clamp[512+i] = 255;
233         }
234 }
235
236 void
237 changestretch(double stretch)
238 {
239         state.stretch = stretch;
240 }
241
242 /*
243  * There is greyscale data for the rectangle datar in data;
244  * extract square r and write it into the 48x48 pixel image small.
245  */
246 void
247 process(double *data, Rectangle datar, Rectangle r, Image *small)
248 {
249         double black, center, delta, *k, shrink, sum, *tmp[48], *tt, w, white, x;
250         int datadx, dp, dx, dy, error, i, ii, j, jj;
251         int ksize, ksizeby2, sdata[48*48], sd, sh, sm, sv, u, uu, uuu, v, vv;
252         uchar bdata[48*48];
253
254         datadx = Dx(datar);
255         dx = Dx(r);
256         dy = Dy(r);
257         shrink = dx/48.0;
258
259         ksize = 1+2*(int)(shrink/2.0);
260         if(ksize <= 2)
261                 return;
262
263         k = emalloc(ksize*sizeof(k[0]));
264
265         /* center of box */
266         for(i=1; i<ksize-1; i++)
267                 k[i] = 1.0;
268
269         /* edges */
270         x = shrink - floor(shrink);
271         k[0] = x;
272         k[ksize-1] = x;
273
274         sum = 0.0;
275         for(i=0; i<ksize; i++)
276                 sum += k[i];
277
278         for(i=0; i<ksize; i++)
279                 k[i] /= sum;
280
281         ksizeby2 = ksize/2;
282
283         for(i=0; i<48; i++)
284                 tmp[i] = emalloc(datadx*sizeof(tmp[i][0]));
285
286         /* squeeze vertically */
287         for(i=0; i<48; i++) {
288                 ii = r.min.y+i*dy/48;
289                 tt = tmp[i];
290                 uu = ii - ksizeby2;
291                 for(j=r.min.x-ksize; j<r.max.x+ksize; j++) {
292                         if(j<datar.min.x || j>=datar.max.x)
293                                 continue;
294                         w = 0.0;
295
296                         uuu = uu*datadx+j;
297                         if(uu>=datar.min.y && uu+ksize<datar.max.y)
298                                 for(u=0; u<ksize; u++){
299                                         w += k[u]*data[uuu];
300                                         uuu += datadx;
301                                 }
302                         else
303                                 for(u=0; u<ksize; u++){
304                                         if(uu+u>=datar.min.y && uu+u<datar.max.y)
305                                                 w += k[u]*data[uuu];
306                                         uuu+=datadx;
307                                 }
308                         tt[j-datar.min.x] = w;
309                 }
310         }
311
312         /* stretch value scale */
313         center = (state.black+state.white)/2;
314         delta = state.stretch*(state.white-state.black)/2;
315         black = center - delta;
316         white = center + delta;
317
318         /* squeeze horizontally */
319         for(i=0; i<48; i++) {
320                 tt = tmp[i];
321                 for(j=0; j<48; j++) {
322                         jj = r.min.x+j*dx/48;
323                         w = 0.0;
324                         for(v=0; v<ksize; v++) {
325                                 vv = jj - ksizeby2 + v;
326                                 if(vv<datar.min.x || vv>=datar.max.x) {
327                                         w += k[v];              /* assume white surround */
328                                         continue;
329                                 }
330                                 w += k[v]*tt[vv-datar.min.x];
331                         }
332                         if(w < black || black==white)
333                                 w = 0.0;
334                         else if(w > white)
335                                 w = 1.0;
336                         else
337                                 w = (w-black)/(white-black);
338                         sdata[i*48+j] = state.gtab[(int)(1000.0*w)];
339                 }
340         }
341
342         /* dither to lower depth before copying into GREY8 version */
343         if(small->chan != GREY8) {
344                 u = 0;
345                 dp = small->depth;
346                 for(i=0; i<48; i++) {
347                         sm = 0xFF ^ (0xFF>>dp);
348                         sh = 0;
349                         v = 0;
350                         for(j=0; j<48; j++) {
351                                 ii = 48*i+j;
352                                 sd = clamp[sdata[ii]+256];
353                                 sv = sd&sm;
354                                 v |= sv>>sh;
355                                 sh += dp;
356                                 if(sh == 8) {
357                                         bdata[u++] = v;
358                                         v = 0;
359                                         sh = 0;
360                                 }
361
362                                 /* propagate error, with decay (sum errors < 1) */
363                                 error = sd - sv;
364                                 if(ii+49 < 48*48) {     /* one test is enough, really */
365                                         sdata[ii+1] = sdata[ii+1]+((3*error)>>4);
366                                         sdata[ii+48] = sdata[ii+48]+((3*error)>>4);
367                                         sdata[ii+49] = sdata[ii+49]+((3*error)>>3);
368                                 }
369
370                                 /* produce correct color map value by copying bits */
371                                 switch(dp){
372                                 case 1:
373                                         sv |= sv>>1;
374                                 case 2:
375                                         sv |= sv>>2;
376                                 case 4:
377                                         sv |= sv>>4;
378                                 }
379                                 sdata[ii] = sv;
380                         }
381                 }
382                 for(i=0; i<nelem(bdata); i++)
383                         bdata[i] = sdata[i];
384                 if(loadimage(tmp8, tmp8->r, bdata, sizeof bdata) != sizeof bdata)
385                         sysfatal("loadimage: %r");
386                 draw(small, small->r, tmp8, nil, tmp8->r.min);
387         } else {
388                 for(i=0; i<nelem(bdata); i++)
389                         bdata[i] = sdata[i];
390                 if(loadimage(small, small->r, bdata, sizeof bdata) != sizeof bdata)
391                         sysfatal("loadimage: %r");
392         }
393
394         free(k);
395         for(i=0; i<48; i++)
396                 free(tmp[i]);
397 }
398
399 void
400 initval2cmap(void)
401 {
402         int i;
403
404         for(i=0; i<256; i++)
405                 val2cmap[i] = rgb2cmap(i, i, i);
406 }
407
408 void
409 setgtab(State *s)
410 {
411         int i;
412
413         for(i=0; i<=1000; i++)
414                 s->gtab[i] = val2cmap[(int)(255.0*pow((i/1000.0), 1.0/s->gamma))];
415 }
416
417 int
418 section(int x)
419 {
420         int ib, iw;
421
422         ib = state.black * 255.0;
423         iw = state.white * 255.0;
424
425         if(x<ib-5 || iw+5<x)
426                 return -1;
427
428         iw -= ib;
429         x -= ib;
430         if(x < iw/3)
431                 return 0;
432         if(x < 2*iw/3)
433                 return 1;
434         return 2;
435 }
436
437 Image*
438 copyimage(Image *i)
439 {
440         Image *n;
441
442         if(i == nil)
443                 return nil;
444
445         n = allocimage(display, i->r, i->chan, 0, DNofill);
446         if(n == nil)
447                 sysfatal("allocimage: %r");
448
449         draw(n, n->r, i, nil, i->r.min);
450         return n;
451 }
452
453 Image*
454 grey8image(Image *i)
455 {
456         Image *n;
457
458         if(i->chan == GREY8)
459                 return i;
460
461         n = allocimage(display, i->r, GREY8, 0, DNofill);
462         if(n == nil)
463                 sysfatal("allocimage: %r");
464
465         draw(n, n->r, i, nil, i->r.min);
466         freeimage(i);
467         return n;
468 }
469
470
471 void
472 mark(void)
473 {
474         if(osmall != small){
475                 freeimage(osmall);
476                 osmall = small;
477         }
478         ostate = state;
479 }
480
481 void
482 undo(void)
483 {
484         if(small != osmall){
485                 freeimage(small);
486                 small = osmall;
487         }
488         state = ostate;
489         process(rdata, orig->r, state.selr, small);
490         drawface(-1);
491         drawscreen(0);
492 }
493
494 void
495 saveface(Face *f, int slot)
496 {
497         if(slot == -1){
498                 mark();
499                 state = f->state;
500                 small = copyimage(f->small);
501                 drawface(-1);
502                 drawscreen(0);
503                 return;
504         }
505
506         if(face[slot]==nil)
507                 face[slot] = emalloc(sizeof(*face[slot]));
508         else{
509                 freeimage(face[slot]->small);
510                 face[slot]->small = nil;
511         }
512
513         if(f == nil){
514                 face[slot]->small = copyimage(small);
515                 face[slot]->state = state;
516         }else{
517                 face[slot]->small = copyimage(f->small);
518                 face[slot]->state = f->state;
519         }
520         drawface(slot);
521 }
522
523 int
524 writeface(char *outfile, Image *image)
525 {
526         int i, fd, rv, y;
527         uchar data[48*48/2];
528
529         if(outfile == nil)
530                 fd = 1;
531         else{
532                 if((fd = create(outfile, OWRITE, 0666)) < 0) 
533                         return -1;
534         }
535
536         switch(image->chan) {
537         default:
538                 rv = -1;
539                 break;
540
541         case GREY1:
542                 if(unloadimage(image, image->r, data, 48*48/8) != 48*48/8)
543                         sysfatal("unloadimage: %r");
544                 for(y=0; y<48; y++) {
545                         for(i=0; i<3; i++)
546                                 fprint(fd, "0x%.2x%.2x,", data[y*6+i*2+0], data[y*6+i*2+1]);
547                         fprint(fd, "\n");
548                 }
549                 rv = 0;
550                 break;
551                 
552         case GREY2:
553                 if(unloadimage(image, image->r, data, 48*48/4) != 48*48/4)
554                         sysfatal("unloadimage: %r");
555                 for(y=0; y<48; y++) {
556                         for(i=0; i<3; i++)
557                                 fprint(fd, "0x%.2x%.2x,%.2x%.2x,",
558                                         data[y*12+i*4+0], data[y*12+i*4+1],
559                                         data[y*12+i*4+2], data[y*12+i*4+3]);
560                         fprint(fd, "\n");
561                 }
562                 rv = 0;
563                 break;
564
565         case GREY4:
566         case GREY8:
567                 rv = writeimage(fd, image, 0);  /* dolock? */
568                 break;
569         }
570
571         if(outfile)
572                 close(fd);
573         return rv;
574 }
575
576 void
577 room(Rectangle out, Rectangle in, int *a)
578 {
579         a[Left] = out.min.x - in.min.x;
580         a[Right] = out.max.x - in.max.x;
581         a[Top] = out.min.y - in.min.y;
582         a[Bottom] = out.max.y - in.max.y;
583 }
584
585 int
586 min(int a, int b)
587 {
588         if(a < b)
589                 return a;
590         return b;
591 }
592
593 int
594 max(int a, int b)
595 {
596         if(a > b)
597                 return a;
598         return b;
599 }
600
601 int
602 move(Rectangle r, Rectangle picr, Point d, int k, Rectangle *rp)
603 {
604         int a[4], i;
605         Rectangle oldr;
606         static int toggle;
607
608         oldr = r;
609         room(picr, r, a);
610         switch(k){
611         case RTopLeft:
612                 i = (d.x+d.y)/2;
613                 if(i>=Dx(r) || i>=Dy(r))
614                         break;
615                 i = max(i, a[Left]);
616                 i = max(i, a[Top]);
617                 r.min.x += i;
618                 r.min.y += i;
619                 break;
620         case RTop:
621                 i = d.y;
622                 if(i < 0){
623                         /*
624                          * should really check i/2, but this is safe and feedback
625                          * makes the control feel right
626                          */
627                         i = -min(-i, a[Right]);
628                         i = max(i, a[Left]);
629                 }
630                 i = max(i, a[Top]);
631                 if(i >= Dy(r))
632                         break;
633                 r.min.y += i;
634                 /* divide the half bit equally */
635                 toggle = 1-toggle;
636                 if(toggle){
637                         r.min.x += i/2;
638                         r.max.x = r.min.x+Dy(r);
639                 }else{
640                         r.max.x -= i/2;
641                         r.min.x = r.max.x-Dy(r);
642                 }
643                 break;
644         case RTopRight:
645                 i = (-d.x+d.y)/2;
646                 if(i>=Dx(r) || i>=Dy(r))
647                         break;
648                 i = -min(-i, a[Right]);
649                 i = max(i, a[Top]);
650                 r.max.x -= i;
651                 r.min.y += i;
652                 break;
653         case RLeft:
654                 i = d.x;
655                 if(i < 0){
656                         i = -min(-i, a[Bottom]);
657                         i = max(i, a[Top]);
658                 }
659                 i = max(i, a[Left]);
660                 if(i >= Dx(r))
661                         break;
662                 r.min.x += i;
663                 /* divide the half bit equally */
664                 toggle = 1-toggle;
665                 if(toggle){
666                         r.min.y += i/2;
667                         r.max.y = r.min.y+Dx(r);
668                 }else{
669                         r.max.y -= i/2;
670                         r.min.y = r.max.y-Dx(r);
671                 }
672                 break;
673         case RMiddle:
674                 if(d.x >= 0)
675                         d.x = min(d.x, a[Right]);
676                 else
677                         d.x = max(d.x, a[Left]);
678                 if(d.y >= 0)
679                         d.y = min(d.y, a[Bottom]);
680                 else
681                         d.y = max(d.y, a[Top]);
682                 r = rectaddpt(r, d);
683                 break;
684         case RRight:
685                 i = d.x;
686                 if(i > 0){
687                         i = min(i, a[Bottom]);
688                         i = -max(-i, a[Top]);
689                 }
690                 i = min(i, a[Right]);
691                 if(-i >= Dx(r))
692                         break;
693                 r.max.x += i;
694                 /* divide the half bit equally */
695                 toggle = 1-toggle;
696                 if(toggle){
697                         r.min.y -= i/2;
698                         r.max.y = r.min.y+Dx(r);
699                 }else{
700                         r.max.y += i/2;
701                         r.min.y = r.max.y-Dx(r);
702                 }
703                 break;
704         case RBotLeft:
705                 i = (d.x+-d.y)/2;
706                 if(i>=Dx(r) || i>=Dy(r))
707                         break;
708                 i = max(i, a[Left]);
709                 i = -min(-i, a[Bottom]);
710                 r.min.x += i;
711                 r.max.y -= i;
712                 break;
713         case RBot:
714                 i = d.y;
715                 if(i > 0){
716                         i = min(i, a[Right]);
717                         i = -max(-i, a[Left]);
718                 }
719                 i = min(i, a[Bottom]);
720                 if(i >= Dy(r))
721                         break;
722                 r.max.y += i;
723                 /* divide the half bit equally */
724                 toggle = 1-toggle;
725                 if(toggle){
726                         r.min.x -= i/2;
727                         r.max.x = r.min.x+Dy(r);
728                 }else{
729                         r.max.x += i/2;
730                         r.min.x = r.max.x-Dy(r);
731                 }
732                 break;
733         case RBotRight:
734                 i = (-d.x+-d.y)/2;
735                 if(i>=Dx(r) || i>=Dy(r))
736                         break;
737                 i = -min(-i, a[Right]);
738                 i = -min(-i, a[Bottom]);
739                 r.max.x -= i;
740                 r.max.y -= i;
741                 break;
742         }
743         if(Dx(r)<3 || Dy(r)<3){
744                 *rp = oldr;
745                 return 0;
746         }
747         *rp = r;
748         return !eqrect(r, oldr);
749 }
750
751 void
752 rlist(Rectangle r, Rectangle *ra)
753 {
754         Rectangle tr;
755
756         tr = r;
757         tr.max.y = r.min.y+Dy(r)/4;
758         ra[0] = tr;
759         ra[0].max.x = tr.min.x+Dx(tr)/4;
760         ra[1] = tr;
761         ra[1].min.x = ra[0].max.x;
762         ra[1].max.x = tr.max.x-Dx(tr)/4;
763         ra[2] = tr;
764         ra[2].min.x = ra[1].max.x;
765
766         tr.min.y = tr.max.y;
767         tr.max.y = r.max.y-Dy(r)/4;
768         ra[3] = tr;
769         ra[3].max.x = tr.min.x+Dx(tr)/4;
770         ra[4] = tr;
771         ra[4].min.x = ra[3].max.x;
772         ra[4].max.x = tr.max.x-Dx(tr)/4;
773         ra[5] = tr;
774         ra[5].min.x = ra[4].max.x;
775
776         tr.min.y = tr.max.y;
777         tr.max.y = r.max.y;
778         ra[6] = tr;
779         ra[6].max.x = tr.min.x+Dx(tr)/4;
780         ra[7] = tr;
781         ra[7].min.x = ra[6].max.x;
782         ra[7].max.x = tr.max.x-Dx(tr)/4;
783         ra[8] = tr;
784         ra[8].min.x = ra[7].max.x;
785 }
786
787 int
788 abs(int a)
789 {
790         if(a < 0)
791                 return -a;
792         return a;
793 }
794
795 void
796 usage(void)
797 {
798         fprint(2, "usage: mug [file.bit]\n");
799         exits("usage");
800 }
801
802 void
803 eresized(int new)
804 {
805         if(new && getwindow(display, Refmesg) < 0)
806                 fprint(2,"can't reattach to window");
807         drawscreen(1);
808
809 }
810
811 /*
812 interface notes
813
814 cursor changes while in rbig to indicate region.
815 only button 1 works for resizing region
816 only button 1 works for moving thingy in ramp
817
818 button-3 menu: Reset, Depth, Undo, Save, Write
819 */
820
821 Cursor tl = {
822         {-4, -4},
823         {0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff, 
824          0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff, 
825          0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 
826          0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, },
827         {0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00, 
828          0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00, 
829          0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 
830          0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, }
831 };
832
833 Cursor t = {
834         {-7, -8},
835         {0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0, 
836          0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f, 
837          0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff, 
838          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
839         {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 
840          0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80, 
841          0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00, 
842          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
843 };
844
845 Cursor tr = {
846         {-11, -4},
847         {0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1, 
848          0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88, 
849          0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88, 
850          0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, },
851         {0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e, 
852          0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70, 
853          0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 
854          0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, }
855 };
856
857 Cursor r = {
858         {-8, -7},
859         {0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58, 
860          0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02, 
861          0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58, 
862          0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, },
863         {0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 
864          0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc, 
865          0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80, 
866          0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
867 };
868
869 Cursor br = {
870         {-11, -11},
871         {0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 
872          0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 
873          0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05, 
874          0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, },
875         {0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 
876          0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 
877          0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa, 
878          0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, }
879 };
880
881 Cursor b = {
882         {-7, -7},
883         {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
884          0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 
885          0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70, 
886          0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, },
887         {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
888          0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 
889          0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 
890          0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
891 };
892
893 Cursor bl = {
894         {-4, -11},
895         {0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 
896          0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 
897          0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01, 
898          0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, },
899         {0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 
900          0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 
901          0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe, 
902          0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, }
903 };
904
905 Cursor l = {
906         {-7, -7},
907         {0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20, 
908          0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20, 
909          0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20, 
910          0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, },
911         {0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 
912          0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0, 
913          0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0, 
914          0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, }
915 };
916
917 Cursor boxcursor = {
918         {-7, -7},
919         {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
920          0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
921          0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
922          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, },
923         {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
924          0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
925          0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
926          0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, }
927 };
928
929 Cursor clearcursor;
930
931 Cursor *corners[10] = {
932         &tl,    &t,     &tr,
933         &l,     &boxcursor,     &r,
934         &bl,    &b,     &br,
935         nil,    /* default arrow */
936 };
937
938 char *item[] = {
939         "Reset",
940         "Depth",
941         "Undo",
942         "Write",
943         "Exit",
944         nil
945 };
946
947 Menu menu = {
948         item, 
949         nil,
950         2
951 };
952
953 /*BUG make less flashy */
954 void
955 moveface(Image *back, Point lastp, Image *face, Point p, Point d)
956 {
957         draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min);
958         draw(back, back->r, screen, nil, addpt(back->r.min, subpt(p, d)));
959         border(screen, rectaddpt(face->r, subpt(p, d)),
960                  -1, display->black, ZP);
961         draw(screen, rectaddpt(face->r, subpt(p, d)), 
962                 face, nil, face->r.min);
963 }
964
965 int
966 dragface(Mouse *m, Image *im, Point d, int x)
967 {
968         int i;
969         Point lastp;
970         static Image *back;
971
972         if(back == nil){
973                 back = allocimage(display, Rect(-1,-1,49,49), display->image->chan, 0, DNofill);
974                 if(back == nil)
975                         sysfatal("dragface backing store: %r");
976         }
977
978         lastp = m->xy;
979         draw(back, back->r, screen, nil, addpt(back->r.min, subpt(lastp, d)));
980         esetcursor(&clearcursor);
981         do{
982                 moveface(back, lastp, im, m->xy, d);
983                 lastp = m->xy;
984         }while(*m=emouse(), m->buttons==1);
985
986         draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min);
987         esetcursor(nil);
988         if(m->buttons==0){
989                 for(i=0; i<nelem(face); i++)
990                         if(ptinrect(m->xy, rface[i]))
991                                 return i;
992                 if(ptinrect(m->xy, rsmall))
993                         return -1;
994                 return x;
995         }
996         while(*m=emouse(), m->buttons)
997                 ;
998         return x;
999 }
1000
1001 void
1002 initstate(void)
1003 {
1004         state.black = 0.0;
1005         state.white = 1.0;
1006         state.stretch = 1.0;
1007         state.depth = 4;
1008         state.gamma = 1.0;
1009         setgtab(&state);
1010         state.selr = insetrect(orig->r, 5);
1011         sdx = Dx(state.selr);
1012         sdy = Dy(state.selr);
1013         if(sdx > sdy)
1014                 state.selr.max.x = state.selr.min.x+sdy;
1015         else
1016                 state.selr.max.y = state.selr.min.y+sdx;
1017 }
1018
1019 void
1020 main(int argc, char **argv)
1021 {
1022         int ccursor, i, fd, k, n, y;
1023         uchar *data;
1024         double gammatab[256];
1025         Event e;
1026         Mouse m;
1027         Point lastp, p;
1028         Rectangle nselr, rbig9[9];
1029
1030         ARGBEGIN{
1031         default:
1032                 usage();
1033         }ARGEND
1034
1035         if(argc > 1)
1036                 usage();
1037         if(argc == 1){
1038                 if((fd = open(argv[0], OREAD)) < 0)
1039                         sysfatal("open %s: %r", argv[0]);
1040         }else
1041                 fd = 0;
1042
1043         if (initdraw(0, 0, "mug") < 0)
1044                 sysfatal("initdraw failed: %r");
1045
1046         if((orig = readimage(display, fd, 0)) == nil)
1047                 sysfatal("readimage: %r");
1048
1049         orig = grey8image(orig);
1050
1051         initramp();
1052         initclamp();
1053         initval2cmap();
1054         bkgd = allocimagemix(display, DPaleyellow, DWhite);
1055         small = allocimage(display, Rect(0,0,48,48), GREY4, 0, DWhite);
1056         tmp8 = allocimage(display, Rect(0,0,48,48), GREY8, 0, DWhite);
1057         red = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DRed);
1058         green = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DGreen);
1059         blue = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DBlue);
1060         if(bkgd==nil || small==nil || tmp8==nil || red==nil || green==nil || blue==nil)
1061                 sysfatal("allocimage: %r");
1062
1063         n = Dx(orig->r)*Dy(orig->r);
1064         data = emalloc(n*sizeof data[0]);
1065         rdata = emalloc(n*sizeof rdata[0]);
1066
1067         if(unloadimage(orig, orig->r, data, n) != n)
1068                 sysfatal("unloadimage: %r");
1069         
1070         for(i=0; i<256; i++)
1071                 gammatab[i] = pow((255-i)/(double)255.0, GAMMA);
1072
1073         for(i=0; i<n; i++)
1074                 rdata[i] = gammatab[255-data[i]];
1075
1076         initstate();
1077         process(rdata, orig->r, state.selr, small);
1078         drawscreen(1);
1079         flushimage(display, 1);
1080         einit(Emouse|Ekeyboard);
1081         ccursor = 9;
1082         for(;;){
1083                 if((n=eread(Emouse|Ekeyboard, &e))==Ekeyboard)
1084                         continue;
1085                 if(n != Emouse)
1086                         break;
1087
1088                 m = e.mouse;
1089                 if(m.buttons&4){
1090                         ccursor = 9;
1091                         esetcursor(corners[ccursor]);
1092                         switch(emenuhit(3, &m, &menu)){
1093                         case -1:
1094                                 continue;
1095                         case 0: /* Reset */
1096                                 mark();
1097                                 initstate();
1098                                 small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite);
1099                                 if(small == nil)
1100                                         sysfatal("allocimage: %r");
1101                                 process(rdata, orig->r, state.selr, small);
1102                                 drawface(-1);
1103                                 drawscreen(0);
1104                                 break;
1105                         case 1: /* Depth */
1106                                 mark();
1107                                 /* osmall = small, so no freeimage */
1108                                 state.depth /= 2;
1109                                 if(state.depth == 0)
1110                                         state.depth = 8;
1111                                 small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite);
1112                                 if(small == nil)
1113                                         sysfatal("allocimage: %r");
1114                                 process(rdata, orig->r, state.selr, small);
1115                                 drawface(-1);
1116                                 break;
1117                         case 2: /* Undo */
1118                                 undo();
1119                                 break;
1120                         case 3: /* Write */
1121                                 writeface(nil, small);
1122                                 break;
1123                         case 4: /* Exit */
1124                                 exits(nil);
1125                                 break;
1126                         }
1127                 }
1128                         
1129                 if(ptinrect(m.xy, rbig)){
1130                         rlist(rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), rbig9);
1131                         for(i=0; i<9; i++)
1132                                 if(ptinrect(m.xy, rbig9[i]))
1133                                         break;
1134                         if(i != ccursor){
1135                                 ccursor = i;
1136                                 esetcursor(corners[ccursor]);
1137                         }
1138                         if(i==9)
1139                                 continue;
1140
1141                         if(m.buttons & 1){
1142                                 mark();
1143                                 lastp = m.xy;
1144                                 while(m=emouse(), m.buttons&1){
1145                                         if(move(state.selr, orig->r, subpt(m.xy, lastp), i, &nselr)){
1146                                                 moveframe(state.selr, nselr);
1147                                                 state.selr = nselr;
1148                                                 lastp = m.xy;
1149                                                 process(rdata, orig->r, state.selr, small);
1150                                                 drawface(-1);
1151                                         }
1152                                 }
1153                         }
1154                         continue;
1155                 }
1156
1157                 if(ccursor != 9){       /* default cursor */
1158                         ccursor = 9;
1159                         esetcursor(corners[ccursor]);
1160                 }
1161
1162                 if(ptinrect(m.xy, rramp)){
1163                         if(m.buttons != 1)
1164                                 continue;
1165                         mark();
1166                         y = gamma2y(state.gamma);
1167                         if(abs(y-(m.xy.y-rramp.min.y)) > 5)
1168                                 continue;
1169                         k = section(m.xy.x-rramp.min.x);
1170                         drawrampbar(green, &state);
1171                         lastp = m.xy;
1172                         while(m=emouse(), m.buttons&1){
1173                                 if(!ptinrect(m.xy, rramp))
1174                                         continue;
1175                                 switch(k){
1176                                 case -1:
1177                                         continue;
1178                                 case 0:
1179                                         if((m.xy.x-rramp.min.x)/255.0 < state.white){
1180                                                 state.black = (m.xy.x-rramp.min.x)/255.0;
1181                                                 break;
1182                                         }
1183                                         continue;
1184                                 case 1:
1185                                         state.gamma = y2gamma(m.xy.y-rramp.min.y);
1186                                         setgtab(&state);
1187                                         break;
1188                                 case 2:
1189                                         if((m.xy.x-rramp.min.x)/255.0 > state.black){
1190                                                 state.white = (m.xy.x-rramp.min.x)/255.0;
1191                                                 break;
1192                                         }
1193                                         continue;
1194                                 case 10:
1195                                         state.black += (m.xy.x-lastp.x)/255.0;
1196                                         state.white += (m.xy.x-lastp.x)/255.0;
1197                                         state.gamma = y2gamma(p.y);
1198                                         break;
1199                                 }
1200                                 process(rdata, orig->r, state.selr, small);
1201                                 drawface(-1);
1202                                 drawrampbar(green, &state);
1203                         }
1204                         if(m.buttons == 0){
1205                                 process(rdata, orig->r, state.selr, small);
1206                                 drawface(-1);
1207                                 drawrampbar(red, &state);
1208                         }else
1209                                 undo();
1210                         continue;
1211                 }
1212         
1213                 if(ptinrect(m.xy, rsmall)){
1214                         if(m.buttons != 1)
1215                                 continue;
1216                         n=dragface(&m, small, subpt(m.xy, rsmall.min), -1);
1217                         if(n == -1)
1218                                 continue;
1219                         saveface(nil, n);
1220                 }
1221         
1222                 for(i=0; i<nelem(face); i++)
1223                         if(ptinrect(m.xy, rface[i]))
1224                                 break;
1225                 if(i<nelem(face) && face[i] != nil){
1226                         if(m.buttons != 1)
1227                                 continue;
1228                         n=dragface(&m, face[i]->small, subpt(m.xy, rface[i].min), i);
1229                         if(n == i)
1230                                 continue;
1231                         saveface(face[i], n);
1232                         continue;
1233                 }
1234
1235                 do
1236                         m = emouse();
1237                 while(m.buttons==1);
1238         }
1239         exits(nil);
1240 }