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"); 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 } 1241